OpenMV零基础教程

一、资料导航

        “工欲善其事,必先利其器”。在正式学习OpenMV之前,你必须知道一条或几条OpenMV的学习途径。这里推荐星瞳科技的中文官网教程,这个教程里面包括了OpenMV IDE的下载和安装、OpenMV上手教程、OpenMV中文文档、OpenMV详细参数以及OpenMV函数库等,是新手入门OpenMV的一条非常好的途径。

OpenMV零基础教程_第1张图片

                                                   1 OpenMV星瞳科技官网

二、OpenMV简介

         OpenMV是一个开源,低成本,功能强大的机器视觉模块。以STM32F427CPU为核心,集成了OV7725摄像头芯片,在小巧的硬件模块上,用C语言高效地实现了核心机器视觉算法,提供Python编程接口。使用者们可以用python语言使用OpenMV提供的机器视觉功能,为自己的产品和发明增加有特色的竞争力。OpenMV上的机器视觉算法包括寻找色块、人脸检测、眼球跟踪、边缘检测、标志跟踪等。可以用来实现非法入侵检测、产品的残次品筛选、跟踪固定的标记物等。使用者仅需要写一些简单的Python代码,即可轻松的完成各种机器视觉相关的任务。小巧的设计,使得OpenMV可以用到很多创意的产品上。比如,可以给自己的机器人提供周边环境感知能力;给智能车增加视觉巡线功能;给智能玩具增加识别人脸功能,提高产品趣味性等;甚至,可以给工厂产品线增加残次品筛选功能等。

       OpenMV采用的STM32F427拥有丰富的硬件资源,引出UART,I2C,SPI,PWM,ADC,DAC以及GPIO等接口方便扩展外围功能。USB接口用于连接电脑上的集成开发环境OpenMV IDE,协助完成编程、调试和更新固件等工作。TF卡槽支持大容量的TF卡,可以用于存放程序和保存照片等。

OpenMV零基础教程_第2张图片

 2 OpenMV实物

 三、OpenMV IDE的使用

3.1、IDE功能一览

OpenMV零基础教程_第3张图片

                                                        3 OpenMV IDE界面

  1. 代码编辑区 编辑对应的OpenMV的python代码。
  2. 视频缓冲区 实时显示OpenMV上的图像。
  3. 交互式终端 可以与OpenMV交互编程,可将帧率、串口传输内容等打印在该终端上。
  4. 直方图 可通过直方图来获取图像的阈值。
  5. 快捷功能区 包含保存、复制、粘贴、剪切等快捷功能。
  6. 设备连接与运行 用于连接OpenMV与运行代码。

3.2、OpenMV连接及运行代码

     先把板子通过USB连接到电脑,等之出现flash闪存盘,然后按下白色连接图标,连接成功会出现绿色图标,点击绿色按钮之后变成红色的X,即可把当前的python代码在OpenMV中运行;若要中断代码或要运行其他代码,点击X再点击绿色按钮即可重新运行。

OpenMV零基础教程_第4张图片

                                                    4 OpenMV 连接与运行流程

3.3、打开官方例程

      OpenMV IDE里有很多官方提供的例程,这里以helloworld例程为例教大家如何去打开这些例程。文件——示例——OpenMV——Basics——helloworld打开官方提供的helloworld例程。

OpenMV零基础教程_第5张图片

图 5 打开OpenMV 官方例程

 点击运行之后,IDE右上角就有图像显示,以及右下角的RGB数值显示。并在终端上打印数据。

3.4、菜单栏功能

文件部分:新建文件、打开文件、保存文件等操作功能。

OpenMV零基础教程_第6张图片

                                              6 OpenMV IDE菜单栏文件部分

编辑部分:此部分是对编辑区进行设置,比如常用的撤销、复制、粘贴、剪切、重做全选查找等功能。也有相对应的快捷键,也比较方便。

