边缘检测
实验原理
本实验使用 cv_lite 提供的边缘检测接口 ,对摄像头采集的图像实时提取边缘,并将边缘图直接显示出来。其底层实现基于 OpenCV 的 Canny 边缘检测,输入为通过 to_numpy_ref() 零拷贝获取的 ulab.numpy.ndarray:灰度图调用 cv_lite.grayscale_find_edges,彩色图调用 cv_lite.rgb888_find_edges,二者均输出一张灰度边缘图。
什么是边缘
边缘是指图像中像素灰度发生剧烈变化的位置。物体的边界、纹理的突变等都会在灰度上形成明显的阶跃或斜坡,因此边缘检测的本质是求图像梯度的幅值,并在梯度较大的位置标记为边缘。
Canny 边缘检测
cv_lite 底层调用的是 OpenCV 的 Canny 算子,它是一种多阶段的经典边缘检测算法,主要包括以下步骤:
- 高斯滤波:先对图像做高斯平滑,抑制噪声,避免噪声被误判为边缘;
- 计算梯度:用 Sobel 算子分别计算水平、垂直方向的梯度 、,由此得到梯度幅值与方向:
- 非极大值抑制:沿梯度方向比较每个像素的梯度幅值,只保留局部极 大值,从而把粗边缘细化为单像素宽的细线;
- 双阈值检测:用高阈值
threshold2和低阈值threshold1把像素分为三类--- 梯度 ≥
threshold2:强边缘,必定保留; - 梯度 <
threshold1:非边缘,丢弃; - 介于两者之间:弱边缘,仅当与强边缘相连时才保留;
- 梯度 ≥
- 滞后连接:通过连通性判断,把真正与强边缘相连的弱边缘连成完整边界,剔除孤立的弱边缘响应。
其中 threshold1(低阈值)与 threshold2(高阈值)是仅有的两个可调参数,通常高阈值约为低阈值的 2~3 倍。阈值越大,检出的边缘越少但越可靠;阈值越小,边缘越完整但噪声也越多。
cv_lite 的封装与数据流
cv_lite 的边缘检测接口接收 ulab.numpy.ndarray,返回的同样是一个 ndarray(灰度边缘图,边缘处为亮、背景为暗):
- 通过
img.to_numpy_ref()以零拷贝方式取得输入图像的 ndarray 引用,灰度图为单通道、彩色图为 RGB888 三通道; - 灰度图直接进入 Canny;RGB888 图在底层转换为灰度后再做检测(Canny 依赖单通道梯度);
- 输出
edge_np是一张与原图同尺寸的灰度边缘图 ndarray,再用image.Image(..., image.GRAYSCALE, alloc=image.ALLOC_REF, data=edge_np)以零拷贝方式包回image对象交给Display.show_image()显示。
由于输入、输出都与 image 共享同一块内存,整个“采集 -> 检测 -> 显示”链路 无需额外拷贝,因此相比 openmv 原生方案有明显的速度优势(参见 cv_lite 概述中的速度对比)。
代码解析
两个示例(灰度图与彩色图)的处理流程基本一致,区别仅在于像素格式与调用的检测函数。下面按关键步骤进行解析。
导入模块
import cv_lite # cv_lite 扩展模块(包含边缘检测)
import ulab.numpy as np # NumPy-like ndarray for MicroPython
from media.sensor import * # 摄像头接口
from media.display import * # 显示接口
from media.media import * # 媒体资源管理器
cv_lite:提供grayscale_find_edges/rgb888_find_edges等基于 OpenCV Canny 的加速接口;ulab.numpy:MicroPython 下的类 NumPy 库,to_numpy_ref()返回的 ndarray 即为该类型,作为 cv_lite 的输入;media.sensor/media.display/media.media:openmv 风格的摄像头、显示与媒体管理接口,负责采集与显示。
初始化摄像头与显示
image_shape = [480, 640] # 高 x 宽
sensor = Sensor(id=2, width=1280, height=720, fps=90)
sensor.reset()
sensor.set_framesize(width=image_shape[1], height=image_shape[0])
sensor.set_pixformat(Sensor.GRAYSCALE) # 灰度示例
# sensor.set_pixformat(Sensor.RGB888) # 彩色示例
Display.init(Display.ST7701, width=image_shape[1], height=image_shape[0], to_ide=True, quality=100)
sensor.run()
image_shape = [480, 640]:处理图像的高 × 宽为 480×640,后续既用于设置帧大小,也作为 cv_lite 检测函数的入参;Sensor(...):创建摄像头对象,set_framesize将输出帧设为 640×480,set_pixformat设置像素格式--灰度示例用Sensor.GRAYSCALE,彩色示例用Sensor.RGB888;Display.init(Display.ST7701, ...):初始化 3.5 寸 LCD,to_ide=True同时把画面回传给 IDE,quality为 JPEG 压缩质量;sensor.run():启动摄像头采集。