文章目录
- 前言
- 一、级联分类器简介
- 二、训练步骤
- 1.采集图像
- 2.图像处理
- 2.1 图像文件重命名
- 2.2 裁剪ROI区域
- 2.3 批量灰度化及压缩
- 3 制作样本数据
- 3.1 正样本制作
- 3.2 负样本制作
- 4.生成正样本矢量文件
- 5.开始训练
- 6.结果
- 总结
前言
因为导师的一个项目,笔者近期在学习如何训练一个基于级联分类的目标识别器。笔者选取螺钉作为例子,训练一个基于级联分类的螺钉识别器。
一、级联分类器简介
级联分类器是一种基于树的技术。它主要基于boosted拒绝级联这一重要概念构建而成。该技术早已非常成功地应用于人脸检测上,当然它也可以应用到其他目标的检测上。一般而言,具有刚性结构和强纹理特征的目标使用这种方法会得到很好的检测效果。本文选取的螺丝属于刚体,但因为螺丝个体较小,笔者第一次的检测效果并不是很好。话不多说,上代码。
二、训练步骤
1.采集图像
笔者用的是罗技相机采集的图像,拍摄时光源和背景没有特意设置。采集出的图像如图
2.图像处理
2.1 图像文件重命名
相机采集到的图像文件命名很是杂乱,不利于后续批量化处理,笔者采用python写了一个重命名脚本
def rename(path1):
'''
对文件夹中文件进行批量重命名,文件名为1.jpg,2.jpg。。。。。。这种形式。
参数解释,
path1:以绝对路径传入文件路径
'''
# path1 = 'C:\\Users\\Administrator\\Desktop\\logitech'
filename = os.listdir(path1)
print(len(filename))
for i,name in enumerate(filename):
i+=1
st = str(i)+'.jpg'
os.renames(os.path.join(path1,name),os.path.join(path1,st))
2.2 裁剪ROI区域
这一步可以说是整个识别过程中唯一不能用代码实现自动化的过程,因为在这一步,你需要将螺丝部分从原图中裁剪出来,假如这一步也能用代码自动化操作的话,那就没有必要再训练一个识别螺丝的训练器了。裁剪之后的图如下
2.3 批量灰度化及压缩
这一步需要将上述图片压缩到较小尺寸(尺寸大了电脑跑不动,电脑配置极高的小伙伴可以自动忽略),一般来说压缩到40X40就可以了,但因为笔者拍照的问题,原图图片的长宽比不太一致,压缩到40X40就太失真了,所以笔者这里是压缩到了80X80,压缩之后还要全部灰度化处理。这里笔者也是用python写了一个脚本(能让电脑做的就不要人来做)。
def cut(path):
filename = os.listdir(path)
for i,name in enumerate(filename):
i+= 1
i = str(i)
imgpath = path + '\\' + name
print(imgpath)
o = cv2.imread(imgpath)
o = cv2.cvtColor(o,cv2.COLOR_BGR2GRAY)
img = cv2.resize(o,(80,80))
cv2.imwrite('C:\\Users\\Administrator\\Desktop\\bolt\\' + i +'.jpg' ,img)
3 制作样本数据
3.1 正样本制作
我们已经获得了处理好的螺丝图片,下面就要制作正样本集合描述文件,该文件是为了后面用createsample程序生成正样本矢量文件做准备。集合描述文件如图
pos/1.jpg代表图片所在相对地址,1代表图片中含有正例(本文中指螺丝)的个数,0 0代表目标在图片中的左上角坐标,正确裁剪后一般都是(0,0),80,80分别代表该目标的长宽。
3.2 负样本制作
负样本制作只有一个要求,不能包含目标对象。其他的都较随意,图片尺寸大小不需一样(太大尺寸也不好,电脑跑不动),也不需要裁减,只需要再制作一个集合文件就行
负样本描述文件中,只有图片相对地址,没有其他参数。
4.生成正样本矢量文件
将上述得到的文件全部放在同一个文件夹中,如图
其中,还需要将opencv自带的两个用于训练级联分类的exe(opencv_createsamples,opencv_traincascade)和它们所依赖的dll(opencv_world342,opencv_world342d)放在该文件夹中。
下面就要使用opencv_createsamples来生成正样本矢量文件
首先打开cmd平台,使用cd命令转到目标文件所在文件夹(该步骤很重要,若不提前转到目标文件夹,后面生成矢量文件时有可能发生找不到图片的错误)
然后再运行一个bat文件即可生成正样本矢量文件,
该bat文件内容如下
opencv_createsamples.exe -vec posvec.vec -info pos.txt -num 50 -w 80 -h 80
pause
其中,opencv_createsamples.exe代表你要运行这个程序,
vec posvec.vec代表你要生成名为posvec的矢量文件,保存在你cd过去的文件夹中,info pos.txt代表正样本的集合描述文件,num 50代表正样本有50个,w 80和h 80分别代表样本长宽。该程序中还有很多其他参数,感兴趣的小伙伴可以参考学习opencv3一书。运行之后,
同时文件夹中会多出一个posvec.vec矢量文件
负样本不需要生成矢量文件,直接使用即可
5.开始训练
需要的数据都已经准备好,接下来只需要使用opencv_traincascade程序即可训练出自己的一个分类器。
依然和上面一样,写一个bat批处理文件来进行自动化操作,五文件内容如下
opencv_traincascade.exe -data bolt -vec posvec.vec -bg neg.txt -numPos 50 -numNeg 666 -numStages 10 -w 80 -h 80 -minHitRate 0.999 -maxFalseAlarmRate 0.2 -weightTrimRate 0.95 -featureType LBP
pause
其中,data bolt代表训练的参数文件会保存在bolt文件夹中(这意味着你必须先在目标文件夹中新建一个bolt文件夹,不然会报错),bg neg.txt代表负样本的集合文件,numPos 50代表正样本数目为50,numNeg 666代表负样本数目为666,numStages 10代表训练轮数为10轮,minHitRate 0.999代表最小命中率为0.999,(理想情况下应该为100%,但算法达不到这一点,所以一般为0.995以上),maxFalseAlarmRate 0.2代表最大错误警告率为0.2(理想情况下,该值应该为0,实际情况中依然做不到这一点,一般为0.5以下),weightTrimRate 0.95代表boosting算法的参数,默认为0.95,featureType LBP代表选取的特征类型,目前级联分类器支持haar和lbp两种特征类型,lbp用的较多。
将bat批处理文件拖到cmd命令台中,程序就会开始训练,训练时间视个人电脑配置不同而不同,笔者电脑配置较差(i5处理器,4G运存),花了近100分钟,最后会得到这样一个文件
其中bolt_cascade(默认为cascade,笔者重命名了)就是我们需要的xml文件。
6.结果
将训练好的xml文件用于识别,识别代码如下
from cv2 import cv2
# 加载opencv自带的人脸分类器
# faceCascade = cv2.CascadeClassifier(r"C:\Users\Administrator\Desktop\haarcascade_frontalface_alt2.xml")
# faceCascade.load(r'C:\Users\Administrator\Desktop\haarcascade_frontalface_alt2.xml')
faceCascade = cv2.CascadeClassifier(r"C:\Users\Administrator\Desktop\cascade_bolt.xml")
faceCascade.load(r'C:\Users\Administrator\Desktop\cascade_bolt.xml')
cap = cv2.VideoCapture(0)
flag = 0
timeF = 4
i=0
while True:
flag+=1
ret, frame = cap.read()
img = frame.copy()
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
rect = faceCascade.detectMultiScale(
gray,
scaleFactor=1.15,
minNeighbors=3,
minSize=(3,3),
flags = cv2.IMREAD_GRAYSCALE
)
for (x, y, w, h) in rect:
i+=1
cv2.rectangle(frame, (x, y), (x+w, y+h), (0, 255, 0), 2)
# 识别到物体后进行裁剪保存
# tongue = img[x:(x+w), y:(y+h)]
if (flag%timeF == 0):
cv2.imwrite('C:\\Users\\Administrator\\Desktop\\boltimg\\bolt%d.jpg' % i,frame)
cv2.imshow('frame', frame)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
cap.release()
cv2.destroyAllWindows()
识别效果不是很好,识别了44张图片,精准识别到的只有6张,准确率只有14%。
总结
虽然识别效果很差,但笔者已经通过这次训练掌握了如何训练一个级联分类器,后面将要对这个螺丝分类器进行优化。
待改进的点:
第一,拍照长宽比不一样,导致批量压缩的时候出现部分图片失真。
第二,拍照时背景选取的不好,应该尽量用白色幕布,减少背景干扰
第三,正样本数量不够,只有50个,负样本数量也可以继续增加。
后续将针对这几个点进行改进。