1

I am trying to detect faces in local recorded video using Vision framework. Most of samples provided are detecting faces in Live cam video.

  • How to do face detection in Local video and place a rectangle in detected face in runtime using Vision/CoreML framework ?
Andy Jazz
  • 49,178
  • 17
  • 136
  • 220
Sravan
  • 41
  • 1
  • 5

1 Answers1

6
  • wait for your videoItem to be ready to play
  • add an output to it
  • add a periodic observer that should get pinged on every frame
  • extract the new pixel buffer and process them in Vision / CoreML as you want:
  • if you use the vision framework, you want to use a VNSequenceRequestHandler instead of an VNImageRequestHandler.

.

import UIKit
import AVFoundation
import CoreML
import Vision

class ViewController: UIViewController {
  var player: AVPlayer!
  var videoOutput: AVPlayerItemVideoOutput?

  override func viewDidLoad() {
    super.viewDidLoad()

    let player = AVPlayer(url: localURL)
    player.play()

    player.currentItem?.addObserver(
      self,
      forKeyPath: #keyPath(AVPlayerItem.status),
      options: [.initial, .old, .new],
      context: nil)
    player.addPeriodicTimeObserver(
      forInterval: CMTime(value: 1, timescale: 30),
      queue: DispatchQueue(label: "videoProcessing", qos: .background),
      using: { time in
        self.doThingsWithFaces()
    })
    self.player = player
  }

  override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
    guard let keyPath = keyPath, let item = object as? AVPlayerItem
      else { return }

    switch keyPath {
    case #keyPath(AVPlayerItem.status):
      if item.status == .readyToPlay {
        self.setUpOutput()
      }
      break
    default: break
    }
  }

  func setUpOutput() {
    guard self.videoOutput == nil else { return }
    let videoItem = player.currentItem!
    if videoItem.status != AVPlayerItemStatus.readyToPlay {
      // see https://forums.developer.apple.com/thread/27589#128476
      return
    }

    let pixelBuffAttributes = [
      kCVPixelBufferPixelFormatTypeKey as String: kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange,
      ] as [String: Any]

    let videoOutput = AVPlayerItemVideoOutput(pixelBufferAttributes: pixelBuffAttributes)
    videoItem.add(videoOutput)
    self.videoOutput = videoOutput
  }

  func getNewFrame() -> CVPixelBuffer? {
    guard let videoOutput = videoOutput, let currentItem = player.currentItem else { return nil }

    let time = currentItem.currentTime()
    if !videoOutput.hasNewPixelBuffer(forItemTime: time) { return nil }
    guard let buffer = videoOutput.copyPixelBuffer(forItemTime: time, itemTimeForDisplay: nil)
      else { return nil }
    return buffer
  }

  func doThingsWithFaces() {
    guard let buffer = getNewFrame() else { return }
    // some CoreML / Vision things on that.
    // There are numerous examples with this
  }
}
Guig
  • 9,891
  • 7
  • 64
  • 126
  • I seem to make it work fine, but when i had an AVPlayerLayer to display the video it doesnt anymore. – Arthuraw Sep 22 '17 at 16:04
  • Hi Arthur :) You mean as soon as you display the video it breaks? Do you know at what point things are getting weird? – Guig Sep 22 '17 at 17:25
  • I use the same ViewDidLoad as you (copy pasted) but when i add this code it stops working: playerLayer = AVPlayerLayer(player: player) playerLayer.frame = CGRect(x: 0, y: 100, width: 300, height: 170) self.view.layer.addSublayer(playerLayer) – Arthuraw Sep 25 '17 at 08:05
  • When i comment the player.currentItem?.addObserver method it displays the AVPlayerLayer, but well, then i dont get the observer :D – Arthuraw Sep 25 '17 at 08:26
  • It's because this example adds the videoOutput over and over. Try switch keyPath { case #keyPath(AVPlayerItem.status): if item.status == .readyToPlay && self.videoOutput == nil { self.setUpOutput() } break – Gareth Simpson Mar 31 '18 at 06:59
  • thanks Gareth, I've edited the answer – Guig Apr 02 '18 at 18:02
  • @Guig thank you ,you save my life. but my question is CMTime(value: 1, timescale: 30), why 30 setted here? if I change to 3000, it seems not diffrent. – zhouxinle Dec 25 '21 at 14:04