当前位置:首页 > 科技 > 正文

详解Python实现进阶版人脸识别

详解Python实现进阶版人脸识别 2022-01-10 14:34 · Python可乐

使用到的库:dlib+Opencvpython版本:3.8编译环境:Jupyter Notebook(Anaconda3)

0.Dlib人脸特征检测原理
  • 提取特征点:请参考

  • 首选抓取多张图片,从中获取特征数据集和平均特征值然后写入csv文件 - 计算特征数据集的欧式距离作对比:首先使用Opencv库将摄像头中的人脸框出来,再将摄像头中采取到的人脸特征值与数据集中的每个人的特征均值作对比,选取最接近(欧氏距离最小)的值,将其标注为欧氏距离最小的数据集的人名

私信小编01即可获取大量Python学习资源

一、构建人脸特征数据集 1. 安装Dlib

请参考

2. 构建自己的数据集 2.1 抓取人脸图片

在视频流中抓取人脸特征,并保存为256*256大小的图片文件共20张,这就是我们建立数据集的第一步,用来训练人脸识别。

不一定是256*256的尺寸,可以根据自己的需求来调整大小,图片越大训练结果会愈加精确,但也会影响训练模型的时间。

其中:

  • 光线:曝光和黑暗的图片需手动剔除- 请使用同一个设备进行数据采集,不同设备的摄像头采集到的数据集会有出入- 这里采用的是从视频流中进行捕捉截图,也可以自己准备20张左右的人脸图片

代码:

importcv2importdlibimportosimportsysimportrandom#存储位置output_dir='D:/No1WorkSpace/JupyterNotebook/Facetrainset/Num&Name'#这里填编号+人名size=256#图片边长ifnotos.path.exists(output_dir):
os.makedirs(output_dir)#改变图片的亮度与对比度defrelight(img,light=1,bias=0):
w=img.shape[1]
h=img.shape[0]#image=[]
foriinrange(0,w):
forjinrange(0,h):
forcinrange(3):
tmp=int(img[j,i,c]*light+bias)
iftmp>255:
tmp=255
eliftmp<0:
tmp=0
img[j,i,c]=tmp
returnimg#使用dlib自带的frontal_face_detector作为我们的特征提取器detector=dlib.get_frontal_face_detector()#打开摄像头参数为输入流,可以为摄像头或视频文件camera=cv2.VideoCapture(0)#camera=cv2.VideoCapture('C:/Users/CUNGU/Videos/Captures/wang.mp4')index=1whileTrue:
if(index<=20):#存储15张人脸特征图像
print('Beingprocessedpicture%s'%index)#从摄像头读取照片
success,img=camera.read()#转为灰度图片
gray_img=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)#使用detector进行人脸检测
dets=detector(gray_img,1)

fori,dinenumerate(dets):
x1=d.top()ifd.top()>0else0
y1=d.bottom()ifd.bottom()>0else0
x2=d.left()ifd.left()>0else0
y2=d.right()ifd.right()>0else0

face=img[x1:y1,x2:y2]#调整图片的对比度与亮度,对比度与亮度值都取随机数,这样能增加样本的多样性
face=relight(face,random.uniform(0.5,1.5),random.randint(-50,50))

face=cv2.resize(face,(size,size))

cv2.imshow('image',face)

cv2.imwrite(output_dir+'/'+str(index)+'.jpg',face)

index+=1
key=cv2.waitKey(30)&0xff
ifkey==27:
break
else:
print('Finished!')#释放摄像头releasecamera
camera.release()#删除建立的窗口deleteallthewindows
cv2.destroyAllWindows()
break

运行效果:

2.2 分析每张人脸的特征值并存入csv文件

根据抓取的图片和人脸识别模型->训练得到的20个的68个特征数据集以及1个平均特征值存入csv文件

每张图片的68个特征数据集可以不用存取,他们只是中间量,计算平均值以后就可以抛弃了,这里把他们输出出来只是为了方便学习。

代码:

#从人脸图像文件中提取人脸特征存入CSV#Featuresextractionfromimagesandsaveintofeatures_all.csv#return_128d_features()获取某张图像的128D特征#compute_the_mean()计算128D特征均值fromcv2importcv2ascv2
importos
importdlib
fromskimageimportio
importcsv
importnumpyasnp

#要读取人脸图像文件的路径path_images_from_camera="D:/No1WorkSpace/JupyterNotebook/Facetrainset/"#Dlib正向人脸检测器detector=dlib.get_frontal_face_detector()

#Dlib人脸预测器predictor=dlib.shape_predictor("D:/No1WorkSpace/JupyterNotebook/model/shape_predictor_68_face_landmarks.dat")

