I’ve got some 8-bit TIFF stacks, on the order of 300x300x150 (x * y * slices) in size. I can load these into Julia fine and display them, what I’d really like to be able to do now is interactively crop out a region define by the mouse (rectangular is fine, no need for spline fits to many points) and save that crop to a new image.
The basic code I’ve got so far is:
using Images, ImageView;
using Gtk;
using QuartzImageIO;
#On a mac, it seems I need this else I get errors about no being able to load image
filename = open_dialog("My Open dialog")
image = load(filename);
imshow(image);
mouse_pos = map(d->println("x = $(float(d.position.x)), y = $(float(d.position.y))"),s["gui"]["canvas"].mouse.buttonpress)
@show mouse_pos;
I’ll admit the mouse_pos bit is complete cargo cult programming, it came from a google search and I’m not sure how it is supposed to work. It just doesn’t seem to for me. I think I could probably do this using GTKReactive, but I don’t really want t create a whole GUI program for this simple-ish task.
Any pointers to info or help with the code is greatly appreciated.
Here’s a solution with GLMakie, although somehow it seems the zoom rectangle was broken in the last version so it’s not actually visible. Would have been cooler if the selection was actually visible.
I didn’t write the cropping and saving part but you can see where you’d put it in the code.
using GLMakie
images = randn(300, 300, 150)
fig = Figure()
i_slice = Node(1)
ax = Axis(fig[1, 1], aspect = DataAspect(), yreversed = true,
title = @lift("Slice $($i_slice)"))
hidedecorations!(ax)
chosen_slice = @lift(images[:, :, $i_slice]')
i = image!(ax, chosen_slice)
translate!(i, 0, 0, -5)
buttongrid = fig[1, 2] = GridLayout(tellheight = false)
b = Button(buttongrid[1, 1], label = "Save crop")
b2 = Button(buttongrid[2, 1], label = "Next image")
b3 = Button(buttongrid[3, 1], label = "Previous image")
on(b.clicks) do c
lims = ax.finallimits[]
println("save image cropped to $lims")
end
on(b2.clicks) do c
i_slice[] = mod1(i_slice[] + 1, 150)
autolimits!(ax)
end
on(b3.clicks) do c
i_slice[] = mod1(i_slice[] - 1, 150)
autolimits!(ax)
end
fig
Hi everyone,
I’m digging up this topic just to know if someone could help me to save the cropped images with Jules’s solution?
I modified the code as above, but i must be missing something as it always returns an error when I try to save the cropped version of my image.
Thank you for your help,
Josselin
using GLMakie
using FileIO
using ImageIO
input = load("/path/to/input.png")
images = input
images
fig = Figure()
i_slice = Observable(1)
ax = Axis(fig[1, 1], aspect = DataAspect(), yreversed = true,
title = @lift("Slice $($i_slice)"))
hidedecorations!(ax)
chosen_slice = @lift(images[:, :, $i_slice]')
i = image!(ax, chosen_slice)
translate!(i, 0, 0, -5)
buttongrid = fig[1, 2] = GridLayout(tellheight = false)
b = Button(buttongrid[1, 1], label = "Save crop")
b2 = Button(buttongrid[2, 1], label = "Next image")
b3 = Button(buttongrid[3, 1], label = "Previous image")
on(b.clicks) do c
lims = ax.finallimits[]
println("save image cropped to $lims")
# I tried to save $lims, $i, $ax...
save("ouput.png", lims)
end
on(b2.clicks) do c
i_slice[] = mod1(i_slice[] + 1, 150)
autolimits!(ax)
end
on(b3.clicks) do c
i_slice[] = mod1(i_slice[] - 1, 150)
autolimits!(ax)
end
fig
Hi Jules,
With save("ouput.png", lims) I have the following error message :
Errors encountered while save File{DataFormat{:PNG}, String}("ouput.png").
All errors:
=============
MethodError: no method matching save(::File{DataFormat{:PNG}, String}, ::GeometryBasics.HyperRectangle{2, Float32})
You would ideally use the limits as guidelines to figure out how to actually crop + save the image, so for example:
on(b.clicks) do c
lims = ax.finallimits[]
println("save image cropped to $lims")
# I tried to save $lims, $i, $ax...
mini, maxi = extrema(lims) # get the bottom left and top right corners of `lims`
mini = round.(Int, mini) # to index into an image, which is an array, we need ints
maxi = round.(Int, maxi) # and the axis bounding box is always represented in floats
cropped_slice = chosen_slice[][mini[1]:maxi[1], mini[2]:maxi[2]]
save("output_$(i_slice[]).png", cropped_slice)
end
or something like this. Basically, you’re saving a cropped version of chosen_slice[] because that is what is actually displayed on the screen, and you know the number of the image from i_slice[]. I hope it makes sense!