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?
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.
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)]
}
Il y a deux façons de faire cela, et vous essayez de faire les deux en même temps:
photo.depthData
de photoOutput(_:didFinishProcessingPhoto:error:)
. J'explique pourquoi cela n'a pas fonctionné pour vous ci-dessous.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.
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];