2016-12-28 9 views
35

Как мне распознать лицо в реальном времени так же, как «Камера»?Обнаружение лица с помощью камеры

enter image description here

Я заметил, что AVCaptureStillImageOutput осуждается после 10.0, поэтому я использую AVCapturePhotoOutput вместо этого. Однако я обнаружил, что изображение, которое я сохранил для обнаружения лица, не так удовлетворено? Есть идеи?


UPDATE

Дав попробовать из @Shravya Boggarapu упоминается. В настоящее время я использую AVCaptureMetadataOutput для обнаружения лица без CIFaceDetector. Он работает так, как ожидалось. Однако, когда я пытаюсь нарисовать границы лица, это кажется неправильным. Есть идеи?

enter image description here

let metaDataOutput = AVCaptureMetadataOutput() 

captureSession.sessionPreset = AVCaptureSessionPresetPhoto 
    let backCamera = AVCaptureDevice.defaultDevice(withDeviceType: .builtInWideAngleCamera, mediaType: AVMediaTypeVideo, position: .back) 
    do { 
     let input = try AVCaptureDeviceInput(device: backCamera) 

     if (captureSession.canAddInput(input)) { 
      captureSession.addInput(input) 

      // MetadataOutput instead 
      if(captureSession.canAddOutput(metaDataOutput)) { 
       captureSession.addOutput(metaDataOutput) 

       metaDataOutput.setMetadataObjectsDelegate(self, queue: DispatchQueue.main) 
       metaDataOutput.metadataObjectTypes = [AVMetadataObjectTypeFace] 

       previewLayer = AVCaptureVideoPreviewLayer(session: captureSession) 
       previewLayer?.frame = cameraView.bounds 
       previewLayer?.videoGravity = AVLayerVideoGravityResizeAspectFill 

       cameraView.layer.addSublayer(previewLayer!) 
       captureSession.startRunning() 
      } 

     } 

    } catch { 
     print(error.localizedDescription) 
    } 

и

extension CameraViewController: AVCaptureMetadataOutputObjectsDelegate { 
func captureOutput(_ captureOutput: AVCaptureOutput!, didOutputMetadataObjects metadataObjects: [Any]!, from connection: AVCaptureConnection!) { 
    if findFaceControl { 
     findFaceControl = false 
     for metadataObject in metadataObjects { 
      if (metadataObject as AnyObject).type == AVMetadataObjectTypeFace { 
       print("") 
       print(metadataObject) 
       let bounds = (metadataObject as! AVMetadataFaceObject).bounds 
       print("origin x: \(bounds.origin.x)") 
       print("origin y: \(bounds.origin.y)") 
       print("size width: \(bounds.size.width)") 
       print("size height: \(bounds.size.height)") 
       print("cameraView width: \(self.cameraView.frame.width)") 
       print("cameraView height: \(self.cameraView.frame.height)") 
       var face = CGRect() 
       face.origin.x = bounds.origin.x * self.cameraView.frame.width 
       face.origin.y = bounds.origin.y * self.cameraView.frame.height 
       face.size.width = bounds.size.width * self.cameraView.frame.width 
       face.size.height = bounds.size.height * self.cameraView.frame.height 
       print(face) 

       showBounds(at: face) 
      } 
     } 
    } 

} 
} 

Оригинал

see in Github

var captureSession = AVCaptureSession() 
var photoOutput = AVCapturePhotoOutput() 
var previewLayer: AVCaptureVideoPreviewLayer?  

override func viewWillAppear(_ animated: Bool) { 
    super.viewWillAppear(true) 

    captureSession.sessionPreset = AVCaptureSessionPresetHigh 

    let backCamera = AVCaptureDevice.defaultDevice(withMediaType: AVMediaTypeVideo) 
    do { 
     let input = try AVCaptureDeviceInput(device: backCamera) 

     if (captureSession.canAddInput(input)) { 
      captureSession.addInput(input) 

      if(captureSession.canAddOutput(photoOutput)){ 
       captureSession.addOutput(photoOutput) 
       captureSession.startRunning() 

       previewLayer = AVCaptureVideoPreviewLayer(session: captureSession) 
       previewLayer?.videoGravity = AVLayerVideoGravityResizeAspectFill 
       previewLayer?.frame = cameraView.bounds 

       cameraView.layer.addSublayer(previewLayer!) 
      } 
     } 

    } catch { 
     print(error.localizedDescription) 
    } 

} 

func captureImage() { 
    let settings = AVCapturePhotoSettings() 
    let previewPixelType = settings.availablePreviewPhotoPixelFormatTypes.first! 
    let previewFormat = [kCVPixelBufferPixelFormatTypeKey as String: previewPixelType 
         ] 
    settings.previewPhotoFormat = previewFormat 
    photoOutput.capturePhoto(with: settings, delegate: self) 

} 



