Почему мой код обнаружения лица, использующий CIDetector, не работает должным образом?
Я пытаюсь определить лица в приложении 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])!
}
Обратный вызов faceUpdated() выглядит следующим образом:
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
}
}
}
Я также попробовал следующее, но они не помогли:
- Установка VideoSettings AVCaptureVideoDataOutput в ноль.
- Явная установка ориентации CIDetector на портрет. Телефон в этом тесте портретный, поэтому это не должно иметь значения.
- Установка и удаление CIDetectorTracking: true
- Установка и удаление CIDetectorAccuracy: CIDetectorAccuracyHigh
- Попытка отследить только одно лицо, глядя только на первую обнаруженную функцию.
- Замена CVImageBufferGetDisplaySize() на CVImageBufferGetEncodedSize() - они в любом случае одинаковы, при 1440 x 1080.