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

iOS, How to do face tracking using the rear camera?

我打算使用 ARKit 的摄像头输入作为 Apple Vision 的输入 API,这样我就可以在屏幕上识别人脸 -space,不需要深度信息。为了简化这个过程,我试图在此处修改 Apple 的基于帧的面部跟踪示例:实时跟踪用户的面部

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

 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)

在此之后,跟踪应该起作用并且结果应该是正确的。