2011-10-31 158 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大小,但使用上述方法質量差,掃描儀無法正常工作,如果我將原始圖片調整大小並將其大小調整爲掃描儀正在工作。

相關問題