iOS tutorial 2:用Core Image進(jìn)行面部識(shí)別(Swift)

參考:Face Detection in iOS Using Core Image

面部識(shí)別API不僅可以是識(shí)別面部,也可識(shí)別面部的特殊細(xì)節(jié),例如微笑甚至眨眼睛。

建立初始項(xiàng)目

原文建好了初始項(xiàng)目,我自己新建了初始項(xiàng)目

  • 新建項(xiàng)目Detector
  • 刪除IB中原本View Controller Scene。
  • 拖動(dòng)UITabBarController到IB中,得到三個(gè)Scene。選擇UITabBarControllerIs Initial View Controller,使其作為初始控制器。
  • 修改Item 1的title和其Bar Item都為Photo,修改其ClassViewController。
  • Assets中添加幾張人物圖片
  • Photo Scene中添加一個(gè)Image View,Content Mode改為Aspect Fit,選擇一個(gè)圖片。在ViewController添加圖片對(duì)應(yīng)@IBOutlet:
    @IBOutlet var personPic: UIImageView!
  • 選中Item 2,點(diǎn)擊菜單欄EDitor > Embed In > Navigation Controller,新生成一個(gè)與之關(guān)聯(lián)的Scene。
  • 新建CameraViewController類(lèi),繼承至UIViewController。修改上面生成的SceneClass屬性為CameraViewController
  • 拖動(dòng)一個(gè)UIBarButtonItemCamera View Controller SceneUINavigationItem的右邊,并選擇System ItemCamera
  • CameraViewController中建立outlet和Action

識(shí)別照片的面部

  • ViewController.swift中引入CoreImage:
    import CoreImage
  • ViewController.swift中添加函數(shù)detect():
func detect() {
    // 1    
    guard let personciImage = CIImage(image: personPic.image!) else {
        return
    }
    // 2 
    let accuracy = [CIDetectorAccuracy: CIDetectorAccuracyHigh]
    let faceDetector = CIDetector(ofType: CIDetectorTypeFace, context: nil, options: accuracy)
    let faces = faceDetector?.features(in: personciImage)
    
    // 3 
    for face in faces as! [CIFaceFeature] {
        
        print("Found bounds are \(face.bounds)")
        
        let faceBox = UIView(frame: face.bounds)
        
        faceBox.layer.borderWidth = 3
        faceBox.layer.borderColor = UIColor.red.cgColor
        faceBox.backgroundColor = UIColor.clear
        personPic.addSubview(faceBox)
        // 4
        if face.hasLeftEyePosition {
            print("Left eye bounds are \(face.leftEyePosition)")
        }
        
        if face.hasRightEyePosition {
            print("Right eye bounds are \(face.rightEyePosition)")
        }
    }
}
  • 1 根據(jù)UIImage獲取CoreImage中圖片對(duì)象。guardif功能類(lèi)似,區(qū)別可查看以擼代碼的形式學(xué)習(xí)Swift-5:Control Flow6 guard 與 if
  • 2 初始化檢測(cè)器CIDetector, accuray是檢查器配置選項(xiàng),表示精確度;因?yàn)?code>CIDetector可以進(jìn)行幾種類(lèi)型的檢測(cè),所以CIDetectorTypeFace用來(lái)表示面部檢測(cè);features方法返回具體的檢測(cè)結(jié)果
  • 3 給每個(gè)檢測(cè)到的臉添加紅色框
  • 4 檢測(cè)是否有左眼位置
  • viewDidLoad中添加 detect(),運(yùn)行結(jié)果類(lèi)似:


打印結(jié)果,顯示檢測(cè)到的面部位置是不對(duì)的:
Found bounds are (177.0, 416.0, 380.0, 380.0)
這是因?yàn)閁IKit的坐標(biāo)系統(tǒng)與Core Image的坐標(biāo)系統(tǒng)是不同的:

  • 把Core Image的坐標(biāo)系統(tǒng)轉(zhuǎn)換為UIKit的坐標(biāo)系統(tǒng),修改detect()為:
func detect() {
        
    guard let personciImage = CIImage(image: personPic.image!) else {
        return
    }
    
    let accuracy = [CIDetectorAccuracy: CIDetectorAccuracyHigh]
    let faceDetector = CIDetector(ofType: CIDetectorTypeFace, context: nil, options: accuracy)
    let faces = faceDetector?.features(in: personciImage)
    //
    let ciImageSize = personciImage.extent.size
    var transform = CGAffineTransform(scaleX: 1, y: -1)
    transform = transform.translatedBy(x: 0, y: -ciImageSize.height)
    
    for face in faces as! [CIFaceFeature] {
        
        print("Found bounds are \(face.bounds)")
        
        // 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 = personPic.bounds.size
        let scale = min(viewSize.width / ciImageSize.width,
                        viewSize.height / ciImageSize.height)
        let offsetX = (viewSize.width - ciImageSize.width * scale) / 2
        let offsetY = (viewSize.height - ciImageSize.height * scale) / 2
        
        faceViewBounds = faceViewBounds.applying(CGAffineTransform(scaleX: scale, y: scale))
        faceViewBounds.origin.x += offsetX
        faceViewBounds.origin.y += offsetY
        
        let faceBox = UIView(frame: faceViewBounds)
        
        faceBox.layer.borderWidth = 3
        faceBox.layer.borderColor = UIColor.red.cgColor
        faceBox.backgroundColor = UIColor.clear
        personPic.addSubview(faceBox)
        
        if face.hasLeftEyePosition {
            print("Left eye bounds are \(face.leftEyePosition)")
        }
        
        if face.hasRightEyePosition {
            print("Right eye bounds are \(face.rightEyePosition)")
        }
    }
}

