SwiftUI / WWDC23 / Meet Object Capture for iOS

Minsang Kang·2023년 6월 23일
0

SwiftUI

목록 보기
6/12

해당 내용은 WWDC 2023: Meet Object Capture for iOS 세션 내용을 토대로 작성되었습니다.

Meet Object Capture for iOS

Object Capture 데모 프로젝트
















데모 프로젝트를 사용해볼 수 있으며, 이제 간단하게 iOS 에서 LiDAR 스캐너가 장착된 디바이스를 통해 USDZ 파일을 생성할 수 있다.
360도 모든 면을 캡쳐한 후 뒤집어 바닥또한 캡쳐하여 스캔을 완료한 후 iOS 온디바이스에서 재구성을 거쳐 USDZ 파일이 생성된다.

세션 목차




기존에 low-texture 인 개체의 경우 Object Capture의 한계가 있었으나, LiDAR 스캐너를 통한 개선점과
Object Capture 방법에 대한 소개, 관련 API 소개,
그리고 추가적인 개선사항들을 알 수 있습니다.

LiDAR 스캐너를 통해 low-texture 개체의 Object Capture 품질 향상











의자를 예시로, low-texture인 영역의 경우 RGB 이미지들로만은 완전히 캡쳐할 수 없습니다.
하지만 해당 부분을 LiDAR 스캐너를 통해 얻은 밀도 있는 Point Cloud Data를 통해 완전한 Object를 생성할 수 있습니다.
하지만 극단적으로 low-texture 상황인 반사되는 물질, 투명한 물질, 너무 얇은 물질들의 경우는 제한적입니다.

Object Capture 방법: Guided capture































Guided capture 기능을 통해 Object Capture 모든 과정이 자동화되어 있어 손쉽게 고품질의 USDZ 파일을 생성할 수 있습니다.
Guided capture 기능은 실시간으로 RGB 이미지와 LiDAR 데이터를 수집합니다.
이때 좋은 품질이 되기 위해서는 다양한 각도의 RGB 이미지와 LiDAR 데이터가 필요합니다.

첫번째로 Automatic capture 기능을 통해 측정되고 있는 개체의 형상을 나타내는 capture dial을 표시합니다.
capture dial을 통해 개체의 측정된 영역을 확인할 수 있고, 모든 면을 측정하도록 유도할 수 있습니다.

두번째로 Capture feedback 기능을 통해 알맞지 않는 상황이 발생할 경우를 인지하여 사용자에게 바람직한 상황으로 유도하기 위한 기능을 제공합니다.
빛이 부족한 상황이나, 빠르게 움직여 이미지가 흐릿한 경우, 그리고 카메라 앵글에서 너무 가깝거나 멀거나, 혹은 벗어나는 경우에 대해 경고를 나타냅니다.

세번째로 Flipping objects 기능을 통해 객체를 뒤집어서 추가적인 측정을 할지 여부에 대해 판단합니다.
객체를 뒤집기에 알맞은 상황으로는 객체가 딱딱하거나 객체의 질감(표면 무늬)가 풍부한 경우 권장되지만, 물체 모양이 변형될 수 있을 정도로 딱딱하지 않는 물체나 객체의 질감이 반복적인 무늬인 경우, 그리고 질감이 없는 민무늬 형태의 경우는 바람직하지 않습니다.
객체를 뒤집기에 알맞은지 여부는 API를 통해 제안됩니다.

객체를 뒤집어서 추가적인 측정이 알맞은 경우는 객체를 올바르게 뒤집을 수 있도록 애니메이션으로 뒤집는 방향에 대해 표시합니다.
고품질로 측정되기 위해 모든 면의 이미지를 얻을 수 있도록 3 방향으로 스캔하는 것이 좋으며, 세부적으로는 그림자, 반사를 줄이기 위한 확산 조명을 사용하는 것이 좋고, 서로 다른 방향간의 이미지 중첩이 중요합니다.

객체를 뒤집기에 알맞지 않는 경우는 3가지의 다양한 시야각에서 스캔합니다.
또한 민무늬와 같은 질감의 객체의 경우 눈에 잘 띄는 질감이 있는 바닥, 또는 배경이 좋습니다.

이러한 과정은 모두 자동화되어 제공되며, 다양한 각도로 측정된 이미지와 LiDAR 데이터를 통해 고품질의 USDZ 파일이 손쉽게 생성됩니다.
해당 Object Capture 기능은 LiDAR 스캐너가 장착된 iPhone 12 Pro 및 iPad Pro 2021 이후 모델에서 사용할 수 있습니다.

