Java将图像文件缩小到目标文件大小

时间:2019-03-24 19:24:11

标签: java android image-processing

我正在使用android应用,用户将使用相机拍摄照片。生成的照片大小可以从<1 MB到3-4 MB以上不等。我需要确保照片小于1 MB,才能将其存储在服务器上。因此,对于大于1 MB的图像,我希望将其缩小到小于1 MB,但要保持尽可能高的质量。我不担心失去(某些)质量。

我该如何实现?据我所知,大多数解决方案都涉及将图像缩小到目标尺寸(宽度X高度),但就我而言,我只关心缩小图像的结果尺寸。

非常重要的是,结果必须小于1 MB,如果存在任何错误余量,则必须在较小的文件大小上。

有没有一种方法可以计算出达到目标文件大小的宽度和高度?还是更好,在不指定宽度和高度的情况下缩小它?

2 个答案:

答案 0 :(得分:1)

  

我该如何实现?

三大选择是:

  • 降低分辨率(宽度和高度)
  • 降低JPEG质量(将位图压缩为JPEG时使用的0-100值)
  • 减少位深度(例如,从ARGB888到RGB565),尽管这主要是用于PNG,但我怀疑这会真的破坏您的照片质量
  

有没有一种方法可以计算出达到目标文件大小的宽度和高度?

不,因为它在很大程度上取决于图像的内容以及使用JPEG的压缩算法进行压缩的程度。

  

或者更好的是,在不指定宽度和高度的情况下缩小它?

要从3-4 MB减小到1 MB以下,几乎可以保证您需要降低分辨率。

但是,由于只有您知道自己认为可接受的输出,因此您将需要进行实验以确定如何最好地应用这三个选项。

答案 1 :(得分:1)

大约15年前,我使用Swing和javax.image做过,唯一可行的解​​决方案是对JPG编码器应用不同的质量级别,以找到最佳质量设置,而该图像的图像质量仍小于要求的最大值尺寸。代码是这样的(这里的代码是由实际代码合成的,从未在此版本中测试过……):

/** Create JPG version of some image with given JPG quality setting (0..1), helper method */  
private byte[] createResultBytes(BufferedImage losslessimage,float lossyquality) {
  Iterator<ImageWriter> iter = ImageIO.getImageWritersByFormatName("JPG");
  ByteArrayOutputStream baos = new ByteArrayOutputStream();
  if (iter.hasNext()) {
    ImageWriter writer = iter.next();
    ImageWriteParam iwp = writer.getDefaultWriteParam();
    iwp.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
    iwp.setCompressionQuality(lossyquality);

    MemoryCacheImageOutputStream mcios = new MemoryCacheImageOutputStream(baos);
    writer.setOutput(mcios);

    // workaround for alpha-channel problems with lossy images: Seems to be non-functional for some pictures, anyway.
    BufferedImage img2=new BufferedImage(losslessimage.getWidth(),losslessimage.getHeight(),BufferedImage.TYPE_INT_RGB);
    Graphics g=img2.getGraphics();
    g.drawImage(new ImageIcon(losslessimage).getImage(),0,0,null);
    g.dispose();
    IIOImage iioimg = new IIOImage(img2, null, null);
    try {
      writer.write(null, iioimg, iwp);
      mcios.close();
    }
    catch (IOException ioe) {}
  }
  return baos.toByteArray();
}

/** Create JPG version of some image with best JPG quality where the file size is still not larger than maxsize. */
public byte[] getLossyImage(BufferedImage losslessimage,int maxsize,float maxquality) {
  byte[] resultbytes=createResultBytes(losslessimage,maxquality);
  if (resultbytes.length>size) {
    int minsize=(int)(size*.9f);
    float newq=maxquality/2f;
    resultbytes=createResultBytes(losslessimage,newq);
    float qdiff=newq/2f;
    while ((resultbytes.length<minsize || resultbytes.length>size) &&  diff>.02f) {
      newq+=(resultbytes.length<minsize?qdiff:-qdiff);
      resultbytes=createResultBytes(losslessimage,newq);
      qdiff=qdiff/2f;
    }
  }
  return resultbytes;
}

请注意,我仅通过JPG质量设置来更改文件大小,而不是通过自动更改图像大小来更改文件大小。如果要这样,则必须首先应用这些大小更改。