简体   繁体   English

如何找到矩形的角点?

[英]How to find the corner pixels of a rectangle?

Given a simple monochrome bitmap that contains a single, randomly rotated rectangle. 给定一个简单的单色位图,其中包含一个随机旋转的矩形。 How can I find the left/top, left/bottom, right/bottom and right/top corner positions of the rectangle inside the bitmap? 如何找到位图内矩形的左/上,左/下,右/下和右/上角位置?

For example, this is how the bitmap could look like, where the X marks the pixels in question: 例如,这就是位图的外观,其中X标记了有问题的像素:

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

Please excuse the bad ascii art. 请原谅糟糕的ascii艺术。 For the second example, the corner pixel at the top could either be the rectangles left/top or right/top corner. 对于第二个示例,顶部的角点像素可以是左/上或右/上角的矩形。 Either is fine. 要么没事。

What steps are required to determine the corner pixels/positions in the above examples? 在上面的示例中,确定角像素/位置需要哪些步骤?

The corner pixels are the pixels the furthest apart. 角落像素是最远的像素。 Find the top most row and the bottom most row. 找到最顶行和最底行。 There will always be a corner pixel in those. 那些将始终有一个角落像素。

The corner pixel can only be the first or last pixel in this the topmost row row (or both if there's just the one). 角点像素只能是最顶行行中的第一个或最后一个像素(如果只有一个,则两者都是)。

So compare the distances between the first pixel in the topmost row and the last pixel in the bottom most row. 因此,比较最上面一行中第一个像素与最下面一行中最后一个像素之间的距离。 And last pixel in topmost with the first in bottom most. 并且最后一个像素在最顶部,第一个在最底部。 The corners there are the the ones that are the furthest apart. 那里的角落是最远的角落。

Since they are all the same distance in the Y you need the pixels with the greatest difference with regard to their x location. 由于它们在Y中的距离都相同,因此需要与x位置差异最大的像素。 The corners are the pixels for which abs(x0-x1) is the greatest, where x0 is in the topmost row and x1 is in the bottom most. 角是abs(x0-x1)最大的像素,其中x0在最顶行,x1在最底部。

Repeat this for the rightmost and leftmost rows. 对最右边和最左边的行重复此操作。

If the topmost corner is on the left then the leftmost corner is on the bottom, the bottom most corner is on the right and the rightmost corner is on the top. 如果最左边的角落在左边,则最左边的角落在底部,最底部的角落在右边,最右边的角落在顶部。 Once you have the top, bottom, left, and right rows there's really just the two possibilities that can be solved in an if statement. 一旦你有顶行,底行,左行和右行,实际上只有两种可能性可以在if语句中解决。 But, due to the edge condition of having one pixel on the topmost row and two on the rightmost row, you're better off just running the algorithm again with transposed x and ys to solve for the other two corners rather than trying to spare yourself an if statement. 但是,由于在最上面的行上有一个像素而在最右边的行上有两个像素的边缘条件,你最好再次使用转置的x和ys运行算法来解决其他两个角而不是试图自己节省一个if语句。

Not every monochrome bitmap is going to give you an answer. 并非每个单色位图都能给您答案。 A complete algorithm needs an output that says "Unique corners not present". 完整的算法需要输出“不存在唯一角落”。 The following figures give an example of the problem: 下图给出了问题的一个示例:

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

The degeneracy illustrated happens when the slopes of the rectangle are +1 and -1 and the position of the center is half-integral . 当矩形的斜率为+1和-1并且中心的位置是半积分时,发生简并说明。 It can also occur with other combinations of slopes and positions. 它也可以与斜率和位置的其他组合一起发生。 The general answer will need to contain pixel-pairs as the best approximation of a vertex. 一般答案需要包含像素对作为顶点的最佳近似。

  1. Start with the bounding box of the rectangle. 从矩形的边界框开始。
  2. For each corner, move it clockwise until there is a black square. 对于每个角落,顺时针移动它,直到有一个黑色方块。

     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 = px; this.y = py; } 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 + dx, y + dy); } } 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); } } } 

prints 版画

    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}]

Could be improved by looking for a run of black and choosing the middle of the run. 可以通过寻找一系列黑色并选择中间运行来改进。

Scan the image from the top, row by row until you find a black run. 从顶部逐行扫描图像,直到找到黑色运行。

Repeat four ways, from the bottom up, left, right, giving you eight corner candidates. 重复四种方式,从下到上,左,右,给你八个角候选。

Take the run endpoints the farthest apart in the top and bottom rows. 将运行端点放在顶行和底行中最远的位置。 This tells you which endpoints to take vertically. 这告诉您垂直采用哪些端点。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM