树莓派学习专题<9>:使用V4L2驱动获取摄像头数据--设定分辨率和帧率
树莓派学习专题<9>:使用V4L2驱动获取摄像头数据--设定分辨率和帧率
- 1. 设定分辨率
- 2. 设定帧率
- 3. 设定分辨率代码解析
- 4. 获取与设定帧率代码解析
- 5. 实测
1. 设定分辨率
使用如下代码设定摄像头的分辨率:
#define CAMERA_RESOLUTION_WIDTH ( 1280u)
#define CAMERA_RESOLUTION_HEIGHT ( 720u)
/*********************************************** other codes * ********************************************/
/*******************************************************************************
- Function : __SetCameraResolution
- Description : 本函数设置摄像头输出格式。
- Input : void
- Output : null
- Return : void
- Others :
*******************************************************************************/
void __SetCameraResolution(void)
{struct v4l2_format stFormat ;memset(&stFormat, 0, sizeof(stFormat)) ;/* 设置输出格式 */stFormat.type = V4L2_BUF_TYPE_VIDEO_CAPTURE ;stFormat.fmt.pix.width = CAMERA_RESOLUTION_WIDTH ;stFormat.fmt.pix.height = CAMERA_RESOLUTION_HEIGHT ;stFormat.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV ; /* 根据需要的格式填写 */if(-1 == ioctl(g_iFDVideo, VIDIOC_S_FMT, &stFormat)) {printf("ioctl VIDIOC_S_FMT error.\n") ;exit(-1) ;}/* 检查设置效果 */stFormat.type = V4L2_BUF_TYPE_VIDEO_CAPTURE ;if(-1 == ioctl(g_iFDVideo, VIDIOC_G_FMT, &stFormat)){printf("ioctl VIDIOC_G_FMT error.\n") ;exit(-1) ;}else{printf("--Set format result---------------------------------------\n") ;printf("-- Resolution width : %d\n""-- Resolution height : %d\n""-- Pixel format : %c%c%c%c\n",stFormat.fmt.pix.width, stFormat.fmt.pix.height,stFormat.fmt.pix.pixelformat & 0xFF,(stFormat.fmt.pix.pixelformat >> 8) & 0xFF,(stFormat.fmt.pix.pixelformat >> 16) & 0xFF,(stFormat.fmt.pix.pixelformat >> 24) & 0xFF);printf("----------------------------------------------------------\n\n\n") ;}return ;
}
2. 设定帧率
使用如下代码设定摄像头的输出帧率:
#define CAMERA_FPS ( 30u)
/*********************************************** other codes * ********************************************/
/*******************************************************************************
- Function : __SetCameraFPS
- Description : 本函数设置摄像头输出格式。
- Input : void
- Output : null
- Return : void
- Others :
*******************************************************************************/
void __SetCameraFPS(void)
{struct v4l2_streamparm stStreamParm ;stStreamParm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE ;/* 获取当前设置 */if(-1 == ioctl(g_iFDVideo, VIDIOC_G_PARM, &stStreamParm)){printf("ioctl VIDIOC_G_PARM error.\n") ;exit(-1) ;}else{printf("--Previous camera stream param----------------------------\n") ;printf("-- Capability : %08x\n""-- Capturemode : %08x\n""-- Numerator : %d\n""-- Denominator : %d\n",stStreamParm.parm.capture.capability, stStreamParm.parm.capture.capturemode, stStreamParm.parm.capture.timeperframe.numerator, stStreamParm.parm.capture.timeperframe.denominator) ;printf("----------------------------------------------------------\n\n\n") ;}/* 修改FPS */stStreamParm.parm.capture.timeperframe.numerator = 1 ;stStreamParm.parm.capture.timeperframe.denominator = CAMERA_FPS ;if(-1 == ioctl(g_iFDVideo, VIDIOC_S_PARM, &stStreamParm)){printf("ioctl VIDIOC_S_PARM error.\n") ;exit(-1) ;}/* 获取新设置 */if(-1 == ioctl(g_iFDVideo, VIDIOC_G_PARM, &stStreamParm)){printf("ioctl VIDIOC_G_PARM error.\n") ;exit(-1) ;}else{printf("--Current camera stream param-----------------------------\n") ;printf("-- Capability : %08x\n""-- Capturemode : %08x\n""-- Numerator : %d\n""-- Denominator : %d\n",stStreamParm.parm.capture.capability, stStreamParm.parm.capture.capturemode, stStreamParm.parm.capture.timeperframe.numerator, stStreamParm.parm.capture.timeperframe.denominator) ;printf("----------------------------------------------------------\n\n\n") ;} return ;
}
3. 设定分辨率代码解析
使用命令 VIDIOC_S_FMT 来设定输出格式。命令原型为:
#define VIDIOC_S_FMT _IOWR('V', 5, struct v4l2_format)
可见,需要一个 v4l2_format 结构体。结构体的定义如下:
/*** struct v4l2_format - stream data format* @type: enum v4l2_buf_type; type of the data stream* @pix: definition of an image format* @pix_mp: definition of a multiplanar image format* @win: definition of an overlaid image* @vbi: raw VBI capture or output parameters* @sliced: sliced VBI capture or output parameters* @raw_data: placeholder for future extensions and custom formats* @fmt: union of @pix, @pix_mp, @win, @vbi, @sliced, @sdr, @meta* and @raw_data*/
struct v4l2_format {__u32 type;union {struct v4l2_pix_format pix; /* V4L2_BUF_TYPE_VIDEO_CAPTURE */struct v4l2_pix_format_mplane pix_mp; /* V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE */struct v4l2_window win; /* V4L2_BUF_TYPE_VIDEO_OVERLAY */struct v4l2_vbi_format vbi; /* V4L2_BUF_TYPE_VBI_CAPTURE */struct v4l2_sliced_vbi_format sliced; /* V4L2_BUF_TYPE_SLICED_VBI_CAPTURE */struct v4l2_sdr_format sdr; /* V4L2_BUF_TYPE_SDR_CAPTURE */struct v4l2_meta_format meta; /* V4L2_BUF_TYPE_META_CAPTURE */__u8 raw_data[200]; /* user-defined */} fmt;
};
结构体中:
type 应指定为:V4L2_BUF_TYPE_VIDEO_CAPTURE;
联合体中,使用 v4l2_pix_format。该结构体的定义如下:
struct v4l2_pix_format {__u32 width;__u32 height;__u32 pixelformat;__u32 field; /* enum v4l2_field */__u32 bytesperline; /* for padding, zero if unused */__u32 sizeimage;__u32 colorspace; /* enum v4l2_colorspace */__u32 priv; /* private data, depends on pixelformat */__u32 flags; /* format flags (V4L2_PIX_FMT_FLAG_*) */union {/* enum v4l2_ycbcr_encoding */__u32 ycbcr_enc;/* enum v4l2_hsv_encoding */__u32 hsv_enc;};__u32 quantization; /* enum v4l2_quantization */__u32 xfer_func; /* enum v4l2_xfer_func */
};
这里主要填写 width、height、pixelformat三个成员。
width 指输出图像的宽度;
height指输出图像的高度;
pixelformat指输出图像的像素格式。根据需要填写 V4L2_PIX_FMT_XXXX。
设置执行后,未必能执行成功。需要读出再检查。读出设置,使用 VIDIOC_G_FMT 命令。该命令的原型为:
#define VIDIOC_G_FMT _IOWR('V', 4, struct v4l2_format)
返回值同样为 struct v4l2_format 类型。
4. 获取与设定帧率代码解析
使用命令 VIDIOC_G_PARM 来获取当前摄像头的参数(包含帧率)。该命令的原型是:
#define VIDIOC_G_PARM _IOWR('V', 21, struct v4l2_streamparm)
该命令需要一个 struct v4l2_streamparm 类型的参数。
struct v4l2_streamparm {__u32 type; /* enum v4l2_buf_type */union {struct v4l2_captureparm capture;struct v4l2_outputparm output;__u8 raw_data[200]; /* user-defined */} parm;
};
对于摄像头,type 仍然填写 V4L2_BUF_TYPE_VIDEO_CAPTURE。
联合体中,对于摄像头而言,应该取 struct v4l2_captureparm capture。该结构体的定义如下:
struct v4l2_captureparm {__u32 capability; /* Supported modes */__u32 capturemode; /* Current mode */struct v4l2_fract timeperframe; /* Time per frame in seconds */__u32 extendedmode; /* Driver-specific extensions */__u32 readbuffers; /* # of buffers for read */__u32 reserved[4];
};
和帧率有关的是其中的结构体成员 struct v4l2_fract timeperframe 。其定义如下:
struct v4l2_fract {__u32 numerator;__u32 denominator;
};
参数 timeperframe ,意指每一帧图像的输出时间。成员 numerator (分子)和 denominator (分母)参数决定了具体的值。例如:
numerator = 1
denominator = 30
则表明每一帧的时间为1/30秒,则相当于30fps输出。此外:
numerator = 100
denominator = 3000
效果是一样的。
需要注意的是,摄像头未必能按照设定的参数来输出。主要有2点:
- 设置未必生效。如果设置的FPS过大,设置下去后,重新读回配置,会发现参数 numerator 和 denominator 被修改为一个受限制的值。
- 即使能够设置下去,输出帧率和硬件还有关系,例如,对于同一个CMOS,MIPI-CSI的2-lane和4-lane所能支持的最大帧率是不同的。
使用命令 VIDIOC_S_PARM 来设置设定的参数。命令的定义:
#define VIDIOC_S_PARM _IOWR('V', 22, struct v4l2_streamparm)
同样还是需要结构体 struct v4l2_streamparm。
同设定输出格式一样,也需要检查是否设定成功。
5. 实测
材料:
- Raspberry Pi 4B计算机;
- IMX219摄像头组件。
运行上述代码,打印结果:
--Set format result---------------------------------------
-- Resolution width : 1280
-- Resolution height : 720
-- Pixel format : YUYV
------------------------------------------------------------Previous camera stream param----------------------------
-- Capability : 00001000
-- Capturemode : 00000000
-- Numerator : 1000
-- Denominator : 30000
------------------------------------------------------------Current camera stream param-----------------------------
-- Capability : 00001000
-- Capturemode : 00000000
-- Numerator : 1
-- Denominator : 30
----------------------------------------------------------
可以看到,在设置帧率之前, Numerator 参数和 Denominator参数,分别是1000和30000,即帧率是30fps。设置后仍然是30fps。
上述代码中,如果修改帧率设置:
numerator = 1
denominator = 100
则打印信息为:
--Current camera stream param-----------------------------
-- Capability : 00001000
-- Capturemode : 00000000
-- Numerator : 1
-- Denominator : 90
----------------------------------------------------------
可见,Denominator 参数被修改为了90,相当于最大输出90fps。此时尝试连续捕获,最终计算帧率为:
FPS : 90.25