Openmv 学习笔记(一)

基本背景知识

像素、分辨率、帧率

感光元件读取到的一张图像是有很多个感光点构成的,比如有640*480个点,每个点就是一个像素,把每个点的像素收集整理起来,就是一副图片,那么这张图片的分辨率就是640*480。

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

图像颜色

RGB三原色

 

LAB亮度-对比度

1. L代表亮度

2. A如果是正数代表红色,如果是负数代表绿色

3. B正数代表黄色,负数代表蓝色

所以,可以通过调整L来平衡A和B输出的色阶来平衡图像颜色

查找色块时用的就是LAB模式

 镜头

 镜头焦距长的所成的象大,镜头焦距短的所成的象小。注意焦距越长,视角越小。

 因为光学原理,镜头图像边缘会出现鱼眼效果(桶型畸变)可以在代码中使用算法来矫正畸变,    注:OpenMV中使用image.lens_corr(1.8)来矫正2.8mm焦距的镜头。也可以直接使用无畸变镜       头。

感光读取

 用sensor模块的相关函数。

初始化

sensor.reset() 

 #初始化感光原件


设置图像 彩色/灰色

sensor.plxformat(参数1)
#参数1: # sensor.GRAYSCALE   灰度图,每个像素8bit
         #  sensor.RGB565      彩色,每个像素16Bit

设置图像大小

sensor.set_framesize(参数1)

参数1:(Q多则小)
#CIF系列
sensor.QQCIF: 88x72
sensor.QCIF: 176x144
sensor.CIF: 352x288
#SIF系列
sensor.QQSIF: 88x60
sensor.QSIF: 176x120
sensor.SIF: 352x240
#VGA系列
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.skip_frames(参数1)

参数1
两种方式实现:

sensor.skip_frame(20) # 跳过20帧数
sensor.skip_frame(time=2000) # 跳过2000ms=2s 

 自动增益/白平衡/曝光

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

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

sensor.set_auto_exposure(enable[\, exposure_us])

enable 打开(True)或关闭(False)自动曝光。默认打开。
如果 enable 为False,则可以用 exposure_us 设置一个固定的曝光时间(以微秒为单位)。

 设置窗口ROI

sensor.set_windowing(roi)
 

roi格式为(x,y,w,h)

roi为感兴趣区域,即设置摄像头读取到图像中的某一部分区域

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

  • x:ROI区域中左上角的x坐标
  • y:ROI区域中左上角的y坐标
  • w:ROI的宽度
  • h:ROI的高度

  设置图像反转

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

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

  返回image

sensor.snapshot()

使用相机拍照并返回一个 image 对象。

  读取示例

import sensor

# 初始化
sensor.reset()
sensor.set_pixformat(sensor.RGB565)
sensor.set_framesize(sensor.QVGA)
sensor.skip_frames()

# 拍图像
while(True):
    sensor.snapshot()

图像的简单运算

 获取/设置像素点

image.get_pixel(x,y)
#获取到(x,y)的像素点
#对于灰度图: 返回(x,y)坐标的灰度值.
#对于彩色图: 返回(x,y)坐标的(r,g,b)的tuple.


image.set_pixel(x,y,pixel)
#参数x,y:定义的x,y坐标
#参数pixel:将选中的像素点设置成的样式
#对于灰度图: 设置(x,y)坐标的灰度值。
#对于彩色图: 设置(x,y)坐标的(r,g,b)的值。

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

  获取图像的宽度和高度

  • image.width()
    返回图像的宽度(像素)

  • image.height()
    返回图像的高度(像素)

  • image.format()
    灰度图会返回 sensor.GRAYSCALE,彩色图会返回 sensor.RGB565。

  • image.size()
    返回图像的大小(byte)

 图像的运算

  • image.invert()

取反,对于二值化的图像,0(黑)变成1(白),1(白)变成0(黑)。

注:
图像可以是另一个image对象,或者是从 (bmp/pgm/ppm)文件读入的image对象。
两个图像都必须是相同的尺寸和类型(灰度图/彩色图)。

  • image.nand(image)
    与另一个图片进行与非(NAND)运算。

  • image.nor(image)
    与另一个图片进行或非(NOR)运算。

  • image.xor(image)
    与另一个图片进行异或(XOR)运算。

  • image.xnor(image)
    与另一个图片进行异或非(XNOR)运算。

  • image.difference(image)
    从这张图片减去另一个图片。比如,对于每个通道的每个像素点,取相减绝对值操作。这个函数,经常用来做移动检测。

统计图像信息

image.get_statistics(roi=Auto)

其中roi是目标区域。注意,这里的roi,bins之类的参数,一定要显式地标明,例如:

img.get_statistics(roi=(0,0,10,20))

如果是 img.get_statistics((0,0,10,20)),ROI不会起作用。

  • 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]获得。

  • statistics.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]获得。

上面的是灰度的值,接下来的

  • 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,

是LAB三个通道的平均数,中位数,众数,标准差,最小值,最大值,第一四分数,第三四分数

 具体应用

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)

图像上画图

画线

image.draw_line(line_tuple,color)

