嵌入式Linux摄像头采集与图像处理

嵌入式Linux摄像头采集与图像处理

一句话总览:从插上摄像头到存出 JPG 图片,本文用流程图和可视化图表串联 V4L2 驱动框架、YUV 颜色格式、RGB 转换公式、libjpeg 压缩库的完整知识链,每个概念都有生活类比帮助理解。


专业术语读音速查

技术术语在交流中常常需要读出来,下表列出了国内嵌入式工程师最通用的读法------照着念就能跟同行无障碍交流。

术语 中文读法 说明
V4L2 V-4-L-2,或 v- fò- tù Video for Linux Two,逐个读字母
YUV Y-U-V,或 wài- yōu- wéi 颜色空间,逐个读字母
YUYV Y-U-Y-V,或 wài- yōu- wài- wéi 像素格式,逐个读字母
RGB R-G-B,或 àr- jī- bī 颜色空间,逐个读字母
JPEG jié- pài- gé /ˈdʒeɪpeɡ/,中文音译"杰派格"
libjpeg lì- bù- jié- pài- gé library + JPEG 的连读
ioctl I/O control,或 ài- ōu- kòng Input/Output Control 缩写
mmap M-map,或 m- mài- pǔ memory map 缩写
select sè- lái- kè- tè /sɪˈlekt/,中文音译"瑟莱克特"
Planar pǔ- léi- nà /ˈpleɪnɑːr/,平面存储方式
Packed pài- kè- tè /pækt/,打包存储方式
DCT D-C-T,或 dé- xī- tì 离散余弦变换,读字母
Huffman hā- fū- màn /ˈhʌfmən/,霍夫曼编码
FourCC Four-C-C,或 fò- kè- kè Four Character Code
v4l2-ctl V4L2 control,或 v- fò- tù- kòng V4L2 控制工具
VIDIOC wéi- dí- ō- kè V4L2 的 ioctl 命令前缀
MIPI mǐ- pǐ 手机摄像头接口标准
CSI C-S-I,或 xī- s- ài Camera Serial Interface
HDMI H-D-M-I,或 hà- d- m- ài 高清多媒体接口
BT.601 BT-601,或 bī- tì- liù- líng- yī 标清转换标准,读 B-T-601
BT.709 BT-709,或 bī- tì- qī- líng- jiǔ 高清转换标准,读 B-T-709
BT.2020 BT-2020,或 bī- tì- èr- líng- èr- líng 超高清转换标准,读 B-T-2020
clamp kè- lán- pǔ 钳位操作
ISP I-S-P,或 ài- s- pì Image Signal Processor
UVC U-V-C,或 yōu- wéi- xī USB Video Class
EXIF yì- xī- fú Exchangeable Image File Format
YCbCr Y-Cb-Cr,或 wài- xī- bī- xī- àr 数字颜色空间
NV12 NV-12,或 n- wéi- shí- èr Android 视频格式
Bayer bèi- yě /ˈbeɪər/,Bayer 阵列发明者
CMOS C-MOS,或 xī- māo- sī 互补金属氧化物半导体
CCD C-C-D,或 xī- xī- dì 电荷耦合器件

小建议:开会或面试时直接读字母(如 V4L2 念"V-4-L-2",YUV 念"Y-U-V")是最保险的,所有人都听得懂。


