18

Hello I have a QR code Image , and I want to resize it , when I try to resize it to a small image using this code , I always get a blury image , and the QR code is no longer valid when I scan it , but it works fine when I resize to a big sized images with the same code :

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.setRenderingHint(RenderingHints.KEY_DITHERING, hint);
    g2.drawImage(ret, 0, 0, w, h, null);
    g2.dispose();

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

return ret;
}

what is the problem , I don't exactly understand , please give me at least a hint , thank you

Walllzzz
  • 550
  • 1
  • 5
  • 16
  • You haven't shown a crucial detail -- what interpolation hint you are setting! – Sean Owen Mar 21 '13 at 21:37
  • thanks a lot , my interpolation hint is RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR , I tried with all the possible values , still didn't work – Walllzzz Mar 21 '13 at 21:54

7 Answers7

32

I use affine transformation to achieve this task, here is my code, hope it helps

/**
 * scale image
 * 
 * @param sbi image to scale
 * @param imageType type of image
 * @param dWidth width of destination image
 * @param dHeight height of destination image
 * @param fWidth x-factor for transformation / scaling
 * @param fHeight y-factor for transformation / scaling
 * @return scaled image
 */
public static BufferedImage scale(BufferedImage sbi, int imageType, int dWidth, int dHeight, double fWidth, double fHeight) {
    BufferedImage dbi = null;
    if(sbi != null) {
        dbi = new BufferedImage(dWidth, dHeight, imageType);
        Graphics2D g = dbi.createGraphics();
        AffineTransform at = AffineTransform.getScaleInstance(fWidth, fHeight);
        g.drawRenderedImage(sbi, at);
    }
    return dbi;
}
A4L
  • 17,353
  • 6
  • 49
  • 70
  • Thanks a lot it works , but dude what to put in fWidth and fHeight ?, everytime I get different output – Walllzzz Mar 21 '13 at 21:45
  • 1
    those are the scale factors for the coordinates on the X and Y axis see the [javadoc](http://docs.oracle.com/javase/6/docs/api/java/awt/geom/AffineTransform.html#getScaleInstance%28double,%20double%29). for example if you want to have resulting image `half` a big as the original one so pass `0.5` for both fWidth and fHeight. Values less that `1` will scale down, values more than `1` will scale up, `1` will result in an image of the same dimensions. – A4L Mar 21 '13 at 21:55
  • Thanks it solved my problem , I will choose this as the accepted answer , altough all the answers are accepted – Walllzzz Mar 21 '13 at 22:21
  • 3
    dWidth = sbi.getWidth()*fWidth and dHeight = sbi.getGeight()*fHeight – caub Mar 16 '15 at 23:14
  • 1
    @A4L Reducing the size of an image to half its original size is not so simple, strictly speaking. For example: if you have an image that is a square in dimensions, reducing the width and height both by half will give you an image that is **one quarter** the size of the original image. You need to apply the [Pythagorean theorem](https://en.wikipedia.org/wiki/Pythagorean_theorem) to get the desired dimensions: scale down `c` and solve for `a` and `b` for your actual desired height and width. – nasukkin Nov 17 '16 at 21:40
  • @nasukkin technically you are totally right, but generally speacking the size here mostly refers to the length of the dimensions. – A4L Nov 18 '16 at 11:53
16

Based on @A4L's answer:

A more straigt forward version. Also his solution did only scale the canvas not the image itself.

public static BufferedImage scale(BufferedImage imageToScale, int dWidth, int dHeight) {
        BufferedImage scaledImage = null;
        if (imageToScale != null) {
            scaledImage = new BufferedImage(dWidth, dHeight, imageToScale.getType());
            Graphics2D graphics2D = scaledImage.createGraphics();
            graphics2D.drawImage(imageToScale, 0, 0, dWidth, dHeight, null);
            graphics2D.dispose();
        }
        return scaledImage;
    }

to increase the quality you could add

graphics2D.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
graphics2D.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
graphics2D.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
Patrick
  • 33,984
  • 10
  • 106
  • 126
  • Also to increse quality one strategy could be to do the scalling in several small steps until the desired desired size is reached. – A4L Nov 18 '16 at 11:56
  • True, called "Progressive Scaling" - I explain this here: http://stackoverflow.com/questions/24745147/java-resize-image-without-losing-quality/36367652#36367652 based on Campbell's blog: https://community.oracle.com/docs/DOC-983611 – Patrick Nov 18 '16 at 12:00
7

I wrote this class which i personally also use. I hope the code is straight forward.

import java.awt.Graphics;
import java.awt.Image;
import java.awt.Toolkit;
import java.awt.image.BufferedImage;
import java.awt.image.CropImageFilter;
import java.awt.image.FilteredImageSource;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JComponent;


public class ImageScaler {

        private ImageIcon originalImage;
        private ImageIcon scaledImage;

        public ImageScaler(Image image) {
                this.originalImage = new ImageIcon(image);
        }

        public ImageScaler(String fileName) {
                originalImage = new ImageIcon(fileName);
        }

        public void createScaledImage(int size, ScalingDirection scalingDirection) {
                if (scalingDirection == ScalingDirection.HORIZONTAL) {
                        scaledImage = new ImageIcon(originalImage.getImage().getScaledInstance(size, -1, Image.SCALE_SMOOTH));
                } else {
                        scaledImage = new ImageIcon(originalImage.getImage().getScaledInstance(-1, size, Image.SCALE_SMOOTH));
                }       
        }

        public void createScaledImage(int size, ScalingDirection scalingDirection, int scale) {
                if (scalingDirection == ScalingDirection.HORIZONTAL) {
                        scaledImage = new ImageIcon(originalImage.getImage().getScaledInstance(size, -1, scale));
                } else {
                        scaledImage = new ImageIcon(originalImage.getImage().getScaledInstance(-1, size, scale));
                }
        }

        public void createScaledImage(int width, int height, ScaleType scaleType) {
                int imageWidth = originalImage.getImage().getWidth(null);
                int imageHeight = originalImage.getImage().getHeight(null);
                double originalImageRatio = imageWidth / (double) imageHeight;
                double scaledImageRatio = width / (double) height;

                if(scaleType == ScaleType.FIT) {
                        if(imageHeight - (Math.abs(imageWidth - width) / originalImageRatio) <= height) {
                                scaledImage = new ImageIcon(originalImage.getImage().getScaledInstance(width, -1, Image.SCALE_SMOOTH));
                        } else if(imageWidth - (Math.abs(imageHeight - height) * originalImageRatio) <= width) {
                                scaledImage = new ImageIcon(originalImage.getImage().getScaledInstance(-1, height, Image.SCALE_SMOOTH));
                        }
                } else if(scaleType == ScaleType.FILL) {
                        if(imageHeight - (Math.abs(imageWidth - width) / originalImageRatio) >= height) {
                                scaledImage = new ImageIcon(originalImage.getImage().getScaledInstance(width, -1, Image.SCALE_SMOOTH));
                                int thumbHeight = scaledImage.getImage().getHeight(null);

                                // Crop the image
                                scaledImage = new ImageIcon(Toolkit.getDefaultToolkit().createImage(new FilteredImageSource(scaledImage.getImage().getSource(), new CropImageFilter(0, (thumbHeight-height)/2, width, height))));
                        } else if(imageWidth - (Math.abs(imageHeight - height) * originalImageRatio) >= width) {
                                scaledImage = new ImageIcon(originalImage.getImage().getScaledInstance(-1, height, Image.SCALE_SMOOTH));
                                int thumbWidth = scaledImage.getImage().getWidth(null);

                                // Crop the image
                                scaledImage = new ImageIcon(Toolkit.getDefaultToolkit().createImage(new FilteredImageSource(scaledImage.getImage().getSource(), new CropImageFilter((thumbWidth-width)/2, 0, width, height))));
                        }               
                }
        }

        public void saveScaledImage(File file, ImageType imageType) {
                if (scaledImage != null) {
                        BufferedImage bi = new BufferedImage(scaledImage.getIconWidth(), scaledImage.getIconHeight(), BufferedImage.TYPE_INT_RGB);
                        Graphics g = bi.getGraphics();
                        g.drawImage(scaledImage.getImage(), 0, 0, null);
                        try {
                                ImageIO.write(bi, imageType.value(), file);
                        } catch (IOException ioe) {
                                System.out.println("Error occured saving scaled image");
                        }
                } else {
                        System.out.println("Scaled image has not yet been created");
                }
        }

        public void saveOriginalImage(File file, ImageType imageType) {
                if (originalImage != null) {
                        BufferedImage bi = new BufferedImage(originalImage.getIconWidth(), originalImage.getIconHeight(), BufferedImage.TYPE_INT_RGB);
                        Graphics g = bi.getGraphics();
                        g.drawImage(originalImage.getImage(), 0, 0, null);
                        try {
                                ImageIO.write(bi, imageType.value(), file);
                        } catch (IOException ioe) {
                                System.out.println("Error occured saving original image");
                        }
                } else {
                        System.out.println("Original image has not yet been created");
                }
        }

        // ENUMS
        public enum ScalingDirection {VERTICAL, HORIZONTAL};
        public enum ScaleType {FIT, FILL};
        public enum ImageType {
                IMAGE_JPEG ("jpeg"),
                IMAGE_JPG ("jpg"),
                IMAGE_PNG ("png");

                private String value = null;

                ImageType(String value) {
                        this.value = value;
                }

                String value() {
                        return value;
                }
        };
}   
emd
  • 740
  • 4
  • 12
2

Please check this out Image.getScaledInstance() details can be found in this answer: How to improve the performance of g.drawImage() method for resizing images

Hope it helps

Community
  • 1
  • 1
Jabir
  • 2,776
  • 1
  • 22
  • 31
1
    public static BufferedImage resizeImg(BufferedImage img, int newW, int newH)
    {
    int w = img.getWidth();
    int h = img.getHeight();
    BufferedImage dimg = new BufferedImage(newW, newH, img.getType());
    Graphics2D g = dimg.createGraphics();
    g.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
            RenderingHints.VALUE_INTERPOLATION_BILINEAR);
    g.drawImage(img, 0, 0, newW, newH, 0, 0, w, h, null);
    g.dispose();
    return dimg;      
   }
Shahadeo
  • 683
  • 2
  • 5
  • 13
  • 1
    Provide more information on how this code solves the OP's problem. It is unclear how this code helps. – aravind Dec 12 '14 at 14:25
1

I recommend Thumbnailnator because it gave me better quality images than the Java multi-step approach. The speed however might be better with your Graphics2D drawImage code.

See also Java - resize image without losing quality

Community
  • 1
  • 1
pi3
  • 1,235
  • 13
  • 15
0

In fact, the solution is even more simple. You don't have to create a new BufferedImage. You can apply the method mentioned by for3st directly to the original BufferedImage image ('image') and set the width and height you wish for it. Thus a single statement is only needed (included in stadard Java documentation):

drawImage(BufferedImage image, int x, int y, int width, int height, ImageObserver observer) 
Apostolos
  • 3,115
  • 25
  • 28