libjpeg学习1:简单使用示例

libjpeg这个库主要用于处理jpeg数据,比如将RGB压缩成JPEG,或者将JPEG解压为RGB。其实早在4年前已经接触过,但一直没写过这方面的文章。后来想想还是有必要写出来,至少可以证明自己搞过这东西。

libjpeg使用十分简单,而且源码带有例子程序,下面的代码基本上就是该例子。所以没什么技术含量。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <setjmp.h>
#include <math.h>
#include <sys/time.h>
#include <time.h>

// jpeg库头文件必须放到stdio.h后面
#include "libjpeg/include/jpeglib.h"
#include "libjpeg/include/jerror.h"

typedef struct my_error_mgr * my_error_ptr;

void my_error_exit (j_common_ptr cinfo)
{
my_error_ptr myerr = (my_error_ptr) cinfo->err;

(*cinfo->err->output_message) (cinfo);

longjmp(myerr->setjmp_buffer, 1);
}

// 读取JPG图片数据,并解压到内存中,*rgb_buffer需要自行释放
int read_jpeg_file(const char* jpeg_file, unsigned char** rgb_buffer, int* size, int* width, int* height)
{
struct jpeg_decompress_struct cinfo;
struct my_error_mgr jerr;
FILE* fp;

JSAMPARRAY buffer;
int row_stride = 0;
unsigned char* tmp_buffer = NULL;
int rgb_size;

fp = fopen(jpeg_file, "rb");
if (fp == NULL)
{
printf("open file %s failed.\n", jpeg_file);
return -1;
}

cinfo.err = jpeg_std_error(&jerr.pub);
jerr.pub.error_exit = my_error_exit;

if (setjmp(jerr.setjmp_buffer))
{
jpeg_destroy_decompress(&cinfo);
fclose(fp);
return -1;
}

jpeg_create_decompress(&cinfo);

jpeg_stdio_src(&cinfo, fp);

jpeg_read_header(&cinfo, TRUE);

//cinfo.out_color_space = JCS_RGB; //JCS_YCbCr; // 设置输出格式

jpeg_start_decompress(&cinfo);

row_stride = cinfo.output_width * cinfo.output_components;
*width = cinfo.output_width;
*height = cinfo.output_height;

rgb_size = row_stride * cinfo.output_height; // 总大小
*size = rgb_size;

buffer = (*cinfo.mem->alloc_sarray)((j_common_ptr) &cinfo, JPOOL_IMAGE, row_stride, 1);

*rgb_buffer = (unsigned char *)malloc(sizeof(char) * rgb_size); // 分配总内存

printf("debug--:\nrgb_size: %d, size: %d w: %d h: %d row_stride: %d \n", rgb_size,
cinfo.image_width*cinfo.image_height*3,
cinfo.image_width,
cinfo.image_height,
row_stride);
tmp_buffer = *rgb_buffer;
while (cinfo.output_scanline < cinfo.output_height) // 解压每一行
{
jpeg_read_scanlines(&cinfo, buffer, 1);
// 复制到内存
memcpy(tmp_buffer, buffer[0], row_stride);
tmp_buffer += row_stride;
}

jpeg_finish_decompress(&cinfo);
jpeg_destroy_decompress(&cinfo);

fclose(fp);

return 0;
}

int write_jpeg_file(const char* jpeg_file, unsigned char* rgb_buffer, int width, int height, int quality)
{
struct jpeg_compress_struct cinfo;
struct jpeg_error_mgr jerr;
int row_stride = 0;
FILE* fp = NULL;
JSAMPROW row_pointer[1];

cinfo.err = jpeg_std_error(&jerr);

jpeg_create_compress(&cinfo);
fp = fopen(jpeg_file, "wb");
if (fp == NULL)
{
printf("open file %s failed.\n", jpeg_file);
return -1;
}
jpeg_stdio_dest(&cinfo, fp);
cinfo.image_width = width;
cinfo.image_height = height;
cinfo.input_components = 3;
cinfo.in_color_space = JCS_RGB;

jpeg_set_defaults(&cinfo);
jpeg_set_quality(&cinfo, quality, 1); // todo 1 == true
jpeg_start_compress(&cinfo, TRUE);
row_stride = width * cinfo.input_components;

while (cinfo.next_scanline < cinfo.image_height)
{
row_pointer[0] = &rgb_buffer[cinfo.next_scanline * row_stride];
jpeg_write_scanlines(&cinfo, row_pointer, 1);
}

jpeg_finish_compress(&cinfo);
jpeg_destroy_compress(&cinfo);
fclose(fp);

return 0;
}

实际上我是使用保存为bmp格式图片的方式进行测试的,bmp图片是RGB分量实际上应该是BGR,所以如果要正确保存bmp,则要进行R、B分量互换,在解压时,libjpeg可以设置输出颜色,使用cinfo.out_color_space,如果不想自己调换R、B,则将其赋值为JCS_EXT_BGR。另外,bmp文件头信息中的高值为负数,否则保存的图片是上下颠倒的。

李迟 2015.6.30 晚饭后