ImageDraw.jl icon indicating copy to clipboard operation
ImageDraw.jl copied to clipboard

Implementation of poly2mask from matlab

Open VPetukhov opened this issue 5 years ago • 8 comments

Hi,

I was looking for implementation of a function, which would fill an image region based on the provided polygon. It exists in Matlab (poly2mask) and also was re-implemented in python (see the discussion).

The implementation is almost trivial (see below), and the function seems generally useful. Would you be interested in me making a PR?

function point_in_polygon(poly_xs::Vector{T}, poly_ys::Vector{T}, x::T, y::T) where T<: Real
    n_verts = length(poly_xs)
    j = n_verts
    c = false
    for i in 1:n_verts
        if (((poly_ys[i] <= y) && (y < poly_ys[j])) || ((poly_ys[j] <= y) && (y < poly_ys[i]))) && 
            (x < (poly_xs[j] - poly_xs[i]) * (y - poly_ys[i]) / (poly_ys[j] - poly_ys[i]) + poly_xs[i])
            c = !c
        end
        j = i
    end
    return c
end

function draw_polygon!(mask::Matrix{T2}, poly_xs::Vector{T}, poly_ys::Vector{T}, value::T2) where T<: Integer where T2 <: Real
    min_x, max_x = max(minimum(poly_xs), 1), min(maximum(poly_xs), size(mask, 2))
    min_y, max_y = max(minimum(poly_ys), 1), min(maximum(poly_ys), size(mask, 1))
    for y in min_y:max_y
        for x in min_x:max_x
            if point_in_polygon(poly_xs, poly_ys, x, y)
                mask[y, x] = value
            end
        end
    end
end

function polygons_to_mask(polygons::Array{Matrix{T}, 1} where T <: Real, max_x::Int, max_y::Int)
    poly_mask = zeros(Int, max_y, max_x);
    for (i,p) in enumerate(polygons)
        draw_polygon!(poly_mask, round.(Int, p[:,1]), round.(Int, p[:,2]), i)
    end
    return poly_mask
end

VPetukhov avatar Oct 09 '20 08:10 VPetukhov

It might be interesting. However, what was the inspiration for this code? If you looked at the Matlab implementation then you are license-contaminated, and we can't accept it.

timholy avatar Oct 09 '20 09:10 timholy

Could be possibly added to ImageDraw.jl with a new field fill in Polygon

https://github.com/JuliaImages/ImageDraw.jl/blob/d0324f84e9730b633e441fe34fe926ca1fc713e7/src/paths.jl#L19-L22

I guess fill here is not ambiguous to Base.fill?

struct Polygon <: Drawable
    vertices::Vector{Point}
    fill::Bool
end

johnnychen94 avatar Oct 09 '20 10:10 johnnychen94

However, what was the inspiration for this code?

It was python scikit-image. Looks like they use a variation of the MIT license.

Could be possibly added to ImageDraw.jl with a new field fill in Polygon

If you think people would use it for other purposes.. Otherwise, changing an existing class for such tiny functionality seems like over-kill to me. Though I agree that having both draw!(img, polygon) and draw_polygon! is a bad style. Would it maybe be better to add fill_color::T where T<: Colorant as a keyword argument instead?

VPetukhov avatar Oct 15 '20 12:10 VPetukhov

Adding an extra fill_color keyword to draw! sounds good to me.

johnnychen94 avatar Oct 15 '20 14:10 johnnychen94

Yes, scikit-image is not a problem with regards to licenses, and the plan you've come up with seems fine to me.

timholy avatar Oct 15 '20 15:10 timholy

But maybe Union{Number,Colorant}, some people might want a Bool instead (if it's a mask).

timholy avatar Oct 15 '20 15:10 timholy

Just realized that it does not exactly work, as the current draw! works only with Colorant, while working with a mask assumes it to be Integer or Bool. Maybe I'm missing something, but converting integers to Colorant and then back introduces a lot of hustle.

VPetukhov avatar Nov 03 '20 14:11 VPetukhov

Generally, we require functions accepting AbstractArray{<:Colorant}, with AbstractArray{<:Real} treated as gray image.

The type annotations in your example codes seem to be quite too restrictive.

johnnychen94 avatar Nov 06 '20 06:11 johnnychen94