#Dlib人脸识别模型#Facerecognitionmodel,theobjectmapshumanfacesinto128Dvectorsface_rec=dlib.face_recognition_model_v1("D:/No1WorkSpace/JupyterNotebook/model/dlib_face_recognition_resnet_model_v1.dat")


#返回单张图像的128D特征defreturn_128d_features(path_img):
img_rd=io.imread(path_img)
img_gray=cv2.cvtColor(img_rd,cv2.COLOR_BGR2RGB)
faces=detector(img_gray,1)

print("%-40s%-20s"%("检测到人脸的图像/imagewithfacesdetected:",path_img),'\n')

#因为有可能截下来的人脸再去检测,检测不出来人脸了
#所以要确保是检测到人脸的人脸图像拿去算特征
iflen(faces)!=0:
shape=predictor(img_gray,faces[0])
face_descriptor=face_rec.compute_face_descriptor(img_gray,shape)
else:
face_descriptor=0
print("noface")

returnface_descriptor


#将文件夹中照片特征提取出来,写入CSVdefreturn_features_mean_personX(path_faces_personX):
features_list_personX=[]
photos_list=os.listdir(path_faces_personX)
ifphotos_list:
foriinrange(len(photos_list)):
withopen("D:/No1WorkSpace/JupyterNotebook/feature/featuresGiao"+str(i)+".csv","w",newline="")ascsvfile:
writer=csv.writer(csvfile)
#调用return_128d_features()得到128d特征
print("%-40s%-20s"%("正在读的人脸图像/imagetoread:",path_faces_personX+"/"+photos_list[i]))
features_128d=return_128d_features(path_faces_personX+"/"+photos_list[i])
print(features_128d)
writer.writerow(features_128d)
#遇到没有检测出人脸的图片跳过
iffeatures_128d==0:
i+=1
else:
features_list_personX.append(features_128d)
else:
print("文件夹内图像文件为空/Warning:Noimagesin"+path_faces_personX+'/','\n')

#计算128D特征的均值
#Nx128D->1x128D
iffeatures_list_personX:
features_mean_personX=np.array(features_list_personX).mean(axis=0)
else:
features_mean_personX='0'

returnfeatures_mean_personX


#读取某人所有的人脸图像的数据people=os.listdir(path_images_from_camera)
people.sort()

withopen("D:/No1WorkSpace/JupyterNotebook/feature/features_all.csv","w",newline="")ascsvfile:
writer=csv.writer(csvfile)
forpersoninpeople:
print("#####"+person+"#####")
#Getthemean/averagefeaturesofface/personX,itwillbealistwithalengthof128D
features_mean_personX=return_features_mean_personX(path_images_from_camera+person)
writer.writerow(features_mean_personX)
print("特征均值/Themeanoffeatures:",list(features_mean_personX))
print('\n')
print("所有录入人脸数据存入/Saveallthefeaturesoffacesregisteredinto:D:/myworkspace/JupyterNotebook/People/feature/features_all2.csv")

如果要输出每一张图片的特征数据集,这里要用到Python的文件批量生成。

代码运行效果

二、识别人脸并匹配数据集 1. 原理:

通过计算特征数据集的欧氏距离作对比来识别人脸,取欧氏距离最小的数据集进行匹配。

欧氏距离也称欧几里得距离或欧几里得度量,是一个通常采用的距离定义,它是在m维空间中两个点之间的真实距离。在二维和三维空间中的欧氏距离的就是两点之间的距离。使用这个距离,欧氏空间成为度量空间。相关联的范数称为欧几里得范数。较早的文献称之为毕达哥拉斯度量。二维空间公式:

2. 视频流实时识别人脸数据

代码:

#摄像头实时人脸识别importos
importdlib#人脸处理的库Dlibimportcsv#存入表格importtime
importsys
importnumpyasnp#数据处理的库numpyfromcv2importcv2ascv2#图像处理的库OpenCvimportpandasaspd#数据处理的库Pandas#人脸识别模型,提取128D的特征矢量#facerecognitionmodel,theobjectmapshumanfacesinto128Dvectors#Referthistutorial:http://dlib.net/python/index.html#dlib.face_recognition_model_v1facerec=dlib.face_recognition_model_v1("D:/No1WorkSpace/JupyterNotebook/model/dlib_face_recognition_resnet_model_v1.dat")


#计算两个128D向量间的欧式距离#computethee-distancebetweentwo128Dfeaturesdefreturn_euclidean_distance(feature_1,feature_2):
feature_1=np.array(feature_1)
feature_2=np.array(feature_2)
dist=np.sqrt(np.sum(np.square(feature_1-feature_2)))
returndist


