尝试对您的图像进行广度优先搜索(或遍历点列表),并标记每个邻接不属于相同组的像素。不清楚您想要的周长是请求对象外部边缘上的每个像素,还是与对象相邻的每个像素。暂且假设前者。
设置图像:
以下是操作步骤。首先,将您的图像设置为一个二维数组,并使用组号标记每个像素:
[ 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( );
String pointRegex = "\\(\\d,\\d\\)";
while(!scanner.hasNext(pointRegex)){
String pointText = scanner.next(pointRegex);
Point point = getPointFromText(pointText);
points.add(point);
}
请注意使用
Scanner.next(String pattern)
方法。这是一个会返回下一个与该模式相似的
String
的方法。(如果您想了解更多关于如何工作的正则表达式,可以阅读相关资料。)
现在我们开始填充网格:
boolean[][] binaryImage = new boolean[width][height];
for(Point p : points){
binaryImage[p.getX()][p.getY()] = true;
}
这将我们的Point
对象集合"points
"表示的对象放入一个boolean
网格中。我们只需要关注这一个对象,所以不需要加载其他任何对象。现在要找出哪些点在周边。
递归方法:
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));
while(!searchNext.isEmpty()){
Point p = searchNext.remove();
if(visitedBefore[p.getX()][p.getY()]){
continue;
}
visited[p.getX()][p.getY()] = true;
for(int i = 0 ; i < deltaX.length ; i++){
int newX = p.getX() + deltaX[i];
int newY = p.getY() + deltaY[i];
if(newX < 0 || newX >= width || newY<0 || newY>=height){
isOnPerimeter[p.getX()][p.getY()] = true;
continue;
}
if( binaryImage[p.getX()][p.getY()] != binaryImage[newX][newY] ){
isOnPerimeter[p.getX()][p.getY()] = true;
} else {
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];
if(newX < 0 || newX >= width || newY<0 || newY>=height){
perimeter.add(p);
continue outer;
}
if( binaryImage[p.getX()][p.getY()] != binaryImage[newX][newY] ){
perimeter.add(p);
continue outer;
}
}
}
请注意标签 outer:
和 inner:
。这允许我们在使用 continue outer;
时选择要跳过的循环。
完成了!这应该可以帮助你以二进制图像或列表形式获取任何对象的周长。