Openmv(一)OpenMV图像处理的基本方法


一、图像处理基础知识

  1. 摄像头:光学信号转换成电信号。计算机视觉中,最简单的模型是小孔成像模型。
    小孔成像是一种理想模型,实际镜头会存在场曲和畸变等,但可以通过在标定过程中引入畸变参数解决,仍可广泛使用。

  2. 像素和分辨率

  3. 帧率:每秒钟处理的图片数量,>20人眼分辨不出卡顿。

  4. 颜色:不同波长的电磁波
    通过RGB. CMYK HSB LAB色域来描述
    RGB:三原色原理不是物理原因而是人的生理原因造成(锥形感光细胞)
    LAB:L(亮度)、A(+:红色,-:绿色)、B(+:黄色,-:蓝色)用来接近人类视觉
    L用来调整亮度对,a和b用来输出色阶来做精确的颜色平衡(查找色块算法)
    HSB/HSV:H(Hue色相)、S(Saturation饱和度)、B(Brightness明度)
    HSL:L(亮度)
    CMYK:C(蓝色)、M(红色)、Y(黄色)、K(黑色)
    CMYK和RGB的区别:
    (1)CMYK是减色模式,而RGB是加色模式
    (2)CMYK需要外界光源才能看到,印刷品颜色;RGB是屏幕显示发光的色彩模式,在黑暗房间也能看到

  5. 光源的选择(光源对整个画面的颜色变化有很大影响)

  6. 镜头的焦距和镜头畸变
    焦距决定了拍摄的成像大小,视场角大小,景深大小和画面的透视强弱。焦距长成像大(视角越小),焦距短成像小
    镜头畸变:用代码矫正镜头或直接使用无畸变镜头(加入了额外的矫正透镜部分)
    在感光芯片上的不同位置,与镜头距离不同近大远小,在边缘出现鱼眼效果(桶形畸变)
    标配镜头——桶形畸变
    广角镜头——同样的区域取景范围大了
    无畸变镜头
    长焦镜头——锁定原来图像中的某一具体物体

  7. 镜头的滤片
    滤掉红外光(根据需要滤掉不同波长的光),红外光在正常分析颜色图片的时候会使图像泛白影响分析。(只能通过650nm以内的)

二、OpenMV图像处理的基本方法

1. 感光元件

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

sensor常用函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import sensor # 引入感光元件部分
# 设置摄像头
sensor.reset() # 初始化感光元件
sensor.set_pixformat(sensor.RGB565)# 设置像素模式 sensor.GRAYSCALE:灰度,每个像素8bit, sensor.RGB565:彩色。每个像素16bit
sensor.set_framesize(sensor.QVGA)# 设置图像大小
sensor.skip_frames(n)# n是跳过的帧数,等待感光元件稳定
# 拍摄
while(True):
   img = sensor.snapshot();# 拍摄一张照片,img为一个image对象

# 自动增益/白平衡/曝光
sensor.set_auto_gain() # True开启;False关闭,使用颜色追踪时,需关闭
sensor.set_auto_whitebal() # True开启;False关闭,使用颜色追踪时,需关闭
sensor.set_auto_exposure(enable[\,exposure_us])
# enable True打开/False关闭
# 如果关闭可以自己设置一个曝光值us为单位

# 设置窗口ROI
sensor.set_windowing(roi) #roi的格式是(x, y, w, h)的tupple.
sensor.set_framesize(sensor.VGA)
sensor.set_windowing((640,80))#选择出自己想要的元素

