Android图片压缩(质量压缩和尺寸压缩)

在网上调查了图片压缩的方法并实装后,大致上可以认为有两类压缩:质量压缩(不改变图片的尺寸)和尺寸压缩(相当于是像素上的压缩);质量压缩一般可用于上传大图前的处理,这样就可以节省一定的流量,毕竟现在的手机拍照都能达到3M左右了,尺寸压缩一般可用于生成缩略图。
两种方法都实装在了我的项目中,结果却发现在质量压缩的模块中,本来1.9M的图片压缩后反而变成3M多了,很是奇怪,再做了进一步调查终于知道原因了。下面这个博客说的比较清晰:

android图片压缩总结

总结来看,图片有三种存在形式:硬盘上时是file,网络传输时是stream,内存中是stream或bitmap,所谓的质量压缩,它其实只能实现对file的影响,你可以把一个file转成bitmap再转成file,或者直接将一个bitmap转成file时,这个最终的file是被压缩过的,但是中间的bitmap并没有被压缩(或者说几乎没有被压缩,我不确定),因为bigmap在内存中的大小是按像素计算的,也就是width * height,对于质量压缩,并不会改变图片的像素,所以就算质量被压缩了,但是bitmap在内存的占有率还是没变小,但你做成file时,它确实变小了;

而尺寸压缩由于是减小了图片的像素,所以它直接对bitmap产生了影响,当然最终的file也是相对的变小了;

