之前的人脸识别匹配需要大量图片进行建模,然后通过概率匹配,结果不是很准确,同时也不符合一般需求。一般需求是人员通过摄像头拍摄一张照片,然后将照片保存进行命名,之后如果再次通过摄像头进行验证时候,通过算法特征匹配这个人跟保存的图片中的相似度,相似度最低的就是这个人,当然如果有足够的理论跟实验支持,可以确定最低的这个如果大于某个值,也认为不是这个人。
LBP算法
参考博客地址: http://blog.csdn.net/zouxy09/article/details/7929531
http://blog.csdn.net/u010006643/article/details/46417091
第二篇的最后结果是监测照片中是否是人脸,和人脸的各种表情匹配。但是我们的需求是要让摄像头里的人匹配上后台保存的那张单独的图片,所有改进一下让LBP算法只比较两张图片差异,差异最小的就是这个人,虽然最后误差仍然存在,但是算法是慢慢改进的,至少测试的过程我的识别结果都是我自己。
下面说思路:
一 。摄像头截图,截图成灰色处理的,并且尺寸为98*116的,(后面LBP算法是按照98*116的,方便分割),然后手动挑取一张最好的当作对比库放到指定路径下。****注意文件名不要中文
说明:这个截取如果判定是人脸了,就进行截取图片,有时候不会识别出人脸,人稍微移动一下就可以,这个跟手机识别软件差不多,多数情况你要动态的才能更好识别。
操作:运行shibieJietu.py
importnumpy as npimportcv2importsysimporttimeimportosdefCatVideo():
cv2.namedWindow("shibie")#1调用摄像头
cap=cv2.VideoCapture(0)#2人脸识别器分类器
classfier=cv2.CascadeClassifier("Train.xml")
color=(0,255,0)whilecap.isOpened():
ok,frame=cap.read()if notok:break
#2灰度转换
grey=cv2.cvtColor(frame,cv2.COLOR_BGR2GRAY)#人脸检测,1.2和2分别为图片缩放比例和需要检测的有效点数
faceRects = classfier.detectMultiScale(grey, scaleFactor = 1.2, minNeighbors = 3, minSize = (32, 32))if len(faceRects) > 0: #大于0则检测到人脸
for (x, y, w, h) infaceRects :
listStr= [str(int(time.time())), str(0)] #以时间戳和读取的排序作为文件名称
fileName = ''.join(listStr)
f= cv2.resize(grey[y:(y + h), x:(x + w)], (98, 116))
cv2.imwrite('D:\opencv\pictures\picTest'+os.sep+'%s.jpg' %fileName, f)
cv2.rectangle(frame, (x- 10, y - 10), (x + w + 10, y + h + 10), color, 3)
cv2.imshow("shibie",frame)print("ceshi2")if cv2.waitKey(10)&0xFF==ord('q'):breakcap.release()
cv2.destroyAllWindows()
CatVideo()
二 。photoKu文件夹的六个文件夹,分别放上不同人,照片都是灰化处理的,尺寸98*116。灰化图片详情参考上一篇博客的pick_face.py。如果周围人多的话也可以让他通过摄像头进行步骤一取到图片,但 是我这实在没什么人帮忙,就用网上图片凑活了。
运行compare.py首先执行runLBP算法,将六个文件夹里的图片通过算法算出LBP算子和统计直方图。
三 。开启摄像头,识别人脸后将人脸进行judgeFace 方法与刚才的结果进行逐一匹配,找到方差最小的那个就是要匹配的那个人了。
from numpy import *
from numpy importlinalg as laimportcv2importosimportmathfrom read_data importread_name_list
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2'
#为了让LBP具有旋转不变性,将二进制串进行旋转。#假设一开始得到的LBP特征为10010000,那么将这个二进制特征,#按照顺时针方向旋转,可以转化为00001001的形式,这样得到的LBP值是最小的。#无论图像怎么旋转,对点提取的二进制特征的最小值是不变的,#用最小值作为提取的LBP特征,这样LBP就是旋转不变的了。
defminBinary(pixel):
length=len(pixel)
zero= ''
for i in range(length)[::-1]:if pixel[i] == '0':
pixel=pixel[:i]
zero+= '0'
else:return zero +pixelif len(pixel) ==0:return '0'
#加载图像
def loadImageSet(add): #add是路径
print("步骤1")
FaceMat= mat(zeros((1,98*116)))#根据图片尺寸更改 一共有几行
j =0for i inos.listdir(add):#print(i) #图片正常显示了
#print(i.split('.')[1]) 输出结果:jpg
if i.split('.')[1] == 'jpg':try:
img= cv2.imread(add+i,0)#print(add+i) 输出结果:D:\opencv\huge/15138183801.jpg
#cv2.imwrite(str(i)+'.jpg',img)
except:print ('load %s failed'%i)
FaceMat[j,:]=mat(img).flatten()#print(FaceMat[j,:]) #取第j行
#print(FaceMat[:,j]) #取第j列
#http://blog.csdn.net/qq_18433441/article/details/54916991 flatten详解
j += 1
#print(FaceMat)
returnFaceMat#算法主过程
def LBP(FaceMat,R = 2,P = 8):print("步骤2")
Region8_x=[-1,0,1,1,1,0,-1,-1]
Region8_y=[-1,-1,-1,0,1,1,1,0]
pi=math.pi
LBPoperator=mat(zeros(shape(FaceMat)))for i in range(shape(FaceMat)[1]):#对每一个图像进行处理 转化成116*98的二维矩阵
face = FaceMat[:,i].reshape(116,98)
W,H=shape(face)
tempface=mat(zeros((W,H)))for x in range(R,W-R):for y in range(R,H-R):
repixel= ''pixel=int(face[x,y]) #取每一个值
#圆形LBP算子
for p in [2,1,0,7,6,5,4,3]:
p=float(p)
xp= x + R* cos(2*pi*(p/P))
yp= y - R* sin(2*pi*(p/P))#print(xp) 输出结果 2.0
#print(pixel) 输出结果 1
#print(yp) 0.0
#print(face[2,0]) 1.0
if int(face[int(xp),int(yp)])>pixel:
repixel+= '1'
else:
repixel+= '0'
#minBinary保持LBP算子旋转不变
tempface[x,y] = int(minBinary(repixel),base=2)
LBPoperator[:,i]=tempface.flatten().T#cv2.imwrite(str(i)+'hh.jpg',array(tempface,uint8))
returnLBPoperator#judgeImg:未知判断图像
#LBPoperator:实验图像的LBP算子
#exHistograms:实验图像的直方图分布
defjudgeFace(judgeImg,LBPoperator,exHistograms):
judgeImg=judgeImg.T
ImgLBPope=LBP(judgeImg)#把图片分为7*4份 , calHistogram返回的直方图矩阵有28个小矩阵内的直方图
judgeHistogram =calHistogram(ImgLBPope)
minIndex=0
minVals= inf #正无穷
for i in range(shape(LBPoperator)[1]):
exHistogram=exHistograms[:,i]
diff= (array(exHistogram-judgeHistogram)**2).sum()print(diff)returndiff#统计直方图
defcalHistogram(ImgLBPope):
Img= ImgLBPope.reshape(116,98)
W,H=shape(Img)#把图片分为7*4份
Histogram = mat(zeros((256,7*4)))
maskx,masky= W/4,H/7 #29 14
for i in range(4):for j in range(7):#使用掩膜opencv来获得子矩阵直方图
mask =zeros(shape(Img), uint8)
mask[int(i*maskx): int((i+1)*maskx),int(j*masky) :int((j+1)*masky)] = 255hist= cv2.calcHist([array(Img,uint8)],[0],mask,[ 256],[0,256])
Histogram[:,(i+1)*(j+1)-1] =mat(hist).flatten().TreturnHistogram.flatten().TdefrunLBP(tuPianPath):#加载图像
FaceMat = loadImageSet(tuPianPath).T #反转矩阵
LBPoperator = LBP(FaceMat) #获得实验图像LBP算子
#获得实验图像的直方图分布,这里计算是为了可以多次使用
exHistograms = mat(zeros((256*4*7,shape(LBPoperator)[1])))for i in range(shape(LBPoperator)[1]):
exHistogram=calHistogram(LBPoperator[:,i])
exHistograms[:,i]=exHistogram
allLBPoperator.append(LBPoperator)
allexHistograms.append(exHistograms)#build_camera(LBPoperator,exHistograms)
#loadname = 'D:\opencv/'+'8.jpg'
#judgeImg = cv2.imread(loadname,0)
#jresult=judgeFace(mat(judgeImg).flatten(),LBPoperator,exHistograms)
#if judgeFace(mat(judgeImg).flatten(),LBPoperator,exHistograms)+1 == int(nameList[i]):
defbuild_camera():print(1111)#opencv文件中人脸级联文件的位置,用于帮助识别图像或者视频流中的人脸
face_cascade = cv2.CascadeClassifier('Train.xml')#打开摄像头并开始读取画面
cameraCapture =cv2.VideoCapture(0)
success, frame=cameraCapture.read()while success and cv2.waitKey(1) == -1:
success, frame=cameraCapture.read()
grey= cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) #图像灰化
faces = face_cascade.detectMultiScale(grey, 1.3, 5) #识别人脸
for (x, y, w, h) infaces:
f= cv2.resize(grey[y:(y + h), x:(x + w)], (98, 116))
result=inf
show_name=''
for i inrange(len(allTuPianPath)):
jresult=judgeFace(mat(f).flatten(),allLBPoperator[i],allexHistograms[i])if jresult==0:
show_name=allmen[i]break
elif jresult
result=jresult
show_name=allmen[i]#print(f)
#if prob >0.5: #如果模型认为概率高于70%则显示为模型中已有的label
#show_name = name_list[label]
#else:
#show_name = 'Stranger'
cv2.putText(frame, show_name, (x, y - 20), cv2.FONT_HERSHEY_SIMPLEX, 1, 255, 2) #显示名字
frame = cv2.rectangle(frame, (x, y), (x + w, y + h), (255, 0, 0), 2) #在人脸区域画一个正方形出来
cv2.imshow("Camera", frame)returnshow_name
cameraCapture.release()
cv2.destroyAllWindows()if __name__ == '__main__':
allLBPoperator=[]#六个LBP算子
allexHistograms=[]#六个统计直方图
#六个文件夹路径
allTuPianPath=['D:\opencv\photoKu\huge/','D:\opencv\photoKu\luyi/','D:\opencv\photoKu\me/','D:\opencv\photoKu\shayi/','D:\opencv\photoKu\wuyanzu/','D:\opencv\photoKu\zhoujielun/']
#六个人名
allmen=['hege','luyi','me','shayi','wuyanzu','zhoujielun']
#分别获取每张图片的LBP算子,和统计直方图for tuPianPath inallTuPianPath:
runLBP(tuPianPath)
#开启摄像头
name=build_camera()
#最后摄像头如果关闭会打印出这个人名print(name)
这张图片是输出的结果,开始分别算出六个LBP算子和统计立方图片,步骤一步骤二打印六次,然后1111进入摄像头,开始比较识别到的人脸,与六张图的平方差分别是
245164,175674,50472,248620,162249,222144。 (第三张是我本人,平方差是5万多,明显比其他的低的多)
****这里需要注意的是,摄像头之前截取的头像,应该跟对比时候的头像差别不会太大,因为都只有一个头,环境都差不太多,至于化妆,戴帽子什么的应该会有差异,但是谁家早上视频识别时候不好好的,非得戴帽子,带镜子啥的,所以,只要尽量做到一致性,识别差距就不会太大。
由于是while循环摄像头,所以识别到的下一张头像继续运算下去,这里其实可以将摄像头关掉了,然后打印出人名即可,有需要的可以自己改动。我懒。
最后不得不承认,运算过程太卡顿,而且摄像头循环也没做退出处理,需要慢慢等着,然后六个平方差都算完后头像上显示人名,以后争取改进成多线程的,估计会快一些。现在是截取了一张图片然后就开始比对,这张图片此时不能再动了,导致摄像头发生卡顿,多线程应该能够解决这个问题。
代码地址:https://github.com/chaoyuebeijita/face
facecompare+LBP.zip