繁体   English   中英

我的洪水填充实施有什么问题?

[英]What's wrong with my flood fill implementation?

我用Java编写了一个洪水填充实现(代码见下文)。 我想用它填充以下多边形(黄色区域)的内部:

图片

点( . )代表白色像素, X代表黑色像素,在当前迭代过程中代表变量n +位置。

此实现有问题,因为该算法使用Xes填充整个网格,而不仅是多边形内部的空间:

图片2

您可以在此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.

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