admin管理员组

文章数量:1544799

一.

openmv适合做一些小项目,比如追踪小球的小车,云台。还有对成本要求较高的嵌入式工业方案,比如流水线物品分拣。不适合复杂的算法,比如车牌识别,猫狗分类,深度学习,ocr识别等。

openmv图像处理的方法:

  1. 感光元件
  2. 基本方法
  3. 使用统计信息
  4. 滤波
  5. 画图
  6. 寻找色块
  7. AprilTag标记跟踪
  8. 模板匹配
  9. 特征点检测

 二.感光元件

1.sensor模块,用于设置感光元件的参数

sensor.reset()   初始化感光元件

那么,感光元件有哪些模式设置参数呢?

  1. sensor.set_pixformat()   设置彩色/黑白     sensor.GRAYSCALE灰度模式,每个像素8bit;    sensor.RGB565彩色模式,每个像素16bit。
  2. sensor.set_framesize()        设置图像大小。有多种参数,表示不同的图像大小,适用于不同的感光元件。
  3. sensor.skip_frames(n=10)        跳过一些帧。在更改设置之后,跳过n张照片,等待感光元件变稳定。
  4. sensor.snapshot()        拍摄一张照片,返回一个image对象。
  5. sensor.set_auto_gain()        自动增益开启(True),关闭(False)。在使用颜色追踪时,要关闭自动增益。
  6. sensor.set_auto_whitebal()        自动白平衡开启,关闭。在使用颜色追踪时,要关闭自动白平衡。
  7. sensor.set_auto_exposure(enable[\,exposure_us])        enable打开True自动曝光(默认打开),关闭False可以使用exposure_us设置一个固定曝光时间,单位微秒。
  8. sensor.set_windowing(roi)        设置窗口ROI。ROI是感兴趣区,从要处理的图像中提取出要处理的区域。roi的格式是(x,y,w,h)。x,y是roi区域左上角的坐标;w,h是图像大小。
  9. sensor.set_hmirror(True)水平方向翻转
  10. sensor.set_vflip(True)垂直方向翻转

三.基本方法

image模块,对图像/像素的设置,获取和运算

1.设置/获取像素点

  • image.get_pixel(x,y)        对于灰度图,返回像素点的灰度值;对于彩色图,返回RGB值
  • image.set_pixel(x,y,pixel)        设置像素点的灰度值或RGB值

2.获取图像的宽度和高度

  • image.width()
  • image.height()
  • image.format()        返回灰度值或者RGB值
  • image.size()

3.图像运算

  • image.invert()        对于二值化图像,取反,黑变白,白变黑
  • image.nand(image)        与另一个图像进行与非运算
  • image.nor(image)        或非运算
  • image.xor(image)        异或运算
  • image.difference(image)        从这张图片中减去另一个图片。常用作移动检测。

四.使用统计信息

image.get_statistics(roi=(x,y,w,h))        从ROI中获取数据

对于灰度图,有灰度的平均数,众数,中位数等;彩色图有L,A,B三个通道的平均数,众数,中位数等。

  • statistics.mean() 返回灰度平均数        statistics.l_mean()  返回L通道的平均数
  •  statistics.median()中位数         statistics.l_median()
  • statistics.mode()众数        statistics.l_mode()

五.画图

可以画线,画框,画圆,画十字,写字。

  • img.draw_line(line_tuple, color=White),line_tuple是(x0,y0,x1,y1),表示从0点到1点的直线
  • img.draw_rectangle(rect_tuple, color=White),rect_tuple是(x,y,w,h)
  • img.draw_circle(x, y, radius, color=White),圆心坐标和半径
  • img.draw_cross(x, y, size, color=White),坐标和两侧尺寸
  • img.draw_string(x, y, text, color=White)

六.寻找色块

find_blobs函数:对象返回是一个列表,类似于C语言的数组。一个blobs列表包含很多blob对象,每一个对象包含一个色块的信息。

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)
 

  • thresholds颜色阈值,这个参数是一个列表,可包含多个颜色。元组里面的数值分别为LAB的最大值和最小值。
  • x_stride是查找色块的x方向上最小宽度的像素。y_stride同理。
  • invert反转阈值,把阈值之外的颜色作为阈值进行查找。
  • area_threshold面积阈值,如果色块被框起来的面积小于这个值,会被过滤掉。
  • pixels_threshold像素阈值,与面积阈值同理。
  • merge合并所有的blob。
  • margin边界,如果设为1,那么两个blobs如果距离一个像素点,也会被合并。

