缩放和缩放后屏幕坐标和图像坐标之间的转换

3
我有一个类来管理从屏幕坐标到图像坐标的转换。但是,我有一个“偏移一”的错误。
以下代码输出318, 198,而不是319, 199:
@Test
public void test6rightScreenCornerToImageCoOrdAfterZoomingAndScaling() {
    PointTranslation pointTranslation = new PointTranslation();
    pointTranslation.setOriginalSize(0, 0, 320, 200); // original image
    pointTranslation.zoomIn(9, 9, 310, 190); // zoomed image starting at 9,9
    pointTranslation.scale(0, 0, 800, 800);

    Point translatedPoint = pointTranslation.transformPoint(799,799);
    System.out.println(testName.getMethodName() + " : " + translatedPoint.toString());
    assertTrue(translatedPoint.x == 319);
    assertTrue(translatedPoint.y == 199);
}

完整列表:

package gtx;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.internal.gdip.PointF;
import org.eclipse.swt.internal.gdip.RectF;

@SuppressWarnings("restriction")
public class PointTranslation {
RectF originalSize = null;
RectF currentSize = null;
RectF scaledSize = null;

public void setOriginalSize(int originX, int originY, int width, int height) {
    originalSize = getRectangle(originX, originY, width, height);
    currentSize = originalSize;
}

public void zoomIn(int originX, int originY, int width, int height) {
    // System.out.println("addTranslation: " + originX + " " + originY + " "
    // + width + " " + height);
    currentSize = getRectangle(originX, originY, width, height);
}

public void scale(int originX, int originY, int width, int height) {
    // System.out.println("addTranslation: " + originX + " " + originY + " "
    // + width + " " + height);
    scaledSize = getRectangle(originX, originY, width, height);
}

public boolean isPointWithinBounds(Point point) {
    return isPointWithinBounds(point.x, point.y);
}

public boolean isPointWithinBounds(int xPos, int yPos) {
    boolean ret = false;

    if (scaledSize != null) {
        RectF sourceRec = scaledSize;
        int xBounds = (int) (sourceRec.Width + sourceRec.X);
        int yBounds = (int) (sourceRec.Height + sourceRec.Y);
        ret = (xPos < xBounds) && (yPos < yBounds) && (xPos > sourceRec.X) && (yPos > sourceRec.Y);
    }

    return ret;
}

public Point transformPoint(Point point) {
    return transformPoint(point.x, point.y);
}

public Point transformPoint(int xPos, int yPos) {
    Point sourcePoint = new Point((int) xPos, (int) yPos);
    Point retPoint = sourcePoint;

    if (this.scaledSize != null) {
        retPoint = transformPoint(this.scaledSize, this.currentSize, sourcePoint);
    }
    return retPoint;
}

/*
 * Rectangle 1 has (x1, y1) origin and (w1, h1) for width and height, and
 * Rectangle 2 has (x2, y2) origin and (w2, h2) for width and height, then
 * 
 * Given point (x, y) in terms of Rectangle 1 co-ords, to convert it to
 * Rectangle 2 co-ords: xNew = ((x-x1)/w1)*w2 + x2; yNew = ((y-y1)/h1)*h2 +
 * y2;
 */
private Point transformPoint(RectF source, RectF destination, Point intPoint) {
    PointF point = new PointF();
    point.X = intPoint.x;
    point.Y = intPoint.y;
    return transformPoint(source, destination, point);
}

private Point transformPoint(RectF source, RectF destination, PointF point) {
    return new Point((int) ((((point.X - source.X) / source.Width) * destination.Width + destination.X)),
            (int) ((((point.Y - source.Y) / source.Height) * destination.Height + destination.Y)));
}

private RectF getRectangle(int x, int y, int width, int height) {
    RectF rect = new RectF();
    rect.X = x;
    rect.Y = y;
    rect.Height = height;
    rect.Width = width;
    return rect;
}

private PointF getPoint(int x, int y) {
    PointF retPoint = new PointF();
    retPoint.X = x;
    retPoint.Y = y;
    return retPoint;
}

public void reset() {
    this.originalSize = null;
    this.currentSize = null;
    this.scaledSize = null;
}
}

更新:

我的问题明显与舍入有关。 奇怪的是,对于某些测试用例,我需要向上舍入才能获得正确的点,而有时我需要向下舍入。 我好像缺少一个比例因子或其他东西。 有没有建议如何正确地在两个矩形之间进行转换?

更新2:

