RM新时代网站-首页

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認識你,還能領取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)申請緩沖區(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

pYYBAGMIzRuAX7EtAAJmDR7Ghjc003.png#pic_center

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。

poYBAGMIzRuAYQHJAAKznya94As712.png#pic_center
  • 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通道。

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.申請緩沖區(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 采集圖像數據

??循環(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++)>
聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發(fā)燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規(guī)問題,請聯系本站處理。 舉報投訴
  • Linux
    +關注

    關注

    87

    文章

    11292

    瀏覽量

    209318
  • 攝像頭
    +關注

    關注

    59

    文章

    4836

    瀏覽量

    95597
  • 編程
    +關注

    關注

    88

    文章

    3614

    瀏覽量

    93685
  • V4L2
    +關注

    關注

    0

    文章

    17

    瀏覽量

    3894
收藏 人收藏

    評論

    相關推薦

    項目外包:編寫linux攝像頭驅動開發(fā)

    項目名稱:雙攝像頭驅動開發(fā)報價:10000元(可商談)要求完成時間:2016-07-18需求描述:1.編寫linux驅動程序,支持兩個mipi接口的攝像頭,
    發(fā)表于 06-15 15:56

    ESM6802支持Qt攝像頭應用

    是Logitech C310 USB攝像頭,ESM6802的Linux系統(tǒng)能夠自動識別,正常使用,更多攝像頭將在后續(xù)進行測試。camera程序運行效果見下圖:  在程序中需要首先檢查攝像頭
    發(fā)表于 10-20 10:33

    回收蘋果攝像頭 收購蘋果攝像頭

    回收蘋果攝像頭 收購蘋果攝像頭回收蘋果攝像頭,大量收購蘋果攝像頭?。。?帝歐電子 趙先生 TEL:135-3012-2202 QQ:879821252 帝歐電子專業(yè)電子收購,現急購
    發(fā)表于 12-29 18:14

    回收攝像頭ic 收購攝像頭ic

    回收攝像頭ic 收購攝像頭ic 攝像頭ic實力回收 ||優(yōu)勢高價回收攝像頭ic @@@ 趙先生 135-3012-2202同步微信 QQ:8798-21252)帝歐電子 實力回收 工廠
    發(fā)表于 01-08 17:26

    回收手機攝像頭 收購手機攝像頭

    `回收手機攝像頭,大量收購手機攝像頭!??! 帝歐電子 趙先生 TEL:135-3012-2202 QQ:879821252 帝歐電子專業(yè)電子收購,現急購攝像頭,大量回收攝像頭!手機
    發(fā)表于 07-05 11:01

    回收手機攝像頭,收購攝像頭芯片

    `帝歐電子趙生135-3012-2202,QQ:8798-21252長期高價回收手機攝像頭,回收攝像頭芯片。 攝像頭廣泛運用于我們的生活之中,大街上隨處可見的安防監(jiān)控,人手至少一臺的手機平板,汽車
    發(fā)表于 07-14 17:53

    Linux操作系統(tǒng)攝像頭設備是如何實現驅動并移植的

    DCMI是什么?有何作用?Linux操作系統(tǒng)攝像頭設備是如何實現驅動并移植的?
    發(fā)表于 02-28 09:40

    如何對基于Linux操作系統(tǒng)攝像頭設備進行驅動并移植呢

    如何對基于Linux操作系統(tǒng)攝像頭設備進行驅動并移植呢?有哪些操作步驟?
    發(fā)表于 02-28 09:19

    Linux基于ARM920T的USB攝像頭圖像采集

    本文介紹了基于ARM920T的嵌入式Linux利用USB攝像頭采集圖像的硬件、軟件設計過程,最終實現了在目標板上圖像的采集和顯示。
    發(fā)表于 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攝像頭圖像采集
    發(fā)表于 10-30 16:36 ?13次下載
    基于<b class='flag-5'>Linux</b><b class='flag-5'>下</b>基于ARM920T的USB<b class='flag-5'>攝像頭</b>圖像采集

    Linux系統(tǒng)中如何安裝攝像頭驅動

    1、攝像頭(Webcam)驅動說明; 攝像頭在Windows的驅動極為容易,最多是點幾下鼠標,沒有什么太大的難度。但在Linux中,驅動起來是有點困難,這并不是說Linux多高雅。只能
    發(fā)表于 11-07 11:45 ?5次下載

    盤點屏攝像頭技術

    指紋技術及聽筒隱藏設計的出現,讓全面屏技術日趨成熟,水滴屏、美人尖應運而生,而隱藏攝像頭成為最后難題。因此我們也的逐漸將目光轉向屏攝像頭技術。
    的頭像 發(fā)表于 11-13 08:45 ?7411次閱讀

    攝像頭的難點_屏攝像頭的專利是誰的

    如果不考慮成像質量,只談論搭載屏攝像頭,那么已經普及的具有在光電屏下方指紋技術的手機已經擁有了它。光電屏指紋模塊的工作原理是在屏下方放置一個微型傳感器和一個攝像頭,每次手指觸摸區(qū)域
    的頭像 發(fā)表于 08-12 14:37 ?9498次閱讀

    三星新推出屏攝像頭實現“透明攝像頭”技術

    1月14日,@三星顯示 官宣全新的OLED屏幕,不僅更輕更薄,而且還配合屏攝像頭技術實現了“透明攝像頭”,將攝像頭隱藏在屏幕下方,同時邊框繼續(xù)收窄,在筆記本上實現了更為極致的全面屏視
    的頭像 發(fā)表于 01-14 14:59 ?3934次閱讀

    Linux開發(fā)_攝像頭編程(實現拍照功能)

    這篇文章主要介紹LinuxUVC免驅攝像頭操作流程,介紹V4L2框架、完成攝像頭拍照保存為BMP圖像到本地,最后貼出了利用CJSON庫解析天氣預報、北京時間接口返回的數據例子代碼(上
    的頭像 發(fā)表于 09-17 15:34 ?1845次閱讀
    RM新时代网站-首页