blob色块对象

  • blob.rect()返回色块的外框--矩形元组(x,y,w,h)
  • blob.x()返回色块外框的x坐标
  • blob.y()
  • blob.w()
  • blob.h()
  • blob.pixels()
  • blob.cx()返回外框的中心x坐标
  • blob.cy()
  • blob.rotation()返回色块的旋转角度,弧度制。
  • blob.code()可用于查找颜色代码。返回16位数字,每一位代表一个颜色。如果是合并色块,则多个位为1.
  • blob.count()返回被合并的色块的数量。没有合并则返回1.
  • blob.area()返回w*h
  • blob.density()色块密度

七.AprilTag标记跟踪

AprilTag是一个视觉基准系统,可用于AR,机器人,相机校准。Tag可以用打印机打印出来。AprilTag检测程序可以计算标签图片相对于相机的3D位置,方向以及ID。TAG36H11是AprilTag的一种。使用TAG36H11比较好,因为方块数量比较多,看得距离要远,信息更准确,出错率更少。Tag可以在openmvIDE生成。

img.find_apriltags()

八.模板匹配

由于模板图片的大小超过openmv内置的flash,需要SD卡,插到openmv上面。可支持32GB内存卡。模板匹配采用灰度图。只能识别和模板图片类似大小的范围。如果需要识别不同的大小,那就要存储多个不同大小的模板。所以,模板匹配适用于摄像头与目标物体之间距离确定,不需要动态移动的情况。

首先,我们需要创建或者导入一个模板,可以直接从openmv里面截取模板图片。可以先运行helloword.py例程,让frambuffer显示出图像,鼠标右击点击save image selection to pc即可保存。但是格式不对,需要转换成PGM格式。使用的模板图片要求是PGM格式。模板图片建议小于80*60.如果出现:MemoryError.FB Alloc Collision(内存不够),要把QQVGA改成QQCIF。

BMP轉PGM轉換器。在线自由 — Convertio

想要识别多个模板图片则多次调用多个模板识别函数,或者for t in templates。

模板匹配使用NCC算法【计算机视觉】NCC匹配算法_荔枝酱的博客-CSDN博客

find_template(template,thresholds,step,search,roi)不设置roi的话,默认整个图像范围。仅支持灰度图像。

  • thresholds是0~1的浮点数。较小的值可以在提高检测速率的同时增加误报率。
  • step是查找模板时需要跳过的像素数量。跳过像素可大大提高算法运行的速度。这种方法只适用于SEARCH_EX模式下的算法。
  • saerch常常是取SAERCH_EX,表示寻找的方法。其他方法还有:image.SEARCH_DS。image.SEARCH_DS算法较快,但是当模板位于图像边缘的时候,可能无法搜索成功。
  • roi的大小要比模板图片大,比frambuffer小。

九.特征点检测

如果是刚开始运行程序,例程提取最开始的图像作为目标物体特征,kpts1保存目标物体的特征。默认会匹配目标特征的多种比例大小和角度,而不仅仅是保存目标特征时的大小角度,比模版匹配灵活,也不需要像多模板匹配一样保存多个模板图像。也可以尝试提前保存目标特征。

特征点检测使用FAST/AGAST算法进行特征提取和目标追踪,仅支持灰度图。在程序最开始的十秒左右,将目标物体放在摄像头中央识别,直至出现特征角点,证明已经识别记录目标特征。匹配过程中,如果画面出现十字和矩形框,说明匹配成功。

image — 机器视觉 — MicroPython 1.9.2 文档 (singtown)


        image.find_keypoints(roi=Auto, threshold=20, normalized=False, scale_factor=1.5, max_keypoints=100, corner_detector=CORNER_AGAST)

  •       threshold是0~255的一个阈值,用来控制特征点检测的角点数量。用默认的AGAST特征点检测,这个阈值大概是20。用FAST特征点检测,这个阈值大概是60~80。阈值越低,获得的角点越多。
  •        normalized是一个布尔数值,默认是False,可以匹配目标特征的多种大小(比ncc模版匹配效果灵活)。如果设置为True,关闭特征点检测的多比例结果,仅匹配目标特征的一种大小(类似于模版匹配),但是运算速度会更快一些。
  •       scale_factor是一个大于1.0的浮点数。这个数值越高,检测速度越快,但是匹配准确率会下降。一般在1.35~1.5左右最佳。
  •      max_keypoints是一个物体可提取的特征点的最大数量。如果一个物体的特征点太多导致RAM内存爆掉,减小这个数值。
  •     corner_detector是特征点检测采取的算法,默认是AGAST算法。FAST算法会更快但是准确率会下降。

