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)
在此之后,跟踪应该起作用并且结果应该是正确的。
我打算使用 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)
在此之后,跟踪应该起作用并且结果应该是正确的。