運(yùn)行可看到正確識(shí)別位置:


相機(jī)拍照后的臉部識(shí)別

之前是項(xiàng)目中照片識(shí)別,現(xiàn)在是拍完照再識(shí)別,原理是相同的,就是多一個(gè)拍完照,取照片的過(guò)程。

  • 更新CameraViewController類(lèi)的代碼
// 1
class CameraViewController: UIViewController, UIImagePickerControllerDelegate, UINavigationControllerDelegate {

    @IBOutlet var imageView: UIImageView!
    // 2
    let imagePicker = UIImagePickerController()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        imagePicker.delegate = self
    }
    
    @IBAction func takePhoto(_ sender: AnyObject) {
        // 3
        if !UIImagePickerController.isSourceTypeAvailable(.camera) {
            return
        }
        imagePicker.allowsEditing = false
        imagePicker.sourceType = .camera
        present(imagePicker, animated: true, completion: nil)
    }
    // 4
    //MARK: -UIImagePickerControllerDelegate
    func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
        if let pickedImage = info[UIImagePickerControllerOriginalImage] as? UIImage {
            imageView.contentMode = .scaleAspectFit
            imageView.image = pickedImage
        }
        
        dismiss(animated: true, completion: nil)
        self.detect()
        
    }
    // 5 
    func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
        dismiss(animated: true, completion: nil)
    }
}
  • 1 實(shí)現(xiàn)UIImagePickerControllerDelegate協(xié)議,用于拍照相關(guān)代理。
  • 2 初始化UIImagePickerController。UIImagePickerController是照相或攝影界面和功能管理的類(lèi)。
  • 3 判斷設(shè)備照相機(jī)是否可用。
  • 4 實(shí)現(xiàn)一個(gè)UIImagePickerControllerDelegate中的代理方法,當(dāng)拍攝完備確實(shí)使用照片時(shí)調(diào)用。
  • 5 也是UIImagePickerControllerDelegate中的代理方法,取消拍攝時(shí)調(diào)用。
  • 添加detect()代碼,與ViewController中不同的是,不用紅色框框處識(shí)別出的面部,而是識(shí)別出面部的細(xì)節(jié),并用UIAlertController彈出顯示。
func detect() {
        let imageOptions = NSDictionary(object: NSNumber(value: 5) as NSNumber, forKey: CIDetectorImageOrientation as NSString)
        let personciImage = CIImage(cgImage: imageView.image!.cgImage!)
        let accuracy = [CIDetectorAccuracy: CIDetectorAccuracyHigh]
        let faceDetector = CIDetector(ofType: CIDetectorTypeFace, context: nil, options: accuracy)
        let faces = faceDetector?.features(in: personciImage, options: imageOptions as? [String : AnyObject])
        
        if let face = faces?.first as? CIFaceFeature {
            print("found bounds are \(face.bounds)")
            
            var message = "有個(gè)臉"
            
            if face.hasSmile {
                print("臉是笑的")
                message += ",臉是笑的"
            }
            if face.hasMouthPosition {
                print("有嘴唇")
                message += ",有嘴唇"
            }
            
            if face.hasLeftEyePosition {
                print("左眼鏡的位置是 \(face.leftEyePosition)")
                message += ",左眼鏡的位置是 \(face.leftEyePosition)"
            }
            
            if face.hasRightEyePosition {
                print("右眼鏡的位置是 \(face.rightEyePosition)")
                message += ",右眼鏡的位置是 \(face.rightEyePosition)"
            }
            
            let alert = UIAlertController(title: "嘿嘿", message: message, preferredStyle: .alert)
            alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
            self.present(alert, animated: true, completion: nil)
            
        } else {
            let alert = UIAlertController(title: "沒(méi)臉了", message: "沒(méi)有檢測(cè)到臉", preferredStyle: .alert)
            alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
            self.present(alert, animated: true, completion: nil)
        }        
}

運(yùn)行就可以識(shí)別照片的面部具體細(xì)節(jié)
CIFaceFeature還提供了其他很多面部細(xì)節(jié):

        open var hasLeftEyePosition: Bool { get }

        open var leftEyePosition: CGPoint { get }

        open var hasRightEyePosition: Bool { get }

        open var rightEyePosition: CGPoint { get }

        open var hasMouthPosition: Bool { get }

        open var mouthPosition: CGPoint { get }

        open var hasTrackingID: Bool { get }

        open var trackingID: Int32 { get }

        open var hasTrackingFrameCount: Bool { get }

        open var trackingFrameCount: Int32 { get }

        open var hasFaceAngle: Bool { get }

        open var faceAngle: Float { get }

        open var hasSmile: Bool { get }

        open var leftEyeClosed: Bool { get }

        open var rightEyeClosed: Bool { get }     

代碼

Detector

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容