Construir un objeto AffineTransform que coincidan con la transformación de 3 puntos

Cl00e9ment:

Sé que la posición (X y Y) de 3 puntos (p0, p1, p2) antes y después de una transformación afín. Quiero construir el objeto AffineTransformation que coincidan con esta transformación. En otras palabras, quiero encontrar la transformación afín que moverá los conocidos puntos p0, p1, p2 a su destino conocido.

Esto es lo que he hecho hasta ahora:

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);

        // the scaling to adjust the distance between p0 and p1
        // problem: it will induce a translation
        Vector2d origin = src[1].sub(src[0]);
        Vector2d target = origin.normalize().mult(dest[1].sub(dest[0]).length());
        Vector2d scale = new Vector2d(target.x / origin.x, target.y / origin.y);
        if (Double.isNaN(scale.x)) scale.x = 1D;
        if (Double.isNaN(scale.y)) scale.y = 1D;
        at.scale(scale.x, scale.y);

        // TODO compute the induced translation and apply its inverse to move p0 and p1 to their destination

        // TODO terminate the transformation to move p2 to its destination

        // apply the transformation to an image to check if it works
        BufferedImage inImg = ImageIO.read(new File("input.png"));
        BufferedImage outImg = new BufferedImage(inImg.getWidth(), inImg.getHeight(), BufferedImage.TYPE_INT_ARGB);
        new AffineTransformOp(at, AffineTransformOp.TYPE_BICUBIC).filter(inImg, outImg);
        File outFile = new File("output.png");
        outFile.createNewFile();
        ImageIO.write(outImg, "png", outFile);
    }

    private static double orientedAngle(Vector2d vec0, Vector2d vec1) {
        return Math.atan2(vec0.x * vec1.y - vec0.y * vec1.x, vec0.x * vec1.x + vec0.y * vec1.y);
    }

}

La clase Vector2d hacer algunos cálculos básica acerca de vectores, cada uno de sus métodos se explican por sí por su nombre (sub [stract], mult [iply], longitud, normalizar, etc ...).

No tengo idea de cómo terminar este algoritmo. Además, si un método ya existe que hacer todo esto, yo estaría muy contento de usarlo.

Marco13:

Esto es, al menos, estrechamente relacionada con la textura de deformación, 4 puntos , pero yo no diría que se puede considerar como un duplicado.

Lo has hecho muy poco de materia de matemáticas allí. Pero tal vez eso no es necesario. Con el enfoque correcto, el problema en sí es bastante trivial. Considere el significado de una transformación afín en 2D: Se transforma un espacio a otro. El punto clave aquí es:

Las columnas de la matriz son los resultados de aplicar la matriz de los vectores unitarios

Ahora, cuando usted tiene 3 puntos, se puede calcular vectores de ellas:

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

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

Y a continuación, sólo tiene que enchufar estos valores en las primeras columnas de una AffineTransform. La última columna de la AffineTransformcontiene la traducción, que viene dada por p0. El resultado es una AffineTransformque transforma los puntos (0,0), (1,0) y (0,1) en los puntos p0, p1y p2, respectivamente. Cuando se invierte esta transformación, que convierte los puntos p0, p1y p2en los puntos (0,0), (1,0) y (0,1).

Así que todo lo que tiene que hacer es

  • crear la transformación que convierte los puntos de origen en los vectores unitarios
  • crear la transformación que convierte los vectores unitarios en los puntos de destino
  • concatenar los dos

El pseudocódigo (!) Es realmente así de simple

    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());

Todo esto se lleva a cabo aquí, como un MCVE. Los puntos rojos son los puntos "fuente", y los puntos verdes son los puntos "destino". Puede arrastrarlas con el ratón:

AffineTransformFromPoints

Los círculos azules indican los resultados de aplicar la transformación a los puntos de origen, y se puede ver que terminan en la posición de destino deseado.

El cálculo real se realiza con los computeTransformmétodos. Tenga en cuenta que esto se lleva a cabo sobre la base de java.awt.geom.Point2Dla clase (y no la Vector2dclase que se omitiera), pero esto debe ser fácil de cambio: La única cosa que se utilizan de la clase punto- o vector son las coordenadas x / y. Más allá de eso, no hay ninguna matemáticas (personalizado) que participan en absoluto en la implementación. La única matemáticas es invertir la transformada afín, pero hay una funcionalidad integrada para eso.

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
    }

}

Supongo que te gusta

Origin http://43.154.161.224:23101/article/api/json?id=235013&siteId=1
Recomendado
Clasificación