实践——使用tensorflow与opencv-python进行人脸识别

环境说明

前文博客有讲到如何安装anaconda,这是一个非常好用的环境管理工具。在这里我将使用他来快速构建环境。为了提升下载速度,建议为anaconda换清华源,操作步骤请阅读官网:Anaconda 镜像使用帮助https://mirror.tuna.tsinghua.edu.cn/help/anaconda/

python与tensorflow版本

之前博文中安装的python版本为3.7,对应的tensorflow为2.4.1,导致numpy版本与其他包冲突,降级为python3.6与tensorflow2.1.0可以完美解决问题。若仍出现问题,可以对numpy降级为1.19.5。在使用pip卸载,安装指定版本的numpy时,可能会出现红色警告,警告中会详细指明哪些库的版本不对,需按照提示逐一修改版本。
可能用到的指令:(其余有问题的库类似,印象中有3个库有问题,six和scipy,scikit-learn都经历过类似下面的操作)

pip show numpy
pip uninstall numpy
pip install numpy==1.19.5

安装环境

如果你已经安装好环境,并且没有或解决了上述问题,请直接跳过此部分,可以开始实现人脸识别的代码了,如果没有,我将简要讲述如何构建环境。

1.创建py环境

conda create –n tf python=3.6
conda activate tf

2.安装tensorflow

conda install tensorflow

conda安装tensorflow
这里建议大家不再使用pip下载tensorflow,conda会帮你一键搞定cudann的匹配问题,并为你选择合适的tensorflow版本

3.安装本文用到的库

conda activate tf
pip install numpy matplotlib scikit-learn opencv-python

如果后续出现库版本不匹配的问题,请参考最开始的解决方法。

实现步骤

采集人脸

我们首先要解决的是深度学习需要的数据问题,我采用opencv进行图像处理。并且opencv有官方训练好的参数用于检验是否是人脸,文件haarcascade_frontalface_alt2.xml的下载连接:https://blog.csdn.net/songjinxaing/article/details/79958082
你需要将次xml文件放在代码的文件夹下,或者记住他的绝对路径,然后在代码所在的文件夹下新建data文件夹,用于存放照片。不同的人存放于不同的文件夹下如图:(代码在tensorflow中)
在这里插入图片描述
关于opencv这里不再细讲,通过注释应该可以读懂逻辑。下面代码将会调用笔记本的摄像头为你采集1000张照片用于人脸识别,当然部分照片会截的不好需要你进行筛选,将只有半张脸的照片删除。

#这是take_pictures.py
import cv2
import sys
 
from PIL import Image
 
def CatchPICFromVideo(window_name, camera_idx, catch_pic_num, path_name):
    cv2.namedWindow(window_name)
    
    #视频来源,可以来自一段已存好的视频,也可以直接来自USB摄像头
    cap = cv2.VideoCapture(camera_idx)                
    
    #告诉OpenCV使用人脸识别分类器
    classfier = cv2.CascadeClassifier("E:\\DeepLearning\\tensorflow\\haarcascade_frontalface_alt2.xml")
    
    #识别出人脸后要画的边框的颜色,RGB格式
    color = (0, 255, 0)
    
    num = 0    
    while cap.isOpened():
        ok, frame = cap.read() #读取一帧数据
        if not ok:            
            break                
    
        grey = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)  #将当前桢图像转换成灰度图像            
        
        #人脸检测,1.2和2分别为图片缩放比例和需要检测的有效点数
        faceRects = classfier.detectMultiScale(grey, scaleFactor = 1.2, minNeighbors = 3, minSize = (32, 32))
        if len(faceRects) > 0:          #大于0则检测到人脸                                   
            for faceRect in faceRects:  #单独框出每一张人脸
                x, y, w, h = faceRect                        
                
                #将当前帧保存为图片
                img_name = '%s/%d.jpg'%(path_name, num)                
                image = frame[y - 10: y + h + 10, x - 10: x + w + 10]
                cv2.imwrite(img_name, image)                                
                                
                num += 1                
                if num > (catch_pic_num):   #如果超过指定最大保存数量退出循环
                    break
                
                #画出矩形框
                cv2.rectangle(frame, (x - 10, y - 10), (x + w + 10, y + h + 10), color, 2)
                
                #显示当前捕捉到了多少人脸图片了,这样站在那里被拍摄时心里有个数,不用两眼一抹黑傻等着
                font = cv2.FONT_HERSHEY_SIMPLEX
                cv2.putText(frame,'num:%d' % (num),(x + 30, y + 30), font, 1, (255,0,255),4)                
        
        #超过指定最大保存数量结束程序
        if num > (catch_pic_num): break                
                       
        #显示图像
        cv2.imshow(window_name, frame)        
        c = cv2.waitKey(10)
        if c & 0xFF == ord('q'):
            break        
    
    #释放摄像头并销毁所有窗口
    cap.release()
    cv2.destroyAllWindows() 
    