#参数1 line_tuple格式是(x0,y0,x1,y1)指的是从坐标(x0,y0)到(x1,y1)画一条线
#参数2 color这个关键字必须显示的表明,可以是灰度值中的(0~255),或者(r,g,b)turple
#颜色默认白色

画框

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是两侧的尺寸

写字

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

  • 其中的color关键字必须显示的标明color=。例如:

image.draw_line((10,10,20,30), color=(255,0,0))
image.draw_rectangle(rect_tuple, color=(255,0,0))

示例代码:

import sensor
import time

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


while True:
    img=sensor.snapshot()
    img.draw_line(20,20,40,40,color=(255,0,0))
    img.draw_rectangle(50,50,10,10,color=(0,255,0))
    img.draw_cross(10,10,color=(0,0,255),size=10)
    img.draw_circle(100,100,10,color=(0,0,0))
    img.draw_string(120,120,"hello world")

结果

寻找色块

image.find_blobs()

寻找色块时,主要是基于image.find_blobs()函数进行相关操作。

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

是图像颜色的阈值,这个参数是一个列表,颜色可以包含多个,根据列表实际决定。

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

感兴趣区域,roi=(x,y,w,h)

left_roi = (0,0,160,240)
blobs = img.find_blobs([red],roi=left_roi)

x_stride

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

表示在x方向上搜索色块时的步长。

y_stride

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

表示在y方向上搜索色块时的步长。

invert:是否反转阈值,将不在阈值内的颜色视为目标色块。

area_threshold 和 pixels_threshold:面积和像素数量的阈值,用于过滤掉小于这些值的色块。

merge:是否合并重叠的色块。这会合并所有的blob,无论是什么颜色的。如果你想混淆多种颜色的blob,只需要分别调用不同颜色阈值的find_blobs。

margin:合并色块时的边界容忍度,如果设置为1,那么两个blobs如果间距1一个像素点,也会被合并。

返回值:

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

可以用for循环把所有的色块找一遍。

blobs = img.find_blobs([red])
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()是圆的外接正方形的面积。

阈值

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

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

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

示例程序

import sensor, image, time

# 颜色追踪的例子,一定要控制环境的光,保持光线是稳定的。
green_threshold   = (   0,   80,  -70,   -10,   -0,   30)
#设置绿色的阈值,括号里面的数值分别是L A B 的最大值和最小值(minL, maxL, minA,
# maxA, minB, maxB),LAB的值在图像左侧三个坐标图中选取。如果是灰度图,则只需
#设置(min, max)两个数字即可。

sensor.reset() # 初始化摄像头
sensor.set_pixformat(sensor.RGB565) # 格式为 RGB565.
sensor.set_framesize(sensor.QQVGA) # 使用 QQVGA 速度快一些
sensor.skip_frames(time = 2000) # 跳过2000s,使新设置生效,并自动调节白平衡
sensor.set_auto_gain(False) # 关闭自动自动增益。默认开启的,在颜色识别中,一定要关闭白平衡。
sensor.set_auto_whitebal(False)
#关闭白平衡。白平衡是默认开启的,在颜色识别中,一定要关闭白平衡。
clock = time.clock() # 追踪帧率

while(True):
    clock.tick() # Track elapsed milliseconds between snapshots().
    img = sensor.snapshot() # 从感光芯片获得一张图像

    blobs = img.find_blobs([green_threshold])
    #find_blobs(thresholds, invert=False, roi=Auto),thresholds为颜色阈值,
    #是一个元组,需要用括号[ ]括起来。invert=1,反转颜色阈值,invert=False默认
    #不反转。roi设置颜色识别的视野区域,roi是一个元组, roi = (x, y, w, h),代表
    #从左上顶点(x,y)开始的宽为w高为h的矩形区域,roi不设置的话默认为整个图像视野。
    #这个函数返回一个列表,[0]代表识别到的目标颜色区域左上顶点的x坐标,[1]代表
    #左上顶点y坐标,[2]代表目标区域的宽,[3]代表目标区域的高,[4]代表目标
    #区域像素点的个数,[5]代表目标区域的中心点x坐标,[6]代表目标区域中心点y坐标,
    #[7]代表目标颜色区域的旋转角度(是弧度值,浮点型,列表其他元素是整型),
    #[8]代表与此目标区域交叉的目标个数,[9]代表颜色的编号(它可以用来分辨这个
    #区域是用哪个颜色阈值threshold识别出来的)。
    if blobs:
    #如果找到了目标颜色
        for b in blobs:
        #迭代找到的目标颜色区域
            # Draw a rect around the blob.
            img.draw_rectangle(b[0:4]) # rect
            #用矩形标记出目标颜色区域
            img.draw_cross(b[5], b[6]) # cx, cy
            #在目标颜色区域的中心画十字形标记

    print(clock.fps()) # 注意: 你的OpenMV连到电脑后帧率大概为原来的一半
    #如果断开电脑,帧率会增加

ApriTag

简介与函数

AprilTag是一个视觉基准系统,可用于各种任务,包括AR,机器人和相机校准。这个tag可以直接用打印机打印出来,而AprilTag检测程序可以计算相对于相机的精确3D位置,方向和id。

