AVDepthDataを使用してiOS 11のカメラから深度データを取得しようとしています。AVCapturePhotoCaptureDelegateでphotoOutputを設定すると、photo.depthDataはnilになります。
それで、AVCaptureDepthDataOutputDelegateをAVCaptureDepthDataOutputで設定してみましたが、奥行きの写真をキャプチャする方法がわかりません。
AVDepthDataから画像を取得したことはありますか?
編集:
これが私が試したコードです:
// delegates: AVCapturePhotoCaptureDelegate & AVCaptureDepthDataOutputDelegate
@IBOutlet var image_view: UIImageView!
@IBOutlet var capture_button: UIButton!
var captureSession: AVCaptureSession?
var sessionOutput: AVCapturePhotoOutput?
var depthOutput: AVCaptureDepthDataOutput?
var previewLayer: AVCaptureVideoPreviewLayer?
@IBAction func capture(_ sender: Any) {
self.sessionOutput?.capturePhoto(with: AVCapturePhotoSettings(format: [AVVideoCodecKey: AVVideoCodecType.jpeg]), delegate: self)
}
func photoOutput(_ output: AVCapturePhotoOutput, didFinishProcessingPhoto photo: AVCapturePhoto, error: Error?) {
self.previewLayer?.removeFromSuperlayer()
self.image_view.image = UIImage(data: photo.fileDataRepresentation()!)
let depth_map = photo.depthData?.depthDataMap
print("depth_map:", depth_map) // is nil
}
func depthDataOutput(_ output: AVCaptureDepthDataOutput, didOutput depthData: AVDepthData, timestamp: CMTime, connection: AVCaptureConnection) {
print("depth data") // never called
}
override func viewDidLoad() {
super.viewDidLoad()
self.captureSession = AVCaptureSession()
self.captureSession?.sessionPreset = .photo
self.sessionOutput = AVCapturePhotoOutput()
self.depthOutput = AVCaptureDepthDataOutput()
self.depthOutput?.setDelegate(self, callbackQueue: DispatchQueue(label: "depth queue"))
do {
let device = AVCaptureDevice.default(for: .video)
let input = try AVCaptureDeviceInput(device: device!)
if(self.captureSession?.canAddInput(input))!{
self.captureSession?.addInput(input)
if(self.captureSession?.canAddOutput(self.sessionOutput!))!{
self.captureSession?.addOutput(self.sessionOutput!)
if(self.captureSession?.canAddOutput(self.depthOutput!))!{
self.captureSession?.addOutput(self.depthOutput!)
self.previewLayer = AVCaptureVideoPreviewLayer(session: self.captureSession!)
self.previewLayer?.frame = self.image_view.bounds
self.previewLayer?.videoGravity = AVLayerVideoGravity.resizeAspectFill
self.previewLayer?.connection?.videoOrientation = AVCaptureVideoOrientation.portrait
self.image_view.layer.addSublayer(self.previewLayer!)
}
}
}
} catch {}
self.captureSession?.startRunning()
}
私は2つのことを試みています。1つは深度データがnilであり、もう1つは深度デリゲートメソッドを呼び出そうとしています。
誰かが私が欠けているものを知っていますか?
まず、デュアルカメラを使用する必要があります。そうしないと、深度データを取得できません。
let device = AVCaptureDevice.default(.builtInDualCamera, for: .video, position: .back)
そして、キューへの参照を保持します
let dataOutputQueue = DispatchQueue(label: "data queue", qos: .userInitiated, attributes: [], autoreleaseFrequency: .workItem)
また、おそらくビデオと深度データを同期する必要があります
var outputSynchronizer: AVCaptureDataOutputSynchronizer?
次に、このようにviewDidLoad()メソッドで2つの出力を同期できます。
if sessionOutput?.isDepthDataDeliverySupported {
sessionOutput?.isDepthDataDeliveryEnabled = true
depthDataOutput?.connection(with: .depthData)!.isEnabled = true
depthDataOutput?.isFilteringEnabled = true
outputSynchronizer = AVCaptureDataOutputSynchronizer(dataOutputs: [sessionOutput!, depthDataOutput!])
outputSynchronizer!.setDelegate(self, queue: self.dataOutputQueue)
}
私はWWDCセッション507を視聴することをお勧めします-彼らはまたあなたが望むものを正確に行う完全なサンプルアプリを提供します。
@klingerの回答の詳細を説明するために、各ピクセルの深度データを取得するために必要なことを以下に示します。
func photoOutput(_ output: AVCapturePhotoOutput, didFinishProcessingPhoto photo: AVCapturePhoto, error: Error?) {
//## Convert Disparity to Depth ##
let depthData = (photo.depthData as AVDepthData!).converting(toDepthDataType: kCVPixelFormatType_DepthFloat32)
let depthDataMap = depthData.depthDataMap //AVDepthData -> CVPixelBuffer
//## Data Analysis ##
// Useful data
let width = CVPixelBufferGetWidth(depthDataMap) //768 on an iPhone 7+
let height = CVPixelBufferGetHeight(depthDataMap) //576 on an iPhone 7+
CVPixelBufferLockBaseAddress(depthDataMap, CVPixelBufferLockFlags(rawValue: 0))
// Convert the base address to a safe pointer of the appropriate type
let floatBuffer = unsafeBitCast(CVPixelBufferGetBaseAddress(depthDataMap), to: UnsafeMutablePointer<Float32>.self)
// Read the data (returns value of type Float)
// Accessible values : (width-1) * (height-1) = 767 * 575
let distanceAtXYPoint = floatBuffer[Int(x * y)]
}
これには2つの方法があり、同時に両方を実行しようとしています。
photoOutput(_:didFinishProcessingPhoto:error:)
の_photo.depthData
_オブジェクトを使用して行われます。これがうまくいかなかった理由を以下で説明します。AVCaptureDepthDataOutput
を使用してdepthDataOutput(_:didOutput:timestamp:connection:)
を実装します。これがうまくいかなかった理由はわかりませんが、depthDataOutput(_:didOutput:timestamp:connection:)
を実装すると、その理由がわかる場合があります。深度データと画像を組み合わせるので、#1の方が良いオプションだと思います。これを行う方法は次のとおりです。
_@IBAction func capture(_ sender: Any) {
let settings = AVCapturePhotoSettings(format: [AVVideoCodecKey: AVVideoCodecType.jpeg])
settings.isDepthDataDeliveryEnabled = true
self.sessionOutput?.capturePhoto(with: settings, delegate: self)
}
// ...
override func viewDidLoad() {
// ...
self.sessionOutput = AVCapturePhotoOutput()
self.sessionOutput.isDepthDataDeliveryEnabled = true
// ...
}
_
次に、_depth_map
_をnil
にしないでください。深度データの取得の詳細については、必ず this と this (別々だが類似したページ)の両方を読んでください。
#2の場合、depthDataOutput(_:didOutput:timestamp:connection:)
が呼び出されない理由はよくわかりませんが、depthDataOutput(_:didDrop:timestamp:connection:reason:)
を実装して、何らかの理由で深度データがドロップされているかどうかを確認する必要があります。
キャプチャデバイスを初期化する方法が正しくありません。
デュアルカメラモードを使用する必要があります。
ocは次のように:
AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithDeviceType:AVCaptureDeviceTypeBuiltInDualCamera mediaType:AVMediaTypeVideo position:AVCaptureDevicePositionBack];