opencv haar训练自己的人脸分类器(主要为正脸)

版权声明://若需转载,请各位大虾注明出处,小生在此有礼. https://blog.csdn.net/sos768/article/details/85095176

项目实施情况(包括取得成果描述)及项目作品描述:

最下方附有检测代码和项目训练下载以及正负样本下载(附带在百度网盘中)
  • 此项目是想要对去年一度很火热的声控游戏进行拓展玩法,增加了人脸识别功能,并且此功能所使用的分类器为自己进行训练的,此次训练主分为数据采集,分类器训练,和运用训练好的分类器进行人脸检测并归一化保存,为以后测试所用,此次介绍主要为对训练过程进行叙述
  • 主要是haar特征提取+adaboost算法,adaboost算法简单理解就是迭代训练,训练完一个弱分类器之后,去除不正确的样本,保存正确的样本训练结果继续反复训练保存,直至样本利用率达到完全生成强分类器,haar特征提取则是通过特征提取公式在10几种模式中进行特征提取。

样本采集:

  • 在训练前,我们需要进行正样本和负样本的采集,正样本在这里本来选用了ORL人脸库进行训练,共40*10,400张,但是最后的结果并不太好,猜测可能是训练样本数目太少的原因,所以在此训练时候,增添了另一组人脸库,正样本共计18000张左右,部分截图如下:
    在这里插入图片描述
  • 其中训练时候出现了内存不足的警告,所以经过py脚本修改,将所有的人脸按照百分百比例进行了缩放,修改成了20*20,代码如下: 在这里插入图片描述
  • 负样本在这里部分选用weizmann团队的图片样本,并进行了灰度化,方便对比
  • http://www.wisdom.weizmann.ac.il/~vision/Seg_Evaluation_DB/dl.html (网站允许爬取)
  • 因为样本稀缺的原因放入了其他的负样本进行填补,这里选择了中科院放出来的训练样本,共计11000多张,部分截图如下:
  • 在这里插入图片描述
  • 进行完数据采集后,
    将采集到的数据分批放置,在这里我将正样本数据放在了pos_img文件中,将负样本放在了neg_img中,并建立了一个xml文件(其中会放置有训练过程中产生的弱分类器),再将所有的弱分类器训练完成之后,会根据每个弱分类器的权重生成最终的强分类器,也就是xml.xml 分类器(与建立的xml文件名字一致)
  • 接下来需要进行的则是opencv所提供的两份工具(类似于tf中封装用来训练的优化器等函数),所用版本为opencv-2-4-11,在进行完opencv的环境配置后,在其主目录下进行如下跳转build->x64,找到需要的文件,opencv_createsample.exe和opencv_haartraining.exe,

简单介绍下两个工具的作用:

  • Opencv_creaatesamples.exe是用来创建样本描述的,创建生成的文件后缀名为.vec,专门为opencv训练准备,只需要对正样本进行创建

  • Open_haartraining.exe里面封装了haar特征的提取(小波提取,特征黑白提取公式,具体不再这里累述,适用于所有训练的特征,人脸,人眼,人耳等),以及adaboos算法的训练过程。

  • 在进行多次小批量的训练测试后,发现正负样本比例为1:2.67左右的训练结果较好,在这里采取1:3的比例。这个比例不是绝对的,但理论上说,负样本的多样性越大越好,这样的话我们可以有效的降低误检率,而不仅仅是通过正样本的训练让其能够识别物体,在最后训练的时候,我选取了2700个正样本和9000个负样本,且均已转换成灰度图像

  • 在样本准备好之后,打开windows下的cmd窗口,cd到指定目录下,生成所需要的txt
    图像目录文件,代码如下(neg_img和pos_img的操作一致):

    C:\Users\wz>cd Desktop
    C:\Users\wz\Desktop>cd pos_img
    C:\Users\wz\Desktop\pos_img>dir /b >pos.txt
  • 打开pos.txt,并且删除pos.txt这一行,并将jpg 修改为jpg 1 0 0 20 20
    在这里插入图片描述
    在这里面,1代表该图片只会出现一次,0 0 20 20 表示该图片大小是矩形框从(0,0)到(20,20),
  • 同样的,在neg.txt里面删除neg.txt这一行,其余不做改动
  • 做完之前的操作之后将txt文件拷贝出来,两个exe封装工具也拷贝出来,类似于下图:
    在这里插入图片描述
  • 之后就可以进入cmd’使用openve_createsamples.exe进行创建vec样本描述文件了
  • 根据其的帮助文件:
  [-info <collection_file_name>]  //目标图片描述文件,下属命令中为pos.txt
  [-img <image_file_name>]
  [-vec <vec_file_name>]   //指令生成的文件,在下述命令中生成的为pos.vec
  [-bg <background_file_name>]   //背景图片描述文件,为neg.txt
  [-num <number_of_samples = 1000>] //产生的正样本数量
  [-bgcolor <background_color = 0>] 
  [-inv] [-randinv] [-bgthresh <background_color_threshold = 80>]
  [-maxidev <max_intensity_deviation = 40>]
  [-maxxangle <max_x_rotation_angle = 1.100000>]
  [-maxyangle <max_y_rotation_angle = 1.100000>]
  [-maxzangle <max_z_rotation_angle = 0.500000>]
  [-show [<scale = 4.000000>]]
  [-w <sample_width = 24>]    //输出的样本宽度20
  [-h <sample_height = 24>]  //输出的样本高度20
  [-pngoutput]
  • 使用以下命令创建一个包含2700样本的样本描述文件pos.vec :
