tobac 1.6.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. tobac/__init__.py +112 -0
  2. tobac/analysis/__init__.py +31 -0
  3. tobac/analysis/cell_analysis.py +628 -0
  4. tobac/analysis/feature_analysis.py +212 -0
  5. tobac/analysis/spatial.py +619 -0
  6. tobac/centerofgravity.py +226 -0
  7. tobac/feature_detection.py +1758 -0
  8. tobac/merge_split.py +324 -0
  9. tobac/plotting.py +2321 -0
  10. tobac/segmentation/__init__.py +10 -0
  11. tobac/segmentation/watershed_segmentation.py +1316 -0
  12. tobac/testing.py +1179 -0
  13. tobac/tests/segmentation_tests/test_iris_xarray_segmentation.py +0 -0
  14. tobac/tests/segmentation_tests/test_segmentation.py +1183 -0
  15. tobac/tests/segmentation_tests/test_segmentation_time_pad.py +104 -0
  16. tobac/tests/test_analysis_spatial.py +1109 -0
  17. tobac/tests/test_convert.py +265 -0
  18. tobac/tests/test_datetime.py +216 -0
  19. tobac/tests/test_decorators.py +148 -0
  20. tobac/tests/test_feature_detection.py +1321 -0
  21. tobac/tests/test_generators.py +273 -0
  22. tobac/tests/test_import.py +24 -0
  23. tobac/tests/test_iris_xarray_match_utils.py +244 -0
  24. tobac/tests/test_merge_split.py +351 -0
  25. tobac/tests/test_pbc_utils.py +497 -0
  26. tobac/tests/test_sample_data.py +197 -0
  27. tobac/tests/test_testing.py +747 -0
  28. tobac/tests/test_tracking.py +714 -0
  29. tobac/tests/test_utils.py +650 -0
  30. tobac/tests/test_utils_bulk_statistics.py +789 -0
  31. tobac/tests/test_utils_coordinates.py +328 -0
  32. tobac/tests/test_utils_internal.py +97 -0
  33. tobac/tests/test_xarray_utils.py +232 -0
  34. tobac/tracking.py +613 -0
  35. tobac/utils/__init__.py +27 -0
  36. tobac/utils/bulk_statistics.py +360 -0
  37. tobac/utils/datetime.py +184 -0
  38. tobac/utils/decorators.py +540 -0
  39. tobac/utils/general.py +753 -0
  40. tobac/utils/generators.py +87 -0
  41. tobac/utils/internal/__init__.py +2 -0
  42. tobac/utils/internal/coordinates.py +430 -0
  43. tobac/utils/internal/iris_utils.py +462 -0
  44. tobac/utils/internal/label_props.py +82 -0
  45. tobac/utils/internal/xarray_utils.py +439 -0
  46. tobac/utils/mask.py +364 -0
  47. tobac/utils/periodic_boundaries.py +419 -0
  48. tobac/wrapper.py +244 -0
  49. tobac-1.6.2.dist-info/METADATA +154 -0
  50. tobac-1.6.2.dist-info/RECORD +53 -0
  51. tobac-1.6.2.dist-info/WHEEL +5 -0
  52. tobac-1.6.2.dist-info/licenses/LICENSE +29 -0
  53. tobac-1.6.2.dist-info/top_level.txt +1 -0
