基于Caffe的人脸检测实现

来源:互联网 发布:2016年开淘宝店还行吗 编辑:程序博客网 时间:2024/05/22 06:13
博客链接:http://blog.csdn.net/chenriwei2/article/details/50321085
0. 引言深度学习可以说是在人脸分析相关领域遍地开花,近年来在人脸识别,深度学习在人脸检测,人脸关键点检测中有很广泛的应用,这篇文章中,初步实现了基于深度学习CNN的人脸检测。
1. 方法讨论深度学习一般没有进行直接的检测,现有的检测大多都是基于分类的检测,主要的方法有两种:
1.1. 基于滑动窗口的分类最典型的方法就是OverFeat那一套,其主要的方法是:对于每一个尺度、每一个可能的滑动窗口,进行分类。其主要的缺点是:对于稍微大一点的图像,滑动窗口往往有好几百万个之多,所以直接利用这个方法往往速度比较的慢。
如果只是对每一个滑动窗口进行分类的话,那速度的确会变得非常的慢,但是,卷积有一个显著的优点就是权值共享,它可以很好的进行计算结果的重复利用。所以最后基于CNN的全卷积网络速度也不会特别的慢。
1.2. 基于目标显著性方法最典型的方法是R-CNN那一套,其主要的方法是:先快速的检测可能的目标区域块,然后用训练好的深度网络模型进行特征提取,之后再进行分类。它主要解决的问题就是基于滑动窗口的目标检测方法窗口过多的问题。
然而这种方法可能不适合于人脸检测,因为人脸是属于局部目标,而显著目标检测通常用来检测通用的完整目标区域。
在这里,我实现的是基于滑动窗口的检测方法,利用caffe的机制,直接将训练好了的网络模型转换为全卷积网络,从而实现直接输入任意图像的大小。
2. 实验步骤2.1. 数据生成首先是样本的采样,需要的是两类数据,人脸图像和非人脸图像。可以用自己喜欢的方法进行人脸框和非人脸框的选取,并把截取的人脸图像块分别放在face-images 和no-face-images 文件夹中。
在这里需要注意的一点是:如果随机采样,很有可能正负数据及其的不平衡,从而导致网络无法训练,需要特别注意。
紧接着是将数据转换为LMDB,这一点其实挺重要的,直接的文件列表虽然方便,但是训练速度会比LMDB格式的低5倍左右,而且LMDB或者LevelDB支持更多的数据预处理方法。
利用如下脚本:{convert_data_lmdb.sh},可以将数据转化为LMDB。
[C++] 纯文本查看 复制代码
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
#!/usr/bin/env sh
# Create the image to lmdb inputs
TOOLS=/home/crw/caffe-master/.build_release/tools
#图像文件的存放位置
TRAIN_DATA_ROOT=/media/crw/MyBook/Dataset/faceImages/
VAL_DATA_ROOT=/media/crw/MyBook/Dataset/faceImages/
IMAGE_LIST_ROOT=./
#LMDB文件的存放位置
ROOT_LMDB=/media/crw/MyBook/TrainData/LMDB/FaceDetection/50000_32X32
 
# Set RESIZE=true to resize the images to 256x256. Leave as false if images have
# already been resized using another tool.
 
#是否剪切为相同的大小
RESIZE=true
if $RESIZE; then
  RESIZE_HEIGHT=32
  RESIZE_WIDTH=32
else
  RESIZE_HEIGHT=0
  RESIZE_WIDTH=0
fi
 
if [ ! -d "$TRAIN_DATA_ROOT" ]; then
  echo"Error: TRAIN_DATA_ROOT is not a path to a directory: $TRAIN_DATA_ROOT"
  echo"Set the TRAIN_DATA_ROOT variable in create_imagenet.sh to the path"\
       "where the ImageNet training data is stored."
  exit1
fi
 
if [ ! -d "$VAL_DATA_ROOT" ]; then
  echo"Error: VAL_DATA_ROOT is not a path to a directory: $VAL_DATA_ROOT"
  echo"Set the VAL_DATA_ROOT variable in create_imagenet.sh to the path"\
       "where the ImageNet validation data is stored."
  exit1
fi
 
echo "Creating train lmdb..."
 
GLOG_logtostderr=1 $TOOLS/convert_imageset \
    --resize_height=$RESIZE_HEIGHT \
    --resize_width=$RESIZE_WIDTH \
    --shuffle \
    --gray \
    $TRAIN_DATA_ROOT \
    $IMAGE_LIST_ROOT/train_2.list \
    $ROOT_LMDB/train
 
echo "Creating val lmdb..."
 
GLOG_logtostderr=1 $TOOLS/convert_imageset \
    --resize_height=$RESIZE_HEIGHT \
    --resize_width=$RESIZE_WIDTH \
    --shuffle \
    --gray \
    $VAL_DATA_ROOT \
    $IMAGE_LIST_ROOT/val_2.list \
    $ROOT_LMDB/val
 
$TOOLS/compute_image_mean $ROOT_LMDB/train \
  $ROOT_LMDB/mean.binaryproto
 
echo "Done."

2.2. 网络配置由于我们是用来做人脸二分类,所以没有必要训练一个非常大的网络,小一点的就可以,我这边是改进DeepID的网络,采用人脸图像大小是48*48 彩色图像。当然你也可以直接那别人训练好了的网络进行微调处理。
网络结构图如下所示:

20151215203246288.jpg (154.66 KB, 下载次数: 2)

下载附件

2015-12-30 23:38 上传



完整的训练参数及其文件在最后面的链接文件给出。。
2.3. 训练网络训练网络也跟普通的所有的分类网络训练一样。
    配置好相对应的路径和超参数,在当前路径下,运行  
./train.sh
  • 1
由于是二分类,网络收敛的很快,差不多几万个迭代就可以达到99%以上的二分类精度。
3. 测试3.1. 网络转换训练好了的人脸二分类器,不能直接应用于人脸检测,需要进行转换为全卷积网络的格式,具体的方法在Caffe官网上有详细的说明,这里不再赘述。
关键代码如下:
[Shell] 纯文本查看 复制代码
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
def convert_full_conv(model_define,model_weight,model_define_fc,model_weight_fc):
    '''
    @breif : 将原始网络转换为全卷积模型
    @param: model_define,二分类网络定义文件
    @param: model_weight,二分类网络训练好的参数
    @param: model_define_fc,生成的全卷积网络定义文件
    @param: model_weight_fc,转化好的全卷积网络的参数
    '''
    net = caffe.Net(model_define, model_weight, caffe.TEST)
    fc_params = {pr: (net.params[pr][0].data, net.params[pr][1].data)for prin params}
    net_fc = caffe.Net(model_define_fc, model_weight, caffe.TEST)
    conv_params = {pr: (net_fc.params[pr][0].data, net_fc.params[pr][1].data)for prin params_fc}
    forpr, pr_conv in zip(params, params_fc):
       conv_params[pr_conv][0].flat = fc_params[pr][0].flat # flat unrolls the arrays
       conv_params[pr_conv][1][...] = fc_params[pr][1]
    net_fc.save(model_weight_fc)
    print'convert done!'
    returnnet_fc

3.2. 非极大值阈值直接使用了这个代码,已经实现了非极大值阈值。
3.3. 人脸检测主要代码如下:
[Shell] 纯文本查看 复制代码
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
def face_detection_image(net,net_vf,image_name):
    '''
    @检测单张人脸图像
    '''
    scales = []
    imgs = skimage.io.imread(image_name)
    ifimgs.ndim==3:
            rows,cols,ch = imgs.shape
    else:
            rows,cols = imgs.shape
    #计算需要的检测的尺度因子
    min = rowsif  rows<=cols else  cols
    max = rowsif  rows>=cols else  cols
    # 放大的尺度   
    delim = 2500/max
    while(delim >= 1):
        scales.append(delim)
        delim=delim-0.5
    #缩小的尺度
    min = min * factor
    factor_count = 1
    while(min >= face_w):
        scale = pow(factor,  factor_count)
        scales.append(scale)
        min = min * factor
        factor_count += 1
    #=========================
    #scales.append(1)
    total_boxes = []
    ###显示热图用
    num_scale = len(scales)
    s1=int(np.sqrt(num_scale))+1
    tt=1
    plt.subplot(s1, s1+1, tt)
    plt.axis('off')
    plt.title("Input Image")
    im=caffe.io.load_image(image_name)
    plt.imshow(im)
    #============
    forscale in scales:
        w,h = int(rows* scale),int(cols* scale)
        scale_img= tf.resize(imgs,(w,h))
        #更改网络输入data图像的大小
        net.blobs['data'].reshape(1,channel,w,h)
        #转换结构
        transformer = caffe.io.Transformer({'data': net.blobs['data'].data.shape})
        #transformer.set_mean('data', np.load(caffe_root + 'python/caffe/imagenet/ilsvrc_2012_mean.npy').mean(1).mean(1))
        transformer.set_transpose('data', (2,0,1))
        transformer.set_channel_swap('data', (2,1,0))
        transformer.set_raw_scale('data', raw_scale)
        #前馈一次
        out = net.forward_all(data=np.asarray([transformer.preprocess('data', scale_img)]))
        ###显示热图用
        tt=tt+1
        plt.subplot(s1, s1+1, tt)
        plt.axis('off')
        plt.title("sacle: "+"%.2f" %scale)
        plt.imshow(out['prob'][0,map_idx])
        #===========
        boxes = generateBoundingBox(out['prob'][0,map_idx], scale)
        if(boxes):
            total_boxes.extend(boxes)
    #非极大值抑制
    boxes_nms = np.array(total_boxes)
    true_boxes1 = nms_max(boxes_nms, overlapThresh=0.3)
    true_boxes = nms_average(np.array(true_boxes1), overlapThresh=0.07)
    #===================
    plt.savefig('heatmap/'+image_name.split('/')[-1])
    #在图像中画出检测到的人脸框
    fig, ax = plt.subplots(ncols=1, nrows=1, figsize=(6, 6))
    ax.imshow(imgs)
    forbox in true_boxes:
        im_crop = im[box[0]:box[2],box[1]:box[3],:]
        ifim_crop.shape[0] == 0 or im_crop.shape[1] == 0:
            continue
        ifre_verify(net_vf, im_crop) == True:
            rect = mpatches.Rectangle((box[0], box[1]), box[2]-box[0], box[3]-box[1],
                fill=False, edgecolor='red', linewidth=1)
            ax.text(box[0], box[1]+20,"{0:.3f}".format(box[4]),color='white', fontsize=6)
            ax.add_patch(rect)
    plt.savefig('result/'+image_name.split('/')[-1])
    plt.close()
    returnout['prob'][0,map_idx]

4. 实验结果4.1. 响应图其中,颜色越红的地方出现就是检测器判断人脸出现的地方。

QQ截图20151230233634.jpg (60.65 KB, 下载次数: 1)

下载附件

2015-12-30 23:40 上传

QQ截图20151230233641.jpg (62.77 KB, 下载次数: 1)

下载附件

2015-12-30 23:40 上传

QQ截图20151230233649.jpg (60.48 KB, 下载次数: 1)

下载附件

2015-12-30 23:40 上传

QQ截图20151230233702.jpg (58.98 KB, 下载次数: 1)

下载附件

2015-12-30 23:40 上传

4.2 检测结果图

QQ截图20151230233709.jpg (57.14 KB, 下载次数: 2)

下载附件

2015-12-30 23:40 上传

QQ截图20151230233717.jpg (63.31 KB, 下载次数: 1)

下载附件

2015-12-30 23:40 上传

QQ截图20151230233726.jpg (47.32 KB, 下载次数: 1)

下载附件

2015-12-30 23:40 上传

这里面已经设置了比较高的阈值,不然误检率会很高。
5. 讨论1,阈值的设定,是在准确率和召回率之前的权衡。
2,基于以上方法,定位还不够准确。
所有代码地址:Github代码
PS: 如果对你有帮助,还请点个star吧
至此,完成了基于Caffe的人脸检测、人脸点检测、人脸识别的基本工作
0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 如果淘宝被盗了店铺乱上东西怎么办 快递不送货直接代售点签收怎么办 淘宝快递没收到却显示已签收怎么办 支付宝余额未满16受限怎么办 未满16岁支付宝余额受限怎么办 手机天猫购物买的数量大怎么办 扣扣游戏领礼包出现账号异常怎么办 美容院转让给别人客人要退钱怎么办 卖家毁约中介费担保费怎么办 天猫买东西店家不开增值税票怎么办 天猫专卖店品牌不授权了怎么办 临时京东账号被冻结买的东西怎么办 天猫强行退款给买家商家怎么办 淘宝店铺没交保证金被释放了怎么办 沭阳县地段生过了报名时间怎么办 淘宝买东西地址和收件人填错怎么办 才装修的房子马上要住怎么办 淘宝发货显示无效的发货人怎么办 微信位置和所在地位置不一样怎么办 qq号被冻结了短信发不出去怎么办 商户刷自己信息卡被冻结怎么办 天猫超市买的东西坏了怎么办 天猫超市买东西地址填错了怎么办 天猫超市地址填错了怎么办 天猫超市退货达不到包邮条件怎么办 保千里达令手机锁屏密码忘记怎么办 淘宝买的东西给的发票不见了怎么办 在天猫超市买到发霉怎么办 支付宝红包券金额消费不完怎么办 支付宝向对方发起收款不付怎么办 支付宝收款别人少付了怎么办 红牛领到50元加油优惠券怎么办用 苹果淘宝看评价图片看不清楚怎么办 淘宝账号账户体验中心有违规怎么办 淘宝买的东西需要寄回去维修怎么办 淘宝闪电退款了卖家不肯退了怎么办 云视听会员账号账号密码忘了怎么办 移动卡异地补卡忘记服务密码怎么办 欠我钱的人不接电话怎么办 苹果手机换屏升级系统了黑屏怎么办 苹果手机黑屏时接不到电话怎么办