iOS,如何使用后置摄像头进行人脸追踪?

标签 ios camera computer-vision tracking

我计划使用 ARKit 的相机输入作为 Apple 视觉 API 的输入,这样我就可以识别屏幕空间中的人脸,不需要深度信息。为了简化过程,我尝试修改 Apple 的面部跟踪示例:Tracking the User’s Face in Real Time

我想我可以简单地改变这里的功能:

 fileprivate func configureFrontCamera(for captureSession: AVCaptureSession) throws -> (device: AVCaptureDevice, resolution: CGSize) {
        let deviceDiscoverySession = AVCaptureDevice.DiscoverySession(deviceTypes: [.builtInWideAngleCamera], mediaType: .video, position: .front)

        if let device = deviceDiscoverySession.devices.first {
            if let deviceInput = try? AVCaptureDeviceInput(device: device) {
                if captureSession.canAddInput(deviceInput) {
                    captureSession.addInput(deviceInput)
                }

                if let highestResolution = self.highestResolution420Format(for: device) {
                    try device.lockForConfiguration()
                    device.activeFormat = highestResolution.format
                    device.unlockForConfiguration()

                    return (device, highestResolution.resolution)
                }
            }
        }

        throw NSError(domain: "ViewController", code: 1, userInfo: nil)
    }

在该函数的第一行中,其中一个参数是用于前置摄像头的 .front。我将其更改为 .back。这成功地给了我后置摄像头。然而,识别区域似乎有点不稳定,一旦它固定在图像中的人脸上,Xcode就会报告错误:

VisionFaceTrack[877:54517] [ServicesDaemonManager] interruptionHandler is called. -[FontServicesDaemonManager connection]_block_invoke
Message from debugger: Terminated due to memory issue

换句话说,当识别出人脸时,程序似乎崩溃了。显然,这不仅仅是简单地改变所使用的常数。也许某处的缓冲区大小错误,或者分辨率错误。我可以帮忙找出这里可能出了什么问题吗?

更好的解决方案还包括有关如何使用 arkit 的相机源实现此目的的信息,但我很确定它与 CVPixelBuffer 的想法相同。

如何调整此示例以使用后置摄像头?

编辑:我认为问题是我的设备内存太少,无法支持使用后置摄像头的算法,因为后置摄像头具有更高的分辨率。

但是,即使在其他性能更高的设备上,跟踪质量也相当糟糕。 ——然而视觉算法只需要原始图像,不是吗?既然如此,这不应该行得通吗?我在网上找不到任何使用后置摄像头进行面部跟踪的示例。

最佳答案

以下是我如何调整该示例以使其在我的 iPad Pro 上运行。


1) 从这里下载示例项目:Tracking the User’s Face in Real Time.


2) 将加载前置摄像头的功能更改为使用后置摄像头。将其重命名为 configureBackCamera 并调用此方法 setupAVCaptureSession:

fileprivate func configureBackCamera(for captureSession: AVCaptureSession) throws -> (device: AVCaptureDevice, resolution: CGSize) {
    let deviceDiscoverySession = AVCaptureDevice.DiscoverySession(deviceTypes: [.builtInWideAngleCamera], mediaType: .video, position: .back)

    if let device = deviceDiscoverySession.devices.first {
        if let deviceInput = try? AVCaptureDeviceInput(device: device) {
            if captureSession.canAddInput(deviceInput) {
                captureSession.addInput(deviceInput)
            }

            if let highestResolution = self.highestResolution420Format(for: device) {
                try device.lockForConfiguration()
                device.activeFormat = highestResolution.format
                device.unlockForConfiguration()

                return (device, highestResolution.resolution)
            }
        }
    }

    throw NSError(domain: "ViewController", code: 1, userInfo: nil)
}

3) 更改方法highestResolution420Format的实现。问题是,既然使用了后置摄像头,您就可以访问比前置摄像头分辨率高得多的格式,这可能会影响跟踪的性能。您需要适应您的用例,但这里有一个将分辨率限制为 1080p 的示例。

fileprivate func highestResolution420Format(for device: AVCaptureDevice) -> (format: AVCaptureDevice.Format, resolution: CGSize)? {
    var highestResolutionFormat: AVCaptureDevice.Format? = nil
    var highestResolutionDimensions = CMVideoDimensions(width: 0, height: 0)

    for format in device.formats {
        let deviceFormat = format as AVCaptureDevice.Format

        let deviceFormatDescription = deviceFormat.formatDescription
        if CMFormatDescriptionGetMediaSubType(deviceFormatDescription) == kCVPixelFormatType_420YpCbCr8BiPlanarFullRange {
            let candidateDimensions = CMVideoFormatDescriptionGetDimensions(deviceFormatDescription)
            if (candidateDimensions.height > 1080) {
                continue
            }
            if (highestResolutionFormat == nil) || (candidateDimensions.width > highestResolutionDimensions.width) {
                highestResolutionFormat = deviceFormat
                highestResolutionDimensions = candidateDimensions
            }
        }
    }

    if highestResolutionFormat != nil {
        let resolution = CGSize(width: CGFloat(highestResolutionDimensions.width), height: CGFloat(highestResolutionDimensions.height))
        return (highestResolutionFormat!, resolution)
    }

    return nil
}

4) 现在可以进行跟踪,但面部位置不正确。原因是UI呈现错误,因为原始示例是为前置摄像头设计的镜像显示,而后置摄像头不需要镜像。

为了对此进行调整,只需更改 updateLayerGeometry() 方法即可。具体来说,您需要更改此:

// Scale and mirror the image to ensure upright presentation.
let affineTransform = CGAffineTransform(rotationAngle: radiansForDegrees(rotation))
    .scaledBy(x: scaleX, y: -scaleY)
overlayLayer.setAffineTransform(affineTransform)

进入此:

// Scale the image to ensure upright presentation.
let affineTransform = CGAffineTransform(rotationAngle: radiansForDegrees(rotation))
    .scaledBy(x: -scaleX, y: -scaleY)
overlayLayer.setAffineTransform(affineTransform)

此后,跟踪应该可以工作并且结果应该是正确的。

关于iOS,如何使用后置摄像头进行人脸追踪?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61046411/

相关文章:

android - 在 Android 上获取当前快门速度/iso

javascript - WebGL:对象根据当前相机方向移动

javascript - Base64 图像到 WebGL 上下文(用于 readPixels)

ios - 使用 NSMutableArray 中对象的元素设置 cell.backgroundColor

ios - 在其他函数中使用位置管理器函数的值

ios - 如何将框架更改为标题标题(UITableView)

objective-c - UIImagePickerController 如何隐藏翻转相机按钮?

ios - 如何使用 Swift 实时对视频应用滤镜

matlab - 如何增加数据集?

image-processing - 图像交叉检测