人若无名 便可潜心练剑.|

hazy1k

园龄:7个月粉丝:14关注:0

2025-02-20 10:00阅读: 10评论: 0推荐: 0

第17章 特征检测

第七章 特征检测

1. 线段检测

线段检测的基本思想有两个:

  1. 空间变换:霍夫变换中,会将图像边缘像素映射到一个极坐标或其它累加空间,并在累加空间中找极大值点对应的直线或线段。
  2. 后续合并:如果检出很多近似平行并且重叠度较高的线段,会做一个“合并距离”与“角度差”判断,把它们合并成一条线段。在庐山派中就是体现为两个参数,merge_distancemax_theta_difference

1.1 find_line_segments

image.find_line_segments(roi, merge_distance=0, max_theta_difference=15)

该函数使用霍夫变换查找图像中的线段,并返回一个 image.line 对象的列表。

  • roi 为感兴趣区域的矩形元组 (x, y, w, h)。若未指定,ROI 默认为整个图像的矩形。操作仅限于该区域内的像素。
  • merge_distance 指定两条线段之间的最大像素距离,若小于该值则合并为一条线段。
  • max_theta_difference 为需合并的两条线段之间的最大角度差。

该方法使用 LSD 库(OpenCV 亦采用)来查找图像中的线段。虽然速度较慢,但准确性高,且线段不会出现跳跃现象。

1.2 使用示例

import time, os, sys
from media.sensor import *
from media.display import *
from media.media import *

sensor_id = 2
sensor = None
# 处理尺寸
picture_width = 400
picture_height = 240

DISPLAY_MODE = "LCD"

# 根据模式设置显示宽高
if DISPLAY_MODE == "VIRT":
    # 虚拟显示器模式
    DISPLAY_WIDTH = ALIGN_UP(1920, 16)
    DISPLAY_HEIGHT = 1080
elif DISPLAY_MODE == "LCD":
    # 3.1寸屏幕模式
    DISPLAY_WIDTH = 800
    DISPLAY_HEIGHT = 480
elif DISPLAY_MODE == "HDMI":
    # HDMI扩展板模式
    DISPLAY_WIDTH = 1920
    DISPLAY_HEIGHT = 1080
else:
    raise ValueError("未知的 DISPLAY_MODE,请选择 'VIRT', 'LCD' 或 'HDMI'")

try:
    sensor = Sensor(id=sensor_id,width=1920, height=1080)
    sensor.reset()

    sensor.set_framesize(width=picture_width, height=picture_height, chn=CAM_CHN_ID_0)
    sensor.set_pixformat(Sensor.RGB565, chn=CAM_CHN_ID_0)

    # 根据模式初始化显示器
    if DISPLAY_MODE == "VIRT":
        Display.init(Display.VIRT, width=DISPLAY_WIDTH, height=DISPLAY_HEIGHT, fps=60)
    elif DISPLAY_MODE == "LCD":
        Display.init(Display.ST7701, width=DISPLAY_WIDTH, height=DISPLAY_HEIGHT, to_ide=True)
    elif DISPLAY_MODE == "HDMI":
        Display.init(Display.LT9611, width=DISPLAY_WIDTH, height=DISPLAY_HEIGHT, to_ide=True)

    MediaManager.init()
    sensor.run()

    while True:
        os.exitpoint()
        # 捕获通道0的图像
        img = sensor.snapshot(chn=CAM_CHN_ID_0)
        # 查找线段(LSD算法)
        lines = img.find_line_segments(merge_distance=5, max_theta_diff=10)
        count = 0
        print("----------线段统计开始----------")
        for line in lines:
            img.draw_line(line.line(), color=(1, 147, 230), thickness=3) # 画线
            print(f"Line {count}: {line}")
            count += 1
        print("----------线段统计结束----------")

        Display.show_image(img, x=int((DISPLAY_WIDTH-picture_width)/2),y=int((DISPLAY_HEIGHT-picture_height)/2))

except KeyboardInterrupt as e:
    print("用户停止: ", e)
except BaseException as e:
    print(f"异常: {e}")
finally:
    if isinstance(sensor, Sensor):
        sensor.stop()
    # 反初始化显示模块
    Display.deinit()
    os.exitpoint(os.EXITPOINT_ENABLE_SLEEP)
    time.sleep_ms(100)
    MediaManager.deinit()

2. 矩形检测

