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

Consider rewriting pieces of the Crimisini algorithm for greater performance

Open juliohm opened this issue 4 years ago • 6 comments

Some parts of the algorithm could possibly be rewritten to improve performance. Specifically, I think that these two lines where we are finding the frontier of the mask could be replaced by a simple graph representation as opposed to performing dilation + subtraction:

https://github.com/JuliaImages/ImageInpainting.jl/blob/d776c4b237426c13cfb388085b16d6e86797f5be/src/criminisi.jl#L56

https://github.com/JuliaImages/ImageInpainting.jl/blob/d776c4b237426c13cfb388085b16d6e86797f5be/src/criminisi.jl#L94

The dilation + subtraction are great to make the code very clean, but I guess too expensive compared to just doing a graph search on the pixels. We would have to benchmark. Just saving this idea here as an issue in case someone wants to give a try.

juliohm avatar Jul 02 '20 16:07 juliohm

@juliohm I would like to do give this a try,tried this on example provided by @mgautam98

using ImageInpainting, TestImages, ImageCore

img  = Float64.(Gray.(testimage("lighthouse")));

Gray.(img)

# Create a mask of same size as of image
mask = falses(size(img));

# pixels where mask is set to true will be inpainted
mask[50:350,300:400] .= true;
Gray.(mask)

# inpaint take the parametes image, mask and algorithm.  
# We will be using Criminisi [1] algorithm
out = inpaint(img, mask, Criminisi(30,30));

In line 94, δΩ = findall(dilate(ϕ) .& .!ϕ) In this case,its run like 151 times..consuming 1.41sec in total and 0.009363451 on average For others,we can see below for 1 round

while !isempty(δΩ)
    # update confidence values in frontier
    @time for p in δΩ
      c = selectpatch(C, patchsize, p)
      b = selectpatch(ϕ, patchsize, p)
      C[p] = sum(c[b]) / prod(patchsize)
    end  #0.009453 seconds (800 allocations: 1.513 MiB)

    # isophote map
    @time ∇I = pointgradients(I, δΩ) * R     # 0.000932 seconds (3.25 k allocations: 3.642 MiB)
    @time nϕ = pointgradients(ϕ, δΩ) #  0.001477 seconds (3.25 k allocations: 380.266 KiB)
    @time D  = vec(abs.(sum(∇I .* nϕ, dims=2)))
    @time D /= maximum(D)

    # select patch in frontier
    @time p  = δΩ[argmax(C[δΩ].*D)] # 0.000013 seconds (2 allocations: 9.625 KiB)
    @time ψₚ = selectpatch(I, patchsize, p)
    @time bₚ = selectpatch(ϕ, patchsize, p)
   
    # compute distance to all other patches
    @time Δ = convdist(I, ψₚ, bₚ) #  0.086936 seconds (357 allocations: 81.306 MiB, 13.39% gc time)


    # only consider patches in filled region
    @time Δ[mask] .= Inf

  0.000078 seconds (4 allocations: 237.719 KiB)

    # select best candidate
    @time q = argmin(Δ)  # 0.002975 seconds
    @time ψᵦ = selectpatch(I, patchsize, q)
    @time bᵦ = selectpatch(ϕ, patchsize, q)

    # paste patch and mark pixels as painted
    @time b = bᵦ .& .!bₚ
    @time ψₚ[b] .= ψᵦ[b]
    @time bₚ[b] .= true

    # update frontier
    @time δΩ = findall(dilate(ϕ) .& .!ϕ) #0.010699 seconds (15 allocations: 118.562 KiB)
    count =count+1
    break;
  end

I want to improve this to complete this:https://github.com/JuliaImages/juliaimages.github.io/pull/138

ashwanirathee avatar Apr 01 '21 11:04 ashwanirathee

Thank you @ashwani-rathee , that would be great. Can you provide the time output with https://github.com/KristofferC/TimerOutputs.jl? That would give us a better and cleaner perspective on what needs to be improved the most.

juliohm avatar Apr 01 '21 11:04 juliohm

it would be great if it runs faster. I tried with a 2080*2080 1 channel image, and it seems run forever, until I stopped after waiting around 10min. If I reduce the size to 500*500, it needs around 10min to finish. So for my case, it's not practically useful. I don't know how much it can improve by better implementations, or how good the Crimisini scales?

babaq avatar Sep 03 '21 18:09 babaq

Yes, if someone can prepare a PR with performance improvements I will be happy to review it. Currently too busy with other projects to work on it.

juliohm avatar Sep 03 '21 19:09 juliohm

It seems that most time is spent in calculating the distance function for finding a relevant patch. If there are gains, it is in writing non-allocating versions, but even that won't speed up the already quick FFT by much I fear.

julia> @time out = inpaint(img, mask, Criminisi(30, 30));
 ────────────────────────────────────────────────────────────────────
                            Time                    Allocations
                   ───────────────────────   ────────────────────────
 Tot / % measured:      13.1s /  97.1%           12.6GiB /  99.5%

 Section   ncalls     time    %tot     avg     alloc    %tot      avg
 ────────────────────────────────────────────────────────────────────
 Δ            151    10.9s   85.9%  72.4ms   11.5GiB   92.0%  78.3MiB
 δΩ           152    799ms    6.3%  5.26ms   7.92MiB    0.1%  53.4KiB
 p            151    560ms    4.4%  3.71ms    433MiB    3.4%  2.87MiB
 nϕ           151    281ms    2.2%  1.86ms   48.4MiB    0.4%   328KiB
 ∇I           151    152ms    1.2%  1.00ms    542MiB    4.2%  3.59MiB
 ψᵦ           151    297μs    0.0%  1.97μs     0.00B    0.0%    0.00B
 bᵦ           151    264μs    0.0%  1.75μs     0.00B    0.0%    0.00B
 ────────────────────────────────────────────────────────────────────

evetion avatar Jun 14 '22 09:06 evetion

Yes, probably the best way to speedup this code is add a CUDA implementation of the distance function. Both ImageQuilting.jl and ImageInpainting.jl need this.

Em ter., 14 de jun. de 2022 06:13, Maarten Pronk @.***> escreveu:

It seems that most time is spent in calculating the distance function for finding a relevant patch. If there are gains, it is in writing non-allocating versions, but even that won't speed up the already quick FFT by much I fear.

julia> @time out = inpaint(img, mask, Criminisi(30, 30)); ──────────────────────────────────────────────────────────────────── Time Allocations ─────────────────────── ──────────────────────── Tot / % measured: 13.1s / 97.1% 12.6GiB / 99.5%

Section ncalls time %tot avg alloc %tot avg ──────────────────────────────────────────────────────────────────── Δ 151 10.9s 85.9% 72.4ms 11.5GiB 92.0% 78.3MiB δΩ 152 799ms 6.3% 5.26ms 7.92MiB 0.1% 53.4KiB p 151 560ms 4.4% 3.71ms 433MiB 3.4% 2.87MiB nϕ 151 281ms 2.2% 1.86ms 48.4MiB 0.4% 328KiB ∇I 151 152ms 1.2% 1.00ms 542MiB 4.2% 3.59MiB ψᵦ 151 297μs 0.0% 1.97μs 0.00B 0.0% 0.00B bᵦ 151 264μs 0.0% 1.75μs 0.00B 0.0% 0.00B ────────────────────────────────────────────────────────────────────

— Reply to this email directly, view it on GitHub https://github.com/JuliaImages/ImageInpainting.jl/issues/15#issuecomment-1154925726, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAZQW3I6PISD54XLJBWRJH3VPBELXANCNFSM4OPCFFNA . You are receiving this because you were mentioned.Message ID: @.***>

juliohm avatar Jun 14 '22 09:06 juliohm