batoolset 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (162) hide show
  1. batoolset/GUI/__init__.py +0 -0
  2. batoolset/GUI/list_gui.py +513 -0
  3. batoolset/GUI/multi_image_display.py +989 -0
  4. batoolset/GUI/open.py +325 -0
  5. batoolset/GUI/overlay_hints.py +45 -0
  6. batoolset/GUI/paint2.py +1345 -0
  7. batoolset/GUI/scrollablepaint.py +1152 -0
  8. batoolset/IJ_ROIs/__init__.py +0 -0
  9. batoolset/IJ_ROIs/tools.py +40 -0
  10. batoolset/PATH/__init__.py +0 -0
  11. batoolset/PATH/tools.py +79 -0
  12. batoolset/SQLite_tools/__init__.py +0 -0
  13. batoolset/SQLite_tools/tools.py +840 -0
  14. batoolset/__init__.py +0 -0
  15. batoolset/binary/__init__.py +0 -0
  16. batoolset/binary/tools.py +123 -0
  17. batoolset/binarytools/__init__.py +0 -0
  18. batoolset/binarytools/cell_center_detector.py +290 -0
  19. batoolset/clipboard_tools/__init__.py +0 -0
  20. batoolset/clipboard_tools/tools.py +7 -0
  21. batoolset/colors/__init__.py +0 -0
  22. batoolset/colors/colorgen.py +193 -0
  23. batoolset/colors/tools.py +22 -0
  24. batoolset/command_line/__init__.py +0 -0
  25. batoolset/command_line/tools.py +18 -0
  26. batoolset/dialogs/__init__.py +0 -0
  27. batoolset/dialogs/opensave.py +228 -0
  28. batoolset/dicts/__init__.py +0 -0
  29. batoolset/dicts/tools.py +299 -0
  30. batoolset/dims/__init__.py +0 -0
  31. batoolset/dims/tools.py +83 -0
  32. batoolset/draw/__init__.py +0 -0
  33. batoolset/draw/shapes/Position.py +183 -0
  34. batoolset/draw/shapes/__init__.py +0 -0
  35. batoolset/draw/shapes/automated_alignment_drawing_refactored.py +346 -0
  36. batoolset/draw/shapes/circle2d.py +99 -0
  37. batoolset/draw/shapes/compute_optimal_size_all_merged.py +317 -0
  38. batoolset/draw/shapes/ellipse2d.py +336 -0
  39. batoolset/draw/shapes/freehand2d.py +235 -0
  40. batoolset/draw/shapes/group.py +1830 -0
  41. batoolset/draw/shapes/image2d.py +2469 -0
  42. batoolset/draw/shapes/line2d.py +575 -0
  43. batoolset/draw/shapes/point2d.py +257 -0
  44. batoolset/draw/shapes/polygon2d.py +357 -0
  45. batoolset/draw/shapes/polyline2d.py +235 -0
  46. batoolset/draw/shapes/rect2d.py +388 -0
  47. batoolset/draw/shapes/rectangle2d.py +601 -0
  48. batoolset/draw/shapes/scalebar.py +304 -0
  49. batoolset/draw/shapes/serializablefont.py +126 -0
  50. batoolset/draw/shapes/serializableguiparams.py +10 -0
  51. batoolset/draw/shapes/square2d.py +103 -0
  52. batoolset/draw/shapes/textorientation.py +45 -0
  53. batoolset/draw/shapes/tools.py +467 -0
  54. batoolset/draw/shapes/transform2D.py +350 -0
  55. batoolset/draw/shapes/txt2d.py +550 -0
  56. batoolset/draw/widgets/__init__.py +0 -0
  57. batoolset/draw/widgets/col.py +343 -0
  58. batoolset/draw/widgets/image_group.py +248 -0
  59. batoolset/draw/widgets/image_organizer.py +96 -0
  60. batoolset/draw/widgets/paint.py +184 -0
  61. batoolset/draw/widgets/vectorial.py +257 -0
  62. batoolset/figure/__init__.py +0 -0
  63. batoolset/figure/alignment.py +814 -0
  64. batoolset/figure/column.py +1117 -0
  65. batoolset/figure/fig_tools.py +285 -0
  66. batoolset/figure/minimal_debugger_scale_etc.py +139 -0
  67. batoolset/figure/panel.py +697 -0
  68. batoolset/figure/row.py +852 -0
  69. batoolset/figure/user_selection.py +287 -0
  70. batoolset/files/__init__.py +0 -0
  71. batoolset/files/tools.py +1321 -0
  72. batoolset/images/__init__.py +0 -0
  73. batoolset/images/tools.py +81 -0
  74. batoolset/img.py +5216 -0
  75. batoolset/linux/__init__.py +0 -0
  76. batoolset/linux/tools.py +72 -0
  77. batoolset/lists/__init__.py +0 -0
  78. batoolset/lists/tools.py +436 -0
  79. batoolset/luts/__init__.py +0 -0
  80. batoolset/luts/lut_minimal_test.py +1207 -0
  81. batoolset/matplolib/__init__.py +0 -0
  82. batoolset/matplolib/tools.py +44 -0
  83. batoolset/np_tools/__init__.py +0 -0
  84. batoolset/np_tools/tools.py +252 -0
  85. batoolset/np_tools/trash.py +15 -0
  86. batoolset/pandas_tools/__init__.py +0 -0
  87. batoolset/pandas_tools/tools.py +880 -0
  88. batoolset/partials/__init__.py +0 -0
  89. batoolset/partials/tools.py +83 -0
  90. batoolset/plot_tools/__init__.py +0 -0
  91. batoolset/plot_tools/tools.py +70 -0
  92. batoolset/pyqt/__init__.py +0 -0
  93. batoolset/pyqt/tools.py +691 -0
  94. batoolset/re_tools/__init__.py +0 -0
  95. batoolset/re_tools/tools.py +35 -0
  96. batoolset/serialization/__init__.py +0 -0
  97. batoolset/serialization/tools.py +1192 -0
  98. batoolset/sets/__init__.py +0 -0
  99. batoolset/sets/tools.py +109 -0
  100. batoolset/settings/__init__.py +0 -0
  101. batoolset/settings/global_settings.py +134 -0
  102. batoolset/strings/__init__.py +0 -0
  103. batoolset/strings/tools.py +225 -0
  104. batoolset/ta/__init__.py +0 -0
  105. batoolset/ta/database/__init__.py +0 -0
  106. batoolset/ta/database/advanced_sql_plotter.py +1170 -0
  107. batoolset/ta/database/my_qt_table.py +79 -0
  108. batoolset/ta/database/preview_and_edit_sqlite_db.py +405 -0
  109. batoolset/ta/database/sql.py +2141 -0
  110. batoolset/ta/database/sql_column_name_dialog.py +122 -0
  111. batoolset/ta/measurements/TAmeasures.py +2226 -0
  112. batoolset/ta/measurements/__init__.py +0 -0
  113. batoolset/ta/measurements/measurements3D/__init__.py +0 -0
  114. batoolset/ta/measurements/measurements3D/get_point_on_surface_if_centroid_is_bad.py +175 -0
  115. batoolset/ta/measurements/measurements3D/height_map_to_projection.py +82 -0
  116. batoolset/ta/measurements/measurements3D/measures3D.py +208 -0
  117. batoolset/ta/measurements/measurements3D/triangulate.py +331 -0
  118. batoolset/ta/measurements/nematic.py +562 -0
  119. batoolset/ta/measurements/nematic_measurements.py +107 -0
  120. batoolset/ta/segmentation/__init__.py +0 -0
  121. batoolset/ta/segmentation/neo_wshed.py +314 -0
  122. batoolset/ta/selections/__init__.py +0 -0
  123. batoolset/ta/selections/selection.py +329 -0
  124. batoolset/ta/tracking/__init__.py +0 -0
  125. batoolset/ta/tracking/last_tracking_based_on_matching_or_on_translation_from_mermaid_warp.py +653 -0
  126. batoolset/ta/tracking/local_to_track_correspondance.py +138 -0
  127. batoolset/ta/tracking/rapid_detection_of_vertices.py +517 -0
  128. batoolset/ta/tracking/registration.py +108 -0
  129. batoolset/ta/tracking/swap_detector_and_fixer.py +296 -0
  130. batoolset/ta/tracking/track_correction.py +238 -0
  131. batoolset/ta/tracking/tracking_error_detector_and_fixer.py +3099 -0
  132. batoolset/ta/tracking/tracking_yet_another_approach_pyramidal_registration_n_neo_swapping_correction.py +725 -0
  133. batoolset/ta/utils/TA_tools.py +43 -0
  134. batoolset/ta/utils/__init__.py +0 -0
  135. batoolset/ta/utils/rps_tools.py +1783 -0
  136. batoolset/tests/__init__.py +0 -0
  137. batoolset/tests/rps_tools.py +51 -0
  138. batoolset/tests/tools.py +10 -0
  139. batoolset/tools/__init__.py +0 -0
  140. batoolset/tools/early_stopper_class.py +9 -0
  141. batoolset/tools/guided_data_recoverer.py +110 -0
  142. batoolset/tools/logger.py +100 -0
  143. batoolset/tools/mytimer.py +31 -0
  144. batoolset/tools/prompt_color.py +160 -0
  145. batoolset/tools/qthandler.py +118 -0
  146. batoolset/uitools/__init__.py +0 -0
  147. batoolset/uitools/blinker.py +56 -0
  148. batoolset/unsafe/__init__.py +0 -0
  149. batoolset/unsafe/tools.py +115 -0
  150. batoolset/utils/__init__.py +0 -0
  151. batoolset/utils/commontools.py +158 -0
  152. batoolset/utils/loadlist.py +728 -0
  153. batoolset/version.py +10 -0
  154. batoolset/xml/__init__.py +0 -0
  155. batoolset/xml/tools.py +65 -0
  156. batoolset/zip_tools/__init__.py +0 -0
  157. batoolset/zip_tools/tools.py +76 -0
  158. batoolset-0.1.0.dist-info/LICENSE +24 -0
  159. batoolset-0.1.0.dist-info/METADATA +45 -0
  160. batoolset-0.1.0.dist-info/RECORD +162 -0
  161. batoolset-0.1.0.dist-info/WHEEL +5 -0
  162. batoolset-0.1.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,108 @@
