我的AffineTransformation之间的像素之间有间隙。

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

My AffineTransformation has gaps between pixels

问题

首先,我想说我没有使用 Oracle 制作的 AffineTransform 类。我正在使用自己的 "Affine 转换"。为什么?因为我正在制作一个像素引擎,而我找不到其他解决方案。所以我开始稍微查看了一下 维基百科 和矩阵的工作原理。然后我观看了 javidx9视频,了解如何实现它。我搞定了。旋转、反射、剪切等都正常工作,但是我出现了像素间隙。Javidx9 在他的视频中提到了这个问题,他解决了这个问题。我尝试了他的解决方案,但在进行反射、旋转和剪切时出现了 "ArrayOutOfIndex 错误"。我一直在努力解决这个问题,但由于代码过于复杂,我就是无法理解我在做什么。这就是我为什么来这里!我需要 你的 帮助!

我像这样搞定了它(有间隙):

GIF

我使用以下代码对带有间隙的变换进行渲染:

使用矩阵渲染图像

for (int x = 0; x < image2.getWidth(); x++)
{
    for (int y = 0; y < image2.getHeight(); y++)
    {
        matrixLast.forward(x, y);
        renderer.setPixel((int)matrixLast.getNx(), (int)matrixLast.getNy(), image2.getPixels()[x + y * image2.getWidth()], null, 255);
    }
}

然而,如你所见,你需要更多的信息。我还制作了一个 "Matrix3x3" 类,以下是重要的方法/函数

public static void Identity(Matrix3x3 mat)
{
    // ...(此处省略代码)
}

public static void Translate(Matrix3x3 mat, float ox, float oy)
{
    // ...(此处省略代码)
}

public static void Rotate(Matrix3x3 mat, float degrees)
{
    // ...(此处省略代码)
}

// ...(省略其他方法)

在上面的 "渲染" 代码中,你可以看到有一个名为 "matrixLast" 的变量。以下是我如何获得它的(它们在其他地方定义,例如 "Matrix3x3 name;")

Matrix3x3.Translate(matrixFinal3, -image2.getWidth() / 2, -image2.getHeight() / 2);
Matrix3x3.Rotate(matrixFinal2, rotation);
matrixSum = Matrix3x3.multiplyMatrix(matrixFinal2, matrixFinal3);
Matrix3x3.Translate(matrixFinal, 700, 600);
matrixLast = Matrix3x3.multiplyMatrix(matrixFinal, matrixSum);
matrixInv = Matrix3x3.invert(matrixLast);

我遇到的问题基本上是我正在尝试解决这些 "间隙"。我尝试了 Javidx9 的解决方案,它将渲染代码更改为以下内容(Utils.getMaximum() 返回最大的数,Utils.getMinimum() 返回最小的数):

float ex, ey, sx, sy;

matrixLast.forward(0f, 0f);
sx = matrixLast.getNx();
ex = matrixLast.getNx();
sy = matrixLast.getNy();
ey = matrixLast.getNy();

// ...(省略部分代码)

for (int x = (int)sx; x < (int)ex; x++)
{
    for (int y = (int)sy; y < (int)ey; y++)
    {
        matrixInv.forward(x, y);
        renderer.setPixel(x, y, image2.getPixels()[(int)(matrixInv.getNx() + 0.5) + (int)(matrixInv.getNy() + 0.5) * image2.getWidth()], null, 255);
    }
}

他还添加了一个 invert 方法:

public static Matrix3x3 invert(Matrix3x3 matIn) {
    // ...(此处省略代码)
}

这是当他讲到矩阵求逆时,我完全摸不着头脑,它变得过于复杂,而且由于我不知道代码确切地在做什么,所以我不知道如何进行故障排除。他的解决方案给我带来了 ArrayOutOfIndex 错误,但只在进行反射、旋转和剪切时出现:

Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: Index -9 out of bounds for length 589824
at com.dubstepzedd.gameengine.gamemanager.GameManager.render(GameManager.java:112)
// ...(省略部分错误信息)

我不知道为什么会发生这种情况。如果有人对如何以不同方式解决 "间隙" 问题或代码有何问题有任何建议,请留言或发表问题。

英文:

