簡體   English   中英

在不降低質量的情況下重新調整圖像大小

[英]Re-sizing an image without losing quality

我制作了這段代碼以使用兩個因素調整圖像大小。 它可以工作,但是調整大小后圖像質量很差! 你能幫助我嗎?

這是代碼

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 是在范圍內完成的,以免質量下降..但確實如此! 為什么? 你能幫我解決這個問題嗎?

我讀過的關於這個主題的最好的文章是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;
}

以下代碼為我生成了最高質量的調整大小,並保留了縱橫比。 嘗試了一些事情並閱讀了其他答案中提供的幾個條目。 失去了兩天,最后我用普通的 Java 方法得到了最好的結果(也嘗試了 ImageMagick 和 java-image-scaling 庫):

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

知道問題很老......我嘗試了不同的解決方案,然后在網上沖浪,我使用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();
}

如果您的圖像源是png則使用如下:

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

如果你想在不丟失太多質量的情況下調整 jpeg 或 gif 的大小,我在 2010 年為此創建了一個庫: github上的Beautylib ,它在內部使用了另一個庫: java-image-scaling 您可以直接查看源代碼以找到有用的東西: https : //github.com/felipelalli/beautylib/blob/master/src/br/eti/fml/beautylib/ResizeImage.java

沒有一個答案能幫助你獲得你想要的真正質量。 在您的項目中包含thumailator.jar(從這里下載它):

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

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

然后首先上傳圖像(作為文件,沒有縮略圖 - 它的用途是創建拇指,但您當然可以使用它創建大圖像),並將其調整為您想要的每個尺寸(例如使用縮略圖 800x600)。 質量會很好。 我搜索了很長時間,這個 .jar 幫助我實現了我想要的。

是的,我遇到了同樣的問題並解決了它們,請閱讀我的問題(答案嵌入在問題中)。 我嘗試了imgscalrjava-image-scaling庫,發現第二個質量要好得多。 靠近顯示器以了解縮略圖示例之間的差異。

盡管我最初的想法是,調整圖像大小似乎是一件非常復雜的事情,你不想自己做。 例如,我告訴java-image-scaling使用ResampleFilters.getLanczos3Filter()以獲得更好的結果。

它還解決了如何保存質量高於標准 75 的 JPG,這會產生不好的結果,尤其是對於縮略圖。

我還編寫了一個名為MyImage來幫助完成常見任務,例如從字節數組中讀取圖像,從文件中讀取圖像,通過僅指定寬度或僅高度進行縮放,通過指定邊界框進行縮放,通過指定寬度和高度並添加白帶使圖像不失真並寫入 JPG 文件。

答案:刪除提示VALUE_INTERPOLATION_BILINEARVALUE_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 會模糊正在調整大小的圖像。

發布的所有方法對我都不起作用我必須減小 QrCode 大小,但是使用上述方法質量很差並且掃描儀不起作用,如果我拍攝原始圖片並在油漆中調整其大小,則掃描儀正在工作。

The Perils of Image.getScaledInstance() 中使用的 do while 循環將進入無限循環,給定這些值,w = 606; h = 505,目標寬度 = 677,目標高度 = 505

這是一個簡單的測試代碼,你可以試試。

public class LoopTest {

    public static void main(String[] args) {
        new LoopTest(true, 606, 505, 677, 505);
    }

    public LoopTest(boolean higherQuality, int w, int h, int targetWidth, int targetHeight) {
        do {
            if (higherQuality && w > targetWidth) {
                w /= 2;
                if (w < targetWidth) {
                    w = targetWidth;
                }
            }

            if (higherQuality && h > targetHeight) {
                h /= 2;
                if (h < targetHeight) {
                    h = targetHeight;
                }
            }
        } while (w != targetWidth || h != targetHeight);        // TODO Auto-generated constructor stub
    }
}

一個快速解決方法:為循環計數定義一個索引。 如果索引 >=10,則跳出循環。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM