Почему мой код обнаружения лица, использующий 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
    }
  }
}

Я также попробовал следующее, но они не помогли:

  1. Установка VideoSettings AVCaptureVideoDataOutput в ноль.
  2. Явная установка ориентации CIDetector на портрет. Телефон в этом тесте портретный, поэтому это не должно иметь значения.
  3. Установка и удаление CIDetectorTracking: true
  4. Установка и удаление CIDetectorAccuracy: CIDetectorAccuracyHigh
  5. Попытка отследить только одно лицо, глядя только на первую обнаруженную функцию.
  6. Замена CVImageBufferGetDisplaySize() на CVImageBufferGetEncodedSize() - они в любом случае одинаковы, при 1440 x 1080.

0 ответов

Другие вопросы по тегам