0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

Linux下摄像头应用编程

嵌入式技术 来源: 嵌入式技术 作者: 嵌入式技术 2022-08-26 21:39 次阅读

Linux下摄像头应用编程

V4L2是Video for linux2的简称,为linux中关于视频设备的内核驱动。在Linux中,视频设备是设备文件,可以像访问普通文件一样对其进行读写,摄像头在/dev/video*下,如果只有一个视频设备,通常为/dev/video0。

v4L2是针对uvc免驱usb设备的编程框架 ,主要用于采集usb摄像头等。

1.摄像头框架编程步骤

(1)打开摄像头设备(/dev/video0 、/dev/video1 )。
(2)设置图像格式:VIDIOC_S_FMT(视频捕获格式、图像颜色数据格式、图像宽和高)。
(3)申请缓冲区:VIDIOC_REQBUFS(缓冲区数量、缓冲映射方式、视频捕获格式)。
(4)将缓冲区映射到进程空间:VIDIOC_QUERYBUF(要映射的缓冲区下标、缓冲映射方式、视频捕获格式)。
(5)将缓冲区添加到队列中:VIDIOC_QBUF(映射的缓冲区下标、缓冲映射方式、视频捕获格式)。
(6)开启摄像头采集:VIDIOC_STREAMON (视频捕获格式)。
(7)从采集队列中取出图像数据VIDIOC_DQBUF,进行图像渲染。

2.V4L2头文件信息

V4L2是Linux下标准视频驱动框架,相关头文件信息在include/linux/videodev2.h中。

V4L2 驱动对用户空间提供字符设备,主设备号为 81,对于视频设备,其次设备号为 0-63。除此之外,次设备号为 64-127 的 Radio 收音机设备,次设备号为 192-223 的是 Teletext 广播设备,次设备号为 224-255 的是 VBI视频消影设备。

2.1 常用ioctl参数

设置视频捕获格式
#define VIDIOC_S_FMT _IOWR(‘V’, 5, struct v4l2_format)
向内核申请缓冲区
#define VIDIOC_REQBUFS _IOWR(‘V’, 8, struct v4l2_requestbuffers)
将缓冲区映射到进程空间
#define VIDIOC_QUERYBUF _IOWR(‘V’, 9, struct v4l2_buffer)
将缓冲区添加到采集队列
#define VIDIOC_QBUF _IOWR(‘V’, 15, struct v4l2_buffer)
从队列中获取图像数据
#define VIDIOC_DQBUF _IOWR(‘V’, 17, struct v4l2_buffer)
开启摄像头图像采集
#define VIDIOC_STREAMON _IOW(‘V’, 18, int)

2.2 核心结构体信息

  • struct v4l2_format
struct v4l2_format {
__u32 type;/* 类型V4L2_BUF_TYPE_VIDEO_CAPTURE*/
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 /
__u8 raw_data[200]; / user-defined */
} fmt;
};
  • struct 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 */
};
  • struct v4l2_requestbuffers
//内存映射缓冲区
struct v4l2_requestbuffers {
__u32 count; //申请缓冲区个数
__u32 type; /* enum v4l2_buf_type 视频类型 /
__u32 memory; / enum v4l2_memory 映射方式*/
__u32 reserved[2];
};
  • struct v4l2_buffer
//视频缓冲区信息
struct v4l2_buffer {
__u32 index;/数组下标/
__u32 type;/视频捕获格式/
__u32 bytesused;
__u32 flags;
__u32 field;
struct timeval timestamp;
struct v4l2_timecode timecode;
__u32 sequence;
/* memory location */
__u32 memory;/映射格式/
union {
__u32 offset;/偏移量/
unsigned long userptr;
struct v4l2_plane *planes;
int fd;
} m;
__u32 length;/映射缓冲区大小/
__u32 input;
__u32 reserved;
};

2.3 图像颜色编码格式

关于YUV格式图像参考:https://blog.csdn.net/sway913/article/details/120602052

  • YUV

YUV,是一种颜色编码方法。常使用在各个视频处理组件中。 YUV在对照片或视频编码时,考虑到人类的感知能力,允许降低色度的带宽。

