Android中缩放图片的方法

简介

在Android项目中常常需要调整原始图片的尺寸大小以适应存储、传输和图片处理等需求。在Android API中提供了一些缩放图片的方法,在项目中发现,使用Android API中的Canvas、BitmapFactory和ThumbnailUtils等类的相关方法缩放图片,锯齿感明显,图像质量不高;另外还有一些第三方的开源库专门用于在Android平台缩放图片;在FFmpeg中也提供了缩放图片和视频的方法,可以编译FFmpeg在Android平台调用相关方法。本文将总结在项目中使用上述方法的操作和实现的效果。

使用Canvas

首先创建一个Canvas对象,使用Canvas的drawBitmap(Bitmap bitmap, Rect src, Rect dst, Paint paint)方法,根据Rect dst指定bitmap绘制在canvas上的位置,从而改变bitmap的大小。需要注意的是,使用这种方法时,为了得到更好效果的输出,要添加抗锯齿处理。

/**
     * 使用Canvas
     * @param bitmap 原始的Bitmap
     * @param rect Bitmap被缩放放置的Rect
     * @return 缩放后的Bitmap
     */
    public static Bitmap scaleCanvas(Bitmap bitmap, Rect rect) {
        Bitmap newBitmap = Bitmap.createBitmap(rect.width(), rect.height(), Bitmap.Config.ARGB_8888);//创建和目标相同大小的空Bitmap
        Canvas canvas = new Canvas(newBitmap);
        Paint paint = new Paint();
        Bitmap temp = bitmap;

        //针对绘制bitmap添加抗锯齿
        PaintFlagsDrawFilter pfd= new PaintFlagsDrawFilter(0, Paint.ANTI_ALIAS_FLAG|Paint.FILTER_BITMAP_FLAG);
        paint.setFilterBitmap(true); //对Bitmap进行滤波处理
        paint.setAntiAlias(true);//设置抗锯齿
        canvas.setDrawFilter(pfd);
        canvas.drawBitmap(temp, null, rect, paint);

        return newBitmap;
    }

在我们的项目中,需要将一个尺寸较大的图片缩小后合并到另外一个图片上,那么使用这种方法缩小原始图片和原始图片的对比如下。

