基于机器学习的捡球机器人设计与实现(探索)第5篇——训练并使用Haar分类器(2019-03-02)

2019-03-03 by 崔斐然

2019-03-09 更新 视频上传到了b站?

不展示成品没动力写文章?

       昨天车架和电机之类的都到了,摄像头准备用的以前玩的ps3 摄像头 20块钱,拍照质量渣渣。千万不要买这款啊。
       那么剩下的任务就是让小车能识别出网球了。在训练自己的分类器前不妨先试试OpenCV自带的分类器吧。

Demo:人脸检测

       在pycharm中新建一个项目,找一张清晰的含有人脸的照片,找到OpenCV安装目录下的⁨haarcascades⁩,里面有很多OpenCV3官方训练好的分类器,我们选择haarcascade_frontalface_default.xml复制到项目里,为了方便调用重命名为face.xml。
        测试环境Python3.6 OpenCV3.4,测试代码:

import cv2
# 载入分类器
face_haar = cv2.CascadeClassifier('face.xml')
# 载入测试图
img = cv2.imread('test.jpg')
# 转换彩色空间
gray_img = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
# cv2.imshow('theImg',gray_img) #窗口显示

faces=face_haar.detectMultiScale(gray_img,1.3,3) #目标检测
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()

BUT,你可能会遇到这样的报错:

Traceback (most recent call last):
  File "/Users/dave/PycharmProjects/OpenCVtest/2.py", line 7, in <module>
    gray_img = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
cv2.error: OpenCV(3.4.2) /opt/concourse/worker/volumes/live/9523d527-1b9e-48e0-7ed0-a36adde286f0/volume/opencv-suite_1535558719691/work/modules/imgproc/src/color.hpp:253: error: (-215:Assertion failed) VScn::contains(scn) && VDcn::contains(dcn) && VDepth::contains(depth) in function 'CvtHelper'