YUV是编译true-color颜色空间(color space)的种类,Y’UV, YUV, YCbCr,YPbPr等专有名词都可以称为YUV,彼此有重叠。“Y”表示明亮度(Luminance或Luma),也就是灰阶值,“U”和“V”表示的则是色度(Chrominance或Chroma),作用是描述影像色彩及饱和度,用于指定像素的颜色。

YUV格式有两大类:planar(平面)和packed(交错)。

对于planar的YUV格式,先连续存储所有像素点的Y,紧接着存储所有像素点的U,随后是所有像素点的V。

对于packed的YUV格式,每个像素点的Y,U,V是连续交错存储的。

YUV的主要优势在于可以兼容之前的黑白电视,单独只有Y数据就可以显示完整的黑白图像,UV是后期加入的色彩参数。

  • YUYV格式:YUV422

YUV 4:2:2采样,表示在每4个像素中,Y采集4份,U采集2份,V采集2份。每两个 Y 分量共享一组 UV 分量。单个像素占用空间为:1byte(Y)+1/2byte(U)+1/2byte(V)=2字节;一帧图像占用的空间为:width * height * 2

pYYBAGMIzRuAX7EtAAJmDR7Ghjc003.png#pic_center

YUV420

YUV420 每四个Y分量公用一个UV分量,并不是没有V分量,而是UV分量交替采样,所以每个像素点占用1.5个字节空间。根据planar(平面)和packed(交错)方式存储有4种存储方式。下面举其中一种示例说明:

每4个Y共用一组UV分量,单个像素占用空间为:1byte(Y)+1/4byte(U)+1/4byte(V)=1.5字节;一帧图像占用的空间为:width * height * 3/2 byte。

poYBAGMIzRuAYQHJAAKznya94As712.png#pic_center
  • RGB

RGB色彩模式是工业界的一种颜色标准,是通过对红®、绿(G)、蓝(B)三个颜色通道的变化以及它们相互之间的叠加来得到各式各样的颜色的,RGB即是代表红、绿、蓝三个通道的颜色,这个标准几乎包括了人类视力所能感知的所有颜色,是运用最广的颜色系统之一。

  • 常用的RGB格式:

RGB565:5位红色+6位绿色+5位蓝色=16位颜色值
RGB888:8位红色+8位绿色+8位蓝色=24真彩色
RGB32:RGB32是在RGB基础上附加上Alpha(透明度)通道,RGB个占8位,剩余8位用作Alpha通道。

pYYBAGMIzRuAMRETAABxx0FXrDQ759.png#pic_center

3.摄像头编程示例

3.1 摄像头初始化

  打开摄像头设备,设置视频捕获格式,设置图像格式为YUYV422。

