3. V4L2 裁剪(Crop)概念:源裁剪与目标裁剪
在 V4L2 视频处理 pipeline 中,裁剪(Crop)是图像数据进入硬件或软件处理引擎前的关键预处理步骤。V4L2 将裁剪抽象为两个独立但关联的概念:源裁剪(Source Crop)与目标裁剪(Destination Crop / Compose)。理解这两者的区别,是正确配置 ISP、M2M 设备以及实现高效缩放的基础。
3.1 源裁剪 (Source Crop) —— 从原始帧中“切出”感兴趣区域
源裁剪定义从输入缓冲区(即传感器原始图像或解码后的帧)中选取一个矩形区域,该区域称为“感兴趣区域”(ROI)。后续所有处理(缩放、格式转换、编码)都仅针对这个子区域进行。
- 作用对象:输入图像(Source Buffer)。
- 坐标系统:相对于原始图像左上角 (0,0)。
- 典型用途:去除黑边、聚焦画面中心、电子防抖裁剪、多路拼接前的单路裁剪。
- V4L2 控制标识:
V4L2_SEL_TGT_CROP。
关键理解:源裁剪不改变图像的分辨率,它只是定义了一个“窗口”。裁剪后的数据尺寸等于
src_rect.width × src_rect.height,但该数据会作为后续缩放或格式转换的输入。
3.2 目标裁剪 (Destination Crop / Compose) —— 定义输出图像中的放置位置
目标裁剪(在 V4L2 中常被称为 Compose)定义经过缩放或格式转换后的图像,在输出缓冲区中的放置位置和大小。它决定了最终图像在目标帧中的布局。
- 作用对象:输出图像(Destination Buffer)。
- 坐标系统:相对于输出缓冲区左上角 (0,0)。
- 典型用途:画中画(PIP)、多路合成、将裁剪缩放后的图像贴到输出帧的特定位置。
- V4L2 控制标识:
V4L2_SEL_TGT_COMPOSE。
记忆技巧:“CROP”是“从源里切”,“COMPOSE”是“往目标里贴”。CROP 决定看什么,COMPOSE 决定放哪里、放多大。
3.3 裁剪与缩放的关系:从 CROP 到 COMPOSE 的映射
当设备同时支持裁剪和缩放时,V4L2_SEL_TGT_CROP 和 V4L2_SEL_TGT_COMPOSE 共同定义了一个完整的几何变换 pipeline:
- 输入:原始图像(例如 1920x1080)。
- 源裁剪:从原始图像中切出
src_rect(例如 1280x720,从 (320, 180) 开始)。 - 缩放:硬件或软件将裁剪出的 1280x720 图像缩放至
dst_rect指定的尺寸(例如 640x360)。 - 目标放置:将缩放后的 640x360 图像放置在输出缓冲区的
compose_rect位置(例如从 (100, 50) 开始)。
如果设备不支持缩放,则 src_rect 的尺寸必须等于 compose_rect 的尺寸,否则驱动会返回错误。
常见误区:不要混淆
V4L2_SEL_TGT_CROP 与 V4L2_SEL_TGT_COMPOSE 的尺寸关系。CROP 尺寸是缩放前的输入尺寸,COMPOSE 尺寸是缩放后的输出尺寸。如果不需要缩放,两者尺寸应一致。
3.4 V4L2_SEL_TGT_CROP 与 V4L2_SEL_TGT_COMPOSE 对比表
| 属性 | V4L2_SEL_TGT_CROP (源裁剪) | V4L2_SEL_TGT_COMPOSE (目标裁剪) |
|---|---|---|
| 语义 | 从输入图像中选取子区域 | 在输出图像中定义放置区域 |
| 坐标基准 | 输入缓冲区坐标系 | 输出缓冲区坐标系 |
| 影响阶段 | 预处理(缩放/转换前) | 后处理(缩放/转换后) |
| 尺寸含义 | 缩放前的像素尺寸 | 缩放后的像素尺寸 |
| 典型应用 | 去黑边、电子防抖、ROI 提取 | 画中画、多路合成、帧布局 |
| V4L2 标识 | V4L2_SEL_TGT_CROP |
V4L2_SEL_TGT_COMPOSE |
| 是否可缩放 | 否(仅定义窗口) | 是(定义最终尺寸) |
3.5 裁剪与缩放 Pipeline 流程图
下图展示了从源裁剪到目标裁剪的完整数据流,以及缩放器(Scaler)在其中的位置:
3.6 实际应用场景:画中画 (PIP) 配置示例
假设我们需要实现一个画中画功能:主画面为 1920x1080,子画面(PIP)为 320x180,放置在右下角。使用 V4L2 M2M 设备的配置流程如下:
struct v4l2_selection sel;
// 1. 配置源裁剪:从原始帧中切出子画面区域
sel.type = V4L2_BUF_TYPE_VIDEO_OUTPUT; // 输入队列
sel.target = V4L2_SEL_TGT_CROP;
sel.r.left = 0; // 从原始帧左上角开始
sel.r.top = 0;
sel.r.width = 1280; // 裁剪宽度
sel.r.height = 720; // 裁剪高度
ioctl(fd, VIDIOC_S_SELECTION, &sel);
// 2. 配置目标裁剪:将缩放后的子画面放到输出帧右下角
sel.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; // 输出队列
sel.target = V4L2_SEL_TGT_COMPOSE;
sel.r.left = 1920 - 320 - 20; // 右边距 20 像素
sel.r.top = 1080 - 180 - 20; // 底边距 20 像素
sel.r.width = 320; // 缩放后宽度
sel.r.height = 180; // 缩放后高度
ioctl(fd, VIDIOC_S_SELECTION, &sel);
性能提示:在配置裁剪时,尽量让
src_rect 的宽度和高度对齐到 16 或 32 字节边界。许多硬件 ISP 和 DMA 引擎对非对齐裁剪会进行额外的填充或拷贝,导致性能下降 10%~30%。
3.7 常见问题与避坑指南
- 裁剪坐标越界:
src_rect.left + src_rect.width不能超过输入图像的宽度,否则驱动会返回-ERANGE。建议先通过VIDIOC_G_SELECTION查询设备支持的裁剪边界。 - 缩放比例限制:并非所有设备都支持任意缩放比例。通常硬件缩放器有最小/最大缩放因子(如 1/16x ~ 16x),超出范围会静默截断或返回错误。
- 格式对齐要求:YUV 格式下,裁剪的起始坐标和宽高必须满足色度子采样对齐(如 NV12 要求宽高为偶数,起始坐标为偶数)。
- COMPOSE 与 CROP 的互斥性:某些简单设备不支持同时设置 CROP 和 COMPOSE,此时只能设置其中一个,另一个会被驱动自动计算。
调试建议:当裁剪结果不符合预期时,使用
v4l2-ctl --get-crop 和 v4l2-ctl --get-compose 查看当前设备的实际配置。同时检查驱动日志(dmesg)中是否有“crop rect clipped to bounds”之类的警告。