Giter Site home page Giter Site logo

imagedraw.jl's People

Contributors

annimesh2809 avatar ashwani-rathee avatar codyk12 avatar dskkato avatar evizero avatar femtocleaner[bot] avatar github-actions[bot] avatar hyrodium avatar ianbutterworth avatar johnnychen94 avatar juliatagbot avatar mgautam98 avatar mronian avatar opiateblush avatar tejus-gupta avatar timholy avatar yakir12 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

imagedraw.jl's Issues

TagBot trigger issue

This issue is used to trigger TagBot; feel free to unsubscribe.

If you haven't already, you should update your TagBot.yml to include issue comment triggers.
Please see this post on Discourse for instructions and more details.

If you'd like for me to do this for you, comment TagBot fix on this issue.
I'll open a PR within a few hours, please be patient!

Docs

Not required before the end of GSoC, but ideally we'd get this package documented on the JuliaImages site.

Discussion on circle fill,circle stroke option

I needed those for example : Detection and measurement,indentification of circular objects in a image
Even though Luxor.jl provides functions for that,would we need it here?Just a simple circular,elliptical drawable is unintuitive when we explain more advanced ideas there
Idea: https://github.com/ashwani-rathee/Demos/blob/main/%F0%9F%8E%88%202.pdf
relevant issues:
JuliaImages/ImageEdgeDetection.jl#21

What I kind of need

@johnnychen94 What do you think?

text?

i'm looking for a simple way to draw some text onto an existing array. is there an easy way to do this? cairo, luxor, compose, etc. all seem to output to files. i just want to modify the array i already have. thanks.

incorrect `closed` behavior

I'm not sure if this is an expected behavior or not:

img = zeros(RGB{Float64}, 10, 10)

verts = [CartesianIndex(2, 2), CartesianIndex(2, 6), CartesianIndex(6, 2), CartesianIndex(2,2)]
alg = BoundaryFill(4, 4; fill_value = RGB(1), boundary_value = RGB(1))

mosaic(
    draw(img, verts, alg; closed=true),
    draw(img, verts, alg; closed=false);
    npad=2, fillvalue=0.5
)

closed=false is an all-white image:

image

cc: @ashwani-rathee

Implementation of poly2mask from matlab

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

Documentation on "Drawing a Rectangle" is misleading

The example gives the impression that draw! returns a copy like draw

img = testimage("lighthouse")
img_example_stage1 = draw!(img, Polygon(RectanglePoints(Point(10, 10), Point(100, 100))), RGB{N0f8}(1))
img_example_stage2 = draw!(img_example_stage1, Polygon(RectanglePoints(CartesianIndex(110, 10), CartesianIndex(200, 200))), RGB{N0f8}(1))
img_example_stage3 = draw!(img_example_stage2, Polygon(RectanglePoints(220, 10, 300, 300)), RGB{N0f8}(1))
save("images/lighthouse_rectangle.png", img); nothing # hide

Polygon/Any closed path filling algorithms

Since this project is still WIP,these filling algorithms commonly used in computer graphics would be quite useful addition

  • Boundary Fill Algorithm
    • 4- pixel method
    • 8- pixel method
  • Flood fill algorithm
  • Scan-line polygon filling

References:

Friendly request for patch release v0.2.4

Hi, I'm running into a compatibility problem downstream where [email protected] is definitely required but the latest available tag on ImageDraw.jl only has compat support for Distances up to v0.9. I see the CompatHelper PR for v0.10 was merged here in October #44 but has not made it into a stable tag just yet. Hence a request for a new patch release if at all possible please.

request for a maintainership

Looks like this package isn't maintained very actively; Several good feature PRs are pending, and docs are missing.

If currently nobody's interested in maintaining this, I'd like to take the place. CC: @Codyk12

Boundary checks

Drawing ellipses and paths can produce out of bound errors. I am working on something else a.t.m but this is what fixed it for me:

function setifinbounds!_(A::AbstractArray{T,2}, i, j, c::T) where {T}
    if checkbounds(Bool, A, i, j)
        @inbounds A[i, j] = c
    end
    c
end
function draw!(img::AbstractArray{T, 2}, ellipse::Ellipse, color::T) where T<:Colorant
    ps = Tuple{Int,Int}[]
    for i in ellipse.center.y : ellipse.center.y + ellipse.ρy
        for j in ellipse.center.x : ellipse.center.x + ellipse.ρx
            val = ((i - ellipse.center.y) / ellipse.ρy) ^ 2 + ((j - ellipse.center.x) / ellipse.ρx) ^ 2
            if val < 1
                push!(ps, (i, j))
            end
        end
    end
    for (yi, xi) in ps
        setifinbounds!_(img, yi, xi, color)
        setifinbounds!_(img, 2 * ellipse.center.y - yi, xi, color)
        setifinbounds!_(img, yi, 2 * ellipse.center.x - xi, color)
        setifinbounds!_(img, 2 * ellipse.center.y - yi, 2 * ellipse.center.x - xi, color)
    end
    img