/*摄像头初始化*/
int Camera_Init(void)
{
	int i=0;
	/*1.打开摄像头设备*/
	int fd=open(VIDEO_DEV,2);
	if(fd<0)return -1;//摄像头打开失败
	/*2.设置摄像头捕获格式*/
	struct v4l2_format v4l2fmt;
	memset(&v4l2fmt,0,sizeof(v4l2fmt));//初始化结构体
	v4l2fmt.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;//视频捕获格式
	v4l2fmt.fmt.pix.width=1920;//图像宽度
	v4l2fmt.fmt.pix.height=1080;//图像高度
	v4l2fmt.fmt.pix.pixelformat=V4L2_PIX_FMT_YUYV;//YUYV颜色编码格式
	if(ioctl(fd,VIDIOC_S_FMT,&v4l2fmt))return -2;//设置格式失败
	printf("采集图像大小:%d*%d\n",v4l2fmt.fmt.pix.width,v4l2fmt.fmt.pix.height);
	imag_w=v4l2fmt.fmt.pix.width;
	imag_h=v4l2fmt.fmt.pix.height;
	/*3.申请缓冲区*/
	struct v4l2_requestbuffers v4l2reqbuf;
    memset(&v4l2reqbuf,0,sizeof(v4l2reqbuf));//初始化结构体
	v4l2reqbuf.count=4;//申请的缓冲区个数
	v4l2reqbuf.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;//视频捕获格式
	v4l2reqbuf.memory=V4L2_MEMORY_MMAP;//内存映射
	if(ioctl(fd,VIDIOC_REQBUFS,&v4l2reqbuf))return -3;//申请缓冲区失败
	printf("申请的缓冲区个数:%d\n",v4l2reqbuf.count);
	/*4.将缓冲区映射到进程空间*/
	struct v4l2_buffer v4l2buf;
	memset(&v4l2buf,0,sizeof(v4l2buf));//初始化结构体
	v4l2buf.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;//视频捕获格式
	v4l2buf.memory=V4L2_MEMORY_MMAP;//内存映射
	for(i=0;i

3.2 采集图像数据

  循环采集图像数据,将一帧图像数据保存为BMP图片。   由于摄像头图像颜色格式为YUYV格式,BMP图片颜色格式为RGB,因此需要将YUYV转RGB再写入到文件中。

YUYV转换RGB函数
/*YUYV转RGB888*/
void yuv_to_rgb(unsigned char *yuv_buffer,unsigned char *rgb_buffer,int iWidth,int iHeight)
{
	int x;
	int z=0;
	unsigned char *ptr = rgb_buffer;
	unsigned char *yuyv= yuv_buffer;
	for (x = 0; x < iWidth*iHeight; x++)
	{
		int r, g, b;
		int y, u, v;
		if (!z)
		y = yuyv[0] << 8;
		else
		y = yuyv[2] << 8;
		u = yuyv[1] - 128;
		v = yuyv[3] - 128;
		r = (y + (359 * v)) >> 8;
		g = (y - (88 * u) - (183 * v)) >> 8;
		b = (y + (454 * u)) >> 8;
		*(ptr++) = (b > 255) ? 255 : ((b < 0) ? 0 : b);
		*(ptr++) = (g > 255) ? 255 : ((g < 0) ? 0 : g);
		*(ptr++) = (r > 255) ? 255 : ((r < 0) ? 0 : r);
		if(z++)
		{
			z = 0;
			yuyv += 4;
		}
	}
}
图像采集和BMP图片编码

int main()
{
	int fd=Camera_Init();
	if(fd<0)
	{
		printf("初始化摄像头失败,res=%d\n",fd);
		return 0;
	}
	printf("初始化摄像头成功\n");
	struct v4l2_buffer v4l2buf;
	memset(&v4l2buf,0,sizeof(v4l2buf));//初始化结构体
	v4l2buf.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;//视频捕获格式
	v4l2buf.memory=V4L2_MEMORY_MMAP;//内存映射
	BMP_HEADER bmp_head;//bmp头数据
	BMP_INFO bmp_info;//位图数据
	memset(&bmp_head,0,sizeof(BMP_HEADER));
	memset(&bmp_info,0,sizeof(BMP_INFO));
	bmp_head.bfType='M'<<8|'B';//图片类型
	bmp_head.bfSize=imag_w*imag_h*3+sizeof(BMP_HEADER)+sizeof(BMP_INFO);
	bmp_head.bfOffBits=sizeof(BMP_INFO)+sizeof(BMP_HEADER);/*RGB颜色偏移量*/

	bmp_info.biSize=sizeof(BMP_INFO);//当前结构体大小
	bmp_info.biWidth=imag_w;//图片宽
	bmp_info.biHeight=imag_h;//图片高
	bmp_info.biPlanes=1;//固定为1
	bmp_info.biBitCount=24;//24位真彩色
	
	char *rgb=malloc(imag_w*imag_h*3);//存放rgb图像数据
	int count=1;
	char buff[20];
	FILE *fp;
	while(1)
	{
		/*从采集队列中取出图像数据*/
		if(ioctl(fd,VIDIOC_DQBUF,&v4l2buf))break;//取数据失败
		printf("v4l2buff[%d]=%p\n",v4l2buf.index,video_buff[v4l2buf.index]);
		/*将头数据和位图数据写入到文件中*/
		snprintf(buff,sizeof(buff),"./image/%d.bmp",count);//图片名字
		fp=fopen(buff,"w+b");
		if(fp==NULL)
		{
			printf("文件创建失败\n");
			continue;
		}
		count++;
		fwrite(&bmp_head,sizeof(BMP_HEADER),1,fp);//写头数据
		fwrite(&bmp_info,sizeof(BMP_INFO),1,fp);//写头数据
		/*将yuyv数据转换RGB888*/
		yuv_to_rgb(video_buff[v4l2buf.index],rgb,imag_w,imag_h);
		/*将颜色数据写入到文件中*/
		fwrite(rgb,imag_w*imag_h*3,1,fp);//写头数据
		fclose(fp);
		/*将缓冲区添加回采集队列中*/
		if(ioctl(fd,VIDIOC_QBUF,&v4l2buf))break;//添加到采集队列失败
	}
	close(fd);//关闭摄像头
	
}
图像采集和BMP图片编码

int main()
{
	int fd=Camera_Init();
	if(fd<0)
	{
		printf("初始化摄像头失败,res=%d\n",fd);
		return 0;
	}
	printf("初始化摄像头成功\n");
	struct v4l2_buffer v4l2buf;
	memset(&v4l2buf,0,sizeof(v4l2buf));//初始化结构体
	v4l2buf.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;//视频捕获格式
	v4l2buf.memory=V4L2_MEMORY_MMAP;//内存映射
	BMP_HEADER bmp_head;//bmp头数据
	BMP_INFO bmp_info;//位图数据
	memset(&bmp_head,0,sizeof(BMP_HEADER));
	memset(&bmp_info,0,sizeof(BMP_INFO));
	bmp_head.bfType='M'<<8|'B';//图片类型
	bmp_head.bfSize=imag_w*imag_h*3+sizeof(BMP_HEADER)+sizeof(BMP_INFO);
	bmp_head.bfOffBits=sizeof(BMP_INFO)+sizeof(BMP_HEADER);/*RGB颜色偏移量*/

	bmp_info.biSize=sizeof(BMP_INFO);//当前结构体大小
	bmp_info.biWidth=imag_w;//图片宽
	bmp_info.biHeight=imag_h;//图片高
	bmp_info.biPlanes=1;//固定为1
	bmp_info.biBitCount=24;//24位真彩色
	
	char *rgb=malloc(imag_w*imag_h*3);//存放rgb图像数据
	int count=1;
	char buff[20];
	FILE *fp;
	while(1)
	{
		/*从采集队列中取出图像数据*/
		if(ioctl(fd,VIDIOC_DQBUF,&v4l2buf))break;//取数据失败
		printf("v4l2buff[%d]=%p\n",v4l2buf.index,video_buff[v4l2buf.index]);
		/*将头数据和位图数据写入到文件中*/
		snprintf(buff,sizeof(buff),"./image/%d.bmp",count);//图片名字
		fp=fopen(buff,"w+b");
		if(fp==NULL)
		{
			printf("文件创建失败\n");
			continue;
		}
		count++;
		fwrite(&bmp_head,sizeof(BMP_HEADER),1,fp);//写头数据
		fwrite(&bmp_info,sizeof(BMP_INFO),1,fp);//写头数据
		/*将yuyv数据转换RGB888*/
		yuv_to_rgb(video_buff[v4l2buf.index],rgb,imag_w,imag_h);
		/*将颜色数据写入到文件中*/
		fwrite(rgb,imag_w*imag_h*3,1,fp);//写头数据
		fclose(fp);
		/*将缓冲区添加回采集队列中*/
		if(ioctl(fd,VIDIOC_QBUF,&v4l2buf))break;//添加到采集队列失败
	}
	close(fd);//关闭摄像头
	
}


审核编辑 黄昊宇


;i++)>
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • Linux
    +关注

    关注

    87

    文章

    10990

    浏览量

    206738
  • 摄像头
    +关注

    关注

    59

    文章

    4610

    浏览量

    92907
  • 编程
    +关注

    关注

    88

    文章

    3441

    浏览量

    92406
  • V4L2
    +关注

    关注

    0

    文章

    17

    浏览量

    3828
收藏 人收藏

    评论

    相关推荐

    摄像头无线监控及远程控制

    想问的是,在室内的环镜,采用哪种实现无线的摄像头的的监控和控制更为现实和有价值;因为我需要多个摄像头的不同房间的监控,考虑到上位机的多线程的问题,上位机的设计是不是更应该采用基于limux系统的QT
    发表于 08-06 16:20

    项目外包:编写linux摄像头驱动开发

    项目名称:双摄像头驱动开发报价:10000元(可商谈)要求完成时间:2016-07-18需求描述:1.编写linux驱动程序,支持两个mipi接口的摄像头
    发表于 06-15 15:56

    电赛 摄像头

    想请问这种摄像头数据传输是怎么传的,老板说是通过USB接线传给WIFI,然后我继续问下去老板就没回我了。我想问一各位大神,这种是将摄像头采集到的数据通过USB接线直接传给WIFI吗?传输的过程不需要
    发表于 07-21 12:23

    ESM6802支持Qt摄像头应用

    是Logitech C310 USB摄像头,ESM6802的Linux系统能够自动识别,正常使用,更多摄像头将在后续进行测试。camera程序运行效果见下图:  在程序中需要首先检查摄像头
    发表于 10-20 10:33

    u***摄像头如何改成水下无线摄像头

    请教大神,我有一个u***摄像头(也可买直接视频输出的那种摄像头),有线连接也想过,想把它改成可以实时监控水下鱼情的无线摄像头(有线的挑手机型号,很多手机不能用。)摄像头通过u***连
    发表于 01-31 23:26

    【HarmonyOS HiSpark AI Camera】鸿蒙摄像头开发

    项目名称:鸿蒙摄像头开发试用计划:申请理由:从事linux内核方向性能和稳定性工作,想体验一智能摄像头设备上不同os之间的开发和使用差距第一步 根据文档 进行学习第二步 编写不同平台
    发表于 09-25 10:11

    摄像头如何使用?

    本章将介绍计算机视觉中最核心传感器-摄像头的基本使用,主要讲解了CSI摄像头,USB摄像头,网络摄像头的基本使用。
    发表于 11-06 06:47

    回收苹果摄像头 收购苹果摄像头

    回收苹果摄像头 收购苹果摄像头回收苹果摄像头,大量收购苹果摄像头!!! 帝欧电子 赵先生 TEL:135-3012-2202 QQ:879821252 帝欧电子专业电子收购,现急购
    发表于 12-29 18:14

    回收摄像头ic 收购摄像头ic

    回收摄像头ic 收购摄像头ic 摄像头ic实力回收 ||优势高价回收摄像头ic @@@ 赵先生 135-3012-2202同步微信 QQ:8798-21252)帝欧电子 实力回收 工厂
    发表于 01-08 17:26

    回收手机摄像头 收购手机摄像头

    `回收手机摄像头,大量收购手机摄像头!!! 帝欧电子 赵先生 TEL:135-3012-2202 QQ:879821252 帝欧电子专业电子收购,现急购摄像头,大量回收摄像头!手机
    发表于 07-05 11:01

    回收手机摄像头,收购摄像头芯片

    `帝欧电子赵生135-3012-2202,QQ:8798-21252长期高价回收手机摄像头,回收摄像头芯片。 摄像头广泛运用于我们的生活之中,大街上随处可见的安防监控,人手至少一台的手机平板,汽车
    发表于 07-14 17:53

    基于AT32的开发板和一个OV2640摄像头模块的USB摄像头设计

    手里有一块AT32的开发板和一个OV2640摄像头模块,因为做智能车模型需要一个摄像头,就想能不能废物利用一,用这俩做一个即插即用的USB摄像头,能够直接用在树莓派的
    发表于 08-06 06:22

    Linux操作系统摄像头设备是如何实现驱动并移植的

    DCMI是什么?有何作用?Linux操作系统摄像头设备是如何实现驱动并移植的?
    发表于 02-28 09:40

    如何对基于Linux操作系统摄像头设备进行驱动并移植呢

    如何对基于Linux操作系统摄像头设备进行驱动并移植呢?有哪些操作步骤?
    发表于 02-28 09:19

    Linux开发_摄像头编程(实现拍照功能)

    这篇文章主要介绍Linux下UVC免驱摄像头操作流程,介绍V4L2框架、完成摄像头拍照保存为BMP图像到本地,最后贴出了利用CJSON库解析天气预报、北京时间接口返回的数据例子代码(上篇文章的案例补充)。
    的头像 发表于 09-17 15:34 1358次阅读