在不丢失质量的情况下重新调整图像。

时间:2022-03-20 21:49:39

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! why? can you help me with this?

我在web上看到,resizeImageWithHint是在范围内完成的,以免丢失质量。但它!为什么?你能帮我一下吗?

8 个解决方案

#1


28  

The best article I have ever read on this topic is The Perils of Image.getScaledInstance() (web archive).

关于这个主题,我读过的最好的文章是Image.getScaledInstance() (web archive)的危险。

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

#2


8  

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- scale库):

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

#3


5  

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. In fact the resulting image quality was really better. My code below:

知道问题是老…我尝试了不同的解决方案,然后上网,我得到了最好的结果使用getScaledInstance(),提供图像。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();
}

#4


2  

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

如果您想要调整jpeg或gif的大小,而不需要太大的质量,我在2010年创建了一个库:beautylib在github上使用内部的这个库:java-image- scale。您可以直接查看源代码以找到有用的内容:https://github.com/felipelalli/beautylib/blob/master/src/eti/fml/beautylib/resizeimage .java

#5


2  

None of the answers will help you to get real quality you desire. Include thumbailator.jar in your project (download it form here):

所有的答案都不能帮助你获得你真正想要的品质。包括thumbailator。项目中的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). Quality will be very good. I was searching for this long time, this .jar helped me to achieve what i want.

然后首先上传图像(作为文件,没有缩略图-它的用途是创建拇指,但你当然可以用它创建大的图像),并将它调整到你想要的每个维度(例如使用缩略图800x600)。质量会很好。我搜索了很长时间,这个。jar帮助我实现了我想要的。

#6


1  

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. Get close to the monitor to appreciate the difference between the thumbnail examples.

是的,我遇到了同样的问题并解决了它们,请阅读我的问题(答案嵌入在问题中)。我尝试了imgscalr和java-image- scale库,发现第二个更好的质量。靠近监视器,欣赏缩略图示例之间的差异。

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- scale使用resamplefilter . 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.

我也写了一个小类,称为模板帮助常见的任务,比如阅读一个图像从一个字节数组,从一个文件,扩展通过指定唯一的宽度或高度,扩展通过指定一个边界框,通过指定宽度和高度和添加一个白色带图像不失真和写作JPG文件。

#7


0  

All the methods posted does'nt work for me i have to reduce QrCode size, but with above methods the quality is poor and scanner doesn't work , if i take original picture and resize it in paint the scanner is working.

所有的方法都不适合我,我必须减少QrCode的大小,但是在上面的方法中,质量很差,而且扫描器不起作用,如果我把原来的图片和它的大小调整到扫描仪的工作状态。

#8


-1  

Answer: Remove the hints VALUE_INTERPOLATION_BILINEAR, and VALUE_RENDERING_QUALITY.

答:删除提示value_插补_双线性,以及VALUE_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_插补_bilinear和VALUE_RENDERING_QUALITY模糊了正在调整的图像大小。

#1


28  

The best article I have ever read on this topic is The Perils of Image.getScaledInstance() (web archive).

关于这个主题,我读过的最好的文章是Image.getScaledInstance() (web archive)的危险。

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

#2


8  

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- scale库):

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

#3


5  

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. In fact the resulting image quality was really better. My code below:

知道问题是老…我尝试了不同的解决方案,然后上网,我得到了最好的结果使用getScaledInstance(),提供图像。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();
}

#4


2  

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

如果您想要调整jpeg或gif的大小,而不需要太大的质量,我在2010年创建了一个库:beautylib在github上使用内部的这个库:java-image- scale。您可以直接查看源代码以找到有用的内容:https://github.com/felipelalli/beautylib/blob/master/src/eti/fml/beautylib/resizeimage .java

#5


2  

None of the answers will help you to get real quality you desire. Include thumbailator.jar in your project (download it form here):

所有的答案都不能帮助你获得你真正想要的品质。包括thumbailator。项目中的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). Quality will be very good. I was searching for this long time, this .jar helped me to achieve what i want.

然后首先上传图像(作为文件,没有缩略图-它的用途是创建拇指,但你当然可以用它创建大的图像),并将它调整到你想要的每个维度(例如使用缩略图800x600)。质量会很好。我搜索了很长时间,这个。jar帮助我实现了我想要的。

#6


1  

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. Get close to the monitor to appreciate the difference between the thumbnail examples.

是的,我遇到了同样的问题并解决了它们,请阅读我的问题(答案嵌入在问题中)。我尝试了imgscalr和java-image- scale库,发现第二个更好的质量。靠近监视器,欣赏缩略图示例之间的差异。

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- scale使用resamplefilter . 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.

我也写了一个小类,称为模板帮助常见的任务,比如阅读一个图像从一个字节数组,从一个文件,扩展通过指定唯一的宽度或高度,扩展通过指定一个边界框,通过指定宽度和高度和添加一个白色带图像不失真和写作JPG文件。

#7


0  

All the methods posted does'nt work for me i have to reduce QrCode size, but with above methods the quality is poor and scanner doesn't work , if i take original picture and resize it in paint the scanner is working.

所有的方法都不适合我,我必须减少QrCode的大小,但是在上面的方法中,质量很差,而且扫描器不起作用,如果我把原来的图片和它的大小调整到扫描仪的工作状态。

#8


-1  

Answer: Remove the hints VALUE_INTERPOLATION_BILINEAR, and VALUE_RENDERING_QUALITY.

答:删除提示value_插补_双线性,以及VALUE_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_插补_bilinear和VALUE_RENDERING_QUALITY模糊了正在调整的图像大小。