Skip to content

Instantly share code, notes, and snippets.

@yakushevichsv
Created September 26, 2021 15:37
Show Gist options
  • Save yakushevichsv/7ba306e35cd67c6074f1c14e216821d2 to your computer and use it in GitHub Desktop.
Save yakushevichsv/7ba306e35cd67c6074f1c14e216821d2 to your computer and use it in GitHub Desktop.
Allows to detect smile & blink using CoreImage
//
// CIFaceFilter.swift
//
import Foundation
import CoreImage
import CoreGraphics
// MARK: - FaceFilterFeatures
struct FaceFilterFeatures {
let smileScheduled: Bool
let smiled: Bool?
let blinkScheduled: Bool
let blinked: Bool?
}
// MARK: - FaceFilterFeaturesExtended
struct FaceFilterFeaturesExtended {
let features: FaceFilterFeatures
let image: CIImage
}
// MARK: - VisionFaceTrackerDetectionType
enum VisionFaceTrackerDetectionType {
case eye
case smile
}
/*
if let image = UIImage(imageLiteralResourceName: "test").cgImage {
CIFaceFilter().detectFaceFeatures(ciImage: .init(cgImage: image))
}
*/
// MARK: - CIFaceFilter
/// Allows to detect smile, blink
final class CIFaceFilter {
let detector: CIDetector!
typealias FaceFeaturesResultType = [FaceFilterFeaturesExtended]
let context: CIContext?
init(context: CIContext? = nil,
highAccuracy: Bool = true) {
self.context = context
self.detector = CIDetector(ofType: CIDetectorTypeFace,
context: context,
options: [CIDetectorAccuracy: highAccuracy ? CIDetectorAccuracyHigh
: CIDetectorAccuracyLow,
CIDetectorMinFeatureSize: 0.4])
}
var isRunning: Bool = false
func detectFaceFeatures(ciImage: CIImage,
options: [String: Any] = [:]) -> FaceFeaturesResultType? {
defer {
self.isRunning = false
}
isRunning = true
guard let detector = detector else {
return nil
}
let faces = detector.features(in: ciImage,
options: options) as? [CIFaceFeature]
let detectBlink = (options[CIDetectorEyeBlink] as? NSNumber)?.boolValue ?? false
let detectSmile = (options[CIDetectorSmile] as? NSNumber)?.boolValue ?? false
assert(detectBlink != detectSmile || detectSmile)
return faces?.map({ (face) -> FaceFeaturesResultType.Element in
var blinked: Bool?
if detectBlink, face.hasLeftEyePosition || face.hasRightEyePosition {
blinked = (face.hasLeftEyePosition && face.leftEyeClosed) ||
(face.hasRightEyePosition && face.rightEyeClosed)
#if !RELEASE_APP_STORE
if blinked == true {
debugPrint("!!! Blinked!!!!")
}
#endif
}
var smiled: Bool?
if detectSmile, face.hasMouthPosition {
smiled = face.hasSmile
}
let element = FaceFilterFeatures(smileScheduled: detectSmile,
smiled: smiled,
blinkScheduled: detectBlink,
blinked: blinked)
return .init(features: element,
image: ciImage)
})
}
func image(pixelBuffer: CVPixelBuffer,
cropRect: CGRect? = nil) -> CIImage {
var ciImage = CIImage(cvPixelBuffer: pixelBuffer,
options: [:])
if let cropRect = cropRect {
ciImage = ciImage.cropped(to: cropRect)
}
return ciImage
}
func detectFaceFeatures(pixelBuffer: CVPixelBuffer,
exifOrientation: CGImagePropertyOrientation?,
detectionType: VisionFaceTrackerDetectionType,
cropRect: CGRect? = nil) -> FaceFeaturesResultType? {
let ciImage = self.image(pixelBuffer: pixelBuffer,
cropRect: cropRect)
return detectFaceFeatures(ciImage: ciImage,
exifOrientation: exifOrientation,
detectionType: detectionType)
}
func detectFaceFeatures(ciImage: CIImage,
exifOrientation: CGImagePropertyOrientation?,
detectionType: VisionFaceTrackerDetectionType) -> FaceFeaturesResultType? {
return detectFaceFeatures(ciImage: ciImage,
exifOrientation: exifOrientation,
detectionTypes: [detectionType])
}
func detectFaceFeatures(ciImage: CIImage,
exifOrientation: CGImagePropertyOrientation?,
detectionTypes: [VisionFaceTrackerDetectionType] = [.eye, .smile]) -> FaceFeaturesResultType? {
let detectBlink = detectionTypes.firstIndex(where: { $0 == .eye }) != nil
let detectSmile = detectionTypes.firstIndex(where: { $0 == .smile }) != nil
var options: [String: Any] = [CIDetectorEyeBlink: NSNumber(booleanLiteral: detectBlink),
CIDetectorSmile: NSNumber(booleanLiteral: detectSmile)]
assert(detectBlink != detectSmile || detectSmile)
var orienation: Int?
if let value = exifOrientation.map({ Int($0.rawValue) }) {
orienation = value
} else if let value = (ciImage.properties[kCGImagePropertyOrientation as String] as? NSNumber)?.intValue {
orienation = value
}
if let orientation = orienation {
options[CIDetectorImageOrientation] = NSNumber(integerLiteral: orientation)
}
return detectFaceFeatures(ciImage: ciImage,
options: options)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment