【问题标题】:How to capture depth data from camera in iOS 11 and Swift 4?如何在 iOS 11 和 Swift 4 中从相机捕获深度数据?
【发布时间】:2017-06-12 19:03:30
【问题描述】:

我正在尝试使用 AVDepthData 从 iOS 11 中的相机获取深度数据,但是当我使用 AVCapturePhotoCaptureDelegate 设置 photoOutput 时,photo.depthData 为 nil。

所以我尝试使用 AVCaptureDepthDataOutput 设置 AVCaptureDepthDataOutputDelegate,但我不知道如何捕获深度照片?

有人从 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()

}

我正在尝试两件事,一件是深度数据为零,另一件是我试图调用深度委托方法。

有人知道我错过了什么吗?

【问题讨论】:

  • 能否提供您尝试过的代码?
  • 当然。我在尝试两件事合二为一,我会尝试用代码更多地解释它。
  • 您使用的是 iPhone 7 吗?我认为您需要双摄像头才能获得深度
  • 是的,iPhone 7 配备了双摄像头,但 AVDepthData 仍然为零。
  • 有人让它工作吗? @Coder256 你用的是什么硬件?

标签: swift camera ios11 color-depth


【解决方案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() 方法中同步两个输出

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 - 他们还提供了一个完整的示例应用程序,可以完全满足您的需求。

https://developer.apple.com/videos/play/wwdc2017/507/

【讨论】:

    【解决方案2】:

    有两种方法可以做到这一点,而您正试图同时做到这两种方法:

    1. 随图像一起捕获深度数据。这是通过使用来自photoOutput(_:didFinishProcessingPhoto:error:)photo.depthData 对象来完成的。我在下面解释了为什么这对您不起作用。
    2. 使用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。请务必阅读thisthis(单独但相似的页面)以获取有关获取深度数据的更多信息。

    对于 #2,我不太确定为什么没有调用 depthDataOutput(_:didOutput:timestamp:connection:),但您应该实现 depthDataOutput(_:didDrop:timestamp:connection:reason:) 以查看是否由于某种原因丢弃了深度数据。

    【讨论】:

    • 谢谢!虽然我在将 .isDepthDataDeliveryEnabled 设置为 true 时发生了崩溃:[AVCapturePhotoOutput setDepthDataDeliveryEnabled:] 当前配置不支持深度数据传递',然后我阅读了您发布并尝试的两个链接:self.sessionOutput.isDepthDataDeliverySupported,但它重新调整为 false对于我的 iPhone 7 Plus,不知道为什么,也许这是一些 iPhone 8 的东西:macrumors.com/2017/06/14/apple-camera-lens-supplier-3d/…
    • 很高兴我能帮上忙!虽然问题可能是您在 iPhone 上运行的是 iOS 10 而不是 iOS 11。
    • 这如何工作?这不需要带有特殊硬件的手机吗?
    【解决方案3】:

    为了给@klinger 答案提供更多细节,这里是获取每个像素的深度数据所需要做的事情,我写了一些 cmets,希望对您有所帮助!

    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)]
    
    }
    

    【讨论】:

      【解决方案4】:

      您启动捕获设备的方式不正确。

      您应该使用双摄像头模式。

      至于oc如下:

      AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithDeviceType:AVCaptureDeviceTypeBuiltInDualCamera mediaType:AVMediaTypeVideo position:AVCaptureDevicePositionBack];
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2019-11-30
        • 2018-07-10
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-01-05
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多