delta-microscopy 3.0.0a0__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.
delta/__init__.py ADDED
@@ -0,0 +1,50 @@
1
+ """DeLTA."""
2
+
3
+ import importlib.util
4
+ import logging
5
+ import os
6
+ import sys
7
+
8
+ logging.basicConfig(
9
+ format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
10
+ level=logging.WARNING,
11
+ )
12
+
13
+ LOGGER = logging.getLogger(__name__)
14
+
15
+ if "TF_CPP_MIN_LOG_LEVEL" not in os.environ:
16
+ os.environ["TF_CPP_MIN_LOG_LEVEL"] = "2"
17
+
18
+ if "KERAS_BACKEND" not in os.environ:
19
+ for backend in ("torch", "tensorflow", "jax"): # Order of preference
20
+ if importlib.util.find_spec(backend) is not None:
21
+ os.environ["KERAS_BACKEND"] = backend
22
+ break
23
+ else:
24
+ LOGGER.critical(
25
+ "No deep learning backend has been found. At least one library "
26
+ "among torch, tensorflow or jax should be installed. "
27
+ "You can then select the library by passing its name to "
28
+ "the KERAS_BACKEND environment variable."
29
+ )
30
+ sys.exit(1)
31
+
32
+ # isort: off
33
+
34
+ from delta import assets, config
35
+ from delta import cli, data, imgops, lineage, model, pipeline, utils
36
+ from delta._version import __version__
37
+
38
+ # isort: on
39
+
40
+ __all__ = (
41
+ "__version__",
42
+ "assets",
43
+ "cli",
44
+ "config",
45
+ "data",
46
+ "lineage",
47
+ "model",
48
+ "pipeline",
49
+ "utils",
50
+ )
delta/_conversions.py ADDED
@@ -0,0 +1,425 @@
1
+ """Various conversions to save objects to disk."""
2
+
3
+ import ast
4
+ import logging
5
+ from json import JSONDecodeError
6
+
7
+ import cv2
8
+ import numpy as np
9
+ import numpy.typing as npt
10
+ import xarray as xr
11
+
12
+ import delta
13
+ from delta import imgops, utils
14
+ from delta.config import Config
15
+ from delta.lineage import Pole
16
+ from delta.model import hash_model
17
+
18
+ LOGGER = logging.getLogger(__name__)
19
+
20
+
21
+ def _roi_to_xarray(roi: "delta.pipeline.ROI") -> xr.Dataset:
22
+ # DataArray coordinates
23
+ frames = np.arange(len(roi.img_stack), dtype=np.int32) + roi.first_frame
24
+ y_orig = np.arange(roi.img_stack[0].shape[0], dtype=np.int16)
25
+ x_orig = np.arange(roi.img_stack[0].shape[1], dtype=np.int16)
26
+ tyx_orig = {"frame": frames, "y_orig": y_orig, "x_orig": x_orig}
27
+ y_resized = np.arange(roi.seg_stack[0].shape[0], dtype=np.int16)
28
+ x_resized = np.arange(roi.seg_stack[0].shape[1], dtype=np.int16)
29
+ tyx_resized = {"frame": frames, "y_resized": y_resized, "x_resized": x_resized}
30
+ cells = np.array(list(roi.lineage.cells.keys()), dtype=np.uint16)
31
+ ct = {"cell": cells, "frame": frames}
32
+ channels = roi.fluo_stack.channel
33
+ edge = ["-x", "+x", "-y", "+y"]
34
+
35
+ # Image stacks
36
+ img_stack = xr.DataArray(
37
+ roi.img_stack, dims=("frame", "y_orig", "x_orig"), coords=tyx_orig
38
+ )
39
+ fluo_stack = xr.DataArray(
40
+ roi.fluo_stack,
41
+ dims=("frame", "channel", "y_orig", "x_orig"),
42
+ coords=tyx_orig | {"channel": channels},
43
+ )
44
+ seg_stack = xr.DataArray(
45
+ roi.seg_stack, dims=("frame", "y_resized", "x_resized"), coords=tyx_resized
46
+ ).astype(bool)
47
+ label_stack = xr.DataArray(
48
+ roi.label_stack, dims=("frame", "y_orig", "x_orig"), coords=tyx_orig
49
+ )
50
+
51
+ # Lineage (first define numpy arrays, then transform them into DataArrays)
52
+ nmother = np.zeros((len(cells),), dtype=np.uint16)
53
+ ndaughters = np.zeros((len(cells), len(frames)), dtype=np.uint16)
54
+ nnew_pole = np.zeros((len(cells), len(frames), 2), dtype=np.int16)
55
+ nold_pole = np.zeros((len(cells), len(frames), 2), dtype=np.int16)
56
+ nedges = np.zeros((len(cells), len(frames), 4), dtype=bool)
57
+ nscalars = {
58
+ "length": np.full((len(cells), len(frames)), np.nan, dtype=np.float32),
59
+ "width": np.full((len(cells), len(frames)), np.nan, dtype=np.float32),
60
+ "area": np.full((len(cells), len(frames)), np.nan, dtype=np.float32),
61
+ "perimeter": np.full((len(cells), len(frames)), np.nan, dtype=np.float32),
62
+ "growthrate_length": np.full(
63
+ (len(cells), len(frames)), np.nan, dtype=np.float32
64
+ ),
65
+ "growthrate_area": np.full((len(cells), len(frames)), np.nan, dtype=np.float32),
66
+ }
67
+ nfluo = np.full((len(cells), len(frames), len(channels)), np.nan, dtype=np.float32)
68
+ for icell, cellid in enumerate(cells):
69
+ cell = roi.lineage.cells[cellid]
70
+ nmother[icell] = cell.motherid or 0
71
+ for frame in cell.frames:
72
+ features = cell.features(frame)
73
+ ndaughters[icell, frame - roi.first_frame] = cell.daughterid(frame) or 0
74
+ nnew_pole[icell, frame - roi.first_frame] = features.new_pole
75
+ nold_pole[icell, frame - roi.first_frame] = features.old_pole
76
+ for arg, narg in nscalars.items():
77
+ narg[icell, frame - roi.first_frame] = getattr(features, arg)
78
+ nfluo[icell, frame - roi.first_frame] = features.fluo
79
+ for iedge, edg in enumerate(edge):
80
+ nedges[icell, frame - roi.first_frame, iedge] = edg in features.edges
81
+
82
+ # Transform lineage information into DataArrays
83
+ mother = xr.DataArray(nmother, dims=("cell",), coords={"cell": cells})
84
+ daughters = xr.DataArray(ndaughters, dims=("cell", "frame"), coords=ct)
85
+ ctyx = ct | {"yx": ["y", "x"]}
86
+ new_pole = xr.DataArray(nnew_pole, dims=("cell", "frame", "yx"), coords=ctyx)
87
+ old_pole = xr.DataArray(nold_pole, dims=("cell", "frame", "yx"), coords=ctyx)
88
+ scalars = {
89
+ arg: xr.DataArray(narg, dims=("cell", "frame"), coords=ct)
90
+ for arg, narg in nscalars.items()
91
+ }
92
+ fluo = xr.DataArray(
93
+ nfluo, dims=("cell", "frame", "channel"), coords=ct | {"channel": channels}
94
+ )
95
+ edges = xr.DataArray(
96
+ nedges, dims=("cell", "frame", "edge"), coords=ct | {"edge": edge}
97
+ )
98
+ first_frame = xr.DataArray(
99
+ np.array(
100
+ [roi.lineage.cells[cellid].first_frame for cellid in cells], dtype=np.int32
101
+ ),
102
+ dims=("cell",),
103
+ coords={"cell": cells},
104
+ )
105
+ last_frame = xr.DataArray(
106
+ np.array(
107
+ [roi.lineage.cells[cellid].last_frame for cellid in cells], dtype=np.int32
108
+ ),
109
+ dims=("cell",),
110
+ coords={"cell": cells},
111
+ )
112
+
113
+ model_hashes = {}
114
+ for model_name, model_config in roi.config.models.items():
115
+ try:
116
+ h = model_config._model_hash # type: ignore[attr-defined]
117
+ except AttributeError:
118
+ h = hash_model(model_config.model())
119
+ model_hashes[f"{model_name}_model_hash"] = h
120
+
121
+ # Create Dataset
122
+ dataset = xr.Dataset(
123
+ data_vars={
124
+ "img_stack": img_stack,
125
+ "fluo_stack": fluo_stack,
126
+ "seg_stack": seg_stack,
127
+ "label_stack": label_stack,
128
+ "mother": mother,
129
+ "daughter": daughters,
130
+ "new_pole": new_pole,
131
+ "old_pole": old_pole,
132
+ "edges": edges,
133
+ "fluo": fluo,
134
+ "first_frame": first_frame,
135
+ "last_frame": last_frame,
136
+ }
137
+ | scalars,
138
+ attrs={
139
+ "roi_nb": roi.roi_nb,
140
+ "box": str(roi.box.__dict__),
141
+ "scaling": roi.scaling,
142
+ "config": roi.config.serialize(),
143
+ "DeLTA_version": delta.__version__,
144
+ "file_format_version": "0.1.0",
145
+ }
146
+ | model_hashes,
147
+ )
148
+ return dataset
149
+
150
+
151
+ def _xarray_to_roi(dataset: xr.Dataset) -> "delta.pipeline.ROI":
152
+ box = imgops.CroppingBox(**ast.literal_eval(dataset.attrs["box"]))
153
+ try:
154
+ config = Config.deserialize(dataset.attrs["config"])
155
+ except JSONDecodeError:
156
+ LOGGER.warning(
157
+ "The configuration stored in this nc file is deprecated. "
158
+ "It will not be possible to read it anymore in a future DeLTA version. "
159
+ "To avoid that, just load and save this file with this DeLTA version: "
160
+ "`delta.pipeline.Position.load_netcdf(path).to_netcdf(path)`"
161
+ )
162
+ old_config = delta.config.OldConfig(**ast.literal_eval(dataset.attrs["config"]))
163
+ config = old_config.to_tomlconfig()
164
+ for name, model in config.models.items():
165
+ model._model_hash = dataset.attrs[f"{name}_model_hash"] # type: ignore[attr-defined]
166
+ img_stack = dataset.img_stack.expand_dims({"channel": [0]}, axis=1)
167
+ fluo_stack = dataset.fluo_stack.assign_coords(
168
+ {"channel": dataset.fluo_stack.channel + 1}
169
+ )
170
+ stack = xr.concat([img_stack, fluo_stack], dim="channel")
171
+ roi = delta.pipeline.ROI(
172
+ stack=stack.rename({"y_orig": "yc", "x_orig": "xc"}),
173
+ roi_nb=dataset.attrs["roi_nb"],
174
+ first_frame=dataset.frame[0].to_numpy() if len(dataset.frame) > 0 else 0,
175
+ box=box,
176
+ config=config,
177
+ )
178
+ roi.scaling = tuple(dataset.attrs["scaling"])
179
+ roi.seg_stack = list(dataset.seg_stack.to_numpy().astype(np.uint8))
180
+ roi.label_stack = list(dataset.label_stack.to_numpy())
181
+ cells = {}
182
+ for cellid in [int(cellid) for cellid in dataset.cell]:
183
+ xrcell = dataset.sel(cell=cellid)
184
+ mother = int(xrcell.mother)
185
+ frames = np.arange(xrcell.first_frame, xrcell.last_frame + 1)
186
+ data = xrcell.sel(frame=frames)
187
+ daughters = data.daughter.to_numpy()
188
+ edges = [
189
+ "".join(
190
+ str(e.to_numpy()) if data.edges.sel(frame=frame, edge=e) else ""
191
+ for e in data.edge
192
+ )
193
+ for frame in data.frame
194
+ ]
195
+ cells[cellid] = delta.lineage.Cell(
196
+ motherid=mother if mother > 0 else None,
197
+ first_frame=frames[0],
198
+ _daughterids=[did if did > 0 else None for did in daughters],
199
+ _features=[
200
+ delta.lineage.CellFeatures(
201
+ new_pole=new_pole,
202
+ old_pole=old_pole,
203
+ length=length,
204
+ width=width,
205
+ area=area,
206
+ perimeter=perimeter,
207
+ fluo=fluo,
208
+ edges=edges,
209
+ growthrate_length=gr_l,
210
+ growthrate_area=gr_a,
211
+ )
212
+ for (
213
+ new_pole,
214
+ old_pole,
215
+ length,
216
+ width,
217
+ area,
218
+ perimeter,
219
+ fluo,
220
+ edges,
221
+ gr_l,
222
+ gr_a,
223
+ ) in zip(
224
+ data.new_pole.to_numpy(),
225
+ data.old_pole.to_numpy(),
226
+ data.length.to_numpy(),
227
+ data.width.to_numpy(),
228
+ data.area.to_numpy(),
229
+ data.perimeter.to_numpy(),
230
+ data.fluo.to_numpy(),
231
+ edges,
232
+ data.growthrate_length.to_numpy(),
233
+ data.growthrate_area.to_numpy(),
234
+ strict=True,
235
+ )
236
+ ],
237
+ )
238
+ roi.lineage.cells = cells
239
+ return roi
240
+
241
+
242
+ def _position_to_movie(
243
+ position: "delta.pipeline.Position",
244
+ reader: delta.utils.XPReader,
245
+ frames: range | None = None,
246
+ *,
247
+ with_labels: bool = False,
248
+ ) -> list[npt.NDArray[np.uint8]]:
249
+ # Re-read trans frames:
250
+ trans_images = reader.images(
251
+ position=position.position_nb,
252
+ channels=reader.channels[0],
253
+ frames=frames,
254
+ rotate=position.rotate,
255
+ ).isel(channel=0)
256
+ if frames is None:
257
+ frames = reader.frames
258
+
259
+ if position.config.correct_drift:
260
+ da_drift_values = xr.DataArray(
261
+ position.drift_values,
262
+ coords={"frame": trans_images.frame},
263
+ dims=("frame", "xy"),
264
+ )
265
+ trans_images = imgops.correct_drift(trans_images, da_drift_values)
266
+
267
+ # Fix brightness
268
+ ti_min = trans_images.min()
269
+ ti_max = trans_images.max()
270
+ trans_images = (trans_images - ti_min) / (ti_max - ti_min)
271
+ trans_images = trans_images.to_numpy()
272
+
273
+ movie = []
274
+
275
+ # Run through frames, compile movie:
276
+ for frame, init_trans_frame in zip(frames, trans_images, strict=True):
277
+ trans_frame = init_trans_frame
278
+
279
+ # RGB-ify:
280
+ trans_frame = np.repeat(trans_frame[:, :, np.newaxis], 3, axis=-1)
281
+
282
+ # Add frame number text:
283
+ trans_frame = cv2.putText(
284
+ trans_frame,
285
+ text=f"frame {frame:06d}",
286
+ org=(int(trans_frame.shape[0] * 0.05), int(trans_frame.shape[0] * 0.97)),
287
+ fontFace=cv2.FONT_HERSHEY_SIMPLEX,
288
+ fontScale=1,
289
+ color=(1, 1, 1, 1),
290
+ thickness=2,
291
+ )
292
+
293
+ for roi in position.rois:
294
+ # Get chamber-specific variables:
295
+ colors = utils.random_colors(
296
+ list(roi.lineage.cells.keys()), seed=roi.roi_nb
297
+ )
298
+ labels = roi.get_labels(frame)
299
+ cellids, contours = utils.cells_in_frame(labels, return_contours=True)
300
+
301
+ xtl, ytl = (roi.box.xtl, roi.box.ytl)
302
+
303
+ # Run through cells in labelled frame:
304
+ for icell, cellid in enumerate(cellids):
305
+ cell = roi.lineage.cells[cellid]
306
+ # Draw contours:
307
+ trans_frame = cv2.drawContours(
308
+ trans_frame,
309
+ contours,
310
+ icell,
311
+ color=colors[cellid],
312
+ thickness=1,
313
+ offset=(xtl, ytl),
314
+ )
315
+
316
+ if with_labels:
317
+ cell_y, cell_x = np.where(labels == cellid)
318
+ trans_frame = cv2.putText(
319
+ trans_frame,
320
+ text=str(cellid),
321
+ org=(
322
+ int(np.mean(cell_x) + xtl + 5),
323
+ int(np.mean(cell_y) + ytl + 5),
324
+ ),
325
+ fontFace=cv2.FONT_HERSHEY_SIMPLEX,
326
+ fontScale=0.5,
327
+ color=(1, 1, 1, 1),
328
+ thickness=1,
329
+ )
330
+
331
+ # Draw poles:
332
+ oldpole = cell.features(frame).old_pole
333
+ assert isinstance(oldpole, np.ndarray) # for mypy
334
+ trans_frame = _draw_pole(
335
+ trans_frame,
336
+ contours[icell],
337
+ oldpole,
338
+ (xtl, ytl),
339
+ color=colors[cellid],
340
+ )
341
+
342
+ daughter = cell.daughterid(frame)
343
+ bornago = frame - cell.first_frame
344
+ mother = cell.motherid
345
+
346
+ if daughter is None and (bornago > 0 or mother is None):
347
+ newpole = cell.features(frame).new_pole
348
+ trans_frame = _draw_pole(
349
+ trans_frame,
350
+ contours[icell],
351
+ newpole,
352
+ (xtl, ytl),
353
+ color=(1.0, 1.0, 1.0),
354
+ )
355
+
356
+ # Plot division arrow:
357
+ if daughter is not None:
358
+ newpole = cell.features(frame).new_pole
359
+ daupole = roi.lineage.cells[daughter].features(frame).new_pole
360
+ # Plot arrow:
361
+ trans_frame = cv2.arrowedLine(
362
+ trans_frame,
363
+ (newpole[1] + xtl, newpole[0] + ytl),
364
+ (daupole[1] + xtl, daupole[0] + ytl),
365
+ color=(1, 1, 1),
366
+ thickness=1,
367
+ )
368
+
369
+ # Add to movie array:
370
+ movie += [imgops.to_integer_values(trans_frame, np.uint8)]
371
+
372
+ return movie
373
+
374
+
375
+ def _draw_pole(
376
+ image: npt.NDArray[np.float32],
377
+ contour: npt.NDArray[np.int32],
378
+ pole: Pole,
379
+ offset: tuple[int, int],
380
+ color: tuple[float, float, float],
381
+ width: int = 7,
382
+ ) -> npt.NDArray[np.float32]:
383
+ contour = contour[:, 0, :]
384
+ pole_ind = np.argmin(np.sum(np.abs(contour - pole[::-1]), axis=1))
385
+ pole_cnt = contour[np.arange(pole_ind - width, pole_ind + width) % len(contour)]
386
+ return cv2.drawContours(image, [pole_cnt[:, None, :]], -1, color, -1, offset=offset)
387
+
388
+
389
+ def _position_to_labels(
390
+ position: "delta.pipeline.Position",
391
+ frames: range | None = None,
392
+ *,
393
+ undo_corrections: bool = True,
394
+ ) -> list[imgops.Labels]:
395
+ if frames is None:
396
+ frames = range(
397
+ position.rois[0].first_frame,
398
+ position.rois[0].first_frame + len(position.rois[0].label_stack),
399
+ )
400
+
401
+ labels = []
402
+ for frame in frames:
403
+ labelled = np.zeros(position.shape, dtype=np.uint16)
404
+
405
+ for roi in position.rois:
406
+ roi.box.patch(labelled, roi.get_labels(frame))
407
+
408
+ # Undo drift and rotation corrections:
409
+ if undo_corrections:
410
+ shift = (0, 0)
411
+ if position.config.correct_drift:
412
+ shift = position.drift_values[frame - position.rois[0].first_frame]
413
+ shift = (-shift[0], -shift[1])
414
+
415
+ angle = 0.0
416
+ if position.config.correct_rotation:
417
+ angle = -position.rotate
418
+
419
+ labelled = imgops.affine_transform(
420
+ labelled, shift=shift, angle=angle, order=0
421
+ )
422
+
423
+ labels.append(labelled)
424
+
425
+ return labels
delta/_version.py ADDED
@@ -0,0 +1 @@
1
+ __version__ = "v3.0.0a0"
delta/assets.py ADDED
@@ -0,0 +1,195 @@
1
+ """
2
+ Asset downloads (model files, training sets, and demonstration movies).
3
+
4
+ We provide our trained models, training sets, and evaluation movies in `this
5
+ google drive folder
6
+ <https://drive.google.com/drive/folders/1nTRVo0rPP9CR9F6WUunVXSXrLNMT_zCP?usp=sharing>`_.
7
+ We refer to these data files as "assets".
8
+
9
+ You can of course download them manually, but shouldn't have to, because DeLTA
10
+ knows where to find them and downloads them automatically when needed, with the
11
+ `pooch library <https://www.fatiando.org/pooch/latest/>`_, which makes everything
12
+ completely transparent.
13
+
14
+ Cache location
15
+ --------------
16
+
17
+ Upon downloading the assets, DeLTA caches them in the default OS cache location
18
+ (see `the pooch documentation
19
+ <https://www.fatiando.org/pooch/latest/api/generated/pooch.os_cache.html>`_ for
20
+ details). If you want to download them to a different location, you can
21
+ specify this path in the ``DELTA_ASSETS_CACHE`` environment variable.
22
+
23
+ For example, to set this variable permanently within your conda environment,
24
+ you can run the following command in a terminal, inside your conda environment:
25
+
26
+ .. code:: none
27
+
28
+ (delta_env)$ conda env config vars set DELTA_ASSETS_CACHE="D:/data/delta_cache"
29
+ """
30
+
31
+ from pathlib import Path
32
+ from typing import Literal
33
+
34
+ import pooch
35
+
36
+ from delta._version import __version__
37
+
38
+ PRESETS = Literal["2D", "mothermachine"]
39
+
40
+ MODEL = Literal["rois", "seg", "track"]
41
+
42
+ BASE_URL = "https://drive.usercontent.google.com/download?confirm=t&id="
43
+
44
+ MODEL_REGISTRY = pooch.create(
45
+ path=pooch.os_cache("delta") / "models",
46
+ base_url="",
47
+ version=__version__,
48
+ version_dev="dev",
49
+ registry={
50
+ "moma_rois.keras": "sha256:a4585ee1a163c6a9aa83609d84246ca30c4b1f08c4656ab289476a120830e27e",
51
+ "moma_seg.keras": "sha256:e2428e125b013691846278224a4f607a022af41302b6dbc6c780dd97d4aa5f5a",
52
+ "moma_track.keras": "sha256:762f20bcfda307fbc30111b6f3f5bb67fcccc60d644dc305276fd48cbe45c35e",
53
+ "2D_seg.keras": "sha256:97941f89ed37fdd937fde8c4fb797dfec7b8f2972cfec26527786e823dda40c0",
54
+ "2D_track.keras": "sha256:e4ed540d9e126c7d3605cb38d00332d492c38676f146f75c6b139bb73c2f9fb6",
55
+ },
56
+ urls={
57
+ "moma_rois.keras": BASE_URL + "1ZeBOFPG7xdY3R7AY_Ba7lOfPNxzGYfaO",
58
+ "moma_seg.keras": BASE_URL + "1tJuRufC12MXvKDGBE90hi8yfWEUMkNMo",
59
+ "moma_track.keras": BASE_URL + "1MsXiNeqxXDJMoBJu-5fAfXVtMGaFcXhv",
60
+ "2D_seg.keras": BASE_URL + "190V0tgHk1HDhxGWQN2E4W5cozvN_5wvz",
61
+ "2D_track.keras": BASE_URL + "1wH4G7b191I9xcIdVTCwGO_qzC7MhepPl",
62
+ },
63
+ env="DELTA_ASSETS_CACHE",
64
+ )
65
+
66
+ TRAININGSETS_REGISTRY = pooch.create(
67
+ path=pooch.os_cache("delta") / "training_sets",
68
+ base_url="",
69
+ version=__version__,
70
+ version_dev="dev",
71
+ registry={
72
+ "2D_training_segmentation.zip": "sha256:28d582ce7c3e99486e7323433fbec441e48b03f651b4c123dcc805bcf85a37c4",
73
+ "2D_training_tracking.zip": "sha256:af24c80b6a161ba67cfc4ee43ab08dd4ad1078343f55bcda4163337a8cc1890c",
74
+ "mothermachine_training_rois.zip": "sha256:51136a5e0278ed600fb01f208f452d6dfacf4dbf540af1c85ef1e125bc1567b9",
75
+ "mothermachine_training_segmentation.zip": "sha256:b1ec15052293ff5b16c1b8332bac8f31596f7aaa028c3c720d350fd21e7a113f",
76
+ "mothermachine_training_tracking.zip": "sha256:75c8b7c764a9463d4ab1dcb432b464c17456ccd0e8b76b120b8eecf73364414d",
77
+ },
78
+ urls={
79
+ "2D_training_segmentation.zip": BASE_URL + "1eMYwEhImt9-v8q4XlFMOAJbF6UvTkmlQ",
80
+ "2D_training_tracking.zip": BASE_URL + "1G4Hujltlr330rlPrFX31GR-BmnxnECLQ",
81
+ "mothermachine_training_rois.zip": BASE_URL
82
+ + "15rg5uVZIWVy6n-trQPMHK_zSQrlcLOvl",
83
+ "mothermachine_training_segmentation.zip": BASE_URL
84
+ + "1P3bnx0WaKf07kj7KPh1it0f_-1P8iQlf",
85
+ "mothermachine_training_tracking.zip": BASE_URL
86
+ + "1XphzQzNKB97-JKPJ1QtGTI4_Wb7eY-pd",
87
+ },
88
+ env="DELTA_ASSETS_CACHE",
89
+ )
90
+
91
+ DEMO_REGISTRY = pooch.create(
92
+ path=pooch.os_cache("delta") / "demo_movies",
93
+ base_url="",
94
+ version=__version__,
95
+ version_dev="dev",
96
+ registry={
97
+ "2D_demo.zip": "sha256:dbc294aaf67f1966dacf51fb61aa7623c1d8a477f9586e71e18b0a9a42f303dc",
98
+ "mothermachine_demo.zip": "sha256:3ce91358ea9982b17b0d3693a544bf168d3ee6385018e2607401647e62de0b71",
99
+ },
100
+ urls={
101
+ "2D_demo.zip": BASE_URL + "1qtlgQRVZ0bX5DPmr4knQBv7WoDFjQqxi",
102
+ "mothermachine_demo.zip": BASE_URL + "1jG9aV_MfwWDDZk5JMUnWz5ERySvydmiD",
103
+ },
104
+ env="DELTA_ASSETS_CACHE",
105
+ )
106
+
107
+
108
+ def download_training_set(presets: PRESETS, model: MODEL) -> Path:
109
+ """
110
+ Return the path of the training set for the currently loaded configuration.
111
+
112
+ Depending on the current value of presets ("2D" or "mothermachine") and
113
+ of the value of the argument `model` ("rois", "seg" or "track"), this
114
+ function returns the path of the corresponding training set. If necessary,
115
+ it downloads it first and caches it under a directory that can be specified
116
+ by the environment variable `DELTA_ASSETS_CACHE`.
117
+
118
+ Parameters
119
+ ----------
120
+ presets : PRESETS
121
+ 2D or mothermachine
122
+ model : str
123
+ Type of training set: `rois`, `seg` or `track`.
124
+
125
+ Returns
126
+ -------
127
+ path : Path
128
+ Path of the training set.
129
+
130
+ """
131
+ long_model = {"rois": "rois", "seg": "segmentation", "track": "tracking"}[model]
132
+ TRAININGSETS_REGISTRY.fetch(
133
+ f"{presets}_training_{long_model}.zip",
134
+ processor=pooch.Unzip(extract_dir="unzipped"),
135
+ progressbar=True,
136
+ )
137
+ path = Path(TRAININGSETS_REGISTRY.path) / "unzipped"
138
+ if presets == "mothermachine" and model in {"seg", "track"}:
139
+ return path / f"{presets}_training_{long_model}/train_multisets"
140
+ return path / f"{presets}_training_{long_model}"
141
+
142
+
143
+ def download_demo_movie(presets: PRESETS) -> Path:
144
+ """
145
+ Return the path of the demo movie for the currently loaded configuration.
146
+
147
+ If necessary, it downloads it first and caches it under a directory that
148
+ can be specified by the environment variable `DELTA_ASSETS_CACHE`.
149
+
150
+ Parameters
151
+ ----------
152
+ presets : PRESETS
153
+ 2D or mothermachine
154
+
155
+ Returns
156
+ -------
157
+ path : Path
158
+ Path of the demo movie.
159
+ """
160
+ DEMO_REGISTRY.fetch(
161
+ f"{presets}_demo.zip",
162
+ processor=pooch.Unzip(extract_dir="unzipped"),
163
+ progressbar=True,
164
+ )
165
+ if presets == "mothermachine":
166
+ prototype = "Pos{p}Chan{c}Frames{t}.png"
167
+ else:
168
+ prototype = "pos{p}cha{c}fra{t}.png"
169
+ return Path(DEMO_REGISTRY.path) / f"unzipped/{presets}_demo/{prototype}"
170
+
171
+
172
+ def download_model(presets: PRESETS, model: MODEL) -> Path:
173
+ """
174
+ Return the path of the downloaded model, after downloading it if needed.
175
+
176
+ Parameters
177
+ ----------
178
+ presets: PRESETS
179
+ 2D or mothermachine
180
+ model: MODEL
181
+ seg, track or rois
182
+
183
+ Returns
184
+ -------
185
+ path: Path
186
+ Path of the downloaded model.
187
+ """
188
+ filename = {
189
+ ("2D", "seg"): "2D_seg.keras",
190
+ ("2D", "track"): "2D_track.keras",
191
+ ("mothermachine", "rois"): "moma_rois.keras",
192
+ ("mothermachine", "seg"): "moma_seg.keras",
193
+ ("mothermachine", "track"): "moma_track.keras",
194
+ }[presets, model]
195
+ return Path(MODEL_REGISTRY.fetch(filename, progressbar=True))