本篇转自博友https://blog.csdn.net/xuyangwyw/article/details/40476653文章,感谢分享。

废话不多说,直接开始流程。

1、驱动支持
    在那位法国牙医的无私奉献下,Linux内核几乎支持所有的USB摄像头,不过要想自己的Linux内核支持USB免驱摄像头,还需要先配置内核,

Device Drivers  --->
  <*> Multimedia support  --->
  <*>   Video For Linux
  [ ]   Enable Video For Linux API 1 (DEPRECATED)
  [*]   Video capture adapters  ---> 
      [*]   V4L USB devices  ---> 
      <*>   USB Video Class (UVC) 
  [*]     UVC input events device support

这样在板子上插入摄像头后终端就会有显示:

[root@farsight /]# usb 1-1.1: new full speed USB device using s3c2410-ohci and a ddress 4
uvcvideo: Found UVC 1.00 device Webcam C110 (046d:0829)
input: Webcam C110 as /class/input/input2

同时输入命令:lsusb 也会有相应信息,在此不就不详细展开了,网上有很多资料。最主要的是此时进入/dev 目录下,ls 会新增加一个设备,我的是video0,不同情况下需自己确认,这个设备名很重要。至此,Linux内核对摄像头的驱动支持就没问题了。

2、开始操作摄像头
    经典操作v4l2的方法一共也就那么步,大致为:打开设备->查看设备功能->设置图片格式->申请帧缓冲->内存映射->帧缓冲入列->开始采集->读数据(包括处理数据)->帧缓冲重新入列->关闭设备。看着名字挺霸气的,其实每一步都是调用内核驱动提供的出来的接口就可以了。

2.1 打开设备

 fd = open(dev_name, O_RDWR, 0 );//打开设备文件,阻塞模式if (fd < 0){perror("open /dev/video0  fialed! ");return -1;}

打开一个open就OK了,注意此处用的是阻塞模式,如果是非阻塞模式(O_NONBLOCK)的话,即使摄像头尚未捕获到信息,驱动依旧会把缓存(DQBUFF)里的东西返回给应用程序,感觉这样有点不合理,也懂内核为何要这样设计。

2.2  查看设备功能

    struct v4l2_capability cap;ret = ioctl(fd, VIDIOC_QUERYCAP, &cap);//查看设备功能if (ret < 0){perror("requre VIDIOC_QUERYCAP fialed! \n");return -1;}printf("driver:%s\n",cap.driver);printf("card:%s\n",cap.card);printf("bus_info:%s\n",cap.bus_info);printf("version:%d\n",cap.version);printf("capabilities:%x\n",cap.capabilities);if ((cap.capabilities & V4L2_CAP_VIDEO_CAPTURE) == V4L2_CAP_VIDEO_CAPTURE){printf("Device %s: supports capture.\n",dev_name);}if ((cap.capabilities & V4L2_CAP_STREAMING) == V4L2_CAP_STREAMING){printf("Device %s: supports streaming.\n",dev_name);}

查看设备功能也没什么好说的,看代码就OK啦。

2.3  设置图片格式

struct v4l2_format fmt;fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;fmt.fmt.pix.width = WIDTH;fmt.fmt.pix.height = HEIGHT;fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;fmt.fmt.pix.field = V4L2_FIELD_INTERLACED;if(-1 == ioctl(fd, VIDIOC_S_FMT, &fmt)){//设置图片格式perror("set format failed!");return -1;}if(-1 == ioctl(fd, VIDIOC_G_FMT, &fmt)){//得到图片格式perror("set format failed!");return -1;}printf("fmt.type:\t\t%d\n",fmt.type);printf("pix.pixelformat:\t%c%c%c%c\n", \fmt.fmt.pix.pixelformat & 0xFF,\(fmt.fmt.pix.pixelformat >> 8) & 0xFF, \(fmt.fmt.pix.pixelformat >> 16) & 0xFF,\(fmt.fmt.pix.pixelformat >> 24) & 0xFF);printf("pix.width:\t\t%d\n",fmt.fmt.pix.width);printf("pix.height:\t\t%d\n",fmt.fmt.pix.height);printf("pix.field:\t\t%d\n",fmt.fmt.pix.field);

也是一个命令就完成:VIDIOC_S_FMT,其中WIDTH,HEGHT 是定义的宏,后面很多地方都要用这两个参数,定义成宏比传参方便。V4L2_PIX_FMT_YUYV 指定输出格式为YUYV,关于YUYV,RGB等等什么什么格式,网上也有很详细的介绍,比如这篇:谈谈RGB、YUY2、YUYV、YVYU、UYVY、AYUV。
       当然,图片格式设置也不只这3个,还有像帧率什么的也是可以设置的。
       需要注意的是,对于不用的摄像头,内核有不一样的支持,并不是你设置了就一定能用,如果内核中该视频设备驱动不支持你所设定的图像格式,视频驱动会重新修改struct v4l2_format结构体变量的值为该视频设备所支持的图像格式,所以在程序设计中,设定完所有的视频格式后,要获取实际的视频格式,要重新读取 struct v4l2_format结构体变量。

2.3.1  查看图片格式

