为了账号安全,请及时绑定邮箱和手机立即绑定

建立一个匹配3点变换的AffineTransform对象

建立一个匹配3点变换的AffineTransform对象

至尊宝的传说 2023-06-04 11:23:22
我知道仿射变换前后 3 个点(p0、p1、p2)的位置(X 和 Y)。我想构建匹配此转换的 AffineTransformation 对象。换句话说,我想找到将已知点 p0、p1、p2 移动到它们已知目的地的仿射变换。这是我到目前为止所做的:package image_transformation;import java.awt.geom.AffineTransform;import java.awt.image.AffineTransformOp;import java.awt.image.BufferedImage;import java.io.File;import java.io.IOException;import javax.imageio.ImageIO;import math.Vector2d;public class ImageTransformation {    public static void main(String[] args) throws IOException {        // the position of the points before the transformation        Vector2d[] src = new Vector2d[] {                new Vector2d(486, 191),                new Vector2d(456, 565),                new Vector2d(149, 353)        };        // the position of the points after the transformation        Vector2d[] dest = new Vector2d[] {                new Vector2d(0, 0),                new Vector2d(0, 600),                new Vector2d(600, 600)        };        // the transformation that we are building        AffineTransform at = new AffineTransform();        // the translation to move the p0 to its destination        Vector2d translationVec = dest[0].sub(src[0]);        at.translate(translationVec.x, translationVec.y);        // the rotation around p0 (it will not move) to align p0, p1 and p1's destination        Vector2d vec0 = src[1].sub(src[0]);        Vector2d vec1 = dest[1].sub(dest[0]);        double angle = orientedAngle(vec0, vec1);        at.rotate(angle, src[0].x, src[0].y);    }Vector2d 类做一些关于向量的基本数学运算,它的每个方法都通过它们的名称(sub[stract]、mult[iply]、length、normalize 等)不言自明。我不知道如何终止这个算法。此外,如果已经存在可以完成所有这些操作的方法,我会非常乐意使用它。
查看完整描述

1 回答

?
波斯汪

TA贡献1811条经验 获得超4个赞

这至少与Texture deforming密切相关,4分,但我不会说它可以被认为是重复的。

你在那里做了很多数学工作。但也许这不是必需的。使用正确的方法,问题本身是相当微不足道的。考虑二维仿射变换的含义:它将一个空间变换到另一个空间。这里的关键点是:

矩阵列是将矩阵应用于单位向量的结果

现在,当你有 3 个点时,你可以从它们计算向量:

double dx1 = p1.getX() - p0.getX();

double dy1 = p1.getY() - p0.getY();


double dx2 = p2.getX() - p0.getX();

double dy2 = p2.getY() - p0.getY();

然后您可以简单地将这些值插入AffineTransform. 最后一列AffineTransform包含由 给出的翻译p0。结果是AffineTransform将点 (0,0)、(1,0) 和 (0,1) 分别转换为点p0p1p2。当您反转此变换时,它会将点p0p1和转换p2为点 (0,0)、(1,0) 和 (0,1)。

所以你所要做的就是

  • 创建将源点转换为单位向量的变换

  • 创建将单位向量转换为目标点的变换

  • 将两者连接起来

伪代码 (!) 真的很简单

    AffineTransform unitToSrc = computeTransform(src[0], src[1], src[2]);

    AffineTransform unitToDst = computeTransform(dst[0], dst[1], dst[2]);

    AffineTransform at = new AffineTransform();

    at.concatenate(unitToDst);

    at.concatenate(unitToSrc.inverted());

整个事情都在这里实现,作为 MCVE。红色点是“源”点,绿色点是“目的地”点。你可以用鼠标拖动它们:

//img3.sycdn.imooc.com/647c03e00001305202740287.jpg

蓝色圆圈表示将变换应用于源点的结果,您可以看到它们最终到达了所需的目标位置。


实际计算是通过computeTransform方法完成的。请注意,这是基于java.awt.geom.Point2D类(而不是Vector2d您省略的类)实现的,但这应该很容易更改:点或矢量类唯一使用的是 x/y 坐标。除此之外,实现中根本不涉及(自定义)数学。唯一的数学是反转仿射变换,但有一个内置的功能。


import java.awt.BorderLayout;

import java.awt.Color;

import java.awt.Graphics;

import java.awt.Graphics2D;

import java.awt.RenderingHints;

import java.awt.event.MouseEvent;

import java.awt.event.MouseListener;

import java.awt.event.MouseMotionListener;

import java.awt.geom.AffineTransform;

import java.awt.geom.Ellipse2D;

import java.awt.geom.NoninvertibleTransformException;

import java.awt.geom.Point2D;

import java.util.Arrays;


import javax.swing.JFrame;

import javax.swing.JPanel;

import javax.swing.SwingUtilities;


public class AffineTransformFromPoints

{

    public static void main(String[] args)

    {

        SwingUtilities.invokeLater(() -> createAndShowGUI());

    }


    private static void createAndShowGUI()

    {

        JFrame f = new JFrame();

        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        AffineTransformFromPointsPanel panel = 

            new AffineTransformFromPointsPanel();

        f.getContentPane().setLayout(new BorderLayout());

        f.getContentPane().add(panel, BorderLayout.CENTER);

        f.setSize(1200,900);

        f.setLocationRelativeTo(null);

        f.setVisible(true);

    }


}


class AffineTransformFromPointsPanel extends JPanel 

    implements MouseListener, MouseMotionListener

{

    private Point2D draggedPoint;


    // the position of the points before the transformation

    Point2D[] src = new Point2D[] {

        new Point2D.Double(486, 191),

        new Point2D.Double(456, 565),

        new Point2D.Double(149, 353)

    };


    // the position of the points after the transformation

    Point2D[] dst = new Point2D[] {

        new Point2D.Double(0, 0),

        new Point2D.Double(0, 600),

        new Point2D.Double(600, 600)

    };



    public AffineTransformFromPointsPanel()

    {

        addMouseListener(this);

        addMouseMotionListener(this);

    }


    @Override

    protected void paintComponent(Graphics gr)

    {

        super.paintComponent(gr);

        Graphics2D g = (Graphics2D)gr;

        g.setColor(Color.WHITE);

        g.fillRect(0, 0, getWidth(), getHeight());


        g.setRenderingHint(

            RenderingHints.KEY_ANTIALIASING, 

            RenderingHints.VALUE_ANTIALIAS_ON);


        g.setColor(Color.RED);

        for (Point2D v : src)

        {

            paint(g, v);

        }


        g.setColor(Color.GREEN);

        for (Point2D v : dst)

        {

            paint(g, v);

        }


        g.setColor(Color.BLUE);

        AffineTransform at = computeTransform(src, dst);

        for (Point2D v : src)

        {

            draw(g, v, at);

        }

    }


    private static AffineTransform computeTransform(

        Point2D src[], Point2D dst[])

    {

        AffineTransform unitToSrc = computeTransform(src[0], src[1], src[2]);

        AffineTransform unitToDst = computeTransform(dst[0], dst[1], dst[2]);

        AffineTransform srcToUnit = null;

        try

        {

            srcToUnit = unitToSrc.createInverse();

        }

        catch (NoninvertibleTransformException e)

        {

            System.out.println(e.getMessage());

            return new AffineTransform();

        }

        AffineTransform at = new AffineTransform();

        at.concatenate(unitToDst);

        at.concatenate(srcToUnit);

        return at;

    }


    private static AffineTransform computeTransform(

        Point2D p0, Point2D p1, Point2D p2)

    {

        AffineTransform at = new AffineTransform();

        double dx1 = p1.getX() - p0.getX();

        double dy1 = p1.getY() - p0.getY();

        double dx2 = p2.getX() - p0.getX();

        double dy2 = p2.getY() - p0.getY();

        at.setTransform(dx1, dy1, dx2, dy2, p0.getX(), p0.getY());

        return at;

    }


    private static void paint(Graphics2D g, Point2D p)

    {

        double r = 6;

        g.fill(new Ellipse2D.Double(

            p.getX() - r, p.getY() - r, r + r, r + r));

    }


    private static void draw(Graphics2D g, Point2D v, AffineTransform at)

    {

        double r = 8;

        Point2D p = new Point2D.Double(v.getX(), v.getY());

        at.transform(p, p);

        g.draw(new Ellipse2D.Double(

            p.getX() - r, p.getY() - r, r + r, r + r));

    }


    @Override

    public void mouseDragged(MouseEvent e)

    {

        if (draggedPoint != null)

        {

            draggedPoint.setLocation(e.getPoint());

            repaint();

        }

    }



    @Override

    public void mousePressed(MouseEvent e)

    {

        draggedPoint = closest(e.getPoint(), Arrays.asList(src));

        if (draggedPoint == null)

        {

            draggedPoint = closest(e.getPoint(), Arrays.asList(dst));

        }

    }


    private static Point2D closest(

        Point2D p, Iterable<? extends Point2D> points)

    {

        final double threshold = 10;

        Point2D closestPoint = null;

        double minDistance = Double.MAX_VALUE;


        for (Point2D point : points)

        {

            double dd = point.distance(p);

            if (dd < threshold && dd < minDistance)

            {

                minDistance = dd;

                closestPoint = point;

            }

        }

        return closestPoint;

    }


    @Override

    public void mouseReleased(MouseEvent e)

    {

        draggedPoint = null;

    }


    @Override

    public void mouseMoved(MouseEvent e)

    {

        // Nothing to do here

    }


    @Override

    public void mouseClicked(MouseEvent e)

    {

        // Nothing to do here

    }


    @Override

    public void mouseEntered(MouseEvent e)

    {

        // Nothing to do here

    }


    @Override

    public void mouseExited(MouseEvent e)

    {

        // Nothing to do here

    }


}



查看完整回答
反对 回复 2023-06-04
  • 1 回答
  • 0 关注
  • 131 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信