驴友花雕 发表于 昨天 17:30

【花雕动手做】CanMV K230 AI 视觉模块之绘制关键点图框



什么是 CanMV K230?
CanMV K230是一款高性价比的RISC-V边缘AI平台,凭借低功耗、强视觉处理能力和开放的开发生态,成为嵌入式AI开发的理想选择,尤其适合需要快速部署视觉与AI功能的创客、中小企业及教育场景。CanMV 是一套 AI 视觉开发平台,K230 是其核心芯片。该模块结合了图像采集、AI推理、边缘计算等能力,适合嵌入式视觉应用开发。

CanMV:类似 OpenMV 的图像处理框架,支持 Python 编程,简化视觉识别开发流程。
K230 芯片:嘉楠科技推出的 AIoT SoC,采用 RISC-V 架构,内置第三代 KPU(AI加速单元),算力高达 6 TOPS,性能是 K210 的 13.7 倍。



驴友花雕 发表于 昨天 17:37

【花雕动手做】CanMV K230 AI视觉模块之绘制关键点图框

绘制关键点的draw_keypoints方法

1、什么是关键点?
这里的关键点(keypoints)是指图像中的特征点,通常是图像中比较显著或者独特的点,具体来说:

2、特征点的特性:
这些点通常是图像中的角点、边缘交叉点等具有显著特征的位置
它们在图像的不同视角下都容易被识别
这些点的周围像素值变化较大,使其具有独特性
实际应用中的关键点可能是:

3、物体的角点
纹理丰富区域的特征点
图案中的显著交叉点
物体轮廓上的重要点

4、用途:
目标跟踪
物体识别
图像匹配
运动检测
由于寻找关键点对性能消耗较大,所以我们减少了识别的区域,只识别屏幕最中心的部分的关键点

5、寻找关键点
image.find_keypoints(]]]]])
该函数从指定的 ROI 元组 (x, y, w, h) 中提取 ORB 关键点。您可以使用 image.match_descriptor 函数比较两组关键点以获取匹配区域。若未发现关键点,则返回 None。

roi 是感兴趣区域的矩形元组 (x, y, w, h)。若未指定,默认 ROI 为整个图像。操作范围仅限于该区域内的像素。
threshold 控制提取关键点的数量(取值范围为 0-255)。对于默认的 AGAST 角点检测器,该值应设为约 20;对于 FAST 角点检测器,该值应设为约 60 至 80。阈值越低,提取的角点越多。
normalized 是布尔值。若为 True,则在多分辨率下关闭关键点提取。若您不关心处理扩展问题,且希望算法运行更快,则将其设置为 True。
scale_factor 是一个大于 1.0 的浮点数。较高的比例因子运行速度较快,但图像匹配效果相对较差。理想值介于 1.35 和 1.5 之间。
max_keypoints 是关键点对象能够容纳的最大关键点数量。若关键点对象过大导致内存问题,请适当降低该值。
corner_detector 是提取关键点所使用的角点检测器算法。可选值为 image.CORNER_FAST 或 image.CORNER_AGAST。FAST 角点检测器速度较快,但准确度较低。
注意: 此方法仅支持灰度图像。

6、绘制关键点
image.draw_keypoints(keypoints[, color[, size=10[, thickness=1[, fill=False]]]])
在图像上绘制特征点。

color:指定颜色,适用于灰度或 RGB565 图像。默认为白色。对于灰度图像,可以传递灰度值(0-255);对于 RGB565 图像,可以传递反向字节序的 RGB565 值。
size:控制特征点的大小。
thickness:控制线条的粗细(以像素为单位)。
fill:如果为 True,则填充特征点。
返回图像对象,以便后续方法可以链式调用。

该方法不支持压缩图像和 Bayer 格式图像

7、项目测试实验代码

#【花雕动手做】CanMV K230 AI视觉识别模块之使用draw_keypoints()方法绘制关键点

"""
Camera preview demo
摄像头预览演示

This script initializes camera sensor, displays preview and handles cleanup
本脚本初始化摄像头传感器、显示预览并处理清理工作
"""

import sys
import uos as os
import time
from media.sensor import *
from media.display import *
from media.media import *

