[MCU] 【嘉楠科技 CanMV K230测评】机器视觉进阶-Find something(边缘、直线、圆形、矩形)

王嘉辉   2024-10-13 22:07 楼主

前面介绍了基于K230相关外设以及机器视觉相关的基础,像是摄像头的使用以及图像的显示和画图相关的内容。下面会逐渐开始机器视觉真正的内容,循序渐进的进行,首先是常见的边缘检测、直线检测等等,后面跟着例程学习颜色相关、码类识别以及后续的AI相关的内容。

边缘检测

边缘检测使用image.find_edges(edge_type[, threshold])函数,可以选择的边缘检测算法有两种,Simple和Canny两种。其中Canny检测算法更好一些。而后面的高低阈值参数设置,与Canny检测算法中的双阈值确定边界有关。阈值设置较高,则只能检测出较为明显的边缘,阈值设置较低,则可以检测出大部分的边缘。

代码演示如下,其基本内容为图像的采集和显示,主要就是在snapshot和show_image之间插入了find_edges函数。除此之外重要的是,边缘检测是需要图像转换为黑白图像,因此在输出图像格式的位置,应该选择的参数为GRAYSCALE。

import time, os, sys, gc

from media.sensor import * #导入sensor模块,使用摄像头相关接口
from media.display import * #导入display模块,使用display相关接口
from media.media import * #导入media模块,使用meida相关接口

try:

    sensor = Sensor(width=1280, height=960) #构建摄像头对象,将摄像头长宽设置为4:3
    sensor.reset() #复位和初始化摄像头
    sensor.set_framesize(width=320, height=240) #设置帧大小为LCD分辨率(320x240),默认通道0
    sensor.set_pixformat(Sensor.GRAYSCALE) #设置输出图像格式,默认通道0

    Display.init(Display.VIRT, sensor.width(), sensor.height()) #只使用IDE缓冲区显示图像

    MediaManager.init() #初始化media资源管理器

    sensor.run() #启动sensor

    clock = time.clock()

    while True:

        os.exitpoint() #检测IDE中断

        ################
        ## 这里编写代码 ##
        ################
        clock.tick()

        img = sensor.snapshot() #拍摄一张图片

        #使用 Canny 边缘检测器
        img.find_edges(image.EDGE_CANNY, threshold=(10, 20))

        Display.show_image(img) #显示图片

        #显示图片,仅用于LCD居中方式显示
#        Display.show_image(img, x=round((800-sensor.width())/2),y=round((480-sensor.height())/2))


        print(clock.fps()) #打印FPS

###################
# IDE中断释放资源代码
###################
except KeyboardInterrupt as e:
    print("user stop: ", e)
except BaseException as e:
    print(f"Exception {e}")
finally:
    # sensor stop run
    if isinstance(sensor, Sensor):
        sensor.stop()
    # deinit display
    Display.deinit()
    os.exitpoint(os.EXITPOINT_ENABLE_SLEEP)
    time.sleep_ms(100)
    # release media buffer
    MediaManager.deinit()

下面使用同一张素材,对参数中的高低阈值进行更改,观察不同的效果。

素材原图及素材灰度图:

边缘检测原图.png

高阈值200、低阈值100:

边缘检测200-100.png

高阈值20、低阈值10:

边缘检测20-10.png

高阈值200、低阈值10:

边缘检测200-10.png

可以明显的看出,阈值设置为10和20的时候,检测出的边缘数量远大于阈值设置为100和200的时候。可以更加清楚的看到人脸的轮廓。但是对应也产生了更多“噪声”,因此选择一个合适的阈值是十分重要的。

线段检测

线段检测使用的函数为image.find_line_segments([roi[, merge_distance=0[, max_theta_difference=15]]]),其返回值为一个线段对象列表image.line。其中参数roi为识别的区域范围,若无指定则默认是整张图片;merge_distance为两条线段不被合并的最大像素值;max_theta_difference两条线段不被合并的最大角度值。

代码展示如下。

import time, os, sys

from media.sensor import * #导入sensor模块,使用摄像头相关接口
from media.display import * #导入display模块,使用display相关接口
from media.media import * #导入media模块,使用meida相关接口

enable_lens_corr = False # 设为True可以获得更直的线段

try:

    sensor = Sensor(width=1280, height=960) #构建摄像头对象,将摄像头长宽设置为4:3
    sensor.reset() #复位和初始化摄像头
    sensor.set_framesize(width=320, height=240) #设置帧大小,默认通道0
    sensor.set_pixformat(Sensor.RGB565) #设置输出图像格式,默认通道0

