標籤:

ubuntu通過USB攝相頭捕捉

一、目的

在ubuntu系統下通過USB攝相頭捕捉視頻流中的靜態畫面

二、引用

  1. (原創)基於ZedBoard的Webcam設計(一):USB攝像頭(V4L2介面)的圖片採集 - 超群天晴 - 博客園 ---主要參考程序
  2. Linux Media Subsystem Documentation ---linux官方文檔
  3. 關於BMP文件格式的詳解 - 未緒 - CSDN博客 --- 參考BMP圖片格式信息
  4. C primer plus 第6版

三、

  • 程序主要參考引用1,但引用1中的程序在不同的機器中(主要因為不同的彙編器對不同的整型類型分配的空間不一致)會產生不同的錯誤,我個人的機器上遇到的問題主要有以下2個:

    1、cant open BMP. ----最開始以為是 fopen函數第2個參數有問題,後來在window下測試fopen函數,發現問題的主要核心在第1個參數,也就是地址有問題。網上下載一張BMP格式的圖片文件無法複製到引用中給出的 "/usr/"中,可能原因是ubuntu系統不允許用戶對目錄/usr/操作。然後將位置引用到個人桌面文件夾中就解決了。

    2、BMP引用了錯誤的頭文件。 ----主要原因就是出現不同的機器不同的彙編器對 int, char, short, long分配不同的空間。參考引用3了解到 bmpfile佔14個位元組, bmpinfo佔40個位元組,然後編寫測試程序sizeof(int/char/short/long)查看個人機器的分配空間情況: char 1, short 2, long 8, int 4。所以將引用中的程序對應代碼修改就行了。
  • 程序在ubuntu16.04 + QT creater 4.5.1上進行編寫測試,組織結構如下:

  • 分塊給出代碼如下

1、v4l2_video _test.pro ----配置編譯器

TEMPLATE = app
CONFIG += console-stdc11
CONFIG -= app_bundle
CONFIG -= qt

TARGET = test
SOURCES += main.c
v4l2.c
TARGET = test

HEADERS +=
v4l2.h

INCLUDEPATH +=
/usr/include
/usr/local/include

2、v4l2.h

#ifndef V4L2_H
#define V4L2_H

#define TRUE 1
#define FALSE 0

#define FREE(x) if( (x) ) { free( (x) ); (x) = NULL; }

#define FILE_VIDEO "/dev/video0"

#define BMP "/home/andy/Desktop/image_bmp.bmp"
#define YUV "/home/andy/Desktop/image_yuv.yuv"

#define IMAGEWIDTH 640
#define IMAGEHEIGHT 480

int fd;
struct v4l2_capability cap;
struct v4l2_input input, g_input;
struct v4l2_output output, g_output;
struct v4l2_fmtdesc fmtdesc;
struct v4l2_format fmt, fmtack;
struct v4l2_streamparm setfps;
struct v4l2_requestbuffers req;
struct v4l2_buffer buf;
enum v4l2_buf_type type;
unsigned char frame_buffer [IMAGEWIDTH * IMAGEHEIGHT *3];

struct buffer
{
void* start;
unsigned int length;
}* buffers;

int init_v4l2(void);
int v4l2_grab(void);
int close_v4l2(void);

void yuyv2_rgb888(void);

typedef unsigned char BYTE; // the size of char is 1
typedef unsigned short WORD; // the size of short is 2
typedef unsigned int DWORD; // the size of int is 4, not use long(8 bytes)

#pragma pack(1)

typedef struct tagBITMAPFILEHEADER // must be 14 bytes
{
WORD bfType; // the flag of bmp, value is "BM"
DWORD bfSize; // size BMP file, uint is bytes
DWORD bfReserved; // 0
DWORD bf0ffBits; // must be 54, offsets = bmp file header size + bmp info size + color palette size
}BITMAPFILEHEADER;

typedef struct tagBITMAPINFOHEADER // must be 40 bytes
{
DWORD biSize; // must be 0x0028, equal 40
DWORD biWidth; // unit is pixel
DWORD biHeight; // unit is pixel
WORD biPlanes; // must be 1
WORD biBitCount; // the bit of every pixel
DWORD biCompression;
DWORD biSizeImage;
DWORD biXPelsPerMeter;
DWORD biYPelsPerMeter;
DWORD biClrUsed;
DWORD biClrImportant;
}BITMAPINFOHEADER;

typedef struct tagRGBQUAD
{
BYTE rgbBlue;
BYTE rgbGreen;
BYTE rgbRed;
BYTE rgbReserved;
}RGBQUAD;

int openVideo(void);
int closeVideo(void);

void getVideoData(unsigned char* data, int size);

#endif // V4L2_H

3、v4l2.c

