Java swt - 缩放和缩放后从Image获取实际的x,y坐标

时间:2016-06-08 22:27:24

标签: java image image-processing swt coordinates

我的图像已经缩放以适应。用户通过缩放图像选择矩形。

然后我根据这个选择重新绘制:

gc.drawImage(imageDisplayed, minX, minY, width, height, imageDisplayed.getBounds().x,  imageDisplayed.getBounds().y, imageDisplayed.getBounds().width, imageDisplayed.getBounds().height );

所以现在我希望能够从缩放和缩放的图像中获得原始坐标。这是对的吗?:

public Coordinate GetScaledXYCoordinate(int oldX, int oldY, int width, int height, int scaledWidth, int scaledHeight)
{       
    int newX = (int)(oldX * width)/scaledWidth;
    int newY = (int)(oldY * height)/scaledHeight;

    Coordinate retXY = new Coordinate(newX, newY);
    return retXY;
}


public Coordinate GetZoomedXYCoordinate(int oldX, int oldY, int startX, int endX, int startY, int endY,
        int width, int height,int scaledWidth, int scaledHeight)
{       
    // First get x,y after scaling
    Coordinate xy = GetScaledXYCoordinate(oldX, oldY, width, height, scaledWidth, scaledHeight);

    // Now get x.y after zooming
    int minX = Math.min(startX, endX);
    int minY = Math.min(startY, endY);

    int maxX = Math.max(startX, endX);
    int maxY = Math.max(startY, endY);

    int rectWidth = maxX - minX;
    int rectHeight = maxY - minY;
    return GetScaledXYCoordinate(xy.getX(), xy.getY(), width, height, scaledWidth, scaledHeight);
}

注意:我想要一种适用于多种缩放的算法,而不仅仅是一种缩放。

更新

理想情况下,我想要一个采用屏幕点X,Y的函数,并返回原始图像X,Y。在缩放和缩放后,该函数仍将返回正确的X,Y

4 个答案:

答案 0 :(得分:9)

方法selectionToOriginal应返回Rectangle,其中包含相对于原始图像的最后一个缩放选区的位置和尺寸。

收到:

  • scaledDimensionsPoint,其中包含缩放图片的尺寸,即执行缩放选择的位置
  • levelsList,连续缩放Rectangle选项;在第一级,您放置原始图像的尺寸

此测试程序显示其使用尺寸为800x600且缩放尺寸为400x300的原始图像。将两个连续的缩放选择应用于它。

import java.util.ArrayList;
import java.util.List;

import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;

public class ScaleTest {

    public static void main(String[] args) {

        Point scaledDimensions = new Point(400, 300);

        List<Rectangle> levels = new ArrayList<Rectangle>();

        // first level is the original image dimension
        levels.add(new Rectangle(0, 0, 800, 600));

        // other levels are the zooming selection inside the scaled image
        levels.add(new Rectangle(0, 0, 200, 150));
        levels.add(new Rectangle(200, 150, 200, 150));

        Rectangle selectionToOriginal = selectionToOriginal(scaledDimensions,
            levels);

        System.out.println(selectionToOriginal);
    }

    public static Rectangle selectionToOriginal(Point scaledDimensions,
        List<Rectangle> levels) {

        int numberOfLevels = levels.size();
        double scaledX = 0;
        double scaledY = 0;

        // we will work with the size of the last selection
        double scaledWidth = levels.get(numberOfLevels - 1).width;
        double scaledHeight = levels.get(numberOfLevels - 1).height;

        // start from the last selection to the first 
        for (int currentLevel = numberOfLevels - 1; currentLevel > 0; currentLevel--) {

            // get the width of the level N - 1
            double previousSelectionWidth = levels.get(currentLevel - 1).width;

            // convert the width of 1 unit in level N to its width in level N - 1
            double unitaryWidth = previousSelectionWidth / scaledDimensions.x;
            // convert the X position in level N in its X position in level N - 1
            scaledX = unitaryWidth * (levels.get(currentLevel).x + scaledX);
            // convert the width in level N in its width in level N - 1
            scaledWidth *= unitaryWidth;

            // get the height of the level N - 1
            double previousSelectionHeight = levels.get(currentLevel - 1).height;

            // convert the height of 1 unit in level N to its height in level N - 1
            double unitaryHeight = previousSelectionHeight / scaledDimensions.y;
            // convert the Y position in level N in its Y position in level N - 1
            scaledY = unitaryHeight * (levels.get(currentLevel).y + scaledY);
            // convert the height in level N in its height in level N - 1
            scaledHeight *= unitaryHeight;
        }

        return new Rectangle((int) scaledX, (int) scaledY, (int) scaledWidth,
            (int) scaledHeight);
    }

}

