Skip to content

Commit a4691a0

Browse files
committed
use position to identify stimulus spot
1 parent 1187528 commit a4691a0

File tree

2 files changed

+68
-40
lines changed

2 files changed

+68
-40
lines changed

src/gui.jl

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,13 @@ function gui(
2424
btnclick = Condition(), # used for testing
2525
whichbutton = Ref{Symbol}(), # used for testing
2626
preclick::Union{Int,Nothing} = nothing, # used for testing
27-
background_path = joinpath(pkgdir(@__MODULE__),"docs","src","assets","blurred_calibration.bmp"), # used to correct for non-uniform illumination
28-
crop_top::Int = 93, # crop this many pixels off of each side
29-
crop_bottom::Int = 107,
30-
crop_left::Int = 55,
31-
crop_right::Int = 45,
27+
background_path = nothing, # used to correct for non-uniform illumination (see joinpath(pkgdir(@__MODULE__),"docs","src","assets","blurred_calibration.bmp"))
28+
crop_top::Int = 0, # crop this many pixels off of each side
29+
crop_bottom::Int = 0,
30+
crop_left::Int = 0,
31+
crop_right::Int = 0,
32+
colorproj = RGB{Float32}(1, 1, -2), # used for identifying the stimulus
33+
expectedloc = nothing, # ""
3234
)
3335
channelpct(x) = string(round(Int, x * 100)) * '%'
3436

