【火爐煉AI】機器學習055-使用LBP直方圖創建人臉識別器

【火爐煉AI】機器學習055-使用LBP直方圖創建人臉識別器

(本文所使用的Python庫和版本號: Python 3.6, Numpy 1.14, scikit-learn 0.19, matplotlib 2.2 )html

在我前面的博文【火爐煉AI】機器學習052-OpenCV構建人臉鼻子眼睛檢測器中,講到了人臉檢測的方法和代碼實現,但在不少實際場合,咱們須要作的是人臉識別,即判斷圖片中的那張臉是張三仍是李四,故而本篇文章咱們來看看如何使用LBP直方圖來創建一我的臉識別器。git


1. 局部二值模式簡介

局部二值模式(Local Binary Pattern, LBP)是一種用來描述圖像局部紋理特徵的算子,其最大優點在於旋轉不變性,灰度不變性,可以多分辨分析。局部紋理分析有不少潛在的應用,好比工業表層檢測,遠程監控,圖像分析等。github

LBP的基本思想是:原始的LBP算子是3*3的窗口,以中心像素爲閾值,將相鄰的8個像素的灰度值與中心像素進行比較,若是大於,則設爲1,小於則爲0,故而獲得這9個像素的二值化圖,故而名稱爲局部二值化,以下圖所示。從二值化圖的左邊中心點像素爲起點,逆時針方向爲正方形,按順序取該二值化數值,便獲得圖中Pattern的二進制數值,此數值就是一個LBP編碼,此時,咱們稱該中心像素點的LBP值爲11110001。若是對一幅圖像中的全部像素點都計算LBP值,獲得的就是這幅圖的LBP特徵圖。算法

關於灰度不變性:很明顯,原始的局部圖中若是灰度值都同時增長一個值或同時減去一個值,便至關於亮度增長或減小,但此時,獲得的LBP編碼不變,故而稱爲灰度不變性。須要注意的是:該灰度不變性僅僅適用於灰度值的單調變化。以下圖app

上面的LBP算子有一個缺陷,它只覆蓋一個固定半徑範圍內的小區域,這顯然不能知足不一樣尺寸和頻率紋理的須要,故而有人對其進行改進,將3*3領域擴展到任意領域,並用圓形領域代替正方形領域,以下圖爲以中心像素點爲圓心,R爲半徑,在圓上均勻的選取P個點做爲採樣點的狀況。機器學習

上圖中,R的大小決定了圓的大小,反映了二維空間的尺度;而P的大小決定了採樣點數,反映了角度空間的分辨率。一樣的,咱們還能夠改變R和P的值,實現不一樣的尺度和角度分辨率(以下圖)。這也是之後「多分辨率分析」的理論基礎。函數

上面的LBP算子雖然可以實現多分辨率,但卻不是旋轉不變性,圖像的旋轉會獲得不一樣的LBP值,故有人提出了具備旋轉不變性的LBP算子,即不斷旋轉圓形領域獲得一系列初始定義的LBP值,取其最小值做爲該領域的LBP值。學習

在LBP的應用中,好比人臉識別,紋理分析中,咱們通常不將LBP圖譜做爲特徵向量用於分類識別,而是採用LBP特徵圖的統計直方圖來做爲特徵向量。測試

使用LBP直方圖來進行特徵提取的步驟通常爲:編碼

1) 首先將檢測窗口劃分爲16×16的小區域(cell)

2) 對於每一個cell中的一個像素,將相鄰的8個像素的灰度值與其進行比較,若周圍像素值大於中心像素值,則該像素點的位置被標記爲1,不然爲0。這樣,3*3鄰域內的8個點經比較可產生8位二進制數,即獲得該窗口中心像素點的LBP值

3) 而後計算每一個cell的直方圖,即每一個數字(假定是十進制數LBP值)出現的頻率;而後對該直方圖進行歸一化處理

