陈 捷, 何志坚, 宋占伟
(吉林大学 电子科学与工程学院, 长春 130012)
Android系统以其开放、 免费、 功能强大等多个特性已经逐渐占据了智能手机操作系统和平板电脑操作系统的市场[1], 凭借其强大的API(Application Programming Interface)控件和高端ARM(Advanced RISC Machines)处理器的强大处理能力逐渐在可视通讯领域中发挥其巨大潜力[2]。笔者目前正在研究开发基于Android系统的视讯终端, 在开发过程中需要重写Android的View控件中onDraw方法[3]绘制解码完成的视频图像并放大到全屏显示, 然而使用文献[4,5]或网络文档[6]中常见的创建放大图片的方式时出现效率低、 消耗大量内存的问题, 不能满足较大屏幕的即时播放需求。因此, 笔者在分析效率低下的原因后, 研究了Android下其他几种可行的图像放大的方法, 分别进行了实验测试, 得到其执行效率和放大效果。并对各种实现方法在即时视频播放应用中的可行性进行了系统分析, 最终从源代码中获得启发, 得到一个兼顾放大效果和效率以及刷新可控性的最佳方案。
测试用平板电脑使用基于Cortex-A8核心, 频率为1.0 GHz处理器, 内存为512 MByte。Android版本为4.0.3, Linux内核版本为3.0.8+。使用Eclipse indigo作为编程环境[7], 从视频解码器中获得的视频图像为RGB565形式320×240像素的图像, 使用Byte型数组顺序存储所有像素点的颜色信息。播放窗口默认为480×360像素。
文献[4,5]与网络文档[6]中提到对图像进行拉伸的方法, 即首先创建一个原始图像的Bitmap对象, 其大小为视频解码的原始尺寸, 再调用createBitmap方法结合放大矩阵Matrix创建一个放大的新Bitmap对象。其主要实现代码如下:
Matrix matrix=new Matrix();
Matrix.postScale((float)480/320,(float)360/240);
NewBMP=Bitmap.createBitmap(OldBMP,0,0,320,240,matrix,true)
其中使用Matrix定义放大倍数, 放大倍数是一个浮点型的小数。View及其继承类可以在子线程里调用postInvalidate方法刷新图像, 重写onDraw方法将Bitmap对象直接绘制到View的画板中即可显示放大后的图像。该方法可以由线程主动控制刷新图像, 其拉伸方式由系统决定。
使用该方法, 每次处理一张解码后图片都必须创建一个新Bitmap对象, 旧Bitmap图像占用的内存变为可回收状态。由于Android不存在主动释放内存资源的方法, 必须靠虚拟机自动回收资源。因为Bitmap对象占用的内存资源量很大, 一张480×360像素的图像使用565格式存放需要的内存空间为345 600 Byte, Android下为每个应用程序分配的堆空间有限, 内存损耗过大时dalvikvm虚拟机会立刻进行内存回收[8], 并产生一定的时间延迟。回收内存资源时, 在开发环境Eclipse的消息窗口LogCat[9]中会出现如表1所示的消息。
上面是一个使用这种方式解压播放9.5帧/s视频图像的测试代码执行时得到的系统消息, 在这里, 放大处理时间并不包括解码所需的时间, 每播放一幅画面, 虚拟机就需要立刻释放338 kByte的空间, 此外还能看见释放这些数据需要的时间, 为17~30 ms不等。刷新频率越高, 图片越大, 导致内存的消耗也越大, 虚拟机疲于释放内存空间, 处理效率严重下降[8]。在测试函数两端添加了使用System.currentTimemillis方法得到的每幅图片放大所需大致时间, 显示在Logcat中, 后文中得到的图片处理时间也是基于此方法。从结果中可以看出, 每幅图片间隔在33~92 ms之间不等, 加上释放内存需要的时间, 根据时间戳, 播放一帧画面对图像的处理时间有时已经超过110 ms, 这也意味着已经不能保证视频的刷新率在9.5帧/s, 虽然能得到图像放大的效果, 但由于效率的低下, 这种图像放大方式不适合在即时视频刷新中使用。必须在Android的API中寻找其他放大方法。
研究Bitmap类的各种创建图片方法后发现, 使用Bitmap类的copyPixelFromBuffer方法可以将一个表示像素颜色的字节数组数据写入一个现存的Bitmap中, 不需要重新创建Bitmap对象。该方法支持使用565格式的16位图片数据, 也支持A8888格式的32位图片数据。因此可以编写软件算法直接处理解码器得到的颜色数组数据进行图像放大, 结果存入一个预先创建可重用且大小为放大后图像尺寸两倍的Byte数组(假设使用16位图片数据), 再使用copyPixelFromBuffer方法将数组内容读入一个现有的Bitmap对象, 显示方式采用重写onDraw方法。使用该方式, 能做到内存空间的重复利用, 但算法的复杂度和处理器性能是提高播放器刷新率的关键[10]。
由于需要保证刷新的实时性, 考虑到设备处理器性能有限, 使用软件线性插值算法[11]或边缘锐化增强放大算法[12]都无法在有限的刷新周期(在测试平台上为110 ms)内完成所有处理。因为这些算法基本都要对每个目标像素点进行对应位置计算[13], 对于480×360像素的图像, 需执行172 800次变换。如果变换过程中涉及浮点运算, 则消耗的时间更多[14]。一个将目标图像坐标按放大比例变换为源图像坐标, 将源图像坐标对应的值作为目标图像对应坐标值的示例算法如下:
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); } 释放空间部分代码没有包含在内, 其中width1和height1为原始图像的长宽, width2和height2为拉伸后图像长宽。 算法中已采用了一些优化方式, 由于目标图像每个横坐标像素对应的源图像横坐标位置是相同的, 纵坐标的位置同理。为减少除法运算, 预先定义了两个坐标对应关系表, 省略了对每个坐标都要进行的两次除法运算, 使用二重循环而不是使用一重循环的目的在于: 省略一重循环中为得到目标所在的行列位置所要进行的除法和取模运算, 因为多数处理器对取模运算需要很多时间周期, 如果条件允许尽量不使用取模运算。该算法使用JNI方式编译, 使用C代码能方便地将Byte数组转换为short指针取数运算。 考虑到项目前期完成的网络图片传输显示中使用的ImageView显示窗口, 在使用setImageBitmap方法设置显示图片且scaleType设置参数为FIT_CENTER的情况下, 能自动将图片拉伸后显示, 但该方法只能用在主线程中调用, 在子线程调用将抛出异常。由于View及其继承类在子线程里调用postInvalidate方法实际上是通知主线程调用onDraw方法刷新图像, 因而可通过在onDraw方法中添加setImageBitmap过程, 使主线程自动调用刷新, 实现图像放大播放, 其拉伸方式由系统决定。 在3.1中使用的copyPixelFromBuffer能重用数据缓冲区和Bitmap对象, 起到了节约内存资源的作用, 减少因为虚拟机频繁自动回收内存空间造成的延迟, 最终得到的每幅放大处理时间如表2所示。 同样采用在拉伸函数两端添加System.currentTimeMillis方法得到函数执行时间的方式, 从LogCat中的结果可见, 这种放大方式的效率较高, 每帧需要的放大时间基本上只有4 ms, 能保证前面提到的9.5帧/s的帧率, 在实验中有时会有一帧10~20 ms的突发情况, 这与系统的其他进程调度有关[15]。该方式不需要耗费系统频繁执行回收方法的延迟时间, 已经大大减少了时间开支, 以每幅视频解压需要平均10 ms时间计算, 该方式播放视频的刷新率比较容易达到60 Hz。但如果图像放大比例较大, 会出现锯齿现象, 这是因为没有使用插值算法柔化边缘的缘故。如果能使用插值算法, 或使用硬件对底层图像进行处理, 则图像效果会更佳。然而从算法的复杂度考虑, 对于锐化和反锯齿插值算法中需要的大量浮点运算, 以及色彩565形式变换成RGB888形式进行处理的过程, 测试平台使用软件方式无法在可接受的时间间隔内达到目标, 需要使用性能更高的处理器。 使用ImageView类放大图像的测试如表3所示。 表3 使用ImageView方法放大图片的效率 观察实验结果中的时间戳发现, 使用该方式在解码开始前也在不断进行刷新, 虽然该方式放大效率很高, 一般在1 ms以内, 与3.1节方法一样偶尔会有10~20 ms的突发情况, 但由于自动刷新的刷新率太高, 在播放帧率较低的即时采集传输视频, 如前面提到的9.5帧/s的视频时, 把每110 ms刷新需要的时间相加, 与前一种方式每帧刷新时使用的时间非常接近, 理论上有许多次处理时间是可以省略的。该方式不能做到随子线程调用postInvalidate方法刷新这种可控刷新方法, 也间接导致其他线程处理速度受到影响。这种放大方式得到的图像经过平滑处理, 图像边缘放大后比较模糊, 锯齿现象不明显, 然而该方式的放大算法不可修改, 不能对图像进行锐化等特殊处理, 只能按系统定义的默认放大方式放大。对于非特殊要求的应用, 该放大方式比较理想, 只需要研究一下如何将多余的刷新时间去除即可。 基于ImageView类的图像放大方法单帧图像放大效率比使用软件算法的方式高, 但缺乏3.1节方法中拥有的刷新时间的可控性, 存在多余的刷新时间使其效率下降的问题, 由于Android的API提供了源代码, 可以从源代码中进行分析。 从Android SDK的Android-15源代码包资源中找到ImageView的源代码, 研究源码后发现了方法2中出现无法控制的高刷新率的原因。ImageView的setImageBitmap方法针对图像大小和视窗边界作了一些比例运算, 并根据ImageView类中名为ScaleType的设置参数调整比例矩阵mDrawMatrix, 最终得到一个源图像尺寸和显示尺寸相关的矩阵作为全局变量。setImageBitmap函数最后调用了invalidate方法使当前画板失效, 通知主线程调用onDraw方法重画, 如果把setImageBitmap写在onDraw中用于放大图像, 则相当于在onDraw方法中调用invalidate方法循环不断地使画面失效, 主线程不断得到通知刷新画面重调用onDraw方法, 因此, 界面刷新过于频繁, 影响到其他线程的执行效率。 ImageView重写了View的onDraw方法, 根据已得到的全局比例矩阵mDrawMatrix和一些设置参数在onDraw中调用canvas的本地方法concat和translate等方式设置比例矩阵并放大图像。根据相同的原理, 3.1节中的View或SurfaceView的onDraw方法中直接加入同样一段代码canvas.concat(mMatrix), 将其他处理设置参数的过程可省略, 以达到最快速最简化效果。 该本地方法的官方解释为使用当前mMatrix作为绘制图形用的放大矩阵[15]。mMatrix可以在播放窗口建立时根据视频大小和播放窗口大小的比例预先创建, 其余解码和显示图像部分和3.1节或3.2节的方法相同, 即能得到放大后的图像。值得注意的是, 在每次调用onDraw方法时都需要调用该函数, 否则只对一次刷新有效。最终得到每张图片放大时间几乎都为0, 且能保证每110 ms刷新一次。 使用该方式避免了3.2节中不断重复的刷新过程, 同时也消除了该方法每次调用ImageView类的setImageBitmap方法时放大比例矩阵的计算时间和onDraw函数中判断过程, 在最大程度上提高了性能。如果对放大后的图像清晰度效果没有特殊要求, 这是测试平台上最为合适的即时视频放大手段。 根据实验和分析的结果表明, 基于软件算法实现拉伸图像的3.1节方法刷新时间可控制, 且能根据具体需要使用边缘锐化、 线性插值等特殊算法, 但实现的可行性受具体算法的复杂度和硬件处理器性能限制; 基于ImageView设置图片的方法存在自动刷新率太高影响其他线程的处理性能, 缺乏刷新时间可控性的缺点, 但这种方式效率较高。研究源代码发现, 如果对放大清晰度效果没有要求, 可以使用canvas的一些本地方法结合缩放矩阵达到目标效果。该方式结合了3.1节的刷新可控性和3.2节效率高的优点, 目前已经在项目的视频解码应用中得到使用。笔者从研究中体会到, Android图形化界面的编程由于资料分散, 官方文档比较简略缺少范例, 某些尚未被普遍使用的实用API还需要继续探索和普及。通过阅读控件源代码发现这些新功能是一种很好的学习方法。 参考文献: [1]陈木生. Google Android手机推出市场分析 [J]. 电子与电脑, 2008(12): 10-14. CHEN Mu-sheng. Market Analysis When Google Android Phone Release [J]. Compotech China, 2008(12): 10-14. [2]杨明极, 毕晶. 基于Android视频客户端的设计 [J]. 电视技术, 2012(3): 43-47. YANG Ming-ji, BI Jing. Design of Video Client Based on Android [J]. Video Engieering, 2012(3): 43-47. [3]宋强, 齐贵宝, 宋占伟. 基于Android系统的H.264视频监控设计 [J]. 吉林大学学报: 信息科学版, 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开发平台之旅 [M]. 北京: 机械工业出版社, 2012. WANG Yong-song. Travel of Android Development Platform [M]. Beijing: China Machine Press, 2012. [5]杨丰盛. Android应用开发揭秘 [M]. 北京: 机械工业出版社, 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平台的应用开发研究 [J]. 软件导刊, 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应用程序中的内存泄漏与规避方法 [J]. 单片机与嵌入式系统应用, 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平台图像处理算法的研究与实现 [D]. 北京: 北京邮电大学软件学院, 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]江铭炎, 李兴江, 袁东风. 2*图像插值放大处理的方法 [J]. 山东大学学报: 理学版, 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]曾生达. 几种图像放大算法原理比较与分析 [J]. 金华职业技术学院学报, 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类的设置图片方法
4 放大图像测试代码执行结果分析
4.1 使用软件算法放大图像的测试代码结果
4.2 使用ImageView类放大图像的测试代码结果
5 从源码中研究改进方式
6 结 语