배우고픈 공돌이

10-1. V4L2를 사용한 bitmap 촬영 C소스 본문

ARM/RasberryPi3

10-1. V4L2를 사용한 bitmap 촬영 C소스

내 마음 아홉수 2017. 10. 18. 12:03

출처 : https://ameblo.jp/sy-eng/entry-12072692011.html


saibara 씨의 소스 코드를 분할시켰다.


capture.h


#ifndef __CAPTURE_H__
#define __CAPTURE_H__
#define OUTFILE_NAME "capture.bmp"
#define COUNT_IGNORE 10 // frame count for initilize camera
#define IMAGE_WIDTH 640
#define IMAGE_HEIGHT 480
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <assert.h>
#include <fcntl.h> /* low-level i/o */
#include <unistd.h>
#include <errno.h>
#include <malloc.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/mman.h>
#include <sys/ioctl.h>
#include <asm/types.h> /* for videodev2.h */
#include <linux/videodev2.h>
#define CLEAR(x) memset (&(x), 0, sizeof (x))
#define uchar unsigned char
/* sequence
*open_device();
*init_device();
*start_capturing();
*mainloop();
*stop_capturing();
*uninit_device();
*close_device();
*exit(EXIT_SUCCESS);
*/
static void open_device(void);
static void init_device(void);
static void start_capturing(void);
static void mainloop(void);
static void stop_capturing (void);
static void uninit_device(void);
static void close_device(void);
void captureBM(void);
#endif //capture.h




capture.c


#include "capture.h"