 if(-1 == ioctl(fd, VIDIOC_G_FMT, &fmt)){//得到图片格式perror("set format failed!");return -1;}printf("fmt.type:\t\t%d\n",fmt.type); printf("pix.pixelformat:\t%c%c%c%c\n", \fmt.fmt.pix.pixelformat & 0xFF,\(fmt.fmt.pix.pixelformat >> 8) & 0xFF, \(fmt.fmt.pix.pixelformat >> 16) & 0xFF,\(fmt.fmt.pix.pixelformat >> 24) & 0xFF);printf("pix.width:\t\t%d\n",fmt.fmt.pix.width);printf("pix.height:\t\t%d\n",fmt.fmt.pix.height);printf("pix.field:\t\t%d\n",fmt.fmt.pix.field); <span style="font-family: Arial, Helvetica, sans-serif; font-size: 12px;">    </span>

对于我们组的摄像头来说,设置的WIDTH=320,HEGHT=240,但是重新读取图片格式后,得到的却是 WIDTH=176,HEGHT=144,内核只支持这样,没办法......
2.4 申请帧缓冲

    req.count = 4;//申请缓冲数量req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;req.memory = V4L2_MEMORY_MMAP;ioctl(fd, VIDIOC_REQBUFS, &req);//申请缓冲if (req.count < 2){perror("buffer memory is Insufficient! \n");return -1;}

需要注意的是设定申请帧缓冲数量=4,但不一定就一定有4,一般要求帧缓冲数量大于2,因此有个判断。测试过最多能申请的数量为23。

2.5 映射用户空间

    yuyv_buffers0 = calloc(req.count, sizeof(*yuyv_buffers0));//内存中建立对应空间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 == ioctl(fd, VIDIOC_QUERYBUF, &buf)){//映射用户空间perror("VIDIOC_QUERYBUF error!\n");return -1;}yuyv_buffers0[n_buffers].length = buf.length;yuyv_buffers0[n_buffers].start =(char*) mmap(NULL, buf.length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, buf.m.offset);if (MAP_FAILED == yuyv_buffers0[n_buffers].start){close(fd);perror("mmap faild! \n");return -1;}printf("Frame buffer %d: address = 0x%x, length = %d \n",req.count, (unsigned int)yuyv_buffers0[n_buffers].start, yuyv_buffers0[n_buffers].length);}

2.6 申请到的缓冲进入队列

    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 == ioctl(fd, VIDIOC_QBUF, &buf)){close(fd);perror("VIDIOC_QBUF failed! \n");            return -1;}}      

2.7 开始捕捉图像数据

    type = V4L2_BUF_TYPE_VIDEO_CAPTURE;//开始捕捉图像数据if (-1 == ioctl(fd, VIDIOC_STREAMON, &type)){close(fd);perror("VIDIOC_STREAMON failed!  ");exit(-1);}   

