图像大小调整质量(Java)

图像大小调整质量(Java),java,image,resize,jpeg,photo,Java,Image,Resize,Jpeg,Photo,我有一个开源应用程序,可以将照片上传到Facebook。为了节省带宽,照片在上传前会自动调整大小(Facebook规定了最大尺寸限制)。一些人抱怨过照片质量,事实上,你可以看到不同之处(请参阅一些演示图像) 因此,我的问题是,在Java中缩小图像(即照片)的“最佳”方式是什么,而不损失质量,或者至少是质量损失/工件最小 您可以查看我的当前代码(通过调整代码大小)。您使用的渲染提示是什么?通常双三次重采样是最好的。在你链接到的照片中,它们非常参差不齐,这让我觉得你是在用最近的邻居作为暗示 在链接到

我有一个开源应用程序,可以将照片上传到Facebook。为了节省带宽,照片在上传前会自动调整大小(Facebook规定了最大尺寸限制)。一些人抱怨过照片质量,事实上,你可以看到不同之处(请参阅一些演示图像)

因此,我的问题是,在Java中缩小图像(即照片)的“最佳”方式是什么,而不损失质量,或者至少是质量损失/工件最小


您可以查看我的当前代码(通过调整代码大小)。

您使用的渲染提示是什么?通常双三次重采样是最好的。在你链接到的照片中,它们非常参差不齐,这让我觉得你是在用最近的邻居作为暗示


在链接到的类中,在
paintComponent
方法中,它使用了六种不同的方法来调整图像的大小。你试过这六种方法了吗?

我试过了所有方法——包括技巧,我能说的是你最好在任何界面上使用ImageMagick。Java的图像库在这方面还没有达到标准。你需要支持如此多的格式和算法才能使它正确。

经过几次令人沮丧的实验后,我发现了以下内容,并在我的项目中采用了多通道方法

为此,我将getScaleInstance()方法复制到我的缩略图生成器类中,将我的图像读取方法更改为使用ImageIO(该方法返回一个BuffereImage),现在我非常高兴


我将结果与Photoshop CS3中的调整大小进行了比较,结果基本相同。

Phil,我不知道您最终使用了哪种解决方案,但如果您:

  • 避免JDK不支持的BuffereImage类型
  • 使用增量缩放
  • 使用增量缩放时,请坚持使用双三次
我已经用这些方法做了相当多的测试,增量缩放以及坚持良好支持的图像类型是关键——我看到Alexander提到他仍然没有在这方面获得好运气,这是一个令人沮丧的问题

大约6个月前,我发布了(Apache2)来解决“我想要这个图像的好看的缩放副本,现在就做!”这一问题,在阅读了类似这样的10个问题之后

标准用法如下所示:

BufferedImage img = ImageIO.read(...); // load image
BufferedImage scaledImg = Scalr.resize(img, 640);
第二个参数是imgscalr用于缩放图像的边界宽度和高度——即使传递了无效的维度,也要保持其比例正确——有很多,但这是最简单的用法

例如,如果Facebook将图像限制为800x600像素,您需要的用例如下所示:

BufferedImage img = ImageIO.read(...); // load image
BufferedImage scaledImg = Scalr.resize(img, Method.QUALITY, 800, 600);
这将确保图像保持在受支持的最佳图像类型中,并使用Java能够获得的最高质量方法进行缩放

在我自己的高分辨率测试中,我没有注意到使用此库/这些方法缩放的图像存在任何差异,除非图像被ImageIO加载程序放入支持较差的图像类型中——例如,GIF经常出现这种情况。如果你就这样离开它们,而不把它们从那些支持不好的类型中解救出来,它最终看起来真的很不稳定,很糟糕

原因是Java2D团队实际上对JDK可以处理的所有不同类型的BuffereImage都有不同的硬件加速管道-这些不太常见的映像类型的子集都会在Java2D的封面下使用相同的软件渲染管道,导致图像质量差,有时甚至完全不正确。这是一个很好的解释,我只是把逻辑直接写进了图书馆


最好支持的两种类型是buffereImage.TYPE_INT_RGB和_ARGB(如果您感兴趣)。

