Linux系统下调用摄像头实现拍照功能
【摘要】 这篇文章介绍在Linux如何调用摄像头获取图像,并保存为图片,可以顺便巩固一下文件编程章节的知识点。
1. 功能介绍
这篇文章介绍在Linux如何调用摄像头获取图像,并保存为图片,可以顺便巩固一下文件编程章节的知识点。
Linux系统下可以通过V4L2框架,读取摄像头数据;如果是常规的UVC免驱摄像头,在/dev/目录下可以看到videoX节点,不需要写驱动。一般电脑自带的摄像头、常规外置USB摄像头等都支持。 V4L2框架就是字符设备框架,操作摄像头的流程,和操作其他字符设备类似。调用open
打开设备之后,就可以通过ioctl使用特定的命令对摄像头设备进行配置,然后启动采集;一般得到的数据都是YUV格式,好一点的摄像头可以配置,输出其他格式数据,一般的摄像头只是支持YUV格式,得到数据之后还需要转码,转成RGB格式,再进行编码保存,编码视频,进行图像处理,或者显示出来都可以。
下面总结一下流程:
1.打开摄像头的设备文件 open("/dev/video0");
2.获取摄像头参数。判断摄像头是否支持一些特有的操作。read
3.配置摄像头的参数。(1) 输出的源数据格式RGB888 (2) 输出的图像尺寸
RGB888:数字数据格式
其他格式: YUV 模拟数据格式
4.判断摄像头的参数是否设置成功。
5.读取摄像头的数据。 队列的形式。
6.将读取的YUV格式数据转为RGB格式
7.将RGB格式数据编码为BMP格式的图片存放到电脑上
如果是在虚拟机上运行Linux系统,那么需要先将摄像头挂载进来才可以使用。
2. 案例代码
下面例子代码里实现的功能是: 读取摄像头的数据,将格式转为RGB24,再保存为BMP格式的图片存放到本地。
2.1 头文件
#ifndef CAMERA_H
#define CAMERA_H
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <unistd.h>
#include <strings.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <pthread.h>
#include <linux/fb.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <dirent.h>
#include <stdlib.h>
#include <fcntl.h>
#include <poll.h>
#include <linux/videodev2.h>
#include <sys/time.h>
#include <assert.h>
#include <sys/select.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
/* 图片的象素数据 */
typedef struct PixelDatas {
int iWidth; /* 宽度: 一行有多少个象素 */
int iHeight; /* 高度: 一列有多少个象素 */
int iBpp; /* 一个象素用多少位来表示 */
int iLineBytes; /* 一行数据有多少字节 */
int iTotalBytes; /* 所有字节数 */
unsigned char *VideoBuf; //存放一帧摄像头的数据
}T_PixelDatas;
/*
相关函数声明
*/
void save_bmp(char *); //保存编码为BMP图片
int camera_init(char *);
void camera_pthread();
/*
YUV转RGB格式相关定义
*/
typedef struct Myrgb16
{
unsigned short blue:5;
unsigned short green:6;
unsigned short red:5;
} Myrgb16;
typedef struct Myrgb24
{
unsigned char blue;
unsigned char green;
unsigned char red;
} Myrgb24;
typedef struct Myrgb32
{
unsigned char blue;
unsigned char green;
unsigned char red;
unsigned char alpha;
} Myrgb32;
typedef struct MyYUV422
{
unsigned char y0;
unsigned char u;
unsigned char y1;
unsigned char v;
} MyYUV422;
typedef struct MyYUV444
{
unsigned char y;
unsigned char u;
unsigned char v;
} MyYUV444;
#define CLIP(color) (unsigned char)(((color)>0xFF)?0xff:(((color)<0)?0:(color)))
unsigned char RGB24_TO_Y(unsigned char r, unsigned char g, unsigned char b);
unsigned char YR_TO_V(unsigned char r, unsigned char y);
unsigned char YB_TO_U(unsigned char b, unsigned char y);
unsigned char R_FROMYV(unsigned char y, unsigned char v);
unsigned char G_FROMYUV(unsigned char y, unsigned char u, unsigned char v);
unsigned char B_FROMYU(unsigned char y, unsigned char u);
#define YfromRGB(r,g,b) CLIP((77*(r)+150*(g)+29*(b))>>8)
#define UfromRGB(r,g,b) CLIP(((128*(b)-85*(g)-43*(r))>>8 )+128)
#define VfromRGB(r,g,b) CLIP(((128*(r)-107*(g)-21*(b))>>8) +128)
#define PACKRGB16(r,g,b) (__u16) ((((b) & 0xF8) << 8 ) | (((g) & 0xFC) << 3 ) | (((r) & 0xF8) >> 3 ))
#define UNPACK16(pixel,r,g,b) r=((pixel)&0xf800) >> 8; g=((pixel)&0x07e0) >> 3; b=(((pixel)&0x001f) << 3)
void initLut(void);
void freeLut(void);
#endif
2.2 源文件
#include "camera_bmp.h"
T_PixelDatas Pixedata; //存放实际的图像数据
/*
USB摄像头相关参数定义
*/
struct v4l2_buffer tV4l2Buf;
int iFd;
int ListNum;
unsigned char* pucVideBuf[4]; // 视频BUFF空间地址
int main(int argc ,char *argv[])
{
if(argc!=2)
{
printf("./app /dev/videoX\n");
return -1;
}
camera_init(argv[1]); //摄像头设备初始化
//开始采集摄像头数据,并编码保存为BMP图片
camera_pthread();
return 0;
}
//YUV转RGB实现
unsigned int Pyuv422torgb32(unsigned char * ptr,unsigned int width, unsigned int height)
{
unsigned int i, size;
unsigned char Y, Y1, U, V;
unsigned char *buff = ptr; //源数据
unsigned char *output_pt=Pixedata.VideoBuf; //存放转换之后的数据
unsigned char r, g, b;
size = width * height /2;
for (i = size; i > 0; i--)
{
Y = buff[0];
U = buff[1];
Y1= buff[2];
V = buff[3];
buff += 4;
r = R_FROMYV(Y,V);
g = G_FROMYUV(Y,U,V); //b
b = B_FROMYU(Y,U); //v
*output_pt++ = b;
*output_pt++ = g;
*output_pt++ = r;
r = R_FROMYV(Y1,V);
g = G_FROMYUV(Y1,U,V); //b
b = B_FROMYU(Y1,U); //v
*output_pt++ = b;
*output_pt++ = g;
*output_pt++ = r;
}
return 0;
}
//摄像头设备的初始化
int camera_init(char *video)
{
int i=0;
int cnt=0;
//定义摄像头驱动的BUF的功能捕获视频
int iType = V4L2_BUF_TYPE_VIDEO_CAPTURE;
/* 1、打开视频设备 */
iFd = open(video,O_RDWR);
if(iFd < 0)
{
printf("摄像头设备打开失败!\n");
return 0;
}
struct v4l2_format tV4l2Fmt;
/* 2、 VIDIOC_S_FMT 设置摄像头使用哪种格式 */
memset(&tV4l2Fmt, 0, sizeof(struct v4l2_format));
tV4l2Fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; //视频捕获
//设置摄像头输出的图像格式
tV4l2Fmt.fmt.pix.pixelformat=V4L2_PIX_FMT_YUYV;
/*设置输出的尺寸*/
tV4l2Fmt.fmt.pix.width = 640;
tV4l2Fmt.fmt.pix.height = 480;
tV4l2Fmt.fmt.pix.field = V4L2_FIELD_ANY;
//VIDIOC_S_FMT 设置摄像头的输出参数
ioctl(iFd, VIDIOC_S_FMT, &tV4l2Fmt);
//打印摄像头实际的输出参数
printf("Support Format:%d\n",tV4l2Fmt.fmt.pix.pixelformat);
printf("Support width:%d\n",tV4l2Fmt.fmt.pix.width);
printf("Support height:%d\n",tV4l2Fmt.fmt.pix.height);
/* 初始化Pixedata结构体,为转化做准备 */
Pixedata.iBpp =24;
//高度 和宽度的赋值
Pixedata.iHeight = tV4l2Fmt.fmt.pix.height;
Pixedata.iWidth = tV4l2Fmt.fmt.pix.width;
//一行所需要的字节数
Pixedata.iLineBytes = Pixedata.iWidth*Pixedata.iBpp/8;
//一帧图像的字节数
Pixedata.iTotalBytes = Pixedata.iLineBytes * Pixedata.iHeight;
Pixedata.VideoBuf=malloc(Pixedata.iTotalBytes); //申请存放图片数据空间
//v412请求命令
struct v4l2_requestbuffers tV4l2ReqBuffs;
/* 3、VIDIOC_REQBUFS 申请buffer */
memset(&tV4l2ReqBuffs, 0, sizeof(struct v4l2_requestbuffers));
/* 分配4个buffer:实际上由VIDIOC_REQBUFS获取到的信息来决定 */
tV4l2ReqBuffs.count = 4;
/*支持视频捕获功能*/
tV4l2ReqBuffs.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
/* 表示申请的缓冲是支持MMAP */
tV4l2ReqBuffs.memory = V4L2_MEMORY_MMAP;
/* 为分配buffer做准备 */
ioctl(iFd, VIDIOC_REQBUFS, &tV4l2ReqBuffs);
for (i = 0; i < tV4l2ReqBuffs.count; i++)
{
memset(&tV4l2Buf, 0, sizeof(struct v4l2_buffer));
tV4l2Buf.index = i;
tV4l2Buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
tV4l2Buf.memory = V4L2_MEMORY_MMAP;
/* 6、VIDIOC_QUERYBUF 确定每一个buffer的信息 并且 mmap */
ioctl(iFd, VIDIOC_QUERYBUF, &tV4l2Buf);
//映射空间地址
pucVideBuf[i] = mmap(0 /* start anywhere */ ,
tV4l2Buf.length, PROT_READ, MAP_SHARED, iFd,
tV4l2Buf.m.offset);
printf("mmap %d addr:%p\n",i,pucVideBuf[i]);
}
/* 4、VIDIOC_QBUF 放入队列*/
for (i = 0; i <tV4l2ReqBuffs.count; i++)
{
memset(&tV4l2Buf, 0, sizeof(struct v4l2_buffer));
tV4l2Buf.index = i;
tV4l2Buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
tV4l2Buf.memory = V4L2_MEMORY_MMAP;
ioctl(iFd, VIDIOC_QBUF, &tV4l2Buf); //放入队列
}
/*5、启动摄像头开始读数据*/
ioctl(iFd, VIDIOC_STREAMON, &iType);
return 0;
}
/*循环采集摄像头数据*/
void camera_pthread()
{
int error;
int cnt=0;
int i=0;
int ListNum;
/* 8.1、等待是否有数据 */
fd_set readfds;
/* YUV格式的数据<------>在LCD上显示:rgb888 */
initLut();
printf("开始采集数据.......\n");
FD_ZERO(&readfds);
FD_SET(iFd,&readfds);
select(iFd+1,&readfds,NULL,NULL,NULL);
memset(&tV4l2Buf, 0, sizeof(struct v4l2_buffer));
tV4l2Buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; //类型
tV4l2Buf.memory = V4L2_MEMORY_MMAP; //存储空间类型
/* 9、VIDIOC_DQBUF 从队列中取出 */
error = ioctl(iFd, VIDIOC_DQBUF, &tV4l2Buf); //取出一帧数据
ListNum = tV4l2Buf.index; //索引编号
//将YUV转换为RGB
Pyuv422torgb32(pucVideBuf[ListNum],Pixedata.iWidth,Pixedata.iHeight);
//保存BMP
save_bmp(Pixedata.VideoBuf);
memset(&tV4l2Buf, 0, sizeof(struct v4l2_buffer));
tV4l2Buf.index = ListNum;
tV4l2Buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
tV4l2Buf.memory = V4L2_MEMORY_MMAP;
error = ioctl(iFd, VIDIOC_QBUF, &tV4l2Buf);
}
/*-----------------保存为BMP格式的图片------------------*/
typedef unsigned char BYTE;
typedef unsigned short WORD;
typedef unsigned long DWORD;
#pragma pack(1)
typedef struct tagBITMAPFILEHEADER{
WORD bfType; // the flag of bmp, value is "BM"
DWORD bfSize; // size BMP file ,unit is bytes
DWORD bfReserved; // 0
DWORD bfOffBits; // must be 54 RGB数据存放位置
}BITMAPFILEHEADER;
typedef struct tagBITMAPINFOHEADER{
DWORD biSize; // must be 0x28
DWORD biWidth; // 宽度
DWORD biHeight; // 高度
WORD biPlanes; // must be 1
WORD biBitCount; // 像素位数
DWORD biCompression; //
DWORD biSizeImage; //
DWORD biXPelsPerMeter; //
DWORD biYPelsPerMeter; //
DWORD biClrUsed; //
DWORD biClrImportant; //
}BITMAPINFOHEADER;
//保存为BMP格式的文件
void save_bmp(char *src)
{
/*-----------------------------------------------------------
获取时间参数,用来给图片命名
-------------------------------------------------------------*/
time_t t;
struct tm *tmp;
char buffer[1024] = {0};
t = time(NULL);
tmp=localtime(&t);
if(strftime(buffer, sizeof(buffer), "%Y%m%d%H%M%S_", tmp) == 0)
{
printf("timer error\n");
}
static int cnt=0; //静态变量存放总数量
cnt++;
if(cnt>=20)cnt=0; //清理计数器
char str[10];
sprintf(str,"%d",cnt); //整数转为字符串
strcat(buffer,str);
strcat(buffer,".bmp");
printf("%s\n",buffer); //打印图片的名称
/*-----------------------------------------------------------
获取图片数据,用来保存为BMP图片格式
-------------------------------------------------------------*/
FILE * fp;
int i;
BITMAPFILEHEADER bf;
BITMAPINFOHEADER bi;
memset(&bf ,0 ,sizeof(BITMAPFILEHEADER));
memset(&bi ,0 ,sizeof(BITMAPINFOHEADER));
fp = fopen(buffer, "wb");
if(!fp)
{
printf("open %s error\n",buffer);
return ;
}
//Set BITMAPINFOHEADER 设置BMP信息头
bi.biSize = sizeof(BITMAPINFOHEADER);//40;
bi.biWidth = Pixedata.iWidth;//IMAGEWIDTH;
bi.biHeight = Pixedata.iHeight;//IMAGEHEIGHT;
bi.biPlanes = 1;
bi.biBitCount = 24;//8;
bi.biCompression = 0;
bi.biSizeImage =Pixedata.iHeight*Pixedata.iWidth*3; //;0
bi.biXPelsPerMeter = 0;
bi.biYPelsPerMeter = 0;
bi.biClrUsed = 0;// 1<<(bi.biBitCount)
bi.biClrImportant = 0;
//Set BITMAPFILEHEADER
bf.bfType = 0x4d42; //'B''M'
bf.bfSize = 54 + bi.biSizeImage;// sizeof(BITMAPFILEHEADER);
bf.bfReserved = 0;
bf.bfOffBits = 54;
fwrite(&bf, sizeof(BITMAPFILEHEADER), 1, fp);
fwrite(&bi, sizeof(BITMAPINFOHEADER), 1, fp);
fwrite(src, Pixedata.iWidth*Pixedata.iHeight*3,1,fp);
fclose(fp);
}
/*----------------------YUV转RGB算法-------------------*/
static int *LutYr = NULL;
static int *LutYg = NULL;;
static int *LutYb = NULL;;
static int *LutVr = NULL;;
static int *LutVrY = NULL;;
static int *LutUb = NULL;;
static int *LutUbY = NULL;;
static int *LutRv = NULL;
static int *LutGu = NULL;
static int *LutGv = NULL;
static int *LutBu = NULL;
unsigned char RGB24_TO_Y(unsigned char r, unsigned char g, unsigned char b)
{
return (LutYr[(r)] + LutYg[(g)] + LutYb[(b)]);
}
unsigned char YR_TO_V(unsigned char r, unsigned char y)
{
return (LutVr[(r)] + LutVrY[(y)]);
}
unsigned char YB_TO_U(unsigned char b, unsigned char y)
{
return (LutUb[(b)] + LutUbY[(y)]);
}
unsigned char R_FROMYV(unsigned char y, unsigned char v)
{
return CLIP((y) + LutRv[(v)]);
}
unsigned char G_FROMYUV(unsigned char y, unsigned char u, unsigned char v)
{
return CLIP((y) + LutGu[(u)] + LutGv[(v)]);
}
unsigned char B_FROMYU(unsigned char y, unsigned char u)
{
return CLIP((y) + LutBu[(u)]);
}
void initLut(void)
{
int i;
#define Rcoef 299
#define Gcoef 587
#define Bcoef 114
#define Vrcoef 711 //656 //877
#define Ubcoef 560 //500 //493 564
#define CoefRv 1402
#define CoefGu 714 // 344
#define CoefGv 344 // 714
#define CoefBu 1772
LutYr = malloc(256*sizeof(int));
LutYg = malloc(256*sizeof(int));
LutYb = malloc(256*sizeof(int));
LutVr = malloc(256*sizeof(int));
LutVrY = malloc(256*sizeof(int));
LutUb = malloc(256*sizeof(int));
LutUbY = malloc(256*sizeof(int));
LutRv = malloc(256*sizeof(int));
LutGu = malloc(256*sizeof(int));
LutGv = malloc(256*sizeof(int));
LutBu = malloc(256*sizeof(int));
for (i= 0;i < 256;i++)
{
LutYr[i] = i*Rcoef/1000 ;
LutYg[i] = i*Gcoef/1000 ;
LutYb[i] = i*Bcoef/1000 ;
LutVr[i] = i*Vrcoef/1000;
LutUb[i] = i*Ubcoef/1000;
LutVrY[i] = 128 -(i*Vrcoef/1000);
LutUbY[i] = 128 -(i*Ubcoef/1000);
LutRv[i] = (i-128)*CoefRv/1000;
LutBu[i] = (i-128)*CoefBu/1000;
LutGu[i] = (128-i)*CoefGu/1000;
LutGv[i] = (128-i)*CoefGv/1000;
}
}
void freeLut(void)
{
free(LutYr);
free(LutYg);
free(LutYb);
free(LutVr);
free(LutVrY);
free(LutUb);
free(LutUbY);
free(LutRv);
free(LutGu);
free(LutGv);
free(LutBu);
}
【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱:
cloudbbs@huaweicloud.com
- 点赞
- 收藏
- 关注作者
评论(0)