func capture(_ captureOutput: AVCapturePhotoOutput, didFinishProcessingPhotoSampleBuffer photoSampleBuffer: CMSampleBuffer?, previewPhotoSampleBuffer: CMSampleBuffer?, resolvedSettings: AVCaptureResolvedPhotoSettings, bracketSettings: AVCaptureBracketedStillImageSettings?, error: Error?) { 
    if let error = error { 
     print(error.localizedDescription) 
    } 
    // Not include previewPhotoSampleBuffer 
    if let sampleBuffer = photoSampleBuffer, 
     let dataImage = AVCapturePhotoOutput.jpegPhotoDataRepresentation(forJPEGSampleBuffer: sampleBuffer, previewPhotoSampleBuffer: nil) { 
      self.imageView.image = UIImage(data: dataImage) 
      self.imageView.isHidden = false 
      self.previewLayer?.isHidden = true 
      self.findFace(img: self.imageView.image!) 
     } 
} 

findFace работает с нормальным изображением. Однако изображение, которое я захватываю с помощью камеры, не работает или иногда распознает только одно лицо.

Нормальное изображение

enter image description here

Захват изображения

enter image description here

func findFace(img: UIImage) { 
    guard let faceImage = CIImage(image: img) else { return } 
    let accuracy = [CIDetectorAccuracy: CIDetectorAccuracyHigh] 
    let faceDetector = CIDetector(ofType: CIDetectorTypeFace, context: nil, options: accuracy) 


    // For converting the Core Image Coordinates to UIView Coordinates 
    let detectedImageSize = faceImage.extent.size 
    var transform = CGAffineTransform(scaleX: 1, y: -1) 
    transform = transform.translatedBy(x: 0, y: -detectedImageSize.height) 


    if let faces = faceDetector?.features(in: faceImage, options: [CIDetectorSmile: true, CIDetectorEyeBlink: true]) { 
     for face in faces as! [CIFaceFeature] { 

      // Apply the transform to convert the coordinates 
      var faceViewBounds = face.bounds.applying(transform) 
      // Calculate the actual position and size of the rectangle in the image view 
      let viewSize = imageView.bounds.size 
      let scale = min(viewSize.width/detectedImageSize.width, 
          viewSize.height/detectedImageSize.height) 
      let offsetX = (viewSize.width - detectedImageSize.width * scale)/2 
      let offsetY = (viewSize.height - detectedImageSize.height * scale)/2 

      faceViewBounds = faceViewBounds.applying(CGAffineTransform(scaleX: scale, y: scale)) 
      print("faceBounds = \(faceViewBounds)") 
      faceViewBounds.origin.x += offsetX 
      faceViewBounds.origin.y += offsetY 

      showBounds(at: faceViewBounds) 
     } 

     if faces.count != 0 { 
      print("Number of faces: \(faces.count)") 
     } else { 
      print("No faces ") 
     } 
    } 


} 

func showBounds(at bounds: CGRect) { 
    let indicator = UIView(frame: bounds) 
    indicator.frame = bounds 
    indicator.layer.borderWidth = 3 
    indicator.layer.borderColor = UIColor.red.cgColor 
    indicator.backgroundColor = .clear 

    self.imageView.addSubview(indicator) 
    faceBoxes.append(indicator) 

} 
+0

вы должны использовать 'CIDetector' для обнаружения лица. – aircraft

+0

Вот ссылка, в которой есть пример использования обнаружения лица Core Image из живой видеопотоки. Это от iOS 5 дней, поэтому, очевидно, оба датированы и в Objective-C, но если вы раньше работали с CI, вы, вероятно, могли бы перевести его. http://www.icapps.com/face-detection-with-core-image-on-live-video/. Извините, hit return не понимает, что это соответствует редактированию. Вот вторая ссылка, которая поможет с использованием Swift 2 и применения фильтров CI к каналу камеры: http://flexmonkey.blogspot.com/2015/07/applying-cifilters-to-live-camera-feed.html?q=camera – dfd

+0