if __name__ == '__main__':
    if len(sys.argv) != 1:
        print("Usage:%s camera_id face_num_max path_name\r\n" % (sys.argv[0]))
    else:
        CatchPICFromVideo("截取人脸", 0, 1000, 'E:\\DeepLearning\\tensorflow\\data\\liujiexuan')

数据处理与网络搭建,训练

数据处理

首先定义一个类Dataset,他包含两个方法,初始化__init__(),载入数据load()

初始化时我们规定三个数据集,验证集,训练集,还有测试集。以及照片所在路径

load()中,我们需要对数据进行预处理。首先把数据分类,使用sklearn库中的train_test_split函数随机挑选样本形成三个数据集,并将其转化为浮点数再除以255进行归一化。

网络搭建与训练

定义一个类Model,首先定义初始化方法__init__(),下来build_mode()方法定义网络结构,train()方法用于编译,训练网络。save_model(),load_model()方法用来保存和载入网络。evaluate()方法可以评估模型准确度。face_predict()方法可以对单张图片进行预测。

# 这是face_train_tensorflow.py
# TensorFlow and tf.keras
from numpy.lib.function_base import select
import tensorflow as tf
from tensorflow import keras
from tensorflow.python.keras.utils.np_utils import to_categorical
from tensorflow.keras import datasets, layers, models
# Helper libraries
import numpy as np
import matplotlib.pyplot as plt
import random
from sklearn.model_selection import train_test_split
#from sklearn.model_selection import train_test_split

# load_data.py
from load_data import load_dataset, resize_image, IMAGE_SIZE


class Dataset:
    def __init__(self, path_name):
        #训练集
        self.train_images = None
        self.train_labels = None
        
        #验证集
        self.valid_images = None
        self.valid_labels = None
        
        #测试集
        self.test_images  = None            
        self.test_labels  = None
        
        #数据集加载路径
        self.path_name    = path_name
        
        #当前库采用的维度顺序
        self.input_shape = None
        
    #加载数据集并按照交叉验证的原则划分数据集并进行相关预处理工作
    def load(self, img_rows = IMAGE_SIZE, img_cols = IMAGE_SIZE, 
             img_channels = 3, nb_classes = 2):
        #加载数据集到内存
        images, labels = load_dataset(self.path_name)        
        
        train_images, valid_images, train_labels, valid_labels = train_test_split(images, labels, test_size = 0.3, random_state = random.randint(0, 100))        
        _, test_images, _, test_labels = train_test_split(images, labels, test_size = 0.5, random_state = random.randint(0, 100))                
        
        train_images = train_images.reshape(train_images.shape[0], img_rows, img_cols, img_channels)
        valid_images = valid_images.reshape(valid_images.shape[0], img_rows, img_cols, img_channels)
        test_images = test_images.reshape(test_images.shape[0], img_rows, img_cols, img_channels)
        self.input_shape = (img_rows, img_cols, img_channels)            
        
        #输出训练集、验证集、测试集的数量
        print(train_images.shape[0], 'train samples')
        print(valid_images.shape[0], 'valid samples')
        print(test_images.shape[0], 'test samples')
                 
    
        #像素数据浮点化以便归一化
        train_images = train_images.astype('float32')            
        valid_images = valid_images.astype('float32')
        test_images = test_images.astype('float32')
        
        #将其归一化,图像的各像素值归一化到0~1区间
        train_images /= 255
        valid_images /= 255
        test_images /= 255            
    
        self.train_images = train_images
        self.valid_images = valid_images
        self.test_images  = test_images
        self.train_labels = train_labels
        self.valid_labels = valid_labels
        self.test_labels  = test_labels