end
function draw!(img::AbstractArray{T, 2}, path::Path, color::T) where T<:Colorant
    vertices = [CartesianIndex(p.y, p.x) for p in path.vertices]
    f = CartesianIndex(map(r->first(r)-1, indices(img)))
    l = CartesianIndex(map(r->last(r), indices(img)))

    inrange1 = min(f, vertices[1])==f && max(l, vertices[1])==l
    for i in 1:length(vertices)-1
        inrange2 = min(f,vertices[i+1])==f && max(l,vertices[i+1])==l
        if inrange1 && inrange2
            draw!(img, LineSegment(vertices[i], vertices[i+1]), color)
        end
        inrange1 = inrange2
    end
end

Bezier_curves and Random shapes drawables

These two drawables will also be quite useful addition to the project in 2d drawing methods
What do you people think?
References:

RectanglePoints confuses me

When using CartesianIndex to define a rectangle, the resulting rectangle is not bounded by the pixels mentioned in the CartesianIndex.

Example:

julia>  RectanglePoints(CartesianIndex(1,2), CartesianIndex(3,4))
RectanglePoints(Point(1, 2), Point(3, 4))

But Point(1, 2) is the pixel with CartesianIndex(2,1).

The definition says:

 RectanglePoints(p1::CartesianIndex{2}, p2::CartesianIndex{2}) = RectanglePoints(Point(p1[1], p1[2]), Point(p2[1], p2[2]))

To me it would make a lot more sense to define it as

 RectanglePoints(p1::CartesianIndex{2}, p2::CartesianIndex{2}) = RectanglePoints(Point(p1[2], p1[1]), Point(p2[2], p2[1]))

Of course this would be breaking.

Am I just thinking about this the wrong way?

[RFC] Redesign the Drawable type

The current ImageDraw design has significantly restricted its possibility to the 2D case, and that's not good in general (even though it's the most common case).

What I have in mind is:

abstract type Drawable end

struct Point{C<:Colorant, N} <: Drawable
    pos::CartesianIndex{N}
    color::C
    thickness::Int
end

struct Polygon{C<:Colorant, N, T<:CartesianIndex} <: Drawable
    vertices::NTuple{N, T}
    border_color::C
    border_width::Int
    fill_color::C
    fill::Bool # whether we need to fill the region with color C
end

struct Layers{A:: PriorityQueue} <: Drawable
    objects::A
end

Basically, each drawable type consists of the whole information on how it can be drawn.

Layers is a simple wrapper on DataStructures.PriortyQueue that controls how individual objects are drawn in one draw call.

Point is not serving as the index, but instead as a real physical "point" with color and thickness information.

This is a draft type design, we could definitely add more information into each drawable struct.

cc: @jiviteshjain @mgautam98

Ellipse/Circle thickness with fill=false is broken

eg:

img = zeros(RGB, (200,200));
draw(img, Ellipse(100, 100, 100, 100; thickness=29, fill=false), RGB(1,1,0))

will not draw anything because the code is using a bogus normalized distance for the inner ellipse.

Add a cross

I often need to mark something in the image without obscuring it so much. A cross does exactly that.

I'll therefor try to PR something like this:

struct Cross <: Drawable
    c::Point
    arm::Int
end

function draw!(img::AbstractArray{T, 2}, cross::Cross, color::T) where T<:Colorant
    for x in -cross.arm:cross.arm
        img[cross.c.y, cross.c.x + x] = color
    end
    for y in -cross.arm:cross.arm
        img[cross.c.y + y, cross.c.x] = color
    end
    img
end

Circle / Ellipse Drawing

PR #3 will handle general circle/ellipse drawing. Implementations of circle/ellipse perimeter drawing like the bresenham algorithm still need to be implemented as they are faster.

idea: interpolate the fillvalue

It would be nice to allow fill the region with multiple fillvalues:

  • a constant value
  • interpolating/extrapolate a list of values
  • calling a custom function like the etp here
using Interpolations, ImageCore, OffsetArrays, ImageShow
make_circle(r, fillvalues; kwargs...) = make_circle(eltype(fillvalues), r, fillvalues; kwargs...)
function make_circle(T, r, fillvalues::Vector)
    etp = extrapolate(interpolate(fillvalues, BSpline(Linear())), Line())
    sz = (2r+1, 2r+1)
    canvas = OffsetArrays.centered(Array{T, 2}(undef, sz))

    for i in CartesianIndices(canvas)
        d = sqrt(mapreduce(abs2, +, i.I))
        canvas[i] = etp(d)
    end
    return canvas
end

