Skip to content

Commit 162bde8

Browse files
committed
Add the GUI
1 parent 7cf2612 commit 162bde8

File tree

6 files changed

+191
-4
lines changed

6 files changed

+191
-4
lines changed

Project.toml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,22 +6,28 @@ version = "1.0.0"
66
[deps]
77
FileIO = "5789e2e9-d7fb-5bc7-8068-2c6fae9b9549"
88
Glob = "c27321d9-0574-5035-807b-f59d2c89b15c"
9+
Gtk4 = "9db2cae5-386f-4011-9d63-a5602296539b"
10+
GtkObservables = "8710efd8-4ad6-11eb-33ea-2d5ceb25a41c"
911
ImageCore = "a09fc81d-aa75-5fe9-8630-4744c3626534"
1012
ImageIO = "82e4d734-157c-48bb-816b-45c225c6df19"
1113
ImageMorphology = "787d08f9-d448-5407-9aad-5290dd7ab264"
1214
ImageSegmentation = "80713f31-8817-5129-9cf8-209ff8fb23e1"
1315
ImageView = "86fae568-95e7-573e-a6b2-d8a6b900c9ef"
16+
JLD2 = "033835bb-8acc-5ee8-8aae-3f567f8a3819"
1417
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
1518
XLSX = "fdbf4ff8-1666-58a4-91e7-1b58723a45e0"
1619

1720
[compat]
1821
FileIO = "1"
1922
Glob = "1"
23+
Gtk4 = "0.7"
24+
GtkObservables = "2"
2025
ImageCore = "0.10"
2126
ImageIO = "0.6"
2227
ImageMorphology = "0.4"
2328
ImageSegmentation = "1.9"
2429
ImageView = "0.12"
30+
JLD2 = "0.5"
2531
XLSX = "0.10"
2632
julia = "1.10"
2733

docs/src/assets/gui.png

824 KB
Loading