class Model:
    def __init__(self):
        self.model = None
    
    def build_model(self, dataset, nb_classes = 2):
        self.model = models.Sequential()
        self.model.add(layers.Conv2D(32, (3, 3), activation='relu',padding ="same", input_shape = dataset.input_shape))
        self.model.add(layers.Conv2D(32, (3, 3), activation='relu'))
        self.model.add(layers.MaxPooling2D((2, 2)))
        self.model.add(layers.Dropout(rate=0.25))
        self.model.add(layers.Conv2D(64, (3, 3), activation='relu'))
        self.model.add(layers.Conv2D(64, (3, 3), activation='relu'))
        self.model.add(layers.MaxPooling2D((2, 2)))
        self.model.add(layers.Dropout(rate=0.25))

        self.model.add(layers.Flatten())
        self.model.add(layers.Dense(512, activation='relu'))
        self.model.add(layers.Dropout(rate=0.5))
        self.model.add(layers.Dense(nb_classes))
        
        #输出模型概况
        self.model.summary()


    def train(self,dataset,epochs=10):
        self.model.compile(optimizer='adam',
            loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),
            metrics=['accuracy'])
        self.model.fit(dataset.train_images, dataset.train_labels, epochs, 
                validation_data=(dataset.valid_images, dataset.valid_labels))
    

    MODEL_PATH = './ljx_face_model.h5'
    def save_model(self, file_path = MODEL_PATH):
        self.model.save(file_path)
    
    def load_model(self, file_path = MODEL_PATH):
        self.model = models.load_model(file_path)
        self.model.summary()
    
    def evaluate(self, dataset):
        score = self.model.evaluate(dataset.test_images, dataset.test_labels, verbose = 1)
        print("%s: %.2f%%" % (self.model.metrics_names[1], score[1] * 100))
    
    def face_predict(self,image):    
        #依然是根据后端系统确定维度顺序
        image = resize_image(image)
        image = image.reshape((1, IMAGE_SIZE, IMAGE_SIZE, 3))                    
        
        #浮点并归一化
        image = image.astype('float32')
        image /= 255
        
        #给出输入属于各个类别的概率,我们是二值类别,则该函数会给出输入图像属于0和1的概率各为多少
        result = self.model.predict_proba(image)
        print('result:', result)
        
        #给出类别预测:0或者1
        result = self.model.predict_classes(image)        

        #返回类别预测结果
        return result[0]
    
        
dataset = Dataset('E:\\DeepLearning\\tensorflow\\data')    
dataset.load()
model = Model()
#先前添加的测试build_model()函数的代码
model.build_model(dataset)
 
#测试训练函数的代码
model.train(dataset)
model.save_model(file_path='./ljx_face_model.h5')
model.load_model(file_path='./ljx_face_model.h5')
model.evaluate(dataset)  

使用网络进行预测

#这是face_con.py
import cv2
import sys
import gc
from face_train_tensorflow import Model

model = Model()
model.load_model(file_path = 'E:\\DeepLearning\\tensorflow\\ljx_face_model.h5')    

#框住人脸的矩形边框颜色       
color = (0, 255, 0)

#捕获指定摄像头的实时视频流
cap = cv2.VideoCapture(0)

#人脸识别分类器本地存储路径
cascade_path = "E:\DeepLearning\tensorflow\haarcascade_frontalface_alt2.xml"    

#循环检测识别人脸
while True:
    ret, frame = cap.read()   #读取一帧视频
    
    # if ret is True:
        
    #     #图像灰化,降低计算复杂度
    #     frame_gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
    # else:
    #     continue

    grey = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
    #使用人脸识别分类器,读入分类器
    classfier = cv2.CascadeClassifier("E:\\DeepLearning\\tensorflow\\haarcascade_frontalface_alt2.xml")             

    #利用分类器识别出哪个区域为人脸
    faceRects = classfier.detectMultiScale(grey, scaleFactor = 1.1, minNeighbors = 4, minSize = (32, 32))
    if len(faceRects) > 0:                 
        for faceRect in faceRects: 
            x, y, w, h = faceRect
            
            #截取脸部图像提交给模型识别这是谁
            image = frame[y - 10: y + h + 10, x - 10: x + w + 10]
            # faceID = face_predict(image)   
            faceID = model.face_predict(image) 
            #如果是“我”
            if faceID == 0:                                                        
                cv2.rectangle(frame, (x - 10, y - 10), (x + w + 10, y + h + 10), color, thickness = 2)
                
                #文字提示是谁
                cv2.putText(frame,'liujiexuan', 
                            (x + 30, y + 30),                      #坐标
                            cv2.FONT_HERSHEY_SIMPLEX,              #字体
                            1,                                     #字号
                            (255,0,255),                           #颜色
                            2)                                     #字的线宽
            else:
                cv2.putText(frame,'liuji', 
                            (x + 30, y + 30),                      #坐标
                            cv2.FONT_HERSHEY_SIMPLEX,              #字体
                            1,                                     #字号
                            (255,0,255),                           #颜色
                            2)                                     #字的线宽
                        
    cv2.imshow("face_predict", frame)
    
    #等待10毫秒看是否有按键输入
    k = cv2.waitKey(10)
    #如果输入q则退出循环
    if k & 0xFF == ord('q'):
        break

#释放摄像头并销毁所有窗口
cap.release()
cv2.destroyAllWindows()

简单总结

前任栽树,后人乘凉。感谢就是这个七昂的博客分享
本文使用tensorflow2进行复现,改变了网络变异和训练方法,但大体结构没有改变。

  • 1
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值