如何找到矩形的角点?

时间:2015-12-17 12:45:47

标签: java bitmap geometry geometry-surface

给出一个简单的单色位图,其中包含一个随机旋转的矩形。如何找到位图内矩形的左/上,左/下,右/下和右/上角位置?

例如,这就是位图的样子,其中X标记了所讨论的像素:

......... ......... ......... .........
.X11111X. ....X.... ..X11.... ....11X..
.1111111. ...111... ..11111X. X111111..
.1111111. ..X111X.. ..111111. .111111..
.X11111X. ...111... .1111111. .1111111.
......... ....X.... .111111.. ..111111.
......... ......... .X11111.. ..11111X.
......... ......... ....11X.. ..X11....
......... ......... ......... .........

请原谅糟糕的ascii艺术。 对于第二个示例,顶部的角点像素可以是左/上或右/上角的矩形。要么没事。

确定上述示例中的角点像素/位置需要哪些步骤?

4 个答案:

答案 0 :(得分:3)

角落像素是最远的像素。找到最顶行和最底行。这些中总会有一个角落像素。

角点像素只能是最顶行行中的第一个或最后一个像素(如果只有那个,则两者都是。)

因此,比较最上面一行中第一个像素与最下面一行中最后一个像素之间的距离。并且最后一个像素在最顶部,第一个在最底部。那里的角落是最远的角落。

由于它们在Y中的距离相同,因此需要与x位置差异最大的像素。角是abs(x0-x1)最大的像素,其中x0在最顶行,x1在最底部。

对最右边和最左边的行重复此操作。

如果最左边的角落在左边,那么最左边的角落在底部,最底部的角落在右边,最右边的角落在顶部。一旦你有了top,bottom,left和right行,那么在if语句中就可以解决两种可能性。但是,由于在最上面一行上有一个像素而在最右边一行有两个像素的边缘条件,你最好再次使用转置的x和ys运行算法来解决另外两个角而不是试图给自己一个if语句。

答案 1 :(得分:1)

并非每个单色位图都能给您答案。一个完整的算法需要一个输出,表示"唯一的角落不存在"。下图给出了问题的一个例子:

......... ......... .......... ...XX.... ....X.... ....XX.... ..X11X... ...111... ...1111... ..X11X... ..X111X.. ..X1111X.. ...XX.... ...111... ..X1111X.. ......... ....X.... ...X11X... ......... ......... ....XX.... ......... ......... ..........

当矩形的斜率为+1和-1且中心的位置为半积分时,会出现简并说明。它也可以与斜率和位置的其他组合一起发生。一般答案需要包含像素对作为顶点的最佳近似值。

