|
1 |
| -flood_fill(f, src::AbstractArray, idx::Union{Integer,CartesianIndex}, nbrhood_function=diamond_iterator(window_neighbors(src))) = |
| 1 | +""" |
| 2 | + mask = flood(f, src, idx, nbrhood_function=diamond_iterator((3,3,...))) |
| 3 | +
|
| 4 | +Return an array `mask` with the same axes as `src`, marked `true` for all elements of `src` that: |
| 5 | +
|
| 6 | +- satisfy `f(src[i]) == true` and |
| 7 | +- are connected by such elements to the starting point `idx` (an integer index or `CartesianIndex`). |
| 8 | +
|
| 9 | +This throws an error if `f` evaluates as `false` for the starting value `src[idx]`. |
| 10 | +The sense of connectivity is defined by `nbrhood_function`, with two choices being |
| 11 | +[`ImageSegmentation.diamond_iterator`](@ref) and [`ImageSegmentation.box_iterator`](@ref.) |
| 12 | +
|
| 13 | +# Examples |
| 14 | +
|
| 15 | +```jldoctest; setup=:(using ImageSegmentation, ImageCore) |
| 16 | +julia> mostly_red(c) = red(c) > green(c) && red(c) > blue(c) |
| 17 | +mostly_red (generic function with 1 method) |
| 18 | +
|
| 19 | +julia> img = repeat(LinRange(colorant"red", colorant"blue", 4), 1, 2) # red-to-blue |
| 20 | +4×2 Array{RGB{Float32},2} with eltype RGB{Float32}: |
| 21 | + RGB{Float32}(1.0,0.0,0.0) RGB{Float32}(1.0,0.0,0.0) |
| 22 | + RGB{Float32}(0.666667,0.0,0.333333) RGB{Float32}(0.666667,0.0,0.333333) |
| 23 | + RGB{Float32}(0.333333,0.0,0.666667) RGB{Float32}(0.333333,0.0,0.666667) |
| 24 | + RGB{Float32}(0.0,0.0,1.0) RGB{Float32}(0.0,0.0,1.0) |
| 25 | +
|
| 26 | +julia> flood(mostly_red, [img; img], 1) # only first copy of `img` is connected |
| 27 | +8×2 BitMatrix: |
| 28 | + 1 1 |
| 29 | + 1 1 |
| 30 | + 0 0 |
| 31 | + 0 0 |
| 32 | + 0 0 |
| 33 | + 0 0 |
| 34 | + 0 0 |
| 35 | + 0 0 |
| 36 | +``` |
| 37 | +
|
| 38 | +See also [`flood_fill!`](@ref). |
| 39 | +""" |
| 40 | +flood(f, src::AbstractArray, idx::Union{Integer,CartesianIndex}, nbrhood_function=diamond_iterator(window_neighbors(src))) = |
2 | 41 | flood_fill!(f, falses(axes(src)) #=fill!(similar(src, Bool), false)=#, src, idx, nbrhood_function)
|
3 | 42 |
|
4 |
| -function flood_fill(src::AbstractArray, idx::Union{Integer,CartesianIndex}, |
| 43 | +function flood(src::AbstractArray, idx::Union{Integer,CartesianIndex}, |
5 | 44 | nbrhood_function=diamond_iterator(window_neighbors(src)); thresh)
|
6 | 45 | validx = src[idx]
|
7 | 46 | validx = accum_type(typeof(validx))(validx)
|
8 | 47 | return let validx=validx
|
9 |
| - flood_fill(val -> default_diff_fn(val, validx) < thresh, src, idx, nbrhood_function) |
| 48 | + flood(val -> default_diff_fn(val, validx) < thresh, src, idx, nbrhood_function) |
10 | 49 | end
|
11 | 50 | end
|
12 | 51 |
|
13 |
| -function flood_fill!(f, dest, src::AbstractArray, idx::Union{Int,CartesianIndex}, nbrhood_function=diamond_iterator(window_neighbors(src))) |
| 52 | +""" |
| 53 | + flood_fill!(f, dest, src, idx, nbrhood_function=diamond_iterator((3,3,...)); fillvalue=true, isfilled = isequal(fillvalue)) |
| 54 | +
|
| 55 | +Set entries of `dest` to `fillvalue` for all elements of `src` that: |
| 56 | +
|
| 57 | +- satisfy `f(src[i]) == true` and |
| 58 | +- are connected by such elements to the starting point `idx` (an integer index or `CartesianIndex`). |
| 59 | +
|
| 60 | +This throws an error if `f` evaluates as `false` for the starting value `src[idx]`. |
| 61 | +The sense of connectivity is defined by `nbrhood_function`, with two choices being |
| 62 | +[`ImageSegmentation.diamond_iterator`](@ref) and [`ImageSegmentation.box_iterator`](@ref.) |
| 63 | +
|
| 64 | +You can optionally omit `dest`, in which case entries in `src` will be set to `fillvalue`. |
| 65 | +You may also supply `isfilled`, which should return `true` for any value in `dest` |
| 66 | +which does not need to be set or visited; one requirement is that `isfilled(fillvalue) == true`. |
| 67 | +
|
| 68 | +# Examples |
| 69 | +
|
| 70 | +```jldoctest; setup=:(using ImageSegmentation) |
| 71 | +julia> a = repeat([1:4; 1:4], 1, 3) |
| 72 | +8×3 Matrix{Int64}: |
| 73 | + 1 1 1 |
| 74 | + 2 2 2 |
| 75 | + 3 3 3 |
| 76 | + 4 4 4 |
| 77 | + 1 1 1 |
| 78 | + 2 2 2 |
| 79 | + 3 3 3 |
| 80 | + 4 4 4 |
| 81 | +
|
| 82 | +julia> flood_fill!(>=(3), a, CartesianIndex(3, 2); fillvalue = -1, isfilled = <(0)) |
| 83 | +8×3 Matrix{Int64}: |
| 84 | + 1 1 1 |
| 85 | + 2 2 2 |
| 86 | + -1 -1 -1 |
| 87 | + -1 -1 -1 |
| 88 | + 1 1 1 |
| 89 | + 2 2 2 |
| 90 | + 3 3 3 |
| 91 | + 4 4 4 |
| 92 | +``` |
| 93 | +
|
| 94 | +See also [`flood`](@ref). |
| 95 | +""" |
| 96 | +function flood_fill!(f, |
| 97 | + dest, |
| 98 | + src::AbstractArray, |
| 99 | + idx::Union{Int,CartesianIndex}, |
| 100 | + nbrhood_function = diamond_iterator(window_neighbors(src)); |
| 101 | + fillvalue = true, |
| 102 | + isfilled = (fillvalue === true) ? identity : isequal(fillvalue)) |
14 | 103 | R = CartesianIndices(src)
|
15 |
| - axes(dest) == R.indices || throw(DimensionMismatch("$(axes(dest)) do not match $(Tuple(R))")) |
16 | 104 | idx = R[idx] # ensure cartesian indexing
|
17 | 105 | f(src[idx]) || throw(ArgumentError("starting point fails to meet criterion"))
|
18 | 106 | q = [idx]
|
19 |
| - _flood_fill!(f, dest, src, R, q, nbrhood_function) |
| 107 | + fillvalue = convert(eltype(dest), fillvalue) |
| 108 | + axes(dest) == R.indices || throw(DimensionMismatch("$(axes(dest)) do not match $(Tuple(R))")) |
| 109 | + _flood_fill!(f, dest, src, R, q, nbrhood_function, fillvalue, isfilled) |
20 | 110 | return dest
|
21 | 111 | end
|
22 |
| -flood_fill!(f, dest, src::AbstractArray, idx::Integer, nbrhood_function=diamond_iterator(window_neighbors(src))) = |
23 |
| - flood_fill!(f, dest, src, Int(idx)::Int, nbrhood_function) |
| 112 | +flood_fill!(f, dest, src::AbstractArray, idx::Integer, args...; kwargs...) = |
| 113 | + flood_fill!(f, dest, src, Int(idx)::Int, args...; kwargs...) |
| 114 | +flood_fill!(f, src::AbstractArray, idx::Union{Integer,CartesianIndex}, args...; kwargs...) = |
| 115 | + flood_fill!(f, src, src, idx, args...; kwargs...) |
24 | 116 |
|
25 | 117 | # This is a trivial implementation (just to get something working), better would be a raster implementation
|
26 |
| -function _flood_fill!(f::F, dest, src, R::CartesianIndices{N}, q, nbrhood_function::FN) where {F,N,FN} |
| 118 | +function _flood_fill!(f::F, dest, src, R::CartesianIndices{N}, q, nbrhood_function::FN, fillvalue, isfilled::C) where {F,N,FN,C} |
| 119 | + isfilled(fillvalue) == true || throw(ArgumentError("`isfilled(fillvalue)` must return `true`")) |
27 | 120 | while !isempty(q)
|
28 | 121 | idx = pop!(q)
|
29 |
| - dest[idx] = true |
| 122 | + dest[idx] = fillvalue |
30 | 123 | @inbounds for j in nbrhood_function(idx)
|
31 | 124 | j ∈ R || continue
|
32 |
| - if f(src[j]) && !dest[j] |
| 125 | + if f(src[j]) && !isfilled(dest[j]) |
33 | 126 | push!(q, j)
|
34 | 127 | end
|
35 | 128 | end
|
|
0 commit comments