Как захват данных с камеры в iOS 11 и Swift 4?
Я пытаюсь получить данные глубины с камеры в iOS 11 С AVDepthData, tho, когда я настраиваю photoOutput с AVCapturePhotoCaptureDelegate фотографию.depthData равна нулю.
поэтому я попытался настроить AVCaptureDepthDataOutputDelegate с помощью AVCaptureDepthDataOutput, то я не знаю, как захватить фотографию глубины?
у кого-нибудь есть картинка из AVDepthData?
Edit:
вот код I попробовал:
// 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()
}
Я пытаюсь две вещи: одна, где данные глубины равны нулю, и одна, где я пытаюсь вызвать метод делегата глубины.
кто-нибудь знает, чего мне не хватает?
4 ответов
во-первых, вам нужно использовать двойную камеру, иначе вы не получите никаких данных о глубине.
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 - они также предоставляют полный образец приложения, которое делает именно то, что вы хотеть.
чтобы дать более подробную информацию @klinger answer, вот что вам нужно сделать, чтобы получить данные глубины для каждого пикселя, я написал несколько комментариев, Надеюсь, это поможет!
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)]
}
есть два способа сделать это, и вы пытаетесь сделать так сразу:
-  захват данных глубины вместе с изображением. Это делается с помощью 
photo.depthDataобъектphotoOutput(_:didFinishProcessingPhoto:error:). Я объясню, почему это не сработало для вас ниже. - использовать 
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. Обязательно прочитайте оба этой и этой (отдельные, но похожие страницы) для получения дополнительной информации о получении данных глубины.
для #2, я не совсем уверен, почему depthDataOutput(_:didOutput:timestamp:connection:) не вызывается, но вы должны реализовать depthDataOutput(_:didDrop:timestamp:connection:reason:) чтобы увидеть, если данные глубины отбрасываются по какой-то причине.
то, как вы init устройство захвата не правильно.
вы должны использовать режим двойной камеры.
что касается oc, как следует:
AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithDeviceType:AVCaptureDeviceTypeBuiltInDualCamera mediaType:AVMediaTypeVideo position:AVCaptureDevicePositionBack];