2011-10-31 128 views
11

我制作了此代码以使用两个因素调整图像大小。它可以工作,但调整后的图像质量非常差!你可以帮我吗?重新调整图像的大小而不会丢失质量

这是代码

public class ImageTest { 

private static final int factor1 = 3; 
private static final int factor2 = 4; 
public static void main(String [] args){ 

    JFileChooser cs = new JFileChooser(); 
    cs.setFileSelectionMode(cs.DIRECTORIES_ONLY); 
    int i = cs.showOpenDialog(null); 
    if(i==cs.APPROVE_OPTION){ 
     File f = cs.getSelectedFile(); 
     File[] ff = f.listFiles(); 
     for(int j=0;j<ff.length;j++){ 
      String end = ff[j].getName().substring(ff[j].getName().indexOf(".")+1); 
      System.out.println(end); 
      try{ 
       BufferedImage originalImage = ImageIO.read(ff[j]); 
       int type = originalImage.getType() == 0? BufferedImage.TYPE_INT_ARGB : originalImage.getType(); 
       BufferedImage resizeImageJpg = resizeImageWithHint(originalImage, type); 
       ImageIO.write(resizeImageJpg, end, new File("pr/"+ff[j].getName())); 
      }catch(IOException e){ 
       e.printStackTrace(); 
      } 
     } 
    } 


} 
private static BufferedImage resizeImageWithHint(BufferedImage originalImage, int type){ 
    int IMG_WIDTH = (originalImage.getWidth()*factor1)/factor2; 
    int IMG_HEIGHT = (originalImage.getHeight()*factor1)/factor2; 
    BufferedImage resizedImage = new BufferedImage(IMG_WIDTH, IMG_HEIGHT, type); 
    Graphics2D g = resizedImage.createGraphics(); 
    g.drawImage(originalImage, 0, 0, IMG_WIDTH, IMG_HEIGHT, null); 
    g.dispose();  
    g.setComposite(AlphaComposite.Src); 

    g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, 
      RenderingHints.VALUE_INTERPOLATION_BILINEAR); 
    g.setRenderingHint(RenderingHints.KEY_RENDERING, 
      RenderingHints.VALUE_RENDER_QUALITY); 
    g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, 
      RenderingHints.VALUE_ANTIALIAS_ON); 

    return resizedImage; 
} 
    } 

我在网上看到resizeImageWithHint的范围内,这样做不失去质量..但它确实!为什么?你能帮助我吗?

回答

27

我曾阅读过的最好的文章是The Perils of Image.getScaledInstance()(网络存档)。

简而言之:您需要使用多个调整大小的步骤才能获得良好的图像。文章中的助手方法:

public BufferedImage getScaledInstance(BufferedImage img, 
             int targetWidth, 
             int targetHeight, 
             Object hint, 
             boolean higherQuality) 
{ 
    int type = (img.getTransparency() == Transparency.OPAQUE) ? 
     BufferedImage.TYPE_INT_RGB : BufferedImage.TYPE_INT_ARGB; 
    BufferedImage ret = (BufferedImage)img; 
    int w, h; 
    if (higherQuality) { 
     // Use multi-step technique: start with original size, then 
     // scale down in multiple passes with drawImage() 
     // until the target size is reached 
     w = img.getWidth(); 
     h = img.getHeight(); 
    } else { 
     // Use one-step technique: scale directly from original 
     // size to target size with a single drawImage() call 
     w = targetWidth; 
     h = targetHeight; 
    } 

    do { 
     if (higherQuality && w > targetWidth) { 
      w /= 2; 
      if (w < targetWidth) { 
       w = targetWidth; 
      } 
     } 

     if (higherQuality && h > targetHeight) { 
      h /= 2; 
      if (h < targetHeight) { 
       h = targetHeight; 
      } 
     } 

     BufferedImage tmp = new BufferedImage(w, h, type); 
     Graphics2D g2 = tmp.createGraphics(); 
     g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, hint); 
     g2.drawImage(ret, 0, 0, w, h, null); 
     g2.dispose(); 

     ret = tmp; 
    } while (w != targetWidth || h != targetHeight); 

    return ret; 
} 
+0

是的,这篇文章很好,imgscalr和java-image-scaling都引用了它,但是结果又不同,我认为java-image-scaling可以应用的过滤器会有所作为。 – stivlo

+0

我的图片是PNG ...可以发布我的multistep代码?请? – JackTurky

+1

好文章,真的有助于提高缩放图像的质量。改变'w/= 2'为'w/= 1.2'会给我更好的质量(更多的循环会被执行,所以质量更好) –

1

是的,我有同样的问题,解决它们,请read my question(答案被嵌入的问题)。我试图imgscalrJava的图像缩放库,发现第二个更好的质量。靠近显示器以了解缩略图示例之间的差异。

尽管我最初的想法,调整图像大小,似乎很复杂的东西,你不想自己做。例如,我告诉java-image-scaling使用ResampleFilters.getLanczos3Filter()有更好的结果。

