使用ResNet50預(yù)置算法訓(xùn)練美食分類模型-優(yōu)化建議
877
2025-04-01
2019年7月至2019年12月期間,參加了華為云ModelArt訓(xùn)練營(yíng)活動(dòng),在ModelArt平臺(tái)上,做了一些AI實(shí)驗(yàn),現(xiàn)在整理一下資料,把我主要做的幾拓展實(shí)驗(yàn)內(nèi)容做了記錄,方便以后查閱,且分享給那些對(duì)使用華為云ModelArt開發(fā)有興趣的朋友。第三個(gè)拓展實(shí)驗(yàn)就是訓(xùn)練營(yíng)第十一期內(nèi)容的拓展,基于dlib和openCV,關(guān)于人臉測(cè)試方面的內(nèi)容。
1、概述
內(nèi)容主要是基于dlib和openCV,從圖像和視頻兩個(gè)方向做了人臉檢測(cè)實(shí)驗(yàn),做了以下兩部分內(nèi)容:
1)基于dlib的視頻檢測(cè)效果提升的案例:主要是對(duì)案例上的dlib視頻檢測(cè)內(nèi)容進(jìn)行了解讀,對(duì)其中一些細(xì)節(jié)進(jìn)行了推敲,根據(jù)一些實(shí)驗(yàn)和測(cè)試,提升了一些檢測(cè)速度。根據(jù)實(shí)驗(yàn)的結(jié)果,對(duì)過(guò)程做了說(shuō)明(包含主要代碼的說(shuō)明),并且闡述了其中原理,并且對(duì)一些拓展的知識(shí)內(nèi)容也捎帶做了說(shuō)明。
2)openCV的人臉檢測(cè):根據(jù)一些文檔和資料,基于openCV庫(kù),實(shí)現(xiàn)了人臉的檢測(cè)。對(duì)openCV實(shí)現(xiàn)的原理和方式做了說(shuō)明,對(duì)代碼主要部分做了描述,并且將在實(shí)踐中遇到的問(wèn)題重點(diǎn)基于分析,并且寫出了解決思路和方式,對(duì)檢測(cè)結(jié)果數(shù)據(jù)做了一些對(duì)比。
1.1、dlib簡(jiǎn)介
Dlib是一個(gè)老牌的跨平臺(tái)的C公共庫(kù),支持C語(yǔ)言和Python語(yǔ)言進(jìn)行開發(fā)和學(xué)習(xí),主要以C為主。它除了線程支持,網(wǎng)絡(luò)支持,提供測(cè)試以及大量工具等等優(yōu)點(diǎn),Dlib還是一個(gè)強(qiáng)大的機(jī)器學(xué)習(xí)的C庫(kù),包含了許多機(jī)器學(xué)習(xí)常用的算法。同時(shí)支持大量的數(shù)值算法如矩陣、大整數(shù)、隨機(jī)數(shù)運(yùn)算等等。Dlib同時(shí)還包含了大量的圖形模型算法。最重要的是Dlib的文檔和例子都非常詳細(xì),可以訪問(wèn)它的網(wǎng)站來(lái)獲取相關(guān)資料,它和ModelArt兼容性非常好。Dlib人臉識(shí)別有兩種檢測(cè)器:1、基于特征(HOG)+分類器(SVM)的檢測(cè)器;2、基于深度學(xué)習(xí)的卷積神經(jīng)網(wǎng)方法(resnet_model)的檢測(cè)器。
1.2、openCV簡(jiǎn)介
OpenCV是一個(gè)基于BSD許可(開源)發(fā)行的跨平臺(tái)計(jì)算機(jī)視覺(jué)庫(kù),它輕量級(jí)而且高效——由一系列 C 函數(shù)和少量 C++ 類構(gòu)成,同時(shí)提供了Python、Ruby、MATLAB等語(yǔ)言的接口,實(shí)現(xiàn)了圖像處理和計(jì)算機(jī)視覺(jué)方面的很多通用算法。OpenCV用C++語(yǔ)言編寫,它的主要接口也是C++語(yǔ)言,現(xiàn)在對(duì)Python也有了很好的支持。 cv2是OpenCV官方的一個(gè)更新和擴(kuò)展庫(kù),里面含有各種有用的函數(shù)以及進(jìn)程,目前我們基本都是用它來(lái)工作。python使用的模塊也是基于為cv2,被寫成cv2,是因?yàn)樵撃K引入了一個(gè)更好的cv2的api接口。 cv2對(duì)人臉檢測(cè)也有兩種不同的檢測(cè)器:1、基于?Haar 級(jí)聯(lián)分類器的檢測(cè)器;2、基于DNN網(wǎng)絡(luò)的人臉檢測(cè)器。
2、基于dlib的視頻檢測(cè)效果的提升
主要是對(duì)案例上的dlib視頻檢測(cè)內(nèi)容進(jìn)行了解讀,對(duì)其中一些細(xì)節(jié)進(jìn)行了推敲,根據(jù)一些實(shí)驗(yàn)和測(cè)試,提升了一些檢測(cè)速度。根據(jù)實(shí)驗(yàn)的結(jié)果,對(duì)過(guò)程做了說(shuō)(明包含主要代碼的說(shuō)明),并且闡述了其中原理。
2.1、dlib的檢測(cè)機(jī)制
Dlib人臉識(shí)別有兩種檢測(cè)器:1、基于特征(HOG)+分類器(SVM)的檢測(cè)器;2、基于深度學(xué)習(xí)的卷積神經(jīng)網(wǎng)方法(resnet_model)的檢測(cè)器。
HOG和SVM檢測(cè):該檢測(cè)器由兩部分組成:特征工程部分和分類器,特征工程算法采用了HOG算法,分類器用了SVM分類器。 在計(jì)算機(jī)視覺(jué)以及數(shù)字圖像處理中,梯度方向直方圖(HOG:Histogram of Oriented Gridients)是一種能對(duì)物體進(jìn)行檢測(cè)的基于形狀邊緣特征的描述算子,它的基本思想是利用梯度信息能很好的反映圖像目標(biāo)的邊緣信息并通過(guò)局部梯度的大小將圖像局部的外觀和形狀特征化。
簡(jiǎn)單來(lái)說(shuō)分為以下幾個(gè)過(guò)程:
1)圖像預(yù)處理。
2)計(jì)算圖像像素點(diǎn)梯度值,得到梯度圖(尺寸和原圖同等大小)。
3)圖像劃分多個(gè)cell,統(tǒng)計(jì)cell內(nèi)梯度直方向方圖。
4)將2×2個(gè)cell聯(lián)合成一個(gè)block,對(duì)每個(gè)block做塊內(nèi)梯度歸一化。
5)獲取HOG特征值。
分類器用了常見(jiàn)的SVM方式,支持向量機(jī)(Support Vector Machine, SVM)是一類按監(jiān)督學(xué)習(xí)(supervised learning)方式對(duì)數(shù)據(jù)進(jìn)行二元分類的廣義線性分類器(generalized linear classifier),其決策邊界是對(duì)學(xué)習(xí)樣本求解的最大邊距超平面(maximum-margin hyperplane)。HOG+SVM可以說(shuō)是經(jīng)典組合,應(yīng)用廣泛,除了人臉識(shí)別,近些年來(lái),還有人用它做行人檢測(cè)、車牌識(shí)別等。
基礎(chǔ)的人臉關(guān)鍵點(diǎn)共有 68 個(gè),分別是人臉各部位的點(diǎn),如嘴角,眼睛邊等。 dlib提供了很友好的檢測(cè)人臉landmarkers的接口,landmarkers是一種人臉部特征點(diǎn)提取的技術(shù),大致基于ERT(ensemble of regression trees)級(jí)聯(lián)回歸算法,即基于梯度提高學(xué)習(xí)的回歸樹方法。現(xiàn)在,有些商家的人臉關(guān)鍵點(diǎn)實(shí)用中往往不止68個(gè),會(huì)更多,上次在百度看到一個(gè)API提供的關(guān)鍵點(diǎn)有150個(gè)之多。
2.2、原有檢測(cè)過(guò)程的代碼分析
代碼分兩部分內(nèi)容,第一部分,是對(duì)人臉進(jìn)行分析部分:
detector?=?dlib.get_frontal_face_detector() ??cnn_face_detector?=?dlib.cnn_face_detection_model_v1("./models/detector.dat")
以上兩行代碼,第一行定義了一個(gè)dlib的人臉檢測(cè)器,第二行代碼加載了CNN檢測(cè)器的數(shù)據(jù)模型,數(shù)據(jù)模型位置在./models/detector.dat下。這樣完成了定義了一個(gè)可用的人臉檢測(cè)器。
dets?=?cnn_face_detector(image,?1)
上面這行代碼,將我們載入的圖片加載進(jìn)入檢測(cè)器,進(jìn)行檢測(cè)后,返回一個(gè)檢測(cè)結(jié)果dets。dets是一個(gè)多個(gè)檢測(cè)結(jié)果的數(shù)據(jù)集合,包含檢測(cè)到的每張圖片的位置等數(shù)據(jù)。
for?i,?d?in?enumerate(dets):???? ????????print("Detection?{}:?Left:?{}?Top:?{}?Right:?{}?Bottom:?{}?Confidence:?{}".format( ????????????i,?d.rect.left(),?d.rect.top(),?d.rect.right(),?d.rect.bottom(),?d.confidence)) ????Number?of?faces?detected:?1 ????Detection?0:?Left:?443?Top:?119?Right:?613?Bottom:?289?Confidence:?1.0296450853347778
上面代碼是對(duì)dets數(shù)據(jù)進(jìn)行解析,利用for循環(huán)輸出每一張被解析到的數(shù)據(jù)內(nèi)容,返回的i和d參數(shù),i是指第N張圖片,d是這張圖片的數(shù)據(jù)內(nèi)容,i從0開始計(jì)數(shù)。上面打印出來(lái)了這張圖片的位置信息:左上角坐標(biāo)(443,119),右下角坐標(biāo)(613,289),這個(gè)是一個(gè)圖片絕對(duì)坐標(biāo)值的位置。
res_img?=?cv2.rectangle(image,?(443,?119),?(613,?289),?0,?1)???? ????Image.fromarray(res_img)
上面兩行代碼是圖片顯示,利用openCV庫(kù),在圖片上畫位置框,位置信息就是上面的絕對(duì)坐標(biāo)值的位置信息,后面兩個(gè)參數(shù)是框的顏色等信息。顯示使用的PIL庫(kù),進(jìn)行圖片的展示,就如我們?cè)诎咐锌吹降膱D像。
predictor_kp?=?dlib.shape_predictor("./models/shape_predictor_68_face_landmarks.dat")
上面這行代碼就是加載了一個(gè)人臉關(guān)鍵點(diǎn)的檢測(cè)器,模型文件為shape_predictor_68_face_landmarks.dat。
shape?=?predictor_kp(image,?d) ????print("Part?0:?{},?Part?1:?{}?...".format(shape.part(0),shape.part(1))) ????Part?0:?(435,?200),?Part?1:?(442,?224)?...
上面代碼就是用人臉關(guān)鍵點(diǎn)檢測(cè)器對(duì)圖像進(jìn)行關(guān)鍵點(diǎn)解析,d就是用人臉位置識(shí)別器識(shí)別出來(lái)的人臉位置數(shù)據(jù)。然后打印出來(lái)檢測(cè)的位置信息。
for?i?in?range(68):???? ????????res_img?=?cv2.circle(res_img,(shape.part(i).x,shape.part(i).y),?1,?255,?4) ????Image.fromarray(res_img)
上面代碼是顯示圖片中人臉關(guān)鍵點(diǎn)位置,利用for循環(huán)在圖片上逐一描繪每一個(gè)點(diǎn),如果你的關(guān)鍵點(diǎn)不是68個(gè),就修改掉68這個(gè)數(shù),最后用使用的PIL庫(kù),在案例中顯示圖片。
第二部分是,對(duì)視頻進(jìn)行檢測(cè):
cap?=?cv2.VideoCapture(video_name) ????while?True: ????try: ????????clear_output(wait=True) ????????ret,?frame?=?cap.read() ????????if?ret: ??????res_img?=?keypoint_detector(frame) ????????????img?=?arrayShow(res_img) ????????????display(img) ????????????display(img) ????????????time.sleep(0.05) ????????else: ????????????break ????except?KeyboardInterrupt: ????????cap.release() ????cap.release()
以上代碼是視頻檢測(cè)的主要代碼,首先,cap = cv2.VideoCapture(video_name)讀取視頻文件,將視頻文件處理的結(jié)果數(shù)據(jù)集合返回到cap,然后按照一幀一幀的方式循環(huán)讀取每一幀,每一幀畫面作為一張圖像,放入自定義keypoint_detector函數(shù)中處理,然后將出后返回的圖像文件img進(jìn)行顯示。代碼time.sleep(0.05)是中間設(shè)置間歇的延遲時(shí)間,以便于顯示(讓我們的肉眼可以明顯看出來(lái))。
def?keypoint_detector(image): ????global?res_img ????detector_kp?=?dlib.get_frontal_face_detector() ????predictor_kp?=?dlib.shape_predictor("./models/shape_predictor_68_face_landmarks.dat") ????dets?=?detector_kp(image,?1) ????for?k,?d?in?enumerate(dets): ????????print("Detection?{}:?Left:?{}?Top:?{}?Right:?{}?Bottom:?{}".format( ????????????k,?d.left(),?d.top(),?d.right(),?d.bottom())) ????????res_img?=?cv2.rectangle(image,(?d.left(),?d.top()),?(d.right(),?d.bottom()),?0,?1) ????????shape?=?predictor_kp(image,?d) ????????for?i?in?range(68): ????????????res_img?=?cv2.circle(image,(shape.part(i).x,shape.part(i).y),?1,?255,?2) ????return?res_img
以上代碼是自定義函數(shù)keypoint_detector,它傳入的參數(shù)是一幀圖像數(shù)據(jù),dlib.get_frontal_face_detector()定義了一個(gè)基于dlib的HOG的人臉檢測(cè)器,dlib.shape_predictor 是載入這個(gè)人臉檢測(cè)器的模型數(shù)據(jù),這樣就可以使用用了。進(jìn)行檢測(cè)后,返回一個(gè)dets檢測(cè)結(jié)果的對(duì)象。和上次一樣,對(duì)結(jié)果進(jìn)行循環(huán)讀取人臉,然后打印出人臉在圖像中的絕對(duì)位置坐標(biāo)。還是用predictor_kp(image, d) 方式進(jìn)行人臉關(guān)鍵點(diǎn)檢測(cè),還是熟悉的68個(gè)點(diǎn)特征點(diǎn),使用循環(huán)將這些數(shù)據(jù)在圖像中進(jìn)行描繪。 這段代碼就是熟悉的味道,將視頻中每一幀的畫面當(dāng)作一張圖像進(jìn)行處理。
2.3、視頻提升的方式
主要還是想辦法優(yōu)化檢測(cè)的速度,以提升圖像的檢測(cè)速度為目標(biāo)任務(wù)。為了檢測(cè)結(jié)果,我在代碼中增加了代碼,可以檢測(cè)出來(lái)這個(gè)過(guò)程耗時(shí),單位是秒。
cap?=?cv2.VideoCapture(video_name) ????start_time?=?datetime.now()?#獲得當(dāng)前開始時(shí)間 ????#............省略各種處理步驟 ????end_time?=?datetime.now()?#獲得當(dāng)前結(jié)束時(shí)間 ????durn?=?(end_time?-?start_time?).seconds??#兩個(gè)時(shí)間差,并以秒顯示出來(lái) ????print('cost?time:',?durn,?count)
基本的案例運(yùn)行消耗時(shí)間: ?這個(gè)是基本的消耗時(shí)間552秒。
修改圖像的灰度,首先,想到了圖像的灰度處理方式,我們檢測(cè)人臉,用彩色圖片和灰度圖片對(duì)結(jié)果并無(wú)太大差異。但是,把原始圖像轉(zhuǎn)為灰度之后,減小圖像原始數(shù)據(jù)量,便于后續(xù)處理時(shí)計(jì)算量更少,因?yàn)闄z測(cè)處理不一定需要對(duì)彩色圖像的RGB三個(gè)分量都進(jìn)行處理。增加了如下代碼:
gray?=?cv2.cvtColor(image,?cv2.COLOR_BGR2GRAY)
這樣,每次輸入的圖片都從彩色變成了灰色,對(duì)比一下結(jié)果吧。 ?現(xiàn)在耗時(shí)527秒,這個(gè)點(diǎn)提升程度,微不足道啊。
代碼的優(yōu)化方式:
def?keypoint_detector(image): ????global?res_img ????detector_kp?=?dlib.get_frontal_face_detector() ????predictor_kp?=?dlib.shape_predictor("./models/shape_predictor_68_face_landmarks.dat")
觀察以上代碼,在原始代碼中,發(fā)現(xiàn)每次調(diào)用函數(shù)都要重復(fù)進(jìn)行定義人臉模型檢測(cè)器,如果1000幀都要重新定義1000次模型檢測(cè)器,這個(gè)每次執(zhí)行都是一模一樣的代碼,毫無(wú)區(qū)別,可以做一下優(yōu)化,把模型檢測(cè)器放到函數(shù)之外,這樣可以節(jié)省大量的執(zhí)行時(shí)間,如下:
detector_kp?=?dlib.get_frontal_face_detector() ????predictor_kp?=?dlib.shape_predictor("./models/shape_predictor_68_face_landmarks.dat") ????def?keypoint_detector(image): ????global?res_img
并且,添加如下代碼,每計(jì)算一幀畫面就計(jì)數(shù)器+1,看看默認(rèn)的視頻有多少幀:
count?=?0 ????while?True: ?????#省略中間代碼 ????time.sleep(0.01) ????count?=?count?+?1
看看結(jié)果這樣修改之后的執(zhí)行結(jié)果: ?這下明顯有了提升啊,耗時(shí)22秒,和剛才比,真心飛躍,這個(gè)視頻一共369幀圖像,也就是說(shuō)我們節(jié)約了368次的模型定義和設(shè)置。
2.4、重建模型的優(yōu)化方式
要提高檢測(cè)速度,最根本的還是要優(yōu)化模型,即你的模型是和你的檢測(cè)結(jié)果有直接的關(guān)系。于是,對(duì)dlib 的HOG模型進(jìn)行了重新的訓(xùn)練,構(gòu)建了一個(gè)新的模型文件,進(jìn)行了測(cè)試。 實(shí)踐步驟和相關(guān)代碼:
下載訓(xùn)練樣本圖片,很不客氣的直接從dlib的網(wǎng)站上下載樣本圖片了,做了一回伸手黨,地址:????? ? ? http://dlib.net/files/data/dlib_face_detector_training_data.tar.gz。 下載之后,在notebook的work工作目錄中,使用upload進(jìn)行上傳。上傳完成之后,在python jupyter編輯器中,進(jìn)行解壓縮,結(jié)果如下: ?這個(gè)文件解壓縮之后,里面有兩個(gè)xml的文件,是圖型的配置文件,有個(gè)images目錄,下面是各種人臉圖像文件,有興趣的話,可以打開看看,他們的大小就沒(méi)有小于80 × 80 的哦!
進(jìn)行參數(shù)設(shè)置和訓(xùn)練模型
#?options用于設(shè)置訓(xùn)練的參數(shù)和模式 ????options?=?dlib.simple_object_detector_training_options() ????options.add_left_right_image_flips?=?True
#?支持向量機(jī)的C參數(shù),通常默認(rèn)取為5.自己適當(dāng)更改參數(shù)以達(dá)到最好的效果 ??options.C?=?5 ??#?線程數(shù), ??options.num_threads?=?8 ??options.be_verbose?=?True
上面代碼是設(shè)置訓(xùn)練的參數(shù), 首先,定義一個(gè)人臉識(shí)別器,dlib.simple_object_detector_training_options。然后設(shè)置options參數(shù),配置詳情:
1)add_left_right_image_flips: 面部左右對(duì)稱,選擇是
2)C:支持向量機(jī)SVM的超參數(shù),這個(gè)值如果大一些會(huì)比較準(zhǔn)確,但是會(huì)帶來(lái)過(guò)擬合,選擇一個(gè)居中的值吧,那就選擇5。
3)num_threads:線程數(shù),考慮Notebook是在一個(gè)8核的cpu虛擬機(jī)上,毫不猶豫選了8
4)be_verbose:這個(gè)參數(shù)似乎是什么提示信息,就寫個(gè)True吧。
current_path?=?os.getcwd() ????train_folder?=?current_path?+?'/dlib_face_detector_training_data/' ????train_xml_path?=?train_folder?+?'frontal_faces.xml'???? ????dlib.train_simple_object_detector(train_xml_path,?'detector.svm',?options)
上面代碼是加載訓(xùn)練的圖形目錄和配置文件,就是剛才解壓的數(shù)據(jù)包里面的,用train_simple_object_detector 載入配置參數(shù),申明模型文件detector.svm,構(gòu)成一個(gè)檢測(cè)訓(xùn)練器,并且進(jìn)行訓(xùn)練。
print("Training?accuracy:?{}".format(dlib.test_simple_object_detector(train_xml_path,?"detector.svm")))
上面代碼是將訓(xùn)練完成后的模型結(jié)果保存在模型文件之中,detector.svm 這個(gè)模型文件大小是 44.7kB。加載自建模型和測(cè)試
my_detector?=?dlib.simple_object_detector("detector.svm") ????image?=?dlib.load_rgb_image("./face_1.jpeg") ????dets?=?my_detector(image,?1)
上面代碼是將自己訓(xùn)練的模型文件,載入檢測(cè)器,然后加載本期官方的范例圖片,進(jìn)行檢測(cè)。 還是拿官方的圖片自測(cè)一下效果,如下:? 兩張人臉檢測(cè)對(duì)比,上面一張是本期官方的圖片檢測(cè)結(jié)果,下面這張是自己訓(xùn)練的模型進(jìn)行檢測(cè)的結(jié)果。
最后,我們以自己訓(xùn)練模型進(jìn)行視頻檢測(cè),結(jié)果如下: ?現(xiàn)在耗時(shí)17秒,又有所提升。。。看來(lái)通用的模型文件不如自己訓(xùn)練過(guò)的本地化模型文件執(zhí)行更快啊!感覺(jué)就是大鍋飯和開小灶的區(qū)別吧。。從22秒減少到17秒,減少了了22.7%的時(shí)間,這個(gè)可是切切實(shí)實(shí)的提升。
2.5、其它
其間,還考慮過(guò)其它方式,比如改變圖片尺寸,變的更小,但是考慮到dlib的識(shí)別圖像的只有80 * 80,太小的圖片會(huì)導(dǎo)致小臉就無(wú)法識(shí)別了。又參考了于仕琪老師的文章《怎么把人臉檢測(cè)的速度做到極致》,里面有很多好東西啊,可惜建議自己的能力和時(shí)間不足,沒(méi)有去做,有興趣的可以嘗試用LBP做特征,用AdaBoost這種Boosting方法做分類。于仕琪老師寫了一個(gè)很快很準(zhǔn)的人臉檢測(cè)算法庫(kù),以二進(jìn)制形式免費(fèi)發(fā)布,地址在:https://github.com/ShiqiYu/libfacedetection。里面可惜沒(méi)有python形式的,沒(méi)有辦法直接采用。
3、cv2人臉檢測(cè)
就CV2 檢測(cè),分兩部分內(nèi)容進(jìn)行試驗(yàn)。
3.1、OpenCV Haar Cascade人臉檢測(cè)
該檢測(cè)器由特征提取和分類器兩個(gè)部分組成,現(xiàn)在對(duì)原理和代碼進(jìn)行說(shuō)明。
檢測(cè)原理簡(jiǎn)述:在此之前要先介紹一下級(jí)聯(lián)分類器CascadeClassifier,CascadeClassifier為OpenCV下用來(lái)做目標(biāo)檢測(cè)的級(jí)聯(lián)分類器的一個(gè)類。該類中封裝的目標(biāo)檢測(cè)機(jī)制,簡(jiǎn)而言之是滑動(dòng)窗口機(jī)制+級(jí)聯(lián)分類器的方式。級(jí)聯(lián)分類器: 可以理解為將N個(gè)單類的分類器串聯(lián)起來(lái)。如果一個(gè)事物能屬于這一系列串聯(lián)起來(lái)的的所有分類器,則最終結(jié)果就是 是,若有一項(xiàng)不符,則判定為否。人臉,它有很多屬性(兩條眉毛,兩只眼睛,一個(gè)鼻子等等),我們將每個(gè)屬性做一成個(gè)分類器,如果一個(gè)模型符合了我們定義的人臉的所有屬性,則我們?nèi)藶檫@個(gè)模型就是一個(gè)人臉。 opencv目前僅支持三種特征的訓(xùn)練檢測(cè), HAAR、LBP、HOG,這里我們主要用的是Haar。Haar:從OpenCV1.0以來(lái),一直都是只有用haar特征的級(jí)聯(lián)分類器訓(xùn)練和檢測(cè)(檢測(cè)函數(shù)稱為cvHaarDetectObjects,訓(xùn)練得到的也是特征和node放在一起的xml),在之后當(dāng)CascadeClassifier出現(xiàn)并統(tǒng)一三種特征到同一種機(jī)制和數(shù)據(jù)結(jié)構(gòu)下時(shí),沒(méi)有放棄原來(lái)的C代碼編寫的haar檢測(cè),仍保留了原來(lái)的檢測(cè)部分。openCV的的Haar分類器是一個(gè)監(jiān)督分類器,首先對(duì)圖像進(jìn)行直方圖均衡化并歸一化到同樣大小(例如,30x30),然后標(biāo)記里面是否包含要監(jiān)測(cè)的物體。為了檢測(cè)整副圖像,可以在圖像中移動(dòng)搜索窗口,檢測(cè)每一個(gè)位置來(lái)確定可能的目標(biāo)。 為了搜索不同大小的目標(biāo)物體,分類器被設(shè)計(jì)為可以進(jìn)行尺寸改變,這樣比改變待檢圖像的尺寸大小更為有效。所以,為了在圖像中檢測(cè)未知大小的目標(biāo)物體,掃描程序通常需要用不同比例大小的搜索窗口對(duì)圖片進(jìn)行幾次掃描。 CascadeClassifier檢測(cè)的基本原理:檢測(cè)的時(shí)候可以簡(jiǎn)單理解為就是將每個(gè)固定size特征(檢測(cè)窗口)與輸入圖像的同樣大小區(qū)域比較,如果匹配那么就記錄這個(gè)矩形區(qū)域的位置,然后滑動(dòng)窗口,檢測(cè)圖像的另一個(gè)區(qū)域,重復(fù)操作。由于輸入的圖像中特征大小不定,比如在輸入圖像中眼睛是50x50的區(qū)域,而訓(xùn)練時(shí)的是25x25,那么只有當(dāng)輸入圖像縮小到一半的時(shí)候,才能匹配上,所以這里還有一個(gè)逐步縮小圖像,也就是制作圖像金字塔的流程。這個(gè)size由訓(xùn)練的參數(shù)而定。 由于人臉可能出現(xiàn)在圖像的任何位置,在檢測(cè)時(shí)用固定大小的窗口對(duì)圖像從上到下、從左到右掃描,判斷窗口里的子圖像是否為人臉,這稱為滑動(dòng)窗口技術(shù)(sliding window)。
首先,先要去下載人臉訓(xùn)練的數(shù)據(jù),這個(gè)數(shù)據(jù)可以去openCV在git上的資料中進(jìn)行下載,數(shù)據(jù)是一個(gè)xml文件,在openCV網(wǎng)站上(https://github.com/opencv/opencv/tree/master/data/haarcascades),你可以看到各種xml文件,我們這里選取的haarcascade_frontalface_alt2.xml是一個(gè)快速檢測(cè)模型文件。獲取訓(xùn)練好的人臉的參數(shù)數(shù)據(jù),這里直接從GitHub上使用默認(rèn)值github文件
face_cascade?=?cv2.CascadeClassifier("./models/haarcascade_frontalface_alt2.xml")
以上代碼是模型文件加載到級(jí)聯(lián)分類器內(nèi),構(gòu)成一個(gè)檢測(cè)器對(duì)象。
gray?=?cv2.cvtColor(image,?cv2.COLOR_BGR2GRAY) ????faces?=?face_cascade.detectMultiScale(gray,?scaleFactor=1.3,?minNeighbors=4,?minSize=(30,?30),flags=cv2.CASCADE_SCALE_IMAGE)
以上代碼對(duì)圖片做灰度處理 ,然后利用上面構(gòu)成的檢測(cè)器用來(lái)探測(cè)圖片中的人臉。
vis?=?image.copy() ????for?x1,?y1,?x2,?y2?in?faces:?#?x2,y2?為x1,y1?的偏離位置 ????x2?=?x2?+?x1 ????y2?=?y2?+?y1 ????cv2.rectangle(vis,?(x1,?y1),?(x2,?y2),?(0,255,0),?2) ????print("Detection?:?x1:?{}?y1:?{}?x2:?{}?y2:?{}".format( ???????x1,?y1,?x2,?y2)) ????Detection?:?x1:?448?y1:?88?x2:?655?y2:?295
這里做了一個(gè)檢測(cè)結(jié)果圖片的copy,為了讓我更靈活的調(diào)測(cè)代碼:)。利用for循環(huán)展開檢測(cè)結(jié)果數(shù)據(jù),得到圖片檢測(cè)坐標(biāo),這里的坐標(biāo)和dlib的坐標(biāo)不同,它的x2和y2不是圖片的絕對(duì)位置,是相對(duì)于x1,y1的相對(duì)距離位置。
from?PIL?import?Image ????Image.fromarray(vis)
還是那熟悉的味道,利用PIL庫(kù)展示圖片,這里友情提示一下,cv2本身也自帶圖片顯示的功能,cv2.imshow("Image",image)。。。結(jié)果就是notebook執(zhí)行崩潰。。因?yàn)榫庉嬈鞑恢С謈v2的imshow。檢測(cè)如下圖:
3.2、OpenCV的DNN網(wǎng)絡(luò)檢測(cè)
opencv的DNN主要的算法出自論文《SSD: Single Shot MultiBox Detector》,使用ResNet-10作為骨干網(wǎng)。? ??? ? openCV3.3以上版本就有該分類器的模型的方式,openCV提供了兩種不同的模型。一種是16位浮點(diǎn)數(shù)的caffe人臉模型(5.4MB),另外一種是8bit量化后的tensorflow人臉模型(2.7MB)。量化是指比如可以用0~255表示原來(lái)32個(gè)bit所表示的精度,通過(guò)犧牲精度來(lái)降低每一個(gè)權(quán)值所需要占用的空間。通常情況深度學(xué)習(xí)模型會(huì)有冗余計(jì)算量,冗余性決定了參數(shù)個(gè)數(shù)。因此合理的量化網(wǎng)絡(luò)也可保證精度的情況下減小模型的存儲(chǔ)體積,不會(huì)對(duì)網(wǎng)絡(luò)的精度造成影響。具體可以看看深度學(xué)習(xí)fine-tuning的論文。通常這種操作可以稍微降低精度,提高速度,大大減少模型體積。 opencv的了3.4版本,主要增強(qiáng)了dnn模塊,特別是添加了對(duì)faster-rcnn的支持,并且?guī)в衞penCL加速,效果還不錯(cuò),基本上是向MTCNN看齊了。
實(shí)現(xiàn)過(guò)程:
print('version:',cv2.__version__) ?from?cv2?import?dnn
以上代碼首先檢測(cè)一下opencv版本,我們的版本結(jié)果是3.4。
#modelFile?=?'./models/opencv_face_detector_uint8.pb' ????#configFile?=?'./models/opencv_face_detector.pbtxt' ????#net?=?cv2.dnn.readNetFromTensorflow(modelFile,?configFile) ????#CAFF ????modelFile?=?'./model/res10_300x300_ssd_iter_140000.caffemodel' ????configFile?=?'./model/deploy.prototxt' ????net?=?cv2.dnn.readNetFromCaffe(configFile,modelFile)
這是兩種方式,基于caff和tf的兩種寫法,我一開始還是先做的tf的方式,結(jié)果失敗了,原因很簡(jiǎn)單。。。官方版本已經(jīng)升級(jí)了,不支持3.4的版本了,我也是找不到tf的3.4的模型文件和配置文件啊。 然后,我開始做caff的實(shí)現(xiàn),也是版本問(wèn)題,幸運(yùn)的是我在https://stackoverflow.com/questions/54660069/opencv-deep-learning-face-detection-assertion-error-in-function-cvdnnconvol 這里找到了問(wèn)題根源和對(duì)應(yīng)的模型文件和配置說(shuō)明。。。如果有興趣,可以打開看一下opencv在git上的deploy.prototxt文件和這里的文件,他們?cè)谂渲蒙嫌衛(wèi)ayer里面bottom數(shù)據(jù)的差異。又是個(gè)版本兼容問(wèn)題。 cv2.dnn.readNetFromCaffe方式讀取配置文件和模型文件,這樣就構(gòu)成一個(gè)檢測(cè)網(wǎng)絡(luò)。
inWidth?=?300 ????inHeight?=?300 ????confThreshold?=?0.5 ????image?=?cv2.imread('./face_x01.jpg') ????cols?=?image.shape[1] ????rows?=?image.shape[0]
上面代碼是加載輸入圖像并為圖像構(gòu)造輸入blob,設(shè)置像素為300 × 300。并且讀取圖像文件的參數(shù)。confidence是一個(gè)閥值,默認(rèn)為0.5,就是如果檢測(cè)區(qū)域結(jié)果數(shù)值(這里的數(shù)值應(yīng)該是一個(gè)數(shù)學(xué)距離)>0.5,確認(rèn)該區(qū)域?yàn)槿四槄^(qū)域。
blob?=?cv2.dnn.blobFromImage(cv2.resize(image,?(300,300)),?1.0,(300,300),?(104.0,?177.0,?123.0)) ????net.setInput(blob) ????detections?=?net.forward() ????perf_stats?=?net.getPerfProfile() ????print('Inference?time,?ms:?%.2f'?%?(perf_stats[0]?/?cv2.getTickFrequency()?*?1000)) ????Inference?time,?ms:?36.49
上面的代碼,將圖像調(diào)整到固定的300x300像素,然后將其規(guī)格化,設(shè)置檢測(cè)參數(shù),然后將圖片裝入檢測(cè)器,對(duì)其進(jìn)行檢測(cè)。perf_stats是用來(lái)做檢測(cè)時(shí)間的計(jì)算,最后檢測(cè)這個(gè)圖片,耗時(shí)36.49ms,也算不錯(cuò)的!
for?i?in?range(detections.shape[2]): ????confidence?=?detections[0,?0,?i,?2] ????if?confidence?>?confThreshold: ????????x1?=?int(detections[0,?0,?i,?3]?*?cols) ????????y1?=?int(detections[0,?0,?i,?4]?*?rows) ????????x2?=?int(detections[0,?0,?i,?5]?*?cols) ????????y2?=?int(detections[0,?0,?i,?6]?*?rows) ????????cv2.rectangle(image,?(x1,?y1),?(x2,?y2),?(0,?0,?255)) ????from?PIL?import?Image ????Image.fromarray(image)
上面代碼是將檢測(cè)結(jié)果進(jìn)行顯示,還是利用cv2.rectangle在圖像上描繪檢測(cè)到的區(qū)域。檢測(cè)結(jié)果是一組數(shù)據(jù)在detections.shape[2]內(nèi),每一組數(shù)據(jù)都是一個(gè)特征區(qū)域的計(jì)算結(jié)果,只有大于confidence的區(qū)域,才會(huì)被檢測(cè)判斷為人臉區(qū)域。 檢測(cè)結(jié)果圖如下:
AI
版權(quán)聲明:本文內(nèi)容由網(wǎng)絡(luò)用戶投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔(dān)相應(yīng)法律責(zé)任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實(shí)的內(nèi)容,請(qǐng)聯(lián)系我們jiasou666@gmail.com 處理,核實(shí)后本網(wǎng)站將在24小時(shí)內(nèi)刪除侵權(quán)內(nèi)容。
版權(quán)聲明:本文內(nèi)容由網(wǎng)絡(luò)用戶投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔(dān)相應(yīng)法律責(zé)任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實(shí)的內(nèi)容,請(qǐng)聯(lián)系我們jiasou666@gmail.com 處理,核實(shí)后本網(wǎng)站將在24小時(shí)內(nèi)刪除侵權(quán)內(nèi)容。