First I want to say that I am not using the AffineTransform class that Oracle made. I am doing my "own" AffineTransform. Why? I am making a pixel engine and I couldn't find any other solution. So I began looking a bit at Wikipedia and how matrices work. I then watched javidx9's video at how to implement it. I got it working. Rotation,reflection,shear etc worked fine but I got a pixel gap. Javidx9 mentioned this in his video and he solved the problem. I tried his solution but I got an "ArrayOutOfIndex error" when doing reflection,rotation and shear. I have been trying to solve the issue but I just can't understand what I am doing due to the complex code. That's why I am here! I need your help!

I got it working like this (with gaps):

GIF

The code I use for the transformation with gaps is the following:

Render the image with a matrix

		for (int x = 0; x &lt; image2.getWidth(); x++)
	{
		for (int y = 0; y &lt; image2.getHeight(); y++)
		{
			
			
			matrixLast.forward(x,y);
			renderer.setPixel((int)matrixLast.getNx(),(int) matrixLast.getNy(), image2.getPixels()[x+y*image2.getWidth()], null, 255);
		}
	}

However as you can see you need more information than that. I also made a "Matrix3x3" class, here are the important methods/functions:

	public static void Identity(Matrix3x3 mat)
{
	mat.matrix[0][0] = 1.0f; mat.matrix[1][0] = 0.0f; mat.matrix[2][0] = 0.0f;
	mat.matrix[0][1] = 0.0f; mat.matrix[1][1] = 1.0f; mat.matrix[2][1] = 0.0f;
	mat.matrix[0][2] = 0.0f; mat.matrix[1][2] = 0.0f; mat.matrix[2][2] = 1.0f;
}

public static void Translate(Matrix3x3 mat, float ox, float oy)
{
	mat.matrix[0][0] = 1.0f; mat.matrix[1][0] = 0.0f; mat.matrix[2][0] = ox;
	mat.matrix[0][1] = 0.0f; mat.matrix[1][1] = 1.0f; mat.matrix[2][1] = oy;
	mat.matrix[0][2] = 0.0f;	mat.matrix[1][2] = 0.0f;	mat.matrix[2][2] = 1.0f;
}

public static void Rotate(Matrix3x3 mat, float degrees)
{
	double fTheta = Math.toRadians(degrees);
	mat.matrix[0][0] = (float)Math.cos(fTheta);  mat.matrix[1][0] = (float)-Math.sin(fTheta); mat.matrix[2][0] = 0.0f;
	mat.matrix[0][1] = (float)Math.sin(fTheta); mat.matrix[1][1] = (float)Math.cos(fTheta); mat.matrix[2][1] = 0.0f;
	mat.matrix[0][2] = 0.0f;			 		 mat.matrix[1][2] = 0.0f;		 mat.matrix[2][2] = 1.0f;
}

public static void Scale(Matrix3x3 mat, float sx, float sy)
{
	mat.matrix[0][0] = sx;   mat.matrix[1][0] = 0.0f; mat.matrix[2][0] = 0.0f;
	mat.matrix[0][1] = 0.0f; mat.matrix[1][1] = sy;   mat.matrix[2][1] = 0.0f;
	mat.matrix[0][2] = 0.0f;	mat.matrix[1][2] = 0.0f;	mat.matrix[2][2] = 1.0f;
}

public static void Shear(Matrix3x3 mat, float sx, float sy)
{	
	mat.matrix[0][0] = 1.0f; mat.matrix[1][0] = sx;   mat.matrix[2][0] = 0.0f;
	mat.matrix[0][1] = sy;   mat.matrix[1][1] = 1.0f; mat.matrix[2][1] = 0.0f;
	mat.matrix[0][2] = 0.0f;	mat.matrix[1][2] = 0.0f;	mat.matrix[2][2] = 1.0f;
}

public static void Reflection(Matrix3x3 mat) {
	
	mat.matrix[0][0] = -1.0f; mat.matrix[1][0] = 0;   mat.matrix[2][0] = 0.0f;
	mat.matrix[0][1] = 0;   mat.matrix[1][1] = 1.0f; mat.matrix[2][1] = 0.0f;
	mat.matrix[0][2] = 0.0f;	mat.matrix[1][2] = 0.0f;	mat.matrix[2][2] = 1.0f;
}

public void forward(float inX, float inY) { //Ingen aning vad denna g&#246;r
	float outX,outY;
	
	outX = (inX * this.matrix[0][0]) + (inY * this.matrix[1][0]) + this.matrix[2][0];	
	outY = (inX * this.matrix[0][1]) + (inY * this.matrix[1][1]) + this.matrix[2][1];
	
	this.ny = outY;
	this.nx = outX;
}