opencv_createsamples.exe -vec pos.vec -info pos_img\pos.txt -bg neg_img\neg.txt -w 20 -h 20 -num 2700
  • 同样cmd,在所放目录使用openc_haartraining.exe ,查看其的帮助如下:
  -data <dir_name>  //指定存放训练好的分类器的路径名字,在这里为之前在当前目录下建立的xml文件
  -vec <vec_file_name>  //正样本的文件名(pos.vec)
  -bg <background_file_name> //背景描述文件
  [-bg-vecfile]
  [-npos <number_of_positive_samples = 2000>]  //正样本数目2700[-nneg <number_of_negative_samples = 2000>]  //负样本数目9000[-nstages <number_of_stages = 14>]  //训练层数
  [-nsplits <number_of_splits = 1>]  //分裂节点数目为2
  [-mem <memory_in_MB = 200>]
  [-sym (default)] [-nonsym]
  [-minhitrate <min_hit_rate = 0.995000>]
  [-maxfalsealarm <max_false_alarm_rate = 0.500000>]
  [-weighttrimming <weight_trimming = 0.950000>]
  [-eqw]
  [-mode <BASIC (default) | CORE | ALL>] //级联器的类型,all表示所有类型
  [-w <sample_width = 24>]  //样本宽度
  [-h <sample_height = 24>]  //样本高度
  [-bt <DAB | RAB | LB | GAB (default)>]
  [-err <misclass (default) | gini | entropy>]
  [-maxtreesplits <max_number_of_splits_in_tree_cascade = 0>]
  [-minpos <min_number_of_positive_samples_per_cluster = 500>]
  • 使用命令:
opencv_haartraining.exe -vec pos.vec -bg neg_img\neg.txt -data xml -w 20 -h 20 -men 2048 -npos 2700 -nneg 9800 -nstages 9 -mode all
  • 之后就是等待训练结束了,训练部分截图如下:
    在这里插入图片描述

参数意思分别为:

N           对应训练的层数
%SMP        提供样本的使用率 
ST.THR      分类器的阈值(这里是极大值)
HR          当前分类器对正样本识别正确的概率
FA          当前分类器对负样本识别错误的概率
EXP.ERR     分类器的期望错误率(基于所提供的样本)
  • 最终最终分类器为如下,及每层的弱分类器如下:
    在这里插入图片描述

在这里插入图片描述

  • 因为环境配置问题(这是后来跑的9层,识错率还有待调整),在作品里面使用的是去年跑的16层识别器
  • 整体运行如下:
  • 在这里插入图片描述
    在这里插入图片描述

项目存在的不足或欠缺,尚需深入研究的问题:

  • 这方面其实有很多,第一就是原先想要加入的方向键控制,在cocos2d里面可能因为疏漏只找到了任意键盘响应,更多的实现实在cocos3d-x中,这一方面是需要后续改进的

  • 还有的方面,比如在识别训练上,haar+adaboost的分类训练虽然是很经典,不过其也有很多缺憾,不够灵活,不过从一个方面看稳定也是他的优点

  • 至于后续,还想研究的就是真正的换脸技术了,dlib的后续模型训练,下一步我想要进行的是将角色的头像进行完美拼接,像下面这样:
    在这里插入图片描述 在这里插入图片描述
    ------------------------------------------------ 合成------------------------------------------------------
    在这里插入图片描述

  • 实现最简单的当然是调用shape_68特征点进行替换,并且通过转换进行脸模覆盖

  • 所以下一步,比较想要研究的就是进行完美替换人脸了。

附录:

测试代码:
# -*- coding: utf-8 -*-
# import 进openCV的库
import cv2
import sys
import dlib
import cv2
import os
import glob
import numpy as np
import os.path
from PIL import Image
# img_file:图片的路径
# path_save:保存路径
# width:宽度
# height:长度

def img_resize(img_file, path_save, width=16,height=16):
    img = Image.open(img_file)
    new_image = img.resize((width,height),Image.BILINEAR)
    new_image.save(os.path.join(path_save,os.path.basename(img_file)))

#调用电脑摄像头检测人脸并截图
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('xml.xml')

    #识别出人脸后要画的边框的颜色,RGB格式, color是一个不可增删的数组
    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.01, minNeighbors = 3, minSize = (212, 212))
        if len(faceRects) > 0:          #大于0则检测到人脸
            for faceRect in faceRects:  #单独框出每一张人脸
                x, y, w, h = faceRect
                
                #画出矩形框
                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/1 %d' % (num,len(faceRects)),(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__':
    # 连续截100张图像,存进image文件夹中
    # CatchPICFromVideo("get face", 0, 2, "C:\\Users\\Administrator\\Desktop\\face_recognition\\faces")
    CatchPICFromVideo("get_face", 0, 100, "/home/wz/Desktop/chuangke/simple/")
    for jpgfile in glob.glob("/home/wz/Desktop/chuangke/simple/*.jpg"):
        img_resize(jpgfile,"/home/wz/Desktop/chuangke/simple/",96,96)



参考blogs:
链接:

猜你喜欢

转载自blog.csdn.net/sos768/article/details/85095176