函数执行成功后,摄像头开始采数据,一般来说可以用一个select判断一帧视频数据是否采集完成,当视频设备驱动完成一帧视频数据采集并保存到视频缓冲区中时,select函数返回,应用程序接着可以读取视频数据;否则select函数阻塞直到视频数据采集完成。

    enum v4l2_buf_type type;type = V4L2_BUF_TYPE_VIDEO_CAPTURE;fd_set fds;struct timeval tv;int r;FD_ZERO(&fds);//将指定文件描述符集清空FD_SET(fd, &fds);//在文件描述符集合中增加一个新的文件描述符tv.tv_sec = 2;//time outtv.tv_usec = 0;r = select(fd+1, &fds, NULL, NULL, &tv);//判断摄像头是否准备好,tv是定时if(-1 == r){if(EINTR == errno){printf("select erro! \n");}}else if(0 == r){printf("select timeout! \n");//超时return 1;//exit(EXIT_FAILURE);}read_frame(); //处理一帧数据

2.8 读数据

    file_fd = fopen(path1, "w");//yuyv图片if (file_fd < 0){perror("open test_mmap.jpg fialed! \n");exit(-1);        }CLEAR(buf);buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;buf.memory = V4L2_MEMORY_MMAP;ret = ioctl(fd, VIDIOC_DQBUF, &buf);//出列采集的帧缓冲,成功返回0//将摄像头采集得到的yuyv数据写入文件中if(ret == 0){ret = fwrite(yuyv_buffers0[buf.index].start, yuyv_buffers0[buf.index].length, 1,file_fd);}

这里有个帧缓冲出列的概念,也就是读取里面的数据并保存置文件中,不过保存的文件是yuyv图片,直接打开自然是不行的,需要用工具YUVViewer.exe,并且工具软件里面的参数配置也必须符合你设定的图片格式,这样才能看到真正的效果。到此为止,恭喜你,已经成功的迈出第一步了。是的,你没听错,才第一步,一张图片要直接在LCD上显示,要远程发送,是不能直接用yuyv数据的,还得经过一系列转换。
2.9 帧缓冲入列

    ret = ioctl(fd, VIDIOC_QBUF,&buf);//帧缓冲入列if(0 != ret){printf("VIDIOC_QBUF failed!\n");exit(-1);}

读取完帧缓冲里面的数据,别忘了将其入列,方便下次使用。
2.10 关闭设备

static void v4l2_close(void)
{int i=0;for(i=0; i<n_buffers; ++i){if(-1 == munmap(yuyv_buffers0[i].start, yuyv_buffers0[i].length)){printf("munmap error! \n");exit(-1);}}close(fd);exit(EXIT_SUCCESS);
}

关闭设备一个close就可以了,不过需要注意的是还有一个解除内存映射的工作需要完成。

3.1 yuyv 转RGB
      以上2.1 - 2.10步骤 只是完成一帧数据采集的过程,实际运用中我们需要的是摄像头不停的工作,同时还要对数据进行转换等工作,因此,2.1 - 2.7步只需初始化一次就OK,使用VIDIOC_STREAMON,开始采集数据后2.8,.29要不停地循环进行,最后才是2.10,关闭设备。
      数据的转换等处理主要集中在2.8步,读出数据后就进行相应的转换。
      首先是完成yuyv转RGB。

void yuyv_to_rgb(unsigned char* yuv,unsigned char* rgb)
{unsigned int i;unsigned char* y0 = yuv + 0;   unsigned char* u0 = yuv + 1;unsigned char* y1 = yuv + 2;unsigned char* v0 = yuv + 3;unsigned  char* r0 = rgb + 0;unsigned  char* g0 = rgb + 1;unsigned  char* b0 = rgb + 2;unsigned  char* r1 = rgb + 3;unsigned  char* g1 = rgb + 4;unsigned  char* b1 = rgb + 5;float rt0 = 0, gt0 = 0, bt0 = 0, rt1 = 0, gt1 = 0, bt1 = 0;for(i = 0; i <= (WIDTH * HEIGHT) / 2 ;i++){bt0 = 1.164 * (*y0 - 16) + 2.018 * (*u0 - 128); gt0 = 1.164 * (*y0 - 16) - 0.813 * (*v0 - 128) - 0.394 * (*u0 - 128); rt0 = 1.164 * (*y0 - 16) + 1.596 * (*v0 - 128); bt1 = 1.164 * (*y1 - 16) + 2.018 * (*u0 - 128); gt1 = 1.164 * (*y1 - 16) - 0.813 * (*v0 - 128) - 0.394 * (*u0 - 128); rt1 = 1.164 * (*y1 - 16) + 1.596 * (*v0 - 128); if(rt0 > 250)      rt0 = 255;if(rt0< 0)        rt0 = 0;    if(gt0 > 250)     gt0 = 255;if(gt0 < 0)    gt0 = 0;    if(bt0 > 250)    bt0 = 255;if(bt0 < 0)    bt0 = 0;    if(rt1 > 250)    rt1 = 255;if(rt1 < 0)    rt1 = 0;    if(gt1 > 250)    gt1 = 255;if(gt1 < 0)    gt1 = 0;    if(bt1 > 250)    bt1 = 255;if(bt1 < 0)    bt1 = 0;    *r0 = (unsigned char)rt0;*g0 = (unsigned char)gt0;*b0 = (unsigned char)bt0;*r1 = (unsigned char)rt1;*g1 = (unsigned char)gt1;*b1 = (unsigned char)bt1;yuv = yuv + 4;rgb = rgb + 6;if(yuv == NULL)break;y0 = yuv;u0 = yuv + 1;y1 = yuv + 2;v0 = yuv + 3;r0 = rgb + 0;g0 = rgb + 1;b0 = rgb + 2;r1 = rgb + 3;g1 = rgb + 4;b1 = rgb + 5;}
}