矩形检测一般用于条码检测,可以先定位条码或者二维码,然后再去解码,提高运行速度。也常用来OCR的前置检测,先定位文档中的文字块或段落。CanMV固件中的find_rects函数使用了与 AprilTag 中的四边形检测类似的算法,能适应一定程度的平移、旋转和仿射变化(扭曲)。

2.1 find_rects

image.find_rects([roi=Auto, threshold=10000])

此函数使用与 AprilTag 相同的四边形检测算法查找图像中的矩形。该算法最适用于与背景形成鲜明对比的矩形。AprilTag 的四边形检测能够处理任意缩放、旋转和剪切的矩形,并返回一个包含 image.rect 对象的列表。

  • roi 是用于指定感兴趣区域的矩形元组 (x, y, w, h)。若未指定,ROI 默认为整个图像。操作范围仅限于该区域内的像素。

在返回的矩形列表中,边界大小(通过在矩形边缘的所有像素上滑动索贝尔算子并累加其值)小于 threshold 的矩形将被过滤。适当的 threshold 值取决于具体的应用场景。

注意: 不支持压缩图像和 Bayer 图像。

2.2 使用示例

import time, os, sys
from media.sensor import *
from media.display import *
from media.media import *

sensor_id = 2
sensor = None
# 处理尺寸
picture_width = 400
picture_height = 240

DISPLAY_MODE = "LCD"

# 根据模式设置显示宽高
if DISPLAY_MODE == "VIRT":
    # 虚拟显示器模式
    DISPLAY_WIDTH = ALIGN_UP(1920, 16)
    DISPLAY_HEIGHT = 1080
elif DISPLAY_MODE == "LCD":
    # 3.1寸屏幕模式
    DISPLAY_WIDTH = 800
    DISPLAY_HEIGHT = 480
elif DISPLAY_MODE == "HDMI":
    # HDMI扩展板模式
    DISPLAY_WIDTH = 1920
    DISPLAY_HEIGHT = 1080
else:
    raise ValueError("未知的 DISPLAY_MODE,请选择 'VIRT', 'LCD' 或 'HDMI'")

try:
    sensor = Sensor(id=sensor_id,width=1920, height=1080)
    sensor.reset()

    sensor.set_framesize(width=picture_width, height=picture_height, chn=CAM_CHN_ID_0)
    sensor.set_pixformat(Sensor.RGB565, chn=CAM_CHN_ID_0)

    # 根据模式初始化显示器
    if DISPLAY_MODE == "VIRT":
        Display.init(Display.VIRT, width=DISPLAY_WIDTH, height=DISPLAY_HEIGHT, fps=60)
    elif DISPLAY_MODE == "LCD":
        Display.init(Display.ST7701, width=DISPLAY_WIDTH, height=DISPLAY_HEIGHT, to_ide=True)
    elif DISPLAY_MODE == "HDMI":
        Display.init(Display.LT9611, width=DISPLAY_WIDTH, height=DISPLAY_HEIGHT, to_ide=True)

    MediaManager.init()
    sensor.run()

    while True:
        os.exitpoint()
        # 捕获通道0的图像
        img = sensor.snapshot(chn=CAM_CHN_ID_0)
        # 查找矩形
        rects = img.find_rects(threshold=5000)
        count = 0
        print("----------统计开始----------")
        for rect in rects:
            img.draw_rectangle(rect.rect(), color=(1, 147, 230), thickness=3) # 画矩形
            print(f"Rect {count}: {rect}")
            count += 1
        print("----------统计结束----------")

        Display.show_image(img, x=int((DISPLAY_WIDTH-picture_width)/2),y=int((DISPLAY_HEIGHT-picture_height)/2))

except KeyboardInterrupt as e:
    print("用户停止: ", e)
except BaseException as e:
    print(f"异常: {e}")
finally:
    if isinstance(sensor, Sensor):
        sensor.stop()
    # 反初始化显示模块
    Display.deinit()
    os.exitpoint(os.EXITPOINT_ENABLE_SLEEP)
    time.sleep_ms(100)
    MediaManager.deinit()

3.圆形检测

可以用来快速定位圆心与半径,可以用来检测圆环标记,交通标志等,也可以用于物体检测(如五子棋棋子定位)。

3.1 find_circles

image.find_circles([roi[, x_stride=2[, y_stride=1[, threshold=2000[, x_margin=10[, y_margin=10[, r_margin=10]]]]]]])

