第二章-Openmv图像处理的方法


OpenMV官方中文文档:
https://docs.singtown.com/micropython/zh/latest/openmvcam/library/omv.sensor.html

感光元件

感光元件

sensor模块,用于设置感光元件的参数。
举个例子:

import sensor#引入感光元件的模块

# 设置摄像头
sensor.reset()#初始化感光元件
sensor.set_pixformat(sensor.RGB565)#设置为彩色
sensor.set_framesize(sensor.QVGA)#设置图像的大小
sensor.skip_frames()#跳过n张照片,在更改设置后,跳过一些帧,等待感光元件变稳定。

# 一直拍照
while(True):
    img = sensor.snapshot()#拍摄一张照片,img为一个image对象

初始化

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

设置彩色/黑白

在这里插入图片描述

设置图像大小

在这里插入图片描述
在这里插入图片描述

跳过一些帧

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

获取一张图像

  • sensor.snapshot() 拍摄一张照片,返回一个image对象。

自动增益/白平衡/曝光

  • sensor.set_auto_gain() 自动增益开启(True)或者关闭(False)。在使用颜色追踪时,需要关闭自动增益。
  • sensor.set_auto_whitebal() 自动白平衡开启(True)或者关闭(False)。在使用颜色追踪时,需要关闭自动白平衡。
    在这里插入图片描述

设置窗口ROI

sensor.set_windowing(roi)

ROI:Region Of Interest,图像处理中的术语“感兴趣区”。就是在要处理的图像中提取出的要处理的区域。

sensor.set_framesize(sensor.VGA) # 高分辨率
sensor.set_windowing((640, 80)) #取中间的640*80区域

roi的格式是(x, y, w, h)。

设置翻转

sensor.set_hmirror(True)
水平方向翻转

sensor.set_vflip(True)
垂直方向翻转

经典示例

这个示例就是openmv一开始的那个Hello World Example,看完这个例子,突然发现#这个符号,应该是python的注释符号。

# Hello World Example
#
# Welcome to the OpenMV IDE! Click on the green run arrow button below to run the script!

import sensor, image, time

sensor.reset()                      # Reset and initialize the sensor.
sensor.set_pixformat(sensor.RGB565) # Set pixel format to RGB565 (or GRAYSCALE)
sensor.set_framesize(sensor.QVGA)   # Set frame size to QVGA (320x240)
sensor.skip_frames(time = 2000)     # Wait for settings take effect.
clock = time.clock()                # Create a clock object to track the FPS.

while(True):
    clock.tick()                    # Update the FPS clock.
    img = sensor.snapshot()         # Take a picture and return the image.
    print(clock.fps())              # Note: OpenMV Cam runs about half as fast when connected
                                    # to the IDE. The FPS should increase once disconnected.

基本方法

坐标

在这里插入图片描述

获取/设置像素点

在这里插入图片描述
举例:

img = sensor.snapshot()
img.get_pixel(10,10)
img.set_pixcel(10,10,(255,0,0))#设置坐标(10,10)的像素点为红色(255,0,0)

获取图像的宽度和高度

在这里插入图片描述
使用了这个函数,然后看了看,OpenMV4的宽度(像素)是320,高度是240

图像的运算

在这里插入图片描述

使用图像的统计信息

如果我想知道一个区域内的平均颜色或者占面积最大的颜色?
使用统计信息——Statistics.

ROI感兴趣的区域

在这里插入图片描述
在这里插入图片描述

Statistics

在这里插入图片描述
在这里插入图片描述
其中LAB亮度-对比度
Lab颜色空间中,L亮度;a的正数代表红色,负端代表绿色;b的正数代表黄色,负端代表兰色。不像RGB和CMYK色彩空间,Lab颜色被设计来接近人类视觉。

因此L分量可以调整亮度对,修改a和b分量的输出色阶来做精确的颜色平衡。

举例

在这里插入图片描述

import sensor, image, time

sensor.reset() # 初始化摄像头
sensor.set_pixformat(sensor.RGB565) # 格式为 RGB565.
sensor.set_framesize(sensor.QVGA)
sensor.skip_frames(10) # 跳过10帧,使新设置生效
sensor.set_auto_whitebal(False)               # Create a clock object to track the FPS.

ROI=(80,30,15,15)

while(True):
    img = sensor.snapshot()         # Take a picture and return the image.
    statistics=img.get_statistics(roi=ROI)
    color_l=statistics.l_mode()
    color_a=statistics.a_mode()
    color_b=statistics.b_mode()
    print(color_l,color_a,color_b)
    img.draw_rectangle(ROI)

画图

视觉系统通常需要给使用者提供一些反馈信息。直接在图像中显示出来,很直观。当找到色块,把这个区域用矩形框标注出来,这样非常直观。
在这里插入图片描述
像上图这个+一样,非常直观。

