当前位置:首页 期刊杂志

基于JPEG 2000的Kakadu开源代码结构和移植性分析

时间:2024-05-04

孔祥焰 罗桂娥

摘 要:JPEG 2000是新一代的静态图像压缩标准,而Kakadu是JPEG 2000官方建议的实现代码之一,也是目前效率最高的JPEG 2000开源代码,然而Kakadu代码的复杂性和移植高难度性制约了JPEG 2000代码的实现。为了实现Kakadu代码在不同平台的移植,针对Kakadu代码结构的高复杂性,在分析Kakadu系统结构以及参考程序参数的基础上,以Kakadu标准的编码工程Kducompress对代码的具体结构进行剖析,阐明了代码运行效率高的原因。最后以KakaduV2.2.3移植到TI TMS320DM642为例,对代码进行移植性分析及实现,对可能需要的修改给出了方向,为代码在不同平台的实现进行了有益的探索。

关键词:JPEG 2000;Kakadu;代码分析;DM642

中图分类号:TP31111文献标识码:A

文章编号:1004-373X(2009)12-064-04

Analysis of Kakadu′s Structure and Portability Based on JPEG 2000

KONG Xiangyan,LUO Gui′e

(School of Information Science and Engineering,Central South University,Changsha,410083,China)

Abstract:JPEG 2000 is the next generation of static image compression standard.Kakadu is one of the JPEG 2000 implemental codes of which the official proposed,and is also the most efficient codes around the world.However the difficulty and complexity of Kakadu′s portability restricts code′s realization of JPE2000.In order to implement Kakadu′s portability on different platforms,aiming at the highly complexity of JPEG 2000 and its implemental codes-Kakadu,and a detailed analysis is given on Kakadu′s architecture,referential appreciation′s parameter and the code′s specific structure based on the standard coding project-Kducompress.An analysis on the reason why it has high efficiency is proposed.In the end,an analysis on portability and implementation of KakaduV2.2.3,so as to give an beneficial research on different platforms.All the analysis and implementation of portability based on TI TMS320DM642.

Keywords:JPEG 2000;Kakadu;code analysis;DM642

0 引 言

JPEG 2000与传统的JPEG标准有很大的不同[1],由于其卓越的图像压缩性能和很高的灵活性,在各个领域有着广阔的应用前景[2,3]。JPEG 2000的这些特性主要来源于小波变换[4]、比特平面编码和算术编码技术。由于这些技术的引入,JPEG 2000的算法复杂度也相应提高,且在一定程度上限制了JPEG 2000的应用[5]。

Kakadu作为JPEG 2000开源代码的“开山鼻祖”,其作者David Taubman正是JPEG 2000核心编码EBCOT[6]的发明者。由此可见,Kakadu在JPEG 2000实现代码中的地位。但由于Kakadu是使用标准C++ 编写的程序,并加入一些JPEG 2000标准所没有的特性,因此其代码复杂性非常高[7],而且标准C++并不像C语言那样受到各嵌入式系统的广泛支持。以上种种原因表明,Kakadu代码的入门和移植都非常困难。因此对Kakadu代码和移植性的分析就显得非常必要。

1 JPEG 2000标准的概述

JPEG 2000即ISO/ITU15444,是由国际标准化组织ISO和国际电信标准化联盟ITU-T于2000年底联合颁布的新一代图像压缩国际标准。JPEG 2000的编、解码流程[8]如图1所示。

图1 JPEG 2000的编、解码流程

在JPEG 2000编码中,图像的预处理为颜色空间的转换和对图像分切片的处理。小波变换采用的小波基在JPEG 2000标准中统一为Le Gall 5/3小波和Daubechies 9/7小波。JPEG 2000的量化与JPEG的量化过程基本一致。熵编码是整个JPEG 2000编码过程的核心,在JPEG 2000中采用David Taubman教授的EBCOT算法。JPEG 2000的解码过程仅是编码过程的逆过程而已。

2 Kakadu的系统结构

