Mobile Vision Face - проблема положения лица - PullRequest
0 голосов
/ 04 апреля 2020

Я новичок в разработке iOS, и моя текущая миссия состоит в том, чтобы реализовать функцию распознавания лиц с помощью библиотеки Mobile Vision от Google (не могу использовать распознавание лиц Apple из-за минимума iOS версия = 10). Я следую за официальный пример из Google Все работает хорошо, за исключением того, что, даже если я запускаю пример, прямоугольник лица (и ориентиры) не расположен в правильном месте (они всегда отражаются и смещаются), похоже, проблема в том, что в снятом с камеры изображении, потому что на снимках c в данном случае распознавание лица работает хорошо. Я гуглил подобные проблемы, и никто из них не помог мне. Что я делаю не так? Любая помощь приветствуется:)

Примечание: я использую только фронтальную камеру в портретном режиме

Исходный код:

import UIKit
import AVFoundation
import GoogleMobileVision

class FaceDetectorViewController: BaseViewController , AVCaptureVideoDataOutputSampleBufferDelegate {

@IBOutlet weak var placeholder: UIView!
@IBOutlet weak var overlay: UIView!

private var session   =  AVCaptureSession()
private var videoDataOutput  = AVCaptureVideoDataOutput ()
private var previewLayer  = AVCaptureVideoPreviewLayer()
private let device = AVCaptureDevice.default(.builtInWideAngleCamera, for: .video, position: .front)

private var detector :GMVDetector? = nil
private var videoDataOutputQueue  = DispatchQueue(label: "face_queue")

override func setup() {
    super.setup()
    session.sessionPreset = AVCaptureSession.Preset.high
    setupCamera()
    setupVideoProcessing()
    setupCameraPreview()

    self.detector = GMVDetector(ofType: GMVDetectorTypeFace, options:
        [
            GMVDetectorFaceMinSize: 0.3,
            GMVDetectorFaceTrackingEnabled: true,
            GMVDetectorFaceLandmarkType: GMVDetectorFaceLandmark.all.rawValue
    ])
}

override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)
    session.startRunning()
}

override func viewDidDisappear(_ animated: Bool) {
    super.viewDidAppear(animated)
    session.stopRunning()
}

override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
    if UIDevice.current.orientation.isLandscape {
         previewLayer.connection?.videoOrientation = AVCaptureVideoOrientation.portrait
    } else {
        previewLayer.connection?.videoOrientation = AVCaptureVideoOrientation.portrait
    }
}

override func willAnimateRotation(to toInterfaceOrientation: UIInterfaceOrientation, duration: TimeInterval) {
    previewLayer.connection?.videoOrientation = .portrait
}

func scaledRect(_ rect: CGRect, xScale xscale: CGFloat, yScale yscale: CGFloat, offset: CGPoint) -> CGRect {
    var resultRect = CGRect(
        x: rect.origin.x * xscale,
        y: rect.origin.y * yscale,
        width: rect.size.width * xscale,
        height: rect.size.height * yscale)
    resultRect = resultRect.offsetBy(dx: offset.x, dy: offset.y)
    return resultRect
}

func scaledPoint(_ point: CGPoint, xScale xscale: CGFloat, yScale yscale: CGFloat, offset: CGPoint) -> CGPoint {
    let resultPoint = CGPoint(x: point.x * xscale + offset.x, y: point.y * yscale + offset.y)
    return resultPoint
}

override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()
    self.previewLayer.frame = self.view.layer.bounds;
    self.previewLayer.position = CGPoint(
        x : self.previewLayer.frame.midX,
        y : self.previewLayer.frame.midY);
}

private func cleanupVideoProcessing() {
    session.removeOutput(videoDataOutput)
}

private func setupVideoProcessing() {
    videoDataOutput = AVCaptureVideoDataOutput()
    videoDataOutput.alwaysDiscardsLateVideoFrames = true
    videoDataOutput.videoSettings = [kCVPixelBufferPixelFormatTypeKey as AnyHashable as! String: NSNumber(value: kCVPixelFormatType_32BGRA)]
    videoDataOutput.setSampleBufferDelegate(self, queue: videoDataOutputQueue ) //
    if(!session.canAddOutput(videoDataOutput)){
        cleanupVideoProcessing()
        return
    }
    session.addOutput(videoDataOutput)
    session.commitConfiguration()
    session.startRunning()
}