ApriTag家族

TAG16H5 → 0 to 29
TAG25H7 → 0 to 241
TAG25H9 → 0 to 34
TAG36H10 → 0 to 2319
TAG36H11 → 0 to 586
ARTOOLKIT → 0 to 511
也就是说TAG16H5的家族(family)有30个,每一个都有对应的id,从0~29。

区别

比如说TAG16H5的有效区域是4 x 4的方块,那么它比TAG36H11看的更远(因为他有6 x 6个方块)。但是TAG16H5的错误率比TAG36H11高很多,因为TAG36H11的校验信息多,所以,如果没有别的理由,推荐用TAG36H11

ApriTag对象是由image.find_apritags()返回的

find_apriltags([roi[, families=image.TAG36H11[, fx[, fy[, cx[, cy]]]]]])

查找 roi 内的所有AprilTags,并返回一个 image.apriltag 对象列表。

与QR码不同,AprilTags可以在距离更远的情况下、光照更差的情况下、变形的图像中等情况下被检测到。AprilTags对各种图像失真问题都非常强大,而QR码则不是。即便如此,AprilTags只能将数值ID编码为其有效载荷。

AprilTags也可用于定位目的。每个 image.apriltag 对象都会返回相机的平移和旋转。平移的单位由 fxfycx 和 cy 确定,它们分别是图像在X和Y方向上的焦距和中心点。

roi 是感兴趣区域的矩形元组(x, y, w, h)。如果未指定,则等于图像矩形。仅操作 roi 内的像素。

families 是要解码的标签族的位掩码。它是以下内容的逻辑或:

默认值仅为 image.TAG36H11,这是最佳标签族。请注意,每启用一个标签族,Image.find_apriltags() 的速度就会减慢。

fx 是相机X方向的焦距(像素)。对于标准的OpenMV Cam,这是 (2.8 / 3.984) *n。这是以mm为单位的镜头焦距,除以X方向的相机传感器长度,再乘以X方向上相机传感器的像素数(适用于OV7725相机)。

fy 是相机Y方向的焦距(像素)。对于标准的OpenMV Cam,这是 (2.8 / 2.952) * m。这是以mm为单位的镜头焦距,除以Y方向的相机传感器长度,再乘以Y方向上相机传感器的像素数(适用于OV7725相机)。

n,m取决于设置的图像大小,例如sensor.QQVGA,则n为160、m为120。

cx 是图像中心,即 image.width()/2 不是 roi.w()/2

cy 是图像中心,即 image.height()/2 不是 roi.h()/2

AprilTag的标记追踪

1.首先我们要加入我们使用的函数库

import sensor,image,time,math  #导入模块

2.然后我们需要进行感光元件的初始化,以及基本参数的设置。

#初始化感光原件
sensor.reset()
#设置相机模块的色素模式
sensor.set_pixformat(sensor.RGB565)
#设置相机模块帧的大小
sensor.set_framesize(sensor.QQVGA)
#保证图片质量,跳过前面的照片
sensor.skip_frames(n=30)
#关闭自动增益
sensor.set_auto_gain(False)
#关闭白平衡
sensor.set_auto_whitebal(False)

 3.输出的旋转量信息要为角度,函数读取的是弧度制,所以我们需要编写一个弧度制与角度制的转换函数。

#封装一个角度制和弧度制转换的函数
def Degree(radians):          #输入弧度参数
    return (180*radians)/math.pi #返回角度

示例代码

#初始化感光原件
sensor.reset()
#设置相机模块的色素模式
sensor.set_pixformat(sensor.RGB565)
#设置相机模块帧的大小
sensor.set_framesize(sensor.QQVGA)
#保证图片质量,跳过前面的照片
sensor.skip_frames(n=30)
#关闭自动增益
sensor.set_auto_gain(False)
#关闭白平衡
sensor.set_auto_whitebal(False)

#封装一个角度制和弧度制转换的函数
def Degree(radians):          #输入弧度参数
    return (180*radians)/math.pi #返回角度


while(True):
    img=sensor.snapshot()
    for tag in img.find_apriltags():  #默认为TAG36H11格式,不带参数families
        img.draw_rectangle(tag.rect(),color=(255,0,0))
        img.draw_cross(tag.cx(),tag.cy(),color=(0,255,0))
        degree=Degree(tag.rotation())
        print(tag.id(),degree)

AprilTag的3D定位

根据相机的成像原理,根据相机焦距、Tag的尺寸计算出相机坐标系下的三维坐标(openmv会生成一个以自身为坐标原点的三维坐标系);



import sensor, image, time, math

sensor.reset()
sensor.set_pixformat(sensor.RGB565)
sensor.set_framesize(sensor.QQVGA) 
sensor.skip_frames(30)
sensor.set_auto_gain(False) 
sensor.set_auto_whitebal(False) 
clock = time.clock()

# 注意!与find_qrcodes不同,find_apriltags 不需要软件矫正畸变就可以工作。

# 注意,输出的姿态的单位是弧度,可以转换成角度,但是位置的单位是和你的大小有关,需要等比例换算

