基于opencv的人脸识别和检测

人脸识别作为一个热门项目,目前有多种方法实现,利用python和opencv来实现,是一个比较简单的项目。

1.环境配置

windows平台

python版本:3.8.6

pycharm版本:2019.1.2

Opencv版本:4.5.3

安装了python后,在命令行直接安装opencv库

pip install opencv-python

然后再去opencv官网,下载软件源码,如果下载速度过慢,可以选择网盘下载

2.实现思路

1.读取图片

2.灰度转换

3.修改图片大小

4.确定识别范围(脸)

5.实现人脸检测

6.多个目标的同时检测

7.对视频流实现检测

8.收集数据(录有带有人脸图片)

10.训练数据

11.实现人脸识别

3.具体实现

1.读取图片

#导入cv模块
import cv2 as cv
#读取图片
img = cv.imread('face1.jpg')
#显示图片
cv.imshow('show_img',img)
#等待,0表示无限等待后关闭窗口
cv.waitKey(0)
#释放内存
cv.destroyAllWindows()

cv的自带函数用起来很方便,imread,imshow函数等

2.灰度转换

#导入cv模块
import cv2 as cv
#读取图片
img = cv.imread('face2.jpg')
#灰度转换,使用到了cvt函数,其中img为图片参数,第二个为转换的颜色参数
gray_img = cv.cvtColor(img,cv.COLOR_BGR2GRAY)
#显示灰度图片
cv.imshow('gray_img',gray_img)
#保存灰度图片
cv.imwrite('gray_face2.jpg',gray_img)
#显示图片
cv.imshow('show_img',img)
#等待
cv.waitKey(0)
#释放内存
cv.destroyAllWindows()

3.修改图片大小

#导入cv模块
import cv2 as cv
#读取图片
img = cv.imread('face2.jpg')
#修改尺寸,使用到resize函数,参数为图片名和修改后的大小
resize_img = cv.resize(img,dsize=(200,200))
#显示原图
cv.imshow('img',img)
#显示修改后的
cv.imshow('resize_img',resize_img)
#打印原图尺寸大小
print('未修改:',img.shape)
#打印修改后的大小
print('修改后:',resize_img.shape)
#等待关闭窗口
while True:
    if ord('g') == cv.waitKey(0):
        break
#释放内存
cv.destroyAllWindows()

 这一步中,我们可以自主选择是否关闭窗口,采用一个if语句,如果我们按下g键,关闭窗口

4.确定识别范围(脸)

#导入cv模块
import cv2 as cv
#读取图片
img = cv.imread('face2.jpg')
#坐标,确定识别的起始像素点位置坐标,识别的高度和宽度
x,y,w,h = 100,100,100,100
#绘制矩形,使用到rectangle函数,参数img为绘制的矩形位置和大小,边框颜色(bgr),边框粗细
cv.rectangle(img,(x,y,x+w,y+h),color=(0,0,255),thickness=2)
#绘制圆形,使用circle函数,参数为圆心,半径
cv.circle(img,center=(x+w,y+h),radius=100,color=(255,0,0),thickness=2)
#显示
cv.imshow('re_img',img)
while True:
    if ord('g') == cv.waitKey(0):
        break
#释放内存
cv.destroyAllWindows()

5.实现人脸检测

调用opencv训练好的分类器和自带的检测函数检测人脸人眼等的步骤简单直接:

1.加载分类器,当然分类器事先要放在工程目录中去。分类器本来的位置是在*\opencv\sources\data\haarcascades(harr分类器,也有其他的可以用,也可以自己训练)

2.调用detectMultiScale()函数检测,调整函数的参数可以使检测结果更加精确。

3.把检测到的人脸等用矩形(或者圆形等其他图形)画出来。

