这两小节都有相关博文做过介绍,这里直接甩链接就好,减少文章篇幅
链接 → OpenMV新手上路1 – OpenMV简介、参数描述
同样的,甩博文链接
OpenMV新手上路2 – 驱动、IDE安装及简单使用(window环境)
打开OpenMV IDE,点击左上角的“文件→新建文件”进行新的OpenMV项目创建。
创建成功后IDE页面将打开一个“untitled_1.py”文件,并带有OpenMV项目代码的初始代码(类似新建Arduino例程时的初始代码是setup()函数和loop()函数),初始代码运行的功能为初始化OpenMV传感器,并循环将传感器获取到的图像显示到IDE上,同时打印帧数。
在智能巡防小车的项目中,OpenMV的主要功能是做黑线识别(即颜色识别)和坐标值发送,关于OpenMV的颜色识别功能,有以下几个知识点需要掌握。
追踪小球是OpenMV用得最多的功能,实质上是通过find_blobs函数去找到图像中的指定色块,即在OpenMV图像视野中分辨出需要查找的颜色。
接下来看一下find_blobs函数的细节:
image.find_blobs(thresholds, roi=Auto, x_stride=2, y_stride=1, invert=False, area_threshold=10, pixels_threshold=10, merge=False, margin=0, threshold_cb=None, merge_cb=None)
这里的参数比较多。
red = (xxx,xxx,xxx,xxx,xxx,xxx)
blue = (xxx,xxx,xxx,xxx,xxx,xxx)
yellow = (xxx,xxx,xxx,xxx,xxx,xxx)
img=sensor.snapshot()
red_blobs = img.find_blobs([red])
color_blobs = img.find_blobs([red,blue, yellow])
left_roi = [0,0,160,240]
blobs = img.find_blobs([red],roi=left_roi)
blobs = img.find_blobs([red],x_stride=10)
blobs = img.find_blobs([red],y_stride=5)
all_blobs = img.find_blobs([red,blue,yellow],merge=True)
red_blobs = img.find_blobs([red],merge=True)
blue_blobs = img.find_blobs([blue],merge=True)
yellow_blobs = img.find_blobs([yellow],merge=True)
阈值即对某个特定颜色或某个色域规定的一组参数,一个颜色阈值的结构是这样的:
red = (minL, maxL, minA, maxA, minB, maxB)
元组里面的数值分别是L A B 的最大值和最小值。
L A B参数可在图像直方图中直接看到数据。
在IDE里,还有更方便的阈值选择工具。
OpenMV 的IDE里加入了阈值选择工具,极大的方便了对于颜色阈值的调试。
首先运行untitled_1.py让IDE里的帧缓冲区显示图案。
然后打开“工具→机器视觉→阈值编辑器”。
选择帧缓冲区可以获取IDE中的图像,选择图像文件可以另外选择一张图片进行阈值编辑。
选择帧缓冲区打开阈值编辑器,可看到如图中的二进制图像及原图像,拖动下方L A B最大值及最小值的滑轨改变参数可进行颜色阈值获取。
想要获取源图像中蓝色小方块的阈值时,拖动L最小值与L最大值形成L值范围,确保在二进制图像中蓝色小方块所在的位置为白色,二进制图像中其他位置为黑色,A值与B值的调参方式也是如此,最后复制底部调参好的L A B数值。
find_blobs对象返回的是多个blob的列表。(注意区分blobs和blob,这只是一个名字,用来区分多个色块,和一个色块)。
列表类似与C语言的数组,一个blobs列表里包含很多blob对象,blobs对象就是色块,每个blobs对象包含一个色块的信息。
blobs = img.find_blobs([red])
blobs就是很多色块。
可以用for循环把所有的色块找一遍。
for blob in blobs:
print(blob.cx())
blob有多个方法:
blob.rect() 返回这个色块的外框——矩形元组(x, y, w, h),可以直接在image.draw_rectangle中使用。
blob.x() 返回色块的外框的x坐标(int),也可以通过blob[0]来获取。
blob.y() 返回色块的外框的y坐标(int),也可以通过blob[1]来获取。
blob.w() 返回色块的外框的宽度w(int),也可以通过blob[2]来获取。
blob.h() 返回色块的外框的高度h(int),也可以通过blob[3]来获取。
blob.pixels() 返回色块的像素数量(int),也可以通过blob[4]来获取。
blob.cx() 返回色块的外框的中心x坐标(int),也可以通过blob[5]来获取。
blob.cy() 返回色块的外框的中心y坐标(int),也可以通过blob[6]来获取。
blob.rotation() 返回色块的旋转角度(单位为弧度)(float)。如果色块类似一个铅笔,那么这个值为0 ~ 180°。如果色块是一个圆,那么这个值是无用的。如果色块完全没有对称性,那么你会得到0 ~ 360°,也可以通过blob[7]来获取。
blob.code() 返回一个16bit数字,每一个bit会对应每一个阈值。
blobs = img.find_blobs([red, blue, yellow], merge=True)
如果这个色块是红色,那么它的code就是0001,如果是蓝色,那么它的code就是0010。注意:一个blob可能是合并的,如果是红色和蓝色的blob,那么这个blob就是0011。这个功能可以用于查找颜色代码。也可以通过blob[8]来获取。
blob.count() 如果merge=True,那么就会有多个blob被合并到一个blob,这个函数返回的就是这个的数量。如果merge=False,那么返回值总是1。也可以通过blob[9]来获取。
blob.area() 返回色块的外框的面积。应该等于(w * h)。
blob.density() 返回色块的密度。这等于色块的像素数除以外框的区域。如果密度较低,那么说明目标锁定的不是很好。
比如,识别一个红色的圆,返回的blob.pixels()是目标圆的像素点数,blob.area()是圆的外接正方形的面积。
下面以智能巡防小车的OpenMV源码做分段说明。
相关调用模块引入。
import sensor, image, time # 引入感光元件模块、图片处理模块、时间模块
from pyb import UART # 引入串口模块
import json # 引入json字符串模块
from pyb import LED # 引入LED控制模块
设置项目中黑线检测的阈值,以及在图像中的感兴趣区(ROI)。
user_threshold = (0, 32, -128, 127, -128, 127) # 设置黑线阈值参数
ROIS = (0,100,320,40) # 设置roi感兴趣区
设置串口及串口波特率。
#设置串口号及串口波特率
uart = UART(3,115200)
初始化摄像头参数,包括感光元件复位,设置图像色彩,图像大小,以及在寻找色块(巡线)功能下需要关闭自动增益功能和白平衡功能。
#设置摄像头
sensor.reset() # 初始化感光元件
sensor.set_pixformat(sensor.RGB565) # 设置像素模式为彩色
sensor.set_framesize(sensor.QVGA) # 设置图像大小为QVGA:320*240
sensor.skip_frames(time = 2000) # 跳过前几帧的图片
sensor.set_auto_gain(False) # 关闭自动增益 -- 巡线模式下需要关闭
sensor.set_auto_whitebal(False) # 关闭白平衡 -- 巡线模式下需要关闭
取得系统时间,并点亮板载RGB灯为白色,加强图像颜色识别。
# 获取系统时间
clock = time.clock()
# 打开板载LED灯(白色)
LED(1).on()
LED(2).on()
LED(3).on()
新建全局变量。
# 新建全局变量
dis_sum = 0
sum_count = 0
value_get = False
进入while循环,开始计算运行时间,并用OpenMV摄像头进行拍照将图像传至IDE中,随后新建局部变量。
while(True):
clock.tick() # 开始计算运行时间
img = sensor.snapshot() # 获取一张图像
# 新建变量
distance = 0
distance_send = False
在获取的图像中进行roi区域划分和黑线颜色阈值检测,并把找到的黑线用矩形和矩形中心坐标表示出来,判断找到的黑线是否符合要求。
# 将find_blobs返回的blob对象赋值给b
for b in img.find_blobs([user_threshold],roi=ROIS[0:4], merge=True):
# 判断黑线色块大小,排除图像干扰
if (b[4] >= 30) and (b[3] >= 30):
img.draw_rectangle(b[0:4],color=(255,0,0)) # 画矩形
img.draw_cross(b.cx(),b.cy(),color=(0,0,0)) # 画中心十字
# 判断所检测到的黑线是否在图像范围内
if b[5]< 0 or b[5] > 320:
distance = 0
distance_send = False
else:
distance = b[5]
distance_send = True
将获取到的中心坐标每10次取一次平均值再取整,使用json脚本将角度值转换成字符串通过串口发送出去。
# 判断正确获取到黑线位置
if distance_send == True:
dis_sum += distance
sum_count += 1
# 取10次的坐标平均值
if sum_count == 10:
AngleValue = round(dis_sum/sum_count - 160) # 计算角度值并取整数
# LastAngleValue进行角度赋值
if not value_get:
LastAngleValue = AngleValue
value_get = True
# 对比角度是否发生变化
if (LastAngleValue != AngleValue):
ubuffer = json.dumps(AngleValue) # 将新角度转换成字符串
LastAngleValue = AngleValue # 将角度值进行赋值
uart.write(ubuffer+'\n') # 串口发送角度值字符串
dis_sum = 0
sum_count = 0
为了用OpenMV做黑线检测,博主也是简单学了一门新语言(Micropython),并用简单的逻辑将摄像头捕捉到的黑线坐标变化通过串口进行发送,因为OpenMV为32位处理器,对每一帧图像的处理速度是比较快的,同时将坐标数据发送到8位处理器(UNO)上可能造成UNO的串口数据堵塞,处理不来,所以博主在OpenMV坐标发送前也是做了取平均值的操作,每10个坐标数据折算成1个平均值进行发送,减少UNO串口数据接收压力,坐标数据也相对更加稳定、准确(初学micropython对相关数学函数及逻辑使用方面不熟,Python大神勿喷)
OpenMV IDE除了可以在线进行脚本运行仿真,也能将调试好的py脚本上传至OpenMV Cam成为OpenMV Cam的上电运行脚本。
调试好OpenMV脚本之后,断开脚本运行,在菜单栏“工具 → 将打开的脚本保存到OpenMV Cam(作为main.py)”,在OpenMV Cam中main.py为上电运行脚本,此操作相当于把OpenMV Cam内存中main.py文件进行替换。
在脚本正确上传过程中,OpenMV Cam板载的RGB灯会是红色状态,在上传完成后,RGB灯熄灭,如果不能正确上传脚本,请检查IDE是否正常连接了OpenMV Cam。