【K230 CanMV】图像识别 色块追踪 颜色识别 blobs 全面详解

引言:在计算机视觉和图像处理领域,颜色识别和色块追踪是实现目标检测、物体追踪、智能交互等应用的基础技术之一。通过分析图像中颜色的分布,我们可以识别并跟踪特定的物体或区域,进而为智能系统提供精确的视觉反馈。在嵌入式系统中,图像处理通常受限于硬件资源,因此需要高效且精确的算法来实现实时识别和追踪。
本文将以 K230 CanMV 开发平台为例,深入探讨如何利用其强大的图像处理能力,通过颜色识别技术实现对特定颜色物体的色块追踪。我们将介绍颜色识别函数 find_blobs 的原理与用法,详细讲解如何使用该函数进行图像中的色块追踪,最后提供一个实际案例,帮助读者在实际项目中运用这一技术。

目录

一、颜色识别(find_blobs)概述

二、函数讲解

三、图像色块追踪方法介绍

四、示例模版

五、实际案例


一、颜色识别(find_blobs)概述

find_blobs 是 OpenMV 的图像处理函数,用于在图像中寻找并识别“斑点”(blobs)。这些斑点是指图像中具有相似颜色或亮度的区域。这个函数通常用于视觉检测和识别应用中,例如物体跟踪、颜色识别等。

CanMV支持OpenMV算法,同样可以使用find_blobs

二、函数讲解

blobs = img.find_blobs([thresholds], area_threshold=area_threshold, pixels_threshold=pixels_threshold, merge=True, margin=0)
  • thresholds: 这是一个包含颜色范围的列表,用于定义要查找的斑点的颜色范围。通常是一个包含两个或三个元素的元组。例如,(100, 200, -64, 127, -128, 127) 表示 HSV 颜色空间中的范围,其中第一个和第二个值是 Hue,第三和第四值是 Saturation,最后两个值是 Value。

  • area_threshold: 斑点的面积阈值。只有面积大于这个值的斑点才会被返回。默认值是 0。

  • pixels_threshold: 斑点的像素数阈值。只有包含的像素数大于这个值的斑点才会被返回。默认值是 0。

  • merge: 是否合并相邻的斑点。设为 True 时,相邻的斑点会被合并成一个大斑点;设为 False 时,斑点不会合并。默认值是 True

  • margin: 用于合并斑点时的边距。设置为一个正整数,表示合并斑点时的最大距离。默认值是 0。

返回值:

find_blobs 函数返回一个包含斑点信息的列表。每个斑点都是一个 Blob 对象,通常包含以下属性:

  • cx 和 cy:斑点的中心坐标。

  • x 和 y:斑点的左上角坐标。

  • w 和 h:斑点的宽度和高度。

  • area:斑点的面积(像素数)。

三、图像色块追踪方法介绍

image 模块为 Image 对象提供了 find_blobs()方法,用于查找图像中的所有色块,find_blobs() 方法如下所示:

image.find_blobs(thresholds, invert=False, roi, x_stride=2, y_stride=1, 
area_threshold=10, pixels_threshold=10, merge=False, margin=0, threshold_cb, 
merge_cb) 

find_blobs() 方法在图像处理中用于根据指定的颜色阈值查找图像中的色块,并返回符合条件的 image.blob 对象列表。色块通常是指在图像中某一特定颜色范围内的连续像素区域。该方法支持对不同颜色空间和图像类型进行处理,允许在一定条件下筛选、合并或过滤色块。