#导入cv模块
import cv2 as cv
#定义检测函数
def face_detect_demo():
    #灰度转换
    gary = cv.cvtColor(img,cv.COLOR_BGR2GRAY)
    #加载调用opencv自带分类器cv.CascadeClassifier,减少训练成本
    face_detect = cv.CascadeClassifier('D:/Opencv453/opencv/sources/data/haarcascades/haarcascade_frontalface_alt2.xml')
    #调用函数,后两个参数表示检测的矩形范围。
    face = face_detect.detectMultiScale(gary,1.01,5,0,(100,100),(300,300))
    #绘制检测范围
    for x,y,w,h in face:
        cv.rectangle(img,(x,y),(x+w,y+h),color=(0,0,255),thickness=2)
    cv.imshow('face_detect',img)
#读取图像
img = cv.imread('face2.jpg')
#检测函数
face_detect_demo()
#等待
while True:
    if ord('g') == cv.waitKey(0):
        break
#释放内存
cv.destroyAllWindows()

 这一步采用了opencv自带的分类器,Haar特征分类器就是一个XML文件,该文件中会描述人体各个部位的Haar特征值。包括人脸、眼睛、嘴唇等等。加载分类器的步骤如下:

加载分类器cv.CascadeClassifier

找到opencv的安装目录,如下图

在har下提供大量的分类器

我们先把路径复制,粘贴到

再把我们需要使用的分类器的名称复制

粘贴到

再把路径中的符号更改

同时使用到opencv中的face_detect.detectMultiScale函数

总共有5个参数

1.image表示的是要检测的输入图像(一般为灰度图,加快检测速度)

2.objects表示检测到的人脸目标序列,检测物体的矩形框向量组

3.scaleFactor表示每次图像尺寸减小的比例

4. minNeighbors表示每一个目标至少要被检测到n次才算是真的目标(因为周围的像素和不同的窗口大小都可以检测到人脸),

5.默认为0

6.minSize为目标的最小尺寸

7.minSize为目标的最大尺寸

改变上面的2,3,4参数,可以让检测到的人脸矩形框更准确

6.多个目标的同时检测

#导入cv模块
import cv2 as cv
#检测函数
def face_detect_demo():
    #灰度转换
    gary = cv.cvtColor(resize_img,cv.COLOR_BGR2GRAY)
    face_detect = cv.CascadeClassifier('D:/Opencv453/opencv/sources/data/haarcascades/haarcascade_frontalface_default.xml')
    face = face_detect.detectMultiScale(gary)
    for x,y,w,h in face:
        cv.rectangle(resize_img,(x,y),(x+w,y+h),color=(0,0,255),thickness=2)
    cv.imshow('multi_face_detect',resize_img)
img = cv.imread('duoren.jpg')
resize_img = cv.resize(img,dsize=(2048,1024))
print('未修改:',img.shape)
print('修改后:',resize_img.shape)
#检测函数
face_detect_demo()
#等待
while True:
    if ord('g') == cv.waitKey(0):
        break
#释放内存
cv.destroyAllWindows()

此步导入多目标图片即可

7.对视频流实现检测

#导入cv模块
import cv2 as cv
#检测函数
def face_detect_demo(img):
    gary = cv.cvtColor(img,cv.COLOR_BGR2GRAY)
    face_detect = cv.CascadeClassifier('D:/Opencv453/opencv/sources/data/haarcascades/haarcascade_frontalface_default.xml')
    face = face_detect.detectMultiScale(gary)
    for x,y,w,h in face:
        cv.rectangle(img,(x,y),(x+w,y+h),color=(0,0,255),thickness=2)
    cv.imshow('result',img)

#读取摄像头,参数为0,调用默认摄像头
#cap = cv.VideoCapture(0)
#读取视频,按帧数读取
cap = cv.VideoCapture("test1.mp4")
#循环
while True:
    #调用cap.read函数
    flag,frame = cap.read()
    if not flag:
        break
    face_detect_demo(frame)
    if ord('g') == cv.waitKey(1):
        break
#释放内存
cv.destroyAllWindows()
#释放摄像头
cap.release()

8.收集数据(录有带有人脸图片)

#导入模块
import cv2
#摄像头
cap=cv2.VideoCapture(0)

falg = 1
num = 1

