2015-10-03 2 views
1

Я написал реализацию заливки заливки в Java (код см. Ниже). Я хочу, чтобы использовать его, чтобы заполнить внутреннюю часть следующего многоугольника (желтая область):Что случилось с моей реализацией заливки флуда?

Image

Точка (.) представляет собой белый пиксель, X черный пиксель и + положение переменной n во время текущая итерация.

Что-то не так с этой реализацией, поскольку алгоритм заполняет всю сетку с Xes, а не только пространство внутри полигона:

Image 2

Вы можете увидеть состояние сетки при различной итерации this PDF file. Первая страница показывает сетку перед вызовом метода floodFill, все последующие - состояние по вызову 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; 
    } 

} 
+0

Что предлагает вам отладчик? –

+0

@ThomasS. Я не знаю, как отладчик может помочь в диагностике этого (предложения приветствуются). –

+0

Возможно, небольшой тестовый образец будет полезен, когда легче понять, что происходит при выполнении каждого шага и думать об этом. –

ответ

1

Другие правильные сведения о том, что часть вопросов является вашими диагональными углами - ваши границы с востока и запада - это фактические точки, где происходит черный цвет, и переход на север или юг от одной из этих точек может привести вас к белой который фактически находится за пределами контейнера. Выполнение его быстрого запуска, похоже, также указывало на другие проблемы. Я заметил, что если вы настроите определение границ востока и запада как точку только налево/вправо от границ восток/запад, это не будет заполнено некоторые контейнеры. Вот корректировка вашего алгоритма, который делает его более простым (избегая явного нахождения границ востока/запада), и его легче понять, логически правильно.Это безопасно против диагонального угла вопроса, а также:

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 not Repeat Yourself» («DRY») есть руководитель, что означает, что каждая часть информации должна иметь одно представление. Это может стоить того, что было в таком простом случае, но я подумал, что стоит упомянуть, поскольку я столкнулся с ошибкой, когда внутренняя точка (x, y) была перенесена из сетки - это вызвало всевозможные странности. Если у вас все еще есть проблемы, убедитесь, что вы сами. И реорганизовать дублирование, если это позволяют другие ограничения.

2

Алгоритм рассматривает это как отверстие на границе:

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

Вы видите, что трещины на вершине? Наводнение проливается через него, и заканчивается тем, что покрывает всю сетку.

Учитывая эту сетку, он отлично работает, и в состоянии правильно заполнить либо внутри или снаружи внутреннего квадрата, он делает правильную вещь:

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

Так что если вы хотите, чтобы рассмотреть квадрат в первом примере, так как закрыл, и предотвратил размывание наводнений по диагонали, затем просмотрите свою реализацию и внесите необходимые изменения.

2

Пиксели Юг и север граничных пикселей не должны помещаться в очередь, так как это может вызвать «диагональные шаги», как показано на стр. 4 в формате PDF. То есть вы можете попробовать

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

, чтобы исключить граничные пиксели из рекурсии.

Смежные вопросы