OpenCV+python 人臉識別
首先給大家推薦一本書:機器學習演算法原理與程式設計實踐
本文內容全部轉載於書中,相當於一個讀書筆記了吧
緒論
1992年麻省理工學院通過實驗對比了基於結構特徵的方法與基於模版匹配的方法,發現模版匹配的方法要優於基於特徵的方法。
以支援向量機為代表的統計學習理論在隨後被應用到了人臉識別與確認中去。但是由於演算法執行效率問題,很快被一種新的演算法替代了。這就是2001年康柏研究院提出的基於簡單矩形特徵和AdaBoost的實時人臉檢測系統。該方法的主要貢獻包括:
1.可以快速計算簡單矩形特徵作為人臉影象特徵
2.基於AdaBoost將大量弱分類器進行組合形成強分類器的學習方法。
3.採用了級聯(Cascade)技術提高檢測速度。目前,基於這種人臉/非人臉學習的策略已經能夠實現準實時的多姿態人臉檢測與跟蹤,這為後端的人臉識別提供了良好的基礎。
人臉檢測
人臉檢測主要用於人臉識別的預處理,即在影象中標註出人臉所處的位置和大小。為了能夠確定圖片中包含一張或幾張人臉,首先要確定人臉的通用結構。我們都有:眼鏡、鼻子,前額,顴骨和嘴,所有這些構成了一張通用的人臉結構。下圖的特徵元件分別標識了上述結構。
組合這些特徵就可以得到一張近似的人臉:
人臉檢測的主流方法是AdaBoost,它是一種用來分類的方法,通過把一些比較弱的分類方法合在一起,可以組合出新的更強的分類器。AdaBoost有一個迭代的過程,為了快速處理,在每次迭代中,我們僅僅快速地排除圖片中不屬於人臉的區域,保留那些我們還不確定的區域。在每次迭代中,我們都提高了對圖片中人臉定位的概率,直到做出最終的決定。換句話說,不同於確定圖片中人臉的位置,我們選擇的排除圖片中不包含人臉位置,因為排除演算法的運算速度更快。我們稱這個過程為級聯過程。
OpenCV中常用的特徵分類器有兩類:Haar特徵和LBP特徵
在OpenCV中使用Haar特徵檢測人臉,那麼需要使用OpenCV提供的xml檔案(級聯表)在sources\data目錄下。這張級聯表有一個訓練好的AdaBoost訓練集。首先要採用樣本的Haar特徵訓練分類器,從而得到一個級聯的AdaBoost分類器。訓練的方式包含兩個方面:
1.正例樣本,即待檢測的目標樣本
2.反例樣本,即其他任意的圖片
然後將這些圖片統一縮放為相同的尺寸,這個過程就是歸一化。最後統計出分類結果。
實現效果:
程式碼:
# -*- coding: utf-8 -*-
"""
Created on Wed Jun 22 20:59:21 2016
@author: Administrator
"""
# -*- coding: utf-8 -*
import numpy as np
import cv2
#要使用Haar cascade實現,僅需要把庫修改為lbpcascade_frontalface.xml
face_cascade = cv2.CascadeClassifier('lbpcascade_frontalface.xml')
img = cv2.imread('woman.jpg')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 識別輸入圖片中的人臉物件.返回物件的矩形尺寸
# 函式原型detectMultiScale(gray, 1.2,3,CV_HAAR_SCALE_IMAGE,Size(30, 30))
# gray需要識別的圖片
# 1.03:表示每次影象尺寸減小的比例
# 5:表示每一個目標至少要被檢測到4次才算是真的目標(因為周圍的畫素和不同的視窗大小都可以檢測到人臉)
# CV_HAAR_SCALE_IMAGE表示不是縮放分類器來檢測,而是縮放影象,Size(30, 30)為目標的最小最大尺寸
# faces:表示檢測到的人臉目標序列
faces = face_cascade.detectMultiScale(gray, 1.03, 5)
for (x,y,w,h) in faces:
if w+h>200:#//針對這個圖片畫出最大的外框
img2 = cv2.rectangle(img,(x,y),(x+w,y+h),(255,255,255),4)
roi_gray = gray[y:y+h, x:x+w]
roi_color = img[y:y+h, x:x+w]
cv2.imshow('img',img)
cv2.waitKey(0)
cv2.destroyAllWindows()
cv2.imwrite("head.jpg", img) # 儲存圖片
AdaBoost演算法概述
Haar級聯檢測和LBP檢測都是使用AdaBoost演算法實現的。AdaBoost演算法是Adaptive Boosting演算法的縮寫,是Boosting演算法的改進版本。AdaBoost與其前身Boost演算法都是從決策樹發展出來的一種演算法,其演算法思想是針對同一個訓練集樣本的不同特徵,分別訓練出不同的弱分類器,然後把這些弱分類器級聯起來,構成一個最終分類器,即強分類器。
從結構上講,AdaBoost與其他的機器學習演算法不同,該演算法可以分為兩層,第一層是AdaBoost主演算法,第二層是其他的二分類演算法,所以該演算法是一種複合型演算法。第二層最常用的是單層決策樹。當然也可以是其他任何二分類演算法。例如梯度下降演算法、SVM等。
人臉識別
目前最常用的自動人臉識別技術仍舊是特徵臉提取方法。特徵臉方法是從整體上對人臉識別的方法:一種面部影象可以表示為從高維影象空間對映到地位空間的一個點。這樣可以使得分類邊的更加容易。
降維
一幅影象只能表示為一個物件,對於w x h的灰度影象,只能表示為w*h維的向量,那麼100*100畫素大小的影象就需要10000維的向量空間。對於一副人臉影象,顯然在維數空間中只有少量畫素對我們有用。所以可以降維,矩陣可以近似的表示為一個特徵值和特徵向量的乘積,如果我們能夠提取出高維向量中某些特有的特徵或者相關變數,就能用一個低維空間的向量近似地表示這個高維向量。對於這個高維向量,只有高喊最多資訊的那些維上的資料才有意義,不重要的維可以在計算中忽略,並且降維之後的低維向量不會損失掉特徵間的差異性。這就是主成份分析的思想(PCA),1901年就由皮爾遜釋出了基本原理:
PCA人臉識別演算法
PCA人臉識別演算法的實現步驟如下:
1.首先把所有的訓練圖片集的每張圖片都轉換為行向量的形式
2.計算向量集的PCA子空間,並得到特徵值和特徵向量及均值
3.將訓練集的圖片與對應的標籤都投影到這個PCA子空間,行程一個投影矩陣
4.匯入待識別的影象,並進行向量化,也投影到這個PCA子空間
5.計算PCA投影后的訓練集向量與待識別圖片投影后向量的距離,並找出最接近的那個
# -*- coding: utf-8 -*-
from numpy import *
import numpy as np
import sys,os
import copy
import cv2
import PIL.Image as Image
import matplotlib.pyplot as plt
class Eigenfaces(object):
def __init__(self):
self.eps = 1.0e-16
self.X = []
self.y = []
self.Mat=[]
self.eig_v = 0
self.eig_vect = 0
self.mu = 0
self.projections = []
self.dist_metric=0
def loadimgs(self,path): # 載入圖片資料集
classlabel = 0
for dirname, dirnames, filenames in os.walk(path):
for subdirname in dirnames:
sub_path = os.path.join(dirname, subdirname)
for filename in os.listdir(sub_path):
im = Image.open(os.path.join(sub_path, filename))
im = im.convert("L") #資料轉換為long型別
self.X.append(np.asarray(im, dtype=np.uint8))
self.y.append(classlabel)
classlabel += 1
# 將圖片變為行向量 # 生成圖片矩陣
def genRowMatrix(self):
self.Mat = np.empty((0, self.X[0].size), dtype=self.X[0].dtype)
for row in self.X:
self.Mat = np.vstack((self.Mat, np.asarray(row).reshape(1,-1)))
# 計算特徵臉
def PCA(self, pc_num =0):
self.genRowMatrix()
[n,d] = shape(self.Mat)
if ( pc_num <= 0) or ( pc_num>n): pc_num = n
self.mu = self.Mat.mean(axis =0)
self.Mat -= self.mu
if n>d:
XTX = np.dot (self.Mat.T,self.Mat)
[ self.eig_v , self.eig_vect ] = linalg.eigh (XTX)
else :
XTX = np.dot(self.Mat,self.Mat.T)
[ self.eig_v , self.eig_vect ] = linalg.eigh (XTX)
self.eig_vect = np.dot(self.Mat.T, self.eig_vect)
for i in xrange(n):
self.eig_vect[:,i] = self.eig_vect[:,i]/linalg.norm(self.eig_vect[:,i])
idx = np.argsort(-self.eig_v)
self.eig_v = self.eig_v[idx]
self.eig_vect = self.eig_vect[:,idx ]
self.eig_v = self.eig_v[0:pc_num ].copy () # select only pc_num
self.eig_vect = self.eig_vect[:,0:pc_num].copy ()
def compute(self):
self.PCA()
for xi in self.X:
self.projections.append(self.project(xi.reshape(1,-1)))
def distEclud(self, vecA, vecB): # 歐氏距離
return linalg.norm(vecA-vecB)+self.eps
def cosSim(self, vecA, vecB): # 夾角餘弦
return (dot(vecA,vecB.T)/((linalg.norm(vecA)*linalg.norm(vecB))+self.eps))[0,0]
# 對映
def project(self,XI):
if self.mu is None: return np.dot(XI,self.eig_vect)
return np.dot(XI-self.mu, self.eig_vect)
#預測最接近的特徵臉
def predict(self,XI):
minDist = np.finfo('float').max
minClass = -1
Q = self.project(XI.reshape(1,-1))
for i in xrange(len(self.projections)):
dist = self.dist_metric(self.projections[i], Q)
if dist < minDist:
minDist = dist
minClass = self.y[i]
return minClass
# 生成特徵臉
def subplot(self,title, images):
fig = plt.figure()
fig.text(.5, .95, title, horizontalalignment='center')
for i in xrange(len(images)):
ax0 = fig.add_subplot(4,4,(i+1))
plt.imshow(asarray(images[i]), cmap="gray")
plt.xticks([]), plt. yticks([]) # 隱藏 X Y 座標
plt.show()
# 歸一化
def normalize(self, X, low, high, dtype=None):
X = np.asarray(X)
minX, maxX = np.min(X), np.max(X)
X = X - float(minX)
X = X / float((maxX - minX))
X = X * (high-low)
X = X + low
if dtype is None:
return np.asarray(X)
return np.asarray(X, dtype=dtype)
'''
# 重構
def reconstruct(self,W, Y, mu=None):
if mu is None: return np.dot(Y,W.T)
return np.dot(Y, W.T) + mu
# 從外部資料計算投影
def out_project(self,W,XI,mu):
if mu is None: return np.dot(XI,W)
return np.dot(XI-mu, W)
'''
生成特徵臉:
程式碼:
# -*- coding: utf-8 -*-
from numpy import *
import sys,os
from pca import *
reload(sys)
sys.setdefaultencoding('utf-8')
ef = Eigenfaces()
ef.dist_metric=ef.distEclud
ef.loadimgs("orl_faces/")
ef.compute()
E = []
X = mat(zeros((10,10304)))
for i in xrange(16):
X = ef.Mat[i*10:(i+1)*10,:].copy()
# X = ef.normalize(X.mean(axis =0),0,255)
X = X.mean(axis =0)
imgs = X.reshape(112,92)
E.append(imgs)
ef.subplot(title="AT&T Eigen Facedatabase", images=E)
執行人臉識別:
from numpy import *
import sys,os
from pca import *
reload(sys)
sys.setdefaultencoding('utf-8')
ef = Eigenfaces()
ef.dist_metric=ef.distEclud
ef.loadimgs("orl_faces/")
ef.compute()
# 建立測試集
testImg = ef.X[30]
print "實際值 =", ef.y[30], "->", "預測值 =",ef.predict(testImg)
待續。。。
程式碼下載:
參考文獻
機器學習演算法原理與程式設計實踐