【问题标题】:Filtering Depth Data on iOS 12 appears to be rotatediOS 12 上的过滤深度数据似乎已旋转
【发布时间】:2019-03-05 18:17:38
【问题描述】:

我遇到了一个问题,当isFilteringEnabled = true 时,.builtInDualCamera 的深度数据似乎旋转了 90 度

这是我的代码:

fileprivate let session = AVCaptureSession()

fileprivate let meta = AVCaptureMetadataOutput()
fileprivate let video = AVCaptureVideoDataOutput()
fileprivate let depth = AVCaptureDepthDataOutput()

fileprivate let camera: AVCaptureDevice
fileprivate let input: AVCaptureDeviceInput

fileprivate let synchronizer: AVCaptureDataOutputSynchronizer

init(delegate: CaptureSessionDelegate?) throws {
    self.delegate = delegate
    session.sessionPreset = .vga640x480

    // Setup Camera Input
    let discovery = AVCaptureDevice.DiscoverySession(deviceTypes: [.builtInDualCamera], mediaType: .video, position: .unspecified)
    if let device = discovery.devices.first {
        camera = device
    } else {
        throw SessionError.CameraNotAvailable("Unable to load camera")
    }

    input = try AVCaptureDeviceInput(device: camera)
    session.addInput(input)

    // Setup Metadata Output (Face)
    session.addOutput(meta)
    if meta.availableMetadataObjectTypes.contains(AVMetadataObject.ObjectType.face) {
        meta.metadataObjectTypes = [ AVMetadataObject.ObjectType.face ]
    } else {
        print("Can't Setup Metadata: \(meta.availableMetadataObjectTypes)")
    }

    // Setup Video Output
    video.videoSettings = [kCVPixelBufferPixelFormatTypeKey as String: kCVPixelFormatType_32BGRA]
    session.addOutput(video)
    video.connection(with: .video)?.videoOrientation = .portrait

    // ****** THE ISSUE IS WITH THIS BLOCK HERE ******
    // Setup Depth Output
    depth.isFilteringEnabled = true
    session.addOutput(depth)
    depth.connection(with: .depthData)?.videoOrientation = .portrait

    // Setup Synchronizer
    synchronizer = AVCaptureDataOutputSynchronizer(dataOutputs: [depth, video, meta])


    let outputRect = CGRect(x: 0, y: 0, width: 1, height: 1)
    let videoRect = video.outputRectConverted(fromMetadataOutputRect: outputRect)
    let depthRect = depth.outputRectConverted(fromMetadataOutputRect: outputRect)

    // Ratio of the Depth to Video
    scale = max(videoRect.width, videoRect.height) / max(depthRect.width, depthRect.height)

    // Set Camera to the framerate of the Depth Data Collection
    try camera.lockForConfiguration()
    if let fps = camera.activeDepthDataFormat?.videoSupportedFrameRateRanges.first?.minFrameDuration {
        camera.activeVideoMinFrameDuration = fps
    }
    camera.unlockForConfiguration()

    super.init()
    synchronizer.setDelegate(self, queue: syncQueue)
}

func dataOutputSynchronizer(_ synchronizer: AVCaptureDataOutputSynchronizer, didOutput data: AVCaptureSynchronizedDataCollection) {
    guard let delegate = self.delegate else {
        return
    }

    // Check to see if all the data is actually here
    guard
        let videoSync = data.synchronizedData(for: video) as? AVCaptureSynchronizedSampleBufferData,
        !videoSync.sampleBufferWasDropped,
        let depthSync = data.synchronizedData(for: depth) as? AVCaptureSynchronizedDepthData,
        !depthSync.depthDataWasDropped
    else {
        return
    }

    // It's OK if the face isn't found.
    let face: AVMetadataFaceObject?
    if let metaSync = data.synchronizedData(for: meta) as? AVCaptureSynchronizedMetadataObjectData {
            face = (metaSync.metadataObjects.first { $0 is AVMetadataFaceObject }) as? AVMetadataFaceObject
    } else {
            face = nil
    }

    // Convert Buffers to CIImage
    let videoImage = convertVideoImage(fromBuffer: videoSync.sampleBuffer)
    let depthImage = convertDepthImage(fromData: depthSync.depthData, andFace: face)

    // Call Delegate
    delegate.captureImages(video: videoImage, depth: depthImage, face: face)
}