def init_sensor():
    """
    Initialize camera sensor with specified configuration
    使用指定配置初始化摄像头传感器
   
    返回:
      Sensor: 初始化好的传感器对象
    """
    # Create sensor instance with resolution 1280x960
    # 创建分辨率为1280x960的传感器实例
    sensor = Sensor()

    # Reset sensor to default state
    # 将传感器重置为默认状态(恢复默认参数)
    sensor.reset()

    # Configure channel 1 output format to 640x480 RGB565
    # 配置通道1输出格式为640x480 RGB565(彩色图像)
    # RGB565格式:每个像素16位(5位红色,6位绿色,5位蓝色)
    sensor.set_framesize(width=640, height=480, chn=CAM_CHN_ID_1)
    sensor.set_pixformat(Sensor.RGB565, chn=CAM_CHN_ID_1)

    # Configure channel 0 output format to 640x480 GRAYSCALE
    # 配置通道0输出格式为640x480 GRAYSCALE(灰度图像)
    # 灰度图像更适合特征点检测算法
    sensor.set_framesize(width=640, height=480, chn=CAM_CHN_ID_0)
    sensor.set_pixformat(Sensor.GRAYSCALE, chn=CAM_CHN_ID_0)

    return sensor

def main():
    """
    Main function to run camera preview
    运行摄像头预览的主函数
    """
    sensor = None# 传感器对象初始化为None
   
    # 定义感兴趣区域(ROI) - Region of Interest
    # 格式: (x, y, width, height)
    # 从(220,140)开始,宽200像素,高200像素的正方形区域
    roi = (220, 140, 200, 200)
   
    try:
      # Initialize camera sensor
      # 初始化摄像头传感器
      sensor = init_sensor()

      # Initialize display with ST7701 driver
      # 使用ST7701驱动初始化显示器
      # 分辨率640x480,to_ide=True表示同时输出到IDE和硬件屏幕
      Display.init(Display.ST7701, width=640, height=480, to_ide=True)

      # Initialize media management
      # 初始化媒体管理(管理摄像头、显示等硬件资源)
      MediaManager.init()

      # Start sensor operation
      # 启动传感器运行(开始捕获图像)
      sensor.run()

      # Main loop to capture and display frames
      # 捕获和显示帧的主循环
      while True:
            # Capture color frame from channel 1 for display
            # 从通道1捕获彩色帧用于显示
            # RGB565格式,适合人眼观看
            img = sensor.snapshot(chn=CAM_CHN_ID_1)

            # Capture grayscale frame from channel 0 for keypoint detection
            # 从通道0捕获灰度帧用于关键点检测
            # 灰度图像计算量小,更适合计算机视觉算法
            img_g = sensor.snapshot(chn=CAM_CHN_ID_0)
            
            # Draw ROI rectangle on the color image
            # 在彩色图像上绘制ROI矩形框
            # color=(173, 216, 230): 浅蓝色
            # fill=False: 不填充,只绘制边框
            # thickness=3: 线宽3像素
            img.draw_rectangle(roi, color=(173, 216, 230), fill=False, thickness=3)

            # Find keypoints in the grayscale image within ROI
            # 在灰度图像的ROI区域内查找关键点
            keypoints = img_g.find_keypoints(
                threshold=30,      # 特征点检测阈值,值越小检测越敏感
                scale_factor=1.2,    # 尺度因子,用于构建图像金字塔
                max_keypoints=30,    # 最大特征点数量,限制计算复杂度
                roi=roi            # 指定感兴趣区域,只在该区域内检测
            )

            # If keypoints are detected
            # 如果检测到特征点
            if keypoints:
                # Print keypoints information for debugging
                # 打印特征点信息用于调试
                print(keypoints)
               
                # Draw keypoints on the color image
                # 在彩色图像上绘制特征点
                img.draw_keypoints(
                  keypoints,      # 特征点列表,包含每个特征点的坐标和分数
                  color=(255, 0, 0), # 红色 (RGB格式)
                  size=8,         # 特征点显示大小(像素)
                  thickness=4,      # 绘制线条的粗细
                  fill=True         # 填充特征点(实心圆点)
                )
               
            # Display captured frame with keypoints
            # 显示带有关键点的捕获帧
            Display.show_image(img)

    except KeyboardInterrupt:
      # Handle user interruption (Ctrl+C)
      # 处理用户中断(Ctrl+C)
      print("User interrupted the program")
      print("用户中断了程序")

    except Exception as e:
      # Handle other exceptions
      # 处理其他异常
      print(f"An error occurred: {str(e)}")
      print(f"发生错误: {str(e)}")

    finally:
      # Cleanup section - always executed
      # 清理部分 - 始终执行
      
      # Stop sensor if initialized
      # 如果传感器已初始化则停止
      if isinstance(sensor, Sensor):
            sensor.stop()

      # Deinitialize display
      # 反初始化显示(释放显示资源)
      Display.deinit()

      # Enable sleep mode
      # 启用睡眠模式(节能)
      os.exitpoint(os.EXITPOINT_ENABLE_SLEEP)
      time.sleep_ms(100)# 等待100毫秒确保资源释放完成

      # Release media resources
      # 释放媒体资源
      MediaManager.deinit()