# 设置翻转
sensor.set_hmirror(True)# 水平方向翻转
sensor.set_vflip((True)# 垂直方向翻转
sensor图像大小常用数据
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
sensor.QQCIF: 88x72
sensor.QCIF: 176x144
sensor.CIF: 352x288
sensor.QQSIF: 88x60
sensor.QSIF: 176x120
sensor.SIF: 352x240
sensor.QQQQVGA: 40x30
sensor.QQQVGA: 80x60
sensor.QQVGA: 160x120
sensor.QVGA: 320x240
sensor.VGA: 640x480
sensor.HQQQVGA: 80x40
sensor.HQQVGA: 160x80
sensor.HQVGA: 240x160
sensor.B64X32: 64x32 #(用于帧差异 image.find_displacement())
sensor.B64X64: 64x64 #用于帧差异 image.find_displacement())
sensor.B128X64: 128x64 #(用于帧差异 image.find_displacement())
sensor.B128X128: 128x128 #(用于帧差异 image.find_displacement())
sensor.LCD: 128x160 #(用于LCD扩展板)
sensor.QQVGA2: 128x160 #(用于LCD扩展板)
sensor.WVGA: 720x480 #(用于 MT9V034)
sensor.WVGA2:752x480 #(用于 MT9V034)
sensor.SVGA: 800x600 #(仅用于 OV5640 感光元件)
sensor.XGA: 1024x768 #(仅用于 OV5640 感光元件)
sensor.SXGA: 1280x1024 #(仅用于 OV5640 感光元件)
sensor.UXGA: 1600x1200 #(仅用于 OV5640 感光元件)
sensor.HD: 1280x720 #(仅用于 OV5640 感光元件)
sensor.FHD: 1920x1080 #(仅用于 OV5640 感光元件)
sensor.QHD: 2560x1440 #(仅用于 OV5640 感光元件)
sensor.QXGA: 2048x1536 #(仅用于 OV5640 感光元件)
sensor.WQXGA: 2560x1600 #(仅用于 OV5640 感光元件)
sensor.WQXGA2: 2592x1944 #(仅用于 OV5640 感光元件)

相关名词解释

  1. 自动增益
    为了能在不同的景物照度条件下都能输出的标准视频信号,必须使放大器的增益能够在较大的范围内进行调节。这种增益调节通常都是通过检测视频信号的平均电平而自动完成的,实现此功能的电路称为自动增益控制电路,简称AGC电路 。具有AGC功能的摄像机,在低照度时的灵敏度会有所提高,但此时的噪点也会比较明显。这是由于信号和噪声被同时放大的缘故
  2. 白平衡
    就是让白色看起来是白色。在日光灯的房间里拍摄的影像会显得发绿,在室内钨丝灯光下拍摄出来的景物就会偏黄,而在日光阴影处拍摄到的照片则莫名其妙地偏蓝,其原因就在于白平衡的设置上。
  3. 色温
    光线的温度——暖光或冷光(K:开尔文)冷光色温高偏蓝;暖光色温低偏红
    光的色调是通过色温来实现的,色温会告诉相机这些光是暖色调还是冷色调
    在这里插入图片描述
  4. 曝光
    光圈越大,单位时间内通过的光越多;快门,允许光通过的时间。
    光圈表示方法:F+数值,数值越小光圈越大,相邻差2倍
    快门表示方法:1/30s,直接使用时间单位,相邻差2倍
    光圈和快门的组合形成了曝光量(不同的组合虽然能够达到相同的曝光量,但是所拍摄出来的图片效果是不同的)
    根据物体运动的规律选择快门,在拍摄运动物体时发现,往往拍摄出来的主体是模糊的,这多半就是因为快门的速度不够快。快门快了,进光量可能减少,色彩偏淡,这就需要增加曝光来加强图片亮度。
    曝光模式是计算机采用自然光源的模式——快门优先、光圈优先、手动曝光、AE锁等模式。
    (1)快门和光圈优先:机器自动测光系统计算出曝光量的值,然后自己选择快门和光圈,系统根据你选的快门或光圈去确定另一个的参数(在手动定义快门(光圈)的情况下通过相机测光而获取光圈(快门)值)
    (2)手动曝光模式
    每次拍摄时都需手动完成光圈和快门速度的调节
    (3)AE模式(Auto Exposure自动曝光)
    光圈优先AE式,快门速度优先AE式,程式AE式,闪光AE式和深度优先AE式
  5. ROI
    要处理的图像中提取出的要处理的区域
    在这里插入图片描述
    roi的格式是(x, y, w, h)的tupple.
    x:ROI区域中左上角的x坐标
    y:ROI区域中左上角的y坐标
    w:ROI的宽度
    h:ROI的高度

2. 图像的基本运算

相关函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 1. 获取/设置像素点
image.get_pixel(x,y)
# 获取一个像素点的值
# 灰度:得到灰度值
# RGB:得到(r,g,b)的tuple

image.set_pixel(x,y,pixel)
# 设置一个像素点的值
# 灰度:设置灰度值
# RGB:设置(r,g,b)的tuple

# 2. 获取图像的宽度和高度
image.width()
image.height()
image.format()# 返回灰度还是彩色
image.size() # 返回图像大小byte

# 3. 图像的运算
# 图像可以是从文件读入的iamge对象,但是二者必须是相同的尺寸和类型(灰色/彩色)
image.invert() # 取反,二值图像0变1,1变0
image.nand(image) #与非
image.nor(image) #或非
image.xor(image) #异或
image.xnor(image) # 异或非
image.difference(image) # 从一张图片减去另一张图片,绝对值相减,经常用于移动检测

3. 使用图像的统计信息

使用ROI选择需要处理的图像区域,然后根据相应函数返回对所要研究区域的对应值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
image.get_statistics(roi=Auto)# roi bins之类的参数一定要显式声明

image.get_statistics(roi=(0,0,10,20))
#statistics.mean() 返回灰度的平均数(0-255) (int)。你也可以通过statistics[0]获得。
#statistics.median() 返回灰度的中位数(0-255) (int)。你也可以通过statistics[1]获得。
#statistics.mode() 返回灰度的众数(0-255) (int)。你也可以通过statistics[2]获得。
#statistics.stdev() 返回灰度的标准差(0-255) (int)。你也可以通过statistics[3]获得。
#staistics.min() 返回灰度的最小值(0-255) (int)。你也可以通过statistics[4]获得。
#statistics.max() 返回灰度的最大值(0-255) (int)。你也可以通过statistics[5]获得。
#statistics.lq() 返回灰度的第一四分数(0-255) (int)。你也可以通过statistics[6]获得。
#statistics.uq() 返回灰度的第三四分数(0-255) (int)。你也可以通过statistics[7]获得。

# lab三个通道的相关信息
#l_mean,l_median,l_mode,l_stdev,l_min,l_max,l_lq,l_uq,
#a_mean,a_median,a_mode,a_stdev,a_min,a_max,a_lq,a_uq,
#b_mean,b_median,b_mode,b_stdev,b_min,b_max,b_lq,b_uq,
1
 

4. 画图

将所研究区域标注出来

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# 颜色可以是灰度值0-255,也可以是彩色值(r,g,b)的tuple,但是color必须显式注明,默认是白色
image.draw_line((10,10,20,30),color=(255,0,0))
image.draw_rectangle(rect_tuple,color=(255,0,0))

# 画直线
image.draw_line(line_tuple,color=White)
# line_tuple=(x1,y1,x2,y2) 从(x1,y1)到(x2,y2)的直线
# 颜色可以是灰度值(0-255),也可以是彩色值(r,g.b)的tuple

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

# 画圆
image.draw_circle(x, y, radius, color=White)
# 在图像中画一个圆
# 圆心+半径

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

# 写字
image.draw_string(x, y, text, color=White)
# 在图像中写字 8x10的像素
# xy是坐标,使用\n\r或\r\n会使光标移动到下一行
# text是要写的字符串
1
 

5. 寻找色块

find_blobs函数的参数介绍:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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 颜色的阈值
# roi感兴趣区
# x_stride 查找的色块的x方向上最小宽度的像素,比如想要找x方向上宽度大于10的像素,=10即可(默认是2)
# y_stride 查找的色块的y方向上最小宽度的像素(默认是1)
# invert 反转阈值,将阈值以外的颜色作为阈值进行查找
# area_threshold 面积阈值,如果色块被包围起来的面积小于这个值会被舍弃
# pixels_threshold 像素个数阈值,如果色块的像素数量小于这个值,会被过滤掉
# merge True,合并所有重叠的blob为1个,这个是合并所有的blob,无论什么颜色,如果想要分别混合的话,分别调用不同颜色阈值的find_blobs
# margin 边界,如果设置为1,那么两个blobs如果间距1一个像素点,也会被合并
# 合并所有
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)