![原始图片](https://img-blog.csdn.net/20180620171543160?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L1R1R2VMZQ==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70) 原图 ![缩小后的图片](https://img-blog.csdn.net/20180620171729581?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L1R1R2VMZQ==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70) 使用Canvas缩小

使用Matrix

Bitmap实际上就是由像素点组成的矩阵,在Android中的Matrix主要用于对图像缩放、平移和旋转处理操作,Matrix对象调用postScale(float sx, float sy)方法设置缩放,在Bitmap的createBitmap(Bitmap source, int x, int y, int width, int height, Matrix m, boolean filter)方法中将Matrix对象传入,即可根据Matrix规则生成新的Bitmap。

    /**
     * 使用Matrix
     * @param bitmap 原始的Bitmap
     * @param width 目标宽度
     * @param height 目标高度
     * @return 缩放后的Bitmap
     */
    public static Bitmap scaleMatrix(Bitmap bitmap, int width, int height){
        int w = bitmap.getWidth();
        int h = bitmap.getHeight();
        float scaleW = width/w;
        float scaleH = height/h;
        Matrix matrix = new Matrix();
        matrix.postScale(scaleW, scaleH); // 长和宽放大缩小的比例
        return Bitmap.createBitmap(bitmap, 0, 0, w, h, matrix, true);
    }

使用ThumbnailUtils

ThumbnailUtils是Android API提供的获取缩略图的工具类,所以可以很容易地获得缩放后的Bitmap,使用该类中extractThumbnail(Bitmap source, int width, int height)方法返回获得缩放的Bitmap,该方法的参数中,source指输入待缩放的原始Bitmap,width和height分别指目标宽度和高度。
canvas
Matrix
ThumbnailUtils
上面分别是使用Canvas、Matrix和ThumbnailUtils得到的缩小后的图片。

上述方法存在的问题

使用上述方法得到的缩放图片的质量并不太好,失真比较严重。例如在我们的项目中,是将一个较大尺寸的图片缩小后贴到一个较小尺寸的图片上,合并后的图片只要稍微放大就会看到明显的锯齿感,与原始图片存在着明显的差异。
在Android中使用的图像缩放的算法都是比较简单高效的,所以输出的质量就不太好。比如使用Matrix缩小图片采用的是双线性采样(Bilinear Resampling)的算法,产生的图片斜率不太准确、细节会出现退化。对于同样的图片,使用Matrix方法缩小尺寸,和使用Photoshop缩小到相同的尺寸,可以明显分辨出差别。所以,使用Android中采用的缩放图片的算法处理效果不好,失真也更严重。

使用第三方库

有一些开源的Android平台的第三方库可以用来调整图片的尺寸,它们采用一些算法可以产生不同质量的缩放图。比如ComPressHelperCompressorLuban等压缩图片的Android库,也提供了缩放图片的方法,具体的使用方法可以参考链接页面。在实际使用中,发现其输出质量并不比使用Android API的高。使用CompressHelper缩小图片尺寸和原始图片的对比如下。

原始图片
原图
CompressHelper
使用CompressHelper缩小

使用FFmpeg

FFmpeg的libswscale类库提供了将输入的图片缩放到指定的大小,使用scale过滤器指定缩放后的宽高大小,也可以只设置宽和高中的一个,另一个置为-1,保持输出的宽高比。使用-sws_flags方法可以指定缩放所采用的算法,下面是一个指定缩放宽高和缩放算法的命令示例:ffmpeg -i test.tif -vf scale=504:376 -sws_flags bilinear out.bmp
之前的博文已经介绍过编译在Android以命令调用FFmpeg的动态库,可以通过指令调用具体的FFmpeg的处理操作。指定输入、输出路径、目标的宽高和采用的缩放算法,生成缩放图片命令的函数如下。

/**
 * FFmpeg缩放图片
 * @param input 输入路径
 * @param output 输出路径
 * @param width 目标宽度
 * @param height 目标高度
 * @param algorithm 缩放算法
 * @return FFmpeg缩放命令
 */
public static String[] scaleImage(String input, String output, int width, int height, String algorithm) {
   final int count = 8;
   String[] commands = new String[count];
   int index = 0;
   commands[index++] = "ffmpeg";
   commands[index++] = "-i";
   commands[index++] = input;
   commands[index++] = "-vf";
   commands[index++] = "scale="+width+":"+height;
   commands[index++] = "-sws_flags";
   commands[index++] = algorithm;
   commands[index++] = output;
   return commands;
}

FFmpeg中提供的各种缩放算法,采用其中bicubic、spline、sinc等许多算法产生的缩放后的图片质量要明显好于使用Android API生成的缩放图片。

原始图片
原图
ffmpeg
调用FFmpeg使用bicubic算法缩小


下面是使用Android API和使用FFmpeg产生的缩放图,肉眼可以明显分辨出二者的质量差别。
Matrix –使用Matrix
ffmpeg –使用FFmpeg

总结

在Android项目中需要缩放图片的尺寸,一般使用Android API中提供的方法就可以满足需求,而且这些方法调用简单,处理效率高。如果仅仅缩放图片,并不提倡使用CompressHelper等第三方库,这些库主要侧重于图片压缩效率,缩放图片的效果并不比Android API好。如果需要得到更高质量的缩放图片,则应该使用其它的缩放算法处理,FFmpeg中不光提供了简单的缩放图片的方法,还提供了十几种缩放算法选项,另外,根据具体的图片特征选择合适的算法,可以更好地提高缩放图片的质量。

参考

  1. https://mp.weixin.qq.com/s/H9Tz1n4O2-Aawgu7p-XL5w
  2. http://www.runoob.com/w3cnote/android-tutorial-imageview.html
  3. https://github.com/nanchen2251/CompressHelper

猜你喜欢

转载自blog.csdn.net/TuGeLe/article/details/80751870