#处理存放所有人脸特征的csvpath_features_known_csv="D:/No1WorkSpace/JupyterNotebook/feature/features_all.csv"
csv_rd=pd.read_csv(path_features_known_csv,header=None)


#用来存放所有录入人脸特征的数组#thearraytosavethefeaturesoffacesinthedatabasefeatures_known_arr=[]

#读取已知人脸数据#printknownfacesforiinrange(csv_rd.shape[0]):
features_someone_arr=[]
forjinrange(0,len(csv_rd.loc[i,:])):
features_someone_arr.append(csv_rd.loc[i,:][j])
features_known_arr.append(features_someone_arr)
print("FacesinDatabase:",len(features_known_arr))

#Dlib检测器和预测器#Thedetectorandpredictorwillbeuseddetector=dlib.get_frontal_face_detector()
predictor=dlib.shape_predictor('D:/No1WorkSpace/JupyterNotebook/model/shape_predictor_68_face_landmarks.dat')

#创建cv2摄像头对象#cv2.VideoCapture(0)tousethedefaultcameraofPC,#andyoucanuselocalvideonamebyusecv2.VideoCapture(filename)cap=cv2.VideoCapture(0)

#cap.set(propId,value)#设置视频参数,propId设置的视频参数,value设置的参数值cap.set(3,480)

#cap.isOpened()返回true/false检查初始化是否成功#whenthecameraisopenwhilecap.isOpened():

flag,img_rd=cap.read()
kk=cv2.waitKey(1)

#取灰度
img_gray=cv2.cvtColor(img_rd,cv2.COLOR_RGB2GRAY)

#人脸数faces
faces=detector(img_gray,0)

#待会要写的字体fonttowritelater
font=cv2.FONT_HERSHEY_COMPLEX

#存储当前摄像头中捕获到的所有人脸的坐标/名字
#thelisttosavethepositionsandnamesofcurrentfacescaptured
pos_namelist=[]
name_namelist=[]

#按下q键退出
#press'q'toexit
ifkk==ord('q'):
break
else:
#检测到人脸whenfacedetected
iflen(faces)!=0:
#获取当前捕获到的图像的所有人脸的特征,存储到features_cap_arr
#getthefeaturescapturedandsaveintofeatures_cap_arr
features_cap_arr=[]
foriinrange(len(faces)):
shape=predictor(img_rd,faces[i])
features_cap_arr.append(facerec.compute_face_descriptor(img_rd,shape))

#遍历捕获到的图像中所有的人脸
#traversalallthefacesinthedatabase
forkinrange(len(faces)):
print("#####cameraperson",k+1,"#####")
#让人名跟随在矩形框的下方
#确定人名的位置坐标
#先默认所有人不认识,是unknown
#setthedefaultnamesoffaceswith"unknown"
name_namelist.append("unknown")

#每个捕获人脸的名字坐标thepositionsoffacescaptured
pos_namelist.append(tuple([faces[k].left(),int(faces[k].bottom()+(faces[k].bottom()-faces[k].top())/4)]))

#对于某张人脸,遍历所有存储的人脸特征
#foreveryfacesdetected,comparethefacesinthedatabase
e_distance_list=[]
foriinrange(len(features_known_arr)):
#如果person_X数据不为空
ifstr(features_known_arr[i][0])!='0.0':
print("withperson",str(i+1),"theedistance:",end='')
e_distance_tmp=return_euclidean_distance(features_cap_arr[k],features_known_arr[i])
print(e_distance_tmp)
e_distance_list.append(e_distance_tmp)
else:
#空数据person_X
e_distance_list.append(999999999)
#找出最接近的一个人脸数据是第几个
#Findtheonewithminimumedistance
similar_person_num=e_distance_list.index(min(e_distance_list))
print("Minimumedistancewithperson",int(similar_person_num)+1)