image.match_descriptor(descritor0, descriptor1, threshold=70, filter_outliers=False)

本函数返回kptmatch对象。

  • threshold阈值设置匹配的准确度,用来过滤掉有歧义的匹配。这个值越小,准确度越高。阈值范围0~100,默认70
  • filter_outliers默认关闭。

pyb各种外设

常用函数: 

  • pyb.delay(50)延时50ms
  • pyb.millis()获取从启动开始计时的毫秒数

from pyb import LED

  • led.toggle()
  • led.on()        led.off()   
  • LED(1)红色   LED(2)绿色    LED(3)蓝色   LED(4)红外LED两个      

from pyb import Pin

  • p_in=Pin('P7',Pin.in,Pin.OUT_PP)       
  •  p_out.heigh()      p_outlow()     
  •  value= p_in.value()

from pyb import Servo

  • s1=Servo(1)
  • Servo(1)->P7(PD12)
  • s1.angle(angle,time)       move to xx degrees in xx time
  • s1.speed(speed)

from pyb import Pin,ExtInt

  • callback=lambda e:print("intr") 中断触发时调用的函数。
  • ext=ExtInt(Pin('P7'),ExtInt.IRQ_RISING,Pin.PULL_NONE,callback)

from pyb import Timer

  • tim=Timer(4,freq=1) 使用定时器4,1Hz触发
  • tim.counter() 获取或设置定时器
  • tim.freq()  获取或设置定时器频率
  • tim.callback() 设置定时器触发时调用的函数
  • Timer 1 Channel 3 Negative->P0

from pyb import Pin, Timer

  • p = Pin('P7')    P7 has TIM4, CH1
  • tim = Timer(4, freq=1000)
  • ch = tim.channel(1, Timer.PWM, pin=p)
  • ch.pulse_width_percent(50)
     

from pyb import Pin, ADC

  • adc = ADC('P6')
  • adc.read()    read value, 0-4095
     

from pyb import Pin, DAC

  • dac = DAC('P6')
  • dac.write(120)    output between 0 and 255
     

from pyb import UART

  • uart = UART(3, 9600)
  • uart.write('hello')
  • uart.read(5) # read up to 5 bytes
     

from pyb import SPI

  • spi = SPI(2, SPI.MASTER, baudrate=200000, polarity=1, phase=0)
  • spi.send('hello')
  • spi.recv(5)    receive 5 bytes on the bus
  • spi.send_recv('hello')    send a receive 5 bytes
     

from machine import I2C, Pin

  • i2c = I2C(sda=Pin('P5'),scl=Pin('P4'))
  • i2c.scan()
  • i2c.writeto(0x42, b'123')       write 3 bytes to slave with 7-bit address 42
  • i2c.readfrom(0x42, 4)           read 4 bytes from slave with 7-bit address 42
  • i2c.readfrom_mem(0x42, 8, 3)      read 3 bytes from memory of slave 42, starting at memory-address 8 in the slave
  • i2c.writeto_mem(0x42, 2, b'\x10')   write 1 byte to memory of slave 42    starting at address 2 in the slave

openmv常使用的功能:颜色追踪,模板匹配,特征点匹配,二维码条形码识别

常应用的场景:十字路口识别,数字识别,红线追踪,二维码条码识别

1.关于颜色追踪 find_blobs

颜色追踪,先要完成颜色识别。

single_color_rgb565_blob_tracking例程,单颜色识别

multi_color_rgb565_blob_tracking例程,多颜色识别

颜色识别,首当其冲的就是颜色阈值。颜色阈值在工具--阈值工具里通过LAB模式将需要追踪的颜色的部分调整为白色,复制颜色LAB数字即为颜色阈值。也可以在直方图中,直接选择LAB色域,在显示区直接选出需要的颜色区域,然后看LAB的最大值和最小值。

其次就是区域阈值,像素阈值,合并。阈值表示低于阈值的面积大小或者像素大小会被过滤,按照实际情况定下区域阈值和像素阈值的大小即可。合并为true时,多个颜色框框会合并为一个框框。

识别颜色之后,还要进行追踪,那么我们在硬件上必然需要运动和驱动的部件,比如电机。此时,需要颜色识别的结果来确定电机的运动方向。