OpenMV零基础教程_第7张图片

 7 OpenMV IDE菜单栏编辑部分

 工具部分:此部分有保存脚本到OpenMV、复位OpenMV、还有串口终端的设置、机器视觉设置读取图像LAB阈值等功能。

OpenMV零基础教程_第8张图片

                                                  8 OpenMV IDE菜单栏工具部分

窗口部分:可对软件进行全屏。

OpenMV零基础教程_第9张图片

 9 OpenMV IDE菜单栏窗口部分

四、OpenMV理论基础

摄像头

我们都见过各种摄像头,比如:

OpenMV零基础教程_第10张图片

                                                        10 常见摄像头

那么什么是摄像头,说到底,就是一个将光学信号转变成电信号的一个装置。在计算机视觉中,最简单的相机模型是小孔成像模型:

OpenMV零基础教程_第11张图片

                                                      11 小孔成像模型

小孔模型是一种理想相机模型,没有考虑实际相机中存在的场曲、畸变等问题。但是在实际使用时,这些问题可以通过在标定的过程中引入畸变参数解决,所以小孔模型仍然是目前最广泛使用的相机模型。图像透过镜头,照在一个感光芯片上,感光芯片可以把光照的波长和强度等信息转成计算机(数字电路)可以识别的数字信号,OpenMV感光元件是长这样的:

OpenMV零基础教程_第12张图片

                                                      12 OpenMV感光元件

(中间的方形元件就是感光元件)

像素和分辨率

图像由很多个小点构成,这些小点我们称之为像素,也叫像素点。相同物理面积下,像素点越多,显示的图像就越清晰,像素点越少,显示的图像就越模糊。例如,一张人像照片,如果有2000万个像素点,可能连毛孔都看得很清晰,如果只有100万个像素点可能鼻子眼睛都会模糊,这就是用像素的多少来描述图片的清晰程度。我们通常说的1000万像素、2000万像素、一亿像素指的就是图像的像素点总数。

OpenMV零基础教程_第13张图片

 13 由像素构成的人眼图

明白了像素的意义,分辨率就很好理解。一张图像里像素的排列并非杂乱无序,而是横向成行、竖向成列的规则排列。分辨率一般描述为M x N,M代表水平方向即横向像素数,N代表垂直方面即竖向像素数,两者相乘即为此图像的像素总数。例如,一张640乘480分辨率的图片,即代表横向有640个像素点,竖向有480个像素点,这张图的像素总数为640 x 480 = 307200。分辨率其实还是在描述一张图片的像素总数,只不过是换了一种表述方法而已。           

帧率

帧率(FPS)就是每秒钟处理的图片数量,如果超过20帧,人眼就基本分辨不出卡顿。当然,如果用在机器上,帧率是越高越好的。

LAB亮度-对比度

Lab颜色空间中,L亮度;a的正端代表红色,负端代表绿色;b的正端代表黄色,负端代表兰色。不像RGB和CMYK色彩空间,Lab颜色被设计来接近人类视觉。因此L分量可以调整亮度对,修改a和b分量的输出色阶来做精确的颜色平衡。而在OpenMV的查找色块的算法中,运用的就是这个LAB模式。

镜头的焦距

因为图像是通过镜头的光学折射,照到感光元件上的。那么镜头就决定了整个画面的大小和远近。一个最重要的参数就是焦距。镜头焦距是指镜头光学后主点到焦点的距离,是镜头的重要性能指标。镜头焦距的长短决定着拍摄的成像大小,视场角大小,景深大小和画面的透视强弱。当对同一距离远的同一个被摄目标拍摄时,镜头焦距长的所成的象大,镜头焦距短的所成的像小。下面是,当OpenMV距离桌面20cm左右时,不同焦距镜头的对比图:

OpenMV零基础教程_第14张图片

                                                             14 镜头的焦距

镜头调焦

有时当你的OpenMV连接到电脑上会发现在OpenMV帧缓冲区里的图像比较模糊,这时你需要去手动调节OpenMV摄像头的焦距,使图像变的更加清晰。

