传统图像处理模块
更新时间 | 负责人 | 内容 | 备注 |
---|---|---|---|
2022年4月13日 | Ray | 初次编写文档 | --- |
2022年4月24日 | Coty | 添加AprilTag识别、三维坐标系以及红蓝小球追踪 | --- |
2022年4月25日 | Coty | 添加AprilTag多个三维坐标显示,二维码和条形码的定位、读取信息 | --- |
2022年4月28日 | Dls | 修订排版,以下功能仅在 0.4.7 以上版本支持 | --- |
2022年7月04日 | Dls | 更新了 特征检测模块(特征提取追踪)相关的内容 | --- |
一、传统视觉模块 #
2022年07月22日 想参与开发的点此了解【图像处理开发】传统视觉算法
主要分为以下几大块:
图像统计模块(获取图像信息)
颜色追踪模块(追踪颜色信息)
标记追踪模块(条码定位测距)
图像滤波模块(传统图像处理)
特征检测模块(特征提取追踪)
二、颜色统计模块 #
Traceback (most recent call last): File "<string>", line unknown, in <module> Remote.KeyboardInterrupt
三、颜色追踪模块 #
(2022年07月21日)以红蓝小球追踪为例识别并通过串口发送数据,基于 0.5.1 以后版本实现。
此代码配合其他MCU运行效果演示如图所示:
从演示图可以看出,MaixII-Dock 成功获得了小球距离中心点的偏差,通过串口发送数据给 MCU ,通过 PID 控制舵机带动摄像头转动,使得小球重新回归中心点位。
set_LAB = [[(10, 35, -37, 70, 73, 62)], #red
[(10, 5, -87, 79, 62, -28)]] #blue
对于LAB阈值的设置需要注意的是初始化的格式,格式为:[L_MIN , A_MIN , B_MIN , L_MAX , A_MAX , B_MAX],此处初始化了红色和蓝色的LAB阈值。
blobs = img.find_blobs(set_LAB[j])
find_blobs() 函数目的是寻找对应的色块。
将目标区域的信息获取之后使用 img.draw_circle() 函数将目标圈住,最后将球距离中心点的偏差值( x 和 y 的偏差值)发送至 MCU,由 MCU 进行后续的计算。
此代码运行效果演示如图所示:
在演示1中可以看到 MaixII-Dock 能够清楚地跟踪标签四个点的坐标并且显示出来(绿色框),以及能够跟踪标签的最大框的坐标(蓝色框),其实用户想要获取这些信息并不难。
如果想要获取标签四个点的坐标只需要获取字典中键值为 corners 的列表中的值即可,而想获得外框(蓝色框)的坐标只需要获取字典中键值为 x , y , w , h 的的值,最后像代码中所操作的一样即可。
字典的键值如下所示: 'x' , 'y' , 'w' , 'h' , 'id' , 'family' , 'centroid' , 'corners' , 'x_translation' , 'y_translation' , 'z_translation' , 'decision_margin' , 'hamming' , 'goodness' , 'x_rotation' , 'y_rotation' , 'z_rotation'。
在此仅介绍简单几个键值的含义。'x' , 'y' , 'w' , 'h' 键值返回的值分别是外框左上角 x 坐标和 y 坐标以及外框的长和宽。特别注意的是键值 'corners' 返回的是一个列表,列表中的值代表着内框的四个顶点的坐标。
在演示2中可以看到两个标签距离摄像头大概是22cm左右,那么究竟是如何测出这个距离的呢?请接着往下看:
对于测距算法,需要知道的是:
fx,以像素为单位的相机 X 焦距;
fy,以像素为单位的相机 Y 焦距;
cx,图像中心image.width()/2;
cy,图像中心image.height()/2;
具体 fx , fy , cx , cy 的计算在上诉代码注释区。
测距算法使用流程:首先将 fx ,fy ,cx ,cy 传入 find_apriltags() 函数内部,读取返回的 x_translation ,y_translation ,z_translation 参数。由length = (x_tranx_tran + y_trany_tran + z_tranz_tran)*0.5 可以算出虚拟距离,这个虚拟距离 length 乘上一个比例系数 K 才是最终物体离 CMOS 的最终距离。
比例系数 K 的计算:假设我们现在将一个物体放置在离 CMOS 距离 20cm 的地方,然后直接打印出 length 计算的结果,此时的 length 假设是 6.525 ,那么 K = 20 / 6.525 = 3.065。这时我们将 length 乘上这个比例系数 K 得出的结果就是真实的距离。也就是说在第一次使用的时候需要确定比例系数 K ,在确定了 K 的大小过后,以后的使用就直接使用计算出来的比例系数 K 乘上计算出来的虚拟距离 length ,就是最后真实的距离。(需要注意的是:最后算出的真实距离并不是标签距离摄像头的距离,而是标签距离CMOS的距离。以MaixII-Dock为例,标配的摄像头镜头距离CMOS的距离大概是2cm,那么需要将最后算出的距离再减去2cm最后得出的结果才是标签距离摄像头的距离。)
4.2. AprilTag 获取角度信息以及三维坐标的显示 #
此代码运行效果演示如图所示:
可以看出在显示屏左上角打印出了标签的当前角度信息,那么如何获取这些信息呢?
如果想要获取标签的角度信息只需要获取字典中键值分别是 x_rotation,y_rotation,z_rotation 的的值即可,需要指出的是获取的值是弧度,需要转换成角度的话需要使用弧度转角度公式。将获取的弧度其乘上 180 再除以 3.14 即可(3.14选取了圆周率小数点后两位)。
x_rotation 转为角度后取值范围是 [90°,270°] 正对着标签时显示为 180° ,在标签前后移动时变化。 y_rotation 转为角度后取值范围是 [0°,90°]U[270°,360°] 正对着标签时显示为 0° ,在标签左右移动时变化。z_rotation 转为角度后取值范围是 [0°,360°] 正对着标签时显示为 0° ,在标签旋转时变化。
三维坐标系显示
三维坐标系生成代码如上代码中所示,将每一个轴的变化线性叠加即可获得一个不太准确的三维坐标系。
前后的变化会导致 x_rotation 的变化。当 x_rotation 变化时,X 轴 y 的长度应该为 40 * cos(x_rol) ,但是 x_rotation 转换为角度的范围是 [90°,270°] 且正对标签时为 180°,画出 cos 图发现在定义域内 180° 点为极小值点,且整个取值区域内 cos 值都为负,我们不希望得到这样的图像。解决方法是将 cos 向上平移 40 ,使得 180° 点cos值为 0 ,而 90° 和 270° 为区间的最高点,是一个正数。此时终点 y 的结果为 **200 + 40 + 40 * cos(x_rol),即转至 90° 或 270° 时,X 轴的长度为 200,与开始点重合,实际长度为 0 。而对于 Z 轴的长度处理思路相似,但是 Z 轴的投影使用的是 sin 函数,在 [90°,270°] 这个区间 90° 是极大值点且此时 sin 值为正数,270° 是极小值点且此时 sin 值为负数,正好符合实际需求,故不作处理。最后 Z 轴终点 y 的结果为200 - 40 * sin(x_rol)**。
左右的变化会导致 y_rotation 的变化。当 y_rotation 变化时,Y 轴 x 的长度应该为 40 * cos(y_rol),但是 y_rotation 转换为角度的范围是 [0°,90°]U[270°,360°],且正对标签时为 0°,画出cos图发现图像被分割为了两个部分,但是不影响使用,因为在角度 [270°,360°] 内会随着倾角的增大而逐渐减小,所以最后终点 x 的结果是 **180 + 40 * cos(y_rol)。而对于 Z 轴的长度处理思路相似,此处不做过多解释,最终 Z 轴终点 x 的结果为180 + 40 * sin(y_rol)**。
旋转的变化会导致 z_rotation 的变化。当 z_rotation 变化时,X 轴 x 的长度应该为 40 * sin(z_rol),但此时由于 z_rotation 转换而来的角度会随着逆时针的转动而增大,所以 X 轴 x 的长度应该乘以 -1 ,这样才能符合实际变化,最后终点 x 的结果为**180 - 40 * sin(z_rol)**。X 轴 y 的长度为 40 * cos(z_rol),但是屏幕从上而下的像素点的值是逐渐增大的,所以虽然 cos 值在区间内部的变化是符合我们的预期,但是依旧要乘以 -1,最后终点 y 结果为 **200 - 40 * cos(z_rol)。对于 Y 轴的 x 和 y 的长度处理思路与上诉一样,但对于 x 的长度需要减去一个 40 抵消掉初始增量,最后终点结果分别为 180 - 40 + 40 * cos(z_rol)** 和 **200 - 40 * sin(z_rol)**。
最后将所有计算式线性叠加即可获得最终三维坐标系。由于算式是基于正对着标签码进行计算的,所以对于非正拿标签的情况,坐标系会出现混乱
4.3. AprilTag多个三维坐标的显示 #
此代码运行效果演示如图所示:
在演示图中可以看到在每个标签上都打印出了一个三维坐标系,并且能够很好显示当前标签的角度信息。只需要将三维坐标系的原点定义在标签的左下角,且将三维坐标画线函数放置在for循环内部,即可出现如上图所示效果。
4.4. 定位二维码及识别结果 #
此代码运行效果演示如图所示:
在演示中可以看到 MaixII-Dock 能够清楚地跟踪并且框住二维码,将二维码信息打印到屏幕上。那么如何获取二维码坐标值以及信息呢?其实非常简单。
- 获取外框坐标:读取 find_qrcodes() 函数返回的字典中键值为: x , y , w , h 的值作为二维码外框的坐标信息(蓝色框)。
- 获取内框坐标:读取 find_qrcodes() 函数返回的字典中键值为: corners 的列表中的值作为二维码内框的坐标(绿色框)。
- 获取二维码信息:读取 find_qrcodes() 函数返回的字典中键值为: payload 的值作为二维码信息。
字典的键值如下所示:'x' , 'y' , 'w' , 'h' , 'payload' , 'version' , 'ecc_level' , 'mask' , 'data_type' , 'eci' , 'corners'。
在此仅介绍简单几个键值的含义。'x' , 'y' , 'w' , 'h' 键值返回的值分别是外框左上角 x 坐标和 y 坐标以及外框的长和宽。而键值 'payload' 返回的值是二维码的信息,例如在二维码生成网站上输入 "Sipeed" 生成二维码,此时这个二维码的 'payload' 就是 "Sipeed"。
4.5. 定位条形码及识别结果 #
此代码运行效果演示如图所示:
在演示中可以看到 MaixII-Dock 能够清楚地跟踪并且框住条形码,将条形码信息以及类型打印到屏幕上。那么如何获取二维码坐标值以及相关信息呢?其实非常简单。
- 获取条形码坐标:读取 find_barcodes() 函数返回的字典中键值为: corners 的列表中的值作为条形码的坐标(绿色框)。
- 获取条形码信息:读取 find_barcodes() 函数返回的字典中键值为: payload 的值作为条形码信息。
- 获取条形码类型:读取 find_barcodes() 函数返回的字典中键值为: type 的值作为条形码的类型 (注意:此处条形码类型选择的是 CODE39 ,故键值为 type 返回的值为 12 ,如果将条形码类型换为 CODE128 ,则键值为 type 返回的值为 15 )。
字典的键值如下所示:'x' , 'y' , 'w' , 'h' , 'payload' , 'rotation' , 'type' , 'quality' , 'corners'。
在此仅介绍简单几个键值的含义。'corners' 键值返回的是一个列表,列表中的值分别条形码四个顶点的坐标。而键值 'type' 返回的值是条形码的类型,'payload' 返回的值是条形码的内容。
五、图像滤波模块 (maixpy3 >= 0.4.9) #
为 maixpy3 添加 opencv 的函数请看这个提交 [image] How to add _opencv_Canny.。
为 maixpy3 添加 openmv 的函数请看这个提交[example] use costom_imlib_config and image imlib_rotation_corr.。
- lens_corr
- rotation_corr
- histeq
- mean
- Canny
暂不做接口说明,但均已实现,详细请看源码。
Traceback (most recent call last): File "<string>", line unknown, in <module> Remote.KeyboardInterrupt
6.1. 模板匹配( find_template ) #
采用的是ncc算法,只能匹配与模板图片大小和角度近乎一致的图案。局限性相对来说比较大,视野中的目标图案稍微比模板图片大一些或者小一些就可能匹配不成功,想要多角度多大小匹配可以尝试保存多个模板。
模板匹配适应于摄像头与目标物体之间距离确定,不需要动态移动的情况,比如适应于流水线上特定物体的检测。而不适应于小车追踪一个运动的排球(因为运动的排球与摄像头的距离是动态的,摄像头看到的球大小会变化,不会与模板图片完全一样)。
Traceback (most recent call last): File "<string>", line unknown, in <module> Remote.KeyboardInterrupt
6.2. orb 特征提取与匹配 image.cv_orb().match #
ORB(Oriented FAST and Rotated BRIEF)是一种快速特征点提取和描述的算法。这个算法是由Ethan Rublee, Vincent Rabaud, Kurt Konolige以及Gary R.Bradski在2011年一篇名为“ORB:An Efficient Alternative to SIFTor SURF”的文章中提出。ORB算法分为两部分,分别是特征点提取和特征点描述。
该实现流程如下:70b4e4e3777ed70f9b3f59724362295470d7f0e4
提取一张图像作为目标物体特征,确定了目标特征后运行匹配函数(match)进行比较,返回识别特征点合集,如果匹配结果在预定的特征范围内,则可以认为是具备同一类特征事物的物体。
感兴趣的同学可以参考该文章:ORB 特征提取算法(理论篇)
(0.5.2 更新)该实现仅供学习和示意,实际表现效果帧率不错,但只适用于一些特定场景下的识别,如果想要泛化更好的效果推荐使用神经网络模型。
Traceback (most recent call last): File "<string>", line unknown, in <module> Remote.KeyboardInterrupt
以下为 openmv 版本实现流程,和上述是实现效果一致的,差异在匹配的后处理如何过滤一些数据,让追踪的效果更好。
find_keypoints 找特征点
draw_keypoints 画特征点
match_descriptor 匹配的点
if (match.count()>10): 满足匹配点结果
match.count(), match.theta() 特征物体相对目标物体的旋转角度。
主要看性能和效果了,传统视觉中 orb 的速度是比较快的,就算在嵌入式设备上纯 CPU 运算也有不错的效果。
Related Issues not found
Please login GitHub to create issue