之前对ssd的整个原理已经了解了,对于整个网络走动的流程,loss的计算这些,已经心里有数了,那么接下来,要么去训练ssd模型,要么就去利用ssd去测试模型,这不,我们现在就学习一下利用opencv来使用ssd,在这之前,感谢一只稚嫩的小金毛提供的代码和模型,这位作者写的挺好的,但是我个人觉得还是有些地方没有讲清楚,比如模型预测后输出的每个值的含义,而且在筛选预测的边框的时候,我觉得还能再优化,所以,才有了这篇文章。
上面作者模型链接,大家可以拿去练手:链接:https://pan.baidu.com/s/1zvIw1rkRvYqk33xwyAMjhg
提取码:n90t
下面直接上代码,代码中,注释已经很清楚了。
'''author:nike hu'''
# 这是使用的模型中所对应的类型
objName = ["background",
"aeroplane", "bicycle", "bird", "boat",
"bottle", "bus", "car", "cat", "chair",
"cow", "diningtable", "dog", "horse",
"motorbike", "person", "pottedplant",
"sheep", "sofa", "train", "tvmonitor"]
def test(image, model, model_layer, thred=0.7):
imageH = image.shape[0] # 输入照片的高
imageW = image.shape[1] # 宽
show_image = image # 这个用来显示图片
model = cv2.dnn.readNetFromCaffe(model_layer, model)
# 加载模型,第一个参数是网络的每一层的信息,第二个参数是训练好的模型,我们现在使用的是caffe训练的模型
image = cv2.dnn.blobFromImage(image, 0.007843, (300, 300), (127.5, 127.5, 127.5), True, False)
'''
函数cv2.dnn.blobFromImage(image[, scalefactor[, size[, mean[, swapRB[, crop[, ddepth]]]]]])
作用:对图像进行预处理,包括减均值,比例缩放,裁剪,交换通道等,返回一个4通道的blob(blob可以简单理解为一个N维的数组,用于神经网络的输入)
参数:image:输入图像(1、3或者4通道)
可选参数
scalefactor:图像各通道数值的缩放比例
size:输出图像的空间尺寸,如size=(200,300)表示高h=300,宽w=200
mean:用于各通道减去的值,以降低光照的影响(e.g. image为bgr3通道的图像,mean=[104.0, 177.0, 123.0],表示b通道的值-104,g-177,r-123)
swapRB:交换RB通道,默认为False.(cv2.imread读取的是彩图是bgr通道)
crop:图像裁剪,默认为False.当值为True时,先按比例缩放,然后从中心裁剪成size尺寸
ddepth:输出的图像深度,可选CV_32F 或者 CV_8U.
总之,这个函数就是用来处理图片的,将图片的维度转化为神经网络需要的维度。
'''
model.setInput(image) # 感觉这一步就是将数据放到神经网络的输入层
out = model.forward()
# 神经网络向前推进,最后输出结果类似[1, 1, 100, 7],其中的100代表的是ssd中的预测框,7代表含义分别对应
# 照片编号(这是是猜测,因为ssd预测的时候,如果输入多张图片,得标记一下),类别下标,类别预测可能性,x1,y1, x2, y2
out = torch.from_numpy(out) # 转化为torch类型,方便后面筛选box
score_id = out[:, :, :, 2] > thred # 将预测可能性大于阈值的标记为true
pred_box = out[score_id].view(-1, 7) # 将预测的box找出来
pred_num = pred_box.numpy() # 转化为numpy类型
for box in pred_num:
score = round(box[2], 2) # 保留小数点后两位
class_id = int(box[1]) # 这里预测的类别数据为浮点型,转化一下
x1 = int(box[3] * imageW) # 左上角的x坐标,注意转化为整形
y1 = int(box[4] * imageH) # 左上角的y坐标
x2 = int(box[5] * imageW) # 右下角的x坐标
y2 = int(box[6] * imageH) # 右下角的y坐标
cv2.rectangle(show_image, (x1, y1), (x2, y2), (0, 255, 255), 2) # 画边框
str_show = str(score) + ' ' + objName[class_id]
# 要做图像上写的字符,注意,opencv只能写上英文,输入中文,要迂回一下,有兴趣者看我相关文章,类别下标是从0开始的
cv2.putText(show_image, str_show, (x1 + 15, y1 + 15), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 0, 255), 2, 8)
# 将英文字符串画在图上
cv2.imshow('detect_image', show_image)
cv2.waitKey()
if __name__ == '__main__':
image = cv2.imread('images/1.jpg')
if image.shape[1] > 1600:
image = cv2.resize(image, None, fx=0.3, fy=0.3)
model_path = 'ssd/MobileNetSSD_deploy.caffemodel'
model_layer = 'ssd/MobileNetSSD_deploy.prototxt'
test(image, model_path, model_layer)
最后输出效果如下:
总结一下,opencv提供的这个接口,还是很方便,至于准确率这些,就跟模型的训练程度有关了,也还跟ssd本身有关,毕竟现在ssd的优化版本也很多。我们如果要训练自己的数据,那么我们输入的模型就得变成我们自己训练的模型了,目前我没有发现opencv能训练ssd模型这些,只能使用ssd模型。所以我们还得学习怎么去训练自己的模型,而在训练自己模型的过程中,如何进行数据预处理,这一块我个人觉得是最麻烦的,后面我学了之后有空会写一篇。
2020 4.22