需要电机,那么必然需要PID算法。PID的各参数在线调试和脱机运行是有差别的。因为使用了IDE的显示图像的帧率和不使用IDE的帧率会有差别。在线调试的参数大小会大于脱机运行的参数。除此之外,PID需要有一个error值,这个值与颜色方块在x/y方向的质心位置与整个图像的中心位置的偏差有关。这个error值代表了方块在整个图像中中心的偏差位置,进入PID函数中获得两个电机在x/y方向的旋转角度。

openmv提供的库代码,颜色追踪的程序是否有哪些局限?

在灯光变化时,框框的区域会发生变化。因为颜色本身就会受光照影响而发生变化。由于使用IDE会导致PID参数调了之后,在脱机时会有差别。如果不使用IDE查看图像,可以选择使用LCD。是需要导入LCD模块,初始化LCD,然后lcd.display(img)。注意需要使用和LCD一样的分辨率

2.关于模板匹配NCC  find_template

外接内存卡,最大32GB。只支持灰度图模式。

创建模板图片文件,匹配阈值,寻找方法,step。

模板图片可以直接在显示区截取。将bmp图像保存在pc里。bmp格式转为pgm格式后保存到openmv的u盘或者内存卡中。多个模板匹配,使用for循环。

模板匹配NCC的局限?

NCC算法只能匹配近似大小,如果需要识别不同的大小或者角度,那么就需要保存多个大小和角度的模板图片。

3.关于小车的速度与方向控制

小车看成两轮,使用差速法来控制方向。差速法,那必然会有一个基础速度,再加上方向速度。基础速度就是小车的速度,使用追踪物体的大小来判断,原理是近大远小。左右轮的基础速度是一样的,同样需要PID来计算,使得基础速度的变化较为稳定。差速的实现是为了变化方向,方向的变化同样也需要是稳定平滑的,所以也需要PID来计算。此时,是根据物体质心位置与视野中心位置的偏移为error值进入PID。得出的结果为差速,左右两轮一正一负与基础速度相加。

做一个巡线小车:

巡线小车需要实现巡线功能,那就必然需要小车速度和方向控制,路线识别。当巡线速度没有特别要求时,那么基础速度可以是一直不变的。方向由差速来实现。需要根据路线与视野的中心位置的偏移为error值进入PID得到差速值。左右轮是一正一负与基础速度相加。

由路线来得到偏移值,使用get_regression函数。将直线信息返回,由rho值和theta值获得小车的转弯角度。其实,主要的思路就是获得直线中心与视野中心位置的偏移大小。

4.关于扫码识别

只有openmv2以上的版本才可以扫码。扫码识别使用灰度图模式。openmv的二维码识别采用的是Apriltag的四元检测算法。条形码识别是不需要考虑畸变,是一维信息。对于二维码识别和矩形识别需要考虑镜头畸变问题。镜头畸变问题如果使用软件算法来解决,那会导致帧率降低。使用无畸变的镜头可以有效避免畸变问题和帧率下降问题。

find_barcodes()条形码识别
find_qrcodes()二维码识别

在线二维码生成器 ~ 二维工坊 (2weima)    二维码生产器网址

5.关于特征点检测  find_keypoints,match_descriptor

特征点检测经常用于目标追踪,适用于对象的大小和角度不断变化的情况。在检测开始前保存物体的特征。使用灰度图模式。

1.事先保存特征点,然后进行特征点匹配:save_keypoint在开始时就会保存特征点,之后需要重新连接然后重置openmv,此时u盘上可以看到保存的特征图片。然后使用特征点匹配的例程,需要将key1改为在保存的文件里寻找特征点。

2.程序开始运行时,保存特征点之后直接匹配:直接使用特征点匹配例程,key1是none。

6.SD卡的使用

外接SD卡可拓展存储空间。在断电后,插入SD卡。内存卡会替代原来的flash。将文件保存到SD卡中,重置openmv。如果SD卡不兼容,则格式化为FAT32。

7.数字识别LENAT,minst

LENAT卷积识别网络,可以识别打印或者手写的数字。之前的模板匹配也可以识别数字。由于模板匹配的局限性,lenat数字识别预先保存lenat神经网络的模型文件到flash里面,运行例程就可以实现数字识别,大小或者角度变化可识别。openmv只能识别较大的数字。lenat识别在openmv4上使用。

目标检测:

