我计划使用 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/