zoukankan      html  css  js  c++  java
  • 通过摄像头设备采集一帧数据的例子程序(完整版)【转】

    转自:http://blog.csdn.net/chance_yin/article/details/8947138

    版权声明:本文为博主原创文章,未经博主允许不得转载。
    
    [cpp] view plain copy
    
        #include <stdio.h>  
        #include <stdlib.h>  
        #include <string.h>  
        #include <assert.h>  
          
        #include <getopt.h>             
          
        #include <fcntl.h>               
        #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>           
        #include <linux/videodev2.h>  
          
          
        #define VIDEO_WIDTH 640     
        #define VIDEO_HEIGHT 480    
        #define BUFFER_COUNT 4     
          
        #define CLEAR(x) memset (&(x), 0, sizeof (x))  
          
          
        struct buffer {  
            void *      start;      //记录缓冲帧地址  
            size_t      length;     //一帧的大小  
        };  
          
          
        //pic process---------------------------------------------------------------------------------------------------------------------------------------------------------------  
          
        #pragma pack(1)    
        typedef struct BITMAPFILEHEADER{  
              unsigned short bfType;//位图文件的类型,    
              unsigned long bfSize;//位图文件的大小,以字节为单位    
              unsigned short bfReserved1;//位图文件保留字,必须为0    
              unsigned short bfReserved2;//同上    
              unsigned long bfOffBits;//位图阵列的起始位置,以相对于位图文件   或者说是头的偏移量表示,以字节为单位    
        } BITMAPFILEHEADER;  
        #pragma pack()    
          
        typedef struct BITMAPINFOHEADER{//位图信息头类型的数据结构,用于说明位图的尺寸        
              unsigned long biSize;//位图信息头的长度,以字节为单位    
              unsigned long biWidth;//位图的宽度,以像素为单位    
              unsigned long biHeight;//位图的高度,以像素为单位    
              unsigned short biPlanes;//目标设备的级别,必须为1    
              unsigned short biBitCount;//每个像素所需的位数,必须是1(单色),4(16色),8(256色)或24(2^24色)之一    
              unsigned long biCompression;//位图的压缩类型,必须是0-不压缩,1-BI_RLE8压缩类型或2-BI_RLE4压缩类型之一    
              unsigned long biSizeImage;//位图大小,以字节为单位    
              unsigned long biXPelsPerMeter;//位图目标设备水平分辨率,以每米像素数为单位    
              unsigned long biYPelsPerMeter;//位图目标设备垂直分辨率,以每米像素数为单位    
              unsigned long biClrUsed;//位图实际使用的颜色表中的颜色变址数    
              unsigned long biClrImportant;//位图显示过程中被认为重要颜色的变址数    
        } BITMAPINFOHEADER;  
          
          
        void create_bmp_header(struct BITMAPFILEHEADER * bfh,struct BITMAPINFOHEADER * bih){  
              bfh->bfType = (unsigned short)0x4D42;  
              bfh->bfSize = (unsigned long)(14 + 40 + VIDEO_WIDTH * VIDEO_HEIGHT*3);  
              bfh->bfReserved1 = 0;  
              bfh->bfReserved2 = 0;  
              bfh->bfOffBits = (unsigned long)(14 + 40);  
          
          
              bih->biBitCount = 24;  
              bih->biWidth = VIDEO_WIDTH;  
              bih->biHeight = VIDEO_HEIGHT;  
              bih->biSizeImage = VIDEO_WIDTH * VIDEO_HEIGHT * 3;  
              bih->biClrImportant = 0;  
              bih->biClrUsed = 0;  
              bih->biCompression = 0;  
              bih->biPlanes = 1;  
              bih->biSize = 40;//sizeof(bih);    
              bih->biXPelsPerMeter = 0x00000ec4;  
              bih->biYPelsPerMeter = 0x00000ec4;  
          
             printf("----create bmp header successfully !----
    ");  
        }  
          
        void store_bmp(FILE * fd,char * file_name,unsigned char * newBuf,int bmp_len,struct BITMAPFILEHEADER * bfh,struct BITMAPINFOHEADER * bih){  
              
            fd = fopen(file_name, "wb");  
            if (fd < 0) {  
                    printf("open frame data file failed
    ");  
                    return;  
            }  
            fwrite(bfh,sizeof(*bfh),1,fd);  
            fwrite(bih,sizeof(*bih),1,fd);  
            fwrite(newBuf, 1, bmp_len, fd);  
            fclose(fd);  
            printf("----store bmp successfuylly ,bmp name is :  %s----
    ", file_name);  
            return;  
        }  
          
        /* 
        * YUYV 转 RGB 算法 
        */  
          
        void yuyv2rgb(unsigned int index, struct buffer * buffers,unsigned char * newBuf){  
            unsigned char YUYV[4],RGB[6];  
                int j,k,i;  
            unsigned int location  = 0;  
            unsigned char * starter;  
              
            starter                = (unsigned char *)buffers[index].start;  /* starter 代表当前数据帧在用户空间的首地址*/  
            j=0;  
                for(i=0;i < buffers[index].length;i+=4){  
                YUYV[0]=starter[i];//Y0    
                YUYV[1]=starter[i+1];//U    
                YUYV[2]=starter[i+2];//Y1    
                YUYV[3]=starter[i+3];//V    
                if(YUYV[0]<1){     
                    RGB[0]=0;  
                    RGB[1]=0;  
                    RGB[2]=0;  
                }else{  
                    RGB[0]=YUYV[0]+1.772*(YUYV[1]-128);//b    
                    RGB[1]=YUYV[0]-0.34413*(YUYV[1]-128)-0.71414*(YUYV[3]-128);//g    
                    RGB[2]=YUYV[0]+1.402*(YUYV[3]-128);//r    
                }     
                if(YUYV[2]<0){     
                    RGB[3]=0;  
                    RGB[4]=0;  
                    RGB[5]=0;  
                }else{  
                    RGB[3]=YUYV[2]+1.772*(YUYV[1]-128);//b    
                    RGB[4]=YUYV[2]-0.34413*(YUYV[1]-128)-0.71414*(YUYV[3]-128);//g    
                    RGB[5]=YUYV[2]+1.402*(YUYV[3]-128);//r    
                }  
                for(k=0;k<6;k++){  
                    if(RGB[k]<0)  
                        RGB[k]=0;  
                    if(RGB[k]>255)  
                        RGB[k]=255;  
                }         
                //请记住:扫描行在位图文件中是反向存储的!    
                if(j%(VIDEO_WIDTH*3)==0){//定位存储位置  
                    location=(VIDEO_HEIGHT-j/(VIDEO_WIDTH*3))*(VIDEO_WIDTH*3);  
                }  
                bcopy(RGB,newBuf+location+(j%(VIDEO_WIDTH*3)),sizeof(RGB));  
                j+=6;  
                }  
            printf("---- yuyv 2 rgb CHANGE DONE ! ----");  
            return;  
        }  
          
        /* 
         *去噪算法 
        */  
        void move_noise(unsigned char * newBuf){//双滤波器    
            int i,j,k,temp[3],temp1[3];  
            unsigned char BGR[13*3];  
            unsigned int sq,sq1,loc,loc1;  
            int h=VIDEO_HEIGHT,w=VIDEO_WIDTH;  
          
            for(i=2;i<h-2;i++){  
                for(j=2;j<w-2;j++){  
                    memcpy(BGR,newBuf+(i-1)*w*3+3*(j-1),9);  
                    memcpy(BGR+9,newBuf+i*w*3+3*(j-1),9);  
                    memcpy(BGR+18,newBuf+(i+1)*w*3+3*(j-1),9);  
                    memcpy(BGR+27,newBuf+(i-2)*w*3+3*j,3);  
                    memcpy(BGR+30,newBuf+(i+2)*w*3+3*j,3);  
                    memcpy(BGR+33,newBuf+i*w*3+3*(j-2),3);  
                    memcpy(BGR+36,newBuf+i*w*3+3*(j+2),3);  
                      
                    memset(temp,0,4*3);  
                    for(k=0;k<9;k++){  
                        temp[0]+=BGR[k*3];  
                        temp[1]+=BGR[k*3+1];  
                        temp[2]+=BGR[k*3+2];  
                    }  
          
                    temp1[0]=temp[0];  
                    temp1[1]=temp[1];  
                    temp1[2]=temp[2];  
                      
                    for(k=9;k<13;k++){  
                        temp1[0]+=BGR[k*3];  
                        temp1[1]+=BGR[k*3+1];  
                        temp1[2]+=BGR[k*3+2];  
                    }  
          
                    for(k=0;k<3;k++){  
                        temp[k]/=9;  
                        temp1[k]/=13;  
                    }  
              
                    sq=0xffffffff;loc=0;  
                    sq1=0xffffffff;loc1=0;  
                    unsigned int a;  
                      
                    for(k=0;k<9;k++){  
                        a=abs(temp[0]-BGR[k*3])+abs(temp[1]-BGR[k*3+1])+abs(temp[2]-BGR[k*3+2]);  
                        if(a<sq){  
                            sq=a;  
                            loc=k;  
                        }  
                    }  
                    for(k=0;k<13;k++){  
                        a=abs(temp1[0]-BGR[k*3])+abs(temp1[1]-BGR[k*3+1])+abs(temp1[2]-BGR[k*3+2]);  
                        if(a<sq1){  
                            sq1=a;  
                            loc1=k;  
                        }  
                    }  
                    newBuf[i*w*3+3*j]=(unsigned char)((BGR[3*loc]+BGR[3*loc1])/2);  
                            newBuf[i*w*3+3*j+1]=(unsigned char)((BGR[3*loc+1]+BGR[3*loc1+1])/2);  
                                newBuf[i*w*3+3*j+2]=(unsigned char)((BGR[3*loc+2]+BGR[3*loc1+2])/2);  
                             /*还是有些许的噪点   
                    temp[0]=(BGR[3*loc]+BGR[3*loc1])/2;   
                        temp[1]=(BGR[3*loc+1]+BGR[3*loc1+1])/2;   
                        temp[2]=(BGR[3*loc+2]+BGR[3*loc1+2])/2;   
                        sq=abs(temp[0]-BGR[loc*3])+abs(temp[1]-BGR[loc*3+1])+abs(temp[2]-BGR[loc*3+2]);   
                        sq1=abs(temp[0]-BGR[loc1*3])+abs(temp[1]-BGR[loc1*3+1])+abs(temp[2]-BGR[loc1*3+2]);   
                        if(sq1<sq) loc=loc1;   
                        newBuf[i*w*3+3*j]=BGR[3*loc];   
                        newBuf[i*w*3+3*j+1]=BGR[3*loc+1];   
                        newBuf[i*w*3+3*j+2]=BGR[3*loc+2];*/  
                }  
                }  
            printf("----move noise successfully---- !
    ");  
                return;  
        }  
          
        //end of pic process------------------------------------------------------------------------------------------------------------------------------------------------------  
          
          
        //v4l2 -------------------------------------------------------------------------------------------------------------------------------------------------------------------  
          
        /* 
        查询设备能力,v4l2 规定这一步是必须(暂时这里不指定特别的功能) */  
        void do_cap(int fd,struct v4l2_capability * cap ){  
          if( ioctl(fd,VIDIOC_QUERYCAP,cap) != -1 ){  
            printf("  1:  step1:device capbility checed succssfully
    ");  
            return;  
          }  
          exit(EXIT_FAILURE);  
        }  
          
          
        /* 
        规定设备帧捕获格式的信息 
        */  
        void init_fmt(struct  v4l2_format  * fmt){  
          CLEAR(*fmt);  
          fmt->type                     = V4L2_BUF_TYPE_VIDEO_CAPTURE;  
          fmt->fmt.pix.width            = VIDEO_WIDTH;  
          fmt->fmt.pix.height           = VIDEO_HEIGHT;  
          fmt->fmt.pix.pixelformat      = V4L2_PIX_FMT_YUYV;           
          fmt->fmt.pix.field            = V4L2_FIELD_INTERLACED;     
          return;  
        }  
        /* 
        设置设备帧捕获格式 
        */  
        void do_set_fmt(int fd,struct  v4l2_format  * fmt){  
            if(ioctl(fd,VIDIOC_S_FMT,fmt) != -1){  
               printf("  2:  Set devic format  succssfully
    ");  
               return;  
            }  
            exit(EXIT_FAILURE);  
        }  
          
        /* 
        初始化缓冲帧请求表单 
        */  
        void init_req(struct v4l2_requestbuffers * req){;  
          CLEAR(*req);  
          req->count                   = BUFFER_COUNT;  
          req->type                    = V4L2_BUF_TYPE_VIDEO_CAPTURE;  
          req->memory                  = V4L2_MEMORY_MMAP;  
        }  
          
        /* 
         *发送命令申请缓冲帧 
        */  
        void do_reqBuf(int fd,struct v4l2_requestbuffers * req ){  
          if(ioctl(fd,VIDIOC_REQBUFS,req) != -1){  
               printf("  3:  request buffers successfully 
    ");  
               return;  
            }  
            exit(EXIT_FAILURE);  
        }  
          
        /* 
         *获得驱动开辟的缓冲区的物理地址,并将此地址映射到用户空间可用的虚拟地址 
        */  
        void process_bufferAddr(int fd,int num, struct buffer * buffers){  
                  
            int i;  
          
                for (i = 0; i < num; ++i)  
                {  
                   struct v4l2_buffer buf;     
                   CLEAR (buf);  
                   buf.type        = V4L2_BUF_TYPE_VIDEO_CAPTURE;  
                   buf.memory      = V4L2_MEMORY_MMAP;  
                   buf.index       = i;            //记录当前帧是buffers中的第及帧    
          
                   if (-1 == ioctl (fd, VIDIOC_QUERYBUF, &buf)){ //获得缓冲帧的物理地址  
                        printf ("VIDIOC_QUERYBUF error
    ");  
                        exit(EXIT_FAILURE);  
                   }  
                   buffers[i].length = buf.length;  
                
                   buffers[i].start =            //将内存中缓冲帧的物理地址映射为用户空间地址  
                   mmap (NULL,  
                        buf.length,   
                        PROT_READ | PROT_WRITE ,  
                        MAP_SHARED,  
                        fd, buf.m.offset);  
                   if (MAP_FAILED == buffers[i].start){  
                        printf ("mmap failed
    ");  
                exit(EXIT_FAILURE);  
               }       
                }//end of for  
              
               printf("  4:  address processed successfully! 
    ");  
               return;  
        }  
          
          
        /* 
         *操作内存中已开辟好空间的缓冲帧入队列,准备记录数据信息 
        */  
        void in_queue(int fd, int num){  
            int i;  
            for(i = 0; i < num; ++i){  
                struct v4l2_buffer buf;         //工作数据帧,用户填好index和type字段后,传给驱动,驱动根据这些信息去处理内存中实际存在的数据帧  
                CLEAR(buf);  
                buf.index     =  i;  
                buf.type      =  V4L2_BUF_TYPE_VIDEO_CAPTURE;  
                buf.memory    = V4L2_MEMORY_MMAP;  
                      
                    if(-1 == ioctl(fd, VIDIOC_QBUF,&buf)){  
                    printf("VIDIOC_QBUF error !
    ");  
                    exit(EXIT_FAILURE);  
                }  
            }  
              
                printf("  5:  in queue successfully !
    ");  
            return;  
        }  
          
          
        /* 
         *开始采集数据  
        */  
        void do_capture(int fd){  
            enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;  
            if( -1 == ioctl(fd,VIDIOC_STREAMON,&type)){  
                printf("VIDIOC_STREAMON error ! 
    ");  
                exit(EXIT_FAILURE);  
            }  
              
                printf("  6:  do capture  successfully !
    ");  
            return;  
        }  
          
        /* 
         *将数据帧存储为jpg图片 
        */  
          
        void save_asJpg(__u32 index, struct buffer * buffers){  
              
            /* 
                   采集到的图片相关的变量 
                */  
                FILE *           pic_fd;                    //图片文件描述符  
                unsigned long    pic_length;                        //文件大小  
                char *           pic_fileName        = "test.jpg";      //图像文件名  
          
            pic_fd = fopen(pic_fileName,"wb");  
            fwrite(buffers[index].start, buffers[index].length, 1,pic_fd);  
              
            fclose(pic_fd);                                         /*关闭文件*/  
              
                printf("----  save pic as jpg  successfully !----
    ");  
            return;  
        }  
          
        void save_asBmp(unsigned int index, struct buffer * buffers){  
          
            FILE *         bmp_fd;         /*新创建的bmp文件的文件描述副*/  
            char *         bmp_name =  "testBMP.bmp";  
          
            unsigned char * bmpBuf;        /*为了处理jpg 转 bmp ,需要在内存中开辟一段工作空间*/  
            int bmp_len;                   /*bmp 图片大小*/  
                  
            struct BITMAPFILEHEADER bfh;   /*bmp 文件结构体*/  
            struct BITMAPINFOHEADER bih;     
          
            bmp_len         =  buffers[index].length*3/2;  
                bmpBuf          =  calloc((unsigned int)bmp_len,sizeof(unsigned char));  
              
                if(!bmpBuf){  
                 printf("cannot assign the memory for bmp!
    ");  
                     exit(EXIT_FAILURE);  
                }  
          
            yuyv2rgb(index,buffers,bmpBuf);   /*将 buffers[index]代表的jpg格式的数据转换成bmp格式*/  
              
            move_noise(bmpBuf);  
              
            create_bmp_header(&bfh,&bih);  
            store_bmp(bmp_fd,bmp_name,bmpBuf,bmp_len,&bfh,&bih);  
              
        }  
          
        /* 
         * 将一帧放入数据缓冲队列 
        */  
        int put_oneFrame_inQue(int camera_fd,struct v4l2_buffer * buf){  
            ioctl(camera_fd,VIDIOC_QBUF,buf);  
            return buf->index;  
        }  
          
        /* 
        *此函数由monitor_queue 函数调用,用来从设备文件中读取视频数据 
        */  
        int read_frame (int camera_fd,struct buffer * buffers)  
        {  
            struct v4l2_buffer buf;  
            unsigned int i;  
          
            CLEAR (buf);  
            buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;  
            buf.memory = V4L2_MEMORY_MMAP;  
          
            ioctl (camera_fd, VIDIOC_DQBUF, &buf);       /*从数据缓冲队列中取出数据帧*/  
          
            //assert (buf.index < num);     
              
            save_asJpg(buf.index,buffers);              /* 将读取到的帧存为jpg格式的图片  */  
              
            save_asBmp(buf.index,buffers);  
          
            put_oneFrame_inQue(camera_fd,&buf);         /*重新把帧插入队列*/  
          
                printf("  7:  read frame  successfully !
    ");  
            return 1;  
        }  
          
          
        /* 
        *监听数据队列数据帧的状态,状态发生改变(说明采集到了数据),则调用read_frame函数返回数据帧 
        */  
        void monitor_queue(int fd,struct buffer * buffers){  
              
            while (1)   
                {  
                   fd_set fds;           //为select函数设置监听文件集,即:fds文件集合中的所有文件的状态将会被监听  
                   struct timeval tv;    //设置等待超时时间  
                   int ret;  
          
                   FD_ZERO (&fds);  
                   FD_SET (fd, &fds);  
          
                   /* Timeout. */  
                   tv.tv_sec = 2;  
                   tv.tv_usec = 0;  
          
                   ret = select (fd + 1, &fds, NULL, NULL, &tv);  
                    
                   //监听出错  
                   if (-1 == ret) {  
                     if (EINTR == errno)  
                             continue;  
                         printf ("select err
    ");  
                   }  
               //等待超时  
                   if (0 == ret) {  
                    fprintf (stderr, "select timeout
    ");  
                        exit (EXIT_FAILURE);  
                   }  
                   //当ret != -1 && != 0时,则ret的值表示状态改变的文件个数  
                   if (read_frame (fd,buffers))  
                        break;  
                }   
          
                printf("  8:  do capture  successfully !
    ");  
            return;  
        }  
          
          
          
        /* 
         *将mmap映射的地址空间还原 
        */  
          
        void release_mmap_buffer(int num,struct buffer * buffers){  
          
            int i;  
          
            for (i = 0; i < num; ++i){  
                if (-1 == munmap (buffers[i].start, buffers[i].length)){  
                    printf ("munmap error !
    ");  
                    exit(EXIT_FAILURE);  
                }  
            }  
            printf("  9:  mmap buffer released successfuly !
    ");  
            return;  
        }  
        /* 
        初始化程序的上下s,然后开始视频采s 
         */  
        void init_context(){  
            /* 
               摄像头设备相关变量  
                */  
            char *          camera_fileName      = "/dev/video0";   // linux下摄像头设备的文件名  
            int             camera_fd            = -1;              // 摄像头的文件描述符  
              
            /* 
                 视频采集相关变量               
                */  
            struct v4l2_capability   cap;                  //查询设备的能力  
                struct v4l2_format       fmt;                  //设置设备当前驱动的帧捕获格式  
                enum   v4l2_buf_type     type;                 //定义设备的采集方式  
            struct v4l2_requestbuffers req;  
              
                struct buffer *          buffers;              //向系统内存申请缓冲区,用于在内存中保存数据帧信息  
          
            /* 
             * 图片采集处理相关变量 
            */  
                unsigned char *          bmp_picBuf;  
            int              bmp_len;  
          
          
            /* 
                 启动采集工作 
               */  
          
            //以阻塞方式打开一个设备文件  
            camera_fd = open(camera_fileName,O_RDWR,0);    
                
                //1、v4l2 规定此步必须有  
                do_cap(camera_fd,&cap);  
                  
                //2、设置设备捕获数据格式  
                init_fmt(&fmt);  
                do_set_fmt(camera_fd,&fmt);  
                  
                //3、申请缓冲区  
                init_req(&req);  
                do_reqBuf(camera_fd,&req);  
                  
            //4、获得缓冲帧地址,并且将其映射到用户空间        
                buffers = calloc (req.count, sizeof (*buffers));   //系统为为应用程序分配缓冲区,并清零  
                process_bufferAddr(camera_fd,req.count,buffers);   //处理地址问题,函数返回后,buffers中即可获得缓冲帧的虚拟地址(应用程序可操作)   
          
            //5、通知驱动把内存中的数据帧入队,准备记录数据信息  
            in_queue(camera_fd,req.count);  
          
            //6、通知驱动开始视频采集  
            do_capture(camera_fd);    
              
            //7、监听数据队列,并从队列中取出数据帧  
            monitor_queue(camera_fd,buffers);  
          
            //8、图片处理  
          
          
          
                //9、清场  
            /* 解除地址映射 */  
            release_mmap_buffer(req.count,buffers);   
            /* 关闭设备文件*/  
                close(camera_fd);  
            /* 关闭图像文件*/  
        }  
          
        void main(){  
          init_context();  
        }  
    
    
    这里的例子程序摘自网络,为了方便像我一样的新手阅读,小人斗胆对每一行加了注释,错误难免,欢迎大家留言更正。我会在第一时间更新。光荣属于前辈和你们。 
  • 相关阅读:
    VRChat简易教程1-开发环境准备(SDK)
    BeatSaber节奏光剑插件开发官方教程2-简单的插件示例
    BeatSaber节奏光剑插件开发官方教程1-创建一个插件模板
    BeatSaber节奏光剑双手柄MR教程
    第四章 HTML与JavaScript
    第二章数据和判定
    蓝港在线笔试经验
    数据结构总复习(查找)
    数据结构总复习(3)
    数据结构总复习(2)
  • 原文地址:https://www.cnblogs.com/sky-heaven/p/6904193.html
Copyright © 2011-2022 走看看