若要使用自定义质量调整图像大小,请使用thumbnailator.jar

示例代码

我希望在保持纵横比的情况下调整高质量的尺寸。 尝试了一些事情,读了一些条目。损失了两天,最后我用纯Java方法得到了最好的结果(还尝试了ImageMagick和Java图像缩放库):

public static boolean resizeUsingJavaAlgo(字符串源、文件目的、int-width、int-height)抛出IOException{
BuffereImage sourceImage=ImageIO.read(新文件输入流(源));
double ratio=(double)sourceImage.getWidth()/sourceImage.getHeight();
如果(宽度<1){
宽度=(整数)(高度*比率+0.4);
}否则,如果(高度<1){
高度=(整数)(宽度/比率+0.4);
}
图像缩放=sourceImage.getScaledInstance(宽度、高度、图像缩放面积平均值);
BuffereImage bufferedScaled=新的BuffereImage(scaled.getWidth(null)、scaled.getHeight(null)、BuffereImage.TYPE\u INT\u RGB);
Graphics2D g2d=bufferedScaled.createGraphics();
g2d.setRenderingHint(renderingHits.KEY\u插值,renderingHits.VALUE\u插值双三次);
g2d.drawImage(缩放,0,0,宽度,高度,空);
dest.createNewFile();
writeJpeg(bufferedScaled,dest.getCanonicalPath(),1.0f);
返回true;
}
/**
*编写一个JPEG文件,设置压缩质量。
*
*@param image要保存的缓冲区映像
*@param destFile目标文件(绝对或相对路径)
*@param-quality是介于0和1之间的浮点,其中1表示未压缩。
*@在写入文件时引发IOException
*/
私有静态void writeJpeg(buffereImage图像、字符串destFile、浮点质量)
抛出IOException{
ImageWriter=null;
FileImageOutputStream输出=null;
试一试{
writer=ImageIO.getImageWritersByFormatName(“jpeg”).next();
ImageWriteParam参数=writer.getDefaultWriteParam();
参数setCompressionMode(ImageWriteParam.MODE_显式);
参数设置压缩质量(质量);
输出=新文件ImageOutputStream(新文件(destFile));
writer.setOutput(输出);
IIOImage IIOImage=新IIOIma
public static boolean resizeUsingJavaAlgo(String source, File dest, int width, int height) throws IOException {
  BufferedImage sourceImage = ImageIO.read(new FileInputStream(source));
  double ratio = (double) sourceImage.getWidth()/sourceImage.getHeight();
  if (width < 1) {
    width = (int) (height * ratio + 0.4);
  } else if (height < 1) {
    height = (int) (width /ratio + 0.4);
  }

  Image scaled = sourceImage.getScaledInstance(width, height, Image.SCALE_AREA_AVERAGING);
  BufferedImage bufferedScaled = new BufferedImage(scaled.getWidth(null), scaled.getHeight(null), BufferedImage.TYPE_INT_RGB);
  Graphics2D g2d = bufferedScaled.createGraphics();
  g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
  g2d.drawImage(scaled, 0, 0, width, height, null);
  dest.createNewFile();
  writeJpeg(bufferedScaled, dest.getCanonicalPath(), 1.0f);
  return true;
}


/**
* Write a JPEG file setting the compression quality.
*
* @param image a BufferedImage to be saved
* @param destFile destination file (absolute or relative path)
* @param quality a float between 0 and 1, where 1 means uncompressed.
* @throws IOException in case of problems writing the file
*/
private static void writeJpeg(BufferedImage image, String destFile, float quality)
      throws IOException {
  ImageWriter writer = null;
  FileImageOutputStream output = null;
  try {
    writer = ImageIO.getImageWritersByFormatName("jpeg").next();
    ImageWriteParam param = writer.getDefaultWriteParam();
    param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
    param.setCompressionQuality(quality);
    output = new FileImageOutputStream(new File(destFile));
    writer.setOutput(output);
    IIOImage iioImage = new IIOImage(image, null, null);
    writer.write(null, iioImage, param);
  } catch (IOException ex) {
    throw ex;
  } finally {
    if (writer != null) {
      writer.dispose();
    }
    if (output != null) {
      output.close();
    }
  }
}