簡體   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