和图像分类不同,图像检测涉及更多的技术基础,本程序是基于传统的滑窗模式完成检测,当下已经有了最新的基于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
时间: 2024-10-11 17:51:27