这个错误很大原因因为你没有使用绝对路径。我曾经把图片移动到img文件夹并使用/img/***.jpg相对路径会报错。

效果:在这里插入图片描述

test pass!

       那么,我们想要识别其他物体如 显示器 键盘 鼠标等怎么办呢,办法有两个 其一 论坛搜索其他人共享的训练好的分类器。其二 自己训练分类器。

如果自己训练Haar分类器主要步骤:

第0步,安装OpenCV: 7步完成opencv的安装

针对物体非刚性:如人脸(丑的帅的都是脸)

    因为人脸千变万化,好看的不好看的都是人脸,圆的方的脸,被头发遮住的脸,秃头的脸,等等这就意味着我们要采集很多正样本人脸信息,以便提取人脸的主要特征。因为Haar对光影变化比较敏感,针对眼球,鼻影等特征提出来,检测视频或者图片中是否包含人脸时,和训练结果集xml文件中记录的特征对比,比如:有鼻子,有眼,就是人脸,OK。那训练这样物体的办法如下:
        1、搜集制作成千上万张正样本图像,确保这些图像中包含要检测的对象
        2、搜集制作成千上万张负样本图像,什么图片都行,但是确保要检测的对象不在图像中
(http://image-net.org是不错的图像资源站)
        3、创建”积极”向量文件
        4、使用OpenCV训练Haar分类器

注意:正负样本最好源于实际应用场地效果才会最好。

针对刚性物体的办法:如网球 (都长一个样)

        如果像我这样网球不好采集怎么办呢,或者网球这样子不像人脸那样有诸多特征,这样可能只需要一张网球全身照就可以了,不过这样在实际效果中可能差一点(网球也有logo之类不同),可以这样办:
        1、opencv_createsamples命令创建pos.txt文件。它会把要识别的图片嵌入到消极图像中,允许我们快速创建”积极”图像。
        2、搜集制作成千上万张负样本图像,什么图片都行,但是确保要检测的对象不在图像中
(http://image-net.org是不错的图像资源站)
        3、创建”积极”向量文件
        4、使用OpenCV训练Haar分类器

step 1 and 2 采集

       因为是毕设,我想让预期达到最佳效果,所以才用的是采集大量不同的正负样本训练,我还在家,没有场地,所以网上搜了400张网球照片和1200张负样本,(截400张图手指都抽筋,不采集了)如下:
在这里插入图片描述

       百度爬的照片然后一张一张裁的 笨办法。。我感觉负样本质量可能不好,后面果然出了问题?
       采集好之后我找了个windows电脑,用美图秀秀批处理成灰度图然后序列化重命名(重命名这一步可以不做)?其中,按照OpenCV官方建议正样本是20像素x20像素,但是哪样的话网球就太模糊了,所以就调整为50X50像素了(是否影响最终效果我弄懂了再修改=_=)。 负样本除了灰度以外不必调整,分辨率自由。
(2019-03-08更新:采用50x50像素后识别效果好了很多,但同时训练时间也是成倍增加,在20X20像素下大约3-5分钟训练完成。50x50下9小时完成)

(2019-03-14更新:分辨率的选择可以参考另外一个博主的文章 https://blog.csdn.net/qq_37791134/article/details/80583726)
在这里插入图片描述
就这样 我把正样本也就是有网球的图片放在了pos文件夹中,把消极图片放在了neg文件夹中。
然后创建正负样本目录,格式如下:
< 绝对路径 1 0 0 w h >
在这里有个简单的方法创建pos.txt文件,我用的Windows 下的这个命令:

cd 你的正样本目录
dir  /s/b >pos.txt

这句话的意思是将当前目录下的文件名写入到pos.txt文件中,当然最后一行会多出来///pos.txt,需要把这一行删掉。「特别注意:如果在Mac或者服务器等其他地方训练记得把pos.txt 和 neg.txt文件的绝对路径修改正确」

在这里插入图片描述
其中 /***/***/***.jpg是图片的绝对路径

1 指只这个目录下只有1个文件

0 0 指的是正样本从像素的00位置(左上角开始)到20 20(图像的尺寸) 位置结束是正样本。
如果你的尺寸和我的不一样后面那个值改为你的像素即可。

再然后创建负样本路径:这里只需写绝对路径即可。
在这里插入图片描述

step 3:创建向量

一行代码:

cd 你的pos.txt所在目录
 opencv_createsamples -info pos.txt -num 400 -w 20 -h 30 -vec pos.vec

pop.txt 是上一步骤的正向量目录文件
num是其中的正样本数量,
w是宽h高
pos.vec是生成的正向量文件名

step 4:开始训练

在你的电脑或者服务器切换到你的工作目录,我的是
–/boost
----/data (空文件夹,用于存储训练结果)
----/neg (消极图片)
----/pos (积极图片)
----/pos.txt (积极图片的绝对路径等)
----/pos.vec (生成的积极向量)

我的参数:

mkdir data
$ opencv_traincascade -data data -vec pos.vec -bg neg.txt -numPos 320 -numNeg 960 -numStages 15 -w 20-h 20  

    我在服务器训练的,使用的156G运行内存,在上述命令尾部添加了-mem 150000.实际占用不了这么多,在20x20像素下占用了2-4GB 60x60像素下占用了20-25GB 。训练的特征值会存入到内存中。在一定条件下内存越大理论训练速度越快
opencv_createsamples各个参数的意义:

-data:指定保存训练结果的文件夹;

-vec:指定正样本集;

-bg:指定负样本的描述文件夹;

-numPos:指定每一级参与训练的正样本的数目(要小于正样本总数,一般取值85%左右);

-numNeg:指定每一级参与训练的负样本的数目(可以大于负样本图片的总数,大于的话就会自动裁剪负样本训练);

-numStage:训练的级数;

-w:正样本的宽;

-h:正样本的高;

-mem 1280 表示允许使用计算机的1280M内存

-minHitRate:每一级需要达到的命中率(一般取值0.95-0.995);

-maxFalseAlarmRate:每一级所允许的最大误检率;

-mode:使用Haar-like特征时使用,可选BASIC、CORE或者ALL;

-featureType:可选HAAR或LBP,默认为HAAR;


    如果你在ssh远程登陆服务器上训练的话,建议采用screen窗口来执行,以免训练时间过长网络不稳定或者ssh超时等中断训练。具体请参见:

树莓派安装screen保证SSH断开任务不中断

    开始时候忘记截图了,这边的是训练完成的截图。可以看到下面的Requird leaf false alarm rate achieved …… 这个意思是设置的训练层数过多,设置的15层,实际在11层时候就已经达到了预期效果。
在这里插入图片描述ojbk,现在你会发现在data目录下多了很多文件,(服务器关了没截图=_=),里面的cascade.xml是我们训练好的级联分类器。现在来使用我们自己的这个分类器吧。


Demo和上面的人脸识别一样,只需要把Demo中cascade.xml替换为我们的就行,Example:

import cv2
# 载入分类器
face_haar = cv2.CascadeClassifier('cascade2.xml')
# 载入测试图
img = cv2.imread('z2.jpg')
# 转换彩色空间
gray_img = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
# cv2.imshow('theImg',gray_img) #窗口显示

faces=face_haar.detectMultiScale(gray_img,1.1,2) #目标检测
# xy是ball起点位置,w h是宽高
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()

用的20块钱还包邮的ps3摄像头,夜间拍摄,能识别出来已经谢天谢地了。
在这里插入图片描述
你可能第一次出现检测不到的情况,别急,试着调节以下参数:

faces=face_haar.detectMultiScale(gray_img,1.1,2) #目标检测

可以吧比例因子 1.1 改为1.2或者1.3试试,minNeighbors 2改为3或者1试试

detectMultiScale函数参数解释:

cv.CascadeClassifier.detectMultiScale(	image [,scaleFactor [,minNeighbors [,flags [,minSize [,maxSize [,outputRejectLevels]]]]]]	)

image : CV_8U类型的矩阵,包含检测到对象的图像。

scaleFactor : 比例因子 参数指定在每个图像比例下图像尺寸减少的程度。

minNeighbors: 参数指定每个相邻候选矩形应保留多少个。

flag标志位:3.X新的级联已放弃治疗。

minSize属性 最小可能的对象大小。小于该值的对象将被忽略。比如偏远的网球太小了可以忽略

MAXSIZE 最大可能的对象大小。大于该值的对象将被忽略。

同时,如果maxSize == minSize模型是单一尺度评估的。可以用于检测正视图中同一水平线的网球。

详细可以参考官方文档或阅读源码:https://www.docs.opencv.org/3.4/d1/de5/classcv_1_1CascadeClassifier.html#aaf8181cb63968136476ec4204ffca498


    至此,已经完成了基于Haar分类器的网球识别。再往下呢,不如开启摄像头识别下吧。
Demo:
已经23:20了不写注释了

import cv2

cap = cv2.VideoCapture(0)
cap.set(3, 640)
cap.set(4, 480)
ball_cascade = cv2.CascadeClassifier('cascade2.xml')
while (True):
    flag, video = cap.read()
    gray = cv2.cvtColor(video, cv2.COLOR_RGB2GRAY)
    ball = ball_cascade.detectMultiScale(gray , 1.1, 2)
    for (x, y, w, h) in ball:
        cv2.rectangle(video, (x, y), (x + w, y + h), (255, 0, 0), 2)
        cv2.rectangle(video, (x + [w/2], y + [h/2]), (x + [w/2] + 4, y + [h/2] + 4), (0, 0, 255), 10)
        print('中心位置:  '+str(x + w/2)+'  '+str(y + h/2)+'  '+'横坐标偏离:'+str(320 - x - w/2))
        # cv2.namedWindow("img", 0)
        cv2.imshow("Capture_Test", cv2.cvtColor(video, cv2.COLOR_RGB2GRAY))  # 窗口显示,显示名为 Capture_Test
        k = cv2.waitKey(100) & 0xFF  # 每帧数据延时 1ms,延时不能为 0,否则读取的结果

cap.release()
cv2.destroyAllWindows()

test:
视频怎么上传啊???黑人问号














  • 7
    点赞
  • 34
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 8
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

崔斐然

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值