Java 按正确顺序重新排列矩形的四个点

Java 按正确顺序重新排列矩形的四个点,java,sorting,Java,Sorting,纵横比=高度/宽度总是>1(大多数情况下甚至>2),因此我希望如何旋转应该是清晰/精确的 我在OpenCV/Java中有一个RotatedRect对象。 我可以得到一个数组,其中包含4个类型为Point和Point定义x/y值的对象 现在我想对这4个点进行排序,使左上角的点是数组的第一个元素,然后顺时针,使上下角的点是第四个元素 我假设矩形没有旋转太多(只是一些小角度),例如 在这个例子中,我已经指出了哪个点是左上角(TL) 怎么做 你不需要特别告诉我OpenCV等,只要假设你有两个数组 i

纵横比=高度/宽度总是>1(大多数情况下甚至>2),因此我希望如何旋转应该是清晰/精确的


我在OpenCV/Java中有一个
RotatedRect
对象。
我可以得到一个数组,其中包含4个类型为Point和Point定义x/y值的对象

现在我想对这4个点进行排序,使左上角的点是数组的第一个元素,然后顺时针,使上下角的点是第四个元素

我假设矩形没有旋转太多(只是一些小角度),例如

在这个例子中,我已经指出了哪个点是左上角(TL)

怎么做

你不需要特别告诉我OpenCV等,只要假设你有两个数组

int[] x = new int[4];
int[] y = new int[4];

n
-th点具有坐标
(x[n-1],y[n-1])
。然后我可以专门为OpenCV做这件事。