# f_x 是x的像素为单位的焦距。对于标准的OpenMV,应该等于2.8/3.984*656,这个值是用毫米为单位的焦距除以x方向的感光元件的长度,乘以x方向的感光元件的像素(OV7725)
# f_y 是y的像素为单位的焦距。对于标准的OpenMV,应该等于2.8/2.952*488,这个值是用毫米为单位的焦距除以y方向的感光元件的长度,乘以y方向的感光元件的像素(OV7725)

# c_x 是图像的x中心位置
# c_y 是图像的y中心位置

f_x = (2.8 / 3.984) * 160 # 默认值
f_y = (2.8 / 2.952) * 120 # 默认值
c_x = 160 * 0.5 # 默认值(image.w * 0.5)
c_y = 120 * 0.5 # 默认值(image.h * 0.5)

def degrees(radians):
    return (180 * radians) / math.pi

while(True):
    clock.tick()
    img = sensor.snapshot()
    for tag in img.find_apriltags(fx=f_x, fy=f_y, cx=c_x, cy=c_y): # 默认为TAG36H11
        img.draw_rectangle(tag.rect(), color = (255, 0, 0))
        img.draw_cross(tag.cx(), tag.cy(), color = (0, 255, 0))
       #输出Tag的相关信息
        #为变量复制,定义一个数据信息的大变量,来简化下面的输出函数
        print_args = (tag.x_translation(), tag.y_translation(), tag.z_translation(), \
            degrees(tag.x_rotation()), degrees(tag.y_rotation()),degrees(tag.z_rotation()))

        # 位置的单位是未知的,旋转的单位是角度
        print("Tx: %f, Ty %f, Tz %f, Rx %f, Ry %f, Rz %f" % print_args)
    print(clock.fps())#将相机的帧率输出

模板匹配

函数

 先介绍两个sensor库函数

sensor.set_contrast()
设计相机图像对比度。-3 到 +3。
sensor.set_gainceiling()
设置相机图像增益上限。2、4、8、16、32、64 或 128。

创建图像函数

image.Image(path[, buffer=None[, copy_to_fb=False]])

#从 path 处的文件创建一个新的图像对象。或者,您可以传递 width 、 height ,以及任何图像格式值(如 image.GRAYSCALE )来创建新的空白图像对象(初始化为0 - 黑色)。

寻找模板的函数

find_template(templatethreshold[, roi[, step=2[, search=image.SEARCH_EX]]])

尝试在图像中找到模板匹配的第一个位置,使用归一化互相关。如果找到匹配位置,则返回一个边界框元组 (x, y, w, h),否则返回None。

template 是要与此图像对象匹配的小图像对象。请注意,两个图像都必须是灰度图像

threshold 是一个浮点数(0.0-1.0),其中较高的阈值可以防止错误的正检率,同时降低检测率,而较低的阈值则相反。

roi 是感兴趣区域的矩形元组(x, y, w, h)。如果未指定,则等于图像矩形。仅操作 roi 内的像素

step 是在查找模板时要跳过的像素数。跳过像素可以大大加快算法速度。这仅影响SEARCH_EX模式下的算法。

search 可以是 image.SEARCH_DS 或 image.SEARCH_EX

image.SEARCH_DS 使用比 image.SEARCH_EX 更快的算法搜索模板,但可能无法在图像边缘附近找到模板。 image.SEARCH_EX 对图像进行穷举搜索,但比 image.SEARCH_DS 慢得多。

仅适用于灰度图像

注意点

使用NCC算法进行模板匹配对大小要求和角度要求都较严格,大小或角度变化稍微打一点可能就很难识别出来了。所有我们需要多采集一些不同大小不同角度的模板来避免这一问题。

templates1 = ["/300.pgm","/301.pgm","/302.pgm","/303.pgm","/310.pgm","/311.pgm","/312.pgm","/313.pgm","/320.pgm","/321.pgm","/322.pgm","/323.pgm"] #保存三角形多个模板
templates2 = ["/100.pgm","/101.pgm","/102.pgm","/103.pgm","/110.pgm","/111.pgm","/112.pgm","/113.pgm","/120.pgm","/121.pgm","/122.pgm","/123.pgm"] #保存矩形多个模板
templates3 = ["/000.pgm","/001.pgm","/002.pgm","/003.pgm","/010.pgm","/011.pgm","/012.pgm","/013.pgm","/020.pgm","/021.pgm","/022.pgm","/023.pgm"] #保存圆多个模板

由于我们的模板图片大小超过openmv内置的flash,所以我们需要插上sd卡后进行。(注意先插sd卡再上电) 而且此模板匹配只能用于1.6及以上版本的固件,否则运行时会提示 “can not find SEARCH_EX”哦

首先,我们需要创建或导入一个模板,注意这个模板必须得是pgm格式的,而且大小有限制,不能超过openmv的像素大小。 我们可以直接从openmv里面截取一个模板图像,可以先运行helloworld.py例程,让frambuffer显示出图像,然后进行截取。

选择 save image selection to pc,注意从openmv里面直接截取保存的图片是bmp格式的,我们需要把它转换成pgm格式。可以在这个网站进行在线转换BMP轉PGM轉換器。在线自由 — Convertio