这里所用Kakadu版本为KakaduV2.2.3。Kakadu核心代码由3个子系统组成:编码参数子系统、压缩数据和结构子系统、处理引擎子系统[9]。Kakadu系统结构如图2所示。

其中,编码参数子系统处理用于编解码时需要的各种参数。应用程序把编解码参数传送到压缩数据和结构子系统,形成切片、分量切片、分辨率、编码块等JPEG 2000的数据块,再传送到处理引擎子系统。处理引擎子系统处理完输入数据后,可选择把数据流保存在内存中或者把输出保存到文件里。

图2 Kakadu系统结构图

如图2所示,这3个子系统的通信是通过一系列的类完成的,如kduparams,kducodestream,kdutile等。在Kakadu里,这些相互通信的类都是接口类,即它仅是3个子系统内部对象的接口。因此,这些接口类并没有标准的析构函数——销毁接口对于内部对象没有任何意义。这种处理方式可以使开发人员随意调用这些接口类对象,而又不会使内部数据结构受到破坏性影响,有利于整个编解码系统的稳定性。

3 Kakadu代码分析

下面以Kakadu标准的编码工程Kducompress来对Kakadu代码进行剖析。

3.1 数据的读入和存放

(1) 在栈(Stack)内建立存放警告和错误信息的缓存空间,并定义当警告和错误发生时,程序的回调函数。它是通过kducustomizewarnings和kducustomizeerrors这两个函数来定义的。在默认情况下,当警告发生时,程序会记录警告信息并继续运行;当错误发生时,程序记录错误信息并停止运行。

(2) 程序实例化一个叫kduargs类的对象来保存命令行参数,并通过kduargs的成员函数kduargs::getfirst,kduargs::find,kduargs::advance来分析该命令行参数,以获取输入文件路径、输出文件路径和一些基本的编解码参数。这些基本参数包括质量层数量、质量层码率及切片大小等。

(3) 程序将利用上面得到的输出文件路径,实例化2个空的输出文件类对象:kdusimplefiletarget和jp2target。然而,最后的代码流将保存到哪个对象,取决于输出文件设定为原始代码流格式,还是标准的jp2格式文件。

(4) 程序继续实例化kduimagein类的对象,并通过kduimagein对象的构造函数kduimagein::kduimagein()把输入文件保存在系统堆(heap)中,并通过kduimagein对象内部私有的in指针返回原始数据地址。

(5) 系统在分析完命令行参数和读取输入文件的文件头信息后,把这些编解码参数放到sizparams的对象所连接的栈中。由于JPEG 2000的参数众多,还有一些默认的参数没有设定,此时程序需要调用sizparams:: finalize()完成整个编码参数子系统的构造。

3.2 数据的压缩编码

(1) 程序首先实例化kducodestream类的对象。kducodestream的对象是整个程序的核心对象。如前所述,kducodestream类是一个接口类,所有的压缩编解码和中间数据都由对象连接,而且由于kducodestream仅是接口类,在初始化时不能调用其对象的构造函数,此时程序必须调用成员函数kducodestream::create完成初始化工作。在初始化时,程序已经把sizparams指向的数据自动保存到kducodestream指向的内存中。因此,此时可以销毁sizparams内部对象,以节省内存空间。

(2) 计算各质量层输出的字节数大小。程序根据给定的质量层码率rate[i](由命令行参数给定)、原始图像每像素所占用的位数b及原始图像的像素总数num进行计算。计算公式为:

byte[i]=rate[i]×num(b/64)

式中:i为质量层的索引号。若没有给定码率,即在无损压缩的情况下,质量层的字节数为0。

(3) 判断是否需要率预测。率预测并不是JPEG 2000中原有的功能,而是Kakadu中为了加速编解码速度而引入的机制。这是个非常实用且强大的功能,其原理是把压缩后率失真优化算法(PCRD-opt)中丢弃数据流的操作提前到位平面编码中进行,既降低了内存,又加快了速度。只有当率预测标记allowrateprediction和最高质量层字节数byte[max]同时大于零时,程序才会调用kducodestream::setmaxbytes使能率预测。