In the "render" code as you can see above you see that there is a "matrixLast" in there. Here is how I got that one (they are defined elsewhere as for example: "Matrix3x3 name;"):

	Matrix3x3.Translate(matrixFinal3, -image2.getWidth() / 2, -image2.getHeight() / 2);
	
	Matrix3x3.Rotate(matrixFinal2,rotation);			//Rotation och shear &#228;r det n&#229;got fel p&#229;.
	
	matrixSum = Matrix3x3.multiplyMatrix(matrixFinal2, matrixFinal3);
	Matrix3x3.Translate(matrixFinal, 700, 600);
	
	matrixLast = Matrix3x3.multiplyMatrix(matrixFinal, matrixSum);
	
	matrixInv = Matrix3x3.invert(matrixLast);

The issue I am having is basically that I am trying to fix the "gaps". I tried Javidx9's solution which changes the render code to the following (Utils.getMaximum() returns the largest number and the opposite for Utils.getMinimum() ):

	float ex,ey,sx,sy;
	
	
	
	matrixLast.forward(0f, 0f);	
	sx = matrixLast.getNx();
	ex = matrixLast.getNx();
	sy = matrixLast.getNy();
	ey = matrixLast.getNy();
			
	matrixLast.forward((float) image2.getWidth(), (float) image2.getHeight());		
	sx = Utils.getMinimum(sx, matrixLast.getNx()); sy = Utils.getMinimum(sy, matrixLast.getNy());
	ex  = Utils.getMaximum(ex, matrixLast.getNx()); ey = Utils.getMaximum(ey, matrixLast.getNy());
	
	matrixLast.forward(0f, (float) image2.getHeight());		
	sx = Utils.getMinimum(sx, matrixLast.getNx()); sy = Utils.getMinimum(sy, matrixLast.getNy());
	ex  = Utils.getMaximum(ex, matrixLast.getNx()); ey = Utils.getMaximum(ey, matrixLast.getNy());
	
	matrixLast.forward((float) image2.getWidth(), 0f);		
	sx = Utils.getMinimum(sx, matrixLast.getNx()); sy = Utils.getMinimum(sy, matrixLast.getNy());
	ex  = Utils.getMaximum(ex, matrixLast.getNx()); ey = Utils.getMaximum(ey, matrixLast.getNy());
	
	for(int x = (int)sx; x &lt; (int)ex; x++) {
		
		for(int y = (int)sy; y &lt; (int)ey; y++) {
			
			matrixInv.forward(x,y);
			
			
			
					
			
			renderer.setPixel(x, y, image2.getPixels()[(int)(matrixInv.getNx() + 0.5) + (int)(matrixInv.getNy() + 0.5) * image2.getWidth()], null, 255);
			
		}
	}

He also added an invert method:

	public static Matrix3x3 invert(Matrix3x3 matIn) {
	
	float det = matIn.matrix[0][0] * (matIn.matrix[1][1] * matIn.matrix[2][2] - matIn.matrix[1][2] * matIn.matrix[2][1]) -
			matIn.matrix[1][0] * (matIn.matrix[0][1] * matIn.matrix[2][2] - matIn.matrix[2][1] * matIn.matrix[0][2]) +
			matIn.matrix[2][0] * (matIn.matrix[0][1] * matIn.matrix[1][2] - matIn.matrix[1][1] * matIn.matrix[0][2]);

		float idet = 1.0f / det;
		Matrix3x3 matOut = new Matrix3x3();
		
		matOut.matrix[0][0] = (matIn.matrix[1][1] * matIn.matrix[2][2] - matIn.matrix[1][2] * matIn.matrix[2][1]) * idet;
		matOut.matrix[1][0] = (matIn.matrix[2][0] * matIn.matrix[1][2] - matIn.matrix[1][0] * matIn.matrix[2][2]) * idet;
		matOut.matrix[2][0] = (matIn.matrix[1][0] * matIn.matrix[2][1] - matIn.matrix[2][0] * matIn.matrix[1][1]) * idet;
		matOut.matrix[0][1] = (matIn.matrix[2][1] * matIn.matrix[0][2] - matIn.matrix[0][1] * matIn.matrix[2][2]) * idet;
		matOut.matrix[1][1] = (matIn.matrix[0][0] * matIn.matrix[2][2] - matIn.matrix[2][0] * matIn.matrix[0][2]) * idet;
		matOut.matrix[2][1] = (matIn.matrix[0][1] * matIn.matrix[2][0] - matIn.matrix[0][0] * matIn.matrix[2][1]) * idet;
		matOut.matrix[0][2] = (matIn.matrix[0][1] * matIn.matrix[1][2] - matIn.matrix[0][2] * matIn.matrix[1][1]) * idet;
		matOut.matrix[1][2] = (matIn.matrix[0][2] * matIn.matrix[1][0] - matIn.matrix[0][0] * matIn.matrix[1][2]) * idet;
		matOut.matrix[2][2] = (matIn.matrix[0][0] * matIn.matrix[1][1] - matIn.matrix[0][1] * matIn.matrix[1][0]) * idet;
	
	return matOut;
	
}