#    Display.init(Display.ST7701, to_ide=True) #同时使用3.5寸mipi屏和IDE缓冲区显示图像,800x480分辨率
    Display.init(Display.VIRT,320,240) #只使用IDE缓冲区显示图像

    MediaManager.init() #初始化media资源管理器

    sensor.run() #启动sensor

    clock = time.clock()

    while True:

        os.exitpoint() #检测IDE中断

        ################
        ## 这里编写代码 ##
        ################
        clock.tick()

        img = sensor.snapshot() #拍摄一张图片

        if enable_lens_corr: img.lens_corr(1.8) # for 2.8mm lens...

        # `merge_distance` 控制相近的线段是否合并.  数值 0 (默认值)表示不合并。数值
        #为1时候表示相近1像素的线段被合并。因此你可以通过改变这个参数来控制检测到线
        #段的数量。

        # `max_theta_diff` 控制相差一定角度的线段合并,默认是15度,表示15度内的线
        # 段都会合并

        for l in img.find_line_segments(merge_distance = 5, max_theta_diff = 15):

            img.draw_line(l.line(), color = (255, 0, 0), thickness=2)
            print(l)

        #Display.show_image(img) #显示图片

        #显示图片,仅用于LCD居中方式显示
        Display.show_image(img, x=0,y=0)

        print(clock.fps()) #打印FPS

###################
# IDE中断释放资源代码
###################
except KeyboardInterrupt as e:
    print("user stop: ", e)
except BaseException as e:
    print(f"Exception {e}")
finally:
    # sensor stop run
    if isinstance(sensor, Sensor):
        sensor.stop()
    # deinit display
    Display.deinit()
    os.exitpoint(os.EXITPOINT_ENABLE_SLEEP)
    time.sleep_ms(100)
    # release media buffer
    MediaManager.deinit()

在这个代码中,在拍摄图片和显示图片之间加入了find_line_segments函数。并将返回的数据存储在了l中。然后再将l列表中的信息,画成线表示在图片上,并将l列表的信息打印在串行终端。

下面进行效果的演示。

原图:

直线检测原图.jpg

检测后效果:

直线检测效果.png

可以看到公路上的线段就可以被识别出来,且在左下角的串行终端中进行了线段数据的打印。

圆形检测

圆形检测使用的函数为image.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]]]]]]]]]]),其返回值为一个圆形对象,有四个参数值,分别为圆心的X和Y,半径以及量级(量级数值越大圆的可信度越高)。其中参数roi为识别的区域范围,若无指定则默认是整张图片;threshold为阈值,返回大于或等于量级大于这个数值的圆;x_stride和y_stride为检测过程中跳过的像素量;x_margin 、y_margin、 r_margin为所检测圆的合并;r_min和r_max用于控制识别圆形的半径范围;r_step用于控制识别的步骤。

代码演示。

import time, os, sys

from media.sensor import * #导入sensor模块,使用摄像头相关接口
from media.display import * #导入display模块,使用display相关接口
from media.media import * #导入media模块,使用meida相关接口

try:

    sensor = Sensor(width=1280, height=960) #构建摄像头对象,将摄像头长宽设置为4:3
    sensor.reset() #复位和初始化摄像头
    sensor.set_framesize(width=320, height=240) #设置帧大小,默认通道0
    sensor.set_pixformat(Sensor.RGB565) #设置输出图像格式,默认通道0

    Display.init(Display.VIRT, 320, 240) #只使用IDE缓冲区显示图像

    MediaManager.init() #初始化media资源管理器

    sensor.run() #启动sensor

    clock = time.clock()

    while True:

        os.exitpoint() #检测IDE中断

        ################
        ## 这里编写代码 ##
        ################
        clock.tick()

        img = sensor.snapshot() #拍摄一张图片
        # 圆形类有 4 个参数值: 圆心(x, y), r (半径)和 magnitude(量级);
        # 量级越大说明识别到的圆可信度越高。
        # `threshold` 参数控制找到圆的数量,数值的提升会降低识别圆形的总数。
        # `x_margin`, `y_margin`, and `r_margin`控制检测到接近圆的合并调节.
        # r_min, r_max, and r_step 用于指定测试圆的半径范围。
        for c in img.find_circles(threshold = 1300, x_margin = 10, y_margin= 10,
                                  r_margin = 10,r_min = 5, r_max = 50, r_step = 2):
            #画红色圆做指示
            img.draw_circle(c.x(), c.y(), c.r(), color = (255, 0, 0),thickness=2)

            print(c) #打印圆形的信息

        #Display.show_image(img) #显示图片

        #显示图片,仅用于LCD居中方式显示
        Display.show_image(img, x=0,y=0)

        print(clock.fps()) #打印FPS

