简体   繁体   English

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

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

I wrote a flood fill implementation in Java (code see below). 我用Java编写了一个洪水填充实现(代码见下文)。 I want to use it to fill the inside of the following polygon (the yellow area): 我想用它填充以下多边形(黄色区域)的内部:

图片

A dot ( . ) represents a white pixel, X a black pixel and + position of the variable n during current iteration. 点( . )代表白色像素, X代表黑色像素,在当前迭代过程中代表变量n +位置。

Something is wrong with this implementation because the algorithm fills the entire grid with Xes, and not only the space inside the polygon: 此实现有问题,因为该算法使用Xes填充整个网格,而不仅是多边形内部的空间:

图片2

You can see the state of the grid at various iteration in this PDF file . 您可以在此PDF文件中查看各种迭代时网格的状态。 First page shows the grid before the invokation of the floodFill method, all subsequent ones - the state at the call of visualizer.visualize(grid, n); 第一页显示了调用floodFill方法之前的网格,以及所有后续visualizer.visualize(grid, n); -调用visualizer.visualize(grid, n); .

What is wrong with my algorithm (method floodFill ) and how can I fix it? 我的算法(方法floodFill )有什么问题,如何解决?

Code

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

}

The algorithm treats this as a hole on the boundary: 该算法将其视为边界上的孔:

 .......... ....X..... ..XX.XXX.. ..X....X.. ..X....X.. ..X....X.. ..X....X.. ..XXXXXX.. .......... .......... 

You see that crack at the top? 您看到顶部的裂缝了吗? The flooding spills out through there, and ends up covering the entire grid. 洪水从那里溢出,最终覆盖了整个网格。

Given this grid, it works fine, and able to correctly fill either from inside or outside the inner square, it does the right thing: 给定此网格,它可以正常工作,并且能够从内部正方形的内部或外部正确填充,它做对了:

 .......... .......... ..XXXXXX.. ..X....X.. ..X....X.. ..X....X.. ..X....X.. ..XXXXXX.. .......... .......... 

So if you want to consider the square in the first example as closed , and prevent the flooding to spill out diagonally, then review your implementation and make the necessary changes. 因此,如果您想将第一个示例中的正方形视为“ 封闭” ,并防止洪水沿对角线溢出,请查看实施并进行必要的更改。

The pixels South and North of boundary pixels should not be put into the queue as this might cause "diagonal steps", as seen on page 4 of the PDF. 不应将边界像素以南和以北的像素放入队列中,因为这可能会导致“对角台阶”,如PDF第4页所示。 Ie you might want to try 即您可能想尝试

for (int x=west.x()+1; x <= east.x()-1; x++) {

to exclude the boundary pixels from recursion. 从递归中排除边界像素。

The others are correct about part of the issue being your diagonal corners- your east and west boundaries are the actual points where a black occurs, and going to the north or south of one of these points may lead you to a white which is actually outside the container. 其他的关于该问题的部分是正确的,即您的对角线-您的东西边界是黑色的实际出现点,而向这些点之一的北或南移动可能会导致您得到一个白色,而实际上该白色在外面容器。 Doing a quick run of it myself seemed to indicate other issues as well- I noted that if you adjusted the definition of east and west boundaries to be the point just to the left/right of the east/west boundaries, it wouldn't fill some containers. 我自己进行一下快速浏览似乎也表明了其他问题-我注意到,如果您将东西方边界的定义调整为仅在东西方边界的左侧/右侧的点,则不会填充一些容器。 Here's an adjustment to your algorithm which both makes it simpler (by avoiding explicitly finding the east/west boundaries) and is easier to see is logically correct. 这是对算法的一种调整,既使算法更简单(通过避免明确查找东西边界),又在逻辑上更容易看到。 It is safe against the diagonal corner issue as well: 同样也可以解决对角线问题:

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);
            }
        }
    }

Results of an example run: 运行示例的结果:

.........    
.........    
....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....    
.........    
.........

------------------------------------------------

*Also, a word to the wise: note that you have defined two places where the position of a point is considered. *此外,明智的一句话:请注意,您已经定义了两个考虑点位置的地方。 One is in the actual location in the grid data structure (defined by indexes into the grid), and the other is in the point itself (in its x and y fields). 一个在网格数据结构的实际位置(由网格中的索引定义),另一个在点本身(在其x和y字段中)。 There's a principal in software "Don't Repeat Yourself" (DRY), meaning that each piece of info should have one representation. 软件“ Do n't Repeat Yourself”(DRY)中有一个原则,这意味着每条信息都应具有一个表示形式。 It may be worth it in a simple case like this, but I figured it was worth mentioning since I bumped into an error where the point's internal (x,y) were transposed from the grid's- it caused all sorts of weirdness. 在像这样的简单情况下可能值得这样做,但是我认为值得一提,因为我遇到了一个错误,即点的内部(x,y)从网格的位置开始转置-这引起了各种怪异。 If you're still having trouble, check that yourself. 如果仍然有问题,请检查一下自己。 And refactor the duplication out if your other constraints allow it. 如果您的其他约束允许,请重构复制。

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

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