Python 基于OpenCV+face_recognition实现人脸捕捉与人脸识别(照片对比)$ F2 x) A4 f$ D
5 S$ Y8 G# A! k. p: @# @
1.安装包依赖 $ h( {- G8 J0 L" ~与上篇通过摄像头动态识别人脸一样,先下载好opencv-python、face-recognition,这里因为使用的是照片对比的方式,特意使用tkinter画了一个简单的GUI方便操作。% w. s9 I! R% `% L2 R2 Q4 y: q0 H
9 @7 A/ P/ i+ o" u$ H4 ~" N
在python 3以上版本tkinter是环境自带的,所以这里不需要安装 - h# K) B2 L3 A6 ?1 i* Q( L7 T( G2 O4 E# U* C% b5 a/ T8 Q* m* B
2.代码示例% ?( \+ j7 c$ s! y$ |* D
import os 8 w) x$ N; \! j. R& B- c0 aimport cv21 T0 B, O9 K( O7 ?- x
import numpy as np 2 h8 q& m3 a1 p/ R& x7 p5 Wimport face_recognition8 r& m L( ]3 [/ k5 X, I# T% M
import tkinter as tk " t' k4 h/ o( Z& a+ _& Y+ nimport tkinter.filedialog& b' b: J3 E. o0 T1 a. g
from PIL import Image,ImageTk & H& E8 u2 z( r3 C# i- d8 A
* v; x3 v3 I- u+ O0 B( O" `classNames=[] . o" a; M% O8 r- }/ q/ zimg_path='Picture'2 h% [- P4 V) h6 U2 i0 W9 H9 T1 G
img_recognition_path='Recognition'+ q4 y: Y5 N6 F
existsEncodeingList=[] ^& o3 J3 M9 l U/ r
#对人脸集合进行编码进行处理. S8 f$ T9 `/ `; Q. [: ]7 L2 o( x
def findEncodeings(images): 4 q7 [% v _! t8 a& v for img in images: $ l3 W( A; _+ Q/ \8 V #灰度处理8 g1 X1 [. {/ l' n, ?2 i9 f! e7 u
img=cv2.cvtColor(src=img,code=cv2.COLOR_BGR2RGB) / w! V5 |" y, h. Y7 ^ ~ #face_encodings对图片对象a_images进行编码并返回数组0位置编码结果 ( ?% t5 @+ m: u7 Z encode=face_recognition.face_encodings(img)[0] & _5 u1 R4 f/ }4 n& u- ?* l+ B7 I existsEncodeingList.append(encode) 8 |: K1 g9 _, F# g1 v% w7 S' o$ A2 T5 Q i) }9 d
#获取当前存储的人脸编码集合 # Y0 F* y4 l" {, r- qdef findExistsEncodeingList(img_path): . d5 L0 Z+ n o" M. E0 ~) K images=[]" C# B9 `" ]+ B5 @; S* d" p
#列出已经上传的所有图片' c2 A. C7 S% V: a7 i5 u8 Q i
imgList=os.listdir(img_path) / {8 Q/ v* B: s7 e #处理存储的图片得到其人脸编码 * M/ {& p+ T* w1 J# A5 o for pic in imgList:) z- U: u6 C9 @* X4 N0 Z& N& Y
img=cv2.imread('{}/{}'.format(img_path,pic)) / Z2 R% Z; c# Q images.append(img) q2 K: U: u3 m$ o. B0 L
classNames.append(os.path.splitext(pic)[0]) 4 z/ {+ T6 h2 \* b; c" f# r findEncodeings(images)0 o* Y% P8 q6 f3 A9 a0 P
t; j5 L0 V! ]+ q! m" z2 b J6 L
#选择并对比图片 , j5 _0 [ q7 S: e0 ]def choosepic():; a" k/ Y+ g, J4 `% R) F& {
choosepath = tkinter.filedialog.askopenfilename() ' W2 U% P" \' S3 `! a. v path.set(choosepath) + b+ j: Q6 k7 Z( e; a" p/ n) I N img_open = Image.open(entry.get()).resize((530,750)) " B3 d3 c" F6 R img = ImageTk.PhotoImage(img_open) 9 h: m O& ~) A+ K+ G. H lableShowImage.config(image=img), a" `4 p* n3 w7 ^8 c4 J
lableShowImage.image = img) Q1 s% M) S! G
lableShowImage.place(x=30, y=70, width=530, height=750) / q, l1 ^) o* r9 `- b9 N3 F$ ~ faceRecognition(choosepath)& A. L& t l5 J# y, B0 ~; I
9 n( ]! C7 e9 f3 a3 R. W0 x/ p- g
def faceRecognition(choosepath): 9 g! D$ {' } {* |. V1 q frame=cv2.imread(choosepath) * u% E5 Z0 Y4 Q q+ A+ }+ ` frameRGB=cv2.cvtColor(src=frame,code=cv2.COLOR_BGR2RGB)9 d# O3 ~: I9 Z5 U7 k% I1 s' J
#对摄像头读取的检测人脸 . ^! I' I% y `" Y- v: k facesLocate=face_recognition.face_locations(frameRGB)- w! S7 [3 q: P+ `# U) ?
#进行特征编码8 k0 q1 A5 Y; j2 a* N- T# E H$ M' _
faceEncoded=face_recognition.face_encodings(frameRGB,facesLocate) , c1 h N. A) {8 l& M4 f& ?. h+ Q #遍历检测的人脸和库中读取的图片进行对比,计算其相似度" R0 R3 T9 w; X) e- |% b; S
name='unknow' 5 C6 K3 s- A' e1 K* x for (top,right, bottom,left),face_encoding in zip(facesLocate,faceEncoded):7 `: v k. Q X: W ~4 Z4 S0 H$ j
#进行匹配# v* C8 {# q, h! K* o& t6 U
matchs=face_recognition.compare_faces(existsEncodeingList,face_encoding) o7 \4 L, P k* T+ {" ]6 b8 u6 c
#计算相似度 ! `# C1 C) _- M) m0 q# }2 G distance=face_recognition.face_distance(existsEncodeingList,face_encoding) $ P: T! ]) r9 c- d8 i4 F/ T( K lab='unknow' 2 S' ~1 m! ]- k J; k! |, G for index, item in enumerate(distance):; F/ f% F5 J* R3 z) M1 v6 M
if item<0.5: & ^* W: [+ t! \1 X: t S- K: j6 E) Q if matchs[index]: " B6 F. ^) N' z9 Z #得到匹配到的图片名称与相似度值 5 _! \/ d/ l. m# z/ _2 Q lab='name:{}; Similarity:{}'.format(classNames[index],item)! f1 g0 T4 L8 E5 j: d
name=classNames[index] E {1 b+ i9 M; } break5 c$ R/ p" `8 e, q- {9 {
#初始化面部捕捉框显示绿色$ Y k$ V- f$ M
color1 =(0,255,0) * \. }) {+ b% _% K& I" q. | if name =='unknow': 1 o; w9 B4 i; u$ B1 Z #未能识别的时候显示蓝色 & ]( q2 v+ A/ }6 _' v color1 =(255,0,0) & K9 x3 l' w- I4 V" {! X0 P# J #画面部捕捉框2 `( g2 f7 e) O( E% B+ c
cv2.rectangle(img=frame,pt1=(left,top),pt2=(right,bottom),color=color1,thickness=3)' e( ~; |6 q( R+ j
#在捕捉框上添加匹配到的图片信息+ l6 h5 B* c' T9 n$ g7 P
cv2.putText(frame, lab, (left,top-8),cv2.FONT_HERSHEY_SIMPLEX, 0.7, color1, 2)! I# {8 D. k% Z
cv2.imwrite('{}/{}.png'.format(img_recognition_path,name),frame)6 P( v9 Z" O( J; x
img_Recognition = Image.open('{}/{}.png'.format(img_recognition_path,name)).resize((530,750))$ ~" \6 j: Y1 l0 d. |' F8 R
img = ImageTk.PhotoImage(img_Recognition) 1 l2 k3 B M4 p lableShowImage2.config(image=img)5 O. o( Q& L7 ? M
lableShowImage2.image = img4 l+ d- s0 O- D- H
lableShowImage2.place(x=630, y=70, width=530, height=750) ) G( }/ r1 S `& R. A7 P: u- n0 o
if __name__ == '__main__':% P2 Y, E8 T, F% c- n( d4 E" a
findExistsEncodeingList(img_path)( ~. U/ i6 B0 p
#生成tk界面 app即主窗口; z- t& N. P6 D9 e( u7 l7 d
app = tk.Tk() 9 f1 n& c E/ V2 m #修改窗口titile5 Z1 D- N! U6 {, ~
app.title("show pictue") ) O- {+ J6 j4 C" T; g" o' O
#设置主窗口的大小和位置 ! k p6 c8 @ d0 G8 e% P app.geometry("1200x900+200+50")) U/ {; j2 q& D% D. j: D& N9 Z
#Entry widget which allows displaying simple text. ' W. F9 x# o8 U+ ?0 }4 {* i( ~ path = tk.StringVar()& x' S7 l5 i6 n* _ Z* k4 O
entry = tk.Entry(app, state='readonly', text=path,width = 100) 7 D4 ?$ |) I, B9 `1 P entry.pack() 0 F' l0 O9 O7 e% N #使用Label显示图片* {) T1 b. b* X6 ]- ~5 Y7 c7 R
lableShowImage = tk.Label(app)2 b9 C9 B0 O2 k: J) l- ]# r* d
lableShowImage.pack() 9 P3 T( j4 d4 E9 e o! A #使用Label2显示处理后的图片 ! E V* V8 t6 u* s( ] lableShowImage2 = tk.Label(app) : l+ d; R b: C7 O, c lableShowImage2.pack() & ~) A& d/ ?# j7 ~- j4 k% }. \ #选择图片的按钮 - D9 T. v4 b, c0 J1 [4 P L. G buttonSelImage = tk.Button(app, text='choose picture', command=choosepic); n3 U6 `3 t) j
buttonSelImage.pack()5 y/ `0 n9 R/ ~8 t
app.mainloop() , j2 Y/ Z3 p5 O6 @3 }' }3 z7 v5 a4 @
3.说明 5 B: V) ^, ~5 y# M. c7 t$ n8 p) y首先我将需要被识别的人脸的照片预设到项目目录的Picture文件夹下,然后创建一个Recognition目录存放识别过的图片,这样方便在一个界面上展示对比结果照片。 ; I8 m1 C" O. t, q9 [& R$ o ! P3 c8 r: ~6 z % l0 W. l% h4 V, @: L/ l3 Z$ m$ C+ O: [; w
其实对比结果也可以不用存,直接将处理后的图片缓存直接展示在界面上,这里需要改一下此处的代码,将上述代码注释掉,然后换成下面的那行,通过数组直接转成图片 # ?: {: F8 Z0 S) R4 x0 ~8 t$ \2 {7 T$ c6 |! V) y% |
6 }% _# z" F' I5 y! ]) u, I3 [: n* }
但是效果会存在色彩的失真,效果如下:5 P5 ]5 E# l% t9 G) Z
. ?2 E) L6 D" q3 n" K! i3 c9 ? u
9 A& A8 s5 | L; B b0 G G 3 T+ z K& u% h) w5 C也尝试了PIL的九种不同图片模式: 1,L,P,RGB,RGBA,CMYK,YCbCr,I,F,最终效果也没达到,大概与我resize((530,750))这个有关,也没继续纠结,有兴趣的同学可以尝试一下。 # X y: j3 b& i ! ^+ y8 t1 q7 u5 ^0 X k" Y7 n这里简单提下PIL的九种不同图片模式:% f t+ k( J; A5 j% d