AffineTransform truncates image

2019-02-20 05:16发布

问题:

I have an image and I have to rotate it by 45, 90, 135, 180 degrees. What I am doing:

try {
    BufferedImage src = ImageIO.read(new File("src.png"));
    double ang = Math.toRadians(90);

    AffineTransform t = new AffineTransform();
    t.setToRotation(ang, src.getWidth() / 2, src.getHeight() / 2);

    AffineTransformOp op = new AffineTransformOp(t, null);
    BufferedImage dst = new BufferedImage(src.getWidth(), src.getHeight(), src.getType());
    op.filter(src, dst);

    ImageIO.write(dst, "png", new File("output.png"));
} catch(Exception ex) { ex.printStackTrace();
}

The problem is that image changes its position and gets out of bounds of destination image:

The problem http://img32.imageshack.us/img32/3328/resultcs.png

I've googled this and found the solution in this question: AffineTransform truncates image, what do I wrong? But I quite don't understand it and it works only for quadrants. I've tried to multiply twice width and height of destination, but it failed:

Another fail http://img401.imageshack.us/img401/2417/result2a.png

How to fix this? The destination image shouldn't have any extra (except required for diagonal rotation) whitespace or truncated area. Angle problems (0 == 180 or is it clockwise) aren't important.

Thanks for any help.

回答1:

Edit: Now it works for the general case.

The rotation is performed around the center, and the center is placed at the same position in the destination image as it was in the source image (correct behavior).

I've modified your code to transform the source image rectangle so we can easily get the new dimensions/image offset. This is used to construct a destination BufferedImage of the correct dimensions, and to append a translation to your AffineTransform so the image center is placed at the center of the destination image.

        BufferedImage src = ImageIO.read(new File(INPUT));
        int w = src.getWidth();
        int h = src.getHeight();

        AffineTransform t = new AffineTransform();
        double ang = Math.toRadians(35);
        t.setToRotation(ang, w / 2d, h / 2d);

        // source image rectangle
        Point[] points = {
            new Point(0, 0),
            new Point(w, 0),
            new Point(w, h),
            new Point(0, h)
        };

        // transform to destination rectangle
        t.transform(points, 0, points, 0, 4);

        // get destination rectangle bounding box
        Point min = new Point(points[0]);
        Point max = new Point(points[0]);
        for (int i = 1, n = points.length; i < n; i ++) {
            Point p = points[i];
            double pX = p.getX(), pY = p.getY();

            // update min/max x
            if (pX < min.getX()) min.setLocation(pX, min.getY());
            if (pX > max.getX()) max.setLocation(pX, max.getY());

            // update min/max y
            if (pY < min.getY()) min.setLocation(min.getX(), pY);
            if (pY > max.getY()) max.setLocation(max.getX(), pY);
        }

        // determine new width, height
        w = (int) (max.getX() - min.getX());
        h = (int) (max.getY() - min.getY());

        // determine required translation
        double tx = min.getX();
        double ty = min.getY();

        // append required translation
        AffineTransform translation = new AffineTransform();
        translation.translate(-tx, -ty);
        t.preConcatenate(translation);

        AffineTransformOp op = new AffineTransformOp(t, null);
        BufferedImage dst = new BufferedImage(w, h, src.getType());
        op.filter(src, dst);

        ImageIO.write(dst, "png", new File(OUTPUT));


回答2:

I suggest to replace

AffineTransformOp op = new AffineTransformOp(t, null);

with

AffineTransformOp op = new AffineTransformOp(t,  AffineTransformOp.TYPE_BILINEAR);

It will improve a lot the quality of the output.