How to scale BufferedImage

After javadocs, I tried scaling BufferedImage without success, here is my code:

 BufferedImage image = MatrixToImageWriter.getBufferedImage(encoded); Graphics2D grph = image.createGraphics(); grph.scale(2.0, 2.0); grph.dispose(); 

I do not understand why it does not work, no help?

+39
java image image-processing bufferedimage image-scaling
Nov 18 2018-10-18
source share
7 answers

AffineTransformOp offers additional flexibility in choosing the type of interpolation.

 BufferedImage before = getBufferedImage(encoded); int w = before.getWidth(); int h = before.getHeight(); BufferedImage after = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB); AffineTransform at = new AffineTransform(); at.scale(2.0, 2.0); AffineTransformOp scaleOp = new AffineTransformOp(at, AffineTransformOp.TYPE_BILINEAR); after = scaleOp.filter(before, after); 

The described fragment illustrates repeated sampling , but not cropping ; this answer answers this question ; Some related examples are discussed here .

+58
Nov 18 2018-10-18
source share

Unfortunately, getScaledInstance () performance is very poor, if not a problem.

An alternative approach is to create a new BufferedImage and draw a scaled version of the original on a new one.

 BufferedImage resized = new BufferedImage(newWidth, newHeight, original.getType()); Graphics2D g = resized.createGraphics(); g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR); g.drawImage(original, 0, 0, newWidth, newHeight, 0, 0, original.getWidth(), original.getHeight(), null); g.dispose(); 

newWidth, newHeight indicates the new size of the BufferedImage and must be correctly calculated. In case of coefficient scaling:

 int newWidth = new Double(original.getWidth() * widthFactor).intValue(); int newHeight = new Double(original.getHeight() * heightFactor).intValue(); 

EDIT . Found an article illustrating a performance issue: Dangers Image.getScaledInstance () p>

+32
Nov 18 '10 at 15:15
source share

As @Bozho says, you probably want to use getScaledInstance .