Используйте этот пример из [здесь] (https://github.com/shinobicontrols/iOS8-day-by-day/blob/master/13-coreimage-detectors/13-coreimage-detectors.md). В этом примере реализовано обнаружение в реальном времени для прямоугольников/квадратов и qr-кодов, но вы можете легко настроить это для обнаружения лиц. Вы можете использовать этот пример, чтобы изменить наложения и всевозможные другие вещи тоже, его очень настраиваемый. Надеюсь, что это поможет: D – Munib

ответ

9

Есть 2 пути идти об обнаружении лица: один CIFaceDetector и другая AVCaptureMetadataOutput

в зависимости от ваших требований, выбрать то, что важно для вас.

CIFaceDetector имеет более Особенности- Eg: Дает вам расположение глаз и рта, детектор улыбки и т.д.

С другой стороны, AVCaptureMetadataOutput вычисляется на рамах и обнаруженные лица отслеживаются и не существует дополнительный код должны быть добавлены нами. Я нахожу, что из-за отслеживания лица обнаружены более надежно в этом процессе. Кон этим является то, что вы просто обнаружите лица, нет положения глаз/рта. Другим преимуществом этого метода является то, что проблемы ориентации меньше, чем вы можете видеоориентировать, когда ориентация устройства изменяется, а ориентация граней будет относительно этой ориентации.

В моем случае мое приложение использует YUV420 как необходимый формат поэтому использование CIDetector (который работает с RGB) в режиме реального времени не является жизнеспособным. Использование AVCaptureMetadataOutput сэкономило массу усилий и выполнило более надежно из-за непрерывного отслеживания.

Как только у меня была ограничивающая рамка для лиц, я закодировал дополнительные функции, такие как обнаружение кожи и наложил их на неподвижное изображение.

Примечание: при захвате неподвижного изображения информация о лицевом поле добавляется вместе с метаданными, поэтому нет проблем с синхронизацией.

Вы также можете использовать комбинацию из двух, чтобы получить лучшие результаты.

Изучите и оцените плюсы и минусы согласно вашему приложению.

UPDATE

Face прямоугольник WRT изображение происхождения. Так что для экрана это может быть иначе. Используйте следующего:

for (AVMetadataFaceObject *faceFeatures in metadataObjects) { 
    CGRect face = faceFeatures.bounds; 
    CGRect facePreviewBounds = CGRectMake(face.origin.y * previewLayerRect.size.width, 
           face.origin.x * previewLayerRect.size.height, 
           face.size.width * previewLayerRect.size.height, 
           face.size.height * previewLayerRect.size.width); 

    /* Draw rectangle facePreviewBounds on screen */ 
} 
+1

Я установил 'metadataObjectTypes' в' [AVMetadataObjectTypeFace] '. Кроме того, 'didOutputMetadataObjects' будет вызываться после того, как будут найдены грани. Однако, как я могу нарисовать прямоугольник на экране? – WeiJay

+0

В [iOS7-день за днем] (https://github.com/shinobicontrols/iOS7-day-by-day/blob/master/18-coreimage-features/SimleyFace/SimleyFace/SCViewController.m), он обнаруживает грани с помощью 'AVCaptureMetadataOutput' и использует' CIFaceDetector' впоследствии с 'AVCaptureStillImageOutput' – WeiJay

+0

. Мой вопрос в том, что даже если я обнаруживаю грани через' AVCaptureMetadataOutput', когда я беру его с помощью 'AVCapturePhotoOutput', тогда я хочу нарисуйте расположение лица с использованием прямоугольника 'CIFaceDetector', CIFaceDetector не работает должным образом. – WeiJay

0

Глядя на ваш код я обнаружил 2 вещи, которые могли бы привести к неправильному/бедные лица обнаружение.

  1. Одним из них является особенности детектора лицо варианты, где вы фильтрации результатов по [CIDetectorSmile: true, CIDetectorEyeBlink: true]. Попробуйте установить его на ноль: faceDetector?.features(in: faceImage, options: nil)
  2. Еще одна догадка у меня есть результат Ориентация изображения. Я заметил, что вы используете метод AVCapturePhotoOutput.jpegPhotoDataRepresentation для генерации исходного изображения для обнаружения и системы, по умолчанию он генерирует это изображение с определенной ориентацией типа Left/LandscapeLeft, я думаю. Таким образом, в основном вы можете сказать, что детектор лица имеет это в виду, используя ключ CIDetectorImageOrientation.

CIDetectorImageOrientation: значение этого ключа представляет собой целое число от NSNumber 1..8 как, например, найти в kCGImagePropertyOrientation. Если присутствует, обнаружение будет выполняться на основе этой ориентации, но координаты в возвращаемых функциях будут по-прежнему основываться на координатах изображения.

Попробуйте установить его как faceDetector?.features(in: faceImage, options: [CIDetectorImageOrientation: 8 /*Left, bottom*/]).

+0

Я не думаю, что '[CIDetectorSmile: true, CIDetectorEyeBlink: true]' является фильтром. Он сообщает детектору, чтобы тратить больше времени, чтобы он мог вернуть указанную информацию. Таким образом, он фактически расширяет результаты. –

0
  1. Создать CaptureSession
  2. Для AVCaptureVideoDataOutput создать следующие параметры

    output.videoSettings = [kCVPixelBufferPixelFormatTypeKey, как AnyHashable: Int (kCMPixelFormat_32BGRA)]

3.When вы получаете CMSampleBuffer , создать изображение

DispatchQueue.main.async { 
    let sampleImg = self.imageFromSampleBuffer(sampleBuffer: sampleBuffer) 
    self.imageView.image = sampleImg 
} 
func imageFromSampleBuffer(sampleBuffer : CMSampleBuffer) -> UIImage 
    { 
     // Get a CMSampleBuffer's Core Video image buffer for the media data 
     let imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer); 
     // Lock the base address of the pixel buffer 
     CVPixelBufferLockBaseAddress(imageBuffer!, CVPixelBufferLockFlags.readOnly); 


     // Get the number of bytes per row for the pixel buffer 
     let baseAddress = CVPixelBufferGetBaseAddress(imageBuffer!); 

     // Get the number of bytes per row for the pixel buffer 
     let bytesPerRow = CVPixelBufferGetBytesPerRow(imageBuffer!); 
     // Get the pixel buffer width and height 
     let width = CVPixelBufferGetWidth(imageBuffer!); 
     let height = CVPixelBufferGetHeight(imageBuffer!); 

     // Create a device-dependent RGB color space 
     let colorSpace = CGColorSpaceCreateDeviceRGB(); 

     // Create a bitmap graphics context with the sample buffer data 
     var bitmapInfo: UInt32 = CGBitmapInfo.byteOrder32Little.rawValue 
     bitmapInfo |= CGImageAlphaInfo.premultipliedFirst.rawValue & CGBitmapInfo.alphaInfoMask.rawValue 
     //let bitmapInfo: UInt32 = CGBitmapInfo.alphaInfoMask.rawValue 
     let context = CGContext.init(data: baseAddress, width: width, height: height, bitsPerComponent: 8, bytesPerRow: bytesPerRow, space: colorSpace, bitmapInfo: bitmapInfo) 
     // Create a Quartz image from the pixel data in the bitmap graphics context 
     let quartzImage = context?.makeImage(); 
     // Unlock the pixel buffer 
     CVPixelBufferUnlockBaseAddress(imageBuffer!, CVPixelBufferLockFlags.readOnly); 

     // Create an image object from the Quartz image 
     let image = UIImage.init(cgImage: quartzImage!); 

     return (image); 
    } 
5

Для выполнения функции распознавания лиц на прошивке, есть либо CIDetector (Apple) или Mobile Vision (Google) API.

IMO, Google Mobile Vision обеспечивает лучшую производительность.

Если вы заинтересованы, here is the project you can play with. (iOS 10.2, Swift 3)


После WWDC 2017, Apple вводит CoreML в прошивкой 11. видения рамки делает обнаружение лица более точным :)