Object Capture API 사용 방법


Object Capture API는 Image Capture 단계와 Model Reconstruction 단계로 이뤄지며, SwiftUI에서 사용할 수 있습니다.

Image Capture













RealityKitSwiftUI 프레임워크를 사용하며, ObjectCapcureSessionObjectCaptureView 조합으로 구성됩니다.

ObjectCaptureSession의 경우 Object Capcure 전체 과정의 상태값을 지니며, 참조 유형의 인스턴스 입니다.
Session의 상태값은 Initializing -> Ready -> Detecting -> Capturing -> Finishing -> Completed 값들로 변화됩니다.

  • Initializing: Session이 생성될 때 설정됩니다.
  • 이후 Ready, Detecting, Capturing, Finishing 상태값으로 변화되며, 자동으로 Completed 상태가 됩니다.
  • Completed 상태가 되면 Model Reconstruction 과정으로 이어집니다.

ObjectCaptureView의 경우 Session의 상태값을 기준으로 Image Capture 일련의 과정을 자동으로 View 형태로 표시합니다.
2D 텍스트나 버튼이 없는 형태이며, 추가적인 Text나 Button의 표시가 필요한 경우 ZStack을 통해 겹쳐서 표시하면 됩니다.

1. Session 생성




import RealityKit
import SwiftUI

var session = ObjectCaptureSession()
  • session의 인스턴스를 생성합니다.
  • session의 상태값은 initializing이 됩니다.

2. Session의 configuration 생성 및 start() 실행



var configuration = ObjectCaptureSession.Configuration()
configuration.checkpointDirectory = getCodumentsDir().appendingPathComponent("Snapshots/")

session.start(imagesDirectory: getDocumentsDir().appendingPathComponent("Images/"), configuration: configuration)
  • 빠른 Reconstruction 과정을 위한 Snapshots/ 경로와 캡쳐된 이미지가 저장될 Images/ 경로를 설정한 후 start() 함수를 통해 Object Capture가 시작됩니다.
  • session의 상태는 .ready가 됩니다.

3. ObjectCaptureView 표시





import RealityKit
import SwiftUI

struct CapturePrimaryView: View {
	var body: some View {
    	ZStack {
        	ObjectCaptureView(session: session)
        }
    }
}
  • 다른 View와 마찬가지로 ObjectCaptureView를 사용합니다.
  • 기존에 생성된 session 값을 전달합니다.
  • ObjectCaptureView는 session의 상태값에 따라 UI로 표시됩니다.
  • session의 상태값이 .ready이면 객체를 선택하도록 안내하는 윤곽이 표시됩니다.

4. session.startDetecting()



var body: some View {
    ZStack {
        ObjectCaptureView(session: session)
        if case .ready = session.state {
        	CreateButton(label: "Continue") {
            	session.startDetecting()
            }
        }
    }
}
  • session의 상태값이 .ready일 때 캡처할 객체를 설정하기 위한 Button을 표시합니다.
  • 객체를 윤곽 이내로 위치한 후 Button을 눌러 session.startDetecting() 함수를 실행합니다.
  • session의 상태값은 .detecting이 됩니다.

5. session.startCapturing()






var body: some View {
    ZStack {
        ObjectCaptureView(session: session)
        if case .ready = session.state {
        	CreateButton(label: "Continue") {
            	session.startDetecting()
            }
        } else if case .detecting = session.state {
        	CreateButton(label: "Start Capture") {
            	session.startCapturing()
            }
        }
    }
}
  • session의 상태값이 .detecting이 되면 캡처할 객체 주위에 경계상자가 표시되며, 캡처를 진행하기 위한 Button을 표시합니다.
  • 경계상자의 크기와 방향을 수정할수도 있습니다.
  • 경계상자가 표시된 상태에서 Button을 눌러 session.startCapturing() 함수를 실행합니다.
  • session의 상태값은 .capturing이 됩니다.
  • 만약 경계상자를 해제한 후 다른 객체를 선택하고자 하는 경우 session.resetDetection() 함수를 실행하여 .ready 상태로 돌아갈 수 있습니다.

6. session.capturing




  • session의 상태값이 .capturing이 되면 자동으로 측정되고 있는 개체의 형상을 나타내는 capture dial이 표시됩니다.
  • 주위를 천천히 이동하면 자동으로 캡처된 영역과 포인트 클라우드가 표시됩니다.

7. session.userCompletedScanPass == true