To understand how grph.scale(2.0, 2.0) works, you can look at this code:

 import java.awt.*; import java.awt.image.BufferedImage; import java.io.*; import javax.imageio.ImageIO; import javax.swing.ImageIcon; class Main { public static void main(String[] args) throws IOException { final int SCALE = 2; Image img = new ImageIcon("duke.png").getImage(); BufferedImage bi = new BufferedImage(SCALE * img.getWidth(null), SCALE * img.getHeight(null), BufferedImage.TYPE_INT_ARGB); Graphics2D grph = (Graphics2D) bi.getGraphics(); grph.scale(SCALE, SCALE); // everything drawn with grph from now on will get scaled. grph.drawImage(img, 0, 0, null); grph.dispose(); ImageIO.write(bi, "png", new File("duke_double_size.png")); } } 

Given duke.png :
enter image description here

it creates duke_double_size.png :
enter image description here

+10
Nov 18 2018-10-18
source share

Using imgscalr - Java Scaling Library :

 BufferedImage image = Scalr.resize(originalImage, Scalr.Method.BALANCED, newWidth, newHeight); 

This is fast enough for me.

+9
Feb 23 '14 at 21:52
source share

If you don't mind using an external library, Thumbnailator can scale BufferedImage s.

Thumbnailator will take care of the processing of Java 2D processing (for example, using Graphics2D and setting the appropriate hints for rendering ), so that a simple simple API call can be used to resize images:

 BufferedImage image = Thumbnails.of(originalImage).scale(2.0).asBufferedImage(); 

Although the Thumbnailator, as its name implies, is focused on image compression, it will also do a decent job enlarging images using bilinear interpolation in its default resizer implementation.




Disclaimer: I am a supporter of the Thumbnailator library.

+4
Nov 16 '14 at 10:35
source share

scale(..) works a little differently. You can use bufferedImage.getScaledInstance(..)

+3
Nov 18 2018-10-18
source share

To scale the image, you need to create a new image and draw it. One way is to use the filter() method for AffineTransferOp , as suggested here . This allows you to select the interpolation method.

 private static BufferedImage scale1(BufferedImage before, double scale) { int w = before.getWidth(); int h = before.getHeight(); // Create a new image of the proper size int w2 = (int) (w * scale); int h2 = (int) (h * scale); BufferedImage after = new BufferedImage(w2, h2, BufferedImage.TYPE_INT_ARGB); AffineTransform scaleInstance = AffineTransform.getScaleInstance(scale, scale); AffineTransformOp scaleOp = new AffineTransformOp(scaleInstance, AffineTransformOp.TYPE_BILINEAR); scaleOp.filter(before, after); return after; } 

Another way is to simply draw the original image in a new image, using the zoom operation to scale. This method is very similar, but it also illustrates how you can draw whatever you want in the final image. (I put an empty line where the two methods start to differ.)

 private static BufferedImage scale2(BufferedImage before, double scale) { int w = before.getWidth(); int h = before.getHeight(); // Create a new image of the proper size int w2 = (int) (w * scale); int h2 = (int) (h * scale); BufferedImage after = new BufferedImage(w2, h2, BufferedImage.TYPE_INT_ARGB); AffineTransform scaleInstance = AffineTransform.getScaleInstance(scale, scale); AffineTransformOp scaleOp = new AffineTransformOp(scaleInstance, AffineTransformOp.TYPE_BILINEAR); Graphics2D g2 = (Graphics2D) after.getGraphics(); // Here, you may draw anything you want into the new image, but we're // drawing a scaled version of the original image. g2.drawImage(before, scaleOp, 0, 0); g2.dispose(); return after; } 

Addendum: Results

To illustrate the differences, I compared the results of the five methods below. Here's what the results look like, scalable both up and down, as well as performance data. (Performance varies from one run to another, so use these numbers only as a rough guide.) The top image is original. I scale it in half and half size.

As you can see, AffineTransformOp.filter() used in scaleBilinear() is faster than the standard Graphics2D.drawImage() drawing method in scale2() . Also, BiCubic interpolation is the slowest, but gives the best results when expanding the image. (For performance, it should only be compared with scaleBilinear() and scaleNearest(). ) A bilinear image seems better for compressing the image, although this is not an easy call. And NearestNeighbor is the fastest, with worse results. Bilinear seems to be the best compromise between speed and quality. Image.getScaledInstance() , called by the questionable() method, performed very poorly and returned to the same poor quality as NearestNeighbor. (Performance metrics are for image expansion only.)

enter image description here

 public static BufferedImage scaleBilinear(BufferedImage before, double scale) { final int interpolation = AffineTransformOp.TYPE_BILINEAR; return scale(before, scale, interpolation); } public static BufferedImage scaleBicubic(BufferedImage before, double scale) { final int interpolation = AffineTransformOp.TYPE_BICUBIC; return scale(before, scale, interpolation); } public static BufferedImage scaleNearest(BufferedImage before, double scale) { final int interpolation = AffineTransformOp.TYPE_NEAREST_NEIGHBOR; return scale(before, scale, interpolation); } @NotNull private static BufferedImage scale(final BufferedImage before, final double scale, final int type) { int w = before.getWidth(); int h = before.getHeight(); int w2 = (int) (w * scale); int h2 = (int) (h * scale); BufferedImage after = new BufferedImage(w2, h2, before.getType()); AffineTransform scaleInstance = AffineTransform.getScaleInstance(scale, scale); AffineTransformOp scaleOp = new AffineTransformOp(scaleInstance, type); scaleOp.filter(before, after); return after; } /** * This is a more generic solution. It produces the same result, but it shows how you * can draw anything you want into the newly created image. It slower * than scaleBilinear(). * @param before The original image * @param scale The scale factor * @return A scaled version of the original image */ private static BufferedImage scale2(BufferedImage before, double scale) { int w = before.getWidth(); int h = before.getHeight(); // Create a new image of the proper size int w2 = (int) (w * scale); int h2 = (int) (h * scale); BufferedImage after = new BufferedImage(w2, h2, before.getType()); AffineTransform scaleInstance = AffineTransform.getScaleInstance(scale, scale); AffineTransformOp scaleOp = new AffineTransformOp(scaleInstance, AffineTransformOp.TYPE_BILINEAR); Graphics2D g2 = (Graphics2D) after.getGraphics(); // Here, you may draw anything you want into the new image, but we're just drawing // a scaled version of the original image. This is slower than // calling scaleOp.filter(). g2.drawImage(before, scaleOp, 0, 0); g2.dispose(); return after; } /** * I call this one "questionable" because it uses the questionable getScaledImage() * method. This method is no longer favored because it slow, as my tests confirm. * @param before The original image * @param scale The scale factor * @return The scaled image. */ private static Image questionable(final BufferedImage before, double scale) { int w2 = (int) (before.getWidth() * scale); int h2 = (int) (before.getHeight() * scale); return before.getScaledInstance(w2, h2, Image.SCALE_FAST); } 
+1
Sep 14 '17 at 6:19 06:19
source share



All Articles