我尝试了以下方法,但仍然没有成功:

    private Point transformPoint(RectF source, RectF destination, PointF point) {   
        float xPercent = normalize(point.X,source.X,source.Width);
        float destX = xPercent*(Math.abs(destination.Width - destination.X)) + destination.X;

        float yPercent = normalize(point.Y,source.Y,source.Height);
        float destY = yPercent*(Math.abs(destination.Height - destination.Y)) + destination.Y;

        System.out.println("Float x,y: " + destX + ", " + destY);
        System.out.println("Ceil Float x,y: " + Math.floor(destX) + ", " + Math.floor(destY) );

        return new Point((int)Math.floor(destX), (int)Math.floor(destY));
    }

    private float normalize(float value, float min, float max) {
        return Math.abs((value - min) / (max - min));
    }

test6 中,您使用了哪个点? - Loris Securo
@LorisSecuro - 已更新 - 谢谢 - Robben_Ford_Fan_boy
你提供的代码不完整。请提供一个 [mcve]。 - Roman
您的代码现在缺少PointTranslation.addTranslation方法,正如您的测试用例所引用的那样。在您更新之前,该代码是存在的。 - Reenactor Rob
@ReenactorRob - 已更新 - Robben_Ford_Fan_boy
显示剩余2条评论
3个回答

2
在运行测试用例并逐步执行代码时,我的调试显示以下替换...
transformPoint(RectF source, RectF destination, PointF point) {
    return new Point (
        (int) (((( 799 - 0 ) / 800 ) * 310 ) + 9 )),
        (int) (((( 799 - 0 ) / 800 ) * 190 ) + 9 ))
    );
}

方程的前半部分返回318.6125。方程的后半部分返回198.7625
你需要做以下操作之一:
  1. 修改方程,使int转换截断到所需值(例如在末尾添加+ 1
  2. 在转换为int之前进行四舍五入
  3. 接受结果
正如马修所指出的那样,连续多次翻译会扭曲和放大问题,就像平均平均值一样。

1
如Reenactor所述,您需要在将点数转换为整数之前进行四舍五入:
private Point transformPoint(RectF source, RectF destination, PointF point) {
   final int ptx = Math.round((((point.X - source.X) / source.Width) * destination.Width + destination.X));
   final int pty = Math.round((((point.Y - source.Y) / source.Height) * destination.Height + destination.Y));
   return new Point(ptx, pty);    
}

我认为这比那要复杂一些,因为有时需要将输出向下舍入,有时则需要向上舍入。 - Robben_Ford_Fan_boy

0

你是否可能在每次翻译后将中间结果转换为整数点?

请记住,org.eclipse.swt.graphics.Pointxy 存储为 int,因此在计算中任何小数部分都会被舍弃

也就是说:

1)第一次翻译:

x1 = ((799 - 0) / 800) * 301 + 9 = 309.62375
y1 = ((799 - 0) / 800) * 181 + 9 = 189.77375

如果您在进行下一次转换之前将它们存储在一个Point对象中,它们将被截断为(309, 189),然后您将得到以下结果:
2) 第二次翻译
x2 = ((309 - 9) / 301) * 320 + 0 = 318.93...
y2 = ((189 - 9) / 181) * 200 + 0 = 198.89...

这将被截断为(318,198)。


我肯定认为这是一个四舍五入的问题。我不确定在哪里以及如何修复它。不过我现在已经简化了问题。 - Robben_Ford_Fan_boy
你的代码中第二次翻译(回到原始大小)是如何实现的?我在你发布的内容中没有看到错误,所以我猜问题就出在这里。也就是说,你将你的(799,799)点转换为你的(9,9,310,190)坐标系,然后将其存储为“Point”而不是“PointF”,并将其转换为你的(0,0,320,200)坐标系。这就是错误所在。 - Matthew McPeak
我得到了318和198,而不是319和199。 - Robben_Ford_Fan_boy
啊,我明白了。310是矩形的宽度,而不是它的边界。在这种情况下,你遇到了一个简单的四舍五入问题。你的公式返回'x'的318.62375。当Java将其放入'int'中时,会丢掉小数部分,而不是四舍五入。在将它们传递给“Point”构造函数之前,请使用“Math.round()”将表达式括起来。 - Matthew McPeak
很奇怪,对于某些测试用例,我需要向上取整才能得到正确的结果,而有时我又需要向下取整。我可能缺少一个比例因子或其他东西。 - Robben_Ford_Fan_boy
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接