它还涉及如何保存JPG与质量比标准75高,这尤其会产生坏的结果的缩略图。

我还写了一个小类,名为MyImage以帮助执行常见任务,例如从字节数组读取图像,从文件读取图像,仅指定宽度或仅指定高度进行缩放,指定边界框进行缩放,缩放通过指定宽度和高度并添加一个白色条纹使图像不失真并写入JPG文件。

8

以下代码为我带来了最高质量的调整大小,并保留了纵横比。 尝试了几件事,并阅读其他答案中的几个条目。失去了两天,最后我得到了最好的结果与普通的Java方法(也尝试ImageMagick的和Java的图像缩放库):

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(); 
    } 
    } 
} 
+0

@nfechner发布的代码是对您的改进。如果图片很大,质量就不会像你期望的那么好。正如文中所写:“质量差异的原因是由于使用了不同的过滤算法。如果缩小比例超过两倍,则由于从源图像中采样像素的方式,BILINEAR和BICUBIC算法倾向于丢失信息。通过使用多步渲染方式,调整大小的图像会更好。 –

+0

我用我的眼睛和缩小图像到320px左右,这在我看来产生了更好的质量。我明白你在说什么,但是,我对@nfechner算法得到的结果并不满意,我也尝试过这种方法...... –

2

如果图像源是png然后使用这样的:

Image imgSmall = imgBig.getScaledInstance(
     targetWidth, targetHeight, Image.SCALE_SMOOTH); 

如果要调整JPEG或GIF没有松动太多的质量,我在2010年做了一个库这样的:beautylib on github在内部使用这个其他图书馆:java-image-scaling。你可以直接看到源代码找到有用的东西:https://github.com/felipelalli/beautylib/blob/master/src/br/eti/fml/beautylib/ResizeImage.java

+0

它只是推荐用于“png”吗? – Tiny

+0

我不记得了,对不起。 –

2

没有答案会帮助你获得你想要的真实质量。包括thumblailator。罐子在您的项目(下载构成这里):

https://code.google.com/p/thumbnailator/

https://code.google.com/p/thumbnailator/wiki/Downloads?tm=2

然后第一次上传的图像(如文件,而Thumbnailator - 它的用途是创建拇指,但您可以创建大型图片与它当然),并调整到你想要的每个尺寸(例如Thumbnailator 800x600)。质量会很好。我在寻找这么长时间,这个.jar帮助我实现了我想要的。

+0

嗨,谢谢你的分享!这是一个旧帖子,但我会尝试并记住未来。谢谢! – JackTurky

5

知道的问题是老...我试过不同的解决方案,然后网上冲浪,我得到了最好的结果使用getScaledInstance(),提供Image.SCALE_SMOOTH作为论点。事实上,由此产生的图像质量真的更好。 我下面的代码:

final int THUMB_SIDE = 140; 
try { 
    BufferedImage masterImage = ImageIO.read(startingImage); 
    BufferedImage thumbImage = new BufferedImage(THUMB_SIDE, THUMB_SIDE, BufferedImage.TYPE_INT_ARGB); 
    Graphics2D g2d = thumbImage.createGraphics(); 
    g2d.drawImage(masterImage.getScaledInstance(THUMB_SIDE, THUMB_SIDE, Image.SCALE_SMOOTH), 0, 0, THUMB_SIDE, THUMB_SIDE, null); 
    g2d.dispose(); 
    String thumb_path = path.substring(0, path.indexOf(".png")) + "_thumb.png"; 
    ImageIO.write(thumbImage, "png", new File(thumb_path)); 
} catch (IOException e) { 
    e.printStackTrace(); 
} 
+0

仅建议使用“png”吗? – Tiny

+0

@Tiny据我记忆,我只用png测试过它... – user3564042

-1

答:删除提示VALUE_INTERPOLATION_BILINEAR,并VALUE_RENDERING_QUALITY

实施例:

public static BufferedImage resizeImage(BufferedImage image, int width, int height) { 

    // Temporary image 

    BufferedImage tmp = image; 

    // Result image 

    BufferedImage result = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); 

    // Graphics object 

    Graphics2D graphics = (Graphics2D)result.createGraphics(); 

    // Add rendering hints 

    graphics.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY); 
    graphics.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_ENABLE); 

    // Draw tmp 

    graphics.drawImage(tmp, 0, 0, width, height, null); 

    // Dispose of graphics object 

    graphics.dispose(); 

    // Return image result 

    return result; 
} 

注:出于某种原因,所述提示VALUE_INTERPOLATION_BILINEARVALUE_RENDERING_QUALITY模糊图像被调整大小。

0

发布的所有方法都不适用于我我必须减少QrCode大小,但使用上述方法质量差,扫描仪无法正常工作,如果我将原始图片调整大小并将其大小调整为扫描仪正在工作。

相关问题