然后,我们将转换完的pgm模板保存到sd卡中。我们打开模板匹配的程序就可以进行匹配了。

示例:单模板匹配

# NCC模板匹配示例-Normalized Cross Correlation (NCC)

import time, sensor, image
from image import SEARCH_EX, SEARCH_DS
#从imgae模块引入SEARCH_EX和SEARCH_DS。使用from import仅仅引入SEARCH_EX, 
#SEARCH_DS两个需要的部分,而不把image模块全部引入。

# 重置传感器
sensor.reset()

# 设置传感器
sensor.set_contrast(1)
sensor.set_gainceiling(16)
# Max resolution for template matching with SEARCH_EX is QQVGA
# 模板与SEARCH_EX匹配的最大分辨率是QQVGA
sensor.set_framesize(sensor.QQVGA)
# 你可以设置windowing窗口来减少搜索图片。
#sensor.set_windowing(((640-80)//2, (480-60)//2, 80, 60))
sensor.set_pixformat(sensor.GRAYSCALE)#灰度图像。

# 加载模板。
# 模板应该是一个小的(例如。32x32像素)灰度图像。
template = image.Image("/1.pgm")

clock = time.clock()

#运行模板匹配
while (True):
    clock.tick()
    img = sensor.snapshot()
      r = img.find_template(template, 0.70,[roi=(10, 0, 60, 60),step=4, search=SEARCH_EX]) 

    # find_template(template, threshold, [roi, step, search])
    # ROI: 感兴趣区域元组 (x, y, w, h).
    # Step:使用的循环步长(y+= Step, x+= Step) 使用更大的步长使其更快。
    # search 为image.SEARCH_EX进行详尽搜索,或者为image.SEARCH_DS进行菱形搜索
    # ROI必须比图像小,比模板大。
    # 在菱形diamond搜索中,step和ROI都被忽略。 
    # find_template(template, threshold, [roi, step, search]),
    # threshold中的0.7是相似度阈值,roi是进行匹配的区域(左上顶点为(10,0),长80宽60的矩形),
    # 注意roi的大小要比模板图片大,比frambuffer小。
    # 把匹配到的图像标记出来
    if r:
        img.draw_rectangle(r)
        print("匹配")

    print(clock.fps())

多模板匹配

import time, sensor, image
from image import SEARCH_EX, SEARCH_DS
#从imgae模块引入SEARCH_EX和SEARCH_DS。使用from import仅仅引入SEARCH_EX,
#SEARCH_DS两个需要的部分,而不把image模块全部引入。

sensor.reset() #初始化传感器(摄像头)
# 设置传感器
sensor.set_contrast(1)
sensor.set_gainceiling(16)
sensor.set_framesize(sensor.QQVGA) #分辨率,用LCD屏幕的话需要设为LCD。SEARCH_EX 最大用 QQVGA
sensor.set_pixformat(sensor.GRAYSCALE) #照片模式,灰度图像方式
sensor.skip_frames(time = 200) #延时跳过一些帧,等待感光元件变稳定。

templates1 = ["/300.pgm","/301.pgm","/302.pgm","/303.pgm","/310.pgm","/311.pgm","/312.pgm","/313.pgm","/320.pgm","/321.pgm","/322.pgm","/323.pgm"] #保存三角形多个模板
templates2 = ["/100.pgm","/101.pgm","/102.pgm","/103.pgm","/110.pgm","/111.pgm","/112.pgm","/113.pgm","/120.pgm","/121.pgm","/122.pgm","/123.pgm"] #保存矩形多个模板
templates3 = ["/000.pgm","/001.pgm","/002.pgm","/003.pgm","/010.pgm","/011.pgm","/012.pgm","/013.pgm","/020.pgm","/021.pgm","/022.pgm","/023.pgm"] #保存圆多个模板


clock = time.clock() #跟踪FPS帧率

#运行模板匹配
while (True):
    clock.tick() # 追踪两个snapshots()之间经过的毫秒数.
    img = sensor.snapshot().lens_corr(strength = 1.8, zoom = 1.0) #去畸变
    
    #初始化计数常量
    n1 = 0
    n2 = 0
    n3 = 0
    
    for t in templates1:  #如果与模板匹配    
        template = image.Image(t) #template获取图片
        r = img.find_template(template, 0.70, step=4, search=SEARCH_EX) #进行相关设置,可以设置roi缩小区域
        if r: #如果有目标
            img.draw_rectangle(r) #画矩形,框出匹配的目标
            n1 = n1 + 1

    for t in templates2:        
        template = image.Image(t)
        r = img.find_template(template, 0.70, step=4, search=SEARCH_EX) #, roi=(10, 0, 60, 60))
        if r:
            img.draw_rectangle(r)
            n2 = n2 + 1

    for t in templates3:
        template = image.Image(t)
        r = img.find_template(template, 0.70, step=4, search=SEARCH_EX) #, roi=(10, 0, 60, 60))
        if r:
            img.draw_rectangle(r)
            n3 = n3 + 1
        
        #打印模板名字
        tuple([n1,n2,n3])
        tu = tuple([n1,n2,n3])
        max_value = max(tu) #获取最大匹配度的模板
        max_value_index = tu.index(max_value) #获取最大匹配度的模板下标
        if max_value_index == 0:
            print("三角形")
        if max_value_index ==1 :
            print("矩形")
        if max_value_index == 2:
            print("圆形")

        print('模板元组{}中最大值为:{},下标为:{}'.format(tu,max_value,max_value_index))