镜头的畸变

因为光学原理,在感光芯片上不同的位置,与镜头的距离不同的,简单说就是近大远小,所以在边缘会出现鱼眼效果(桶型畸变)。为了解决这个问题,可以在代码中使用算法来矫正畸变,注:OpenMV中使用image.lens_corr(1.8)来矫正2.8mm焦距的镜头。也可以直接使用无畸变镜头。

光源的选择

如果你的机器是在工业上,或者24小时长时间运行的设备,保持一个稳定的光源是至关重要的,尤其在颜色算法中。亮度一变,整个颜色的值会变化的很大!

SD卡

如果代码太多,Flash装不下,就可以使用SD卡,这个是OpenMV自带的,SD卡也是一个文件系统,当上电的时候,如果插入SD卡,那么SD卡的文件系统就会自动取代内置的Flash文件系统,每次上电,就会运行SD卡中的main.py。SD卡最大支持32G的容量。

脱机运行

OpenMV把内置Flash虚拟成一个文件系统,当你插入OpenMV到电脑上的时候,电脑会弹出一个U盘,里面就是OpenMV的文件系统。当你想烧录固件的时候,直接把脚本文件复制到这个“U盘”的main.py中。每次上电的时候,OpenMV会自动运行里面的main.py,这样就实现了脱机运行。

一键下载

在工具栏里,点击将打开的脚本保存到OpenMV Cam(作为main.py), IDE就会自动将当前文件保存到main.py,很方便。       

                             OpenMV零基础教程_第15张图片               

                                                    15 OpenMV一键下载流程                

电源

OpenMV有两个电源输入端:VIN有时也会标识为VCC)和USB输入。VIN输入为3.6V~5V,推荐5V。USB和VIN可以同时供电。OpenMV有一个电源输出端:3.3V,这个电压是OpenMV的稳压器输出的,用于给其他传感器供电。注意:不要给3.3V直接供电,没有内部芯片的保护,很容易烧毁。

五、案例讲解

5.1、小球追踪

1、第一步:配置感光元件并完成图像的拍摄

简介

在这个案例中,我们需要在黄、红、蓝三个小球中识别蓝色小球,并将蓝色小球的位置坐标通过串口发送给STM32单片机

感光元件

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

初始化

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

设置彩色/黑白

sensor.set_pixformat() 设置像素模式。

     sensor.GRAYSCALE: 灰度,每个像素8bit。

     sensor.RGB565: 彩色,每个像素16bit。

设置图像的大小

sensor.set_framesize() 设置图像的大小

     sensor.QQVGA: 160x120

     sensor.QVGA: 320x240

     sensor.VGA: 640x480

跳过一些帧

sensor.skip_frames(n=20) 跳过n张照片,在更改设置后,跳过一些帧,等待感光元件变稳定。

获取一张图像

sensor.set_auto_gain() 自动增益开启(True)或者关闭(False)。在使用颜色追踪时,需要关闭自动增益默认打开

自动增益/白平衡

sensor.set_auto_whitebal() 自动白平衡开启(True)或者关闭(False)。在使用颜色追踪时,需要关闭自动白平衡默认打开

                                                          第一步 程序

/********************************************************************
说明:完成此部分程序并在OpenMV IDE中运行,帧缓冲区中即可出现图像。
*********************************************************************/
import sensor, image, time                   # 导入传感器模块、图像模块、时间模块
/********************************************************************
感光元件配置
*********************************************************************/
sensor.reset()                        	     # 初始化感光元件
sensor.set_pixformat(sensor.RGB565)          # 设置像素模式为RGB565格式
sensor.set_framesize(sensor.QVGA)     	     # 设置图像的大小为QVGA (320x240)
sensor.skip_frames(20)                       # 跳过一些帧,等待感光元件变得稳定
sensor.set_auto_gain(False)                  # 关闭自动增益
sensor.set_auto_whitebal(False)              # 关闭白平衡
clock = time.clock()       	                 # 创建一个时钟对象以便去追踪帧率
/********************************************************************
主函数
*********************************************************************/
while(True):                             	# 无限循环
    clock.tick()                            # 更新图像的帧率
    img = sensor.snapshot()            	    # 拍一张照片并返回图像
    print(clock.fps())                      # 打印帧率