目录

  • [第一章 摄像头基础与 V4L2 驱动框架](#第一章 摄像头基础与 V4L2 驱动框架)
    • [1.1 摄像头硬件基础](#1.1 摄像头硬件基础)
    • [1.2 V4L2 驱动框架](#1.2 V4L2 驱动框架)
    • [1.3 V4L2 拍照 10 步流程](#1.3 V4L2 拍照 10 步流程)
  • [第二章 图像格式与转换](#第二章 图像格式与转换)
    • [2.1 YUV 格式详解](#2.1 YUV 格式详解)
    • [2.2 YUV 数据转 RGB](#2.2 YUV 数据转 RGB)
    • [2.3 JPEG 压缩原理与 libjpeg](#2.3 JPEG 压缩原理与 libjpeg)
  • [第三章 完整代码工程](#第三章 完整代码工程)
    • [3.1 系统架构总览](#3.1 系统架构总览)
    • [3.2 代码实现](#3.2 代码实现)
  • 附录
    • [A. 常见报错与解决](#A. 常见报错与解决)
    • [B. 调试命令速查](#B. 调试命令速查)
    • [C. 关键概念速查表](#C. 关键概念速查表)
    • [D. 知识体系脑图](#D. 知识体系脑图)

第一章 摄像头基础与 V4L2 驱动框架

1.1 摄像头硬件基础

一句话总结

摄像头本质上是一个"光电转换器"------外界光线通过镜头投射到传感器上,传感器把光信号转成电信号,再经过模数转换变成数字信号,最终交给 CPU 处理。

类比:摄像头就像人的眼睛------角膜(镜头)把光线聚焦到视网膜(传感器)上,视神经(数据总线)把信号传给大脑(CPU)。

1.1.1 摄像头工作原理

#mermaid-svg-jcUWBBUTdPCqNwL7{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-jcUWBBUTdPCqNwL7 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-jcUWBBUTdPCqNwL7 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-jcUWBBUTdPCqNwL7 .error-icon{fill:#552222;}#mermaid-svg-jcUWBBUTdPCqNwL7 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-jcUWBBUTdPCqNwL7 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-jcUWBBUTdPCqNwL7 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-jcUWBBUTdPCqNwL7 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-jcUWBBUTdPCqNwL7 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-jcUWBBUTdPCqNwL7 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-jcUWBBUTdPCqNwL7 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-jcUWBBUTdPCqNwL7 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-jcUWBBUTdPCqNwL7 .marker.cross{stroke:#333333;}#mermaid-svg-jcUWBBUTdPCqNwL7 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-jcUWBBUTdPCqNwL7 p{margin:0;}#mermaid-svg-jcUWBBUTdPCqNwL7 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-jcUWBBUTdPCqNwL7 .cluster-label text{fill:#333;}#mermaid-svg-jcUWBBUTdPCqNwL7 .cluster-label span{color:#333;}#mermaid-svg-jcUWBBUTdPCqNwL7 .cluster-label span p{background-color:transparent;}#mermaid-svg-jcUWBBUTdPCqNwL7 .label text,#mermaid-svg-jcUWBBUTdPCqNwL7 span{fill:#333;color:#333;}#mermaid-svg-jcUWBBUTdPCqNwL7 .node rect,#mermaid-svg-jcUWBBUTdPCqNwL7 .node circle,#mermaid-svg-jcUWBBUTdPCqNwL7 .node ellipse,#mermaid-svg-jcUWBBUTdPCqNwL7 .node polygon,#mermaid-svg-jcUWBBUTdPCqNwL7 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-jcUWBBUTdPCqNwL7 .rough-node .label text,#mermaid-svg-jcUWBBUTdPCqNwL7 .node .label text,#mermaid-svg-jcUWBBUTdPCqNwL7 .image-shape .label,#mermaid-svg-jcUWBBUTdPCqNwL7 .icon-shape .label{text-anchor:middle;}#mermaid-svg-jcUWBBUTdPCqNwL7 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-jcUWBBUTdPCqNwL7 .rough-node .label,#mermaid-svg-jcUWBBUTdPCqNwL7 .node .label,#mermaid-svg-jcUWBBUTdPCqNwL7 .image-shape .label,#mermaid-svg-jcUWBBUTdPCqNwL7 .icon-shape .label{text-align:center;}#mermaid-svg-jcUWBBUTdPCqNwL7 .node.clickable{cursor:pointer;}#mermaid-svg-jcUWBBUTdPCqNwL7 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-jcUWBBUTdPCqNwL7 .arrowheadPath{fill:#333333;}#mermaid-svg-jcUWBBUTdPCqNwL7 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-jcUWBBUTdPCqNwL7 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-jcUWBBUTdPCqNwL7 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-jcUWBBUTdPCqNwL7 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-jcUWBBUTdPCqNwL7 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-jcUWBBUTdPCqNwL7 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-jcUWBBUTdPCqNwL7 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-jcUWBBUTdPCqNwL7 .cluster text{fill:#333;}#mermaid-svg-jcUWBBUTdPCqNwL7 .cluster span{color:#333;}#mermaid-svg-jcUWBBUTdPCqNwL7 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-jcUWBBUTdPCqNwL7 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-jcUWBBUTdPCqNwL7 rect.text{fill:none;stroke-width:0;}#mermaid-svg-jcUWBBUTdPCqNwL7 .icon-shape,#mermaid-svg-jcUWBBUTdPCqNwL7 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-jcUWBBUTdPCqNwL7 .icon-shape p,#mermaid-svg-jcUWBBUTdPCqNwL7 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-jcUWBBUTdPCqNwL7 .icon-shape .label rect,#mermaid-svg-jcUWBBUTdPCqNwL7 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-jcUWBBUTdPCqNwL7 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-jcUWBBUTdPCqNwL7 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-jcUWBBUTdPCqNwL7 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 输出部分
传感器部分
光学部分
外界光线
镜头组

聚焦光线
CMOS/CCD 传感器

光信号→电信号
ADC 模数转换

模拟信号→数字信号
ISP 图像处理器

色彩校正/降噪
输出数据流

YUYV / MJPEG

1.1.1a CMOS 与 CCD 传感器对比

摄像头传感器主要分为 CCD 和 CMOS 两种,当前嵌入式设备几乎全部使用 CMOS 传感器。

对比维度 CCD(电荷耦合器件) CMOS(互补金属氧化物半导体)
工作原理 电荷逐行转移到输出端统一放大 每个像素独立放大,行列寻址读出
功耗 高(需要多路电压) 低(单电压供电,省电 80%)
速度 慢(串行读出) 快(并行读出,支持高速)
噪声 低(统一放大,一致性高) 较高(各像素放大差异大)
集成度 低(需要外部驱动电路) 高(可集成 ISP、ADC 在同一芯片)
成本 高(制造工艺复杂) 低(标准半导体工艺)
典型应用 高端工业相机、卫星遥感 手机摄像头、嵌入式设备、监控
市场现状 逐渐被 CMOS 取代 占比超过 95%

一句话总结:CCD 画质好但贵且耗电,像专业单反;CMOS 成本低、集成度高、省电,像手机摄像头。嵌入式设备几乎清一色用 CMOS。

1.1.1b ISP(图像信号处理器)流水线

摄像头传感器输出的原始数据(Bayer RAW)不能直接使用,必须经过 ISP 处理才能变成我们看到的彩色图像。ISP 就像"照片的暗房"------把原始底片变成精美的照片。
#mermaid-svg-iTIkIrNPOkEIVrgq{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-iTIkIrNPOkEIVrgq .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-iTIkIrNPOkEIVrgq .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-iTIkIrNPOkEIVrgq .error-icon{fill:#552222;}#mermaid-svg-iTIkIrNPOkEIVrgq .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-iTIkIrNPOkEIVrgq .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-iTIkIrNPOkEIVrgq .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-iTIkIrNPOkEIVrgq .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-iTIkIrNPOkEIVrgq .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-iTIkIrNPOkEIVrgq .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-iTIkIrNPOkEIVrgq .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-iTIkIrNPOkEIVrgq .marker{fill:#333333;stroke:#333333;}#mermaid-svg-iTIkIrNPOkEIVrgq .marker.cross{stroke:#333333;}#mermaid-svg-iTIkIrNPOkEIVrgq svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-iTIkIrNPOkEIVrgq p{margin:0;}#mermaid-svg-iTIkIrNPOkEIVrgq .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-iTIkIrNPOkEIVrgq .cluster-label text{fill:#333;}#mermaid-svg-iTIkIrNPOkEIVrgq .cluster-label span{color:#333;}#mermaid-svg-iTIkIrNPOkEIVrgq .cluster-label span p{background-color:transparent;}#mermaid-svg-iTIkIrNPOkEIVrgq .label text,#mermaid-svg-iTIkIrNPOkEIVrgq span{fill:#333;color:#333;}#mermaid-svg-iTIkIrNPOkEIVrgq .node rect,#mermaid-svg-iTIkIrNPOkEIVrgq .node circle,#mermaid-svg-iTIkIrNPOkEIVrgq .node ellipse,#mermaid-svg-iTIkIrNPOkEIVrgq .node polygon,#mermaid-svg-iTIkIrNPOkEIVrgq .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-iTIkIrNPOkEIVrgq .rough-node .label text,#mermaid-svg-iTIkIrNPOkEIVrgq .node .label text,#mermaid-svg-iTIkIrNPOkEIVrgq .image-shape .label,#mermaid-svg-iTIkIrNPOkEIVrgq .icon-shape .label{text-anchor:middle;}#mermaid-svg-iTIkIrNPOkEIVrgq .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-iTIkIrNPOkEIVrgq .rough-node .label,#mermaid-svg-iTIkIrNPOkEIVrgq .node .label,#mermaid-svg-iTIkIrNPOkEIVrgq .image-shape .label,#mermaid-svg-iTIkIrNPOkEIVrgq .icon-shape .label{text-align:center;}#mermaid-svg-iTIkIrNPOkEIVrgq .node.clickable{cursor:pointer;}#mermaid-svg-iTIkIrNPOkEIVrgq .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-iTIkIrNPOkEIVrgq .arrowheadPath{fill:#333333;}#mermaid-svg-iTIkIrNPOkEIVrgq .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-iTIkIrNPOkEIVrgq .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-iTIkIrNPOkEIVrgq .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-iTIkIrNPOkEIVrgq .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-iTIkIrNPOkEIVrgq .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-iTIkIrNPOkEIVrgq .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-iTIkIrNPOkEIVrgq .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-iTIkIrNPOkEIVrgq .cluster text{fill:#333;}#mermaid-svg-iTIkIrNPOkEIVrgq .cluster span{color:#333;}#mermaid-svg-iTIkIrNPOkEIVrgq div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-iTIkIrNPOkEIVrgq .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-iTIkIrNPOkEIVrgq rect.text{fill:none;stroke-width:0;}#mermaid-svg-iTIkIrNPOkEIVrgq .icon-shape,#mermaid-svg-iTIkIrNPOkEIVrgq .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-iTIkIrNPOkEIVrgq .icon-shape p,#mermaid-svg-iTIkIrNPOkEIVrgq .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-iTIkIrNPOkEIVrgq .icon-shape .label rect,#mermaid-svg-iTIkIrNPOkEIVrgq .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-iTIkIrNPOkEIVrgq .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-iTIkIrNPOkEIVrgq .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-iTIkIrNPOkEIVrgq :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 输出
ISP 处理流水线
传感器输出
Bayer RAW 数据

每个像素只有一种颜色
去马赛克

插值补全 R/G/B
白平衡校正

适应不同色温
色彩校正矩阵

修正传感器色偏
Gamma 校正

非线性亮度映射
降噪处理

去除传感器噪声
边缘增强

提升清晰度
YUV/RGB 输出

640×480 彩色

学术背景:Bayer 格式是 1976 年由 Kodak 的 Bryce Bayer 发明的,它让每个像素只采集 R/G/B 中的一种颜色(通常 GR/BG 交替排列),再通过插值算法还原全彩。这是单传感器数码相机的核心技术。

1.1.2 摄像头关键参数速查

参数 说明 类比 典型值
分辨率 像素网格的密度,越高越清晰 网格纸的格子数 640×480 / 1920×1080
帧率 FPS 每秒采集的画面数,越高越流畅 翻页动画的速度 30fps / 60fps
像素格式 每个像素如何编码 用哪种语言描述颜色 YUYV / MJPEG / RGB3
传感器尺寸 感光面积大小,越大进光越多 水桶的口径 1/4" / 1/2.3"
接口类型 与主板的连接方式 水管的口径 USB / CSI / HDMI

1.1.2a 分辨率标准速查

标准名称 分辨率 宽高比 总像素数 典型应用
QQVGA 160×120 4:3 19,200 低端监控、玩具
QVGA 320×240 4:3 76,800 老式手机拍照
VGA 640×480 4:3 307,200 本文示例、USB 摄像头
SVGA 800×600 4:3 480,000 老式显示器
HD(720p) 1280×720 16:9 921,600 入门高清视频
FHD(1080p) 1920×1080 16:9 2,073,600 主流高清视频
QHD(2K) 2560×1440 16:9 3,686,400 高端手机
UHD(4K) 3840×2160 16:9 8,294,400 超高清视频

注意:嵌入式摄像头最常用的分辨率是 VGA(640×480)和 HD(1280×720),因为它们在画质和带宽之间取得了最佳平衡。

1.1.3 参数之间的制约关系

#mermaid-svg-I3gZL2cOe53q8X9T{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-I3gZL2cOe53q8X9T .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-I3gZL2cOe53q8X9T .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-I3gZL2cOe53q8X9T .error-icon{fill:#552222;}#mermaid-svg-I3gZL2cOe53q8X9T .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-I3gZL2cOe53q8X9T .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-I3gZL2cOe53q8X9T .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-I3gZL2cOe53q8X9T .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-I3gZL2cOe53q8X9T .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-I3gZL2cOe53q8X9T .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-I3gZL2cOe53q8X9T .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-I3gZL2cOe53q8X9T .marker{fill:#333333;stroke:#333333;}#mermaid-svg-I3gZL2cOe53q8X9T .marker.cross{stroke:#333333;}#mermaid-svg-I3gZL2cOe53q8X9T svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-I3gZL2cOe53q8X9T p{margin:0;}#mermaid-svg-I3gZL2cOe53q8X9T .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-I3gZL2cOe53q8X9T .cluster-label text{fill:#333;}#mermaid-svg-I3gZL2cOe53q8X9T .cluster-label span{color:#333;}#mermaid-svg-I3gZL2cOe53q8X9T .cluster-label span p{background-color:transparent;}#mermaid-svg-I3gZL2cOe53q8X9T .label text,#mermaid-svg-I3gZL2cOe53q8X9T span{fill:#333;color:#333;}#mermaid-svg-I3gZL2cOe53q8X9T .node rect,#mermaid-svg-I3gZL2cOe53q8X9T .node circle,#mermaid-svg-I3gZL2cOe53q8X9T .node ellipse,#mermaid-svg-I3gZL2cOe53q8X9T .node polygon,#mermaid-svg-I3gZL2cOe53q8X9T .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-I3gZL2cOe53q8X9T .rough-node .label text,#mermaid-svg-I3gZL2cOe53q8X9T .node .label text,#mermaid-svg-I3gZL2cOe53q8X9T .image-shape .label,#mermaid-svg-I3gZL2cOe53q8X9T .icon-shape .label{text-anchor:middle;}#mermaid-svg-I3gZL2cOe53q8X9T .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-I3gZL2cOe53q8X9T .rough-node .label,#mermaid-svg-I3gZL2cOe53q8X9T .node .label,#mermaid-svg-I3gZL2cOe53q8X9T .image-shape .label,#mermaid-svg-I3gZL2cOe53q8X9T .icon-shape .label{text-align:center;}#mermaid-svg-I3gZL2cOe53q8X9T .node.clickable{cursor:pointer;}#mermaid-svg-I3gZL2cOe53q8X9T .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-I3gZL2cOe53q8X9T .arrowheadPath{fill:#333333;}#mermaid-svg-I3gZL2cOe53q8X9T .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-I3gZL2cOe53q8X9T .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-I3gZL2cOe53q8X9T .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-I3gZL2cOe53q8X9T .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-I3gZL2cOe53q8X9T .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-I3gZL2cOe53q8X9T .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-I3gZL2cOe53q8X9T .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-I3gZL2cOe53q8X9T .cluster text{fill:#333;}#mermaid-svg-I3gZL2cOe53q8X9T .cluster span{color:#333;}#mermaid-svg-I3gZL2cOe53q8X9T div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-I3gZL2cOe53q8X9T .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-I3gZL2cOe53q8X9T rect.text{fill:none;stroke-width:0;}#mermaid-svg-I3gZL2cOe53q8X9T .icon-shape,#mermaid-svg-I3gZL2cOe53q8X9T .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-I3gZL2cOe53q8X9T .icon-shape p,#mermaid-svg-I3gZL2cOe53q8X9T .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-I3gZL2cOe53q8X9T .icon-shape .label rect,#mermaid-svg-I3gZL2cOe53q8X9T .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-I3gZL2cOe53q8X9T .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-I3gZL2cOe53q8X9T .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-I3gZL2cOe53q8X9T :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 硬件限制
计算结果
可调参数
分辨率

Width × Height
帧率 FPS
像素格式

决定每像素字节数
数据带宽

= 分辨率 × 帧率 × 每像素字节数
USB 带宽上限

约 480 Mbps
CPU 处理能力

一句话总结:分辨率越高画面越细腻,帧率越高动作越流畅,但两者都会让数据量暴增------嵌入式设备常常需要在画质和速度之间做取舍,就像手机拍照时选"高清"还是"流畅"模式。

1.1.3a 带宽计算示例

理论计算可以帮助我们在选型阶段就判断硬件是否够用,避免"买了才发现跑不动"的尴尬。

场景:640×480 @ 30fps,YUYV 格式

复制代码
每像素字节数 = 2(YUYV 是 4:2:2,每像素 2 字节)
每帧数据量 = 640 × 480 × 2 = 614,400 字节 ≈ 600 KB
每秒数据量 = 600 KB × 30 = 18,000 KB/s ≈ 17.6 MB/s
USB 2.0 理论带宽 = 480 Mbps ≈ 60 MB/s
实际可用带宽 ≈ 60 × 60% = 36 MB/s(USB 协议开销约 40%)

结论:640×480 @ 30fps 仅占 USB 2.0 实际带宽的约 50%,完全可行。但如果升级到 1080p @ 60fps,带宽需求会飙升到 237 MB/s,远超 USB 2.0 的能力,必须用 USB 3.0 或 MIPI CSI。

场景 分辨率 帧率 格式 带宽需求 USB 2.0 USB 3.0
低端监控 320×240 15fps YUYV 2.2 MB/s ✅ 轻松 ✅ 轻松
本文示例 640×480 30fps YUYV 17.6 MB/s ✅ 可行 ✅ 轻松
高清视频 1280×720 30fps MJPEG ~10 MB/s* ✅ 可行 ✅ 轻松
全高清 1920×1080 30fps YUYV 118 MB/s ❌ 超载 ✅ 可行
超高清 3840×2160 30fps YUYV 474 MB/s ❌ 超载 ❌ 超载

*MJPEG 是压缩格式,实际带宽取决于画面复杂度,通常为原始 YUYV 的 1/5~1/10。

1.1.4 常见摄像头接口对比

接口类型 传输速度 适用场景 优点 缺点
USB 480 Mbps (USB 2.0) 电脑外接摄像头 即插即用,通用性强 线长受限
CSI 1 Gbps+ 手机/开发板内置 高速低延迟,体积小 需要专用接口
HDMI 10 Gbps+ 高清采集卡 远距离传输,画质无损 需要额外编码

类比:USB 就像快递三轮车------灵活方便但速度有限;CSI 像专用轨道车------速度快但只能走固定路线;HDMI 像高速公路------速度快距离远但收费高。


1.2 V4L2 驱动框架

一句话总结

V4L2(Video for Linux Two)是 Linux 内核自带的视频设备 API,它让开发者用同一套函数操作不同品牌的摄像头------就像用同一个遥控器控制不同牌子的电视。

类比:V4L2 框架就像"手机操作系统"------App 通过系统 API 调用摄像头,不用管底层是索尼传感器还是三星传感器。换摄像头就像换手机后盖,App 不用改。

1.2.1 V4L2 分层架构

#mermaid-svg-M2VV4DandgFfLhYJ{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-M2VV4DandgFfLhYJ .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-M2VV4DandgFfLhYJ .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-M2VV4DandgFfLhYJ .error-icon{fill:#552222;}#mermaid-svg-M2VV4DandgFfLhYJ .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-M2VV4DandgFfLhYJ .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-M2VV4DandgFfLhYJ .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-M2VV4DandgFfLhYJ .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-M2VV4DandgFfLhYJ .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-M2VV4DandgFfLhYJ .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-M2VV4DandgFfLhYJ .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-M2VV4DandgFfLhYJ .marker{fill:#333333;stroke:#333333;}#mermaid-svg-M2VV4DandgFfLhYJ .marker.cross{stroke:#333333;}#mermaid-svg-M2VV4DandgFfLhYJ svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-M2VV4DandgFfLhYJ p{margin:0;}#mermaid-svg-M2VV4DandgFfLhYJ .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-M2VV4DandgFfLhYJ .cluster-label text{fill:#333;}#mermaid-svg-M2VV4DandgFfLhYJ .cluster-label span{color:#333;}#mermaid-svg-M2VV4DandgFfLhYJ .cluster-label span p{background-color:transparent;}#mermaid-svg-M2VV4DandgFfLhYJ .label text,#mermaid-svg-M2VV4DandgFfLhYJ span{fill:#333;color:#333;}#mermaid-svg-M2VV4DandgFfLhYJ .node rect,#mermaid-svg-M2VV4DandgFfLhYJ .node circle,#mermaid-svg-M2VV4DandgFfLhYJ .node ellipse,#mermaid-svg-M2VV4DandgFfLhYJ .node polygon,#mermaid-svg-M2VV4DandgFfLhYJ .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-M2VV4DandgFfLhYJ .rough-node .label text,#mermaid-svg-M2VV4DandgFfLhYJ .node .label text,#mermaid-svg-M2VV4DandgFfLhYJ .image-shape .label,#mermaid-svg-M2VV4DandgFfLhYJ .icon-shape .label{text-anchor:middle;}#mermaid-svg-M2VV4DandgFfLhYJ .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-M2VV4DandgFfLhYJ .rough-node .label,#mermaid-svg-M2VV4DandgFfLhYJ .node .label,#mermaid-svg-M2VV4DandgFfLhYJ .image-shape .label,#mermaid-svg-M2VV4DandgFfLhYJ .icon-shape .label{text-align:center;}#mermaid-svg-M2VV4DandgFfLhYJ .node.clickable{cursor:pointer;}#mermaid-svg-M2VV4DandgFfLhYJ .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-M2VV4DandgFfLhYJ .arrowheadPath{fill:#333333;}#mermaid-svg-M2VV4DandgFfLhYJ .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-M2VV4DandgFfLhYJ .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-M2VV4DandgFfLhYJ .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-M2VV4DandgFfLhYJ .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-M2VV4DandgFfLhYJ .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-M2VV4DandgFfLhYJ .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-M2VV4DandgFfLhYJ .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-M2VV4DandgFfLhYJ .cluster text{fill:#333;}#mermaid-svg-M2VV4DandgFfLhYJ .cluster span{color:#333;}#mermaid-svg-M2VV4DandgFfLhYJ div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-M2VV4DandgFfLhYJ .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-M2VV4DandgFfLhYJ rect.text{fill:none;stroke-width:0;}#mermaid-svg-M2VV4DandgFfLhYJ .icon-shape,#mermaid-svg-M2VV4DandgFfLhYJ .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-M2VV4DandgFfLhYJ .icon-shape p,#mermaid-svg-M2VV4DandgFfLhYJ .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-M2VV4DandgFfLhYJ .icon-shape .label rect,#mermaid-svg-M2VV4DandgFfLhYJ .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-M2VV4DandgFfLhYJ .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-M2VV4DandgFfLhYJ .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-M2VV4DandgFfLhYJ :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 硬件层
内核空间
用户空间
open / ioctl / mmap
驱动回调
USB 传输
I2C / MIPI
PCIe
应用程序

你的 C 代码
V4L2 核心框架

video_device / v4l2_device
摄像头驱动

如 uvcvideo
USB 摄像头
CSI 摄像头
HDMI 采集卡

1.2.1a UVC 协议:USB 摄像头如何即插即用

扩展知识 :UVC(USB Video Class)是 USB 标准化组织定义的视频设备协议标准。符合 UVC 标准的摄像头插入电脑后,操作系统自动加载通用驱动(Linux 下的 uvcvideo),无需安装厂商专用驱动。

UVC 协议的核心能力

  1. 自动协商:主机查询摄像头支持的格式列表,选择最优配置
  2. 标准控制:亮度、对比度、饱和度等参数通过统一接口调节
  3. 等时传输:视频数据通过 USB 等时端点传输,保证带宽但不保证可靠性
  4. 帧同步:通过帧头(Frame Header)标记每帧的起始和结束

#mermaid-svg-JNQJbyNb4cYPDD9Y{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-JNQJbyNb4cYPDD9Y .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-JNQJbyNb4cYPDD9Y .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-JNQJbyNb4cYPDD9Y .error-icon{fill:#552222;}#mermaid-svg-JNQJbyNb4cYPDD9Y .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-JNQJbyNb4cYPDD9Y .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-JNQJbyNb4cYPDD9Y .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-JNQJbyNb4cYPDD9Y .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-JNQJbyNb4cYPDD9Y .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-JNQJbyNb4cYPDD9Y .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-JNQJbyNb4cYPDD9Y .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-JNQJbyNb4cYPDD9Y .marker{fill:#333333;stroke:#333333;}#mermaid-svg-JNQJbyNb4cYPDD9Y .marker.cross{stroke:#333333;}#mermaid-svg-JNQJbyNb4cYPDD9Y svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-JNQJbyNb4cYPDD9Y p{margin:0;}#mermaid-svg-JNQJbyNb4cYPDD9Y .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-JNQJbyNb4cYPDD9Y .cluster-label text{fill:#333;}#mermaid-svg-JNQJbyNb4cYPDD9Y .cluster-label span{color:#333;}#mermaid-svg-JNQJbyNb4cYPDD9Y .cluster-label span p{background-color:transparent;}#mermaid-svg-JNQJbyNb4cYPDD9Y .label text,#mermaid-svg-JNQJbyNb4cYPDD9Y span{fill:#333;color:#333;}#mermaid-svg-JNQJbyNb4cYPDD9Y .node rect,#mermaid-svg-JNQJbyNb4cYPDD9Y .node circle,#mermaid-svg-JNQJbyNb4cYPDD9Y .node ellipse,#mermaid-svg-JNQJbyNb4cYPDD9Y .node polygon,#mermaid-svg-JNQJbyNb4cYPDD9Y .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-JNQJbyNb4cYPDD9Y .rough-node .label text,#mermaid-svg-JNQJbyNb4cYPDD9Y .node .label text,#mermaid-svg-JNQJbyNb4cYPDD9Y .image-shape .label,#mermaid-svg-JNQJbyNb4cYPDD9Y .icon-shape .label{text-anchor:middle;}#mermaid-svg-JNQJbyNb4cYPDD9Y .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-JNQJbyNb4cYPDD9Y .rough-node .label,#mermaid-svg-JNQJbyNb4cYPDD9Y .node .label,#mermaid-svg-JNQJbyNb4cYPDD9Y .image-shape .label,#mermaid-svg-JNQJbyNb4cYPDD9Y .icon-shape .label{text-align:center;}#mermaid-svg-JNQJbyNb4cYPDD9Y .node.clickable{cursor:pointer;}#mermaid-svg-JNQJbyNb4cYPDD9Y .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-JNQJbyNb4cYPDD9Y .arrowheadPath{fill:#333333;}#mermaid-svg-JNQJbyNb4cYPDD9Y .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-JNQJbyNb4cYPDD9Y .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-JNQJbyNb4cYPDD9Y .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-JNQJbyNb4cYPDD9Y .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-JNQJbyNb4cYPDD9Y .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-JNQJbyNb4cYPDD9Y .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-JNQJbyNb4cYPDD9Y .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-JNQJbyNb4cYPDD9Y .cluster text{fill:#333;}#mermaid-svg-JNQJbyNb4cYPDD9Y .cluster span{color:#333;}#mermaid-svg-JNQJbyNb4cYPDD9Y div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-JNQJbyNb4cYPDD9Y .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-JNQJbyNb4cYPDD9Y rect.text{fill:none;stroke-width:0;}#mermaid-svg-JNQJbyNb4cYPDD9Y .icon-shape,#mermaid-svg-JNQJbyNb4cYPDD9Y .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-JNQJbyNb4cYPDD9Y .icon-shape p,#mermaid-svg-JNQJbyNb4cYPDD9Y .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-JNQJbyNb4cYPDD9Y .icon-shape .label rect,#mermaid-svg-JNQJbyNb4cYPDD9Y .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-JNQJbyNb4cYPDD9Y .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-JNQJbyNb4cYPDD9Y .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-JNQJbyNb4cYPDD9Y :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 应用层
驱动层
物理层
USB 物理连接
uvcvideo.ko

UVC 通用驱动
V4L2 核心框架
应用程序

通过 V4L2 API 访问

注意:非 UVC 摄像头(如某些工业相机、MIPI 摄像头)需要厂商提供专用 V4L2 驱动,不能即插即用。这也是为什么嵌入式开发板上用 CSI 摄像头时需要额外配置设备树和驱动。

操作 函数 类比 说明
打开设备 open() 打开水龙头阀门 获取文件描述符 fd
查询能力 ioctl(VIDIOC_QUERYCAP) 看水龙头说明书 确认设备支持什么功能
枚举格式 ioctl(VIDIOC_ENUM_FMT) 看能接哪几种水管 列出支持的像素格式
设置格式 ioctl(VIDIOC_S_FMT) 选好水管尺寸接上去 设置宽高、像素格式
申请缓冲区 ioctl(VIDIOC_REQBUFS) 在出水口放桶 申请内核缓冲区
映射内存 mmap() 在桶上开观察窗 用户空间直接访问缓冲区
入队 ioctl(VIDIOC_QBUF) 把空桶放流水线 缓冲区交给驱动填数据
开流 ioctl(VIDIOC_STREAMON) 打开水龙头 启动视频采集
等待 select() / poll() 盯着桶看水满了没 等待数据就绪
取帧 ioctl(VIDIOC_DQBUF) 把满桶拿下来 取出已填满的缓冲区

1.2.3 V4L2 核心结构体关系

#mermaid-svg-zxBw6GTvNbEpb5tF{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-zxBw6GTvNbEpb5tF .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-zxBw6GTvNbEpb5tF .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-zxBw6GTvNbEpb5tF .error-icon{fill:#552222;}#mermaid-svg-zxBw6GTvNbEpb5tF .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-zxBw6GTvNbEpb5tF .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-zxBw6GTvNbEpb5tF .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-zxBw6GTvNbEpb5tF .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-zxBw6GTvNbEpb5tF .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-zxBw6GTvNbEpb5tF .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-zxBw6GTvNbEpb5tF .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-zxBw6GTvNbEpb5tF .marker{fill:#333333;stroke:#333333;}#mermaid-svg-zxBw6GTvNbEpb5tF .marker.cross{stroke:#333333;}#mermaid-svg-zxBw6GTvNbEpb5tF svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-zxBw6GTvNbEpb5tF p{margin:0;}#mermaid-svg-zxBw6GTvNbEpb5tF g.classGroup text{fill:#9370DB;stroke:none;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:10px;}#mermaid-svg-zxBw6GTvNbEpb5tF g.classGroup text .title{font-weight:bolder;}#mermaid-svg-zxBw6GTvNbEpb5tF .cluster-label text{fill:#333;}#mermaid-svg-zxBw6GTvNbEpb5tF .cluster-label span{color:#333;}#mermaid-svg-zxBw6GTvNbEpb5tF .cluster-label span p{background-color:transparent;}#mermaid-svg-zxBw6GTvNbEpb5tF .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-zxBw6GTvNbEpb5tF .cluster text{fill:#333;}#mermaid-svg-zxBw6GTvNbEpb5tF .cluster span{color:#333;}#mermaid-svg-zxBw6GTvNbEpb5tF .nodeLabel,#mermaid-svg-zxBw6GTvNbEpb5tF .edgeLabel{color:#131300;}#mermaid-svg-zxBw6GTvNbEpb5tF .edgeLabel .label rect{fill:#ECECFF;}#mermaid-svg-zxBw6GTvNbEpb5tF .label text{fill:#131300;}#mermaid-svg-zxBw6GTvNbEpb5tF .labelBkg{background:#ECECFF;}#mermaid-svg-zxBw6GTvNbEpb5tF .edgeLabel .label span{background:#ECECFF;}#mermaid-svg-zxBw6GTvNbEpb5tF .classTitle{font-weight:bolder;}#mermaid-svg-zxBw6GTvNbEpb5tF .node rect,#mermaid-svg-zxBw6GTvNbEpb5tF .node circle,#mermaid-svg-zxBw6GTvNbEpb5tF .node ellipse,#mermaid-svg-zxBw6GTvNbEpb5tF .node polygon,#mermaid-svg-zxBw6GTvNbEpb5tF .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-zxBw6GTvNbEpb5tF .divider{stroke:#9370DB;stroke-width:1;}#mermaid-svg-zxBw6GTvNbEpb5tF g.clickable{cursor:pointer;}#mermaid-svg-zxBw6GTvNbEpb5tF g.classGroup rect{fill:#ECECFF;stroke:#9370DB;}#mermaid-svg-zxBw6GTvNbEpb5tF g.classGroup line{stroke:#9370DB;stroke-width:1;}#mermaid-svg-zxBw6GTvNbEpb5tF .classLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.5;}#mermaid-svg-zxBw6GTvNbEpb5tF .classLabel .label{fill:#9370DB;font-size:10px;}#mermaid-svg-zxBw6GTvNbEpb5tF .relation{stroke:#333333;stroke-width:1;fill:none;}#mermaid-svg-zxBw6GTvNbEpb5tF .dashed-line{stroke-dasharray:3;}#mermaid-svg-zxBw6GTvNbEpb5tF .dotted-line{stroke-dasharray:1 2;}#mermaid-svg-zxBw6GTvNbEpb5tF #compositionStart,#mermaid-svg-zxBw6GTvNbEpb5tF .composition{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-zxBw6GTvNbEpb5tF #compositionEnd,#mermaid-svg-zxBw6GTvNbEpb5tF .composition{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-zxBw6GTvNbEpb5tF #dependencyStart,#mermaid-svg-zxBw6GTvNbEpb5tF .dependency{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-zxBw6GTvNbEpb5tF #dependencyStart,#mermaid-svg-zxBw6GTvNbEpb5tF .dependency{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-zxBw6GTvNbEpb5tF #extensionStart,#mermaid-svg-zxBw6GTvNbEpb5tF .extension{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-zxBw6GTvNbEpb5tF #extensionEnd,#mermaid-svg-zxBw6GTvNbEpb5tF .extension{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-zxBw6GTvNbEpb5tF #aggregationStart,#mermaid-svg-zxBw6GTvNbEpb5tF .aggregation{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-zxBw6GTvNbEpb5tF #aggregationEnd,#mermaid-svg-zxBw6GTvNbEpb5tF .aggregation{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-zxBw6GTvNbEpb5tF #lollipopStart,#mermaid-svg-zxBw6GTvNbEpb5tF .lollipop{fill:#ECECFF!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-zxBw6GTvNbEpb5tF #lollipopEnd,#mermaid-svg-zxBw6GTvNbEpb5tF .lollipop{fill:#ECECFF!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-zxBw6GTvNbEpb5tF .edgeTerminals{font-size:11px;line-height:initial;}#mermaid-svg-zxBw6GTvNbEpb5tF .classTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-zxBw6GTvNbEpb5tF .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-zxBw6GTvNbEpb5tF .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-zxBw6GTvNbEpb5tF :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} fmt.pix
产生
映射到用户空间
v4l2_capability
+char driver16
+char card32
+__u32 capabilities
+查询设备能力
v4l2_fmtdesc
+__u32 index
+__u32 type
+__u32 pixelformat
+char description32
+枚举支持的格式
v4l2_format
+__u32 type
+struct v4l2_pix_format pix
+设置/获取当前格式
v4l2_pix_format
+__u32 width
+__u32 height
+__u32 pixelformat
+__u32 bytesperline
+__u32 sizeimage
v4l2_requestbuffers
+__u32 count
+__u32 type
+__u32 memory
+申请内核缓冲区
v4l2_buffer
+__u32 index
+__u32 type
+__u32 memory
+__u32 length
+查询/入队/出队缓冲区
mmap

一句话总结 :V4L2 的每个 ioctl 操作都配一个结构体------QUERYCAP 用 v4l2_capability,S_FMT 用 v4l2_format,REQBUFS 用 v4l2_requestbuffers,QBUF/DQBUF 用 v4l2_buffer------填表办事,各司其职。

1.2.4 ioctl 通信时序

摄像头硬件 V4L2 内核驱动 应用程序 摄像头硬件 V4L2 内核驱动 应用程序 #mermaid-svg-OciTVPoWFhXzxjvM{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-OciTVPoWFhXzxjvM .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-OciTVPoWFhXzxjvM .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-OciTVPoWFhXzxjvM .error-icon{fill:#552222;}#mermaid-svg-OciTVPoWFhXzxjvM .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-OciTVPoWFhXzxjvM .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-OciTVPoWFhXzxjvM .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-OciTVPoWFhXzxjvM .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-OciTVPoWFhXzxjvM .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-OciTVPoWFhXzxjvM .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-OciTVPoWFhXzxjvM .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-OciTVPoWFhXzxjvM .marker{fill:#333333;stroke:#333333;}#mermaid-svg-OciTVPoWFhXzxjvM .marker.cross{stroke:#333333;}#mermaid-svg-OciTVPoWFhXzxjvM svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-OciTVPoWFhXzxjvM p{margin:0;}#mermaid-svg-OciTVPoWFhXzxjvM .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-OciTVPoWFhXzxjvM text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-OciTVPoWFhXzxjvM .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-OciTVPoWFhXzxjvM .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-OciTVPoWFhXzxjvM .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-OciTVPoWFhXzxjvM .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-OciTVPoWFhXzxjvM #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-OciTVPoWFhXzxjvM .sequenceNumber{fill:white;}#mermaid-svg-OciTVPoWFhXzxjvM #sequencenumber{fill:#333;}#mermaid-svg-OciTVPoWFhXzxjvM #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-OciTVPoWFhXzxjvM .messageText{fill:#333;stroke:none;}#mermaid-svg-OciTVPoWFhXzxjvM .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-OciTVPoWFhXzxjvM .labelText,#mermaid-svg-OciTVPoWFhXzxjvM .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-OciTVPoWFhXzxjvM .loopText,#mermaid-svg-OciTVPoWFhXzxjvM .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-OciTVPoWFhXzxjvM .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-OciTVPoWFhXzxjvM .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-OciTVPoWFhXzxjvM .noteText,#mermaid-svg-OciTVPoWFhXzxjvM .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-OciTVPoWFhXzxjvM .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-OciTVPoWFhXzxjvM .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-OciTVPoWFhXzxjvM .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-OciTVPoWFhXzxjvM .actorPopupMenu{position:absolute;}#mermaid-svg-OciTVPoWFhXzxjvM .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-OciTVPoWFhXzxjvM .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-OciTVPoWFhXzxjvM .actor-man circle,#mermaid-svg-OciTVPoWFhXzxjvM line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-OciTVPoWFhXzxjvM :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} open("/dev/video0") fd = 3 ioctl(VIDIOC_QUERYCAP) 查询设备能力 返回能力位图 cap = {driver, card, capabilities} ioctl(VIDIOC_ENUM_FMT, index=0) fmt = {YUYV, "YUYV 4:2:2"} ioctl(VIDIOC_ENUM_FMT, index=1) fmt = {MJPEG, "Motion-JPEG"} ioctl(VIDIOC_ENUM_FMT, index=2) 返回 -1 (没有更多格式) ioctl(VIDIOC_S_FMT, {640,480,YUYV}) 配置传感器 确认配置 format = {实际设置的值}

类比:ioctl 是 V4L2 的"万能遥控器按钮"------按不同的按钮(VIDIOC_xxx),传不同的参数(结构体),驱动就会执行对应的操作。

1.2.5 V4L2 与普通文件读写的对比

特性 普通文件读写 V4L2 视频采集
打开方式 open() + read() / write() open() + ioctl() 系列命令
数据流向 内核→用户(拷贝) 内核→用户(mmap 零拷贝)
缓冲机制 单缓冲区 多缓冲区环形队列
等待方式 阻塞式 read() select() / poll() 非阻塞等待
设备类型 文件系统 字符设备 /dev/video*
配置灵活性 可配置分辨率、帧率、格式等

1.3 V4L2 拍照 10 步流程

一句话总结

用 V4L2 拍照就是一套"10 步流程":打开设备 → 查能力 → 查格式 → 设格式 → 申请缓冲区 → 映射内存 → 入队 → 开流 → 等待数据 → 取帧。

1.3.1 完整流程图

#mermaid-svg-yrQ8L25Vet1cVKtc{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-yrQ8L25Vet1cVKtc .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-yrQ8L25Vet1cVKtc .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-yrQ8L25Vet1cVKtc .error-icon{fill:#552222;}#mermaid-svg-yrQ8L25Vet1cVKtc .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-yrQ8L25Vet1cVKtc .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-yrQ8L25Vet1cVKtc .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-yrQ8L25Vet1cVKtc .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-yrQ8L25Vet1cVKtc .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-yrQ8L25Vet1cVKtc .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-yrQ8L25Vet1cVKtc .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-yrQ8L25Vet1cVKtc .marker{fill:#333333;stroke:#333333;}#mermaid-svg-yrQ8L25Vet1cVKtc .marker.cross{stroke:#333333;}#mermaid-svg-yrQ8L25Vet1cVKtc svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-yrQ8L25Vet1cVKtc p{margin:0;}#mermaid-svg-yrQ8L25Vet1cVKtc .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-yrQ8L25Vet1cVKtc .cluster-label text{fill:#333;}#mermaid-svg-yrQ8L25Vet1cVKtc .cluster-label span{color:#333;}#mermaid-svg-yrQ8L25Vet1cVKtc .cluster-label span p{background-color:transparent;}#mermaid-svg-yrQ8L25Vet1cVKtc .label text,#mermaid-svg-yrQ8L25Vet1cVKtc span{fill:#333;color:#333;}#mermaid-svg-yrQ8L25Vet1cVKtc .node rect,#mermaid-svg-yrQ8L25Vet1cVKtc .node circle,#mermaid-svg-yrQ8L25Vet1cVKtc .node ellipse,#mermaid-svg-yrQ8L25Vet1cVKtc .node polygon,#mermaid-svg-yrQ8L25Vet1cVKtc .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-yrQ8L25Vet1cVKtc .rough-node .label text,#mermaid-svg-yrQ8L25Vet1cVKtc .node .label text,#mermaid-svg-yrQ8L25Vet1cVKtc .image-shape .label,#mermaid-svg-yrQ8L25Vet1cVKtc .icon-shape .label{text-anchor:middle;}#mermaid-svg-yrQ8L25Vet1cVKtc .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-yrQ8L25Vet1cVKtc .rough-node .label,#mermaid-svg-yrQ8L25Vet1cVKtc .node .label,#mermaid-svg-yrQ8L25Vet1cVKtc .image-shape .label,#mermaid-svg-yrQ8L25Vet1cVKtc .icon-shape .label{text-align:center;}#mermaid-svg-yrQ8L25Vet1cVKtc .node.clickable{cursor:pointer;}#mermaid-svg-yrQ8L25Vet1cVKtc .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-yrQ8L25Vet1cVKtc .arrowheadPath{fill:#333333;}#mermaid-svg-yrQ8L25Vet1cVKtc .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-yrQ8L25Vet1cVKtc .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-yrQ8L25Vet1cVKtc .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-yrQ8L25Vet1cVKtc .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-yrQ8L25Vet1cVKtc .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-yrQ8L25Vet1cVKtc .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-yrQ8L25Vet1cVKtc .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-yrQ8L25Vet1cVKtc .cluster text{fill:#333;}#mermaid-svg-yrQ8L25Vet1cVKtc .cluster span{color:#333;}#mermaid-svg-yrQ8L25Vet1cVKtc div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-yrQ8L25Vet1cVKtc .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-yrQ8L25Vet1cVKtc rect.text{fill:none;stroke-width:0;}#mermaid-svg-yrQ8L25Vet1cVKtc .icon-shape,#mermaid-svg-yrQ8L25Vet1cVKtc .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-yrQ8L25Vet1cVKtc .icon-shape p,#mermaid-svg-yrQ8L25Vet1cVKtc .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-yrQ8L25Vet1cVKtc .icon-shape .label rect,#mermaid-svg-yrQ8L25Vet1cVKtc .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-yrQ8L25Vet1cVKtc .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-yrQ8L25Vet1cVKtc .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-yrQ8L25Vet1cVKtc :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} open 设备
VIDIOC_QUERYCAP 查能力
VIDIOC_ENUM_FMT 枚举格式
VIDIOC_S_FMT 设置格式
VIDIOC_REQBUFS 申请缓冲区
VIDIOC_QUERYBUF + mmap 映射
VIDIOC_QBUF 入队
VIDIOC_STREAMON 开流
select 等待数据
VIDIOC_DQBUF 取帧
处理数据 YUV→RGB→JPG
VIDIOC_QBUF 重新入队

1.3.2 mmap 零拷贝原理

类比:没有 mmap 就像你去快递站取包裹,先要把包裹从货架搬到柜台(内核→用户拷贝),再从柜台拿回家。有了 mmap 就像快递站直接在你家墙上开了一扇门,你伸手就能从货架上拿。
#mermaid-svg-7LlEXDTcedjWBIwa{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-7LlEXDTcedjWBIwa .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-7LlEXDTcedjWBIwa .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-7LlEXDTcedjWBIwa .error-icon{fill:#552222;}#mermaid-svg-7LlEXDTcedjWBIwa .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-7LlEXDTcedjWBIwa .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-7LlEXDTcedjWBIwa .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-7LlEXDTcedjWBIwa .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-7LlEXDTcedjWBIwa .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-7LlEXDTcedjWBIwa .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-7LlEXDTcedjWBIwa .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-7LlEXDTcedjWBIwa .marker{fill:#333333;stroke:#333333;}#mermaid-svg-7LlEXDTcedjWBIwa .marker.cross{stroke:#333333;}#mermaid-svg-7LlEXDTcedjWBIwa svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-7LlEXDTcedjWBIwa p{margin:0;}#mermaid-svg-7LlEXDTcedjWBIwa .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-7LlEXDTcedjWBIwa .cluster-label text{fill:#333;}#mermaid-svg-7LlEXDTcedjWBIwa .cluster-label span{color:#333;}#mermaid-svg-7LlEXDTcedjWBIwa .cluster-label span p{background-color:transparent;}#mermaid-svg-7LlEXDTcedjWBIwa .label text,#mermaid-svg-7LlEXDTcedjWBIwa span{fill:#333;color:#333;}#mermaid-svg-7LlEXDTcedjWBIwa .node rect,#mermaid-svg-7LlEXDTcedjWBIwa .node circle,#mermaid-svg-7LlEXDTcedjWBIwa .node ellipse,#mermaid-svg-7LlEXDTcedjWBIwa .node polygon,#mermaid-svg-7LlEXDTcedjWBIwa .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-7LlEXDTcedjWBIwa .rough-node .label text,#mermaid-svg-7LlEXDTcedjWBIwa .node .label text,#mermaid-svg-7LlEXDTcedjWBIwa .image-shape .label,#mermaid-svg-7LlEXDTcedjWBIwa .icon-shape .label{text-anchor:middle;}#mermaid-svg-7LlEXDTcedjWBIwa .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-7LlEXDTcedjWBIwa .rough-node .label,#mermaid-svg-7LlEXDTcedjWBIwa .node .label,#mermaid-svg-7LlEXDTcedjWBIwa .image-shape .label,#mermaid-svg-7LlEXDTcedjWBIwa .icon-shape .label{text-align:center;}#mermaid-svg-7LlEXDTcedjWBIwa .node.clickable{cursor:pointer;}#mermaid-svg-7LlEXDTcedjWBIwa .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-7LlEXDTcedjWBIwa .arrowheadPath{fill:#333333;}#mermaid-svg-7LlEXDTcedjWBIwa .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-7LlEXDTcedjWBIwa .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-7LlEXDTcedjWBIwa .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-7LlEXDTcedjWBIwa .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-7LlEXDTcedjWBIwa .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-7LlEXDTcedjWBIwa .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-7LlEXDTcedjWBIwa .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-7LlEXDTcedjWBIwa .cluster text{fill:#333;}#mermaid-svg-7LlEXDTcedjWBIwa .cluster span{color:#333;}#mermaid-svg-7LlEXDTcedjWBIwa div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-7LlEXDTcedjWBIwa .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-7LlEXDTcedjWBIwa rect.text{fill:none;stroke-width:0;}#mermaid-svg-7LlEXDTcedjWBIwa .icon-shape,#mermaid-svg-7LlEXDTcedjWBIwa .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-7LlEXDTcedjWBIwa .icon-shape p,#mermaid-svg-7LlEXDTcedjWBIwa .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-7LlEXDTcedjWBIwa .icon-shape .label rect,#mermaid-svg-7LlEXDTcedjWBIwa .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-7LlEXDTcedjWBIwa .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-7LlEXDTcedjWBIwa .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-7LlEXDTcedjWBIwa :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 有mmap方案
mmap 映射

零拷贝
内核缓冲区
用户空间直接访问
CPU 省下时间

处理数据
无mmap方案
read 数据拷贝
内核缓冲区
用户空间缓冲区
CPU 浪费时间

在拷贝上

1.3.3 有 mmap 与无 mmap 对比

对比维度 无 mmap(read/write) 有 mmap
数据拷贝次数 2 次(内核→用户缓冲区) 0 次(直接映射)
CPU 占用 高,每次拷贝都占用 CPU 低,省下的 CPU 可用于处理数据
内存占用 需要两份缓冲区 只需要一份,共享访问
适用场景 单帧采集,数据量小 视频流采集,数据量大
实现复杂度 简单,直接 read() 稍微复杂,需要 mmap 配置
速度

1.3.3a V4L2 三种内存映射方式对比

V4L2 支持三种缓冲区管理方式,mmap 只是其中一种:

方式 原理 优点 缺点 适用场景
MMAP 内核分配缓冲区,映射到用户空间 使用简单,兼容性好 缓冲区由内核管理,灵活性低 绝大多数场景
USERPTR 用户分配缓冲区,传给内核使用 用户可管理内存池 需要内存对齐,部分硬件不支持 需要自定义内存管理
DMABUF 通过 DMA 缓冲区共享 零拷贝跨设备共享 实现复杂,需要硬件支持 GPU/VPU 协同处理

一句话总结:MMAP 是"内核帮你管内存",USERPTR 是"我自己管内存",DMABUF 是"我把内存直接给硬件用"。初学者用 MMAP 就够了,90% 的项目都只用到它。

1.3.4 缓冲区循环机制

4 个缓冲区就像流水线上的 4 个桶------驱动不断往空桶里装水,你不断拿走满桶,倒完水再放回流水线,循环往复,永不停止。
#mermaid-svg-2BDqLFttIi8Jd9dz{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-2BDqLFttIi8Jd9dz .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-2BDqLFttIi8Jd9dz .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-2BDqLFttIi8Jd9dz .error-icon{fill:#552222;}#mermaid-svg-2BDqLFttIi8Jd9dz .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-2BDqLFttIi8Jd9dz .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-2BDqLFttIi8Jd9dz .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-2BDqLFttIi8Jd9dz .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-2BDqLFttIi8Jd9dz .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-2BDqLFttIi8Jd9dz .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-2BDqLFttIi8Jd9dz .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-2BDqLFttIi8Jd9dz .marker{fill:#333333;stroke:#333333;}#mermaid-svg-2BDqLFttIi8Jd9dz .marker.cross{stroke:#333333;}#mermaid-svg-2BDqLFttIi8Jd9dz svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-2BDqLFttIi8Jd9dz p{margin:0;}#mermaid-svg-2BDqLFttIi8Jd9dz .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-2BDqLFttIi8Jd9dz .cluster-label text{fill:#333;}#mermaid-svg-2BDqLFttIi8Jd9dz .cluster-label span{color:#333;}#mermaid-svg-2BDqLFttIi8Jd9dz .cluster-label span p{background-color:transparent;}#mermaid-svg-2BDqLFttIi8Jd9dz .label text,#mermaid-svg-2BDqLFttIi8Jd9dz span{fill:#333;color:#333;}#mermaid-svg-2BDqLFttIi8Jd9dz .node rect,#mermaid-svg-2BDqLFttIi8Jd9dz .node circle,#mermaid-svg-2BDqLFttIi8Jd9dz .node ellipse,#mermaid-svg-2BDqLFttIi8Jd9dz .node polygon,#mermaid-svg-2BDqLFttIi8Jd9dz .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-2BDqLFttIi8Jd9dz .rough-node .label text,#mermaid-svg-2BDqLFttIi8Jd9dz .node .label text,#mermaid-svg-2BDqLFttIi8Jd9dz .image-shape .label,#mermaid-svg-2BDqLFttIi8Jd9dz .icon-shape .label{text-anchor:middle;}#mermaid-svg-2BDqLFttIi8Jd9dz .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-2BDqLFttIi8Jd9dz .rough-node .label,#mermaid-svg-2BDqLFttIi8Jd9dz .node .label,#mermaid-svg-2BDqLFttIi8Jd9dz .image-shape .label,#mermaid-svg-2BDqLFttIi8Jd9dz .icon-shape .label{text-align:center;}#mermaid-svg-2BDqLFttIi8Jd9dz .node.clickable{cursor:pointer;}#mermaid-svg-2BDqLFttIi8Jd9dz .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-2BDqLFttIi8Jd9dz .arrowheadPath{fill:#333333;}#mermaid-svg-2BDqLFttIi8Jd9dz .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-2BDqLFttIi8Jd9dz .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-2BDqLFttIi8Jd9dz .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-2BDqLFttIi8Jd9dz .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-2BDqLFttIi8Jd9dz .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-2BDqLFttIi8Jd9dz .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-2BDqLFttIi8Jd9dz .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-2BDqLFttIi8Jd9dz .cluster text{fill:#333;}#mermaid-svg-2BDqLFttIi8Jd9dz .cluster span{color:#333;}#mermaid-svg-2BDqLFttIi8Jd9dz div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-2BDqLFttIi8Jd9dz .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-2BDqLFttIi8Jd9dz rect.text{fill:none;stroke-width:0;}#mermaid-svg-2BDqLFttIi8Jd9dz .icon-shape,#mermaid-svg-2BDqLFttIi8Jd9dz .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-2BDqLFttIi8Jd9dz .icon-shape p,#mermaid-svg-2BDqLFttIi8Jd9dz .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-2BDqLFttIi8Jd9dz .icon-shape .label rect,#mermaid-svg-2BDqLFttIi8Jd9dz .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-2BDqLFttIi8Jd9dz .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-2BDqLFttIi8Jd9dz .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-2BDqLFttIi8Jd9dz :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 应用端
缓冲区池
驱动端
QBUF 入队
驱动填入数据
填充完成
DQBUF 出队
APP 读取
处理完 QBUF 重新入队
输入队列

空缓冲区等待填数据
摄像头驱动

采集一帧数据
缓冲区 0 空闲
缓冲区 1 已填满
缓冲区 2 已填满
缓冲区 3 空闲
应用程序

处理数据
输出队列

满缓冲区等待处理

1.3.5 select 等待机制

类比:select 就像你在餐厅等餐------你不用每分钟去问厨子"做好了没"(轮询),而是拿一个号码牌等着,叫到你再去取。select 就是那个"叫号系统"。
#mermaid-svg-h61q6rDARRWwc301{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-h61q6rDARRWwc301 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-h61q6rDARRWwc301 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-h61q6rDARRWwc301 .error-icon{fill:#552222;}#mermaid-svg-h61q6rDARRWwc301 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-h61q6rDARRWwc301 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-h61q6rDARRWwc301 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-h61q6rDARRWwc301 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-h61q6rDARRWwc301 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-h61q6rDARRWwc301 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-h61q6rDARRWwc301 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-h61q6rDARRWwc301 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-h61q6rDARRWwc301 .marker.cross{stroke:#333333;}#mermaid-svg-h61q6rDARRWwc301 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-h61q6rDARRWwc301 p{margin:0;}#mermaid-svg-h61q6rDARRWwc301 defs #statediagram-barbEnd{fill:#333333;stroke:#333333;}#mermaid-svg-h61q6rDARRWwc301 g.stateGroup text{fill:#9370DB;stroke:none;font-size:10px;}#mermaid-svg-h61q6rDARRWwc301 g.stateGroup text{fill:#333;stroke:none;font-size:10px;}#mermaid-svg-h61q6rDARRWwc301 g.stateGroup .state-title{font-weight:bolder;fill:#131300;}#mermaid-svg-h61q6rDARRWwc301 g.stateGroup rect{fill:#ECECFF;stroke:#9370DB;}#mermaid-svg-h61q6rDARRWwc301 g.stateGroup line{stroke:#333333;stroke-width:1;}#mermaid-svg-h61q6rDARRWwc301 .transition{stroke:#333333;stroke-width:1;fill:none;}#mermaid-svg-h61q6rDARRWwc301 .stateGroup .composit{fill:white;border-bottom:1px;}#mermaid-svg-h61q6rDARRWwc301 .stateGroup .alt-composit{fill:#e0e0e0;border-bottom:1px;}#mermaid-svg-h61q6rDARRWwc301 .state-note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-h61q6rDARRWwc301 .state-note text{fill:black;stroke:none;font-size:10px;}#mermaid-svg-h61q6rDARRWwc301 .stateLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.5;}#mermaid-svg-h61q6rDARRWwc301 .edgeLabel .label rect{fill:#ECECFF;opacity:0.5;}#mermaid-svg-h61q6rDARRWwc301 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-h61q6rDARRWwc301 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-h61q6rDARRWwc301 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-h61q6rDARRWwc301 .edgeLabel .label text{fill:#333;}#mermaid-svg-h61q6rDARRWwc301 .label div .edgeLabel{color:#333;}#mermaid-svg-h61q6rDARRWwc301 .stateLabel text{fill:#131300;font-size:10px;font-weight:bold;}#mermaid-svg-h61q6rDARRWwc301 .node circle.state-start{fill:#333333;stroke:#333333;}#mermaid-svg-h61q6rDARRWwc301 .node .fork-join{fill:#333333;stroke:#333333;}#mermaid-svg-h61q6rDARRWwc301 .node circle.state-end{fill:#9370DB;stroke:white;stroke-width:1.5;}#mermaid-svg-h61q6rDARRWwc301 .end-state-inner{fill:white;stroke-width:1.5;}#mermaid-svg-h61q6rDARRWwc301 .node rect{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-h61q6rDARRWwc301 .node polygon{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-h61q6rDARRWwc301 #statediagram-barbEnd{fill:#333333;}#mermaid-svg-h61q6rDARRWwc301 .statediagram-cluster rect{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-h61q6rDARRWwc301 .cluster-label,#mermaid-svg-h61q6rDARRWwc301 .nodeLabel{color:#131300;}#mermaid-svg-h61q6rDARRWwc301 .statediagram-cluster rect.outer{rx:5px;ry:5px;}#mermaid-svg-h61q6rDARRWwc301 .statediagram-state .divider{stroke:#9370DB;}#mermaid-svg-h61q6rDARRWwc301 .statediagram-state .title-state{rx:5px;ry:5px;}#mermaid-svg-h61q6rDARRWwc301 .statediagram-cluster.statediagram-cluster .inner{fill:white;}#mermaid-svg-h61q6rDARRWwc301 .statediagram-cluster.statediagram-cluster-alt .inner{fill:#f0f0f0;}#mermaid-svg-h61q6rDARRWwc301 .statediagram-cluster .inner{rx:0;ry:0;}#mermaid-svg-h61q6rDARRWwc301 .statediagram-state rect.basic{rx:5px;ry:5px;}#mermaid-svg-h61q6rDARRWwc301 .statediagram-state rect.divider{stroke-dasharray:10,10;fill:#f0f0f0;}#mermaid-svg-h61q6rDARRWwc301 .note-edge{stroke-dasharray:5;}#mermaid-svg-h61q6rDARRWwc301 .statediagram-note rect{fill:#fff5ad;stroke:#aaaa33;stroke-width:1px;rx:0;ry:0;}#mermaid-svg-h61q6rDARRWwc301 .statediagram-note rect{fill:#fff5ad;stroke:#aaaa33;stroke-width:1px;rx:0;ry:0;}#mermaid-svg-h61q6rDARRWwc301 .statediagram-note text{fill:black;}#mermaid-svg-h61q6rDARRWwc301 .statediagram-note .nodeLabel{color:black;}#mermaid-svg-h61q6rDARRWwc301 .statediagram .edgeLabel{color:red;}#mermaid-svg-h61q6rDARRWwc301 #dependencyStart,#mermaid-svg-h61q6rDARRWwc301 #dependencyEnd{fill:#333333;stroke:#333333;stroke-width:1;}#mermaid-svg-h61q6rDARRWwc301 .statediagramTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-h61q6rDARRWwc301 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} select(fd)
摄像头数据到来
20秒无数据
设备异常
FD_ISSET 检查
YUV→RGB→JPG
重新 QBUF 入队
等待数据
数据就绪
超时返回
出错返回
读取帧
处理数据
处理帧

1.3.5a select 与 poll 对比

在实际嵌入式开发中,select()poll() 都可以用来等待数据,两者各有优劣。

对比维度 select() poll()
最大监视数 FD_SETSIZE(通常 1024) 无限制
数据结构 fd_set 位图 struct pollfd 数组
可读性 宏操作(FD_SET/FD_ISSET)较繁琐 结构体成员直观
性能(大量 fd) 每次遍历全部 fd,O(n) 每次遍历全部 fd,O(n)
超时精度 微秒(struct timeval) 毫秒(int)
POSIX 标准 ✅ 是 ✅ 是
嵌入式适用性 ✅ 简单场景,fd 少时推荐 ✅ 大量 fd 或有特殊需求时推荐

一句话总结:监视 1 个摄像头 fd 时用 select 完全够用且代码更简单;如果需要同时监视几十个 fd(如多路摄像头),建议用 poll 以避免 1024 上限。

1.3.6 完整数据流时序

内核缓冲区 摄像头 V4L2 驱动 应用程序 内核缓冲区 摄像头 V4L2 驱动 应用程序 #mermaid-svg-T11hRcVSyAITMBBN{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-T11hRcVSyAITMBBN .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-T11hRcVSyAITMBBN .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-T11hRcVSyAITMBBN .error-icon{fill:#552222;}#mermaid-svg-T11hRcVSyAITMBBN .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-T11hRcVSyAITMBBN .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-T11hRcVSyAITMBBN .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-T11hRcVSyAITMBBN .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-T11hRcVSyAITMBBN .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-T11hRcVSyAITMBBN .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-T11hRcVSyAITMBBN .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-T11hRcVSyAITMBBN .marker{fill:#333333;stroke:#333333;}#mermaid-svg-T11hRcVSyAITMBBN .marker.cross{stroke:#333333;}#mermaid-svg-T11hRcVSyAITMBBN svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-T11hRcVSyAITMBBN p{margin:0;}#mermaid-svg-T11hRcVSyAITMBBN .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-T11hRcVSyAITMBBN text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-T11hRcVSyAITMBBN .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-T11hRcVSyAITMBBN .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-T11hRcVSyAITMBBN .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-T11hRcVSyAITMBBN .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-T11hRcVSyAITMBBN #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-T11hRcVSyAITMBBN .sequenceNumber{fill:white;}#mermaid-svg-T11hRcVSyAITMBBN #sequencenumber{fill:#333;}#mermaid-svg-T11hRcVSyAITMBBN #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-T11hRcVSyAITMBBN .messageText{fill:#333;stroke:none;}#mermaid-svg-T11hRcVSyAITMBBN .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-T11hRcVSyAITMBBN .labelText,#mermaid-svg-T11hRcVSyAITMBBN .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-T11hRcVSyAITMBBN .loopText,#mermaid-svg-T11hRcVSyAITMBBN .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-T11hRcVSyAITMBBN .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-T11hRcVSyAITMBBN .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-T11hRcVSyAITMBBN .noteText,#mermaid-svg-T11hRcVSyAITMBBN .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-T11hRcVSyAITMBBN .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-T11hRcVSyAITMBBN .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-T11hRcVSyAITMBBN .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-T11hRcVSyAITMBBN .actorPopupMenu{position:absolute;}#mermaid-svg-T11hRcVSyAITMBBN .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-T11hRcVSyAITMBBN .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-T11hRcVSyAITMBBN .actor-man circle,#mermaid-svg-T11hRcVSyAITMBBN line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-T11hRcVSyAITMBBN :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} loop 映射 4 个缓冲区 loop 入队 4 个缓冲区 YUYV → RGB → JPEG open(/dev/video0) fd VIDIOC_QUERYCAP 确认支持 CAPTURE VIDIOC_S_FMT (640x480 YUYV) 格式已设置 VIDIOC_REQBUFS (count=4) 分配 4 个缓冲区 VIDIOC_QUERYBUF (index=i) 缓冲区信息 mmap 映射到用户空间 VIDIOC_QBUF (index=i) VIDIOC_STREAMON 启动采集 填入第一帧数据 select 等待 数据就绪! VIDIOC_DQBUF 取出满缓冲区 VIDIOC_QBUF (重新入队)


第二章 图像格式与转换

2.1 YUV 格式详解

一句话总结

YUV 把颜色拆成"亮度(Y)"和"色度(UV)"两部分存储,利用人眼对亮度敏感、对色彩不敏感的特点,通过让多个像素共用 UV 分量来节省 50% 的存储空间。

类比:YUV 就像"黑白素描本 + 彩色画笔"------Y 是素描的轮廓线(亮度),UV 是用来上色的颜料(颜色)。即使你的彩色笔颜色很淡(UV 精度低),素描轮廓依然清晰。

2.1.0a 学术辨析:YUV 与 YCbCr 的区别

注意:严格来说,本文讨论的"YUV"实际上是 YCbCr。这是一个常见的学术混淆,值得专门澄清。

概念 全称 标准来源 数值范围 应用场景
YUV Y Luma + U/V Chrominance 模拟电视(PAL/NTSC) 连续模拟值 历史遗留,模拟视频
YCbCr Y Luma + Cb/Cr Chroma 数字视频标准(ITU-R BT.601) 量化后的数字值 数字摄像头、JPEG、H.264

两者关系 :YCbCr 是 YUV 的数字化版本。当我们在代码中操作 unsigned char Y, Cb, Cr 时,实际上是在操作 YCbCr。但业界(包括摄像头厂商、V4L2 文档)约定俗成地统称为"YUV",本文也沿用这个惯例。

一句话总结:YUV 是"模拟时代的名字",YCbCr 是"数字时代的真身"------就像你身份证上的名字和你的网名,大家叫惯了也就不改了。

2.1.1 为什么不用 RGB?

RGB 存储的 3 个痛点

  1. 数据量大:每个像素 3 字节,一张 640×480 的图 ≈ 900KB,不利于传输和存储
  2. 兼容性差:几乎所有摄像头原生输出 YUV,而不是 RGB
  3. 不支持亮度调节:RGB 在光线昏暗时难以区分颜色

类比:RGB 存储每个像素的 R/G/B 三个值,就像给每个学生单独发一份教材;YUV 则是让相邻 4 个学生共用一份教材------因为人眼对颜色细节不敏感,看不太出来区别,但存储空间省了一半。

2.1.2 RGB 与 YUV 对比

对比维度 RGB YUV
存储方式 每个像素 R/G/B 各 1 字节 Y 亮度 + UV 色度分离
每像素字节数 3 字节(固定) 1.5~3 字节(可调)
人眼适配 均匀分配,不区分敏感度 亮度高精度 + 色度低精度
亮度调节 需要同时调整 R/G/B 三个分量 只需调整 Y 分量
摄像头输出 少数摄像头支持 几乎所有摄像头原生输出
兼容性 显示器直接使用 需要转换后才能显示
典型应用 图像处理、显示 视频传输、摄像头采集

2.1.3 YUV 三平面可视化

#mermaid-svg-RUbESFVRgYCoiJef{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-RUbESFVRgYCoiJef .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-RUbESFVRgYCoiJef .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-RUbESFVRgYCoiJef .error-icon{fill:#552222;}#mermaid-svg-RUbESFVRgYCoiJef .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-RUbESFVRgYCoiJef .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-RUbESFVRgYCoiJef .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-RUbESFVRgYCoiJef .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-RUbESFVRgYCoiJef .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-RUbESFVRgYCoiJef .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-RUbESFVRgYCoiJef .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-RUbESFVRgYCoiJef .marker{fill:#333333;stroke:#333333;}#mermaid-svg-RUbESFVRgYCoiJef .marker.cross{stroke:#333333;}#mermaid-svg-RUbESFVRgYCoiJef svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-RUbESFVRgYCoiJef p{margin:0;}#mermaid-svg-RUbESFVRgYCoiJef .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-RUbESFVRgYCoiJef .cluster-label text{fill:#333;}#mermaid-svg-RUbESFVRgYCoiJef .cluster-label span{color:#333;}#mermaid-svg-RUbESFVRgYCoiJef .cluster-label span p{background-color:transparent;}#mermaid-svg-RUbESFVRgYCoiJef .label text,#mermaid-svg-RUbESFVRgYCoiJef span{fill:#333;color:#333;}#mermaid-svg-RUbESFVRgYCoiJef .node rect,#mermaid-svg-RUbESFVRgYCoiJef .node circle,#mermaid-svg-RUbESFVRgYCoiJef .node ellipse,#mermaid-svg-RUbESFVRgYCoiJef .node polygon,#mermaid-svg-RUbESFVRgYCoiJef .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-RUbESFVRgYCoiJef .rough-node .label text,#mermaid-svg-RUbESFVRgYCoiJef .node .label text,#mermaid-svg-RUbESFVRgYCoiJef .image-shape .label,#mermaid-svg-RUbESFVRgYCoiJef .icon-shape .label{text-anchor:middle;}#mermaid-svg-RUbESFVRgYCoiJef .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-RUbESFVRgYCoiJef .rough-node .label,#mermaid-svg-RUbESFVRgYCoiJef .node .label,#mermaid-svg-RUbESFVRgYCoiJef .image-shape .label,#mermaid-svg-RUbESFVRgYCoiJef .icon-shape .label{text-align:center;}#mermaid-svg-RUbESFVRgYCoiJef .node.clickable{cursor:pointer;}#mermaid-svg-RUbESFVRgYCoiJef .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-RUbESFVRgYCoiJef .arrowheadPath{fill:#333333;}#mermaid-svg-RUbESFVRgYCoiJef .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-RUbESFVRgYCoiJef .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-RUbESFVRgYCoiJef .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-RUbESFVRgYCoiJef .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-RUbESFVRgYCoiJef .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-RUbESFVRgYCoiJef .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-RUbESFVRgYCoiJef .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-RUbESFVRgYCoiJef .cluster text{fill:#333;}#mermaid-svg-RUbESFVRgYCoiJef .cluster span{color:#333;}#mermaid-svg-RUbESFVRgYCoiJef div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-RUbESFVRgYCoiJef .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-RUbESFVRgYCoiJef rect.text{fill:none;stroke-width:0;}#mermaid-svg-RUbESFVRgYCoiJef .icon-shape,#mermaid-svg-RUbESFVRgYCoiJef .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-RUbESFVRgYCoiJef .icon-shape p,#mermaid-svg-RUbESFVRgYCoiJef .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-RUbESFVRgYCoiJef .icon-shape .label rect,#mermaid-svg-RUbESFVRgYCoiJef .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-RUbESFVRgYCoiJef .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-RUbESFVRgYCoiJef .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-RUbESFVRgYCoiJef :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 重建
拆分为三个平面
原始彩色图像
彩色图片

640×480
Y 平面

亮度图

就像黑白照片

640×480 每个像素

都有 Y 值
U 平面

蓝色色度

模糊的蓝色色调图

4:2:0 下仅

320×240 个 U 值
V 平面

红色色度

模糊的红色色调图

4:2:0 下仅

320×240 个 V 值
重新合成

Y + U + V = 彩色

一句话总结:YUV 把一张彩色图拆成"黑白轮廓图(Y)"+"蓝色色调图(U)"+"红色色调图(V)"三张图,合成回去就是彩色。UV 分辨率可以比 Y 低,因为人眼对彩色细节不敏感。

2.1.4 三种采样方式对比

采样方式 每 4 像素所需数据 节省比例 画质 典型应用
YUV 4:4:4 12 字节 0%(同 RGB) 无损 专业图像处理
YUV 4:2:2 8 字节 33% 广播级视频
YUV 4:2:0 6 字节 50% 良好 视频通话、流媒体

注意:4:2:0 是最常用的格式,视频通话和流媒体几乎都用它------当像素足够密集时,人眼几乎看不出 4:2:0 和 4:4:4 的区别,但文件大小差了一倍。
类比:4:4:4 就像 4 个人各吃一份套餐(每份都带饮料 UV);4:2:2 就像两个人共用一杯饮料;4:2:0 就像四个人共用一杯饮料------省钱了,但大家喝同一杯。

2.1.5 三种采样方式可视化

#mermaid-svg-XNEn2R9tSzbYhUo7{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-XNEn2R9tSzbYhUo7 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-XNEn2R9tSzbYhUo7 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-XNEn2R9tSzbYhUo7 .error-icon{fill:#552222;}#mermaid-svg-XNEn2R9tSzbYhUo7 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-XNEn2R9tSzbYhUo7 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-XNEn2R9tSzbYhUo7 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-XNEn2R9tSzbYhUo7 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-XNEn2R9tSzbYhUo7 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-XNEn2R9tSzbYhUo7 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-XNEn2R9tSzbYhUo7 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-XNEn2R9tSzbYhUo7 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-XNEn2R9tSzbYhUo7 .marker.cross{stroke:#333333;}#mermaid-svg-XNEn2R9tSzbYhUo7 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-XNEn2R9tSzbYhUo7 p{margin:0;}#mermaid-svg-XNEn2R9tSzbYhUo7 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-XNEn2R9tSzbYhUo7 .cluster-label text{fill:#333;}#mermaid-svg-XNEn2R9tSzbYhUo7 .cluster-label span{color:#333;}#mermaid-svg-XNEn2R9tSzbYhUo7 .cluster-label span p{background-color:transparent;}#mermaid-svg-XNEn2R9tSzbYhUo7 .label text,#mermaid-svg-XNEn2R9tSzbYhUo7 span{fill:#333;color:#333;}#mermaid-svg-XNEn2R9tSzbYhUo7 .node rect,#mermaid-svg-XNEn2R9tSzbYhUo7 .node circle,#mermaid-svg-XNEn2R9tSzbYhUo7 .node ellipse,#mermaid-svg-XNEn2R9tSzbYhUo7 .node polygon,#mermaid-svg-XNEn2R9tSzbYhUo7 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-XNEn2R9tSzbYhUo7 .rough-node .label text,#mermaid-svg-XNEn2R9tSzbYhUo7 .node .label text,#mermaid-svg-XNEn2R9tSzbYhUo7 .image-shape .label,#mermaid-svg-XNEn2R9tSzbYhUo7 .icon-shape .label{text-anchor:middle;}#mermaid-svg-XNEn2R9tSzbYhUo7 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-XNEn2R9tSzbYhUo7 .rough-node .label,#mermaid-svg-XNEn2R9tSzbYhUo7 .node .label,#mermaid-svg-XNEn2R9tSzbYhUo7 .image-shape .label,#mermaid-svg-XNEn2R9tSzbYhUo7 .icon-shape .label{text-align:center;}#mermaid-svg-XNEn2R9tSzbYhUo7 .node.clickable{cursor:pointer;}#mermaid-svg-XNEn2R9tSzbYhUo7 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-XNEn2R9tSzbYhUo7 .arrowheadPath{fill:#333333;}#mermaid-svg-XNEn2R9tSzbYhUo7 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-XNEn2R9tSzbYhUo7 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-XNEn2R9tSzbYhUo7 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-XNEn2R9tSzbYhUo7 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-XNEn2R9tSzbYhUo7 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-XNEn2R9tSzbYhUo7 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-XNEn2R9tSzbYhUo7 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-XNEn2R9tSzbYhUo7 .cluster text{fill:#333;}#mermaid-svg-XNEn2R9tSzbYhUo7 .cluster span{color:#333;}#mermaid-svg-XNEn2R9tSzbYhUo7 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-XNEn2R9tSzbYhUo7 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-XNEn2R9tSzbYhUo7 rect.text{fill:none;stroke-width:0;}#mermaid-svg-XNEn2R9tSzbYhUo7 .icon-shape,#mermaid-svg-XNEn2R9tSzbYhUo7 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-XNEn2R9tSzbYhUo7 .icon-shape p,#mermaid-svg-XNEn2R9tSzbYhUo7 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-XNEn2R9tSzbYhUo7 .icon-shape .label rect,#mermaid-svg-XNEn2R9tSzbYhUo7 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-XNEn2R9tSzbYhUo7 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-XNEn2R9tSzbYhUo7 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-XNEn2R9tSzbYhUo7 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} YUV 4:2:0 --- 6 字节 --- 省 50%
像素0

Y 共用UV
像素1

Y 共用UV
像素2

Y 共用UV
像素3

Y 共用UV
YUV 4:2:2 --- 8 字节 --- 省 33%
像素0

Y U V
像素1

Y 共用UV
像素2

Y U V
像素3

Y 共用UV
YUV 4:4:4 --- 12 字节 --- 无损
像素0

Y U V
像素1

Y U V
像素2

Y U V
像素3

Y U V

2.1.6 数据量对比

#mermaid-svg-QUbXaDd7PNY54kgp{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-QUbXaDd7PNY54kgp .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-QUbXaDd7PNY54kgp .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-QUbXaDd7PNY54kgp .error-icon{fill:#552222;}#mermaid-svg-QUbXaDd7PNY54kgp .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-QUbXaDd7PNY54kgp .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-QUbXaDd7PNY54kgp .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-QUbXaDd7PNY54kgp .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-QUbXaDd7PNY54kgp .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-QUbXaDd7PNY54kgp .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-QUbXaDd7PNY54kgp .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-QUbXaDd7PNY54kgp .marker{fill:#333333;stroke:#333333;}#mermaid-svg-QUbXaDd7PNY54kgp .marker.cross{stroke:#333333;}#mermaid-svg-QUbXaDd7PNY54kgp svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-QUbXaDd7PNY54kgp p{margin:0;}#mermaid-svg-QUbXaDd7PNY54kgp .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-QUbXaDd7PNY54kgp .cluster-label text{fill:#333;}#mermaid-svg-QUbXaDd7PNY54kgp .cluster-label span{color:#333;}#mermaid-svg-QUbXaDd7PNY54kgp .cluster-label span p{background-color:transparent;}#mermaid-svg-QUbXaDd7PNY54kgp .label text,#mermaid-svg-QUbXaDd7PNY54kgp span{fill:#333;color:#333;}#mermaid-svg-QUbXaDd7PNY54kgp .node rect,#mermaid-svg-QUbXaDd7PNY54kgp .node circle,#mermaid-svg-QUbXaDd7PNY54kgp .node ellipse,#mermaid-svg-QUbXaDd7PNY54kgp .node polygon,#mermaid-svg-QUbXaDd7PNY54kgp .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-QUbXaDd7PNY54kgp .rough-node .label text,#mermaid-svg-QUbXaDd7PNY54kgp .node .label text,#mermaid-svg-QUbXaDd7PNY54kgp .image-shape .label,#mermaid-svg-QUbXaDd7PNY54kgp .icon-shape .label{text-anchor:middle;}#mermaid-svg-QUbXaDd7PNY54kgp .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-QUbXaDd7PNY54kgp .rough-node .label,#mermaid-svg-QUbXaDd7PNY54kgp .node .label,#mermaid-svg-QUbXaDd7PNY54kgp .image-shape .label,#mermaid-svg-QUbXaDd7PNY54kgp .icon-shape .label{text-align:center;}#mermaid-svg-QUbXaDd7PNY54kgp .node.clickable{cursor:pointer;}#mermaid-svg-QUbXaDd7PNY54kgp .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-QUbXaDd7PNY54kgp .arrowheadPath{fill:#333333;}#mermaid-svg-QUbXaDd7PNY54kgp .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-QUbXaDd7PNY54kgp .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-QUbXaDd7PNY54kgp .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-QUbXaDd7PNY54kgp .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-QUbXaDd7PNY54kgp .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-QUbXaDd7PNY54kgp .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-QUbXaDd7PNY54kgp .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-QUbXaDd7PNY54kgp .cluster text{fill:#333;}#mermaid-svg-QUbXaDd7PNY54kgp .cluster span{color:#333;}#mermaid-svg-QUbXaDd7PNY54kgp div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-QUbXaDd7PNY54kgp .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-QUbXaDd7PNY54kgp rect.text{fill:none;stroke-width:0;}#mermaid-svg-QUbXaDd7PNY54kgp .icon-shape,#mermaid-svg-QUbXaDd7PNY54kgp .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-QUbXaDd7PNY54kgp .icon-shape p,#mermaid-svg-QUbXaDd7PNY54kgp .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-QUbXaDd7PNY54kgp .icon-shape .label rect,#mermaid-svg-QUbXaDd7PNY54kgp .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-QUbXaDd7PNY54kgp .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-QUbXaDd7PNY54kgp .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-QUbXaDd7PNY54kgp :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 640x480 一帧大小
每像素字节数
RGB

3.0 B/px
YUV 4:4:4

3.0 B/px
YUV 4:2:2

2.0 B/px
YUV 4:2:0

1.5 B/px
RGB

921 KB
YUV 4:4:4

921 KB
YUV 4:2:2

614 KB
YUV 4:2:0

460 KB

2.1.6a 色彩深度:8-bit vs 10-bit vs 12-bit

色彩深度决定了每个颜色分量用多少位(bit)来表示,直接影响画质的细腻程度。

色彩深度 每像素位数 每通道灰度级 总颜色数 文件大小 典型应用
8-bit 24 0~255 1,670 万 基准 嵌入式摄像头、JPEG
10-bit 30 0~1023 10.7 亿 +25% 广播级视频、H.265
12-bit 36 0~4095 687 亿 +50% RAW 摄影、专业影视

注意:本文讨论的摄像头输出均为 8-bit,这是嵌入式设备最主流的选择。10-bit 以上需要更宽的带宽和更强的处理能力,通常只在高端应用中见到。

2.1.7 两种存储方式对比

存储方式 排列方式 内存布局 优点 缺点 典型格式
Planar(平面) 所有 Y 连续→所有 U 连续→所有 V 连续 YYYYYYYY UUUU VVVV 适合压缩算法 需要多通道访问 I420、NV12
Packed(打包) 每个像素的 YUV 连续存放 YUYV YUYV YUYV 访问简单,缓存友好 压缩效率略低 YUYV、UYVY

类比:Planar 就像把全班同学按性别排成三队------所有男生一队(Y)、所有女生一队(U)、所有老师一队(V)。Packed 就像一男一女一老师交替排队走。

2.1.8 Planar vs Packed 可视化

#mermaid-svg-Bv0S7anrMcwWOndl{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-Bv0S7anrMcwWOndl .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-Bv0S7anrMcwWOndl .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-Bv0S7anrMcwWOndl .error-icon{fill:#552222;}#mermaid-svg-Bv0S7anrMcwWOndl .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Bv0S7anrMcwWOndl .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-Bv0S7anrMcwWOndl .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Bv0S7anrMcwWOndl .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Bv0S7anrMcwWOndl .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-Bv0S7anrMcwWOndl .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Bv0S7anrMcwWOndl .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Bv0S7anrMcwWOndl .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Bv0S7anrMcwWOndl .marker.cross{stroke:#333333;}#mermaid-svg-Bv0S7anrMcwWOndl svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Bv0S7anrMcwWOndl p{margin:0;}#mermaid-svg-Bv0S7anrMcwWOndl .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-Bv0S7anrMcwWOndl .cluster-label text{fill:#333;}#mermaid-svg-Bv0S7anrMcwWOndl .cluster-label span{color:#333;}#mermaid-svg-Bv0S7anrMcwWOndl .cluster-label span p{background-color:transparent;}#mermaid-svg-Bv0S7anrMcwWOndl .label text,#mermaid-svg-Bv0S7anrMcwWOndl span{fill:#333;color:#333;}#mermaid-svg-Bv0S7anrMcwWOndl .node rect,#mermaid-svg-Bv0S7anrMcwWOndl .node circle,#mermaid-svg-Bv0S7anrMcwWOndl .node ellipse,#mermaid-svg-Bv0S7anrMcwWOndl .node polygon,#mermaid-svg-Bv0S7anrMcwWOndl .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-Bv0S7anrMcwWOndl .rough-node .label text,#mermaid-svg-Bv0S7anrMcwWOndl .node .label text,#mermaid-svg-Bv0S7anrMcwWOndl .image-shape .label,#mermaid-svg-Bv0S7anrMcwWOndl .icon-shape .label{text-anchor:middle;}#mermaid-svg-Bv0S7anrMcwWOndl .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-Bv0S7anrMcwWOndl .rough-node .label,#mermaid-svg-Bv0S7anrMcwWOndl .node .label,#mermaid-svg-Bv0S7anrMcwWOndl .image-shape .label,#mermaid-svg-Bv0S7anrMcwWOndl .icon-shape .label{text-align:center;}#mermaid-svg-Bv0S7anrMcwWOndl .node.clickable{cursor:pointer;}#mermaid-svg-Bv0S7anrMcwWOndl .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-Bv0S7anrMcwWOndl .arrowheadPath{fill:#333333;}#mermaid-svg-Bv0S7anrMcwWOndl .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-Bv0S7anrMcwWOndl .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-Bv0S7anrMcwWOndl .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Bv0S7anrMcwWOndl .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-Bv0S7anrMcwWOndl .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Bv0S7anrMcwWOndl .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-Bv0S7anrMcwWOndl .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-Bv0S7anrMcwWOndl .cluster text{fill:#333;}#mermaid-svg-Bv0S7anrMcwWOndl .cluster span{color:#333;}#mermaid-svg-Bv0S7anrMcwWOndl div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-Bv0S7anrMcwWOndl .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-Bv0S7anrMcwWOndl rect.text{fill:none;stroke-width:0;}#mermaid-svg-Bv0S7anrMcwWOndl .icon-shape,#mermaid-svg-Bv0S7anrMcwWOndl .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Bv0S7anrMcwWOndl .icon-shape p,#mermaid-svg-Bv0S7anrMcwWOndl .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-Bv0S7anrMcwWOndl .icon-shape .label rect,#mermaid-svg-Bv0S7anrMcwWOndl .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Bv0S7anrMcwWOndl .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-Bv0S7anrMcwWOndl .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-Bv0S7anrMcwWOndl :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Packed 存储
Packed 打包存储
Y0 U0 Y1 V0 | Y2 U1 Y3 V1 | Y4 U2 Y5 V2 | Y6 U3 Y7 V3

每个像素的 YUV 挨着放
Planar 存储
Planar 平面存储
Y 区

Y0 Y1 Y2 Y3 Y4 Y5 Y6 Y7

所有 Y 连续排在一起
U 区

U0 U1 U2 U3

所有 U 连续排在一起
V 区

V0 V1 V2 V3

所有 V 连续排在一起

2.1.8a 补充:NV12 / NV21 格式(Android 标准格式)

除了 YUYV(Packed 4:2:2)之外,嵌入式领域还有一种非常常见的格式------NV12,它是 Android 系统 Camera2 API 的默认输出格式,也是视频编码器(如 H.264/H.265)最常用的输入格式。

NV12 的存储方式(Planar 变体------半平面 Semi-Planar):

  • Y 平面:所有 Y 值连续存储(640×480 = 307,200 字节)

  • UV 平面:U 和 V 交错存储,U 在前 V 在后(320×240 × 2 = 153,600 字节)

    内存布局:YYYYYYYYYYYY...(Y 平面,连续)
    UVUVUVUVUV...(UV 平面,交错)

NV21 与 NV12 的区别仅在于 UV 平面的顺序------NV21 是 V 在前 U 在后(VUVUVU...),这是 Android 早期版本的标准。

格式 类型 UV 排列 常见于
NV12 Semi-Planar U 先 V 后 Android Camera2、视频编码器
NV21 Semi-Planar V 先 U 后 Android 旧版 Camera1
I420 Full Planar Y 平面 + U 平面 + V 平面 视频会议、FFmpeg

类比:NV12 就像 Planar 的"变形版"------Y 仍然是一整队男生,但 U 和 V 不再分两队,而是男女搭配站成一队。

2.1.9 YUYV(4:2:2 Packed)字节级布局

这是实际摄像头最常输出的格式------每 4 个字节描述 2 个像素。字节索引 0=Y0, 1=U, 2=Y1, 3=V。两个像素各取各的 Y,但共享 U 和 V。
#mermaid-svg-XSWxwGnJoWUNnlQI{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-XSWxwGnJoWUNnlQI .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-XSWxwGnJoWUNnlQI .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-XSWxwGnJoWUNnlQI .error-icon{fill:#552222;}#mermaid-svg-XSWxwGnJoWUNnlQI .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-XSWxwGnJoWUNnlQI .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-XSWxwGnJoWUNnlQI .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-XSWxwGnJoWUNnlQI .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-XSWxwGnJoWUNnlQI .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-XSWxwGnJoWUNnlQI .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-XSWxwGnJoWUNnlQI .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-XSWxwGnJoWUNnlQI .marker{fill:#333333;stroke:#333333;}#mermaid-svg-XSWxwGnJoWUNnlQI .marker.cross{stroke:#333333;}#mermaid-svg-XSWxwGnJoWUNnlQI svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-XSWxwGnJoWUNnlQI p{margin:0;}#mermaid-svg-XSWxwGnJoWUNnlQI .mermaid-main-font{font-family:"trebuchet ms",verdana,arial,sans-serif;}#mermaid-svg-XSWxwGnJoWUNnlQI .exclude-range{fill:#eeeeee;}#mermaid-svg-XSWxwGnJoWUNnlQI .section{stroke:none;opacity:0.2;}#mermaid-svg-XSWxwGnJoWUNnlQI .section0{fill:rgba(102, 102, 255, 0.49);}#mermaid-svg-XSWxwGnJoWUNnlQI .section2{fill:#fff400;}#mermaid-svg-XSWxwGnJoWUNnlQI .section1,#mermaid-svg-XSWxwGnJoWUNnlQI .section3{fill:white;opacity:0.2;}#mermaid-svg-XSWxwGnJoWUNnlQI .sectionTitle0{fill:#333;}#mermaid-svg-XSWxwGnJoWUNnlQI .sectionTitle1{fill:#333;}#mermaid-svg-XSWxwGnJoWUNnlQI .sectionTitle2{fill:#333;}#mermaid-svg-XSWxwGnJoWUNnlQI .sectionTitle3{fill:#333;}#mermaid-svg-XSWxwGnJoWUNnlQI .sectionTitle{text-anchor:start;font-family:"trebuchet ms",verdana,arial,sans-serif;}#mermaid-svg-XSWxwGnJoWUNnlQI .grid .tick{stroke:lightgrey;opacity:0.8;shape-rendering:crispEdges;}#mermaid-svg-XSWxwGnJoWUNnlQI .grid .tick text{font-family:"trebuchet ms",verdana,arial,sans-serif;fill:#333;}#mermaid-svg-XSWxwGnJoWUNnlQI .grid path{stroke-width:0;}#mermaid-svg-XSWxwGnJoWUNnlQI .today{fill:none;stroke:red;stroke-width:2px;}#mermaid-svg-XSWxwGnJoWUNnlQI .task{stroke-width:2;}#mermaid-svg-XSWxwGnJoWUNnlQI .taskText{text-anchor:middle;font-family:"trebuchet ms",verdana,arial,sans-serif;}#mermaid-svg-XSWxwGnJoWUNnlQI .taskTextOutsideRight{fill:black;text-anchor:start;font-family:"trebuchet ms",verdana,arial,sans-serif;}#mermaid-svg-XSWxwGnJoWUNnlQI .taskTextOutsideLeft{fill:black;text-anchor:end;}#mermaid-svg-XSWxwGnJoWUNnlQI .task.clickable{cursor:pointer;}#mermaid-svg-XSWxwGnJoWUNnlQI .taskText.clickable{cursor:pointer;fill:#003163!important;font-weight:bold;}#mermaid-svg-XSWxwGnJoWUNnlQI .taskTextOutsideLeft.clickable{cursor:pointer;fill:#003163!important;font-weight:bold;}#mermaid-svg-XSWxwGnJoWUNnlQI .taskTextOutsideRight.clickable{cursor:pointer;fill:#003163!important;font-weight:bold;}#mermaid-svg-XSWxwGnJoWUNnlQI .taskText0,#mermaid-svg-XSWxwGnJoWUNnlQI .taskText1,#mermaid-svg-XSWxwGnJoWUNnlQI .taskText2,#mermaid-svg-XSWxwGnJoWUNnlQI .taskText3{fill:white;}#mermaid-svg-XSWxwGnJoWUNnlQI .task0,#mermaid-svg-XSWxwGnJoWUNnlQI .task1,#mermaid-svg-XSWxwGnJoWUNnlQI .task2,#mermaid-svg-XSWxwGnJoWUNnlQI .task3{fill:#8a90dd;stroke:#534fbc;}#mermaid-svg-XSWxwGnJoWUNnlQI .taskTextOutside0,#mermaid-svg-XSWxwGnJoWUNnlQI .taskTextOutside2{fill:black;}#mermaid-svg-XSWxwGnJoWUNnlQI .taskTextOutside1,#mermaid-svg-XSWxwGnJoWUNnlQI .taskTextOutside3{fill:black;}#mermaid-svg-XSWxwGnJoWUNnlQI .active0,#mermaid-svg-XSWxwGnJoWUNnlQI .active1,#mermaid-svg-XSWxwGnJoWUNnlQI .active2,#mermaid-svg-XSWxwGnJoWUNnlQI .active3{fill:#bfc7ff;stroke:#534fbc;}#mermaid-svg-XSWxwGnJoWUNnlQI .activeText0,#mermaid-svg-XSWxwGnJoWUNnlQI .activeText1,#mermaid-svg-XSWxwGnJoWUNnlQI .activeText2,#mermaid-svg-XSWxwGnJoWUNnlQI .activeText3{fill:black!important;}#mermaid-svg-XSWxwGnJoWUNnlQI .done0,#mermaid-svg-XSWxwGnJoWUNnlQI .done1,#mermaid-svg-XSWxwGnJoWUNnlQI .done2,#mermaid-svg-XSWxwGnJoWUNnlQI .done3{stroke:grey;fill:lightgrey;stroke-width:2;}#mermaid-svg-XSWxwGnJoWUNnlQI .doneText0,#mermaid-svg-XSWxwGnJoWUNnlQI .doneText1,#mermaid-svg-XSWxwGnJoWUNnlQI .doneText2,#mermaid-svg-XSWxwGnJoWUNnlQI .doneText3{fill:black!important;}#mermaid-svg-XSWxwGnJoWUNnlQI .doneText0.taskTextOutsideLeft,#mermaid-svg-XSWxwGnJoWUNnlQI .doneText0.taskTextOutsideRight,#mermaid-svg-XSWxwGnJoWUNnlQI .doneText1.taskTextOutsideLeft,#mermaid-svg-XSWxwGnJoWUNnlQI .doneText1.taskTextOutsideRight,#mermaid-svg-XSWxwGnJoWUNnlQI .doneText2.taskTextOutsideLeft,#mermaid-svg-XSWxwGnJoWUNnlQI .doneText2.taskTextOutsideRight,#mermaid-svg-XSWxwGnJoWUNnlQI .doneText3.taskTextOutsideLeft,#mermaid-svg-XSWxwGnJoWUNnlQI .doneText3.taskTextOutsideRight{fill:black!important;}#mermaid-svg-XSWxwGnJoWUNnlQI .crit0,#mermaid-svg-XSWxwGnJoWUNnlQI .crit1,#mermaid-svg-XSWxwGnJoWUNnlQI .crit2,#mermaid-svg-XSWxwGnJoWUNnlQI .crit3{stroke:#ff8888;fill:red;stroke-width:2;}#mermaid-svg-XSWxwGnJoWUNnlQI .activeCrit0,#mermaid-svg-XSWxwGnJoWUNnlQI .activeCrit1,#mermaid-svg-XSWxwGnJoWUNnlQI .activeCrit2,#mermaid-svg-XSWxwGnJoWUNnlQI .activeCrit3{stroke:#ff8888;fill:#bfc7ff;stroke-width:2;}#mermaid-svg-XSWxwGnJoWUNnlQI .doneCrit0,#mermaid-svg-XSWxwGnJoWUNnlQI .doneCrit1,#mermaid-svg-XSWxwGnJoWUNnlQI .doneCrit2,#mermaid-svg-XSWxwGnJoWUNnlQI .doneCrit3{stroke:#ff8888;fill:lightgrey;stroke-width:2;cursor:pointer;shape-rendering:crispEdges;}#mermaid-svg-XSWxwGnJoWUNnlQI .milestone{transform:rotate(45deg) scale(0.8,0.8);}#mermaid-svg-XSWxwGnJoWUNnlQI .milestoneText{font-style:italic;}#mermaid-svg-XSWxwGnJoWUNnlQI .doneCritText0,#mermaid-svg-XSWxwGnJoWUNnlQI .doneCritText1,#mermaid-svg-XSWxwGnJoWUNnlQI .doneCritText2,#mermaid-svg-XSWxwGnJoWUNnlQI .doneCritText3{fill:black!important;}#mermaid-svg-XSWxwGnJoWUNnlQI .doneCritText0.taskTextOutsideLeft,#mermaid-svg-XSWxwGnJoWUNnlQI .doneCritText0.taskTextOutsideRight,#mermaid-svg-XSWxwGnJoWUNnlQI .doneCritText1.taskTextOutsideLeft,#mermaid-svg-XSWxwGnJoWUNnlQI .doneCritText1.taskTextOutsideRight,#mermaid-svg-XSWxwGnJoWUNnlQI .doneCritText2.taskTextOutsideLeft,#mermaid-svg-XSWxwGnJoWUNnlQI .doneCritText2.taskTextOutsideRight,#mermaid-svg-XSWxwGnJoWUNnlQI .doneCritText3.taskTextOutsideLeft,#mermaid-svg-XSWxwGnJoWUNnlQI .doneCritText3.taskTextOutsideRight{fill:black!important;}#mermaid-svg-XSWxwGnJoWUNnlQI .vert{stroke:navy;}#mermaid-svg-XSWxwGnJoWUNnlQI .vertText{font-size:15px;text-anchor:middle;fill:navy!important;}#mermaid-svg-XSWxwGnJoWUNnlQI .activeCritText0,#mermaid-svg-XSWxwGnJoWUNnlQI .activeCritText1,#mermaid-svg-XSWxwGnJoWUNnlQI .activeCritText2,#mermaid-svg-XSWxwGnJoWUNnlQI .activeCritText3{fill:black!important;}#mermaid-svg-XSWxwGnJoWUNnlQI .titleText{text-anchor:middle;font-size:18px;fill:#333;font-family:"trebuchet ms",verdana,arial,sans-serif;}#mermaid-svg-XSWxwGnJoWUNnlQI :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 0 0 0 0 0 0 0 0 0 0 1 像素0-Y 像素0 像素0-U 像素0 像素1 像素1-Y 像素1 像素0-V 像素0 像素1 字节偏移 像素取值 YUYV 内存布局 --- 每 4 字节 = 2 像素


2.2 YUV 数据转 RGB

一句话总结

YUV 转 RGB 不是随便乘一乘就能算出来的,它有一套国际标准公式(BT.601/BT.709/BT.2020),对应不同清晰度的电视标准。

2.2.1 三大转换标准对比

标准 发布年份 适用场景 分辨率级别 类比 嵌入式常用
BT.601 1982 标清电视 SDTV 640×480 / 720×576 老式 CRT 电视 ✅ 最常用
BT.709 1990 高清电视 HDTV 1280×720 / 1920×1080 1080p 液晶电视 部分使用
BT.2020 2012 超高清 UHDTV 4K / 8K 最新的 HDR 电视 很少使用

2.2.2 标准演进时间线

#mermaid-svg-0BRp2DAMMYU7Ex89{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-0BRp2DAMMYU7Ex89 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-0BRp2DAMMYU7Ex89 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-0BRp2DAMMYU7Ex89 .error-icon{fill:#552222;}#mermaid-svg-0BRp2DAMMYU7Ex89 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-0BRp2DAMMYU7Ex89 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-0BRp2DAMMYU7Ex89 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-0BRp2DAMMYU7Ex89 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-0BRp2DAMMYU7Ex89 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-0BRp2DAMMYU7Ex89 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-0BRp2DAMMYU7Ex89 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-0BRp2DAMMYU7Ex89 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-0BRp2DAMMYU7Ex89 .marker.cross{stroke:#333333;}#mermaid-svg-0BRp2DAMMYU7Ex89 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-0BRp2DAMMYU7Ex89 p{margin:0;}#mermaid-svg-0BRp2DAMMYU7Ex89 .edge{stroke-width:3;}#mermaid-svg-0BRp2DAMMYU7Ex89 .section--1 rect,#mermaid-svg-0BRp2DAMMYU7Ex89 .section--1 path,#mermaid-svg-0BRp2DAMMYU7Ex89 .section--1 circle,#mermaid-svg-0BRp2DAMMYU7Ex89 .section--1 path{fill:hsl(240, 100%, 76.2745098039%);}#mermaid-svg-0BRp2DAMMYU7Ex89 .section--1 text{fill:#ffffff;}#mermaid-svg-0BRp2DAMMYU7Ex89 .node-icon--1{font-size:40px;color:#ffffff;}#mermaid-svg-0BRp2DAMMYU7Ex89 .section-edge--1{stroke:hsl(240, 100%, 76.2745098039%);}#mermaid-svg-0BRp2DAMMYU7Ex89 .edge-depth--1{stroke-width:17;}#mermaid-svg-0BRp2DAMMYU7Ex89 .section--1 line{stroke:hsl(60, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-0BRp2DAMMYU7Ex89 .lineWrapper line{stroke:#ffffff;}#mermaid-svg-0BRp2DAMMYU7Ex89 .disabled,#mermaid-svg-0BRp2DAMMYU7Ex89 .disabled circle,#mermaid-svg-0BRp2DAMMYU7Ex89 .disabled text{fill:lightgray;}#mermaid-svg-0BRp2DAMMYU7Ex89 .disabled text{fill:#efefef;}#mermaid-svg-0BRp2DAMMYU7Ex89 .section-0 rect,#mermaid-svg-0BRp2DAMMYU7Ex89 .section-0 path,#mermaid-svg-0BRp2DAMMYU7Ex89 .section-0 circle,#mermaid-svg-0BRp2DAMMYU7Ex89 .section-0 path{fill:hsl(60, 100%, 73.5294117647%);}#mermaid-svg-0BRp2DAMMYU7Ex89 .section-0 text{fill:black;}#mermaid-svg-0BRp2DAMMYU7Ex89 .node-icon-0{font-size:40px;color:black;}#mermaid-svg-0BRp2DAMMYU7Ex89 .section-edge-0{stroke:hsl(60, 100%, 73.5294117647%);}#mermaid-svg-0BRp2DAMMYU7Ex89 .edge-depth-0{stroke-width:14;}#mermaid-svg-0BRp2DAMMYU7Ex89 .section-0 line{stroke:hsl(240, 100%, 83.5294117647%);stroke-width:3;}#mermaid-svg-0BRp2DAMMYU7Ex89 .lineWrapper line{stroke:black;}#mermaid-svg-0BRp2DAMMYU7Ex89 .disabled,#mermaid-svg-0BRp2DAMMYU7Ex89 .disabled circle,#mermaid-svg-0BRp2DAMMYU7Ex89 .disabled text{fill:lightgray;}#mermaid-svg-0BRp2DAMMYU7Ex89 .disabled text{fill:#efefef;}#mermaid-svg-0BRp2DAMMYU7Ex89 .section-1 rect,#mermaid-svg-0BRp2DAMMYU7Ex89 .section-1 path,#mermaid-svg-0BRp2DAMMYU7Ex89 .section-1 circle,#mermaid-svg-0BRp2DAMMYU7Ex89 .section-1 path{fill:hsl(80, 100%, 76.2745098039%);}#mermaid-svg-0BRp2DAMMYU7Ex89 .section-1 text{fill:black;}#mermaid-svg-0BRp2DAMMYU7Ex89 .node-icon-1{font-size:40px;color:black;}#mermaid-svg-0BRp2DAMMYU7Ex89 .section-edge-1{stroke:hsl(80, 100%, 76.2745098039%);}#mermaid-svg-0BRp2DAMMYU7Ex89 .edge-depth-1{stroke-width:11;}#mermaid-svg-0BRp2DAMMYU7Ex89 .section-1 line{stroke:hsl(260, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-0BRp2DAMMYU7Ex89 .lineWrapper line{stroke:black;}#mermaid-svg-0BRp2DAMMYU7Ex89 .disabled,#mermaid-svg-0BRp2DAMMYU7Ex89 .disabled circle,#mermaid-svg-0BRp2DAMMYU7Ex89 .disabled text{fill:lightgray;}#mermaid-svg-0BRp2DAMMYU7Ex89 .disabled text{fill:#efefef;}#mermaid-svg-0BRp2DAMMYU7Ex89 .section-2 rect,#mermaid-svg-0BRp2DAMMYU7Ex89 .section-2 path,#mermaid-svg-0BRp2DAMMYU7Ex89 .section-2 circle,#mermaid-svg-0BRp2DAMMYU7Ex89 .section-2 path{fill:hsl(270, 100%, 76.2745098039%);}#mermaid-svg-0BRp2DAMMYU7Ex89 .section-2 text{fill:#ffffff;}#mermaid-svg-0BRp2DAMMYU7Ex89 .node-icon-2{font-size:40px;color:#ffffff;}#mermaid-svg-0BRp2DAMMYU7Ex89 .section-edge-2{stroke:hsl(270, 100%, 76.2745098039%);}#mermaid-svg-0BRp2DAMMYU7Ex89 .edge-depth-2{stroke-width:8;}#mermaid-svg-0BRp2DAMMYU7Ex89 .section-2 line{stroke:hsl(90, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-0BRp2DAMMYU7Ex89 .lineWrapper line{stroke:#ffffff;}#mermaid-svg-0BRp2DAMMYU7Ex89 .disabled,#mermaid-svg-0BRp2DAMMYU7Ex89 .disabled circle,#mermaid-svg-0BRp2DAMMYU7Ex89 .disabled text{fill:lightgray;}#mermaid-svg-0BRp2DAMMYU7Ex89 .disabled text{fill:#efefef;}#mermaid-svg-0BRp2DAMMYU7Ex89 .section-3 rect,#mermaid-svg-0BRp2DAMMYU7Ex89 .section-3 path,#mermaid-svg-0BRp2DAMMYU7Ex89 .section-3 circle,#mermaid-svg-0BRp2DAMMYU7Ex89 .section-3 path{fill:hsl(300, 100%, 76.2745098039%);}#mermaid-svg-0BRp2DAMMYU7Ex89 .section-3 text{fill:black;}#mermaid-svg-0BRp2DAMMYU7Ex89 .node-icon-3{font-size:40px;color:black;}#mermaid-svg-0BRp2DAMMYU7Ex89 .section-edge-3{stroke:hsl(300, 100%, 76.2745098039%);}#mermaid-svg-0BRp2DAMMYU7Ex89 .edge-depth-3{stroke-width:5;}#mermaid-svg-0BRp2DAMMYU7Ex89 .section-3 line{stroke:hsl(120, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-0BRp2DAMMYU7Ex89 .lineWrapper line{stroke:black;}#mermaid-svg-0BRp2DAMMYU7Ex89 .disabled,#mermaid-svg-0BRp2DAMMYU7Ex89 .disabled circle,#mermaid-svg-0BRp2DAMMYU7Ex89 .disabled text{fill:lightgray;}#mermaid-svg-0BRp2DAMMYU7Ex89 .disabled text{fill:#efefef;}#mermaid-svg-0BRp2DAMMYU7Ex89 .section-4 rect,#mermaid-svg-0BRp2DAMMYU7Ex89 .section-4 path,#mermaid-svg-0BRp2DAMMYU7Ex89 .section-4 circle,#mermaid-svg-0BRp2DAMMYU7Ex89 .section-4 path{fill:hsl(330, 100%, 76.2745098039%);}#mermaid-svg-0BRp2DAMMYU7Ex89 .section-4 text{fill:black;}#mermaid-svg-0BRp2DAMMYU7Ex89 .node-icon-4{font-size:40px;color:black;}#mermaid-svg-0BRp2DAMMYU7Ex89 .section-edge-4{stroke:hsl(330, 100%, 76.2745098039%);}#mermaid-svg-0BRp2DAMMYU7Ex89 .edge-depth-4{stroke-width:2;}#mermaid-svg-0BRp2DAMMYU7Ex89 .section-4 line{stroke:hsl(150, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-0BRp2DAMMYU7Ex89 .lineWrapper line{stroke:black;}#mermaid-svg-0BRp2DAMMYU7Ex89 .disabled,#mermaid-svg-0BRp2DAMMYU7Ex89 .disabled circle,#mermaid-svg-0BRp2DAMMYU7Ex89 .disabled text{fill:lightgray;}#mermaid-svg-0BRp2DAMMYU7Ex89 .disabled text{fill:#efefef;}#mermaid-svg-0BRp2DAMMYU7Ex89 .section-5 rect,#mermaid-svg-0BRp2DAMMYU7Ex89 .section-5 path,#mermaid-svg-0BRp2DAMMYU7Ex89 .section-5 circle,#mermaid-svg-0BRp2DAMMYU7Ex89 .section-5 path{fill:hsl(0, 100%, 76.2745098039%);}#mermaid-svg-0BRp2DAMMYU7Ex89 .section-5 text{fill:black;}#mermaid-svg-0BRp2DAMMYU7Ex89 .node-icon-5{font-size:40px;color:black;}#mermaid-svg-0BRp2DAMMYU7Ex89 .section-edge-5{stroke:hsl(0, 100%, 76.2745098039%);}#mermaid-svg-0BRp2DAMMYU7Ex89 .edge-depth-5{stroke-width:-1;}#mermaid-svg-0BRp2DAMMYU7Ex89 .section-5 line{stroke:hsl(180, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-0BRp2DAMMYU7Ex89 .lineWrapper line{stroke:black;}#mermaid-svg-0BRp2DAMMYU7Ex89 .disabled,#mermaid-svg-0BRp2DAMMYU7Ex89 .disabled circle,#mermaid-svg-0BRp2DAMMYU7Ex89 .disabled text{fill:lightgray;}#mermaid-svg-0BRp2DAMMYU7Ex89 .disabled text{fill:#efefef;}#mermaid-svg-0BRp2DAMMYU7Ex89 .section-6 rect,#mermaid-svg-0BRp2DAMMYU7Ex89 .section-6 path,#mermaid-svg-0BRp2DAMMYU7Ex89 .section-6 circle,#mermaid-svg-0BRp2DAMMYU7Ex89 .section-6 path{fill:hsl(30, 100%, 76.2745098039%);}#mermaid-svg-0BRp2DAMMYU7Ex89 .section-6 text{fill:black;}#mermaid-svg-0BRp2DAMMYU7Ex89 .node-icon-6{font-size:40px;color:black;}#mermaid-svg-0BRp2DAMMYU7Ex89 .section-edge-6{stroke:hsl(30, 100%, 76.2745098039%);}#mermaid-svg-0BRp2DAMMYU7Ex89 .edge-depth-6{stroke-width:-4;}#mermaid-svg-0BRp2DAMMYU7Ex89 .section-6 line{stroke:hsl(210, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-0BRp2DAMMYU7Ex89 .lineWrapper line{stroke:black;}#mermaid-svg-0BRp2DAMMYU7Ex89 .disabled,#mermaid-svg-0BRp2DAMMYU7Ex89 .disabled circle,#mermaid-svg-0BRp2DAMMYU7Ex89 .disabled text{fill:lightgray;}#mermaid-svg-0BRp2DAMMYU7Ex89 .disabled text{fill:#efefef;}#mermaid-svg-0BRp2DAMMYU7Ex89 .section-7 rect,#mermaid-svg-0BRp2DAMMYU7Ex89 .section-7 path,#mermaid-svg-0BRp2DAMMYU7Ex89 .section-7 circle,#mermaid-svg-0BRp2DAMMYU7Ex89 .section-7 path{fill:hsl(90, 100%, 76.2745098039%);}#mermaid-svg-0BRp2DAMMYU7Ex89 .section-7 text{fill:black;}#mermaid-svg-0BRp2DAMMYU7Ex89 .node-icon-7{font-size:40px;color:black;}#mermaid-svg-0BRp2DAMMYU7Ex89 .section-edge-7{stroke:hsl(90, 100%, 76.2745098039%);}#mermaid-svg-0BRp2DAMMYU7Ex89 .edge-depth-7{stroke-width:-7;}#mermaid-svg-0BRp2DAMMYU7Ex89 .section-7 line{stroke:hsl(270, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-0BRp2DAMMYU7Ex89 .lineWrapper line{stroke:black;}#mermaid-svg-0BRp2DAMMYU7Ex89 .disabled,#mermaid-svg-0BRp2DAMMYU7Ex89 .disabled circle,#mermaid-svg-0BRp2DAMMYU7Ex89 .disabled text{fill:lightgray;}#mermaid-svg-0BRp2DAMMYU7Ex89 .disabled text{fill:#efefef;}#mermaid-svg-0BRp2DAMMYU7Ex89 .section-8 rect,#mermaid-svg-0BRp2DAMMYU7Ex89 .section-8 path,#mermaid-svg-0BRp2DAMMYU7Ex89 .section-8 circle,#mermaid-svg-0BRp2DAMMYU7Ex89 .section-8 path{fill:hsl(150, 100%, 76.2745098039%);}#mermaid-svg-0BRp2DAMMYU7Ex89 .section-8 text{fill:black;}#mermaid-svg-0BRp2DAMMYU7Ex89 .node-icon-8{font-size:40px;color:black;}#mermaid-svg-0BRp2DAMMYU7Ex89 .section-edge-8{stroke:hsl(150, 100%, 76.2745098039%);}#mermaid-svg-0BRp2DAMMYU7Ex89 .edge-depth-8{stroke-width:-10;}#mermaid-svg-0BRp2DAMMYU7Ex89 .section-8 line{stroke:hsl(330, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-0BRp2DAMMYU7Ex89 .lineWrapper line{stroke:black;}#mermaid-svg-0BRp2DAMMYU7Ex89 .disabled,#mermaid-svg-0BRp2DAMMYU7Ex89 .disabled circle,#mermaid-svg-0BRp2DAMMYU7Ex89 .disabled text{fill:lightgray;}#mermaid-svg-0BRp2DAMMYU7Ex89 .disabled text{fill:#efefef;}#mermaid-svg-0BRp2DAMMYU7Ex89 .section-9 rect,#mermaid-svg-0BRp2DAMMYU7Ex89 .section-9 path,#mermaid-svg-0BRp2DAMMYU7Ex89 .section-9 circle,#mermaid-svg-0BRp2DAMMYU7Ex89 .section-9 path{fill:hsl(180, 100%, 76.2745098039%);}#mermaid-svg-0BRp2DAMMYU7Ex89 .section-9 text{fill:black;}#mermaid-svg-0BRp2DAMMYU7Ex89 .node-icon-9{font-size:40px;color:black;}#mermaid-svg-0BRp2DAMMYU7Ex89 .section-edge-9{stroke:hsl(180, 100%, 76.2745098039%);}#mermaid-svg-0BRp2DAMMYU7Ex89 .edge-depth-9{stroke-width:-13;}#mermaid-svg-0BRp2DAMMYU7Ex89 .section-9 line{stroke:hsl(0, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-0BRp2DAMMYU7Ex89 .lineWrapper line{stroke:black;}#mermaid-svg-0BRp2DAMMYU7Ex89 .disabled,#mermaid-svg-0BRp2DAMMYU7Ex89 .disabled circle,#mermaid-svg-0BRp2DAMMYU7Ex89 .disabled text{fill:lightgray;}#mermaid-svg-0BRp2DAMMYU7Ex89 .disabled text{fill:#efefef;}#mermaid-svg-0BRp2DAMMYU7Ex89 .section-10 rect,#mermaid-svg-0BRp2DAMMYU7Ex89 .section-10 path,#mermaid-svg-0BRp2DAMMYU7Ex89 .section-10 circle,#mermaid-svg-0BRp2DAMMYU7Ex89 .section-10 path{fill:hsl(210, 100%, 76.2745098039%);}#mermaid-svg-0BRp2DAMMYU7Ex89 .section-10 text{fill:black;}#mermaid-svg-0BRp2DAMMYU7Ex89 .node-icon-10{font-size:40px;color:black;}#mermaid-svg-0BRp2DAMMYU7Ex89 .section-edge-10{stroke:hsl(210, 100%, 76.2745098039%);}#mermaid-svg-0BRp2DAMMYU7Ex89 .edge-depth-10{stroke-width:-16;}#mermaid-svg-0BRp2DAMMYU7Ex89 .section-10 line{stroke:hsl(30, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-0BRp2DAMMYU7Ex89 .lineWrapper line{stroke:black;}#mermaid-svg-0BRp2DAMMYU7Ex89 .disabled,#mermaid-svg-0BRp2DAMMYU7Ex89 .disabled circle,#mermaid-svg-0BRp2DAMMYU7Ex89 .disabled text{fill:lightgray;}#mermaid-svg-0BRp2DAMMYU7Ex89 .disabled text{fill:#efefef;}#mermaid-svg-0BRp2DAMMYU7Ex89 .section-root rect,#mermaid-svg-0BRp2DAMMYU7Ex89 .section-root path,#mermaid-svg-0BRp2DAMMYU7Ex89 .section-root circle{fill:hsl(240, 100%, 46.2745098039%);}#mermaid-svg-0BRp2DAMMYU7Ex89 .section-root text{fill:#ffffff;}#mermaid-svg-0BRp2DAMMYU7Ex89 .icon-container{height:100%;display:flex;justify-content:center;align-items:center;}#mermaid-svg-0BRp2DAMMYU7Ex89 .edge{fill:none;}#mermaid-svg-0BRp2DAMMYU7Ex89 .eventWrapper{filter:brightness(120%);}#mermaid-svg-0BRp2DAMMYU7Ex89 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} BT.601 1982 年发布 标清电视 SDTV 640×480 / 720×576 嵌入式摄像头最常用 BT.709 1990 年发布 高清电视 HDTV 1280×720 / 1920×1080 现代显示器标准 BT.2020 2012 年发布 超高清 UHDTV 4K / 8K HDR 广色域 YUV→RGB 转换标准演进

2.2.3 三大标准色域范围对比

#mermaid-svg-43NnCLdRLx4DLUyB{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-43NnCLdRLx4DLUyB .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-43NnCLdRLx4DLUyB .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-43NnCLdRLx4DLUyB .error-icon{fill:#552222;}#mermaid-svg-43NnCLdRLx4DLUyB .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-43NnCLdRLx4DLUyB .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-43NnCLdRLx4DLUyB .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-43NnCLdRLx4DLUyB .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-43NnCLdRLx4DLUyB .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-43NnCLdRLx4DLUyB .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-43NnCLdRLx4DLUyB .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-43NnCLdRLx4DLUyB .marker{fill:#333333;stroke:#333333;}#mermaid-svg-43NnCLdRLx4DLUyB .marker.cross{stroke:#333333;}#mermaid-svg-43NnCLdRLx4DLUyB svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-43NnCLdRLx4DLUyB p{margin:0;}#mermaid-svg-43NnCLdRLx4DLUyB .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-43NnCLdRLx4DLUyB .cluster-label text{fill:#333;}#mermaid-svg-43NnCLdRLx4DLUyB .cluster-label span{color:#333;}#mermaid-svg-43NnCLdRLx4DLUyB .cluster-label span p{background-color:transparent;}#mermaid-svg-43NnCLdRLx4DLUyB .label text,#mermaid-svg-43NnCLdRLx4DLUyB span{fill:#333;color:#333;}#mermaid-svg-43NnCLdRLx4DLUyB .node rect,#mermaid-svg-43NnCLdRLx4DLUyB .node circle,#mermaid-svg-43NnCLdRLx4DLUyB .node ellipse,#mermaid-svg-43NnCLdRLx4DLUyB .node polygon,#mermaid-svg-43NnCLdRLx4DLUyB .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-43NnCLdRLx4DLUyB .rough-node .label text,#mermaid-svg-43NnCLdRLx4DLUyB .node .label text,#mermaid-svg-43NnCLdRLx4DLUyB .image-shape .label,#mermaid-svg-43NnCLdRLx4DLUyB .icon-shape .label{text-anchor:middle;}#mermaid-svg-43NnCLdRLx4DLUyB .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-43NnCLdRLx4DLUyB .rough-node .label,#mermaid-svg-43NnCLdRLx4DLUyB .node .label,#mermaid-svg-43NnCLdRLx4DLUyB .image-shape .label,#mermaid-svg-43NnCLdRLx4DLUyB .icon-shape .label{text-align:center;}#mermaid-svg-43NnCLdRLx4DLUyB .node.clickable{cursor:pointer;}#mermaid-svg-43NnCLdRLx4DLUyB .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-43NnCLdRLx4DLUyB .arrowheadPath{fill:#333333;}#mermaid-svg-43NnCLdRLx4DLUyB .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-43NnCLdRLx4DLUyB .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-43NnCLdRLx4DLUyB .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-43NnCLdRLx4DLUyB .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-43NnCLdRLx4DLUyB .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-43NnCLdRLx4DLUyB .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-43NnCLdRLx4DLUyB .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-43NnCLdRLx4DLUyB .cluster text{fill:#333;}#mermaid-svg-43NnCLdRLx4DLUyB .cluster span{color:#333;}#mermaid-svg-43NnCLdRLx4DLUyB div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-43NnCLdRLx4DLUyB .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-43NnCLdRLx4DLUyB rect.text{fill:none;stroke-width:0;}#mermaid-svg-43NnCLdRLx4DLUyB .icon-shape,#mermaid-svg-43NnCLdRLx4DLUyB .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-43NnCLdRLx4DLUyB .icon-shape p,#mermaid-svg-43NnCLdRLx4DLUyB .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-43NnCLdRLx4DLUyB .icon-shape .label rect,#mermaid-svg-43NnCLdRLx4DLUyB .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-43NnCLdRLx4DLUyB .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-43NnCLdRLx4DLUyB .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-43NnCLdRLx4DLUyB :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} BT.2020 超高清 广色域
红色更深
绿色更翠
蓝色更紫
BT.709 高清 = sRGB 标准
红色正红
绿色正绿
蓝色正蓝
BT.601 标清
红色偏橙
绿色偏黄
蓝色偏紫

2.2.4 BT.601 转换公式

Limited Range(标准范围) :Y: 16~235, UV: 16~240(中心值 128)

Full Range(完整范围):Y: 0~255, UV: 0~255(中心值 128)

复制代码
R = Y + 1.402 × (V - 128)
G = Y - 0.344 × (U - 128) - 0.714 × (V - 128)
B = Y + 1.772 × (U - 128)

注意:公式完全一样,区别仅在于输入范围不同。Limited Range 是广播电视标准,Full Range 是 PC 标准。嵌入式摄像头通常输出 Full Range。

2.2.5 YUYV 转 RGB 数据流

#mermaid-svg-tWf00CIEmol4ccAe{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-tWf00CIEmol4ccAe .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-tWf00CIEmol4ccAe .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-tWf00CIEmol4ccAe .error-icon{fill:#552222;}#mermaid-svg-tWf00CIEmol4ccAe .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-tWf00CIEmol4ccAe .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-tWf00CIEmol4ccAe .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-tWf00CIEmol4ccAe .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-tWf00CIEmol4ccAe .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-tWf00CIEmol4ccAe .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-tWf00CIEmol4ccAe .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-tWf00CIEmol4ccAe .marker{fill:#333333;stroke:#333333;}#mermaid-svg-tWf00CIEmol4ccAe .marker.cross{stroke:#333333;}#mermaid-svg-tWf00CIEmol4ccAe svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-tWf00CIEmol4ccAe p{margin:0;}#mermaid-svg-tWf00CIEmol4ccAe .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-tWf00CIEmol4ccAe .cluster-label text{fill:#333;}#mermaid-svg-tWf00CIEmol4ccAe .cluster-label span{color:#333;}#mermaid-svg-tWf00CIEmol4ccAe .cluster-label span p{background-color:transparent;}#mermaid-svg-tWf00CIEmol4ccAe .label text,#mermaid-svg-tWf00CIEmol4ccAe span{fill:#333;color:#333;}#mermaid-svg-tWf00CIEmol4ccAe .node rect,#mermaid-svg-tWf00CIEmol4ccAe .node circle,#mermaid-svg-tWf00CIEmol4ccAe .node ellipse,#mermaid-svg-tWf00CIEmol4ccAe .node polygon,#mermaid-svg-tWf00CIEmol4ccAe .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-tWf00CIEmol4ccAe .rough-node .label text,#mermaid-svg-tWf00CIEmol4ccAe .node .label text,#mermaid-svg-tWf00CIEmol4ccAe .image-shape .label,#mermaid-svg-tWf00CIEmol4ccAe .icon-shape .label{text-anchor:middle;}#mermaid-svg-tWf00CIEmol4ccAe .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-tWf00CIEmol4ccAe .rough-node .label,#mermaid-svg-tWf00CIEmol4ccAe .node .label,#mermaid-svg-tWf00CIEmol4ccAe .image-shape .label,#mermaid-svg-tWf00CIEmol4ccAe .icon-shape .label{text-align:center;}#mermaid-svg-tWf00CIEmol4ccAe .node.clickable{cursor:pointer;}#mermaid-svg-tWf00CIEmol4ccAe .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-tWf00CIEmol4ccAe .arrowheadPath{fill:#333333;}#mermaid-svg-tWf00CIEmol4ccAe .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-tWf00CIEmol4ccAe .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-tWf00CIEmol4ccAe .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-tWf00CIEmol4ccAe .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-tWf00CIEmol4ccAe .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-tWf00CIEmol4ccAe .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-tWf00CIEmol4ccAe .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-tWf00CIEmol4ccAe .cluster text{fill:#333;}#mermaid-svg-tWf00CIEmol4ccAe .cluster span{color:#333;}#mermaid-svg-tWf00CIEmol4ccAe div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-tWf00CIEmol4ccAe .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-tWf00CIEmol4ccAe rect.text{fill:none;stroke-width:0;}#mermaid-svg-tWf00CIEmol4ccAe .icon-shape,#mermaid-svg-tWf00CIEmol4ccAe .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-tWf00CIEmol4ccAe .icon-shape p,#mermaid-svg-tWf00CIEmol4ccAe .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-tWf00CIEmol4ccAe .icon-shape .label rect,#mermaid-svg-tWf00CIEmol4ccAe .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-tWf00CIEmol4ccAe .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-tWf00CIEmol4ccAe .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-tWf00CIEmol4ccAe :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} RGB输出
钳位
公式计算 像素1
公式计算 像素0
YUYV输入
Y0
U
Y1
V
R0 = Y0 + 1.402 × (V - 128)
G0 = Y0 - 0.344 × (U - 128) - 0.714 × (V - 128)
B0 = Y0 + 1.772 × (U - 128)
R1 = Y1 + 1.402 × (V - 128)
G1 = Y1 - 0.344 × (U - 128) - 0.714 × (V - 128)
B1 = Y1 + 1.772 × (U - 128)
像素0 ← 钳位到 0~255
像素1 ← 钳位到 0~255
RGB 数据流

R0 G0 B0 R1 G1 B1 ...

一句话总结:YUYV 每 4 字节转成 6 字节 RGB------两个像素共享同一组 UV,各自用自己的 Y 算出 R/G/B,最后钳位到 0~255 防止溢出。


2.3 JPEG 压缩原理与 libjpeg

一句话总结

JPEG 用"有损压缩"把图片缩小到原来的 1/10 甚至更小,libjpeg 就是帮我们完成这个压缩的开源库。

类比:JPEG 压缩就像给一幅画拍照发朋友圈------你保留了画面的主要内容,但丢掉了一些细节(比如背景的纹理),这样照片文件很小,但看起来还是那幅画。

2.3.1 JPEG 与原始格式对比

对比维度 原始 YUV/RGB JPEG
文件大小 大(640×480 ≈ 900KB) 小(640×480 ≈ 50~200KB)
压缩方式 无压缩 有损压缩
质量可调 不可调 1~100 可调
解码开销 需要解码才能显示
适用场景 图像处理、中间数据 存储、传输、分享
色彩精度 精确 有损,人眼可接受

2.3.1a JPEG 质量参数与文件大小关系

质量参数 1~100 控制的是"丢掉多少细节"------质量越低,文件越小,但画质损失越明显。

质量值 640×480 文件大小 压缩比 画质评价 典型应用
100 ~300 KB 3:1 肉眼无损 专业存档
90 ~120 KB 8:1 几乎无损 默认推荐值
75 ~70 KB 13:1 良好 Web 图片
50 ~45 KB 20:1 可接受 缩略图
25 ~30 KB 30:1 有明显块效应 低带宽传输
10 ~20 KB 45:1 画质差 极低带宽场景

建议:嵌入式摄像头拍照保存时,质量 90 是最好的平衡点------文件足够小,人眼几乎看不出压缩痕迹。

2.3.2 JPEG 压缩 5 步流程

#mermaid-svg-YaebsmjZE74H2THJ{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-YaebsmjZE74H2THJ .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-YaebsmjZE74H2THJ .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-YaebsmjZE74H2THJ .error-icon{fill:#552222;}#mermaid-svg-YaebsmjZE74H2THJ .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-YaebsmjZE74H2THJ .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-YaebsmjZE74H2THJ .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-YaebsmjZE74H2THJ .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-YaebsmjZE74H2THJ .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-YaebsmjZE74H2THJ .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-YaebsmjZE74H2THJ .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-YaebsmjZE74H2THJ .marker{fill:#333333;stroke:#333333;}#mermaid-svg-YaebsmjZE74H2THJ .marker.cross{stroke:#333333;}#mermaid-svg-YaebsmjZE74H2THJ svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-YaebsmjZE74H2THJ p{margin:0;}#mermaid-svg-YaebsmjZE74H2THJ .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-YaebsmjZE74H2THJ .cluster-label text{fill:#333;}#mermaid-svg-YaebsmjZE74H2THJ .cluster-label span{color:#333;}#mermaid-svg-YaebsmjZE74H2THJ .cluster-label span p{background-color:transparent;}#mermaid-svg-YaebsmjZE74H2THJ .label text,#mermaid-svg-YaebsmjZE74H2THJ span{fill:#333;color:#333;}#mermaid-svg-YaebsmjZE74H2THJ .node rect,#mermaid-svg-YaebsmjZE74H2THJ .node circle,#mermaid-svg-YaebsmjZE74H2THJ .node ellipse,#mermaid-svg-YaebsmjZE74H2THJ .node polygon,#mermaid-svg-YaebsmjZE74H2THJ .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-YaebsmjZE74H2THJ .rough-node .label text,#mermaid-svg-YaebsmjZE74H2THJ .node .label text,#mermaid-svg-YaebsmjZE74H2THJ .image-shape .label,#mermaid-svg-YaebsmjZE74H2THJ .icon-shape .label{text-anchor:middle;}#mermaid-svg-YaebsmjZE74H2THJ .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-YaebsmjZE74H2THJ .rough-node .label,#mermaid-svg-YaebsmjZE74H2THJ .node .label,#mermaid-svg-YaebsmjZE74H2THJ .image-shape .label,#mermaid-svg-YaebsmjZE74H2THJ .icon-shape .label{text-align:center;}#mermaid-svg-YaebsmjZE74H2THJ .node.clickable{cursor:pointer;}#mermaid-svg-YaebsmjZE74H2THJ .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-YaebsmjZE74H2THJ .arrowheadPath{fill:#333333;}#mermaid-svg-YaebsmjZE74H2THJ .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-YaebsmjZE74H2THJ .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-YaebsmjZE74H2THJ .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-YaebsmjZE74H2THJ .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-YaebsmjZE74H2THJ .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-YaebsmjZE74H2THJ .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-YaebsmjZE74H2THJ .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-YaebsmjZE74H2THJ .cluster text{fill:#333;}#mermaid-svg-YaebsmjZE74H2THJ .cluster span{color:#333;}#mermaid-svg-YaebsmjZE74H2THJ div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-YaebsmjZE74H2THJ .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-YaebsmjZE74H2THJ rect.text{fill:none;stroke-width:0;}#mermaid-svg-YaebsmjZE74H2THJ .icon-shape,#mermaid-svg-YaebsmjZE74H2THJ .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-YaebsmjZE74H2THJ .icon-shape p,#mermaid-svg-YaebsmjZE74H2THJ .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-YaebsmjZE74H2THJ .icon-shape .label rect,#mermaid-svg-YaebsmjZE74H2THJ .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-YaebsmjZE74H2THJ .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-YaebsmjZE74H2THJ .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-YaebsmjZE74H2THJ :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} RGB 像素数据

原始数据
色彩空间转换

RGB → YCbCr
下采样

4:2:0 降低 UV 分辨率

丢掉人眼不敏感的彩色细节
DCT 离散余弦变换

把 8×8 像素块

转为频率系数
量化

丢掉高频细节

保留低频轮廓

这是有损压缩的关键
熵编码

Huffman 编码

无损压缩剩余数据
JPEG 文件

压缩完成

一句话总结:JPEG 的"有损"发生在量化这一步------它把图像中细微的纹理细节(高频信息)近似掉,因为人眼看不太出来,但文件能小很多。质量参数 1~100 控制的就是"丢掉多少细节"。

2.3.2a JPEG 量化表------"有损"的秘密武器

JPEG 的"有损"发生在量化步骤。量化表是一个 8×8 的矩阵,每个元素代表对应 DCT 频率系数的量化步长:

复制代码
标准亮度量化表(质量 = 50):

  16  11  10  16  24   40   51   61
  12  12  14  19  26   58   60   55
  14  13  16  24  40   57   69   56
  14  17  22  29  51   87   80   62
  18  22  37  56  68  109  103   77
  24  35  55  64  81  104  113   92
  49  64  78  87 103  121  120  101
  72  92  95  98 112  100  103   99

解读:左上角是最低频分量(决定轮廓),量化步长小(16~10),保留更多细节。右下角是最 高频分量(决定纹理细节),量化步长大(61~99),大量信息被丢弃。这就是为什么 JPEG 文件能那么小------人眼对高频细节不敏感,丢掉也看不出来。

质量参数 1~100 的本质就是缩放这个量化表:质量越高,所有元素越小,保留越多;质量越低,所有元素越大,丢弃越多。

2.3.3 图片格式生态对比

除了 JPEG,嵌入式开发中还会遇到其他图片格式,了解它们的特点有助于在合适的场景做正确的选择。

格式 压缩方式 透明度 适合场景 不适合场景
JPEG 有损 照片、实景图 文字截图、图表
PNG 无损 图标、截图、文字 照片(文件太大)
BMP 无压缩 调试、中间数据 存储、传输
WebP 有损/无损 Web 图片(比 JPEG 小 30%) 兼容性(老旧设备)

一句话总结:照片存 JPEG,截图存 PNG,调试用 BMP,Web 图片用 WebP。

2.3.3a JPEG 文件中的 EXIF 信息

JPEG 文件不仅仅是压缩后的图像数据,还包含一个称为 EXIF(Exchangeable Image File Format)的元数据区,记录了拍摄时的各种信息:
#mermaid-svg-B1l5nUi45F16IxNL{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-B1l5nUi45F16IxNL .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-B1l5nUi45F16IxNL .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-B1l5nUi45F16IxNL .error-icon{fill:#552222;}#mermaid-svg-B1l5nUi45F16IxNL .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-B1l5nUi45F16IxNL .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-B1l5nUi45F16IxNL .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-B1l5nUi45F16IxNL .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-B1l5nUi45F16IxNL .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-B1l5nUi45F16IxNL .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-B1l5nUi45F16IxNL .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-B1l5nUi45F16IxNL .marker{fill:#333333;stroke:#333333;}#mermaid-svg-B1l5nUi45F16IxNL .marker.cross{stroke:#333333;}#mermaid-svg-B1l5nUi45F16IxNL svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-B1l5nUi45F16IxNL p{margin:0;}#mermaid-svg-B1l5nUi45F16IxNL .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-B1l5nUi45F16IxNL .cluster-label text{fill:#333;}#mermaid-svg-B1l5nUi45F16IxNL .cluster-label span{color:#333;}#mermaid-svg-B1l5nUi45F16IxNL .cluster-label span p{background-color:transparent;}#mermaid-svg-B1l5nUi45F16IxNL .label text,#mermaid-svg-B1l5nUi45F16IxNL span{fill:#333;color:#333;}#mermaid-svg-B1l5nUi45F16IxNL .node rect,#mermaid-svg-B1l5nUi45F16IxNL .node circle,#mermaid-svg-B1l5nUi45F16IxNL .node ellipse,#mermaid-svg-B1l5nUi45F16IxNL .node polygon,#mermaid-svg-B1l5nUi45F16IxNL .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-B1l5nUi45F16IxNL .rough-node .label text,#mermaid-svg-B1l5nUi45F16IxNL .node .label text,#mermaid-svg-B1l5nUi45F16IxNL .image-shape .label,#mermaid-svg-B1l5nUi45F16IxNL .icon-shape .label{text-anchor:middle;}#mermaid-svg-B1l5nUi45F16IxNL .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-B1l5nUi45F16IxNL .rough-node .label,#mermaid-svg-B1l5nUi45F16IxNL .node .label,#mermaid-svg-B1l5nUi45F16IxNL .image-shape .label,#mermaid-svg-B1l5nUi45F16IxNL .icon-shape .label{text-align:center;}#mermaid-svg-B1l5nUi45F16IxNL .node.clickable{cursor:pointer;}#mermaid-svg-B1l5nUi45F16IxNL .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-B1l5nUi45F16IxNL .arrowheadPath{fill:#333333;}#mermaid-svg-B1l5nUi45F16IxNL .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-B1l5nUi45F16IxNL .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-B1l5nUi45F16IxNL .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-B1l5nUi45F16IxNL .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-B1l5nUi45F16IxNL .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-B1l5nUi45F16IxNL .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-B1l5nUi45F16IxNL .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-B1l5nUi45F16IxNL .cluster text{fill:#333;}#mermaid-svg-B1l5nUi45F16IxNL .cluster span{color:#333;}#mermaid-svg-B1l5nUi45F16IxNL div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-B1l5nUi45F16IxNL .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-B1l5nUi45F16IxNL rect.text{fill:none;stroke-width:0;}#mermaid-svg-B1l5nUi45F16IxNL .icon-shape,#mermaid-svg-B1l5nUi45F16IxNL .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-B1l5nUi45F16IxNL .icon-shape p,#mermaid-svg-B1l5nUi45F16IxNL .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-B1l5nUi45F16IxNL .icon-shape .label rect,#mermaid-svg-B1l5nUi45F16IxNL .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-B1l5nUi45F16IxNL .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-B1l5nUi45F16IxNL .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-B1l5nUi45F16IxNL :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 文件尾
图像数据
编码参数表
元数据区
文件头
SOI 0xFFD8

文件起始标记
APP1 EXIF 数据
拍摄时间
摄像头型号
焦距、光圈、ISO
缩略图
旋转方向
DHT Huffman 表
DQT 量化表
SOF 帧参数
压缩图像数据

多个 MCU 块
EOI 0xFFD9

文件结束标记

注意:我们的嵌入式代码生成的 JPEG 不包含 EXIF 信息,因为 libjpeg 默认只输出纯图像数据。如果需要嵌入 EXIF,需要额外用 libexif 库处理。

2.3.4 libjpeg 压缩流程

#mermaid-svg-5vnUHptNJYivbasc{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-5vnUHptNJYivbasc .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-5vnUHptNJYivbasc .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-5vnUHptNJYivbasc .error-icon{fill:#552222;}#mermaid-svg-5vnUHptNJYivbasc .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-5vnUHptNJYivbasc .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-5vnUHptNJYivbasc .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-5vnUHptNJYivbasc .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-5vnUHptNJYivbasc .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-5vnUHptNJYivbasc .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-5vnUHptNJYivbasc .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-5vnUHptNJYivbasc .marker{fill:#333333;stroke:#333333;}#mermaid-svg-5vnUHptNJYivbasc .marker.cross{stroke:#333333;}#mermaid-svg-5vnUHptNJYivbasc svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-5vnUHptNJYivbasc p{margin:0;}#mermaid-svg-5vnUHptNJYivbasc defs #statediagram-barbEnd{fill:#333333;stroke:#333333;}#mermaid-svg-5vnUHptNJYivbasc g.stateGroup text{fill:#9370DB;stroke:none;font-size:10px;}#mermaid-svg-5vnUHptNJYivbasc g.stateGroup text{fill:#333;stroke:none;font-size:10px;}#mermaid-svg-5vnUHptNJYivbasc g.stateGroup .state-title{font-weight:bolder;fill:#131300;}#mermaid-svg-5vnUHptNJYivbasc g.stateGroup rect{fill:#ECECFF;stroke:#9370DB;}#mermaid-svg-5vnUHptNJYivbasc g.stateGroup line{stroke:#333333;stroke-width:1;}#mermaid-svg-5vnUHptNJYivbasc .transition{stroke:#333333;stroke-width:1;fill:none;}#mermaid-svg-5vnUHptNJYivbasc .stateGroup .composit{fill:white;border-bottom:1px;}#mermaid-svg-5vnUHptNJYivbasc .stateGroup .alt-composit{fill:#e0e0e0;border-bottom:1px;}#mermaid-svg-5vnUHptNJYivbasc .state-note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-5vnUHptNJYivbasc .state-note text{fill:black;stroke:none;font-size:10px;}#mermaid-svg-5vnUHptNJYivbasc .stateLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.5;}#mermaid-svg-5vnUHptNJYivbasc .edgeLabel .label rect{fill:#ECECFF;opacity:0.5;}#mermaid-svg-5vnUHptNJYivbasc .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-5vnUHptNJYivbasc .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-5vnUHptNJYivbasc .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-5vnUHptNJYivbasc .edgeLabel .label text{fill:#333;}#mermaid-svg-5vnUHptNJYivbasc .label div .edgeLabel{color:#333;}#mermaid-svg-5vnUHptNJYivbasc .stateLabel text{fill:#131300;font-size:10px;font-weight:bold;}#mermaid-svg-5vnUHptNJYivbasc .node circle.state-start{fill:#333333;stroke:#333333;}#mermaid-svg-5vnUHptNJYivbasc .node .fork-join{fill:#333333;stroke:#333333;}#mermaid-svg-5vnUHptNJYivbasc .node circle.state-end{fill:#9370DB;stroke:white;stroke-width:1.5;}#mermaid-svg-5vnUHptNJYivbasc .end-state-inner{fill:white;stroke-width:1.5;}#mermaid-svg-5vnUHptNJYivbasc .node rect{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-5vnUHptNJYivbasc .node polygon{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-5vnUHptNJYivbasc #statediagram-barbEnd{fill:#333333;}#mermaid-svg-5vnUHptNJYivbasc .statediagram-cluster rect{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-5vnUHptNJYivbasc .cluster-label,#mermaid-svg-5vnUHptNJYivbasc .nodeLabel{color:#131300;}#mermaid-svg-5vnUHptNJYivbasc .statediagram-cluster rect.outer{rx:5px;ry:5px;}#mermaid-svg-5vnUHptNJYivbasc .statediagram-state .divider{stroke:#9370DB;}#mermaid-svg-5vnUHptNJYivbasc .statediagram-state .title-state{rx:5px;ry:5px;}#mermaid-svg-5vnUHptNJYivbasc .statediagram-cluster.statediagram-cluster .inner{fill:white;}#mermaid-svg-5vnUHptNJYivbasc .statediagram-cluster.statediagram-cluster-alt .inner{fill:#f0f0f0;}#mermaid-svg-5vnUHptNJYivbasc .statediagram-cluster .inner{rx:0;ry:0;}#mermaid-svg-5vnUHptNJYivbasc .statediagram-state rect.basic{rx:5px;ry:5px;}#mermaid-svg-5vnUHptNJYivbasc .statediagram-state rect.divider{stroke-dasharray:10,10;fill:#f0f0f0;}#mermaid-svg-5vnUHptNJYivbasc .note-edge{stroke-dasharray:5;}#mermaid-svg-5vnUHptNJYivbasc .statediagram-note rect{fill:#fff5ad;stroke:#aaaa33;stroke-width:1px;rx:0;ry:0;}#mermaid-svg-5vnUHptNJYivbasc .statediagram-note rect{fill:#fff5ad;stroke:#aaaa33;stroke-width:1px;rx:0;ry:0;}#mermaid-svg-5vnUHptNJYivbasc .statediagram-note text{fill:black;}#mermaid-svg-5vnUHptNJYivbasc .statediagram-note .nodeLabel{color:black;}#mermaid-svg-5vnUHptNJYivbasc .statediagram .edgeLabel{color:red;}#mermaid-svg-5vnUHptNJYivbasc #dependencyStart,#mermaid-svg-5vnUHptNJYivbasc #dependencyEnd{fill:#333333;stroke:#333333;stroke-width:1;}#mermaid-svg-5vnUHptNJYivbasc .statediagramTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-5vnUHptNJYivbasc :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} jpeg_create_compress
jpeg_stdio_dest
image_width / height

input_components / in_color_space
jpeg_set_defaults
jpeg_set_quality(quality)
jpeg_start_compress
jpeg_write_scanlines
while next_scanline < height
jpeg_finish_compress
jpeg_destroy_compress
创建压缩对象
绑定输出文件
设置参数
设置默认值
设置质量
开始压缩
逐行写入
完成压缩
销毁对象
在写入每一行之前

先将 YUYV 转为 RGB

存入 line_buf

2.3.4a libjpeg 交叉编译(ARM 平台)

要在 ARM 开发板上使用 libjpeg,需要先交叉编译 libjpeg 库:

bash 复制代码
# 1. 下载 libjpeg-turbo 源码
wget https://github.com/libjpeg-turbo/libjpeg-turbo/archive/1.5.3.tar.gz
tar -zxvf libjpeg-turbo-1.5.3.tar.gz
cd libjpeg-turbo-1.5.3/

# 2. 交叉编译配置
./configure CC="arm-linux-gnueabihf-gcc" \
            --host="arm-linux-gnueabihf" \
            --prefix="/opt/arm/libjpeg/result" \
            --enable-static
make
make install

编译产物

  • include/ --- 头文件(jpeglib.h 等)
  • lib/ --- 库文件(libjpeg.a 静态库,libturbojpeg.a
  • bin/ --- 工具程序

注意 :如果只是在 Ubuntu 本机开发和测试,可以直接用 sudo apt-get install libjpeg-dev 安装,无需交叉编译。

2.3.5 完整数据链路:从摄像头到 JPEG

#mermaid-svg-bnEzjYhPJV2yjkJA{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-bnEzjYhPJV2yjkJA .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-bnEzjYhPJV2yjkJA .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-bnEzjYhPJV2yjkJA .error-icon{fill:#552222;}#mermaid-svg-bnEzjYhPJV2yjkJA .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-bnEzjYhPJV2yjkJA .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-bnEzjYhPJV2yjkJA .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-bnEzjYhPJV2yjkJA .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-bnEzjYhPJV2yjkJA .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-bnEzjYhPJV2yjkJA .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-bnEzjYhPJV2yjkJA .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-bnEzjYhPJV2yjkJA .marker{fill:#333333;stroke:#333333;}#mermaid-svg-bnEzjYhPJV2yjkJA .marker.cross{stroke:#333333;}#mermaid-svg-bnEzjYhPJV2yjkJA svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-bnEzjYhPJV2yjkJA p{margin:0;}#mermaid-svg-bnEzjYhPJV2yjkJA .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-bnEzjYhPJV2yjkJA .cluster-label text{fill:#333;}#mermaid-svg-bnEzjYhPJV2yjkJA .cluster-label span{color:#333;}#mermaid-svg-bnEzjYhPJV2yjkJA .cluster-label span p{background-color:transparent;}#mermaid-svg-bnEzjYhPJV2yjkJA .label text,#mermaid-svg-bnEzjYhPJV2yjkJA span{fill:#333;color:#333;}#mermaid-svg-bnEzjYhPJV2yjkJA .node rect,#mermaid-svg-bnEzjYhPJV2yjkJA .node circle,#mermaid-svg-bnEzjYhPJV2yjkJA .node ellipse,#mermaid-svg-bnEzjYhPJV2yjkJA .node polygon,#mermaid-svg-bnEzjYhPJV2yjkJA .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-bnEzjYhPJV2yjkJA .rough-node .label text,#mermaid-svg-bnEzjYhPJV2yjkJA .node .label text,#mermaid-svg-bnEzjYhPJV2yjkJA .image-shape .label,#mermaid-svg-bnEzjYhPJV2yjkJA .icon-shape .label{text-anchor:middle;}#mermaid-svg-bnEzjYhPJV2yjkJA .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-bnEzjYhPJV2yjkJA .rough-node .label,#mermaid-svg-bnEzjYhPJV2yjkJA .node .label,#mermaid-svg-bnEzjYhPJV2yjkJA .image-shape .label,#mermaid-svg-bnEzjYhPJV2yjkJA .icon-shape .label{text-align:center;}#mermaid-svg-bnEzjYhPJV2yjkJA .node.clickable{cursor:pointer;}#mermaid-svg-bnEzjYhPJV2yjkJA .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-bnEzjYhPJV2yjkJA .arrowheadPath{fill:#333333;}#mermaid-svg-bnEzjYhPJV2yjkJA .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-bnEzjYhPJV2yjkJA .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-bnEzjYhPJV2yjkJA .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-bnEzjYhPJV2yjkJA .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-bnEzjYhPJV2yjkJA .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-bnEzjYhPJV2yjkJA .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-bnEzjYhPJV2yjkJA .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-bnEzjYhPJV2yjkJA .cluster text{fill:#333;}#mermaid-svg-bnEzjYhPJV2yjkJA .cluster span{color:#333;}#mermaid-svg-bnEzjYhPJV2yjkJA div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-bnEzjYhPJV2yjkJA .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-bnEzjYhPJV2yjkJA rect.text{fill:none;stroke-width:0;}#mermaid-svg-bnEzjYhPJV2yjkJA .icon-shape,#mermaid-svg-bnEzjYhPJV2yjkJA .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-bnEzjYhPJV2yjkJA .icon-shape p,#mermaid-svg-bnEzjYhPJV2yjkJA .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-bnEzjYhPJV2yjkJA .icon-shape .label rect,#mermaid-svg-bnEzjYhPJV2yjkJA .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-bnEzjYhPJV2yjkJA .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-bnEzjYhPJV2yjkJA .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-bnEzjYhPJV2yjkJA :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 可选输出
压缩
转换
采集
摄像头硬件
V4L2 驱动

YUYV 格式
内核缓冲区

YUYV 原始数据
mmap 映射

用户空间直接访问
YUYV 数据

Y0 U Y1 V 每4字节2像素
BT.601 公式

YUV → RGB
RGB 数据

R0 G0 B0 每像素3字节
libjpeg 逐行压缩
picture.jpg
picture.yuv

原始数据
picture.rgb

调试用

一句话总结:摄像头 → YUYV 原始数据 → RGB 中间数据 → JPEG 压缩文件,三步走完一条完整的图像采集处理链路。


第三章 完整代码工程

3.1 系统架构总览

#mermaid-svg-mPBLdM1hI60W2nXW{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-mPBLdM1hI60W2nXW .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-mPBLdM1hI60W2nXW .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-mPBLdM1hI60W2nXW .error-icon{fill:#552222;}#mermaid-svg-mPBLdM1hI60W2nXW .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-mPBLdM1hI60W2nXW .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-mPBLdM1hI60W2nXW .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-mPBLdM1hI60W2nXW .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-mPBLdM1hI60W2nXW .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-mPBLdM1hI60W2nXW .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-mPBLdM1hI60W2nXW .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-mPBLdM1hI60W2nXW .marker{fill:#333333;stroke:#333333;}#mermaid-svg-mPBLdM1hI60W2nXW .marker.cross{stroke:#333333;}#mermaid-svg-mPBLdM1hI60W2nXW svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-mPBLdM1hI60W2nXW p{margin:0;}#mermaid-svg-mPBLdM1hI60W2nXW .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-mPBLdM1hI60W2nXW .cluster-label text{fill:#333;}#mermaid-svg-mPBLdM1hI60W2nXW .cluster-label span{color:#333;}#mermaid-svg-mPBLdM1hI60W2nXW .cluster-label span p{background-color:transparent;}#mermaid-svg-mPBLdM1hI60W2nXW .label text,#mermaid-svg-mPBLdM1hI60W2nXW span{fill:#333;color:#333;}#mermaid-svg-mPBLdM1hI60W2nXW .node rect,#mermaid-svg-mPBLdM1hI60W2nXW .node circle,#mermaid-svg-mPBLdM1hI60W2nXW .node ellipse,#mermaid-svg-mPBLdM1hI60W2nXW .node polygon,#mermaid-svg-mPBLdM1hI60W2nXW .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-mPBLdM1hI60W2nXW .rough-node .label text,#mermaid-svg-mPBLdM1hI60W2nXW .node .label text,#mermaid-svg-mPBLdM1hI60W2nXW .image-shape .label,#mermaid-svg-mPBLdM1hI60W2nXW .icon-shape .label{text-anchor:middle;}#mermaid-svg-mPBLdM1hI60W2nXW .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-mPBLdM1hI60W2nXW .rough-node .label,#mermaid-svg-mPBLdM1hI60W2nXW .node .label,#mermaid-svg-mPBLdM1hI60W2nXW .image-shape .label,#mermaid-svg-mPBLdM1hI60W2nXW .icon-shape .label{text-align:center;}#mermaid-svg-mPBLdM1hI60W2nXW .node.clickable{cursor:pointer;}#mermaid-svg-mPBLdM1hI60W2nXW .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-mPBLdM1hI60W2nXW .arrowheadPath{fill:#333333;}#mermaid-svg-mPBLdM1hI60W2nXW .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-mPBLdM1hI60W2nXW .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-mPBLdM1hI60W2nXW .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-mPBLdM1hI60W2nXW .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-mPBLdM1hI60W2nXW .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-mPBLdM1hI60W2nXW .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-mPBLdM1hI60W2nXW .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-mPBLdM1hI60W2nXW .cluster text{fill:#333;}#mermaid-svg-mPBLdM1hI60W2nXW .cluster span{color:#333;}#mermaid-svg-mPBLdM1hI60W2nXW div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-mPBLdM1hI60W2nXW .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-mPBLdM1hI60W2nXW rect.text{fill:none;stroke-width:0;}#mermaid-svg-mPBLdM1hI60W2nXW .icon-shape,#mermaid-svg-mPBLdM1hI60W2nXW .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-mPBLdM1hI60W2nXW .icon-shape p,#mermaid-svg-mPBLdM1hI60W2nXW .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-mPBLdM1hI60W2nXW .icon-shape .label rect,#mermaid-svg-mPBLdM1hI60W2nXW .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-mPBLdM1hI60W2nXW .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-mPBLdM1hI60W2nXW .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-mPBLdM1hI60W2nXW :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 输出文件
格式转换层
应用层
V4L2 驱动层
硬件层
USB 摄像头

/dev/video0
open 打开设备
VIDIOC_QUERYCAP

查询能力
VIDIOC_ENUM_FMT

枚举格式
VIDIOC_S_FMT

设置 YUYV 640x480
VIDIOC_REQBUFS

申请4个缓冲区
VIDIOC_QUERYBUF

查询缓冲区信息
mmap 映射到用户空间
VIDIOC_QBUF

入队
VIDIOC_STREAMON

开流
VIDIOC_DQBUF

取帧
init_camera
init_mmap
start_capturing
select 等待20秒
read_frame
yuyv_to_rgb

YUYV → RGB
yuyv_to_jpeg

YUYV → JPEG
picture.yuv

原始 YUYV
picture.rgb

RGB 数据
picture.jpg

JPEG 图片

3.1.1 函数调用关系

#mermaid-svg-EfA9EWFA1wd84A16{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-EfA9EWFA1wd84A16 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-EfA9EWFA1wd84A16 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-EfA9EWFA1wd84A16 .error-icon{fill:#552222;}#mermaid-svg-EfA9EWFA1wd84A16 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-EfA9EWFA1wd84A16 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-EfA9EWFA1wd84A16 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-EfA9EWFA1wd84A16 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-EfA9EWFA1wd84A16 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-EfA9EWFA1wd84A16 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-EfA9EWFA1wd84A16 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-EfA9EWFA1wd84A16 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-EfA9EWFA1wd84A16 .marker.cross{stroke:#333333;}#mermaid-svg-EfA9EWFA1wd84A16 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-EfA9EWFA1wd84A16 p{margin:0;}#mermaid-svg-EfA9EWFA1wd84A16 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-EfA9EWFA1wd84A16 .cluster-label text{fill:#333;}#mermaid-svg-EfA9EWFA1wd84A16 .cluster-label span{color:#333;}#mermaid-svg-EfA9EWFA1wd84A16 .cluster-label span p{background-color:transparent;}#mermaid-svg-EfA9EWFA1wd84A16 .label text,#mermaid-svg-EfA9EWFA1wd84A16 span{fill:#333;color:#333;}#mermaid-svg-EfA9EWFA1wd84A16 .node rect,#mermaid-svg-EfA9EWFA1wd84A16 .node circle,#mermaid-svg-EfA9EWFA1wd84A16 .node ellipse,#mermaid-svg-EfA9EWFA1wd84A16 .node polygon,#mermaid-svg-EfA9EWFA1wd84A16 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-EfA9EWFA1wd84A16 .rough-node .label text,#mermaid-svg-EfA9EWFA1wd84A16 .node .label text,#mermaid-svg-EfA9EWFA1wd84A16 .image-shape .label,#mermaid-svg-EfA9EWFA1wd84A16 .icon-shape .label{text-anchor:middle;}#mermaid-svg-EfA9EWFA1wd84A16 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-EfA9EWFA1wd84A16 .rough-node .label,#mermaid-svg-EfA9EWFA1wd84A16 .node .label,#mermaid-svg-EfA9EWFA1wd84A16 .image-shape .label,#mermaid-svg-EfA9EWFA1wd84A16 .icon-shape .label{text-align:center;}#mermaid-svg-EfA9EWFA1wd84A16 .node.clickable{cursor:pointer;}#mermaid-svg-EfA9EWFA1wd84A16 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-EfA9EWFA1wd84A16 .arrowheadPath{fill:#333333;}#mermaid-svg-EfA9EWFA1wd84A16 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-EfA9EWFA1wd84A16 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-EfA9EWFA1wd84A16 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-EfA9EWFA1wd84A16 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-EfA9EWFA1wd84A16 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-EfA9EWFA1wd84A16 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-EfA9EWFA1wd84A16 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-EfA9EWFA1wd84A16 .cluster text{fill:#333;}#mermaid-svg-EfA9EWFA1wd84A16 .cluster span{color:#333;}#mermaid-svg-EfA9EWFA1wd84A16 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-EfA9EWFA1wd84A16 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-EfA9EWFA1wd84A16 rect.text{fill:none;stroke-width:0;}#mermaid-svg-EfA9EWFA1wd84A16 .icon-shape,#mermaid-svg-EfA9EWFA1wd84A16 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-EfA9EWFA1wd84A16 .icon-shape p,#mermaid-svg-EfA9EWFA1wd84A16 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-EfA9EWFA1wd84A16 .icon-shape .label rect,#mermaid-svg-EfA9EWFA1wd84A16 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-EfA9EWFA1wd84A16 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-EfA9EWFA1wd84A16 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-EfA9EWFA1wd84A16 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} main
init_camera
init_mmap
start_capturing
select
read_frame
open 设备
ioctl QUERYCAP
ioctl ENUM_FMT
ioctl S_FMT
ioctl REQBUFS
ioctl QUERYBUF
mmap 映射
ioctl QBUF
ioctl DQBUF
fwrite 保存 YUV
yuyv_to_rgb
yuyv_to_jpeg
ioctl QBUF 重新入队
fopen 写 RGB
fwrite RGB 数据
fopen 写 JPEG
jpeg_create_compress
设置参数
jpeg_start_compress
jpeg_write_scanlines 逐行
jpeg_finish_compress

3.2 代码实现

3.2.1 camera.h --- 头文件

c 复制代码
#ifndef _CAMERA_H_
#define _CAMERA_H_

#define IMG_WIDTH  640
#define IMG_HEIGHT 480

// 存储映射缓冲区信息
struct camera_t {
    void *start;  // 映射后的用户空间地址
    int length;   // 缓冲区长度
};

// V4L2 操作
extern int init_camera(const char *dev_path);
extern int init_mmap(int fd);
extern int start_capturing(int fd);
extern int read_frame(int fd);

// 格式转换
extern int yuyv_to_rgb(const unsigned char *yuv,
                       const char *file_name,
                       int width, int height);
extern int yuyv_to_jpeg(const unsigned char *yuv,
                        const char *file_name,
                        int width, int height,
                        int quality);

#endif //_CAMERA_H_

3.2.2 camera.c --- 核心实现

c 复制代码
#include "camera.h"
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <error.h>
#include <linux/videodev2.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <jpeglib.h>

static struct camera_t *camera_data;
static int n_buffer;

/**
 * init_camera - 打开摄像头并初始化为 YUYV 格式
 * @dev_path: 设备路径,如 "/dev/video0"
 * 返回: 成功返回 fd,失败返回 -1
 *
 * 一句话总结:打开设备、查能力、看格式、设格式,四步到位。
 */
int init_camera(const char *dev_path)
{
    // 1. 打开设备(非阻塞模式)
    int fd = open(dev_path, O_RDWR | O_NONBLOCK);
    if (fd < 0) {
        perror("open dev failure!");
        return -1;
    }

    // 2. 查询设备能力
    struct v4l2_capability cap;
    if (ioctl(fd, VIDIOC_QUERYCAP, &cap) < 0) {
        perror("VIDIOC_QUERYCAP failure!");
        return -1;
    }

    // 3. 确认是视频捕获设备
    if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)) {
        fprintf(stderr, "错误:该设备不是视频捕获设备!\n");
        return -1;
    }

    // 4. 枚举所有支持的格式
    struct v4l2_fmtdesc fmt;
    fmt.index = 0;
    fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    printf("支持的像素格式:\n");
    while (ioctl(fd, VIDIOC_ENUM_FMT, &fmt) == 0) {
        printf("  [%c%c%c%c] %s\n",
               fmt.pixelformat & 0xFF,
               (fmt.pixelformat >> 8) & 0xFF,
               (fmt.pixelformat >> 16) & 0xFF,
               (fmt.pixelformat >> 24) & 0xFF,
               fmt.description);
        fmt.index++;
    }

    // 5. 设置采集格式为 YUYV 640x480
    struct v4l2_format format;
    format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    format.fmt.pix.width = IMG_WIDTH;
    format.fmt.pix.height = IMG_HEIGHT;
    format.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
    format.fmt.pix.field = V4L2_FIELD_INTERLACED;

    if (ioctl(fd, VIDIOC_S_FMT, &format) < 0) {
        perror("VIDIOC_S_FMT failure!");
        return -1;
    }

    return fd;
}

/**
 * init_mmap - 申请内核缓冲区并映射到用户空间
 * @fd: 摄像头文件描述符
 *
 * 一句话总结:申请 4 个内核缓冲区,映射到用户空间,然后入队等待相机使用。
 * 类比:准备 4 个桶放在流水线上,相机拍一帧就自动装满一个桶。
 */
int init_mmap(int fd)
{
    // 1. 申请 4 个内核缓冲区
    struct v4l2_requestbuffers reqbuf;
    reqbuf.count = 4;
    reqbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    reqbuf.memory = V4L2_MEMORY_MMAP;

    if (ioctl(fd, VIDIOC_REQBUFS, &reqbuf) < 0) {
        perror("VIDIOC_REQBUFS failure!");
        return -1;
    }

    // 2. 分配存储映射信息的数组
    camera_data = calloc(reqbuf.count, sizeof(struct camera_t));

    // 3. 逐个查询并映射
    for (int i = 0; i < reqbuf.count; i++) {
        struct v4l2_buffer buf;
        buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
        buf.memory = V4L2_MEMORY_MMAP;
        buf.index = i;

        if (ioctl(fd, VIDIOC_QUERYBUF, &buf) < 0) {
            perror("VIDIOC_QUERYBUF failure!");
            return -1;
        }

        camera_data[i].length = buf.length;
        camera_data[i].start = mmap(NULL, buf.length,
                                     PROT_READ | PROT_WRITE,
                                     MAP_SHARED, fd, buf.m.offset);
        if (camera_data[i].start == MAP_FAILED) {
            perror("mmap failure!");
            return -1;
        }
    }

    // 4. 将所有缓冲区放入输入队列
    for (int i = 0; i < reqbuf.count; i++) {
        struct v4l2_buffer buf;
        buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
        buf.memory = V4L2_MEMORY_MMAP;
        buf.index = i;

        if (ioctl(fd, VIDIOC_QBUF, &buf) < 0) {
            perror("VIDIOC_QBUF failure");
            return -1;
        }
    }

    n_buffer = reqbuf.count;
    return 0;
}

/**
 * start_capturing - 启动视频流采集
 * @fd: 摄像头文件描述符
 *
 * 一句话总结:打开水龙头,相机开始往缓冲区里填数据。
 */
int start_capturing(int fd)
{
    enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    if (ioctl(fd, VIDIOC_STREAMON, &type) < 0) {
        perror("VIDIOC_STREAMON failure!");
        return -1;
    }
    return 0;
}

/**
 * read_frame - 从队列中取出一帧数据,保存为 YUV/RGB/JPEG
 * @fd: 摄像头文件描述符
 *
 * 一句话总结:从流水线上拿下一个满的桶,把里面的数据存成三种格式。
 */
int read_frame(int fd)
{
    // 1. 从输出队列取出一个满的缓冲区
    struct v4l2_buffer buf;
    buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    buf.memory = V4L2_MEMORY_MMAP;

    if (ioctl(fd, VIDIOC_DQBUF, &buf) < 0) {
        perror("VIDIOC_DQBUF failure!");
        return -1;
    }

    // 2. 获取帧数据指针和长度
    void *frame_ptr = camera_data[buf.index].start;
    int frame_len = camera_data[buf.index].length;

    // 3. 保存原始 YUYV 数据
    FILE *yuv_fp = fopen("./picture.yuv", "wb");
    if (yuv_fp) {
        fwrite(frame_ptr, frame_len, 1, yuv_fp);
        fclose(yuv_fp);
    }

    // 4. 转换为 RGB 文件
    yuyv_to_rgb(frame_ptr, "./picture.rgb", IMG_WIDTH, IMG_HEIGHT);

    // 5. 压缩为 JPEG
    yuyv_to_jpeg(frame_ptr, "./picture.jpg", IMG_WIDTH, IMG_HEIGHT, 90);

    // 6. 将缓冲区重新放回输入队列
    if (ioctl(fd, VIDIOC_QBUF, &buf) < 0) {
        perror("VIDIOC_QBUF failure!");
        return -1;
    }

    return 0;
}

/**
 * yuyv_to_rgb - 将 YUYV 数据转换为 RGB 文件
 * @yuv: YUYV 原始数据
 * @file_name: 输出 RGB 文件名
 * @width: 图片宽度
 * @height: 图片高度
 *
 * 一句话总结:遍历每个像素,用 BT.601 公式把 YUYV 变成 RGB,写入文件。
 * 关键:YUYV 中每 2 个像素共享一组 UV。
 */
int yuyv_to_rgb(const unsigned char *yuv, const char *file_name,
                int width, int height)
{
    if (!yuv || !file_name || width == 0 || height == 0)
        return -1;

    FILE *fp = fopen(file_name, "wb");
    if (!fp) return -1;

    unsigned char *line_buf = malloc(width * 3);
    int y, u, v, r, g, b;

    for (int row = 0; row < height; row++) {
        unsigned char *ptr = line_buf;

        for (int col = 0; col < width; col += 2) {
            // 第一个像素
            y = yuv[0];
            u = yuv[1];
            v = yuv[3];

            r = y + 1.402 * (v - 128);
            g = y - 0.344 * (u - 128) - 0.714 * (v - 128);
            b = y + 1.772 * (u - 128);

            // 钳位到 0~255
            ptr[0] = (r > 255) ? 255 : (r < 0 ? 0 : r);
            ptr[1] = (g > 255) ? 255 : (g < 0 ? 0 : g);
            ptr[2] = (b > 255) ? 255 : (b < 0 ? 0 : b);

            // 第二个像素(U、V 不变,只换 Y)
            y = yuv[2];

            r = y + 1.402 * (v - 128);
            g = y - 0.344 * (u - 128) - 0.714 * (v - 128);
            b = y + 1.772 * (u - 128);

            ptr[3] = (r > 255) ? 255 : (r < 0 ? 0 : r);
            ptr[4] = (g > 255) ? 255 : (g < 0 ? 0 : g);
            ptr[5] = (b > 255) ? 255 : (b < 0 ? 0 : b);

            ptr += 6;
            yuv += 4;  // 跳过 Y0 U Y1 V
        }

        fwrite(line_buf, width * 3, 1, fp);
    }

    fclose(fp);
    free(line_buf);
    return 0;
}

/**
 * yuyv_to_jpeg - 将 YUYV 数据压缩为 JPEG 文件
 * @yuv: YUYV 原始数据
 * @file_name: 输出 JPEG 文件名
 * @width: 图片宽度
 * @height: 图片高度
 * @quality: 压缩质量 (1~100)
 *
 * 一句话总结:每一行先把 YUYV 转成 RGB,再喂给 libjpeg 逐行压缩。
 * 类比:就像流水线作业------先清洗(YUV→RGB),再打包(libjpeg 压缩)。
 */
int yuyv_to_jpeg(const unsigned char *yuv, const char *file_name,
                 int width, int height, int quality)
{
    FILE *fp = fopen(file_name, "wb");
    if (!fp) return -1;

    struct jpeg_compress_struct cinfo;
    struct jpeg_error_mgr jerr;
    JSAMPROW row_pointer[1];

    cinfo.err = jpeg_std_error(&jerr);
    jpeg_create_compress(&cinfo);
    jpeg_stdio_dest(&cinfo, fp);

    cinfo.image_width = width;
    cinfo.image_height = height;
    cinfo.input_components = 3;
    cinfo.in_color_space = JCS_RGB;

    jpeg_set_defaults(&cinfo);
    jpeg_set_quality(&cinfo, quality, TRUE);
    jpeg_start_compress(&cinfo, TRUE);

    unsigned char *line_buf = calloc(width * 3, 1);
    int y, u, v, r, g, b;

    while (cinfo.next_scanline < height) {
        unsigned char *ptr = line_buf;

        for (int col = 0; col < width; col += 2) {
            y = yuv[0];
            u = yuv[1];
            v = yuv[3];

            r = y + 1.402 * (v - 128);
            g = y - 0.344 * (u - 128) - 0.714 * (v - 128);
            b = y + 1.772 * (u - 128);

            ptr[0] = (r > 255) ? 255 : (r < 0 ? 0 : r);
            ptr[1] = (g > 255) ? 255 : (g < 0 ? 0 : g);
            ptr[2] = (b > 255) ? 255 : (b < 0 ? 0 : b);

            y = yuv[2];

            r = y + 1.402 * (v - 128);
            g = y - 0.344 * (u - 128) - 0.714 * (v - 128);
            b = y + 1.772 * (u - 128);

            ptr[3] = (r > 255) ? 255 : (r < 0 ? 0 : r);
            ptr[4] = (g > 255) ? 255 : (g < 0 ? 0 : g);
            ptr[5] = (b > 255) ? 255 : (b < 0 ? 0 : b);

            ptr += 6;
            yuv += 4;
        }

        row_pointer[0] = line_buf;
        jpeg_write_scanlines(&cinfo, row_pointer, 1);
    }

    jpeg_finish_compress(&cinfo);
    jpeg_destroy_compress(&cinfo);
    fclose(fp);
    free(line_buf);
    return 0;
}

3.2.3 main.c --- 主程序入口

c 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/select.h>
#include "camera.h"

int main()
{
    // 1. 初始化摄像头
    int cam_fd = init_camera("/dev/video0");
    if (cam_fd < 0) {
        fprintf(stderr, "init_camera 失败!\n");
        return -1;
    }

    // 2. 申请并映射缓冲区
    if (init_mmap(cam_fd) < 0) {
        fprintf(stderr, "init_mmap 失败!\n");
        return -1;
    }

    // 3. 开始采集
    if (start_capturing(cam_fd) < 0) {
        fprintf(stderr, "start_capturing 失败!\n");
        return -1;
    }

    // 4. 用 select 等待数据,超时 20 秒
    struct timeval tv;
    tv.tv_sec = 20;
    tv.tv_usec = 0;

    fd_set fds;
    FD_ZERO(&fds);
    FD_SET(cam_fd, &fds);

    printf("等待摄像头数据...(超时 20 秒)\n");
    int r = select(cam_fd + 1, &fds, NULL, NULL, &tv);

    if (r == 0) {
        printf("超时:20 秒内没有数据到来\n");
        return 0;
    } else if (r < 0) {
        perror("select 出错");
        return -1;
    }

    // 5. 数据就绪,读取一帧
    if (FD_ISSET(cam_fd, &fds)) {
        printf("数据就绪,正在读取帧...\n");
        if (read_frame(cam_fd) < 0) {
            fprintf(stderr, "read_frame 失败!\n");
            return -1;
        }
        printf("成功!已生成以下文件:\n");
        printf("   picture.yuv  --- 原始 YUYV 数据\n");
        printf("   picture.rgb  --- 转换后的 RGB 数据\n");
        printf("   picture.jpg  --- 压缩后的 JPEG 图片\n");
    }

    return 0;
}

3.2.4 Makefile 与编译说明

makefile 复制代码
# 交叉编译工具链(ARM 平台示例)
CROSS_COMPILE = arm-linux-gnueabihf-
CC = $(CROSS_COMPILE)gcc

# 目标平台编译(需要 libjpeg 库)
CFLAGS = -I/opt/arm/libjpeg/result/include
LDFLAGS = -L/opt/arm/libjpeg/result/lib -ljpeg

# 本机编译测试(Ubuntu x86)
# CFLAGS = $(shell pkg-config --cflags libjpeg)
# LDFLAGS = $(shell pkg-config --libs libjpeg)

TARGET = camera_capture
OBJS = main.o camera.o

all: $(TARGET)

$(TARGET): $(OBJS)
	$(CC) -o $@ $^ $(LDFLAGS)

%.o: %.c
	$(CC) $(CFLAGS) -c -o $@ $<

clean:
	rm -f $(TARGET) $(OBJS) *.yuv *.rgb *.jpg

.PHONY: all clean

编译步骤

bash 复制代码
# 1. 安装 libjpeg 开发库(Ubuntu x86 本机测试)
sudo apt-get install libjpeg-dev

# 2. 编译
make

# 3. 运行(需要摄像头设备)
sudo ./camera_capture

# 4. 查看结果
eog picture.jpg

注意:ARM 交叉编译需要先交叉编译 libjpeg 库(参考 2.3.4a 节),然后将生成的可执行文件拷贝到开发板上运行。


附录

A. 常见报错与解决

报错信息 原因 解决方法
open dev failure! 摄像头设备不存在或权限不足 检查 /dev/video* 是否存在,用 sudo 运行
VIDIOC_QUERYCAP failure! 该设备不是 V4L2 设备 确认设备路径是否正确
camera can't CAPTURE! 设备不支持视频捕获 换一个支持拍照的摄像头
VIDIOC_S_FMT failure! 设置格式失败 先用 v4l2-ctl --list-devices 确认支持的格式
cheese 窗口黑屏 虚拟机 USB 兼容性问题 调整 USB 控制器版本(2.0/3.0)
JPEG 图片花屏 RGB 数据排列错误或计算公式不对 检查 YUYV 字节序和 BT.601/709 标准选择

B. 调试命令速查

bash 复制代码
# 查看设备列表
v4l2-ctl --list-devices

# 查看摄像头所有参数
v4l2-ctl -d /dev/video0 --all

# 列出支持的格式
v4l2-ctl -d /dev/video0 --list-formats

# 查看当前格式
v4l2-ctl -d /dev/video0 --get-fmt-video

# 抓取一帧原始数据
v4l2-ctl -d /dev/video0 --set-fmt-video=width=640,height=480,pixelformat=YUYV \
         --stream-mmap --stream-to=frame.yuv --stream-count=1

# 查看图片
eog picture.jpg

C. 关键概念速查表

概念 一句话总结
V4L2 Linux 内核自带的视频设备 API,让开发者用一套函数操作不同品牌的摄像头
ioctl 万能控制函数,V4L2 的所有操作都通过它完成
mmap 在用户空间和内核空间之间开一扇门,避免数据拷贝,提升效率
select 用来等数据的函数,设好超时时间,有数据来或超时都会返回
YUV 把颜色拆成"亮度(Y)"和"色度(UV)",人眼对亮度敏感对色彩不敏感
YUV 4:2:2 每 2 个像素共用一组 UV,节省 33% 空间
YUV 4:2:0 每 4 个像素共用一组 UV,节省 50% 空间,视频通话最常用
Planar 存储 所有 Y 挨着存,所有 U 挨着存,所有 V 挨着存
Packed 存储 每个像素的 YUV 挨着存,再存下一个像素
BT.601 标清电视时代的 YUV→RGB 转换标准,嵌入式摄像头最常用
JPEG 有损压缩格式,允许用户调整质量(1~100),文件小但会丢失细节
libjpeg 一个 C 语言库,接收 RGB 数据,输出 JPEG 文件

D. 知识体系脑图

#mermaid-svg-nggNHiYj9jijV0cX{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-nggNHiYj9jijV0cX .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-nggNHiYj9jijV0cX .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-nggNHiYj9jijV0cX .error-icon{fill:#552222;}#mermaid-svg-nggNHiYj9jijV0cX .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-nggNHiYj9jijV0cX .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-nggNHiYj9jijV0cX .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-nggNHiYj9jijV0cX .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-nggNHiYj9jijV0cX .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-nggNHiYj9jijV0cX .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-nggNHiYj9jijV0cX .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-nggNHiYj9jijV0cX .marker{fill:#333333;stroke:#333333;}#mermaid-svg-nggNHiYj9jijV0cX .marker.cross{stroke:#333333;}#mermaid-svg-nggNHiYj9jijV0cX svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-nggNHiYj9jijV0cX p{margin:0;}#mermaid-svg-nggNHiYj9jijV0cX .edge{stroke-width:3;}#mermaid-svg-nggNHiYj9jijV0cX .section--1 rect,#mermaid-svg-nggNHiYj9jijV0cX .section--1 path,#mermaid-svg-nggNHiYj9jijV0cX .section--1 circle,#mermaid-svg-nggNHiYj9jijV0cX .section--1 polygon,#mermaid-svg-nggNHiYj9jijV0cX .section--1 path{fill:hsl(240, 100%, 76.2745098039%);}#mermaid-svg-nggNHiYj9jijV0cX .section--1 text{fill:#ffffff;}#mermaid-svg-nggNHiYj9jijV0cX .node-icon--1{font-size:40px;color:#ffffff;}#mermaid-svg-nggNHiYj9jijV0cX .section-edge--1{stroke:hsl(240, 100%, 76.2745098039%);}#mermaid-svg-nggNHiYj9jijV0cX .edge-depth--1{stroke-width:17;}#mermaid-svg-nggNHiYj9jijV0cX .section--1 line{stroke:hsl(60, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-nggNHiYj9jijV0cX .disabled,#mermaid-svg-nggNHiYj9jijV0cX .disabled circle,#mermaid-svg-nggNHiYj9jijV0cX .disabled text{fill:lightgray;}#mermaid-svg-nggNHiYj9jijV0cX .disabled text{fill:#efefef;}#mermaid-svg-nggNHiYj9jijV0cX .section-0 rect,#mermaid-svg-nggNHiYj9jijV0cX .section-0 path,#mermaid-svg-nggNHiYj9jijV0cX .section-0 circle,#mermaid-svg-nggNHiYj9jijV0cX .section-0 polygon,#mermaid-svg-nggNHiYj9jijV0cX .section-0 path{fill:hsl(60, 100%, 73.5294117647%);}#mermaid-svg-nggNHiYj9jijV0cX .section-0 text{fill:black;}#mermaid-svg-nggNHiYj9jijV0cX .node-icon-0{font-size:40px;color:black;}#mermaid-svg-nggNHiYj9jijV0cX .section-edge-0{stroke:hsl(60, 100%, 73.5294117647%);}#mermaid-svg-nggNHiYj9jijV0cX .edge-depth-0{stroke-width:14;}#mermaid-svg-nggNHiYj9jijV0cX .section-0 line{stroke:hsl(240, 100%, 83.5294117647%);stroke-width:3;}#mermaid-svg-nggNHiYj9jijV0cX .disabled,#mermaid-svg-nggNHiYj9jijV0cX .disabled circle,#mermaid-svg-nggNHiYj9jijV0cX .disabled text{fill:lightgray;}#mermaid-svg-nggNHiYj9jijV0cX .disabled text{fill:#efefef;}#mermaid-svg-nggNHiYj9jijV0cX .section-1 rect,#mermaid-svg-nggNHiYj9jijV0cX .section-1 path,#mermaid-svg-nggNHiYj9jijV0cX .section-1 circle,#mermaid-svg-nggNHiYj9jijV0cX .section-1 polygon,#mermaid-svg-nggNHiYj9jijV0cX .section-1 path{fill:hsl(80, 100%, 76.2745098039%);}#mermaid-svg-nggNHiYj9jijV0cX .section-1 text{fill:black;}#mermaid-svg-nggNHiYj9jijV0cX .node-icon-1{font-size:40px;color:black;}#mermaid-svg-nggNHiYj9jijV0cX .section-edge-1{stroke:hsl(80, 100%, 76.2745098039%);}#mermaid-svg-nggNHiYj9jijV0cX .edge-depth-1{stroke-width:11;}#mermaid-svg-nggNHiYj9jijV0cX .section-1 line{stroke:hsl(260, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-nggNHiYj9jijV0cX .disabled,#mermaid-svg-nggNHiYj9jijV0cX .disabled circle,#mermaid-svg-nggNHiYj9jijV0cX .disabled text{fill:lightgray;}#mermaid-svg-nggNHiYj9jijV0cX .disabled text{fill:#efefef;}#mermaid-svg-nggNHiYj9jijV0cX .section-2 rect,#mermaid-svg-nggNHiYj9jijV0cX .section-2 path,#mermaid-svg-nggNHiYj9jijV0cX .section-2 circle,#mermaid-svg-nggNHiYj9jijV0cX .section-2 polygon,#mermaid-svg-nggNHiYj9jijV0cX .section-2 path{fill:hsl(270, 100%, 76.2745098039%);}#mermaid-svg-nggNHiYj9jijV0cX .section-2 text{fill:#ffffff;}#mermaid-svg-nggNHiYj9jijV0cX .node-icon-2{font-size:40px;color:#ffffff;}#mermaid-svg-nggNHiYj9jijV0cX .section-edge-2{stroke:hsl(270, 100%, 76.2745098039%);}#mermaid-svg-nggNHiYj9jijV0cX .edge-depth-2{stroke-width:8;}#mermaid-svg-nggNHiYj9jijV0cX .section-2 line{stroke:hsl(90, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-nggNHiYj9jijV0cX .disabled,#mermaid-svg-nggNHiYj9jijV0cX .disabled circle,#mermaid-svg-nggNHiYj9jijV0cX .disabled text{fill:lightgray;}#mermaid-svg-nggNHiYj9jijV0cX .disabled text{fill:#efefef;}#mermaid-svg-nggNHiYj9jijV0cX .section-3 rect,#mermaid-svg-nggNHiYj9jijV0cX .section-3 path,#mermaid-svg-nggNHiYj9jijV0cX .section-3 circle,#mermaid-svg-nggNHiYj9jijV0cX .section-3 polygon,#mermaid-svg-nggNHiYj9jijV0cX .section-3 path{fill:hsl(300, 100%, 76.2745098039%);}#mermaid-svg-nggNHiYj9jijV0cX .section-3 text{fill:black;}#mermaid-svg-nggNHiYj9jijV0cX .node-icon-3{font-size:40px;color:black;}#mermaid-svg-nggNHiYj9jijV0cX .section-edge-3{stroke:hsl(300, 100%, 76.2745098039%);}#mermaid-svg-nggNHiYj9jijV0cX .edge-depth-3{stroke-width:5;}#mermaid-svg-nggNHiYj9jijV0cX .section-3 line{stroke:hsl(120, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-nggNHiYj9jijV0cX .disabled,#mermaid-svg-nggNHiYj9jijV0cX .disabled circle,#mermaid-svg-nggNHiYj9jijV0cX .disabled text{fill:lightgray;}#mermaid-svg-nggNHiYj9jijV0cX .disabled text{fill:#efefef;}#mermaid-svg-nggNHiYj9jijV0cX .section-4 rect,#mermaid-svg-nggNHiYj9jijV0cX .section-4 path,#mermaid-svg-nggNHiYj9jijV0cX .section-4 circle,#mermaid-svg-nggNHiYj9jijV0cX .section-4 polygon,#mermaid-svg-nggNHiYj9jijV0cX .section-4 path{fill:hsl(330, 100%, 76.2745098039%);}#mermaid-svg-nggNHiYj9jijV0cX .section-4 text{fill:black;}#mermaid-svg-nggNHiYj9jijV0cX .node-icon-4{font-size:40px;color:black;}#mermaid-svg-nggNHiYj9jijV0cX .section-edge-4{stroke:hsl(330, 100%, 76.2745098039%);}#mermaid-svg-nggNHiYj9jijV0cX .edge-depth-4{stroke-width:2;}#mermaid-svg-nggNHiYj9jijV0cX .section-4 line{stroke:hsl(150, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-nggNHiYj9jijV0cX .disabled,#mermaid-svg-nggNHiYj9jijV0cX .disabled circle,#mermaid-svg-nggNHiYj9jijV0cX .disabled text{fill:lightgray;}#mermaid-svg-nggNHiYj9jijV0cX .disabled text{fill:#efefef;}#mermaid-svg-nggNHiYj9jijV0cX .section-5 rect,#mermaid-svg-nggNHiYj9jijV0cX .section-5 path,#mermaid-svg-nggNHiYj9jijV0cX .section-5 circle,#mermaid-svg-nggNHiYj9jijV0cX .section-5 polygon,#mermaid-svg-nggNHiYj9jijV0cX .section-5 path{fill:hsl(0, 100%, 76.2745098039%);}#mermaid-svg-nggNHiYj9jijV0cX .section-5 text{fill:black;}#mermaid-svg-nggNHiYj9jijV0cX .node-icon-5{font-size:40px;color:black;}#mermaid-svg-nggNHiYj9jijV0cX .section-edge-5{stroke:hsl(0, 100%, 76.2745098039%);}#mermaid-svg-nggNHiYj9jijV0cX .edge-depth-5{stroke-width:-1;}#mermaid-svg-nggNHiYj9jijV0cX .section-5 line{stroke:hsl(180, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-nggNHiYj9jijV0cX .disabled,#mermaid-svg-nggNHiYj9jijV0cX .disabled circle,#mermaid-svg-nggNHiYj9jijV0cX .disabled text{fill:lightgray;}#mermaid-svg-nggNHiYj9jijV0cX .disabled text{fill:#efefef;}#mermaid-svg-nggNHiYj9jijV0cX .section-6 rect,#mermaid-svg-nggNHiYj9jijV0cX .section-6 path,#mermaid-svg-nggNHiYj9jijV0cX .section-6 circle,#mermaid-svg-nggNHiYj9jijV0cX .section-6 polygon,#mermaid-svg-nggNHiYj9jijV0cX .section-6 path{fill:hsl(30, 100%, 76.2745098039%);}#mermaid-svg-nggNHiYj9jijV0cX .section-6 text{fill:black;}#mermaid-svg-nggNHiYj9jijV0cX .node-icon-6{font-size:40px;color:black;}#mermaid-svg-nggNHiYj9jijV0cX .section-edge-6{stroke:hsl(30, 100%, 76.2745098039%);}#mermaid-svg-nggNHiYj9jijV0cX .edge-depth-6{stroke-width:-4;}#mermaid-svg-nggNHiYj9jijV0cX .section-6 line{stroke:hsl(210, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-nggNHiYj9jijV0cX .disabled,#mermaid-svg-nggNHiYj9jijV0cX .disabled circle,#mermaid-svg-nggNHiYj9jijV0cX .disabled text{fill:lightgray;}#mermaid-svg-nggNHiYj9jijV0cX .disabled text{fill:#efefef;}#mermaid-svg-nggNHiYj9jijV0cX .section-7 rect,#mermaid-svg-nggNHiYj9jijV0cX .section-7 path,#mermaid-svg-nggNHiYj9jijV0cX .section-7 circle,#mermaid-svg-nggNHiYj9jijV0cX .section-7 polygon,#mermaid-svg-nggNHiYj9jijV0cX .section-7 path{fill:hsl(90, 100%, 76.2745098039%);}#mermaid-svg-nggNHiYj9jijV0cX .section-7 text{fill:black;}#mermaid-svg-nggNHiYj9jijV0cX .node-icon-7{font-size:40px;color:black;}#mermaid-svg-nggNHiYj9jijV0cX .section-edge-7{stroke:hsl(90, 100%, 76.2745098039%);}#mermaid-svg-nggNHiYj9jijV0cX .edge-depth-7{stroke-width:-7;}#mermaid-svg-nggNHiYj9jijV0cX .section-7 line{stroke:hsl(270, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-nggNHiYj9jijV0cX .disabled,#mermaid-svg-nggNHiYj9jijV0cX .disabled circle,#mermaid-svg-nggNHiYj9jijV0cX .disabled text{fill:lightgray;}#mermaid-svg-nggNHiYj9jijV0cX .disabled text{fill:#efefef;}#mermaid-svg-nggNHiYj9jijV0cX .section-8 rect,#mermaid-svg-nggNHiYj9jijV0cX .section-8 path,#mermaid-svg-nggNHiYj9jijV0cX .section-8 circle,#mermaid-svg-nggNHiYj9jijV0cX .section-8 polygon,#mermaid-svg-nggNHiYj9jijV0cX .section-8 path{fill:hsl(150, 100%, 76.2745098039%);}#mermaid-svg-nggNHiYj9jijV0cX .section-8 text{fill:black;}#mermaid-svg-nggNHiYj9jijV0cX .node-icon-8{font-size:40px;color:black;}#mermaid-svg-nggNHiYj9jijV0cX .section-edge-8{stroke:hsl(150, 100%, 76.2745098039%);}#mermaid-svg-nggNHiYj9jijV0cX .edge-depth-8{stroke-width:-10;}#mermaid-svg-nggNHiYj9jijV0cX .section-8 line{stroke:hsl(330, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-nggNHiYj9jijV0cX .disabled,#mermaid-svg-nggNHiYj9jijV0cX .disabled circle,#mermaid-svg-nggNHiYj9jijV0cX .disabled text{fill:lightgray;}#mermaid-svg-nggNHiYj9jijV0cX .disabled text{fill:#efefef;}#mermaid-svg-nggNHiYj9jijV0cX .section-9 rect,#mermaid-svg-nggNHiYj9jijV0cX .section-9 path,#mermaid-svg-nggNHiYj9jijV0cX .section-9 circle,#mermaid-svg-nggNHiYj9jijV0cX .section-9 polygon,#mermaid-svg-nggNHiYj9jijV0cX .section-9 path{fill:hsl(180, 100%, 76.2745098039%);}#mermaid-svg-nggNHiYj9jijV0cX .section-9 text{fill:black;}#mermaid-svg-nggNHiYj9jijV0cX .node-icon-9{font-size:40px;color:black;}#mermaid-svg-nggNHiYj9jijV0cX .section-edge-9{stroke:hsl(180, 100%, 76.2745098039%);}#mermaid-svg-nggNHiYj9jijV0cX .edge-depth-9{stroke-width:-13;}#mermaid-svg-nggNHiYj9jijV0cX .section-9 line{stroke:hsl(0, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-nggNHiYj9jijV0cX .disabled,#mermaid-svg-nggNHiYj9jijV0cX .disabled circle,#mermaid-svg-nggNHiYj9jijV0cX .disabled text{fill:lightgray;}#mermaid-svg-nggNHiYj9jijV0cX .disabled text{fill:#efefef;}#mermaid-svg-nggNHiYj9jijV0cX .section-10 rect,#mermaid-svg-nggNHiYj9jijV0cX .section-10 path,#mermaid-svg-nggNHiYj9jijV0cX .section-10 circle,#mermaid-svg-nggNHiYj9jijV0cX .section-10 polygon,#mermaid-svg-nggNHiYj9jijV0cX .section-10 path{fill:hsl(210, 100%, 76.2745098039%);}#mermaid-svg-nggNHiYj9jijV0cX .section-10 text{fill:black;}#mermaid-svg-nggNHiYj9jijV0cX .node-icon-10{font-size:40px;color:black;}#mermaid-svg-nggNHiYj9jijV0cX .section-edge-10{stroke:hsl(210, 100%, 76.2745098039%);}#mermaid-svg-nggNHiYj9jijV0cX .edge-depth-10{stroke-width:-16;}#mermaid-svg-nggNHiYj9jijV0cX .section-10 line{stroke:hsl(30, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-nggNHiYj9jijV0cX .disabled,#mermaid-svg-nggNHiYj9jijV0cX .disabled circle,#mermaid-svg-nggNHiYj9jijV0cX .disabled text{fill:lightgray;}#mermaid-svg-nggNHiYj9jijV0cX .disabled text{fill:#efefef;}#mermaid-svg-nggNHiYj9jijV0cX .section-root rect,#mermaid-svg-nggNHiYj9jijV0cX .section-root path,#mermaid-svg-nggNHiYj9jijV0cX .section-root circle,#mermaid-svg-nggNHiYj9jijV0cX .section-root polygon{fill:hsl(240, 100%, 46.2745098039%);}#mermaid-svg-nggNHiYj9jijV0cX .section-root text{fill:#ffffff;}#mermaid-svg-nggNHiYj9jijV0cX .section-root span{color:#ffffff;}#mermaid-svg-nggNHiYj9jijV0cX .section-2 span{color:#ffffff;}#mermaid-svg-nggNHiYj9jijV0cX .icon-container{height:100%;display:flex;justify-content:center;align-items:center;}#mermaid-svg-nggNHiYj9jijV0cX .edge{fill:none;}#mermaid-svg-nggNHiYj9jijV0cX .mindmap-node-label{dy:1em;alignment-baseline:middle;text-anchor:middle;dominant-baseline:middle;text-align:center;}#mermaid-svg-nggNHiYj9jijV0cX :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 嵌入式摄像头

采集与处理
采集层
硬件设备
USB 摄像头
CSI 摄像头
HDMI 采集卡
工具
cheese 测试
v4l2-ctl 命令行
参数
分辨率 Width x Height
帧率 FPS
颜色空间 Colorspace
接口类型
V4L2 驱动层
核心概念
一切皆文件 /dev/video
ioctl 万能控制
文件描述符 fd
关键操作
QUERYCAP 查能力
ENUM_FMT 枚举格式
S_FMT 设置格式
REQBUFS 申请缓冲区
QUERYBUF 查询缓冲区
QBUF / DQBUF 入队出队
STREAMON / OFF 开关流
内存管理
mmap 零拷贝映射
select 等待机制
颜色空间
RGB vs YUV
数据量差异
人眼敏感度差异
摄像头兼容性
采样方式
4:4:4 无损
4:2:2 省33%
4:2:0 省50% 最常用
存储方式
Planar 平面存储
Packed 打包存储
转换标准
BT.601 标清
BT.709 高清
BT.2020 超高清
压缩输出
JPEG 格式
有损压缩
质量可调 1-100
DCT + 量化 + Huffman
libjpeg 库
初始化压缩对象
设置参数
逐行写入
完成压缩
代码工程
camera.h 头文件
camera.c 驱动实现
main.c 主程序入口


最终生成文件picture.yuv(原始数据)、picture.rgb(RGB 数据)、picture.jpg(JPEG 图片)。

相关推荐
studytosky1 小时前
OpenClaw 入门与 Skill 开发
linux·服务器·ai编程
kaoa0001 小时前
Linux入门攻坚——82、kvm虚拟化-2
linux·运维·服务器
不羁的木木1 小时前
HarmonyOS APP实战-基于Image Kit的图像处理APP - 第8篇:图片编码与保存
图像处理·华为·harmonyos
学无止境_永不停歇2 小时前
3. 进程状态
linux·服务器
kdxiaojie3 小时前
Linux 驱动研究 —— V4L2 (2)
linux·运维·笔记·学习
DB哥讲数据库3 小时前
MySQL 8.4 安装教程:超详细图文讲解(附mysql安装包)
linux·数据库·mysql·centos
xlxxy_3 小时前
sap获取批次特性报表
java·linux·开发语言·前端·数据库·abap·mm
木卫二号Coding3 小时前
CentOS 系统 Shell 脚本开机自启动方法详解
linux
l1t3 小时前
DeepSeek总结的RegreSQL 2.0测试通过了。计划却没通过。
linux·数据库·postgresql