前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >5. 小项目之Face_detection

5. 小项目之Face_detection

作者头像
和蔼的zhxing
发布2018-09-04 11:15:49
6770
发布2018-09-04 11:15:49
举报

git地址:face_detection

功能和框架

想做的是这么一个东西:识别视频(或者摄像头获得的实时视频)中的人脸,并判断是谁(因为数据采集的原因,找了身边的5个朋友采集了一些数据),如果不是这几个人,标记为其他人。

功能上其实比较简单,主要是想体会一下这整个过程,做下来还是有很多值得注意的地方的。大致框架也比较简单:

框架

下面就分别说一下。

1.视频采集

这次的整个编程语言选择的是python,之所以选择python主要是因为要遇到CNN来进行分类,所以还是在python下容易实现一些。linux还是用的不熟,所以还是用的windows10+pycharm。

视频采集用的是opencv的模块,应该说是很简单易用的模块。c++版本的videocapture原来写过,在这里,python版本的大同小异。典型的简单使用:

代码语言:javascript
复制
#需要调用摄像头的话只需要参数设置为0就可以打开笔记本自带的摄像头
camera = cv2.VideoCapture('liuyang.mp4')
success, img = camera.read()

这样就没有问题了。

2. 人脸定位

人脸定位采用的harr特征来做的,使用的是opencv训练好的harr特征分类器,这个在opencv的源文件里有:haarcascade_frontalface_default.xml,这个文件保存的就是harr特征正脸检测的模型,是已经训练好的。

harr特征是基础图像处理领域三个基本特征之一(还有hog和Lbp)

加载特征使用的是CascadeClassifier这个分类器,是一个级联分类器。在官方文档中有定义。

Use the cv::CascadeClassifier class to detect objects in a video stream. Particularly, we will use the functions:

定义很简单,这个是c++版本的解释,python载入模型的时候:

haar = cv2.CascadeClassifier('haarcascade_frontalface_default.xml')

检测的时候一般使用的是:detectMultiScale这个函数。使用起来也比较简单,python的结果是返回在函数外边,c++版本是将Vector<Rect>作为参数传入。

代码语言:javascript
复制
CV_WRAP virtual void detectMultiScale( const Mat& image,  
                                   CV_OUT vector<Rect>& objects,  
                                   double scaleFactor=1.1,  
                                   int minNeighbors=3, int flags=0,  
                                   Size minSize=Size(),  
                                   Size maxSize=Size() ); 
const Mat& image: 需要被检测的图像(灰度图)
vector<Rect>& objects: 保存被检测出的人脸位置坐标序列
double scaleFactor: 每次图片缩放的比例
int minNeighbors: 每一个人脸至少要检测到多少次才算是真的人脸
int flags: 决定是缩放分类器来检测,还是缩放图像
Size(): 表示人脸的最大最小尺寸

python版本的使用就更加简单:

代码语言:javascript
复制
haar = cv2.CascadeClassifier('haarcascade_frontalface_default.xml')
faces = haar.detectMultiScale(gray_img, 1.3, 5)
#返回的face就是人脸的位置以及大小(左上角的位置及宽和高),相当于c++版本中的vector<Rect>

所以这样下来进行人脸定位就比较简单了。下面的这段程序不仅仅是实现了一个人脸的定位,这个实际上是我获取训练样本使用的一个程序,也是先从摄像头获取视频,然后定位人脸,把人脸图像取下来resize到64的尺寸,然后随机变换进行存储(增加样本多样性)。

代码语言:javascript
复制
import cv2
import os
import sys
import random
import time


#新建存储文件夹
out_dir = './my_faces'
if not os.path.exists(out_dir):
    os.makedirs(out_dir)


# 改变图片的对比度和亮度,增加图像多样性
def relight(img, alpha=1, bias=0):
    w = img.shape[1]
    h = img.shape[0]
    #image = []
    for i in range(0,w):
        for j in range(0,h):
            for c in range(3):
                tmp = int(img[j,i,c]*alpha + bias)
                if tmp > 255:
                    tmp = 255
                elif tmp < 0:
                    tmp = 0
                img[j,i,c] = tmp
    return img


# 获取分类器,用的是opencv的harr默认特征
haar = cv2.CascadeClassifier('haarcascade_frontalface_default.xml')

# 打开摄像头 参数为输入流,可以为摄像头或视频文件
camera = cv2.VideoCapture('liuyang.mp4')

n = 1