训练识别物体。适用于openmv4h7/(plus)。在edge impulse网站上训练模型。FOMO模型只能检测到目标的位置,无法获知的物体的尺寸。与YOVLO相比,FOMO的效果更好。

采集并上传数据集;标注目标;在线网站模型训练;模型测试;模型文件放到flash中

在采集过程中,每一个分类至少100张图片。工具-数据集编辑器-新数据集-新建文件夹-保存分类图片(连接openmv-点击左侧保存数据集照片,保存多个角度的照片)。将数据集上传到网站上。数据集编辑器-export-upload to edge impulse by API Key-复制API Key-上传中。

8.脱机运行

将在打开的文件的界面,点击工具-将打开的文件保存到openmv,保存之后将U盘弹出。重新上电之后可以看到保存的代码。在U盘里面就可以找到保存的文件。

如果没有正确地保存代码,可以格式化,然后重新保存。右键u盘,点击格式化,文件系统选择FAT,点击确定。关闭IDE。

openmv与STM32的通信

下面是openmv与STM32使用uart协议的通讯。

在数据发送和接收之前,我们需要设置openmv和STM32的uart协议:8位数据位,1位停止位,无校验位,波特率设置得低一点,为9600.

Openmv数据发送:

在openmvIDE的示例里面,提供了uart的例程,较为简单,使用uart.write来发送数据。自己调试的时候,发现uart.write只能发送字符串,无法以十六进制的形式发送数据。

openmv与STM32的uart通信有两种方式。

  1. bytearray( [ 0x2C,0x12,data,ox5B] ) ,使用bytearray函数。它是python的内置函数,返回一个新字节数组,数组元素可变,每个元素的值范围是0~255.然后uart.write( bytearray( ) )发送出去。【需要import math】如果需要发送两个字节的数据,那么把这个数据分为高8位和低8位两段来发送出去:bytearray( [ ox2c , ox2c , A>>8 , A , ox5b ] )
  2. ustruct.pack()。根据格式字符串fmt,将数据打包,返回一个解码该值的字节对象。然后uart.write( ustruct.pack() )发送出去。数据包一般会有帧头和帧尾,主要是为了保证数据传输的准确性。fmt格式有规定。【需要import ustruct】

#pack各字母对应类型                              字节大小
#x   pad byte        no value                          1
#c   char            string of length 1                1
#b   signed char     integer                           1
#B   unsigned char   integer                         1
#?   _Bool           bool                                    1
#h   short           integer                                 2
#H   unsigned short  integer                          2
#i   int             integer                                    4
#I   unsigned int    integer or long                  4
#l   long            integer                                  4
#L   unsigned long   long                               4
#q   long long       long                                  8
#Q   unsilong long   long                              8
#f   float           float                                      4
#d   double          float                                  8
#s   char[]          string                                  1
#p   char[]          string                                  1
#P   void *          long

调试方法:

在使用以上两种方式写程序的时候,openmv使用USB转串口,打开电脑的串口助手并连接后,串口助手上看不到uart.write的内容。只有使用print()才能显示内容。所以,如果想要看到uart传输的结果,那么就连接到单片机上,中断函数读取到数据之后将数据发送到串口助手上显示。

STM32数据接收:

在openmvIDE中写完发送函数之后,主控单片机上需要接收数据。需要特别注意中断函数。数据接收与数据的发送格式紧密相关。

数据的发送格式是8位数据位,意味着数据是1个字节1个字节地发送过来的,每次中断只能接收到8位数据位,即1个字节。openmv发送的数据包里有帧头和帧尾。在中断函数中需要一一识别帧头帧尾,判断这个数据是否是正确的,获得我们需要的数据。如果data是2个字节的,那么data的传输需要两次中断完成,我们需要设计相应的程序正确接收到数据。

数据转换和处理

在发送和接收,以及数据接收后的处理过程中,需要注意数据的内存大小,数据的类型。防止出现截断,溢出等改变数据的情况。

如果openmv需要发送较多的数据给其他单片机,可以借鉴一下下面这篇文章:

OpenMV与STM32单片机串口通信,如何使用openmv连续发送多帧数据给单片机_openmv串口进制转换_小柱er的博客-CSDN博客https://blog.csdn/qq_41037104/article/details/89485500?ops_request_misc=&request_id=&biz_id=102&utm_term=openmv%E4%B8%8Estm32%E9%80%9A%E4%BF%A1&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduweb~default-5-89485500.nonecase&spm=1018.2226.3001.4187

本文标签: openmv