概述
拍照的总体流程,在数码相机或影像传感器(如CMOS、CCD)中,拍摄图像的过程可以分为以下几个核心阶段:
- 光线进入镜头 → 打在感光元件上(图像传感器)
- 感光元件将光信号转换为电信号(电压信号)
- 电信号经过采样、放大、模数转换(ADC)成为数字信号
- 数字信号经图像处理(白平衡、降噪、锐化)→ 输出图像文件
曝光
- 曝光(Exposure)指的是传感器接收到的总光量,它决定了图像的明暗程度。曝光由以下三个要素共同决定:
- 快门时间(Shutter Speed):光照射在传感器上的时间长度。
- 光圈大小(Aperture):光线进入镜头的通光孔径。
- 感光度(ISO):传感器对光信号的放大倍数。
快门
- 快门的作用:控制光照时间窗口
- 快门是控制光照射时间的"门"。它决定了传感器在多长时间内"接受"光信号。
- 机械快门:通过物理遮光板打开/关闭。
- 电子快门:通过控制传感器读出电路的开关时间实现。
- 在快门打开的时间内,光子打到传感器的每个像素上,产生电子积累。快门关闭后,传感器开始读取这些电子信号。
- 快门时间:从
传感器开始接收光信号到停止接收光信号之间的时间。在这段时间内,每个像素的光电二极管都在"积分"入射光能量,即不断累积电子。当快门关闭后,积分结束,电荷量固定下来,随后才进入读出阶段。
电信号采集
电压信号采集过程:从光子到电压
- 当光照射到图像传感器(以CMOS为例)时:
- 光电转换:光子打在像素点的光电二极管上,释放电子,形成电荷累积。
- 电荷-电压转换:在快门关闭后,像素的电荷通过电容转化为电压信号。
- 读出放大与采样保持(Sample & Hold):每个像素的电压信号被读取、放大,并暂存。
- 模数转换(ADC):模拟电压信号转换为数字信号,供图像处理使用。
bash
时间轴: |--- 快门开启 --->--- 快门关闭 ---> 读出/采样 ---> ADC转换 ---> 数字信号输出 |
信号变化: | 光子积累(电荷增加) | 电荷保持 | 电压采样 | 电压转数字 | 图像信号生成 |
全局快门(Global Shutter)和卷帘快门(Rolling Shutter):
CMOS 传感器**"理论上"每个像素都能独立采样**,但"**实际上"电路和物理架构根本无法让所有像素同时采样**:
- 回顾拍照流程:光进入感光元器件、快门打开、开始曝光、曝光结束、快门关闭、电压采样。
- 因为无法所有像素同时采样,所以发明了一些方法:
- 卷帘快门:逐行曝光 + 逐行采样。(果冻效应,但是电荷转移少,噪点少)
- 全局快门:整体曝光 + 电容保持电荷 + 逐行挨个采样。(电容保持电荷存在噪点,但是没有果冻效应)
其余的折中方案:
| 方案 | 思想 | 优缺点 |
|---|---|---|
| 卷帘快门 | 行扫描,逐行曝光+读出 | 简单、便宜、有果冻效应 |
| 全局快门 | 所有像素同时曝光+采样 | 无畸变、结构复杂、成本高 |
| 准全局快门(Near-Global) | 多行同步曝光、快速读出 | 畸变极小,成本可控 |
| 存储层分离架构(stacked sensor) | 在像素层下增加独立读出层 | 紧凑、性能提升,但制造贵(Sony 近年采用) |
卷帘快门带来的果冻效应:因为卷帘快门是一行一行曝光、一行一行采样,所以会产生"果冻效应",也就是运动模糊。