最后把自己总结的工具类贴出来:
[java] view plain copy
  1. import java.io.ByteArrayInputStream;  
  2. import java.io.ByteArrayOutputStream;  
  3. import java.io.File;  
  4. import java.io.FileNotFoundException;  
  5. import java.io.FileOutputStream;  
  6. import java.io.IOException;  
  7.   
  8. import android.graphics.Bitmap;  
  9. import android.graphics.Bitmap.Config;  
  10. import android.graphics.BitmapFactory;  
  11.   
  12. /** 
  13.  * Image compress factory class 
  14.  *  
  15.  * @author  
  16.  * 
  17.  */  
  18. public class ImageFactory {  
  19.   
  20.     /** 
  21.      * Get bitmap from specified image path 
  22.      *  
  23.      * @param imgPath 
  24.      * @return 
  25.      */  
  26.     public Bitmap getBitmap(String imgPath) {  
  27.         // Get bitmap through image path  
  28.         BitmapFactory.Options newOpts = new BitmapFactory.Options();  
  29.         newOpts.inJustDecodeBounds = false;  
  30.         newOpts.inPurgeable = true;  
  31.         newOpts.inInputShareable = true;  
  32.         // Do not compress  
  33.         newOpts.inSampleSize = 1;  
  34.         newOpts.inPreferredConfig = Config.RGB_565;  
  35.         return BitmapFactory.decodeFile(imgPath, newOpts);  
  36.     }  
  37.       
  38.     /** 
  39.      * Store bitmap into specified image path 
  40.      *  
  41.      * @param bitmap 
  42.      * @param outPath 
  43.      * @throws FileNotFoundException  
  44.      */  
  45.     public void storeImage(Bitmap bitmap, String outPath) throws FileNotFoundException {  
  46.         FileOutputStream os = new FileOutputStream(outPath);  
  47.         bitmap.compress(Bitmap.CompressFormat.JPEG, 100, os);  
  48.     }  
  49.       
  50.     /** 
  51.      * Compress image by pixel, this will modify image width/height.  
  52.      * Used to get thumbnail 
  53.      *  
  54.      * @param imgPath image path 
  55.      * @param pixelW target pixel of width 
  56.      * @param pixelH target pixel of height 
  57.      * @return 
  58.      */  
  59.     public Bitmap ratio(String imgPath, float pixelW, float pixelH) {  
  60.         BitmapFactory.Options newOpts = new BitmapFactory.Options();    
  61.         // 开始读入图片,此时把options.inJustDecodeBounds 设回true,即只读边不读内容  
  62.         newOpts.inJustDecodeBounds = true;  
  63.         newOpts.inPreferredConfig = Config.RGB_565;  
  64.         // Get bitmap info, but notice that bitmap is null now    
  65.         Bitmap bitmap = BitmapFactory.decodeFile(imgPath,newOpts);  
  66.             
  67.         newOpts.inJustDecodeBounds = false;    
  68.         int w = newOpts.outWidth;    
  69.         int h = newOpts.outHeight;    
  70.         // 想要缩放的目标尺寸  
  71.         float hh = pixelH;// 设置高度为240f时,可以明显看到图片缩小了  
  72.         float ww = pixelW;// 设置宽度为120f,可以明显看到图片缩小了  
  73.         // 缩放比。由于是固定比例缩放,只用高或者宽其中一个数据进行计算即可    
  74.         int be = 1;//be=1表示不缩放    
  75.         if (w > h && w > ww) {//如果宽度大的话根据宽度固定大小缩放    
  76.             be = (int) (newOpts.outWidth / ww);    
  77.         } else if (w < h && h > hh) {//如果高度高的话根据宽度固定大小缩放    
  78.             be = (int) (newOpts.outHeight / hh);    
  79.         }    
  80.         if (be <= 0) be = 1;    
  81.         newOpts.inSampleSize = be;//设置缩放比例  
  82.         // 开始压缩图片,注意此时已经把options.inJustDecodeBounds 设回false了  
  83.         bitmap = BitmapFactory.decodeFile(imgPath, newOpts);  
  84.         // 压缩好比例大小后再进行质量压缩  
  85. //        return compress(bitmap, maxSize); // 这里再进行质量压缩的意义不大,反而耗资源,删除  
  86.         return bitmap;  
  87.     }  
  88.       
  89.     /** 
  90.      * Compress image by size, this will modify image width/height.  
  91.      * Used to get thumbnail 
  92.      *  
  93.      * @param image 
  94.      * @param pixelW target pixel of width 
  95.      * @param pixelH target pixel of height 
  96.      * @return 
  97.      */  
  98.     public Bitmap ratio(Bitmap image, float pixelW, float pixelH) {  
  99.         ByteArrayOutputStream os = new ByteArrayOutputStream();  
  100.         image.compress(Bitmap.CompressFormat.JPEG, 100, os);  
  101.         if( os.toByteArray().length / 1024>1024) {//判断如果图片大于1M,进行压缩避免在生成图片(BitmapFactory.decodeStream)时溢出      
  102.             os.reset();//重置baos即清空baos    
  103.             image.compress(Bitmap.CompressFormat.JPEG, 50, os);//这里压缩50%,把压缩后的数据存放到baos中    
  104.         }    
  105.         ByteArrayInputStream is = new ByteArrayInputStream(os.toByteArray());    
  106.         BitmapFactory.Options newOpts = new BitmapFactory.Options();    
  107.         //开始读入图片,此时把options.inJustDecodeBounds 设回true了    
  108.         newOpts.inJustDecodeBounds = true;  
  109.         newOpts.inPreferredConfig = Config.RGB_565;  
  110.         Bitmap bitmap = BitmapFactory.decodeStream(is, null, newOpts);    
  111.         newOpts.inJustDecodeBounds = false;    
  112.         int w = newOpts.outWidth;    
  113.         int h = newOpts.outHeight;    
  114.         float hh = pixelH;// 设置高度为240f时,可以明显看到图片缩小了  
  115.         float ww = pixelW;// 设置宽度为120f,可以明显看到图片缩小了  
  116.         //缩放比。由于是固定比例缩放,只用高或者宽其中一个数据进行计算即可    
  117.         int be = 1;//be=1表示不缩放    
  118.         if (w > h && w > ww) {//如果宽度大的话根据宽度固定大小缩放    
  119.             be = (int) (newOpts.outWidth / ww);    
  120.         } else if (w < h && h > hh) {//如果高度高的话根据宽度固定大小缩放    
  121.             be = (int) (newOpts.outHeight / hh);    
  122.         }    
  123.         if (be <= 0) be = 1;    
  124.         newOpts.inSampleSize = be;//设置缩放比例    
  125.         //重新读入图片,注意此时已经把options.inJustDecodeBounds 设回false了    
  126.         is = new ByteArrayInputStream(os.toByteArray());    
  127.         bitmap = BitmapFactory.decodeStream(is, null, newOpts);  
  128.         //压缩好比例大小后再进行质量压缩  
  129. //      return compress(bitmap, maxSize); // 这里再进行质量压缩的意义不大,反而耗资源,删除  
  130.         return bitmap;  
  131.     }  
  132.       
  133.     /** 
  134.      * Compress by quality,  and generate image to the path specified 
  135.      *  
  136.      * @param image 
  137.      * @param outPath 
  138.      * @param maxSize target will be compressed to be smaller than this size.(kb) 
  139.      * @throws IOException  
  140.      */  
  141.     public void compressAndGenImage(Bitmap image, String outPath, int maxSize) throws IOException {  
  142.         ByteArrayOutputStream os = new ByteArrayOutputStream();  
  143.         // scale  
  144.         int options = 100;  
  145.         // Store the bitmap into output stream(no compress)  
  146.         image.compress(Bitmap.CompressFormat.JPEG, options, os);    
  147.         // Compress by loop  
  148.         while ( os.toByteArray().length / 1024 > maxSize) {  
  149.             // Clean up os  
  150.             os.reset();  
  151.             // interval 10  
  152.             options -= 10;  
  153.             image.compress(Bitmap.CompressFormat.JPEG, options, os);  
  154.         }  
  155.           
  156.         // Generate compressed image file  
  157.         FileOutputStream fos = new FileOutputStream(outPath);    
  158.         fos.write(os.toByteArray());    
  159.         fos.flush();    
  160.         fos.close();    
  161.     }  
  162.       
  163.     /** 
  164.      * Compress by quality,  and generate image to the path specified 
  165.      *  
  166.      * @param imgPath 
  167.      * @param outPath 
  168.      * @param maxSize target will be compressed to be smaller than this size.(kb) 
  169.      * @param needsDelete Whether delete original file after compress 
  170.      * @throws IOException  
  171.      */  
  172.     public void compressAndGenImage(String imgPath, String outPath, int maxSize, boolean needsDelete) throws IOException {  
  173.         compressAndGenImage(getBitmap(imgPath), outPath, maxSize);  
  174.           
  175.         // Delete original file  
  176.         if (needsDelete) {  
  177.             File file = new File (imgPath);  
  178.             if (file.exists()) {  
  179.                 file.delete();  
  180.             }  
  181.         }  
  182.     }  
  183.       
  184.     /** 
  185.      * Ratio and generate thumb to the path specified 
  186.      *  
  187.      * @param image 
  188.      * @param outPath 
  189.      * @param pixelW target pixel of width 
  190.      * @param pixelH target pixel of height 
  191.      * @throws FileNotFoundException 
  192.      */  
  193.     public void ratioAndGenThumb(Bitmap image, String outPath, float pixelW, float pixelH) throws FileNotFoundException {  
  194.         Bitmap bitmap = ratio(image, pixelW, pixelH);  
  195.         storeImage( bitmap, outPath);  
  196.     }  
  197.       
  198.     /** 
  199.      * Ratio and generate thumb to the path specified 
  200.      *  
  201.      * @param image 
  202.      * @param outPath 
  203.      * @param pixelW target pixel of width 
  204.      * @param pixelH target pixel of height 
  205.      * @param needsDelete Whether delete original file after compress 
  206.      * @throws FileNotFoundException 
  207.      */  
  208.     public void ratioAndGenThumb(String imgPath, String outPath, float pixelW, float pixelH, boolean needsDelete) throws FileNotFoundException {  
  209.         Bitmap bitmap = ratio(imgPath, pixelW, pixelH);  
  210.         storeImage( bitmap, outPath);  
  211.           
  212.         // Delete original file  
  213.                 if (needsDelete) {  
  214.                     File file = new File (imgPath);  
  215.                     if (file.exists()) {  
  216.                         file.delete();  
  217.                     }  
  218.                 }  
  219.     }  
  220.       
  221. }  


