图像基础与MaixPy图像处理:图像格式、色彩空间、图像采集与显示的基本API使用

在K210物体识别项目中,图像是一切视觉算法的输入源头。无论是后续的图像分类、目标检测,还是简单的颜色识别,都依赖于对图像数据的正确理解与高效处理。MaixPy作为K210芯片的官方MicroPython固件,封装了底层复杂的硬件加速单元(KPU、FPIOA、DMA等),为开发者提供了简洁而强大的图像处理接口。本章将系统梳理图像在数字世界中的表示方式、MaixPy中图像对象的本质,以及如何通过标准API完成图像的采集与显示,为后续的物体识别模型部署打下坚实基础。

图像采集链路 sensor 摄像头 DVP接口 RGB565/JPEG 24MHz PCLK VGA/QVGA MaixPy图像对象 img Image类 width/height format(RGB565/GRAY) size/bytes 像素访问 显示输出 lcd LCD屏 SPI接口 显示尺寸 刷新率 DMA 绘制 图像处理流水线 sensor.snapshot() 采集一帧 img.to_rgb565() 格式转换 img.draw_string() 绘制结果 lcd.display(img) 显示输出 典型帧率:QVGA(320x240) RGB565 约30fps | JPEG 约15fps 内存占用:320*240*2 = 150KB(RGB565)

图像格式与色彩空间:从像素到数据

数字图像的本质是一个二维矩阵,每个元素称为像素(Pixel)。对于灰度图,每个像素用一个字节(0-255)表示亮度;对于彩色图,则需要多个通道来描述颜色信息。K210的摄像头传感器(如OV2640、OV5640)输出原始Bayer格式数据,经过ISP(图像信号处理器)处理后,通常转换为RGB565、RGB888或YUV422等格式。MaixPy中,sensor.set_pixformat()函数用于设置采集格式,最常用的是sensor.RGB565和sensor.GRAYSCALE。

格式位深内存占用(320x240)适用场景MaixPy常量
RGB56516bit/像素150KB彩色显示、颜色识别sensor.RGB565
GRAYSCALE8bit/像素75KB灰度识别、边缘检测sensor.GRAYSCALE
JPEG压缩格式约20-50KB网络传输、存储sensor.JPEG
RGB88824bit/像素225KB高精度颜色处理sensor.RGB888

色彩空间是颜色在数学上的表示方法。RGB空间适合显示设备,但亮度与色度耦合,不利于图像分析。YUV空间将亮度(Y)与色度(U/V)分离,YUV422格式在K210上常用于人脸检测等算法,因为KPU硬件加速器对YUV数据有优化。MaixPy内部会自动处理格式转换,但开发者需要理解:RGB565是K210显示和存储的默认格式,而YUV422是KPU推理的常用输入格式。当调用img.to_yuv422()时,MaixPy会执行像素格式转换,这个过程会消耗CPU周期,应避免在循环中频繁转换。

经验提示:在物体识别项目中,建议保持RGB565格式进行图像采集和显示,仅在送入KPU模型前转换为模型所需的格式。MaixPy的kpu模块内部会自动处理格式对齐,但若手动预处理,务必确认输入张量的shape和dtype与模型要求一致。

图像采集API:sensor模块的核心用法

MaixPy的sensor模块封装了摄像头驱动,其初始化流程遵循固定顺序:复位→设置像素格式→设置帧大小→设置帧率→关闭白平衡/曝光补偿→启动。以下是一个标准初始化代码片段:

import sensor, lcd
# 初始化摄像头
sensor.reset()                      # 复位摄像头
sensor.set_pixformat(sensor.RGB565) # 设置RGB565格式
sensor.set_framesize(sensor.QVGA)   # 设置320x240分辨率
sensor.set_vflip(1)                 # 垂直翻转(根据安装方向调整)
sensor.set_hmirror(1)               # 水平镜像
sensor.set_auto_whitebal(False)     # 关闭自动白平衡(固定色温)
sensor.set_auto_exposure(False)     # 关闭自动曝光(固定亮度)
sensor.skip_frames(30)              # 跳过30帧,等待传感器稳定
# 初始化LCD
lcd.init(freq=15000000)             # 初始化LCD,SPI频率15MHz
lcd.rotation(2)                     # 旋转屏幕方向

sensor.snapshot()是采集一帧图像的核心函数,返回一个Image对象。该函数会阻塞当前线程直到一帧数据准备就绪,默认超时时间为100ms。在实时识别场景中,应确保主循环的帧处理时间小于帧间隔(例如30fps时帧间隔约33ms),否则实际帧率会下降。MaixPy还提供了sensor.run(1)和sensor.stop()来控制采集的启停,但通常不需要手动调用。

