用AffineTransform旋转图像会产生视觉上正确的像素,但getRGB不匹配。

huangapple go评论77阅读模式
英文:

Rotating Image with AffineTransform Produces Visually Correct Pixels, but getRGB Doesn't Match

问题

我正在尝试使用AffineTransform将图像旋转180度。我的“out.jpg”在视觉上看起来已经旋转了,但当我尝试通过编程验证输出图像是否已经旋转时,代码却不同意。这是我的代码:

public static void main(String[] args) throws Exception {
    BufferedImage origImage = ImageIO.read(new File("input.jpg"));
    
    AffineTransform af = new AffineTransform();
    int centerX = origImage.getWidth() / 2;
    int centerY = origImage.getHeight() / 2;
    // 旋转180度
    af.quadrantRotate(2, centerX, centerY);

    AffineTransformOp affineTransformOp = new AffineTransformOp(af, null);
    BufferedImage destImage =
        new BufferedImage(origImage.getWidth(), origImage.getHeight(), origImage.getType());
    affineTransformOp.filter(origImage, destImage);

    // 验证destImage是否确实已经旋转,通过随机像素点的点检查。
    int origX = 90;
    int origY = 32;
    // 我们期望此像素点已经被翻译到的位置。
    int expX = destImage.getWidth() - origX;
    int expY = destImage.getHeight() - origY;

    // 总是打印false。为什么???
    System.out.println("Pixels are equal: " +
      origImage.getRGB(origX, origY) == destImage.getRGB(expX, expY));

    ImageIO.write(destImage, "jpg", new File("out.jpg"));

}

关于旋转或编程检查,我在文档中已经验证过:

  • getRGB()返回一个整数,不需要使用.equals()
  • BufferedImage从左上角的(0,0)开始索引
  • AffineTransform.quadrantRotate()与锚点一起使用后,旋转完成后会转回(0,0)
  • AffineTransform.quadrantRotate()是顺时针旋转的(尽管这对于180度旋转不应该有影响)

感谢您的帮助!

英文:

I'm attempting to rotate an image 180 using AffineTransform. My "out.jpg" visually appears rotated, however when I attempt to programatically verify that the output image has been rotated , the code disagrees. Here's my code:

public static void main(String[] args) throws Exception {
    BufferedImage origImage = ImageIO.read(new File("input.jpg"));
    
    AffineTransform af = new AffineTransform();
    int centerX = origImage.getWidth() / 2;
    int centerY = origImage.getHeight() / 2;
    // Rotate 180 degrees
    af.quadrantRotate(2, centerX, centerY);

    AffineTransformOp affineTransformOp = new AffineTransformOp(af, null);
    BufferedImage destImage =
        new BufferedImage(origImage.getWidth(), origImage.getHeight(), origImage.getType());
    affineTransformOp.filter(origImage, destImage);

    // Verify that destImage has indeed been rotated by point checking a random pixel.
    int origX = 90;
    int origY = 32;
    // Where we expect this pixel to have been translated to.
    int expX = destImage.getWidth()-origX;
    int expY = destImage.getHeight()-origY;

    // Always prints false. Why????
    System.out.println("Pixels are equal: " +
      origImage.getRGB(origX, origY) == destImage.getRGB(expX, expY));

    ImageIO.write(destImage, "jpg", new File("out.jpg"));

}

What am I doing wrong with either my rotation or the programatic check?