注意:

  • 颜色可以是灰度值(0-255),或者是彩色值(r,g,b)的tupple。默认是白色。
  • 其中的color关键字必须显示的标明color =。例如:
image.draw_line((10,10,20,30), color=(255,0,0))
image.draw_rectangle(rect_tuple, color=(255,0,0))

画线

在这里插入图片描述

画框

在这里插入图片描述

画圆

在这里插入图片描述

画十字

在这里插入图片描述

写字

在这里插入图片描述

例子:

在这里插入图片描述

# Hello World Example
#
# Welcome to the OpenMV IDE! Click on the green run arrow button below to run the script!

import sensor, image, time

sensor.reset() # 初始化摄像头
sensor.set_pixformat(sensor.RGB565) # 格式为 RGB565.
sensor.set_framesize(sensor.QQVGA)
sensor.skip_frames(10) # 跳过10帧,使新设置生效
while(True):
    img = sensor.snapshot()         # Take a picture and return the image.
    img.draw_line((20, 30, 40, 50))
    img.draw_line((80, 50, 100, 100), color=(255,0,0))
    img.draw_rectangle((20, 30, 41, 51), color=(255,0,0))
    img.draw_circle(50, 50, 30)
    img.draw_cross(90,60,size=10)
    img.draw_string(10,10, "hello world!")

寻找色块

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)

这里参数比较多

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

在这里插入图片描述

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])

  • roi是“感兴趣区”。在使用统计信息中已经介绍过了。
    left_roi = [0,0,160,240]
    blobs = img.find_blobs([red],roi=left_roi)
    如果不设置roi的话,就默认是整个图像中进行颜色识别
  • x_stride 就是查找的色块的x方向上最小宽度的像素,默认为2,如果你只想查找宽度10个像素以上的色块,那么就设置这个参数为10:
    blobs = img.find_blobs([red],x_stride=10)
  • y_stride 就是查找的色块的y方向上最小宽度的像素,默认为1,如果你只想查找宽度5个像素以上的色块,那么就设置这个参数为5:
    blobs = img.find_blobs([red],y_stride=5)
  • invert 反转阈值,把阈值以外的颜色作为阈值进行查找
    如果想查找阈值以外的颜色的话,就设置invert = True,若不设置的话,就是默认查找阈值内的颜色
  • area_threshold 面积阈值,如果色块被框起来的面积小于这个值,会被过滤掉
  • pixels_threshold 像素个数阈值,如果色块像素数量小于这个值,会被过滤掉
    在这里插入图片描述
    area_threshold与pixels_threshold两个是不一样的,area_threshold是被框起来的面积,里面可能包含一些其他的背景的像素数。然而pixels_threshold是色块像素的面积,是在框中所有红色的像素的面积,而不包含背景的颜色
  • merge 合并,如果设置为True,那么合并所有重叠的blob为一个。
    如果merge设置为True的话,那么所有在图像里面查找到的blobs都会合并成一个大的框框
    注意:这会合并所有的blob,无论是什么颜色的。如果你想混淆多种颜色的blob,只需要分别调用不同颜色阈值的find_blobs。
    在这里插入图片描述
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)

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

阈值

一个颜色阈值的结构是这样的:
在这里插入图片描述

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

元组里面的数值分别是L A B 的最大值和最小值。

颜色阈值选择工具

OpenMV 的IDE里加入了阈值选择工具,极大的方便了对于颜色阈值的调试。

首先运行hello world.py让IDE里的framebuffer显示图案。
在这里插入图片描述
然后打开 工具 → Mechine Vision → Threshold Editor
在这里插入图片描述
点击 Frame Buffer可以获取IDE中的图像,Image File可以自己选择一个图像文件。
在这里插入图片描述
拖动六个滑块,可以实时的看到阈值的结果,我们想要的结果就是,将我们的目标颜色变成白色,其他颜色全变为黑色。
在这里插入图片描述

blobs是一个列表

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有多个方法:

  • 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内置了特征点检测的算法,利用的是FAST和AGAST两种算法来进行特征点检测,我们可以利用特征点匹配来检测一些不规则的物件,或者是一些经常需要变化角度、变化大小、不同距离进行识别的一些物体特征。

特征点检测经常用于目标追踪,之前的OpenMV的模板匹配,对于特征点检测和模板匹配两种算法而言,他们既有相似的地方,也有不同的地方,他们都是用来检测目标物体特征的一种算法,其中模板匹配对于目标物体的要求是,我们保存了模板图片之后,在以后的识别中,尽量的让目标物体不要变换角度也不要变换大小,也就是说,适合于OpenMV物体固定的距离来进行识别,并且物体的角度不会发生改变,如果想要模板匹配这个物体的多个角度或者多个大小的话,我们就需要保存多张模板图片,比如说保存物体的多个角度多个大小的图片,然而模板匹配一般最多保存十张图片进行识别,过多的话,可能会导致内存的问题,相反,特征点检测,它就可以识别目标物体的多个角度多个大小,不那么受目标物体大小和角度的影响,相对来说非常灵活,但是对于特征点检测而言,一般建议在检测开始前保存目标物体的特征,因为不同环境不同背景下的特征可能会是不一样的,所以最好是开始检测之前再进行目标特征的保存提取。