find_blobs的返回值介绍:
find_blobs对象返回的是多个blob的列表,一个blobs列表里面包含很多的blob对象,blobs对象就是色块,每个blobs对象包含一个色块的信息。
可以使用for循环遍历色块:

1
2
3
blobs = img.find_blobs([red])
for blob in blobs:
    print(blob.cx())

色块blob对象

常见blob的方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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会对应每一个阈值。
# 一个色块red-0001,blue-0010,如果是red和blue的merge得到的blob-0011
blob.count() #如果merge=True,那么就会有多个blob被合并到一个blob,这个函数返回的就是这个的数量。如果merge=False,那么返回值总是1。也可以通过blob[9]来获取
blob.area() #返回色块的外框的面积。应该等于(w * h)
blob.density() #返回色块的密度。这等于色块的像素数除以外框的区域。
#如果密度较低,那么说明目标锁定的不是很好。
#比如,识别一个红色的圆,返回的blob.pixels()是目标圆的像素点数,blob.area()是圆的外接正方形的面积。

颜色阈值

1
2
3
4
# 分别是LAB的最小值和最大值
red = (minL, maxL, minA, maxA, minB, maxB)
# 如果是灰度图的话,只有min和max
grey = (min,max)
颜色阈值的获得:

在这里插入图片描述
在这里插入图片描述
帧缓冲区是IDE实时获取的图像;图像文件是SD卡中存储的。