搜索具有最高y值的2个点,其中一个始终是定义中的TL(宽度<高度和小角度(不高于45°)

按y值的降序对数组排序,并获取具有第二个最高y值的元素

如果此点的x值最低,则定义右侧图片(1)。否则,具有最高值的点是TL,并定义左侧图片(2)

现在得到顺时针的顺序,其中TL是第一个元素

在案例(1)中:更改排序数组的最后2个元素的位置 情况(2):更改前2个元素的位置


这是真的,因为你的定义,但我不能用正确的数学方法解释它。

我只是尝试了一种方法。我不确定它是“更简单”还是在任何其他方面比你的“更好”,但我将它作为一个建议发布在这里:

您可以计算矩形的中心。然后您可以移动矩形,使其中心位于原点。然后您可以使用
Math.atan2
方法计算每个点与x轴的角度。这里最简洁的一点是:它返回的角度范围为-PI…+PI,与您所需的顺序完全匹配:upper左点将具有“最负”角度,左下点将具有“最正”角度

此描述仅用于说明。其中一些步骤(尤其是“移动”矩形)不必显式完成

但是:在此示例中,您可以通过鼠标单击来设置角点。每个点都将标记其索引和角度,如上所述。当设置第四个点时,这些点将相应地重新排序,并显示结果索引/角度

就我所见,结果似乎是你想要计算的

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Point2D;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;


public class RectanglePointReorderingTest
{
    public static void main(String[] args)
    {
        SwingUtilities.invokeLater(new Runnable()
        {
            @Override
            public void run()
            {
                createAndShowGUI();
            }
        });
    }

    private static void createAndShowGUI()
    {
        JFrame f = new JFrame();
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.getContentPane().add(new RectanglePointReorderingPanel());
        f.setSize(800, 800);
        f.setLocationRelativeTo(null);
        f.setVisible(true);
    }

    static Point2D computeCenter(List<Point2D> points)
    {
        double x = 0;
        double y = 0;
        for (Point2D p : points)
        {
            x += p.getX();
            y += p.getY();
        }
        x /= points.size();
        y /= points.size();
        return new Point2D.Double(x, y);
    }

    static double computeAngle(Point2D center, Point2D point)
    {
        double dx = point.getX() - center.getX();
        double dy = point.getY() - center.getY();
        double angleRad = Math.atan2(dy, dx);
        return angleRad;
    }

    static Comparator<Point2D> createComparator(Point2D center)
    {
        final Point2D finalCenter = 
            new Point2D.Double(center.getX(), center.getY());
        return new Comparator<Point2D>()
        {
            @Override
            public int compare(Point2D p0, Point2D p1)
            {
                double angle0 = computeAngle(finalCenter, p0);
                double angle1 = computeAngle(finalCenter, p1);
                return Double.compare(angle0, angle1);
            }
        };
    }    

    static void sortPoints(List<Point2D> points)
    {
        Collections.sort(points, createComparator(computeCenter(points)));
    }

}


class RectanglePointReorderingPanel extends JPanel implements MouseListener
{
    private List<Point2D> points = new ArrayList<Point2D>();

    public RectanglePointReorderingPanel()
    {
        addMouseListener(this);
    }

    @Override
    protected void paintComponent(Graphics gr)
    {
        super.paintComponent(gr);
        Graphics2D g = (Graphics2D)gr;

        g.setColor(Color.BLACK);
        if (points.size() < 4)
        {
            g.drawString("Click to create points", 20, 20);
        }
        else
        {
            g.drawString("Sorted points. Click again to clear.", 20, 20);
        }
        for (int i=0; i<points.size(); i++)
        {
            Point2D point = points.get(i);
            double x = point.getX();
            double y = point.getY();
            g.setColor(Color.RED);
            g.fill(new Ellipse2D.Double(x-5,y-5,10,10));

            g.setColor(Color.BLACK);

            double angleRad = 
                RectanglePointReorderingTest.computeAngle(
                    RectanglePointReorderingTest.computeCenter(points), point);
            String angleString = String.valueOf((int)Math.toDegrees(angleRad));
            g.drawString(String.valueOf(i)+" ("+angleString+")", (int)x+5, (int)y+5);


        }
    }

    @Override
    public void mouseClicked(MouseEvent e)
    {
        if (points.size() == 4)
        {
            points.clear();
            repaint();
        }
        else
        {
            points.add(e.getPoint());
            if (points.size() == 4)
            {
                RectanglePointReorderingTest.sortPoints(points);
            }
            repaint();
        }
    }


    @Override
    public void mouseEntered(MouseEvent e) {}

    @Override
    public void mouseExited(MouseEvent e) { }

    @Override
    public void mousePressed(MouseEvent e) { }

    @Override
    public void mouseReleased(MouseEvent e) { }


}
导入java.awt.Color;
导入java.awt.Graphics;
导入java.awt.Graphics2D;
导入java.awt.event.MouseEvent;
导入java.awt.event.MouseListener;
导入java.awt.geom.Ellipse2D;
导入java.awt.geom.Point2D;
导入java.util.ArrayList;
导入java.util.Collections;
导入java.util.Comparator;
导入java.util.List;
导入javax.swing.JFrame;
导入javax.swing.JPanel;
导入javax.swing.SwingUtilities;
公共类矩形点重新排序测试
{
公共静态void main(字符串[]args)
{
SwingUtilities.invokeLater(新的Runnable()
{
@凌驾
公开募捐
{
createAndShowGUI();
}
});
}
私有静态void createAndShowGUI()
{
JFrame f=新的JFrame();
f、 setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f、 getContentPane().add(新的矩形点重新排序面板());
f、 设置大小(800800);
f、 setLocationRelativeTo(空);
f、 setVisible(真);
}
静态点2D计算中心(列出点)
{
双x=0;
双y=0;
对于(点2D p:点)
{
x+=p.getX();
y+=p.getY();
}
x/=points.size();
y/=points.size();
返回新的点2d.Double(x,y);
}
静态双计算角(点2D中心,点2D点)
{
double dx=point.getX()-center.getX();
double dy=point.getY()-center.getY();
双角度rad=数学atan2(dy,dx);
返回角度;
}
静态比较器createComparator(点2D中心)
{
最终点2D最终中心=
新的Point2D.Double(center.getX(),center.getY());
返回新的比较器()
{
@凌驾
公共整数比较(点2D p0,点2D p1)
{
双角度0=计算角度(最终中心,p0);
双角度1=计算角度(最终中心,p1);
返回双精度。比较(角度0,角度1);
}
};
}    
静态无效排序点(列表点)
{
排序(点,createComparator(计算中心(点));
}
}
类RectanglePointReorderingPanel扩展JPanel实现MouseListener
{
私有列表点=新的ArrayList();
公共矩形点重新排序面板()
{
addMouseListener(这个);
}
@凌驾
受保护的组件(图形组)
{
超级油漆组件(gr);
Graphics2D g=(Graphics2D)gr;
g、 设置颜色(颜色为黑色);
if(points.size()<4)
{
g、 抽绳(“点击创建点”,20,20);
}
其他的
{
g、 抽绳(“已排序的点。再次单击以清除。”、20、20);
}
对于(int i=0;iAnswer)
如果您知道,则有一个非常简单的解决方案:

  • -45
  • roundedRect.size.height>roundedRect.size.width
  • 如果这是真的,那么按顺时针顺序排列的点将始终按以下顺序排列:

    pts[0], pts[3], pts[2], pts[1]
    
    作为旁白,如果
    import java.awt.BorderLayout;
    import java.awt.Dimension;
    import java.awt.EventQueue;
    import java.awt.Graphics;
    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
    
    import javax.swing.JComponent;
    import javax.swing.JFrame;
    import javax.swing.JPanel;
    import javax.swing.Timer;
    
    import org.opencv.core.Point;
    import org.opencv.core.RotatedRect;
    import org.opencv.core.Size;
    
    public class TestFrame extends JFrame {
        public static void main(String... args) {
            final TestFrame frame = new TestFrame();
            EventQueue.invokeLater(new Runnable() {
                @Override
                public void run() {
                    frame.setVisible(true);
                }
            });
        }
    
        private RectComponent rect;
    
        public TestFrame() {
            JPanel containerPane = new JPanel(new BorderLayout());
            setDefaultCloseOperation(EXIT_ON_CLOSE);
            rect = new RectComponent();
            containerPane.add(rect);
            setContentPane(containerPane);
            setSize(400,400);
            new Timer(100, rect).start();
        }
    
        public class RectComponent extends JComponent implements ActionListener {
            private RotatedRect rect = new RotatedRect(new Point(0,0), new Size(1, 3), 0);
    
            private final Point[] pts = new Point[4];
    
            @Override
            protected void paintComponent(Graphics g) {
                rect.points(pts);
                printPoints();
                Dimension size = getSize();
                drawRectLine(g, pts[0], pts[1], size);
                drawRectLine(g, pts[1], pts[2], size);
                drawRectLine(g, pts[2], pts[3], size);
                drawRectLine(g, pts[0], pts[3], size);
            }
    
            private void printPoints() {
                System.out.format("A: %d, TL: %s, TR: %s, BR: %s, BL%s%n",
                        (int) (rect.angle + (rect.angle < 0 ? -1e-6 : 1e-6)), // Stupid doubles, stupid rounding error
                        pointToString(pts[0]),
                        pointToString(pts[3]),
                        pointToString(pts[2]),
                        pointToString(pts[1]));
            }
    
            private String pointToString(Point p) {
                return String.format("{%.2f,%.2f}",p.x, p.y);
            }
    
            private void drawRectLine(Graphics g, Point left, Point right, Dimension size) {
                g.drawLine(scale(left.x, size.width), scale(left.y, size.height),
                        scale(right.x, size.width), scale(right.y, size.height));
            }
    
    
            private int scale(double value, int coord) {
                return (int) (value * coord) / 4 + coord / 2;
            }
    
    
            @Override
            public void actionPerformed(ActionEvent e) {
                rect.angle += 1;
                if(rect.angle > 44) rect.angle = -44;
                repaint();
            }
        }
    }
    
    shortest distance = top right point 
    longest distance = bottom right point 
    middle distance = bottom left point
    
    point[0] - bottom left
    point[1] - top left
    point[2] - top right
    point[3] - bottom right
    
    public void points(Point pt[])
        {
            double _angle = angle * Math.PI / 180.0;
            double b = (double) Math.cos(_angle) * 0.5f;
            double a = (double) Math.sin(_angle) * 0.5f;
    
            pt[0] = new Point(
                    center.x - a * size.height - b * size.width,
                    center.y + b * size.height - a * size.width);
    
            pt[1] = new Point(
                    center.x + a * size.height - b * size.width,
                    center.y - b * size.height - a * size.width);
    
            pt[2] = new Point(
                    2 * center.x - pt[0].x,
                    2 * center.y - pt[0].y);
    
            pt[3] = new Point(
                    2 * center.x - pt[1].x,
                    2 * center.y - pt[1].y);
        }
    
    // Distance (x1, y1) to (x2, y2) = abs( sqrt( (x2-x1)^2 + (y2-y1)^2 ) )
    // Note:This is a quite literal translation of the formula, there are more efficient ways.
    public static final double pointsDist(Point pt1, Point pt2){        
       return  Math.abs( Math.sqrt( Math.pow((double) (pt2.x - pt1.x), 2) + Math.pow((double) (pt2.y - pt1.y), 2) ) );          
    }