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 ViewContent 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類,繼承至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功能類似租幕,區(qū)別可查看以擼代碼的形式學(xué)習(xí)Swift-5:Control Flow6 guard 與 if
  • 2 初始化檢測(cè)器CIDetector拧簸, accuray是檢查器配置選項(xiàng)令蛉,表示精確度;因?yàn)?code>CIDetector可以進(jìn)行幾種類型的檢測(cè)狡恬,所以CIDetectorTypeFace用來表示面部檢測(cè)珠叔;features方法返回具體的檢測(cè)結(jié)果
  • 3 給每個(gè)檢測(cè)到的臉添加紅色框
  • 4 檢測(cè)是否有左眼位置
  • viewDidLoad中添加 detect(),運(yùn)行結(jié)果類似:


打印結(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è)拍完照,取照片的過程庸追。

  • 更新CameraViewController類的代碼
// 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是照相或攝影界面和功能管理的類读整。
  • 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: "沒臉了", message: "沒有檢測(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)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市昧诱,隨后出現(xiàn)的幾起案子晓淀,更是在濱河造成了極大的恐慌,老刑警劉巖鳄哭,帶你破解...
    沈念sama閱讀 212,657評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件要糊,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡妆丘,警方通過查閱死者的電腦和手機(jī)锄俄,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,662評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來勺拣,“玉大人奶赠,你說我怎么就攤上這事∫┯校” “怎么了毅戈?”我有些...
    開封第一講書人閱讀 158,143評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)愤惰。 經(jīng)常有香客問我苇经,道長(zhǎng),這世上最難降的妖魔是什么宦言? 我笑而不...
    開封第一講書人閱讀 56,732評(píng)論 1 284
  • 正文 為了忘掉前任扇单,我火速辦了婚禮,結(jié)果婚禮上奠旺,老公的妹妹穿的比我還像新娘蜘澜。我一直安慰自己,他們只是感情好响疚,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,837評(píng)論 6 386
  • 文/花漫 我一把揭開白布鄙信。 她就那樣靜靜地躺著,像睡著了一般忿晕。 火紅的嫁衣襯著肌膚如雪装诡。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 50,036評(píng)論 1 291
  • 那天,我揣著相機(jī)與錄音慎王,去河邊找鬼蚓土。 笑死宏侍,一個(gè)胖子當(dāng)著我的面吹牛赖淤,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播谅河,決...
    沈念sama閱讀 39,126評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼咱旱,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了绷耍?” 一聲冷哼從身側(cè)響起吐限,我...
    開封第一講書人閱讀 37,868評(píng)論 0 268
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎褂始,沒想到半個(gè)月后诸典,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,315評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡崎苗,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,641評(píng)論 2 327
  • 正文 我和宋清朗相戀三年狐粱,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片胆数。...
    茶點(diǎn)故事閱讀 38,773評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡肌蜻,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出必尼,到底是詐尸還是另有隱情蒋搜,我是刑警寧澤,帶...
    沈念sama閱讀 34,470評(píng)論 4 333
  • 正文 年R本政府宣布判莉,位于F島的核電站豆挽,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏券盅。R本人自食惡果不足惜帮哈,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,126評(píng)論 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望渗饮。 院中可真熱鬧但汞,春花似錦、人聲如沸互站。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,859評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽胡桃。三九已至踩叭,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背容贝。 一陣腳步聲響...
    開封第一講書人閱讀 32,095評(píng)論 1 267
  • 我被黑心中介騙來泰國(guó)打工自脯, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人斤富。 一個(gè)月前我還...
    沈念sama閱讀 46,584評(píng)論 2 362
  • 正文 我出身青樓膏潮,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親满力。 傳聞我的和親對(duì)象是個(gè)殘疾皇子焕参,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,676評(píng)論 2 351

推薦閱讀更多精彩內(nèi)容