web-dev-qa-db-fra.com

Comment capturer les données de profondeur de la caméra sous iOS 11 et Swift 4?

J'essaie d'obtenir des données de profondeur de la caméra dans iOS 11 avec AVDepthData, mais lorsque je configure un photoOutput avec AVCapturePhotoCaptureDelegate, la photo.depthData est nulle.

J'ai donc essayé de configurer AVCaptureDepthDataOutputDelegate avec un AVCaptureDepthDataOutput, mais je ne sais pas comment capturer la photo de profondeur?

Quelqu'un a-t-il déjà reçu une image d'AVDepthData?

Modifier:

Voici le code que j'ai essayé:

// 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()

}

J'essaie deux choses, l'une pour laquelle les données de profondeur sont nulles et l'autre pour laquelle j'appelle une méthode de délégation de profondeur.

Dose quelqu'un sait ce que je manque?

7
Hexagons

Tout d'abord, vous devez utiliser la double caméra, sinon vous n'obtiendrez aucune donnée de profondeur.

let device = AVCaptureDevice.default(.builtInDualCamera, for: .video, position: .back)

Et gardez une référence à votre file d'attente

let dataOutputQueue = DispatchQueue(label: "data queue", qos: .userInitiated, attributes: [], autoreleaseFrequency: .workItem)

Vous voudrez probablement aussi synchroniser les données vidéo et de profondeur

var outputSynchronizer: AVCaptureDataOutputSynchronizer?

Ensuite, vous pouvez synchroniser les deux sorties dans votre méthode viewDidLoad () comme ceci

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

Je recommanderais de regarder la session 507 de la WWDC. Ils fournissent également un exemple d'application complet qui fait exactement ce que vous voulez.

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

6
klinger

Pour donner plus de détails à la réponse de @klinger, voici ce que vous devez faire pour obtenir des données de profondeur pour chaque pixel. J'ai écrit quelques commentaires. J'espère que cela aidera!

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
Oscar

Il y a deux façons de faire cela, et vous essayez de faire les deux en même temps:

  1. Capturez les données de profondeur avec l'image. Ceci est fait en utilisant l'objet photo.depthData de photoOutput(_:didFinishProcessingPhoto:error:). J'explique pourquoi cela n'a pas fonctionné pour vous ci-dessous.
  2. Utilisez une AVCaptureDepthDataOutput et implémentez depthDataOutput(_:didOutput:timestamp:connection:). Je ne sais pas pourquoi cela n’a pas fonctionné pour vous, mais l’application de depthDataOutput(_:didOutput:timestamp:connection:) pourrait vous aider à comprendre pourquoi.

Je pense que le n ° 1 est une meilleure option, car il associe les données de profondeur à l'image. Voici comment vous feriez cela:

@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
    // ...
}

Alors, depth_map ne devrait pas être nil. Assurez-vous de lire les deux this et this (pages séparées mais similaires) pour plus d'informations sur l'obtention de données de profondeur.

Pour le n ° 2, je ne sais pas trop pourquoi depthDataOutput(_:didOutput:timestamp:connection:) n'est pas appelé, mais vous devez implémenter depthDataOutput(_:didDrop:timestamp:connection:reason:) pour voir si les données de profondeur ont été supprimées pour une raison quelconque.

1
Coder256

La façon dont vous initiez votre périphérique de capture n'est pas correcte.

Vous devriez utiliser le mode double caméra.

comme pour oc comme suit:

AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithDeviceType:AVCaptureDeviceTypeBuiltInDualCamera mediaType:AVMediaTypeVideo position:AVCaptureDevicePositionBack];
0
周景锦