pythonarraysnumpyscipyflood-fill

Flood fill NumPy Array `numpy.ndarray`, i. e. assign new value to element and change neighboring elements of the same value, too


Another, similar post called Flood Fill in Python is a very general question on flood fill and the answer only contains a broad pseudo code example. I'm look for an explicit solution with numpy or scipy.


Let's take this array for example:

a = np.array([
    [0, 1, 1, 1, 1, 0],
    [0, 0, 1, 2, 1, 1],
    [0, 1, 1, 1, 1, 0]
])

For selecting element 0, 0 and flood fill with value 3, I'd expect:

[
    [3, 1, 1, 1, 1, 0],
    [3, 3, 1, 2, 1, 1],
    [3, 1, 1, 1, 1, 0]
]

For selecting element 0, 1 and flood fill with value 3, I'd expect:

[
    [0, 3, 3, 3, 3, 0],
    [0, 0, 3, 2, 3, 3],
    [0, 3, 3, 3, 3, 0]
]

For selecting element 0, 5 and flood fill with value 3, I'd expect:

[
    [0, 1, 1, 1, 1, 3],
    [0, 0, 1, 2, 1, 1],
    [0, 1, 1, 1, 1, 0]
]

This should be a fairly basic operation, no? Which numpy or scipy method am I overlooking?


Solution

  • Approach #1

    Module scikit-image offers the built-in to do the same with skimage.segmentation.flood_fill -

    from skimage.morphology import flood_fill
    
    flood_fill(image, (y, x), newval)
    

    Sample runs -

    In [17]: a
    Out[17]: 
    array([[0, 1, 1, 1, 1, 0],
           [0, 0, 1, 2, 1, 1],
           [0, 1, 1, 1, 1, 0]])
    
    In [18]: flood_fill(a, (0, 0), 3)
    Out[18]: 
    array([[3, 1, 1, 1, 1, 0],
           [3, 3, 1, 2, 1, 1],
           [3, 1, 1, 1, 1, 0]])
    
    In [19]: flood_fill(a, (0, 1), 3)
    Out[19]: 
    array([[0, 3, 3, 3, 3, 0],
           [0, 0, 3, 2, 3, 3],
           [0, 3, 3, 3, 3, 0]])
    
    In [20]: flood_fill(a, (0, 5), 3)
    Out[20]: 
    array([[0, 1, 1, 1, 1, 3],
           [0, 0, 1, 2, 1, 1],
           [0, 1, 1, 1, 1, 0]])
    

    Approach #2

    We can use skimage.measure.label with some array-masking -

    from skimage.measure import label
    
    def floodfill_by_xy(a, pos, newval):
        x,y = pos
        l = label(a==a[y,x])
        a[l==l[y,x]] = newval
        return a
    

    To make use of SciPy based label function - scipy.ndimage.measurements.label, it would mostly be the same -

    from scipy.ndimage.measurements import label
    
    def floodfill_by_xy_scipy(a, pos, newval):
        x,y = pos
        l = label(a==a[y, x])[0]
        a[l==l[y, x]] = newval
        return a
    

    Note : These would work as in-situ edits.