-
Notifications
You must be signed in to change notification settings - Fork 0
/
lineage.jl
264 lines (248 loc) · 10.1 KB
/
lineage.jl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
using Images
using ImageSegmentation
"""
Version Comment
0.1 initial
0.2 extract time-lines and split contacted branches. hf@0427
-1.2.1 add error handle to avoid no longlived_cell hf@0512
"""
# Get living time
cal_livingtime(stack) = [ sum(stack[:, :, i])>0 for i in 1:size(stack)[3] ]
area_t(stack) = [ sum(stack[:, :, i]) for i in 1:size(stack)[3] ]
function get_unique_label(_old_labels, number::Int=1)
i = 100 # start from 100 to distiguish with oldest labels
labels = zeros(UInt, number)
for count in 1:number
while( i ∈ _old_labels)
i+=1
end
_old_labels = union(_old_labels, i)
labels[count] = i
end
labels
end
"Find long-lived track by searching connected components in 3D"
function find_time_line(markers_t; shortest=90)
println("Finding connected component")
local time_line = label_components( markers_t )
local time_line_whole = copy(time_line)
local line_amount = maximum(time_line)
# More advanced and fine punch and merge could be done
# But we just select long living trajactory, remove short-lived one
# Calculate living length
x_len, y_len, t_len = size(time_line)
local n = zeros(UInt32, t_len, line_amount+1)
# label 0 mean background
@inbounds Threads.@threads for t in 1:t_len
@inbounds for x in 1:x_len
@inbounds for y in 1:y_len
n[t, time_line[x, y, t]+1] += 1
end
end
end
living_time =[ n[:, line].>0 for line in 2:line_amount+1 ]
longlived = [sum(living_time[line]) for line in 1:line_amount] .> shortest
local longlived_label = (1:line_amount)[longlived]
# Remove shortlived branches
@inbounds for I in CartesianIndices(size(time_line))
if time_line[I] ∉ longlived_label
time_line[I] = 0
end
end
time_line, longlived_label, living_time[longlived_label], time_line_whole
end
function find_index4label(_labels)
index = 1
indexs = zeros(Int, maximum(_labels))
for label in _labels
indexs[label]=index
index+=1
end
indexs
end
"Split contacted cell by watershed"
function split_contacted_cell!(old_time_line::Array{Int64,3},
old_longlived_labels::Array{Int64,1}, old_living_time::AbstractArray,
old_time_line_whole::Array{Int64, 3} )
t_len = size(old_time_line)[3]
if length(old_longlived_labels) == 0
println("No longlived cell is found, skipping")
end
println("Detecting contacted branch")
local conn_z_t = zeros(Int, length(old_longlived_labels), t_len)
local label2index = find_index4label(old_longlived_labels)
@time for t in 1:t_len
Threads.@threads for label in old_longlived_labels
local branches = old_time_line[:, :, t] .== label
# count connected component number of each time point
conn_z_t[label2index[label], t] = maximum(label_components(branches))
end
end
contacted_labels = old_longlived_labels[ sum(conn_z_t.>1, dims=2)[:] .> 3 ]
#If two more connected component touch to at same z slice more than 3 times
print("Found contacted branch: ")
println(contacted_labels)
# select area longer than 5e4
#old_label = longlived_label_2[mean.(area_longlived) .> 5e4 ]
# split 3d branch by split 2d cell slice by slice
for contacted_label in contacted_labels
dist_const = 30 # distance constant, 与接触时间和距离有关
local contacted_branch = old_time_line .== contacted_label
local dist = zeros(size( contacted_branch ))
local local_markers = zeros(Bool, size( contacted_branch ))
println("Splitting branch $contacted_label now")
Threads.@threads for t in 1:t_len # TODO: only split at contacted time point
dist[:,:, t] = distance_transform(feature_transform(.~contacted_branch[:,:,t]))
local_markers[:,:,t] = dist[:,:,t] .> dist_const
#split_water = watershed( .- dist[:,:,t], label_components(markers[:,:,t]))
# 直接用 dist 可能会错误打断完整轨迹,需额外膨胀
# 用分水岭 可能会误连,需额外腐蚀,需要更细的 marker
# TODO: 用未接触时的 marker 对原图或距离映射做分水岭, 然后收缩
end
local_time_line, local_longlived_labels, local_living_time, local_time_line_whole =
find_time_line( local_markers, shortest=90);
println("Reassigning contacted branch $contacted_label ")
# Assign new label to labels set and update mask
if length(local_longlived_labels) > 0
# TODO: what if all lines are shorter than 90 after breaking
# remove old branch, old label, old living time.
@inbounds for point in eachindex(old_time_line)
if old_time_line[point] == contacted_label
old_time_line[point] = 0
old_time_line_whole[point] = 0
end
end
index2remove = old_longlived_labels .== contacted_label
deleteat!( old_longlived_labels, index2remove )
deleteat!( old_living_time, index2remove)
for i in 1:length(local_longlived_labels)
global_label = get_unique_label(old_longlived_labels)[1]
# update time_line index
@inbounds Threads.@threads for point in eachindex(local_time_line)
if local_time_line[point] == local_longlived_labels[i]
old_time_line[point] = global_label
old_time_line_whole[point] = global_label
end
end
push!( old_longlived_labels, global_label )
push!( old_living_time, local_living_time[i] )
println("Branch $contacted_label -> $global_label")
end
# add remaining markers to time_line_whole
local_label_max = maximum(local_time_line_whole)
global_label_max = maximum(old_time_line_whole)
tmp = get_unique_label(1:global_label_max, local_label_max - length(local_longlived_labels))
tmp_index = 1
global_labels = zeros(UInt, local_label_max+1)
for i in 1:local_label_max
if i ∉ local_longlived_labels
# preserve 1th index for 0, so start at 2
global_labels[i+1] = tmp[tmp_index]
tmp_index += 1
end
end
@inbounds Threads.@threads for point in eachindex(local_time_line)
γ = global_labels[local_time_line_whole[point]+1]
if γ ≠ 0
old_time_line_whole[point] = γ
end
end
end
end
#old_time_line, old_longlived_labels, old_living_time, old_time_line_whole
nothing
end
"extract walking pathway, [xy, time, labels]"
function walking(_time_line, _longlived_labels, _livingtime)
_tracks = zeros( 2, length(_livingtime[1]), length(_longlived_labels))
_max_index = maximum(_time_line)
@inbounds for t in 1: length(_livingtime[1])
tmp = component_centroids_lables(_time_line[:, :, t], _longlived_labels, _max_index)
@inbounds for i in 1:length(_longlived_labels)
_tracks[:, t, i] = [ tmp[i][1] tmp[i][2] ]
end
end
_tracks #[pos, t, branch]
#TODO: soomth pathway to make sence in biology
end
" 分封领地"
function grant_domain( _raw_imgs::Array{Gray{Normed{UInt16,16}},4},
_time_line, _longlived_labels, _livingtime, _time_line_whole)
h, w, z_depth, t_len = size(_raw_imgs)
watershed_maps = zeros(Integer, h, w, t_len)
println("Granting domain for each detected cell by watershed")
@time @inbounds Threads.@threads for t in 1:t_len
# watershed cost lots of time rather than maximal projection
# TODO:
watershed_maps[:, :, t] = labels_map(watershed(
.-maximum(_raw_imgs[:,:,2:end-1,t],dims=3)[:,:,1], _time_line_whole[:,:,t]) )
end
println("Drawing longlived_maps")
longlived_maps = zeros(Integer, h, w, t_len)
@inbounds Threads.@threads for I in eachindex(longlived_maps)
if watershed_maps[I] ∈ _longlived_labels
longlived_maps[I] = watershed_maps[I]
end
end
println(_longlived_labels)
longlived_maps, watershed_maps
end
" Using given mask to export roi of cell "
function pick_cell(_raw_imgs, _longlived_maps::Array{Integer,3},
cell_label::Int, cell_tracks::Array{Float64,2}, cell_livingtime::BitArray{1})
h, w, z_depth, t_len = size(_raw_imgs)
roi = 240
cell_img = zeros(eltype(_raw_imgs), 2*roi, 2*roi, z_depth, t_len)
@inbounds Threads.@threads for t in (1:t_len)[cell_livingtime]
# only choose frame when object exist
bounder = box(cell_tracks[:, t], h, w, α=roi)
#cell_img[:, :, (t-1)*z_depth+1:t*z_depth] =
# _raw_imgs[bounder[1], bounder[2], (t-1)*z_depth+1:t*z_depth] .*
# (_longlived_maps[bounder[1], bounder[2], t] .== _longlived_labels[_index])
d₁ref = (bounder[1])[1] -1
d₂ref = (bounder[2])[1] -1
@inbounds for d₁ in bounder[1]
@inbounds for d₂ in bounder[2]
if _longlived_maps[d₁, d₂, t] == cell_label
cell_img[d₁-d₁ref, d₂-d₂ref, :, t] = _raw_imgs[d₁, d₂, :, t]
end
end
end
end
cell_img
end
"`component_centroids(labeled_array)` -> an array of centroids for selected lables, excluding the background label 0"
function component_centroids_lables(img::AbstractArray{Int,N}, labels::AbstractArray{Int, 1}, max_index::Integer) where N
len = length(0:maximum(max_index))
n = fill(zero(CartesianIndex{N}), len)
counts = fill(0, len)
@inbounds for I in CartesianIndices(size(img))
v = img[I] + 1
n[v] += I
counts[v] += 1
end
map(v -> n[v].I ./ counts[v], labels.+1)
end
" Return box with fixed height and width"
function box(cell_center, _d₁max, _d₂max; α=256)
Α = 2*α
d₁, d₂ = Int.(floor.(cell_center))
d₁min, d₁max, d₂min, d₂max = d₁-α+1, d₁+α, d₂-α+1, d₂+α
if d₁min < 1
d₁max = Α
d₁min = 1
end
if d₁max > _d₁max
d₁min = _d₁max - Α + 1
d₁max = _d₁max
end
if d₂min < 1
d₂max = Α
d₂min = 1
end
if d₂max > _d₂max
d₂min = _d₂max - Α + 1
d₂max = _d₂max
end
d₁min:d₁max, d₂min:d₂max
end