print(clock.fps())

特征点检测

模板匹配:只能匹配与模板大小和角度基本一致的图案,局限性大,适用于摄像头与目标图像距离相对固定,比如流水线上物体检测,而不适用小车追踪一个运动的排球,可通过保存多角度多大小模板,进行多模板匹配。

特征点检测:提取最开始的图像作为目标物体特征,kpts1保存目标物体的特征。默认会匹配目标特征的多种比例大小和角度,而不仅仅是保存目标特征时的大小角度,比模版匹配灵活,也不需要像多模板匹配一样保存多个模板图像。也可以提前保存目标特征,之前是不推荐这么做的,因为环境光线等原因的干扰,可能导致每次运行程序光线不同特征不同,匹配度会降低。但是最新版本的固件中,增加了对曝光度、白平衡、自动增益值的调节,可以人为的定义曝光值和白平衡值,相对来说会减弱光线的干扰。也可以尝试提前保存目标特征。

函数、注意

利用 FAST/AGAST 算法进行特征提取,并且进行目标追踪,仅支持灰度图。

                     image.find_keypoints()

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

                image.match_desscriptor() 

image.match_descriptor(kpts1, kpts2, threshold=85)
  #image.match_descriptor(descritor0, descriptor1, threshold=70, filter_outliers=False)。

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

特征点检测时的思路

1.定义一个存储目标物体的特征的空变量1

2.开始运行程序后,先进行一次查找特征点,此时要开启多种比例大小的查找(normalized=False),将返回值赋给空变量1,相当于识别存储了目标特征

3.再次进行一次查找特征点,此时只要匹配目标特征的一种大小,将返回值存储,用于与先前存储的目标特征进行接下来的比对

4.进行特征比对,匹配成功就画出十字和矩形框

示例

本例程会把程序运行最开始的十秒左右出现的物体作为目标特征,请在程序运行的最开始,将目标物体放在摄像头中央识别,直至出现特征角点,证明已经识别记录目标特征。

匹配过程中,如果画面出现十字和矩形框,证明匹配成功。

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

# Reset sensor
sensor.reset()
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)

#画出特征点
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保存目标物体的特征
        #默认会匹配目标特征的多种比例大小,而不仅仅是保存目标特征时的大小,比模版匹配灵活。
        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特征点检测,这个阈值大概是60~80。阈值越低,获得的角点越多。
        #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阈值设置匹配的准确度,用来过滤掉有歧义的匹配。这个值越小,准确度越高。阈值范围0~100,默认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()))

用openmv单目摄像头测距

公式

众所周知,乒乓球距离摄像头越远,摄像头里乒乓球的大小就越小。

公式:

等号左边的Lm是长度,Bpix是摄像头中球所占的像素(直径的像素)。等号右边,Rm是球真实的半径,Apix是是固定的像素,a是视角的一半。

这个公式告诉我们的就是:

实际长度和摄像头里的像素成反比

简化就是

距离 = 一个常数/直径的像素

 得出常数:先让球距离摄像头10cm,打印出摄像头里直径的像素值,然后相乘,就得到了k的值!

示例代码

# Measure the distance
#
# This example shows off how to measure the distance through the size in imgage
# This example in particular looks for yellow pingpong ball.

import sensor, image, time

# For color tracking to work really well you should ideally be in a very, very,
# very, controlled enviroment where the lighting is constant...
yellow_threshold   = ( 56,   83,    5,   57,   63,   80)
# You may need to tweak the above settings for tracking green things...
# Select an area in the Framebuffer to copy the color settings.

sensor.reset() # Initialize the camera sensor.
sensor.set_pixformat(sensor.RGB565) # use RGB565.
sensor.set_framesize(sensor.QQVGA) # use QQVGA for speed.
sensor.skip_frames(10) # Let new settings take affect.
sensor.set_auto_whitebal(False) # turn this off.
clock = time.clock() # Tracks FPS.

K=5000#the value should be measured

while(True):
    clock.tick() # Track elapsed milliseconds between snapshots().
    img = sensor.snapshot() # Take a picture and return the image.

    blobs = img.find_blobs([yellow_threshold])
    if len(blobs) == 1:
        # Draw a rect around the blob.
        b = blobs[0]
        img.draw_rectangle(b[0:4]) # rect
        img.draw_cross(b[5], b[6]) # cx, cy
        Lm = (b[2]+b[3])/2
        length = K/Lm
        print(length)

    #print(clock.fps()) # Note: Your OpenMV Cam runs about half as fast while
    # connected to your computer. The FPS should increase once disconnected.

扫码识别

QR码