在这里插入图片描述
拖动滑块可以直接看到阈值的效果,想要的结果就是将目标颜色都变成白色,其他颜色全为黑色

6. AprilTag实现标记跟踪

AprilTag是一个视觉基准系统,可用于各种任务,包括AR,机器人和相机校准。这个tag可以直接用打印机打印出来,而AprilTag检测程序可以计算相对于相机的精确3D位置,方向和id在这里插入图片描述
可以得到空间位置量和旋转角度进行3D定位
在这里插入图片描述
???

6. 模版匹配NCC

归一化互相关匹配
???
注意:

  1. 模版图片的大小如果超过openmv内置的flash,需要SD卡
  2. 创建或导入的模版必须是pgm格式的,而且大小不能超过openmv的像素大小
  3. 可以直接使用openmv截取模版图像,但是一定要注意转换成pgm格式

7. 多模版匹配

提前存取多个模版即可

8. 特征点检测

特征点检测要比模版匹配更加灵活,模版匹配只要大小和角度有一点不同,就可能匹配失败。但是特征点检测,在刚开始运行程序的时候,例程提取最开始的图像作为目标物体特征,kpts1保存目标物体的特征。默认会匹配目标特征的多种比例大小和角度,而不仅仅是保存目标特征时的大小角度。也可以提前保存目标特征,之前不这么做是因为每次运行程序光线不同特征不同,匹配度会降低,但是增加了对曝光度、白平衡、自动增益值的调节之后,相对来说会减弱光线的干扰,也可以提前保存特征(车里光线的干扰情况也得考虑。。)

例程有参考价值(待。。)
例程只支持灰度图,注意一下使用的特征提取算法是FAST/AGAST算法

9. 测距

  1. 第一种方法是Apriltag进行3D定位测距
  2. 第二种方法按照参照物距离的比例进行测距
    得第一次通过一个实际距离已知的测距来得到参数k的值,然后再进行后面的计算。

OpenMV使用的是单目摄像头。
关于单目摄像头和双目摄像头:
无人驾驶的智能摄像头要求:看得远,质量高。
看得远长焦距,但视角会变窄,主要是反应时间充足;质量高采用黑白相机成像。
测距原理上,两种摄像头便完全不同。单目摄像头需要对目标进行识别,也就是说在测距前先识别障碍物是车、人还是别的什么。在此基础上再进行测距。而双目摄像头则更加像人类的双眼,主要通过两幅图像的视差计算来确定距离。?双目摄像头当然也不是十全十美,因为需要靠计算来进行测距。其最大的难点就在于计算量巨大。这带来的直接问题就是小型化难度很大。

例程有参考价值(待。。)

10. 扫码识别

暂时没用

11. 颜色形状同时识别

例程是首先在图像中进行圆形识别,然后统计圆形中的图像颜色

12. 颜色模版匹配同时识别

首先进行颜色识别,然后再去识别到的颜色区域里去找到所要的模版
和11一样,例程具有参考价值而且如何进行算法的组合使用需要考虑(待。。)

13.分辨不同人脸

例程可用(待。。)
使用LBP特征分辨不同的人脸。这里是首先选择了20张同一个人不同表情的人脸保存(通过不同的灯光显示告知被采集数据者数据采集完成与否)
然后识别的是当前摄像头视野内的人脸并输出与当前对象最匹配的人。

14. 高级特征

HAAR
HOG
LBP
看一下其他实际应用效果比较好的特征可以应用的选择一下

(待。。)