/********************************************************************
结束
*********************************************************************/

 2、第二步:识别蓝色小球并将中心坐标打印在串行终端  

寻找色块函数——find_blobs函数

寻找色块是OpenMV用的最多的功能,find_blobs函数可以找到色块.以下是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])

     a、thresholds是颜色的阈值注意:这个参数是一个列表,可以包含多个颜色。如果你只需要一个颜色,那么在这个列表中只需要有一个颜色值,如果你想要多个颜色阈值,那这个列表就需要多个颜色阈值。在返回的色块对象blob可以调用code方法,来判断是什么颜色的色块。

     b、roi是“感兴趣区”

     c、x_stride 是查找的色块的x方向上最小宽度的像素,默认为2,如果你想查找宽度10个像素以上的色块,那么就设置这个参数为10

     d、y_stride 是查找的色块的y方向上最小宽度的像素,默认为1,如果你想查找宽度5个像素以上的色块,那么就设置这个参数为5

     e、invert 反转阈值,把阈值以外的颜色作为阈值进行查找

     f、area_threshold 面积阈值,如果色块被框起来的面积小于这个值,会被过滤掉

     g、pixels_threshold 像素个数阈值,如果色块像素数量小于这个值,会被过滤掉

     h、merge 合并,如果设置为True,那么合并所有重叠的blob为一个。 注意:这会合并所有的blob,无论是什么颜色的。如果你想混淆多种颜色的blob,只需要分别调用不同颜色阈值的find_blobs。

     i、margin 边界,如果设置为1,那么两个blobs如果间距1一个像素点,也会被合并。

手动调节阈值

一个颜色阈值的结构是这样的:

                                threshold = (minL,maxL,minA,maxA,minB,maxB)

元组里面的数值分别是L A B 的最大值和最小值。如果需要获取自己需要的色块的阈值,可以使用如下操作:

  1. 首先运行hello world.py让IDE里的右上角帧缓冲区显示图案。
  2. 在IDE中的菜单栏-工具中找到机器视觉->阈值编辑器中可以选取需要的阈值
  3. 手动调整到一个合适的值

OpenMV零基础教程_第16张图片

                                                    图 16 手动调节阈值    

blobs是一个列表

find_blobs对象返回的是多个blob的列表。(注意区分blobs和blob,这只是一个名字,用来区分多个色块,和一个色块)。 列表类似与C语言的数组,一个blobs列表里包含很多blob对象,blobs对象就是色块,每个blobs对象包含一个色块的信息。blobs就是很多色块。可以用for循环把所有的色块找一遍。

blob色块对象

blob有多个方法这里介绍三个常用的方法:

(1)、blob.rect() 返回这个色块的外框——矩形元组(x, y, w, h),可以直接在image.draw_rectangle()中使用。

(2)、blob.cx() 返回色块的外框的中心x坐标(int),也可以通过blob[5]来获取

(3)、blob.cy() 返回色块的外框的中心y坐标(int),也可以通过blob[6]来获取。

标识函数

画框

image.draw_rectangle(rect_tuple, color=White) 在图像中画一个矩形框。rect_tuple 的格式是 (x, y, w, h)。

画圆

image.draw_circle(x, y, radius, color=White) 在图像中画一个圆。x,y是圆心坐标radius是圆的半径。

画十字

image.draw_cross(x, y, size=5, color=White) 在图像中画一个十字x,y是坐标size是两侧的尺寸

                                                             【第二步 程序

