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

    文章

    11292

    浏览量

    209328
  • 摄像头
    +关注

    关注

    59

    文章

    4836

    浏览量

    95599
  • 编程
    +关注

    关注

    88

    文章

    3614

    浏览量

    93686
  • V4L2
    +关注

    关注

    0

    文章

    17

    浏览量

    3894
收藏 人收藏

    评论

    相关推荐

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

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

    ESM6802支持Qt摄像头应用

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

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

    回收苹果摄像头 收购苹果摄像头回收苹果摄像头,大量收购苹果摄像头!!! 帝欧电子 赵先生 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

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

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

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

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

    Linux基于ARM920T的USB摄像头图像采集

    本文介绍了基于ARM920T的嵌入式Linux利用USB摄像头采集图像的硬件、软件设计过程,最终实现了在目标板上图像的采集和显示。
    发表于 02-03 14:55 70次下载
    <b class='flag-5'>Linux</b><b class='flag-5'>下</b>基于ARM920T的USB<b class='flag-5'>摄像头</b>图像采集

    基于Linux基于ARM920T的USB摄像头图像采集

    基于Linux基于ARM920T的USB摄像头图像采集
    发表于 10-30 16:36 13次下载
    基于<b class='flag-5'>Linux</b><b class='flag-5'>下</b>基于ARM920T的USB<b class='flag-5'>摄像头</b>图像采集

    Linux系统中如何安装摄像头驱动

    1、摄像头(Webcam)驱动说明; 摄像头在Windows的驱动极为容易,最多是点几下鼠标,没有什么太大的难度。但在Linux中,驱动起来是有点困难,这并不是说Linux多高雅。只能
    发表于 11-07 11:45 5次下载

    盘点屏摄像头技术

    指纹技术及听筒隐藏设计的出现,让全面屏技术日趋成熟,水滴屏、美人尖应运而生,而隐藏摄像头成为最后难题。因此我们也的逐渐将目光转向屏摄像头技术。
    的头像 发表于 11-13 08:45 7411次阅读

    摄像头的难点_屏摄像头的专利是谁的

    如果不考虑成像质量,只谈论搭载屏摄像头,那么已经普及的具有在光电屏下方指纹技术的手机已经拥有了它。光电屏指纹模块的工作原理是在屏下方放置一个微型传感器和一个摄像头,每次手指触摸区域
    的头像 发表于 08-12 14:37 9500次阅读

    三星新推出屏摄像头实现“透明摄像头”技术

    1月14日,@三星显示 官宣全新的OLED屏幕,不仅更轻更薄,而且还配合屏摄像头技术实现了“透明摄像头”,将摄像头隐藏在屏幕下方,同时边框继续收窄,在笔记本上实现了更为极致的全面屏视
    的头像 发表于 01-14 14:59 3936次阅读

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

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