人臉識別-PCA特徵臉(大BOSS)
文章目錄
寫在前面
你好,我是禪墨!好久,不見!
在忙了好久的各科考試之後,我終於閒下來了。
自從小凱上次輸給我K210之後,一心想搞我,想要報仇。
小凱:阿墨,我給你說啊,我XX不服,給你三個小時,你給我做出來一個人臉識別,識別咱兩個班一共57人,正確率不能低於96%,我賭100塊錢!!!你得在我的監視下完成!
禪墨:確定?我怕你後悔!
小凱:確定!後悔,不存在的!你搞吧!!!小樣!
思考了片刻決定用PCA實現!
PCA特徵臉
PCA原理
PCA全名爲主成分分析,其主要目的就是尋找一個矩陣,然後把原來的一組帶有相關性的矩陣映射到尋找到的那個矩陣中,達到降維的目的。一般的,如果我們有M個N維向量,想將其變換爲由R個N維向量表示的新空間中,那麼首先將R個基按行組成矩陣A,然後將向量按列組成矩陣B,那麼兩矩陣的乘積AB就是變換結果,其中AB的第m列爲A中第m列變換後的結果。 這句話就相當於找到了一個R行N列矩陣,然後乘一個N行M列矩陣,這樣就得到了一個R行M列矩陣(其中R<=N),達到降維的目的。其中M和N的含義爲,M可以代表樣本個數,而N代表每個樣本的特徵個數,所以最終結果就是把原來N個特徵變爲了R個特徵,達到降維目的。
算法解析
1、構建一個樣本集合, 可以看做是一個N行M列的矩陣,也就是有M個樣本,每個樣本有N個特徵。其中是一個向量。
2、0均值化,爲了便於計算方差的時候需要減去均值,所以如果本身樣本是零均值的,就方便計算。
,這個是計算均值在python中可以使用
m = T.mean(axis = 1)
進行計算,其中axis = 1代表按行求均值。
然後 這個相當於把每個樣本都減去均值,這樣之後就相當於做了0均值化。
3、計算投影矩陣(就是相當於上面的那個R行M列矩陣)
這個投影矩陣其實就是由矩陣的特徵向量構成,但是由於大多數情況的維度太大(是N行N列矩陣,如果是一張圖片的話N就代表像素點個數,所以是相當大的),所以這個時候就利用數學的小技巧轉化爲先求的特徵向量矩陣V,其中V的每一列是一個特徵向量,那麼V是一個M行M列的矩陣,然後我們再從V中取出前R個最大特徵值對應的特徵向量,所以V就變成了M行R列矩陣,然後,那麼這個C矩陣就是計算出的投影矩陣,C爲一個N行R列的矩陣。
4、把原來樣本進行投影
第三步我們得到了一個N行R列的矩陣C,其中每一列是一個特徵向量,但是我們在講PCA原理的時候我們需要一個R行N列的矩陣,每一行是一個特徵向量,所以我們可以使用,所以我們投影后的樣本變爲 其中P就是一個R行M列的矩陣,可以看出已經達到了降維的目的。
特徵臉的實現
特徵臉就是我們上面求得的C矩陣,所謂的基於特徵臉進行的人臉識別,就是先把人臉映射到一個低緯空間,然後再計算映射後的臉之間的距離,把距離最近的兩個特徵臉歸爲同一個人的臉。
所以特徵臉的步驟爲:
- 加載訓練集中的臉,轉爲一個M行N列矩陣T
- 對T進行均值化
- 找到T的投影矩陣C
- 計算投影后的矩陣P
- 設置簡單的UI界面,進行圖片選擇與識別
- 加載一個測試圖片,並利用C矩陣也把其投影爲test_P
- 計算test_P和P中每個樣本的距離,最近的就是結果
- 通過結果檢索對應的列表,找到並輸出人名
Python程序解析
createDatabase函數
創建一個存放所有圖片的數據庫,具體的步驟看程序註釋
def createDatabase(path):
# 查看路徑下所有文件
TrainFiles = os.listdir(path)
# 計算有幾個文件(圖片命名都是以 序號.jpg方式)
Train_Number = len(TrainFiles)
T = []
# 把所有圖片轉爲1-D並存入T中
for i in range(0,Train_Number):
ip = path+'/'+str(i)+'.jpg'
image = cv.imread(ip,cv.IMREAD_GRAYSCALE)
image=cv.resize(image,img_size)
# 轉爲1-D
image = image.reshape(image.size,1)
T.append(image)
T = np.array(T)
# 不能直接T.reshape(T.shape[1],T.shape[0]) 這樣會打亂順序,
T = T.reshape(T.shape[0],T.shape[1])
return np.mat(T).T
eigenfaceCore函數
特徵臉核心處理函數,對T進行數據處理操作,包括進行均值化,計算特徵向量和特徵值。
def eigenfaceCore(T):
# 把均值變爲0 axis = 1代表對各行求均值
m = T.mean(axis = 1)
A = T-m
L = (A.T)*(A)
# 計算AT *A的 特徵向量和特徵值V是特徵值,D是特徵向量
# L = np.cov(A,rowvar = 0)
V, D = np.linalg.eig(L)
L_eig = []
for i in range(A.shape[1]):
L_eig.append(D[:,i])
L_eig = np.mat(np.reshape(np.array(L_eig),(-1,len(L_eig))))
# 計算 A *AT的特徵向量
eigenface = A * L_eig
return eigenface,m,A
recognize函數
識別器函數:找到投影矩陣C,計算投影后的矩陣樣本P,加載一個測試圖片投影爲test-p,然後在總樣本P中進行比對,找到與test-p距離最近的樣本,即爲比對結果
def recognize(testImage, eigenface,m,A):
_,trainNumber = np.shape(eigenface)
# 投影到特徵臉後的
projectedImage = eigenface.T*(A)
# 可解決中文路徑不能打開問題(相當於英文路徑下imread)
testImageArray = cv.imdecode(np.fromfile(testImage,dtype=np.uint8),cv.IMREAD_GRAYSCALE)
# 轉爲1-D
testImageArray=cv.resize(testImageArray,img_size)
testImageArray = testImageArray.reshape(testImageArray.size,1)
testImageArray = np.mat(np.array(testImageArray))
differenceTestImage = testImageArray - m
projectedTestImage = eigenface.T*(differenceTestImage)
distance = []
for i in range(0, trainNumber):
q = projectedImage[:,i]
temp = np.linalg.norm(projectedTestImage - q)
distance.append(temp)
minDistance = min(distance)
index = distance.index(minDistance)
cv.imshow("recognize result",cv.imread('./TrainDatabase'+'/'+str(index+1 )+'.jpg',cv.IMREAD_GRAYSCALE))
cv.waitKey()
return index+1
構建可視化界面
製作一個簡單的UI界面,通過簡單的按鈕選擇需要識別人臉的圖片,點擊開始識別,輸出結果,檢索列表輸出人名
def gui():
root = tk.Tk()
root.title("pca face")
#點擊選擇圖片時調用
def select():
filename = tkinter.filedialog.askopenfilename()
if filename != '':
s=filename
# jpg圖片文件名 和 路徑。
im=Image.open(s)
tkimg=ImageTk.PhotoImage(im)
# 執行此函數之前, Tk() 必須已經實例化。
l.config(image=tkimg)
btn1.config(command=lambda : example(filename))
btn1.config(text = "開始識別")
btn1.pack()
# 重新繪製
root.mainloop()
# 顯示圖片的位置
l = tk.Label(root)
l.pack()
btn = tk.Button(root,text="選擇識別的圖片",command=select)
btn.pack()
btn1 = tk.Button(root) # 開始識別按鈕,剛開始不顯示
root.mainloop()
if __name__ == "__main__":
gui()
圖片問題
我覺得有必要吐槽一下給的570張圖片:jpg , png, jpeg, pmg各種格式各種分辨率應有盡有,真的是搞死我了!(一寸人臉照一般爲92*112) 不過還好,只有七八十張,其他的就是正常的jpg格式,怎麼辦呢,我不可能說一張張進行裁剪,所以 就寫個程序,批量裁剪吧。我直接給出核心代碼,比較簡單,就不廢話說明了
for (path, dirnames, filenames) in os.walk(input_dir):
for filename in filenames:
if filename.endswith('.jpg'):
print('正在處理第 %s 張圖片' % index)
img_path = path + '/' + filename
print(img_path)
img = cv.imdecode(np.fromfile(img_path,dtype=np.uint8),-1)
new_img = cv.resize(img, (width, height))
imwritedir = output_dir + '/' + str(499+index) + '.jpg'
print(imwritedir)
cv.imwrite(imwritedir, new_img)
index += 1
key = cv.waitKey(30) & 0xff
if key == 27:
sys.exit(0)
用過opencv的應該知道,讀取中文路徑會報錯,怎麼解決呢,也很簡單
cv.imdecode(np.fromfile(img_path,dtype=np.uint8),-1)
用這個代替imread 完美!
做些優化
由於部分照片的問題,背景太花,所以做些調整,就是先把人臉裁剪出來。
這裏就是用到上篇博客基於OpenCV的人臉及笑臉檢測
就是檢測出來,保存人臉尺寸照片
寫在後面
計時結束:2:58:56!
哈哈哈,小凱又輸了,收錢去!
憑良心說,這一篇博文乾貨滿滿。
簡單的總結:
1.OpenCV中文路徑解決方法
2.批量進行圖片的簡單裁剪
3.解析PCA算法原理
4.Python程序實現
歡迎關注:禪墨雲
公衆號: