『Caffe』图像检测程序(待续)

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了『Caffe』图像检测程序(待续)相关的知识,希望对你有一定的参考价值。

和图像分类不同,图像检测涉及更多的技术基础,本程序是基于传统的滑窗模式完成检测,当下已经有了最新的基于caffe的RCNN、Fast-RCNN、Faster-RCNN以及SSD框架 ,个中思路不同,不得叹惋前路漫漫。由于接触本部时走了很多弯路,所以给出几个关键词用于学习滑窗检测理论基础学习:overfeat;全卷积网络;图像金字塔;非极大值抑制,当然了,传统的卷积分类网络也要了解。

程序调用逻辑图如下:

技术分享

库导入以及参数设置部分:

 1 # coding=utf-8
 2 import numpy as np
 3 import sys
 4 import os
 5 import math
 6 sys.path.append(/usr/local/lib/python2.7/dist-packages)    # 实验室服务器包安装紊乱,cv2库的位置
 7 sys.path.append(/home/caf/zm/caffe/python)                 # caffe库的位置
 8 import cv2
 9 import caffe
10 caffe.set_device(0)
11 caffe.set_mode_gpu()
12 
13 
14 box_stride      = 32        # 感受野大小
15 threshold_value = 0.95      # 初始化框阈值
16 input_size      = 227       # 训练图片尺寸
17 mean_file       = /home/caf/zm/caffe/python/caffe/imagenet/ilsvrc_2012_mean.npy   # 均值文件(这里是caffe自带的)
18 image_path      = ./120.1 +1.4.jpg                                                # 待检测图片                                                              # 训练图片尺寸
19 # 注意,训练网络、分类网络、检测网络是三个不同的网络,详情自行查阅文献
20 model_def       = ./deploy_full_conv.prototxt                                     # 检测用全卷积网络
21 model_weights   = ./_iter_20000.caffemodel                                        # 训练好的模型

注:没什么好说的,input_size和之前不同不分长宽,没什么原因纯粹因为改起来好麻烦,所以训练的时候要统一宽高尺寸,或者愿意来改这个程序

 

程序入口和face_detection()函数(程序主干):

 1 def face_detection(imgFile):
 2 
 3     # 加载网络和参数
 4     net_full_conv = caffe.Net(model_def,
 5                               model_weights,
 6                               caffe.TEST)              # 全卷积网络(导入训练好的模型和deploy配置文件)
 7                                                        # 用caffe的测试模式,即只是提取特征,不训练
 8 
 9     img = cv2.imread(imgFile)                          # 读入测试图像
10     print img.shape                                    # 测试图像尺寸
11 
12     # 生成图像金字塔
13     scales = []                                        # 存储scale的容器,组成图像金字塔
14     factor = 0.793700526                               # 图像放大或者缩小的一个因子(经验值)
15     largest = min(2, 4000/max(img.shape[0:2]))
16     scale = largest                                    # 初始化scale(最大放缩比)
17     minD = largest*min(img.shape[0:2])
18     while minD >= input_size:                          # 保证输入尺寸不小于训练尺寸
19         scales.append(scale)
20         scale *= factor
21         minD *= factor
22 
23     total_boxes = []                                   # 存储候选框的容器
24 
25     for scale in scales:
26         # 使用cv2库整形为金字塔的尺寸并存储
27         scale_img = cv2.resize(img,((int(img.shape[0] * scale), int(img.shape[1] * scale))))
28         cv2.imwrite(./scale_img.jpg,scale_img)
29 
30         # caffe载入整形好的图片
31         im = caffe.io.load_image(./scale_img.jpg)
32         # 将data接收数据格式改为适应图片(全卷积网络不限制输入图片尺寸)
33         net_full_conv.blobs[data].reshape(1,3,scale_img.shape[1],scale_img.shape[0])
34 
35         # 预处理部分,同分类网络
36         # 用转换函数Transformer函数使transformer得到data层的数据格式
37         transformer = caffe.io.Transformer({data: net_full_conv.blobs[data].data.shape})
38         transformer.set_mean(data, np.load(mean_file).mean(1).mean(1))
39         transformer.set_transpose(data, (2,0,1))
40         transformer.set_channel_swap(data, (2,1,0))
41         transformer.set_raw_scale(data, 255.0)
42 
43         # 进行一次前向传播
44         out = net_full_conv.forward_all(data=np.asarray([transformer.preprocess(data, im)]))
45             # 这里介绍一下forward_all的输出,打印print out[‘prob‘].shape会反回[1,2,n,n],
46             # 表示一个含有两张featuremap的n*n输出,而2的原因是因为这是个二分类网络,是对应概率的.
47             # out[‘prob‘].[0,1]表示取第二张featuremap.
48             # [0,1]表示是的featuremap,[0,0]表示非的featuremap
49             # 以2*2(n=2)为例,featuremap上的每个数字表示原图相应位置的概率
50 
51         # 将featuremap的box还原为图片上的box
52         boxes = generateBoundingBox(out[prob][0,1], scale)
53 
54         # 存储图片上的box
55         if(boxes):
56             total_boxes.extend(boxes)
57 
58     print total_boxes
59     boxes_nms = np.array(total_boxes)
60 
61     # 合并图片上的box
62     # 利用非极大值算法过滤出人脸概率最大的框,并对结果进行合并处理
63     true_boxes = nms_average(boxes_nms, 2, 0.2)
64 
65     if not true_boxes == []:
66         # 提取坐标剔除得分
67         (x1, y1, x2, y2) = true_boxes[0][:-1]
68         # 画框
69         cv2.rectangle(img, (int(x1),int(y1)), (int(x2),int(y2)), (0,255,0),thickness=2)
70         # 展示
71         cv2.imshow(test win, img)  
72         # 存储
73         cv2.imwrite(./result.jpg,img)
74         # 等待键盘指令(没啥大用)
75         cv2.waitKey(1)
76 
77 if __name__ == "__main__":
78 
79     face_detection(image_path)

注:本部分通过对各个分部的调用实现了程序的主要功能,直接涉及到的模块是图像金字塔的生成和使用。值得一提的是net_full_conv.forward_all()的输出,直接把(1,2,n,n)的featuremaps输出了,正好和之前在论文里看到的全卷积神经网络原理对应上了,这里提取了代表label’是‘的那一层featuremap进行后续处理。

 

接受主函数中的featuremap并还原生成候选框的程序:

 1 def generateBoundingBox(featureMap, scale):
 2     ‘‘‘
 3     还原候选框
 4     :param featureMap:    m*n的矩阵
 5     :param scale:         放缩参数,前面转换时乘了上来,这里要除回去 
 6     :return:              {y1,x1,y2,x2,prob}
 7     ‘‘‘
 8     boundingBox = []                                          # 用于存储候选框
 9     stride = box_stride                                       # 感受野大小
10     cellSize = input_size                                     # 框的尺寸
11     for (x,y), prob in np.ndenumerate(featureMap):            # np的(index,value)迭代器
12         if(prob >= threshold_value):                                     # 判断概率阙值
13             # print prob
14             boundingBox.append([float(stride * y)/scale,                   # 还原对角线上点坐标
15                                 float(stride * x)/scale,
16                                 float(stride * y + cellSize - 1)/scale,    # 还原对角线下点坐标
17                                 float(stride * x + cellSize - 1)/scale,
18                                 prob])                                     # 存储概率值
19     #sort by prob, from max to min.
20     #boxes = np.array(boundingBox)
21     return boundingBox

注:本部分把featuremap像素还原为原图上的候选框,原图指的是非金字塔放缩的原图。

其中np.ndenumerate()是类比原生python中enumerate()的函数,用法如下(pycharm下ipython拷贝代码真方便,推荐):

 1 import numpy as np
 2 feature = np.arange(4).reshape(2,2)
 3 feature
 4 # Out[4]: 
 5 # array([[0, 1],
 6 #        [2, 3]])
 7 for (x,y), prob in np.ndenumerate(feature):
 8     print(x,y,prob) 
 9 # (0, 0, 0)
10 # (0, 1, 1)
11 # (1, 0, 2)
12 # (1, 1, 3)

 

接受上部分还原出的大量候选框进行合并策略的程序:

 1 def nms_average(boxes, groupThresh=2, overlapThresh=0.2):
 2     ‘‘‘
 3     
 4     :param boxes:          多个矩形框组成的向量 [left, bottom, right, top, prob]
 5     :param groupThresh:    合并阙值,n张以上才能合并,否则放弃
 6     :param overlapThresh:  相关性阙值
 7     :return:               [left, bottom, right, top, 1]
 8     ‘‘‘
 9     # boxes{x1,y1,x2,y2,prob}
10     # rects{x,y,w,h}
11     rects = []
12     for i in range(len(boxes)):
13         # if boxes[i][4] > 0.2:     # 概率大于20%才录入,由于前面已经处理过了,所以没用
14         rects.append([boxes[i,0], boxes[i,1], boxes[i,2]-boxes[i,0], boxes[i,3]-boxes[i,1]])
15     # 合并矩形框,非极大值一致
16     # 重叠率过大的框只保留最大概率的那张
17     # cv2的合并框函数,返回的rects和输入的名称相同,最少有groupThresh个框才合并,overlapThresh是是否合并的计算参数(相关关系参数)
18     rects, weights = cv2.groupRectangles(rects, groupThresh, overlapThresh)
19     rectangles = []
20     for i in range(len(rects)):
21         testRect = Rect( Point(rects[i,0], rects[i,1]), Point(rects[i,0]+rects[i,2], rects[i,1]+rects[i,3]))
22         rectangles.append(testRect)
23     # clusters{x1,y1,x2,y2}  集合
24     # cluster{x1,y1,x2,y2}   属于Reck类
25     # rect{x1,y1,x2,y2}      属于Reck类
26     clusters = []
27     for rect in rectangles:
28         matched = 0
29         for cluster in clusters:
30             # 重叠率达到20%就进行合并
31             if (rect_merge( rect, cluster , 0.2) ):
32                 matched=1
33                 cluster.left   =  (cluster.left + rect.left   )/2
34                 cluster.right  = ( cluster.right+  rect.right  )/2
35                 cluster.top    = ( cluster.top+    rect.top    )/2
36                 cluster.bottom = ( cluster.bottom+ rect.bottom )/2
37         # 新框和所有cluster重叠率都不到20%就直接添加为新的cluster
38         if ( not matched ):
39             clusters.append( rect )
40     result_boxes = []
41     for i in range(len(clusters)):
42         result_boxes.append([clusters[i].left, clusters[i].bottom, clusters[i].right, clusters[i].top, 1])
43     return result_boxes

 

以上是关于『Caffe』图像检测程序(待续)的主要内容,如果未能解决你的问题,请参考以下文章

读caffe源码(未完待续)

使用Caffe完成图像目标检测 和 caffe 全卷积网络

『cs231n』图像定位与检测(下,待续)

图像处理之图像的特征检测1SIFT算法(未完待续)

在 Caffe 中生成用于对象检测的 lmdb 文件

待续Oracle VM VirtualBox+ubuntu14.04+cuda+caffe