/********************************************************************
说明: 完成此部分程序可以识别视野中的蓝色小球,并在OpenMV IDE帧缓冲区
中用矩形框和十字将蓝色小球标出。
*********************************************************************/
import sensor, image, time                      # 导入传感器模块、图像模块、时间模块
blue_threshold   = (0,89,-52,-17,-15,38)        # 设置蓝色的阈值
/********************************************************************
感光元件配置
*********************************************************************/
sensor.reset()                                  # 初始化感光原件
sensor.set_pixformat(sensor.RGB565)             # 设置图像色彩格式
sensor.set_framesize(sensor.QVGA)           	# 设置图像的大小为QVGA (320x240)
sensor.skip_frames(20)                          # 跳过一些帧,等待感光元件变得稳定
sensor.set_auto_gain(False)           	        # 关闭自动增益
sensor.set_auto_whitebal(False)                 # 关闭白平衡
clock = time.clock()                      	    # 创建一个时钟对象以便去追踪帧率
/********************************************************************
寻找最大色块函数
*********************************************************************/
def find_max(blobs):                            # 定义函数
    max_size=0                                  # 定义色块尺寸变量
    for blob in blobs:                          # 遍历色块blobs
        if blob[2]*blob[3] > max_size:          # 比较色块的大小
            max_blob=blob                       # 保留较大的色块
            max_size = blob[2]*blob[3]          # 保留较大的色块的尺寸
    return max_blob                             # 返回最大色块
/********************************************************************
主函数
*********************************************************************/
while(True):                                    # 无限循环
    clock.tick()                                # 更新图像的帧率
    img = sensor.snapshot()                     # 拍摄一张照片并返回图像。
blobs = img.find_blobs([blue_threshold])        # 调用find_blobs()函数
blob = find_max(blobs)                          # 调用 find_max()函数
    if blob:                                    # 如果找到了目标颜色      
	img.draw_rectangle(blob[0:4])               # 用矩形标记出目标颜色区域
    	img.draw_cross(blob[5], b[6])           # 在目标颜色区域的中心画十字形标记
    	print(blob[5], blob[6])            	    # 输出颜色目标中心坐标
    	print(clock.fps())                      # 打印帧率
/********************************************************************
结束
*********************************************************************/

3、第三步:将蓝色小球的中心通过串口发送给STM32单片机

创建串口对象

uart = UART(3, 115200),使用串口三,波特率115200。

串口初始化

uart.init(115200, bits=8, parity=None, stop=1),8位数据位,无校验位,1位停止位

字节数组

data = bytearray([0xb3,0xb3,OpenMV_X,OpenMV_Y,0x5b])。bytearray()方法返回一个新字节数组。这个数组里的元素是可变的,每个元素的值范围: 0 <= x < 256。

串口发送

函数uart.write(data)

接线

TTL串口至少需要3根线:TXD,RXD,GND。TXD是发送端,RXD是接收端,GND是地线。 连线的时候,需要把OpenMV的RXD连到另一个MCU的TXD,TXD连到RXD。

                                                            【第三步 程序

/********************************************************************
说明: 完成此部分程序可以识别视野中的蓝色小球,并在OpenMV IDE帧缓冲区中用
矩形框和十字将蓝色小球标出,同时将蓝色小球的中心坐标通过串口发送给STNM32单片机。
*********************************************************************/
import sensor, image, time                        # 导入传感器模块、图像模块、时间模块
from pyb import UART                              # 导入串口模块
blue_threshold   = (0,89,-52,-17,-15,38)          # 设置蓝色的阈值
/********************************************************************
感光元件配置
*********************************************************************/
sensor.reset()                                    # 初始化感光原件
sensor.set_pixformat(sensor.RGB565)               # 设置图像色彩格式
sensor.set_framesize(sensor.QVGA)          	      # 设置图像的大小为QVGA (320x240)
sensor.skip_frames(20)                            # 跳过一些帧,等待感光元件变得稳定
sensor.set_auto_gain(False)                       # 关闭自动增益
sensor.set_auto_whitebal(False)                   # 关闭白平衡
clock = time.clock()                              # 创建一个时钟对象以便去追踪帧率 
/********************************************************************
创建串口对象并初始化
*********************************************************************/
uart = UART(3, 115200)                            # 创建串口对象
uart.init(115200, bits=8, parity=None, stop=1)    # 串口初始化
/********************************************************************
寻找最大色块函数
*********************************************************************/
def find_max(blobs):                              # 定义函数
    max_size=0                                    # 定义色块尺寸变量
    for blob in blobs:                            # 遍历色块blobs
        if blob[2]*blob[3] > max_size:            # 判断色块的大小
            max_blob=blob                         # 保留较大的色块
            max_size = blob[2]*blob[3]            # 保留较大的色块的尺寸
