库源码:http://www.ijg.org/files/
这里下载的是jpegsrc.v9b.tar.gz,点击该文件即可下载。然后拷贝到虚拟机上。
解压命令:
tar -xzf jpegsrc.v9b.tar.gz
解压成功之后会生成 jpeg-9b 文件夹,也就是 libjpeg 源码文件夹
进入改目录下,编译整个工程:
初始化交叉编译工具的环境:source /opt/fsl-imx-x11/4.1.15-2.1.0/environment-setup-cortexa7hf-neon-poky-linux-gnueabi
配置工程:./configure --host=arm-poky-linux-gnueabi --prefix=/home/用户名/tool/jpeg/
后面的路径是指点安装的位置,可自行选择编译工程:make
安装libjpeg: make install
安装成功后,进入目录可以看到如下几个文件夹:
bin 目录下包含一些测试工具;include 目录下包含头文件; lib 目录下包含动态链接库文件。
移植到开发板:进入到 libjpeg 安装目录下,将 bin 目录下的所有测试工具拷贝到开发板 Linux 系统/usr/bin 目录;将 lib目录下的所有库文件拷贝到开发板 Linux 系统/usr/lib 目录。
拷贝 lib 目录下的库文件时,需要注意符号链接的问题, 不能破坏原有的符号链接; 可以将 lib 目录下的所有文件打包成压缩包的形式。指令如下:
将 lib.tar.gz 压缩文件拷贝到开发板 Linux 的用户家目录下,在解压之前,若开发板中已经移植的 libjpeg 库删除,执行命令:rm -rf /usr/lib/libjpeg.*
将 lib.tar.gz 压缩文件解压到开发板 Linux 系统/usr/lib 目录下:tar -xfz lib.tar.gz -C /usr/lib
查看是否只要成功:djpeg --help 。djpeg 是编译 libjpeg 源码得到的测试工具(在 libjpeg 安装目录下的 lib 目录中) ,当执行命令之后,能够成功打印出这些信息就表示我们的移植成功。
然后再拷贝bin中文件,移植结束。
libjpeg 提供 JPEG 解码、 JPEG 编码和其他的 JPEG 功能的实现。我们可以使用libjpeg 提供的库函数对.jpg/.jpeg 进行解码(解压),得到 RGB 数据。
使用 libjpeg 库需要在我们的应用程序中包含它的头文件 jpeglib.h,该头文件包含了一些结构体
数据结构以及 API 接口的申明。
解码操作的过程:
(1)创建 jpeg 解码对象
(2)指定解码数据源(3)读取图像信息
(4)设置解码参数
(5)开始解码
(6)读取解码后的数据
(7)解码完毕
(8)释放/销魂解码对象
用 libjpeg 库解码 jpeg 数据的时候,最重要的一个数据结构为 struct jpeg_decompress_struct 结构体, 该数据结构记录着 jpeg 数据的详细信息, 也保存着解码之后输出数据的详细信息。 除此之外, 还需要定义一个用于处理错误的对象, 错误处理对象是一个 struct jpeg_error_mgr 结构
体变量。
定义了 JPEG 解码对象和错误处理对象
struct jpeg_decompress_struct cinfo;
struct jpeg_error_mgr jerr;
使用 libjpeg 库函数的时候难免会产生错误,所以在使用 libjpeg 解码之前,首先要做好错误处理。
在 libjpeg 库中,实现了默认错误处理函数,当错误发生时, 譬如如果内存不足、文件格式不对等, 则会 libjpeg实现的默认错误处理函数, 默认错误处理函数将会调用 exit()结束束整个进程;当然,可以修改错误处理的方式, libjpeg 提供了接口让用户可以注册一个自定义错误处理函数。
错误处理对象使用 struct jpeg_error_mgr 结构体描述。error_exit 函数指针指向了错误处理函数。使用 libjpeg 库函数 jpeg_std_error()会将 libjpeg 错误处理设置为默认处理方式。
//初始化错误处理对象、并将其与解压对象绑定
cinfo.err = jpeg_std_error(&jerr);
//修改默认的错误处理函数
void my_error_exit(struct jpeg_decompress_struct *cinfo)
{
/* ... */
}
cinfo.err.error_exit = my_error_exit;
要使用 libjpeg 解码 jpeg 数据,需创建解码对象:
jpeg_create_decompress(&cinfo);
在创建解码对象之后,如果解码结束或者解码出错时,需要调用 jpeg_destroy_decompress 销毁/释放解码对象,否则将会内存泄漏。
设置需要进行解码的 jpeg 文件,使用 jpeg_stdio_src()函数设置数据源:
FILE *jpeg_file = NULL;
//打开.jpeg/.jpg 图像文件
jpeg_file = fopen("./image.jpg", "r"); //只读方式打开
if (NULL == jpeg_file)
{
perror("fopen error");
return -1;
}
//指定图像文件
jpeg_stdio_src(&cinfo, jpeg_file);
待解码的 jpeg 文件使用标准 I/O 方式 fopen 将其打开。 除此之外, jpeg 数据源还可以来自内存中、而不一定的是文件流。
在解码之前,需要读取 jpeg文件的头部信息,以获取该文件的信息,这些获取到的信息会直接赋值给 cinfo 对象的某些成员变量。
jpeg_read_header(&cinfo, TRUE);
调用 jpeg_read_header()后,可以得到 jpeg 图像的一些信息,如 jpeg 图像的宽度、高度、 颜色通道数以及 colorspace 等,这些信息会赋值给 cinfo 对象中的相应成员变量。
cinfo.image_width cinfo.image_height cinfo.num_components cinfo.jpeg_color_space |
//jpeg 图像宽度 //jpeg 图像高度 //颜色通道数 //jpeg 图像的颜色空间 |
在进行解码之前,可以对一些解码参数进行设置, 这些参数有一个默认值,调用jpeg_read_header()函数后,这些参数被设置成相应的默认值。
直接对 cinfo 对象的成员变量进行修改即可:
输出的颜色(cinfo.out_color_space): 默认配置为 RGB 颜色,JCS_RGB
图像缩放操作(cinfo.scale_num 和 cinfo.scale_denom): libjpeg 可以设置解码出来的图像的大小,与原图的比例。使用 scale_num 和 scale_denom 两个参数,解出来的图像大小就是scale_num/scale_denom, JPEG 当前仅支持 1/1、 1/2、 1/4、 和 1/8 这几种缩小比例。 默认是 1/1,就是保持原图大小
调用 jpeg_start_decompress()函数解码:
jpeg_start_decompress(&cinfo);
在完成解压缩操作后 ,会将解压后的图像信息填充至 cinfo 结构中。
读取解码后的数据了, 数据是按照行读取的, 解码后的数据按照从左到右、 从上到下的顺序存储,每个像素点对应的各颜色或灰度通道数据是依次存储。
libjpeg 默认解码得到的图像数据是 BGR888 格式,即 R 颜色在低 8 位、而 B 颜色在高 8 位。
typedef struct bgr888_color { unsigned char red; unsigned char green; unsigned char blue; } __attribute__ ((packed)) bgr888_t;
每次读取一行数据, 计算每行数据需要的空间大小,比如 RGB 图像就是宽度× 3(24-bit RGB 真彩色一个像素 3 个字节) ,灰度图就是宽度× 1(一个像素 1 个字节)。
bgr888_t *line_buf = malloc(cinfo.output_width * cinfo.output_components);
分配了一个行缓冲区,它的大小为 cinfo.output_width * cinfo.output_components,输出图像的宽度乘上每一个像素的字节大小。 我们除了使用 malloc 分配缓冲区外,还可以使用 libjpeg 的内存管理器来分配缓冲区。
缓冲区分配好之后,可以调用 jpeg_read_scanlines()来读取数据, jpeg_read_scanlines()可以指定一次读多少行,但是目前该函数还只能支持一次只读 1 行
jpeg_read_scanlines(&cinfo, &buf, 1);
通过循环方式依次读取解码后的所有数据:
while(cinfo.output_scanline < cinfo.output_height)
{
jpeg_read_scanlines(&cinfo, buffer, 1);
//do something
}
cinfo.output_scanline 表示接下来要读取的行对应的索引值, 初始化为 0(表示第一行)、 1 表示第二行等,每读取一行数据,该变量就会加 1。
解码完毕之后调用 jpeg_finish_decompress()函数:
jpeg_finish_decompress(&cinfo);
当解码完成之后,需要调用 jpeg_destroy_decompress()函数销毁/释放解码对象:
jpeg_destroy_decompress(&cinfo);