AffineTransform crop image, what am I wrong?

I have a 2156x1728 black and white png file that I want to rotate 90 degrees with AffineTransform. The resulting image does not have the correct proportions. Here is a sample code (given that I have successfully uploaded the png file to BufferedImage):

public BufferedImage transform(BufferedImage image){ System.out.println("Input width: "+ image.getWidth()); System.out.println("Input height: "+ image.getHeight()); AffineTransform affineTransform = new AffineTransform(); affineTransform.setToQuadrantRotation(1, image.getWidth() / 2, image.getHeight() / 2); AffineTransformOp opRotated = new AffineTransformOp(affineTransform, AffineTransformOp.TYPE_BILINEAR); BufferedImage transformedImage = opRotated.createCompatibleDestImage(image, image.getColorModel()); System.out.println("Resulting width: "+ transformedImage.getWidth()); System.out.println("Resulting height: "+ transformedImage.getHeight()); transformedImage = opRotated.filter(image, transformedImage); return transformedImage; } 

Result:

Input Width: 2156

Entry Height: 1728

Resulting Width: 1942

Resulting height: 1942

How is it that rotation returns such completely unrelated dimensions?

+1
source share
3 answers

I'm not a pro at this, but why not just create the right sized BufferedImage? Also note that your center of revolution is wrong. You will need to rotate above the center [w / 2, w / 2] or [h / 2, h / 2] (w width and h - height) depending on which quadrant you rotate, 1 or 3, as well as the relative height and width of the image. For example:

 import java.awt.geom.AffineTransform; import java.awt.image.AffineTransformOp; import java.awt.image.BufferedImage; import java.io.IOException; import java.net.MalformedURLException; import java.net.URL; import javax.imageio.ImageIO; import javax.swing.ImageIcon; import javax.swing.JLabel; import javax.swing.JOptionPane; public class RotateImage { public static final String IMAGE_PATH = "http://duke.kenai.com/" + "models/Duke3DprogressionSmall.jpg"; public static void main(String[] args) { try { URL imageUrl = new URL(IMAGE_PATH); BufferedImage img0 = ImageIO.read(imageUrl); ImageIcon icon0 = new ImageIcon(img0); int numquadrants = 1; BufferedImage img1 = transform(img0, numquadrants ); ImageIcon icon1 = new ImageIcon(img1); JOptionPane.showMessageDialog(null, new JLabel(icon0)); JOptionPane.showMessageDialog(null, new JLabel(icon1)); } catch (MalformedURLException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } public static BufferedImage transform(BufferedImage image, int numquadrants) { int w0 = image.getWidth(); int h0 = image.getHeight(); int w1 = w0; int h1 = h0; int centerX = w0 / 2; int centerY = h0 / 2; if (numquadrants % 2 == 1) { w1 = h0; h1 = w0; } if (numquadrants % 4 == 1) { if (w0 > h0) { centerX = h0 / 2; centerY = h0 / 2; } else if (h0 > w0) { centerX = w0 / 2; centerY = w0 / 2; } // if h0 == w0, then use default } else if (numquadrants % 4 == 3) { if (w0 > h0) { centerX = w0 / 2; centerY = w0 / 2; } else if (h0 > w0) { centerX = h0 / 2; centerY = h0 / 2; } // if h0 == w0, then use default } AffineTransform affineTransform = new AffineTransform(); affineTransform.setToQuadrantRotation(numquadrants, centerX, centerY); AffineTransformOp opRotated = new AffineTransformOp(affineTransform, AffineTransformOp.TYPE_BILINEAR); BufferedImage transformedImage = new BufferedImage(w1, h1, image.getType()); transformedImage = opRotated.filter(image, transformedImage); return transformedImage; } } 

Change 1
You asked:

Can you explain to me why this should be [w / 2, w / 2] or [h / 2, h / 2]?

To explain this best, it is best to visualize and physically manipulate the rectangle:

Cut a rectangular sheet of paper and place it on a sheet of paper so that its upper left corner is in the upper left corner of the sheet of paper - this is your image on the screen. Now check where you need to rotate this rectangle 1 or 3 of the quadrant so that its new top left corner is higher than the paper, and you will see why you need to use [w / 2, w / 2] or [h / 2, h / 2].

+5
source

The above solution had problems with wdith and image height, the code below does not depend on w> h || h> w

 public static BufferedImage rotateImage(BufferedImage image, int quadrants) { int w0 = image.getWidth(); int h0 = image.getHeight(); int w1 = w0; int h1 = h0; int centerX = w0 / 2; int centerY = h0 / 2; if (quadrants % 2 == 1) { w1 = h0; h1 = w0; } if (quadrants % 4 == 1) { centerX = h0 / 2; centerY = h0 / 2; } else if (quadrants % 4 == 3) { centerX = w0 / 2; centerY = w0 / 2; } AffineTransform affineTransform = new AffineTransform(); affineTransform.setToQuadrantRotation(quadrants, centerX, centerY); AffineTransformOp opRotated = new AffineTransformOp(affineTransform, AffineTransformOp.TYPE_BILINEAR); BufferedImage transformedImage = new BufferedImage(w1, h1, image.getType()); transformedImage = opRotated.filter(image, transformedImage); return transformedImage; } 
+2
source

Furykid's answer is great and helped me a lot. But it is not so beautiful. If the image is rectangular, the resulting rotated image may contain some additional black pixels on one side.

I tried a photo of Marty Feldman, the original and the results can be viewed at this link: Martin Feldman rotation test

It's hard to see on a black background, but on any image editing software it's easy to see a small black frame on the right and bottom of the resulting images. This may not be a problem for some, but if it is for you, there is a fixed code (I saved the original as comments for an easier comparison):

 public BufferedImage rotateImage(BufferedImage image, int quadrants) { int w0 = image.getWidth(); int h0 = image.getHeight(); /* These are not necessary anymore * int w1 = w0; * int h1 = h0; */ int centerX = w0 / 2; int centerY = h0 / 2; /* This is not necessary anymore * if (quadrants % 2 == 1) { * w1 = h0; * h1 = w0; * } */ //System.out.println("Original dimensions: "+w0+", "+h0); //System.out.println("Rotated dimensions: "+w1+", "+h1); if (quadrants % 4 == 1) { centerX = h0 / 2; centerY = h0 / 2; } else if (quadrants % 4 == 3) { centerX = w0 / 2; centerY = w0 / 2; } //System.out.println("CenterX: "+centerX); //System.out.println("CenterY: "+centerY); AffineTransform affineTransform = new AffineTransform(); affineTransform.setToQuadrantRotation(quadrants, centerX, centerY); AffineTransformOp opRotated = new AffineTransformOp(affineTransform, AffineTransformOp.TYPE_BILINEAR); /*Old code for comparison //BufferedImage transformedImage = new BufferedImage(w1, h1,image.getType()); //transformedImage = opRotated.filter(image, transformedImage); */ BufferedImage transformedImage = opRotated.filter(image, null); return transformedImage; } 

WARNING: Opinion Ahead. I'm not sure why this is happening, but I have an assumption. If you can explain better, edit.

I believe that the reason for this "glitch" is associated with odd sizes. When calculating the dimensions for the new BufferedImage height of 273 will generate a center of Y 136, for example, when the correct value is 136.5. This can cause the rotation to occur in a small off-center location. However, by sending null to filter as the target image, "a BufferedImage is created with the ColorModel source", and this seems to work best.

0
source

All Articles