본문 바로가기
개발하기/SwiftUI

[1탄]The Oxford-IIIT Pet Dataset를 이용해 닮은 동물 ios 앱 만들기

by lovedeveloping 2024. 9. 27.
반응형

SwiftUI로 나를 닮은 앱 만들기

자격증 공부하던 도중 문득 나를 닮은 동물은 뭘까? 싶어 간단하게 구현 해볼까? 해서 이 프로젝트를 시작하게 됐습니다. 이번 시간에는

카메라를 구현해서 정상적으로 얼굴이 감지 되는지까지의 코드를 작성 해보겠습니다.

준비물

1. Xcode
2. 실제 실행 해볼 iphone
3. The Oxford-IIIT Pet Dataset
4. 엄청나게 소중한 구글

IOS 카메라 구현 하기

이번엔 카메라가 재대로 작동하고 있는지 기능에 따라 파일을 만들어 보겠습니다.

Privacy - Camera Usage Description 생성하기

카메라 허용하기

프로젝트를 클릭하여 Info 탭에서 Privacy - Camera Usage Description을 찾아서 생성해주시면 됩니다.

다 구현해도 이거 추가 안 하면 카메라 안 나옵니다. 중요하니 프로젝트 생성해서 제일 처음으로 해주세요.

CameraManager.swift 파일 생성

import AVFoundation
import Vision
import UIKit

class CameraManager: NSObject, ObservableObject {
    @Published var isRecognizing: Bool = false
    @Published var detectedFacesCount: Int = 0
    
    let captureSession = AVCaptureSession()
    private var detectionRequest: VNDetectFaceRectanglesRequest?
    private var detectionSequenceHandler = VNSequenceRequestHandler()
    
    override init() {
        super.init()
        setupCaptureSession()
        setupVision()
    }
    
    private func setupVision() {
        detectionRequest = VNDetectFaceRectanglesRequest { [weak self] request, error in
            guard let results = request.results as? [VNFaceObservation] else { return }
            DispatchQueue.main.async {
                self?.detectedFacesCount = results.count
                self?.isRecognizing = results.count > 0
            }
        }
    }
    
    private func setupCaptureSession() {
        guard let camera = AVCaptureDevice.default(.builtInWideAngleCamera, for: .video, position: .front) else {
            print("전면 카메라를 찾을 수 없습니다.")
            return
        }
        
        do {
            let input = try AVCaptureDeviceInput(device: camera)
            if captureSession.canAddInput(input) {
                captureSession.addInput(input)
            }
        } catch {
            print("카메라 설정 중 오류 발생: \(error.localizedDescription)")
        }
    }
    
    func startSession() {
        DispatchQueue.global(qos: .background).async {
            self.captureSession.startRunning()
        }
    }
    
    func stopSession() {
        captureSession.stopRunning()
    }
    
    func detectFaces(in image: CVPixelBuffer) {
        guard let request = detectionRequest else { return }
        do {
            try detectionSequenceHandler.perform([request], on: image, orientation: .right)
        } catch {
            print("얼굴 감지 실패: \(error.localizedDescription)")
        }
    }
}

 

코드 설명

  1. 클래스 구조:
    • NSObject를 상속받고 ObservableObject 프로토콜을 준수합니다.
    • AVFoundation, Vision, UIKit 프레임워크를 사용합니다.
  2. 주요 프로퍼티:
    • isRecognizing: 얼굴 인식 중인지 여부 (Published)
    • detectedFacesCount: 감지된 얼굴 수 (Published)
    • captureSession: 카메라 캡처 세션
    • detectionRequest: 얼굴 감지 요청
    • detectionSequenceHandler: 비전 요청 처리기
  3. 초기화 및 설정:
    • init(): 카메라 세션과 비전 설정을 초기화합니다.
    • setupVision(): 얼굴 감지 요청을 설정합니다.
    • setupCaptureSession(): 전면 카메라를 사용하여 캡처 세션을 설정합니다.
  4. 주요 메서드:
    • startSession(): 백그라운드 큐에서 캡처 세션을 시작합니다.
    • stopSession(): 캡처 세션을 중지합니다.
    • detectFaces(in:): 주어진 이미지에서 얼굴을 감지합니다.
  5. 얼굴 감지 로직:
    • VNDetectFaceRectanglesRequest를 사용하여 얼굴을 감지합니다.
    • 감지 결과를 메인 큐에서 detectedFacesCount와 isRecognizing 프로퍼티에 반영합니다.
  6. 오류 처리:
    • 카메라 설정 및 얼굴 감지 과정에서 발생할 수 있는 오류를 처리합니다.

카메라를 통한 실시간 얼굴 감지 기능을 구현하는 데 사용될 수 있으며, ObservableObject를 통해 UI와 쉽게 연동될 수 있습니다.

 

CameraView.swift 파일 생성

import SwiftUI
import AVFoundation
import Vision

struct CameraView: UIViewRepresentable {
    @ObservedObject var cameraManager: CameraManager
    