出列帧缓冲后就可以调用该函数,rgb 需要先开辟大小为WIDTH * HEIGTH *  3的空间,因为我们用的RGB是24位格式,3个字节分别代表一个像素点的R、G、B,根据公式转换就好了。
        这里解释一下为什么我们要定义:float rt0 = 0, gt0 = 0, bt0 = 0, rt1 = 0, gt1 = 0, bt1 = 0;  for 开始后的前6个公式是对2个像素点的转换,结果是浮点数,而且也会大于255,小于0,但是每个像素点的每一位范围又只有0到255, 因此紧跟着有6个if判断,做一个范围判定。网上有些程序是直接用unsigned char 型变量做变换计算,完了也有个判断,但是感觉这样的话因为unsigned char都直接带强制转换了,那么后面的if根本就不会执行,不过怎样选择大家可以自己考虑。同时我们的if判断还有个修正,颜色大于250的都直接置为255,这样出来的效果明显要好一点,但是不同的摄像头,结果肯定有不一样,请慎重考虑,多实践。
        说到这里就不得不吐槽一下我们组领的那个摄像头了,罗技的,小巧,效果还不错,但是,别人家的数据出来的颜色排列的是RGB,我们的出来的排列偏偏是BGR,BGR!!!还半天没反应过来!!!!!!!

3.2  RGB 转BMP

void rgb_to_bmp(unsigned char* pdata, FILE* bmp_fd)
{//分别为rgb数据,要保存的bmp文件名 int size = WIDTH * HEIGHT * 3 * sizeof(char); // 每个像素点3个字节  // 位图第一部分,文件信息  BMPFILEHEADER_T bfh;  bfh.bfType = (unsigned short)0x4d42;  //bm  bfh.bfSize = size  // data size  + sizeof( BMPFILEHEADER_T ) // first section size  + sizeof( BMPINFOHEADER_T ) // second section size  ;  bfh.bfReserved1 = 0; // reserved  bfh.bfReserved2 = 0; // reserved  bfh.bfOffBits = sizeof( BMPFILEHEADER_T )+ sizeof( BMPINFOHEADER_T );//真正的数据的位置
//    printf("bmp_head== %ld\n", bfh.bfOffBits); // 位图第二部分,数据信息  BMPINFOHEADER_T bih;  bih.biSize = sizeof(BMPINFOHEADER_T);  bih.biWidth = WIDTH;  bih.biHeight = -HEIGHT;//BMP图片从最后一个点开始扫描,显示时图片是倒着的,所以用-height,这样图片就正了  bih.biPlanes = 1;//为1,不用改  bih.biBitCount = 24;  bih.biCompression = 0;//不压缩  bih.biSizeImage = size;  bih.biXPelsPerMeter = 0;//像素每米  bih.biYPelsPerMeter = 0;  bih.biClrUsed = 0;//已用过的颜色,为0,与bitcount相同  bih.biClrImportant = 0;//每个像素都重要   fwrite( &bfh, 8, 1, bmp_fd); fwrite(&bfh.bfReserved2, sizeof(bfh.bfReserved2), 1, bmp_fd);  fwrite(&bfh.bfOffBits, sizeof(bfh.bfOffBits), 1, bmp_fd);  fwrite(&bih, sizeof(BMPINFOHEADER_T), 1, bmp_fd);  fwrite(pdata, size, 1, bmp_fd);
} 

