Newer
Older
from __future__ import annotations
import dijkstra_1_to_n as dk_
import map_labeling as ml_
from extension import extension_t
from type import array_t, py_array_picker_h, site_h, site_path_h
import sys as sy_
from typing import Callable, Optional, Sequence, Tuple
import numpy as np_
import scipy.ndimage as im_
import skimage.filters as fl_
import skimage.measure as ms_
import skimage.morphology as mp_
som_ext_path_t = namedtuple_t("som_ext_path_t", "extension length path idx")
#
# bmp=boolean map
# lmp=labeled map (intXX or uintXX array)
# map=extension map (map=binary, int8 or uint8 array))
#
self.contour_points = None
@classmethod
def FromMaps(cls, lmp: array_t, contour_lmp: array_t, uid: int) -> soma_t:
#
instance = cls()
instance.contour_points = tuple(zip(*(contour_lmp == uid).nonzero()))
return instance
@property
def has_extensions(self) -> bool:
#
return self.extensions.__len__() > 0
def Extensions(self, max_level: int = sy_.maxsize) -> Tuple[extension_t]:
#
# max_level=1: directly connected extensions
# ...
soma_ext = self.extensions.copy()
current_level_idx = 0
for level in range(2, max_level + 1):
next_level_idx = soma_ext.__len__()
if next_level_idx == current_level_idx:
break
for extension in soma_ext[current_level_idx:]:
soma_ext.extend(extension.extensions)
current_level_idx = next_level_idx
return tuple(soma_ext)
def ContourPointsCloseTo(
self, point: site_h, max_distance: float
) -> Tuple[Optional[Tuple[site_h, ...]], Optional[py_array_picker_h]]:
#
points = tuple(
contour_point
for contour_point in self.contour_points
if (np_.subtract(point, contour_point) ** 2).sum() <= max_distance
)
if points.__len__() > 0:
points_as_picker = tuple(zip(*points))
else:
points = None
points_as_picker = None
return points, points_as_picker
def ExtensionPointsCloseTo(
self, point: site_h, max_distance: float
) -> Tuple[Optional[Tuple[site_h, ...]], Optional[py_array_picker_h]]:
points = []
for extension in self.Extensions():
ext_sites = tuple(zip(*extension.sites))
points.extend(
ext_point
for ext_point in ext_sites
if (np_.subtract(point, ext_point) ** 2).sum() <= max_distance
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
points = tuple(points)
if points.__len__() > 0:
points_as_picker = tuple(zip(*points))
else:
points = None
points_as_picker = None
return points, points_as_picker
# def Extend(
# self, extensions: Sequence[extension_t], dist_to_soma: array_t, costs: array_t
# ) -> None:
# #
# candidate_ext_nfo = [] # eps=end points
# for extension in extensions:
# new_candidates = extension.EndPointsForSoma(self.uid)
# candidate_ext_nfo.extend(
# (end_point, extension) for end_point in new_candidates
# )
# candidate_ext_nfo.sort(key=lambda elm: dist_to_soma[elm[0]])
# unconnected_candidates = list(
# filter(lambda elm: elm[1].soma_uid is None, candidate_ext_nfo)
# )
#
# should_look_for_connections = True
# while should_look_for_connections:
# som_ext_path_nfos = []
# for ep_idx, (ext_end_point, extension) in enumerate(unconnected_candidates):
# path, length = self.ShortestPathFrom(
# extension.uid, ext_end_point, ep_idx, costs
# )
# if length <= max_weighted_dist:
# som_ext_path_nfos.append(
# som_ext_path_t(
# extension=extension, length=length, path=path, idx=ep_idx
# )
# )
# if path.__len__() == 2:
# break
#
# if som_ext_path_nfos.__len__() > 0:
# som_ext_path_nfos.sort(key=lambda nfo: nfo.length)
# shorest_path = som_ext_path_nfos[0]
# self.ExtendWith(shorest_path.extension, shorest_path.path, costs)
# del unconnected_candidates[shorest_path.idx]
# else:
# should_look_for_connections = False
@staticmethod
def ShortestPathFrom(
point: site_h,
costs: array_t,
soma_candidate_points_fct: Callable,
max_straight_sq_dist: float = np_.inf,
) -> Tuple[site_path_h, float]:
#
soma_candidate_points, candidate_indexing = soma_candidate_points_fct(
point, max_straight_sq_dist
)
if soma_candidate_points is None:
return (), np_.inf
costs[point] = 0.0
costs[candidate_indexing] = 0.0
path, length = dk_.DijkstraShortestPath(costs, point, soma_candidate_points)
costs[point] = np_.inf
costs[candidate_indexing] = np_.inf
return path, length
def BackReferenceSoma(self, glial_cmp: glial_cmp_t):
#
raise NotImplementedError
def __str__(self) -> str:
#
if self.extensions is None:
n_extensions = 0
else:
n_extensions = self.extensions.__len__()
return (
f"Soma.{self.uid}, "
f"contour points={self.contour_points.__len__()}, "
f"extensions={n_extensions}"
)
@staticmethod
def Map(image: array_t, low: float, high: float, selem: array_t) -> array_t:
#
# low = 10 #0.15
# high = 67.4 # 0.7126
#
max_image = image.max()
nonzero_sites = image.nonzero()
nonzero_values = image[nonzero_sites]
min_image = nonzero_values.min()
low = low * (max_image - min_image) + min_image
high = high * (max_image - min_image) + min_image
result = fl_.apply_hysteresis_threshold(image, low, high)
result = result.astype(np_.int8)
for dep in range(image.shape[0]):
result[dep, :, :] = mp_.closing(result[dep, :, :], selem)
result[dep, :, :] = mp_.opening(result[dep, :, :], selem)
return result
@staticmethod
def FilteredMap(map_: array_t, min_area: int) -> array_t:
result = map_.copy()
lmp = ms_.label(map_)
for region in ms_.regionprops(lmp):
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
region_sites = (lmp == region.label).nonzero()
result[region_sites] = 0
return result
@staticmethod
def ContourMap(map_: array_t) -> array_t:
#
part_map = ml_.PartLMap(map_)
# Works because the background is labeled with 27
result = part_map < 26
return result.astype(np_.int8)
@staticmethod
def InfluenceMaps(map_: array_t) -> Tuple[array_t, array_t]:
background = (map_ == 0).astype(np_.int8)
dist_map, idx_map = im_.morphology.distance_transform_edt(
background, return_indices=True
)
# obj_map = np_.empty_like(map_)
# for row in range(0, obj_map.shape[0]):
# for col in range(0, obj_map.shape[1]):
# for dep in range(0, obj_map.shape[2]):
# obj_map[row, col, dep] = map_[
# idx_map[0, row, col, dep],
# idx_map[1, row, col, dep],
# idx_map[2, row, col, dep],
# ]
return dist_map, np_.array(map_[tuple(idx_map)])
@staticmethod
def SomasWithExtensionsLMap(
shape = somas[0].img_shape
result = np_.zeros(shape, dtype=np_.int64)
result[soma.sites] = soma.uid
for connection_path in filter(
lambda path: path is not None, soma.connection_path.values()
):
result[connection_path] = soma.uid
for extension in soma.Extensions():
for connection_path in filter(
lambda path: path is not None, extension.connection_path.values()
):
return result
def NormalizedImage(image: array_t) -> array_t:
nonextreme_values = image[np_.logical_and(image > 0.0, image < image.max())]
nonextreme_avg = np_.mean(nonextreme_values)
result = image / nonextreme_avg
return result