将矩形的四个点重新排序为正确的顺序

2022-09-03 15:06:29

纵横比=高度/宽度始终为>1(在大多数情况下,甚至>2),因此它应该清晰/精确地显示我想要的旋转方式。


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

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

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

我在示例中指出了我将哪个点称为左上角(TL)。

怎么办?

你不需要专门为OpenCV等告诉我,只要假设你有两个数组

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

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


答案 1

有一个非常简单的解决方案,如果你知道

  1. -45 < roundedRect.angle < 45
  2. roundedRect.size.height > roundedRect.size.width

如果这是真的,那么按顺时针顺序排列的点将始终按以下顺序排列:

pts[0], pts[3], pts[2], pts[1]

顺便说一句,如果它不会对你的程序造成太大的伤害,那么积分将按逆时针顺序传递,从左上角开始...那么你就不必做任何重新排序/排序。

其他情况:

  • height > width && 135 < roundedRect.angle < 225
    • 从左上角开始的顺时针顺序为2,3,0,1
    • 左上角的反时针顺序为 。2,1,0,3
  • width > height && -135 < roundedRect.angle < -45
    • 从左上角开始的顺时针顺序为3,2,1,0
    • 左上角的逆时针顺序为3,0,1,2
  • width > height && 45 < roundedRect.angle < 135
    • 从左上角开始的顺时针顺序为1,0,3,2
    • 左上角的逆时针顺序为1,2,3,0

其余情况都意味着矩形从左到右比从上到下大,这在你的方案中不会发生。此外,如果角度在这些范围之外,则可以连续添加或减去 360 以获得这些范围之一中的角度。


解释

(博士)

我们从OpenCV如何计算这些点的值中知道这一点。你可以通过一些实验来解决这个问题。这是我写的一个小程序来演示它:

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

答案 2

编辑:如果您可以自由地假设矩形没有旋转太多,则可以直接继续使用公式长度 = ((y1-y2)^2 + (x1-x2)^2)^(0.5) 计算距原点的距离,找到左上角的点,原点为 (0,0)。距离最小的点将是左上角。然后,您可以使用我在下面给出的步骤继续。

如果您无法假设这一点,那么一旦您确定了矩形的左上角点(因此前三个步骤保持不变),还有另一种方法可以更优雅地进行。确定左上角后:

  1. 使用毕达哥拉斯公式找出从左上角点到其他三个点的距离,长度 = ((y1-y2)^2 + (x1-x2)^2)^(0.5)
  2. 现在,您有三个长度对应于从左上角开始的每个顶点的长度。
  3. 顶点的位置可以很容易地找到(按顺时针顺序排列):

    shortest distance = top right point 
    longest distance = bottom right point 
    middle distance = bottom left point
    

您不需要使用如果条件。

注意:只要保持高度始终大于宽度的条件,这就成立。


推荐