最近用的摄像头OV2640摄像头,支持RGB565、JPEG、YUV和YCbCr格式的输出,想要对采集的图像进行色块识别,这样一般需要用到RGB格式的输出会比较好处理,数字图像处理中常用的采用模型是RGB(红,绿,蓝)模型和HSV(色调,饱和度,亮度),RGB广泛应用于彩色监视器和彩色视频摄像机,我们平时的图片一般都是RGB模型。别人是RGB的LCD屏幕作为上位机,而我却没有LCD屏幕作为上位机,输出JPEG我可以存为.jpg为后缀的文件,输出RGB565的数据不好将图片保存下来,存RGB格式一般存为bmp位图,就想着既然板子都是作为客户端采集图像传出来,在服务器端将JPEG的数据转换成RGB数据在做色块识别的处理,就了解到libjpeg这个库,libjpeg是一款功能强大的开源jpeg图像库工具软件,jpeg是一个国际图像压缩标准,图像的后缀一般为jpeg或者jpg,利用libjpeg可以读取jpeg图像数据,libjpeg库是用于编码数据为jpeg格式或者解码jpeg格式图片的常用库。
既然传出来的数据是JPEG,这里需要做的就是JPEG的解码,下面简单的说说libjpeg库的使用:
一、libjpeg库的简单使用
1、设置出错处理函数并初始化编解码结构对象
在libjpeg库中,实现了默认错误处理函数,当错误发生时,比如如果内存不足(非常可能发生,后面会介绍)等,则默认错误处理函数将会调用exit函数结束整个进程,详细内容可以参考jerror.c文件。在C语言中没有C++的异常处理机制,但是提供了setjmp和longjmp机制来实现类似的功能,设置完出错处理就初始化编解码结构对象
编码:
/* Step 1: allocate and initialize JPEG compression object */
cinfo.err = jpeg_std_error(&jerr);
/* Now we can initialize the JPEG compression object. */
jpeg_create_compress(&cinfo);
解码:
/* We set up the normal JPEG error routines, then override error_exit. */
cinfo.err = jpeg_std_error(&jerr);
/* Now we can initialize the JPEG decompression object. */
jpeg_create_decompress(&cinfo);
2、初始化源数据
在libjpeg库中仅仅提供了文件作为输入数据的接口,在example.c中代码如下:jpeg_stdio_src(&cinfo, infile);
3、读取jpeg文件的头信息
调用jpeg_read_header(&cinfo, TRUE)读取jpeg文件头信息,这个和初始化解码对象一样,是必须要调用的,是约定
(void) jpeg_read_header(&cinfo, TRUE);
/* 源信息 */
printf("image_width = %d\n", cinfo.image_width);
printf("image_height = %d\n", cinfo.image_height);
printf("num_components = %d\n", cinfo.num_components);
4、设置解码参数
很多情况下,这步非常重要。比如设置输出格式,设置scale(缩放)等等功能都是在这一步设置。参数设置通过修改上步得到cinfo的值来实现。这里简单介绍一下一些常用的字段。out_color_space:输出的颜色格式,libjpeg定义如下:
typedef enum {
JCS_UNKNOWN, /* error/unspecified */
JCS_GRAYSCALE, /* monochrome */
JCS_RGB, /* red/green/blue */
JCS_YCbCr, /* Y/Cb/Cr (also known as YUV) */
JCS_CMYK, /* C/M/Y/K */
JCS_YCCK, /* Y/Cb/Cr/K */
} J_COLOR_SPACE;
不设置的话通常就是JCS_RGB,RGB888的格式,除此之外,还可以设置缩放大小等,scale_num,scale_denom:因为实际的显示设备千变万化,我们可能需要根据实际情况对输出数据进行一些缩放才能够显示。libjpeg支持对输出数据进行缩放(scale),这个变量就是用来设置缩放的参数。
mem:可以指定内存管理相关的内容,比如分配和释放内存,指定libjpeg可以使用的最大内存。默认情况下不同的平台下面都有一个libjpeg默认最大可用内存值,比如Android平台上面该值为10000000L(10M),请参考jmemxxxx.c文件中的DEFAULT_MAX_MEM,了解不同平台的默认最大内存值。通过修改mem->pub.max_memory_to_use的值,库的使用者可以自定义libjpeg可以使用的最大内存值。
5、开始解码
经过前面的参数设置,接着调用jpeg_start_decompress(&cinfo);开始解码。
6、对解码出来的数据做处理
/* Step 6: while (scan lines remain to be read) */
/* jpeg_read_scanlines(...); */
while (cinfo.output_scanline < cinfo.output_height)
{
row_pointer[0] = & rdata[(cinfo.output_scanline)*cinfo.image_width*cinfo.num_components];
jpeg_read_scanlines(&cinfo,row_pointer ,1);
//(void) jpeg_read_scanlines(&cinfo, buffer, 1);
/* Assume put_scanline_someplace wants a pointer and sample count. */
//put_scanline_someplace(buffer[0], row_stride);
}
解码出来的数据是一行一行的,我这里读到rdata里面,这里根据个人需求改。
解码JPEG文件输出RGB888数据到out_buf
int read_JPEG_file (char * filename, unsigned char *out_buf,int size)
{
struct jpeg_decompress_struct cinfo;
struct jpeg_error_mgr jerr;
/* More stuff */
FILE *infile; /* source file */
int row_stride; /* physical row width in output buffer */
unsigned char *rdata;
JSAMPROW row_pointer[1];
int image_size = 0 ;
if ((infile = fopen(filename, "rb")) == NULL)
{
fprintf(stderr, "can't open %s\n", filename);
return -1;
}
/* Step 1: allocate and initialize JPEG decompression object */
/* We set up the normal JPEG error routines, then override error_exit. */
cinfo.err = jpeg_std_error(&jerr);
/* Now we can initialize the JPEG decompression object. */
jpeg_create_decompress(&cinfo);
/* Step 2: specify data source (eg, a file) */
jpeg_stdio_src(&cinfo, infile);
/* Step 3: read file parameters with jpeg_read_header() */
(void) jpeg_read_header(&cinfo, TRUE);
/* 源信息 */
printf("image_width = %d\n", cinfo.image_width);
printf("image