程序返回Rectangle位置(200,150)和大小(200,150),图像显示情况:

Program result

注意:

  • 在您的代码中使用了类Coordinate,它类似于我在方法中使用的SWTPoint
  • 返回指令中的演员

    return new Rectangle((int) scaledX, (int) scaledY, (int) scaledWidth,
            (int) scaledHeight);
    

    会截断双打的值,如果您希望舍入值,请考虑使用Math.round

答案 1 :(得分:8)

SWT有一个专门的课程Transform用于执行坐标转换(我宁愿说转换,因为 translation 在这样的上下文中只是一个特殊情况,其他转换是缩放旋转剪切)。 AWT有一个更方便的AffineTransform类,它没有绑定到图形子系统。

使用其中一个类可以简化以下操作。一旦构造了在一个方向上映射坐标的变换对象(例如,源图像坐标到显示坐标),就可以轻松获得逆变换(用于从显示坐标返回到源图像坐标)。使用invert()createInverse()(后者,仅使用 AffineTransform )方法。

使用transform()方法执行实际坐标转换。在SWT.Transform的情况下,如果您需要转换单个点,则其签名有点不方便,但您可以轻松地将其包装在辅助函数中。

出于您的目的,您只需使用scale()translate()方法来定义坐标转换。很可能你会想要根据源矩形和目标矩形来定义变换(类似于你对drawImage()方法的使用); this answer显示了如何做到这一点。然后,在缩放或以其他方式操纵图像的显示方式时,必须使变换对象保持最新。

<强>更新

@code_onkel使用此方法提供了an example program

答案 2 :(得分:8)

这是一个完整的工作示例,使用SWT放大图像,实现Leon's answer背后的想法。使用affine transformations是在2D图形中使用单个坐标系绘制元素的默认方法。

  1. 使用<?php use Phinx\Seed\AbstractSeed; class CategorySeeder extends AbstractSeed { public function run() { $data = array( array( 'name' => 'Confined Spaces' ) ); } } 在正确的位置绘制图片并缩放
  2. 使用Transform的反转来获取所选缩放区域的图像坐标。
  3. 计算新的Transform以显示缩放区域。
  4. 以下课程执行以下操作:

    • Transform存储在Transform
    • 缩放区域的屏幕坐标存储在paintTransformzoomStart
    • 所选区域的图像坐标是在拖动的缩放矩形的zoomEnd中计算的。
    • 新的setVisibleImageAreaInScreenCoordinatesTransform
    • 中计算
    • 其余大部分都可视为样板代码。

    请注意,图像永远不会被缩放版本替换。它是使用setVisibleImageAreaInImageCoordinates绘制的。这意味着图形上下文负责绘制缩放的图像。实际的绘画代码变得像

    一样简单
    paintTransform

    所有计算都是在鼠标事件触发的状态转换期间处理期间完成的,即ev.gc.setTransform(paintTransform); ev.gc.drawImage(img, 0, 0); 处理程序中调用的zoom()方法。

    mouseUp()

    调整窗口大小时,当前不会更改转换。这可以通过与缩放相同的方式实现:使用旧窗口大小计算以前可见的图像坐标,使用新窗口大小计算新变换。

答案 3 :(得分:1)

这是我的尝试。

private static Point transformPoint(ArrayList<RectF> rectangleLevels, PointF intPoint)
{
    RectF sourceRec = rectangleLevels.get(rectangleLevels.size()-1);
    Point sourcePoint = new Point((int)intPoint.X, (int)intPoint.Y);
    Point retPoint = sourcePoint;
    for (int i = rectangleLevels.size()-2; i >=0; i--) {
        RectF destRec = rectangleLevels.get(i);
        retPoint = transformPoint(sourceRec, destRec, sourcePoint);

        // Current destination point and rec become source for next round
        sourcePoint = retPoint;
        sourceRec = destRec;
    }
    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 static 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 static 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));
}

所以这意味着我只需要跟踪我的缩放和缩放,然后传入屏幕X,Y以从原始图像中获取我的x,y:

    ArrayList<RectF> rectangleLevels = new ArrayList<RectF>();
    RectF origImage = getRectangle(0,0,320,200);
    RectF scaledImage = getRectangle(0,0,800,800);
    RectF zoomedImage = getRectangle(310,190,10,10);
    RectF scaledZoomedImage = getRectangle(0,0,800,800);
    rectangleLevels.add(origImage);
    rectangleLevels.add(scaledImage);
    rectangleLevels.add(zoomedImage);
    rectangleLevels.add(scaledZoomedImage);
    PointF pointInZoomedImg = getPoint(799, 799);

    Point retPoint = transformPoint(rectangleLevels, pointInZoomedImg);