0

I'm playing around with Apples CIDetector to detect a face from live video using the front camera of the phone. I've been following this article and have nearly got it working. The problem I'm having is a new red box is being created on every frame rather the same one being reused.

The tutorial I'm following is meant to have code to stop that from happening but it doesn't seem to be working. I'm still very new to Swift and am struggling to work it out.

Here's the code I'm using:

    func drawFaceMasksFor(features: [CIFaceFeature], bufferFrame: CGRect) {

    CATransaction.begin()
    CATransaction.setValue(kCFBooleanTrue, forKey: kCATransactionDisableActions)

    //Hide all current masks
    view.layer.sublayers?.filter({ $0.name == "MaskFace" }).forEach { $0.isHidden = true }

    //Do nothing if no face is dected
    guard !features.isEmpty else {
        CATransaction.commit()
        return
    }

    //The problem is we detect the faces on video image size
    //but when we show on the screen which might smaller or bigger than your video size
    //so we need to re-calculate the faces bounds to fit to your screen

    let xScale = view.frame.width / bufferFrame.width
    let yScale = view.frame.height / bufferFrame.height
    let transform = CGAffineTransform(rotationAngle: .pi).translatedBy(x: -bufferFrame.width,
                                                                       y: -bufferFrame.height)

    for feature in features {
        var faceRect = feature.bounds.applying(transform)
        faceRect = CGRect(x: faceRect.minX * xScale,
                          y: faceRect.minY * yScale,
                          width: faceRect.width * xScale,
                          height: faceRect.height * yScale)

        //Reuse the face's layer
        var faceLayer = view.layer.sublayers?
            .filter { $0.name == "MaskFace" && $0.isHidden == true }
            .first
        if faceLayer == nil {                
            // prepare layer
            faceLayer = CALayer()
            faceLayer?.backgroundColor = UIColor.clear.cgColor
            faceLayer?.borderColor = UIColor.red.cgColor
            faceLayer?.borderWidth = 3.0
            faceLayer?.frame = faceRect
            faceLayer?.masksToBounds = true
            faceLayer?.contentsGravity = kCAGravityResizeAspectFill
            view.layer.addSublayer(faceLayer!)

        } else {
            faceLayer?.frame = faceRect
            faceLayer?.position = faceRect.origin
            faceLayer?.isHidden = false
        }

        //You can add some masks for your left eye, right eye, mouth
    }

    CATransaction.commit()
}
Hardy143
  • 577
  • 4
  • 13
  • don't see where you're setting faceLayer.name = "MaskFace", this would cause new layer to always get created – CSmith Jul 17 '18 at 19:23
  • Yeah I think this is the part of the code I need to work out for myself. In the tutorial they are putting an image mask over the face. While I just want a red box around the face. – Hardy143 Jul 18 '18 at 08:41
  • 1
    faceLayer?.name = "MaskFace", add this to the "prepare layer" code and you'll make more progress – CSmith Jul 18 '18 at 14:07
  • Thank you that was all I needed to do! – Hardy143 Jul 18 '18 at 14:33
  • Now I can see the red square properly it's not quite right. When the face detector loads up the first frame shows the red square in the correct location. Then the frames after that show the red square to the upper left side of the face. I'm assuming that the problem is within the else statement when the faceLayer is being reused? – Hardy143 Jul 18 '18 at 14:46
  • I removed faceLayer?.position = faceRect.origin and now the red box is around the face correctly. – Hardy143 Jul 18 '18 at 15:00

0 Answers0