[英]What's wrong with my flood fill implementation?
我用Java编写了一个洪水填充实现(代码见下文)。 我想用它填充以下多边形(黄色区域)的内部:
点( .
)代表白色像素, X
代表黑色像素,在当前迭代过程中代表变量n
+
位置。
此实现有问题,因为该算法使用Xes填充整个网格,而不仅是多边形内部的空间:
您可以在此PDF文件中查看各种迭代时网格的状态。 第一页显示了调用floodFill
方法之前的网格,以及所有后续visualizer.visualize(grid, n);
-调用visualizer.visualize(grid, n);
。
我的算法(方法floodFill
)有什么问题,如何解决?
码
protected void floodFill(final FloodFillGridPoint[][] grid,
final FloodFillGridPoint centroid, IFloodFillGridVisualizer visualizer) {
final Queue<FloodFillGridPoint> queue = new LinkedList<>();
queue.add(centroid);
while (!queue.isEmpty()) {
final FloodFillGridPoint n = queue.poll();
if ((n.color() == COLOR_WHITE) && !n.isProcessed()) {
n.markProcessed();
final FloodFillGridPoint west = getWestBoundary(grid, n);
final FloodFillGridPoint east = getEastBoundary(grid, n);
for (int x=west.x(); x <= east.x(); x++) {
final FloodFillGridPoint n2 = getPoint(grid, x, n.y());
n2.setColor(COLOR_BLACK);
final FloodFillGridPoint n2North = getPoint(grid, n2.x(),
n2.y()-1);
if ((n2North != null) && (n2North.color() == COLOR_WHITE)) {
queue.add(n2North);
}
final FloodFillGridPoint n2South = getPoint(grid, n2.x(),
n2.y()+1);
if ((n2South != null) && (n2South.color() == COLOR_WHITE)) {
queue.add(n2South);
}
}
visualizer.visualize(grid, n);
}
}
}
private FloodFillGridPoint getEastBoundary(final FloodFillGridPoint[][] grid, final FloodFillGridPoint n) {
FloodFillGridPoint east = n;
while (east.color() == COLOR_WHITE) {
final FloodFillGridPoint newNode =
getPoint(grid, east.x()+1, east.y());
if (newNode == null) {
break;
}
east = newNode;
}
return east;
}
private FloodFillGridPoint getWestBoundary(final FloodFillGridPoint[][] grid, final FloodFillGridPoint n) {
FloodFillGridPoint west = n;
while (west.color() == COLOR_WHITE) {
final FloodFillGridPoint newNode =
getPoint(grid, west.x()-1, west.y());
if (newNode == null) {
break;
}
west = newNode;
}
return west;
}
private FloodFillGridPoint getPoint(final FloodFillGridPoint[][] grid,
final int x, final int y) {
if (x < 0) {
return null;
}
if (y < 0) {
return null;
}
if (x >= grid.length) {
return null;
}
if (y >= grid[0].length) {
return null;
}
return grid[x][y];
}
public class FloodFillGridPoint {
private final int x;
private final int y;
private int color;
private boolean processed = false;
public FloodFillGridPoint(final int x, final int y, final int color) {
this.x = x;
this.y = y;
this.color = color;
}
public void setColor(final int ncolor) {
this.color = ncolor;
}
public int color() {
return this.color;
}
public void markProcessed() {
this.processed = true;
}
public int x() {
return this.x;
}
public int y() {
return this.y;
}
public boolean isProcessed() {
return this.processed;
}
}
该算法将其视为边界上的孔:
.......... ....X..... ..XX.XXX.. ..X....X.. ..X....X.. ..X....X.. ..X....X.. ..XXXXXX.. .......... ..........
您看到顶部的裂缝了吗? 洪水从那里溢出,最终覆盖了整个网格。
给定此网格,它可以正常工作,并且能够从内部正方形的内部或外部正确填充,它做对了:
.......... .......... ..XXXXXX.. ..X....X.. ..X....X.. ..X....X.. ..X....X.. ..XXXXXX.. .......... ..........
因此,如果您想将第一个示例中的正方形视为“ 封闭” ,并防止洪水沿对角线溢出,请查看实施并进行必要的更改。
不应将边界像素以南和以北的像素放入队列中,因为这可能会导致“对角台阶”,如PDF第4页所示。 即您可能想尝试
for (int x=west.x()+1; x <= east.x()-1; x++) {
从递归中排除边界像素。
其他的关于该问题的部分是正确的,即您的对角线-您的东西边界是黑色的实际出现点,而向这些点之一的北或南移动可能会导致您得到一个白色,而实际上该白色在外面容器。 我自己进行一下快速浏览似乎也表明了其他问题-我注意到,如果您将东西方边界的定义调整为仅在东西方边界的左侧/右侧的点,则不会填充一些容器。 这是对算法的一种调整,既使算法更简单(通过避免明确查找东西边界),又在逻辑上更容易看到。 同样也可以解决对角线问题:
protected void floodFill(final FloodFillGridPoint[][] grid,
final FloodFillGridPoint centroid, IFloodFillGridVisualizer visualizer) {
final Queue<FloodFillGridPoint> queue = new LinkedList<>();
queue.add(centroid);
while (!queue.isEmpty()) {
final FloodFillGridPoint n = queue.poll();
if ((n.color() == COLOR_WHITE) && !n.isProcessed()) {
n.markProcessed();
n.setColor(COLOR_BLACK);
final FloodFillGridPoint west = getPoint(grid,n.x()-1,n.y());
final FloodFillGridPoint east = getPoint(grid,n.x()+1,n.y());
final FloodFillGridPoint north = getPoint(grid,n.x(),n.y()-1);
final FloodFillGridPoint south = getPoint(grid,n.x(),n.y()+1);
for(FloodFillGridPoint neighbor : Arrays.asList(west,east,north,south)){
if(neighbor!=null && neighbor.color()!=COLOR_BLACK){
queue.add(neighbor);
}
}
visualizer.visualize(grid, n);
}
}
}
运行示例的结果:
.........
.........
....x....
...x.x...
..x.+.x..
...x.x...
....x....
.........
.........
------------------------------------------------
.........
.........
....x....
...xxx...
..x+x.x..
...x.x...
....x....
.........
.........
------------------------------------------------
.........
.........
....x....
...xxx...
..x.x+x..
...xxx...
....x....
.........
.........
------------------------------------------------
.........
.........
....x....
...x+x...
..xxx.x..
...xxx...
....x....
.........
.........
------------------------------------------------
.........
.........
....x....
...xxx...
..xxxxx..
...x+x...
....x....
.........
.........
------------------------------------------------
*此外,明智的一句话:请注意,您已经定义了两个考虑点位置的地方。 一个在网格数据结构的实际位置(由网格中的索引定义),另一个在点本身(在其x和y字段中)。 软件“ Do n't Repeat Yourself”(DRY)中有一个原则,这意味着每条信息都应具有一个表示形式。 在像这样的简单情况下可能值得这样做,但是我认为值得一提,因为我遇到了一个错误,即点的内部(x,y)从网格的位置开始转置-这引起了各种怪异。 如果仍然有问题,请检查一下自己。 如果您的其他约束允许,请重构复制。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.