(4) 对kducodestream对象进行预处理。预处理工作包含2个部分。首先,判断图像是否需要翻转。翻转因子包括transpose,vflip和hflip,它在上述命令行分析过程中获得。程序通过调用kducodestream::changeappearance(transpose,vflip,hflip)来对图像进行翻转。若transpose,vflip,hflip均为零,图像不翻转。其次,根据命令行参数生成感兴趣区域(roi)信息。它通过实例化类createroisource和kduroiimage的对象完成。注意,这两步预处理工作仅是对坐标的表示方式做了改变。

(5) 创建数据处理引擎。它是通过实例化kdcflowcontrol类的对象和调用kdcflowcontrol的构造函数实现的。程序在调用kdcflowcontrol::kdcflowcontrol()时会自动调用上述kducodestream对象的信息和保存在内存中的原始图像数据信息,以判断小波变化的层数、位平面编码的精度等,以及创建最小、最合适的处理引擎。由于处理引擎的数据量很大,因此程序必须用new操作符在系统堆里面创建。

(6) 数据编码。由于图像大多是彩色图像,既包含了多个图像分量,又包含了多切片。在进行数据编码时,各个切片分量需要独立进行。切片之间是相互独立的,因此有多少个切片,就必须创建多少个处理引擎kduflowcontrol。在分量间切换时,必须调用各个处理引擎的成员函数kduflowcontrol::advancecomponents()。最后的数据处理是调用kduflowcontrol:: processcomponents()进行的。这个成员函数kduflowcontrol:: processcomponents()包括了数据的颜色空间转换、小波变换、量化、位平面编码、MQ,即JPEG 2000里面的tier1过程。由此可见,Kakadu把大部分的编码操作都封装起来,这对于从事实际产品开发的研发人员来说非常方便。

(7) tier2的操作。在上述的(6)中,应用程序已经把数据编码成码流,并保存到kducodestream对象所连接的内部缓存中。tier2的过程是把这些码流通过压缩后率失真优化算法截断,形成最终的压缩码流。通过调用kducodestream::flush,使应用程序实现这个非常复杂的算法。对于工程人员来说,只需调用成员函数kducodestream::flush,而不用去理会其内部机制。

(8) 最后应用程序把代码流输出到文件中,并清理使用过的各种缓存、内部对象。如前所述,在Kakadu里面提供给用户的类都是接口类,即没有给出明确的析构函数,因此需要调用kducodestream::destroy()来销毁内部对象。由于kducodestream对象在内部连接到其他各级对象中,因此kducodestream::destroy()将销毁所有在栈中建立的对象。但对于质量层字节数等在堆中创建的数组或对象,必须通过delete[]删除。

纵上分析,Kakadu应用程序的压缩编码操作并不像其他编码程序。它通过把数据顺序地递交到不用的子系统中进行编码。Kakadu创造性地构造了“处理引擎”,把数据压缩到这个引擎。这个“处理引擎”是可以按需求裁剪的,因此可以最大限度地降低运行时内存的使用率。然而Kakadu创造性的率预测机制,大大减少了编码时间。

4 移植性分析

原始的Kakadu代码是基于PC平台的,而越来越多的应用需要把代码移植到嵌入式设备中。因此,这里以KakaduV 2.2.3移植到TI TMS320DM642为例,对代码进行移植性分析。TMS320DM642是TI公司的第2代高性能超长指令字DSP,其自带的编译器支持标准C语言的全部特性,但并不支持C++的所有特性,如不支持异常处理(Exection Handling);不支持模板参数;不支持标准的输入输出流对象(ostream和istream等)。但在Kakadu中,刚好大量使用了异常处理,如ostream和istream类。DM642编译器支持的C++头文件与VC编译环境的头文件也不一致,也需要用户去手动修改。具体的修改如下:

在原工程的头文件中,string.h,assert.h,stdio.h和math.h要分别用CSTRING,CASSERT,CSTDIO,CMATH来代替。但是,由于fstream,iostream这一类的C++头文件中,istream、ostream已经被改写了,所以可以直接去掉。

