孔祥焰 羅桂娥
摘 要: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.