while 1:
    if (n <= 10000):
        print('It`s processing %s image.' % n)
        # 读帧
        start=time.time()
        success, img = camera.read()
        cv2.imshow('face',img)
        gray_img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
        faces = haar.detectMultiScale(gray_img, 1.3, 5)       #多尺度检测人脸

        for f_x, f_y, f_w, f_h in faces:
            face = img[f_y:f_y+f_h, f_x:f_x+f_w]
            face = cv2.resize(face, (64,64))
            '''
            if n % 3 == 1:
                face = relight(face, 1, 50)
            elif n % 3 == 2:
                face = relight(face, 0.5, 0)
            '''
            face = relight(face, random.uniform(0.5, 1.5), random.randint(-50, 50))
            cv2.imshow('img', face)
            cv2.imwrite(out_dir+'/'+str(n)+'.jpg', face)
            #time_cost
            end=time.time()
            time_cost=end-start
            print('time_cost:\t'+str(time_cost))

            n+=1
        key = cv2.waitKey(30) & 0xff
        if key == 27:
            break
    else:
        break

3. 人脸识别

人脸识别也是先定位再识别,没有想要做的太复杂,所以定位还是使用的上面说的利用harr来定位,识别用的是CNN,用CNN进行识别之后然后再返回结果到主程序进行显示,这其中最重要的两个部分是训练数据制作和模型训练。

3.1训练数据制作。

我找了5个人采集数据,同一个人不同表情的人脸图片大概都是5000张左右,然后下载了一个数据集(LFW)当做“其他类”,下载下来大概有13000多张人脸,而且是在不同的文件夹下的,所以全部读出来放在同一个文件夹下并删掉了一部分只剩下60000多张,与其他类的数量来匹配。所以一共是6类,分别放在6个文件夹里:

然后用matlab读入图片数据,顺便打上标签,所有的结果存放在一个2维矩阵中,为了达到这个目的,每张图片读进来先拉长成为1行:64_64_3的图片,拉长成为长度12288的向量,然后在其最后一列打上其所属类的标签,标签就先用1,2,3,4,5,6这样的数字打上,所以每张图片和其标签就是一个行向量,维度为12289。在这个过程中,每5张照片,选择一张放入测试集,这样的话就相当于分掉1/5的数据去做训练集。下面是这么一个处理过程,完成之后我把这样的数据就存成mat形式了,因为这样的一次处理耗时还是挺可观的,大概需要半个小时的时间才处理完3万多张图片。

代码语言:javascript
复制
% 所有的图片拉成一行,放入矩阵之中,矩阵的最后一列是其每行对应的标签,标签的对应关系在上面的注释中已说明。
% 这个程序所有的图片均是64*64*3的大小。