img = mosaic(
    mosaic(
        make_circle(256, collect(0:0.005:0.8)),
        make_circle(256, repeat(collect(0:0.002:0.8))),
        make_circle(256, repeat(collect(0:0.05:0.8), inner=30));
        rowmajor=true, nrow=1
    ),
    mosaic(
        make_circle(256, colormap("Blues")),
        make_circle(256, colormap("Blues", 256)),
        make_circle(256, colormap("Blues", 512));
        rowmajor=true, nrow=1
    )
)

image

`BoundaryFill` doesn't work for real-size images

using ImageCore, ImageShow, TestImages, ImageDraw

img = testimage("lighthouse")

verts = [CartesianIndex(2, 2), CartesianIndex(2, 6), CartesianIndex(6, 2)]
alg = BoundaryFill(3, 3; fill_value = RGB{N0f8}(1), boundary_value = RGB{N0f8}(1))
draw(img, verts, alg; closed=false)

The recursive call of BoundaryFill simply blows up the call stack:

if (res[y, x] != f.boundary_value && res[y, x] != f.fill_value)
res[y, x] = f.fill_value
f(res, verts, x + 1, y)
f(res, verts, x, y + 1)
f(res, verts, x - 1, y)
f(res, verts, x, y - 1)
end

cc: @ashwani-rathee

Add logo for document

Hi! i'm now planning to add logo images to ImageDraw.jl.

logo
logo-dark

image
image

using Images, ImageDraw

# original image
img = load(download("https://raw.githubusercontent.com/JuliaImages/juliaimages.github.io/source/docs/src/assets/logo.png"))

# Define some colors
c_r, c_g, c_b, c_p = RGBA.([Colors.JULIA_LOGO_COLORS...])
gray_dark = RGBA{N0f8}(0.1,0.1,0.1)
gray_light = RGBA{N0f8}(0.9,0.9,0.9)

# change base corlor black to gray
img = img*N0f8(0.5) .+ RGBA{N0f8}(0.5,0.5,0.5,0)
img_dark = copy(img)
img_light = copy(img)

# define points on the image
p_green = Point(87,35)
p_red = Point(75,55)
p_purple = Point(99,55)
p1 = Point(13,96)
p2 = Point(52,50)
p3 = Point(73,73)
p4 = Point(84,62)
p5 = Point(116,97)

for (img,gray,name) in ((img_dark,gray_light,"logo-dark.png"), (img_light,gray_dark,"logo.png"))
    draw!(img, LineTwoPoints(p1,p2), gray)
    draw!(img, LineTwoPoints(p2,p3), gray)
    draw!(img, LineTwoPoints(p3,p4), gray)
    draw!(img, LineTwoPoints(p4,p5), gray)

    draw!(img, Cross(p1,8), gray)
    draw!(img, Cross(p2,8), gray)
    draw!(img, Cross(p3,8), gray)
    draw!(img, Cross(p4,8), gray)
    draw!(img, Cross(p5,8), gray)

    draw!(img, Ellipse(CirclePointRadius(p_green,10)), c_g)
    draw!(img, Ellipse(CirclePointRadius(p_red,10)), c_r)
    draw!(img, Ellipse(CirclePointRadius(p_purple,10)), c_p)

    save(name,img)
end

Do you have any comments?

Add some text

Other than colored shapes, it would be great if we could also add some text to the images.

I use this to color tracked paths in a composite image from time-lapse images, but I need to some how label individual tracks with an ID (like a bunch of numbers and/or letters).

Hmmm, maybe it would be easier to combine the image with vector graphics somehow.

Polyline with multiple segments

There is a method for making a line in ImageDraw.jl but the method for making a continued lines with multiple points given in arguments in not yet present.I believe single line isn't that useful and having ability to make poly line with continuous vertices/points mentioned in arguments could prove to be quite useful.Better yet if the polyline argument could be provided in vector or similar form.

A better structure for `ImageDraw.jl`

Currently, there are some structural and naming issues in ImageDraw.jl:

  1. line is a method for drawing a line while Line is an abstract type denoting a line.
  2. line used with LineTwoPoints draws only a line segment while line used with LineNormal draws an infinite line on the image, which breaks consistency.
  3. Function names like line, ellipse, path etc. do not clearly suggest whether they are methods to draw an object on an image or methods to create a new drawable object.

I suggest the following changes in the API:

  1. Any object that can be drawn on an image like Line, Ellipse, Polygon etc. should derive from Drawable abstract type.
  2. A single method draw to draw Drawable object(s) on an image(or multiple images). draw would use multiple dispatch to draw the specific objects onto the image. This would produce a much neater API.
  3. The addition of RegularPolygon <: Drawable to draw a regular polygon on an image. This could be used to define other shapes like Square, EquilateralTriangle etc.
  4. Line would be used to draw infinite lines and LineSegment would be used to draw a line segment of a specific length.
    imagedraw jl

Kindly suggest if these changes are relevant. If so, then I can start working on this.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.