如果上面的工具类不满足你,那么看看下面的方法。

一、图片质量压缩
[java] view plain copy
  1. /** 
  2.  * 质量压缩方法 
  3.  * 
  4.  * @param image 
  5.  * @return 
  6.  */  
  7. public static Bitmap compressImage(Bitmap image) {  
  8.   
  9.     ByteArrayOutputStream baos = new ByteArrayOutputStream();  
  10.     image.compress(Bitmap.CompressFormat.JPEG, 100, baos);// 质量压缩方法,这里100表示不压缩,把压缩后的数据存放到baos中  
  11.     int options = 90;  
  12.   
  13.     while (baos.toByteArray().length / 1024 > 100) { // 循环判断如果压缩后图片是否大于100kb,大于继续压缩  
  14.         baos.reset(); // 重置baos即清空baos  
  15.         image.compress(Bitmap.CompressFormat.JPEG, options, baos);// 这里压缩options%,把压缩后的数据存放到baos中  
  16.         options -= 10;// 每次都减少10  
  17.     }  
  18.     ByteArrayInputStream isBm = new ByteArrayInputStream(baos.toByteArray());// 把压缩后的数据baos存放到ByteArrayInputStream中  
  19.     Bitmap bitmap = BitmapFactory.decodeStream(isBm, nullnull);// 把ByteArrayInputStream数据生成图片  
  20.     return bitmap;  
  21. }  