private func setupCameraPreview() {
    previewLayer = AVCaptureVideoPreviewLayer(session: session)
    previewLayer.backgroundColor = UIColor.white.cgColor
    previewLayer.videoGravity = AVLayerVideoGravity.resizeAspect
    placeholder.layer.masksToBounds = true
    previewLayer.frame = placeholder.layer.bounds
    placeholder?.layer.addSublayer(previewLayer)
    previewLayer.connection?.videoOrientation = AVCaptureVideoOrientation.portrait
}

private func setupCamera() {

    session.beginConfiguration()

    let oldInputs = session.inputs
    for old in oldInputs{
        session.removeInput(old)
    }
    guard let input = getInput() else {
        for old in oldInputs{
            session.addInput(old)
        }
        session.commitConfiguration()
        return
    }
    session.addInput(input)
    session.commitConfiguration()

}

private func getInput() -> AVCaptureDeviceInput? {
    guard let device = device else {
        return nil
    }
    guard let input = try? AVCaptureDeviceInput.init(device: device) else {
        return nil
    }
    if session.canAddInput(input) {
        return input
    }
    return nil
}

func convert(cmage:CIImage) -> UIImage
{
    let context:CIContext = CIContext.init(options: nil)
    let cgImage:CGImage = context.createCGImage(cmage, from: cmage.extent)!
    let image:UIImage = UIImage.init(cgImage: cgImage)
    return image
}


func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) {


    let imageBuffer: CVPixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer)!
    let ciimage : CIImage = CIImage(cvPixelBuffer: imageBuffer)
    let image : UIImage = self.convert(cmage: ciimage)
    let imageOrientation = GMVUtility.imageOrientation(from: UIDevice.current.orientation, with: AVCaptureDevice.Position.front, defaultDeviceOrientation: .portrait)

    let options = [
        GMVDetectorImageOrientation: imageOrientation
    ]
    let faces = detector?.features(in: image, options: options)

    guard let fdesc = CMSampleBufferGetFormatDescription(sampleBuffer) else {
        return
    }
    let clap = CMVideoFormatDescriptionGetCleanAperture(fdesc, originIsAtTopLeft: false)
    let parentFrameSize = previewLayer.frame.size
    let cameraRatio: CGFloat = clap.size.height / clap.size.width
    let viewRatio: CGFloat = parentFrameSize.width / parentFrameSize.height
    var xScale: CGFloat = 1
    var yScale: CGFloat = 1
    var videoBox = CGRect.zero
    if viewRatio > cameraRatio {
        videoBox.size.width = parentFrameSize.height * clap.size.width / clap.size.height
        videoBox.size.height = parentFrameSize.height
        videoBox.origin.x = (parentFrameSize.width - videoBox.size.width) / 2
        videoBox.origin.y = (videoBox.size.height - parentFrameSize.height) / 2
        xScale = videoBox.size.width / clap.size.width
        yScale = videoBox.size.height / clap.size.height
    } else {
        videoBox.size.width = parentFrameSize.width
        videoBox.size.height = clap.size.width * (parentFrameSize.width / clap.size.height)
        videoBox.origin.x = (videoBox.size.width - parentFrameSize.width) / 2
        videoBox.origin.y = (parentFrameSize.height - videoBox.size.height) / 2

        xScale = videoBox.size.width / clap.size.height
        yScale = videoBox.size.height / clap.size.width
    }

    var transform = CGAffineTransform(scaleX: parentFrameSize.width/clap.width, y: -parentFrameSize.height/clap.height);
    transform = transform.translatedBy(x: 0, y: -clap.height);
    DispatchQueue.main.async {
        for featureView in self.overlay.subviews {
            featureView.removeFromSuperview()
        }
        //
        guard let f = faces else {
                return
            }
            for face in f {
                let faceRect = self.scaledRect(face.bounds, xScale: xScale, yScale: yScale, offset: videoBox.origin)
                DrawingUtility.addRectangle(faceRect, to: self.overlay, with: UIColor.red)
            }
        }

    }

}

// MARK: Helper, xib initialization

extension FaceDetectorViewController {



      static func xibInit() -> FaceDetectorViewController {

            let vc = FaceDetectorViewController(nibName: "FaceDetectorViewController", bundle: nil)
            vc.modalPresentationStyle = .overFullScreen
            vc.modalTransitionStyle = .coverVertical
            return vc
        }

}
...