图像显示API:LCD与FrameBuffer

K210开发板通常配备2.4英寸或2.8英寸SPI接口LCD屏,分辨率多为320x240。MaixPy的lcd模块提供了简洁的显示接口。lcd.display(img)接受一个Image对象或RGB565字节数组,将其直接刷新到屏幕。该函数内部通过DMA传输数据,不会阻塞CPU,但若连续调用,需要确保上一帧传输完成,否则可能造成撕裂。MaixPy的lcd.display()会自动处理等待,因此开发者无需关心同步问题。

除了直接显示图像,LCD还支持绘制基本图形:lcd.draw_line()、lcd.draw_rect()、lcd.draw_string()等。这些函数直接操作LCD的FrameBuffer,适合在图像上叠加文字或框选目标。但注意,这些绘制操作是覆盖式的,不会修改原始Image对象。若需要在图像数据上绘制(例如保存带标注的图片),应使用img.draw_*()系列方法。

核心区别:img.draw_*()修改的是图像数据本身,后续保存或送入模型时会包含绘制内容;lcd.draw_*()仅修改屏幕显示,不影响图像数据。在物体识别项目中,推荐使用img.draw_*()绘制检测框和标签,然后一次性lcd.display(img),这样既能显示结果,又能保存带标注的图像。

案例拆解:实时显示摄像头画面并叠加帧率信息

下面通过一个完整的案例,演示图像采集、处理、显示的全流程。该案例实现了一个实时视频预览器,在画面左上角显示当前帧率(FPS),并在底部显示图像格式和分辨率信息。

import sensor, lcd, time, gc

# 初始化
sensor.reset()
sensor.set_pixformat(sensor.RGB565)
sensor.set_framesize(sensor.QVGA)
sensor.set_vflip(1)
sensor.set_hmirror(1)
sensor.skip_frames(30)

lcd.init(freq=15000000)
lcd.rotation(2)

# 帧率计算变量
clock = time.clock()
frame_count = 0
start_time = time.ticks_ms()
fps = 0

while True:
    clock.tick()
    img = sensor.snapshot()          # 采集一帧
    
    # 每30帧更新一次FPS
    frame_count += 1
    if frame_count % 30 == 0:
        elapsed = time.ticks_diff(time.ticks_ms(), start_time)
        fps = 30000 // elapsed       # 计算FPS
        start_time = time.ticks_ms()
    
    # 在图像上绘制信息
    img.draw_string(5, 5, "FPS: %d" % fps, color=(255, 255, 0), scale=2)
    img.draw_string(5, 30, "Format: RGB565", color=(0, 255, 0), scale=1)
    img.draw_string(5, 45, "Size: %dx%d" % (img.width(), img.height()), color=(0, 255, 0), scale=1)
    
    # 显示到LCD
    lcd.display(img)
    
    # 内存管理
    gc.collect()

该案例的关键点在于:帧率计算采用滑动窗口法,每30帧计算一次平均FPS,避免单帧波动导致显示跳动。img.draw_string()的color参数使用RGB888元组,但内部会转换为RGB565存储。在长时间运行时,gc.collect()可以回收碎片内存,防止内存泄漏导致系统崩溃。

风险提示:K210的RAM仅有6MB(其中约2MB被固件占用),QVGA RGB565图像占用150KB,若同时保存多帧或进行复杂图像处理,极易内存不足。建议:1) 使用gc.collect()定期回收;2) 避免在循环中创建大对象;3) 必要时降低分辨率到QQVGA(160x120)或使用GRAYSCALE格式。

常见误区与总结复盘

误区一:混淆图像格式与显示格式。很多初学者认为sensor.set_pixformat(sensor.JPEG)后,LCD显示会更流畅。实际上,JPEG格式需要解码才能显示,反而增加CPU负担。LCD显示原生支持RGB565,因此采集RGB565是最佳选择。

误区二:忽略摄像头方向校准。K210开发板的摄像头安装方向不统一,set_vflip和set_hmirror必须根据实际安装调整。若画面上下颠倒或左右镜像,物体识别模型的输入方向错误,会导致识别准确率大幅下降。

误区三:在循环中频繁创建Image对象。sensor.snapshot()返回的Image对象是复用的,但若手动img.copy()或img.resize(),会创建新对象。这些操作应在初始化时完成,循环中应复用预分配的对象。

误区四:忽视LCD刷新率限制。虽然K210的SPI LCD标称支持30fps,但实际刷新率受SPI时钟和DMA带宽影响。若发现画面闪烁或撕裂,可尝试降低lcd.init(freq=)的频率,或使用lcd.display(img, raw=True)跳过格式检查。