答案 2 :(得分:0)

  1. 从矩形的边界框开始。
  2. 对于每个角落,顺时针移动它,直到有一个黑色方块。

    public class Test {
    
        String[][] squares = {
            {
                ".........",
                ".X11111X.",
                ".1111111.",
                ".1111111.",
                ".X11111X.",
                ".........",
                ".........",
                ".........",
                ".........",},
            {
                ".........",
                "....X....",
                "...111...",
                "..X111X..",
                "...111...",
                "....X....",
                ".........",
                ".........",
                ".........",},
            {
                ".........",
                "..X11....",
                "..11111X.",
                "..111111.",
                ".1111111.",
                ".111111..",
                ".X11111..",
                "....11X..",
                ".........",},
            {
                ".........",
                "....11X..",
                "X111111..",
                ".111111..",
                ".1111111.",
                "..111111.",
                "..11111X.",
                "..X11....",
                ".........",}};
    
        private static final int WHITE = 0;
        private static final int BLACK = 1;
    
        class Point {
    
            private final int x;
            private final int y;
    
            public Point(Point p) {
                this.x = p.x;
                this.y = p.y;
            }
    
            public Point(int x, int y) {
                this.x = x;
                this.y = y;
            }
    
            @Override
            public String toString() {
                return "{" + x + "," + y + '}';
            }
    
            // What colour is there?
            public int colour(int[][] bmp) {
                // Make everything off-bmp black.
                if (x < 0 || y < 0 || y >= bmp.length || x >= bmp[y].length) {
                    return BLACK;
                }
                return bmp[y][x];
            }
    
            private Point step(Point d) {
                return new Point(x + d.x, y + d.y);
            }
    
        }
    
        class Rectangle {
    
            private final Point[] corners = new Point[4];
    
            public Rectangle(Point[] corners) {
                // Points are immutable but corners are not.
                System.arraycopy(corners, 0, this.corners, 0, corners.length);
            }
    
            public Rectangle(Rectangle r) {
                this(r.corners());
            }
    
            public Rectangle(Point a, Point b, Point c, Point d) {
                corners[0] = a;
                corners[1] = b;
                corners[2] = c;
                corners[3] = d;
            }
    
            private Rectangle(Point tl, Point br) {
                this(tl, new Point(br.x, tl.y), br, new Point(tl.x, br.y));
            }
    
            public Point[] corners() {
                return Arrays.copyOf(corners, corners.length);
            }
    
            @Override
            public String toString() {
                return Arrays.toString(corners);
            }
    
        }
    
        private Rectangle getBoundingBox(int[][] bmp) {
            int minX = Integer.MAX_VALUE, minY = Integer.MAX_VALUE, maxX = 0, maxY = 0;
            for (int r = 0; r < bmp.length; r++) {
                for (int c = 0; c < bmp[r].length; c++) {
                    if (bmp[r][c] != WHITE) {
                        if (minX > c) {
                            minX = c;
                        }
                        if (minY > r) {
                            minY = r;
                        }
                        if (maxX < c) {
                            maxX = c;
                        }
                        if (maxY < r) {
                            maxY = r;
                        }
                    }
                }
            }
            return new Rectangle(new Point(minX, minY), new Point(maxX, maxY));
        }
    
        Point[] clockwise = new Point[]{
            new Point(1, 0),
            new Point(0, 1),
            new Point(-1, 0),
            new Point(0, -1)};
    
        private void test(int[][] bmp) {
            // Find the bounding box.
            Rectangle bBox = getBoundingBox(bmp);
            System.out.println("bbox = " + bBox);
            Point[] corners = bBox.corners();
            // Move each corner clockwise until it is black.
            for (int p = 0; p < corners.length; p++) {
                while (corners[p].colour(bmp) == WHITE) {
                    corners[p] = corners[p].step(clockwise[p]);
                }
            }
            System.out.println("rect = " + new Rectangle(corners));
        }
    
        private void test(String[] square) {
            // Build the int[][].
            // . -> White
            // X/1 -> Black
            int[][] bmp = new int[square.length][];
            for (int r = 0; r < square.length; r++) {
                bmp[r] = new int[square[r].length()];
                for (int c = 0; c < bmp[r].length; c++) {
                    switch (square[r].charAt(c)) {
                        case '.':
                            bmp[r][c] = WHITE;
                            break;
                        case 'X':
                        case '1':
                            bmp[r][c] = BLACK;
                            break;
                    }
                }
            }
            test(bmp);
        }
    
        public void test() {
            for (String[] square : squares) {
                test(square);
            }
        }
    
        public static void main(String args[]) {
            try {
                new Test().test();
            } catch (Throwable t) {
                t.printStackTrace(System.err);
            }
        }
    
    }
    
  3. 打印

        bbox = [{1,1}, {7,1}, {7,4}, {1,4}]
        rect = [{1,1}, {7,1}, {7,4}, {1,4}]
        bbox = [{2,1}, {6,1}, {6,5}, {2,5}]
        rect = [{4,1}, {6,3}, {4,5}, {2,3}]
        bbox = [{1,1}, {7,1}, {7,7}, {1,7}]
        rect = [{2,1}, {7,2}, {6,7}, {1,6}]
        bbox = [{0,1}, {7,1}, {7,7}, {0,7}]
        rect = [{4,1}, {7,4}, {4,7}, {0,2}]
    

    可以通过查找黑色并选择运行中间来改进。

答案 3 :(得分:0)

逐行扫描图像,直到找到黑色运行。

重复四种方式,从下往上,左,右,给你八个角落候选者。

将运行端点放在顶行和底行中最远的位置。这将告诉您垂直采用哪些端点。