while(cap.isOpened()):#检测是否在开启状态
    ret_flag,Vshow = cap.read()#得到每帧图像
    cv2.imshow("Capture_Test",Vshow)#显示图像
    k = cv2.waitKey(1) & 0xFF#按键判断
    if k == ord('s'):#保存
       cv2.imwrite("E:/PycharmProjects/facer/opencv/data/facedata/"+str(num)+".lumengyun"+".jpg",Vshow)
       print("success to save"+str(num)+".jpg")
       print("-------------------")
       num += 1
    elif k == ord(' '):#退出
        break
#释放摄像头
cap.release()
#释放内存
cv2.destroyAllWindows()
#将人脸照片保存后,后续可以通过特征提取,识别人脸

 当然,也可以直接将数据把保存在待训练数据集data里

10.训练数据 

这一步就是将前面几步录入人脸数据进行训练,通过将人脸特征和人脸的ID对应起来,完成人脸识别,每张图片都有一个ID

在data里面的facedata作为我们的待训练数据,获取到facedata

使用函数getImageAndLabels,得到图片的特征faces和ID(ids)

再用cv2.face.LBPHFaceRecognizer_create()函数,得到训练对象。

FaceRecognizer类,里面有相关的一些人脸识别的算法及函数接口

LBP是一种特征提取方式,能提取出图像的局部的纹理特征,最开始的LBP算子是在3X3窗口中,取中心像素的像素值为阀值,与其周围八个像素点的像素值比较,若像素点的像素值大于阀值,则此像素点被标记为1,否则标记为0。这样就能得到一个八位二进制的码,转换为十进制即LBP码,于是得到了这个窗口的LBP值,用这个值来反映这个窗口内的纹理信息。LBPH是在原始LBP上的一个改进,在opencv支持下我们可以直接调用函数直接创建一个LBPH人脸识别的模型。

再利用我们recognizer对象.去训练整合(train)之前得到的图像特征和图像ID(faces,np.array(ids))

最后将训练后的数据(向量)保存到trainer.yml,以后进行人脸识别,直接调用这个文件就可以了

函数getImageAndLabels的实现原理

首先先建立数组来存储需要的人脸特征相关数据和ID相关数据

再把所有的信息通过路径存储在IMagePaths中

加载分类器

再对上面保存的所有信息,进行一个遍历读取   for imagePath in imagePaths:

首先

#打开图片,以灰度化方式打开。PIL_img=Image.open(imagePath).convert('L')

PIL有九种不同模式:1,L,P,RGB,RGBA,CMYK,YCbCr,I,F 

#图片以灰度表示后,就是用数字来表示,这时候对它进行向量化,将图像转换为数组,以黑白深浅 。img_numpy=np.array(PIL_img,'uint8')

#获取图片人脸特征 。faces = face_detector.detectMultiScale(img_numpy)

#获取每张图片的id和姓名  id = int(os.path.split(imagePath)[1].split('.')[0])

#这里要注意预防无面容照片

for x,y,w,h in faces:
       ids.append(id)
       facesSamples.append(img_numpy[y:y+h,x:x+w])

 #打印脸部特征和id。print('id:', id),print('fs:', facesSamples)

最后将这些遍历的数据,全部都return到faces和ids中,得到全部训练后的数据集

import os
import cv2
import sys
from PIL import Image
import numpy as np

def getImageAndLabels(path):
    facesSamples=[]
    ids=[]
    imagePaths=[os.path.join(path,f) for f in os.listdir(path)]
    #检测人脸
    face_detector = cv2.CascadeClassifier('D:/Opencv453/opencv/sources/data/haarcascades/haarcascade_frontalface_alt2.xml')
    #打印数组imagePaths
    print('数据排列:',imagePaths)
    #遍历列表中的图片
    for imagePath in imagePaths:
        #打开图片,灰度化。PIL有九种不同模式:1,L,P,RGB,RGBA,CMYK,YCbCr,I,F
        PIL_img=Image.open(imagePath).convert('L')
        # PIL_img = cv2.resize(PIL_img, dsize=(400, 400))
        # 向量化,将图像转换为数组,以黑白深浅
        img_numpy=np.array(PIL_img,'uint8')
        #获取图片人脸特征
        faces = face_detector.detectMultiScale(img_numpy)
        #获取每张图片的id和姓名
        id = int(os.path.split(imagePath)[1].split('.')[0])
        #预防无面容照片
        for x,y,w,h in faces:
            ids.append(id)
            facesSamples.append(img_numpy[y:y+h,x:x+w])
        #打印脸部特征和id
        #print('fs:', facesSamples)
        print('id:', id)
        #print('fs:', facesSamples[id])
    print('fs:', facesSamples)
    #print('脸部例子:',facesSamples[0])
    #print('身份信息:',ids[0])
    return facesSamples,ids

