简体   繁体   English

在不降低质量的情况下重新调整图像大小

[英]Re-sizing an image without losing quality

I made this code to resize images with two factors.我制作了这段代码以使用两个因素调整图像大小。 It works, but the quality of image is very bad after it is resized!它可以工作,但是调整大小后图像质量很差! Can you help me?你能帮助我吗?

This is the code这是代码

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

I saw on web that resizeImageWithHint is done within the scope so as not to lose quality.. but it does!我在网上看到 resizeImageWithHint 是在范围内完成的,以免质量下降..但确实如此! why?为什么? can you help me with this?你能帮我解决这个问题吗?

The best article I have ever read on this topic is The Perils of Image.getScaledInstance() (web archive).我读过的关于这个主题的最好的文章是The Perils of Image.getScaledInstance() (网络存档)。

In short: You need to use several resizing steps in order to get a good image.简而言之:您需要使用几个调整大小的步骤才能获得良好的图像。 Helper method from the article:文章中的辅助方法:

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

The following code produced me highest quality resize with aspect ratio preserved.以下代码为我生成了最高质量的调整大小,并保留了纵横比。 Tried few things and read several entries presented here in other answers.尝试了一些事情并阅读了其他答案中提供的几个条目。 Lost two days and in the end I got the best result with plain Java method (tried also ImageMagick and java-image-scaling libraries):失去了两天,最后我用普通的 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();
    }
  }
}

Know question is old... I've tried different solutions surfing then web, I got the best result using getScaledInstance() , supplying Image.SCALE_SMOOTH as argument.知道问题很老......我尝试了不同的解决方案,然后在网上冲浪,我使用getScaledInstance()获得了最好的结果,提供Image.SCALE_SMOOTH作为参数。 In fact the resulting image quality was really better.事实上,由此产生的图像质量确实更好。 My code below:我的代码如下:

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

If your image source is a png then use like this:如果您的图像源是png则使用如下:

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

If you want to resize jpeg or gif without loose too much quality, I made a library in 2010 for this: beautylib on github that uses internally this other library: java-image-scaling .如果你想在不丢失太多质量的情况下调整 jpeg 或 gif 的大小,我在 2010 年为此创建了一个库: github上的Beautylib ,它在内部使用了另一个库: java-image-scaling You can see directly the source code to find something useful: https://github.com/felipelalli/beautylib/blob/master/src/br/eti/fml/beautylib/ResizeImage.java您可以直接查看源代码以找到有用的东西: https : //github.com/felipelalli/beautylib/blob/master/src/br/eti/fml/beautylib/ResizeImage.java

None of the answers will help you to get real quality you desire.没有一个答案能帮助你获得你想要的真正质量。 Include thumbailator.jar in your project (download it form here):在您的项目中包含thumailator.jar(从这里下载它):

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

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

Then upload the image first (as file, without Thumbnailator - it's use is to create thumbs, but you can create large images with it of course), and resize it to every dimensions you want (with Thumbnailator 800x600 for example).然后首先上传图像(作为文件,没有缩略图 - 它的用途是创建拇指,但您当然可以使用它创建大图像),并将其调整为您想要的每个尺寸(例如使用缩略图 800x600)。 Quality will be very good.质量会很好。 I was searching for this long time, this .jar helped me to achieve what i want.我搜索了很长时间,这个 .jar 帮助我实现了我想要的。

Yes, I had the same problems and solved them, please read my question (answer is embedded in the question).是的,我遇到了同样的问题并解决了它们,请阅读我的问题(答案嵌入在问题中)。 I tried imgscalr and java-image-scaling libraries and found the second much better quality.我尝试了imgscalrjava-image-scaling库,发现第二个质量要好得多。 Get close to the monitor to appreciate the difference between the thumbnail examples.靠近显示器以了解缩略图示例之间的差异。

Despite my initial thoughts, resizing an image seems a very complicate thing, you don't want to do it yourself.尽管我最初的想法是,调整图像大小似乎是一件非常复杂的事情,你不想自己做。 For example I tell java-image-scaling to use ResampleFilters.getLanczos3Filter() to have better result.例如,我告诉java-image-scaling使用ResampleFilters.getLanczos3Filter()以获得更好的结果。

It also addresses how to save a JPG with a quality higher than the standard 75, which produces a bad result especially for a thumbnail.它还解决了如何保存质量高于标准 75 的 JPG,这会产生不好的结果,尤其是对于缩略图。

I also wrote a small class, called MyImage to help with common tasks, such as reading an image from a byte array, from a file, scaling by specifying only width or only height, scaling by specifying a bounding box, scaling by specifying width and height and adding a white band to make the image not distorted and writing to JPG file.我还编写了一个名为MyImage来帮助完成常见任务,例如从字节数组中读取图像,从文件中读取图像,通过仅指定宽度或仅高度进行缩放,通过指定边界框进行缩放,通过指定宽度和高度并添加白带使图像不失真并写入 JPG 文件。

Answer: Remove the hints VALUE_INTERPOLATION_BILINEAR , and VALUE_RENDERING_QUALITY .答案:删除提示VALUE_INTERPOLATION_BILINEARVALUE_RENDERING_QUALITY

Example:例子:

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

Note: For some reason, the hints VALUE_INTERPOLATION_BILINEAR and VALUE_RENDERING_QUALITY blur the image being resized.注意:由于某种原因,提示VALUE_INTERPOLATION_BILINEARVALUE_RENDERING_QUALITY 会模糊正在调整大小的图像。

发布的所有方法对我都不起作用我必须减小 QrCode 大小,但是使用上述方法质量很差并且扫描仪不起作用,如果我拍摄原始图片并在油漆中调整其大小,则扫描仪正在工作。

The do while loop used in The Perils of Image.getScaledInstance() will run into an infinite loop, given those values, w = 606; The Perils of Image.getScaledInstance() 中使用的 do while 循环将进入无限循环,给定这些值,w = 606; h = 505, targetWidth = 677, targetHeight = 505 h = 505,目标宽度 = 677,目标高度 = 505

Here is a simplied testing code, you can try it.这是一个简单的测试代码,你可以试试。

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
    }
}

A quick work around: define an index for loop count.一个快速解决方法:为循环计数定义一个索引。 If the index is >=10, break out of loop.如果索引 >=10,则跳出循环。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM