Multithread image processing with Cairo.jl / Luxor.jl possible?

Hi,
This is a toy program that draw circles on the Julia logo. It woks sequentially but when I try to do parallel image processing I have this error with Julia 1.5.3 up to date.

julia: cairo-surface.c :2092 : _cairo_surface_release_source_image: l'assertion « !surface->finished » a échoué.

Is it because the cairo library cannot be used by multiple threads or am I doing something wrong ?
Thanks !

file main.jl

using Luxor

include("proc.jl")

Threads.@threads for i in 1:10
#for i in 1:10
    process(i)
end

file proc.jl

function process(i)
    image = readpng("Julia_Logo.png")
    println(i, " image read")

    w = image.width
    h = image.height

    # file output
    fname = "image$i.png"

    Drawing(w, h, fname)
    
    placeimage(image, 0, 0)

    setcolor((255,0,0,0.3))

    for x in 1:50
        x1 = rand()*w
        y1 = rand()*h
        circle(Point(x1, y1), 12, :fill) # draw a circle on the image
    end
    finish()
end

Hi! It’s a question best directed at Cairo.jl, because Luxor is a just a sugary wrapper for raw Cairo functionality…:icecream::sweat_smile:

1 Like

@cormullion I changed the title to mention Cairo.jl. Thank you for writing Luxor.jl, it is very cool :wink:

1 Like

I tried to put a lock() at the final step of image processing :

function process(i)
    image = readpng("Julia_Logo.png")
    println(i, " image read")

    w = image.width
    h = image.height

    # file output
    fname = "results/image$i.png"

    Drawing(w, h, fname)
    
    placeimage(image, 0, 0)

    setcolor((255,0,0,0.3))

    for x in 1:50
        x1 = rand()*w
        y1 = rand()*h
        circle(Point(x1, y1), 5, :fill) # draw a circle on the image
    end
    begin
        lock(a)
        try
            finish(a)
        finally
            unlock(a)
        end
    end
end

there is a little improvement, some images are processed in parallel, but there is still a race problem related to the library Cairo

$ julia main.jl 
1 image read
9 image read
7 image read
4 image read
julia: cairo-path-fixed.c :392 : _cairo_path_fixed_drop_line_to:  l'assertion « _cairo_path_fixed_last_op (path) == CAIRO_PATH_OP_LINE_TO » a échoué.

I don’t think libcairo is working multithreading and Cairo.jl has no idea about multithreading either (Completely rework pointer management. · Issue #320 · JuliaGraphics/Cairo.jl · GitHub shows some work on pointer management and GC needed).

If you really need to work on drawing in threads, i’d do a surface per thread and combine them after finishing.

@lobingera thanks for your explanation, it is important for me to know that the limitation comes from the library and not a mistake in my code :slight_smile: