其实人脸识别在前面我们已经介绍过了,不过那个时候介绍的人脸识别是基于opencv的识别,使用的是已经训练好的数据集。所以,对于人脸识别这个章节的学习,其实已经不需要太多的说明,主要是讲解一下基于深度学习的人脸识别。
人脸识别的主要方法
1.基于几何特征的方法
几何特征的检测方法最早是由Kelly和Kannade提出,现在基本上已经不用了,根据名字就可以得出,这个检测方法就是根据脸部的一些特征,比如眼睛的大小,鼻子的大小,眼睛到鼻子的距离等等一些基本特征点,所以说,它的优缺点也显而易见,识别简单,速度快,缺点却也很明显,每个人的脸部特征都是不一样的,没有办法做到非常准确,尤其是现在发展的人脸识别扫码支付,如果只是用特征检测,那么很有可能会导致识别错乱,造成不可估量的后果。但是也不是完全不能发展,现在的人来能识别都是二维的,并不能做到三维,如果说能够做到三维的识别,那么几何特征的识别将是非常出众的。
2.特征脸方法
MIT的Turk和Pentland在1991年提出的人脸识别算法,其实简单的来说,就是将识别到的脸放在一个目标空间中,和已经存在的人脸进行匹配,就是对于一个模板的匹配。优缺点肯定也很明显,优点就是快速,一旦匹配成功,就基本不会出错,缺点就是数据库中必须存在这个人脸模板,人脸识别的时候必须保持和数据库中的人脸模板具有相同的特征,识别效果差,可应用范围小。
具体的步骤如下:
(1)、将所有人脸图像变成Nx1的向量,然后将所有人脸向量组成矩阵M。 (2)、对矩阵M做归一化 (3)、求矩阵M的协方差矩阵C。 (4)、求协方差矩阵C的特征值和特征向量,并将最大的R个特征值对应的特征向量按行组成矩阵P,此时P就是算法希望找到的空间转换。
3.渔夫脸方法
由 Belhumeur提出,在特征脸的基础上,加入线性判别分析方法,该方法目前依旧是主流的人脸识别之一。基本思想就是同一人脸由于光照条件和角度变化带来的差异往往要大于不同人脸之间的差异,所以要将这些特征删除,方法就是应用线性判别分析,使得类内的散度最小。具体算法步骤:
(1)、将所有人脸图像变成Nx1的向量。 (2)、应用PCA降维(与特征脸方法类似),得到人脸向量。 (3)、对所有图像求平均值。 (4)、对每一类(同一人脸)做平均值得到:。 (5)、计算类间散度:,为i类的数量。 (6)、计算类内散度:。 (7)、对目标函数(投影矩阵)求极值:
#渔夫脸方法 import sys import os import cv2 import numpy as np class SkinDetector(): """ 对颜色空间取阈值 """ def _R1(self,BGR): B = BGR[:,:,0] G = BGR[:,:,1] R = BGR[:,:,2] e1 = (R>95) & (G>40) & (B>20) & ((np.maximum(R,np.maximum(G,B)) - np.minimum(R, np.minimum(G,B)))>15) & (np.abs(R-G)>15) & (R>G) & (R>B) e2 = (R>220) & (G>210) & (B>170) & (abs(R-G)<=15) & (R>B) & (G>B) return (e1|e2) def _R2(self,YCrCb): Y = YCrCb[:,:,0] Cr = YCrCb[:,:,1] Cb = YCrCb[:,:,2] e1 = Cr <= (1.5862*Cb+20) e2 = Cr >= (0.3448*Cb+76.2069) e3 = Cr >= (-4.5652*Cb+234.5652) e4 = Cr <= (-1.15*Cb+301.75) e5 = Cr <= (-2.2857*Cb+432.85) return e1 & e2 & e3 & e4 & e5 def _R3(self,HSV): H = HSV[:,:,0] S = HSV[:,:,1] V = HSV[:,:,2] return ((H<25) | (H>230)) def detect(self, src): if np.ndim(src) < 3: return np.ones(src.shape, dtype=np.uint8) if src.dtype != np.uint8: return np.ones(src.shape, dtype=np.uint8) srcYCrCb = cv2.cvtColor(src, cv2.COLOR_BGR2YCR_CB) srcHSV = cv2.cvtColor(src, cv2.COLOR_BGR2HSV) skinPixels = self._R1(src) & self._R2(srcYCrCb) & self._R3(srcHSV) return np.asarray(skinPixels, dtype=np.uint8) class CascadedDetector(): """ 利用OpenCV函数构建检测器,可调参数:scaleFactor、minNeighbors、minSize """ def __init__(self, cascade_fn="C:\Users\Administrator\AppData\Local\Programs\python\Python310\Lib\site-packages\cv2\data\haarcascade_frontalface_alt2.xml", scaleFactor=1.2, minNeighbors=5, minSize=(30,30)): self.cascade = cv2.CascadeClassifier(cascade_fn) self.scaleFactor = scaleFactor self.minNeighbors = minNeighbors self.minSize = minSize def detect(self, src): if np.ndim(src) == 3: src = cv2.cvtColor(src, cv2.COLOR_BGR2GRAY) src = cv2.equalizeHist(src) rects = self.cascade.detectMultiScale(src,scaleFactor=self.scaleFactor, minNeighbors=self.minNeighbors, minSize=self.minSize) if len(rects) == 0: return np.ndarray((0,)) rects[:,2:] += rects[:,:2] return rects class SkinFaceDetector(): """ 只接受与肤色相近的候选人脸 """ def __init__(self, threshold=0.3, cascade_fn='C:\Users\Administrator\AppData\Local\Programs\python\Python310\Lib\site-packages\cv2\data\haarcascade_frontalface_alt2.xml', scaleFactor=1.2, minNeighbors=5, minSize=(30,30)): self.faceDetector = CascadedDetector(cascade_fn=cascade_fn, scaleFactor=scaleFactor, minNeighbors=minNeighbors, minSize=minSize) self.skinDetector = SkinDetector() self.threshold = threshold def detect(self, src): rects = [] for i,r in enumerate(self.faceDetector.detect(src)): x0,y0,x1,y1 = r face = src[y0:y1,x0:x1] skinPixels = self.skinDetector.detect(face) skinPercentage = float(np.sum(skinPixels)) / skinPixels.size print(skinPercentage) if skinPercentage > self.threshold: rects.append(r) return rects if __name__ == "__main__": #输入图像 if len(sys.argv) < 2: raise Exception("No image given.") inFileName = sys.argv[1] outFileName = None #输出目录 if len(sys.argv) > 2: outFileName = sys.argv[2] if outFileName == inFileName: outFileName = None #开始检测 img = np.array(cv2.imread('F:\Image\\test16.jpg'), dtype=np.uint8) imgOut = img.copy() detector = CascadedDetector(cascade_fn="C:\Users\Administrator\AppData\Local\Programs\python\Python310\Lib\site-packages\cv2\data\haarcascade_frontalface_alt2.xml") eyesDetector = CascadedDetector(scaleFactor=1.1,minNeighbors=5, minSize=(20,20), cascade_fn="C:\Users\Administrator\AppData\Local\Programs\python\Python310\Lib\site-packages\cv2\data\haarcascade_eye.xml") for i,r in enumerate(detector.detect(img)): x0,y0,x1,y1 = r cv2.rectangle(imgOut, (x0,y0),(x1,y1),(0,255,0),1) face = img[y0:y1,x0:x1] for j,r2 in enumerate(eyesDetector.detect(face)): ex0,ey0,ex1,ey1 = r2 cv2.rectangle(imgOut, (x0+ex0,y0+ey0),(x0+ex1,y0+ey1),(0,255,0),1) #显示图片 if outFileName is None: cv2.imshow('faces', imgOut) cv2.waitKey(0) cv2.imwrite(outFileName, imgOut)
4.基于弹性匹配
Wiskott于1997年使用Gabor小波对人脸图像进行处理,将人脸表达成由若干个 特征点构成的并具有一定拓扑结构信息的人脸弹性图。图的顶点代表关键特征点(Jet),边的属性则为不同特征点之间的关系。此方法用了全局特征,还用了局部特征。
什么叫Gabor小波?为什么要用Habor小波?
Gabor小波是以任意一个高斯函数作为窗口函数的波函数。一个图像像素与不同的Gabor核卷积后的系数集合称为一个Jet(一般为40个系数)。一个jet描述了一个像素周围一小块的灰度。
用Gabor小波的原因是因为Gabor小波对于亮度变化和人脸的不同表情不敏感,匹配精度大大提高。
搜索步骤:
(1)、对每个特征点,从图像中定位起粗略位置。 (2)、在以标准化的人脸图像中计算出处的Gabor变换系数。 (3)、将与模板中的特征点进行比较,其中相似度最高的为候选者,计算其位置误差,则特征点的精确位置修正为。 (4)、重复上述步骤,将最后的得到的精确点的求出Gaboe系数即可。
5.基于支持向量机(SVM)的方法
SVM在后面的专栏会做出详细的介绍,这里就不过多的介绍,SVM分类器训练为一个二分类器(人脸和非人脸),当然也可以每一类训练一个SVM分类器。其实就是利用分类器的特点进行匹配。
6. 基于神经网络的方法
基于神经网络的方法前面已经介绍过,不需要我们来提取特征点,只需要利用已经训练好的样本就可以,尤其是近年来深度学习的快速发展,神经网络的检测也越来越受到欢迎,不过缺点就是需要大量的训练。
深度学习方法
深度学习中对于图像的处理发展最快的就是卷积神经网络,所以对于人脸识别,大多数时候都采用卷积神经网络方法进行训练测试,但是深度学习是一个很复杂的知识脉络,这里不能很深渡的介绍,所以深度学习就放在另一个专栏中介绍,这里就只是简单的介绍一下。
神经网络训练模型有很多,比如GoogleNet、VGG16、YOLO等模型,他们的原理前面都介绍过了,神经网络基本上都差不多,所以他们的具体步骤就不过多介绍。主要就是其中卷积层,全连接层,池化层的操作,以及一些激活函数,损失函数,优化分类器的选择。下面就给出了一个示例,后面对于神经网络的搭建也会深度介绍。
#深度学习人脸识别方法 import os import tensorflow as tf from tensorflow.keras.models import Sequential from tensorflow.keras.layers import Dense, Conv2D, Flatten, Dropout, MaxPooling2D import cv2 import numpy as np from sklearn.model_selection import train_test_split faces_path = '人脸数据集位置' imgs = [] labels = [] #读取数据 for filename in os.listdir(faces_path): if filename.endswith('.jpg'): filename = path + '/' + filename img = cv2.imread(filename) imgs.append(img) labels.append(filename[-1]) #将图片数据与标签转换成数组 imgs = np.array(imgs) labels = np.array(labels) #划分训练集与测试集 train_x,test_x,train_y,test_y = train_test_split(imgs, labels, test_size=0.2) #统一人脸图像大小 train_x = train_x.reshape(train_x.shape[0], 100, 100, 3) test_x = test_x.reshape(test_x.shape[0], 100, 100, 3) #归一化 train_x = train_x.astype('float32')/255.0 test_x = test_x.astype('float32')/255.0 batch_size = 64 num_batch = len(train_x) //batch_size #网络 model = Sequential([ Conv2D(32, 3, padding='same', activation='relu', input_shape=(100, 100,3)), MaxPooling2D(), Dropout(0.5) Conv2D(64, 3, padding='same', activation='relu'), MaxPooling2D(), Dropout(0.5), Conv2D(64, 3, padding='same', activation='relu'), MaxPooling2D(), Dropout(0.5), Conv2D(64, 3, padding='same', activation='relu'), MaxPooling2D(), Dropout(0.5), Flatten(), Dense(256, activation='relu'), Dense(1, activation='sigmoid') ]) #编译模型,输入优化器、损失函数 model.compile(optimizer='adam', loss='binary_crossentropy', metrics=['accuracy']) #训练 model.fit(train_x, train_y,epoch=10, batchsize = 64) #测试 model.eval(test_x, test_y)
好了,本节的只是就到此结束了,计算机视觉专栏也就到此结束了,后面就进入深度学习专栏了。由于博主能力有限,如果后面有其他知识,会补充到这里的。再见了!