    class Coordinator: NSObject, AVCaptureVideoDataOutputSampleBufferDelegate {
        var parent: CameraView
        
        init(_ parent: CameraView) {
            self.parent = parent
        }
        
        func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) {
            guard let pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) else { return }
            self.parent.cameraManager.detectFaces(in: pixelBuffer)
        }
    }
    
    func makeCoordinator() -> Coordinator {
        Coordinator(self)
    }
    
    func makeUIView(context: Context) -> UIView {
        let view = UIView(frame: UIScreen.main.bounds)
        
        let previewLayer = AVCaptureVideoPreviewLayer(session: cameraManager.captureSession)
        previewLayer.frame = view.bounds
        previewLayer.videoGravity = .resizeAspectFill
        view.layer.addSublayer(previewLayer)
        
        let videoOutput = AVCaptureVideoDataOutput()
        videoOutput.setSampleBufferDelegate(context.coordinator, queue: DispatchQueue(label: "videoQueue"))
        if cameraManager.captureSession.canAddOutput(videoOutput) {
            cameraManager.captureSession.addOutput(videoOutput)
        }
        
        return view
    }
    
    func updateUIView(_ uiView: UIView, context: Context) {}
}

코드 설명

  1. 구조체 구조:
    • UIViewRepresentable 프로토콜을 준수하여 UIKit 뷰를 SwiftUI에서 사용할 수 있게 합니다.
    • SwiftUI, AVFoundation, Vision 프레임워크를 사용합니다.
  2. 프로퍼티:
    • cameraManager: CameraManager 인스턴스를 관찰 가능한 객체로 사용합니다.
  3. Coordinator 클래스:
    • AVCaptureVideoDataOutputSampleBufferDelegate 프로토콜을 준수합니다.
    • 카메라로부터 받은 프레임을 처리하고 얼굴 감지를 수행합니다.
  4. 주요 메서드:
    • makeCoordinator(): Coordinator 인스턴스를 생성합니다.
    • makeUIView(context:): UIKit 뷰를 생성하고 설정합니다.
    • updateUIView(_:context:): 뷰 업데이트 시 호출되지만 현재는 구현되지 않았습니다.
  5. 카메라 프리뷰 설정:
    • AVCaptureVideoPreviewLayer를 사용하여 카메라 프리뷰를 표시합니다.
    • 프리뷰 레이어의 크기와 비디오 그래비티를 설정합니다.
  6. 비디오 출력 설정:
    • AVCaptureVideoDataOutput을 설정하여 비디오 프레임을 캡처합니다.
    • 캡처된 프레임은 Coordinator의 captureOutput 메서드로 전달됩니다.
  7. 얼굴 감지 처리:
    • 캡처된 각 프레임에 대해 CameraManager의 detectFaces(in:) 메서드를 호출하여 얼굴을 감지합니다.

위 코드 파일은 카메라 프리뷰를 표시하고, 실시간으로 얼굴을 감지하는 기능을 구현합니다.CameraManager와 연동하여 작동합니다.

ContentView.swiftUI 파일 생성

import SwiftUI

struct ContentView: View {
    @StateObject private var cameraManager = CameraManager()
    
    var body: some View {
        ZStack {
            CameraView(cameraManager: cameraManager)
                .edgesIgnoringSafeArea(.all)
            
            VStack {
                Spacer()
                Text("감지된 얼굴: \(cameraManager.detectedFacesCount)")
                    .foregroundColor(.white)
                    .padding()
                    .background(Color.black.opacity(0.7))
                    .cornerRadius(10)
                    .padding(.bottom)
            }
        }
        .onAppear {
            cameraManager.startSession()
        }
        .onDisappear {
            cameraManager.stopSession()
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

코드 설명

 

  1. 구조체 구조:
    • View 프로토콜을 준수하여 SwiftUI 뷰를 정의합니다.
  2. 프로퍼티:
    • cameraManager: CameraManager 인스턴스를 @StateObject로 선언하여 뷰의 생명주기 동안 유지합니다.
  3. 뷰 구성:
    • ZStack을 사용하여 카메라 뷰와 텍스트 오버레이를 겹쳐 표시합니다.
    • CameraView를 전체 화면으로 표시합니다.
    • VStack을 사용하여 화면 하단에 감지된 얼굴 수를 표시합니다.
  4. 텍스트 스타일링:
    • 감지된 얼굴 수를 표시하는 텍스트에 배경색, 모서리 둥글기, 패딩 등을 적용하여 가독성을 높입니다.
  5. 생명주기 관리:
    • onAppear 수정자를 사용하여 뷰가 나타날 때 카메라 세션을 시작합니다.
    • onDisappear 수정자를 사용하여 뷰가 사라질 때 카메라 세션을 중지합니다.
  6. 프리뷰:
    • ContentView_Previews 구조체를 사용하여 SwiftUI 프리뷰를 제공합니다.

 

 

CameraManager와 CameraView를 사용하여 실시간 카메라 피드를 표시하고, 감지된 얼굴의 수를 화면에 오버레이로 표시합니다.

최종 결과
최종 결과

 

반응형