方法参数解释

  1. thresholds
    该参数指定了颜色的阈值,用于定义哪些颜色区域需要被识别。它是一个元组列表,针对不同的图像和颜色空间,具体形式有所不同:

    • 灰度图像:对于灰度图像,thresholds 应该是一个包含两个数值的元组 (min_gray_value, max_gray_value),表示允许的灰度值范围。
    • RGB565 图像:对于 RGB565 格式的图像,thresholds 应该是包含六个值的元组 (min_L, max_L, min_A, max_A, min_B, max_B),这六个值分别表示在 LAB 色彩空间下的 L、A 和 B 三个通道的最小值和最大值。LAB 色彩空间具有较好的色差感知效果,适用于在不同光照条件下进行色彩追踪。

    例如,若要追踪红色和蓝色的色块,可以在阈值元组列表中分别传入对应的颜色范围。

  2. invert
    该参数控制是否对颜色阈值进行反转操作。如果设置为 True,则追踪的不是在指定阈值内的色块,而是阈值之外的色块。默认为 False,即只关注在阈值范围内的色块。

  3. roi (Region of Interest)
    指定图像中感兴趣的区域。它用于限制图像处理的范围,减少计算量。若未指定该参数,则处理整个图像。

  4. x_stride 和 y_stride
    这些参数用于定义在图像处理中每次跳过多少个像素。若色块较大,增加这些步长可以提高效率,减少计算量。例如,x_stride=2 会让每次在 X 轴方向上跳过 2 个像素,y_stride=2 会在 Y 轴方向上跳过 2 个像素。

  5. area_threshold
    该参数用于过滤掉那些边界框区域小于指定值的色块。设定较大的 area_threshold 可以去除一些过小的色块,以减小检测的噪声。

  6. pixels_threshold
    用于过滤掉那些像素数量少于指定值的色块。该参数通常用于避免误识别一些微小的、无意义的色块。

  7. merge
    该参数控制是否对相互重叠的色块进行合并。如果设置为 True,则会将边缘矩形相互交错且没有被过滤的色块合并成一个色块。若设置为 False,则不合并重叠的色块。

  8. margin
    该参数用于增大或减小检测到的色块边界矩形的大小,通常用于修正边界矩形的形状,确保色块的完整性。

  9. threshold_cb (Threshold callback function)
    这是一个回调函数,作用是在颜色阈值过滤过程中对被过滤的色块进行进一步筛选。该函数会接收到一个 image.blob 对象,返回 True 表示保留该色块,返回 False 则表示过滤掉该色块。

  10. merge_cb (Merge callback function)
    这是一个回调函数,用于决定是否将两个即将合并的色块合并成一个。该函数会接收到两个 image.blob 对象,返回 True 表示合并色块,返回 False 则禁止合并。

方法返回值

find_blobs() 方法返回一个包含 image.blob 对象的列表,每个 image.blob 对象表示一个在图像中找到的色块。每个色块包含以下信息:

  • 色块的边界框位置和大小
  • 色块的质心坐标
  • 色块内像素的颜色值信息
import image 
 
img = image.Image(size=(320, 240)) 
threshold = (8, 63, 8, 49, -77, -36) 
blobs = img.find_blobs([threshold], False, (0, 0, img.width(), img.height()), 
x_stride=2, y_stride=1, area_threshold=10, pixels_threshold=10, merge=True, 
margin=10) 
for b in blobs: 
 img.draw_rectangle(b.rect(), color=(255, 0, 0)) 

关于颜色阈值的设定,CanMV IDE 软件提供了“阈值编辑器”工具,可以方便地获取到想 要的阈值。通过依次点击 CanMV IDE 软件上方工具栏中的“工具”→“机器视觉”→“阈值编 辑器”,即可打开“阈值编辑器”工具,打开时会提示需要导入一张源图像,可以直接使用“帧 缓冲区”窗口中的图像,也可以从本地文件系统中选取,如下图所示:

四、示例模版

这里只列举一个单独的颜色追踪例子,具体demo还请查看固件自带虚拟U盘中的例程

  • 初始化:导入库,设置摄像头和显示参数,初始化传感器和媒体管理器。
  • 循环获取图像:通过传感器不断获取图像帧。
  • 色块检测:通过 find_blobs 函数检测符合阈值条件的色块,并根据颜色编码进一步筛选。
  • 绘制标识:对符合条件的色块进行绘制,如矩形框、十字标记、旋转角度等。
  • 显示图像:将处理后的图像显示在屏幕上。
  • 性能监控:输出帧率,监控程序性能。
  • 清理资源:程序退出时清理传感器和显示资源。
# 单色代码跟踪示例
#
# 这个示例展示了使用 CanMV 相机进行单色代码跟踪。
#
# 颜色代码是由两种或更多颜色组成的斑点。下面的示例将
# 仅跟踪包含以下颜色的彩色物体。
import time, os, gc, sys, math

