我正在尝试查找二进制对象的边界。
考虑以下图片
[ 0 0 0 0 1 1 0 ]
[ 0 0 1 0 0 0 0 ]
[ 0 1 1 0 1 1 0 ]
[ 0 1 1 0 0 1 0 ]
[ 0 1 1 1 0 0 0 ]
[ 0 1 0 0 1 1 0 ]
[ 0 0 0 0 1 1 0 ]
标签图片将如下所示
[ 0 0 0 0 1 1 0 ]
[ 0 0 2 0 0 0 0 ]
[ 0 2 2 0 3 3 0 ]
[ 0 2 2 0 0 3 0 ]
[ 0 2 2 0 0 0 0 ]
[ 0 2 0 0 4 4 0 ]
[ 0 0 0 0 4 4 0 ]
我还收集了数组列表中的每个对象像素
因此,例如对于4个标记的对象,列表将是
{ (5,4), (5,5) , (6,4), (6,5) }
Area只是每个对象像素数组的大小,但是我如何才能找到周长,我是否应该再次遍历整个图像查找单元格的邻居,检查它是否是对象的角点像素,或者有更简单的方法可以做这只是基于坐标。
请建议什么是最简单的查找边界的方法,任何代码示例都将受到高度赞赏
答案 0 :(得分:1)
尝试广度优先搜索您的图片,(或者遍历您的点列表),并标记与其他像素相邻的每个像素不属于同一群体。
我不清楚你想要的周长是所请求对象外边缘的每个像素,还是边界对象的每个像素。我现在假设前者。
以下是您如何做到这一点。首先,将图像设置为2-D阵列,每个像素标有组号:
[ 0 0 0 0 1 1 0 ]
[ 0 0 2 0 0 0 0 ]
[ 0 2 2 0 3 3 0 ]
[ 0 2 2 0 0 3 0 ]
[ 0 2 2 0 0 0 0 ]
[ 0 2 0 0 4 4 0 ]
[ 0 0 0 0 4 4 0 ]
加载此方法的一个好方法是使用Scanner
对象逐个获取每个点:
List<Point> points = new ArrayList<>();
Scanner scanner = new Scanner( /* whatever your input source is */ );
String pointRegex = "\\(\\d,\\d\\)"; //looks for something like "(#,#)"
while(!scanner.hasNext(pointRegex)){
String pointText = scanner.next(pointRegex); //For example, "(5,4)"
Point point = getPointFromText(pointText); //turns a string into a point
points.add(point);
}
请注意Scanner.next(String pattern)
的使用。这是一种方法,它将返回看起来像该模式的下一个String
。 (如果您想了解更多有关其工作方式的信息,请阅读正则表达式。)
现在填充网格:
boolean[][] binaryImage = new boolean[width][height];
for(Point p : points){ //Iterate through each Point inside our List of Point objects
binaryImage[p.getX()][p.getY()] = true;
}
这会将由我们的Point
个对象&#34; points
&#34;的集合表示的对象放入boolean
s的网格中。我们只需要担心这一个对象,因此我们不需要加载任何其他对象。现在找出外围的点。
boolean[][] visitedBefore = new boolean[width][height];
boolean[][] isOnPerimeter = new boolean[width][height];
int[] deltaX = {-1, 0, 1, -1, 1, -1, 0, 1},
deltaY = {-1, -1, -1, 0, 0, 1, 1, 1};
Queue<Point> searchNext = new LinkedList<>();
searchNext.add(points.get(0)); //Just need one point to get going
while(!searchNext.isEmpty()){
Point p = searchNext.remove(); //take what's waiting at the front of the queue
if(visitedBefore[p.getX()][p.getY()]){
continue; //already check this spot!
}
//mark that we've been here
visited[p.getX()][p.getY()] = true;
//look at all of this Point's neighbors
for(int i = 0 ; i < deltaX.length ; i++){
int newX = p.getX() + deltaX[i];
int newY = p.getY() + deltaY[i];
//make sure this isn't out of bounds
if(newX < 0 || newX >= width || newY<0 || newY>=height){
isOnPerimeter[p.getX()][p.getY()] = true; //if you decide bordering the edge of the image counts as being on the perimeter
continue;
}
//check if this new point we're considering isn't part of the image
if( binaryImage[p.getX()][p.getY()] != binaryImage[newX][newY] ){
//if it isn't, then this Point p must be on the perimeter
isOnPerimeter[p.getX()][p.getY()] = true;
} else {
/* otherwise, this new point we're considering is part of the
* same object, and could be part of the perimeter. */
searchNext.add(new Point(newX, newY));
}
}
}
现在你有一个网格,周长上的每个点都标记为true
。如果您需要这些作为列表,那么选择这些点很容易:
List<Point> perimeter = new ArrayList<Point>();
for(int x = 0 ; x < isOnPerimeter.length ; x++)
for(int y = 0 ; y < isOnPerimeter[x].length ; y++)
perimeter.add( new Point(x,y) );
这与上面的内容非常类似,但直接将外围点放入列表中。
int[] deltaX = {-1, 0, 1, -1, 1, -1, 0, 1},
deltaY = {-1, -1, -1, 0, 0, 1, 1, 1};
outer: for(Point p : points){
inner: for(int i = 0 ; i < deltaX.length ; i++){
int newX = p.getX() + deltaX[i];
int newY = p.getY() + deltaY[i];
//check if this new point we're considering is outside the image
if(newX < 0 || newX >= width || newY<0 || newY>=height){
perimeter.add(p); //if you decide bordering the edge of the image counts as being on the perimeter
continue outer;
}
//check if this new point we're considering isn't part of the image
if( binaryImage[p.getX()][p.getY()] != binaryImage[newX][newY] ){
//if it isn't, then this Point p must be on the perimeter
perimeter.add(p);
continue outer;
}
}
}
注意标签outer:
和inner:
。这使我们可以选择在我们说continue outer;
时跳过哪个for循环。