I've verified in the documentation:

  • getRGB() returns an int, there's no need to use .equals()
  • BufferedImage indexes from (0,0) in the upper left corner
  • AffineTransform.quadrantRotate() with an anchor, translates back to (0,0) once it completes the rotation
  • AffineTransform.quadrantRotate() goes clockwise (although this shouldn't matter for 180 rotation)

Thanks for your help!

答案1

得分: 1

I ran your code using a png image of mine. Here are my test results. Image type 6 is TYPE_4BYTE_ABGR from the BufferedImage source code.

图像大小:416 x 199
图像类型:6
原始像素:90,32
目标像素:326,167
像素值:ffeeeeee ffffffff
像素相等:false

这是图片。

用AffineTransform旋转图像会产生视觉上正确的像素,但getRGB不匹配。

这是旋转后的图片。

用AffineTransform旋转图像会产生视觉上正确的像素,但getRGB不匹配。

我不确定为什么颜色不同。我无法肉眼区分eeeeee像素和ffffff像素之间的差异。

补充说明:我做了更多研究,并找到了ImagegetRGB方法的这部分文档。

返回默认RGB颜色模型(TYPE_INT_ARGB)和默认sRGB色彩空间中的整数像素。如果此默认模型与图像的ColorModel不匹配,则进行颜色转换。

因此,看起来getRGB方法本身可能导致颜色转换。

这是我运行的代码。

import java.awt.geom.AffineTransform;
import java.awt.image.AffineTransformOp;
import java.awt.image.BufferedImage;
import java.io.File;

import javax.imageio.ImageIO;

public class RotateImage {

    public static void main(String[] args) throws Exception {
        String input = "C:\\Users\\Owner\\OneDrive\\Pictures"
                + "\\Saved Pictures\\CustomJTextField.png";
        BufferedImage origImage = ImageIO.read(new File(input));

        AffineTransform af = new AffineTransform();
        int centerX = origImage.getWidth() / 2;
        int centerY = origImage.getHeight() / 2;
        // 旋转180度
        af.quadrantRotate(2, centerX, centerY);

        AffineTransformOp affineTransformOp = new AffineTransformOp(
                af, null);
        System.out.println("图像大小:" + origImage.getWidth() +
                " x " + origImage.getHeight());
        System.out.println("图像类型:" + origImage.getType());
        BufferedImage destImage = new BufferedImage(
                origImage.getWidth(), origImage.getHeight(),
                origImage.getType());
        affineTransformOp.filter(origImage, destImage);

        // 验证destImage是否确实通过点检查旋转了一个随机像素。
        int origX = 90;
        int origY = 32;
        // 我们期望这个像素已经被转换到的位置。
        int expX = destImage.getWidth() - origX;
        int expY = destImage.getHeight() - origY;

        System.out.println("原始像素:" + origX + "," + origY);
        System.out.println("目标像素:" + expX + "," + expY);

        int origPixel = origImage.getRGB(origX, origY);
        int expPixel = destImage.getRGB(expX, expY);
        System.out.println("像素值:" +
                Integer.toHexString(origPixel) + " " +
                Integer.toHexString(expPixel));

        // 总是输出false。为什么????
        System.out.println("像素相等:" +
                (origPixel == expPixel));

        String output = "C:\\Eclipse\\Eclipse-2020-workspace"
                + "\\com.ggl.testing2\\resources\\output.png";
        ImageIO.write(destImage, "png", new File(output));
    }

}

补充说明:我编写了自己的变换来查看是变换改变了颜色还是getRGB方法改变了颜色。

这是来自我自己变换的测试结果。

图像大小416 x 199
图像类型6
原始像素90,32
目标像素326,167
像素值ffeeeeee ffeeeeee
像素相等true

我从原始图像创建了一个整数像素数组,并将这些像素写入旋转后的图像。

这是代码。

import java.awt.image.BufferedImage;
import java.io.File;

import javax.imageio.ImageIO;

public class RotateImage {

    public static void main(String[] args) throws Exception {
        String input = "C:\\Users\\Owner\\OneDrive\\Pictures"
                + "\\Saved Pictures\\CustomJTextField.png";
        BufferedImage origImage = ImageIO.read(new File(input));

        System.out.println("图像大小:" + origImage.getWidth() +
                " x " + origImage.getHeight());
        System.out.println("图像类型:" + origImage.getType());
        int[] pixels = getPixels(origImage);

        BufferedImage destImage = new BufferedImage(
                origImage.getWidth(), origImage.getHeight(),
                origImage.getType());
        destImage = putPixels(destImage, pixels);

        // 验证destImage是否确实通过点检查旋转了一个随机像素。
        int origX = 90;
        int origY = 32;
        // 我们期望这个像素已经被转换到的位置。
        int expX = destImage.getWidth() - origX;
        int expY = destImage.getHeight() - origY;

        System.out.println("原始像素:" + origX + "," + origY);
        System.out.println("目标像素:" + expX + "," + expY);

        int origPixel = origImage.getRGB(origX, origY);
        int expPixel = destImage.getRGB(expX, expY);
        System.out.println("像素值:" +
                Integer.toHexString(origPixel) + " " +
                Integer.toHexString(expPixel));
        System.out.println("像素相等:" +
                (origPixel == expPixel));

        String output = "C:\\Eclipse\\Eclipse-2020-workspace"
                + "\\com.ggl.testing2\\resources\\output.png";
        ImageIO.write(destImage, "png", new File(output));
    }

    private static int[] getPixels(BufferedImage image) {
        int length = image.getWidth() * image.getHeight();
        int[] pixels = new int[length];
        int index = 0;

        for (int h = 0; h < image.getHeight(); h++) {
            for (int w = 0; w < image.getWidth(); w++) {
                pixels[index++] = image.getRGB(w, h);
            }
        }

        return pixels;
    }

    private static BufferedImage putPixels(BufferedImage image,
            int[] pixels) {
        int index = 0;

        for (int i = pixels.length - 1; i >= 0; i--) {
            int h = index / image.getWidth();
            int w = index % image.getWidth();
            image.setRGB(w, h, pixels[i]);
            index++;
        }

        return image;
    }

}
英文:

I ran your code using a png image of mine. Here are my test results. Image type 6 is TYPE_4BYTE_ABGR from the BufferedImage source code.

Image size: 416 x 199
Image type: 6
Origin pixel: 90,32
Destination pixel: 326,167
Pixel values: ffeeeeee ffffffff
Pixels are equal: false

Here's the image.

用AffineTransform旋转图像会产生视觉上正确的像素,但getRGB不匹配。

Here's the rotated image.

用AffineTransform旋转图像会产生视觉上正确的像素,但getRGB不匹配。

I'm not sure why the colors are different. I can't visually distinguish the difference between an eeeeee pixel and an ffffff pixel.

Edited to add: I did a little more research and found this bit of documentation for the Image getRGB method.

> Returns an integer pixel in the default RGB color model(TYPE_INT_ARGB)
> and default sRGB colorspace. Color conversion takes place if this
> default model does not match the image ColorModel.

So, it appears that the getRGB method itself could be causing the color conversion.

Here's the code I ran.

import java.awt.geom.AffineTransform;
import java.awt.image.AffineTransformOp;
import java.awt.image.BufferedImage;
import java.io.File;
import javax.imageio.ImageIO;
public class RotateImage {
public static void main(String[] args) throws Exception {
String input = &quot;C:\\Users\\Owner\\OneDrive\\Pictures&quot;
+ &quot;\\Saved Pictures\\CustomJTextField.png&quot;;
BufferedImage origImage = ImageIO.read(new File(input));
AffineTransform af = new AffineTransform();
int centerX = origImage.getWidth() / 2;
int centerY = origImage.getHeight() / 2;
// Rotate 180 degrees
af.quadrantRotate(2, centerX, centerY);
AffineTransformOp affineTransformOp = new AffineTransformOp(
af, null);
System.out.println(&quot;Image size: &quot; + origImage.getWidth() +
&quot; x &quot; + origImage.getHeight());
System.out.println(&quot;Image type: &quot; + origImage.getType());
BufferedImage destImage = new BufferedImage(
origImage.getWidth(), origImage.getHeight(),
origImage.getType());
affineTransformOp.filter(origImage, destImage);
// Verify that destImage has indeed been rotated by point
// checking a random
// pixel.
int origX = 90;
int origY = 32;
// Where we expect this pixel to have been translated to.
int expX = destImage.getWidth() - origX;
int expY = destImage.getHeight() - origY;
System.out.println(&quot;Origin pixel: &quot; + origX + &quot;,&quot; + origY);
System.out.println(&quot;Destination pixel: &quot; + expX + &quot;,&quot; + expY);
int origPixel = origImage.getRGB(origX, origY);
int expPixel = destImage.getRGB(expX, expY);
System.out.println(&quot;Pixel values: &quot; +
Integer.toHexString(origPixel) + &quot; &quot; +
Integer.toHexString(expPixel));
// Always prints false. Why????
System.out.println(&quot;Pixels are equal: &quot; +
(origPixel == expPixel));
String output = &quot;C:\\Eclipse\\Eclipse-2020-workspace&quot;
+ &quot;\\com.ggl.testing2\\resources\\output.png&quot;;
ImageIO.write(destImage, &quot;png&quot;, new File(output));
}
}

Edited to add: I wrote my own transform to see whether or not it was the transform changing the color or the getRGB method changing the color.

Here are the test results from my own transform.

Image size: 416 x 199
Image type: 6
Origin pixel: 90,32
Destination pixel: 326,167
Pixel values: ffeeeeee ffeeeeee
Pixels are equal: true

I created an int array of pixels from the original image and wrote those pixels to the rotated image.

Here's the code.

import java.awt.image.BufferedImage;
import java.io.File;
import javax.imageio.ImageIO;
public class RotateImage {
public static void main(String[] args) throws Exception {
String input = &quot;C:\\Users\\Owner\\OneDrive\\Pictures&quot;
+ &quot;\\Saved Pictures\\CustomJTextField.png&quot;;
BufferedImage origImage = ImageIO.read(new File(input));
System.out.println(&quot;Image size: &quot; + origImage.getWidth() +
&quot; x &quot; + origImage.getHeight());
System.out.println(&quot;Image type: &quot; + origImage.getType());
int[] pixels = getPixels(origImage);
BufferedImage destImage = new BufferedImage(
origImage.getWidth(), origImage.getHeight(),
origImage.getType());
destImage = putPixels(destImage, pixels);
// Verify that destImage has indeed been rotated by point
// checking a random
// pixel.
int origX = 90;
int origY = 32;
// Where we expect this pixel to have been translated to.
int expX = destImage.getWidth() - origX;
int expY = destImage.getHeight() - origY;
System.out.println(&quot;Origin pixel: &quot; + origX + &quot;,&quot; + origY);
System.out.println(&quot;Destination pixel: &quot; + expX + &quot;,&quot; + expY);
int origPixel = origImage.getRGB(origX, origY);
int expPixel = destImage.getRGB(expX, expY);
System.out.println(&quot;Pixel values: &quot; +
Integer.toHexString(origPixel) + &quot; &quot; +
Integer.toHexString(expPixel));
System.out.println(&quot;Pixels are equal: &quot; +
(origPixel == expPixel));
String output = &quot;C:\\Eclipse\\Eclipse-2020-workspace&quot;
+ &quot;\\com.ggl.testing2\\resources\\output.png&quot;;
ImageIO.write(destImage, &quot;png&quot;, new File(output));
}
private static int[] getPixels(BufferedImage image) {
int length = image.getWidth() * image.getHeight();
int[] pixels = new int[length];
int index = 0;
for (int h = 0; h &lt; image.getHeight(); h++) {
for (int w = 0; w &lt; image.getWidth(); w++) {
pixels[index++] = image.getRGB(w, h);
}
}
return pixels;
}
private static BufferedImage putPixels(BufferedImage image,
int[] pixels) {
int index = 0;
for (int i = pixels.length - 1; i &gt;= 0; i--) {
int h = index / image.getWidth();
int w = index % image.getWidth();
image.setRGB(w, h, pixels[i]);
index++;
}
return image;
}
}

答案2

得分: 1

你正在查看错误的像素。

假设您的图像尺寸为 1x1 像素。最初,您正在查看像素 (0, 0)。然后,根据您的代码,在目标图像中,您正在查看像素 (width - 0, height - 1),结果是像素 (1, 1)。然而,明显您仍然需要查看 (0, 0),因为图像并没有像素 (1, 1)

在您的情况下,您的变换坐标并未超出图像范围,但它指向错误的像素。

要修复您的代码,请更改计算 expXexpY 值的行,并从每个值中减去1:

int expX = destImage.getWidth() - origX - 1;
int expY = destImage.getHeight() - origY - 1;

当我在我的图像上运行您的代码时,这将返回正确的像素值(因为您没有提供您使用的图像)。

代码中还存在另一个问题,只有在宽度或高度为奇数时才会显现出来:您正在使用整数算法来确定旋转的中心,这会在不应该时向下取整。更改确定中心的行,使用浮点数(以便您可以得到分数值作为结果):

double centerX = origImage.getWidth() / 2.0;
double centerY = origImage.getHeight() / 2.0;
英文:

You are looking at the wrong pixel.

Assume your image is 1x1 pixel in size. You're originally looking at pixel (0, 0). Then with your code, in the destination image, you're looking at pixel (width - 0, height - 1), which turns out to be pixel (1, 1). When clearly you still need to be looking at (0, 0) as the image doesn't have a pixel at (1, 1).

In your case, your transformed coordinate isn't outside your image, but it points to the wrong pixel.

To fix your code, change the lines that calculate your expX and expY values, and subtract 1 from each:

int expX = destImage.getWidth() - origX - 1;
int expY = destImage.getHeight() - origY - 1;

That returns the correct pixel values when I run your code (on my own image, as you haven't provided the image that you used).

There is another issue in the code that will only surface with an odd-numbered width or height: you're using integer arithmetic to determine the center of rotation, which rounds down when it shouldn't. Change the lines that determine the center to (to use floating-point numbers, so that you can get a fractional number as a result) :

double centerX = origImage.getWidth() / 2.0;
double centerY = origImage.getHeight() / 2.0;

huangapple
  • 本文由 发表于 2020年8月8日 06:47:14
  • 转载请务必保留本文链接:https://go.coder-hub.com/63310035.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定