image.find_qrcodes()
返回值:code
corners()
返回对象的 4 个角的 4 个 (x,y) 元组的列表。角始终按顺时针顺序返回,从左上角开始。
rect()
返回一个矩形元组 (x, y, w, h),可用于其他 image 方法,如 Image.draw_rectangle(),表示 qrcode 的边界框。
x()
返回 qrcode 的边界框 x 坐标(int)。
您也可以对该对象执行 [0] 来获取此值。
y()
返回 qrcode 的边界框 y 坐标(int)。
您也可以对该对象执行 [1] 来获取此值。
w()
返回 qrcode 的边界框 w 坐标(int)。
您也可以对该对象执行 [2] 来获取此值。
h()
返回 qrcode 的边界框 h 坐标(int)。
您也可以对该对象执行 [3] 来获取此值。
payload()
返回 qrcode 的有效载荷字符串。例如,URL。
您也可以对该对象执行 [4] 来获取此值。
version()
返回 qrcode 的版本(int)。
您也可以对该对象执行 [5] 来获取此值。
ecc_level()
返回 qrcode 的错误级别( int)。
您也可以对该对象执行 [6] 来获取此值。
mask()
返回到此qrcode 的掩码(int)。
您也可以对该对象执行 [7] 来获取此值。
data_type()
返回到此 qrcode 的 数据类型(int)。
您也可以对该对象执行 [8] 来获取此值。
eci()
返回 QR 码的 eci(int)。eci 存储了 QR 码中数据字节的编码方式。如果您计划处理包含非标准 ASCII 文本的 QR 码,您需要查看此值。
您也可以对该对象执行 [9] 来获取此值。
is_numeric()
如果 QR 码的数据类型为数字,则返回 True。
is_alphanumeric()
如果 QR 码的数据类型为字母数字,则返回 True。
is_binary()
如果 QR 码的数据类型为二进制,则返回 True。如果您严肃对待处理所有类型的文本,需要检查 eci 是否为 True,以确定数据的文本编码。通常情况下,它只是标准的 ASCII,但它也可能是 UTF8,其中包含一些 2 字节的字符。
is_kanji()
如果 QR 码的数据类型为日文(Kanji),则返回 True。如果是 True,则您需要自行解码字符串,因为日文符号每个字符有 10 位,而 MicroPython 没有支持解析这种类型文本的功能。在这种情况下,负载必须被视为一个大字节数组。

示例

草料二维码生成器该网站生成想要内容的二维码

import image,sensor

sensor.reset()
sensor.set_pixformat(sensor.RGB565)
sensor.set_framesize(sensor.QQVGA)
sensor.skip_frames(30)
sensor.set_auto_gain(False)
sensor.set_auto_whitebal(False)
while(True):
    img=sensor.snapshot()
    img.lens_corr(1.8)
    #用于遍历img.find_qrcodes()函数返回的二维码对象列表。img.find_qrcodes是OpenMV用来检测图像中二维码的函数。
    for code in img.find_qrcodes():
        img.draw_rectangle(code.rect(),color=(255,0,0)) #返回二维码的边界框,即二维码在图像中的位置和大小。
        print(code.payload())
        #调用code.payload()方法,打印出检测到的二维码中包含的数据 即二维码的payload payload是二维码编码的信息。

运行结果

条形码

免费在线条码生成器: Code-128,生成条形码网站。

image.find_barcodes()

image.find_barcodes()
corners()
返回对象的 4 个角的 4 个 (x,y) 元组的列表。角始终按顺时针顺序返回,从左上角开始。
rect()
返回一个矩形元组 (x, y, w, h),可用于其他 image 方法,如 Image.draw_rectangle(),表示 barcode 的边界框。
x()
返回 barcode 的边界框 x 坐标(int)。
也可以对该对象执行 [0] 来获取此值。
y()
返回 barcode 的边界框 y 坐标(int)。
也可以对该对象执行 [1] 来获取此值。
w()
返回 barcode 的边界框 w 坐标(int)。
您也可以对该对象执行 [2] 来获取此值。
h()
返回 barcode 的边界框 h 坐标(int)。
您也可以对该对象执行 [3] 来获取此值。
payload()
返回 barcode 的有效负载字符串。例如,数字。
您也可以对该对象执行 [4] 来获取此值。
type()¶
返回 barcode 的类型枚举(int)。
您也可以对该对象执行 [5] 来获取此值。
"""
image.EAN2
image.EAN5
image.EAN8
image.UPCE
image.ISBN10
image.UPCA
image.EAN13
image.ISBN13
image.I25
image.DATABAR
image.DATABAR_EXP
image.CODABAR
image.CODE39
image.PDF417 - 未来(例如,当前不起作用)。
image.CODE93
image.CODE128
"""
rotation()
返回 barcode 的弧度旋转角度(float)。
您也可以对该对象执行 [6] 来获取此值。
quality()
返回此 barcode 在图像中被检测到的次数(int)。
扫描条形码时,每次新的扫描线都可以解码相同的条形码。对于一个条形码来说,每次这样的情况发生,此值都会递增…
您也可以对该对象执行 [7] 来获取此值。

示例代码

import image,sensor,time,math

