一、像素格式

1. 一句话理解

每个像素是一个小点,用 3 个数(红/绿/蓝)就能描述颜色。但"这 3 个数在内存里怎么摆"能摆出几十种姿势,每种姿势就是一种像素格式。

2. 区别就 3 件事

① 字节顺序:RGB 还是 BGR?

RGB:  内存里依次是 红 绿 蓝
BGR:  内存里依次是 蓝 绿 红

颜色完全一样,只是顺序相反。BGR 来自 Windows BMP 文件格式的早年约定,显示硬件和 OpenCV 跟着抄。纯历史惯例,没有谁更好。

② 占几个字节:3 个还是 4 个?

RGB  (3字节):  R G B         —— 省内存
RGBX (4字节):  R G B X       —— 多 1 个没用的 X(占位)
RGBA (4字节):  R G B A       —— 多 1 个 A(透明度, 255=不透明)

CPU/GPU 读内存喜欢 4 字节对齐,3 字节别扭还不能按整数一口气算;4 字节一个像素正好一个整数,SIMD/GPU 直接吃。多花 33% 内存换速度。

③ 空间换不换:RGB 还是 YUV?

人眼: 对明暗(亮度)敏感, 对颜色(色度)不敏感
YUV = 把颜色拆成 亮度 Y + 颜色 U/V
相机做法: 亮度全保留, 颜色偷工减料
  YUV444: 颜色一点不少   = 3 字节/像素
  YUYV  : 颜色横着减半   = 2 字节/像素 (省 33%)
  NV12  : 颜色横竖都减半 = 1.5 字节/像素 (省 50%)

1280x720 一张图,NV12 比 RGBA 省一半内存,人眼几乎看不出区别。JPEG 压缩也利用这一点(默认 4:2:0)。

3. 20 种格式总表

格式字节/像素内存排布是谁在用
RGB3R G Blibjpeg 解码默认输出,通用
BGR3B G RWindows BMP / 老显示控制器 DMA
RGBX4R G B XD3D/GPU 纹理,X 未定义
BGRX4B G R XBMP 风格 4 字节对齐
XBGR4X B G R小端下当 uint32 = 0x00RRGGBB,位运算友好
XRGB4X R G BOpenGL XRGB8888 约定
RGBA4R G B AGPU/合成管线,需要透明度
BGRA4B G R AWindows 32bpp 屏幕 / COCOA
ABGR4A B G ROpenGL GL_ABGR_EXT
ARGB4A R G BQt ARGB32(数值 0xAARRGGBB 存小端内存)
GRAY1Y灰度,最省内存
CMYK4C M Y K印刷分色(转 RGB 仅近似)
YCBCR4443Y Cb CrJPEG 内部色彩空间
YUYV2Y0 U Y1 VUVC/USB 摄像头经典输出
UYVY2U Y0 V Y1视频采集卡常见
NV121.5Y 平面 + UV 半平面硬件编码器 / RV1106 MPP
NV211.5Y 平面 + VU 半平面Android 相机默认
I4201.5Y | U | V 三平面FFmpeg / 软件编码通用
I4443Y | U | V 三平面专业视频中间格式

实测(1920x1080):GRAY 2.0MB < NV12/I420 3.0MB < YUYV 4.0MB < RGB 5.9MB < 4 字节格式 7.9MB

4. YUV 家族详解

采样档位(人眼对色彩不敏感,逐级偷色度):

4:4:4  每像素 3 字节, 色度全保留 (I444 / YCBCR444)
4:2:2  每像素 2 字节, 色度水平减半 (YUYV / UYVY)
4:2:0  每像素 1.5 字节, 色度横竖都减半 (NV12 / NV21 / I420)

色度摆放:同样是偷色度,摆法不同 → 不同格式。

交织(混装):  YUYV [Y0 U Y1 V] — USB 摄像头芯片直接吐出, 拿来就能用
半平面:      NV12 [Y整块][UV交替] — 硬件编码器最小 DMA 单元
全平面:      I420 [Y整块][U整块][V整块] — 软件编码器缓存友好

原则上:传感器给你什么就用什么,少转一次就快一次。

5. 为什么搞这么多格式?(3 个推动力)

  1. 历史惯性:Windows/BMP/OpenGL/Qt 各定各的顺序,互不相让(BGR/ARGB 家族)。
  2. 硬件胃口:有的硬件要 4 字节对齐才好 DMA(X/RGBA 家族);有的硬件按 YUV 半平面搬运(NV12)。格式跟着芯片走。
  3. 人眼物理:颜色可以偷懒少存,偷多少、怎么偷 → 4:2:2 / 4:2:0 各档位。

6. 选型一句话

省内存 → YUV (1.5~2B),要速度 → 4 字节 RGBX/RGBA,
要通用 → RGB,显示硬件要 BGR → 给它 BGR

二、视频本质

1. 视频 = 按固定频率连续播放图片

1 秒视频 = 30 张图(帧率 30fps)。1080p RGB 一张 6MB,30 张 = 180MB/秒,根本存不下。所以要"视频编码"。

2. 视频编码 = 两种偷懒的组合

懒法一·帧内压缩:  单张图自己压 (JPEG 那套)  → 一张图砍到 1/20
懒法二·帧间压缩:  相邻帧几乎一样, 只存"变化" → 再省 10~30 倍
门派做法代表压缩率
只用懒法一每帧压成独立图片MJPEG约 1/20
两个都用关键帧完整 + 后续帧只存变化H.264 / H.265约 1/200

3. 为什么 MJPEG 既是图片又是视频?

因为它的每一帧就真的是一个标准 JPEG 图片:

MJPEG 视频流 = [JPEG图片][JPEG图片][JPEG图片]... 按帧率播放
  • 当图片看:随便抽一帧都是完整 JPEG,能单独打开、精确跳转、单独裁剪。哪帧坏了解哪帧,互不影响。
  • 当视频看:装在时间轴容器里(如 AVI),按帧率播放就是视频。

所以 MJPEG 站在中间:内容是图片编码,载体是视频容器。

代价:文件是 H.264 的 5~10 倍。

用在哪:安防录像(容错第一)、医疗/工业图像(逐帧精确)、行车记录仪、RV1106 这种硬件 JPEG 编码器现成的板卡。

4. 和我们学的 libjpeg 的关系

MJPEG 编码 = 循环调用 jpeg_en   (一帧一个 JPEG)
MJPEG 解码 = 循环调用 jpeg_de   (逐帧解压播放)

libjpeg 就是 MJPEG 的全部内核 —— 这也是"JPEG 是图片/视频双栖格式"的根本原因:编解码器是同一个。


三、配套代码(/root/c-std/libjpeg/)

文件内容
pixfmt.h / pixfmt.c20 种像素格式转换工具(信息表、逐像素函数、整图转换)
demo/pixfmt_demo.c格式信息/字节形态/内存占用/往返误差演示
demo/jpeg_en.c / jpeg_de.clibjpeg 编码/解码(MJPEG 的帧处理核心)
demo/turbo_demo.cTurboJPEG 一行 API + 缩放解码

下一步建议:mjpeg_demo.c —— 连续生成 30 帧动态画面,逐帧用 jpeg_en 的流程编码,按 AVI/MJPG 容器头存成视频文件,直观看到"视频 = 一串 JPEG"。