基于opencv的简单人脸识别实现

最近开始接触机器学习,虽然还在入门阶段,但还是想实际动手操作一下,于是就着手一个比较简单的项目:基于opencv的人脸识别。它不仅能找到人脸,还可以显示是否是之前录入过的人脸,以达到真正的识别效果。

实现的过程如下:

  • 摄像头获取实时画面(可以换成视频)
  • 捕捉人脸,保存人脸信息,打上标签(相当于对样本做标记,有了标记的样本又称为样例)
  • 使用上述样例的训练集训练,得到自己训练的yml文件(?)
  • 之后就可以使用自己的分类器对视频内的人脸进行对比识别,如果是则输出名字,不是则显示未知。

首先要做一个自己的模型,录入人脸后进行训练。

首先是录入若干人脸图像(faceImgWrite.py):

import cv2 as cv
import os


# 人脸图像保存处,文件夹的路径
myFacePath = os.path.join(os.path.dirname(__file__), 'src/imwrite/me')


def writePic():
    """
        写入人脸数据,并做标记
    :return: None 不返回任何值
    """
    num = 1  # 计数变量
    cap = cv.VideoCapture(0)  # 打开摄像头
    while cap.isOpened():  # 摄像头打开时始终循环
        
        flag, frame = cap.read()  # flag: 有没有截取到图片(bool); frame: 截取到的每一帧图片

        cv.imshow('figure', frame)  # 在名为figure的窗口里显示视频
        
        k = cv.waitKey(1) & 0xFF  # 取按键的ASCII值后8位,降干扰(?)
        if k == ord('s'):  # 按 s 保存当前图片帧,并给个提示
            cv.imwrite(os.path.join(myFacePath, f'{num}.me.jpg'), frame)
            print(f'成功保存{num}.me.jpg')
            num += 1
        elif k == ord('q'):  # 按 q 退出
            break
    
    # 释放内存
    cap.release()
    cv.destroyAllWindows()


if __name__ == '__main__':
    writePic()  # 执行当前文件,测试的时候用,后面省略

接下来使用保存的人脸图片集做训练(dataTraining.py)

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


def getImgAndLabels(path):
    """
        对已保存的人脸图片样本集进行标记
    :param path: 图片样本集文件夹路径
    :return:
    """

    # 创建空列表
    facesSamples = []
    ids = []
    imagePaths = []

    # 将文件夹里的所有图片的路径整理进一个列表里
    for f in os.listdir(path):
        imagePaths.append(os.path.join(path, f))

    # 创建分类器(opencv自带)
    faceDetector = cv.CascadeClassifier('C:\\Users\\28264\\AppData\\Local\\Programs\\Python\\Python38\\Lib\\site-packages\\cv2\\data\\haarcascade_frontalface_default.xml')

    # 对每张图片都做处理
    for imgPath in imagePaths:
        PILImg = Image.open(imgPath).convert('L')
        imgNumpy = np.array(PILImg, 'uint8')
        print(imgNumpy)
        faces = faceDetector.detectMultiScale(imgNumpy)  # 检测人脸,使用默认参数,返回人脸选框的左上角坐标及长宽

        id = int(os.path.split(imgPath)[1].split('.')[0])  # 从图片名字中分离出图片索引

        # 对每张图片都抽出识别到的脸的部分
        for x, y, w, h in faces:
            ids.append(id)
            facesSamples.append(imgNumpy[y:y+h, x:x+w])
            print(id)
    return facesSamples, ids

def dataTraining():
    """
        将采集到的人脸数据集用作训练
    :return: None 不返回任何值
    """
    path = os.path.join(os.path.dirname(__file__), 'src/imwrite/me')
    faces, ids = getImgAndLabels(path)
    
    # 创建LBPH识别器,使用LBPH做训练,并保存
    recognizer = cv.face.LBPHFaceRecognizer_create()  
    recognizer.train(faces, np.array(ids))
    recognizer.write('trainer.yml')

这里使用已有的LBPH算法 cv.face.LBPHFaceRecognizer_create() 直接进行训练,关于该算法的文章可以参考基于LBPH的人脸识别系统 – 知乎 (zhihu.com)

训练出了自己的识别器(trainer.yml)后,就可以用于人脸识别了。

再单独新建一份python脚本文件存放识别功能(faceRecognize.py):

import os.path
import cv2

# 模型路径、分类器路径、创建列表
trainingData = os.path.join(os.path.dirname(__file__), 'trainer.yml')
classifierPath = "C:\\Users\\28264\\AppData\\Local\\Programs\\Python\\Python38\\Lib\\site-packages\\cv2\\data\\haarcascade_frontalface_alt2.xml"

# 标签列表
names = []
def name():
    path = os.path.join(os.path.dirname(__file__), 'src/imwrite/me')
    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)

# 创建识别器对象(自己训练的)
recognizer = cv2.face.LBPHFaceRecognizer_create()
recognizer.read(trainingData)