if __name__ == "__main__":
    # Program entry point
    # 程序入口点
    main()

驴友花雕 发表于 昨天 17:45

【花雕动手做】CanMV K230 AI视觉模块之绘制关键点图框

解读这段CanMV K230关键点检测代码的技术细节和实现原理:

整体架构分析
1. 双通道并行处理架构
python
# 关键的双通道配置

sensor.set_pixformat(Sensor.RGB565, chn=CAM_CHN_ID_1)    # 显示通道

sensor.set_pixformat(Sensor.GRAYSCALE, chn=CAM_CHN_ID_0) # 处理通道
技术优势:
性能优化:灰度图像处理计算量仅为彩色图像的1/3
实时性保证:算法处理和显示渲染并行进行
资源利用:充分利用硬件多通道能力

核心技术深度解析
1. 关键点检测算法原理
find_keypoints() 方法底层可能使用的算法:

python
# 基于FAST角点检测算法的实现原理

def fast_corner_detector(image, threshold):

    """

    FAST (Features from Accelerated Segment Test) 算法

    """

    keypoints = []

    for y in range(3, image.height-3):

      for x in range(3, image.width-3):

            # 获取中心像素强度

            center = image

            

            # 检查16个周边像素

            circle_pixels = get_bresenham_circle(x, y)

            

            # 快速测试:检查连续N个像素是否都大于或小于中心像素

            if fast_test(circle_pixels, center, threshold):

                # 计算角点得分

                score = compute_corner_score(circle_pixels, center)

                keypoints.append(Keypoint(x, y, score))

   

    return non_maximum_suppression(keypoints)
ORB特征点检测流程:

text
1. oFast角点检测
   └── 构建图像金字塔
   └── 多尺度角点检测
   └── Harris角点响应计算

2. rBRIEF描述符计算
   └── 方向归一化
   └── 二进制描述符生成
   └── 特征点排序和筛选
2. 参数调优分析
python
keypoints = img_g.find_keypoints(

    threshold=30,      # 角点检测阈值

    scale_factor=1.2,    # 图像金字塔尺度因子

    max_keypoints=30,    # 最大特征点数量

    roi=roi            # 感兴趣区域

)



3. ROI技术实现原理
python
roi = (220, 140, 200, 200)# (x, y, width, height)

ROI内存访问优化:



c

// 底层C实现可能类似这样

for(int y = roi_y; y < roi_y + roi_height; y++) {

    for(int x = roi_x; x < roi_x + roi_width; x++) {

      // 只处理ROI区域内的像素

      process_pixel(image);

    }

}
计算量对比:
全图检测:640×480 = 307,200 像素
ROI检测:200×200 = 40,000 像素
性能提升:约87%的计算量减少

图像处理流水线分析
1. 数据流架构
text
摄像头传感器
    ↓
双通道输出
    ├── 通道0 (GRAYSCALE) → 特征检测 → 关键点坐标
    └── 通道1 (RGB565) → 显示渲染 ← 绘制关键点
    ↓
显示输出

2. 内存管理策略
python
# 图像缓冲区内存占用分析

gray_buffer = 640 * 480 * 1# 307,200 字节 (300KB)

rgb_buffer= 640 * 480 * 2# 614,400 字节 (600KB)

total_memory = gray_buffer + rgb_buffer# 约900KB



# 关键点数据结构

class Keypoint:

    x: int16          # 2字节

    y: int16          # 2字节

    score: float32    # 4字节

    angle: float32    # 4字节

    size: float32   # 4字节

    # 总计: 16字节/关键点 × 30 = 480字节
算法性能优化技术
1. 图像金字塔优化
python
# 多尺度检测的实现

def build_image_pyramid(image, scale_factor, levels):

    pyramid =

    for i in range(1, levels):

      # 高斯模糊 + 下采样

      blurred = gaussian_blur(pyramid)

      downsampled = resize(blurred, scale_factor)

      pyramid.append(downsampled)

    return pyramid
2. 非极大值抑制(NMS)
python
def non_maximum_suppression(keypoints, radius=3):

    """

    移除相邻区域内响应较低的特征点

    """

    suppressed = []

    keypoints.sort(key=lambda kp: kp.score, reverse=True)

   

    for kp in keypoints:

      is_local_max = True

      for existing in suppressed:

            if distance(kp, existing) < radius and kp.score <= existing.score:

                is_local_max = False

                break

      if is_local_max:

            suppressed.append(kp)

   

    return suppressed
实际应用场景扩展
1. 实时目标跟踪
python
class FeatureTracker:

    def __init__(self):

      self.prev_keypoints = None

      self.prev_descriptors = None

   

    def track(self, current_frame):

      current_keypoints = current_frame.find_keypoints(roi=tracking_roi)

      

      if self.prev_keypoints:

            # 特征点匹配

            matches = self.match_features(self.prev_descriptors,

                                        current_keypoints.descriptors)

            # 计算运动向量

            motion = self.calculate_motion(matches)

            return motion

      

      self.prev_keypoints = current_keypoints

      return None
2. 视觉里程计
python
def visual_odometry(prev_frame, curr_frame):

    prev_kps = prev_frame.find_keypoints()

    curr_kps = curr_frame.find_keypoints()

   

    # 特征匹配

    matches = feature_matcher.match(prev_kps, curr_kps)

   

    # 本质矩阵估计

    E, mask = cv.findEssentialMat(prev_pts, curr_pts, camera_matrix)

   

    # 姿态恢复

    _, R, t, mask = cv.recoverPose(E, prev_pts, curr_pts, camera_matrix)

   

    return R, t# 旋转和平移矩阵
错误处理和鲁棒性分析
1. 异常处理机制
python
try:

    # 主要处理逻辑

    sensor.run()

    while True:

      img = sensor.snapshot(chn=CAM_CHN_ID_1)

      # ... 处理代码

except KeyboardInterrupt:

    # 用户主动中断 - 友好退出

except Exception as e:

    # 其他异常 - 防止系统崩溃

finally:

    # 资源清理 - 确保资源释放
2. 算法鲁棒性考虑
python
def robust_keypoint_detection(image):

    # 1. 图像质量检查

    if image.is_too_dark() or image.is_too_bright():

      adjust_exposure()

   

    # 2. 特征点数量验证

    keypoints = image.find_keypoints()

    if len(keypoints) < 5:# 特征点太少

      # 调整检测参数或返回错误

      return adjust_and_retry(image)

   

    # 3. 特征点分布检查

    if not is_uniformly_distributed(keypoints):

      # 特征点聚集在某些区域

      return redistribute_keypoints(keypoints)

   

    return keypoints
性能基准测试
根据代码参数估算性能:
处理分辨率:200×200 ROI
特征点数量:最多30个
预计帧率:15-30 FPS(取决于硬件)
内存占用:约900KB图像缓冲区 + 算法临时内存

技术总结
这段关键点检测代码展示了:
嵌入式视觉系统设计:双通道并行处理架构
实时算法优化:ROI限制、参数调优、内存管理
计算机视觉基础:特征点检测算法原理
系统集成能力:摄像头、显示、算法的协同工作
工程实践:异常处理、资源管理、性能考量

这是一个典型的嵌入式计算机视觉应用,为更复杂的目标识别、SLAM、AR等应用提供了基础技术支撑。代码在性能、功能和可维护性之间取得了很好的平衡。

驴友花雕 发表于 昨天 17:49

【花雕动手做】CanMV K230 AI视觉模块之绘制关键点图框

实验场景图








页: [1]
查看完整版本: 【花雕动手做】CanMV K230 AI 视觉模块之绘制关键点图框