1
+ from skimage.registration import phase_cross_correlation # change in scipy 0.19
2
+ # image = data.camera()
3
+ from skimage import transform
4
+ import numpy as np
5
+
6
+ __DEBUG__ = False
7
+
8
+ def apply_translation(img, y, x):
9
+ """
10
+ Applies translation to an image.
11
+
12
+ Args:
13
+ img (numpy.ndarray): The input image.
14
+ y (float): Translation along the y-axis.
15
+ x (float): Translation along the x-axis.
16
+
17
+ Returns:
18
+ numpy.ndarray: The translated image.
19
+ """
20
+ afine_tf = transform.AffineTransform(translation=(x, y))
21
+ translated = transform.warp(img, inverse_map=afine_tf, order=0, preserve_range=True)
22
+ return translated
23
+
24
+ def pre_register_images(orig_t0, orig_t1, apply_to_orig_t0=False):
25
+ """
26
+ Performs pre-registration between two images.
27
+
28
+ Args:
29
+ orig_t0 (numpy.ndarray): The first image.
30
+ orig_t1 (numpy.ndarray): The second image.
31
+ apply_to_orig_t0 (bool, optional): Whether to apply the translation to orig_t0. Defaults to False.
32
+
33
+ Returns:
34
+ tuple or numpy.ndarray: If apply_to_orig_t0 is False, returns the global translations (gloabl_t0, gloabl_t1).
35
+ If apply_to_orig_t0 is True, returns the translated orig_t0 image.
36
+
37
+ Notes:
38
+ - The function uses phase cross-correlation to estimate the global translation between the two images.
39
+ - The global translation is then applied to either orig_t0 or both orig_t0 and orig_t1, depending on the
40
+ value of apply_to_orig_t0.
41
+ """
42
+ global_translation, error, diffphase = phase_cross_correlation(orig_t0, orig_t1)
43
+ gloabl_t0 = -global_translation[0]
44
+ gloabl_t1 = -global_translation[1]
45
+ if not apply_to_orig_t0:
46
+ return gloabl_t0, gloabl_t1
47
+ else:
48
+ return apply_translation(orig_t0, -gloabl_t0, -gloabl_t1)
49
+
50
+ def get_pyramidal_registration(orig_t0, orig_t1, depth=2, threshold_translation=20):
51
+ """
52
+ Performs pyramidal registration between two images.
53
+
54
+ Args:
55
+ orig_t0 (numpy.ndarray): The first image.
56
+ orig_t1 (numpy.ndarray): The second image.
57
+ depth (int, optional): The depth of the pyramid. Defaults to 2.
58
+ threshold_translation (float, optional): The threshold for ignoring large translations. Defaults to 20.
59
+
60
+ Returns:
61
+ numpy.ndarray: The translation matrix representing the translations for each pixel.
62
+
63
+ Notes:
64
+ - The function applies pre-registration between orig_t0 and orig_t1.
65
+ - It then iteratively performs registration on blocks of decreasing size.
66
+ - Translations are accumulated in the translation matrix.
67
+ """
68
+
69
+ translation_matrix = np.zeros((*orig_t0.shape, 2))
70
+
71
+ gloabl_t0, gloabl_t1 = pre_register_images(orig_t0, orig_t1)
72
+
73
+ translation_matrix[..., 0] += gloabl_t0
74
+ translation_matrix[..., 1] += gloabl_t1
75
+
76
+ height = orig_t0.shape[0]
77
+ width = orig_t0.shape[1]
78
+
79
+ block_size = 256
80
+
81
+ for i in range(depth):
82
+ try:
83
+ tile_height = block_size // (i * 2)
84
+ tile_width = block_size // (i * 2)
85
+ except:
86
+ tile_height = block_size
87
+ tile_width = block_size
88
+
89
+ for y in range(0, height, tile_height):
90
+ for x in range(0, width, tile_width):
91
+ afine_tf = transform.AffineTransform(
92
+ translation=(-translation_matrix[y, x, 1], -translation_matrix[y, x, 0]))
93
+ translated = transform.warp(orig_t0, inverse_map=afine_tf, order=0, preserve_range=True)
94
+
95
+ tile_t0 = translated[y:y + tile_height, x:x + tile_width]
96
+ tile_t1 = orig_t1[y:y + tile_height, x:x + tile_width]
97
+
98
+ shift, error, diffphase = phase_cross_correlation(tile_t0, tile_t1)
99
+ t0 = -shift[0]
100
+ t1 = -shift[1]
101
+
102
+ if abs(t0) > threshold_translation or abs(t1) > threshold_translation:
103
+ continue
104
+
105
+ translation_matrix[y:y + tile_height, x:x + tile_width, 0] += t0
106
+ translation_matrix[y:y + tile_height, x:x + tile_width, 1] += t1
107
+
108
+ return translation_matrix
@@ -0,0 +1,296 @@
1
+ from batoolset.img import int24_to_RGB, RGB_to_int24
2
+ from timeit import default_timer as timer
3
+ import numpy as np
4
+ from batoolset.img import Img
5
+ # really very good --> now make it recursive then done --> extract the cell from there and continue
6
+ from natsort import natsorted
7
+ import os
8
+
9
+ # parameters
10
+ from batoolset.utils.loadlist import loadlist
11
+ from batoolset.ta.tracking.rapid_detection_of_vertices import associate_vertices_to_cells, detect_vertices_and_bonds
12
+
13
+ __DEBUG__ = False
14
+
15
+
16
+
17
+ def _fix_swapping_internal(tracked_cells_t0, tracked_cells_t1, vertices_t0=None, vertices_t1=None, MIN_CUT_OFF_SWAPPING=None):
18
+ """
19
+ Fixes swapping of cells between two time steps.
20
+
21
+ Args:
22
+ tracked_cells_t0 (numpy.ndarray): Array of tracked cells at time t0.
23
+ tracked_cells_t1 (numpy.ndarray): Array of tracked cells at time t1.
24
+ vertices_t0 (numpy.ndarray, optional): Array of vertices at time t0. Defaults to None.
25
+ vertices_t1 (numpy.ndarray, optional): Array of vertices at time t1. Defaults to None.
26
+ MIN_CUT_OFF_SWAPPING (float, optional): Minimum cutoff score for swapping. Defaults to None.
27
+
28
+ Returns:
29
+ numpy.ndarray: Updated array of tracked cells at time t1.
30
+
31
+ """
32
+ if __DEBUG__:
33
+ cell_to_check = 0x29d785 # 0x976494 #0x29d785 # 0x746502 #0x96c9ef #0x542358 # 0xf36619 #0x496bbd #0x1bf38f
34
+ # map 9921684 2742149 7628034 9882095 5514072 15951385 4811709
35
+ print('cell to check', cell_to_check, format(cell_to_check, 'x'))
36
+
37
+
38
+ # inner_time = timer()
39
+ if vertices_t0 is None:
40
+ vertices_t0 = detect_vertices_and_bonds(tracked_cells_t0)
41
+ vertices_t0 = np.where(vertices_t0 == 255)
42
+ if vertices_t1 is None:
43
+ vertices_t1 = detect_vertices_and_bonds(tracked_cells_t1)
44
+ vertices_t1 = np.where(vertices_t1 == 255)
45
+ # print('current time0', timer() - inner_time)
46
+
47
+ # inner_time = timer()
48
+ t0_cells_n_vertices = associate_vertices_to_cells(tracked_cells_t0, vertices_t0,
49
+ forbidden_colors=(0, 0xFFFFFF),
50
+ output_cells_and_their_neighbors=True)
51
+ t1_cells_n_vertices = associate_vertices_to_cells(tracked_cells_t1, vertices_t1,
52
+ forbidden_colors=(0, 0xFFFFFF),
53
+ output_cells_and_their_neighbors=True)
54
+
55
+
56
+ if __DEBUG__:
57
+ print('t0_cells_n_vertices', t0_cells_n_vertices)
58
+ print('t1_cells_n_vertices', t1_cells_n_vertices)
59
+
60
+ # inner_time = timer()
61
+ best_pairs = {} # dict that contains pairs --> a good idea
62
+ best_pairs_score = {}
63
+
64
+ for key, value in t0_cells_n_vertices.items():
65
+ max_match = 0.
66
+ best_match = 0
67
+ neighs_at_t0 = t0_cells_n_vertices[key]
68
+ best_pair_size = 0
69
+
70
+ for key2, value2 in t1_cells_n_vertices.items():
71
+ neighs_at_t1 = t1_cells_n_vertices[key2]
72
+ common_neighs = set(neighs_at_t1).intersection(neighs_at_t0)
73
+
74
+ # if two are equal --> try to take the best --> find a way to do it smartly
75
+ if len(common_neighs) > 0 and len(
76
+ common_neighs) >= max_match: # if found twice --> find a way to select which cells to take
77
+ # max_match = len(common_neighs) / ((len(neighs_at_t1)+len(neighs_at_t0))/2)
78
+
79
+ conti = True
80
+ if len(common_neighs) == max_match:
81
+ if len(neighs_at_t1) == best_pair_size:
82
+ if key == key2:
83
+ # do favor identity as much as possible
84
+ conti = True
85
+ # find a trick or random
86
+ elif key2 in neighs_at_t0: # not sure this is very smart but give it a try NB recursion may fix bugs
87
+ conti = False
88
+ elif len(neighs_at_t1) > best_pair_size:
89
+ conti = False
90
+ # si les 2 font la meme taille --> pb à nouveau --> y a t'il une astuce...
91
+
92
+ if conti:
93
+ max_match = len(common_neighs) # / ((len(neighs_at_t1)+len(neighs_at_t0))/2)
94
+ best_match = key2
95
+ best_pair_size = len(neighs_at_t1)
96
+ if __DEBUG__:
97
+ if key == cell_to_check:
98
+ hex_list_0 = [format(x, 'x') for x in neighs_at_t0]
99
+ hex_list_1 = [format(x, 'x') for x in neighs_at_t1]
100
+ print('pairs for a test', neighs_at_t0, neighs_at_t1, len(common_neighs), max_match,
101
+ format(key, 'x'), format(key2, 'x'), key, key2, hex_list_0,
102
+ hex_list_1) # that works --> so indeed the best match is found but then cells are lost and swapped --> why is that --> I still miss a ctrl somewhere --> or a secondary swap that is less good somehow --> check that or a coding error!!
103
+ else:
104
+ if __DEBUG__:
105
+ if key == cell_to_check:
106
+ hex_list_0 = [format(x, 'x') for x in neighs_at_t0]
107
+ hex_list_1 = [format(x, 'x') for x in neighs_at_t1]
108
+ print('discarded pairs for a test', neighs_at_t0, neighs_at_t1, len(common_neighs),
109
+ max_match,
110
+ format(key, 'x'), format(key2, 'x'), key, key2, hex_list_0,
111
+ hex_list_1)
112
+ if __DEBUG__:
113
+ if key == cell_to_check:
114
+ print('final match', max_match, cell_to_check, best_match, format(cell_to_check, 'x'),
115
+ format(best_match, 'x'))
116
+ # or best --> compare to its current match and if better then take it otherwise ignore --> maybe
117
+
118
+ # in fact I take all here but is that really worth ??? --> maybe take one but only if the cell has just one neighbor ???? --> shall I start with 2 ??? that would dramatically reduce the nb of cells I guess --> think about it
119
+ if max_match >= 0.5: # take all cells that have at least half of their neighbs --> 40--> maybe ok
120
+
121
+ # compare neighbors of current match versus those in the previous
122
+ best_pairs[key] = best_match
123
+ best_pairs_score[key] = max_match # will be used later to avoid issues
124
+
125
+ # remove cells that did not change much
126
+ if MIN_CUT_OFF_SWAPPING is not None and MIN_CUT_OFF_SWAPPING>0:
127
+ pairs_to_ignore = [k for k, v in best_pairs_score.items() if v <= MIN_CUT_OFF_SWAPPING]
128
+ for ids in pairs_to_ignore:
129
+ del best_pairs[ids]
130
+
131
+ # finding duplicate values
132
+ # from dictionary using set
133
+ rev_dict = {}
134
+ for key, value in best_pairs.items():
135
+ rev_dict.setdefault(value, set()).add(key)
136
+
137
+ dupes_matches_to_be_removed = []
138
+ for key, value in rev_dict.items():
139
+
140
+ if __DEBUG__:
141
+ if key == cell_to_check:
142
+ print('MEGA THERE######')
143
+
144
+ if len(value) > 1:
145
+ best_match = 0
146
+ max_match = 0
147
+ # keep only best and remove all others from the table
148
+ for iii, val in enumerate(value):
149
+ if __DEBUG__:
150
+ if key == cell_to_check:
151
+ print(iii, val, best_pairs_score[val])
152
+
153
+ if best_pairs_score[val] == max_match:
154
+ if val == key:
155
+ max_match = best_pairs_score[val]
156
+ best_match = val
157
+ elif best_pairs_score[val] > max_match:
158
+ max_match = best_pairs_score[val]
159
+ best_match = val
160
+
161
+ if __DEBUG__:
162
+ if key == cell_to_check:
163
+ print(value, val, max_match)
164
+
165
+ value.remove(best_match)
166
+ # I do have a bug here but I don't get it in fact but ok for now
167
+ dupes_matches_to_be_removed.extend(
168
+ value) # not a good idea --> just need to remove it from the value not from everywhere... is that correct ???? remove it only if present again in duplicates in final stuff after cleaning
169
+
170
+ if __DEBUG__:
171
+ print('dupes_matches_to_be_removed', dupes_matches_to_be_removed,
172
+ [format(x, 'x') for x in dupes_matches_to_be_removed])
173
+
174
+ for key in dupes_matches_to_be_removed:
175
+ if key in best_pairs:
176
+ del best_pairs[key]
177
+
178
+ def swap(pairs_of_cells_to_swap,
179
+ tracked_cells_t1): # this is super slow and would benefit from recoding it in C --> this takes most of the time of the function
180
+ # inner_time = timer()
181
+ clone_tracks = np.array(tracked_cells_t1, copy=True)
182
+ # print('current time', timer() - inner_time)
183
+
184
+ for cell1, cell2 in pairs_of_cells_to_swap.items():
185
+ if cell1 == cell2: # remove identity cause it slows down a lot the process
186
+ continue
187
+
188
+ # if cell2 not in pairs_of_cells_to_swap.keys(): # very slow --> find a way to make that faster
189
+ clone_tracks[tracked_cells_t1 == cell1] = cell2
190
+ clone_tracks[tracked_cells_t1 == cell2] = cell1
191
+
192
+ if __DEBUG__:
193
+ print('converting', cell2, '--> ', cell1, format(cell2, 'x'), '-->', format(cell1, 'x'))
194
+
195
+ # find a way todo things and to not duplicate stuff
196
+
197
+ # pb --> this creates duplications but otherwise it does swap correct cells --> find a way to prevent this --> I need to order swaps and other stuff
198
+ # print('current time2', timer() - inner_time)
199
+ return clone_tracks
200
+
201
+ # inner_time = timer()
202
+ tracked_cells_t1 = swap(best_pairs,
203
+ tracked_cells_t1) # c'est ce truc qui est super lent --> plus de la moitié du temps --> try improve that
204
+
205
+ if __DEBUG__:
206
+ print('best_pairs', len(best_pairs), best_pairs) # maybe best ratio versus its own size
207
+ # TODO maybe save swapped cells --> only show those and blacken all the rest
208
+
209
+ return tracked_cells_t1
210
+
211
+
212
+ if __name__ == '__main__':
213
+
214
+ swap_fix_recursions = 3
215
+
216
+ # pb is because after translation the cell is also present in both --> skipped by my stuff --> need detect those cells and chose to get only one in fact
217
+ # pb is because after translation the cell is also present in both --> skipped by my stuff --> need detect those cells and chose to get only one in fact
218
+ # or first do the
219
+
220
+ start_all = timer()
221
+
222
+ # path = '/E/Sample_images/tracking_test/leg_uncropped' # quick test of the tracking algo
223
+ # path = '/E/Sample_images/tracking_test/leg_cropped' # quick test of the tracking algo
224
+ path = '/E/Sample_images/tracking_test/test_uncropped' # quick test of the tracking algo
225
+ # path = '/E/Sample_images/tracking_test/test_cropped' # quick test of the tracking algo
226
+
227
+ if not path.lower().endswith('.txt') and not path.lower().endswith('.lst'):
228
+ images_to_analyze = os.listdir(path)
229
+ images_to_analyze = [os.path.join(path, f) for f in images_to_analyze if
230
+ os.path.isfile(os.path.join(path, f))] # list only files and only if they end by tif
231
+ images_to_analyze = natsorted(images_to_analyze)
232
+ else:
233
+ images_to_analyze = loadlist(path)
234
+
235
+ # get current and next in fact and map next
236
+
237
+ # process first image directly then move on to next
238
+ # images_to_analyze = []
239
+
240
+ # for image 0 --> just randomly fill it with colors --> make sure no dupe and that's it
241
+
242
+ # TODO --> do the first
243
+
244
+ print(images_to_analyze)
245
+
246
+ for l in range(len(images_to_analyze) - 1):
247
+
248
+ start_loop = timer()
249
+
250
+ file_path_0 = images_to_analyze[l]
251
+ file_path_1 = images_to_analyze[l + 1]
252
+
253
+ print('files', file_path_1, file_path_0)
254
+
255
+ if not file_path_1.endswith('.tif') or not file_path_0.endswith('.tif'):
256
+ continue
257
+
258
+ # print('files', file_path_1, file_path_0)
259
+
260
+ filename0_without_ext = os.path.splitext(file_path_0)[0]
261
+ filename1_without_ext = os.path.splitext(file_path_1)[0]
262
+
263
+ tracked_cells_t0 = Img(os.path.join(filename0_without_ext, 'tracked_cells_resized.tif'))
264
+ tracked_cells_t1 = Img(os.path.join(filename1_without_ext, 'tracked_cells_resized.tif'))
265
+
266
+ vertices_t0 = detect_vertices_and_bonds(RGB_to_int24(tracked_cells_t0))
267
+ vertices_t0 = np.where(vertices_t0==255)
268
+ vertices_t1 = detect_vertices_and_bonds(RGB_to_int24(tracked_cells_t1))
269
+ vertices_t1 = np.where(vertices_t1 == 255)
270
+
271
+ # time is always increasing but why is that is some set not reset
272
+ for fix in range(swap_fix_recursions):
273
+ print('swapping fix recursion #', fix)
274
+ begin_loop = timer()
275
+ tracked_cells_t1 = _fix_swapping_internal(tracked_cells_t0, tracked_cells_t1, vertices_t0=vertices_t0, vertices_t1=vertices_t1)
276
+ # tracked_cells_t1 = _fix_swapping_internal(tracked_cells_t0, tracked_cells_t1)
277
+ print('current time', timer() - begin_loop)
278
+ # Img(tracked_cells_t1).save(os.path.join(filename0_without_ext, 'track_after_swap_correction'+ str(fix)+ '.tif'), mode='raw')
279
+
280
+ # example of swapped cells
281
+ print('total time', timer() - start_all) # --> 47 secs --> un peu lent qd meme
282
+
283
+ Img(int24_to_RGB(tracked_cells_t1)).save(os.path.join(filename0_without_ext, 'track_after_swap_correction.tif'), mode='raw')
284
+
285
+ # puis je la faire dynamiquement pr eviter les swaps en live
286
+ # --> void aussi si rentable ou pas
287
+
288
+ # TODO -->
289
+ # this now seems ok and could be done recursively...
290
+ # could also use neighbor cells to reassign most likely cell --> cell that matches most of the surrounding IDs
291
+
292
+ # pas mal --> see how many recursions might be needed to get the stuff to work --> might be time consuming to do plenty of recursion... --> maybe just update the map so that not everything needs be calculated again
293
+
294
+ # if a cell is unsure try reassign it by its neighborood...
295
+
296
+ # TODO --> reidentify cells by neighborhood --> simply use the vertices to detect the neighbors --> split the code
@@ -0,0 +1,238 @@
1
+ # zut le code est pas finalisé...
2
+
3
+ # USE a track object to store the click and its content...
4
+
5
+
6
+ # do a code to do smart track correction that avoids creating dupes
7
+ # checks for dupes, etc...
8
+ # can connect tracks and or swap tracks
9
+
10
+ # si les deux couleurs existent dans les deux images alors c'est du swap et faut swapper à partir de la dernière image sinon
11
+ # si chaque couleur existe dans une image et pas dans l'autre alors faire un connect track. Si le connect risque de dupliquer une cellule alors il faut faire un swap de la cellule puis connecter la track afin de ne pas dupliquer les cellules
12
+ # reflechir mais ça devrait etre bon
13
+ # existe t'il un moyen perenne de restaurer les tracks corrected ???? si oui lequel pr que le job ne soit pas perdu
14
+ # peut etre simplement faire un truc du genre update tracks et rajouter les cellules perdues de maniere aleatoire ? pas si simple en fait
15
+ # puis je aussi swapper plusieurs cellules en même temps ??? peut etre mais pas si simple
16
+ # comment faire un algo de swap intelligent
17
+
18
+
19
+ # need to store cell id or centroid or centre of area and the frame --> should be sufficient to do everything --> the most robust then detect the color of the other
20
+
21
+ # offer apply to all or just before or after the current image
22
+ # do the codes for that
23
+
24
+ # aussi faire un code pour reappliquer les tracks
25
+
26
+
27
+ from batoolset.img import RGB_to_int24, int24_to_RGB
28
+ from timeit import default_timer as timer
29
+ import matplotlib.pyplot as plt
30
+ from batoolset.img import Img
31
+ from batoolset.utils.loadlist import loadlist
32
+ from batoolset.files.tools import smart_name_parser
33
+ from batoolset.tools.logger import TA_logger # logging
34
+
35
+ logger = TA_logger()
36
+
37
+
38
+ def swap(img, id1, id2):
39
+ """
40
+ Swaps the pixel values of two IDs in the image.
41
+
42
+ Args:
43
+ img (numpy.ndarray): The input image.
44
+ id1: The first ID to swap.
45
+ id2: The second ID to swap.
46
+
47
+ Returns:
48
+ numpy.ndarray: The modified image with swapped IDs.
49
+
50
+ """
51
+ idcs1 = img == id1
52
+ idcs2 = img == id2
53
+ img[idcs1], img[idcs2] = id2, id1
54
+ return img
55
+
56
+
57
+ def assign_id(img, old_id, new_id):
58
+ """
59
+ Assigns a new ID to all pixels with the specified old ID in the image.
60
+
61
+ Args:
62
+ img (numpy.ndarray): The input image.
63
+ old_id: The ID to be replaced.
64
+ new_id: The new ID to assign.
65
+
66
+ Returns:
67
+ numpy.ndarray: The modified image with updated IDs.
68
+
69
+ """
70
+ img[img == old_id] = new_id
71
+ return img
72
+
73
+
74
+ def swap_tracks(lst, frame_of_first_connection, id_t0, id_t1, __preview_only=False):
75
+ """
76
+ Swaps the IDs of two tracks in a list of tracks.
77
+
78
+ Args:
79
+ lst (list): The list of tracks.
80
+ frame_of_first_connection: The frame at which the first connection occurs.
81
+ id_t0: The ID of the first track.
82
+ id_t1: The ID of the second track.
83
+ __preview_only (bool, optional): Whether to only preview the swap. Defaults to False.
84
+
85
+ """
86
+ correct_track(lst, frame_of_first_connection, id_t0, id_t1, correction_mode='swap', __preview_only=__preview_only)
87
+
88
+
89
+ def connect_tracks(lst, frame_of_first_connection, id_t0, id_t1, __preview_only=False):
90
+ """
91
+ Connects two tracks in a list of tracks.
92
+
93
+ Args:
94
+ lst (list): The list of tracks.
95
+ frame_of_first_connection: The frame at which the first connection occurs.
96
+ id_t0: The ID of the first track.
97
+ id_t1: The ID of the second track.
98
+ __preview_only (bool, optional): Whether to only preview the connection. Defaults to False.
99
+
100
+ """
101
+ correct_track(lst, frame_of_first_connection, id_t0, id_t1, correction_mode='connect', __preview_only=__preview_only)
102
+
103
+ def correct_track(lst, frame_of_first_connection, id_t0, id_t1, correction_mode='connect', __preview_only=False, early_stop=True):
104
+ """
105
+ Corrects a track in a list of tracks based on the specified correction mode.
106
+
107
+ Args:
108
+ lst (list): The list of tracks.
109
+ frame_of_first_connection: The frame at which the first connection occurs.
110
+ id_t0: The ID of the first track.
111
+ id_t1: The ID of the second track.
112
+ correction_mode (str, optional): The correction mode. Defaults to 'connect'.
113
+ __preview_only (bool, optional): Whether to only preview the correction. Defaults to False.
114
+ early_stop (bool, optional): Whether to stop the correction process early. Defaults to True.
115
+
116
+ """
117
+ if frame_of_first_connection >= len(lst):
118
+ logger.error('error wrong connection frame nb')
119
+ return
120
+
121
+ for l in range(frame_of_first_connection, len(lst)):
122
+ tracked_cell_path = smart_name_parser(lst[l], ordered_output='tracked_cells_resized.tif')
123
+ tracked_cells_t0 = RGB_to_int24(Img(tracked_cell_path))
124
+
125
+ cellpos_1 = id_t0 in tracked_cells_t0
126
+ cellpos_2 = id_t1 in tracked_cells_t0
127
+
128
+ if not cellpos_1 and not cellpos_2:
129
+ logger.info('cells not found --> ignoring track correction')
130
+ continue
131
+
132
+ if cellpos_1 and cellpos_2 and correction_mode == 'connect':
133
+ logger.info('IDs are both present in the same image, to avoid track ID duplication the desired change will be ignored, please ensure the selected cells are not simply swapped in the image')
134
+ break
135
+
136
+ if not cellpos_1 and cellpos_2 or not cellpos_2 and cellpos_1 and correction_mode == 'swap':
137
+ logger.info('missing cell at frame ' + str(l) + ' swapping ignored')
138
+ if early_stop:
139
+ logger.info('quitting track edition')
140
+ break
141
+ else:
142
+ continue
143
+
144
+ if cellpos_1 and cellpos_2 and correction_mode == 'swap':
145
+ logger.info('swapped cell ids: ' + str(id_t0) + ' and ' + str(id_t1))
146
+ tracked_cells_t0 = swap(tracked_cells_t0, id_t0, id_t1)
147
+ if __preview_only:
148
+ plt.imshow(int24_to_RGB(tracked_cells_t0))
149
+ plt.show()
150
+ else:
151
+ Img(int24_to_RGB(tracked_cells_t0), dimensions='hwc').save(tracked_cell_path, mode='raw')
152
+ else:
153
+ if cellpos_1:
154
+ logger.info('changed id ' + str(id_t0) + ' to ' + str(id_t1))
155
+ tracked_cells_t0 = assign_id(tracked_cells_t0, id_t0, id_t1)
156
+ elif cellpos_2:
157
+ logger.info('changed id ' + str(id_t1) + ' to ' + str(id_t0))
158
+ tracked_cells_t0 = assign_id(tracked_cells_t0, id_t1, id_t0)
159
+ if __preview_only:
160
+ plt.imshow(int24_to_RGB(tracked_cells_t0))
161
+ plt.show()
162
+ else:
163
+ Img(int24_to_RGB(tracked_cells_t0), dimensions='hwc').save(tracked_cell_path, mode='raw')
164
+
165
+ if __name__ == "__main__":
166
+ start_all = timer()
167
+
168
+ # path = '/E/Sample_images/tracking_test'
169
+ path = '/E/Sample_images/sample_images_PA/mini10_fake_swaps/liste.lst'
170
+ # path = '/E/Sample_images/test_tracking_with_shift' # quick test of the algo
171
+ #
172
+ # images_to_analyze = os.listdir(path)
173
+ # images_to_analyze = [os.path.join(path, f) for f in images_to_analyze if
174
+ # os.path.isfile(os.path.join(path, f))] # list only files and only if they end by tif
175
+ # images_to_analyze = natsorted(images_to_analyze)
176
+
177
+ images_to_analyze = loadlist(path)
178
+
179
+ # then do amrutha's code watershed imagendarray (2-D, 3-D, …) of integers--> cool --> will also work for 3D --> then need segmentation in 3D
180
+ #
181
+ #
182
+ # for l in range(len(images_to_analyze)):
183
+ # # in fact I don't even need to reopen the mask file because the track file should suffice ???
184
+ #
185
+ # file_path_0 = images_to_analyze[l]
186
+ # if not file_path_0.endswith('.tif'):
187
+ # continue
188
+ #
189
+ #
190
+ # # print('files', file_path_1, file_path_0)
191
+ #
192
+ # filename0_without_ext = os.path.splitext(file_path_0)[0]
193
+ #
194
+ # tracked_cells_t0 = Img(os.path.join(filename0_without_ext, 'tracked_cells_resized.tif')) # 455:455 + 128, 435:435 + 128 #455:455 + 256, 435:435 + 290 #768:1024,0:256 #0:128, 0:128 #500:500+128, 500:500+128
195
+ #
196
+ #
197
+ #
198
+ #
199
+ # # TODO always get the mask instead of any other files in order to have the minimal nb of files available
200
+ # # mask_t0 = Img(os.path.join(filename0_without_ext, 'handCorrection.tif'))[
201
+ # # ..., 0] # 455:455 + 128, 435:435 + 128 #455:455 + 256, 435:435 + 290 #768:1024,0:256 #0:128, 0:128 #500:500+128, 500:500+128
202
+ #
203
+ #
204
+ # # labels_t0 = measure.label(mask_t0, connectivity=1,
205
+ # # background=255) # FOUR_CONNECTED # use bg = 255 to avoid having to invert the image --> a gain of time I think and could be applied more generally
206
+ #
207
+ # height = tracked_cells_t0.shape[0]
208
+ # width = tracked_cells_t0.shape[1]
209
+ #
210
+ #
211
+ # # could maybe store the ID of duplicate cell as a reseed --> can be a pb with extruding cells but should most often be ok though
212
+ # #
213
+ #
214
+ # labels_tracking_t0 = measure.label(tracked_cells_t0[..., 0], connectivity=1, background=255)
215
+ # rps_t0 = regionprops(labels_tracking_t0)
216
+ #
217
+ #
218
+ # centroids_t0 = []
219
+ # for region in rps_t0:
220
+ # # take regions with large enough areas
221
+ # centroids_t0.append(region.centroid)
222
+
223
+ # try create a dupe to see if it fails -> it will and I should really avoid that
224
+
225
+ # swap_tracks(images_to_analyze, 1, 0x0ef5dd, 0xa28ad6, __preview_only=True) # both cells exist
226
+
227
+
228
+ swap_tracks(images_to_analyze, 0, 0x0ef5dd, 0xFAFAFF, __preview_only=True) # one cells exist
229
+
230
+ # ça marche mais reflechir que je ne puisse pas creer des pbs avec ça
231
+ # connect_tracks(images_to_analyze, 0, 0xffdd5d, 0x3b2cdb, __preview_only=True) # both cells exist
232
+ # connect_tracks(images_to_analyze, 0, 0xffdd5d, 0x0ef5dd, __preview_only=True) # create a duplicate cell # --> breaking
233
+
234
+ # codes are the same --> just keep one and
235
+
236
+ # tt a l'air de marcher --> maybe add a flag for recursive --> in fact not...
237
+
238
+ print('total time', timer() - start_all)