该函数使用霍夫变换在图像中查找圆形,并返回一个 image.circle 对象的列表。

  • roi 为感兴趣区域的矩形元组 (x, y, w, h)。若未指定,ROI 默认为整个图像的矩形。操作仅限于该区域内的像素。
  • x_stride 为霍夫变换过程中需要跳过的 x 像素数量。如果已知圆较大,可增加 x_stride
  • y_stride 为霍夫变换过程中需要跳过的 y 像素数量。如果已知圆较大,可增加 y_stride
  • threshold 控制检测到的圆的大小,仅返回大于或等于该阈值的圆。合适的阈值取决于图像内容。请注意,圆的大小(magnitude)是构成圆的所有索贝尔滤波像素

大小的总和。

  • x_margin 为对 x 坐标进行合并时允许的最大像素偏差。
  • y_margin 为对 y 坐标进行合并时允许的最大像素偏差。
  • r_margin 为对半径进行合并时允许的最大像素偏差。

该方法通过在图像上应用索贝尔滤波器,并利用其幅值和梯度响应执行霍夫变换。无需对图像进行任何预处理,尽管图像的清理和过滤将会产生更为稳定的结果。

注意: 此功能不支持压缩图像和 Bayer 图像。

3.2 使用示例

import time, os, sys
from media.sensor import *
from media.display import *
from media.media import *

sensor_id = 2
sensor = None
# 处理尺寸
picture_width = 400
picture_height = 240

DISPLAY_MODE = "LCD"

# 根据模式设置显示宽高
if DISPLAY_MODE == "VIRT":
    # 虚拟显示器模式
    DISPLAY_WIDTH = ALIGN_UP(1920, 16)
    DISPLAY_HEIGHT = 1080
elif DISPLAY_MODE == "LCD":
    # 3.1寸屏幕模式
    DISPLAY_WIDTH = 800
    DISPLAY_HEIGHT = 480
elif DISPLAY_MODE == "HDMI":
    # HDMI扩展板模式
    DISPLAY_WIDTH = 1920
    DISPLAY_HEIGHT = 1080
else:
    raise ValueError("未知的 DISPLAY_MODE,请选择 'VIRT', 'LCD' 或 'HDMI'")

try:
    sensor = Sensor(id=sensor_id,width=1920, height=1080)
    sensor.reset()

    sensor.set_framesize(width=picture_width, height=picture_height, chn=CAM_CHN_ID_0)
    sensor.set_pixformat(Sensor.RGB565, chn=CAM_CHN_ID_0)

    # 根据模式初始化显示器
    if DISPLAY_MODE == "VIRT":
        Display.init(Display.VIRT, width=DISPLAY_WIDTH, height=DISPLAY_HEIGHT, fps=60)
    elif DISPLAY_MODE == "LCD":
        Display.init(Display.ST7701, width=DISPLAY_WIDTH, height=DISPLAY_HEIGHT, to_ide=True)
    elif DISPLAY_MODE == "HDMI":
        Display.init(Display.LT9611, width=DISPLAY_WIDTH, height=DISPLAY_HEIGHT, to_ide=True)

    MediaManager.init()
    sensor.run()

    while True:
        os.exitpoint()
        # 捕获通道0的图像
        img = sensor.snapshot(chn=CAM_CHN_ID_0)
        # 查找圆形
        circles = img.find_circles(threshold=6000)
        count = 0
        print("----------统计开始----------")
        for circle in circles:
            img.draw_circle(circle.circle(), color=(1, 147, 230), thickness=3) # 画矩形
            print(f"Circle {count}: {circle}")
            count += 1
        print("----------统计结束----------")

        Display.show_image(img, x=int((DISPLAY_WIDTH-picture_width)/2),y=int((DISPLAY_HEIGHT-picture_height)/2))

except KeyboardInterrupt as e:
    print("用户停止: ", e)
except BaseException as e:
    print(f"异常: {e}")
finally:
    if isinstance(sensor, Sensor):
        sensor.stop()
    # 反初始化显示模块
    Display.deinit()
    os.exitpoint(os.EXITPOINT_ENABLE_SLEEP)
    time.sleep_ms(100)
    MediaManager.deinit()

本文作者:hazy1k

本文链接:https://www.cnblogs.com/hazy1k/p/18725885

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   hazy1k  阅读(10)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起