% train_img_and_label=[];
% test_img_and_label=[];
% 
% %创建进度条显示处理进程
% h=waitbar(0,'processing img...');
% 
% for i=1:6 
% img_path='C:\Users\zhxing\Desktop\data';
% img_path_i=[img_path '\' num2str(i) '\'];
% img_dir=dir([img_path_i '*.jpg']);          %获取所有文件夹下的所有图像
% num_img=length(img_dir);
% display(['正在处理第 ' num2str(i) ' 类数据:  共' num2str(num_img) '张图片' ])
% tic;
% for j=1:num_img
%     img=imread([img_path_i img_dir(j).name]);
%     if mod(j,5)==0   %每5张把一个扔进测试集
%         img_reshape=reshape(img,[1,4096*3]);
%         test_img_and_label_tmp=[img_reshape i];
%         test_img_and_label=[test_img_and_label;test_img_and_label_tmp];
%     else
%         img_reshape=reshape(img,[1,4096*3]);
%         train_img_and_label_tmp=[img_reshape i];
%         train_img_and_label=[train_img_and_label;train_img_and_label_tmp];
%     end
%     waitbar(j/num_img);
% end
% toc;
% end
% display('finished!!')

得到这样的一个图像矩阵之后,下面的任务就是把矩阵按照行打乱顺序,来防止训练的时候一个batch传入的都是同一类的数据,matlab提供了这么一个函数。先获得一个乱序表,然后按照这个乱序表把矩阵打乱。

rowrank=randperm(num)这个函数可以获得一个乱序表,然后利用B = A(rowrank, :)把矩阵打乱,前面的把图像拉成一行的原因也是为了在此同步打乱数据和标签。再打乱之后就可以把数据和标签分开了,以后的使用中,依据索引就可以完全对应了,一般在制作训练数据到这一步的时候,最好随机选择一些数据输出来看标签和数据是否对应。

代码语言:javascript
复制
train_img_and_label=load('C:\Users\zhxing\Desktop\data\train.mat');
test_img_and_label=load('C:\Users\zhxing\Desktop\data\test.mat');
train_img_and_label=train_img_and_label.train_img_and_label;
test_img_and_label=test_img_and_label.test_img_and_label;

rowrank_test = randperm(size(test_img_and_label, 1));
test_img_and_label=test_img_and_label(rowrank_test,:);

rowrank_train = randperm(size(train_img_and_label, 1));
train_img_and_label=train_img_and_label(rowrank_train,:);


test=test_img_and_label;
train=train_img_and_label;

%把数据和标签分开
train_img=train(:,1:end-1);       
train_label=train(:,end);
test_img=test(:,1:end-1);
test_label=test(:,end);

接着,可以把数据恢复成原始尺寸来进行保存,和python要求的格式相同[batch,width,height,channel]。这里也是一个技巧,如果不这样保存的话读入python里然后再想恢复其实是不容易的,因为python中numpy通道的存储方式和matlab是不同的,matlab中一个通道存储完然后接着存储另一个通道,但是python里是一个位置的三个通道连续存储,所以如果直接把一行向量存入mat里再读入python直接用reshape是恢复不出来三通道的图像的。

另外,标签也转换成码表形式的。(这个是叫码表么?我不太确定)

代码语言:javascript
复制
%把数据和标签分开
train_img=train(:,1:end-1);       
train_label=train(:,end);
test_img=test(:,1:end-1);
test_label=test(:,end);

%把数据重置成四维矩阵并保存为mat形式,然后读入python进行处理
train_sz=size(train_img);
train_img_rz=reshape(train_img,[train_sz(1),64,64,3]);
save('train_img.mat','train_img_rz');


test_sz=size(test_img);
test_img_rz=reshape(test_img,[test_sz(1),64,64,3]);
save('test_img.mat','test_img_rz');

%把数字标签转换为码表形式的。
train_label_sz=size(train_label);          %大小
train_labels=zeros(train_label_sz(1),6);   %建立空label表
for i=1:train_label_sz(1)
    train_labels(i,train_label(i))=1;
end
save('train_label.mat','train_labels');

test_label_sz=size(test_label);          %大小
test_labels=zeros(test_label_sz(1),6);   %建立空label表
for i=1:test_label_sz(1)
    test_labels(i,test_label(i))=1;
end
save('test_label.mat','test_labels');

3.2CNN框架及其训练。

经过上面的训练数据制作,我们一共得到了4个mat,分别是:

train_img.mat, train_label.mat, test_img.mat,test_label.mat

存储的分别是训练数据,训练标签,测试数据,测试标签。

我这里是两份,一份是乱序的,一份是没有乱序的。训练的时候直接使用的是乱序的,这个数据量大小一般,4个文件在400m左右。

CNN框架采用的比较简单,因为只是做一个6分类,框架如下:

框架

所有激活层的激活函数采用的都是relu(),权重初始化使用的是截断的高斯分布,偏置初始化卷积层为0,全连接层为0.1,训练是,全连接层采用0.5的dropout。

损失函数采用的是交叉熵损失:交叉熵损失函数,主要是要避免采用平方损失带来的神经元(以sigmoid为例)参数更新过慢的问题。

优化器采用tf.train.AdamOptimizer()

训练时batch_size为100,只训练了一个epoch就达到了比较好的准确率(0.99-1),所以就没有训练过长时间。

值得注意的是,训练在加载训练数据之前,对数据进行归一化和中心化是必要的,我这里采用的比较简单:

代码语言:javascript
复制
train_img, train_label, test_img, test_label = get_date_and_label()

# 数据归一化,统一/255然后减掉0.5
train_img =np.float32(train_img) / 255 - 0.5
test_img = np.float32(test_img) / 255 - 0.5

训练过程就不说了,GTX1060 6G i57500训练是非常快的,总耗时20s左右,下午用笔记本试了一下则特别慢,每50个batch耗时1秒多,但是可能加载数据比较慢,实际耗时远大于这个。不过上个厕所也差不多训练好了。

训练好的模型保存起来待用就可以了,关于模型保存和使用这里有一篇博客我觉得讲的很清楚,贴在这里

四.识别。

先定位,再识别,于获取人脸那里是差不多的,先定位人脸,然后取出来,resize成64_64的,作为输入输入placeholder,drop设置为1,predictions作为输出,对应可得到分类的信息,虽然图像是三维的,但是在输入的时候要把batch的这一维加上。然后根据分类信息画框图显示就可以了,进行了简单的测试,基本可以实现识别功能,偶有识别不出人脸(其实这个主要是因为harr人脸定位的原因),还有一个就是偶尔会把没出现过的人脸误认为是已知的5类中的。主要原因还是在采集图像的过程中大家的表情还是都单一了一些。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2018.05.28 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 功能和框架
  • 1.视频采集
  • 2. 人脸定位
  • 3. 人脸识别
    • 3.1训练数据制作。
    • 3.2CNN框架及其训练。
    • 四.识别。
    相关产品与服务
    人脸识别
    腾讯云神图·人脸识别(Face Recognition)基于腾讯优图强大的面部分析技术,提供包括人脸检测与分析、比对、搜索、验证、五官定位、活体检测等多种功能,为开发者和企业提供高性能高可用的人脸识别服务。 可应用于在线娱乐、在线身份认证等多种应用场景,充分满足各行业客户的人脸属性识别及用户身份确认等需求。
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档