Я сделал a Demo Project. содержащий Vision v.s. CIDetector. Кроме того, он содержит обнаружение ориентиров лица в режиме реального времени.

+0

Спасибо Вэй! Я разветвил и обновил ваш проект, чтобы также определить ориентиры лица: https://github.com/wanderingstan/AppleFaceDetection –

+0

Новая система Vision не обеспечивает улыбки и обнаружения открытых глаз –

0
extension CameraViewController: AVCaptureMetadataOutputObjectsDelegate { 
    func captureOutput(_ captureOutput: AVCaptureOutput!, didOutputMetadataObjects metadataObjects: [Any]!, from connection: AVCaptureConnection!) { 
    if findFaceControl { 
     findFaceControl = false 
     let faces = metadata.flatMap { $0 as? AVMetadataFaceObject } .flatMap { (face) -> CGRect in 
        guard let localizedFace = 
     previewLayer?.transformedMetadataObject(for: face) else { return nil } 
        return localizedFace.bounds } 
     for face in faces { 
     let temp = UIView(frame: face) 
     temp.layer.borderColor = UIColor.white 
     temp.layer.borderWidth = 2.0 
     view.addSubview(view: temp) 
     } 
    } 
    } 
} 

Обязательно удалите представления, созданные didOutputMetadataObjects.

Отслеживание активных идентификаторов лица является лучшим способом сделать это^

Кроме того, когда вы пытаетесь найти расположение граней для вашего просмотра слоя, гораздо проще использовать данные лиц и преобразование , Также я думаю, что CIDetector - это неакционер, metadataoutput будет использовать аппаратные средства для обнаружения лиц, что делает его очень быстрым.