说是转,其实就是一个另存为,只是加个格式头而已,需要注意的也只有size, biWidth, biHeight 这几个参数而已,如果你发现你生成的BMP图是倒立的,改下bih.biHeight = -HEIGHT就OK了。
         需要注意的是对于我们组来说保存位图只是一个测试方法而已,因为这个时候LCD模块还未准备好,保存位图就可以直接在windows上查看结果了,也不需要再用YUVViewer.exe去查看,很方便。但是由于文件操作比较耗时,每读取一帧数据就要操作一次文件的话,帧率下降也非常明显,加上保存位图对于我们这个项目来说明显没什么必要,因此后期的程序中rgb_to_bmp()函数的调用是注释掉了的 ,特此声明,免得挨砖头。

3.3 RGB放缩算法
      前面说过,我们的摄像头出来的图像分辨率只有176 * 144大小,内核自己优化成这样,花了一下午时间研究内核中的有关v4l2分辨率设置的源码,奈何自己太菜了,岂是源码的对手?还是老老实实用软件实现图片放大吧。
      图像放缩算法有很多种,各有优点,也难免有各自的缺点,要选择最合适的,实践才是好办法。我们组使用的是最临近插值算法。关于图像放缩算法的讨论,这里又有一篇资料:图像放大算法,这位博主好像也是转载的,那谁原创的呢?

void rgb_stretch(char* src_buf, char* dest_buf, int des_width, int des_hight)
{//最临近插值算法//双线性内插值算法放大后马赛克很严重 而且帧率下降严重printf("des_width = %d, des_hight = %d \n ",des_width, des_hight);double rate_w = (double) WIDTH / des_width;//横向放大比double rate_h = (double) HEIGHT / des_hight;//轴向放大比int dest_line_size = ((des_width * BITCOUNT +31) / 32) * 4;    int src_line_size = BITCOUNT * WIDTH / 8;int i = 0, j = 0, k = 0;for (i = 0; i < des_hight; i++)//desH 目标高度{//选取最邻近的点int t_src_h = (int)(rate_h * i + 0.5);//rateH (double)srcH / desH;for (j = 0; j < des_width; j++)//desW 目标宽度{int t_src_w = (int)(rate_w * j + 0.5);                            memcpy(&dest_buf[i * dest_line_size] + j * BITCOUNT / 8, \&src_buf[t_src_h * src_line_size] + t_src_w * BITCOUNT / 8,\BITCOUNT / 8);            }}
}

也尝试了双线性内插值算法,从原理上分析,应该是双线性内插值算法效果更好,结果却不是这样,为什么是这样,我这种菜鸟也弄不明白......
      放大后的数据就适合320 * 240 的LCD屏了,LCD显示的话,直接将RGB数据放入LCD的帧缓冲就OK了,这里也不详说,毕竟是另一位组员的劳动成果嘛。有了Linux系统,就是方便啊,再也不用苦逼的去写裸机驱动了。
        这里补充一个第2.8步的详细调用:

int numb = 0;
static int read_frame(char *rgb_buffers)
{struct v4l2_buffer buf;int ret =0;static char path1[30];static char path2[30];FILE *file_fd;//yuyv 图片文件流FILE *bmp_fd;//bmp 图片文件流 numb ++;sprintf(path1, "./test_mmap%d.jpg", numb);//文件名    sprintf(path2, "./image%d.bmp", numb);    printf("path1=%s,  path2=%s  %d\n", path1, path2, numb);file_fd = fopen(path1, "w");//yuyv图片if (file_fd < 0){perror("open test_mmap.jpg fialed! \n");exit(-1);        }bmp_fd = fopen(path2, "w");//bmp图片if (bmp_fd < 0){perror("open image.bmp failed!");    exit(-1);        }CLEAR(buf);buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;buf.memory = V4L2_MEMORY_MMAP;ret = ioctl(fd, VIDIOC_DQBUF, &buf);//出列采集的帧缓冲,成功返回0if(0 != ret){printf("VIDIOC_DQBUF failed!\n");exit(-1);}yuyv_to_rgb(yuyv_buffers0[buf.index].start, rgb_buffers);//yuyv -> rgb24rgb_stretch(rgb_buffers, dest_buffers, DEST_WIDTH, DEST_HEIGHT);//176 X 144 -> 320 X 240 rgb24_to_rgb565(dest_buffers, rgb565_buffers);//rgb24 -> rgb565ret = fwrite(yuyv_buffers0[buf.index].start, yuyv_buffers0[buf.index].length, 1, file_fd);//将摄像头采集得到的yuyv数据写入文件中if(ret <= 0){printf("write yuyv failed!\n");exit(-1);}rgb_to_bmp(rgb565_buffers, bmp_fd);//rgb -> bmpret = ioctl(fd, VIDIOC_QBUF,&buf);//帧缓冲入列if(0 != ret){printf("VIDIOC_QBUF failed!\n");exit(-1);}fclose(file_fd);fclose(bmp_fd);return 1;
}

这个函数,调用了以上3.1 - 3.3 列举出来的所有函数,当然了,是先放大还是先保存怎样怎样的,大家都可以自己调整。

3.4 RGB 转JPEG

如果本文以上所有部分对您来说,都没有任何价值,那既然都到这里了,或许可以继续翻翻?说不定接下来的内容您会很感兴趣哦!
        众说周知,jpeglib 是一个强大的jpeg类库,直接调用里面的一些接口函数,就能直接实现一些异常复杂的jpeg处理。但老版jpeglib 最不好的地方就是只支持文件流的输入输出,不支持从内存中解压或者压缩至内存中,实际工程中哪会处处都是文件操作???因此用起来挺麻烦的,也照着网上的资料,修改了jpeglib的源码,结果渣渣技术,一运行就报段错误,搞了2天,放弃了,请教老师,结果,一句代码就解决了我2天的麻烦。哪句代码?
       首先是编译源码和准备开发环境。
       1.感觉编译jpeglib ,准备环境也没有网上那些资料说得那么简单样,因此在此还是简单介绍下jpeglib的编译以及使用环境的准备。
       下载源码,注意最好是用新版的jpeglib-8b 版,因为老版本貌似是不支持内存中操作的(当然也没下载到老版源码,没实验,有兴趣的同学可以试试),这里有个链接,自己的资源:jpegsrc.v8b.tar.gz。
2.解压。
3.配置:
        ./configure CC=/opt/arm-cortex_a8/bin/arm-cortex_a8-linux-gnueabi-gcc LD=/opt/arm-cortex_a8/bin/arm-cortex_a8-linux-gnueabi-ld --host=arm-linux --prefix=/opt/arm-cortex_a8/arm-cortex_a8-linux-gnueabi  --exec-prefix=/opt/arm-cortex_a8/arm-cortex_a8-linux-gnueabi  --enable-shared  --enable-static 
       配置好后,make,没报错,就make install , 千万不能忘记make install  !!!
       make install 成功后会在我的/opt/arm-cortex_a8/arm-cortex_a8-linux-gnueabi/lib下生成5个库:libjpeg.a lalibjpeg.la libjpeg.so libjpeg.so.8 libjpeg.so.8.0.2 ,会在我的/opt/arm-cortex_a8/arm-cortex_a8-linux-gnueabi/include下生成4个头文件:jconfig.h jerror.h jmorecfg.h jpeglib.h, 这几个库和头文件是后面会用到。
       注:(1)针对ARM下使用的jpeglib,CC:交叉编译工具链,CC 后面接的路径就是我的交叉编译器的路径,你的在哪里自己才清楚啊;  LD:链接用,同上; --host:指定主机,得是arm-linux,网上有资料说是arm-unkown-linux, 实测不行;--prefix:生成的头文件存放目录;  --exec-prefix:生成的动态库静态库存放目录,这个很关键,必须得是arm-cortex_a8/arm-cortex_a8-linux-gnueabi;--enable-shared : 用GNU libtool编译成动态链接库 。想强调的一点是以上几个参数,大家最好都配置上,注意严格检查路径,"="前后不要留空格,网上有些资料只说了要配置CC,LD,没说配置host,结果自然还是使用不了。
               (2)如果ARM板子是挂载的根文件系统,那还必须把那5个库拷贝到rootfs/lib 目录下。
               (3)如果你是要在PC机上使用jpeglib 库的话,那么只需把生成库的路径改为/lib 或者 /usr/lib 就行了。
         4.所谓的准备开发环境,一是上面刚刚说的在正确的位置下准备好那5个库,二是还需要把那4个头文件放到你的工程目录下,这样基本上就没问题了。
        心里没底的同学可以先测试下jpeglib库能否使用,只需在一个最简单的.C中,包含<jpeglib.h>,编译时加上-ljpeg ,编译(这个编译器一定得是你上面配置的CC后面跟的那编译器哦!)不报错,那就是真正的没问题了。反之,报XXXXlib 找不到,XXXX.h 找不到,那就得好好检查上面几步了。
