We have 3d image data for cell
s and 3d image data for nucleus
.
Think of the cells creating worms in the space-time picture (the third axis is the time).
After some preprocessing (e.g. imfilter!
with Kernel.gaussian((2,2,0))
),
we start finding cells in the first frame by doing a feature transform on their nuclei:
using Images, ImageSegmentation, ImageFiltering
seeds1_ = distance_transform(feature_transform((nucleus[:,:,1]) .< t1))) .> t2
seeds1 = (label_components(seeds1_))
I typically use a Makie script to find good thresholds:
println("Sliders to tune thresholds")
t1slider, t1_ = textslider(0.01:0.01:1.0, "t1", start = t1);
t2slider, t2_ = textslider(0.01:0.01:1.0, "t2", start = t2);
seed_img = lift(t1_, t2_) do t1, t2
nlz(distance_transform(feature_transform(testimage) .< t1))) .> t2
end
p3 = hbox(image(seed_img), t1slider, t2slider)
The next step puts the recognised nuclei into the picture and fixes some background pixels
seeds = zeros(UInt16, m, n, T)
seeds[:,:,1] = seeds1
seeds[end,1,1] = seeds[1,end,1] = seeds[end,end,1] = seeds[1,1,1] = maximum(seeds1) + 1
Then I am segmenting the cells on a picture with cell boundaries being the “walls” separating cell-interiors and background through watershed
nlz(x, (a,b) = (minimum(x), maximum(x)), (c,d) = (zero(eltype(x)), one(eltype(x)))) = clamp.(c .+ (x .- a) .* ((d-c)/(b-a)), c, d)
cell2 = N0f16.(1 .-nlz(abs.(cell .- t))) # normalize
segments = watershed(cell2, seeds)
Basically I am flooding the worms and the outside.
Later I will explain the post processing giving me the cell trajectories.