var body: some View {
	if session.userCompletedScanPass {
    	VStack {
        }
    } else {
    	ZStack {
        	ObjectCaptureView(session: session)
        }
    }
}
  • 다이얼이 완전히 채워지면 현재 Scan pass가 완료되며, session.userCompletedScanPass 값이 true로 설정됩니다.
  • 이 시점에 Object Capture의 품질을 높이기 위하여 다음 Scan pass를 진행합니다.
  • 총 3번의 Scan pass를 진행하는 것이 좋으며, 뒤집는게 좋은 경우와 안좋은 경우로 분리하여 Scan pass를 진행합니다.
  • 뒤집는 경우 session.beginNewScanPassAfterFlip() 함수를 실행하여 다음 Scan pass를 진행합니다. 이때 session의 상태값은 .ready가 됩니다.
  • 뒤집지 않는 경우 session.beginNewScanPass() 함수를 실행하여 다른 높이에서 Scan pass를 진행합니다. 이때 session의 상태값은 .capturing으로 유지됩니다.

8. session.finish()











var body: some View {
	if session.userCompletedScanPass {
    	VStack {
        	ObjectCapturePointCloudView(session: session)
        	CreateButton(label: "Finish") {
            	session.finish()
            }
        }
    } else {
    	ZStack {
        	ObjectCaptureView(session: session)
        }
    }
}
  • 모든 Scan pass가 완료되면 session.finish() 함수를 실행하여 Object Capture를 종료합니다.
  • session의 상태값은 .finishing이 됩니다.
  • 이때 모든 데이터가 저장될 때까지 기다리며, 완료되면 자동으로 상태값이 .completed로 변합니다.
  • 만약 이미지 디렉토리를 사용할 수 없는 경우와 같은 오류가 발생하면 session의 상태값은 .failed가 되며, 새로운 session을 생성하여 다시 진행해야 합니다.
  • 또한 ObjectCapturePointCloudView를 통해 측정된 객체를 Point Cloud로 표시할 수 있습니다.

Model Reconstruction





var body: some View {
	ReconstructionProgressView()
		.task {
			var configuration = PhotogrammetrySession.Configuration ()
			configuration.checkpointDirectory = getDocumentsDir ()
				.appendingPathComponent ("Snapshots/")
			let session = try PhotogrammetrySession (
				input: getDocumentsDir () .appendingPathComponent ("Images/"), 
                configuration: configuration)
			try session.process (requests: [
				•modelFile (url: getDocumentsDir ().appendingPathComponent ("model.usdz") )
			])
			for try await output in session.outputs {
				switch output {
					case •processingComplete:
						handleComplete ()
						// Handle other Output messages here.
                }
            }
        }
    }
}
  • 올해부터 iOS에서도 USDZ 파일을 생성하는 작업인 Reconstruction을 지원합니다.
  • 아이폰 하나로 Object Capture와 Reconstruction 과정을 통해 간편하게 USDZ 파일을 생성할 수 있습니다.
  • iOS는 물론, macOS 에서도 동일하게 동작됩니다.

1. PhotogrammetrySession



  • ReconstructionProgressView()를 통해 Reconstruction 상태를 표시할 수 있습니다.
  • 모든 작업은 비동기로 이뤄지기에 task modifier를 추가합니다.
  • 만약 Image Capture 단계에서 Session 생성시 빠른 Reconstruction 과정을 위해 checkPointDirectory를 설정한 경우 configuration을 생성합니다.
  • 다음으로 PhotogrammetrySession으로 session을 생성합니다.
  • Image Capture 단계에서 캡처된 이미지들의 경로가 input값이며, configuration을 인자로 넣습니다.

2. session.process

  • 다음으로 process() 함수를 호출하며 .modelFile를 통해 생성하고자 하는 usdz 모델명을 설정합니다.

3. session.outputs


  • 마지막으로 for 루프에서 session.output의 결과가 .processingComplete 값이 될 때 까지 기다립니다.
  • 해당 부분에서 Image Capture가 종료된 이후의 코드를 실행하면 됩니다.

iOS 용으로 최적화된 Reconstruction


Object Capture 요약




  • LiDAR Scanner를 활용하여 더욱 많은 texture의 객체의 Object Capture를 지원합니다.
  • 이제 iOS 하나의 기기에서 Object Capture를 하고, Reconstruction을 통해 usdz 파일까지 생성할 수 있습니다.
  • 이러한 Object Capture API를 활용하여 다양한 앱으로 제작할 수 있습니다.
profile
 iOS Developer

0개의 댓글