库准备好了就可以直接用了:

long rgb_to_jpeg(const char *rgb, char *jpeg)
{long jpeg_size;struct jpeg_compress_struct jcs;struct jpeg_error_mgr jem;JSAMPROW row_pointer[1];int row_stride;jcs.err = jpeg_std_error(&jem);jpeg_create_compress(&jcs);jpeg_mem_dest(&jcs, jpeg, &jpeg_size);//就是这个函数!!!!!!!jcs.image_width = WIDTH;jcs.image_height = HEIGHT;jcs.input_components = 3;//1;jcs.in_color_space = JCS_RGB;//JCS_GRAYSCALE;jpeg_set_defaults(&jcs);jpeg_set_quality(&jcs, 180, TRUE);jpeg_start_compress(&jcs, TRUE);row_stride =jcs.image_width * 3;while(jcs.next_scanline < jcs.image_height){//对每一行进行压缩row_pointer[0] = &rgb[jcs.next_scanline * row_stride];(void)jpeg_write_scanlines(&jcs, row_pointer, 1);}jpeg_finish_compress(&jcs);jpeg_destroy_compress(&jcs);#ifdef JPEG    //jpeg 保存,测试用FILE *jpeg_fd;sprintf(path3, "./jpeg%d.jpg", numb);jpeg_fd = fopen(path3,"w");if(jpeg_fd < 0 ){perror("open jpeg.jpg failed!\n");exit(-1);}fwrite(jpeg, jpeg_size, 1, jpeg_fd);close(jpeg_fd);
#endif return jpeg_size;
}

上面的代码注释不是很详细,jpeglib 详细使用方法看这篇博客就OK:利用jpeglib压缩图像为jpg格式,想必大家也发现了,是的,如果你用的是jpeg_stdio_dest()函数,那么就是文件操作,最后压缩完成的结果直接保存在文件中,反之,如果你用的是jpeg_mem_dest()函数,那么压缩完成的结果就保存在内存中。
注:再次声明,我用的jpeglib 是-8b 版本,老版本是否支持jpeg_mem_dest() 函数,我没验证过..........

这里有一个以上功能的完整版程序,完整版与上面的代码片段有稍许不同,请注意。Linux 下V4l2摄像头采集图片,实现yuyv转RGB,RGB转BMP,RGB伸缩,RGB转JPEG(保存到内存中),JPEG经UDP发送功能,或者您也可以直接到我的资源中去下载。

到此为止,大功告成!!!