在OpenMV官方提供的例程当中,首先在程序运行的前十秒来提取目标物体的特征,然后把它记录为目标特征,在以后的识别中与这个目标特征进行对比,来判断是否识别到了目标。

# 利用特征点检测特定物体例程。
# 向相机显示一个对象,然后运行该脚本。 一组关键点将被提取一次,然后
# 在以下帧中进行跟踪。 如果您想要一组新的关键点,请重新运行该脚本。
# 注意:请参阅文档以调整find_keypoints和match_keypoints。
import sensor, time, image

# 重置传感器
sensor.reset()

# 传感器设置
sensor.set_contrast(3)
sensor.set_gainceiling(16)
sensor.set_framesize(sensor.VGA)
sensor.set_windowing((320, 240))
sensor.set_pixformat(sensor.GRAYSCALE)

sensor.skip_frames(time = 2000)
sensor.set_auto_gain(False, value=100)

#画出特征点
def draw_keypoints(img, kpts):
    if kpts:
        print(kpts)
        img.draw_keypoints(kpts)
        img = sensor.snapshot()
        time.sleep_ms(1000)

kpts1 = None
#kpts1保存目标物体的特征,可以从文件导入特征,但是不建议这么做。
#kpts1 = image.load_descriptor("/desc.orb")
#img = sensor.snapshot()
#draw_keypoints(img, kpts1)

clock = time.clock()

while (True):
    clock.tick()
    img = sensor.snapshot()
    if (kpts1 == None):
        #如果是刚开始运行程序,提取最开始的图像作为目标物体特征,kpts1保存目标物体的特征
        #默认会匹配目标特征的多种比例大小,而不仅仅是保存目标特征时的大小,比模版匹配灵活。
        # NOTE: By default find_keypoints returns multi-scale keypoints extracted from an image pyramid.
        kpts1 = img.find_keypoints(max_keypoints=150, threshold=10, scale_factor=1.2)
        #image.find_keypoints(roi=Auto, threshold=20, normalized=False, scale_factor=1.5, max_keypoints=100, corner_detector=CORNER_AGAST)
        #roi表示识别的区域,是一个元组(x,y,w,h),默认与framsesize大小一致。
        #threshold是0~255的一个阈值,用来控制特征点检测的角点数量。用默认的AGAST特征点检测,这个阈值大概是20。用FAST特征点检测,这个阈值大概是6080。阈值越低,获得的角点越多。
        #normalized是一个布尔数值,默认是False,可以匹配目标特征的多种大小(比ncc模版匹配效果灵活)。如果设置为True,关闭特征点检测的多比例结果,仅匹配目标特征的一种大小(类似于模版匹配),但是运算速度会更快一些。
        #scale_factor是一个大于1.0的浮点数。这个数值越高,检测速度越快,但是匹配准确率会下降。一般在1.35~1.5左右最佳。
        #max_keypoints是一个物体可提取的特征点的最大数量。如果一个物体的特征点太多导致RAM内存爆掉,减小这个数值。
        #corner_detector是特征点检测采取的算法,默认是AGAST算法。FAST算法会更快但是准确率会下降。
        draw_keypoints(img, kpts1)
        #画出此时的目标特征
    else:
        # 当与最开始的目标特征进行匹配时,默认设置normalized=True,只匹配目标特征的一种大小。
        # NOTE: When extracting keypoints to match the first descriptor, we use normalized=True to extract
        # keypoints from the first scale only, which will match one of the scales in the first descriptor.
        kpts2 = img.find_keypoints(max_keypoints=150, threshold=10, normalized=True)
        #如果检测到特征物体
        if (kpts2):
            #匹配当前找到的特征和最初的目标特征的相似度
            match = image.match_descriptor(kpts1, kpts2, threshold=85)
            #image.match_descriptor(descritor0, descriptor1, threshold=70, filter_outliers=False)。本函数返回kptmatch对象。
            #threshold阈值设置匹配的准确度,用来过滤掉有歧义的匹配。这个值越小,准确度越高。阈值范围0100,默认70
            #filter_outliers默认关闭。

            #match.count()是kpt1和kpt2的匹配的近似特征点数目。
            #如果大于10,证明两个特征相似,匹配成功。
            if (match.count()>10):
                # If we have at least n "good matches"
                # Draw bounding rectangle and cross.
                #在匹配到的目标特征中心画十字和矩形框。
                img.draw_rectangle(match.rect())
                img.draw_cross(match.cx(), match.cy(), size=10)

            #match.theta()是匹配到的特征物体相对目标物体的旋转角度。
            print(kpts2, "matched:%d dt:%d"%(match.count(), match.theta()))
            # 不建议draw_keypoints画出特征关键点。
            # 注意:如果你想绘制关键点,取消注释
            #img.draw_keypoints(kpts2, size=KEYPOINTS_SIZE, matched=True)

    #打印帧率。
    img.draw_string(0, 0, "FPS:%.2f"%(clock.fps()))

