训练自己的分类器
本小节使用的图片为:
最近在项目中遇到了一个问题,需要识别图像中的红色圆形。首先尝试了Hough圆形检测,详细请见这篇文章
http://blog.csdn.net/u010429424/article/details/72989870Hough变换的效果还可以,但是存在计算量大等问题,因此,还需要一种更为准确的方法,识别图像中的圆形。
在此前提下,我尝试训练级联分类器,尽管分类器的效果不甚理想,但是也收获颇丰。现把训练过程记录如下。
——把大象装进冰箱里需要几步?
——三步
——训练自己的分类器也不例外,三步
首先,准备正样本和负样本数据
然后,训练,得到分类器
最后,测试,看看分类器好不好用
详细步骤如下:
Step1. 准备正样本和负样本数据
不像人脸识别那样,有现成的数据集。有时候,针对特定的问题,需要我们自己构建正负样本。
(1). 正样本的构建
大家可以根据自己的问题构建正样本。在这个问题中,我通过录制视频,得到视频中每一帧的图像。对这些图像,利用Hough圆形检测的方法,分割出圆形区域,将圆形区域作为图像,保存到文件中,最后通过人工的方式删除误检的图形。最后构建了大约800个正样本。
注意: 正样本要求所有图片的大小必须统一,OpenCV中推荐的大小是20 * 20(我这里是32 * 32)。对于大小不一致的图像,可以通过
dst = cv2.resize(src, (32, 32), interpolation=cv2.INTER_AREA)
将图像大小缩放至32 * 32。
(2). 负样本的构建
负样本的构建就容易多了。只要图像的尺寸大于等于正样本图像就可以了。你可以用一些阿猫阿狗的图像,也可以用一些大海森林的图像,但是不推荐用这些图像当做负样本。应该根据实际问题,把常见的场景拿来做负样本。
我将视频中的每一帧图像,进行切割,得到负样本。 具体说来,帧图像的大小是480 * 640,按照32 *32的大小,均分成300张小图片。然后通过人工的方式,排除近似于正样本的小图片,剩下的就是负样本图片。对视频中每一帧都进行上述切割,最后得到一组负样本。当然,也可以按照48*64进行切割,只要保证负样本图片的尺寸大于等于正样本就可以。切割的步骤,可以参考这篇文章。
http://blog.csdn.net/u010429424/article/details/74377617负样本的数量大约为2100个,网上推荐正样本和负样本的比例大致在1 : 2到1 : 3之间。
(3). 把正样本和负样本的路径,写到文件里
什么是“把正样本和负样本的路径写到文件里”呢?比如你正样本在C://your/path/1.jpg,就把C://your/path/1.jpg
写到一个文件中,专业名词叫做路径列表,这里可以写绝对路径,也可以写相对路径。方法有很多,我这里使用几行Python代码,实现了这个功能。当然你也可以用Java写,用C写,甚至自己写一个.bat脚本。
正样本的处理代码如下: 其中 0 0 32 32 表示红色圆形在图像中的范围,也就是正样本图像的大小。
path = 'train_img_set/ball/'
dir = os.listdir(path) # 自动排序
fopen = open('train_img_set/info.txt', 'w')
for d in dir:
string = 'ball/' + d + ' 1 0 0 32 32' +'\n'
fopen.write(string)
fopen.close()
负样本的处理代码如下:
path = 'train_img_set/non-ball/' # 替换为你的路径
dir = os.listdir(path) # dir是目录下的全部文件
fopen = open('train_img_set/bg.txt', 'w') # 替换为你的路径
for d in dir: # d是每一个文件的文件名
string = 'non-ball/' + d + '\n' #拼接字符串并换行
fopen.write(string) # 写入文件中
fopen.close()
正样本如下:
负样本如下:
(4). 生成正样本的描述文件
在cmd中 用一行命令搞定:
opencv_createsamples.exe -info info.txt -vec pos.vec -num 697 -w 32 -h 32
命令行参数如下:
-info 正样本说明文件
-vec 输出文件,内含用于训练的正样本。
-num 生成的正样本的数目。
-w 输出样本的宽度(以像素为单位)。
-h 输出样本的高度(以像素为单位)。
注:除了上述参数外,还可以配置其他参数,详细请见
http://www.opencv.org.cn/opencvdoc/2.3.2/html/doc/user_guide/ug_traincascade.html
在命令行中运行这个命令后,可以得到一个叫 pos.vec 的文件。
注意,在运行前,需要用到opencv_createsamples.exe,这个文件可以从网上下载,也可以在OpenCV的安装目录下找到,我的是:D:\Program Files\OpenCV\OpenCV-249\opencv\build\x64\vc10\bin
Step2. 对样本进行训练
准备好样本后,对样本进行训练,使用一行命令:
opencv_haartraining.exe -data xml -vec pos.vec -bg bg.txt -nsplits 1 -npos 150 -nneg 450 -nonsym -w 32 -h 32 -mode all -mem 1280
命令行参数如下:
-data 目录名,用于存放训练好的分类器。
-vec 包含正样本的vec文件名(由 opencv_createsamples 程序生成)。
-bg 背景描述文件,也就是包含负样本文件名的那个描述文件。
-numPos 每级分类器训练时所用的正样本数目。
-numNeg 每级分类器训练时所用的负样本数目,可以大于 -bg 指定的图片数目。
-numStages 训练的分类器的级数。
-w 训练样本的宽(单位为像素)
-h 训练样本的高(单位为像素)
必须跟训练样本创建(使用 opencv_createsamples 程序创建)时的尺寸保持一致。
-mode all指定haar特征的种类,all表示使用垂直以及45度旋转特征
参数的详细说明请见:
http://www.opencv.org.cn/opencvdoc/2.3.2/html/doc/user_guide/ug_traincascade.html
注意,在运行前,需要用到opencv_haartraining.exe,这个文件的找法与opencv_createsamples.exe类似(见上文)。
训练的过程如下所示:
我在训练的过程中,程序会死掉(无法结束),可能是由于正负样本负样本差距不大所致,还需要继续研究一下。
程序死掉也别急,在xml目录下能看到生成了0、1、2、3几个文件,这些文件是训练时,各层的中间文件。我们可以使用convert_cascade.exe生成最后的xml文件,convert_cascade.exe可以从网上下载。
命令如下:
convert_cascade --size=32x32 C://Users//lijialin//PycharmProjects//Hockey//train_img_set//xml C://Users//lijialin//PycharmProjects//Hockey//train_img_set//xml//haar_adaboost.xml
最后,在xml文件中,能看到一个haar_adaboost.xml文件,这个文件就是我们的通过训练得到分类器。
接下来测试这个分类器的效果。
Step3. 测试
测试程序如下:
cascade = cv2.CascadeClassifier('train_img_set/xml/haar_adaboost.xml')
img = cv2.imread('images/1111.png')
cv2.imshow('img', img)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
circles
= cascade.detectMultiScale(gray, 1.3, 5)
if faces is not None:
# x, y, w, h = circles[0]
for (x, y, w, h) in faces:
cv2.rectangle(img, (x, y), (x + w, y + h), (0, 255, 0), 2)
cv2.imshow('img', img)
cv2.waitKey(0)
cv2.destroyAllWindows()
测试结果如下,首先对一幅简单图片进行测试,可以看出,分类器准确地识别出了图像中的红色圆形:
然后对一幅稍微复杂的图片进行测试,可以看到,分类器仅仅识别出了部分红色圆形,准确率是很低的,大概 4 / 9 = 44.4%:
参考资料:
训练分类器:
http://blog.csdn.net/yangleo1987/article/details/52883864
http://blog.csdn.net/woaipaoche/article/details/41517089
http://www.opencv.org.cn/opencvdoc/2.3.2/html/doc/user_guide/ug_traincascade.html
http://blog.csdn.net/autoliuweijie/article/details/51911289
Hough识别圆形:
http://blog.csdn.net/u010429424/article/details/72989870
图像切割:
http://blog.csdn.net/u010429424/article/details/74377617
Python获取目录下的全部文件名,并写入文件中:
http://blog.csdn.net/u010429424/article/details/74390342
今天就到这里吧~