那一款相机既支持全局快门也支持卷帘快门,你之前说的缺点"光敏面积变小"、"电路复杂度上升",是不是就不存在了,因为他已经做好了全局快门?此时卷帘快门的唯一优势就是功耗低?
- 确实一款相机如果同时支持全局快门与卷帘快门,那么它的底层硬件都已经固定,全局快门带来了的"光敏面积变小"、"电路复杂度上升"的问题,卷帘快门模式下也仍然存在。
- 但是卷帘快门也有它的优势:噪声更低一点。
- 在卷帘快门(Rolling Shutter)模式下,每个像素确实不需要额外的存储电容(Sample & Hold Capacitor),而是在曝光结束后,直接把像素节点的电荷转换为电压并立刻读取。卷帘快门像素的"电荷→电压→数字化"过程是同步发生的。
- 全局快门模式下,电荷需要从光电二极管转移两次(PD → Csh → 放大节点),每次转移都会带来信号失真与噪声。
CCD 与 CMOS 的区别
CCD与CMOS图像传感器光电转换的原理相同,他们最主要的差别在于信号的读出过程不同;
- 光电转化:光子打到感光单元(像素) → 电子被激发 → 形成电荷信号。
- 由于CCD仅有一个(或少数几个)输出节点统一读出,其信号输出的一致性非常好; 而CMOS芯片中,每个像素都有各自的信号放大器,各自进行电荷-电压的转换,其信号输出的一致性较差。
软件调试与验证
V4L2-ctrl 介绍
V4L 是 Video for Linux 的缩写,2 代表是第二个版本,v4l2-ctl 支持绝大多数 USB 摄像头的关键原因在于它依赖的 V4L2 通用标准层 与 UVC 驱动:
通用的 UVC 标准驱动,绝大多数 USB 摄像头都遵循 USB Video Class (UVC) 标准,因此,v4l2-ctl 能通过标准的 V4L2 控制接口访问这些控件。
- Linux 内核中自带的 uvcvideo 驱动支持 UVC 1.0/1.1/1.5 协议。
- uvcvideo 驱动在注册 /dev/videoX 设备时,自动将摄像头的功能映射为 V4L2 控件(controls),例如
V4L2_CID_BRIGHTNESSV4L2_CID_CONTRASTV4L2_CID_EXPOSURE_AUTOV4L2_CID_FOCUS_ABSOLUTE
厂商扩展控制的兼容机制
- 即使部分摄像头有厂商私有控制项(如 Logitech 的人脸跟踪、红外切换),它们通常仍通过
V4L2_CID_PRIVATE_BASE向上注册。 v4l2-ctl --list-ctrls会列出所有这些自定义控制。- 只要驱动正确注册,v4l2-ctl 就能访问。
UVC 驱动与 V4L2 子系统高度统一
- 内核中 uvcvideo 驱动直接实现了 V4L2 子系统的所有关键接口(包括视频采集、流控制、控制项查询)。
- 因此,v4l2-ctl 几乎不需要针对不同品牌做额外适配。
V4L2-ctrl 指令
安装相关工具:
bash
sudo apt update
sudo apt install v4l-utils
v4l2-ctl --version
查看当前的 USB 相机列表
bash
# w300: 摄像头的逻辑名称(由设备固件或驱动提供)
# (usb-0000:00:14.0-6):摄像头连接的 USB 总线信息(物理连接位置)
# /dev/video0:摄像头的主视频流接口(通常是图像采集节点)
# /dev/video1:附属视频接口(可能是元数据流、红外流或麦克风接口)
# /dev/media0:Media Controller 节点,用于高级设备拓扑管理(连接传感器、ISP、编码器等子设备)
w300: w300 (usb-0000:00:14.0-6):
/dev/video0
/dev/video1
/dev/media0
查看某个具体视频流的配置:
bash
v4l2-ctl --device=/dev/video1 --all
# 查看用户可以修改的配置信息:
v4l2-ctl --device=/dev/video0 --list-ctrls
bash
# v4l2-ctl --device=/dev/video1 --all, 关于视频流的输出信息
Format Video Capture:
Width/Height : 1920/1080
Pixel Format : 'MJPG' (Motion-JPEG)
Field : None
Bytes per Line : 0
Size Image : 4147789
Colorspace : sRGB
Transfer Function : Default (maps to sRGB)
YCbCr/HSV Encoding: Default (maps to ITU-R 601)
Quantization : Default (maps to Full Range)
Format Video Capture 中的 Width/Height : 1920/1080,它表示当前输出视频流的分辨率,但不一定等于感光元件(sensor)的物理像素数量。
# 现代摄像头在传感器与输出之间还有许多中间环节
传感器原始像素 → ISP(图像信号处理)→ 编码/压缩 → USB 传输 → 输出帧
# 常见的几种情况:
1. 输出分辨率 = 传感器原生分辨率
最理想情况。
比如传感器物理像素是 1920×1080,驱动输出 1920×1080;
图像清晰,细节完整。
2. 输出分辨率 < 传感器分辨率(下采样)
常见于高分辨率传感器降级输出。
比如传感器 2592×1944(5MP),输出 1280×720;
驱动或 ISP 做了下采样(average/binning);
图像细节减少,但噪声更低。
3. 输出分辨率 > 传感器分辨率(插值放大)
廉价 USB 摄像头常见。
实际传感器也许只有 640×480(VGA),但驱动报告 1920×1080;驱动内部做了数字插值(软件放大),
结果"像素多了,但细节没变",画面看起来虚假清晰。
在 Linux 下通过 v4l2-ctl 或其他用户态命令行工具,你看不到摄像头传感器(MOS/CMOS Sensor)真正的物理像素数量,只能看到驱动报告出来的视频输出格式(分辨率、帧率、像素格式)
v4l2-ctl、ffmpeg、media-ctl等命令访问的是/dev/videoX设备。这条链路最终的输出是"视频流".- 驱动层通常不公开底层传感器信息:
- 对于 USB 摄像头(UVC 协议):内核只看到一个标准化的接口(UVC descriptors),不知道里面是什么传感器芯片(例如 OV5640、GC2145、IMX219 等)。它只知道设备"支持哪些分辨率"和"支持哪些像素格式"。
- 对于 MIPI/CSI 摄像头(嵌入式平台):驱动可能确实知道底层 sensor 信息,但只有在 media-ctl、libcamera 层才可能看到。
- 获取真实像素数的正确途径:先确认摄像头的
USB Vendor ID / Product ID---> 然后上网搜索对应的芯片,一般能查到使用的 CMOS 芯片型号(如 OV2640、GC0308 等),---> 再查该 sensor 的 datasheet,就能看到其原生像素。
查看当前视频流支持的所有分辨率、帧率、图像格式:
bash
v4l2-ctl --list-formats-ext
设置当前视频流的分辨率、帧率、图像格式:
- 修改分辨率只影响当前会话;设备重新插拔后通常会恢复默认。
- 若有多个摄像头,务必带上 --device=/dev/videoX。
- 如果用 ffplay、ffmpeg 等工具采流,它们可能在打开时会覆盖 v4l2-ctl 设置(因为它们会重新请求特定分辨率/格式)。
bash
v4l2-ctl --device=/dev/video0 --set-fmt-video=width=<宽度>,height=<高度>,pixelformat=<格式>
# 把摄像头设置为 1280×720,MJPEG 格式
v4l2-ctl --device=/dev/video0 --set-fmt-video=width=1280,height=720,pixelformat=MJPG
v4l2-ctl --set-fmt-video=width=1920,height=1080,pixelformat=MJPG --set-parm=30
设置当前视频流的曝光信息。
先查看当前的曝光信息:
auto_exposure:min=0 max=3 default=3 value=3 (Aperture Priority Mode),设置为1 的时候,才是手动模式,这时候才可以设置曝光时间。exposure_time_absolute:min=1 max=5000 step=1 default=157 value=157 flags=inactive
bash
v4l2-ctl --list-ctrls
User Controls
brightness 0x00980900 (int) : min=-64 max=64 step=1 default=0 value=0
contrast 0x00980901 (int) : min=0 max=64 step=1 default=32 value=32
saturation 0x00980902 (int) : min=0 max=128 step=1 default=64 value=64
hue 0x00980903 (int) : min=-40 max=40 step=1 default=0 value=0
white_balance_automatic 0x0098090c (bool) : default=1 value=1
gamma 0x00980910 (int) : min=72 max=500 step=1 default=100 value=100
gain 0x00980913 (int) : min=0 max=100 step=1 default=0 value=0
power_line_frequency 0x00980918 (menu) : min=0 max=2 default=1 value=1 (50 Hz)
white_balance_temperature 0x0098091a (int) : min=2800 max=6500 step=1 default=4600 value=4600 flags=inactive
sharpness 0x0098091b (int) : min=0 max=6 step=1 default=4 value=4
backlight_compensation 0x0098091c (int) : min=0 max=160 step=1 default=80 value=80
Camera Controls
auto_exposure 0x009a0901 (menu) : min=0 max=3 default=3 value=3 (Aperture Priority Mode)
exposure_time_absolute 0x009a0902 (int) : min=1 max=5000 step=1 default=157 value=157 flags=inactive
exposure_dynamic_framerate 0x009a0903 (bool) : default=0 value=1
设置不同的曝光率:受限需要关闭自动曝光,设置为手动曝光模式
bash
# 设置为手动曝光模式:
v4l2-ctl -c auto_exposure=1
# 设置曝光时间:
v4l2-ctl -c exposure_time_absolute=80
验证效果:使用 ffplay 来抓取视频流,此时曝光时间越短,画面应该越暗,曝光时间越长,画面应该越亮。
bash
ffplay /dev/video0
Opencv
opencv 安装
https://www.cnblogs.com/booturbo/p/17399215.html
opencv 架构
OpenCV(Open Source Computer Vision Library)是一个跨平台的计算机视觉库,
用 C/C++ 实现,底层封装了各种 图像采集、处理、显示与硬件接口。它可以运行在 Windows、Linux、macOS、Android、iOS、嵌入式板卡 等多平台。
OpenCV 的总体架构,可以概括为一个 "多层抽象模型":
bash
┌────────────────────────────┐
│ 应用层(Python / C++ / Java APIs)│
├────────────────────────────┤
│ 模块层(imgproc, videoio, dnn, calib3d, etc.)│
├────────────────────────────┤
│ 平台适配层(HAL: Hardware Abstraction Layer)│
├────────────────────────────┤
│ 底层系统接口(OS / 驱动 / SDK) │
└────────────────────────────┘
| 层级 | 功能 |
|---|---|
| 应用层 | 用户调用的接口,比如 cv::imread(), cv::VideoCapture(), cv::imshow() |
| 模块层 | 图像处理算法模块(滤波、边缘检测、人脸识别、深度学习等) |
| HAL 层(Hardware Abstraction Layer) | 抽象硬件加速接口,屏蔽不同平台的差异(CPU / CUDA / OpenCL) |
| 底层接口 | 不同操作系统提供的驱动接口(V4L2、DirectShow、AVFoundation 等) |
OpenCV 访问摄像头/视频设备时的底层驱动差异:
OpenCV 的 cv::VideoCapture 模块负责与操作系统的视频接口交互,在不同系统下,它自动选择对应的驱动或后端:
| 平台 | 使用的底层驱动或 API | 说明 |
|---|---|---|
| Linux | 🎥 V4L2 (Video4Linux2) | 直接调用 /dev/videoX 设备(USB 摄像头、CSI 摄像头等) |
| Windows | 🎬 DirectShow 或 Media Foundation | 用 Windows 自带的视频捕获框架访问 USB 摄像头 |
| macOS | 🍎 AVFoundation | Apple 的原生多媒体框架 |
| Android | 📱 NDK Camera API / OpenMAX / MediaNDK | 调用安卓底层相机接口 |
| iOS | 📷 AVFoundation (iOS SDK) | 同样通过苹果的多媒体接口 |
| GStreamer(可选) | 🧩 可跨平台的多媒体框架 | OpenCV 可编译时启用 GStreamer 支持,用于视频流/网络推流 |
| FFmpeg(可选) | 🧠 通用视频解码库 | 主要用于读取文件(MP4、AVI 等),也能采流 |
所以,当你在不同系统上调用:cv::VideoCapture cap(0);
OpenCV 会自动:
- 在 Linux 下走 /dev/video0 → V4L2;
- 在 Windows 下走 DirectShow;
- 在 macOS 下走 AVFoundation;
- 用户完全无需关心底层实现。