V4L2图像采集+图片格式转换(YUYV、RGB、JPEG)相关推荐

  1. java 图片互转_JAVA 图片格式转换 jpg、jpeg、png格式互转

    最近项目遇到一些图片格式的问题,于是就写下这边博客,帮助记忆,代码如下: public static void main(String[] args) { //原文件路径 String src = & ...

  2. 【图片格式转换】python实现批量图片格式转换:emf、jpeg等转为png

    [图片格式转换]python批量进行图片格式转换emf.jpeg等转为png 文章目录 [图片格式转换]python批量进行图片格式转换emf.jpeg等转为png 1. 代码 2. 效果 3. 总结 ...

  3. Python图片格式转换,图片压缩

    PyQt 图片格式转换,图片压缩 界面展示 # -*- coding: utf-8 -*- import osfrom PIL import Image from PIL.ImageFile impo ...

  4. python图片保存jpg、show变成bmp_Python 图片格式转换

    图片格式转换可以利用各种软件 今天介绍一下如何使用 Python 实现各种图片格式的转换 1. SVG 转其他格式 读取 SVG 格式文件,需要安装 svglib 库 pip install svgl ...

  5. 利用Python实现自动批量图片格式转换

    文章目录 利用Python实现自动批量图片格式转换 0.背景 1.简单思路 2.进一步改进 3.最终版本 4.总结 利用Python实现自动批量图片格式转换 0.背景 在一个漫画网站下载了一些图片,但 ...

  6. Pillow图片格式转换

    Pillow图片格式转换 Pillow 库支持多种图片格式,您可以直接使用 open() 方法来读取图片,并且无须考虑图片是何种类型.同时,Pillow 能够很轻松地实现图片格式之间的转换. 图片格式 ...

  7. ASP.NET2.0图片格式转换【月儿原创】

    ASP.NET2.0图片格式转换 作者:清清月儿 主页:http://blog.csdn.net/21aspnet/           时间:2007.4.20 说明:本文实现了 图片格式随意转换( ...

  8. cv2.cvtColor(img,p)图片格式转换的用法

    cv2.cvtColor(img,p) 功能:颜色空间转换 参数:img:需要转换的图片 p:转换成何种格式. p=cv2.COLOR_BGR2RGB :BGR格式转换成RGB格式 p=cv2.COL ...

  9. Winform中实现图片格式转换(附代码下载)

    场景 选择一张照片并选择保存位置和要转换的图片格式实现图片格式转换. 项目运行效果 注: 博客主页: https://blog.csdn.net/badao_liumang_qizhi 关注公众号 霸 ...

最新文章

  1. 大雁塔为什么七层_“大唐不夜城”广场旁边的千年古佛塔为何被叫“大雁塔”?...
  2. 解释上采样和PixelShuffle
  3. Mob统计分析数据模型理解
  4. Linux查看ice版本,Linux下ICE的安装
  5. python量化投资培训清华大学深研院_GitHub - CatsJuice/quantitative-investment-learning: 使用Python进行量化投资的学习报告...
  6. 使用redis的zset实现排行榜
  7. vb6 串口同时读取写入数据怎么避免冲突_实例:S7-200 SMART通过Modbus-RTU读取温湿度传感器数据...
  8. Silverlight 5 Beta新特性[5]隐式模板支持
  9. ShowWindowAsync系统方法cmdShow参数值含义
  10. [leetcode] Median of Two Sorted Arrays 寻找两个有序数组的中位数
  11. learning linux
  12. Oracle刷建表语句
  13. Atitit cache缓存之道 attilax 艾提拉总结 1. 使用缓存的目的(使用缓存) 1 1.1. 1、实现目标减少数据库的负担 1 1.2. 加快查询的速度 2 2. 缓存的常见参数 过
  14. java开发面试流程
  15. ZZULIOJ:1132: 数字字符统计
  16. 易图通: 路口三维实景导航面面观
  17. python爬取微博图片教程_Python爬取微博实例分析
  18. 基于STM32F103 HAL库 MB85RS128 驱动程序
  19. 综述论文要写英文摘要吗_英文论文的写作讲解 写好英文论文要注意这三点
  20. 伙伴系统之伙伴系统概述--Linux内存管理(十五)

热门文章

  1. 医学统计学中RR、OR和HR三个关于比值的概念
  2. 实例072计算工资 己知某公司员工的工资底薪为500元, 员工销售的软件金额与提成方式如下销售额<=2000 没有提成 2000<销售
  3. C# Speech学习笔记(一)
  4. CTF密码学--新手题--Normal_RSA--解题过程及总结
  5. ps—简易理解ps图层的各种混合模式
  6. 人工智能正在重塑生产方式、优化产业结构、提升生产效率、赋能千行百业
  7. 面试问题某个项目中遇到过什么问题
  8. 相机的针孔模型及其内参数,外参数的理解
  9. 【HISI系列】之HISI芯片码率控制使用说明
  10. excel操作系列之中文姓名转英文姓名