return max_blob                                   # 返回最大色块



/*********************************************************************
主函数
*********************************************************************/
while(True):                                       # 无限循环
    clock.tick()                                   # 更新图像的帧率
    img = sensor.snapshot()                    	   # 拍一张照片并返回图像。
blobs = img.find_blobs([blue_threshold])           # 调用find_blobs函数
blob = find_max(blobs)                             # 调用 find_max函数
    if blob:                              	       # 如果找到了目标颜色      
	img.draw_rectangle(blob[0:4])        	       # 用矩形标记出目标颜色区域
    	img.draw_cross(blob[5], b[6])          	   # 在目标颜色区域的中心画十字形标记
      OpenMV_X = int(blob[5])                 	   # 对色块横坐标取整
          OpenMV_Y = int(blob[6])	               # 对色块纵坐标取整
          data = bytearray([0xb3,0xb3,OpenMV_X ,   # 返回字节数组
OpenMV_Y,0x5b])              
          uart.write(data)                         # 调用uart.write()函数发送数据
    	print(blob[5], blob[6])               	   # 打印目标物体中心坐标
    	print(clock.fps())                    	   # 打印帧率
/********************************************************************
结束
*********************************************************************/

5.2、植保飞行器(21电赛G题)视觉设计

1、任务

设计一基于四旋翼飞行器的模拟植保飞行器,能够对指定田块完成“撒药”作业。如图 1 所示作业区中,灰色部分是非播撒区域,绿色部分是待 “播撒农药”的区域,分成多个 50cm×50cm 虚线格区块,用1~28 数字标识,以全覆盖

OpenMV零基础教程_第17张图片

飞行方式完成药”作业。

作业中播撒区域不得漏撒、重复播撒,非播撒区域不得播撒,否则将扣分;播撒作业完成时间越短越好。

图 1 中,黑底白字的“十”字是飞行器起降点标识;“21”是播撒作业起点区块,用“A”标识;飞行器用启闭可控、垂直向下安装的激光笔的闪烁光点表示播撒动作,光点在每个区块闪烁1~3 次视正常播撒;同一区块光点闪烁次数大于3次,将被认定为重复播撒。激光笔光点闪烁周期

2、基本要求

(1)飞行器在“十”字起降点垂直起飞,升空至 150 ± 10cm 巡航高度。

(2)寻找播撒作业起点,从“A”所在区块开始“撒药”作业。

(3)必须在 360 秒内完成对图 1 中所有绿色区块进行全覆盖播撒。

(4)作业完成后稳定准确降落在起降点;飞行器几何中心点与起降点中心距离的偏差不大于

± 10cm。

3、视觉设计说明

(1)识别“十”字起降点。

(2)识别播撒作业起点区标识“A”。

(3)识别两条黑线的交汇点辅助飞行器的路径设计。

(4)获取识别物体的坐标信息,并通过串口发送给飞行器进行处理。

说明:学习完前面的内容,掌握小球追踪例程,你已经初步入门OpenMV了,植保飞行器视觉设计对于初学者来说有一定的难度,因此这里仅做介绍。想要参考此部分代码及讲解请见OpenMV零基础教程

你可能感兴趣的:(OpenMV,python,stm32)