[英]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.