def faceRecognizer(cv_img):
    """
        传入opencv图片,识别人脸并标记
    :param cv_img:
    :return:
    """
    # 转换为灰度图,同样为了加快后续识别速度,RGB图会有三大组数据,而灰度图仅有一组
    grayImg = cv2.cvtColor(cv_img, cv2.COLOR_BGR2GRAY)
    # 创建人脸检测对象,仍然是使用的默认分类器 haarcascade_frontalface_alt2.xml
    faceDetector = cv2.CascadeClassifier(classifierPath)
    # 检测人脸的一行代码,返回所有的选框左上角坐标与长宽,装在一个列表里
    faceData = faceDetector.detectMultiScale(grayImg, 1.01, 5, cv2.CASCADE_SCALE_IMAGE, (100, 100), (300, 300))

    for x, y, w, h in faceData:
        # 画方框,thickness为实数
        cv2.rectangle(cv_img, (x, y), (x+w, y+h), (255, 0, 0), 2)
        # 画实心矩形,thickness为-1
        cv2.rectangle(cv_img, (x, y-20), (x+w, y), (255, 0, 0), -1)
        # 给出图像标签ID和置信度(距离?)
        ids, confidence = recognizer.predict(grayImg[y: y+h, x:x + w])
        if confidence > 80:  # 置信评分大于80则不合格,相当于离标准太远
            # 标为未知
            cv2.putText(cv_img, f'未知 ({round(confidence, 2)})', (x + 5, y - 5), cv2.FONT_ITALIC, 0.5, (255, 255, 255))
        else:
            # 标出对应标签
            cv2.putText(cv_img, f'{names[ids-1]} ({round(confidence, 2)})', (x + 5, y - 5), cv2.FONT_ITALIC, 0.5, (255, 255, 255))
    # 显示带有识别结果的视频
    cv2.imshow('figure', cv_img)


def faceRecognizeOnVideo():
    """
        人脸识别——视频模式
    :return: None 不返回任何值
    """
    # 打开摄像头
    cap = cv2.VideoCapture(0)
    # 实时显示识别结果
    while True:
        flag, frame = cap.read()
        if not flag:
            break
        # 调用上面刚码好的人脸识别函数
        faceRecognizer(frame)
        # 等待按键,按下q就终止视频
        if ord('q') == cv2.waitKey(5):
            break


def faceRecognizeOnPic(picPath):
    """
        人脸识别 图片版
    :param picPath: 图片路径
    :return: None 不返回任何值
    """
    img = cv2.imread(picPath)
    grayImg = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    faceDetecter = cv2.CascadeClassifier(classifierPath)  # 装载分类器
    faceData = faceDetecter.detectMultiScale(grayImg, 1.1, 5, cv2.CASCADE_SCALE_IMAGE, (100, 100), (300, 300))  # 进行人脸检测

    for x, y, w, h in faceData:
        # 画方框,thickness为实数
        cv2.rectangle(img, (x, y), (x+w, y+h), (255, 0, 0), 2)
        # 画实心矩形,thickness为-1
        cv2.rectangle(img, (x, y-20), (x+w, y), (255, 0, 0), -1)
        # 给出图像标签ID和置信度(距离?)
        ids, confidence = recognizer.predict(grayImg[y: y+h, x:x + w])
        if confidence > 80:  # 置信评分大于80则不合格,相当于离标准太远
            # 标为未知
            cv2.putText(img, f'other ({confidence})', (x + 5, y - 5), cv2.FONT_ITALIC, 0.5, (255, 255, 255))
        else:
            # 标出对应标签
            cv2.putText(img, f'{names[ids-1]} ({confidence})', (x + 5, y - 5), cv2.FONT_ITALIC, 0.5, (255, 255, 255))
    # 显示带有识别结果的视频
    cv2.imshow('figure', img)

    # 等待按键,如果没有此行将无法正常显示图像
    cv2.waitKey(0)

    # 释放内存
    cv2.destroyAllWindows()


if __name__ == '__main__':
    name()
    picPath = os.path.join(os.path.dirname(__file__), 'src/photo1.jpg')
    faceRecognizeOnPic(picPath)

测试的时候总共录入了100张人脸,在同样的环境下得到的效果还是不错的,能正确框住人脸,返回的置信度均小于30,但是换成其他不同环境的照片时(比如室外时拍摄的照片,光照等其他因素有所变化时),虽然也能分辨出是不是录过的人脸,但是置信度普遍偏高,一般都在75左右,而设置的阈值为80,只能勉强合格。针对这种情况,个人的改进想法有以下几点:

  • 1.增加训练时的样本数量,在不同的环境下录入人脸;
  • 2.进一步调节detectMultiScale的参数,如步长与检测次数;
  • 3.寻找其他更好的分类器,这里用的是opencv自带的分类器。

到这里只是做了初步的实现,还有很多地方值得改进。实际也不简单

其实之后想在卡通图片上做进一步的练习,收集某一个角色的画,用作样本以进行训练,然后用视频测试训练结果,看能否正确识别出画面里出现的角色是否学习过。到时候将开一篇新的文章进行记录。

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