二、按比例大小压缩 (路径获取图片)

[java] view plain copy
  1. /** 
  2.  * 图片按比例大小压缩方法 
  3.  * 
  4.  * @param srcPath (根据路径获取图片并压缩) 
  5.  * @return 
  6.  */  
  7. public static Bitmap getimage(String srcPath) {  
  8.   
  9.     BitmapFactory.Options newOpts = new BitmapFactory.Options();  
  10.     // 开始读入图片,此时把options.inJustDecodeBounds 设回true了  
  11.     newOpts.inJustDecodeBounds = true;  
  12.     Bitmap bitmap = BitmapFactory.decodeFile(srcPath, newOpts);// 此时返回bm为空  
  13.   
  14.     newOpts.inJustDecodeBounds = false;  
  15.     int w = newOpts.outWidth;  
  16.     int h = newOpts.outHeight;  
  17.     // 现在主流手机比较多是800*480分辨率,所以高和宽我们设置为  
  18.     float hh = 800f;// 这里设置高度为800f  
  19.     float ww = 480f;// 这里设置宽度为480f  
  20.     // 缩放比。由于是固定比例缩放,只用高或者宽其中一个数据进行计算即可  
  21.     int be = 1;// be=1表示不缩放  
  22.     if (w > h && w > ww) {// 如果宽度大的话根据宽度固定大小缩放  
  23.         be = (int) (newOpts.outWidth / ww);  
  24.     } else if (w < h && h > hh) {// 如果高度高的话根据宽度固定大小缩放  
  25.         be = (int) (newOpts.outHeight / hh);  
  26.     }  
  27.     if (be <= 0)  
  28.         be = 1;  
  29.     newOpts.inSampleSize = be;// 设置缩放比例  
  30.     // 重新读入图片,注意此时已经把options.inJustDecodeBounds 设回false了  
  31.     bitmap = BitmapFactory.decodeFile(srcPath, newOpts);  
  32.     return compressImage(bitmap);// 压缩好比例大小后再进行质量压缩  
  33. }  
三、按比例大小压缩 (Bitmap)

