一、移植libjpeg库

库源码: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解码图片

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;

1.错误处理

使用 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;

2.创建解码对象

要使用 libjpeg 解码 jpeg 数据,需创建解码对象:

jpeg_create_decompress(&cinfo);

在创建解码对象之后,如果解码结束或者解码出错时,需要调用 jpeg_destroy_decompress 销毁/释放解码对象,否则将会内存泄漏

3.设置数据源

设置需要进行解码的 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 数据源还可以来自内存中、而不一定的是文件流。

4.读取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 图像的颜色空间

5.设置解码处理参数
 

在进行解码之前,可以对一些解码参数进行设置, 这些参数有一个默认值,调用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,就是保持原图大小
 

 6.开始解码

调用 jpeg_start_decompress()函数解码:

jpeg_start_decompress(&cinfo);

在完成解压缩操作后 ,会将解压后的图像信息填充至 cinfo 结构中。
 

7.读取数据

读取解码后的数据了, 数据是按照行读取的, 解码后的数据按照从左到右、 从上到下的顺序存储,每个像素点对应的各颜色或灰度通道数据是依次存储。

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。

8.解码结束

解码完毕之后调用 jpeg_finish_decompress()函数

jpeg_finish_decompress(&cinfo);

9.释放/销毁解码对象

当解码完成之后,需要调用 jpeg_destroy_decompress()函数销毁/释放解码对象

jpeg_destroy_decompress(&cinfo);

三、libjpeg编码图片

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