fileprivate func convertVideoImage(fromBuffer sampleBuffer: CMSampleBuffer) -> CIImage {
    // Convert from "CoreMovie?" to CIImage - fairly straight-forward
    let pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer)
    let image = CIImage(cvPixelBuffer: pixelBuffer!)
    return image
}

fileprivate func convertDepthImage(fromData depthData: AVDepthData, andFace face: AVMetadataFaceObject?) -> CIImage {

    var convertedDepth: AVDepthData

    // Convert 16-bif floats up to 32
    if depthData.depthDataType != kCVPixelFormatType_DisparityFloat32 {
        convertedDepth = depthData.converting(toDepthDataType: kCVPixelFormatType_DisparityFloat32)
    } else {
        convertedDepth = depthData
    }

    // Pixel buffer comes straight from depthData
    let pixelBuffer = convertedDepth.depthDataMap

    let image = CIImage(cvPixelBuffer: pixelBuffer)
    return image
}

原视频如下:(供参考)

当值是:

// Setup Depth Output
depth.isFilteringEnabled = false
depth.connection(with: .depthData)?.videoOrientation = .portrait

图像是这样的:(你可以看到近的夹克是白色的,远的夹克是灰色的,远的时候是深灰色的——正如预期的那样)

当值是:

// Setup Depth Output
depth.isFilteringEnabled = true
depth.connection(with: .depthData)?.videoOrientation = .portrait

图像看起来像这样:(您可以看到颜色值似乎在正确的位置,但平滑过滤器中的形状似乎是旋转的)

当值是:

// Setup Depth Output
depth.isFilteringEnabled = true
depth.connection(with: .depthData)?.videoOrientation = .landscapeRight

图像看起来像这样:(颜色和形状都是水平的)

我做错了什么来获取这些不正确的值吗?

我已尝试重新排序代码

// Setup Depth Output
depth.connection(with: .depthData)?.videoOrientation = .portrait
depth.isFilteringEnabled = true

但这无济于事。

我认为这是与 iOS 12 相关的问题,因为我记得这在 iOS 11 下运行良好(尽管我没有保存任何图像来证明这一点)

感谢任何帮助,谢谢!

【问题讨论】:

  • 也许你应该知道iphone是如何先保存图像轴的stackoverflow.com/questions/10850184/…
  • 过去我有一个使用前置摄像头的 ARKit 项目,当我尝试录制视频时,它会旋转我录制的方向。我解决此问题的方法是将转换应用于我正在处理的缓冲区。所以在你的情况下,当你想查看过滤后的图像时,也许你可以设置另一个标志isFiltering。您可以在 ConvertVideoImageConvertDepthImage 中使用该标志来旋转 CIImage 以在您的应用中正确显示它。

标签: ios swift avcapturesession ios12 avdepthdata


【解决方案1】:

与在创建后查看其他关于旋转图像的答案的建议不同,我发现这不起作用,在AVDepthData documentation 中,有一种方法可以为您进行方向校正。

该方法被调用:depthDataByApplyingExifOrientation:,它返回一个应用了方向的AVDepthData 实例,即。您可以通过传入您选择的参数以所需的正确方向创建图像。

这是我的辅助方法,它返回带有方向修复的UIImage

- (UIImage *)createDepthMapImageFromCapturePhoto:(AVCapturePhoto *)photo {
    // AVCapturePhoto which has depthData - in swift you should confirm this exists
    AVDepthData *frontDepthData = [photo depthData];
    // Overwrite the instance with the correct orientation applied.
    frontDepthData = [frontDepthData depthDataByApplyingExifOrientation:kCGImagePropertyOrientationRight];
    // Create the CIImage from the depth data using the available method.
    CIImage *ciDepthImage = [CIImage imageWithDepthData:frontDepthData];
    // Create CIContext which enables converting CIImage to CGImage
    CIContext *context = [[CIContext alloc] init];
    // Create the CGImage
    CGImageRef img = [context createCGImage:ciDepthImage fromRect:[ciDepthImage extent]];
    // Create the final image.
    UIImage *depthImage = [UIImage imageWithCGImage:img];
    // Return the depth image.
    return depthImage;
}

【讨论】:

  • 针对我正在从事的特定项目用 Objective-C 编写。翻译成 Swift 应该不会太难。
  • 谢谢你,很遗憾我不再从事这个项目,所以我无法测试你的答案并将其标记为已接受,但我感谢你的工作。
猜你喜欢
  • 2019-01-05
  • 1970-01-01
  • 2017-02-26
  • 1970-01-01
  • 1970-01-01
  • 2022-10-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多