摄像头的使用

Linux中开发摄像头,用到V4L2架构,提供了开发摄像头需要用到的参数变量

摄像头常见的采集格式是YUV格式或JPG格式,本文使用的摄像头格式是YUV格式


用V4L2架构中提供的宏定义和结构体实现摄像头循环显示视频流

用V4L2架构中提供的宏定义和结构体实现摄像头循环显示视频流
使用ioctl()函数给摄像头的驱动发送"指令

#include <stropts.h>  //变参函数
int ioctl(int fildes, int request, ... /* arg */);  /*所有的硬件设备都可以用这个函数实现应用程 
                                                       序和底层驱动的通信
                                                       底层驱动发送数据给应用程序
                                                    应用程序发送数据给底层驱动都可以用该函数*/
   返回值:成功 0  失败 -1
   参数:fildes --》摄像头的文件描述符
         request --》你要发送的指令,指令在V4L2的头文件中有清楚地定义
     ioctl(camerafd,helloworld)

详细步骤

1.打开摄像头驱动
//摄像头的驱动是谁?--》打开摄像头的驱动
//查看开发板中摄像头驱动是哪个可以通过插拔摄像头看/dev中多了哪个驱动文件判断
	camerafd=open("/dev/video7",O_RDWR);  
	if(camerafd==-1)
	{
		perror("打开摄像头的驱动失败!\n");
		return -1;
	}