#include <unistd.h> // unix.std =windows.h, contain read, write, getpid
#include <sys/types.h> // contain size_t, time_t, pid_t, dev_t
#include <sys/stat.h> // contain all the message of a file
#include <fcntl.h> // handle a file
#include <stdio.h>
#include <sys/ioctl.h> // set and control the IO of device
#include <stdlib.h>
#include <sys/mman.h>
#include <linux/types.h>
#include <linux/videodev2.h>
#include "v4l2.h"

// open and initialize the device
int init_v4l2(void)
{
if( (fd =open(FILE_VIDEO, O_RDWR)) ==-1 )
{
printf("Error opening V4l2 interface.
");
return FALSE;
}

if( ioctl(fd, VIDIOC_QUERYCAP, &cap) == -1 )
{
printf("Error opening device %s: unable to query device capbilities.
", FILE_VIDEO);
return FALSE;
}
else {
printf("driver: %s
", cap.driver);
printf("card: %s
", cap.card);
printf("bus_info: %s
", cap.bus_info);
printf("version: %d
", cap.version);
printf("capabilities: %x
", cap.capabilities);
printf("device capbilities: %x
", cap.device_caps);

if( (cap.capabilities & V4L2_CAP_VIDEO_CAPTURE) == V4L2_CAP_VIDEO_CAPTURE )
printf("Device %s: supports capture.
", FILE_VIDEO);
if( (cap.capabilities & V4L2_CAP_STREAMING) == V4L2_CAP_STREAMING )
printf("Device %s: supports streaming.
", FILE_VIDEO);
}

while( ioctl(fd, VIDIOC_ENUMINPUT, &input) != -1 )
{
printf("input: %d", input.index +1);
printf(" %s", input.name);
printf(" %d, non-tuner video input, such as camera sensor.", input.type);
printf(" %d
", input.status);
input.index ++;
}

while( ioctl(fd, VIDIOC_ENUMOUTPUT, &output) != -1 )
{
printf("output: %d", output.index +1);
printf(" %s", output.name);
printf(" %d
", output.type);
input.index ++;
}

if( ioctl(fd, VIDIOC_G_INPUT, &g_input) ==0 )
printf("The current input is: %d %s
", g_input.index +1, g_input.name);

if( ioctl(fd, VIDIOC_G_OUTPUT, &g_output) ==0 )
printf("The current input is: %d %s
", g_output.index +1, g_output.name);

fmtdesc.index = 0;
fmtdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
printf("Support format:
");
while( ioctl(fd, VIDIOC_ENUM_FMT, &fmtdesc) != -1)
{
printf(" %d %s
",fmtdesc.index+1, fmtdesc.description);
fmtdesc.index ++;
}

fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
fmt.fmt.pix.height = IMAGEHEIGHT;
fmt.fmt.pix.width = IMAGEWIDTH;
fmt.fmt.pix.field = V4L2_FIELD_INTERLACED;

if( ioctl(fd, VIDIOC_S_FMT, &fmt) == -1 )
{
printf("Unable to set format.
");
return FALSE;
}

if( ioctl(fd, VIDIOC_G_FMT, &fmt) == -1 )
{
printf("Unable to get format.
");
return FALSE;
}
else{
printf("fmt.type: %d
", fmt.type);
printf("pix.height: %d
", fmt.fmt.pix.height);
printf("pix.width : %d
", fmt.fmt.pix.width);
printf("pix.field: %d
", fmt.fmt.pix.field);
}

setfps.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
setfps.parm.capture.timeperframe.numerator = 10;
setfps.parm.capture.timeperframe.denominator = 10;

if( ioctl(fd, VIDIOC_S_PARM, &setfps) == 0 )
printf("streaming parameters true.
");

printf("init %s [OK]
", FILE_VIDEO);
return TRUE;
}

int v4l2_grab(void)
{
unsigned int n_buffers;

req.count = 4;
req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
req.memory = V4L2_MEMORY_MMAP;
if( ioctl(fd, VIDIOC_REQBUFS, &req) == -1 )
printf("Request for buffers error.
");

buffers = malloc(req.count * sizeof(*buffers));
if( !buffers )
{
printf("Out of memory.
");
return FALSE;
}

for(n_buffers =0; n_buffers <req.count; n_buffers ++)
{
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;
buf.index = n_buffers;

if( ioctl(fd, VIDIOC_QUERYBUF, &buf) == -1)
{
printf("Query buff error.
");
return FALSE;
}

buffers[n_buffers].length = buf.length;

buffers[n_buffers].start = mmap(NULL, buf.length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, buf.m.offset);
if( buffers[n_buffers].start == MAP_FAILED )
{
printf("buffer map error.
");
return FALSE;
}
}

for( n_buffers =0; n_buffers <req.count; n_buffers ++)
{
buf.index = n_buffers;
ioctl(fd, VIDIOC_QBUF, &buf);
}

type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
ioctl(fd, VIDIOC_STREAMON, &type);

ioctl(fd, VIDIOC_DQBUF, &buf);

printf("grab yuyv OK
");
return TRUE;
}

// close the device
int close_v4l2(void)
{
if( fd != -1)
{
close( fd );
return TRUE;
}
return FALSE;
}

void yuyv2_rgb888(void)
{
int i, j;
unsigned char y1, y2, u, v;
int r1, g1, b1, r2, g2, b2;
char* pointer;

pointer = buffers[0].start;

for(i =0; i< 480; i++)
{
for(j =0; j< 320; j++)
{
y1 = *(pointer + (i * 320 + j) * 4);
u = *(pointer + (i * 320 + j) * 4 + 1);
y2 = *(pointer + (i * 320 + j) * 4 + 2);
v = *(pointer + (i * 320 + j) * 4 + 3);

r1 = y1 + 1.042 * (v - 128);
g1 = y1 - 0.34414 * (u - 128) - 0.71414 * (v - 128);
b1 = y1 + 1.772 * (u - 128);

r2 = y2 + 1.042 * (v - 128);
g2 = y2 - 0.34414 * (u - 128) - 0.71414 * (v - 128);
b2 = y2 + 1.772 * (u - 128);

if(r1 > 255)
r1 = 255;
else if(r1 < 0)
r1 = 0;

if(b1 > 255)
b1 = 255;
else if(b1 < 0)
b1 = 0;

if(g1 > 255)
g1 = 255;
else if(g1 < 0)
g1 = 0;

if(r2 > 255)
r2 = 255;
else if(r2 < 0)
r2 = 0;

if(b2 > 255)
b2 = 255;
else if(b2 < 0)
b2 = 0;

if(g2 > 255)
g2 = 255;
else if(g2 < 0)
g2 = 0;

*(frame_buffer + ( (480 -1 -i)* 320 + j )*6 ) = (unsigned char)b1;
*(frame_buffer + ( (480 -1 -i)* 320 + j )*6 + 1) = (unsigned char)g1;
*(frame_buffer + ( (480 -1 -i)* 320 + j )*6 + 2) = (unsigned char)r1;
*(frame_buffer + ( (480 -1 -i)* 320 + j )*6 + 3) = (unsigned char)b2;
*(frame_buffer + ( (480 -1 -i)* 320 + j )*6 + 4) = (unsigned char)g2;
*(frame_buffer + ( (480 -1 -i)* 320 + j )*6 + 5) = (unsigned char)r2;
}
}
printf("change to RGB OK
");
}

4、main.c

#include <unistd.h> // unix.std =windows.h, contain read, write, getpid
#include <sys/types.h> // contain size_t, time_t, pid_t, dev_t
#include <sys/stat.h> // contain all the message of a file
#include <fcntl.h> // handle a file
#include <stdio.h>
#include <sys/ioctl.h> // set and control the IO of device
#include <stdlib.h>
#include <sys/mman.h>
#include <linux/types.h>
#include <linux/videodev2.h>

#include "v4l2.h"

int main(void)
{
FILE *fp1, *fp2;
BITMAPFILEHEADER bf;
BITMAPINFOHEADER bi;

fp1 = fopen(BMP, "wb");
if(!fp1)
{
printf("open "BMP" error
");
return(FALSE);
}

fp2 = fopen(YUV, "wb");
if(!fp2)
{
printf("open "YUV" error
");
return(FALSE);
}

if( init_v4l2() == FALSE )
return FALSE;

bi.biSize = 40;
bi.biWidth = IMAGEWIDTH;
bi.biHeight = IMAGEHEIGHT;
bi.biPlanes = 1;
bi.biBitCount = 24;
bi.biCompression = 0;
bi.biSizeImage = IMAGEWIDTH * IMAGEHEIGHT * 3;
bi.biXPelsPerMeter = 0;
bi.biYPelsPerMeter = 0;
bi.biClrUsed = 0;
bi.biClrImportant = 0;

bf.bfType = 0x4D42;
bf.bfSize = 54 + bi.biSizeImage;
bf.bfReserved = 0;
bf.bf0ffBits = 54;

v4l2_grab();
fwrite(buffers[0].start, 640*480*2, 1, fp2);
printf("save YUV OK
");

yuyv2_rgb888();
fwrite(&bf, 14, 1, fp1);
fwrite(&bi, 40, 1, fp1);
fwrite(frame_buffer, bi.biSizeImage, 1, fp1);
printf("save BMP OK
");

fclose(fp1);
fclose(fp2);
close_v4l2();

return TRUE;
}

5、測試成功

推薦閱讀:

TAG:Linux開發 |