dcnum 0.23.1__tar.gz → 0.23.3__tar.gz
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.
Potentially problematic release.
This version of dcnum might be problematic. Click here for more details.
- {dcnum-0.23.1 → dcnum-0.23.3}/CHANGELOG +8 -0
- {dcnum-0.23.1 → dcnum-0.23.3}/PKG-INFO +2 -2
- {dcnum-0.23.1 → dcnum-0.23.3}/pyproject.toml +1 -1
- {dcnum-0.23.1 → dcnum-0.23.3}/src/dcnum/_version.py +2 -2
- {dcnum-0.23.1 → dcnum-0.23.3}/src/dcnum/feat/event_extractor_manager_thread.py +6 -5
- {dcnum-0.23.1 → dcnum-0.23.3}/src/dcnum/logic/ctrl.py +28 -3
- {dcnum-0.23.1 → dcnum-0.23.3}/src/dcnum/logic/job.py +22 -0
- {dcnum-0.23.1 → dcnum-0.23.3}/src/dcnum/read/hdf5_data.py +3 -1
- {dcnum-0.23.1 → dcnum-0.23.3}/src/dcnum/segm/segm_torch/__init__.py +8 -4
- {dcnum-0.23.1 → dcnum-0.23.3}/src/dcnum/write/writer.py +24 -12
- {dcnum-0.23.1 → dcnum-0.23.3}/src/dcnum.egg-info/PKG-INFO +2 -2
- {dcnum-0.23.1 → dcnum-0.23.3}/src/dcnum.egg-info/requires.txt +1 -1
- {dcnum-0.23.1 → dcnum-0.23.3}/tests/helper_methods.py +1 -1
- {dcnum-0.23.1 → dcnum-0.23.3}/tests/test_feat_brightness.py +2 -8
- {dcnum-0.23.1 → dcnum-0.23.3}/tests/test_feat_haralick.py +1 -6
- {dcnum-0.23.1 → dcnum-0.23.3}/tests/test_feat_moments_based.py +18 -23
- {dcnum-0.23.1 → dcnum-0.23.3}/tests/test_feat_moments_based_extended.py +9 -14
- {dcnum-0.23.1 → dcnum-0.23.3}/tests/test_feat_volume.py +1 -5
- {dcnum-0.23.1 → dcnum-0.23.3}/tests/test_logic_job.py +37 -1
- {dcnum-0.23.1 → dcnum-0.23.3}/tests/test_logic_pipeline.py +8 -8
- {dcnum-0.23.1 → dcnum-0.23.3}/tests/test_read_concat_hdf5.py +10 -24
- {dcnum-0.23.1 → dcnum-0.23.3}/tests/test_read_hdf5.py +7 -12
- {dcnum-0.23.1 → dcnum-0.23.3}/tests/test_segm_base.py +3 -5
- {dcnum-0.23.1 → dcnum-0.23.3}/tests/test_segm_mpo.py +10 -10
- {dcnum-0.23.1 → dcnum-0.23.3}/tests/test_segm_no_mask_proc.py +3 -6
- {dcnum-0.23.1 → dcnum-0.23.3}/tests/test_segm_sto.py +10 -10
- {dcnum-0.23.1 → dcnum-0.23.3}/tests/test_segm_thresh.py +2 -5
- {dcnum-0.23.1 → dcnum-0.23.3}/tests/test_segm_torch.py +12 -17
- {dcnum-0.23.1 → dcnum-0.23.3}/tests/test_write_deque_writer_thread.py +1 -5
- {dcnum-0.23.1 → dcnum-0.23.3}/tests/test_write_writer.py +39 -2
- {dcnum-0.23.1 → dcnum-0.23.3}/.github/workflows/check.yml +0 -0
- {dcnum-0.23.1 → dcnum-0.23.3}/.github/workflows/deploy_pypi.yml +0 -0
- {dcnum-0.23.1 → dcnum-0.23.3}/.gitignore +0 -0
- {dcnum-0.23.1 → dcnum-0.23.3}/.readthedocs.yml +0 -0
- {dcnum-0.23.1 → dcnum-0.23.3}/LICENSE +0 -0
- {dcnum-0.23.1 → dcnum-0.23.3}/README.rst +0 -0
- {dcnum-0.23.1 → dcnum-0.23.3}/docs/conf.py +0 -0
- {dcnum-0.23.1 → dcnum-0.23.3}/docs/extensions/github_changelog.py +0 -0
- {dcnum-0.23.1 → dcnum-0.23.3}/docs/index.rst +0 -0
- {dcnum-0.23.1 → dcnum-0.23.3}/docs/requirements.txt +0 -0
- {dcnum-0.23.1 → dcnum-0.23.3}/setup.cfg +0 -0
- {dcnum-0.23.1 → dcnum-0.23.3}/src/dcnum/__init__.py +0 -0
- {dcnum-0.23.1 → dcnum-0.23.3}/src/dcnum/feat/__init__.py +0 -0
- {dcnum-0.23.1 → dcnum-0.23.3}/src/dcnum/feat/feat_background/__init__.py +0 -0
- {dcnum-0.23.1 → dcnum-0.23.3}/src/dcnum/feat/feat_background/base.py +0 -0
- {dcnum-0.23.1 → dcnum-0.23.3}/src/dcnum/feat/feat_background/bg_copy.py +0 -0
- {dcnum-0.23.1 → dcnum-0.23.3}/src/dcnum/feat/feat_background/bg_roll_median.py +0 -0
- {dcnum-0.23.1 → dcnum-0.23.3}/src/dcnum/feat/feat_background/bg_sparse_median.py +0 -0
- {dcnum-0.23.1 → dcnum-0.23.3}/src/dcnum/feat/feat_brightness/__init__.py +0 -0
- {dcnum-0.23.1 → dcnum-0.23.3}/src/dcnum/feat/feat_brightness/bright_all.py +0 -0
- {dcnum-0.23.1 → dcnum-0.23.3}/src/dcnum/feat/feat_brightness/common.py +0 -0
- {dcnum-0.23.1 → dcnum-0.23.3}/src/dcnum/feat/feat_contour/__init__.py +0 -0
- {dcnum-0.23.1 → dcnum-0.23.3}/src/dcnum/feat/feat_contour/contour.py +0 -0
- {dcnum-0.23.1 → dcnum-0.23.3}/src/dcnum/feat/feat_contour/moments.py +0 -0
- {dcnum-0.23.1 → dcnum-0.23.3}/src/dcnum/feat/feat_contour/volume.py +0 -0
- {dcnum-0.23.1 → dcnum-0.23.3}/src/dcnum/feat/feat_texture/__init__.py +0 -0
- {dcnum-0.23.1 → dcnum-0.23.3}/src/dcnum/feat/feat_texture/common.py +0 -0
- {dcnum-0.23.1 → dcnum-0.23.3}/src/dcnum/feat/feat_texture/tex_all.py +0 -0
- {dcnum-0.23.1 → dcnum-0.23.3}/src/dcnum/feat/gate.py +0 -0
- {dcnum-0.23.1 → dcnum-0.23.3}/src/dcnum/feat/queue_event_extractor.py +0 -0
- {dcnum-0.23.1 → dcnum-0.23.3}/src/dcnum/logic/__init__.py +0 -0
- {dcnum-0.23.1 → dcnum-0.23.3}/src/dcnum/logic/json_encoder.py +0 -0
- {dcnum-0.23.1 → dcnum-0.23.3}/src/dcnum/meta/__init__.py +0 -0
- {dcnum-0.23.1 → dcnum-0.23.3}/src/dcnum/meta/paths.py +0 -0
- {dcnum-0.23.1 → dcnum-0.23.3}/src/dcnum/meta/ppid.py +0 -0
- {dcnum-0.23.1 → dcnum-0.23.3}/src/dcnum/read/__init__.py +0 -0
- {dcnum-0.23.1 → dcnum-0.23.3}/src/dcnum/read/cache.py +0 -0
- {dcnum-0.23.1 → dcnum-0.23.3}/src/dcnum/read/const.py +0 -0
- {dcnum-0.23.1 → dcnum-0.23.3}/src/dcnum/read/mapped.py +0 -0
- {dcnum-0.23.1 → dcnum-0.23.3}/src/dcnum/segm/__init__.py +0 -0
- {dcnum-0.23.1 → dcnum-0.23.3}/src/dcnum/segm/segm_thresh.py +0 -0
- {dcnum-0.23.1 → dcnum-0.23.3}/src/dcnum/segm/segm_torch/segm_torch_base.py +0 -0
- {dcnum-0.23.1 → dcnum-0.23.3}/src/dcnum/segm/segm_torch/segm_torch_mpo.py +0 -0
- {dcnum-0.23.1 → dcnum-0.23.3}/src/dcnum/segm/segm_torch/segm_torch_sto.py +0 -0
- {dcnum-0.23.1 → dcnum-0.23.3}/src/dcnum/segm/segm_torch/torch_model.py +0 -0
- {dcnum-0.23.1 → dcnum-0.23.3}/src/dcnum/segm/segm_torch/torch_postproc.py +0 -0
- {dcnum-0.23.1 → dcnum-0.23.3}/src/dcnum/segm/segm_torch/torch_preproc.py +0 -0
- {dcnum-0.23.1 → dcnum-0.23.3}/src/dcnum/segm/segmenter.py +0 -0
- {dcnum-0.23.1 → dcnum-0.23.3}/src/dcnum/segm/segmenter_manager_thread.py +0 -0
- {dcnum-0.23.1 → dcnum-0.23.3}/src/dcnum/segm/segmenter_mpo.py +0 -0
- {dcnum-0.23.1 → dcnum-0.23.3}/src/dcnum/segm/segmenter_sto.py +0 -0
- {dcnum-0.23.1 → dcnum-0.23.3}/src/dcnum/write/__init__.py +0 -0
- {dcnum-0.23.1 → dcnum-0.23.3}/src/dcnum/write/deque_writer_thread.py +0 -0
- {dcnum-0.23.1 → dcnum-0.23.3}/src/dcnum/write/queue_collector_thread.py +0 -0
- {dcnum-0.23.1 → dcnum-0.23.3}/src/dcnum.egg-info/SOURCES.txt +0 -0
- {dcnum-0.23.1 → dcnum-0.23.3}/src/dcnum.egg-info/dependency_links.txt +0 -0
- {dcnum-0.23.1 → dcnum-0.23.3}/src/dcnum.egg-info/top_level.txt +0 -0
- {dcnum-0.23.1 → dcnum-0.23.3}/tests/conftest.py +0 -0
- {dcnum-0.23.1 → dcnum-0.23.3}/tests/data/fmt-hdf5_cytoshot_extended-moments-features.zip +0 -0
- {dcnum-0.23.1 → dcnum-0.23.3}/tests/data/fmt-hdf5_cytoshot_full-features_2023.zip +0 -0
- {dcnum-0.23.1 → dcnum-0.23.3}/tests/data/fmt-hdf5_cytoshot_full-features_2024.zip +0 -0
- {dcnum-0.23.1 → dcnum-0.23.3}/tests/data/fmt-hdf5_cytoshot_full-features_legacy_allev_2023.zip +0 -0
- {dcnum-0.23.1 → dcnum-0.23.3}/tests/data/fmt-hdf5_shapein_empty.zip +0 -0
- {dcnum-0.23.1 → dcnum-0.23.3}/tests/data/fmt-hdf5_shapein_raw-with-variable-length-logs.zip +0 -0
- {dcnum-0.23.1 → dcnum-0.23.3}/tests/data/segm-torch-model_unet-dcnum-test_g1_910c2.zip +0 -0
- {dcnum-0.23.1 → dcnum-0.23.3}/tests/data/segm-torch-test-data_unet-dcnum-test_g1_910c2.zip +0 -0
- {dcnum-0.23.1 → dcnum-0.23.3}/tests/requirements.txt +0 -0
- {dcnum-0.23.1 → dcnum-0.23.3}/tests/test_feat_background_base.py +0 -0
- {dcnum-0.23.1 → dcnum-0.23.3}/tests/test_feat_background_bg_copy.py +0 -0
- {dcnum-0.23.1 → dcnum-0.23.3}/tests/test_feat_background_bg_roll_median.py +0 -0
- {dcnum-0.23.1 → dcnum-0.23.3}/tests/test_feat_background_bg_sparsemed.py +0 -0
- {dcnum-0.23.1 → dcnum-0.23.3}/tests/test_feat_event_extractor_manager.py +0 -0
- {dcnum-0.23.1 → dcnum-0.23.3}/tests/test_feat_gate.py +0 -0
- {dcnum-0.23.1 → dcnum-0.23.3}/tests/test_init.py +0 -0
- {dcnum-0.23.1 → dcnum-0.23.3}/tests/test_logic_join.py +0 -0
- {dcnum-0.23.1 → dcnum-0.23.3}/tests/test_logic_json.py +0 -0
- {dcnum-0.23.1 → dcnum-0.23.3}/tests/test_meta_paths.py +0 -0
- {dcnum-0.23.1 → dcnum-0.23.3}/tests/test_meta_ppid_base.py +0 -0
- {dcnum-0.23.1 → dcnum-0.23.3}/tests/test_meta_ppid_bg.py +0 -0
- {dcnum-0.23.1 → dcnum-0.23.3}/tests/test_meta_ppid_data.py +0 -0
- {dcnum-0.23.1 → dcnum-0.23.3}/tests/test_meta_ppid_feat.py +0 -0
- {dcnum-0.23.1 → dcnum-0.23.3}/tests/test_meta_ppid_gate.py +0 -0
- {dcnum-0.23.1 → dcnum-0.23.3}/tests/test_meta_ppid_segm.py +0 -0
- {dcnum-0.23.1 → dcnum-0.23.3}/tests/test_read_basin.py +0 -0
- {dcnum-0.23.1 → dcnum-0.23.3}/tests/test_read_hdf5_basins.py +0 -0
- {dcnum-0.23.1 → dcnum-0.23.3}/tests/test_read_hdf5_index_mapping.py +0 -0
- {dcnum-0.23.1 → dcnum-0.23.3}/tests/test_segm_torch_preproc.py +0 -0
- {dcnum-0.23.1 → dcnum-0.23.3}/tests/test_write_queue_collector_thread.py +0 -0
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
0.23.3
|
|
2
|
+
- fix: ignore non-file-type-like basins
|
|
3
|
+
- fix: workaround for slow reading from HDF5 (don't use index arrays)
|
|
4
|
+
- fix: avoid excessive stalling when writer is slow
|
|
5
|
+
0.23.2
|
|
6
|
+
- enh: add DCNumPipelineJob.validate method
|
|
7
|
+
- enh: list Python libraries used in job log
|
|
8
|
+
- setup: change required pytorch version from 2.3 to 2.2 (hardware support)
|
|
1
9
|
0.23.1
|
|
2
10
|
- enh: support passing custom default arguments to get_class_method_info
|
|
3
11
|
- tests: fix torch preprocessing tests
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: dcnum
|
|
3
|
-
Version: 0.23.
|
|
3
|
+
Version: 0.23.3
|
|
4
4
|
Summary: numerics toolbox for imaging deformability cytometry
|
|
5
5
|
Author: Maximilian Schlögel, Paul Müller, Raghava Alajangi
|
|
6
6
|
Maintainer-email: Paul Müller <dev@craban.de>
|
|
@@ -26,7 +26,7 @@ Requires-Dist: opencv-python-headless
|
|
|
26
26
|
Requires-Dist: scikit-image
|
|
27
27
|
Requires-Dist: scipy>=1.8.0
|
|
28
28
|
Provides-Extra: torch
|
|
29
|
-
Requires-Dist: torch>=2.
|
|
29
|
+
Requires-Dist: torch>=2.2; extra == "torch"
|
|
30
30
|
|
|
31
31
|
|dcnum|
|
|
32
32
|
=======
|
|
@@ -96,12 +96,13 @@ class EventExtractorManagerThread(threading.Thread):
|
|
|
96
96
|
# If the writer_dq starts filling up, then this could lead to
|
|
97
97
|
# an oom-kill signal. Stall for the writer to prevent this.
|
|
98
98
|
if (ldq := len(self.writer_dq)) > 1000:
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
99
|
+
stalled_sec = 0.
|
|
100
|
+
for ii in range(60):
|
|
101
|
+
if len(self.writer_dq) > 200:
|
|
102
|
+
time.sleep(.5)
|
|
103
|
+
stalled_sec += .5
|
|
103
104
|
self.logger.warning(
|
|
104
|
-
f"Stalled {
|
|
105
|
+
f"Stalled {stalled_sec:.1f}s due to slow writer "
|
|
105
106
|
f"({ldq} chunks queued)")
|
|
106
107
|
|
|
107
108
|
unavailable_slots = 0
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import collections
|
|
2
2
|
import datetime
|
|
3
3
|
import hashlib
|
|
4
|
+
import importlib
|
|
4
5
|
import json
|
|
5
6
|
import logging
|
|
6
7
|
from logging.handlers import QueueListener
|
|
@@ -33,6 +34,7 @@ from ..write import (
|
|
|
33
34
|
from .job import DCNumPipelineJob
|
|
34
35
|
from .json_encoder import ExtendedJSONEncoder
|
|
35
36
|
|
|
37
|
+
|
|
36
38
|
# Force using "spawn" method for multiprocessing, because we are using
|
|
37
39
|
# queues and threads and would end up with race conditions otherwise.
|
|
38
40
|
mp_spawn = mp.get_context("spawn")
|
|
@@ -430,6 +432,16 @@ class DCNumJobRunner(threading.Thread):
|
|
|
430
432
|
"build": ", ".join(platform.python_build()),
|
|
431
433
|
"implementation":
|
|
432
434
|
platform.python_implementation(),
|
|
435
|
+
"libraries": get_library_versions_dict([
|
|
436
|
+
"cv2",
|
|
437
|
+
"h5py",
|
|
438
|
+
"mahotas",
|
|
439
|
+
"numba",
|
|
440
|
+
"numpy",
|
|
441
|
+
"scipy",
|
|
442
|
+
"skimage",
|
|
443
|
+
"torch",
|
|
444
|
+
]),
|
|
433
445
|
"version": platform.python_version(),
|
|
434
446
|
},
|
|
435
447
|
"system": {
|
|
@@ -551,8 +563,8 @@ class DCNumJobRunner(threading.Thread):
|
|
|
551
563
|
# 3. image features from the input file
|
|
552
564
|
[self.draw.h5, ["image", "image_bg", "bg_off"], "optional"],
|
|
553
565
|
]
|
|
554
|
-
with
|
|
555
|
-
|
|
566
|
+
with HDF5Writer(self.path_temp_out) as hw:
|
|
567
|
+
hout = hw.h5
|
|
556
568
|
# First, we have to determine the basin mapping from input to
|
|
557
569
|
# output. This information is stored by the QueueCollectorThread
|
|
558
570
|
# in the "basinmap0" feature, ready to be used by us.
|
|
@@ -565,7 +577,7 @@ class DCNumJobRunner(threading.Thread):
|
|
|
565
577
|
# mapping of the input file was set to slice(1, 100), then the
|
|
566
578
|
# first image would not be there, and we would have
|
|
567
579
|
# [1, 1, 1, ...].
|
|
568
|
-
idx_um = hout["events/index_unmapped"]
|
|
580
|
+
idx_um = hout["events/index_unmapped"][:]
|
|
569
581
|
|
|
570
582
|
# If we want to convert this to an actual basinmap feature,
|
|
571
583
|
# then we have to convert those indices to indices that map
|
|
@@ -780,6 +792,19 @@ class DCNumJobRunner(threading.Thread):
|
|
|
780
792
|
self.logger.info("Finished segmentation and feature extraction")
|
|
781
793
|
|
|
782
794
|
|
|
795
|
+
def get_library_versions_dict(library_name_list):
|
|
796
|
+
version_dict = {}
|
|
797
|
+
for library_name in library_name_list:
|
|
798
|
+
try:
|
|
799
|
+
lib = importlib.import_module(library_name)
|
|
800
|
+
except BaseException:
|
|
801
|
+
version = None
|
|
802
|
+
else:
|
|
803
|
+
version = lib.__version__
|
|
804
|
+
version_dict[library_name] = version
|
|
805
|
+
return version_dict
|
|
806
|
+
|
|
807
|
+
|
|
783
808
|
def join_thread_helper(thr, timeout, retries, logger, name):
|
|
784
809
|
for _ in range(retries):
|
|
785
810
|
thr.join(timeout=timeout)
|
|
@@ -182,3 +182,25 @@ class DCNumPipelineJob:
|
|
|
182
182
|
if len(ret) == 1:
|
|
183
183
|
ret = ret[0]
|
|
184
184
|
return ret
|
|
185
|
+
|
|
186
|
+
def validate(self):
|
|
187
|
+
"""Make sure the pipeline will run given the job kwargs
|
|
188
|
+
|
|
189
|
+
Returns
|
|
190
|
+
-------
|
|
191
|
+
True:
|
|
192
|
+
for testing convenience
|
|
193
|
+
|
|
194
|
+
Raises
|
|
195
|
+
------
|
|
196
|
+
dcnum.segm.SegmenterNotApplicableError:
|
|
197
|
+
the segmenter is incompatible with the input path
|
|
198
|
+
"""
|
|
199
|
+
# Check segmenter applicability applicability
|
|
200
|
+
seg_cls = get_available_segmenters()[self.kwargs["segmenter_code"]]
|
|
201
|
+
with HDF5Data(self.kwargs["path_in"]) as hd:
|
|
202
|
+
seg_cls.validate_applicability(
|
|
203
|
+
segmenter_kwargs=self.kwargs["segmenter_kwargs"],
|
|
204
|
+
logs=hd.logs,
|
|
205
|
+
meta=hd.meta)
|
|
206
|
+
return True
|
|
@@ -205,7 +205,9 @@ class HDF5Data:
|
|
|
205
205
|
bn_data = "\n".join(
|
|
206
206
|
[s.decode() for s in h5["basins"][bnkey][:].tolist()])
|
|
207
207
|
bn_dict = json.loads(bn_data)
|
|
208
|
-
|
|
208
|
+
if bn_dict["type"] == "file":
|
|
209
|
+
# we only support file-based basins
|
|
210
|
+
basins.append(bn_dict)
|
|
209
211
|
self.basins = sorted(basins, key=lambda x: x["name"])
|
|
210
212
|
|
|
211
213
|
if state["pixel_size"] is not None:
|
|
@@ -1,16 +1,20 @@
|
|
|
1
1
|
import importlib
|
|
2
|
+
import warnings
|
|
2
3
|
|
|
3
4
|
try:
|
|
4
5
|
torch = importlib.import_module("torch")
|
|
5
6
|
req_maj = 2
|
|
6
|
-
req_min =
|
|
7
|
+
req_min = 2
|
|
7
8
|
ver_tuple = torch.__version__.split(".")
|
|
8
9
|
act_maj = int(ver_tuple[0])
|
|
9
10
|
act_min = int(ver_tuple[1])
|
|
10
11
|
if act_maj < req_maj or (act_maj == req_maj and act_min < req_min):
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
12
|
+
warnings.warn(f"Your PyTorch version {act_maj}.{act_min} is "
|
|
13
|
+
f"not supported, please update to at least "
|
|
14
|
+
f"{req_maj}.{req_min} to use dcnum's PyTorch"
|
|
15
|
+
f"segmenters")
|
|
16
|
+
raise ImportError(
|
|
17
|
+
f"Could not find PyTorch {req_maj}.{req_min}")
|
|
14
18
|
except ImportError:
|
|
15
19
|
pass
|
|
16
20
|
else:
|
|
@@ -48,7 +48,13 @@ class HDF5Writer:
|
|
|
48
48
|
self.h5 = obj
|
|
49
49
|
self.h5_owned = False
|
|
50
50
|
else:
|
|
51
|
-
self.h5 = h5py.File(obj,
|
|
51
|
+
self.h5 = h5py.File(obj,
|
|
52
|
+
mode=mode,
|
|
53
|
+
libver="latest",
|
|
54
|
+
# Set chunk cache size to 3 MiB for each
|
|
55
|
+
# dataset to allow partial writes.
|
|
56
|
+
rdcc_nbytes=3145728,
|
|
57
|
+
)
|
|
52
58
|
self.h5_owned = True
|
|
53
59
|
self.events = self.h5.require_group("events")
|
|
54
60
|
ds_kwds = set_default_filter_kwargs(ds_kwds)
|
|
@@ -323,8 +329,6 @@ def copy_features(h5_src: h5py.File,
|
|
|
323
329
|
"""
|
|
324
330
|
ei = h5_src["events"]
|
|
325
331
|
eo = h5_dst.require_group("events")
|
|
326
|
-
# This is the size of the output dataset
|
|
327
|
-
size = h5_dst.attrs["experiment:event count"]
|
|
328
332
|
hw = HDF5Writer(h5_dst)
|
|
329
333
|
for feat in features:
|
|
330
334
|
if feat in eo:
|
|
@@ -341,20 +345,28 @@ def copy_features(h5_src: h5py.File,
|
|
|
341
345
|
dst_name=feat.encode(),
|
|
342
346
|
)
|
|
343
347
|
else:
|
|
344
|
-
#
|
|
345
|
-
#
|
|
348
|
+
# We have to perform mapping.
|
|
349
|
+
# Since h5py is very slow at indexing with arrays,
|
|
350
|
+
# we instead read the data in chunks from the input file,
|
|
351
|
+
# and perform the mapping afterward using the numpy arrays.
|
|
346
352
|
dsi = ei[feat]
|
|
347
353
|
chunk_size = hw.get_best_nd_chunks(dsi[0].shape, dsi.dtype)[0]
|
|
354
|
+
size_in = dsi.shape[0]
|
|
348
355
|
start = 0
|
|
349
|
-
while start <
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
356
|
+
while start < size_in:
|
|
357
|
+
# Get a big chunk of data
|
|
358
|
+
big_chunk = 10 * chunk_size
|
|
359
|
+
stop = start + big_chunk
|
|
360
|
+
data_in = dsi[start:stop]
|
|
361
|
+
# Determine the indices that we need from that chunk.
|
|
362
|
+
mapping_idx = (start <= mapping) * (mapping < stop)
|
|
363
|
+
mapping_chunk = mapping[mapping_idx] - start
|
|
364
|
+
data = data_in[mapping_chunk]
|
|
365
|
+
# Note that HDF5 does its own caching, properly handling
|
|
366
|
+
# partial chunk writes.
|
|
355
367
|
hw.store_feature_chunk(feat, data)
|
|
356
368
|
# increment start
|
|
357
|
-
start
|
|
369
|
+
start = stop
|
|
358
370
|
|
|
359
371
|
|
|
360
372
|
def copy_metadata(h5_src: h5py.File,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: dcnum
|
|
3
|
-
Version: 0.23.
|
|
3
|
+
Version: 0.23.3
|
|
4
4
|
Summary: numerics toolbox for imaging deformability cytometry
|
|
5
5
|
Author: Maximilian Schlögel, Paul Müller, Raghava Alajangi
|
|
6
6
|
Maintainer-email: Paul Müller <dev@craban.de>
|
|
@@ -26,7 +26,7 @@ Requires-Dist: opencv-python-headless
|
|
|
26
26
|
Requires-Dist: scikit-image
|
|
27
27
|
Requires-Dist: scipy>=1.8.0
|
|
28
28
|
Provides-Extra: torch
|
|
29
|
-
Requires-Dist: torch>=2.
|
|
29
|
+
Requires-Dist: torch>=2.2; extra == "torch"
|
|
30
30
|
|
|
31
31
|
|dcnum|
|
|
32
32
|
=======
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import pathlib
|
|
2
|
-
|
|
3
1
|
import h5py
|
|
4
2
|
import numpy as np
|
|
5
3
|
|
|
@@ -7,13 +5,10 @@ from dcnum.feat import feat_brightness
|
|
|
7
5
|
|
|
8
6
|
from helper_methods import retrieve_data
|
|
9
7
|
|
|
10
|
-
data_path = pathlib.Path(__file__).parent / "data"
|
|
11
|
-
|
|
12
8
|
|
|
13
9
|
def test_basic_brightness():
|
|
14
10
|
# This original file was generated with dcevent for reference.
|
|
15
|
-
path = retrieve_data(
|
|
16
|
-
"fmt-hdf5_cytoshot_full-features_2023.zip")
|
|
11
|
+
path = retrieve_data("fmt-hdf5_cytoshot_full-features_2023.zip")
|
|
17
12
|
# Make data available
|
|
18
13
|
with h5py.File(path) as h5:
|
|
19
14
|
data = feat_brightness.brightness_features(
|
|
@@ -35,8 +30,7 @@ def test_basic_brightness():
|
|
|
35
30
|
|
|
36
31
|
def test_basic_brightness_single_image():
|
|
37
32
|
# This original file was generated with dcevent for reference.
|
|
38
|
-
path = retrieve_data(
|
|
39
|
-
"fmt-hdf5_cytoshot_full-features_2023.zip")
|
|
33
|
+
path = retrieve_data("fmt-hdf5_cytoshot_full-features_2023.zip")
|
|
40
34
|
# Make data available
|
|
41
35
|
with h5py.File(path) as h5:
|
|
42
36
|
data = feat_brightness.brightness_features(
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import pathlib
|
|
2
|
-
|
|
3
1
|
import h5py
|
|
4
2
|
import numpy as np
|
|
5
3
|
|
|
@@ -7,13 +5,10 @@ from dcnum.feat import feat_texture
|
|
|
7
5
|
|
|
8
6
|
from helper_methods import retrieve_data
|
|
9
7
|
|
|
10
|
-
data_path = pathlib.Path(__file__).parent / "data"
|
|
11
|
-
|
|
12
8
|
|
|
13
9
|
def test_basic_haralick():
|
|
14
10
|
# This original file was generated with dcevent for reference.
|
|
15
|
-
path = retrieve_data(
|
|
16
|
-
"fmt-hdf5_cytoshot_full-features_2023.zip")
|
|
11
|
+
path = retrieve_data("fmt-hdf5_cytoshot_full-features_2023.zip")
|
|
17
12
|
# Make data available
|
|
18
13
|
with h5py.File(path) as h5:
|
|
19
14
|
ret_arr = feat_texture.haralick_texture_features(
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import pathlib
|
|
2
|
-
|
|
3
1
|
import h5py
|
|
4
2
|
import numpy as np
|
|
5
3
|
import scipy.ndimage as ndi
|
|
@@ -8,8 +6,6 @@ from dcnum.feat import feat_contour
|
|
|
8
6
|
|
|
9
7
|
from helper_methods import retrieve_data
|
|
10
8
|
|
|
11
|
-
data_path = pathlib.Path(__file__).parent / "data"
|
|
12
|
-
|
|
13
9
|
|
|
14
10
|
def test_inert_ratio_prnc():
|
|
15
11
|
"""Test tilt and equivalence of inert_ratio_raw and inert_ratio_prnc"""
|
|
@@ -151,8 +147,7 @@ def test_inert_ratio_prnc_simple_2():
|
|
|
151
147
|
|
|
152
148
|
def test_moments_based_features():
|
|
153
149
|
# This original file was generated with dcevent for reference.
|
|
154
|
-
path = retrieve_data(
|
|
155
|
-
"fmt-hdf5_cytoshot_full-features_2023.zip")
|
|
150
|
+
path = retrieve_data("fmt-hdf5_cytoshot_full-features_2023.zip")
|
|
156
151
|
feats = [
|
|
157
152
|
"deform",
|
|
158
153
|
"size_x",
|
|
@@ -201,9 +196,9 @@ def test_mask_0d():
|
|
|
201
196
|
[0, 0, 0, 0, 0, 0],
|
|
202
197
|
], dtype=bool)[np.newaxis]
|
|
203
198
|
data = feat_contour.moments_based_features(
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
199
|
+
mask=masks,
|
|
200
|
+
pixel_size=0.2645
|
|
201
|
+
)
|
|
207
202
|
assert data["deform"].shape == (1,)
|
|
208
203
|
assert np.isnan(data["deform"][0])
|
|
209
204
|
assert np.isnan(data["area_um"][0])
|
|
@@ -219,9 +214,9 @@ def test_mask_1d():
|
|
|
219
214
|
[0, 0, 0, 0, 0, 0],
|
|
220
215
|
], dtype=bool)[np.newaxis]
|
|
221
216
|
data = feat_contour.moments_based_features(
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
217
|
+
mask=masks,
|
|
218
|
+
pixel_size=0.2645
|
|
219
|
+
)
|
|
225
220
|
assert data["deform"].shape == (1,)
|
|
226
221
|
assert np.isnan(data["deform"][0])
|
|
227
222
|
assert np.isnan(data["area_um"][0])
|
|
@@ -237,9 +232,9 @@ def test_mask_1d_large():
|
|
|
237
232
|
[0, 0, 1, 0, 0, 0],
|
|
238
233
|
], dtype=bool)[np.newaxis]
|
|
239
234
|
data = feat_contour.moments_based_features(
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
235
|
+
mask=masks,
|
|
236
|
+
pixel_size=0.2645
|
|
237
|
+
)
|
|
243
238
|
assert data["deform"].shape == (1,)
|
|
244
239
|
assert np.isnan(data["deform"][0])
|
|
245
240
|
assert np.isnan(data["area_um"][0])
|
|
@@ -257,9 +252,9 @@ def test_mask_1d_large_no_border():
|
|
|
257
252
|
[0, 0, 0, 0, 0, 0],
|
|
258
253
|
], dtype=bool)[np.newaxis]
|
|
259
254
|
data = feat_contour.moments_based_features(
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
255
|
+
mask=masks,
|
|
256
|
+
pixel_size=0.2645
|
|
257
|
+
)
|
|
263
258
|
assert data["deform"].shape == (1,)
|
|
264
259
|
assert np.isnan(data["deform"][0])
|
|
265
260
|
assert np.isnan(data["area_um"][0])
|
|
@@ -275,9 +270,9 @@ def test_mask_2d():
|
|
|
275
270
|
[0, 0, 0, 0, 0, 0],
|
|
276
271
|
], dtype=bool)[np.newaxis]
|
|
277
272
|
data = feat_contour.moments_based_features(
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
273
|
+
mask=masks,
|
|
274
|
+
pixel_size=0.2645
|
|
275
|
+
)
|
|
281
276
|
assert data["deform"].shape == (1,)
|
|
282
277
|
# This is the deformation of a square (compared to circle)
|
|
283
278
|
assert np.allclose(data["deform"][0], 0.11377307454724206)
|
|
@@ -305,8 +300,8 @@ def test_mask_mixed():
|
|
|
305
300
|
mixed_masks = np.append(mask_valid[None, ...],
|
|
306
301
|
mask_invalid[None, ...], axis=0)
|
|
307
302
|
data = feat_contour.moments_based_features(
|
|
308
|
-
|
|
309
|
-
|
|
303
|
+
mask=mixed_masks,
|
|
304
|
+
pixel_size=0.2645)
|
|
310
305
|
assert data["deform"].shape == (2,)
|
|
311
306
|
assert np.all(data["valid"][:] == np.array([True, False]))
|
|
312
307
|
assert not np.isnan(data["deform"][0])
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import pathlib
|
|
2
|
-
|
|
3
1
|
import h5py
|
|
4
2
|
import numpy as np
|
|
5
3
|
|
|
@@ -7,21 +5,18 @@ from dcnum.feat import feat_contour
|
|
|
7
5
|
|
|
8
6
|
from helper_methods import retrieve_data
|
|
9
7
|
|
|
10
|
-
data_path = pathlib.Path(__file__).parent / "data"
|
|
11
|
-
|
|
12
8
|
|
|
13
9
|
def test_moments_based_features():
|
|
14
10
|
# This file has new cell features belonging to
|
|
15
11
|
# fmt-hdf5_cytoshot_full-features_2023.zip
|
|
16
|
-
path = retrieve_data(
|
|
17
|
-
"fmt-hdf5_cytoshot_extended-moments-features.zip")
|
|
12
|
+
path = retrieve_data("fmt-hdf5_cytoshot_extended-moments-features.zip")
|
|
18
13
|
|
|
19
14
|
feats = [
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
15
|
+
"area_um_raw",
|
|
16
|
+
"deform_raw",
|
|
17
|
+
"eccentr_prnc",
|
|
18
|
+
"per_ratio",
|
|
19
|
+
"per_um_raw",
|
|
25
20
|
]
|
|
26
21
|
|
|
27
22
|
# Make data available
|
|
@@ -49,9 +44,9 @@ def test_mask_2d():
|
|
|
49
44
|
[0, 0, 0, 0, 0, 0],
|
|
50
45
|
], dtype=bool)[np.newaxis]
|
|
51
46
|
data = feat_contour.moments_based_features(
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
47
|
+
mask=masks,
|
|
48
|
+
pixel_size=0.2645
|
|
49
|
+
)
|
|
55
50
|
assert data["deform_raw"].shape == (1,)
|
|
56
51
|
# This is the deformation of a square (compared to circle)
|
|
57
52
|
assert np.allclose(data["deform_raw"][0], 0.11377307454724206)
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import itertools
|
|
2
|
-
import pathlib
|
|
3
2
|
|
|
4
3
|
import h5py
|
|
5
4
|
import numpy as np
|
|
@@ -10,8 +9,6 @@ from dcnum.feat.feat_contour import moments_based_features
|
|
|
10
9
|
|
|
11
10
|
from helper_methods import retrieve_data
|
|
12
11
|
|
|
13
|
-
data_path = pathlib.Path(__file__).parent / "data"
|
|
14
|
-
|
|
15
12
|
|
|
16
13
|
def area_of_polygon(x, y):
|
|
17
14
|
return 0.5 * np.abs(np.dot(x, np.roll(y, 1)) - np.dot(y, np.roll(x, 1)))
|
|
@@ -105,8 +102,7 @@ def test_volume_from_file():
|
|
|
105
102
|
1.09564687e+02, 2.39239370e-01, 1.71917437e-01, 8.98323862e+01,
|
|
106
103
|
4.13412223e+00, 2.91659170e+02, 2.00198054e+02, 1.97545320e+00,
|
|
107
104
|
9.15408837e+01, 1.60965362e-01, 3.48553309e-01, 2.04561447e+02])
|
|
108
|
-
path = retrieve_data(
|
|
109
|
-
"fmt-hdf5_cytoshot_full-features_2023.zip")
|
|
105
|
+
path = retrieve_data("fmt-hdf5_cytoshot_full-features_2023.zip")
|
|
110
106
|
|
|
111
107
|
with h5py.File(path) as h5:
|
|
112
108
|
pixel_size = h5.attrs["imaging:pixel size"]
|
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
import multiprocessing as mp
|
|
2
2
|
|
|
3
3
|
from dcnum import logic
|
|
4
|
+
from dcnum.segm.segm_torch import segm_torch_base # noqa: E402
|
|
5
|
+
import h5py
|
|
4
6
|
|
|
5
|
-
|
|
7
|
+
import pytest
|
|
8
|
+
|
|
9
|
+
from helper_methods import retrieve_data, retrieve_model
|
|
6
10
|
|
|
7
11
|
|
|
8
12
|
def test_basic_job():
|
|
@@ -36,3 +40,35 @@ def test_segmenter_mask():
|
|
|
36
40
|
)
|
|
37
41
|
_, pdict = job.get_ppid(ret_dict=True)
|
|
38
42
|
assert pdict["seg_id"] == "thresh:t=-6:cle=1^f=1^clo=3"
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def test_validate_invalid_model():
|
|
46
|
+
model_file = retrieve_model(
|
|
47
|
+
"segm-torch-model_unet-dcnum-test_g1_910c2.zip")
|
|
48
|
+
|
|
49
|
+
# Create a test dataset with metadata that will make the model invalid
|
|
50
|
+
path = retrieve_data("fmt-hdf5_cytoshot_full-features_2024.zip")
|
|
51
|
+
|
|
52
|
+
with h5py.File(path, "a") as h5:
|
|
53
|
+
h5.attrs["setup:chip region"] = "reservoir"
|
|
54
|
+
|
|
55
|
+
job = logic.DCNumPipelineJob(path_in=path,
|
|
56
|
+
segmenter_code="torchmpo",
|
|
57
|
+
segmenter_kwargs={
|
|
58
|
+
"model_file": model_file},
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
with pytest.raises(
|
|
62
|
+
segm_torch_base.SegmenterNotApplicableError,
|
|
63
|
+
match="only experiments in channel region supported"):
|
|
64
|
+
job.validate()
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def test_validate_ok():
|
|
68
|
+
path = retrieve_data("fmt-hdf5_cytoshot_full-features_2023.zip")
|
|
69
|
+
job = logic.DCNumPipelineJob(path_in=path,
|
|
70
|
+
segmenter_code="thresh",
|
|
71
|
+
segmenter_kwargs={
|
|
72
|
+
"kwargs_mask": {"closing_disk": 3}},
|
|
73
|
+
)
|
|
74
|
+
assert job.validate()
|
|
@@ -135,7 +135,7 @@ def test_basin_strategy_tap():
|
|
|
135
135
|
|
|
136
136
|
with h5py.File(path_out) as h5:
|
|
137
137
|
assert h5.attrs["pipeline:dcnum background"] \
|
|
138
|
-
|
|
138
|
+
== "sparsemed:k=150^s=1^t=0^f=0.8^o=1"
|
|
139
139
|
assert "image_bg" in h5["events"]
|
|
140
140
|
assert "bg_off" in h5["events"]
|
|
141
141
|
assert "deform" in h5["events"]
|
|
@@ -174,7 +174,7 @@ def test_basin_relative_path():
|
|
|
174
174
|
# Everything should just work, because we have relative paths in the basin.
|
|
175
175
|
with h5py.File(path_out_new) as h5:
|
|
176
176
|
assert h5.attrs["pipeline:dcnum background"] \
|
|
177
|
-
|
|
177
|
+
== "sparsemed:k=150^s=1^t=0^f=0.8^o=1"
|
|
178
178
|
assert "image_bg" in h5["events"]
|
|
179
179
|
assert "bg_off" in h5["events"]
|
|
180
180
|
assert "deform" in h5["events"]
|
|
@@ -207,7 +207,7 @@ def test_chained_pipeline():
|
|
|
207
207
|
|
|
208
208
|
with h5py.File(path2) as h5:
|
|
209
209
|
assert h5.attrs["pipeline:dcnum background"] \
|
|
210
|
-
|
|
210
|
+
== "sparsemed:k=150^s=1^t=0^f=0.8^o=1"
|
|
211
211
|
assert "image" in h5["events"]
|
|
212
212
|
assert "image_bg" in h5["events"]
|
|
213
213
|
for feat in h5["events"]:
|
|
@@ -228,7 +228,7 @@ def test_chained_pipeline():
|
|
|
228
228
|
assert "image_bg" in h5["events"]
|
|
229
229
|
assert len(h5["events/deform"]) == 285
|
|
230
230
|
assert h5.attrs["pipeline:dcnum background"] \
|
|
231
|
-
|
|
231
|
+
== "sparsemed:k=250^s=1^t=0^f=0.8^o=1"
|
|
232
232
|
for feat in h5["events"]:
|
|
233
233
|
assert len(h5["events"][feat]) == 285
|
|
234
234
|
|
|
@@ -745,8 +745,8 @@ def test_simple_pipeline(debug):
|
|
|
745
745
|
assert h5.attrs["experiment:sample"] == "data"
|
|
746
746
|
assert h5.attrs["experiment:date"] == "2022-04-21"
|
|
747
747
|
assert h5.attrs["experiment:run identifier"] == \
|
|
748
|
-
|
|
749
|
-
|
|
748
|
+
(f"d5a40aed-0b6c-0412-e87c-59789fdd28d0_"
|
|
749
|
+
f"dcn-{pp_hash[:7]}")
|
|
750
750
|
|
|
751
751
|
|
|
752
752
|
@pytest.mark.parametrize("debug", [True, False])
|
|
@@ -814,8 +814,8 @@ def test_simple_pipeline_no_offset_correction(debug):
|
|
|
814
814
|
assert h5.attrs["experiment:sample"] == "data"
|
|
815
815
|
assert h5.attrs["experiment:date"] == "2022-04-21"
|
|
816
816
|
assert h5.attrs["experiment:run identifier"] == \
|
|
817
|
-
|
|
818
|
-
|
|
817
|
+
(f"d5a40aed-0b6c-0412-e87c-59789fdd28d0_"
|
|
818
|
+
f"dcn-{pp_hash[:7]}")
|
|
819
819
|
|
|
820
820
|
|
|
821
821
|
def test_simple_pipeline_in_thread():
|