2.设置摄像头采集格式
 ioctl(camerafd,VIDIOC_S_FMT,你要设置的具体格式)
                              指令:VIDIOC_S_FMT
                              用到的变量类型:
 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 */		                
	       } fmt;
         };
   struct v4l2_pix_format {
	     __u32  width;          //画面宽
	     __u32	height;         //画面高
	     __u32	pixelformat;    //画面的采集格式 

例子:
//设置好摄像头的采集格式--》画面宽,画面高,画面是什么格式。。。。
	struct v4l2_format myfmt;
	bzero(&myfmt,sizeof(myfmt));
	myfmt.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
	myfmt.fmt.pix.width=W;  
	myfmt.fmt.pix.height=H;
	myfmt.fmt.pix.pixelformat=V4L2_PIX_FMT_YUYV; //yuv格式
	ret=ioctl(camerafd,VIDIOC_S_FMT,&myfmt);
	if(ret==-1)
	{
		perror("设置采集格式失败了!\n");
		return -1;
	}
3.申请缓冲块
指令:VIDIOC_REQBUFS
      用到的变量类型:
struct v4l2_requestbuffers {
	    count;  //你打算申请多少个缓冲块,每个缓冲块存放一帧画面数据,一般申请4-8个左右即可
        type;	//V4L2_BUF_TYPE_VIDEO_CAPTURE	
	    memory; //V4L2_MEMORY_MMAP 
        };

例子:

//跟摄像头的驱动申请缓冲区
	struct v4l2_requestbuffers mybuf;
	bzero(&mybuf,sizeof(mybuf));
	mybuf.count=4;
	mybuf.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;  //与上一步的类型一致
	mybuf.memory=V4L2_MEMORY_MMAP;
	ret=ioctl(camerafd,VIDIOC_REQBUFS,&mybuf);
	if(ret==-1)
	{
		perror("申请缓冲区失败!\n");
		return -1;
	}
4.分配缓冲块,映射缓冲块首地址
指令:VIDIOC_QUERYBUF
用到的变量类型:
    struct v4l2_buffer
    {
        index;  //你刚才申请的缓冲块的索引,从0开始
        type; //V4L2_BUF_TYPE_VIDEO_CAPTURE
        memory; //V4L2_MEMORY_MMAP 
        union
        {
             offset;  //很重要,表示分配给你的缓冲块的地址偏移
        }m;
        length; //分配给你的缓冲块大小 
     }
封装结构体
    struct xxxx
    {
        void *start; //保存映射得到的缓冲块首地址
        int somelength; //保存缓冲块大小
    };
    struct v4l2_buffer mybuf;
    for(int i=0; i<4; i++)
    {
        mybuf.index=i;
        ioctl()
        //顺便映射得到每个缓冲块的首地址
        当前缓冲块首地址            =mmap(NULL,mybuf.length,PROT_READ|PROT_WRITE,MAP_SHARED,camerafd,mybuf.m.offset);
     }  
     先申请入队,然后再启动摄像头采集
     入队指令:VIDIOC_QBUF  

例子:
	//定义结构体数组,存放4个缓冲区的信息
	struct bufmsg array[4];
	//分配刚才你申请的4个缓冲区
	struct v4l2_buffer otherbuf;
	for(i=0; i<4; i++)
	{
		bzero(&otherbuf,sizeof(otherbuf));
		otherbuf.index=i;
		otherbuf.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
		otherbuf.memory=V4L2_MEMORY_MMAP;
		ret=ioctl(camerafd,VIDIOC_QUERYBUF,&otherbuf);
		if(ret==-1)
		{
			perror("分配缓冲区失败!\n");
			return -1;
		}
		
		//顺便映射得到4个缓冲块的首地址(方便后面取出画面数据)
		array[i].somelen=otherbuf.length;
		array[i].start=mmap(NULL,otherbuf.length,PROT_READ|PROT_WRITE,MAP_SHARED,
                                                       camerafd,otherbuf.m.offset);
		if(array[i].start==NULL)
		{
			perror("映射缓冲区首地址失败!\n");
			return -1;
		}
		
		//立马申请入队--》下一步摄像头立马就要启动采集了
		ret=ioctl(camerafd,VIDIOC_QBUF,&otherbuf);
		if(ret==-1)
		{
			perror("入队失败!\n");
			return -1;
		}
	}
	
5.启动摄像头采集画面数据
指令:VIDIOC_STREAMON
用到的变量类型:
    enum v4l2_buf_type
    {
        V4L2_BUF_TYPE_VIDEO_CAPTURE
    }

例子:

//启动摄像头采集
	enum v4l2_buf_type mytype;
	mytype=V4L2_BUF_TYPE_VIDEO_CAPTURE;
	ret=ioctl(camerafd,VIDIOC_STREAMON,&mytype);
	if(ret==-1)
	{
		perror("启动摄像头失败!\n");
		return -1;
	}
6.循环出队入队,显示视频流(可以此处截取摄像画面)
概念:出队指的就是把画面数据从映射得到的缓冲区里面取出来
      入队指的就是把画面数据自动存放到刚才映射的缓冲区里面
      入队指令:VIDIOC_QBUF
      出队指令:VIDIOC_DQBUF   
      用到的变量类型:
      struct v4l2_buffer
建议:把显示画面数据的代码放在出队和入队之间

例子:

char rgbbuf[W*H*3];
fd_set myset;
//循环出队,入队显示视频流
while(1)
{
    FD_ZERO(&myset);
	FD_SET(camerafd,&myset); //监测摄像头
	//用多路复用监测摄像头是否有数据可读,有数据可读我才来出队
	ret=select(camerafd+1,&myset,NULL,NULL,NULL);
	if(ret>0)
	{
		for(i=0; i<4; i++)
		{
		    bzero(&otherbuf,sizeof(otherbuf));
		    otherbuf.index=i;
		    otherbuf.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
		    otherbuf.memory=V4L2_MEMORY_MMAP;
			//出队
			ret=ioctl(camerafd,VIDIOC_DQBUF,&otherbuf);
			if(ret==-1)
			{
				perror("出队失败!\n");
				return -1;
			}
			//把出队的画面数据保存成jpeg图片
			//array[i].start里面保存的就是一张张画面数据
            //如果想在屏幕上显示实时的画面,需要将画面数据转换成ARGB格式,然后使用memcpy拷贝画 
            //                 面数据到液晶屏的映射内存中,并减少sleep的时间
            /*
            for(j=0; j<H; j++)
				memcpy(lcdmem+800*j,&argbbuf[W*j],W*4);
            */
			//画面数据是YUV格式,jpeg图片要求是RGB数据压缩得到的
			//把YUV格式转换成RGB格式
			allyuvtorgb(array[i].start,rgbbuf);
			//把刚才转换得到的RGB数据压缩成jpeg图片
			sprintf(jpgname,"%d.jpg",n++);
			rgbtojpg(rgbbuf,jpgname);
			sleep(1);
			//入队
			ret=ioctl(camerafd,VIDIOC_QBUF,&otherbuf);
			if(ret==-1)
			{
				perror("入队失败!\n");
				return -1;
			}
		}
	}		
}
7.关闭摄像头
 指令:VIDIOC_STREAMOFF
 用到的变量类型:
     enum v4l2_buf_type
     {
         V4L2_BUF_TYPE_VIDEO_CAPTURE
     }

例子:
    ret=ioctl(camerafd,VIDIOC_STREAMOFF,&mytype);
	if(ret==-1)
	{
		perror("关闭摄像头失败了!\n");
		return -1;
	}
	
	//收尾工作
	//解除映射
	for(i=0; i<4; i++)
		munmap(array[i].start,array[i].len);
	munmap(lcdmem,800*480*4);
	close(camerafd);
	close(lcdfd);

其他功能函数

//封装函数把YUV转成RGB
int yuvtorgb(int y,int u,int v)
{
	int r,g,b;
	int pix;
	r=y+1.4075*(v-128);
    g=y-0.3455*(u-128)-0.7169*(v-128);
    b=y+1.779*(u-128);
	//修正计算结果
	if(r>255)
		r=255;
	if(g>255)
		g=255;
	if(b>255)
		b=255;
	if(r<0)
		r=0;
	if(g<0)
		g=0;
	if(b<0)
		b=0;
	//因为lcd要求是ARGB,所以我们顺便在这里把RGB转成ARGB,方便后续使用
	pix=0x00<<24|r<<16|g<<8|b;
	return pix;
}
//封装函数把一张完整画面的YUYV数据全部转换成RGB数据
int allyuvtorgb(char *yuvdata,char *rgb)
{
	/*
		yuvdata[0] -->Y
		yuvdata[1] -->U
		yuvdata[2] -->Y
		yuvdata[3] -->V
	*/
	int i,j;
	int pix;
	char *p;
	//有W*H个像素点
	for(i=0,j=0; i<W*H*3; i+=6,j+=4) // W*H*3/6  次数
	{
		pix=yuvtorgb(yuvdata[j],yuvdata[j+1],yuvdata[j+3]);
		p=(char *)&pix;
		//根据学习的C语言指针运算规则
		//p+0-->B p+1-->G  p+2-->R  p+3 -->A
		rgb[i]=*(p+2);
		rgb[i+1]=*(p+1);
		rgb[i+2]=*(p+0);
		
		pix=yuvtorgb(yuvdata[j+2],yuvdata[j+1],yuvdata[j+3]);
		p=(char *)&pix;
		rgb[i+3]=*(p+2);
		rgb[i+4]=*(p+1);
		rgb[i+5]=*(p+0);
	}
	return 0;
}
//YUV图片数据转换成开发板使用的ARGB格式
//
nt yuyvtoargb(char *yuyvdata,int *argbdata)
{
	int i,j;
	/*
		循环究竟要循环多少次呢?--》你设置的画面大小(W*H个像素点)决定了循环的次数
		每一轮循环可以得到2个像素点
		总共的像素点是W*H个
		得出结论:循环的次数应该是(W*H)/2
	*/
	for(i=0,j=0; i<(W*H); i+=2,j+=4)
	{
		argbdata[i]=yuvtorgb(yuyvdata[j],yuyvdata[j+1],yuyvdata[j+3]);
		argbdata[i+1]=yuvtorgb(yuyvdata[j+2],yuyvdata[j+1],yuyvdata[j+3]);
	}
	return 0;
}

 

//把RGB压缩成jpeg图片
int rgbtojpg(char *rgbdata,char *jpgfile)
{
	int i;
	//定义压缩结构体和处理错误的结构体并初始化
	struct jpeg_compress_struct mycom;
	jpeg_create_compress(&mycom);
	struct jpeg_error_mgr myerr;
	mycom.err=jpeg_std_error(&myerr);
	
	//设置压缩参数(宽,高)
	mycom.image_width=W;
	mycom.image_height=H;
	mycom.in_color_space=JCS_RGB;
	mycom.input_components=3;
	jpeg_set_defaults(&mycom);
	
	//设置压缩比例(压缩质量)
	jpeg_set_quality(&mycom,90,true);
	
	//绑定输出
	FILE *myfile=fopen(jpgfile,"w+");
	if(myfile==NULL)
	{
		perror("新建jpg图片失败了!\n");
		return -1;
	}
	jpeg_stdio_dest(&mycom,myfile);
	
	//开始压缩
	jpeg_start_compress(&mycom,true);
	
	//JSAMPARRAY类型本质上就是JSAMPROW类型的指针
	JSAMPROW row_pointer[1];
	//把压缩后的数据写入到空白的jpeg文件
	for(i=0; i<H; i++)
	{
		//把每一行的RGB数据保存到数组中
		row_pointer[0]=(JSAMPROW)(rgbdata+i*W*3);
		//一次写入一行数据
		jpeg_write_scanlines(&mycom,row_pointer,1);
	}
	
	//收尾
	jpeg_finish_compress(&mycom);
    jpeg_destroy_compress(&mycom);
	fclose(myfile);
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值