sensor.reset()
sensor.set_pixformat(sensor.GRAYSCALE)
sensor.set_framesize(sensor.VGA)
sensor.set_windowing((640,80))
sensor.skip_frames(30)
sensor.set_auto_gain(False)
sensor.set_auto_whitebal(False)
clock=time.clock()

def bordcard_name(code):
        if(code.type() == image.EAN2):
            return "EAN2"
        if(code.type() == image.EAN5):
            return "EAN5"
        if(code.type() == image.EAN8):
            return "EAN8"
        if(code.type() == image.UPCE):
            return "UPCE"
        if(code.type() == image.ISBN10):
            return "ISBN10"
        if(code.type() == image.UPCA):
            return "UPCA"
        if(code.type() == image.EAN13):
            return "EAN13"
        if(code.type() == image.ISBN13):
            return "ISBN13"
        if(code.type() == image.I25):
            return "I25"
        if(code.type() == image.DATABAR):
            return "DATABAR"
        if(code.type() == image.DATABAR_EXP):
            return "DATABAR_EXP"
        if(code.type() == image.CODABAR):
            return "CODABAR"
        if(code.type() == image.CODE39):
            return "CODE39"
        if(code.type() == image.PDF417):
            return "PDF417"
        if(code.type() == image.CODE93):
            return "CODE93"
        if(code.type() == image.CODE128):
            return "CODE128"

while(True):
    clock.tick()
    img=sensor.snapshot()
    img.lens_corr(1.8)
    codes=img.find_barcodes()
    for code in codes:
        img.draw_rectangle(code.rect())
        print_args=(bordcard_name(code),code.payload(),(180*code.rotation())/math.pi,code.quality(),clock.fps())
        print("Barcode %s, Payload \"%s\", rotation %f (degrees), quality %d, FPS %f" % print_args)
    if not codes:
        print("FPS %f" % clock.fps())

程序现象

颜色形状的同时识别

首先我们进行圆形识别,然后在识别到的圆形区域内进行颜色统计,判断区域内最多的颜色是否是红色。 

识别圆形需要的库函数

iamge.find_circles([roi[, x_stride=2[, y_stride=1[, threshold=2000[, x_margin=10[, y_margin=10[, r_margin=10[, r_min=2[, r_max[, r_step=2]]]]]]]]]])

使用霍夫变换在图像中查找圆。返回 image.circle 对象的列表。

roi 是感兴趣区域的矩形元组(x, y, w, h)。如果未指定,则等于图像矩形。仅操作 roi 内的像素。

x_stride 是在进行霍夫变换时要跳过的x像素数。仅当要搜索的圆较大且笨重时才增加此值。

y_stride 是在进行霍夫变换时要跳过的y像素数。仅当要搜索的圆较大且笨重时才增加此值。

threshold 控制从霍夫变换中检测到的圆。仅返回幅度大于或等于 threshold 的圆。对于您的应用程序,threshold 的正确值取决于图像。请注意,圆的幅度是由构成该圆的所有sobel滤波器幅度的像素的总和。

x_margin 控制检测到的圆的合并。相隔 x_margin 、 y_margin 和 r_margin 像素的圆将被合并。

y_margin 控制检测到的圆的合并。相隔 x_margin 、 y_margin 和 r_margin 像素的圆将被合并。

r_margin 控制检测到的圆的合并。相隔 x_margin 、 y_margin 和 r_margin 像素的圆将被合并。

r_min 控制检测到的最小圆半径。增加此值以加快算法。默认为2。

r_max 控制检测到的最大圆半径。减小此值以加快算法。默认为min(roi.w/2, roi.h/2)。

r_step 控制半径检测的步长。默认为2。

代码示例

import sensor, image, time

sensor.reset()
sensor.set_pixformat(sensor.RGB565)
sensor.set_framesize(sensor.QQVGA)
sensor.skip_frames(time = 2000)
sensor.set_auto_gain(False) # must be turned off for color tracking
sensor.set_auto_whitebal(False) # must be turned off for color tracking
clock = time.clock()

while(True):
    clock.tick()
    img = sensor.snapshot().lens_corr(1.8)
    for c in img.find_circles(threshold = 3500, x_margin = 10, y_margin = 10, r_margin = 10,
            r_min = 2, r_max = 100, r_step = 2):
        area = (c.x()-c.r(), c.y()-c.r(), 2*c.r(), 2*c.r())
        #area为识别到的圆的区域,即圆的外接矩形框
        statistics = img.get_statistics(roi=area)#像素颜色统计
        print(statistics)
        #(0,100,0,120,0,120)是红色的阈值,所以当区域内的众数(也就是最多的颜色),范围在这个阈值内,就说明是红色的圆。
        #l_mode(),a_mode(),b_mode()是L通道,A通道,B通道的众数。
        if 0<statistics.l_mode()<100 and 0<statistics.a_mode()<127 and 0<statistics.b_mode()<127:#if the circle is red
            img.draw_circle(c.x(), c.y(), c.r(), color = (255, 0, 0))#识别到的红色圆形用红色的圆框出来
        else:
            img.draw_rectangle(area, color = (255, 255, 255))
            #将非红色的圆用白色的矩形框出来
    print("FPS %f" % clock.fps())

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值