陳 捷, 何志堅(jiān), 宋占偉
(吉林大學(xué) 電子科學(xué)與工程學(xué)院, 長(zhǎng)春 130012)
Android系統(tǒng)以其開(kāi)放、 免費(fèi)、 功能強(qiáng)大等多個(gè)特性已經(jīng)逐漸占據(jù)了智能手機(jī)操作系統(tǒng)和平板電腦操作系統(tǒng)的市場(chǎng)[1], 憑借其強(qiáng)大的API(Application Programming Interface)控件和高端ARM(Advanced RISC Machines)處理器的強(qiáng)大處理能力逐漸在可視通訊領(lǐng)域中發(fā)揮其巨大潛力[2]。筆者目前正在研究開(kāi)發(fā)基于Android系統(tǒng)的視訊終端, 在開(kāi)發(fā)過(guò)程中需要重寫(xiě)Android的View控件中onDraw方法[3]繪制解碼完成的視頻圖像并放大到全屏顯示, 然而使用文獻(xiàn)[4,5]或網(wǎng)絡(luò)文檔[6]中常見(jiàn)的創(chuàng)建放大圖片的方式時(shí)出現(xiàn)效率低、 消耗大量?jī)?nèi)存的問(wèn)題, 不能滿足較大屏幕的即時(shí)播放需求。因此, 筆者在分析效率低下的原因后, 研究了Android下其他幾種可行的圖像放大的方法, 分別進(jìn)行了實(shí)驗(yàn)測(cè)試, 得到其執(zhí)行效率和放大效果。并對(duì)各種實(shí)現(xiàn)方法在即時(shí)視頻播放應(yīng)用中的可行性進(jìn)行了系統(tǒng)分析, 最終從源代碼中獲得啟發(fā), 得到一個(gè)兼顧放大效果和效率以及刷新可控性的最佳方案。
測(cè)試用平板電腦使用基于Cortex-A8核心, 頻率為1.0 GHz處理器, 內(nèi)存為512 MByte。Android版本為4.0.3, Linux內(nèi)核版本為3.0.8+。使用Eclipse indigo作為編程環(huán)境[7], 從視頻解碼器中獲得的視頻圖像為RGB565形式320×240像素的圖像, 使用Byte型數(shù)組順序存儲(chǔ)所有像素點(diǎn)的顏色信息。播放窗口默認(rèn)為480×360像素。
文獻(xiàn)[4,5]與網(wǎng)絡(luò)文檔[6]中提到對(duì)圖像進(jìn)行拉伸的方法, 即首先創(chuàng)建一個(gè)原始圖像的Bitmap對(duì)象, 其大小為視頻解碼的原始尺寸, 再調(diào)用createBitmap方法結(jié)合放大矩陣Matrix創(chuàng)建一個(gè)放大的新Bitmap對(duì)象。其主要實(shí)現(xiàn)代碼如下:
Matrix matrix=new Matrix();
Matrix.postScale((float)480/320,(float)360/240);
NewBMP=Bitmap.createBitmap(OldBMP,0,0,320,240,matrix,true)
其中使用Matrix定義放大倍數(shù), 放大倍數(shù)是一個(gè)浮點(diǎn)型的小數(shù)。View及其繼承類可以在子線程里調(diào)用postInvalidate方法刷新圖像, 重寫(xiě)onDraw方法將Bitmap對(duì)象直接繪制到View的畫(huà)板中即可顯示放大后的圖像。該方法可以由線程主動(dòng)控制刷新圖像, 其拉伸方式由系統(tǒng)決定。
使用該方法, 每次處理一張解碼后圖片都必須創(chuàng)建一個(gè)新Bitmap對(duì)象, 舊Bitmap圖像占用的內(nèi)存變?yōu)榭苫厥諣顟B(tài)。由于Android不存在主動(dòng)釋放內(nèi)存資源的方法, 必須靠虛擬機(jī)自動(dòng)回收資源。因?yàn)锽itmap對(duì)象占用的內(nèi)存資源量很大, 一張480×360像素的圖像使用565格式存放需要的內(nèi)存空間為345 600 Byte, Android下為每個(gè)應(yīng)用程序分配的堆空間有限, 內(nèi)存損耗過(guò)大時(shí)dalvikvm虛擬機(jī)會(huì)立刻進(jìn)行內(nèi)存回收[8], 并產(chǎn)生一定的時(shí)間延遲?;厥諆?nèi)存資源時(shí), 在開(kāi)發(fā)環(huán)境Eclipse的消息窗口LogCat[9]中會(huì)出現(xiàn)如表1所示的消息。
上面是一個(gè)使用這種方式解壓播放9.5幀/s視頻圖像的測(cè)試代碼執(zhí)行時(shí)得到的系統(tǒng)消息, 在這里, 放大處理時(shí)間并不包括解碼所需的時(shí)間, 每播放一幅畫(huà)面, 虛擬機(jī)就需要立刻釋放338 kByte的空間, 此外還能看見(jiàn)釋放這些數(shù)據(jù)需要的時(shí)間, 為17~30 ms不等。刷新頻率越高, 圖片越大, 導(dǎo)致內(nèi)存的消耗也越大, 虛擬機(jī)疲于釋放內(nèi)存空間, 處理效率嚴(yán)重下降[8]。在測(cè)試函數(shù)兩端添加了使用System.currentTimemillis方法得到的每幅圖片放大所需大致時(shí)間, 顯示在Logcat中, 后文中得到的圖片處理時(shí)間也是基于此方法。從結(jié)果中可以看出, 每幅圖片間隔在33~92 ms之間不等, 加上釋放內(nèi)存需要的時(shí)間, 根據(jù)時(shí)間戳, 播放一幀畫(huà)面對(duì)圖像的處理時(shí)間有時(shí)已經(jīng)超過(guò)110 ms, 這也意味著已經(jīng)不能保證視頻的刷新率在9.5幀/s, 雖然能得到圖像放大的效果, 但由于效率的低下, 這種圖像放大方式不適合在即時(shí)視頻刷新中使用。必須在Android的API中尋找其他放大方法。
研究Bitmap類的各種創(chuàng)建圖片方法后發(fā)現(xiàn), 使用Bitmap類的copyPixelFromBuffer方法可以將一個(gè)表示像素顏色的字節(jié)數(shù)組數(shù)據(jù)寫(xiě)入一個(gè)現(xiàn)存的Bitmap中, 不需要重新創(chuàng)建Bitmap對(duì)象。該方法支持使用565格式的16位圖片數(shù)據(jù), 也支持A8888格式的32位圖片數(shù)據(jù)。因此可以編寫(xiě)軟件算法直接處理解碼器得到的顏色數(shù)組數(shù)據(jù)進(jìn)行圖像放大, 結(jié)果存入一個(gè)預(yù)先創(chuàng)建可重用且大小為放大后圖像尺寸兩倍的Byte數(shù)組(假設(shè)使用16位圖片數(shù)據(jù)), 再使用copyPixelFromBuffer方法將數(shù)組內(nèi)容讀入一個(gè)現(xiàn)有的Bitmap對(duì)象, 顯示方式采用重寫(xiě)onDraw方法。使用該方式, 能做到內(nèi)存空間的重復(fù)利用, 但算法的復(fù)雜度和處理器性能是提高播放器刷新率的關(guān)鍵[10]。
由于需要保證刷新的實(shí)時(shí)性, 考慮到設(shè)備處理器性能有限, 使用軟件線性插值算法[11]或邊緣銳化增強(qiáng)放大算法[12]都無(wú)法在有限的刷新周期(在測(cè)試平臺(tái)上為110 ms)內(nèi)完成所有處理。因?yàn)檫@些算法基本都要對(duì)每個(gè)目標(biāo)像素點(diǎn)進(jìn)行對(duì)應(yīng)位置計(jì)算[13], 對(duì)于480×360像素的圖像, 需執(zhí)行172 800次變換。如果變換過(guò)程中涉及浮點(diǎn)運(yùn)算, 則消耗的時(shí)間更多[14]。一個(gè)將目標(biāo)圖像坐標(biāo)按放大比例變換為源圖像坐標(biāo), 將源圖像坐標(biāo)對(duì)應(yīng)的值作為目標(biāo)圖像對(duì)應(yīng)坐標(biāo)值的示例算法如下:
int*x=(int*)malloc(width2*sizeof(int));
int*y=(int*)malloc(height2*sizeof(int));
for (i=0;i *(x+i)=(int)(i*width1/width2); for(i=0;i *(y+i)=(int)(i*height1/height2); for (j=0;j for (i=0;i { *(out+i+j*width2)=*(in+x[i]+y[j]*width1); } 釋放空間部分代碼沒(méi)有包含在內(nèi), 其中width1和height1為原始圖像的長(zhǎng)寬, width2和height2為拉伸后圖像長(zhǎng)寬。 算法中已采用了一些優(yōu)化方式, 由于目標(biāo)圖像每個(gè)橫坐標(biāo)像素對(duì)應(yīng)的源圖像橫坐標(biāo)位置是相同的, 縱坐標(biāo)的位置同理。為減少除法運(yùn)算, 預(yù)先定義了兩個(gè)坐標(biāo)對(duì)應(yīng)關(guān)系表, 省略了對(duì)每個(gè)坐標(biāo)都要進(jìn)行的兩次除法運(yùn)算, 使用二重循環(huán)而不是使用一重循環(huán)的目的在于: 省略一重循環(huán)中為得到目標(biāo)所在的行列位置所要進(jìn)行的除法和取模運(yùn)算, 因?yàn)槎鄶?shù)處理器對(duì)取模運(yùn)算需要很多時(shí)間周期, 如果條件允許盡量不使用取模運(yùn)算。該算法使用JNI方式編譯, 使用C代碼能方便地將Byte數(shù)組轉(zhuǎn)換為short指針取數(shù)運(yùn)算。 考慮到項(xiàng)目前期完成的網(wǎng)絡(luò)圖片傳輸顯示中使用的ImageView顯示窗口, 在使用setImageBitmap方法設(shè)置顯示圖片且scaleType設(shè)置參數(shù)為FIT_CENTER的情況下, 能自動(dòng)將圖片拉伸后顯示, 但該方法只能用在主線程中調(diào)用, 在子線程調(diào)用將拋出異常。由于View及其繼承類在子線程里調(diào)用postInvalidate方法實(shí)際上是通知主線程調(diào)用onDraw方法刷新圖像, 因而可通過(guò)在onDraw方法中添加setImageBitmap過(guò)程, 使主線程自動(dòng)調(diào)用刷新, 實(shí)現(xiàn)圖像放大播放, 其拉伸方式由系統(tǒng)決定。 在3.1中使用的copyPixelFromBuffer能重用數(shù)據(jù)緩沖區(qū)和Bitmap對(duì)象, 起到了節(jié)約內(nèi)存資源的作用, 減少因?yàn)樘摂M機(jī)頻繁自動(dòng)回收內(nèi)存空間造成的延遲, 最終得到的每幅放大處理時(shí)間如表2所示。 同樣采用在拉伸函數(shù)兩端添加System.currentTimeMillis方法得到函數(shù)執(zhí)行時(shí)間的方式, 從LogCat中的結(jié)果可見(jiàn), 這種放大方式的效率較高, 每幀需要的放大時(shí)間基本上只有4 ms, 能保證前面提到的9.5幀/s的幀率, 在實(shí)驗(yàn)中有時(shí)會(huì)有一幀10~20 ms的突發(fā)情況, 這與系統(tǒng)的其他進(jìn)程調(diào)度有關(guān)[15]。該方式不需要耗費(fèi)系統(tǒng)頻繁執(zhí)行回收方法的延遲時(shí)間, 已經(jīng)大大減少了時(shí)間開(kāi)支, 以每幅視頻解壓需要平均10 ms時(shí)間計(jì)算, 該方式播放視頻的刷新率比較容易達(dá)到60 Hz。但如果圖像放大比例較大, 會(huì)出現(xiàn)鋸齒現(xiàn)象, 這是因?yàn)闆](méi)有使用插值算法柔化邊緣的緣故。如果能使用插值算法, 或使用硬件對(duì)底層圖像進(jìn)行處理, 則圖像效果會(huì)更佳。然而從算法的復(fù)雜度考慮, 對(duì)于銳化和反鋸齒插值算法中需要的大量浮點(diǎn)運(yùn)算, 以及色彩565形式變換成RGB888形式進(jìn)行處理的過(guò)程, 測(cè)試平臺(tái)使用軟件方式無(wú)法在可接受的時(shí)間間隔內(nèi)達(dá)到目標(biāo), 需要使用性能更高的處理器。 使用ImageView類放大圖像的測(cè)試如表3所示。 表3 使用ImageView方法放大圖片的效率 觀察實(shí)驗(yàn)結(jié)果中的時(shí)間戳發(fā)現(xiàn), 使用該方式在解碼開(kāi)始前也在不斷進(jìn)行刷新, 雖然該方式放大效率很高, 一般在1 ms以內(nèi), 與3.1節(jié)方法一樣偶爾會(huì)有10~20 ms的突發(fā)情況, 但由于自動(dòng)刷新的刷新率太高, 在播放幀率較低的即時(shí)采集傳輸視頻, 如前面提到的9.5幀/s的視頻時(shí), 把每110 ms刷新需要的時(shí)間相加, 與前一種方式每幀刷新時(shí)使用的時(shí)間非常接近, 理論上有許多次處理時(shí)間是可以省略的。該方式不能做到隨子線程調(diào)用postInvalidate方法刷新這種可控刷新方法, 也間接導(dǎo)致其他線程處理速度受到影響。這種放大方式得到的圖像經(jīng)過(guò)平滑處理, 圖像邊緣放大后比較模糊, 鋸齒現(xiàn)象不明顯, 然而該方式的放大算法不可修改, 不能對(duì)圖像進(jìn)行銳化等特殊處理, 只能按系統(tǒng)定義的默認(rèn)放大方式放大。對(duì)于非特殊要求的應(yīng)用, 該放大方式比較理想, 只需要研究一下如何將多余的刷新時(shí)間去除即可。 基于ImageView類的圖像放大方法單幀圖像放大效率比使用軟件算法的方式高, 但缺乏3.1節(jié)方法中擁有的刷新時(shí)間的可控性, 存在多余的刷新時(shí)間使其效率下降的問(wèn)題, 由于Android的API提供了源代碼, 可以從源代碼中進(jìn)行分析。 從Android SDK的Android-15源代碼包資源中找到ImageView的源代碼, 研究源碼后發(fā)現(xiàn)了方法2中出現(xiàn)無(wú)法控制的高刷新率的原因。ImageView的setImageBitmap方法針對(duì)圖像大小和視窗邊界作了一些比例運(yùn)算, 并根據(jù)ImageView類中名為ScaleType的設(shè)置參數(shù)調(diào)整比例矩陣mDrawMatrix, 最終得到一個(gè)源圖像尺寸和顯示尺寸相關(guān)的矩陣作為全局變量。setImageBitmap函數(shù)最后調(diào)用了invalidate方法使當(dāng)前畫(huà)板失效, 通知主線程調(diào)用onDraw方法重畫(huà), 如果把setImageBitmap寫(xiě)在onDraw中用于放大圖像, 則相當(dāng)于在onDraw方法中調(diào)用invalidate方法循環(huán)不斷地使畫(huà)面失效, 主線程不斷得到通知刷新畫(huà)面重調(diào)用onDraw方法, 因此, 界面刷新過(guò)于頻繁, 影響到其他線程的執(zhí)行效率。 ImageView重寫(xiě)了View的onDraw方法, 根據(jù)已得到的全局比例矩陣mDrawMatrix和一些設(shè)置參數(shù)在onDraw中調(diào)用canvas的本地方法concat和translate等方式設(shè)置比例矩陣并放大圖像。根據(jù)相同的原理, 3.1節(jié)中的View或SurfaceView的onDraw方法中直接加入同樣一段代碼canvas.concat(mMatrix), 將其他處理設(shè)置參數(shù)的過(guò)程可省略, 以達(dá)到最快速最簡(jiǎn)化效果。 該本地方法的官方解釋為使用當(dāng)前mMatrix作為繪制圖形用的放大矩陣[15]。mMatrix可以在播放窗口建立時(shí)根據(jù)視頻大小和播放窗口大小的比例預(yù)先創(chuàng)建, 其余解碼和顯示圖像部分和3.1節(jié)或3.2節(jié)的方法相同, 即能得到放大后的圖像。值得注意的是, 在每次調(diào)用onDraw方法時(shí)都需要調(diào)用該函數(shù), 否則只對(duì)一次刷新有效。最終得到每張圖片放大時(shí)間幾乎都為0, 且能保證每110 ms刷新一次。 使用該方式避免了3.2節(jié)中不斷重復(fù)的刷新過(guò)程, 同時(shí)也消除了該方法每次調(diào)用ImageView類的setImageBitmap方法時(shí)放大比例矩陣的計(jì)算時(shí)間和onDraw函數(shù)中判斷過(guò)程, 在最大程度上提高了性能。如果對(duì)放大后的圖像清晰度效果沒(méi)有特殊要求, 這是測(cè)試平臺(tái)上最為合適的即時(shí)視頻放大手段。 根據(jù)實(shí)驗(yàn)和分析的結(jié)果表明, 基于軟件算法實(shí)現(xiàn)拉伸圖像的3.1節(jié)方法刷新時(shí)間可控制, 且能根據(jù)具體需要使用邊緣銳化、 線性插值等特殊算法, 但實(shí)現(xiàn)的可行性受具體算法的復(fù)雜度和硬件處理器性能限制; 基于ImageView設(shè)置圖片的方法存在自動(dòng)刷新率太高影響其他線程的處理性能, 缺乏刷新時(shí)間可控性的缺點(diǎn), 但這種方式效率較高。研究源代碼發(fā)現(xiàn), 如果對(duì)放大清晰度效果沒(méi)有要求, 可以使用canvas的一些本地方法結(jié)合縮放矩陣達(dá)到目標(biāo)效果。該方式結(jié)合了3.1節(jié)的刷新可控性和3.2節(jié)效率高的優(yōu)點(diǎn), 目前已經(jīng)在項(xiàng)目的視頻解碼應(yīng)用中得到使用。筆者從研究中體會(huì)到, Android圖形化界面的編程由于資料分散, 官方文檔比較簡(jiǎn)略缺少范例, 某些尚未被普遍使用的實(shí)用API還需要繼續(xù)探索和普及。通過(guò)閱讀控件源代碼發(fā)現(xiàn)這些新功能是一種很好的學(xué)習(xí)方法。 參考文獻(xiàn): [1]陳木生. Google Android手機(jī)推出市場(chǎng)分析 [J]. 電子與電腦, 2008(12): 10-14. CHEN Mu-sheng. Market Analysis When Google Android Phone Release [J]. Compotech China, 2008(12): 10-14. [2]楊明極, 畢晶. 基于Android視頻客戶端的設(shè)計(jì) [J]. 電視技術(shù), 2012(3): 43-47. YANG Ming-ji, BI Jing. Design of Video Client Based on Android [J]. Video Engieering, 2012(3): 43-47. [3]宋強(qiáng), 齊貴寶, 宋占偉. 基于Android系統(tǒng)的H.264視頻監(jiān)控設(shè)計(jì) [J]. 吉林大學(xué)學(xué)報(bào): 信息科學(xué)版, 2012, 30(3): 272-277. SONG Qiang, QI Gui-bao, SONG Zhan-wei. Design of H.264 Video Monitoring Based on Android System [J]. Journal of Jilin University: Information Science Edition, 2012, 30(3): 272-277. [4]汪永松. Android開(kāi)發(fā)平臺(tái)之旅 [M]. 北京: 機(jī)械工業(yè)出版社, 2012. WANG Yong-song. Travel of Android Development Platform [M]. Beijing: China Machine Press, 2012. [5]楊豐盛. Android應(yīng)用開(kāi)發(fā)揭秘 [M]. 北京: 機(jī)械工業(yè)出版社, 2010. YANG Feng-sheng. Android Unleashed [M]. Beijing: China Machine Press, 2010. [6]XPSHARP. Android圖片放大縮小 [EB/OL]. [2011-12-21]. http://www.linuxidc.com/Linux/2011-12/49926.htm. XPSHARP. Enlarge or Shrink Images in Android [EB/OL]. [2011-12-21]. http://www.linuxidc.com/Linux/2011-12/49926.htm. [7]JEROME DIMARZIO. Android: A Programmer’s Guide [M]. New Youk: Osborne/McGraw-Hill, 2008. [8]宋小倩, 周東升. 基于Android平臺(tái)的應(yīng)用開(kāi)發(fā)研究 [J]. 軟件導(dǎo)刊, 2011(2): 104-106. SONG Xiao-qian, ZHOU Dong-sheng. Development and Researh of Application Based on Android Platform [J]. Software Guide, 2011(2): 104-106. [9]尹文剛, 楊斌. Android應(yīng)用程序中的內(nèi)存泄漏與規(guī)避方法 [J]. 單片機(jī)與嵌入式系統(tǒng)應(yīng)用, 2012(6): 4-6. YIN Wen-gang, YANG Bin. Memory Leak and Avoiding Method of Android Application Program [J]. Microcontrollers & Embedded Systems, 2012(6): 4-6. [10]孫杰. 基于Android平臺(tái)圖像處理算法的研究與實(shí)現(xiàn) [D]. 北京: 北京郵電大學(xué)軟件學(xué)院, 2011. SUN Jie. Research and Implementation of Algorithms for Image Processing Based on Android [D]. Beijing: Software Institute, Beijing University of Posts and Telecommunications, 2011. [11]江銘炎, 李興江, 袁東風(fēng). 2*圖像插值放大處理的方法 [J]. 山東大學(xué)學(xué)報(bào): 理學(xué)版, 2003, 38(3): 79-81. JIANG Ming-yan, LI Xing-jiang, YUAN Dong-feng. The Method of 2*Image Enlarging with Interpolation [J]. Journal of Shandong University: Natural Science,2003, 38(3): 79-81. [12]曾生達(dá). 幾種圖像放大算法原理比較與分析 [J]. 金華職業(yè)技術(shù)學(xué)院學(xué)報(bào), 2012(3): 66-71. ZENG Sheng-da. The Comparision and Analyisis of Several Manification of Image Magnification [J]. Journal of Jinhua Polytechnic, 2012(3): 66-71. [13]WU X, ZHANG X, WANG X. Low Bit-rate Image Compression via Adaptive Down-sampling and Constrained Least Squares Upconversion [J]. IEEE Transactions on Image Processing, 2009, 8(3): 552-561. [14]NIU Yi, SHI Guang-ming, WANG Xiao-tian, et al. JPEG Stream Soft-Decoding Technique Based on Autoregressive Modeling [J]. The Journal of China Universities of Posts and Telecommunications, 2012, 19(5): 115-123. [15]Google Company. Android Developers [EB/OL]. [2012-10]. http://developer.android.com/.3.2 使用ImageView類的設(shè)置圖片方法
4 放大圖像測(cè)試代碼執(zhí)行結(jié)果分析
4.1 使用軟件算法放大圖像的測(cè)試代碼結(jié)果
4.2 使用ImageView類放大圖像的測(cè)試代碼結(jié)果
5 從源碼中研究改進(jìn)方式
6 結(jié) 語(yǔ)