我试图在我的 iOS 相机应用程序中检测人脸,但它不能正常工作,而它在 Camera.app 中工作正常。请注意:
- 在我的应用程序中未检测到第一张脸,仅在 Camera.app 中。
- 对于第三张脸——东亚女性——Camera.app 正确地在她的脸周围绘制了一个矩形,而我的应用程序绘制了一个延伸到她脸下方的矩形。
- 在我的应用程序中没有检测到奥巴马的脸,只有在 Camera.app 中。
- 当相机从普京的脸上缩小时,我的应用程序会在他的右半边脸上绘制一个矩形,将其切成两半,而 Camera.app 会在他的脸上正确地绘制一个矩形。
为什么会这样?
我的代码如下。你看有什么不对吗?
首先,我创建一个视频输出如下:
let videoOutput = AVCaptureVideoDataOutput()
videoOutput.videoSettings =
[kCVPixelBufferPixelFormatTypeKey as AnyHashable:
Int(kCMPixelFormat_32BGRA)]
session.addOutput(videoOutput)
videoOutput.setSampleBufferDelegate(faceDetector, queue: faceDetectionQueue)
这是代表:
class FaceDetector: NSObject, AVCaptureVideoDataOutputSampleBufferDelegate {
func captureOutput(_ captureOutput: AVCaptureOutput!,
didOutputSampleBuffer sampleBuffer: CMSampleBuffer!,
from connection: AVCaptureConnection!) {
let imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer)!
let features = FaceDetector.ciDetector.features(
in: CIImage(cvPixelBuffer: imageBuffer))
let faces = features.map { $0.bounds }
let imageSize = CVImageBufferGetDisplaySize(imageBuffer)
let faceBounds = faces.map { (face: CIFeature) -> CGRect in
var ciBounds = face.bounds
ciBounds = ciBounds.applying(
CGAffineTransform(scaleX: 1/imageSize.width, y: -1/imageSize.height))
CGRect(x: 0, y: 0, width: 1, height: -1).verifyContains(ciBounds)
let bounds = ciBounds.applying(CGAffineTransform(translationX: 0, y: 1.0))
CGRect(x: 0, y: 0, width: 1, height: 1).verifyContains(bounds)
return bounds
}
DispatchQueue.main.sync {
facesUpdated(faceBounds, imageSize)
}
}
private static let ciDetector = CIDetector(ofType: CIDetectorTypeFace,
context: nil,
options: [CIDetectorAccuracy: CIDetectorAccuracyHigh])!
}
facesUpdated() 回调如下:
class PreviewView: UIView {
private var faceRects = [UIView]()
private static func makeFaceRect() -> UIView {
let r = UIView()
r.layer.borderWidth = FocusRect.borderWidth
r.layer.borderColor = FocusRect.color.cgColor
faceRects.append(r)
addSubview(r)
return r
}
private func removeAllFaceRects() {
for faceRect in faceRects {
verify(faceRect.superview == self)
faceRect.removeFromSuperview()
}
faceRects.removeAll()
}
private func facesUpdated(_ faces: [CGRect], _ imageSize: CGSize) {
removeAllFaceRects()
let faceFrames = faces.map { (original: CGRect) -> CGRect in
let face = original.applying(CGAffineTransform(scaleX: bounds.width, y: bounds.height))
verify(self.bounds.contains(face))
return face
}
for faceFrame in faceFrames {
let faceRect = PreviewView.makeFaceRect()
faceRect.frame = faceFrame
}
}
}
我也尝试了以下方法,但它们没有帮助:
- 将 AVCaptureVideoDataOutput 的 videoSettings 设置为 nil。
- 将 CIDetector 的方向显式设置为纵向。手机在此测试中是纵向的,所以没关系。
- 设置和删除 CIDetectorTracking: true
- 设置和删除 CIDetectorAccuracy:CIDetectorAccuracyHigh
- 通过仅查看检测到的第一个特征来尝试仅跟踪一张脸。
- 将 CVImageBufferGetDisplaySize() 替换为 CVImageBufferGetEncodedSize() - 它们无论如何都是相同的,在 1440 x 1080。