• 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也是相对的变小了;

    最后把自己总结的工具类贴出来:
    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. }  

    延伸阅读
    android图片压缩总结

    一.图片的存在形式

    1.文件形式(即以二进制形式存在于硬盘上)
    2.流的形式(即以二进制形式存在于内存中)
    3.Bitmap形式
    这三种形式的区别: 文件形式和流的形式对图片体积大小并没有影响,也就是说,如果你手机SD卡上的如果是100K,那 么通过流的形式读到内存中,也一定是占100K的内存,注意是流的形式,不是Bitmap的形式,当图片以Bitmap的形式存在时,其占用的内存会瞬间 变大, 我试过500K文件形式的图片加载到内存,以Bitmap形式存在时,占用内存将近10M,当然这个增大的倍数并不是固定的

    检测图片三种形式大小的方法:
    文件形式: file.length()
    流的形式: 讲图片文件读到内存输入流中,看它的byte数
    Bitmap:    bitmap.getByteCount()

    二.常见的压缩方式

    1. 将图片保存到本地时进行压缩, 即将图片从Bitmap形式变为File形式时进行压缩,
        特点是:  File形式的图片确实被压缩了, 但是当你重新读取压缩后的file为 Bitmap是,它占用的内存并没有改变   
    [java] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. publicstaticvoid new int;  
    2. while > ) {   
    3. ;  
    4. try new catch     }  
    方法说明: 该方法是压缩图片的质量, 注意它不会减少图片的像素,比方说, 你的图片是300K的, 1280*700像素的, 经过该方法压缩后, File形式的图片是在100以下, 以方便上传服务器, 但是你BitmapFactory.decodeFile到内存中,变成Bitmap时,它的像素仍然是1280*700, 计算图片像素的方法是 bitmap.getWidth()和bitmap.getHeight(), 图片是由像素组成的, 每个像素又包含什么呢? 熟悉PS的人知道, 图片是有色相,明度和饱和度构成的. 

    该方法的官方文档也解释说, 它会让图片重新构造, 但是有可能图像的位深(即色深)和每个像素的透明度会变化,JPEG onlysupports opaque(不透明), 也就是说以jpeg格式压缩后, 原来图片中透明的元素将消失.所以这种格式很可能造成失真

    既然它是改变了图片的显示质量, 达到了对File形式的图片进行压缩, 图片的像素没有改变的话, 那重新读取经过压缩的file为Bitmap时, 它占用的内存并不会少.(不相信的可以试试)

    因为: bitmap.getByteCount() 是计算它的像素所占用的内存, 请看官方解释: Returns the number of bytes used to store this bitmap's pixels.

    2.   将图片从本地读到内存时,进行压缩 ,即图片从File形式变为Bitmap形式
           特点: 通过设置采样率, 减少图片的像素, 达到对内存中的Bitmap进行压缩
           先看一个方法: 该方法是对内存中的Bitmap进行质量上的压缩, 由上面的理论可以得出该方法是无效的, 而且也是没有必要的,因为你已经将它读到内存中了,再压缩多此一举, 尽管在获取系统相册图片时,某些手机会直接返回一个Bitmap,但是这种情况下, 返回的Bitmap都是经过压缩的, 它不可能直接返回一个原声的Bitmap形式的图片, 后果可想而知
    [java] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. private new int;  
    2. , baos);  
    3. while > ) {   
    4. ;  
    5. new nullnull return     }  
      再看一个方法:
    [java] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1.     private new true  
    2. false int int float  
    3. float  
    4. int;  
    5. if int elseif int if)  
    6. ;  
    7.   
    8.   
    9. true  
    10. true  
    11. //      return compressBmpFromBmp(bitmap);//原来的方法调用了这个方法企图进行二次压缩   
    12. return     }  


    方法说明: 该方法就是对Bitmap形式的图片进行压缩, 也就是通过设置采样率, 减少Bitmap的像素, 从而减少了它所占用的内存

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

      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.     } 
  • 相关阅读:
    Java代码检测工具
    java编程工具
    100-days: The one day
    前端书籍整理
    vue 学习笔记(二)
    从零开始写一个npm包及上传
    Vue Baidu Map 插件的使用
    对数组对象进行过滤
    使用css方法使footer保持在页面的最底部
    判断是第一次打开界面?还是刷新
  • 原文地址:https://www.cnblogs.com/huangjianboke/p/5569978.html
Copyright © 2020-2023  润新知