docs/src/index.md

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,50 @@ and it will automatically "activate" this project and you'll have access to all
6868
[Pkg documentation](https://pkgdocs.julialang.org/v1/getting-started/) for
6969
more information.
7070

71-
## Processing your first image
71+
## Processing with the GUI
72+
73+
From within `MyCounterMarkingFolder` created above, start Julia like this:
74+
75+
```sh
76+
MyCounterMarkingFolder$ julia --project
77+
```
78+
79+
Then load the packages:
80+
81+
```
82+
julia> using CounterMarking, Glob
83+
```
84+
85+
Then specify the images you want to process:
86+
87+
```
88+
julia> gui("results_file_name", glob"Picture*.png")
89+
```
90+
91+
This will save your results to `"results_file_name.xlsx"` and `"results_file_name.jld2"`.
92+
The syntax `glob"pattern"` means "all files that match this pattern", where `*` means "one or more characters".
93+
See [this tutorial](https://www.malikbrowne.com/blog/a-beginners-guide-glob-patterns/) for more information about glob syntax.
94+
Alternatively, you can supply a list of files:
95+
96+
```
97+
julia> gui("results_file_name", ["PictureA.png", "mouse7.png"])
98+
```
99+
100+
However you launch it, you should see something like this:
101+
102+
![GUI](assets/gui.png)
103+
104+
On the top is the raw image. On the bottom is the segmented image; you should visually compare the two to check whether you're pleased with the quality of the segmentation.
105+
(If not, click "Skip" to omit that file from analysis.)
106+
107+
If you like the segmentation, your tasks are:
108+
- click on all the checkboxes with colors that correspond to urine spots. You'll notice that the stimulus spot is pre-clicked (you can correct its choice if it didn't pick correctly). Most of the time there will be only one you need to check, but you can click more than one.
109+
In this example image, all the urine spots are marked red, so you'd check the box that has the red border. Leave the stimulus spot checked, too.
110+
- click "Done & Next" to advance to the next image in the sequence
111+
112+
After it finishes cycling through all the images, it will save your results and close the window.
113+
114+
## Processing manually
72115

73116
### Step 1: start Julia with the right project
74117

src/CounterMarking.jl

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,17 @@ using ImageCore
44
using ImageSegmentation
55
using ImageMorphology: label_components
66
using FileIO
7+
using JLD2
78
using XLSX
89
using Glob
10+
using Gtk4
11+
using GtkObservables
912
using ImageView
1013
using Random
1114

1215
export segment_image, stimulus_index, spots, Spot, upperleft
1316
export writexlsx, process_images
14-
export randshow, meanshow
17+
export randshow, meanshow, gui
1518

1619
include("segment.jl")
1720
include("xlxs.jl")

src/gui.jl

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,136 @@
11

2+
function gui(outbase, files; colors=distinguishable_colors(15, [RGB(1, 1, 1)]; dropseed=true))
3+
channelpct(x) = string(round(Int, x * 100)) * '%'
24

5+
outbase, _ = splitext(outbase)
6+
7+
winsize = round.(Int, 0.8 .* screen_size())
8+
win = GtkWindow("CounterMarking", winsize...)
9+
ag = Gtk4.GLib.GSimpleActionGroup()
10+
m = Gtk4.GLib.GActionMap(ag)
11+
push!(win, Gtk4.GLib.GActionGroup(ag), "win")
12+
Gtk4.GLib.add_action(m, "close", ImageView.close_cb, win)
13+
Gtk4.GLib.add_action(m, "closeall", ImageView.closeall_cb, nothing)
14+
Gtk4.GLib.add_stateful_action(m, "fullscreen", false, ImageView.fullscreen_cb, win)
15+
sc = GtkShortcutController(win)
16+
Gtk4.add_action_shortcut(sc,Sys.isapple() ? "<Meta>W" : "<Control>W", "win.close")
17+
Gtk4.add_action_shortcut(sc,Sys.isapple() ? "<Meta><Shift>W" : "<Control><Shift>W", "win.closeall")
18+
Gtk4.add_action_shortcut(sc,Sys.isapple() ? "<Meta><Shift>F" : "F11", "win.fullscreen")
19+
20+
# CSS styling for the colors
21+
io = IOBuffer()
22+
for (i, color) in enumerate(colors)
23+
colorstr = "rgb(" * channelpct(color.r) * ", " *
24+
channelpct(color.g) * ", " *
25+
channelpct(color.b) * ")"
26+
println(io, """
27+
.color$i {
28+
background: $colorstr;
29+
}
30+
""")
31+
end
32+
css = String(take!(io))
33+
cssprov = GtkCssProvider(css)
34+
push!(Gtk4.display(win), cssprov)
35+
36+
win[] = bx = GtkBox(:v)
37+
ImageView.window_wrefs[win] = nothing
38+
signal_connect(win, :destroy) do w
39+
delete!(ImageView.window_wrefs, win)
40+
end
41+
g, frames, canvases = ImageView.canvasgrid((2, 1), :auto)
42+
push!(bx, g)
43+
push!(bx, GtkSeparator(:h))
44+
guibx = GtkBox(:h)
45+
push!(bx, guibx)
46+
seggrid = GtkGrid()
47+
push!(guibx, seggrid)
48+
# Add checkboxes for each color, with the box's color set to the color
49+
cbs = []
50+
for i in 1:length(colors)
51+
cb = checkbox(false)
52+
add_css_class(cb.widget, "color$i")
53+
for prop in ("margin_start", "margin_end", "margin_top", "margin_bottom")
54+
set_gtk_property!(cb.widget, prop, 5)
55+
end
56+
set_gtk_property!(cb.widget, "width-request", 20)
57+
row = div(i - 1, 5) + 1
58+
col = mod(i - 1, 5) + 1
59+
seggrid[col, row] = cb.widget
60+
push!(cbs, cb)
61+
end
62+
# Add "Done & Next" and "Skip" buttons
63+
btnclick = Condition()
64+
whichbutton = Ref{Symbol}()
65+
donebtn = button("Done & Next")
66+
skipbtn = button("Skip")
67+
push!(guibx, donebtn)
68+
push!(guibx, skipbtn)
69+
on(donebtn) do _
70+
whichbutton[] = :done
71+
notify(btnclick)
72+
end
73+
on(skipbtn) do _
74+
whichbutton[] = :skip
75+
notify(btnclick)
76+
end
77+
78+
results = []
79+
for (i, file) in enumerate(files)
80+
img = color.(load(file))
81+
seg = segment_image(img)
82+
nsegs = length(segment_labels(seg))
83+
@assert nsegs < length(colors) "Too many segments for colors"
84+
istim = stimulus_index(seg)
85+
for (j, cb) in enumerate(cbs)
86+
# set_gtk_property!(cb, "active", j <= nsegs)
87+
cb[] = j == istim
88+
end
89+
imshow(canvases[1, 1], img)
90+
imshow(canvases[2, 1], map(i->colors[i], labels_map(seg)))
91+
92+
wait(btnclick)
93+
whichbutton[] == :skip && continue
94+
95+
keep = Int[]
96+
for (j, cb) in enumerate(cbs)
97+
if cb[]
98+
push!(keep, j)
99+
end
100+
end
101+
pixelskeep = map(i -> i keep, labels_map(seg))
102+
L = label_components(pixelskeep)
103+
newseg = SegmentedImage(img, L)
104+
spotdict, stimulus = spots(newseg)
105+
push!(results, (file, spotdict, stimulus, newseg))
106+
end
107+
108+
if !isempty(results)
109+
xlsxname = outbase * ".xlsx"
110+
XLSX.openxlsx(xlsxname; mode="w") do xf
111+
for (i, (file, spotdict, stimulus, seg)) in enumerate(results)
112+
imgsize = size(labels_map(seg))
113+
sheetname = splitext(basename(file))[1]
114+
sheet = if i == 1
115+
XLSX.rename!(xf[1], sheetname)
116+
xf[1]
117+
else
118+
XLSX.addsheet!(xf, sheetname)
119+
end
120+
makesheet!(sheet, spotdict, stimulus, imgsize)
121+
end
122+
end
123+
jldname = outbase * ".jld2"
124+
jldopen(jldname, "w") do jf
125+
for (file, spotdict, stimulus, seg) in results
126+
imgname = splitext(basename(file))[1]
127+
write(jf, imgname, (labels_map(seg), spotdict, stimulus))
128+
end
129+
end
130+
end
131+
destroy(win)
132+
end
133+
gui(outbase, glob::Glob.GlobMatch; kwargs...) = gui(outbase, Glob.glob(glob); kwargs...)
3134

4135
function linkpair(img, imgc)
5136
zr, slicedata = roi(img)

src/xlxs.jl

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,12 @@ function process_images(outfile::AbstractString, glob::Glob.GlobMatch; dirname=p
6464
imgsize = size(labels_map(seg))
6565
spotdict, stimulus = spots(seg)
6666
sheetname = splitext(basename(filename))[1]
67-
sheet = xf[i+=1]
68-
XLSX.rename!(sheet, sheetname)
67+
sheet = if i == 0
68+
xf[i+=1]
69+
XLSX.rename!(sheet, sheetname)
70+
else
71+
XLSX.addsheet!(xf, sheetname)
72+
end
6973
makesheet!(sheet, spotdict, stimulus, imgsize)
7074
end
7175
end

0 commit comments

Comments
 (0)