4

I am new to the Julia language and need to draw a circular sector on an image (2-dimensional UInt8 array for gray version or 3-dimensional UInt8 array for an RGB version). Afterwards this image is to be used as a mask to select data in other arrays, so I need the result, not as an image object, but as an array of booleans or integers.

There is the way to draw a circle by means of the ImageDraw package:

draw!(img, Ellipse(CirclePointRadius(350,200,100), fill = tue))

but found no way to provide a start and end angle.

Antonio Serrano
  • 385
  • 2
  • 15

2 Answers2

3

You can use Luxor.jl's pie or sector function:


julia> begin 
       img = readpng("/path/Images/deepam.png")
       Drawing(img.width, img.height, "sector-on-img.png")
       placeimage(img)
       origin()

       sethue("orange")
       pie(0, 0, 100, π/2, π, :fill)

       sethue("olive")
       sector(25, 125, 3π/2, 0, 15, :fill)

       finish()
       end
true

Result:

PNG image with a pie segment and a sector colored on top

(Original png image scaled down, for comparison: )

Sundar R
  • 13,776
  • 6
  • 49
  • 76
  • Thanks a lot, @Sundar, this solution colud work, but I need a more specific one. I need the resulting image as an array in order to be used as a mask for other arrays, and this library seems to create an object. I have seen the `image_as_matrix()` function in Luxor to convert to a matrix, but it returns a ARGB32 array that should be converted afterwards to a 2-d UInt8 array. I think this all is an unwanted overhead. – Antonio Serrano Dec 13 '21 at 16:26
1

I think Julia is a great language, because (among other things) all libraries are implemented in the same language and you have ease acces to their sources.

And in this way, I have been able to modify the ellipse2d.jl script of the ImageDraw library.

The modification consits of adding another definition of the draw! funciton for ellipse objects (multiple dispatch of Julia is also great) that accepts a start and end angle.

I think the best way could be to define new objects, ellipse_sector and circle_sector, which would be the same as the ellipse and circle objects but with two more members: start_angle and end_angle. Then the correspondent drawing functions should be implemented. I would like to write to the ImageDraw package developers in order to make this suggestion or even offer me to make these changes, but I do not know the manage of github.

My solution, instead, does not modify any existing object, just adds a method to the draw! function that accpets two more arguments: startAngle and endAngle.

Here is the code, to be copied to the end of the ellipse2d.jl script:

function draw!(img::AbstractArray{T, 2}, ellipse::Ellipse, startAng::Real, endAng::Real, color::T) where T<:Colorant
    # Solution to find out if an angle lies between two given ones, borrowed from:
    # https://stackoverflow.com/questions/11406189/determine-if-angle-lies-between-2-other-angles/11412077#11412077

    # Make all angles to lie in [0, 2π)
    # rem2pi(ϕ, RoundNearest) returns the remainder of the division by 2π in the range [−π,π]
    # mod2pi returns the remainder of the division by 2π in the range [0,2π)
    Angle1 = mod2pi(startAng)
    Angle2 = mod2pi(endAng)

    # make the angle from angle1 to angle2 to be <= 180 degrees
    rAngle = mod2pi( mod2pi(Angle2 - Angle1) + 2π)
    if rAngle >= π
        Angle1, Angle2 = Angle2, Angle1 # Swaps the values
    end # if

    ys = Int[]
    xs = Int[]
    break_point = 0
    if ellipse.fill == false
        break_point = ((ellipse.ρy - ellipse.thickness) / ellipse.ρy) ^ 2 + ((ellipse.ρx - ellipse.thickness) / ellipse.ρx) ^ 2 
    end
    for i in ellipse.center.y - ellipse.ρy : ellipse.center.y + ellipse.ρy
        for j in ellipse.center.x - ellipse.ρx: ellipse.center.x + ellipse.ρx
            y = i - ellipse.center.y
            x = j - ellipse.center.x
            val = (x / ellipse.ρy) ^ 2 + (y / ellipse.ρx) ^ 2
            # atan(y, x) returns the angle in the correct quadrant [−π,π], not like atan(y/x)
            # But make it to be in the range [0, 2π)by means of mod2pi()
            ang = mod2pi( atan(y, x) )

            # Test if the angle lies betwen the startAngle and the endAngle
            if (Angle1 <= Angle2)
                AngleIsBetween = ang >= Angle1 && ang <= Angle2
            else
                AngleIsBetween = ang >= Angle1 || ang <= Angle2
            end # if
            if val < 1 && val >= break_point && AngleIsBetween
                push!(ys, i)
                push!(xs, j)
            end
        end
    end
    for (yi, xi) in zip(ys, xs)
        drawifinbounds!(img, yi, xi, color)
    end
    img
end
Antonio Serrano
  • 385
  • 2
  • 15
  • I have edited the code because the old one did not take cara of start and end angles that were on each side of the pi break point. – Antonio Serrano Dec 14 '21 at 17:40