canns 0.13.1__py3-none-any.whl → 0.14.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.
- canns/analyzer/data/__init__.py +5 -1
- canns/analyzer/data/asa/__init__.py +27 -12
- canns/analyzer/data/asa/cohospace.py +336 -10
- canns/analyzer/data/asa/config.py +3 -0
- canns/analyzer/data/asa/embedding.py +48 -45
- canns/analyzer/data/asa/path.py +104 -2
- canns/analyzer/data/asa/plotting.py +88 -19
- canns/analyzer/data/asa/tda.py +11 -4
- canns/analyzer/data/cell_classification/__init__.py +97 -0
- canns/analyzer/data/cell_classification/core/__init__.py +26 -0
- canns/analyzer/data/cell_classification/core/grid_cells.py +633 -0
- canns/analyzer/data/cell_classification/core/grid_modules_leiden.py +288 -0
- canns/analyzer/data/cell_classification/core/head_direction.py +347 -0
- canns/analyzer/data/cell_classification/core/spatial_analysis.py +431 -0
- canns/analyzer/data/cell_classification/io/__init__.py +5 -0
- canns/analyzer/data/cell_classification/io/matlab_loader.py +417 -0
- canns/analyzer/data/cell_classification/utils/__init__.py +39 -0
- canns/analyzer/data/cell_classification/utils/circular_stats.py +383 -0
- canns/analyzer/data/cell_classification/utils/correlation.py +318 -0
- canns/analyzer/data/cell_classification/utils/geometry.py +442 -0
- canns/analyzer/data/cell_classification/utils/image_processing.py +416 -0
- canns/analyzer/data/cell_classification/visualization/__init__.py +19 -0
- canns/analyzer/data/cell_classification/visualization/grid_plots.py +292 -0
- canns/analyzer/data/cell_classification/visualization/hd_plots.py +200 -0
- canns/analyzer/metrics/__init__.py +2 -1
- canns/analyzer/visualization/core/config.py +46 -4
- canns/data/__init__.py +6 -1
- canns/data/datasets.py +154 -1
- canns/data/loaders.py +37 -0
- canns/pipeline/__init__.py +13 -9
- canns/pipeline/__main__.py +6 -0
- canns/pipeline/asa/runner.py +105 -41
- canns/pipeline/asa_gui/__init__.py +68 -0
- canns/pipeline/asa_gui/__main__.py +6 -0
- canns/pipeline/asa_gui/analysis_modes/__init__.py +42 -0
- canns/pipeline/asa_gui/analysis_modes/base.py +39 -0
- canns/pipeline/asa_gui/analysis_modes/batch_mode.py +21 -0
- canns/pipeline/asa_gui/analysis_modes/cohomap_mode.py +56 -0
- canns/pipeline/asa_gui/analysis_modes/cohospace_mode.py +194 -0
- canns/pipeline/asa_gui/analysis_modes/decode_mode.py +52 -0
- canns/pipeline/asa_gui/analysis_modes/fr_mode.py +81 -0
- canns/pipeline/asa_gui/analysis_modes/frm_mode.py +92 -0
- canns/pipeline/asa_gui/analysis_modes/gridscore_mode.py +123 -0
- canns/pipeline/asa_gui/analysis_modes/pathcompare_mode.py +199 -0
- canns/pipeline/asa_gui/analysis_modes/tda_mode.py +112 -0
- canns/pipeline/asa_gui/app.py +29 -0
- canns/pipeline/asa_gui/controllers/__init__.py +6 -0
- canns/pipeline/asa_gui/controllers/analysis_controller.py +59 -0
- canns/pipeline/asa_gui/controllers/preprocess_controller.py +89 -0
- canns/pipeline/asa_gui/core/__init__.py +15 -0
- canns/pipeline/asa_gui/core/cache.py +14 -0
- canns/pipeline/asa_gui/core/runner.py +1936 -0
- canns/pipeline/asa_gui/core/state.py +324 -0
- canns/pipeline/asa_gui/core/worker.py +260 -0
- canns/pipeline/asa_gui/main_window.py +184 -0
- canns/pipeline/asa_gui/models/__init__.py +7 -0
- canns/pipeline/asa_gui/models/config.py +14 -0
- canns/pipeline/asa_gui/models/job.py +31 -0
- canns/pipeline/asa_gui/models/presets.py +21 -0
- canns/pipeline/asa_gui/resources/__init__.py +16 -0
- canns/pipeline/asa_gui/resources/dark.qss +167 -0
- canns/pipeline/asa_gui/resources/light.qss +163 -0
- canns/pipeline/asa_gui/resources/styles.qss +130 -0
- canns/pipeline/asa_gui/utils/__init__.py +1 -0
- canns/pipeline/asa_gui/utils/formatters.py +15 -0
- canns/pipeline/asa_gui/utils/io_adapters.py +40 -0
- canns/pipeline/asa_gui/utils/validators.py +41 -0
- canns/pipeline/asa_gui/views/__init__.py +1 -0
- canns/pipeline/asa_gui/views/help_content.py +171 -0
- canns/pipeline/asa_gui/views/pages/__init__.py +6 -0
- canns/pipeline/asa_gui/views/pages/analysis_page.py +565 -0
- canns/pipeline/asa_gui/views/pages/preprocess_page.py +492 -0
- canns/pipeline/asa_gui/views/panels/__init__.py +1 -0
- canns/pipeline/asa_gui/views/widgets/__init__.py +21 -0
- canns/pipeline/asa_gui/views/widgets/artifacts_tab.py +44 -0
- canns/pipeline/asa_gui/views/widgets/drop_zone.py +80 -0
- canns/pipeline/asa_gui/views/widgets/file_list.py +27 -0
- canns/pipeline/asa_gui/views/widgets/gridscore_tab.py +308 -0
- canns/pipeline/asa_gui/views/widgets/help_dialog.py +27 -0
- canns/pipeline/asa_gui/views/widgets/image_tab.py +50 -0
- canns/pipeline/asa_gui/views/widgets/image_viewer.py +97 -0
- canns/pipeline/asa_gui/views/widgets/log_box.py +16 -0
- canns/pipeline/asa_gui/views/widgets/pathcompare_tab.py +200 -0
- canns/pipeline/asa_gui/views/widgets/popup_combo.py +25 -0
- canns/pipeline/gallery/__init__.py +15 -5
- canns/pipeline/gallery/__main__.py +11 -0
- canns/pipeline/gallery/app.py +705 -0
- canns/pipeline/gallery/runner.py +790 -0
- canns/pipeline/gallery/state.py +51 -0
- canns/pipeline/gallery/styles.tcss +123 -0
- canns/pipeline/launcher.py +81 -0
- {canns-0.13.1.dist-info → canns-0.14.0.dist-info}/METADATA +11 -1
- canns-0.14.0.dist-info/RECORD +163 -0
- canns-0.14.0.dist-info/entry_points.txt +5 -0
- canns/pipeline/_base.py +0 -50
- canns-0.13.1.dist-info/RECORD +0 -89
- canns-0.13.1.dist-info/entry_points.txt +0 -3
- {canns-0.13.1.dist-info → canns-0.14.0.dist-info}/WHEEL +0 -0
- {canns-0.13.1.dist-info → canns-0.14.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,416 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Image Processing Utilities
|
|
3
|
+
|
|
4
|
+
Functions for image manipulation including rotation, filtering, and morphological operations.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import numpy as np
|
|
8
|
+
from scipy import ndimage
|
|
9
|
+
from skimage import measure, morphology
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def rotate_image(
|
|
13
|
+
image: np.ndarray,
|
|
14
|
+
angle: float,
|
|
15
|
+
output_shape: tuple[int, int] | None = None,
|
|
16
|
+
method: str = "bilinear",
|
|
17
|
+
preserve_range: bool = True,
|
|
18
|
+
) -> np.ndarray:
|
|
19
|
+
"""
|
|
20
|
+
Rotate an image by a given angle.
|
|
21
|
+
|
|
22
|
+
Parameters
|
|
23
|
+
----------
|
|
24
|
+
image : np.ndarray
|
|
25
|
+
2D image array to rotate
|
|
26
|
+
angle : float
|
|
27
|
+
Rotation angle in degrees. Positive values rotate counter-clockwise.
|
|
28
|
+
output_shape : tuple of int, optional
|
|
29
|
+
Shape of the output image (height, width). If None, uses input shape.
|
|
30
|
+
method : str, optional
|
|
31
|
+
Interpolation method: 'bilinear' (default), 'nearest', 'cubic'
|
|
32
|
+
preserve_range : bool, optional
|
|
33
|
+
Whether to preserve the original value range. Default is True.
|
|
34
|
+
|
|
35
|
+
Returns
|
|
36
|
+
-------
|
|
37
|
+
rotated : np.ndarray
|
|
38
|
+
Rotated image
|
|
39
|
+
|
|
40
|
+
Examples
|
|
41
|
+
--------
|
|
42
|
+
>>> image = np.random.rand(50, 50)
|
|
43
|
+
>>> rotated = rotate_image(image, 30) # Rotate 30 degrees CCW
|
|
44
|
+
>>> rotated_90 = rotate_image(image, 90) # Rotate 90 degrees
|
|
45
|
+
|
|
46
|
+
Notes
|
|
47
|
+
-----
|
|
48
|
+
Based on MATLAB's imrotate function. Uses scipy.ndimage.rotate.
|
|
49
|
+
The rotation is performed around the center of the image.
|
|
50
|
+
"""
|
|
51
|
+
# Map method names to scipy orders
|
|
52
|
+
order_map = {"nearest": 0, "bilinear": 1, "cubic": 3}
|
|
53
|
+
order = order_map.get(method.lower(), 1)
|
|
54
|
+
|
|
55
|
+
# Rotate image (scipy rotates counter-clockwise for positive angles, same as MATLAB)
|
|
56
|
+
rotated = ndimage.rotate(
|
|
57
|
+
image,
|
|
58
|
+
angle,
|
|
59
|
+
order=order,
|
|
60
|
+
reshape=False if output_shape else True,
|
|
61
|
+
mode="constant",
|
|
62
|
+
cval=0.0,
|
|
63
|
+
prefilter=True,
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
# Resize to requested output shape if specified
|
|
67
|
+
if output_shape is not None:
|
|
68
|
+
if rotated.shape != output_shape:
|
|
69
|
+
# Simple crop/pad to match output shape
|
|
70
|
+
h, w = rotated.shape
|
|
71
|
+
oh, ow = output_shape
|
|
72
|
+
|
|
73
|
+
# Calculate center offsets
|
|
74
|
+
start_h = (h - oh) // 2 if h > oh else 0
|
|
75
|
+
start_w = (w - ow) // 2 if w > ow else 0
|
|
76
|
+
|
|
77
|
+
if h >= oh and w >= ow:
|
|
78
|
+
# Crop
|
|
79
|
+
rotated = rotated[start_h : start_h + oh, start_w : start_w + ow]
|
|
80
|
+
elif h <= oh and w <= ow:
|
|
81
|
+
# Pad
|
|
82
|
+
pad_h = (oh - h) // 2
|
|
83
|
+
pad_w = (ow - w) // 2
|
|
84
|
+
rotated = np.pad(
|
|
85
|
+
rotated,
|
|
86
|
+
((pad_h, oh - h - pad_h), (pad_w, ow - w - pad_w)),
|
|
87
|
+
mode="constant",
|
|
88
|
+
constant_values=0,
|
|
89
|
+
)
|
|
90
|
+
else:
|
|
91
|
+
# Mixed crop/pad - just use zoom
|
|
92
|
+
from scipy.ndimage import zoom
|
|
93
|
+
|
|
94
|
+
zoom_factors = (oh / h, ow / w)
|
|
95
|
+
rotated = zoom(rotated, zoom_factors, order=order)
|
|
96
|
+
|
|
97
|
+
return rotated
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def find_regional_maxima(
|
|
101
|
+
image: np.ndarray, connectivity: int = 1, allow_diagonal: bool = False
|
|
102
|
+
) -> np.ndarray:
|
|
103
|
+
"""
|
|
104
|
+
Find regional maxima in an image.
|
|
105
|
+
|
|
106
|
+
A regional maximum is a connected component of pixels with the same value,
|
|
107
|
+
surrounded by pixels with strictly lower values.
|
|
108
|
+
|
|
109
|
+
Parameters
|
|
110
|
+
----------
|
|
111
|
+
image : np.ndarray
|
|
112
|
+
2D input image
|
|
113
|
+
connectivity : int, optional
|
|
114
|
+
Connectivity for defining neighbors:
|
|
115
|
+
- 1: 4-connectivity (default, equivalent to MATLAB connectivity=4)
|
|
116
|
+
- 2: 8-connectivity (equivalent to MATLAB connectivity=8)
|
|
117
|
+
allow_diagonal : bool, optional
|
|
118
|
+
If True, uses 8-connectivity. If False, uses 4-connectivity.
|
|
119
|
+
Overrides connectivity parameter if specified.
|
|
120
|
+
|
|
121
|
+
Returns
|
|
122
|
+
-------
|
|
123
|
+
maxima : np.ndarray
|
|
124
|
+
Binary image where True indicates regional maxima
|
|
125
|
+
|
|
126
|
+
Examples
|
|
127
|
+
--------
|
|
128
|
+
>>> # Create image with some peaks
|
|
129
|
+
>>> x = np.linspace(-3, 3, 50)
|
|
130
|
+
>>> xx, yy = np.meshgrid(x, x)
|
|
131
|
+
>>> image = np.exp(-(xx**2 + yy**2)) + 0.5 * np.exp(-((xx-1.5)**2 + (yy-1.5)**2))
|
|
132
|
+
>>> maxima = find_regional_maxima(image)
|
|
133
|
+
>>> print(f"Found {np.sum(maxima)} maxima")
|
|
134
|
+
|
|
135
|
+
Notes
|
|
136
|
+
-----
|
|
137
|
+
Based on MATLAB's imregionalmax function.
|
|
138
|
+
|
|
139
|
+
IMPORTANT: Connectivity mapping differs between MATLAB and Python!
|
|
140
|
+
- MATLAB imregionalmax(image, 4) → Python connectivity=1
|
|
141
|
+
- MATLAB imregionalmax(image, 8) → Python connectivity=2
|
|
142
|
+
|
|
143
|
+
Uses skimage.morphology.local_maxima for detection.
|
|
144
|
+
"""
|
|
145
|
+
# Handle connectivity parameter
|
|
146
|
+
if allow_diagonal:
|
|
147
|
+
connectivity = 2
|
|
148
|
+
|
|
149
|
+
# Use skimage to find local maxima
|
|
150
|
+
# Note: skimage uses different connectivity convention than MATLAB
|
|
151
|
+
maxima = morphology.local_maxima(image, connectivity=connectivity)
|
|
152
|
+
|
|
153
|
+
return maxima
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
def find_contours_at_level(image: np.ndarray, level: float) -> list:
|
|
157
|
+
"""
|
|
158
|
+
Find contours in an image at a specific threshold level.
|
|
159
|
+
|
|
160
|
+
Parameters
|
|
161
|
+
----------
|
|
162
|
+
image : np.ndarray
|
|
163
|
+
2D input image
|
|
164
|
+
level : float
|
|
165
|
+
Threshold level for contour detection
|
|
166
|
+
|
|
167
|
+
Returns
|
|
168
|
+
-------
|
|
169
|
+
contours : list of np.ndarray
|
|
170
|
+
List of contours. Each contour is an (N, 2) array of (row, col) coordinates.
|
|
171
|
+
Note: Returns (row, col) = (y, x), opposite of MATLAB's (x, y) order!
|
|
172
|
+
|
|
173
|
+
Examples
|
|
174
|
+
--------
|
|
175
|
+
>>> # Create a simple image with a circular feature
|
|
176
|
+
>>> x = np.linspace(-5, 5, 100)
|
|
177
|
+
>>> xx, yy = np.meshgrid(x, x)
|
|
178
|
+
>>> image = np.exp(-(xx**2 + yy**2))
|
|
179
|
+
>>> contours = find_contours_at_level(image, 0.5)
|
|
180
|
+
>>> print(f"Found {len(contours)} contours")
|
|
181
|
+
|
|
182
|
+
Notes
|
|
183
|
+
-----
|
|
184
|
+
Based on MATLAB's contourc function.
|
|
185
|
+
Uses skimage.measure.find_contours.
|
|
186
|
+
|
|
187
|
+
CRITICAL: Coordinate order difference!
|
|
188
|
+
- MATLAB contourc: returns [x; y] (column major)
|
|
189
|
+
- Python find_contours: returns (row, col) = (y, x)
|
|
190
|
+
|
|
191
|
+
For gridness analysis, this coordinate swap must be handled!
|
|
192
|
+
"""
|
|
193
|
+
contours = measure.find_contours(image, level)
|
|
194
|
+
return contours
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
def gaussian_filter_2d(
|
|
198
|
+
image: np.ndarray, sigma: float, mode: str = "reflect", truncate: float = 4.0
|
|
199
|
+
) -> np.ndarray:
|
|
200
|
+
"""
|
|
201
|
+
Apply 2D Gaussian filter to an image.
|
|
202
|
+
|
|
203
|
+
Parameters
|
|
204
|
+
----------
|
|
205
|
+
image : np.ndarray
|
|
206
|
+
2D input image
|
|
207
|
+
sigma : float
|
|
208
|
+
Standard deviation of Gaussian kernel
|
|
209
|
+
mode : str, optional
|
|
210
|
+
Boundary handling mode:
|
|
211
|
+
- 'reflect' (default): reflect at boundaries
|
|
212
|
+
- 'constant': pad with zeros
|
|
213
|
+
- 'nearest': replicate edge values
|
|
214
|
+
- 'mirror': mirror at boundaries
|
|
215
|
+
- 'wrap': wrap around
|
|
216
|
+
truncate : float, optional
|
|
217
|
+
Truncate filter at this many standard deviations. Default is 4.0.
|
|
218
|
+
|
|
219
|
+
Returns
|
|
220
|
+
-------
|
|
221
|
+
filtered : np.ndarray
|
|
222
|
+
Filtered image
|
|
223
|
+
|
|
224
|
+
Examples
|
|
225
|
+
--------
|
|
226
|
+
>>> image = np.random.rand(100, 100)
|
|
227
|
+
>>> smoothed = gaussian_filter_2d(image, sigma=2.0)
|
|
228
|
+
|
|
229
|
+
Notes
|
|
230
|
+
-----
|
|
231
|
+
Based on MATLAB's imgaussfilt function.
|
|
232
|
+
Uses scipy.ndimage.gaussian_filter.
|
|
233
|
+
"""
|
|
234
|
+
filtered = ndimage.gaussian_filter(image, sigma=sigma, mode=mode, truncate=truncate)
|
|
235
|
+
return filtered
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
def dilate_image(
|
|
239
|
+
image: np.ndarray,
|
|
240
|
+
footprint: np.ndarray | None = None,
|
|
241
|
+
selem_type: str = "square",
|
|
242
|
+
selem_size: int = 3,
|
|
243
|
+
) -> np.ndarray:
|
|
244
|
+
"""
|
|
245
|
+
Perform morphological dilation on a binary image.
|
|
246
|
+
|
|
247
|
+
Parameters
|
|
248
|
+
----------
|
|
249
|
+
image : np.ndarray
|
|
250
|
+
Binary input image
|
|
251
|
+
footprint : np.ndarray, optional
|
|
252
|
+
Structuring element. If None, uses selem_type and selem_size.
|
|
253
|
+
selem_type : str, optional
|
|
254
|
+
Type of structuring element: 'square', 'disk', 'diamond'
|
|
255
|
+
Default is 'square'.
|
|
256
|
+
selem_size : int, optional
|
|
257
|
+
Size of structuring element. Default is 3.
|
|
258
|
+
|
|
259
|
+
Returns
|
|
260
|
+
-------
|
|
261
|
+
dilated : np.ndarray
|
|
262
|
+
Dilated image
|
|
263
|
+
|
|
264
|
+
Examples
|
|
265
|
+
--------
|
|
266
|
+
>>> binary_image = (np.random.rand(50, 50) > 0.8)
|
|
267
|
+
>>> dilated = dilate_image(binary_image, selem_type='square', selem_size=3)
|
|
268
|
+
|
|
269
|
+
Notes
|
|
270
|
+
-----
|
|
271
|
+
Based on MATLAB's imdilate function.
|
|
272
|
+
Uses skimage.morphology.dilation.
|
|
273
|
+
"""
|
|
274
|
+
if footprint is None:
|
|
275
|
+
if selem_type == "square":
|
|
276
|
+
footprint = morphology.footprint_rectangle((selem_size, selem_size))
|
|
277
|
+
elif selem_type == "disk":
|
|
278
|
+
footprint = morphology.disk(selem_size)
|
|
279
|
+
elif selem_type == "diamond":
|
|
280
|
+
footprint = morphology.diamond(selem_size)
|
|
281
|
+
else:
|
|
282
|
+
raise ValueError(f"Unknown structuring element type: {selem_type}")
|
|
283
|
+
|
|
284
|
+
dilated = morphology.binary_dilation(image, footprint=footprint)
|
|
285
|
+
return dilated
|
|
286
|
+
|
|
287
|
+
|
|
288
|
+
def label_connected_components(
|
|
289
|
+
binary_image: np.ndarray, connectivity: int = 2
|
|
290
|
+
) -> tuple[np.ndarray, int]:
|
|
291
|
+
"""
|
|
292
|
+
Label connected components in a binary image.
|
|
293
|
+
|
|
294
|
+
Parameters
|
|
295
|
+
----------
|
|
296
|
+
binary_image : np.ndarray
|
|
297
|
+
Binary input image
|
|
298
|
+
connectivity : int, optional
|
|
299
|
+
Connectivity for defining neighbors:
|
|
300
|
+
- 1: 4-connectivity
|
|
301
|
+
- 2: 8-connectivity (default)
|
|
302
|
+
|
|
303
|
+
Returns
|
|
304
|
+
-------
|
|
305
|
+
labels : np.ndarray
|
|
306
|
+
Labeled image where each connected component has a unique integer label
|
|
307
|
+
num_labels : int
|
|
308
|
+
Number of connected components found
|
|
309
|
+
|
|
310
|
+
Examples
|
|
311
|
+
--------
|
|
312
|
+
>>> binary = (np.random.rand(50, 50) > 0.7)
|
|
313
|
+
>>> labels, n = label_connected_components(binary)
|
|
314
|
+
>>> print(f"Found {n} connected components")
|
|
315
|
+
|
|
316
|
+
Notes
|
|
317
|
+
-----
|
|
318
|
+
Based on MATLAB's bwconncomp function.
|
|
319
|
+
Uses skimage.measure.label.
|
|
320
|
+
"""
|
|
321
|
+
labels = measure.label(binary_image, connectivity=connectivity)
|
|
322
|
+
num_labels = labels.max()
|
|
323
|
+
return labels, num_labels
|
|
324
|
+
|
|
325
|
+
|
|
326
|
+
def regionprops(labeled_image: np.ndarray, intensity_image: np.ndarray | None = None) -> list:
|
|
327
|
+
"""
|
|
328
|
+
Measure properties of labeled image regions.
|
|
329
|
+
|
|
330
|
+
Parameters
|
|
331
|
+
----------
|
|
332
|
+
labeled_image : np.ndarray
|
|
333
|
+
Labeled image (output from label_connected_components)
|
|
334
|
+
intensity_image : np.ndarray, optional
|
|
335
|
+
Intensity image for computing intensity-based properties
|
|
336
|
+
|
|
337
|
+
Returns
|
|
338
|
+
-------
|
|
339
|
+
properties : list of RegionProperties
|
|
340
|
+
List of region property objects. Each object has attributes like:
|
|
341
|
+
- centroid: (row, col) of region center
|
|
342
|
+
- area: number of pixels in region
|
|
343
|
+
- bbox: bounding box coordinates
|
|
344
|
+
- etc.
|
|
345
|
+
|
|
346
|
+
Examples
|
|
347
|
+
--------
|
|
348
|
+
>>> binary = (np.random.rand(50, 50) > 0.7)
|
|
349
|
+
>>> labels, _ = label_connected_components(binary)
|
|
350
|
+
>>> props = regionprops(labels)
|
|
351
|
+
>>> for prop in props:
|
|
352
|
+
... print(f"Region at {prop.centroid}, area={prop.area}")
|
|
353
|
+
|
|
354
|
+
Notes
|
|
355
|
+
-----
|
|
356
|
+
Based on MATLAB's regionprops function.
|
|
357
|
+
Uses skimage.measure.regionprops.
|
|
358
|
+
"""
|
|
359
|
+
props = measure.regionprops(labeled_image, intensity_image=intensity_image)
|
|
360
|
+
return props
|
|
361
|
+
|
|
362
|
+
|
|
363
|
+
if __name__ == "__main__":
|
|
364
|
+
# Simple tests
|
|
365
|
+
print("Testing image processing functions...")
|
|
366
|
+
|
|
367
|
+
# Test 1: Image rotation
|
|
368
|
+
print("\nTest 1 - Image rotation:")
|
|
369
|
+
image = np.random.rand(50, 50)
|
|
370
|
+
rotated_30 = rotate_image(image, 30)
|
|
371
|
+
rotated_90 = rotate_image(image, 90)
|
|
372
|
+
print(f" Original shape: {image.shape}")
|
|
373
|
+
print(f" Rotated 30° shape: {rotated_30.shape}")
|
|
374
|
+
print(f" Rotated 90° shape: {rotated_90.shape}")
|
|
375
|
+
|
|
376
|
+
# Test 2: Regional maxima
|
|
377
|
+
print("\nTest 2 - Regional maxima:")
|
|
378
|
+
x = np.linspace(-3, 3, 50)
|
|
379
|
+
xx, yy = np.meshgrid(x, x)
|
|
380
|
+
# Two Gaussian peaks
|
|
381
|
+
peaks = np.exp(-(xx**2 + yy**2)) + 0.5 * np.exp(-((xx - 1.5) ** 2 + (yy - 1.5) ** 2))
|
|
382
|
+
maxima = find_regional_maxima(peaks)
|
|
383
|
+
print(f" Found {np.sum(maxima)} maxima")
|
|
384
|
+
print(" Maxima locations:")
|
|
385
|
+
coords = np.argwhere(maxima)
|
|
386
|
+
for i, (y, x) in enumerate(coords[:5]): # Show first 5
|
|
387
|
+
print(f" Peak {i + 1}: ({x}, {y}), value={peaks[y, x]:.3f}")
|
|
388
|
+
|
|
389
|
+
# Test 3: Contour detection
|
|
390
|
+
print("\nTest 3 - Contour detection:")
|
|
391
|
+
circle = np.sqrt(xx**2 + yy**2)
|
|
392
|
+
contours = find_contours_at_level(circle, 1.5)
|
|
393
|
+
print(f" Found {len(contours)} contours at level 1.5")
|
|
394
|
+
if len(contours) > 0:
|
|
395
|
+
print(f" Largest contour has {len(contours[0])} points")
|
|
396
|
+
|
|
397
|
+
# Test 4: Gaussian filtering
|
|
398
|
+
print("\nTest 4 - Gaussian filtering:")
|
|
399
|
+
noisy = image + 0.1 * np.random.randn(*image.shape)
|
|
400
|
+
smoothed = gaussian_filter_2d(noisy, sigma=2.0)
|
|
401
|
+
print(f" Noisy image std: {np.std(noisy):.4f}")
|
|
402
|
+
print(f" Smoothed image std: {np.std(smoothed):.4f}")
|
|
403
|
+
|
|
404
|
+
# Test 5: Connected components
|
|
405
|
+
print("\nTest 5 - Connected components:")
|
|
406
|
+
binary = np.random.rand(50, 50) > 0.7
|
|
407
|
+
labels, n = label_connected_components(binary)
|
|
408
|
+
print(f" Binary image has {np.sum(binary)} True pixels")
|
|
409
|
+
print(f" Found {n} connected components")
|
|
410
|
+
|
|
411
|
+
props = regionprops(labels)
|
|
412
|
+
print(" Region properties for first 3 components:")
|
|
413
|
+
for i, prop in enumerate(props[:3]):
|
|
414
|
+
print(f" Region {i + 1}: centroid={prop.centroid}, area={prop.area}")
|
|
415
|
+
|
|
416
|
+
print("\nAll tests completed!")
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"""Visualization modules."""
|
|
2
|
+
|
|
3
|
+
from .grid_plots import (
|
|
4
|
+
plot_autocorrelogram,
|
|
5
|
+
plot_grid_score_histogram,
|
|
6
|
+
plot_gridness_analysis,
|
|
7
|
+
plot_rate_map,
|
|
8
|
+
)
|
|
9
|
+
from .hd_plots import plot_hd_analysis, plot_polar_tuning, plot_temporal_autocorr
|
|
10
|
+
|
|
11
|
+
__all__ = [
|
|
12
|
+
"plot_autocorrelogram",
|
|
13
|
+
"plot_gridness_analysis",
|
|
14
|
+
"plot_rate_map",
|
|
15
|
+
"plot_grid_score_histogram",
|
|
16
|
+
"plot_polar_tuning",
|
|
17
|
+
"plot_temporal_autocorr",
|
|
18
|
+
"plot_hd_analysis",
|
|
19
|
+
]
|