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)申請緩沖區(qū):VIDIOC_REQBUFS(緩沖區(qū)數量、緩沖映射方式、視頻捕獲格式)。
(4)將緩沖區(qū)映射到進程空間:VIDIOC_QUERYBUF(要映射的緩沖區(qū)下標、緩沖映射方式、視頻捕獲格式)。
(5)將緩沖區(qū)添加到隊列中:VIDIOC_QBUF(映射的緩沖區(qū)下標、緩沖映射方式、視頻捕獲格式)。
(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)
向內核申請緩沖區(qū)
#define VIDIOC_REQBUFS _IOWR(‘V’, 8, struct v4l2_requestbuffers)
將緩沖區(qū)映射到進程空間
#define VIDIOC_QUERYBUF _IOWR(‘V’, 9, struct v4l2_buffer)
將緩沖區(qū)添加到采集隊列
#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
//內存映射緩沖區(qū)
struct v4l2_requestbuffers {
__u32 count; //申請緩沖區(qū)個數
__u32 type; /* enum v4l2_buf_type 視頻類型 /
__u32 memory; / enum v4l2_memory 映射方式*/
__u32 reserved[2];
};
- struct v4l2_buffer
//視頻緩沖區(qū)信息
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;/映射緩沖區(qū)大小/
__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格式,先連續(xù)存儲所有像素點的Y,緊接著存儲所有像素點的U,隨后是所有像素點的V。
對于packed的YUV格式,每個像素點的Y,U,V是連續(xù)交錯存儲的。
YUV的主要優(yōu)勢在于可以兼容之前的黑白電視,單獨只有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字節(jié);一幀圖像占用的空間為:width * height * 2
YUV420
YUV420 每四個Y分量公用一個UV分量,并不是沒有V分量,而是UV分量交替采樣,所以每個像素點占用1.5個字節(jié)空間。根據planar(平面)和packed(交錯)方式存儲有4種存儲方式。下面舉其中一種示例說明:
每4個Y共用一組UV分量,單個像素占用空間為:1byte(Y)+1/4byte(U)+1/4byte(V)=1.5字節(jié);一幀圖像占用的空間為:width * height * 3/2 byte。
- RGB
RGB色彩模式是工業(yè)界的一種顏色標準,是通過對紅?、綠(G)、藍(B)三個顏色通道的變化以及它們相互之間的疊加來得到各式各樣的顏色的,RGB即是代表紅、綠、藍三個通道的顏色,這個標準幾乎包括了人類視力所能感知的所有顏色,是運用最廣的顏色系統(tǒng)之一。
- 常用的RGB格式:
RGB565:5位紅色+6位綠色+5位藍色=16位顏色值
RGB888:8位紅色+8位綠色+8位藍色=24真彩色
RGB32:RGB32是在RGB基礎上附加上Alpha(透明度)通道,RGB個占8位,剩余8位用作Alpha通道。
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.申請緩沖區(qū)*/
struct v4l2_requestbuffers v4l2reqbuf;
memset(&v4l2reqbuf,0,sizeof(v4l2reqbuf));//初始化結構體
v4l2reqbuf.count=4;//申請的緩沖區(qū)個數
v4l2reqbuf.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;//視頻捕獲格式
v4l2reqbuf.memory=V4L2_MEMORY_MMAP;//內存映射
if(ioctl(fd,VIDIOC_REQBUFS,&v4l2reqbuf))return -3;//申請緩沖區(qū)失敗
printf("申請的緩沖區(qū)個數:%d\n",v4l2reqbuf.count);
/*4.將緩沖區(qū)映射到進程空間*/
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 采集圖像數據
3.2 采集圖像數據
??循環(huán)采集圖像數據,將一幀圖像數據保存為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("文件創(chuàng)建失敗\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);
/*將緩沖區(qū)添加回采集隊列中*/
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("文件創(chuàng)建失敗\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);
/*將緩沖區(qū)添加回采集隊列中*/
if(ioctl(fd,VIDIOC_QBUF,&v4l2buf))break;//添加到采集隊列失敗
}
close(fd);//關閉攝像頭
}
審核編輯 黃昊宇
;i++)>
-
Linux
+關注
關注
87文章
11292瀏覽量
209326 -
攝像頭
+關注
關注
59文章
4836瀏覽量
95599 -
編程
+關注
關注
88文章
3614瀏覽量
93686 -
V4L2
+關注
關注
0文章
17瀏覽量
3894
發(fā)布評論請先 登錄
相關推薦
評論