from media.sensor import *
from media.display import *
from media.media import *

DETECT_WIDTH = 640
DETECT_HEIGHT = 480

# 颜色跟踪阈值 (L Min, L Max, A Min, A Max, B Min, B Max)
# 以下阈值通常用于跟踪红色/绿色物体。您可能需要调整它们...
thresholds = [(12, 100, -47, 14, -1, 58), # 通用红色阈值 -> 索引为 0,所以代码 == (1 << 0)
              (30, 100, -64, -8, -32, 32)] # 通用绿色阈值 -> 索引为 1,所以代码 == (1 << 1)
# 当 "merge=True" 时,代码会被“或”在一起以进行 "find_blobs"

# 只有那些像素数量超过 "pixel_threshold" 且面积超过 "area_threshold" 的斑点
# 才会被下面的 "find_blobs" 返回。如果更改相机分辨率,请更改 "pixel_threshold" 和 "area_threshold"。
# 必须设置 "merge=True" 以合并重叠的颜色斑点以形成颜色代码。

sensor = None

try:
    # 使用默认配置构造一个Sensor对象
    sensor = Sensor(width = DETECT_WIDTH, height = DETECT_HEIGHT)
    # sensor复位
    sensor.reset()
    # 设置水平镜像
    # sensor.set_hmirror(False)
    # 设置垂直翻转
    # sensor.set_vflip(False)
    # 设置通道 0 输出大小
    sensor.set_framesize(width = DETECT_WIDTH, height = DETECT_HEIGHT)
    # 设置通道 0 输出格式
    sensor.set_pixformat(Sensor.RGB565)

    # 设置显示,如果您选择的屏幕无法点亮,请参考API文档中的K230_CanMV_Display模块API手册自行配置,下面给出四种显示方式
    # 使用 HDMI 作为显示输出,设置为 VGA
    # Display.init(Display.LT9611, width = 640, height = 480, to_ide = True)

    # 使用 HDMI 作为显示输出,设置为 1080P
    # Display.init(Display.LT9611, width = 1920, height = 1080, to_ide = True)

    # 使用 LCD 作为显示输出
    # Display.init(Display.ST7701, to_ide = True)

    # 使用 IDE 作为输出
    Display.init(Display.VIRT, width = DETECT_WIDTH, height = DETECT_HEIGHT, fps = 100)

    # 初始化媒体管理器
    MediaManager.init()
    # sensor开始运行
    sensor.run()

    fps = time.clock()

    while True:
        fps.tick()

        # 检查是否应该退出
        os.exitpoint()
        img = sensor.snapshot()

        for blob in img.find_blobs(thresholds, pixels_threshold=100, area_threshold=100, merge=True):
            if blob.code() == 3: # r/g 代码 == (1 << 1) | (1 << 0)
                # 这些值依赖于斑点不是圆形的 - 否则它们会不稳定
                # if blob.elongation() > 0.5:
                #     img.draw_edges(blob.min_corners(), color=(255,0,0))
                #     img.draw_line(blob.major_axis_line(), color=(0,255,0))
                #     img.draw_line(blob.minor_axis_line(), color=(0,0,255))
                # 这些值在任何时候都是稳定的
                img.draw_rectangle([v for v in blob.rect()])
                img.draw_cross(blob.cx(), blob.cy())
                # 注意 - 斑点的旋转是唯一的,仅限于 0-180
                img.draw_keypoints([(blob.cx(), blob.cy(), int(math.degrees(blob.rotation())))], size=20)

        # 将结果绘制到屏幕上
        Display.show_image(img)
        gc.collect()

        print(fps.fps())
except KeyboardInterrupt as e:
    print(f"user stop")
except BaseException as e:
    print(f"Exception '{e}'")
finally:
    # sensor停止运行
    if isinstance(sensor, Sensor):
        sensor.stop()
    # 销毁display
    Display.deinit()

    os.exitpoint(os.EXITPOINT_ENABLE_SLEEP)
    time.sleep_ms(100)

    # 释放媒体缓冲区
    MediaManager.deinit()

