——第一期:關(guān)于制作簡(jiǎn)單動(dòng)態(tài)眨眼
一、探索背景
隨著人工智能的發(fā)展妇萄,ai人臉識(shí)別已經(jīng)成為人工智能應(yīng)用中不可缺少的一部分换途,各行各業(yè)都開(kāi)始用這個(gè)新興技術(shù),減少審核成本膛薛,增加審核效率听隐,可是人工智能畢竟跟人還是有差別的,因?yàn)槿斯ぶ悄艿膶?duì)人臉的判斷是通過(guò)無(wú)數(shù)算法跟模型訓(xùn)練哄啄,才會(huì)越來(lái)越精確雅任,所以有很多人為看起來(lái)非常假的圖,人工智能計(jì)算后是可以通過(guò)的咨跌。
比如一張人臉沪么,假設(shè)是否是本人人臉的兩個(gè)判斷條件是:1、人臉跟本人身份證照片相似度達(dá)90%锌半。2禽车、檢測(cè)的人是個(gè)活體。滿足這兩個(gè)條件ai的判斷就是通過(guò)刊殉,但是這兩個(gè)條件其實(shí)都是可以偽造的殉摔,比如說(shuō)找本人的圖片,通過(guò)技術(shù)手段讓眼睛嘴巴動(dòng)起來(lái)记焊,ai就會(huì)判斷成活體逸月,并且與本人相似度很高,因此就通過(guò)了遍膜,這種情況我們稱為ai人臉欺詐碗硬。
所以本期的探索就是通過(guò)opencv讓靜態(tài)人臉上的眼睛動(dòng)起來(lái),從而模仿照片為本人且是活體的效果瓢颅,模擬ai人臉欺詐恩尾。
二、需要用到的第三方依賴庫(kù)
OpenCV
OpenCV是一個(gè)跨平臺(tái)的計(jì)算機(jī)視覺(jué)庫(kù)惜索,是開(kāi)源的特笋,可以應(yīng)用在主流操作系統(tǒng)上剃浇,例如:Linux巾兆、windows猎物、Android和Mac os操作系統(tǒng)上。它由一系列的c函數(shù)和少量的c++類構(gòu)成角塑,同時(shí)也提供了Python蔫磨、Ruby、MATLAB等接口圃伶,實(shí)現(xiàn)了圖像處理和計(jì)算機(jī)視覺(jué)方面的很多通用算法堤如。本次的模擬ai人臉欺詐,主要用的就是這個(gè)開(kāi)源庫(kù)窒朋。
Pillow 與PIL
PIL:Python Imaging Library搀罢,已經(jīng)是Python平臺(tái)事實(shí)上的圖像處理標(biāo)準(zhǔn)庫(kù)了。PIL功能非常強(qiáng)大侥猩,但API卻非常簡(jiǎn)單易用榔至。 由于PIL僅支持到Python 2.7,加上年久失修欺劳,于是一群志愿者在PIL的基礎(chǔ)上創(chuàng)建了兼容的版本唧取,名字叫Pillow,支持最新Python 3.x划提,又加入了許多新特性枫弟,因此,我們可以直接安裝使用Pillow鹏往。
三淡诗、環(huán)境的搭建
python版本:2.7
版本依賴庫(kù):
pip:9.0.1
$brew install pip
安裝pillow:5.0.0
$pip install pillow
安裝pillow-PIL 0.1dev
$pip install pillow
numpy 1.14.1
$pip install numpy
這里可能會(huì)遇到已經(jīng)安裝numpy1.0.8rc1無(wú)法卸載的問(wèn)題,請(qǐng)參考鏈接: http://blog.csdn.net/hqzxsc2006/article/details/51602654
OpenCV-python 3.3.0.10
$brew install OpenCV
如果遇到Permissiondenied安裝失敗掸犬,請(qǐng)加上sudo重試袜漩。 除了以上的版本,還需要一個(gè)python編輯器湾碎,推薦用pycharm宙攻。
親測(cè)在真實(shí)環(huán)境下搭建環(huán)境過(guò)程比較繁瑣,而且容易遇到各種問(wèn)題介褥。建議通過(guò)pycharm搭建一個(gè)虛擬環(huán)境來(lái)實(shí)現(xiàn)座掘。
四、用python實(shí)現(xiàn)靜態(tài)圖變動(dòng)態(tài)圖
首先附上靜圖變動(dòng)圖的源碼:
#靜態(tài)圖片變動(dòng)圖代碼
import os
import numpy
from PIL import Image, ImageDraw
import cv2
left = cv2.imread("/Users/aa/PycharmProjects/untitled1/left.png")
right = Image.open("/Users/aa/PycharmProjects/untitled1/right.png")
classifier = cv2.CascadeClassifier("/Users/aa/PycharmProjects/untitled1/haarcascade_eye.xml")
count = 0
while count > -1:
img = cv2.imread("/Users/aa/PycharmProjects/untitled1/timg.jpg")
Face = img[55:136, 170:255]
eyeRects = classifier.detectMultiScale(img, 1.2, 2, cv2.CASCADE_SCALE_IMAGE, (20, 20))
key = cv2.waitKey(1)
if len(eyeRects) > 0:
for faceRect in eyeRects:
x, y, w, h = faceRect
cv2.rectangle(img, (int(x), int(y)), (int(x) + int(w), int(y) + int(h)), (0, 255, 0), 2, 0)
ex_show=cv2.resize(left,(w,h),interpolation=cv2.INTER_CUBIC)
#Y1=y+55
#Y2=(y+h)+55
#X1=x+170
#X2=(x+w)+170
if key == ord('p'):
#img[ Y1:Y2, X1:X2]=ex_show
#Face[y:y+h, x:x+w]=ex_show
img[y:y+h, x:x+w]= ex_show
cv2.imshow('video', img)
cv2.resizeWindow('video',1280,720)
if key == ord('q'):
break
cv2.destroyAllWindows()
以上內(nèi)容及代碼均基于網(wǎng)上搜索和參考別人的代碼柔滔,結(jié)合我的思考和理解溢陪,自己寫(xiě)的代碼,下面內(nèi)容會(huì)解析代碼每一句代表的意義以及作用睛廊,并且我也繪制了一些數(shù)學(xué)模型圖形真,幫助大家對(duì)眼睛識(shí)別及替換的原理有更深刻的理解。
首先我們先導(dǎo)入依賴庫(kù)超全,用它的image跟ImageDraw包來(lái)處理圖片
import os
import numpy
from PIL import Image, ImageDraw
import cv2
然后先讀取一張主圖:
img = cv2.imread("/Users/aa/PycharmProjects/untitled1/timg.png")
格式為:img = cv2.imread("文件路徑")咆霜,默認(rèn)是彩色的邓馒,如果想要灰色可以這樣設(shè)置:img=cv2.imread("文件路徑",0)蛾坯,后面的參數(shù)決定圖片的色彩光酣。
然后我們?cè)儆茫?/p>
cv2.imshow('video', img)
來(lái)查看圖片是否顯示正常,顯示的結(jié)果是這樣子的:
導(dǎo)入成功脉课,想要眼睛動(dòng)起來(lái)救军,首先我們得先識(shí)別出眼睛所在的區(qū)域。這時(shí)候可以用一個(gè)現(xiàn)成的經(jīng)過(guò)訓(xùn)練的人眼識(shí)別xml幫助倘零,文件名字:haarcascade_eye.xml唱遭,這個(gè)xml在網(wǎng)上也能找到,導(dǎo)入這個(gè)xml并建立一個(gè)對(duì)象呈驶。
classifier = cv2.CascadeClassifier("/Users/dingjingjing058/Downloads/haarcascade_eye.xml")
通過(guò)對(duì)象調(diào)用xml的detectMultiScale函數(shù)并且賦值給一個(gè)變量胆萧。
eyeRects = classifier.detectMultiScale(img, 1.2, 2, cv2.CASCADE_SCALE_IMAGE, (20, 20))
這條代碼的意思,就是在整張圖片中俐东,識(shí)別出人眼的區(qū)域跌穗。
detectMultiScale這個(gè)函數(shù)的用法我就不詳細(xì)說(shuō)了,粗略的說(shuō)一下每一個(gè)參數(shù)的意義:
detectMultiScale函數(shù)介紹:
參數(shù)1:image--待檢測(cè)圖片虏辫,一般為灰度圖像加快檢測(cè)速度
參數(shù)2:objects--被檢測(cè)物體的矩形框向量組蚌吸;
參數(shù)3:scaleFactor--表示在前后兩次相繼的掃描中,搜索窗口的比例系數(shù)砌庄。默認(rèn)為1.1即每次搜索窗口依次擴(kuò)大10%;
參數(shù)4:minNeighbors--表示構(gòu)成檢測(cè)目標(biāo)的相鄰矩形的最小個(gè)數(shù)(默認(rèn)為3個(gè))羹唠。 如果組成檢測(cè)目標(biāo)的小矩形的個(gè)數(shù)和小于minneighbors - 1 都會(huì)被排除。如果minneighbors 為 0, 則函數(shù)不做任何操作就返回所有的被檢候選矩形框娄昆,這種設(shè)定值一般用在用戶自定義對(duì)檢測(cè)結(jié)果的組合程序上佩微;
參數(shù)5:flags--flags--要么使用默認(rèn)值,要么使用CV_HAAR_DO_CANNY_PRUNING萌焰,如果設(shè)置為CV_HAAR_DO_CANNY_PRUNING哺眯,那么函數(shù)將會(huì)使用Canny邊緣檢測(cè)來(lái)排除邊緣過(guò)多或過(guò)少的區(qū)域,因此這些區(qū)域通常不會(huì)是人臉?biāo)趨^(qū)域扒俯;
參數(shù)6奶卓、7:minSize和maxSize用來(lái)限制得到的目標(biāo)區(qū)域的范圍。
然后就是把識(shí)別出來(lái)的人眼區(qū)域畫(huà)一個(gè)矩形撼玄,畫(huà)矩形的代碼是這樣的:
cv2.rectangle(img, ((x1,y1) , (X2,Y2), (0, 255, 0), 2, 0)
其中X1,Y1是矩形的左上角坐標(biāo)夺姑,X2,Y2是矩形的右下角坐標(biāo)。根據(jù)這個(gè)坐標(biāo)來(lái)繪制一個(gè)矩形掌猛。
可是我們?cè)趺锤鶕?jù)人眼識(shí)別的點(diǎn)來(lái)畫(huà)矩形呢盏浙?用一個(gè)for循環(huán)在xml里遍歷一下faceRect這個(gè)函數(shù),遍歷的代碼就這樣:
if len(faceRects) > 0:
for faceRect in eyeRects:
x, y, w, h = eyeRect
cv2.rectangle(img, (int(x), int(y)), (int(x) + int(w), int(y) + int(h)), (0, 255, 0), 2, 0)
可能比較抽象,我們就以圖畫(huà)一個(gè)坐標(biāo)废膘,原點(diǎn)為右上角的點(diǎn)辣往,圖像的寬為x軸,高為y軸殖卑,然后畫(huà)矩形就是用x跟y軸的坐標(biāo)。
原理圖如下坊萝,上述代碼就會(huì)繪制出來(lái)下面的這幅圖孵稽。
然后我們?cè)賮?lái)運(yùn)行后的人眼識(shí)別結(jié)果,識(shí)別成功:
識(shí)別成功以后,怎么把這個(gè)靜態(tài)圖片變成一個(gè)動(dòng)態(tài)的圖片呢十偶?這時(shí)候我們就需要替換矩形內(nèi)部的圖像菩鲜,首先先把要換圖的區(qū)域給定位出來(lái),用這部分代碼定位:
img[ y:(y + h), x:(x + w)]
這行代碼的意思是把y1=y y2=y+h惦积,x1=x 接校,X2=x+w,把這四根線重疊的部分給摳出來(lái)狮崩,如下圖黑色陰影部分:
然后再置換成另外一張圖片蛛勉,我們首先導(dǎo)入要替換眼睛部分的圖片,并讓圖片適應(yīng)框的大小睦柴。
ex_show=cv2.resize(left,(w,h),interpolation=cv2.INTER_CUBIC)
然后把剛剛?cè)Τ鰜?lái)的那個(gè)區(qū)域诽凌,替換成這張圖片:
img[ y:(y + h), x:(x + w)]=ex_show
然后就大功告成,效果如下圖所示:
然后怎么讓這個(gè)圖動(dòng)起來(lái)呢坦敌,我們就設(shè)定按鍵盤(pán)某個(gè)按鍵的時(shí)候侣诵,這張圖片才會(huì)替換,這樣不斷按這個(gè)按鈕狱窘,圖片就變成動(dòng)圖啦杜顺。
if key == ord('p'):
img[ y:(y + h), x:(x + w)]=ex_show
這樣一幅動(dòng)態(tài)識(shí)別替換眼睛部分的動(dòng)圖就完成啦,這樣就可以用靜態(tài)圖片不斷替換眼睛部分以營(yíng)造是活體的效果蘸炸,從而瞞過(guò)人工智能躬络。
五、識(shí)別準(zhǔn)確度優(yōu)化的實(shí)現(xiàn)
以上內(nèi)容是我對(duì)源碼的思考和理解搭儒,以下內(nèi)容是我在源碼的基礎(chǔ)上自己寫(xiě)的一些優(yōu)化代碼洗鸵,讓眼部的識(shí)別更加精準(zhǔn)。
雖然我們成功的完成了人眼識(shí)別并替換其他圖片仗嗦,但是我們都發(fā)現(xiàn)膘滨,上圖的識(shí)別部分不太準(zhǔn)確,有一部分已經(jīng)識(shí)別到腋下去了稀拐,那么我們?cè)趺醋屪R(shí)別范圍更加的準(zhǔn)確呢火邓?
小編的想法就是,框定一個(gè)區(qū)域,比如說(shuō)人臉铲咨,然后讓眼睛識(shí)別就在這個(gè)區(qū)域內(nèi)進(jìn)行躲胳,這樣的話準(zhǔn)確度會(huì)大大的提高。
于是決定先把人臉部分的坐標(biāo)先摳出來(lái):
Face = img[55:136, 170:255]
然后在摳出來(lái)的圖里面進(jìn)行遍歷纤勒,識(shí)別人眼坯苹。
if len(faceRects) > 0:
for faceRect in eyeRects:
x, y, w, h = faceRect
cv2.rectangle(Face, (int(x), int(y)), (int(x) + int(w), int(y) + int(h)), (0, 255, 0), 2, 0)
然后再運(yùn)行一下,我們發(fā)現(xiàn)已經(jīng)把矩形準(zhǔn)確的定位到眼睛的部分啦摇天。
但是這時(shí)候我們發(fā)現(xiàn)運(yùn)行程序粹湃,會(huì)發(fā)現(xiàn)替換圖片的時(shí)候跑偏了。為什么會(huì)跑偏呢泉坐?原來(lái)我們?cè)趽赋鰜?lái)的圖中畫(huà)矩形的時(shí)候为鳄,矩形已經(jīng)不是以整個(gè)圖片為原點(diǎn),而是以摳出來(lái)的圖的左上角坐標(biāo)為原點(diǎn)腕让,這時(shí)候的x孤钦,y是相對(duì)于坐標(biāo)(55,170)而設(shè)置的纯丸,再來(lái)看我們替換圖片的代碼:
img[ y:(y + h), x:(x + w)]=ex_show
但是我們替換圖片的時(shí)候偏形,仍然是以原圖為參考的,這時(shí)候的x觉鼻,y識(shí)別到的 是以原圖左上角為原點(diǎn)壳猜,x,y為矩形左上角點(diǎn)的部分滑凉。這時(shí)候我們得把摳出來(lái)的圖的坐標(biāo)再還原到原圖上统扳,從而識(shí)別替換部分的坐標(biāo):
所以替換圖片的坐標(biāo)應(yīng)該改成這樣:
Y1=y+55
Y2=(y+h)+55
X1=x+170
X2=(x+w)+170
if key == ord('p'):
img[ Y1:Y2, X1:X2]=ex_show
其實(shí)還有一種更加簡(jiǎn)單的方式,沒(méi)錯(cuò)就是直接把替換區(qū)域也改成以摳出來(lái)的圖為參考:
Face[y:y+h, x:x+w]=ex_show
這樣一幅準(zhǔn)確的人眼識(shí)別動(dòng)圖就做出來(lái)啦畅姊!
這段代碼中摳出來(lái)的人臉坐標(biāo)是寫(xiě)死的只是為了更便于理解它的精確過(guò)程咒钟,之后可以換成智能人臉識(shí)別,先把人臉識(shí)別出來(lái)若未,然后把人臉的部分摳出來(lái)再進(jìn)行人眼識(shí)別朱嘴,其實(shí)跟上述的原理一樣的,只不過(guò)把寫(xiě)死的這個(gè)坐標(biāo)img[55:136,170:255]替換成變量坐標(biāo)而已粗合。
最后萍嬉,本期只是做一個(gè)特別基本的嘗試探索,在這個(gè)過(guò)程中也遇到了許多問(wèn)題隙疚,環(huán)境上壤追,遇到了很多環(huán)境搭建的問(wèn)題,比如說(shuō)安裝了以后仍然無(wú)法導(dǎo)入image跟imageview的包供屉,最后通過(guò)搭建虛擬環(huán)境解決了問(wèn)題行冰,比如人眼識(shí)別不太精準(zhǔn)總是識(shí)別到其他東西溺蕉。這期做的只是一個(gè)非常簡(jiǎn)單的人眼識(shí)別然后替換的實(shí)現(xiàn),后期會(huì)嘗試實(shí)現(xiàn)人眼蒙版過(guò)渡得更自然-接近于真實(shí)人眼的睜眼閉眼的模擬悼做,然后讓識(shí)別更加的精確疯特,不會(huì)識(shí)別到別的地方。后期會(huì)嘗試加上嘴巴的識(shí)別肛走,讓靜態(tài)圖片的嘴巴實(shí)現(xiàn)動(dòng)態(tài)張閉漓雅,以及靜態(tài)圖搖頭的功能的實(shí)現(xiàn)。