4)最後將獲得的每一個cell的統計直方圖進行鏈接成爲一個特徵向量,也就是整幅圖的LBP紋理特徵向量;而後即可利用SVM或者其餘機器學習算法進行分類了。

關於LBP的深刻理論,能夠參考博文:LBP(局部二值模式)特徵提取原理局部二值模式(Local Binary Patterns)進行紋理分類


2. 準備數據集

本項目所用的數據集是臉部數據集的一個子集,此處我只選擇三我的的臉部圖片來進行測試。數據集有兩部分,一個train的文件夾中有三個子文件夾,每一個子文件夾表明一我的的臉部圖片,test的文件夾只含有各類人臉圖片,沒有子文件夾。因此首先咱們須要將這些圖片加載到內存中,下面定義一個函數來加載圖片。

# 定義一個函數來加載圖片數據集
def load_train_set(imgs_folder,face_cascade):
    ''' 從imgs_folder中加載圖片數據和標記,注意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])
            # 此處有點不合理,本數據集中每張圖片只有一我的臉,故而能夠用這個方式,
            # 若是有多個不一樣人的臉,則不能用折衝方式。
    # 將labels轉換爲數字
    label_encoder=LabelEncoder()
    encode_labels=label_encoder.fit_transform(labels)
    return face_imgs, encode_labels, label_encoder,labels
複製代碼

測試下上面的函數是否正常,且顯示下加載的臉部照片

# 測試上面函數是否正常
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個結果,顯然有幾張圖片中檢測了多張臉
# 顯示任一張人臉
# 因爲cv2讀取的是BGR,而plt是RGB,故而須要轉化一下
plt.imshow(face_imgs[3],cmap='gray')
複製代碼

從打印的結果能夠看出,多了三張圖片,說明有三張不是臉部照片的圖片混入,故而須要找出來刪除。定義一個函數來找出錯誤圖片

def find_false_faces(face_imgs):
    ''' 將全部臉部照片顯示出來,若是發現有錯誤的,按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,則退出循環
            print('esc to exit')
            break
        elif key==100: # 若是輸入d鍵,則記錄該臉對應的id
            need_del_ids.append(idx)
    cv2.destroyAllWindows()
    print('finished...')
    return need_del_ids
複製代碼

故而須要從原始數據集中刪除這三張圖片以及對應的label信息

# 從數據集中刪除這三張照片對應的信息
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張圖沒錯,元素已經變成了np.ndarray,故而只有行
複製代碼

3. 構建LBP直方圖識別器

此處的LBP直方圖識別器至關於一個分類模型,cv2已經幫咱們封裝好了這個分類模型,咱們只須要調用便可。

# 構建createLBPHFaceRecognizer分類模型
from cv2.face import LBPHFaceRecognizer_create
recognizer=LBPHFaceRecognizer_create()
recognizer.train(face_imgs, encode_labels) # 模型訓練
複製代碼

一旦人臉識別器模型訓練好以後,就能夠用來進行人臉識別了,下面看看識別新圖片的人臉結果。

# 用訓練好的模型預測新照片
def predict_imgs(new_imgs_folder, face_cascade,recognizer,label_encoder):
    ''' 用訓練好的人臉識別器來識別人臉'''
    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
複製代碼

獲得的結果分別爲:

固然,這個識別器只能識別訓練圖片中已經有的人臉,對於訓練集中沒有的人臉,它會預測不許確。好比,拿鳳姐的圖片來預測一下試試。

估計鳳姐這張照片和Person3長的比較像,因此本模型將其預測爲Person3

########################小**********結###############################

1,LBP直方圖模型能夠快速訓練並快速識別,在人臉識別領域中有着比較普遍的應用。

#################################################################


注:本部分代碼已經所有上傳到(個人github)上,歡迎下載。

參考資料:

1, Python機器學習經典實例,Prateek Joshi著,陶俊傑,陳小莉譯

相關文章
相關標籤/搜索