这段代码是一个基于色块跟踪的示例,使用 CanMV Cam 摄像头进行目标跟踪。它通过设定颜色阈值来识别图像中的特定颜色区域(色块),并结合色块的属性(如位置、旋转、长宽比等)进行进一步处理。下面将详细解析每个部分的作用和代码流程。
 

  • time:用于处理时间相关的操作,例如计算帧率(fps)。
  • os:用于操作系统相关的功能,例如退出点检查。
  • gc:垃圾回收库,用于管理内存。
  • sys:提供对 Python 运行时环境的访问。
  • math:提供数学函数,特别是 math.degrees() 用于角度转换。

接下来的导入是涉及到图像采集、显示和媒体管理的自定义模块:

  • media.sensor:提供摄像头传感器相关的功能。
  • media.display:处理显示输出的相关功能。
  • media.media:管理媒体相关的资源。
  • 红色阈值:第一个元组 (12, 100, -47, 14, -1, 58) 用于识别红色区域(在 LAB 色彩空间中设置 L, A, B 三个通道的最小值和最大值)。
  • 绿色阈值:第二个元组 (30, 100, -64, -8, -32, 32) 用于识别绿色区域。

每个阈值元组代表 LAB 色彩空间中三个通道的范围,配合 find_blobs() 函数可以找出图像中符合这些颜色范围的色块。

初始化了传感器,设置图像的宽度和高度,并指定图像格式为 RGB565。这意味着每个像素会以 16 位存储,每个通道(红色、绿色、蓝色)分别使用 5 位、6 位和 5 位。

显示被初始化为虚拟显示 (Display.VIRT),并设置分辨率为 640x480,帧率为 100。这里的显示操作是虚拟显示,适合开发和调试时在 IDE 中查看输出。

通过 MediaManager.init() 初始化媒体管理器,接着启动传感器运行。这意味着图像采集开始实时进行。

  • fps.tick() 用于记录帧时间,计算当前帧率。
  • os.exitpoint() 检查是否需要退出程序。
  • sensor.snapshot() 捕获当前图像帧。
  • find_blobs():基于之前定义的 thresholds 参数,查找符合条件的色块。此方法返回一个包含色块的列表。
    • pixels_threshold=100:色块的像素数量必须大于 100。
    • area_threshold=100:色块的面积必须大于 100。
    • merge=True:合并重叠的色块,适用于多个颜色组合。
  • blob.code() == 3:在本示例中,色块的代码为 3,即同时包含红色和绿色,这通过将 1 << 01 << 1 位运算或得到的结果。

色块处理:

  • blob.rect():获取色块的边界矩形,并绘制出来。
  • blob.cx(), blob.cy():获取色块的质心坐标,并在图像上绘制十字。
  • blob.rotation():获取色块的旋转角度,并绘制该角度的键点。
  • Display.show_image(img):将处理后的图像显示在屏幕上。
  • gc.collect():进行垃圾回收,释放不再使用的内存。

通过 fps.fps() 输出当前帧率,用于调试和性能监控。

  • 捕获 KeyboardInterrupt 异常(用户手动停止)以及其他基础异常,并打印出异常信息。
  • finally 块中停止传感器,反初始化显示设备,释放资源并确保应用退出前能进入睡眠状态。

五、实际案例

这段代码是一个基于色块跟踪的示例,旨在通过摄像头捕捉图像并检测特定颜色(红色)的物体。代码利用了 find_blobs 函数来检测图像中的红色区域,并对符合条件的色块进行标记和显示。

 1. 导入必要的库

import time, os, gc, sys, math
from media.sensor import *
from media.display import *
from media.media import *
  • time:用于处理时间相关的功能,如计算帧率。
  • os:提供与操作系统交互的功能,例如检查退出点。
  • gc:垃圾回收库,用于管理内存,避免内存泄漏。
  • sys:提供对Python运行时环境的访问,通常用于调试或获取程序的系统信息。
  • math:提供数学函数,这里使用 math.degrees() 来将弧度转换为度。

接下来的导入是与硬件和显示相关的自定义模块:

  • media.sensor:提供与摄像头传感器相关的功能。
  • media.display:处理图像显示的功能。
  • media.media:用于初始化和管理媒体资源,如缓冲区。

2. 设置摄像头分辨率和颜色阈值

DETECT_WIDTH = 640
DETECT_HEIGHT = 480

