opencv pillow实现人脸识别
爱编程的小段 人气:0本文不涉及分类器、训练识别器等算法原理,仅包含对其应用(未来我也会写自己对机器学习算法原理的一些观点和了解)
首先我们需要知道的是利用现有框架做一个人脸识别系统并不难,然后就开始我们的系统开发吧。
我们的系统主要分为三个部分,然后我还会提出对补获图片不能添加中文的解决方案。我们需要完成的任务:1.人脸检测和数据收集2.训练识别器3.人脸识别和显示
在读此篇文章之前我相信你已经做了python环境部署和opencv模块的下载安装工作,现在我们还需要的模块是pillow(树莓派默认带有此模块,但如果你用的是win系统可能还需要另外安装,在终端输入pip install pillow即可),和opencv-contrib模块,cv2的face模块包含在内(当然我的Linux系统的树莓派貌似仍然默认包含了此模块,所以如果你是用的pc可能需要另外下载),以及最基本的numpy模块。
在开始写代码之前我们首先需要在当前运行目录中添加两个文件夹,dataset用于存放捕获到的人脸图像,方便后面训练识别器,trainer文件夹则存放了训练结果
一。人脸检测和数据收集
#数据采集 cam = cv2.VideoCapture(0)#补获图片 cam.set(3, 640) # set video width cam.set(4, 480) # set video height face_detector = cv2.CascadeClassifier('haarcascade_frontalface_default.xml')#导入分类器 # For each person, enter one numeric face id face_id = input('\n 输入用户id') print("\n 数据采集中,请正视摄像头轻微扭转") # Initialize individual sampling face count count = 0 while(True): ret, img = cam.read()#ret为是否成功读取,是一个布尔值 gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)#转化为灰度图 faces = face_detector.detectMultiScale(gray, 1.3, 5,minSize=(100,100)) for (x,y,w,h) in faces:#此处faces是一个array数组或空的元组,原因我后面会分析 cv2.rectangle(img, (x,y), (x+w,y+h), (255,0,0), 2) count += 1 # Save the captured image into the datasets folder cv2.imwrite("dataset/User." + str(face_id) + '.' + str(count) + ".jpg", gray[y:y+h,x:x+w]) cv2.imshow('image', img) k = cv2.waitKey(100) & 0xff # Press 'ESC' for exiting video if k == 27: break elif count >= 10: # Take 10 face sample and stop video break # Do a bit of cleanup print("\n [INFO] Exiting Program and cleanup stuff") cam.release() cv2.destroyAllWindows()
在这一部分中我们完成了人脸的补获,并将其保存在了我们建立的dataset文件夹,并将每一个人的数据用特定的id表述,这样我们就能训练能识别不同人脸的识别器。cam.set这一步中第二个参数是图像的分辨率,640×480是opencv的默认分辨率,但其仍支持800×600且最大支持1280乘1024像素,即使你的摄像头最大允许分辨率远大于这个值,opencv貌似仍不会允许你使用。之后就是haar级联分类器的导入,在配置过程中,我们已经下载了opencv自带的分类器,你只需要在文件管理器中查找haarcascade_frontalface_default.xml这个文件即可,在这个文件所在的文件夹中有许多分类器,当然如果你要识别例如苹果香蕉等物体你可能需要训练新的分类器(这也很容易做到),本文讨论的是人脸识别,因此这里不再赘述,你可以选择用其绝对路径导入,当然你也可以像我一样将其复制到你当前目录中。然后进入循环,图片的读取->转灰度图,然后是使用你已经导入的分类器识别人脸并将其用方框标出,然后将方框内的图片储存入dataset文件夹中。值得一提的是,因为分类器的算法是很慢的,所以分类器本身就有减帧处理(即使这样我的树莓派带人脸识别系统仍然很吃力),所以faces应该是大部分时间都是空的元组,小部分时间是读取到的array数组,因此你需要特别注意缩进问题,不要让分类器本身的减帧影响你视频读取并显示的帧数。
二.训练识别器
import cv2 #训练器 import numpy as np from PIL import Image import os # Path for face image database path = 'dataset' recognizer = cv2.face.LBPHFaceRecognizer_create()#识别器的导入 detector = cv2.CascadeClassifier("haarcascade_frontalface_default.xml") # function to get the images and label data def getImagesAndLabels(path): imagePaths = [os.path.join(path,f) for f in os.listdir(path)]#在这里os模块可以帮助我们很好的建立路径,建议可以先查看一下相关函数的使用方法。 faceSamples=[] ids = [] for imagePath in imagePaths: PIL_img = Image.open(imagePath).convert('L') #转化为灰度图 img_numpy = np.array(PIL_img,'uint8')#转化为数组 id = int(os.path.split(imagePath)[-1].split(".")[1]) faces = detector.detectMultiScale(img_numpy) for (x,y,w,h) in faces: faceSamples.append(img_numpy[y:y+h,x:x+w]) ids.append(id) return faceSamples,ids print ("\n 训练数据中,请稍后") faces,ids = getImagesAndLabels(path) recognizer.train(faces, np.array(ids)) # Save the model into trainer/trainer.yml recognizer.write('trainer/trainer.yml') # recognizer.save() worked on Mac, but not on Pi # Print the numer of faces trained and end program print("\n {0} 张脸训练完毕. 程序关闭".format(len(np.unique(ids))))
在这一步我们需要对识别器按照不同id分别训练并保存结果,并将结果汇总写入trainer文件夹中命名为trainer.yml。这个文件里就是训练好的识别器。
三.人脸识别和显示
# -*- coding: UTF-8 -*- #识别器 import cv2 import numpy as np import os from PIL import Image, ImageFont, ImageDraw path_to_ttf = 'C:\Windows\Fonts\Microsoft YaHei UI\msyh.ttc'#ttc文件是支持汉语的字体,稍后我会说明。 font1= ImageFont.truetype(path_to_ttf, size=20) recognizer = cv2.face.LBPHFaceRecognizer_create() recognizer.read('trainer/trainer.yml')#读取识别器 cascadePath = "haarcascade_frontalface_default.xml" faceCascade = cv2.CascadeClassifier(cascadePath); font = cv2.FONT_HERSHEY_SIMPLEX #iniciate id counter id = 0 # names related to ids: example ==> Marcelo: id=1, etc names = ['None', '段林晨', 'Paula', 'Ilza', 'Z', 'W']#因为我们不会在人脸识别时只显示你的代号而是要显示你的具体信息。 cam = cv2.VideoCapture(0) cam.set(3, 640) # set video widht cam.set(4, 480) # set video height #最小识别的脸的大小,在识别过程中,我们不会捕获到距离你很远的街上路人的信息,这会导致很多问题,因此我们只需要识别想参与识别的人,而设置最小人脸识别大小可以规避这一点 minW = 0.1*cam.get(3) minH = 0.1*cam.get(4) while True: ret, img =cam.read() gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY) faces = faceCascade.detectMultiScale( gray, scaleFactor = 1.2, minNeighbors = 5, minSize = (int(minW), int(minH)), )#相关参数设置可以自行搜索 for(x,y,w,h) in faces: cv2.rectangle(img, (x,y), (x+w,y+h), (0,255,0), 2) id, confidence = recognizer.predict(gray[y:y+h,x:x+w]) # Check if confidence is less them 100 ==> "0" is perfect match if (confidence < 100): id = names[id] confidence = " {0}%".format(round(100 - confidence))#confidence是置信度指数,等于100-概率,相信大家的概率统计一定比我优秀 else: id = "未识别" confidence = " {0}%".format(round(100 - confidence)) img=Image.fromarray(img) draw = ImageDraw.Draw(img) draw.text(xy=(x+5,y-5), text=str(id), font=font1,fill=(255,255,255)) img=np.array(img) cv2.putText(img, str(confidence), (x+5,y+h-5), font, 1, (255,255,0), 1) cv2.imshow('camera',img) k = cv2.waitKey(10) & 0xff # Press 'ESC' for exiting video if k == 27: break # Do a bit of cleanup print("\n [INFO] Exiting Program and cleanup stuff") cam.release() cv2.destroyAllWindows()
在这一步中,很遗憾的是cv2.putText函数并不支持汉语的应用,即你不能通过这个函数将汉语姓名显示在视频中,虽然你可能会有英文名或干脆用汉语拼音,但这个问题我们必须要解决。因此我们在这里引入了pillow模块,我们只需要使用img.draw功能在图片上先打出你的姓名,再进行cv2.putText函数就能很好的解决这个问题,但是比较麻烦的是这两个函数涉及到的图片类型是不一样的,因此我在代码中对img和array图像进行了转换,最终完成了人脸识别系统。
加载全部内容