Here is when he totally lost me, it got too complex and I don't know how to troubleshoot it due to me not knowing exactly what the code does. His solution gave me an ArrayOutOfIndexerror but only when doing reflections,rotations and shear:

Exception in thread &quot;main&quot; java.lang.ArrayIndexOutOfBoundsException: Index -9 out of bounds for length 589824
at com.dubstepzedd.gameengine.gamemanager.GameManager.render(GameManager.java:112)
at com.dubstepzedd.gameengine.GameContainer.loop(GameContainer.java:94)
at com.dubstepzedd.gameengine.GameContainer.run(GameContainer.java:48)
at java.base/java.lang.Thread.run(Thread.java:832)
at com.dubstepzedd.gameengine.GameContainer.start(GameContainer.java:40)
at com.dubstepzedd.gameengine.gamemanager.GameManager.main(GameManager.java:178)

I have no clue why this is happening. If someone has any advice on how to approach the "gap" situation differently or what is wrong with the code please comment or post a question

UPDATE

Here is the how it acts when I added getPixel():

Link to GIF

The code added to the Image class:

public int getPixel(int x, int y) {
	
	if(0 &lt;= x &amp;&amp; x &lt; this.width &amp;&amp; 0 &lt;= y &amp;&amp; y &lt; this.height) {
		return pixels[x+y*this.width];
	}
	else {
		return Color.black.getRGB();	
	}
}

答案1

得分: 1

你正在尝试访问原始图像范围之外的像素:

image2.getPixels()[(int)(matrixInv.getNx() + 0.5) + (int)(matrixInv.getNy() + 0.5) * image2.getWidth()]

我建议在你的图像类中添加一个方法,根据x/y位置返回像素,如果x和y在范围内,否则返回默认颜色:

// 添加到图像类中:
int getPixel(int x, int y) {
    if (0 <= x && x < width && 0 <= y && y < height) {
        return pixelData[x + width * y];
    } else {
        return backgroundColor;
    }
}

在数组查找的地方使用这个方法 - 例如:

for (int x = (int)sx; x < (int)ex; x++) {
    for (int y = (int)sy; y < (int)ey; y++) {
        matrixInv.forward(x, y);
        int pixel = image2.getPixel((int)(matrixInv.getNx() + 0.5), (int)(matrixInv.getNy() + 0.5));
        renderer.setPixel(x, y, pixel);
    }
}
英文:

You are trying to access a pixel that's outside the original image here:

image2.getPixels()[(int)(matrixInv.getNx() + 0.5) + (int)(matrixInv.getNy() + 0.5) * image2.getWidth()]

I would recommend adding a method to your image class that returns the pixel at a x/y position, if x and y are in range, and a default color if not:

// add to image class:
int getPixel(int x, int y) {
    if (0 &lt;= x &amp;&amp; x &lt; width &amp;&amp; 0 &lt;= y &amp;&amp; y &lt; height) {
        return pixelData[x + width*y];
    } else {
        return backgroundColor;
    }
}

Use this method instead of array lookups - for example

for(int x = (int)sx; x &lt; (int)ex; x++) {
    for(int y = (int)sy; y &lt; (int)ey; y++) {
        matrixInv.forward(x,y);
        int pixel = image2.getPixel((int)(matrixInv.getNx() + 0.5, (int)(matrixInv.getNy() + 0.5))
        renderer.setPixel(x, y, pixel);
    }
}

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

发表评论

匿名网友

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

确定