# 只跟踪红色物体的阈值 (L Min, L Max, A Min, A Max, B Min, B Max)
red_thresholds = [(30, 68, 9, 127, -12, 73)]  # 通用红色阈值 -> 索引为 0,所以代码 == (1 << 0)
  • DETECT_WIDTHDETECT_HEIGHT 设置图像的分辨率为 640x480,这是大多数视觉应用的标准分辨率。

  • 红色阈值 (red_thresholds): 这个阈值使用的是 LAB 色彩空间中的三个通道 L, A, B 的范围。该阈值定义了一个色块的颜色范围,用于识别红色物体。

    • L (亮度) 最小值 30,最大值 68;
    • A (绿色到红色的色差) 最小值 9,最大值 127;
    • B (蓝色到黄色的色差) 最小值 -12,最大值 73。

    红色的色块会被识别并标记为代码 1(因为索引是 0,所以在 find_blobs() 中,1 << 0 表示红色)。

3. 初始化摄像头和显示器

sensor = None

try:
    # 使用默认配置构造一个Sensor对象
    sensor = Sensor(width=DETECT_WIDTH, height=DETECT_HEIGHT)
    # sensor复位
    sensor.reset()
    # 设置通道 0 输出大小
    sensor.set_framesize(width=DETECT_WIDTH, height=DETECT_HEIGHT)
    # 设置通道 0 输出格式
    sensor.set_pixformat(Sensor.RGB565)
  • 创建一个传感器对象 sensor,并指定其分辨率为 640x480。
  • 调用 sensor.reset() 来重置摄像头设置。
  • sensor.set_framesize() 设置图像输出的分辨率。
  • sensor.set_pixformat(Sensor.RGB565) 设置输出图像格式为 RGB565,每个像素占用 16 位存储(5 位红色,6 位绿色,5 位蓝色)。

4. 设置显示

# 设置显示
Display.init(Display.VIRT, width=DETECT_WIDTH, height=DETECT_HEIGHT, fps=100)

这里使用虚拟显示 (Display.VIRT) 初始化显示屏,并设置分辨率为 640x480,帧率为 100 帧每秒。

5. 初始化媒体管理器和启动传感器

# 初始化媒体管理器
MediaManager.init()
# sensor开始运行
sensor.run()
  • MediaManager.init() 初始化媒体管理器,管理摄像头捕捉的图像和相关的缓存。
  • sensor.run() 启动摄像头的图像捕捉过程。

6. 主循环:图像捕捉、色块检测和显示

fps = time.clock()

while True:
    fps.tick()
    # 检查是否应该退出
    os.exitpoint()
    img = sensor.snapshot()
  • fps.tick():记录并更新帧时间,用于计算实时帧率。
  • os.exitpoint():检查是否应退出程序。
  • sensor.snapshot():捕获一帧图像。

7. 查找红色物体

for blob in img.find_blobs(red_thresholds, pixels_threshold=100, area_threshold=100, merge=True):
    if blob.code() == 1:  # 代码为 1 表示仅红色
        # 绘制矩形框
        img.draw_rectangle([v for v in blob.rect()])
        # 绘制十字准星
        img.draw_cross(blob.cx(), blob.cy())
        # 绘制旋转角度
        img.draw_keypoints([(blob.cx(), blob.cy(), int(math.degrees(blob.rotation())))], size=20)
  • find_blobs(red_thresholds, pixels_threshold=100, area_threshold=100, merge=True)

    • red_thresholds:提供红色物体的颜色范围。
    • pixels_threshold=100:只有像素数量超过 100 的色块才会被返回。
    • area_threshold=100:只有面积大于 100 的色块才会被返回。
    • merge=True:如果设置为 True,会合并相互重叠的色块。
  • if blob.code() == 1:检查该色块是否是红色,红色色块的代码为 1,即 1 << 0

绘制操作:

  • blob.rect():获取色块的矩形边界并绘制。
  • blob.cx(), blob.cy():获取色块的质心坐标,并绘制十字准星。
  • blob.rotation():获取色块的旋转角度,使用 math.degrees() 将其从弧度转换为度数,并绘制旋转角度的标记。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

7yewh

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值