[java] view plain copy
  1. /** 
  2.  * 图片按比例大小压缩方法 
  3.  * 
  4.  * @param image (根据Bitmap图片压缩) 
  5.  * @return 
  6.  */  
  7. public static Bitmap compressScale(Bitmap image) {  
  8.   
  9.     ByteArrayOutputStream baos = new ByteArrayOutputStream();  
  10.     image.compress(Bitmap.CompressFormat.JPEG, 100, baos);  
  11.   
  12.     // 判断如果图片大于1M,进行压缩避免在生成图片(BitmapFactory.decodeStream)时溢出  
  13.     if (baos.toByteArray().length / 1024 > 1024) {  
  14.         baos.reset();// 重置baos即清空baos  
  15.         image.compress(Bitmap.CompressFormat.JPEG, 80, baos);// 这里压缩50%,把压缩后的数据存放到baos中  
  16.     }  
  17.     ByteArrayInputStream isBm = new ByteArrayInputStream(baos.toByteArray());  
  18.     BitmapFactory.Options newOpts = new BitmapFactory.Options();  
  19.     // 开始读入图片,此时把options.inJustDecodeBounds 设回true了  
  20.     newOpts.inJustDecodeBounds = true;  
  21.     Bitmap bitmap = BitmapFactory.decodeStream(isBm, null, newOpts);  
  22.     newOpts.inJustDecodeBounds = false;  
  23.     int w = newOpts.outWidth;  
  24.     int h = newOpts.outHeight;  
  25.     Log.i(TAG, w + "---------------" + h);  
  26.     // 现在主流手机比较多是800*480分辨率,所以高和宽我们设置为  
  27.     // float hh = 800f;// 这里设置高度为800f  
  28.     // float ww = 480f;// 这里设置宽度为480f  
  29.     float hh = 512f;  
  30.     float ww = 512f;  
  31.     // 缩放比。由于是固定比例缩放,只用高或者宽其中一个数据进行计算即可  
  32.     int be = 1;// be=1表示不缩放  
  33.     if (w > h && w > ww) {// 如果宽度大的话根据宽度固定大小缩放  
  34.         be = (int) (newOpts.outWidth / ww);  
  35.     } else if (w < h && h > hh) { // 如果高度高的话根据高度固定大小缩放  
  36.         be = (int) (newOpts.outHeight / hh);  
  37.     }  
  38.     if (be <= 0)  
  39.         be = 1;  
  40.     newOpts.inSampleSize = be; // 设置缩放比例  
  41.     // newOpts.inPreferredConfig = Config.RGB_565;//降低图片从ARGB888到RGB565  
  42.   
  43.     // 重新读入图片,注意此时已经把options.inJustDecodeBounds 设回false了  
  44.     isBm = new ByteArrayInputStream(baos.toByteArray());  
  45.     bitmap = BitmapFactory.decodeStream(isBm, null, newOpts);  
  46.   
  47.     return compressImage(bitmap);// 压缩好比例大小后再进行质量压缩  
  48.   
  49.     //return bitmap;  
  50. }  





--------------------------------------------------------------------------------------------------------------------------------

分享个按照图片尺寸压缩:

[java] view plain copy
  1. public static void compressPicture(String srcPath, String desPath) {  
  2.         FileOutputStream fos = null;  
  3.         BitmapFactory.Options op = new BitmapFactory.Options();  
  4.   
  5.         // 开始读入图片,此时把options.inJustDecodeBounds 设回true了  
  6.         op.inJustDecodeBounds = true;  
  7.         Bitmap bitmap = BitmapFactory.decodeFile(srcPath, op);  
  8.         op.inJustDecodeBounds = false;  
  9.   
  10.         // 缩放图片的尺寸  
  11.         float w = op.outWidth;  
  12.         float h = op.outHeight;  
  13.         float hh = 1024f;//  
  14.         float ww = 1024f;//  
  15.         // 最长宽度或高度1024  
  16.         float be = 1.0f;  
  17.         if (w > h && w > ww) {  
  18.             be = (float) (w / ww);  
  19.         } else if (w < h && h > hh) {  
  20.             be = (float) (h / hh);  
  21.         }  
  22.         if (be <= 0) {  
  23.             be = 1.0f;  
  24.         }  
  25.         op.inSampleSize = (int) be;// 设置缩放比例,这个数字越大,图片大小越小.  
  26.         // 重新读入图片,注意此时已经把options.inJustDecodeBounds 设回false了  
  27.         bitmap = BitmapFactory.decodeFile(srcPath, op);  
  28.         int desWidth = (int) (w / be);  
  29.         int desHeight = (int) (h / be);  
  30.         bitmap = Bitmap.createScaledBitmap(bitmap, desWidth, desHeight, true);  
  31.         try {  
  32.             fos = new FileOutputStream(desPath);  
  33.             if (bitmap != null) {  
  34.                 bitmap.compress(Bitmap.CompressFormat.JPEG, 100, fos);  
  35.             }  
  36.         } catch (FileNotFoundException e) {  
  37.             e.printStackTrace();  
  38.         }  
  39.     }  


需要注意两个问题:

一、调用getDrawingCache()前先要测量,否则的话得到的bitmap为null,这个我在OnCreate()、OnStart()、OnResume()方法里都试验过。


二、当调用bitmap.compress(CompressFormat.JPEG, 100, fos);保存为图片时发现图片背景为黑色,如下图:


这时只需要改成用png保存就可以了,bitmap.compress(CompressFormat.PNG, 100, fos);,如下图:


你可能感兴趣的:(Android)