【火炉炼AI】机器学习055-使用LBP直方图建立人脸识别器
【火爐煉AI】機器學(xué)習(xí)055-使用LBP直方圖建立人臉識別器
(本文所使用的Python庫和版本號: Python 3.6, Numpy 1.14, scikit-learn 0.19, matplotlib 2.2 )
在我前面的博文【火爐煉AI】機器學(xué)習(xí)052-OpenCV構(gòu)建人臉鼻子眼睛檢測器中,講到了人臉檢測的方法和代碼實現(xiàn),但在很多實際場合,我們需要做的是人臉識別,即判斷圖片中的那張臉是張三還是李四,故而本篇文章我們來看看如何使用LBP直方圖來建立一個人臉識別器。
1. 局部二值模式簡介
局部二值模式(Local Binary Pattern, LBP)是一種用來描述圖像局部紋理特征的算子,其最大優(yōu)勢在于旋轉(zhuǎn)不變性,灰度不變性,能夠多分辨分析。局部紋理分析有很多潛在的應(yīng)用,比如工業(yè)表層檢測,遠程監(jiān)控,圖像分析等。
LBP的基本思想是:原始的LBP算子是3*3的窗口,以中心像素為閾值,將相鄰的8個像素的灰度值與中心像素進行比較,如果大于,則設(shè)為1,小于則為0,故而得到這9個像素的二值化圖,故而名稱為局部二值化,如下圖所示。從二值化圖的左邊中心點像素為起點,逆時針方向為正方形,按順序取該二值化數(shù)值,便得到圖中Pattern的二進制數(shù)值,此數(shù)值就是一個LBP編碼,此時,我們稱該中心像素點的LBP值為11110001。如果對一幅圖像中的所有像素點都計算LBP值,得到的就是這幅圖的LBP特征圖。
關(guān)于灰度不變性:很明顯,原始的局部圖中如果灰度值都同時增加一個值或同時減去一個值,便相當(dāng)于亮度增加或減少,但此時,得到的LBP編碼不變,故而稱為灰度不變性。需要注意的是:該灰度不變性僅僅適用于灰度值的單調(diào)變化。如下圖
上面的LBP算子有一個缺陷,它只覆蓋一個固定半徑范圍內(nèi)的小區(qū)域,這顯然不能滿足不同尺寸和頻率紋理的需要,故而有人對其進行改進,將3*3領(lǐng)域擴展到任意領(lǐng)域,并用圓形領(lǐng)域代替正方形領(lǐng)域,如下圖為以中心像素點為圓心,R為半徑,在圓上均勻的選取P個點作為采樣點的情況。
上圖中,R的大小決定了圓的大小,反映了二維空間的尺度;而P的大小決定了采樣點數(shù),反映了角度空間的分辨率。同樣的,我們還可以改變R和P的值,實現(xiàn)不同的尺度和角度分辨率(如下圖)。這也是以后“多分辨率分析”的理論基礎(chǔ)。
上面的LBP算子雖然能夠?qū)崿F(xiàn)多分辨率,但卻不是旋轉(zhuǎn)不變性,圖像的旋轉(zhuǎn)會得到不同的LBP值,故有人提出了具有旋轉(zhuǎn)不變性的LBP算子,即不斷旋轉(zhuǎn)圓形領(lǐng)域得到一系列初始定義的LBP值,取其最小值作為該領(lǐng)域的LBP值。
在LBP的應(yīng)用中,比如人臉識別,紋理分析中,我們一般不將LBP圖譜作為特征向量用于分類識別,而是采用LBP特征圖的統(tǒng)計直方圖來作為特征向量。
使用LBP直方圖來進行特征提取的步驟一般為:
1) 首先將檢測窗口劃分為16×16的小區(qū)域(cell)
2) 對于每個cell中的一個像素,將相鄰的8個像素的灰度值與其進行比較,若周圍像素值大于中心像素值,則該像素點的位置被標記為1,否則為0。這樣,3*3鄰域內(nèi)的8個點經(jīng)比較可產(chǎn)生8位二進制數(shù),即得到該窗口中心像素點的LBP值
3) 然后計算每個cell的直方圖,即每個數(shù)字(假定是十進制數(shù)LBP值)出現(xiàn)的頻率;然后對該直方圖進行歸一化處理
4)最后將得到的每個cell的統(tǒng)計直方圖進行連接成為一個特征向量,也就是整幅圖的LBP紋理特征向量;然后便可利用SVM或者其他機器學(xué)習(xí)算法進行分類了。
關(guān)于LBP的深入理論,可以參考博文:LBP(局部二值模式)特征提取原理 和 局部二值模式(Local Binary Patterns)進行紋理分類
2. 準備數(shù)據(jù)集
本項目所用的數(shù)據(jù)集是臉部數(shù)據(jù)集的一個子集,此處我只選擇三個人的臉部圖片來進行測試。數(shù)據(jù)集有兩部分,一個train的文件夾中有三個子文件夾,每個子文件夾代表一個人的臉部圖片,test的文件夾只含有各種人臉圖片,沒有子文件夾。所以首先我們需要將這些圖片加載到內(nèi)存中,下面定義一個函數(shù)來加載圖片。
# 定義一個函數(shù)來加載圖片數(shù)據(jù)集 def load_train_set(imgs_folder,face_cascade):'''從imgs_folder中加載圖片數(shù)據(jù)和標記,注意imgs_folder中包含有多個子文件夾,每個子文件夾的名稱就是label'''folders=glob(os.path.join(imgs_folder,'*'))imgs_paths=[][imgs_paths.extend(glob(os.path.join(folder, '*.*'))) for folder in folders]face_imgs=[]labels=[]# 對每一張圖片都檢測畫面上的人臉for img_path in imgs_paths:image = cv2.imread(img_path, 0) label=os.path.split(img_path)[0]img_folder=os.path.split(img_path)[0]faces = face_cascade.detectMultiScale(image, 1.1, 2, minSize=(100,100))for (x, y, w, h) in faces:face_imgs.append(image[y:y+h, x:x+w])labels.append(os.path.split(img_folder)[1])# 此處有點不合理,本數(shù)據(jù)集中每張圖片只有一個人臉,故而可以用這個方式,# 如果有多個不同人的臉,則不能用折沖方式。# 將labels轉(zhuǎn)換為數(shù)字label_encoder=LabelEncoder()encode_labels=label_encoder.fit_transform(labels)return face_imgs, encode_labels, label_encoder,labels 復(fù)制代碼測試下上面的函數(shù)是否正常,且顯示下加載的臉部照片
# 測試上面函數(shù)是否正常 face_cascade=cv2.CascadeClassifier('E:\PyProjects\DataSet\FireAI\cascade_files/haarcascade_frontalface_alt.xml') face_imgs, labels, label_encoder,labels=load_train_set('E:\PyProjects\/DataSet\FireAI\/faces_dataset/train',face_cascade) print(len(face_imgs)) # 有53張臉,但是檢測得到56個結(jié)果,顯然有幾張圖片中檢測了多張臉 # 顯示任一張人臉 # 由于cv2讀取的是BGR,而plt是RGB,故而需要轉(zhuǎn)化一下 plt.imshow(face_imgs[3],cmap='gray') 復(fù)制代碼從打印的結(jié)果可以看出,多了三張圖片,說明有三張不是臉部照片的圖片混入,故而需要找出來刪除。定義一個函數(shù)來找出錯誤圖片
def find_false_faces(face_imgs):'''將所有臉部照片顯示出來,如果發(fā)現(xiàn)有錯誤的,按d鍵,記錄下錯誤的臉部照片'''need_del_ids=[]for idx,face in enumerate(face_imgs):cv2.namedWindow('check', cv2.WINDOW_NORMAL)cv2.resizeWindow('check', 500, 500)cv2.imshow('check', face)key = cv2.waitKey(0)if key==27: # 如果輸入時Esc,則退出循環(huán)print('esc to exit')breakelif key==100: # 如果輸入d鍵,則記錄該臉對應(yīng)的idneed_del_ids.append(idx)cv2.destroyAllWindows()print('finished...')return need_del_ids 復(fù)制代碼故而需要從原始數(shù)據(jù)集中刪除這三張圖片以及對應(yīng)的label信息
# 從數(shù)據(jù)集中刪除這三張照片對應(yīng)的信息 face_imgs=np.delete(np.array(face_imgs), need_del_ids, axis=0) encode_labels=np.delete(np.array(encode_labels), need_del_ids, axis=0) labels=np.delete(np.array(labels), need_del_ids,axis=0) print(face_imgs.shape) # 53張圖沒錯,元素已經(jīng)變成了np.ndarray,故而只有行 復(fù)制代碼3. 構(gòu)建LBP直方圖識別器
此處的LBP直方圖識別器相當(dāng)于一個分類模型,cv2已經(jīng)幫我們封裝好了這個分類模型,我們只需要調(diào)用即可。
# 構(gòu)建createLBPHFaceRecognizer分類模型 from cv2.face import LBPHFaceRecognizer_create recognizer=LBPHFaceRecognizer_create() recognizer.train(face_imgs, encode_labels) # 模型訓(xùn)練 復(fù)制代碼一旦人臉識別器模型訓(xùn)練好之后,就可以用來進行人臉識別了,下面看看識別新圖片的人臉結(jié)果。
# 用訓(xùn)練好的模型預(yù)測新照片 def predict_imgs(new_imgs_folder, face_cascade,recognizer,label_encoder):'''用訓(xùn)練好的人臉識別器來識別人臉'''img_paths=glob(new_imgs_folder+'/*.*')predicted_imgs=[]for img_path in img_paths:image=cv2.imread(img_path)gray=cv2.cvtColor(image,cv2.COLOR_BGR2GRAY)faces=face_cascade.detectMultiScale(gray,1.1, 2, minSize=(100,100))for (x, y, w, h) in faces:cv2.rectangle(image,(x,y),(x+w,y+h),(0,0,255),3)predicted_index, conf = recognizer.predict(gray[y:y+h, x:x+w])predicted_label=label_encoder.inverse_transform([predicted_index])[0]cv2.putText(image, predicted_label,(x,y-20), cv2.FONT_HERSHEY_SIMPLEX, 2, (0,0,255), 3)predicted_imgs.append(image)return predicted_imgs 復(fù)制代碼得到的結(jié)果分別為:
當(dāng)然,這個識別器只能識別訓(xùn)練圖片中已經(jīng)有的人臉,對于訓(xùn)練集中沒有的人臉,它會預(yù)測不準確。比如,拿鳳姐的圖片來預(yù)測一下試試。
估計鳳姐這張照片和Person3長的比較像,所以本模型將其預(yù)測為Person3
########################小**********結(jié)###############################
1,LBP直方圖模型可以快速訓(xùn)練并快速識別,在人臉識別領(lǐng)域中有著比較廣泛的應(yīng)用。
#################################################################
注:本部分代碼已經(jīng)全部上傳到(我的github)上,歡迎下載。
參考資料:
1, Python機器學(xué)習(xí)經(jīng)典實例,Prateek Joshi著,陶俊杰,陳小莉譯
總結(jié)
以上是生活随笔為你收集整理的【火炉炼AI】机器学习055-使用LBP直方图建立人脸识别器的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: ProxySQL 配置详解及读写分离(+
- 下一篇: 【火炉炼AI】机器学习050-提取图像的