AI時代的到來,手機上的APP開始應用人臉識別去完成事情,如iphoneX的人臉解鎖,百度自動販賣機的人臉識別系統進行支付,支付寶的人臉識別登陸等,提升了使用軟件的易用性,但也由於其便利性,在某些市面上的應用已經發生了非本人(活人)經過圖片可經過人臉識別,想一想這樣本身在應用上存儲的信息和錢財不能獲得保障,是一件很可怕的事情。由於這樣,更應該去了解人臉識別的理論及實現,從而更好的去防範這種行爲的發生。話很少說,把實現方法和在實現過程當中遇到的問題和你們討論討論~html
1、環境準備python
MacOs+PyCharm+Python+OpenCV數組
1.Mac下PyCharm下載、安裝、激活網絡
參考:http://blog.csdn.net/qq_35246620/article/details/78254527?utm_source=gold_browser_extensionapp
2.Python安裝python2.7
Mac系統下自己已自帶python2.7,因此這一步能夠省去。要查看版本的話,終端使用以下命令查看: iphone
which python python
3.OpenCV安裝ide
在實現人臉識別的代碼中,咱們須要用到的依賴庫有:ui
Pillow 5.0.0
Pillow-PIL 0.1dev
numpy 1.14.1
opencv-python 3.3.0.10人工智能
在本機環境安裝並配置以上庫步驟偏複雜,因此直接在PyCharm中該工程中設置虛擬環境,而後再在虛擬環境中下載安裝。虛擬環境與本機進行隔離,在不一樣的項目中使用到的第三方庫版本可切換。下面是虛擬環境的建立和安裝依賴庫步驟:
由於OpenCV是安裝到虛擬環境中,而咱們後面須要的xml文件是在OpenCV/data/haarcascades/下,因此須要到OpenCV官網下載對應安裝包
https://opencv.org/opencv-3-3.html ,解壓縮後,咱們就看到了xml文件。
2、代碼實現
1 # !/usr/bin/env python 2 # coding=utf-8 3 import os 4 import numpy 5 from PIL import Image, ImageDraw 6 import cv2 7 #created by chenfenyu 2018.3.20 8 9 10 cap = cv2.VideoCapture(0) 11 #獲取外接攝像頭 12 eye = cv2.imread("/Users/funny/Downloads/img/eye.png") 13 #讀取眼睛區域替換的圖片 14 mouth = cv2.imread("/Users/funny/Downloads/img/mouth.png") 15 #讀取嘴巴區域替換的圖片 16 size = (int(cap.get(cv2.CAP_PROP_FRAME_WIDTH) + 0.5), int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT) + 0.5)) 17 #獲取攝像頭返回的寬和高 18 fourcc = cv2.VideoWriter_fourcc(*'mp4v') 19 #肯定保存視頻的格式 20 video = cv2.VideoWriter("/Users/funny/Downloads/a.avi", fourcc, 5, size) 21 ''' cv.VideoWriter參數(視頻存放路徑,視頻存放格式,fps幀率,視頻寬高) 22 注意點1:OpenCV只支持avi的格式,並且生成的視頻文件不能大於2GB,並且不能添加音頻 23 注意點2:若填寫的文件名已存在,則該視頻不會錄製成功,但可正常使用 24 ''' 25 print cap.isOpened() 26 #檢測是否攝像頭正常打開:成功打開時,isOpened返回ture 27 classifier_face = cv2.CascadeClassifier("/Users/funny/Documents/haarcascade_frontalface_alt.xml") 28 #定義分類器(人臉識別) 29 classifier_eye = cv2.CascadeClassifier("/Users/funny/Documents/haarcascade_eye.xml") 30 #定義分類器(人眼識別) 31 classifier_mouth=cv2.CascadeClassifier("/Users/funny/Documents/haarcascade_mcs_mouth.xml") 32 #定義分類器(嘴巴識別) 33 while (True): 34 #取得cap.isOpened()返回狀態爲True,即檢測到人臉 35 #img = cv2.imread("/Users/funny/Downloads/img/pp.png") 36 ret, img = cap.read() 37 '''第一個參數ret的值爲True或False,表明有沒有讀到圖片 38 第二個參數是frame,是當前截取一幀的圖片 39 ''' 40 faceRects_face = classifier_face.detectMultiScale(img, 1.2, 2, cv2.CASCADE_SCALE_IMAGE, (20, 20)) 41 #檢測器:detectMultiScale參數(圖像,每次縮小圖像的比例,匹配成功所須要的周圍矩形框的數目,檢測的類型,匹配物體的大小範圍) 42 key = cv2.waitKey(1) 43 #鍵盤等待 44 if len(faceRects_face) > 0: 45 #檢測到人臉 46 for faceRect_face in faceRects_face: 47 x, y, w, h = faceRect_face 48 #獲取圖像x起點,y起點,寬,高 49 h1=int(float(h/1.5)) 50 #截取人臉區域高度的一半位置,以精確識別眼睛的位置 51 intx=int(x) 52 inty=int(y) 53 intw=int(w) 54 inth=int(h) 55 #轉換類型爲int,方便以後圖像截取 56 my = int(float(y + 0.7 * h)) 57 #截取人臉區域下半部分左上角的y起點,以精確識別嘴巴的位置 58 mh = int(0.4 * h) 59 #截取人臉區域下半部分高度,以精確識別嘴巴的位置 60 img_facehalf = img[inty:(inty+h1), intx:intx+intw] 61 img_facehalf_bottom = img[my:(my + mh), intx:intx + intw] 62 '''img獲取座標爲,【y,y+h之間(豎):x,x+w之間(橫)範圍內的數組】 63 img_facehalf是截取人臉識別到區域上半部分 64 img_facehalf_bottom是截取人臉識別到區域下半部分 65 ''' 66 cv2.rectangle(img, (int(x), my), (int(x) + int(w), my + mh), (0, 255, 0), 2, 0) 67 '''矩形畫出區域 rectangle參數(圖像,左頂點座標(x,y),右下頂點座標(x+w,y+h),線條顏色,線條粗細) 68 畫出人臉識別下部分區域,方便定位 69 ''' 70 faceRects_mouth = classifier_mouth.detectMultiScale(img_facehalf_bottom, 1.1, 1, cv2.CASCADE_SCALE_IMAGE, (5, 20)) 71 #嘴巴檢測器 72 if len(faceRects_mouth) > 0: 73 for faceRect_mouth in faceRects_mouth: 74 xm1, ym1, wm1, hm2 = faceRect_mouth 75 cv2.rectangle(img_facehalf_bottom, (int(xm1), int(ym1)), (int(xm1) + int(wm1), int(ym1) + int(hm2)), (0,0, 255), 2, 0) 76 img_mx = cv2.resize(mouth, (wm1, hm2), interpolation=cv2.INTER_CUBIC) 77 #調整覆蓋圖片大小 resize參數(圖像,檢測到的(寬,高),縮放類型) 78 if key == ord('z'): 79 #檢測當鍵盤輸入z時,開始替換圖片 80 img[my+ym1:(my+ym1+hm2), intx+xm1:(intx + xm1+wm1)] = img_mx 81 #將調整大小後的圖片賦值給img 82 cv2.rectangle(img, (int(x), int(y)), (int(x) + int(w), int(y) + int(h1)), (0, 255, 0), 2, 0) 83 # 畫出人臉識別上部分區域,方便定位 84 faceRects_eye = classifier_eye.detectMultiScale(img_facehalf, 1.2, 2, cv2.CASCADE_SCALE_IMAGE, (20, 20)) 85 #檢測器識別眼睛 86 if len(faceRects_eye) > 0: 87 #檢測到眼睛後循環 88 eye_tag = [] 89 #定義一個列表存放兩隻眼睛座標 90 for faceRect_eye in faceRects_eye: 91 x1, y1, w1, h2 = faceRect_eye 92 cv2.rectangle(img_facehalf, (int(x1), int(y1)), (int(x1) + int(w1), int(y1) + int(h2)), (0, 255, 0), 2, 0) 93 #畫出眼睛區域 94 a = ((inty+y1),(inty+y1 + h2), (intx+x1),(intx+x1 + w1)) 95 #定義a變量獲取眼睛座標,如今img頂點位置已經改變,須要加上intx和inty的值才能夠 96 eye_tag.append(a) 97 #經過append存入數組a中 98 n_eyetag = numpy.array(eye_tag) 99 #存放爲ndarray數組類型,輸入內容爲[[x1 y1 x1+w y1+h][x1 y1 x1+w y1+h]...],後面會獲取多維數組的下標來替換數值 100 101 if len(faceRects_eye)==2: 102 #眼睛識別到兩個時,同時替換圖片 103 img_ex=cv2.resize(eye,(n_eyetag[0,1]-n_eyetag[0,0], n_eyetag[0,3]-n_eyetag[0,2]),interpolation=cv2.INTER_CUBIC) 104 img_ex1 = cv2.resize(eye, (n_eyetag[1, 1] - n_eyetag[1, 0], n_eyetag[1, 3] - n_eyetag[1, 2]), interpolation=cv2.INTER_CUBIC) 105 if key == ord('p'): 106 #檢測到鍵盤輸入p時,進行替換 107 img[n_eyetag[0,0]:n_eyetag[0,1],n_eyetag[0,2]:n_eyetag[0,3]]=img_ex 108 img[n_eyetag[1, 0]:n_eyetag[1, 1], n_eyetag[1, 2]:n_eyetag[1, 3]] = img_ex1 109 if len(faceRects_eye)==1: 110 # 眼睛識別到一個時,替換圖片 111 img_ex = cv2.resize(eye, (n_eyetag[0, 1] - n_eyetag[0, 0], n_eyetag[0, 3] - n_eyetag[0, 2]), interpolation=cv2.INTER_CUBIC) 112 if key == ord('p'): 113 img[n_eyetag[0, 0]:n_eyetag[0, 1], n_eyetag[0, 2]:n_eyetag[0, 3]] = img_ex 114 115 video.write(img) 116 cv2.imshow('video', img) 117 #顯示圖片,標題名字爲video 118 cv2.resizeWindow('video',1280,720) 119 #調整窗口大小video爲1280*720 120 if key == ord('q'): 121 #檢測到鍵盤輸入q,退出循環 122 break 123 124 video.release() 125 #再也不錄製視頻 126 cap.release() 127 #釋放攝像頭 128 cv2.destroyAllWindows() 129 #關閉全部窗口顯示
代碼中都有註釋,基本看懂第一個循環輸出人臉區域檢測的內容就足夠了,後面只是在原來的基礎上進行多個區域的循環和判斷。接下來我來說一下這個代碼的實現思路,畫成圖解釋一下:
1.打開攝像頭的時候,開始檢測人臉的區域。
2.將獲取到的區域上、下半部分圖像存儲下來。
3.檢測上半部分圖像區域中雙眼的位置。
4.檢測下半部分圖像區域中嘴巴的位置。
5.模擬人臉識別所進行的操做,如眨眼,搖頭,慢慢張開嘴巴,這裏咱們直接使用鍵盤‘p’來替換眼睛的位置,‘z’替換嘴巴的位置。
6.關閉攝像頭。
說到這裏,你們可能有點疑問爲何要劃出上下部分區域來識別眼睛和鼻子,緣由是識別器的準確度不是很高,若檢測眼睛,鼻子區域也會識別成眼睛,因此才須要劃分範圍,請看下圖:
下面我來跟你們講一下IMG取某區域內圖像的座標以及cv2.rectangle的定位座標。
1)cv2.rectangle,咱們從上面的註釋中知道,獲取的是左上角和右下角的頂點座標,用畫圖的方式表達以下:
2)IMG區域取值範圍則和1)不一樣,IMG[左上頂點y:左下頂點y,左上頂點x:右上頂點x],用圖來表示S◇acef區域以下:
因此,以後截取後記得以最原始的IMG座標爲頂點,再來表示要截取的區域,否則就會報錯。
3、Q&A
一、Q:2018-03-20 11:17:10.309 python[79368:108479406] mMovieWriter.status: 3. Error: Cannot Save
A:存放信息目錄下已有同名文件,只須要刪除文件便可,不影響程序正常使用
二、Q:網絡上下載的opencv 識別器xml文件,運行工程時提示
OpenCV Error: Unknown error code -49 (Input file is empty) in cvOpenFileStorage, file /Users/travis/build/skvark/opencv-python/opencv/
modules/core/src/persistence.cpp, line 4484 Traceback (most recent call last): File "/Users/funny/PycharmProjects/untitled1/open-cv.py", line 19, in <module> classifier_mouth=cv2.CascadeClassifier("/Users/funny/Documents/haarcascade_mcs_mouth1.xml") cv2.error: /Users/travis/build/skvark/opencv-python/opencv/modules/core/src/persistence.cpp:4484: error: (-49) Input file is empty in
function cvOpenFileStorage
A:通常xml文件不會超過1m,我下載的這個文件超過了6m,如何檢測是不是可用文件?在存放xml文件的目錄下,單擊後在預覽區能顯示內容則是正確的,請看下圖:
不能顯示內容則是錯誤不可以使用的文件:
三、Q:報錯信息:沒法覆蓋圖像
File "/Users/funny/PycharmProjects/untitled1/open-cv.py", line 52, in <module> img[ym1:(my+ym1+hm2), intx+xm1:(intx + xm1+wm1)] = img_mx ValueError: could not broadcast input array from shape (86,143,3) into shape (662,143,3)
A:計算圖像的位置不正確,已經超出了範圍,建議參考二代碼中講解圖像IMG顯示的區域座標。
4、程序已知的問題
一、暫時定位嘴巴和眼睛位置是以人臉分開兩個區域來縮小搜索檢測範圍,因此擡起頭或多人時,仍會檢測到別的地方,這是因爲OpenCV內的Haar特徵分類器訓練模型仍是不夠,因此檢測不夠精準。程序實現的是實時檢測攝像頭內圖像,換成識別靜態圖片則準確多了。
二、只要檢測到人臉,就會一直循環檢測,因此當人物晃動的時候,能檢測到多個眼睛和嘴巴,並且檢測框不斷變換大小。
三、程序暫時只實現了單眼、雙眼同時替換,嘴巴替換。當攝像頭檢測到多個眼睛,只能同時替換至多兩個眼睛。
四、替換的圖片在肉眼看來很假,瞳孔顏色,眼睛大小都還不夠精準,只是用替換圖替換。
5、參考資料
一、OpenCV教程
二、Python入門教程
三、人工智能書籍