struct buffer {
void * start;
size_t length;
};
static char * dev_name = "/dev/video0";
static int fd = -1;
struct buffer * buffers = NULL;
static unsigned int n_buffers = 0;
static void errno_exit(const char *s)
{
fprintf(stderr, "%s error %d, %s\n",s, errno, strerror(errno));
exit(EXIT_FAILURE);
}
static int xioctl(int fd,int request,void *arg)
{
int r;
do{ r = ioctl(fd, request, arg); }
while(-1 == r && EINTR == errno);
return r;
}
void saveBitmapFile(char* fileName, uchar* R, uchar* G, uchar* B, int width, int height){
FILE* fp;
int i;
fp = fopen(OUTFILE_NAME,"wb");
if(fp == NULL) return;
//file header
fprintf(fp, "BM");
//If (IMAGE_WIDTH * 3) is not the multiple of 4, some 0(s) must be added to make it multiple of 4.
uint32_t fileSize = 14 + 40 + (IMAGE_WIDTH*3)*IMAGE_HEIGHT;
fwrite(&fileSize, 4, 1, fp);
uint32_t reserved = 0;
fwrite(&reserved, 4, 1, fp);
uint32_t offset = 54;
fwrite(&offset, 4, 1, fp);
//information header
uint32_t size = 40;
fwrite(&size, 4, 1, fp);
int32_t tWidth =(int32_t)width;
fwrite(&tWidth, 4, 1, fp);
int32_t tHeight = (int32_t)height;
fwrite(&tHeight, 4, 1, fp);
uint16_t plane = 1;
fwrite(&plane, 2, 1, fp);
uint16_t bit = 24;
fwrite(&bit, 2, 1, fp);
uint32_t compression = 0;
fwrite(&compression, 4, 1, fp);
//No additional 0(s);
size = (IMAGE_WIDTH*3)*IMAGE_HEIGHT;
fwrite(&size, 4, 1, fp);
uint32_t zero = 0;
fwrite(&zero, 4, 1, fp);
fwrite(&zero, 4, 1, fp);
fwrite(&zero, 4, 1, fp);
fwrite(&zero, 4, 1, fp);
//image data
//no additional 0(s)
for(i = 0; i < IMAGE_WIDTH*IMAGE_HEIGHT; i++){
fwrite(B+i, 1, 1, fp);
fwrite(G+i, 1, 1, fp);
fwrite(R+i, 1, 1, fp);
}
fclose(fp);
printf("Saved\n");
return;
}
static void process_image(const void *p_buf,const int len_buf)
{
int i, width, height;
uchar *d1,*d2,buf;
uchar Y[IMAGE_WIDTH * IMAGE_HEIGHT];
uchar Cb[IMAGE_WIDTH * IMAGE_HEIGHT];
uchar Cr[IMAGE_WIDTH * IMAGE_HEIGHT];
uchar R[IMAGE_WIDTH * IMAGE_HEIGHT];
uchar G[IMAGE_WIDTH * IMAGE_HEIGHT];
uchar B[IMAGE_WIDTH * IMAGE_HEIGHT];
printf("Capture size : %d\n",len_buf);
// mmm... swap byte order
/*
for(i=0;i<len_buf;i+=2)
{
d1 = (char *)(p_buf) + i;
d2 = (char *)(p_buf) + i + 1;
buf = *d1;
*d1 = *d2;
*d2 = buf;
}
*/
//The format must be Y0CbY1Cr
d1 = (uchar *)(p_buf);
int index = 0;
for(height = 0; height < IMAGE_HEIGHT; height++){
for(width = 0; width < IMAGE_WIDTH; width += 2){
Y[index] = *d1;
d1++;
Cb[index] = *d1;
index++;
Cb[index] = *d1;
d1++;
Y[index] = *d1;
d1++;
Cr[index-1] = *d1;
Cr[index] = *d1;
d1++;
index++;
}
}
int imageSize = IMAGE_WIDTH*IMAGE_HEIGHT;
for(index = 0; index < imageSize; index++){
double U = Cb[index] - 128;
double V = Cr[index] - 128;
R[index] = (uchar)(Y[index] + 1.402*V);
G[index] = (uchar)(Y[index] - 0.344*U -0.714*V);
B[index] = (uchar)(Y[index] + 1.772*U);
//printf("%d %d %d\n", R[index], G[index], B[index]);
}
saveBitmapFile(OUTFILE_NAME, R, G, B, IMAGE_WIDTH, IMAGE_HEIGHT);
}
static int read_frame(int count)
{
struct v4l2_buffer buf;
CLEAR(buf);
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;
if(-1 == xioctl(fd, VIDIOC_DQBUF, &buf))
{
switch(errno)
{
case EAGAIN:
return 0;
case EIO:
/* Could ignore EIO, see spec. */
/* fall through */
default:
errno_exit("VIDIOC_DQBUF");
}
}
assert(buf.index < n_buffers);
if(count == 0)
process_image(buffers[buf.index].start,buffers[buf.index].length);
if(-1 == xioctl(fd, VIDIOC_QBUF, &buf))
errno_exit("VIDIOC_QBUF");
return 1;
}
static void mainloop(void)
{
unsigned int count;
count = COUNT_IGNORE;
while(count-- > 0)
{
for(;;)
{
fd_set fds;
struct timeval tv;
int r;
FD_ZERO(&fds);
FD_SET(fd, &fds);
/* Timeout. */
tv.tv_sec = 2;
tv.tv_usec = 0;
r = select(fd + 1, &fds, NULL, NULL, &tv);
if(-1 == r)
{
if(EINTR == errno)
continue;
errno_exit("select");
}
if(0 == r)
{
fprintf(stderr, "select timeout\n");
exit(EXIT_FAILURE);
}
if(read_frame(count))
break;
/* EAGAIN - continue select loop. */
}
}
}
static void stop_capturing (void)
{
enum v4l2_buf_type type;
type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if(-1 == xioctl(fd, VIDIOC_STREAMOFF, &type))
errno_exit("VIDIOC_STREAMOFF");
}
static void start_capturing(void)
{
unsigned int i;
enum v4l2_buf_type type;
for(i = 0; i < n_buffers; ++i)
{
struct v4l2_buffer buf;
CLEAR(buf);
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;
buf.index = i;
if(-1 == xioctl(fd, VIDIOC_QBUF, &buf))
errno_exit("VIDIOC_QBUF");
}
type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if(-1 == xioctl(fd, VIDIOC_STREAMON, &type))
errno_exit("VIDIOC_STREAMON");
}
static void uninit_device(void)
{
unsigned int i;
for(i = 0; i < n_buffers; ++i)
if(-1 == munmap(buffers[i].start, buffers[i].length))
errno_exit("munmap");
free(buffers);
}
static void init_mmap(void)
{
struct v4l2_requestbuffers req;
CLEAR(req);
req.count = 8;
req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
req.memory = V4L2_MEMORY_MMAP;
if(-1 == xioctl(fd, VIDIOC_REQBUFS, &req))
{
if(EINVAL == errno)
{
fprintf(stderr, "%s does not support "
"memory mapping\n", dev_name);
exit(EXIT_FAILURE);
}
else
{
errno_exit("VIDIOC_REQBUFS");
}
}
if(req.count < 2)
{
fprintf(stderr, "Insufficient buffer memory on %s\n",
dev_name);
exit(EXIT_FAILURE);
}
buffers = calloc(req.count, sizeof(*buffers));
if(!buffers)
{
fprintf(stderr, "Out of memory\n");
exit(EXIT_FAILURE);
}
for(n_buffers = 0; n_buffers < req.count; ++n_buffers)
{
struct v4l2_buffer buf;
CLEAR(buf);
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;
buf.index = n_buffers;
if(-1 == xioctl(fd, VIDIOC_QUERYBUF, &buf))
errno_exit("VIDIOC_QUERYBUF");
// printf("Buffer length : %d : %d\n",n_buffers,buf.length);
buffers[n_buffers].length = buf.length;
buffers[n_buffers].start =
mmap(NULL /* start anywhere */,
buf.length,
PROT_READ | PROT_WRITE /* required */,
MAP_SHARED /* recommended */,
fd, buf.m.offset);
if(MAP_FAILED == buffers[n_buffers].start)
errno_exit("mmap");
}
}
static void init_device(void)
{
struct v4l2_capability cap;
struct v4l2_cropcap cropcap;
struct v4l2_crop crop;
struct v4l2_format fmt;
unsigned int min;
if(-1 == xioctl(fd, VIDIOC_QUERYCAP, &cap))
{
if(EINVAL == errno)
{
fprintf(stderr, "%s is no V4L2 device\n",dev_name);
exit(EXIT_FAILURE);
}
else
{
errno_exit("VIDIOC_QUERYCAP");
}
}
if(!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE))
{
fprintf(stderr, "%s is no video capture device\n",dev_name);
exit(EXIT_FAILURE);
}
if(!(cap.capabilities & V4L2_CAP_STREAMING))
{
fprintf(stderr, "%s does not support streaming i/o\n",
dev_name);
exit(EXIT_FAILURE);
}
/* Select video input, video standard and tune here. */
CLEAR(cropcap);
cropcap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if(0 == xioctl(fd, VIDIOC_CROPCAP, &cropcap))
{
crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
crop.c = cropcap.defrect; /* reset to default */
if(-1 == xioctl(fd, VIDIOC_S_CROP, &crop))
{
switch(errno)
{
case EINVAL:
/* Cropping not supported. */
break;
default:
/* Errors ignored. */
break;
}
}
}
else
{
/* Errors ignored. */
}
CLEAR(fmt);
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
fmt.fmt.pix.width = IMAGE_WIDTH;
fmt.fmt.pix.height = IMAGE_HEIGHT;
fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
//fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_RGB24;
fmt.fmt.pix.field = V4L2_FIELD_INTERLACED;
if(-1 == xioctl(fd, VIDIOC_S_FMT, &fmt)) errno_exit("VIDIOC_S_FMT");
/* Note VIDIOC_S_FMT may change width and height. */
/* Buggy driver paranoia. */
min = fmt.fmt.pix.width * 2;
//min = fmt.fmt.pix.width * 3;
if(fmt.fmt.pix.bytesperline < min) fmt.fmt.pix.bytesperline = min;
min = fmt.fmt.pix.bytesperline * fmt.fmt.pix.height;
if(fmt.fmt.pix.sizeimage < min) fmt.fmt.pix.sizeimage = min;
printf("%d\n", fmt.fmt.pix.sizeimage);
init_mmap();
}
static void close_device(void)
{
if(-1 == close(fd)) errno_exit("close");
fd = -1;
}
static void open_device(void)
{
struct stat st;
if(-1 == stat(dev_name, &st))
{
fprintf(stderr, "Cannot identify '%s': %d, %s\n",
dev_name, errno, strerror(errno));
exit(EXIT_FAILURE);
}
if(!S_ISCHR(st.st_mode))
{
fprintf(stderr, "%s is no device\n", dev_name);
exit(EXIT_FAILURE);
}
fd = open(dev_name, O_RDWR /* required */ | O_NONBLOCK, 0);
if(-1 == fd)
{
fprintf(stderr, "Cannot open '%s': %d, %s\n",
dev_name, errno, strerror(errno));
exit(EXIT_FAILURE);
}
}
/*
int main (int argc,char **argv)
{
dev_name = "/dev/video0";
open_device();
init_device();
start_capturing();
mainloop();
stop_capturing();
uninit_device();
close_device();
exit(EXIT_SUCCESS);
return 0;
}
*/
void captureBM(void)
{
open_device();
init_device();
start_capturing();
mainloop();
stop_capturing();
uninit_device();
close_device();
}



Comments