if __name__ == '__main__':
    #图片路径
    path='./data/facedata/'
    #获取图像数组和id标签数组和姓名
    faces,ids=getImageAndLabels(path)
    #获取训练对象
    recognizer=cv2.face.LBPHFaceRecognizer_create()
    #recognizer.train(faces,names)#np.array(ids)
    recognizer.train(faces,np.array(ids))
    #保存文件
    recognizer.write('trainer/trainer.yml')
    #save_to_file('names.txt',names)

 此步是核心,决定人脸识别的准确与否,

11.实现人脸识别

将我们训练后存放在yml文件中的数据,导入到recognizer中,用这个文件来判断我们脸部特征是否符合,给出可执行评分

name = [] 存放我们训练数据图片文件名中,第一个.后面的字符,定义为名字

face_detect_demo中和上面的作用差不多,先把图片灰度化,然后加载分类器,然后进行检测,将整张图片中的人脸部分框起来,这部分就是我们得到的人脸照片。

得到的人脸照片,我们需要进行一个预测评分recoginzer.predict,得到的评分就是confidence

根据这个评分,来进行人脸识别,如果confidence过大,说明程序没有对这个人脸进行训练,无法通过验证,给出unknown提示

confidence未达到预警值,说明这个人脸被我们识别过,这是把上一步中训练识别得到的name打到矩形框上,给出人名。

import cv2
import numpy as np
import os
# coding=utf-8
import urllib
import urllib.request
import hashlib

# 加载训练数据集文件
recognizer = cv2.face.LBPHFaceRecognizer_create()
recoginzer.read('trainer/trainer.yml')
# 名称
names = []
warningtime = 0

# 准备识别的图片
def face_detect_demo(img):
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)  # 转换为灰度
    face_detector = cv2.CascadeClassifier(
        'D:/Opencv453/opencv/sources/data/haarcascades/haarcascade_frontalface_alt2.xml')
    face = face_detector.detectMultiScale(gray, 1.1, 5, cv2.CASCADE_SCALE_IMAGE, (100, 100), (300, 300))
    # face=face_detector.detectMultiScale(gray)
    for x, y, w, h in face:
        cv2.rectangle(img, (x, y), (x + w, y + h), color=(0, 0, 255), thickness=2)
        cv2.circle(img, center=(x + w // 2, y + h // 2), radius=w // 2, color=(0, 255, 0), thickness=1)
        # 人脸识别
        ids, confidence = recoginzer.predict(gray[y:y + h, x:x + w])
        # print('标签id:',ids,'置信评分:', confidence)
        if confidence > 80:
            global warningtime
            warningtime += 1
            if warningtime > 100:
                warningtime = 0
            cv2.putText(img, 'unkonw', (x + 10, y - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.75, (0, 255, 0), 1)
        else:
            cv2.putText(img, str(names[ids - 1]), (x + 10, y - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.75, (0, 255, 0), 1)
    cv2.imshow('result', img)
    # print('bug:',ids)


def name():
    path = './data/facedata/'
    # names = []
    imagePaths = [os.path.join(path, f) for f in os.listdir(path)]
    for imagePath in imagePaths:
        name = str(os.path.split(imagePath)[1].split('.', 2)[1])
        names.append(name)


cap = cv2.VideoCapture(0)
name()
while True:
    flag, frame = cap.read()
    if not flag:
        break
    face_detect_demo(frame)
    if ord(' ') == cv2.waitKey(10):
        break
cv2.destroyAllWindows()
cap.release()
# print(names)

  • 13
    点赞
  • 171
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值