既然涉及到视频输入,就会有很多与 ISP
相关的效果,比如对比度、饱和度、色温、白平衡等等,这些都是通用的、必须的控制项,并且大多数仅需要设置一个整数值即可。Linux
内核中V4L2
已经为我们提供了这个控制类型的API
,我们只需要明确自己需要添加什么控制类型,然后将其添加进内核中即可。该部分摄像头代码一半厂家会提供驱动或者相对应的参数,否则没有图像处理知识很难开发出来。
v4l2
设备可以对应多个v4l2_ctrl_handler
,一个v4l2_ctrl_handler
下可以对应多个控制单元,这些控制单元统一交给v4l2_ctrl_ref
链表统一管理,具体如下:控制类型 | 说明 |
---|---|
V4L2_CID_VBLANK | 垂直消隐。 每帧之后的空闲时间段,在此期间不生成图像数据。垂直消隐的单位是行。 每条线的长度为图像宽度加上行消隐,像素速率由同一子设备中的 V4L2_CID_PIXEL_RATE 控件定义。 |
V4L2_CID_HBLANK | 水平消隐。每一行图像数据后不产生图像数据的空闲时间。水平消隐的单位为像素 |
V4L2_CID_EXPOSURE | 确定相机传感器的曝光时间。曝光时间受帧间隔的限制。驱动程序应该将这些值解释为100 个µs 单位,其中值1 代表1/ 10000s , 10000 代表1 秒,100000 代表10 秒 |
V4L2_CID_ANALOGUE_GAIN | 模拟增益是影响像素矩阵中所有颜色分量的增益。在A/D 转换前,在模拟域进行增益运算 |
V4L2_CID_DIGITAL_GAIN | 数字增益是所有颜色分量相乘的值。通常应用的数字增益是控制值除以例如0x100 ,这意味着要获得无数字增益,控制值需要为0x100 。无增益配置通常也是默认配置 |
V4L2_CID_PIXEL_RATE | 子dev 的源pad 中的像素率。该控件是只读的,其单位是像素/秒 |
V4L2_CID_LINK_FREQ | 数据总线频率。 与媒体总线像素代码、总线类型(每个样本的时钟周期)一起,数据总线频率定义了像素阵列中的像素速率(V4L2_CID_PIXEL_RATE )(如果设备不是图像传感器,则可能在其他地方)。 可以从像素时钟、图像宽度和高度以及水平和垂直消隐计算帧速率。 虽然像素速率控制可以在包含像素阵列的 subdev 之外的其他地方定义,但无法从该信息中获得帧速率。 这是因为只有在像素阵列上才能假定垂直和水平消隐信息是准确的:在像素阵列中不允许其他消隐。 通过选择所需的水平和垂直消隐来执行帧速率的选择。 此控制的单位是 Hz 。 |
ov13850
中注册v4l2_ctrl
步骤如下:drivers\media\i2c\ov13850.c
static int ov13850_initialize_controls(struct ov13850 *ov13850)
{
const struct ov13850_mode *mode;
struct v4l2_ctrl_handler *handler;
struct v4l2_ctrl *ctrl;
s64 exposure_max, vblank_def;
u32 h_blank;
int ret;
handler = &ov13850->ctrl_handler;
mode = ov13850->cur_mode;
ret = v4l2_ctrl_handler_init(handler, 8);
if (ret)
return ret;
handler->lock = &ov13850->mutex;
/* 注册添加一个int控制菜单在handler下,用于控制总线的频率 */
ctrl = v4l2_ctrl_new_int_menu(handler, NULL, V4L2_CID_LINK_FREQ,
0, 0, link_freq_menu_items);
if (ctrl)
ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY;
/* 注册添加一个标准控制在handler下,设置子设备的像素,该控制是只读的,用于读取pad下的子设备像素 */
v4l2_ctrl_new_std(handler, NULL, V4L2_CID_PIXEL_RATE,
0, OV13850_PIXEL_RATE, 1, OV13850_PIXEL_RATE);
h_blank = mode->hts_def - mode->width;
/* 添加水平消隐的时间间隔控制 */
ov13850->hblank = v4l2_ctrl_new_std(handler, NULL, V4L2_CID_HBLANK,
h_blank, h_blank, 1, h_blank);
if (ov13850->hblank)
ov13850->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
vblank_def = mode->vts_def - mode->height;
/* 配置垂直消隐控制 */
ov13850->vblank = v4l2_ctrl_new_std(handler, &ov13850_ctrl_ops,
V4L2_CID_VBLANK, vblank_def,
OV13850_VTS_MAX - mode->height,
1, vblank_def);
exposure_max = mode->vts_def - 4;
/* 控制相机的曝光时间 */
ov13850->exposure = v4l2_ctrl_new_std(handler, &ov13850_ctrl_ops,
V4L2_CID_EXPOSURE, OV13850_EXPOSURE_MIN,
exposure_max, OV13850_EXPOSURE_STEP,
mode->exp_def);
/* 模拟增益控制 */
ov13850->anal_gain = v4l2_ctrl_new_std(handler, &ov13850_ctrl_ops,
V4L2_CID_ANALOGUE_GAIN, OV13850_GAIN_MIN,
OV13850_GAIN_MAX, OV13850_GAIN_STEP,
OV13850_GAIN_DEFAULT);
/* ISP测试ctrl */
ov13850->test_pattern = v4l2_ctrl_new_std_menu_items(handler,
&ov13850_ctrl_ops, V4L2_CID_TEST_PATTERN,
ARRAY_SIZE(ov13850_test_pattern_menu) - 1,
0, 0, ov13850_test_pattern_menu);
if (handler->error) {
ret = handler->error;
dev_err(&ov13850->client->dev,
"Failed to init controls(%d)\n", ret);
goto err_free_handler;
}
ov13850->subdev.ctrl_handler = handler;
return 0;
err_free_handler:
v4l2_ctrl_handler_free(handler);
return ret;
}
返回总目录