在Kakadu中,对基本输入/输出流的使用主要在原始数据的读入和错误、警告信息的输出上。其中,对原始数据的读入,使用了子函数ifstream::read。对于这一部分的ifstream对象,可用File类型的文件指针来改写。而在读入图像数据操作时,则直接用std::fopen和std::fseek进行改写。对于由ostream的对象所保存的错误、警告信息,最简单的方法是把相关的ostream类代码屏蔽,这虽然不影响程序的运行,但不利于调试。处理这个问题的最有效方法是使用TI公司实时操作系统DSP/BIOS[10]中的LOG模块来带代替ostream对象的相关成员函数。LOG模块是基于实时操作系统的,运行起来更有效率。

对于C++标准的异常处理函数,如try,catch,也可以使用屏蔽的方法处理,其前提是需要用户仔细检查代码,确保程序在运行期间不发生任何意外。此外,最高效的方法是使用DSP/BIOS系统所带的软件中断模块。CCS里面使用软件中断非常方便,在C++代码里面设置了中断入口函数,在DSP/BIOS设置工具中只需设置其中断优先级。

可见,尽管DM642的编码器对C++的支持度不是很好(这也是目前嵌入式编译器的通病),但只要按照具体的硬件对Kakadu做相应修改,仍然能很好地将其移植到非PC平台上。

5 结 语

目前,JPEG 2000因其独特的优势受到了业界的关注。JPEG 2000在医疗图像、网络传输、保密性方面对JPEG有无可比拟的优势,几乎可以确定会取代JPEG。由于独立开发编解码器所需人力、物力很大,开发周期也很长,大部分公司都选择了代码移植来缩短研发周期,降低成本。然而目前可选的JPEG 2000开源代码不多,Kakadu几乎是惟一的选择。

现阶段虽然对JPEG 2000的研究很多,但大多停留在理论水平,没有涉及到真正的代码实现。即使涉及到代码实现,也没有涉及到最有效率的Kakadu代码。给出Kakadu 标准C++代码的分析流程和在DM642上的移植性分析,对希望从事Kakadu图像处理平台开发的研发人员有指导意义。

参考文献

[1]Acharya T,Tsai P S.JPEG 2000 Standard for Image Compression: Concepts,Algorithms and VLSI Architectures[M].Wiley-Interscience,2004.

[2]焦晓,朱光喜,马明罡.JPEG 2000的编码技术[J].计算机仿真,2003,20(9):112-114.

[3]赵锦,邢长征.JPEG 2000的编码原理及其优缺点[J].辽宁工程技术大学学报,2004(23):192-193.

[4]Acharya T.A Survey on Lifting-based Discrete Wavelet Transform Architectures[J].Journal of VLSI Signal Processing Systems,2006,42(3):321-339.

[5]沈兰荪,卓力.小波编码与网络视频传输[M].北京:科学出版社,2005.

[6]Taubman D.High Performance Scalable Image Compression with EBCOT[J].IEEE Trans.on Image Processing,2000,19(7):1 158-1 170.

[7]Hong M,Alen D,Faouzi K.Performance Analysis of the JPEG 2000 Image Coding Standard[J].Multimedia Tools and Applications,2005,26(1):27-57.

[8]Skodras A,Christopoulos C,Ebrahimi T.The JPEG 2000 Still Image Compression Standard[J].IEEE Signal Processing Magazine,2001,18(5):36-58.

[9]David Taubman.Kakadu Survey Documentation[Z].2001.

[10]李进.实时操作系统DSP/BIOS在DSP开发中的应用[J].微电子技术,2003(4):63-65.

免责声明

我们致力于保护作者版权,注重分享,被刊用文章因无法核实真实出处,未能及时与作者取得联系,或有版权异议的,请联系管理员,我们会立即处理! 部分文章是来自各大过期杂志,内容仅供学习参考,不准确地方联系删除处理!