###################
# IDE中断释放资源代码
###################
except KeyboardInterrupt as e:
    print("user stop: ", e)
except BaseException as e:
    print(f"Exception {e}")
finally:
    # sensor stop run
    if isinstance(sensor, Sensor):
        sensor.stop()
    # deinit display
    Display.deinit()
    os.exitpoint(os.EXITPOINT_ENABLE_SLEEP)
    time.sleep_ms(100)
    # release media buffer
    MediaManager.deinit()

效果演示。

原图:

圆形检测原图.png

检测后结果展示:

圆形检测效果.png

矩形检测

K230中进行矩形检测的算法采用的是二维码检测中的image.find_rects([roi=Auto, threshold=10000]),其中的参数roi表示的是检测的区域,不配置的话就是默认整张图片。threshold参数用来配置阈值,只有超过这个阈值的矩形,才会被识别并显示出来。使用这个函数同样也会返回一个列表,列表的信息有矩形的起点横纵坐标以及矩形的长度和宽度以及矩形的量级(即前面参数配置中对应的threshold)。

代码:

import time, os, sys

from media.sensor import * #导入sensor模块,使用摄像头相关接口
from media.display import * #导入display模块,使用display相关接口
from media.media import * #导入media模块,使用meida相关接口

try:

    sensor = Sensor(width=1280, height=960) #构建摄像头对象
    sensor.reset() #复位和初始化摄像头
    sensor.set_framesize(width=320, height=240) #设置帧大小为LCD分辨率(800x480),默认通道0
    sensor.set_pixformat(Sensor.RGB565) #设置输出图像格式,默认通道0

    Display.init(Display.ST7701, to_ide=True) #同时使用3.5寸mipi屏和IDE缓冲区显示图像,800x480分辨率
    #Display.init(Display.VIRT, sensor.width(), sensor.height()) #只使用IDE缓冲区显示图像

    MediaManager.init() #初始化media资源管理器

    sensor.run() #启动sensor

    clock = time.clock()

    while True:

        os.exitpoint() #检测IDE中断

        ################
        ## 这里编写代码 ##
        ################
        clock.tick()

        img = sensor.snapshot() #拍摄一张图片

        # `threshold` 需要设置一个比价大的值来过滤掉噪声。
        #这样在图像中检测到边缘亮度较低的矩形。矩形
        #边缘量级越大,对比越强…

        for r in img.find_rects(threshold = 40000):
            img.draw_rectangle(r.rect(), color = (255, 0, 0),thickness=2) #画矩形显示
            for p in r.corners(): img.draw_circle(p[0], p[1], 5, color = (0, 255, 0))#四角画小圆形
            print(r)

        #Display.show_image(img) #显示图片

        #显示图片,仅用于LCD居中方式显示
        Display.show_image(img, x=round((800-sensor.width())/2),y=round((480-sensor.height())/2))

        print(clock.fps()) #打印FPS

###################
# IDE中断释放资源代码
###################
except KeyboardInterrupt as e:
    print("user stop: ", e)
except BaseException as e:
    print(f"Exception {e}")
finally:
    # sensor stop run
    if isinstance(sensor, Sensor):
        sensor.stop()
    # deinit display
    Display.deinit()
    os.exitpoint(os.EXITPOINT_ENABLE_SLEEP)
    time.sleep_ms(100)
    # release media buffer
    MediaManager.deinit()

效果演示:

原图:

矩形检测原图.png

效果图:

矩形检测效果.png

 

回复评论 (3)

所以汽车视觉识别,也有检测图像轮廓是不,感觉这种图更适合计算机。

点赞 (1) 2024-10-14 09:23
引用: wangerxian 发表于 2024-10-14 09:23 所以汽车视觉识别,也有检测图像轮廓是不,感觉这种图更适合计算机。

汽车视觉识别相关的内容,我不是很了解。大概查询了一下关于汽车视觉相关的帖子,发现大部分在网上公开分享的都是基于opencv使用Canny算法进行边缘检测(可能在实际的应用中有更为先进的算法),然后会使用Hough直线检测进行车道的识别。感觉和K230提供的方法差不太多,但是运算的硬件平台可能会更强,相应的算法原理相似,但是会更复杂。这是我一个外行人的认识,要是有懂行的欢迎在评论区解释一下!

点赞  2024-10-14 11:19
引用: 王嘉辉 发表于 2024-10-14 11:19 汽车视觉识别相关的内容,我不是很了解。大概查询了一下关于汽车视觉相关的帖子,发现大部分在网上公开分 ...

公开的一般不是最好的,最好用的基本藏着不公开。

点赞 (1) 2024-10-14 17:58
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 京公网安备 11010802033920号
    写回复