@@ -0,0 +1,462 @@
1
+ """Internal tobac utilities for iris cubes
2
+ The goal will be to, ultimately, remove these when we sunset iris
3
+ """
4
+
5
+ from __future__ import annotations
6
+
7
+ from typing import Union
8
+ import logging
9
+
10
+ import iris
11
+ import iris.cube
12
+ import numpy as np
13
+ import pandas as pd
14
+
15
+ from . import coordinates as tb_utils_gi
16
+
17
+
18
+ def find_axis_from_coord(
19
+ variable_cube: iris.cube.Cube, coord_name: str
20
+ ) -> Union[int, None]:
21
+ """Finds the axis number in an iris cube given a coordinate name.
22
+
23
+ Parameters
24
+ ----------
25
+ variable_cube: iris.cube
26
+ Input variable cube
27
+ coord_name: str
28
+ coordinate to look for
29
+
30
+ Returns
31
+ -------
32
+ axis_number: int
33
+ the number of the axis of the given coordinate, or None if the coordinate
34
+ is not found in the cube or not a dimensional coordinate
35
+ """
36
+
37
+ list_coord_names = [coord.name() for coord in variable_cube.coords()]
38
+ all_matching_axes = list(set(list_coord_names) & {coord_name})
39
+ if (
40
+ len(all_matching_axes) == 1
41
+ and len(variable_cube.coord_dims(all_matching_axes[0])) > 0
42
+ ):
43
+ return variable_cube.coord_dims(all_matching_axes[0])[0]
44
+ if len(all_matching_axes) > 1:
45
+ raise ValueError("Too many axes matched.")
46
+
47
+ return None
48
+
49
+
50
+ def find_vertical_axis_from_coord(
51
+ variable_cube: iris.cube.Cube, vertical_coord: Union[str, None] = None
52
+ ) -> str:
53
+ """Function to find the vertical coordinate in the iris cube
54
+
55
+ Parameters
56
+ ----------
57
+ variable_cube: iris.cube
58
+ Input variable cube, containing a vertical coordinate.
59
+ vertical_coord: str
60
+ Vertical coordinate name. If None, this function tries to auto-detect.
61
+
62
+ Returns
63
+ -------
64
+ str
65
+ the vertical coordinate name
66
+
67
+ Raises
68
+ ------
69
+ ValueError
70
+ Raised if the vertical coordinate isn't found in the cube.
71
+ """
72
+
73
+ list_coord_names = [coord.name() for coord in variable_cube.coords()]
74
+
75
+ if vertical_coord is None or vertical_coord == "auto":
76
+ # find the intersection
77
+ all_vertical_axes = list(
78
+ set(list_coord_names) & set(tb_utils_gi.COMMON_VERT_COORDS)
79
+ )
80
+ if len(all_vertical_axes) >= 1:
81
+ return all_vertical_axes[0]
82
+ raise ValueError(
83
+ "Cube lacks suitable automatic vertical coordinate (z, model_level_number, altitude, "
84
+ "or geopotential_height)"
85
+ )
86
+ if vertical_coord in list_coord_names:
87
+ return vertical_coord
88
+ raise ValueError("Please specify vertical coordinate found in cube")
89
+
90
+
91
+ def find_hdim_axes_3d(
92
+ field_in: iris.cube.Cube,
93
+ vertical_coord: Union[str, None] = None,
94
+ vertical_axis: Union[int, None] = None,
95
+ ) -> tuple[int, int]:
96
+ """Finds what the hdim axes are given a 3D (including z) or
97
+ 4D (including z and time) dataset.
98
+
99
+ Parameters
100
+ ----------
101
+ field_in: iris cube
102
+ Input field, can be 3D or 4D
103
+ vertical_coord: str or None
104
+ The name of the vertical coord, or None, which will attempt to find
105
+ the vertical coordinate name
106
+ vertical_axis: int or None
107
+ The axis number of the vertical coordinate, or None. Note
108
+ that only one of vertical_axis or vertical_coord can be set.
109
+
110
+ Returns
111
+ -------
112
+ (hdim_1_axis, hdim_2_axis): (int, int)
113
+ The axes for hdim_1 and hdim_2
114
+ """
115
+
116
+ if vertical_coord is not None and vertical_axis is not None:
117
+ if vertical_coord != "auto":
118
+ raise ValueError("Cannot set both vertical_coord and vertical_axis.")
119
+
120
+ time_axis = find_axis_from_coord(field_in, "time")
121
+ if vertical_axis is not None:
122
+ vertical_coord_axis = vertical_axis
123
+ vert_coord_found = True
124
+ else:
125
+ try:
126
+ vertical_axis = find_vertical_axis_from_coord(
127
+ field_in, vertical_coord=vertical_coord
128
+ )
129
+ except ValueError:
130
+ vert_coord_found = False
131
+ else:
132
+ vert_coord_found = True
133
+ ndim_vertical = field_in.coord_dims(vertical_axis)
134
+ if len(ndim_vertical) > 1:
135
+ raise ValueError(
136
+ "please specify 1 dimensional vertical coordinate."
137
+ f" Current vertical coordinates: {ndim_vertical}"
138
+ )
139
+ if len(ndim_vertical) != 0:
140
+ vertical_coord_axis = ndim_vertical[0]
141
+ else:
142
+ # this means the vertical coordinate is an auxiliary coordinate of some kind.
143
+ vert_coord_found = False
144
+
145
+ if not vert_coord_found:
146
+ # if we don't have a vertical coordinate, and we are 3D or lower
147
+ # that is okay.
148
+ if (field_in.ndim == 3 and time_axis is not None) or field_in.ndim < 3:
149
+ vertical_coord_axis = None
150
+ else:
151
+ raise ValueError("No suitable vertical coordinate found")
152
+ # Once we know the vertical coordinate, we can resolve the
153
+ # horizontal coordinates
154
+
155
+ all_axes = np.arange(0, field_in.ndim)
156
+ output_vals = tuple(
157
+ all_axes[np.logical_not(np.isin(all_axes, [time_axis, vertical_coord_axis]))]
158
+ )
159
+ return output_vals
160
+
161
+
162
+ def add_coordinates(t: pd.DataFrame, variable_cube: iris.cube.Cube) -> pd.DataFrame:
163
+ """Add coordinates from the input cube of the feature detection
164
+ to the trajectories/features.
165
+
166
+ Parameters
167
+ ----------
168
+ t : pandas.DataFrame
169
+ Trajectories/features from feature detection or linking step.
170
+
171
+ variable_cube : iris.cube.Cube
172
+ Input data used for the tracking with coordinate information
173
+ to transfer to the resulting DataFrame. Needs to contain the
174
+ coordinate 'time'.
175
+
176
+ Returns
177
+ -------
178
+ t : pandas.DataFrame
179
+ Trajectories with added coordinates.
180
+
181
+ """
182
+
183
+ from scipy.interpolate import interp1d, interpn
184
+
185
+ logging.debug("start adding coordinates from cube")
186
+
187
+ # pull time as datetime object and timestr from input data and add it to DataFrame:
188
+ t["time"] = None
189
+ t["timestr"] = None
190
+
191
+ logging.debug("adding time coordinate")
192
+
193
+ time_in = variable_cube.coord("time")
194
+ time_in_datetime = time_in.units.num2date(time_in.points)
195
+
196
+ t["time"] = time_in_datetime[t["frame"]]
197
+ t["timestr"] = [
198
+ x.strftime("%Y-%m-%d %H:%M:%S") for x in time_in_datetime[t["frame"]]
199
+ ]
200
+
201
+ # Get list of all coordinates in input cube except for time (already treated):
202
+ coord_names = [coord.name() for coord in variable_cube.coords()]
203
+ coord_names.remove("time")
204
+
205
+ logging.debug("time coordinate added")
206
+
207
+ # chose right dimension for horizontal axis based on time dimension:
208
+ ndim_time = variable_cube.coord_dims("time")[0]
209
+ if ndim_time == 0:
210
+ hdim_1 = 1
211
+ hdim_2 = 2
212
+ elif ndim_time == 1:
213
+ hdim_1 = 0
214
+ hdim_2 = 2
215
+ elif ndim_time == 2:
216
+ hdim_1 = 0
217
+ hdim_2 = 1
218
+
219
+ # create vectors to use to interpolate from pixels to coordinates
220
+ dimvec_1 = np.arange(variable_cube.shape[hdim_1])
221
+ dimvec_2 = np.arange(variable_cube.shape[hdim_2])
222
+
223
+ # loop over coordinates in input data:
224
+ for coord in coord_names:
225
+ logging.debug("adding coord: %s", coord)
226
+ # interpolate 2D coordinates:
227
+ if variable_cube.coord(coord).ndim == 1:
228
+ if variable_cube.coord_dims(coord) == (hdim_1,):
229
+ f = interp1d(
230
+ dimvec_1,
231
+ variable_cube.coord(coord).points,
232
+ fill_value="extrapolate",
233
+ )
234
+ coordinate_points = f(t["hdim_1"])
235
+
236
+ if variable_cube.coord_dims(coord) == (hdim_2,):
237
+ f = interp1d(
238
+ dimvec_2,
239
+ variable_cube.coord(coord).points,
240
+ fill_value="extrapolate",
241
+ )
242
+ coordinate_points = f(t["hdim_2"])
243
+
244
+ # interpolate 2D coordinates:
245
+ elif variable_cube.coord(coord).ndim == 2:
246
+ if variable_cube.coord_dims(coord) == (hdim_1, hdim_2):
247
+ points = (dimvec_1, dimvec_2)
248
+ values = variable_cube.coord(coord).points
249
+ xi = np.column_stack((t["hdim_1"], t["hdim_2"]))
250
+ coordinate_points = interpn(points, values, xi)
251
+
252
+ if variable_cube.coord_dims(coord) == (hdim_2, hdim_1):
253
+ points = (dimvec_2, dimvec_1)
254
+ values = variable_cube.coord(coord).points
255
+ xi = np.column_stack((t["hdim_2"], t["hdim_1"]))
256
+ coordinate_points = interpn(points, values, xi)
257
+
258
+ # interpolate 3D coordinates:
259
+ # mainly workaround for wrf latitude and longitude (to be fixed in future)
260
+
261
+ elif variable_cube.coord(coord).ndim == 3:
262
+ if variable_cube.coord_dims(coord) == (ndim_time, hdim_1, hdim_2):
263
+ points = (dimvec_1, dimvec_2)
264
+ values = variable_cube[0, :, :].coord(coord).points
265
+ xi = np.column_stack((t["hdim_1"], t["hdim_2"]))
266
+ coordinate_points = interpn(points, values, xi)
267
+
268
+ if variable_cube.coord_dims(coord) == (ndim_time, hdim_2, hdim_1):
269
+ points = (dimvec_2, dimvec_1)
270
+ values = variable_cube[0, :, :].coord(coord).points
271
+ xi = np.column_stack((t["hdim_2"], t["hdim_1"]))
272
+ coordinate_points = interpn(points, values, xi)
273
+
274
+ if variable_cube.coord_dims(coord) == (hdim_1, ndim_time, hdim_2):
275
+ points = (dimvec_1, dimvec_2)
276
+ values = variable_cube[:, 0, :].coord(coord).points
277
+ xi = np.column_stack((t["hdim_1"], t["hdim_2"]))
278
+ coordinate_points = interpn(points, values, xi)
279
+
280
+ if variable_cube.coord_dims(coord) == (hdim_1, hdim_2, ndim_time):
281
+ points = (dimvec_1, dimvec_2)
282
+ values = variable_cube[:, :, 0].coord(coord).points
283
+ xi = np.column_stack((t["hdim_1"], t["hdim_2"]))
284
+ coordinate_points = interpn(points, values, xi)
285
+
286
+ if variable_cube.coord_dims(coord) == (hdim_2, ndim_time, hdim_1):
287
+ points = (dimvec_2, dimvec_1)
288
+ values = variable_cube[:, 0, :].coord(coord).points
289
+ xi = np.column_stack((t["hdim_2"], t["hdim_1"]))
290
+ coordinate_points = interpn(points, values, xi)
291
+
292
+ if variable_cube.coord_dims(coord) == (hdim_2, hdim_1, ndim_time):
293
+ points = (dimvec_2, dimvec_1)
294
+ values = variable_cube[:, :, 0].coord(coord).points
295
+ xi = np.column_stack((t["hdim_2"], t["hdim_1"]))
296
+ coordinate_points = interpn(points, values, xi)
297
+
298
+ # write resulting array or list into DataFrame:
299
+ t[coord] = coordinate_points
300
+
301
+ logging.debug("added coord: " + coord)
302
+ return t
303
+
304
+
305
+ def add_coordinates_3D(
306
+ t: pd.DataFrame,
307
+ variable_cube: iris.cube.Cube,
308
+ vertical_coord: Union[str, int] = None,
309
+ vertical_axis: Union[int, None] = None,
310
+ assume_coords_fixed_in_time=True,
311
+ ):
312
+ """Function adding coordinates from the tracking cube to the trajectories
313
+ for the 3D case: time, longitude&latitude, x&y dimensions, and altitude
314
+
315
+ Parameters
316
+ ----------
317
+ t: pandas DataFrame
318
+ trajectories/features
319
+ variable_cube: iris.cube.Cube
320
+ Cube (usually the one you are tracking on) at least conaining the dimension of 'time'.
321
+ Typically, 'longitude','latitude','x_projection_coordinate','y_projection_coordinate',
322
+ and 'altitude' (if 3D) are the coordinates that we expect, although this function
323
+ will happily interpolate along any dimension coordinates you give.
324
+ vertical_coord: str or int
325
+ Name or axis number of the vertical coordinate. If None, tries to auto-detect.
326
+ If it is a string, it looks for the coordinate or the dimension name corresponding
327
+ to the string. If it is an int, it assumes that it is the vertical axis.
328
+ Note that if you only have a 2D or 3D coordinate for altitude, you must
329
+ pass in an int.
330
+ vertical_axis: int or None
331
+ Axis number of the vertical.
332
+ assume_coords_fixed_in_time: bool
333
+ If true, it assumes that the coordinates are fixed in time, even if the
334
+ coordinates say they vary in time. This is, by default, True, to preserve
335
+ legacy functionality. If False, it assumes that if a coordinate says
336
+ it varies in time, it takes the coordinate at its word.
337
+
338
+ Returns
339
+ -------
340
+ pandas DataFrame
341
+ trajectories with added coordinates
342
+ """
343
+ from scipy.interpolate import interp2d, interp1d, interpn
344
+
345
+ logging.debug("start adding coordinates from cube")
346
+
347
+ # pull time as datetime object and timestr from input data and add it to DataFrame:
348
+ t["time"] = None
349
+ t["timestr"] = None
350
+
351
+ logging.debug("adding time coordinate")
352
+
353
+ time_in = variable_cube.coord("time")
354
+ time_in_datetime = time_in.units.num2date(time_in.points)
355
+
356
+ t["time"] = time_in_datetime[t["frame"]]
357
+ t["timestr"] = [
358
+ x.strftime("%Y-%m-%d %H:%M:%S") for x in time_in_datetime[t["frame"]]
359
+ ]
360
+
361
+ # Get list of all coordinates in input cube except for time (already treated):
362
+ coord_names = [coord.name() for coord in variable_cube.coords()]
363
+ coord_names.remove("time")
364
+
365
+ logging.debug("time coordinate added")
366
+
367
+ # chose right dimension for horizontal and vertical axes based on time dimension:
368
+ ndim_time = variable_cube.coord_dims("time")[0]
369
+
370
+ if type(vertical_coord) is int:
371
+ ndim_vertical = vertical_coord
372
+ vertical_axis = None
373
+ else:
374
+ vertical_axis = tb_utils_gi.find_vertical_coord_name(
375
+ variable_cube, vertical_coord=vertical_coord
376
+ )
377
+
378
+ if vertical_axis is not None:
379
+ ndim_vertical = tb_utils_gi.find_axis_from_coord(variable_cube, vertical_axis)
380
+ if ndim_vertical is None:
381
+ raise ValueError("Vertical Coordinate not found")
382
+
383
+ # We need to figure out the axis number of hdim_1 and hdim_2.
384
+ ndim_hdim_1, ndim_hdim_2 = tb_utils_gi.find_hdim_axes_3D(
385
+ variable_cube, vertical_axis=ndim_vertical
386
+ )
387
+
388
+ if ndim_hdim_1 is None or ndim_hdim_2 is None:
389
+ raise ValueError("Could not find hdim coordinates.")
390
+
391
+ # create vectors to use to interpolate from pixels to coordinates
392
+ dimvec_1 = np.arange(variable_cube.shape[ndim_vertical])
393
+ dimvec_2 = np.arange(variable_cube.shape[ndim_hdim_1])
394
+ dimvec_3 = np.arange(variable_cube.shape[ndim_hdim_2])
395
+ dimvec_time = np.arange(variable_cube.shape[ndim_time])
396
+
397
+ coord_to_ax = {
398
+ ndim_vertical: (dimvec_1, "vdim"),
399
+ ndim_time: (dimvec_time, "time"),
400
+ ndim_hdim_1: (dimvec_2, "hdim_1"),
401
+ ndim_hdim_2: (dimvec_3, "hdim_2"),
402
+ }
403
+
404
+ # loop over coordinates in input data:
405
+ for coord in coord_names:
406
+ logging.debug("adding coord: " + coord)
407
+ # interpolate 1D coordinates:
408
+ var_coord = variable_cube.coord(coord)
409
+ if var_coord.ndim == 1:
410
+ curr_dim = coord_to_ax[variable_cube.coord_dims(coord)[0]]
411
+ f = interp1d(curr_dim[0], var_coord.points, fill_value="extrapolate")
412
+ coordinate_points = f(t[curr_dim[1]])
413
+
414
+ # interpolate 2D coordinates
415
+ elif var_coord.ndim == 2:
416
+ first_dim = coord_to_ax[variable_cube.coord_dims(coord)[1]]
417
+ second_dim = coord_to_ax[variable_cube.coord_dims(coord)[0]]
418
+ points = (second_dim[0], first_dim[0])
419
+ values = var_coord.points
420
+ xi = np.column_stack((t[second_dim[1]], t[first_dim[1]]))
421
+ coordinate_points = interpn(points, values, xi)
422
+
423
+ # Deal with the special case where the coordinate is 3D but
424
+ # one of the dimensions is time and we assume the coordinates
425
+ # don't vary in time.
426
+ elif (
427
+ var_coord.ndim == 3
428
+ and ndim_time in variable_cube.coord_dims(coord)
429
+ and assume_coords_fixed_in_time
430
+ ):
431
+ time_pos = variable_cube.coord_dims(coord).index(ndim_time)
432
+ hdim1_pos = 0 if time_pos != 0 else 1
433
+ hdim2_pos = 1 if time_pos == 2 else 2
434
+ first_dim = coord_to_ax[variable_cube.coord_dims(coord)[hdim2_pos]]
435
+ second_dim = coord_to_ax[variable_cube.coord_dims(coord)[hdim1_pos]]
436
+ points = (second_dim[0], first_dim[0])
437
+ values = var_coord.points
438
+ xi = np.column_stack((t[second_dim[1]], t[first_dim[1]]))
439
+ coordinate_points = interpn(points, values, xi)
440
+
441
+ # interpolate 3D coordinates:
442
+ elif var_coord.ndim == 3:
443
+ first_dim = coord_to_ax[variable_cube.coord_dims(coord)[0]]
444
+ second_dim = coord_to_ax[variable_cube.coord_dims(coord)[1]]
445
+ third_dim = coord_to_ax[variable_cube.coord_dims(coord)[2]]
446
+ coordinate_points = interpn(
447
+ [first_dim[0], second_dim[0], third_dim[0]],
448
+ var_coord.points,
449
+ [
450
+ [a, b, c]
451
+ for a, b, c in zip(
452
+ t[first_dim[1]], t[second_dim[1]], t[third_dim[1]]
453
+ )
454
+ ],
455
+ )
456
+ # coordinate_points=[f(a,b) for a,b in zip(t[first_dim[1]],t[second_dim[1]])]
457
+
458
+ # write resulting array or list into DataFrame:
459
+ t[coord] = coordinate_points
460
+
461
+ logging.debug("added coord: " + coord)
462
+ return t
@@ -0,0 +1,82 @@
1
+ """Internal tobac utilities"""
2
+
3
+ from __future__ import annotations
4
+ import numpy as np
5
+ import skimage.measure
6
+
7
+
8
+ def get_label_props_in_dict(labels: np.array) -> dict:
9
+ """Function to get the label properties into a dictionary format.
10
+
11
+ Parameters
12
+ ----------
13
+ labels : 2D array-like
14
+ Output of the `skimage.measure.label` function.
15
+
16
+ Returns
17
+ -------
18
+ region_properties_dict: dict
19
+ Output from skimage.measure.regionprops in dictionary
20
+ format, where they key is the label number.
21
+ """
22
+
23
+ region_properties_raw = skimage.measure.regionprops(labels)
24
+ region_properties_dict = {
25
+ region_prop.label: region_prop for region_prop in region_properties_raw
26
+ }
27
+
28
+ return region_properties_dict
29
+
30
+
31
+ def get_indices_of_labels_from_reg_prop_dict(region_property_dict: dict) -> tuple[dict]:
32
+ """Function to get the x, y, and z indices (as well as point count) of all labeled regions.
33
+ Parameters
34
+ ----------
35
+ region_property_dict : dict of region_property objects
36
+ This dict should come from the get_label_props_in_dict function.
37
+ Returns
38
+ -------
39
+ curr_loc_indices : dict
40
+ The number of points in the label number (key: label number).
41
+ z_indices : dict
42
+ The z indices in the label number. If a 2D property dict is passed, this value is not returned.
43
+ y_indices : dict
44
+ The y indices in the label number (key: label number).
45
+ x_indices : dict
46
+ The x indices in the label number (key: label number).
47
+ Raises
48
+ ------
49
+ ValueError
50
+ A ValueError is raised if there are no regions in the region
51
+ property dict.
52
+ """
53
+
54
+ if len(region_property_dict) == 0:
55
+ raise ValueError("No regions!")
56
+
57
+ z_indices = dict()
58
+ y_indices = dict()
59
+ x_indices = dict()
60
+ curr_loc_indices = dict()
61
+ is_3D = False
62
+
63
+ # loop through all skimage identified regions
64
+ for region_prop_key in region_property_dict:
65
+ region_prop = region_property_dict[region_prop_key]
66
+ index = region_prop.label
67
+ if len(region_prop.coords[0]) >= 3:
68
+ is_3D = True
69
+ curr_z_ixs, curr_y_ixs, curr_x_ixs = np.transpose(region_prop.coords)
70
+ z_indices[index] = curr_z_ixs
71
+ else:
72
+ curr_y_ixs, curr_x_ixs = np.transpose(region_prop.coords)
73
+ z_indices[index] = -1
74
+
75
+ y_indices[index] = curr_y_ixs
76
+ x_indices[index] = curr_x_ixs
77
+ curr_loc_indices[index] = len(curr_y_ixs)
78
+ # print("indices found")
79
+ if is_3D:
80
+ return [curr_loc_indices, z_indices, y_indices, x_indices]
81
+ else:
82
+ return [curr_loc_indices, y_indices, x_indices]