sensor.set_pixformat(sensor.GRAYSCALE)

这句代码是设置为灰度图模式,特征点检测的算法是使用灰度图,不能使用彩图,然后设置自动增益为关闭。

draw_keypoints这个函数用来画出特征点.

最开始我们设置kpts1等于None,这个kpts1就是运行程序的时候,最开始识别到的物体的特征,将会被保存到kpts1中,这个物体特征会作为我们的目标物体特征,也就是说我们后续的识别将会以这个特征进行对比,如果后续我们的视野中与这一个目标特征匹配相似的话,那么我们就会匹配成功。

当然这个特征除了可以是我们运行程序最开始的时候那几秒的,提取到的一个特征之外,也可以从文件中导入我们预先保存的特征,就是说我们可以利用我们的保存特征点的那个例程,来先保存特征到内存卡然后进行识别。这个例程是keypoints_save这个例程。

但是一般是不建议从文件中导入特征的,因为每次运行的时候可能环境不一样,背景不一样,光线啥的也不一样,会影响特征点的提取与匹配,所以一般建议是检测的时候实时提取特征,就是首先先复制为None。

find_keypoints函数返回的是检测到的目标物体的关键点,如果没有提取到关键点的话那就返回None。

image.match_descriptor这个函数来比较我们的目标物体特征kpts1和我们当前提取到的物体特征kpts2之间的相似度,如果两者十分相似的话那么证明我们在视野中发现了目标物体,就是匹配成功,如果match_descriptor这个函数比较的目标物体特征,和当前物体特征相差过大,那么就相当于我们在视野中没有匹配到目标物体。

对于ORB描述符来说,这个函数返回的是kptmatch对象,例程中的就是使用了ORB描述符,所以它会返回一个kptmatch对象。
在这里插入图片描述
其中kptmatch.copunt()返回的是匹配的特征点的数量(int),这个数量越多证明我们视野中的物体和我们的目标物体更匹配。

特征点保存

例程中的代码

# 特征点保存例程
# 此示例显示如何将关键点描述符保存到文件。向相机显示一个对象,然后运行
# 该脚本。该脚本将提取并保存关键点描述符和图像。
# 您可以使用keypoints_editor.py 来删除不需要的关键点。
#
# 注意:请在运行此脚本后重置摄像头以查看新文件。
import sensor, time, image

# 重置传感器
sensor.reset()

# 传感器设置
sensor.set_contrast(3)
sensor.set_gainceiling(16)
sensor.set_framesize(sensor.VGA)
sensor.set_windowing((320, 240))
sensor.set_pixformat(sensor.GRAYSCALE)

sensor.skip_frames(time = 2000)
sensor.set_auto_gain(False, value=100)

FILE_NAME = "desc"
img = sensor.snapshot()
# 注意:请参阅文档查看其他参数
# 注:默认情况下,find_keypoints返回从图像中提取的多尺度关键点。
kpts = img.find_keypoints(max_keypoints=150, threshold=10, scale_factor=1.2)

if (kpts == None):
    raise(Exception("Couldn't find any keypoints!"))

image.save_descriptor(kpts, "/%s.orb"%(FILE_NAME))
img.save("/%s.pgm"%(FILE_NAME))

img.draw_keypoints(kpts)
sensor.snapshot()
time.sleep_ms(1000)
raise(Exception("Done! Please reset the camera"))

这个代码的最开始和keypoints特征点检测的时候的设置是一样的,设置对比度,自动增益以及图像大小,图像格式灰度图等,然后设置一个变量FILE_NAME为desc,也就是descriptor的简称,我们利用特征点保存这个例程,提取到的特征保存到descriptor这个文件中,然后我们截取一张图像,调用find_keypoints函数,也就是提取我们视野中的特征,将特征保存到kpts这个变量里,如果kpts等于None,意思就是我们在视野中没有提取到特征,那么就抛出一个异常,提示你没有找到任何特征,如果我们找到了事业中的特征的话,我们就接着运行下面的代码,将这一个特征使用save_descriptor这个函数来进行保存。

保存特征的时候,最好使用sd卡

  • 3
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值