2. 图像数据格式与内存布局基础

在STM32N6上做图像预处理,第一件必须搞清楚的事不是算法,而是“像素在内存里到底怎么排”。同一幅图像,RGB888、RGB565、YUV422、灰度8位,它们在内存中的字节数、访问粒度、DMA搬运效率完全不同。如果格式选错或stride没对齐,即使算法正确,帧率也可能掉一半。

一、三种主流格式的存储本质

RGB格式按像素打包颜色分量。RGB888每像素3字节,顺序通常为B、G、R(小端存储时低位在前);RGB565每像素2字节,用5位红、6位绿、5位蓝压缩,适合显示和带宽受限场景。RGB的优点是直观、便于色彩处理,缺点是数据量大,且三通道交织导致按通道访问时步长不连续。

YUV格式把亮度Y与色度UV分离。YUV444每像素3字节;YUV422每2个像素共享一组UV,平均每像素2字节;YUV420每4个像素共享一组UV,平均每像素1.5字节。STM32N6的ISP和DCMIPP通常输出YUV422,因为它在保留色彩信息的同时把带宽压到RGB888的约2/3。

灰度格式每像素1字节,只保留亮度。它没有色彩信息,但内存占用最小、访问最连续,非常适合边缘检测、二值化、光流等只依赖亮度的预处理。

二、平面存储与打包存储

YUV有两种典型布局。打包(packed/interleaved)把Y、U、V按像素顺序交织,例如YUYV:Y0 U0 Y1 V0 Y2 U1 Y3 V1……这种布局一次DMA就能搬完整行,缓存局部性好。平面(planar)把Y、U、V分别放在三个连续区域,先存完整Y平面,再存U平面、V平面。平面布局便于单独处理亮度通道,但访问色度时需要跳到另一块地址,cache命中率可能下降。

STM32N6的DCMIPP和DMA2D对打包YUV422支持更直接;如果算法需要频繁单独读Y,平面格式反而更省事。选择依据是“谁访问得多、访问是否连续”。

三、行对齐与stride

图像每行实际占用的字节数叫stride(行跨距),它不一定等于“宽度×每像素字节数”。硬件常要求stride按4字节、8字节或16字节对齐,目的是让每行起始地址落在总线突发边界上。例如宽度为641的灰度图,每行641字节,若要求16字节对齐,stride应取656(641向上取到16的倍数)。此时每行末尾有15字节填充,这些填充不参与显示,但必须计入地址计算。

访问像素(x, y)的地址公式为:

addr = base + y * stride + x * bytes_per_pixel

如果误用width代替stride,从第二行起就会整体错位,图像出现斜切。STM32N6的LTDC、DCMIPP、DMA2D都提供独立的stride寄存器,配置时必须显式写入对齐后的值。

四、位宽与字节序对访问效率的影响

STM32N6的Cortex-M55总线以32位为基本访问宽度,配合AXI突发传输。每像素1字节的灰度图,一次32位读取可拿到4个像素;RGB565一次可拿2个像素;RGB888则会出现跨字边界,需要两次访问才能凑齐一个像素,效率明显下降。因此,在带宽敏感的场景,优先选择2字节或1字节每像素的格式,并尽量让行起始地址32位对齐。

字节序方面,RGB888在内存中常按B、G、R排列,若按32位字读取,一个字的四个字节可能包含B0 G0 R0 B1,解析时必须按字节拆分。YUV422的YUYV打包中,U和V的顺序也依赖具体外设约定,DCMIPP输出与DMA2D输入若字节序理解不一致,会出现红蓝互换或色度错位。

判断格式是否匹配的快速方法:用DMA2D把图像直接搬到LCD,若颜色正确、无斜切,说明格式、stride、字节序三者一致;若出现斜切,先查stride;若红蓝互换,查RGB字节序;若色度块状错位,查YUV打包顺序。

五、面向STM32N6的布局选择建议

结合STM32N6的cache和DMA特性,可以给出如下实践取向:

  • 只做亮度处理时,优先灰度8位,stride按16字节对齐,DMA一次搬多行。
  • 需要色彩且带宽紧张时,选YUV422打包,stride按8字节对齐,避免平面跳转。
  • 需要逐通道处理时,选平面YUV420,但注意U/V平面宽度是Y的一半,stride也要单独对齐。
  • RGB565适合显示链路,RGB888仅在算法必须高精度色彩时使用,并接受带宽代价。

下面这张图把四种典型格式在内存中的排布、stride对齐和访问粒度放在一起对比,便于在选型时快速判断。

STM32N6 图像格式内存布局对比 灰度 8bpp 1 字节/像素 · 连续 Y0 Y1 Y2 Y3 … Y15 Y16 Y17 … Y31 … stride = 16 对齐 32位读 = 4 像素 带宽最小 · 无色彩 RGB565 2 字节/像素 · 打包 R0G0 B0R1 G1B1 … R2G2 B2R3 G3B3 … … stride = 8 对齐 32位读 = 2 像素 显示友好 · 色彩压缩 YUV422 打包 2 字节/像素 · YUYV Y0 U0 Y1 V0 Y2 U1 … Y4 U2 Y5 V2 … … stride = 8 对齐 32位读 = 2 像素 ISP常用 · 带宽适中 RGB888 3 字节/像素 · 打包 B0 G0 R0 B1 G1 R1 … B2 G2 R2 B3 … … stride = 16 对齐 32位读跨字边界 带宽最大 · 精度最高 stride 对齐与地址计算(以灰度图宽641为例) 第0行:641 字节有效像素 + 15 字节填充 = 656 第1行:起始地址 = base + 1 × 656 第y行:addr = base + y × 656 + x 关键判断 • stride ≠ width × bpp 时,必须用 stride 算行地址 • 行首 32 位对齐 → DMA 突发效率最高 • 字节序错 → 红蓝互换;打包顺序错 → 色度错位