#计算人脸识别特征与数据集特征的欧氏距离
#距离小于0.4则标出为可识别人物
ifmin(e_distance_list)<0.4:
#这里可以修改摄像头中标出的人名
#Hereyoucanmodifythenamesshownonthecamera
#1、遍历文件夹目录
folder_name='D:/No1WorkSpace/JupyterNotebook/Facetrainset/'
#最接近的人脸
sum=similar_person_num+1
key_id=1#从第一个人脸数据文件夹进行对比
#获取文件夹中的文件名:1wang、2zhou、3...
file_names=os.listdir(folder_name)
fornameinfile_names:
#print(name+'->'+str(key_id))
ifsum==key_id:
#winsound.Beep(300,500)#响铃:300频率,500持续时间
name_namelist[k]=name[1:]#人名删去第一个数字(用于视频输出标识)
key_id+=1
#播放欢迎光临音效
#playsound('D:/myworkspace/JupyterNotebook/People/music/welcome.wav')
#print("Maybeperson"+str(int(similar_person_num)+1))
#-----------筛选出人脸并保存到visitor文件夹------------
fori,dinenumerate(faces):
x1=d.top()ifd.top()>0else0
y1=d.bottom()ifd.bottom()>0else0
x2=d.left()ifd.left()>0else0
y2=d.right()ifd.right()>0else0
face=img_rd[x1:y1,x2:y2]
size=64
face=cv2.resize(face,(size,size))
#要存储visitor人脸图像文件的路径
path_visitors_save_dir="D:/No1WorkSpace/JupyterNotebook/KnownFacetrainset/"
#存储格式:2019-06-24-14-33-40wang.jpg
now_time=time.strftime("%Y-%m-%d-%H-%M-%S",time.localtime())
save_name=str(now_time)+str(name_namelist[k])+'.jpg'
#print(save_name)
#本次图片保存的完整url
save_path=path_visitors_save_dir+'/'+save_name
#遍历visitor文件夹所有文件名
visitor_names=os.listdir(path_visitors_save_dir)
visitor_name=''
fornameinvisitor_names:
#名字切片到分钟数:2019-06-26-11-33-00wangyu.jpg
visitor_name=(name[0:16]+'-00'+name[19:])
#print(visitor_name)
visitor_save=(save_name[0:16]+'-00'+save_name[19:])
#print(visitor_save)
#一分钟之内重复的人名不保存
ifvisitor_save!=visitor_name:
cv2.imwrite(save_path,face)
print('新存储:'+path_visitors_save_dir+'/'+str(now_time)+str(name_namelist[k])+'.jpg')
else:
print('重复,未保存!')

else:
#播放无法识别音效
#playsound('D:/myworkspace/JupyterNotebook/People/music/sorry.wav')
print("Unknownperson")
#-----保存图片-------
#-----------筛选出人脸并保存到visitor文件夹------------
fori,dinenumerate(faces):
x1=d.top()ifd.top()>0else0
y1=d.bottom()ifd.bottom()>0else0
x2=d.left()ifd.left()>0else0
y2=d.right()ifd.right()>0else0
face=img_rd[x1:y1,x2:y2]
size=64
face=cv2.resize(face,(size,size))
#要存储visitor-》unknown人脸图像文件的路径
path_visitors_save_dir="D:/No1WorkSpace/JupyterNotebook/UnKnownFacetrainset/"
#存储格式:2019-06-24-14-33-40unknown.jpg
now_time=time.strftime("%Y-%m-%d-%H-%M-%S",time.localtime())
#print(save_name)
#本次图片保存的完整url
save_path=path_visitors_save_dir+'/'+str(now_time)+'unknown.jpg'
cv2.imwrite(save_path,face)
print('新存储:'+path_visitors_save_dir+'/'+str(now_time)+'unknown.jpg')

#矩形框
#drawrectangle
forkk,dinenumerate(faces):
#绘制矩形框
cv2.rectangle(img_rd,tuple([d.left(),d.top()]),tuple([d.right(),d.bottom()]),(0,255,255),2)
print('\n')

#在人脸框下面写人脸名字
#writenamesunderrectangle
foriinrange(len(faces)):
cv2.putText(img_rd,name_namelist[i],pos_namelist[i],font,0.8,(0,255,255),1,cv2.LINE_AA)

print("Facesincameranow:",name_namelist,"\n")

#cv2.putText(img_rd,"Press'q':Quit",(20,450),font,0.8,(84,255,159),1,cv2.LINE_AA)
cv2.putText(img_rd,"FaceRecognition",(20,40),font,1,(0,0,255),1,cv2.LINE_AA)
cv2.putText(img_rd,"Visitors:"+str(len(faces)),(20,100),font,1,(0,0,255),1,cv2.LINE_AA)

#窗口显示showwithopencv
cv2.imshow("camera",img_rd)

#释放摄像头releasecameracap.release()

#删除建立的窗口deleteallthewindowscv2.destroyAllWindows()

若直接使用本代码,文件目录弄成中文会乱码

运行效果:

图中两人的特征数据集均已被收集并录入,所以可以识别出来,如果没有被录入的人脸就会出现unknown。

没有吴京叔叔的数据集,所以他是陌生人

你可能想看:

有话要说...

取消
扫码支持 支付码