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.
- batoolset/GUI/__init__.py +0 -0
- batoolset/GUI/list_gui.py +513 -0
- batoolset/GUI/multi_image_display.py +989 -0
- batoolset/GUI/open.py +325 -0
- batoolset/GUI/overlay_hints.py +45 -0
- batoolset/GUI/paint2.py +1345 -0
- batoolset/GUI/scrollablepaint.py +1152 -0
- batoolset/IJ_ROIs/__init__.py +0 -0
- batoolset/IJ_ROIs/tools.py +40 -0
- batoolset/PATH/__init__.py +0 -0
- batoolset/PATH/tools.py +79 -0
- batoolset/SQLite_tools/__init__.py +0 -0
- batoolset/SQLite_tools/tools.py +840 -0
- batoolset/__init__.py +0 -0
- batoolset/binary/__init__.py +0 -0
- batoolset/binary/tools.py +123 -0
- batoolset/binarytools/__init__.py +0 -0
- batoolset/binarytools/cell_center_detector.py +290 -0
- batoolset/clipboard_tools/__init__.py +0 -0
- batoolset/clipboard_tools/tools.py +7 -0
- batoolset/colors/__init__.py +0 -0
- batoolset/colors/colorgen.py +193 -0
- batoolset/colors/tools.py +22 -0
- batoolset/command_line/__init__.py +0 -0
- batoolset/command_line/tools.py +18 -0
- batoolset/dialogs/__init__.py +0 -0
- batoolset/dialogs/opensave.py +228 -0
- batoolset/dicts/__init__.py +0 -0
- batoolset/dicts/tools.py +299 -0
- batoolset/dims/__init__.py +0 -0
- batoolset/dims/tools.py +83 -0
- batoolset/draw/__init__.py +0 -0
- batoolset/draw/shapes/Position.py +183 -0
- batoolset/draw/shapes/__init__.py +0 -0
- batoolset/draw/shapes/automated_alignment_drawing_refactored.py +346 -0
- batoolset/draw/shapes/circle2d.py +99 -0
- batoolset/draw/shapes/compute_optimal_size_all_merged.py +317 -0
- batoolset/draw/shapes/ellipse2d.py +336 -0
- batoolset/draw/shapes/freehand2d.py +235 -0
- batoolset/draw/shapes/group.py +1830 -0
- batoolset/draw/shapes/image2d.py +2469 -0
- batoolset/draw/shapes/line2d.py +575 -0
- batoolset/draw/shapes/point2d.py +257 -0
- batoolset/draw/shapes/polygon2d.py +357 -0
- batoolset/draw/shapes/polyline2d.py +235 -0
- batoolset/draw/shapes/rect2d.py +388 -0
- batoolset/draw/shapes/rectangle2d.py +601 -0
- batoolset/draw/shapes/scalebar.py +304 -0
- batoolset/draw/shapes/serializablefont.py +126 -0
- batoolset/draw/shapes/serializableguiparams.py +10 -0
- batoolset/draw/shapes/square2d.py +103 -0
- batoolset/draw/shapes/textorientation.py +45 -0
- batoolset/draw/shapes/tools.py +467 -0
- batoolset/draw/shapes/transform2D.py +350 -0
- batoolset/draw/shapes/txt2d.py +550 -0
- batoolset/draw/widgets/__init__.py +0 -0
- batoolset/draw/widgets/col.py +343 -0
- batoolset/draw/widgets/image_group.py +248 -0
- batoolset/draw/widgets/image_organizer.py +96 -0
- batoolset/draw/widgets/paint.py +184 -0
- batoolset/draw/widgets/vectorial.py +257 -0
- batoolset/figure/__init__.py +0 -0
- batoolset/figure/alignment.py +814 -0
- batoolset/figure/column.py +1117 -0
- batoolset/figure/fig_tools.py +285 -0
- batoolset/figure/minimal_debugger_scale_etc.py +139 -0
- batoolset/figure/panel.py +697 -0
- batoolset/figure/row.py +852 -0
- batoolset/figure/user_selection.py +287 -0
- batoolset/files/__init__.py +0 -0
- batoolset/files/tools.py +1321 -0
- batoolset/images/__init__.py +0 -0
- batoolset/images/tools.py +81 -0
- batoolset/img.py +5216 -0
- batoolset/linux/__init__.py +0 -0
- batoolset/linux/tools.py +72 -0
- batoolset/lists/__init__.py +0 -0
- batoolset/lists/tools.py +436 -0
- batoolset/luts/__init__.py +0 -0
- batoolset/luts/lut_minimal_test.py +1207 -0
- batoolset/matplolib/__init__.py +0 -0
- batoolset/matplolib/tools.py +44 -0
- batoolset/np_tools/__init__.py +0 -0
- batoolset/np_tools/tools.py +252 -0
- batoolset/np_tools/trash.py +15 -0
- batoolset/pandas_tools/__init__.py +0 -0
- batoolset/pandas_tools/tools.py +880 -0
- batoolset/partials/__init__.py +0 -0
- batoolset/partials/tools.py +83 -0
- batoolset/plot_tools/__init__.py +0 -0
- batoolset/plot_tools/tools.py +70 -0
- batoolset/pyqt/__init__.py +0 -0
- batoolset/pyqt/tools.py +691 -0
- batoolset/re_tools/__init__.py +0 -0
- batoolset/re_tools/tools.py +35 -0
- batoolset/serialization/__init__.py +0 -0
- batoolset/serialization/tools.py +1192 -0
- batoolset/sets/__init__.py +0 -0
- batoolset/sets/tools.py +109 -0
- batoolset/settings/__init__.py +0 -0
- batoolset/settings/global_settings.py +134 -0
- batoolset/strings/__init__.py +0 -0
- batoolset/strings/tools.py +225 -0
- batoolset/ta/__init__.py +0 -0
- batoolset/ta/database/__init__.py +0 -0
- batoolset/ta/database/advanced_sql_plotter.py +1170 -0
- batoolset/ta/database/my_qt_table.py +79 -0
- batoolset/ta/database/preview_and_edit_sqlite_db.py +405 -0
- batoolset/ta/database/sql.py +2141 -0
- batoolset/ta/database/sql_column_name_dialog.py +122 -0
- batoolset/ta/measurements/TAmeasures.py +2226 -0
- batoolset/ta/measurements/__init__.py +0 -0
- batoolset/ta/measurements/measurements3D/__init__.py +0 -0
- batoolset/ta/measurements/measurements3D/get_point_on_surface_if_centroid_is_bad.py +175 -0
- batoolset/ta/measurements/measurements3D/height_map_to_projection.py +82 -0
- batoolset/ta/measurements/measurements3D/measures3D.py +208 -0
- batoolset/ta/measurements/measurements3D/triangulate.py +331 -0
- batoolset/ta/measurements/nematic.py +562 -0
- batoolset/ta/measurements/nematic_measurements.py +107 -0
- batoolset/ta/segmentation/__init__.py +0 -0
- batoolset/ta/segmentation/neo_wshed.py +314 -0
- batoolset/ta/selections/__init__.py +0 -0
- batoolset/ta/selections/selection.py +329 -0
- batoolset/ta/tracking/__init__.py +0 -0
- batoolset/ta/tracking/last_tracking_based_on_matching_or_on_translation_from_mermaid_warp.py +653 -0
- batoolset/ta/tracking/local_to_track_correspondance.py +138 -0
- batoolset/ta/tracking/rapid_detection_of_vertices.py +517 -0
- batoolset/ta/tracking/registration.py +108 -0
- batoolset/ta/tracking/swap_detector_and_fixer.py +296 -0
- batoolset/ta/tracking/track_correction.py +238 -0
- batoolset/ta/tracking/tracking_error_detector_and_fixer.py +3099 -0
- batoolset/ta/tracking/tracking_yet_another_approach_pyramidal_registration_n_neo_swapping_correction.py +725 -0
- batoolset/ta/utils/TA_tools.py +43 -0
- batoolset/ta/utils/__init__.py +0 -0
- batoolset/ta/utils/rps_tools.py +1783 -0
- batoolset/tests/__init__.py +0 -0
- batoolset/tests/rps_tools.py +51 -0
- batoolset/tests/tools.py +10 -0
- batoolset/tools/__init__.py +0 -0
- batoolset/tools/early_stopper_class.py +9 -0
- batoolset/tools/guided_data_recoverer.py +110 -0
- batoolset/tools/logger.py +100 -0
- batoolset/tools/mytimer.py +31 -0
- batoolset/tools/prompt_color.py +160 -0
- batoolset/tools/qthandler.py +118 -0
- batoolset/uitools/__init__.py +0 -0
- batoolset/uitools/blinker.py +56 -0
- batoolset/unsafe/__init__.py +0 -0
- batoolset/unsafe/tools.py +115 -0
- batoolset/utils/__init__.py +0 -0
- batoolset/utils/commontools.py +158 -0
- batoolset/utils/loadlist.py +728 -0
- batoolset/version.py +10 -0
- batoolset/xml/__init__.py +0 -0
- batoolset/xml/tools.py +65 -0
- batoolset/zip_tools/__init__.py +0 -0
- batoolset/zip_tools/tools.py +76 -0
- batoolset-0.1.0.dist-info/LICENSE +24 -0
- batoolset-0.1.0.dist-info/METADATA +45 -0
- batoolset-0.1.0.dist-info/RECORD +162 -0
- batoolset-0.1.0.dist-info/WHEEL +5 -0
- batoolset-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,1783 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
from skimage.data._binary_blobs import binary_blobs
|
|
3
|
+
from sklearn.neighbors import NearestNeighbors
|
|
4
|
+
from skimage.draw import line, rectangle
|
|
5
|
+
from skimage.measure import label, regionprops
|
|
6
|
+
from scipy.spatial.distance import cdist
|
|
7
|
+
import collections
|
|
8
|
+
import matplotlib.pyplot as plt
|
|
9
|
+
from skimage.measure._regionprops import RegionProperties
|
|
10
|
+
import math
|
|
11
|
+
from timeit import default_timer as timer
|
|
12
|
+
from sklearn.metrics import silhouette_score
|
|
13
|
+
from sklearn.cluster import KMeans
|
|
14
|
+
from numba import jit, njit
|
|
15
|
+
|
|
16
|
+
__DEBUG__ = False # for pavlidis code -> stores and prints decisions and directions
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
pavlidis_front_up = [[-1, -1], # px up left
|
|
20
|
+
[-1, 0], # px up
|
|
21
|
+
[-1, 1]] # px up right
|
|
22
|
+
|
|
23
|
+
pavlidis_front_left = [[1, -1], # px lower left
|
|
24
|
+
[0, -1], # left
|
|
25
|
+
[-1, -1]] # upper left
|
|
26
|
+
|
|
27
|
+
pavlidis_front_bottom = [[1, 1], # px lower right
|
|
28
|
+
[1, 0], # px bottom center
|
|
29
|
+
[1, -1]] # px lower left
|
|
30
|
+
|
|
31
|
+
pavlidis_front_right = [[-1, 1], # px upper right
|
|
32
|
+
[0, 1], # right
|
|
33
|
+
[1, 1]] # px lower right
|
|
34
|
+
|
|
35
|
+
pavlidis_lwr_pixels_up =[[1, -1], # px lower left
|
|
36
|
+
[0, -1], # left
|
|
37
|
+
[1, 1]] # px lower right
|
|
38
|
+
|
|
39
|
+
pavlidis_lwr_pixels_0 =[[1, -1], # px lower left
|
|
40
|
+
[0, -1], # left
|
|
41
|
+
[1, 1]] # px lower right
|
|
42
|
+
|
|
43
|
+
pavlidis_lwr_pixels_90 =[[-1, -1], # px upper left
|
|
44
|
+
[-1, 0], # up
|
|
45
|
+
[1, -1]] # px lower left
|
|
46
|
+
|
|
47
|
+
pavlidis_lwr_pixels_180 =[[-1, 1], # px upper right
|
|
48
|
+
[-1, -1], # upper left
|
|
49
|
+
[0, 1]] # px right
|
|
50
|
+
|
|
51
|
+
pavlidis_lwr_pixels_270 =[[-1, 1], # px upper right
|
|
52
|
+
[1, 0], # bottom
|
|
53
|
+
[1, 1]] # px lower right
|
|
54
|
+
|
|
55
|
+
starting_pavlidis = [pavlidis_lwr_pixels_0, pavlidis_lwr_pixels_90, pavlidis_lwr_pixels_180, pavlidis_lwr_pixels_270]
|
|
56
|
+
|
|
57
|
+
orientation_pavlidis = [pavlidis_front_up, pavlidis_front_right, pavlidis_front_bottom, pavlidis_front_left]
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def __check_pixel_pavlidis(img, y, x, color_of_interest=None):
|
|
61
|
+
"""
|
|
62
|
+
Check if a pixel in the image matches the specified color of interest.
|
|
63
|
+
|
|
64
|
+
Args:
|
|
65
|
+
img (np.ndarray or list): Image or region properties.
|
|
66
|
+
y (int): Y-coordinate of the pixel.
|
|
67
|
+
x (int): X-coordinate of the pixel.
|
|
68
|
+
color_of_interest: Color value to compare against.
|
|
69
|
+
|
|
70
|
+
Returns:
|
|
71
|
+
bool: True if the pixel matches the color of interest, False otherwise.
|
|
72
|
+
"""
|
|
73
|
+
if isinstance(img, np.ndarray):
|
|
74
|
+
return img[y, x] == color_of_interest
|
|
75
|
+
if isinstance(img, list):
|
|
76
|
+
return [y, x] in img
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def get_feret_from_region(region):
|
|
80
|
+
"""
|
|
81
|
+
Calculate the Feret diameter (major axis) of a region.
|
|
82
|
+
|
|
83
|
+
Args:
|
|
84
|
+
region: Region properties.
|
|
85
|
+
|
|
86
|
+
Returns:
|
|
87
|
+
tuple: Coordinates of the two endpoints of the major axis.
|
|
88
|
+
"""
|
|
89
|
+
major_axis_length = region.major_axis_length
|
|
90
|
+
orientation = region.orientation
|
|
91
|
+
centroid = region.centroid
|
|
92
|
+
|
|
93
|
+
x0 = centroid[1] - np.sin(orientation) * major_axis_length / 2
|
|
94
|
+
y0 = centroid[0] - np.cos(orientation) * major_axis_length / 2
|
|
95
|
+
x1 = centroid[1] + np.sin(orientation) * major_axis_length / 2
|
|
96
|
+
y1 = centroid[0] + np.cos(orientation) * major_axis_length / 2
|
|
97
|
+
|
|
98
|
+
return (y0,x0),(y1,x1)
|
|
99
|
+
|
|
100
|
+
@njit
|
|
101
|
+
def pavlidis2(img, start_y=None, start_x=None, closed_contour=False, color_of_interest=None,
|
|
102
|
+
auto_detect_extreme_points_for_region_upon_missing_start_coords=True):
|
|
103
|
+
"""
|
|
104
|
+
Traces a contour in an image using the Pavlidis algorithm.
|
|
105
|
+
|
|
106
|
+
Args:
|
|
107
|
+
img (ndarray): The input image.
|
|
108
|
+
start_y (int): The starting y-coordinate for the contour tracing. Default is None.
|
|
109
|
+
start_x (int): The starting x-coordinate for the contour tracing. Default is None.
|
|
110
|
+
closed_contour (bool): Flag indicating whether the contour is closed. Default is False.
|
|
111
|
+
color_of_interest: The color value of interest for contour tracing. Default is None.
|
|
112
|
+
auto_detect_extreme_points_for_region_upon_missing_start_coords (bool): Flag indicating whether
|
|
113
|
+
to automatically detect extreme points for the region if start coordinates are missing.
|
|
114
|
+
Default is True.
|
|
115
|
+
|
|
116
|
+
Returns:
|
|
117
|
+
list: A list of (y, x) coordinates representing the traced contour.
|
|
118
|
+
|
|
119
|
+
# Examples:
|
|
120
|
+
# >>> img = np.array([[0, 0, 0, 0, 0],
|
|
121
|
+
# ... [0, 1, 1, 1, 0],
|
|
122
|
+
# ... [0, 1, 0, 1, 0],
|
|
123
|
+
# ... [0, 1, 1, 1, 0],
|
|
124
|
+
# ... [0, 0, 0, 0, 0]])
|
|
125
|
+
# >>> contour = pavlidis2(img, start_y=2, start_x=1, closed_contour=True)
|
|
126
|
+
# >>> print(contour)
|
|
127
|
+
# [(2, 1), (1, 2), (1, 3), (2, 4), (3, 3), (3, 2)]
|
|
128
|
+
#
|
|
129
|
+
# >>> img = np.array([[0, 0, 0, 0, 0],
|
|
130
|
+
# ... [0, 1, 1, 1, 0],
|
|
131
|
+
# ... [0, 1, 0, 1, 0],
|
|
132
|
+
# ... [0, 1, 1, 1, 0],
|
|
133
|
+
# ... [0, 0, 0, 0, 0]])
|
|
134
|
+
# >>> contour = pavlidis2(img, start_y=2, start_x=1, closed_contour=False)
|
|
135
|
+
# >>> print(contour)
|
|
136
|
+
# [(2, 1), (1, 2), (1, 3), (2, 4), (3, 3)]
|
|
137
|
+
"""
|
|
138
|
+
|
|
139
|
+
coords = []
|
|
140
|
+
|
|
141
|
+
if start_x >= img.shape[1] or start_y >= img.shape[0] or start_x < 0 or start_y < 0:
|
|
142
|
+
print('error coordinates outside the image')
|
|
143
|
+
return coords
|
|
144
|
+
|
|
145
|
+
if color_of_interest is None:
|
|
146
|
+
color_of_interest = img[start_y, start_x]
|
|
147
|
+
|
|
148
|
+
if img[start_y, start_x] != color_of_interest:
|
|
149
|
+
print('error starting coordinates outside the object')
|
|
150
|
+
return coords
|
|
151
|
+
|
|
152
|
+
cur_coord_x = start_x
|
|
153
|
+
cur_coord_y = start_y
|
|
154
|
+
coords.append((start_y, start_x))
|
|
155
|
+
|
|
156
|
+
look_direction = 0
|
|
157
|
+
counter = 0
|
|
158
|
+
no_success_counter = 0
|
|
159
|
+
nb_of_encounters_of_first_pixel = 0
|
|
160
|
+
|
|
161
|
+
while True:
|
|
162
|
+
counter += 1
|
|
163
|
+
success = False
|
|
164
|
+
no_success_counter = 0
|
|
165
|
+
|
|
166
|
+
if no_success_counter >= 3:
|
|
167
|
+
return coords
|
|
168
|
+
|
|
169
|
+
if nb_of_encounters_of_first_pixel >= 3:
|
|
170
|
+
return coords
|
|
171
|
+
|
|
172
|
+
if cur_coord_x == start_x and cur_coord_y == start_y:
|
|
173
|
+
nb_of_encounters_of_first_pixel += 1
|
|
174
|
+
|
|
175
|
+
print('look_direction', look_direction, counter)
|
|
176
|
+
|
|
177
|
+
for ppp in range(orientation_pavlidis[look_direction].shape[0]+1):
|
|
178
|
+
shift = orientation_pavlidis[look_direction][ppp]
|
|
179
|
+
print('shift', shift, coords)
|
|
180
|
+
|
|
181
|
+
coords_to_test_x = cur_coord_x + int(shift[1])
|
|
182
|
+
coords_to_test_y = cur_coord_y + int(shift[0])
|
|
183
|
+
|
|
184
|
+
print(coords_to_test_x, coords_to_test_y)
|
|
185
|
+
|
|
186
|
+
if True: # Avoiding infinite loop for non-closed contours
|
|
187
|
+
if (coords_to_test_y, coords_to_test_x) in coords:
|
|
188
|
+
return coords
|
|
189
|
+
|
|
190
|
+
if coords_to_test_y >= img.shape[0] or coords_to_test_x >= img.shape[1] or \
|
|
191
|
+
coords_to_test_y < 0 or coords_to_test_x < 0:
|
|
192
|
+
continue
|
|
193
|
+
|
|
194
|
+
if img[coords_to_test_y, coords_to_test_x] == color_of_interest:
|
|
195
|
+
cur_coord_y = coords_to_test_y
|
|
196
|
+
cur_coord_x = coords_to_test_x
|
|
197
|
+
|
|
198
|
+
if cur_coord_x == start_x and cur_coord_y == start_y:
|
|
199
|
+
return coords
|
|
200
|
+
coords.append((cur_coord_y, cur_coord_x))
|
|
201
|
+
|
|
202
|
+
if ppp == 0:
|
|
203
|
+
look_direction -= 1
|
|
204
|
+
if look_direction < 0:
|
|
205
|
+
look_direction = 3
|
|
206
|
+
break
|
|
207
|
+
|
|
208
|
+
if not success:
|
|
209
|
+
look_direction += 1
|
|
210
|
+
if look_direction > 3:
|
|
211
|
+
look_direction = 0
|
|
212
|
+
|
|
213
|
+
if no_success_counter >= 4:
|
|
214
|
+
return coords
|
|
215
|
+
no_success_counter += 1
|
|
216
|
+
|
|
217
|
+
return coords
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
def pavlidis(img, start_y=None, start_x=None, closed_contour=False, color_of_interest=None,
|
|
221
|
+
auto_detect_extreme_points_for_region_upon_missing_start_coords=True, starting_orientation=0, early_stop=None):
|
|
222
|
+
"""
|
|
223
|
+
Traces a contour in an image using the Pavlidis algorithm.
|
|
224
|
+
|
|
225
|
+
Args:
|
|
226
|
+
img: The input image.
|
|
227
|
+
start_y: The starting y-coordinate for the contour tracing. Default is None.
|
|
228
|
+
start_x: The starting x-coordinate for the contour tracing. Default is None.
|
|
229
|
+
closed_contour: Flag indicating whether the contour is closed. Default is False.
|
|
230
|
+
color_of_interest: The color value of interest for contour tracing. Default is None.
|
|
231
|
+
auto_detect_extreme_points_for_region_upon_missing_start_coords: Flag indicating whether
|
|
232
|
+
to automatically detect extreme points for the region if start coordinates are missing.
|
|
233
|
+
Default is True.
|
|
234
|
+
starting_orientation: The starting orientation for contour tracing. Default is 0.
|
|
235
|
+
early_stop: The number of pixels to trace before stopping. Default is None.
|
|
236
|
+
|
|
237
|
+
Returns:
|
|
238
|
+
list: A list of (y, x) coordinates representing the traced contour.
|
|
239
|
+
|
|
240
|
+
Examples:
|
|
241
|
+
>>> img = np.array([[0, 0, 0, 0, 0],
|
|
242
|
+
... [0, 1, 1, 1, 0],
|
|
243
|
+
... [0, 1, 0, 1, 0],
|
|
244
|
+
... [0, 1, 1, 1, 0],
|
|
245
|
+
... [0, 0, 0, 0, 0]])
|
|
246
|
+
>>> contour = pavlidis(img, start_y=1, start_x=1, closed_contour=True)
|
|
247
|
+
>>> print(contour)
|
|
248
|
+
[(1, 1), (1, 2), (1, 3), (2, 3), (3, 3), (3, 2), (3, 1), (2, 1)]
|
|
249
|
+
|
|
250
|
+
>>> img = np.array([[0, 0, 0, 0, 0],
|
|
251
|
+
... [0, 1, 1, 1, 0],
|
|
252
|
+
... [0, 1, 0, 1, 0],
|
|
253
|
+
... [0, 1, 1, 1, 0],
|
|
254
|
+
... [0, 0, 0, 0, 0]])
|
|
255
|
+
>>> contour = pavlidis(img, start_y=3, start_x=1, closed_contour=True)
|
|
256
|
+
>>> print(contour)
|
|
257
|
+
[(3, 1), (2, 1), (1, 1), (1, 2), (1, 3), (2, 3), (3, 3), (3, 2)]
|
|
258
|
+
"""
|
|
259
|
+
coords = []
|
|
260
|
+
|
|
261
|
+
if not isinstance(img, RegionProperties):
|
|
262
|
+
if start_x is not None and start_y is not None:
|
|
263
|
+
if start_x >= img.shape[1] or start_y >= img.shape[0] or start_x < 0 or start_y < 0:
|
|
264
|
+
print('error coordinates outside the image')
|
|
265
|
+
return coords
|
|
266
|
+
if color_of_interest is None:
|
|
267
|
+
color_of_interest = img[start_y, start_x]
|
|
268
|
+
if img[start_y, start_x] != color_of_interest:
|
|
269
|
+
print('error starting coordinates outside the object')
|
|
270
|
+
return coords
|
|
271
|
+
else:
|
|
272
|
+
feret_1, feret_2 = get_feret_from_points(extreme_points(img))
|
|
273
|
+
start_y = feret_1[0]
|
|
274
|
+
start_x = feret_1[1]
|
|
275
|
+
img = img.tolist()
|
|
276
|
+
else:
|
|
277
|
+
img = img.coords
|
|
278
|
+
if start_x is None or start_y is None:
|
|
279
|
+
if not auto_detect_extreme_points_for_region_upon_missing_start_coords:
|
|
280
|
+
start_y, start_x = img[0][0], img[0][1]
|
|
281
|
+
else:
|
|
282
|
+
feret_1, feret_2 = get_feret_from_points(extreme_points(img))
|
|
283
|
+
start_y = feret_1[0]
|
|
284
|
+
start_x = feret_1[1]
|
|
285
|
+
else:
|
|
286
|
+
if not __check_pixel_pavlidis(img.tolist(), start_y, start_x):
|
|
287
|
+
print('error coordinates outside the image')
|
|
288
|
+
return coords
|
|
289
|
+
img = img.tolist()
|
|
290
|
+
|
|
291
|
+
cur_coord_x = start_x
|
|
292
|
+
cur_coord_y = start_y
|
|
293
|
+
coords.append((start_y, start_x))
|
|
294
|
+
|
|
295
|
+
look_direction = 0
|
|
296
|
+
if starting_orientation != 0:
|
|
297
|
+
look_direction = starting_orientation
|
|
298
|
+
|
|
299
|
+
counter = 0
|
|
300
|
+
no_success_counter = 0
|
|
301
|
+
|
|
302
|
+
old_coords_x = -1
|
|
303
|
+
old_coords_y = -1
|
|
304
|
+
|
|
305
|
+
infinite_loop_counter = 0
|
|
306
|
+
|
|
307
|
+
limit = None
|
|
308
|
+
if isinstance(img, list):
|
|
309
|
+
limit = 10 * len(img)
|
|
310
|
+
|
|
311
|
+
while True:
|
|
312
|
+
counter += 1
|
|
313
|
+
success = False
|
|
314
|
+
no_success_counter = 0
|
|
315
|
+
|
|
316
|
+
if early_stop is not None and len(coords) >= early_stop:
|
|
317
|
+
return coords
|
|
318
|
+
|
|
319
|
+
if limit is not None:
|
|
320
|
+
if counter > limit:
|
|
321
|
+
print('error infinite loop Pavlidis --> breaking')
|
|
322
|
+
return coords
|
|
323
|
+
|
|
324
|
+
if infinite_loop_counter > 100:
|
|
325
|
+
return coords
|
|
326
|
+
|
|
327
|
+
if old_coords_y == cur_coord_y and old_coords_x == cur_coord_x:
|
|
328
|
+
infinite_loop_counter += 1
|
|
329
|
+
else:
|
|
330
|
+
old_coords_y = cur_coord_y
|
|
331
|
+
old_coords_x = cur_coord_x
|
|
332
|
+
infinite_loop_counter = 0
|
|
333
|
+
|
|
334
|
+
for ppp, shift in enumerate(orientation_pavlidis[look_direction]):
|
|
335
|
+
coords_to_test_x = cur_coord_x + shift[1]
|
|
336
|
+
coords_to_test_y = cur_coord_y + shift[0]
|
|
337
|
+
|
|
338
|
+
if counter > 1:
|
|
339
|
+
if coords_to_test_x == start_x and coords_to_test_y == start_y:
|
|
340
|
+
return coords
|
|
341
|
+
|
|
342
|
+
if not closed_contour:
|
|
343
|
+
if (coords_to_test_y, coords_to_test_x) in coords:
|
|
344
|
+
return coords
|
|
345
|
+
|
|
346
|
+
if isinstance(img, np.ndarray):
|
|
347
|
+
if coords_to_test_y >= img.shape[0] or coords_to_test_x >= img.shape[1] or \
|
|
348
|
+
coords_to_test_y < 0 or coords_to_test_x < 0:
|
|
349
|
+
continue
|
|
350
|
+
|
|
351
|
+
if __check_pixel_pavlidis(img, coords_to_test_y, coords_to_test_x, color_of_interest):
|
|
352
|
+
cur_coord_y = coords_to_test_y
|
|
353
|
+
cur_coord_x = coords_to_test_x
|
|
354
|
+
|
|
355
|
+
if cur_coord_x == start_x and cur_coord_y == start_y:
|
|
356
|
+
return coords
|
|
357
|
+
coords.append((cur_coord_y, cur_coord_x))
|
|
358
|
+
|
|
359
|
+
if ppp == 0:
|
|
360
|
+
look_direction -= 1
|
|
361
|
+
if look_direction < 0:
|
|
362
|
+
look_direction = 3
|
|
363
|
+
success = True
|
|
364
|
+
break
|
|
365
|
+
|
|
366
|
+
if not success:
|
|
367
|
+
look_direction += 1
|
|
368
|
+
if look_direction > 3:
|
|
369
|
+
look_direction = 0
|
|
370
|
+
if no_success_counter >= 4:
|
|
371
|
+
return coords
|
|
372
|
+
no_success_counter += 1
|
|
373
|
+
|
|
374
|
+
return coords
|
|
375
|
+
|
|
376
|
+
|
|
377
|
+
def __plot_pavlidis(img, coords, skip_plot=True):
|
|
378
|
+
"""
|
|
379
|
+
Plot the image or regions (regionprops) with corresponding Pavlidis coordinates.
|
|
380
|
+
|
|
381
|
+
Args:
|
|
382
|
+
img: Image or RegionProperties object.
|
|
383
|
+
coords: List of coordinates.
|
|
384
|
+
skip_plot: Boolean value indicating whether to skip plotting the image.
|
|
385
|
+
|
|
386
|
+
Returns:
|
|
387
|
+
None
|
|
388
|
+
"""
|
|
389
|
+
|
|
390
|
+
if isinstance(img, RegionProperties):
|
|
391
|
+
region = img
|
|
392
|
+
tmp_img = np.zeros((max(region.bbox[2], region.bbox[0]) + 1,
|
|
393
|
+
max(region.bbox[1], region.bbox[3]) + 1))
|
|
394
|
+
tmp_img[region.coords[:, 0], region.coords[:, 1]] = 255
|
|
395
|
+
img = tmp_img
|
|
396
|
+
|
|
397
|
+
output = np.zeros_like(img)
|
|
398
|
+
|
|
399
|
+
if contains_duplicate_coordinates(coords):
|
|
400
|
+
coords = remove_duplicate_coordinates(coords)
|
|
401
|
+
|
|
402
|
+
for val, (y, x) in enumerate(coords):
|
|
403
|
+
output[y, x] = val + 1
|
|
404
|
+
|
|
405
|
+
if not skip_plot:
|
|
406
|
+
plt.imshow(output)
|
|
407
|
+
plt.show()
|
|
408
|
+
|
|
409
|
+
output[output != 0] = 255
|
|
410
|
+
|
|
411
|
+
if not (output == img).all():
|
|
412
|
+
missed = np.copy(img)
|
|
413
|
+
missed[output != 0] = 128
|
|
414
|
+
if not skip_plot:
|
|
415
|
+
plt.imshow(missed)
|
|
416
|
+
plt.show()
|
|
417
|
+
else:
|
|
418
|
+
print('FULL MATCH')
|
|
419
|
+
|
|
420
|
+
|
|
421
|
+
def extreme_points(coords, return_array=True):
|
|
422
|
+
"""
|
|
423
|
+
Find the extreme points from a set of coordinates.
|
|
424
|
+
|
|
425
|
+
Args:
|
|
426
|
+
coords: List of coordinates.
|
|
427
|
+
return_array: Boolean value indicating whether to return the extreme points as a list or individual tuples.
|
|
428
|
+
|
|
429
|
+
Returns:
|
|
430
|
+
List of extreme points [(top), (bottom), (left), (right)] if return_array is True, else returns individual tuples.
|
|
431
|
+
"""
|
|
432
|
+
tmp_coords = coords
|
|
433
|
+
|
|
434
|
+
if isinstance(coords, tuple):
|
|
435
|
+
tmp_coords = np.asarray(list(zip(coords[0].tolist(), coords[1].tolist())))
|
|
436
|
+
|
|
437
|
+
top = tuple(tmp_coords[tmp_coords[..., 1].argmin()])
|
|
438
|
+
bottom = tuple(tmp_coords[tmp_coords[..., 1].argmax()])
|
|
439
|
+
left = tuple(tmp_coords[tmp_coords[..., 0].argmin()])
|
|
440
|
+
right = tuple(tmp_coords[tmp_coords[..., 0].argmax()])
|
|
441
|
+
|
|
442
|
+
if not return_array:
|
|
443
|
+
return top, bottom, left, right
|
|
444
|
+
else:
|
|
445
|
+
return [top, bottom, left, right]
|
|
446
|
+
|
|
447
|
+
# returns the most distant points in a set of points
|
|
448
|
+
# nb I found a bug in this code in some cases --> needs a fix --> see /home/aigouy/mon_prog/Python/epyseg_pkg/personal/wing/ferret_error.tif
|
|
449
|
+
# no clue why though
|
|
450
|
+
# the feret is wrong with values ((612, 457), (612, 458), (612, 459), (612, 460), (612, 461), (612, 462), (612, 463), (612, 464), (613, 441), (613, 442), (613, 443), (613, 444), (613, 445), (613, 446), (613, 447), (613, 448), (613, 449), (613, 450), (613, 451), (613, 452), (613, 453), (613, 454), (613, 455), (613, 456), (614, 426), (614, 427), (614, 428), (614, 429), (614, 430), (614, 431), (614, 432), (614, 433), (614, 434), (614, 435), (614, 436), (614, 437), (614, 438), (614, 439), (614, 440), (615, 411), (615, 412), (615, 413), (615, 414), (615, 415), (615, 416), (615, 417), (615, 418), (615, 419), (615, 420), (615, 421), (615, 422), (615, 423), (615, 424), (615, 425), (616, 395), (616, 396), (616, 397), (616, 398), (616, 399), (616, 400), (616, 401), (616, 402), (616, 403), (616, 404), (616, 405), (616, 406), (616, 407), (616, 408), (616, 409), (616, 410), (617, 380), (617, 381), (617, 382), (617, 383), (617, 384), (617, 385), (617, 386), (617, 387), (617, 388), (617, 389), (617, 390), (617, 391), (617, 392), (617, 393), (617, 394), (617, 499), (617, 500), (617, 501), (617, 502), (617, 503), (617, 504), (617, 505), (617, 506), (617, 507), (617, 508), (617, 509), (617, 510), (617, 511), (617, 512), (617, 513), (617, 514), (617, 515), (617, 516), (618, 372), (618, 373), (618, 374), (618, 375), (618, 376), (618, 377), (618, 378), (618, 379), (618, 473), (618, 474), (618, 475), (618, 476), (618, 477), (618, 478), (618, 479), (618, 480), (618, 481), (618, 482), (618, 483), (618, 484), (618, 485), (618, 486), (618, 487), (618, 488), (618, 489), (618, 490), (618, 491), (618, 492), (618, 493), (618, 494), (618, 495), (618, 496), (618, 497), (618, 498), (618, 517), (618, 518), (618, 519), (618, 520), (618, 521), (618, 522), (618, 523), (618, 524), (618, 525), (618, 526), (619, 374), (619, 375), (619, 447), (619, 448), (619, 449), (619, 450), (619, 451), (619, 452), (619, 453), (619, 454), (619, 455), (619, 456), (619, 457), (619, 458), (619, 459), (619, 460), (619, 461), (619, 462), (619, 463), (619, 464), (619, 465), (619, 466), (619, 467), (619, 468), (619, 469), (619, 470), (619, 471), (619, 472), (619, 527), (619, 528), (619, 529), (619, 530), (619, 531), (619, 532), (619, 533), (619, 534), (619, 535), (619, 536), (620, 376), (620, 377), (620, 378), (620, 421), (620, 422), (620, 423), (620, 424), (620, 425), (620, 426), (620, 427), (620, 428), (620, 429), (620, 430), (620, 431), (620, 432), (620, 433), (620, 434), (620, 435), (620, 436), (620, 437), (620, 438), (620, 439), (620, 440), (620, 441), (620, 442), (620, 443), (620, 444), (620, 445), (620, 446), (620, 537), (620, 538), (620, 539), (620, 540), (620, 541), (620, 542), (620, 543), (620, 544), (620, 545), (620, 546), (621, 379), (621, 380), (621, 395), (621, 396), (621, 397), (621, 398), (621, 399), (621, 400), (621, 401), (621, 402), (621, 403), (621, 404), (621, 405), (621, 406), (621, 407), (621, 408), (621, 409), (621, 410), (621, 411), (621, 412), (621, 413), (621, 414), (621, 415), (621, 416), (621, 417), (621, 418), (621, 419), (621, 420), (621, 547), (621, 548), (621, 549), (621, 550), (621, 551), (621, 552), (621, 553), (621, 554), (621, 555), (621, 556), (622, 381), (622, 382), (622, 383), (622, 384), (622, 385), (622, 386), (622, 387), (622, 388), (622, 389), (622, 390), (622, 391), (622, 392), (622, 393), (622, 394), (622, 557), (622, 558), (622, 559), (622, 560), (622, 561), (622, 562), (622, 563), (622, 564), (622, 565), (622, 566), (623, 567), (623, 568), (623, 569), (623, 570), (623, 571), (623, 572), (623, 573), (623, 574), (623, 575), (623, 576), (624, 577), (624, 578), (624, 579), (624, 580), (624, 581), (624, 582), (624, 583), (624, 584), (624, 585), (624, 586), (625, 587), (625, 588), (625, 589), (625, 590), (625, 591), (625, 592), (625, 593), (625, 594), (625, 595), (625, 596), (626, 597), (626, 598), (626, 599), (626, 600), (626, 601), (626, 602), (626, 603), (626, 604), (626, 605), (626, 606), (627, 607), (627, 608), (627, 609), (627, 610), (627, 611), (627, 612), (627, 613), (627, 614), (627, 615), (627, 616), (628, 617), (628, 618), (628, 619), (628, 620), (628, 621), (628, 622), (629, 623), (629, 624), (629, 625), (630, 626), (630, 627), (630, 628), (631, 629), (631, 630), (631, 631), (632, 632), (632, 633), (632, 634), (633, 635), (633, 636), (633, 637), (634, 638), (634, 639), (634, 640), (635, 641), (635, 642), (635, 643), (636, 644), (636, 645), (636, 646), (637, 647), (637, 648), (637, 649), (638, 650), (638, 651), (638, 652), (639, 653), (639, 654), (639, 655), (640, 656), (640, 657), (640, 658), (641, 659), (641, 660), (641, 661), (642, 662), (642, 663), (643, 664), (643, 665), (643, 666), (644, 667), (644, 668), (644, 669), (645, 670), (645, 671), (645, 672), (645, 819), (645, 820), (645, 821), (645, 822), (645, 823), (645, 824), (645, 825), (645, 826), (645, 827), (645, 828), (645, 829), (645, 830), (645, 831), (645, 832), (645, 833), (645, 834), (645, 835), (645, 836), (645, 837), (645, 838), (645, 839), (645, 840), (645, 841), (645, 842), (645, 843), (645, 844), (645, 845), (645, 846), (645, 847), (645, 848), (645, 849), (645, 850), (645, 851), (645, 852), (645, 853), (645, 854), (645, 855), (645, 856), (645, 857), (645, 858), (645, 859), (645, 860), (645, 861), (645, 862), (645, 863), (645, 864), (645, 865), (645, 866), (645, 867), (645, 868), (645, 869), (645, 870), (645, 871), (645, 872), (645, 873), (645, 874), (645, 875), (645, 876), (645, 877), (645, 878), (645, 879), (645, 880), (645, 881), (646, 673), (646, 674), (646, 675), (646, 750), (646, 751), (646, 752), (646, 753), (646, 754), (646, 755), (646, 756), (646, 757), (646, 758), (646, 759), (646, 760), (646, 761), (646, 762), (646, 763), (646, 764), (646, 765), (646, 766), (646, 767), (646, 768), (646, 769), (646, 770), (646, 771), (646, 772), (646, 773), (646, 774), (646, 775), (646, 776), (646, 777), (646, 778), (646, 779), (646, 780), (646, 781), (646, 782), (646, 783), (646, 784), (646, 785), (646, 786), (646, 787), (646, 788), (646, 789), (646, 790), (646, 791), (646, 792), (646, 793), (646, 794), (646, 795), (646, 796), (646, 797), (646, 798), (646, 799), (646, 800), (646, 801), (646, 802), (646, 803), (646, 804), (646, 805), (646, 806), (646, 807), (646, 808), (646, 809), (646, 810), (646, 811), (646, 812), (646, 813), (646, 814), (646, 815), (646, 816), (646, 817), (646, 818), (647, 676), (647, 677), (647, 678), (647, 745), (647, 746), (647, 747), (647, 748), (647, 749), (648, 679), (648, 680), (648, 681), (648, 740), (648, 741), (648, 742), (648, 743), (648, 744), (649, 682), (649, 683), (649, 684), (649, 736), (649, 737), (649, 738), (649, 739), (650, 685), (650, 686), (650, 687), (650, 731), (650, 732), (650, 733), (650, 734), (650, 735), (651, 688), (651, 689), (651, 690), (651, 726), (651, 727), (651, 728), (651, 729), (651, 730), (652, 691), (652, 692), (652, 693), (652, 721), (652, 722), (652, 723), (652, 724), (652, 725), (653, 694), (653, 695), (653, 696), (653, 716), (653, 717), (653, 718), (653, 719), (653, 720), (654, 697), (654, 698), (654, 699), (654, 712), (654, 713), (654, 714), (654, 715), (655, 700), (655, 701), (655, 702), (655, 707), (655, 708), (655, 709), (655, 710), (655, 711), (656, 703), (656, 704), (656, 705), (656, 706))
|
|
451
|
+
# BIG BUG THE SOLUTION MAY NOT BE UNIQUE --> NEED A FIX WHEN POSSIBLE IN THE CASE OF BONDS!!--> see example just above and the rps_tools test class
|
|
452
|
+
|
|
453
|
+
def get_feret_from_points(contour_or_extreme_points):
|
|
454
|
+
"""
|
|
455
|
+
Calculate the Feret diameter (maximum distance) between two points from a given set of contour or extreme points.
|
|
456
|
+
|
|
457
|
+
Args:
|
|
458
|
+
contour_or_extreme_points: List or array-like object containing contour or extreme points.
|
|
459
|
+
|
|
460
|
+
Returns:
|
|
461
|
+
Tuple of two points representing the Feret diameter.
|
|
462
|
+
|
|
463
|
+
# Examples:
|
|
464
|
+
# >>> contour_points = [(10, 20), (30, 40), (50, 60), (70, 80)]
|
|
465
|
+
# >>> extreme_points(contour_points, return_array=True)
|
|
466
|
+
#
|
|
467
|
+
# # Calculate Feret diameter from contour points
|
|
468
|
+
# >>> feret_contour = get_feret_from_points(contour_points)
|
|
469
|
+
# >>> print('Feret Diameter (Contour Points):', feret_contour)
|
|
470
|
+
#
|
|
471
|
+
# # Calculate Feret diameter from extreme points
|
|
472
|
+
# >>> feret_extreme = get_feret_from_points(extreme_points)
|
|
473
|
+
# >>> print('Feret Diameter (Extreme Points):', feret_extreme)
|
|
474
|
+
"""
|
|
475
|
+
C = cdist(contour_or_extreme_points, contour_or_extreme_points)
|
|
476
|
+
furthest_points = np.where(C == C.max())
|
|
477
|
+
feret_1 = contour_or_extreme_points[furthest_points[0][0]]
|
|
478
|
+
feret_2 = contour_or_extreme_points[furthest_points[0][1]]
|
|
479
|
+
return feret_1, feret_2
|
|
480
|
+
|
|
481
|
+
|
|
482
|
+
# it is a pixel sorting algo and a very smart alternative to the pavlidis algo for lines or perimeter (especially because it is insensitive to holes in the connectivity and insensitive to the choice of the start point for non closed shapes)
|
|
483
|
+
# THIS IS MUCH FASTER AND POWERFUL THAN PAVLIDIS --> RATHER USE THAT FOR CONTOURS AND FOR BONDS (JUST USE PAVLIDIS FOR FILLED SHAPES)!!!
|
|
484
|
+
# pb can have big jumps in it --> may need rotate the result array --> TODO URGENT --> DO THAT ESPECIALLY IF CONTOUR IS LONG
|
|
485
|
+
# compute dist to first or last or between last and first and detect closest and do roll of the array so that all is ok after that --> TODO
|
|
486
|
+
# RELY ON PAVLIDIS FOR NOW!!!
|
|
487
|
+
|
|
488
|
+
# def nearest_neighbor_ordering(coords, remove_dupes=False):
|
|
489
|
+
# """
|
|
490
|
+
# Sort a set of coordinates to form a continuous line using the nearest neighbor algorithm.
|
|
491
|
+
#
|
|
492
|
+
# Args:
|
|
493
|
+
# coords (array-like): List or array-like object containing the coordinates.
|
|
494
|
+
# remove_dupes (bool): Flag indicating whether to remove duplicate coordinates (default: False).
|
|
495
|
+
#
|
|
496
|
+
# Returns:
|
|
497
|
+
# Sorted coordinates forming a continuous line.
|
|
498
|
+
#
|
|
499
|
+
# # Examples:
|
|
500
|
+
# # >>> coords = [(0, 0), (1, 1), (2, 2), (3, 3)]
|
|
501
|
+
# # >>> ordered_coords = nearest_neighbor_ordering(coords)
|
|
502
|
+
# # >>> print('Ordered Coordinates:', ordered_coords)
|
|
503
|
+
# #
|
|
504
|
+
# # >>> coords = [(0, 0), (1, 1), (2, 2), (1, 1)]
|
|
505
|
+
# # >>> ordered_coords = nearest_neighbor_ordering(coords, remove_dupes=True)
|
|
506
|
+
# # >>> print('Ordered Coordinates (No Dupes):', ordered_coords)
|
|
507
|
+
# """
|
|
508
|
+
# import networkx as nx
|
|
509
|
+
# if not isinstance(coords, np.ndarray):
|
|
510
|
+
# coords = np.asarray(coords)
|
|
511
|
+
#
|
|
512
|
+
# clf = NearestNeighbors(n_neighbors=2).fit(coords)
|
|
513
|
+
# G = clf.kneighbors_graph()
|
|
514
|
+
# T = nx.from_scipy_sparse_matrix(G)
|
|
515
|
+
# order = list(nx.dfs_preorder_nodes(T, 0))
|
|
516
|
+
# coords = coords[order]
|
|
517
|
+
#
|
|
518
|
+
# if remove_dupes and contains_duplicate_coordinates(coords):
|
|
519
|
+
# no_dupes = remove_duplicate_coordinates(coords.tolist())
|
|
520
|
+
# coords = np.asarray(no_dupes)
|
|
521
|
+
#
|
|
522
|
+
# return coords
|
|
523
|
+
|
|
524
|
+
|
|
525
|
+
def find_duplicate_coordinates(coords):
|
|
526
|
+
"""
|
|
527
|
+
Find duplicate coordinates from a given list.
|
|
528
|
+
|
|
529
|
+
Args:
|
|
530
|
+
coords (array-like): List or array-like object containing the coordinates.
|
|
531
|
+
|
|
532
|
+
Returns:
|
|
533
|
+
List of duplicate coordinates.
|
|
534
|
+
"""
|
|
535
|
+
return [item for item, count in collections.Counter(coords).items() if count > 1]
|
|
536
|
+
|
|
537
|
+
|
|
538
|
+
def contains_duplicate_coordinates(coords):
|
|
539
|
+
"""
|
|
540
|
+
Check if a list contains duplicate coordinates.
|
|
541
|
+
|
|
542
|
+
Args:
|
|
543
|
+
coords (array-like): List or array-like object containing the coordinates.
|
|
544
|
+
|
|
545
|
+
Returns:
|
|
546
|
+
True if duplicate coordinates are found, False otherwise.
|
|
547
|
+
"""
|
|
548
|
+
if not isinstance(coords, list):
|
|
549
|
+
coords = coords.tolist()
|
|
550
|
+
try:
|
|
551
|
+
if len(set(coords)) != len(coords):
|
|
552
|
+
return True
|
|
553
|
+
except:
|
|
554
|
+
tmp = {tuple(coords) for coords in coords}
|
|
555
|
+
if len(tmp) != len(coords):
|
|
556
|
+
return True
|
|
557
|
+
return False
|
|
558
|
+
|
|
559
|
+
|
|
560
|
+
def remove_duplicate_coordinates(coords):
|
|
561
|
+
"""
|
|
562
|
+
Remove duplicate coordinates from a list.
|
|
563
|
+
|
|
564
|
+
Args:
|
|
565
|
+
coords (array-like): List or array-like object containing the coordinates.
|
|
566
|
+
|
|
567
|
+
Returns:
|
|
568
|
+
List of coordinates without duplicates.
|
|
569
|
+
"""
|
|
570
|
+
fixed_coords = []
|
|
571
|
+
[fixed_coords.append(item) for item in coords if item not in fixed_coords]
|
|
572
|
+
return fixed_coords
|
|
573
|
+
|
|
574
|
+
|
|
575
|
+
def dist2D(pt1, pt2):
|
|
576
|
+
"""
|
|
577
|
+
Calculate the 2D Euclidean distance between two points.
|
|
578
|
+
|
|
579
|
+
Args:
|
|
580
|
+
pt1 (tuple): First point (x1, y1).
|
|
581
|
+
pt2 (tuple): Second point (x2, y2).
|
|
582
|
+
|
|
583
|
+
Returns:
|
|
584
|
+
Euclidean distance between the two points.
|
|
585
|
+
"""
|
|
586
|
+
return math.sqrt((pt2[0] - pt1[0]) ** 2 + (pt2[1] - pt1[1]) ** 2)
|
|
587
|
+
|
|
588
|
+
|
|
589
|
+
def compute_perimeter(perimeter_pixels):
|
|
590
|
+
"""
|
|
591
|
+
Compute the perimeter length using a set of pixels.
|
|
592
|
+
|
|
593
|
+
Args:
|
|
594
|
+
perimeter_pixels (np.ndarray): Array of shape (N, 2 or 3) containing the perimeter pixels.
|
|
595
|
+
|
|
596
|
+
Returns:
|
|
597
|
+
Perimeter length.
|
|
598
|
+
"""
|
|
599
|
+
length = compute_distance_between_consecutive_points(perimeter_pixels).sum()
|
|
600
|
+
return length
|
|
601
|
+
|
|
602
|
+
def associate_rps_to_rps_using_centroid_distance(primary_rps, rps_to_be_associated, no_repick=False):
|
|
603
|
+
# MEGA TODO needs be greatly be improved but the idea is there and it's much faster than computing the overlap score
|
|
604
|
+
from personal.coccinelle_ladybug.HashableRegionProps import HashableRegionProps
|
|
605
|
+
from batoolset.ta.measurements.TAmeasures import distance_between_points
|
|
606
|
+
output={}
|
|
607
|
+
# detected_arena = []
|
|
608
|
+
for primary_region in primary_rps:
|
|
609
|
+
# primary_region = (HashableRegionProps) primary_region
|
|
610
|
+
output[ (HashableRegionProps)(primary_region)]=[]
|
|
611
|
+
for region in rps_to_be_associated:
|
|
612
|
+
# if compute_coords_overlap_between_0_and_1(region.coords, primary_region.coords)!=0:
|
|
613
|
+
# output[(HashableRegionProps)(primary_region)].append(region)
|
|
614
|
+
if distance_between_points(primary_region.centroid, region.centroid)<6:
|
|
615
|
+
# detected_arena.append(region)
|
|
616
|
+
output[(HashableRegionProps)(primary_region)].append(region)
|
|
617
|
+
if no_repick:
|
|
618
|
+
break
|
|
619
|
+
# if no_repick:
|
|
620
|
+
# rps_to_be_associated = [region for region in rps_to_be_associated if region not in detected_arena]
|
|
621
|
+
return output
|
|
622
|
+
|
|
623
|
+
def associate_rps_to_rps_tmp(primary_rps, rps_to_be_associated, no_repick=False):
|
|
624
|
+
# THIS CODE IS ALSO NOT GREAT BECAUSE I SHOULD BREAK WHEN I FIND OVERLAP IF NO REPICK
|
|
625
|
+
# in fact I should maximize the overlap to do this better
|
|
626
|
+
from personal.coccinelle_ladybug.HashableRegionProps import HashableRegionProps
|
|
627
|
+
from personal.geom.tools import compute_coords_overlap_between_0_and_1
|
|
628
|
+
output={}
|
|
629
|
+
# detected_arena = []
|
|
630
|
+
for primary_region in primary_rps:
|
|
631
|
+
# primary_region = (HashableRegionProps) primary_region
|
|
632
|
+
output[ (HashableRegionProps)(primary_region)]=[]
|
|
633
|
+
for region in rps_to_be_associated:
|
|
634
|
+
if compute_coords_overlap_between_0_and_1(region.coords, primary_region.coords)!=0: # REALLY NOT GREAT I SHOULD TAKE THE MOST OVERLAPPING REGION
|
|
635
|
+
output[(HashableRegionProps)(primary_region)].append(region)
|
|
636
|
+
if no_repick:
|
|
637
|
+
break
|
|
638
|
+
# if no_repick:
|
|
639
|
+
# detected_arena.append(region)
|
|
640
|
+
# if no_repick:
|
|
641
|
+
# rps_to_be_associated = [region for region in rps_to_be_associated if region not in detected_arena]
|
|
642
|
+
return output
|
|
643
|
+
|
|
644
|
+
|
|
645
|
+
def associate_rps_to_rps(primary_rps, rps_to_be_associated, no_repick=False): # THE COOL THING ABOUT THIS CODE IS THAT IT CAN ASSOCAITE SEVERAL SMALL RPS TO A BIG ONE --> WHICH IS USEFUL FOR ARENA MATCHING
|
|
646
|
+
# THIS CODE IS ALSO NOT GREAT BECAUSE I SHOULD BREAK WHEN I FIND OVERLAP IF NO REPICK
|
|
647
|
+
# in fact I should maximize the overlap to do this better
|
|
648
|
+
from personal.coccinelle_ladybug.HashableRegionProps import HashableRegionProps
|
|
649
|
+
from personal.geom.tools import compute_coords_overlap_between_0_and_1
|
|
650
|
+
output={}
|
|
651
|
+
detected_arena = []
|
|
652
|
+
for primary_region in primary_rps:
|
|
653
|
+
# primary_region = (HashableRegionProps) primary_region
|
|
654
|
+
output[ (HashableRegionProps)(primary_region)]=[]
|
|
655
|
+
for region in rps_to_be_associated:
|
|
656
|
+
if compute_coords_overlap_between_0_and_1(region.coords, primary_region.coords)!=0: # REALLY NOT GREAT I SHOULD TAKE THE MOST OVERLAPPING REGION
|
|
657
|
+
output[(HashableRegionProps)(primary_region)].append(region)
|
|
658
|
+
if no_repick:
|
|
659
|
+
detected_arena.append(region)
|
|
660
|
+
if no_repick:
|
|
661
|
+
rps_to_be_associated = [region for region in rps_to_be_associated if region not in detected_arena]
|
|
662
|
+
return output
|
|
663
|
+
|
|
664
|
+
|
|
665
|
+
|
|
666
|
+
def associate_rps_to_rps2(primary_rps, rps_to_be_associated, no_repick=False):
|
|
667
|
+
from personal.coccinelle_ladybug.HashableRegionProps import HashableRegionProps
|
|
668
|
+
from personal.geom.tools import compute_coords_overlap_between_0_and_1
|
|
669
|
+
output={}
|
|
670
|
+
detected_arena = []
|
|
671
|
+
for primary_region in primary_rps:
|
|
672
|
+
# primary_region = (HashableRegionProps) primary_region
|
|
673
|
+
output[ primary_region]=[]
|
|
674
|
+
for region in rps_to_be_associated:
|
|
675
|
+
if compute_coords_overlap_between_0_and_1(region.coords, primary_region)!=0:
|
|
676
|
+
output[primary_region].append(region)
|
|
677
|
+
if no_repick:
|
|
678
|
+
detected_arena.append(region)
|
|
679
|
+
if no_repick:
|
|
680
|
+
rps_to_be_associated = [region for region in rps_to_be_associated if region not in detected_arena]
|
|
681
|
+
return output
|
|
682
|
+
|
|
683
|
+
def sort_rps_by_feature_using_its_name(rps, feature_name, feature_element=None, return_index=False):
|
|
684
|
+
'''
|
|
685
|
+
sorts rps by feature (area, centroid, ...) and feature index (i.e first element/axis of centroid)
|
|
686
|
+
can return the sorted features or an index
|
|
687
|
+
'''
|
|
688
|
+
sorted_list = sorted(enumerate(rps), key=lambda p: getattr(p[1], feature_name) if feature_element is None else getattr(p[1], feature_name)[feature_element])
|
|
689
|
+
if return_index:
|
|
690
|
+
return sorted_list
|
|
691
|
+
else:
|
|
692
|
+
return [region for idx, region in sorted_list]
|
|
693
|
+
|
|
694
|
+
|
|
695
|
+
def get_combined_bbox(regions):
|
|
696
|
+
# Initialize variables for the combined bounding box
|
|
697
|
+
min_row = float('inf')
|
|
698
|
+
min_col = float('inf')
|
|
699
|
+
max_row = 0
|
|
700
|
+
max_col = 0
|
|
701
|
+
|
|
702
|
+
# Iterate over the regions to find the combined bounding box
|
|
703
|
+
for region in regions:
|
|
704
|
+
minr, minc, maxr, maxc = region.bbox
|
|
705
|
+
min_row = min(min_row, minr)
|
|
706
|
+
min_col = min(min_col, minc)
|
|
707
|
+
max_row = max(max_row, maxr)
|
|
708
|
+
max_col = max(max_col, maxc)
|
|
709
|
+
|
|
710
|
+
# Return the combined bounding box as a tuple
|
|
711
|
+
combined_bbox = (min_row, min_col, max_row, max_col)
|
|
712
|
+
return combined_bbox
|
|
713
|
+
|
|
714
|
+
def get_rps_features_by_name(rps, feature_name, feature_element=None):
|
|
715
|
+
"""
|
|
716
|
+
Returns a list of region property features with the specified name, optionally returning only a specific element
|
|
717
|
+
of tuple features.
|
|
718
|
+
|
|
719
|
+
Parameters
|
|
720
|
+
----------
|
|
721
|
+
rps : list of skimage.measure._regionprops._RegionProperties
|
|
722
|
+
A list of region properties objects obtained from skimage.measure.regionprops.
|
|
723
|
+
feature_name : str
|
|
724
|
+
The name of the feature to extract from each region properties object.
|
|
725
|
+
feature_element : int or str, optional
|
|
726
|
+
The index or key of the element to extract from tuple features. Defaults to None.
|
|
727
|
+
|
|
728
|
+
Returns
|
|
729
|
+
-------
|
|
730
|
+
list
|
|
731
|
+
A list of features with the specified name, optionally containing only the specified element of tuple features.
|
|
732
|
+
|
|
733
|
+
Examples
|
|
734
|
+
--------
|
|
735
|
+
>>> # Get region properties and feature values for each region
|
|
736
|
+
>>> props = get_sample_rps()
|
|
737
|
+
>>> print('area_values', get_rps_features_by_name(props, 'area'))
|
|
738
|
+
area_values [42, 36, 45, 4, 3, 6, 3, 43, 32, 76, 2, 2, 35, 3, 104, 2, 4, 1, 37, 4, 5, 1, 1, 6, 3, 230, 1, 2, 2, 48, 5, 6, 7, 128, 40, 2, 1, 1, 6, 7, 1, 1, 8, 7, 7, 223, 3, 2, 82, 1, 5, 55, 3, 184, 1, 2, 28, 1, 38, 1]
|
|
739
|
+
>>> print('centroid_x_values', get_rps_features_by_name(props, 'centroid', 0))
|
|
740
|
+
centroid_x_values [1.5714285714285714, 1.6111111111111112, 5.133333333333334, 3.5, 5.333333333333333, 7.833333333333333, 10.333333333333334, 15.093023255813954, 15.90625, 21.55263157894737, 18.5, 18.5, 23.914285714285715, 24.333333333333332, 31.375, 26.5, 32.75, 33.0, 37.0, 35.25, 36.0, 38.0, 40.0, 41.5, 48.333333333333336, 56.582608695652176, 48.0, 52.0, 57.5, 60.354166666666664, 60.2, 64.83333333333333, 64.71428571428571, 77.359375, 68.5, 67.0, 75.0, 75.0, 78.16666666666667, 82.28571428571429, 81.0, 82.0, 84.0, 85.71428571428571, 88.0, 103.24215246636771, 92.33333333333333, 92.0, 98.28048780487805, 96.0, 100.0, 105.0, 101.66666666666667, 112.59782608695652, 106.0, 109.5, 117.5, 117.0, 124.5, 123.0]
|
|
741
|
+
"""
|
|
742
|
+
|
|
743
|
+
# Get feature values for each region
|
|
744
|
+
features = [getattr(region, feature_name) for region in rps]
|
|
745
|
+
|
|
746
|
+
# If feature is a tuple, extract specified element
|
|
747
|
+
if feature_element is not None:
|
|
748
|
+
features = [feature[feature_element] for feature in features]
|
|
749
|
+
|
|
750
|
+
return features
|
|
751
|
+
|
|
752
|
+
|
|
753
|
+
def get_sample_rps():
|
|
754
|
+
"""
|
|
755
|
+
Returns region properties for connected regions in a binary blobs image.
|
|
756
|
+
|
|
757
|
+
Returns
|
|
758
|
+
-------
|
|
759
|
+
list of skimage.measure._regionprops._RegionProperties
|
|
760
|
+
A list of region properties objects obtained from skimage.measure.regionprops on a binary blobs image.
|
|
761
|
+
|
|
762
|
+
Examples
|
|
763
|
+
--------
|
|
764
|
+
>>> # Get region properties for connected regions in a binary blobs image
|
|
765
|
+
>>> print(len(get_sample_rps()))
|
|
766
|
+
60
|
|
767
|
+
"""
|
|
768
|
+
|
|
769
|
+
# Load binary blobs image with 3 blobs
|
|
770
|
+
image = binary_blobs(length=128, blob_size_fraction=0.1, n_dim=2, volume_fraction=0.1, seed=1)
|
|
771
|
+
|
|
772
|
+
# Label connected regions in image
|
|
773
|
+
label_image = label(image)
|
|
774
|
+
|
|
775
|
+
# Calculate regionprops for each labeled region
|
|
776
|
+
props = regionprops(label_image)
|
|
777
|
+
return props
|
|
778
|
+
|
|
779
|
+
def cluster_regionprops_by_feature(rps, feature_for_clustering, feature_element=None):
|
|
780
|
+
|
|
781
|
+
# sorted_list = sorted(enumerate(rps), key=lambda p: p[1].centroid[SORTING_AXIS])
|
|
782
|
+
sorted_list = sort_rps_by_feature_using_its_name(rps, feature_for_clustering, feature_element=feature_element, return_index=True)
|
|
783
|
+
# sorted_indices = [i for i, _ in sorted_list]
|
|
784
|
+
objects = [object for _, object in sorted_list]
|
|
785
|
+
|
|
786
|
+
# Sort the regionprops based on their centroids Y position
|
|
787
|
+
# objects = sorted(rps, key=lambda p: p.centroid[0])
|
|
788
|
+
# Sort the list and get a list of tuples containing the original index and element
|
|
789
|
+
|
|
790
|
+
# Extract the sorted indices from the list of tuples
|
|
791
|
+
# sorted_indices = [i for i, _ in sorted_list]
|
|
792
|
+
|
|
793
|
+
# indices_comparison = [elm.index()]
|
|
794
|
+
|
|
795
|
+
# X = np.array([[p.centroid[0]] for p in objects])
|
|
796
|
+
#
|
|
797
|
+
# print('X', X.shape)
|
|
798
|
+
|
|
799
|
+
# Determine the optimal number of clusters using the silhouette score
|
|
800
|
+
X = np.array(get_rps_features_by_name(objects, feature_for_clustering, feature_element=feature_element))
|
|
801
|
+
if len(X.shape) ==1:
|
|
802
|
+
X = X.reshape(-1, 1)
|
|
803
|
+
|
|
804
|
+
# best_k=-1
|
|
805
|
+
silhouette_scores = []
|
|
806
|
+
K = range(2, len(np.unique(X))) # max nb of clusters is actually the max nb of unique elements in X
|
|
807
|
+
|
|
808
|
+
|
|
809
|
+
|
|
810
|
+
# print('K',K)
|
|
811
|
+
for k in K:
|
|
812
|
+
kmeans = KMeans(n_clusters=k, random_state=0, n_init='auto').fit(X) # quick fix change in KMeans
|
|
813
|
+
labels = kmeans.labels_
|
|
814
|
+
# if len(labels)==X.shape[0]:
|
|
815
|
+
# best_k = 1
|
|
816
|
+
# else:
|
|
817
|
+
score = silhouette_score(X, labels)
|
|
818
|
+
silhouette_scores.append(score)
|
|
819
|
+
|
|
820
|
+
|
|
821
|
+
# if best_k==-1:
|
|
822
|
+
best_k = K[np.argmax(silhouette_scores)]
|
|
823
|
+
|
|
824
|
+
# print('best_k',best_k)
|
|
825
|
+
|
|
826
|
+
if best_k != 1:
|
|
827
|
+
# Cluster the rows based on the y-coordinates of the centroids
|
|
828
|
+
kmeans = KMeans(n_clusters=best_k, random_state=0,n_init='auto').fit(X)
|
|
829
|
+
labels = kmeans.labels_
|
|
830
|
+
else:
|
|
831
|
+
labels = [0 for _ in objects]
|
|
832
|
+
|
|
833
|
+
# print('labels', len(labels))
|
|
834
|
+
|
|
835
|
+
# Create a new list with the elements of `my_list` in the sorted order
|
|
836
|
+
# reordered_labels = [labels[i] for i in sorted_indices]
|
|
837
|
+
|
|
838
|
+
|
|
839
|
+
# maybe return labels with
|
|
840
|
+
# now group every ROI in each group --> TODO
|
|
841
|
+
groups_n_rps = {}
|
|
842
|
+
for iii,label in enumerate(labels):
|
|
843
|
+
if label in groups_n_rps:
|
|
844
|
+
regions = groups_n_rps[label]
|
|
845
|
+
else:
|
|
846
|
+
regions = []
|
|
847
|
+
regions.append(objects[iii])
|
|
848
|
+
groups_n_rps[label] = regions
|
|
849
|
+
|
|
850
|
+
return groups_n_rps
|
|
851
|
+
|
|
852
|
+
|
|
853
|
+
|
|
854
|
+
def compute_distance_between_consecutive_points(ordered_pixels):
|
|
855
|
+
"""
|
|
856
|
+
Compute the distance between consecutive points.
|
|
857
|
+
|
|
858
|
+
Args:
|
|
859
|
+
ordered_pixels (array-like): List or array-like object containing the ordered pixels.
|
|
860
|
+
|
|
861
|
+
Returns:
|
|
862
|
+
Array of distances between consecutive points.
|
|
863
|
+
"""
|
|
864
|
+
if isinstance(ordered_pixels, list):
|
|
865
|
+
ordered_pixels = np.asarray(ordered_pixels)
|
|
866
|
+
d = np.diff(ordered_pixels, axis=0)
|
|
867
|
+
consecutive_distances = np.sqrt((d ** 2).sum(axis=1))
|
|
868
|
+
return consecutive_distances
|
|
869
|
+
|
|
870
|
+
|
|
871
|
+
def is_distance_continuous(ordered_distances):
|
|
872
|
+
"""
|
|
873
|
+
Check if a set of ordered distances is continuous.
|
|
874
|
+
|
|
875
|
+
Args:
|
|
876
|
+
ordered_distances (array-like): List or array-like object containing the ordered distances.
|
|
877
|
+
|
|
878
|
+
Returns:
|
|
879
|
+
True if the distances are continuous, otherwise returns the maximum distance and its position.
|
|
880
|
+
"""
|
|
881
|
+
if isinstance(ordered_distances, list):
|
|
882
|
+
ordered_distances = np.asarray(ordered_distances)
|
|
883
|
+
if len(ordered_distances.shape) == 2:
|
|
884
|
+
ordered_distances = compute_distance_between_consecutive_points(ordered_distances)
|
|
885
|
+
max_dist_pos = np.argmax(ordered_distances)
|
|
886
|
+
max_dist = ordered_distances[max_dist_pos]
|
|
887
|
+
|
|
888
|
+
if max_dist <= math.sqrt(2):
|
|
889
|
+
return True
|
|
890
|
+
else:
|
|
891
|
+
return max_dist, max_dist_pos
|
|
892
|
+
|
|
893
|
+
def find_pavlidis_optimal_orientation(pixel_coordinates, start_coords=None):
|
|
894
|
+
"""
|
|
895
|
+
Find the optimal orientation for the Pavlidis algorithm based on the pixel coordinates.
|
|
896
|
+
|
|
897
|
+
Args:
|
|
898
|
+
pixel_coordinates (np.ndarray): Array of shape (N, 2) containing the pixel coordinates.
|
|
899
|
+
start_coords (tuple): Tuple containing the start coordinates (default: None).
|
|
900
|
+
|
|
901
|
+
Returns:
|
|
902
|
+
Optimal orientation value (0, 1, 2, or 3).
|
|
903
|
+
|
|
904
|
+
Notes:
|
|
905
|
+
The optimal orientation is chosen such that when standing on the start pixel, the left adjacent pixel is white.
|
|
906
|
+
#
|
|
907
|
+
# Examples:
|
|
908
|
+
# >>> pixel_coords = np.array([[0, 0], [1, 1], [2, 2], [3, 3]])
|
|
909
|
+
# >>> orientation = find_pavlidis_optimal_orientation(pixel_coords, start_coords=(1, 1))
|
|
910
|
+
# >>> print('Optimal Orientation:', orientation)
|
|
911
|
+
#
|
|
912
|
+
# >>> pixel_coords = np.array([[0, 0], [1, 1], [2, 2], [3, 3]])
|
|
913
|
+
# >>> orientation = find_pavlidis_optimal_orientation(pixel_coords)
|
|
914
|
+
# >>> print('Optimal Orientation:', orientation)
|
|
915
|
+
"""
|
|
916
|
+
pixel_coordinates = pixel_coordinates.tolist()
|
|
917
|
+
if start_coords is not None:
|
|
918
|
+
y = start_coords[0]
|
|
919
|
+
x = start_coords[1]
|
|
920
|
+
if [y - 1, x] in pixel_coordinates:
|
|
921
|
+
return 0
|
|
922
|
+
if [y, x + 1] in pixel_coordinates:
|
|
923
|
+
return 1
|
|
924
|
+
if [y + 1, x] in pixel_coordinates:
|
|
925
|
+
return 2
|
|
926
|
+
if [y, x - 1] in pixel_coordinates:
|
|
927
|
+
return 3
|
|
928
|
+
|
|
929
|
+
for y, x in pixel_coordinates:
|
|
930
|
+
if [y - 1, x] in pixel_coordinates:
|
|
931
|
+
return 0
|
|
932
|
+
if [y, x + 1] in pixel_coordinates:
|
|
933
|
+
return 1
|
|
934
|
+
if [y + 1, x] in pixel_coordinates:
|
|
935
|
+
return 2
|
|
936
|
+
if [y, x - 1] in pixel_coordinates:
|
|
937
|
+
return 3
|
|
938
|
+
return None
|
|
939
|
+
|
|
940
|
+
|
|
941
|
+
def find_pavlidis_optimal_start2(pixel_coordinates):
|
|
942
|
+
"""
|
|
943
|
+
Find the optimal start pixel for the Pavlidis algorithm based on the pixel coordinates.
|
|
944
|
+
|
|
945
|
+
Args:
|
|
946
|
+
pixel_coordinates (np.ndarray): Array of shape (N, 2) containing the pixel coordinates.
|
|
947
|
+
|
|
948
|
+
Returns:
|
|
949
|
+
Optimal start pixel coordinates.
|
|
950
|
+
|
|
951
|
+
Notes:
|
|
952
|
+
The optimal start pixel is chosen such that when initially standing on it, the left adjacent pixel is not black.
|
|
953
|
+
|
|
954
|
+
# Examples:
|
|
955
|
+
# >>> pixel_coords = np.array([[0, 0], [1, 1], [2, 2], [3, 3]])
|
|
956
|
+
# >>> start_coords = find_pavlidis_optimal_start2(pixel_coords)
|
|
957
|
+
# >>> print('Optimal Start Coordinates:', start_coords)
|
|
958
|
+
"""
|
|
959
|
+
pixel_coordinates = pixel_coordinates.tolist()
|
|
960
|
+
|
|
961
|
+
for y, x in pixel_coordinates:
|
|
962
|
+
if [y - 1, x] not in pixel_coordinates:
|
|
963
|
+
return y, x
|
|
964
|
+
return None
|
|
965
|
+
|
|
966
|
+
|
|
967
|
+
def find_pavlidis_optimal_start(pixel_coordinates):
|
|
968
|
+
"""
|
|
969
|
+
Find the optimal start pixel and orientation for the Pavlidis algorithm based on the pixel coordinates.
|
|
970
|
+
|
|
971
|
+
Args:
|
|
972
|
+
pixel_coordinates (np.ndarray): Array of shape (N, 2) containing the pixel coordinates.
|
|
973
|
+
|
|
974
|
+
Returns:
|
|
975
|
+
Optimal start pixel coordinates and orientation.
|
|
976
|
+
|
|
977
|
+
# Examples:
|
|
978
|
+
# >>> pixel_coords = np.array([[0, 0], [1, 1], [2, 2], [3, 3]])
|
|
979
|
+
# >>> start_coords, orientation = find_pavlidis_optimal_start(pixel_coords)
|
|
980
|
+
# >>> print('Optimal Start Coordinates:', start_coords)
|
|
981
|
+
# >>> print('Optimal Orientation:', orientation)
|
|
982
|
+
"""
|
|
983
|
+
px_coords = pixel_coordinates.tolist()
|
|
984
|
+
|
|
985
|
+
for y, x in px_coords:
|
|
986
|
+
for orientation, pavlidis_shift in enumerate(starting_pavlidis):
|
|
987
|
+
counter_white = 0
|
|
988
|
+
for shift in pavlidis_shift:
|
|
989
|
+
if (y + shift[0], x + shift[1]) in px_coords:
|
|
990
|
+
counter_white += 1
|
|
991
|
+
|
|
992
|
+
if counter_white != 0:
|
|
993
|
+
print('counter_white', counter_white)
|
|
994
|
+
if counter_white == 3:
|
|
995
|
+
return (y, x), orientation
|
|
996
|
+
|
|
997
|
+
print('Error: Optimal pavlidis not found. Returning None')
|
|
998
|
+
return None, 0
|
|
999
|
+
|
|
1000
|
+
|
|
1001
|
+
def order_regions_by_y_axis(region1, region2):
|
|
1002
|
+
# Assuming region1 and region2 are instances of regionprops
|
|
1003
|
+
|
|
1004
|
+
# Calculate y-axis centroids
|
|
1005
|
+
centroid_y1 = region1.centroid[0]
|
|
1006
|
+
centroid_y2 = region2.centroid[0]
|
|
1007
|
+
|
|
1008
|
+
# Compare based on y-axis centroid
|
|
1009
|
+
if centroid_y1 < centroid_y2:
|
|
1010
|
+
return region1, region2
|
|
1011
|
+
else:
|
|
1012
|
+
return region2, region1
|
|
1013
|
+
|
|
1014
|
+
|
|
1015
|
+
if __name__ == '__main__':
|
|
1016
|
+
|
|
1017
|
+
if True:
|
|
1018
|
+
# TODO finalize my sorting code --> TODO
|
|
1019
|
+
# generate random shapes
|
|
1020
|
+
|
|
1021
|
+
# [('triangle', ((12, 41), (6, 39)))] --> with this traingle the pavlidis is the contour algo that works the best !!!
|
|
1022
|
+
|
|
1023
|
+
# NB with this traingle that really does not work!!!! -->
|
|
1024
|
+
# [('triangle', ((29, 51), (4, 29)))]
|
|
1025
|
+
# True
|
|
1026
|
+
# [[30 16]
|
|
1027
|
+
# [29 16]]
|
|
1028
|
+
|
|
1029
|
+
import skimage.draw
|
|
1030
|
+
# img, labels = skimage.draw.random_shapes((32, 32), max_shapes=3)
|
|
1031
|
+
img, lbs = skimage.draw.random_shapes((64, 64), max_shapes=1)
|
|
1032
|
+
|
|
1033
|
+
img = img[..., 0]
|
|
1034
|
+
img[img == 255] = 0
|
|
1035
|
+
img[img != 0] = 255
|
|
1036
|
+
skip_plot = False
|
|
1037
|
+
|
|
1038
|
+
# single point image
|
|
1039
|
+
# check for single pixel sorting --> TODO
|
|
1040
|
+
# debug for one pixel wide pavlidis -> KEEP
|
|
1041
|
+
# img = np.zeros_like(img)
|
|
1042
|
+
# img[int(img.shape[0]/2), int(img.shape[1]/2)] = 255
|
|
1043
|
+
|
|
1044
|
+
# np.nonzero(x)
|
|
1045
|
+
# plt.imshow(img)
|
|
1046
|
+
# plt.show()
|
|
1047
|
+
|
|
1048
|
+
|
|
1049
|
+
first_pixel = np.nonzero(img) # np.argwhere(img!=255)#np.nonzero(img)
|
|
1050
|
+
print(lbs) # NB THERE ARE DUPLICATED POINTS WITH TRIANGLES --> IS THERE A SMART WAY TO HANDLE DUPLICATED POINTS --> AND OR TO RECONNECT THE MISSING PARTS --> MAYBE BUT THINK ABOUT IT!!!
|
|
1051
|
+
# NB SHALL I DO A NO REPICK !!! BY CUTTING POINTS ALREADY TAKEN ?????
|
|
1052
|
+
|
|
1053
|
+
# need get the contour just otherwsie does not work --> cheat !!!
|
|
1054
|
+
|
|
1055
|
+
lb = label(img, connectivity=1, background=0)
|
|
1056
|
+
rps = regionprops(lb)
|
|
1057
|
+
|
|
1058
|
+
|
|
1059
|
+
first_pixel = np.nonzero(img) # np.argwhere(img!=255)#np.nonzero(img)
|
|
1060
|
+
|
|
1061
|
+
# could loop over those pixels to find a pavlidis compatible entry
|
|
1062
|
+
|
|
1063
|
+
# pb here is I need check orientation
|
|
1064
|
+
# neo_start, orientation = find_pavlidis_optimal_start(first_pixel) # faisl for [('triangle', ((58, 60), (54, 56)))] also for [('circle', ((3, 4), (20, 21)))]
|
|
1065
|
+
# print(first_pixel[0][0], first_pixel[1][0], 'optimal pavlidis start', neo_start)
|
|
1066
|
+
|
|
1067
|
+
|
|
1068
|
+
start = timer()
|
|
1069
|
+
|
|
1070
|
+
|
|
1071
|
+
# simple algo that fails for triangles but ok in fact # maybe a better way would be to run it several times and instead of removing dupes to take the longest pixel repeat
|
|
1072
|
+
sortedpx = pavlidis(img, first_pixel[0][0], first_pixel[1][0], True)
|
|
1073
|
+
# sortedpx = pavlidis(img, neo_start[0], neo_start[1], True) # --> [('circle', ((23, 24), (1, 2)))] infinite loop with neo start --> need check if begining is encountered twice and return if that is True
|
|
1074
|
+
|
|
1075
|
+
# ça marche pr les triangles mais plante des fois pr les cercles --> implement the no start twice algo
|
|
1076
|
+
# if neo_start is None:
|
|
1077
|
+
# neo_start,_ = get_feret_from_points(extreme_points(first_pixel))
|
|
1078
|
+
# plt.imshow(lb)
|
|
1079
|
+
# plt.show()
|
|
1080
|
+
# print('corrected start',neo_start)
|
|
1081
|
+
|
|
1082
|
+
# [('circle', ((3, 4), (20, 21)))] --> infinite loop why --> in fact these are the bounds --> if no empty pixel in the shape then stuff will not work --> must not compute the pavlidis because may enter an infinite loop !!!
|
|
1083
|
+
# somehow would need a control for that!!!
|
|
1084
|
+
# can there be bugs if no data is generated through
|
|
1085
|
+
|
|
1086
|
+
# if there is a single point just return directly coords as there is really nothing todo
|
|
1087
|
+
|
|
1088
|
+
# [('ellipse', ((24, 41), (47, 58)))] --> bug in detection of the contour (but probably an impossible shape --> somehow need to do controls, still but ok
|
|
1089
|
+
|
|
1090
|
+
# sortedpx = pavlidis(img, neo_start[0], neo_start[1], True, starting_orientation=orientation) # evn when distance > sqrt 2 the order of pixels is actually still correct!!! # --> [('circle', ((23, 24), (1, 2)))] infinite loop with neo start --> need check if begining is encountered twice and return if that is True
|
|
1091
|
+
# sortedpx = pavlidis(img, first_pixel[0][0], first_pixel[0][1], True) # evn when distance > sqrt 2 the order of pixels is actually still correct!!! # --> [('circle', ((23, 24), (1, 2)))] infinite loop with neo start --> need check if begining is encountered twice and return if that is True
|
|
1092
|
+
duration = timer() - start
|
|
1093
|
+
|
|
1094
|
+
print('end pavlidis 1')
|
|
1095
|
+
|
|
1096
|
+
|
|
1097
|
+
# --> infinite loop unfortunately...
|
|
1098
|
+
#fails for [('rectangle', ((42, 54), (25, 29)))]
|
|
1099
|
+
# start = timer()
|
|
1100
|
+
# sortedpx2 = pavlidis2(img, first_pixel[0][0], first_pixel[1][0], True) # --> infinite loop for [('rectangle', ((40, 49), (13, 53)))]
|
|
1101
|
+
# duration2 = timer() - start
|
|
1102
|
+
|
|
1103
|
+
# print('pvalidis time difference', duration, duration2, sortedpx, sortedpx2)
|
|
1104
|
+
|
|
1105
|
+
|
|
1106
|
+
__plot_pavlidis(img, sortedpx, skip_plot=False)
|
|
1107
|
+
img[img!=0]=0
|
|
1108
|
+
|
|
1109
|
+
sortedpx = np.asarray(sortedpx)
|
|
1110
|
+
|
|
1111
|
+
|
|
1112
|
+
img[sortedpx[:,0], sortedpx[:,1]]=255
|
|
1113
|
+
# img[sortedpx]=255
|
|
1114
|
+
|
|
1115
|
+
|
|
1116
|
+
lb = label(img, connectivity=1, background=0)
|
|
1117
|
+
rps = regionprops(lb)
|
|
1118
|
+
|
|
1119
|
+
# errors should not happen with a proper entry point for pavlidis --> maybe check that --> auto would do the job
|
|
1120
|
+
|
|
1121
|
+
|
|
1122
|
+
# NB none of these new algos works as nicely to sort pixels as does the pavlidis algo --> stick to that for now
|
|
1123
|
+
# test of other algos compared to pavlidis --> nothing works as nicely
|
|
1124
|
+
# they are immediately stuck at the entry in the case of triangles
|
|
1125
|
+
# both work for squares though and classical shapes --> if px count is ok then maybe I can rely on them and if not ok then roll back to pavlidis --> just to gain speed --> INDEED TRY THAT!!!
|
|
1126
|
+
# none of the two work for ellipses neither but not clear why --> are these algos somehow just four connected --> check them deeply
|
|
1127
|
+
|
|
1128
|
+
|
|
1129
|
+
# crap and if only one pixel --> a bug
|
|
1130
|
+
# sorted_pixels = sort_pixels_clowkise2(rps[0].coords)
|
|
1131
|
+
# print(is_distance_continuous(sorted_pixels))
|
|
1132
|
+
|
|
1133
|
+
|
|
1134
|
+
# mount a talk
|
|
1135
|
+
# __plot_pavlidis(img, sorted_pixels, skip_plot=False)
|
|
1136
|
+
|
|
1137
|
+
# sorted_pixels = nearest_neighbor_ordering(rps[0].coords) # for small triangles it can crash ValueError: Expected n_neighbors <= n_samples, but n_samples = 2, n_neighbors = 3 --> put it in a try loop if used
|
|
1138
|
+
# __plot_pavlidis(img, sorted_pixels, skip_plot=False)
|
|
1139
|
+
|
|
1140
|
+
# qsdqsqsdsqdqsdqsdsqd
|
|
1141
|
+
|
|
1142
|
+
if False:
|
|
1143
|
+
array = np.asarray([[0, 0, 0], [1, 1, 1], [2, 2, 2]])
|
|
1144
|
+
# array=np.asarray([[0,0],[1,1],[2,2]])
|
|
1145
|
+
print(array)
|
|
1146
|
+
print(array.shape)
|
|
1147
|
+
print(np.roll(array, -1, axis=-1))
|
|
1148
|
+
print(np.roll(array, -1, axis=0)) # --> this is what I wwnat in fact
|
|
1149
|
+
print(np.roll(array, 1, axis=0)) # --> this is what I wwnat in fact
|
|
1150
|
+
# print(np.roll(array, -1))
|
|
1151
|
+
|
|
1152
|
+
line_test = np.asarray(
|
|
1153
|
+
[[255, 256], [255, 257], [255, 258], [255, 259], [255, 260], [255, 261], [255, 262], [255, 263], [255, 264],
|
|
1154
|
+
[255, 265], [255, 266], [255, 267], [255, 268], [255, 269], [255, 270], [255, 271], [255, 272], [255, 273],
|
|
1155
|
+
[255, 274], [255, 275], [255, 276], [255, 277], [255, 278], [255, 279], [255, 280], [255, 281], [255, 282],
|
|
1156
|
+
[255, 283], [255, 284], [255, 285], [255, 286], [255, 287], [255, 288], [255, 289], [255, 290], [255, 291],
|
|
1157
|
+
[255, 292], [255, 293], [255, 294], [255, 295], [255, 296], [255, 297], [255, 298], [255, 299], [255, 300],
|
|
1158
|
+
[255, 301], [255, 302], [255, 303], [255, 304], [255, 305], [255, 306], [255, 307], [255, 308], [255, 309],
|
|
1159
|
+
[255, 310], [255, 311], [255, 312], [255, 313], [255, 314], [255, 315], [255, 316], [255, 317], [255, 318],
|
|
1160
|
+
[255, 319], [255, 320], [255, 321], [255, 322], [255, 323], [255, 324], [255, 325], [255, 326], [255, 327],
|
|
1161
|
+
[255, 328], [255, 329], [255, 330], [255, 331], [255, 332], [255, 333], [255, 334], [255, 335], [255, 336],
|
|
1162
|
+
[255, 337], [255, 338], [255, 339], [255, 340], [255, 341], [255, 342], [255, 343], [255, 344], [255, 345],
|
|
1163
|
+
[255, 346], [255, 347], [255, 348], [255, 349], [255, 350], [255, 351], [255, 352], [255, 353], [255, 354],
|
|
1164
|
+
[255, 355], [255, 356], [255, 357], [255, 358], [255, 359], [255, 360], [255, 361], [255, 362], [255, 363],
|
|
1165
|
+
[255, 364], [255, 365], [255, 366], [255, 367], [255, 368], [255, 369], [255, 370], [255, 371], [255, 372],
|
|
1166
|
+
[255, 373], [255, 374], [255, 375], [255, 376], [255, 377], [255, 378], [255, 379], [255, 380], [255, 381],
|
|
1167
|
+
[255, 382], [255, 383], [255, 384], [255, 385], [255, 386], [255, 387], [255, 388], [255, 389], [255, 390],
|
|
1168
|
+
[255, 391], [255, 392], [255, 393], [255, 394], [255, 395], [255, 396], [255, 397], [255, 398], [255, 399],
|
|
1169
|
+
[255, 400], [255, 401], [255, 402], [255, 403], [255, 404], [255, 405], [255, 406], [255, 407], [255, 408],
|
|
1170
|
+
[255, 409], [255, 410], [255, 411], [255, 412], [255, 413], [255, 414], [255, 415], [255, 416], [255, 417],
|
|
1171
|
+
[255, 418], [255, 419], [255, 420], [255, 421], [255, 422], [255, 423], [255, 424], [255, 425], [255, 426],
|
|
1172
|
+
[255, 427], [255, 428], [255, 429], [255, 430], [255, 431], [255, 432], [255, 433], [255, 434], [255, 435],
|
|
1173
|
+
[255, 436], [255, 437], [255, 438], [255, 439], [255, 440], [255, 441], [255, 442], [255, 443], [255, 444],
|
|
1174
|
+
[255, 445], [255, 446], [255, 447], [255, 448], [255, 449], [255, 450], [255, 451], [255, 452], [255, 453],
|
|
1175
|
+
[255, 454], [255, 455], [255, 456], [255, 457], [255, 458], [255, 459], [255, 460], [255, 461], [255, 462],
|
|
1176
|
+
[255, 463], [255, 464], [255, 465], [255, 466], [255, 467], [255, 468], [255, 469], [255, 470], [255, 471],
|
|
1177
|
+
[255, 472], [255, 473], [255, 474], [255, 475], [255, 476], [255, 477], [255, 478], [255, 479], [255, 480],
|
|
1178
|
+
[255, 481], [255, 482], [255, 483], [255, 484], [255, 485], [255, 486], [255, 487], [255, 488], [255, 489],
|
|
1179
|
+
[255, 490], [255, 491], [255, 492], [255, 493], [255, 494], [255, 495], [255, 496], [255, 497], [255, 498],
|
|
1180
|
+
[255, 499], [255, 500], [255, 501], [255, 502], [255, 503], [255, 504], [255, 505], [255, 506], [255, 507],
|
|
1181
|
+
[255, 508], [255, 509], [255, 510], [255, 511], [256, 255], [256, 254], [256, 253], [256, 252], [256, 251],
|
|
1182
|
+
[256, 250], [256, 249], [256, 248], [256, 247], [256, 246], [256, 245], [256, 244], [256, 243], [256, 242],
|
|
1183
|
+
[256, 241], [256, 240], [256, 239], [256, 238], [256, 237], [256, 236], [256, 235], [256, 234], [256, 233],
|
|
1184
|
+
[256, 232], [256, 231], [256, 230], [256, 229], [256, 228], [256, 227], [256, 226], [256, 225], [256, 224],
|
|
1185
|
+
[256, 223], [256, 222], [256, 221], [256, 220], [256, 219], [256, 218], [256, 217], [256, 216], [256, 215],
|
|
1186
|
+
[256, 214], [256, 213], [256, 212], [256, 211], [256, 210], [256, 209], [256, 208], [256, 207], [256, 206],
|
|
1187
|
+
[256, 205], [256, 204], [256, 203], [256, 202], [256, 201], [256, 200], [256, 199], [256, 198], [256, 197],
|
|
1188
|
+
[256, 196], [256, 195], [256, 194], [256, 193], [256, 192], [256, 191], [256, 190], [256, 189], [256, 188],
|
|
1189
|
+
[256, 187], [256, 186], [256, 185], [256, 184], [256, 183], [256, 182], [256, 181], [256, 180], [256, 179],
|
|
1190
|
+
[256, 178], [256, 177], [256, 176], [256, 175], [256, 174], [256, 173], [256, 172], [256, 171], [256, 170],
|
|
1191
|
+
[256, 169], [256, 168], [256, 167], [256, 166], [256, 165], [256, 164], [256, 163], [256, 162], [256, 161],
|
|
1192
|
+
[256, 160], [256, 159], [256, 158], [256, 157], [256, 156], [256, 155], [256, 154], [256, 153], [256, 152],
|
|
1193
|
+
[256, 151], [256, 150], [256, 149], [256, 148], [256, 147], [256, 146], [256, 145], [256, 144], [256, 143],
|
|
1194
|
+
[256, 142], [256, 141], [256, 140], [256, 139], [256, 138], [256, 137], [256, 136], [256, 135], [256, 134],
|
|
1195
|
+
[256, 133], [256, 132], [256, 131], [256, 130], [256, 129], [256, 128], [256, 127], [256, 126], [256, 125],
|
|
1196
|
+
[256, 124], [256, 123], [256, 122], [256, 121], [256, 120], [256, 119], [256, 118], [256, 117], [256, 116],
|
|
1197
|
+
[256, 115], [256, 114], [256, 113], [256, 112], [256, 111], [256, 110], [256, 109], [256, 108], [256, 107],
|
|
1198
|
+
[256, 106], [256, 105], [256, 104], [256, 103], [256, 102], [256, 101], [256, 100], [256, 99], [256, 98],
|
|
1199
|
+
[256, 97], [256, 96], [256, 95], [256, 94], [256, 93], [256, 92], [256, 91], [256, 90], [256, 89],
|
|
1200
|
+
[256, 88], [256, 87], [256, 86], [256, 85], [256, 84], [256, 83], [256, 82], [256, 81], [256, 80],
|
|
1201
|
+
[256, 79], [256, 78], [256, 77], [256, 76], [256, 75], [256, 74], [256, 73], [256, 72], [256, 71],
|
|
1202
|
+
[256, 70], [256, 69], [256, 68], [256, 67], [256, 66], [256, 65], [256, 64], [256, 63], [256, 62],
|
|
1203
|
+
[256, 61], [256, 60], [256, 59], [256, 58], [256, 57], [256, 56], [256, 55], [256, 54], [256, 53],
|
|
1204
|
+
[256, 52], [256, 51], [256, 50], [256, 49], [256, 48], [256, 47], [256, 46], [256, 45], [256, 44],
|
|
1205
|
+
[256, 43], [256, 42], [256, 41], [256, 40], [256, 39], [256, 38], [256, 37], [256, 36], [256, 35],
|
|
1206
|
+
[256, 34], [256, 33], [256, 32], [256, 31], [256, 30], [256, 29], [256, 28], [256, 27], [256, 26],
|
|
1207
|
+
[256, 25], [256, 24], [256, 23], [256, 22], [256, 21], [256, 20], [256, 19], [256, 18], [256, 17],
|
|
1208
|
+
[256, 16], [256, 15], [256, 14], [256, 13], [256, 12], [256, 11], [256, 10], [256, 9], [256, 8], [256, 7],
|
|
1209
|
+
[256, 6], [256, 5], [256, 4], [256, 3], [256, 2], [256, 0], [256, 1]])
|
|
1210
|
+
|
|
1211
|
+
print(line_test[255])
|
|
1212
|
+
print(line_test.shape[0])
|
|
1213
|
+
print(np.roll(line_test, -255 - 1, axis=0))
|
|
1214
|
+
|
|
1215
|
+
# en fait c'est juste pas possible de corriger car le ligne est tt simplement inverser --> faudrait inverser l'ordre complet de la ligne --> reflechir à comment faire en fait --> pas si simple --> TODO
|
|
1216
|
+
|
|
1217
|
+
import sys
|
|
1218
|
+
|
|
1219
|
+
sys.exit(0)
|
|
1220
|
+
|
|
1221
|
+
# test for nearest neighbor ordering
|
|
1222
|
+
# if True:
|
|
1223
|
+
# coords = [(4, 30), (5, 30), (6, 31), (7, 31), (8, 32), (9, 32), (10, 33), (11, 33), (12, 34), (13, 34),
|
|
1224
|
+
# (14, 35),
|
|
1225
|
+
# (15, 36), (15, 35), (15, 34), (15, 33), (15, 32), (15, 31), (15, 30), (15, 29), (15, 28), (15, 27),
|
|
1226
|
+
# (15, 26), (15, 25), (15, 24), (14, 25), (13, 26), (12, 26), (11, 27), (10, 27), (9, 28), (8, 28),
|
|
1227
|
+
# (7, 29),
|
|
1228
|
+
# (6, 29), (5, 30)]
|
|
1229
|
+
# print(coords)
|
|
1230
|
+
#
|
|
1231
|
+
# # remove dupes
|
|
1232
|
+
# # coords = list(set(coords))
|
|
1233
|
+
#
|
|
1234
|
+
# # print(coords.shape)
|
|
1235
|
+
# nn = nearest_neighbor_ordering(coords, remove_dupes=True)
|
|
1236
|
+
#
|
|
1237
|
+
# print(nn)
|
|
1238
|
+
# print(nn.shape)
|
|
1239
|
+
#
|
|
1240
|
+
# # print('ordered coords', no_dupes)
|
|
1241
|
+
# print('dist extremities', dist2D(nn[0], nn[-1]),
|
|
1242
|
+
# dist2D(nn[0], nn[-1]) > math.sqrt(2)) # still dist > sqrt 2 --> impossible to manage for such shape
|
|
1243
|
+
|
|
1244
|
+
# ça marche
|
|
1245
|
+
# test for ordering of pixels of bond like structures --> should always work I think
|
|
1246
|
+
# if True:
|
|
1247
|
+
# img = np.zeros((512, 512))
|
|
1248
|
+
# rr, cc = line(256, 0, 255, img.shape[1] - 1)
|
|
1249
|
+
#
|
|
1250
|
+
# img[rr, cc] = 255
|
|
1251
|
+
#
|
|
1252
|
+
# labels = label(img, connectivity=2, background=0)
|
|
1253
|
+
# rps = regionprops(labels)
|
|
1254
|
+
#
|
|
1255
|
+
# # in the middle there is
|
|
1256
|
+
# # 255 511
|
|
1257
|
+
# # 256 0 --> pixels are scanned line by line and not ordered --> length of the line need be calculated after a pavlidis algo --> practically see how I can do that in a smart way based on ROIs
|
|
1258
|
+
#
|
|
1259
|
+
# for region in rps:
|
|
1260
|
+
# # that is a lot of code and can all be put and stored directly in the pavlidis --> TODO
|
|
1261
|
+
#
|
|
1262
|
+
# # TODO try a plot of the region
|
|
1263
|
+
# # maybe get bounds and plot within them --> should be easy in fact
|
|
1264
|
+
#
|
|
1265
|
+
# print(region.bbox)
|
|
1266
|
+
# # create an image for the bbox
|
|
1267
|
+
#
|
|
1268
|
+
# # tmp_img = np.zeros((abs(region.bbox[2]-region.bbox[0]),abs(region.bbox[1]-region.bbox[3])))
|
|
1269
|
+
# # tmp_img = np.zeros((max(region.bbox[2],region.bbox[0])+1,max(region.bbox[1],region.bbox[3])+1)) # then easy to plt my object in it
|
|
1270
|
+
# # print(tmp_img.shape)
|
|
1271
|
+
# # print(region.coords.shape)
|
|
1272
|
+
# # # fill numpy array using region coords TOP MEGA TOP TIP TIPTOP TIP TOP
|
|
1273
|
+
# # tmp_img[region.coords[:,0], region.coords[:,1]] = 255 # so cool another very easy way to plot coords --> but is that fast or slow ??? --> IF FAST COULD REPLACE SO MANY OF MY CALLS !!!
|
|
1274
|
+
# # tmp_img[np.split(region.coords, axis=-1)] = 255 # there must be a way to do it like that too!!
|
|
1275
|
+
#
|
|
1276
|
+
# # create an image
|
|
1277
|
+
# # then try to plot in it
|
|
1278
|
+
# # plt.imshow(tmp_img)
|
|
1279
|
+
# # plt.show()
|
|
1280
|
+
#
|
|
1281
|
+
# # quite good by the way
|
|
1282
|
+
# # smart is maybe to try to use as a seed of of the most distant points
|
|
1283
|
+
#
|
|
1284
|
+
# # pavlidis for region --> allow __plot_pavlidis to plot this and maybe add checks for the stuff
|
|
1285
|
+
# # add pavlidis
|
|
1286
|
+
#
|
|
1287
|
+
# # --> really not worth it for contours --> rather use nn instead
|
|
1288
|
+
# # total pavlidis 18.360223728988785
|
|
1289
|
+
# # total nn 2.1951778780203313
|
|
1290
|
+
# # start_pavlidis = timer()
|
|
1291
|
+
# # for i in range(100):
|
|
1292
|
+
#
|
|
1293
|
+
#
|
|
1294
|
+
# # optimal_pavlidis_entry = find_pavlidis_optimal_start(region.coords) --> this unfortunately does not work for non closed stuff --> need the stuff really be closed otherwise take an extremity is required...
|
|
1295
|
+
# # print('optimal_pavlidis_entry', optimal_pavlidis_entry)
|
|
1296
|
+
# sorted_coords = pavlidis(region) # --> marche pas si pas à une extremite en fait --> in that case I May need to start from the extreme point --> in that case would that always work
|
|
1297
|
+
# # sorted_coords = pavlidis(region, optimal_pavlidis_entry[0], optimal_pavlidis_entry[1]) # --> marche pas si pas à une extremite en fait --> in that case I May need to start from the extreme point --> in that case would that always work
|
|
1298
|
+
# # pb est que je peux avoir des jumps --> faudrait faire une rotation du truc pr y arriver
|
|
1299
|
+
# # sorted_coords = nearest_neighbor_ordering(region.coords) # --> marche pas si pas à une extremite en fait --> in that case I May need to start from the extreme point --> in that case would that always work
|
|
1300
|
+
#
|
|
1301
|
+
# print('mega test of all', np.asarray(sorted_coords).shape)
|
|
1302
|
+
# compute_perimeter(sorted_coords)
|
|
1303
|
+
#
|
|
1304
|
+
# print('is_distance_continuous pavlidis', is_distance_continuous(sorted_coords))
|
|
1305
|
+
# print('is_distance_continuous nn', is_distance_continuous(nearest_neighbor_ordering(
|
|
1306
|
+
# region.coords))) # could return the coord of the max -- give me the roll to apply # --> TODO but maybe ok in fact
|
|
1307
|
+
# output = is_distance_continuous(nearest_neighbor_ordering(region.coords))
|
|
1308
|
+
#
|
|
1309
|
+
# # orderd_yet_another = order_points_new(region.coords)
|
|
1310
|
+
# # seems to work well too --> but try with different shapes
|
|
1311
|
+
# start_pavlidis = timer()
|
|
1312
|
+
#
|
|
1313
|
+
# # for i in range(1):
|
|
1314
|
+
# # orderd_yet_another = sort_pixels_clowkise2(region.coords)
|
|
1315
|
+
# # print('total other', timer() - start_pavlidis) # total other 0.2618692510004621 --> this is super fast --> if that works then I would
|
|
1316
|
+
#
|
|
1317
|
+
# # print('is_distance_continuous another', is_distance_continuous(orderd_yet_another))
|
|
1318
|
+
# #
|
|
1319
|
+
# # __plot_pavlidis(region, orderd_yet_another, skip_plot=False)
|
|
1320
|
+
#
|
|
1321
|
+
# try:
|
|
1322
|
+
# if len(output) == 2:
|
|
1323
|
+
# max_dist, max_dist_pos = output
|
|
1324
|
+
#
|
|
1325
|
+
# uncorrected = nearest_neighbor_ordering(region.coords)
|
|
1326
|
+
#
|
|
1327
|
+
# print(uncorrected[max_dist_pos]) # --> 255, 511 --> ok in fact
|
|
1328
|
+
#
|
|
1329
|
+
# # cannot correct because would need to invert a big chunk of the ordering --> literally reverse half of the array
|
|
1330
|
+
#
|
|
1331
|
+
# # or I would need signs maybe vectors
|
|
1332
|
+
#
|
|
1333
|
+
# # corrected = np.roll(uncorrected, -max_dist_pos, axis=0) # I HAVE A BUG IN MY ROLL --> FIX IT!!!
|
|
1334
|
+
# # corrected = np.roll(uncorrected, -(len(uncorrected)-max_dist_pos), axis=0) # I HAVE A BUG IN MY ROLL --> FIX IT!!!
|
|
1335
|
+
# # corrected = np.roll(uncorrected, max_dist_pos, axis=0) # I HAVE A BUG IN MY ROLL --> FIX IT!!!
|
|
1336
|
+
# # # , axis = 0
|
|
1337
|
+
# # print('uncorrected', uncorrected.tolist())
|
|
1338
|
+
# #
|
|
1339
|
+
# # # ça ne marche pas le roll --> comment le faire en fait
|
|
1340
|
+
# # print('corrected', corrected.tolist())
|
|
1341
|
+
# # print('is_distance_continuous nn',
|
|
1342
|
+
# # is_distance_continuous(corrected))
|
|
1343
|
+
#
|
|
1344
|
+
# __plot_pavlidis(region, uncorrected, skip_plot=False)
|
|
1345
|
+
# except:
|
|
1346
|
+
# # if iscontinuous --> ignore and do not try to roll
|
|
1347
|
+
# pass
|
|
1348
|
+
#
|
|
1349
|
+
# # crappy_test = 23
|
|
1350
|
+
# # print(len(crappy_test))
|
|
1351
|
+
#
|
|
1352
|
+
# # print('total pavlidis', timer()-start_pavlidis)
|
|
1353
|
+
#
|
|
1354
|
+
# # start_pavlidis = timer()
|
|
1355
|
+
# # for i in range(100):
|
|
1356
|
+
# # sorted_coords = nearest_neighbor_ordering(
|
|
1357
|
+
# # region.coords) # --> marche pas si pas à une extremite en fait --> in that case I May need to start from the extreme point --> in that case would that always work
|
|
1358
|
+
# # print('total nn', timer() - start_pavlidis)
|
|
1359
|
+
#
|
|
1360
|
+
# # ça marche et c'est assez simple en fait
|
|
1361
|
+
# __plot_pavlidis(region, sorted_coords, skip_plot=False)
|
|
1362
|
+
#
|
|
1363
|
+
# # shall I add checks in pavlidis
|
|
1364
|
+
# #
|
|
1365
|
+
#
|
|
1366
|
+
# # print(sorted_coords)
|
|
1367
|
+
# print(len(sorted_coords), region.coords.shape[0])
|
|
1368
|
+
#
|
|
1369
|
+
# # bug ????
|
|
1370
|
+
# # print(sorted_coords)
|
|
1371
|
+
# # if still some missing points then can use the other stuff
|
|
1372
|
+
#
|
|
1373
|
+
# plt.imshow(img)
|
|
1374
|
+
# plt.show()
|
|
1375
|
+
#
|
|
1376
|
+
# import sys
|
|
1377
|
+
#
|
|
1378
|
+
# sys.exit(0)
|
|
1379
|
+
# almost ok now just try to plot stuff
|
|
1380
|
+
|
|
1381
|
+
# test pavlidis on several shapes
|
|
1382
|
+
if True:
|
|
1383
|
+
start_time = timer()
|
|
1384
|
+
img = np.zeros((5, 5), dtype=np.uint8)
|
|
1385
|
+
start = (1, 1)
|
|
1386
|
+
extent = (3, 3)
|
|
1387
|
+
rr, cc = rectangle(start, extent=extent, shape=img.shape)
|
|
1388
|
+
img[rr, cc] = 255
|
|
1389
|
+
# img
|
|
1390
|
+
# array([[0, 0, 0, 0, 0],
|
|
1391
|
+
# [0, 1, 1, 1, 0],
|
|
1392
|
+
# [0, 1, 1, 1, 0],
|
|
1393
|
+
# [0, 1, 1, 1, 0],
|
|
1394
|
+
# [0, 0, 0, 0, 0]], dtype=uint8)
|
|
1395
|
+
# img =
|
|
1396
|
+
|
|
1397
|
+
# false
|
|
1398
|
+
skip_plot = True
|
|
1399
|
+
|
|
1400
|
+
print(pavlidis(img, 0, 0, True, color_of_interest=255))
|
|
1401
|
+
print(pavlidis(img, 4, 4, True, color_of_interest=255))
|
|
1402
|
+
print(pavlidis(img, 120, 120, True, color_of_interest=255))
|
|
1403
|
+
print(pavlidis(img, -1, 2, True, color_of_interest=255))
|
|
1404
|
+
# ok
|
|
1405
|
+
__plot_pavlidis(img, pavlidis(img, 1, 1, True),
|
|
1406
|
+
skip_plot=skip_plot) # expected [(1, 1), (1, 2), (1, 3), (2, 3), (3, 3), (3, 2), (3, 1), (2, 1)]
|
|
1407
|
+
__plot_pavlidis(img, pavlidis(img, 1, 2, True), skip_plot=skip_plot) # expected
|
|
1408
|
+
|
|
1409
|
+
# ça a l'air de marcher et c'est facile --> test speed
|
|
1410
|
+
|
|
1411
|
+
img = np.zeros((2048, 2048), dtype=np.uint8)
|
|
1412
|
+
start = (1, 1)
|
|
1413
|
+
extent = (2046, 2046)
|
|
1414
|
+
rr, cc = rectangle(start, extent=extent, shape=img.shape)
|
|
1415
|
+
img[rr, cc] = 255
|
|
1416
|
+
# img
|
|
1417
|
+
# array([[0, 0, 0, 0, 0],
|
|
1418
|
+
# [0, 1, 1, 1, 0],
|
|
1419
|
+
# [0, 1, 1, 1, 0],
|
|
1420
|
+
# [0, 1, 1, 1, 0],
|
|
1421
|
+
# [0, 0, 0, 0, 0]], dtype=uint8)
|
|
1422
|
+
# img =
|
|
1423
|
+
|
|
1424
|
+
__plot_pavlidis(img, pavlidis(img, 1, 1, True),
|
|
1425
|
+
skip_plot=skip_plot) # expected [(1, 1), (1, 2), (1, 3), (2, 3), (3, 3), (3, 2), (3, 1), (2, 1)]
|
|
1426
|
+
__plot_pavlidis(img, pavlidis(img, 1, 2, True), skip_plot=skip_plot) # expected
|
|
1427
|
+
|
|
1428
|
+
# print(len(pavlidis(img, 1, 1, True)))
|
|
1429
|
+
|
|
1430
|
+
print("done 1", timer() - start_time) # 1000 gigantic sorted contours -->
|
|
1431
|
+
|
|
1432
|
+
# for i in range(100):
|
|
1433
|
+
# print(pavlidis(img, 1, 1)) # expected [(1, 1), (1, 2), (1, 3), (2, 3), (3, 3), (3, 2), (3, 1), (2, 1)]
|
|
1434
|
+
# print(pavlidis(img, 1, 2)) # expected
|
|
1435
|
+
#
|
|
1436
|
+
# # tt a l'air ok --> juste voir le resultat et tt plotter
|
|
1437
|
+
# print("done 100", timer() - start_time) # 1000 gigantic sorted contours --> 14.264636867999798s --> probably not that bad in fact --> if still too slow then code it in Cython --> TODO
|
|
1438
|
+
|
|
1439
|
+
# try with a circle too --> TODO
|
|
1440
|
+
|
|
1441
|
+
from skimage.draw import polygon_perimeter
|
|
1442
|
+
|
|
1443
|
+
img = np.zeros((10, 10), dtype=np.uint8)
|
|
1444
|
+
rr, cc = polygon_perimeter([5, -1, 5, 10], [-1, 5, 11, 5], shape=img.shape, clip=True)
|
|
1445
|
+
img[rr, cc] = 255
|
|
1446
|
+
# img
|
|
1447
|
+
# array([[0, 0, 0, 0, 1, 1, 1, 0, 0, 0],
|
|
1448
|
+
# [0, 0, 0, 1, 0, 0, 0, 1, 0, 0],
|
|
1449
|
+
# [0, 0, 1, 0, 0, 0, 0, 0, 1, 0],
|
|
1450
|
+
# [0, 1, 0, 0, 0, 0, 0, 0, 0, 1],
|
|
1451
|
+
# [1, 0, 0, 0, 0, 0, 0, 0, 0, 1],
|
|
1452
|
+
# [1, 0, 0, 0, 0, 0, 0, 0, 0, 1],
|
|
1453
|
+
# [1, 0, 0, 0, 0, 0, 0, 0, 0, 1],
|
|
1454
|
+
# [0, 1, 1, 0, 0, 0, 0, 0, 0, 1],
|
|
1455
|
+
# [0, 0, 0, 1, 0, 0, 0, 1, 1, 0],
|
|
1456
|
+
# [0, 0, 0, 0, 1, 1, 1, 0, 0, 0]], dtype=uint8)
|
|
1457
|
+
|
|
1458
|
+
# print('in polygon')
|
|
1459
|
+
__plot_pavlidis(img, pavlidis(img, 4, 0, True), skip_plot=skip_plot) # on a polygon
|
|
1460
|
+
# print('done')
|
|
1461
|
+
|
|
1462
|
+
import numpy as np
|
|
1463
|
+
from skimage.draw import bezier_curve
|
|
1464
|
+
|
|
1465
|
+
img = np.zeros((10, 10), dtype=np.uint8)
|
|
1466
|
+
rr, cc = bezier_curve(1, 5, 5, -2, 8, 8, 2)
|
|
1467
|
+
img[rr, cc] = 255
|
|
1468
|
+
|
|
1469
|
+
print('in bezier')
|
|
1470
|
+
# maybe blacken the pixel and or restore it in the end???
|
|
1471
|
+
__plot_pavlidis(img, pavlidis(img, 4, 1, False),
|
|
1472
|
+
skip_plot=skip_plot) # on a bezier --> not closed --> if takes same point again --> then get out --> will be incomple
|
|
1473
|
+
|
|
1474
|
+
print('img[2,6]', img[2, 6]) # there is a bug cause 2,6 = 0 and should never be in
|
|
1475
|
+
|
|
1476
|
+
# il y a un bug ici qui le fait remonter --> marche pas --> maybe record all
|
|
1477
|
+
__plot_pavlidis(img, pavlidis(img, 1, 5, False),
|
|
1478
|
+
skip_plot=skip_plot) # in fact I really need to do things this way !!! and really need check contour or disconnect after me --> no cluse# to get it right --> need start from a vertex # can be set to True if starting from a vertex --> in fact that is even required to get the stuff done properly otherwise pxs are missing # NB THE NO REPICK IS PROBABLY NOT A SMART IDEA
|
|
1479
|
+
__plot_pavlidis(img, pavlidis(img, 8, 8, False), skip_plot=skip_plot)
|
|
1480
|
+
|
|
1481
|
+
# --> there is a bug with duplicated pixels in here--> WHY
|
|
1482
|
+
print(
|
|
1483
|
+
'done bezier') # --> pb as no repick is set the stuff always goes back and forth --> need a no repick just to be sure --> if countour is not connected --> it is needed
|
|
1484
|
+
# img
|
|
1485
|
+
# array([[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
|
1486
|
+
# [0, 0, 0, 0, 0, 1, 0, 0, 0, 0],
|
|
1487
|
+
# [0, 0, 0, 1, 1, 0, 0, 0, 0, 0],
|
|
1488
|
+
# [0, 0, 1, 0, 0, 0, 0, 0, 0, 0],
|
|
1489
|
+
# [0, 1, 0, 0, 0, 0, 0, 0, 0, 0],
|
|
1490
|
+
# [0, 1, 0, 0, 0, 0, 0, 0, 0, 0],
|
|
1491
|
+
# [0, 0, 1, 1, 0, 0, 0, 0, 0, 0],
|
|
1492
|
+
# [0, 0, 0, 0, 1, 1, 1, 0, 0, 0],
|
|
1493
|
+
# [0, 0, 0, 0, 0, 0, 0, 1, 1, 0],
|
|
1494
|
+
# [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], dtype=uint8)
|
|
1495
|
+
|
|
1496
|
+
# NB IL ME MANQUE DES CHANGEMENTS D'orientation sur P1 --> OK now !!!
|
|
1497
|
+
|
|
1498
|
+
# ça marche sur des trucs comme ça
|
|
1499
|
+
|
|
1500
|
+
print("final", timer() - start_time) # 1000 gigantic sorted contours -->
|
|
1501
|
+
|
|
1502
|
+
# all is ok in fact
|
|
1503
|
+
|
|
1504
|
+
from skimage.draw import circle_perimeter
|
|
1505
|
+
|
|
1506
|
+
img = np.zeros((10, 10), dtype=np.uint8)
|
|
1507
|
+
rr, cc = circle_perimeter(4, 4, 3)
|
|
1508
|
+
img[rr, cc] = 255
|
|
1509
|
+
# img
|
|
1510
|
+
# array([[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
|
1511
|
+
# [0, 0, 0, 1, 1, 1, 0, 0, 0, 0],
|
|
1512
|
+
# [0, 0, 1, 0, 0, 0, 1, 0, 0, 0],
|
|
1513
|
+
# [0, 1, 0, 0, 0, 0, 0, 1, 0, 0],
|
|
1514
|
+
# [0, 1, 0, 0, 0, 0, 0, 1, 0, 0],
|
|
1515
|
+
# [0, 1, 0, 0, 0, 0, 0, 1, 0, 0],
|
|
1516
|
+
# [0, 0, 1, 0, 0, 0, 1, 0, 0, 0],
|
|
1517
|
+
# [0, 0, 0, 1, 1, 1, 0, 0, 0, 0],
|
|
1518
|
+
# [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
|
1519
|
+
# [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], dtype=uint8)
|
|
1520
|
+
|
|
1521
|
+
print(img[1, 3])
|
|
1522
|
+
__plot_pavlidis(img, pavlidis(img, 1, 3, False), skip_plot=skip_plot)
|
|
1523
|
+
|
|
1524
|
+
from skimage.draw import circle_perimeter_aa
|
|
1525
|
+
|
|
1526
|
+
img = np.zeros((10, 10), dtype=np.uint8)
|
|
1527
|
+
rr, cc, _ = circle_perimeter_aa(4, 4, 3)
|
|
1528
|
+
img[rr, cc] = 255
|
|
1529
|
+
# img
|
|
1530
|
+
# array([[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
|
1531
|
+
# [ 0, 0, 60, 211, 255, 211, 60, 0, 0, 0],
|
|
1532
|
+
# [ 0, 60, 194, 43, 0, 43, 194, 60, 0, 0],
|
|
1533
|
+
# [ 0, 211, 43, 0, 0, 0, 43, 211, 0, 0],
|
|
1534
|
+
# [ 0, 255, 0, 0, 0, 0, 0, 255, 0, 0],
|
|
1535
|
+
# [ 0, 211, 43, 0, 0, 0, 43, 211, 0, 0],
|
|
1536
|
+
# [ 0, 60, 194, 43, 0, 43, 194, 60, 0, 0],
|
|
1537
|
+
# [ 0, 0, 60, 211, 255, 211, 60, 0, 0, 0],
|
|
1538
|
+
# [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
|
1539
|
+
# [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], dtype=uint8)
|
|
1540
|
+
|
|
1541
|
+
__plot_pavlidis(img, pavlidis(img, 1, 2, True), skip_plot=skip_plot)
|
|
1542
|
+
__plot_pavlidis(img, pavlidis(img, 1, 3, True), skip_plot=skip_plot)
|
|
1543
|
+
# that really seems to work now!!!!
|
|
1544
|
+
|
|
1545
|
+
from skimage.draw import disk
|
|
1546
|
+
|
|
1547
|
+
img = np.zeros((10, 10), dtype=np.uint8)
|
|
1548
|
+
rr, cc = disk((4, 4), 5)
|
|
1549
|
+
img[rr, cc] = 255
|
|
1550
|
+
# img
|
|
1551
|
+
# array([[0, 0, 1, 1, 1, 1, 1, 0, 0, 0],
|
|
1552
|
+
# [0, 1, 1, 1, 1, 1, 1, 1, 0, 0],
|
|
1553
|
+
# [1, 1, 1, 1, 1, 1, 1, 1, 1, 0],
|
|
1554
|
+
# [1, 1, 1, 1, 1, 1, 1, 1, 1, 0],
|
|
1555
|
+
# [1, 1, 1, 1, 1, 1, 1, 1, 1, 0],
|
|
1556
|
+
# [1, 1, 1, 1, 1, 1, 1, 1, 1, 0],
|
|
1557
|
+
# [1, 1, 1, 1, 1, 1, 1, 1, 1, 0],
|
|
1558
|
+
# [0, 1, 1, 1, 1, 1, 1, 1, 0, 0],
|
|
1559
|
+
# [0, 0, 1, 1, 1, 1, 1, 0, 0, 0],
|
|
1560
|
+
# [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], dtype=uint8)
|
|
1561
|
+
|
|
1562
|
+
__plot_pavlidis(img, pavlidis(img, 0, 2, True), skip_plot=skip_plot)
|
|
1563
|
+
|
|
1564
|
+
from skimage.draw import ellipse
|
|
1565
|
+
|
|
1566
|
+
img = np.zeros((10, 12), dtype=np.uint8)
|
|
1567
|
+
rr, cc = ellipse(5, 6, 3, 5, rotation=np.deg2rad(30))
|
|
1568
|
+
img[rr, cc] = 255
|
|
1569
|
+
# img
|
|
1570
|
+
# array([[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
|
1571
|
+
# [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
|
1572
|
+
# [0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0],
|
|
1573
|
+
# [0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0],
|
|
1574
|
+
# [0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0],
|
|
1575
|
+
# [0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0],
|
|
1576
|
+
# [0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0],
|
|
1577
|
+
# [0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0],
|
|
1578
|
+
# [0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0],
|
|
1579
|
+
# [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], dtype=uint8)
|
|
1580
|
+
|
|
1581
|
+
__plot_pavlidis(img, pavlidis(img, 2, 6, True), skip_plot=skip_plot)
|
|
1582
|
+
|
|
1583
|
+
from skimage.draw import ellipse_perimeter
|
|
1584
|
+
|
|
1585
|
+
rr, cc = ellipse_perimeter(2, 3, 4, 5)
|
|
1586
|
+
img = np.zeros((9, 12), dtype=np.uint8)
|
|
1587
|
+
img[rr, cc] = 255
|
|
1588
|
+
# img
|
|
1589
|
+
# array([[0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1],
|
|
1590
|
+
# [0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0],
|
|
1591
|
+
# [0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0],
|
|
1592
|
+
# [0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0],
|
|
1593
|
+
# [0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1],
|
|
1594
|
+
# [1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0],
|
|
1595
|
+
# [0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0],
|
|
1596
|
+
# [0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0],
|
|
1597
|
+
# [1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0]], dtype=uint8)
|
|
1598
|
+
__plot_pavlidis(img, pavlidis(img, 0, 7, False), skip_plot=skip_plot)
|
|
1599
|
+
__plot_pavlidis(img, pavlidis(img, 8, 0, False), skip_plot=skip_plot)
|
|
1600
|
+
|
|
1601
|
+
img = np.zeros((10, 10), dtype=np.uint8)
|
|
1602
|
+
rr, cc = ellipse_perimeter(5, 5, 3, 4)
|
|
1603
|
+
img[rr, cc] = 255
|
|
1604
|
+
# img
|
|
1605
|
+
# array([[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
|
1606
|
+
# [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
|
1607
|
+
# [0, 0, 0, 1, 1, 1, 1, 1, 0, 0],
|
|
1608
|
+
# [0, 0, 1, 0, 0, 0, 0, 0, 1, 0],
|
|
1609
|
+
# [0, 1, 0, 0, 0, 0, 0, 0, 0, 1],
|
|
1610
|
+
# [0, 1, 0, 0, 0, 0, 0, 0, 0, 1],
|
|
1611
|
+
# [0, 1, 0, 0, 0, 0, 0, 0, 0, 1],
|
|
1612
|
+
# [0, 0, 1, 0, 0, 0, 0, 0, 1, 0],
|
|
1613
|
+
# [0, 0, 0, 1, 1, 1, 1, 1, 0, 0],
|
|
1614
|
+
# [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], dtype=uint8)
|
|
1615
|
+
|
|
1616
|
+
__plot_pavlidis(img, pavlidis(img, 4, 1, True), skip_plot=skip_plot)
|
|
1617
|
+
|
|
1618
|
+
# this algo works exactly as I desire --> quite good!!!
|
|
1619
|
+
from skimage.draw import line
|
|
1620
|
+
|
|
1621
|
+
img = np.zeros((10, 10), dtype=np.uint8)
|
|
1622
|
+
rr, cc = line(1, 1, 8, 8)
|
|
1623
|
+
img[rr, cc] = 255
|
|
1624
|
+
# img
|
|
1625
|
+
# array([[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
|
1626
|
+
# [0, 1, 0, 0, 0, 0, 0, 0, 0, 0],
|
|
1627
|
+
# [0, 0, 1, 0, 0, 0, 0, 0, 0, 0],
|
|
1628
|
+
# [0, 0, 0, 1, 0, 0, 0, 0, 0, 0],
|
|
1629
|
+
# [0, 0, 0, 0, 1, 0, 0, 0, 0, 0],
|
|
1630
|
+
# [0, 0, 0, 0, 0, 1, 0, 0, 0, 0],
|
|
1631
|
+
# [0, 0, 0, 0, 0, 0, 1, 0, 0, 0],
|
|
1632
|
+
# [0, 0, 0, 0, 0, 0, 0, 1, 0, 0],
|
|
1633
|
+
# [0, 0, 0, 0, 0, 0, 0, 0, 1, 0],
|
|
1634
|
+
# [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], dtype=uint8)
|
|
1635
|
+
|
|
1636
|
+
__plot_pavlidis(img, pavlidis(img, 1, 1, False), skip_plot=skip_plot)
|
|
1637
|
+
__plot_pavlidis(img, pavlidis(img, 8, 8, False), skip_plot=skip_plot)
|
|
1638
|
+
|
|
1639
|
+
from skimage.draw import line_aa
|
|
1640
|
+
|
|
1641
|
+
img = np.zeros((10, 10), dtype=np.uint8)
|
|
1642
|
+
rr, cc, val = line_aa(1, 1, 8, 8)
|
|
1643
|
+
img[rr, cc] = 255
|
|
1644
|
+
# img
|
|
1645
|
+
# array([[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
|
1646
|
+
# [0, 255, 74, 0, 0, 0, 0, 0, 0, 0],
|
|
1647
|
+
# [0, 74, 255, 74, 0, 0, 0, 0, 0, 0],
|
|
1648
|
+
# [0, 0, 74, 255, 74, 0, 0, 0, 0, 0],
|
|
1649
|
+
# [0, 0, 0, 74, 255, 74, 0, 0, 0, 0],
|
|
1650
|
+
# [0, 0, 0, 0, 74, 255, 74, 0, 0, 0],
|
|
1651
|
+
# [0, 0, 0, 0, 0, 74, 255, 74, 0, 0],
|
|
1652
|
+
# [0, 0, 0, 0, 0, 0, 74, 255, 74, 0],
|
|
1653
|
+
# [0, 0, 0, 0, 0, 0, 0, 74, 255, 0],
|
|
1654
|
+
# [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], dtype=uint8)
|
|
1655
|
+
|
|
1656
|
+
__plot_pavlidis(img, pavlidis(img, 1, 1, False),
|
|
1657
|
+
skip_plot=skip_plot) # ça marche du tonnerre c'est vraiment exactement le contour et juste le contour --> parfait!!!
|
|
1658
|
+
__plot_pavlidis(img, pavlidis(img, 8, 8, False),
|
|
1659
|
+
skip_plot=skip_plot) # ici il y a une petite erreur mais je pense ok en fait --> pr eviter erreur faudrait partir avec un orientation differente --> TODO see if I can find a fix otherwise ok for now -> check if this is one of the exception for the starting pixels --> not so bad in fact and I dunno how I could prevent that
|
|
1660
|
+
|
|
1661
|
+
from skimage.draw import polygon
|
|
1662
|
+
|
|
1663
|
+
img = np.zeros((10, 10), dtype=np.uint8)
|
|
1664
|
+
r = np.array([1, 2, 8])
|
|
1665
|
+
c = np.array([1, 7, 4])
|
|
1666
|
+
rr, cc = polygon(r, c)
|
|
1667
|
+
img[rr, cc] = 255
|
|
1668
|
+
# img
|
|
1669
|
+
# array([[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
|
1670
|
+
# [0, 1, 0, 0, 0, 0, 0, 0, 0, 0],
|
|
1671
|
+
# [0, 0, 1, 1, 1, 1, 1, 1, 0, 0],
|
|
1672
|
+
# [0, 0, 1, 1, 1, 1, 1, 0, 0, 0],
|
|
1673
|
+
# [0, 0, 0, 1, 1, 1, 1, 0, 0, 0],
|
|
1674
|
+
# [0, 0, 0, 1, 1, 1, 0, 0, 0, 0],
|
|
1675
|
+
# [0, 0, 0, 0, 1, 1, 0, 0, 0, 0],
|
|
1676
|
+
# [0, 0, 0, 0, 1, 0, 0, 0, 0, 0],
|
|
1677
|
+
# [0, 0, 0, 0, 1, 0, 0, 0, 0, 0],
|
|
1678
|
+
# [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], dtype=uint8)
|
|
1679
|
+
|
|
1680
|
+
__plot_pavlidis(img, pavlidis(img, 1, 1, True),
|
|
1681
|
+
skip_plot=skip_plot) # this one has a bug but it's a case I'll never face --> would have to choose a better start
|
|
1682
|
+
__plot_pavlidis(img, pavlidis(img, 8, 4, True),
|
|
1683
|
+
skip_plot=skip_plot) # this one has a bug but it's a case I'll never face --> would have to choose a better start # bug here too --> gives duplicated pixels
|
|
1684
|
+
__plot_pavlidis(img, pavlidis(img, 4, 3, True),
|
|
1685
|
+
skip_plot=skip_plot) # I think there is anyway no way of getting a clockwise sorted contour for this shape OR ?
|
|
1686
|
+
|
|
1687
|
+
from skimage.draw import polygon_perimeter
|
|
1688
|
+
|
|
1689
|
+
img = np.zeros((10, 10), dtype=np.uint8)
|
|
1690
|
+
rr, cc = polygon_perimeter([5, -1, 5, 10],
|
|
1691
|
+
[-1, 5, 11, 5],
|
|
1692
|
+
shape=img.shape, clip=True)
|
|
1693
|
+
img[rr, cc] = 255
|
|
1694
|
+
# img
|
|
1695
|
+
# array([[0, 0, 0, 0, 1, 1, 1, 0, 0, 0],
|
|
1696
|
+
# [0, 0, 0, 1, 0, 0, 0, 1, 0, 0],
|
|
1697
|
+
# [0, 0, 1, 0, 0, 0, 0, 0, 1, 0],
|
|
1698
|
+
# [0, 1, 0, 0, 0, 0, 0, 0, 0, 1],
|
|
1699
|
+
# [1, 0, 0, 0, 0, 0, 0, 0, 0, 1],
|
|
1700
|
+
# [1, 0, 0, 0, 0, 0, 0, 0, 0, 1],
|
|
1701
|
+
# [1, 0, 0, 0, 0, 0, 0, 0, 0, 1],
|
|
1702
|
+
# [0, 1, 1, 0, 0, 0, 0, 0, 0, 1],
|
|
1703
|
+
# [0, 0, 0, 1, 0, 0, 0, 1, 1, 0],
|
|
1704
|
+
# [0, 0, 0, 0, 1, 1, 1, 0, 0, 0]], dtype=uint8)
|
|
1705
|
+
|
|
1706
|
+
__plot_pavlidis(img, pavlidis(img, 0, 4, True), skip_plot=skip_plot)
|
|
1707
|
+
|
|
1708
|
+
# generate random shapes
|
|
1709
|
+
import skimage.draw
|
|
1710
|
+
|
|
1711
|
+
# img, labels = skimage.draw.random_shapes((32, 32), max_shapes=3)
|
|
1712
|
+
img, labels = skimage.draw.random_shapes((64, 64), max_shapes=1)
|
|
1713
|
+
|
|
1714
|
+
img = img[..., 0]
|
|
1715
|
+
img[img == 255] = 0
|
|
1716
|
+
img[img != 0] = 255
|
|
1717
|
+
skip_plot = False
|
|
1718
|
+
# np.nonzero(x)
|
|
1719
|
+
# plt.imshow(img)
|
|
1720
|
+
# plt.show()
|
|
1721
|
+
|
|
1722
|
+
first_pixel = np.nonzero(img) # np.argwhere(img!=255)#np.nonzero(img)
|
|
1723
|
+
print(
|
|
1724
|
+
labels) # NB THERE ARE DUPLICATED POINTS WITH TRIANGLES --> IS THERE A SMART WAY TO HANDLE DUPLICATED POINTS --> AND OR TO RECONNECT THE MISSING PARTS --> MAYBE BUT THINK ABOUT IT!!!
|
|
1725
|
+
# NB SHALL I DO A NO REPICK !!! BY CUTTING POINTS ALREADY TAKEN ?????
|
|
1726
|
+
|
|
1727
|
+
__plot_pavlidis(img, pavlidis(img, first_pixel[0][0], first_pixel[1][0], True),
|
|
1728
|
+
skip_plot=skip_plot) # maybe will be a pb with 1 px wide cells --> test it and see if there is a fix for that or not ???
|
|
1729
|
+
# it really seems to work well!!!
|
|
1730
|
+
|
|
1731
|
+
# coords of duplicated triangle (31, 47)
|
|
1732
|
+
# [(30, 47), (31, 47), (32, 48), (33, 48), (34, 49), (35, 49), (36, 50), (37, 51), (38, 51), (39, 52), (40, 52),
|
|
1733
|
+
# (41, 53), (42, 54), (43, 54), (44, 55), (45, 55), (46, 56), (47, 56), (48, 57), (49, 58), (50, 58), (51, 59),
|
|
1734
|
+
# (52, 59), (53, 60), (54, 61), (54, 60), (54, 59), (54, 58), (54, 57), (54, 56), (54, 55), (54, 54), (54, 53),
|
|
1735
|
+
# (54, 52), (54, 51), (54, 50), (54, 49), (54, 48), (54, 47), (54, 46), (54, 45), (54, 44), (54, 43), (54, 42),
|
|
1736
|
+
# (54, 41), (54, 40), (54, 39), (54, 38), (54, 37), (54, 36), (54, 35), (54, 34), (53, 35), (52, 36), (51, 36),
|
|
1737
|
+
# (50, 37), (49, 37), (48, 38), (47, 38), (46, 39), (45, 39), (44, 40), (43, 40), (42, 41), (41, 42), (40, 42),
|
|
1738
|
+
# (39, 43), (38, 43), (37, 44), (36, 44), (35, 45), (34, 45), (33, 46), (32, 46), (31, 47)]
|
|
1739
|
+
|
|
1740
|
+
# another duplicated triangle --> is there an easy fix ???
|
|
1741
|
+
# [(37, 28), (38, 28), (39, 29), (40, 29), (41, 30), (42, 30), (43, 31), (44, 31), (45, 32), (46, 33), (47, 33),
|
|
1742
|
+
# (48, 34), (49, 34), (50, 35), (51, 35), (52, 36), (53, 37), (53, 36), (53, 35), (53, 34), (53, 33), (53, 32),
|
|
1743
|
+
# (53, 31), (53, 30), (53, 29), (53, 28), (53, 27), (53, 26), (53, 25), (53, 24), (53, 23), (53, 22), (53, 21),
|
|
1744
|
+
# (53, 20), (53, 19), (52, 20), (51, 21), (50, 21), (49, 22), (48, 22), (47, 23), (46, 23), (45, 24), (44, 25),
|
|
1745
|
+
# (43, 25), (42, 26), (41, 26), (40, 27), (39, 27), (38, 28)]
|
|
1746
|
+
# DUPLICATED
|
|
1747
|
+
# POINTS
|
|
1748
|
+
# DETECTED
|
|
1749
|
+
|
|
1750
|
+
# almost all done --> just need fix all soon --> TODO
|
|
1751
|
+
|
|
1752
|
+
# [('triangle', ((16, 31), (0, 17)))]
|
|
1753
|
+
# [(16, 8), (17, 8), (18, 9), (19, 9), (20, 10), (21, 10), (22, 11), (23, 12), (24, 12), (25, 13), (26, 13), (27, 14),
|
|
1754
|
+
# (28, 14), (29, 15), (30, 16), (30, 15), (30, 14), (30, 13), (30, 12), (30, 11), (30, 10), (30, 9), (30, 8),
|
|
1755
|
+
# (30, 7), (30, 6), (30, 5), (30, 4), (30, 3), (30, 2), (30, 1), (30, 0), (29, 1), (28, 2), (27, 2), (26, 3),
|
|
1756
|
+
# (25, 3), (24, 4), (23, 4), (22, 5), (21, 6), (20, 6), (19, 7), (18, 7), (17, 8)]
|
|
1757
|
+
|
|
1758
|
+
# [('triangle', ((38, 41), (52, 55)))]
|
|
1759
|
+
# 38 53 255 color_of_interest
|
|
1760
|
+
# [(38, 53), (39, 53), (40, 54), (40, 53), (40, 52), (39, 53)]
|
|
1761
|
+
# DUPLICATED POINTS DETECTED
|
|
1762
|
+
# [(39, 53)]
|
|
1763
|
+
|
|
1764
|
+
# maybe remove last point/dupe to get rid of the error --> would that work ???
|
|
1765
|
+
|
|
1766
|
+
# deduplicated
|
|
1767
|
+
# coords[(29, 50), (30, 50), (31, 51), (31, 50), (31, 49)]
|
|
1768
|
+
|
|
1769
|
+
# nb mini ellipse equivalent to a single px area cell has no error in contour and no dupes --> VERY GOOD
|
|
1770
|
+
# [('ellipse', ((21, 24), (0, 3)))]
|
|
1771
|
+
# [(21, 1), (22, 2), (23, 1), (22, 0)]
|
|
1772
|
+
# no dupes
|
|
1773
|
+
|
|
1774
|
+
# MEGA TODO: NB DEDUPLICATION DID NOT WORK --> distance between end points > sqrt 2 --> is there any way I can fix duplication in a smarter way maybe by 2D distance between adjacent points --> so that I minimize it ???? --> THINK ABOUT THAT!!!
|
|
1775
|
+
# but in fact there is no way to get a perfect contour from this OR --> maybe yes upon removal of pixels futher away than sqrt 2 --> THINK ABOUT IT AND TEST THAT!!!!
|
|
1776
|
+
# nb if I remove 4,30 and one 5,30 then it WOULD WORK --> I WOULD GET A CLOSED CONTOUR THAT IS MINIMAL --> THINK ABOUT THE FASTEST WAY TO DO THAT
|
|
1777
|
+
# [('triangle', ((4, 16), (24, 37)))]
|
|
1778
|
+
# 4 30 255 color_of_interest
|
|
1779
|
+
# [(4, 30), (5, 30), (6, 31), (7, 31), (8, 32), (9, 32), (10, 33), (11, 33), (12, 34), (13, 34), (14, 35), (15, 36), (15, 35), (15, 34), (15, 33), (15, 32), (15, 31), (15, 30), (15, 29), (15, 28), (15, 27), (15, 26), (15, 25), (15, 24), (14, 25), (13, 26), (12, 26), (11, 27), (10, 27), (9, 28), (8, 28), (7, 29), (6, 29), (5, 30)]
|
|
1780
|
+
# DUPLICATED POINTS DETECTED
|
|
1781
|
+
# [(5, 30)]
|
|
1782
|
+
# deduplicated coords [(4, 30), (5, 30), (6, 31), (7, 31), (8, 32), (9, 32), (10, 33), (11, 33), (12, 34), (13, 34), (14, 35), (15, 36), (15, 35), (15, 34), (15, 33), (15, 32), (15, 31), (15, 30), (15, 29), (15, 28), (15, 27), (15, 26), (15, 25), (15, 24), (14, 25), (13, 26), (12, 26), (11, 27), (10, 27), (9, 28), (8, 28), (7, 29), (6, 29)]
|
|
1783
|
+
# dist between extreme points after deduplication 2.23606797749979 False
|