@@ -37,8 +39,8 @@ function gui(
3739
# Initialize segmented image and color similarity threshold
3840
img = nothing
3941
rescaledimg = nothing
40-
bkgimg = Float32.(Gray.(load(background_path)[crop_top+1:end-crop_bottom, crop_left+1:end-crop_right]))
41-
bkgmean = Float32(mean(bkgimg))
42+
bkgimg = isnothing(background_path) ? nothing : Float32.(Gray.(load(background_path)[crop_top+1:end-crop_bottom, crop_left+1:end-crop_right]))
43+
bkgmean = isnothing(bkgimg) ? nothing : Float32(mean(bkgimg))
4244
seg = nothing
4345
threshold = 0.15
4446
labels2idx = Dict{Int,Int}()
@@ -130,7 +132,8 @@ function gui(
130132
push!(labels2idx, l=>idx)
131133
push!(idx2labels, idx=>l)
132134
end
133-
istim = labels2idx[stimulus_index(seg)]
135+
centroidsacc, _ = get_centroidsacc(seg.image_indexmap)
136+
istim = labels2idx[stimulus_index(seg, centroidsacc; colorproj = colorproj, expectedloc = expectedloc)]
134137
for (j, cb) in enumerate(cbs)
135138
# set_gtk_property!(cb, "active", j <= nsegs)
136139
cb[] = (j == istim || j == preclick)
@@ -176,7 +179,7 @@ function gui(
176179
threshold = 0.15
177180
thrshlbl.label = "Color Similarity Threshold: $threshold"
178181
img = color.(load(file))[crop_top+1:end-crop_bottom, crop_left+1:end-crop_right]
179-
rescaledimg = img ./ bkgimg .* bkgmean
182+
rescaledimg = isnothing(bkgimg) ? img : (img ./ bkgimg .* bkgmean)
180183

181184
seg = segment_image(rescaledimg; threshold = threshold)
182185
nsegs = length(segment_labels(seg))
@@ -189,7 +192,8 @@ function gui(
189192
push!(labels2idx, l=>idx)
190193
push!(idx2labels, idx=>l)
191194
end
192-
istim = labels2idx[stimulus_index(seg)]
195+
centroidsacc, _ = get_centroidsacc(seg.image_indexmap)
196+
istim = labels2idx[stimulus_index(seg, centroidsacc; colorproj = colorproj, expectedloc = expectedloc)]
193197
for (j, cb) in enumerate(cbs)
194198
# set_gtk_property!(cb, "active", j <= nsegs)
195199
cb[] = (j == istim || j == preclick)
@@ -209,7 +213,7 @@ function gui(
209213
pixelskeep = map(i -> i keep, labels_map(seg))
210214
L = label_components(pixelskeep)
211215
newseg = SegmentedImage(img, L)
212-
spotdict, stimulus = spots(newseg)
216+
spotdict, stimulus = spots(newseg; colorproj = colorproj, expectedloc = expectedloc)
213217
push!(results, (file, spotdict, stimulus, newseg))
214218
end
215219

src/segment.jl

Lines changed: 53 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -22,16 +22,30 @@ end
2222
segment_image(img::AbstractMatrix{<:Colorant}; kwargs...) = segment_image(color.(img); kwargs...)
2323

2424
"""
25-
idx = stimulus_index(seg::SegmentedImage, colorproj = RGB(1, 1, -2))
25+
idx = stimulus_index(seg::SegmentedImage, centroidsacc; colorproj = RGB(1, 1, -2), expectedloc = nothing)
2626
2727
Given a segmented image `seg`, return the index of the segment that scores
2828
highest on the product of (1) projection (dot product) with `colorproj` and (2)
2929
number of pixels.
30+
31+
Optionally, if images were taken with a fixed location for the stimulus, a segment's score
32+
is divided by the squared distance of its centroid (via `centroidsacc`) from the position given by `expectedloc`.
3033
"""
31-
function stimulus_index(seg::SegmentedImage, colorproj = RGB{Float32}(1, 1, -2))
32-
proj = [l => (colorproj segment_mean(seg, l)) * segment_pixel_count(seg, l) for l in segment_labels(seg)]
33-
(i, _) = argmax(last, proj)
34-
return i
34+
function stimulus_index(seg::SegmentedImage, centroidsacc; colorproj = RGB{Float32}(1, 1, -2), expectedloc = nothing)
35+
if !isnothing(expectedloc)
36+
proj = map(segment_labels(seg)) do l
37+
l == 0 && return 0
38+
val = centroidsacc[l]
39+
centroid = [round(Int, val[1] / val[3]), round(Int, val[2] / val[3])]
40+
return l => (colorproj segment_mean(seg, l) * segment_pixel_count(seg, l) / max(1, sum(abs2, centroid .- expectedloc)))
41+
end
42+
(i, _) = argmax(last, proj)
43+
return i
44+
else
45+
proj = [l => (colorproj segment_mean(seg, l)) * segment_pixel_count(seg, l) for l in segment_labels(seg)]
46+
(i, _) = argmax(last, proj)
47+
return i
48+
end
3549
end
3650

3751
# function contiguous(seg::SegmentedImage, img::AbstractMatrix{<:Color}; min_size::Int = 50)
@@ -47,6 +61,35 @@ end
4761
# contiguous(seg::SegmentedImage, img::AbstractMatrix{<:Colorant}; kwargs...) =
4862
# contiguous(seg, color.(img); kwargs...)
4963

64+
"""
65+
centroidsacc, nadj = get_centroidsacc(seg::SegmentedImage)
66+
67+
Given a the index map `indexmap` of a segmented image, return an accumulator for each segment's centroid
68+
as well as the number of times two segments are adjacent.
69+
"""
70+
function get_centroidsacc(indexmap::Matrix{Int64})
71+
keypair(i, j) = i < j ? (i, j) : (j, i)
72+
R = CartesianIndices(indexmap)
73+
Ibegin, Iend = extrema(R)
74+
I1 = oneunit(Ibegin)
75+
centroidsacc = Dict{Int, Tuple{Int, Int, Int}}() # accumulator for centroids
76+
nadj = Dict{Tuple{Int, Int}, Int}() # number of times two segments are adjacent
77+
for idx in R
78+
l = indexmap[idx]
79+
l == 0 && continue
80+
acc = get(centroidsacc, l, (0, 0, 0))
81+
centroidsacc[l] = (acc[1] + idx[1], acc[2] + idx[2], acc[3] + 1)
82+
for j in max(Ibegin, idx - I1):min(Iend, idx + I1)
83+
lj = indexmap[j]
84+
if lj != l && lj != 0
85+
k = keypair(l, lj)
86+
nadj[k] = get(nadj, k, 0) + 1
87+
end
88+
end
89+
end
90+
return centroidsacc, nadj
91+
end
92+
5093
struct Spot
5194
npixels::Int
5295
centroid::Tuple{Int, Int}
@@ -67,40 +110,21 @@ Spots larger than `max_size_frac * npixels` (default: 10% of the image) are igno
67110
function spots(
68111
seg::SegmentedImage;
69112
max_size_frac=0.1, # no spot is bigger than max_size_frac * npixels
113+
kwargs...
70114
)
71-
keypair(i, j) = i < j ? (i, j) : (j, i)
72-
73-
istim = stimulus_index(seg)
115+
centroidsacc, nadj = get_centroidsacc(seg.image_indexmap)
116+
istim = stimulus_index(seg, centroidsacc; kwargs...)
74117

75-
label = seg.image_indexmap
76-
R = CartesianIndices(label)
77-
Ibegin, Iend = extrema(R)
78-
I1 = oneunit(Ibegin)
79-
centroidsacc = Dict{Int, Tuple{Int, Int, Int}}() # accumulator for centroids
80-
nadj = Dict{Tuple{Int, Int}, Int}() # number of times two segments are adjacent
81-
for idx in R
82-
l = label[idx]
83-
l == 0 && continue
84-
acc = get(centroidsacc, l, (0, 0, 0))
85-
centroidsacc[l] = (acc[1] + idx[1], acc[2] + idx[2], acc[3] + 1)
86-
for j in max(Ibegin, idx - I1):min(Iend, idx + I1)
87-
lj = label[j]
88-
if lj != l && lj != 0
89-
k = keypair(l, lj)
90-
nadj[k] = get(nadj, k, 0) + 1
91-
end
92-
end
93-
end
94118
stimulus = Ref{Pair{Int,Spot}}()
95119
filter!(centroidsacc) do (key, val)
96120
if key == istim
97121
stimulus[] = key => Spot(val[3], (round(Int, val[1] / val[3]), round(Int, val[2] / val[3])))
98122
return false
99123
end
100-
val[3] <= max_size_frac * length(label) || return false
124+
val[3] <= max_size_frac * length(seg.image_indexmap) || return false
101125
# # is the centroid within the segment?
102126
# x, y = round(Int, val[1] / val[3]), round(Int, val[2] / val[3])
103-
# l = label[x, y]
127+
# l = seg.image_indexmap[x, y]
104128
# @show l
105129
# l == key || return false
106130
# is the segment lighter than most of its neighbors?

0 commit comments

Comments
 (0)