(六)像素、色彩与帧缓冲:ESP-VISION图像模型深度拆解
像素、色彩与帧缓冲ESP-VISION图像模型深度拆解文章目录像素、色彩与帧缓冲ESP-VISION图像模型深度拆解一、一个像素里的学问二、像素格式全家桶2.1 RGB565的精妙设计2.2 格式转换开销三、LAB色彩空间为什么颜色阈值是六元组离线计算阈值四、帧缓冲与fb_alloc嵌入式内存管理的黑魔法4.1 帧缓冲是可复用的4.2 fb_alloc栈式分配器五、ROI做减法比做加法快六、实战像素格式对性能的影响参考链接总结与下篇预告一、一个像素里的学问你平时写img sensor.snapshot()的时候可能没想过这一行代码背后发生了什么。一个640×480的RGB图像如果每个像素3字节RGB888就是900KB。这已经接近ESP32-S3可用SRAM的一半了。如果还要处理、显示、推理内存根本不够用。所以ESP-VISION的像素格式设计本质上是在内存、速度和画质之间做权衡。今天这篇文章我们把一个像素掰开揉碎了讲。二、像素格式全家桶ESP-VISION支持7种像素格式每种在内存、速度、用途上都有取舍格式字节/像素典型用途内存占用QVGABINARY1 bit掩膜、二值化结果9.6 KBGRAYSCALE1滤波、检测算法首选76.8 KBRGB5652彩色显示、颜色追踪153.6 KBBAYER1传感器原始数据76.8 KBYUV4222摄像头/编解码流水线153.6 KBJPEG不定压缩存储/传输通常10-50KBPNG不定无损参考帧比JPEG大2.1 RGB565的精妙设计RGB565把一个彩色像素打包进16位位: [15-11] [10-5] [4-0] R(5位) G(6位) B(5位)为什么绿色多1位因为人眼对绿色最敏感。这个设计让RGB565在只有RGB888一半内存的情况下视觉质量几乎看不出差别。# 获取像素值imgsensor.snapshot()r,g,bimg.get_pixel(100,100)# 返回展开的(r, g, b)元组packedimg.get_pixel(100,100,rgb565True)# 返回打包的16位值2.2 格式转换开销# 从RGB565转灰度零开销硬件加速imgsensor.snapshot()# RGB565grayimg.to_grayscale()# 不复制返回视图# 从灰度转RGB565需要格式转换img2gray.to_rgb565()# 复制转换 关键尽可能让传感器直接输出你需要的格式。设置sensor.set_pixformat(sensor.GRAYSCALE)比先采集RGB565再转灰度高效得多。三、LAB色彩空间为什么颜色阈值是六元组如果你用过OpenCV做颜色追踪你可能习惯了HSV。但ESP-VISION用的是LAB色彩空间。RGB像素LAB转换L通道: 亮度 0-100A通道: 绿→红B通道: 蓝→黄LAB的核心优势把亮度L和颜色A、B分开了。这意味着在光照变化时颜色阈值仍然稳定。颜色阈值是六元组(l_min, l_max, a_min, a_max, b_min, b_max)# 红色阈值LAB空间# L: 30-100 亮度范围排除太暗的区域# A: 15-127 正A偏红# B: 15-127 正B偏黄red_threshold(30,100,15,127,15,127)# 灰度阈值只是简单的(min, max)gray_threshold(100,255)离线计算阈值# 在REPL中计算实际颜色的LAB值importimage# 已知RGB颜色转LABlabimage.rgb_to_lab((255,0,0))# 纯红色print(lab)# (53, 80, 67)# 反过来rgbimage.lab_to_rgb((53,80,67))print(rgb)# (255, 0, 0)四、帧缓冲与fb_alloc嵌入式内存管理的黑魔法4.1 帧缓冲是可复用的img1sensor.snapshot()# 帧缓冲被填充img2sensor.snapshot()# 帧缓冲被覆盖img1也变了# 如果你需要保留前一帧img1sensor.snapshot().copy()# 显式复制img2sensor.snapshot()4.2 fb_alloc栈式分配器许多图像处理算法需要只在调用期间存在的临时内存。ESP-VISION使用一个栈式分配器fb_alloc从帧缓冲区域中划出临时缓冲操作返回时一次性释放。这就是为什么重负载方法不会造成堆碎片的原因——所有临时内存来自帧缓冲区域而不是堆。# 这一行代码内部使用了fb_allocblobsimg.find_blobs([threshold])# find_blobs需要的临时内存连通分量标记的栈等# 全部来自fb_alloc调用结束后自动释放# 你的堆内存完全不受影响五、ROI做减法比做加法快感兴趣区域ROI是提高性能最简单的方法# 不用ROI处理整个320×240 76,800像素blobsimg.find_blobs([threshold])# 用ROI只处理左上角100×100 10,000像素blobsimg.find_blobs([threshold],roi(0,0,100,100))# 速度提升7.6倍ROI语法roi(x, y, w, h)坐标是源图像的像素坐标。# 实际应用只关注传送带区域CONVEYOR_ROI(50,100,220,80)# 传送带位置whileTrue:imgsensor.snapshot()# 只在传送带区域检测零件partsimg.find_blobs([RED_THRESHOLD],roiCONVEYOR_ROI)forpartinparts:# 注意blob坐标是相对于ROI的img.draw_rectangle(part.x()CONVEYOR_ROI[0],# 转换回全局坐标part.y()CONVEYOR_ROI[1],part.w(),part.h())六、实战像素格式对性能的影响我做了个对比实验同一段目标检测代码不同像素格式的性能差异像素格式采集帧率 (VGA)处理帧率内存占用RGB56530 fps15 fps600 KBGRAYSCALE30 fps22 fps300 KBBAYER (原始)30 fps30 fps300 KB# 高效做法直接采集灰度sensor.set_pixformat(sensor.GRAYSCALE)# 传感器直接输出灰度sensor.set_framesize(sensor.VGA)# 处理帧率22 fps# 低效做法采RGB565再转灰度sensor.set_pixformat(sensor.RGB565)# 传感器输出RGB565sensor.set_framesize(sensor.VGA)imgsensor.snapshot()grayimg.to_grayscale()# 额外转换开销# 处理帧率15 fps差距47%的性能提升仅仅因为选择了正确的像素格式。参考链接ESP-VISION 图像模型ESP-VISION API - image模块总结与下篇预告图像模型的核心就三件事像素格式决定内存和速度LAB色彩空间让颜色追踪对光照鲁棒帧缓冲复用是嵌入式内存管理的关键。选对像素格式性能提升可以高达47%。下篇我们进入图像处理的世界——从高斯滤波到霍夫变换从形态学操作到AprilTag位姿估计这些算法在ESP-VISION里怎么用、怎么调参。作者码农阿虎关键词像素格式、LAB色彩空间、帧缓冲、ROI、图像模型