dcnum 0.14.0__py3-none-any.whl → 0.15.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.
Potentially problematic release.
This version of dcnum might be problematic. Click here for more details.
- dcnum/_version.py +2 -2
- dcnum/feat/__init__.py +1 -0
- dcnum/feat/event_extractor_manager_thread.py +3 -0
- dcnum/feat/feat_background/__init__.py +2 -12
- dcnum/feat/feat_background/base.py +51 -33
- dcnum/feat/feat_brightness/__init__.py +1 -0
- dcnum/feat/feat_moments/__init__.py +1 -0
- dcnum/feat/feat_texture/__init__.py +1 -0
- dcnum/feat/gate.py +62 -41
- dcnum/feat/queue_event_extractor.py +80 -40
- dcnum/logic/__init__.py +4 -0
- dcnum/logic/ctrl.py +501 -0
- dcnum/logic/job.py +123 -0
- dcnum/meta/ppid.py +48 -7
- dcnum/read/hdf5_data.py +36 -1
- dcnum/segm/__init__.py +1 -13
- dcnum/segm/segm_thresh.py +1 -0
- dcnum/segm/segmenter.py +58 -17
- dcnum/segm/segmenter_cpu.py +2 -0
- dcnum/segm/segmenter_gpu.py +1 -0
- dcnum/write/deque_writer_thread.py +1 -1
- dcnum/write/writer.py +45 -4
- {dcnum-0.14.0.dist-info → dcnum-0.15.0.dist-info}/METADATA +1 -1
- dcnum-0.15.0.dist-info/RECORD +43 -0
- {dcnum-0.14.0.dist-info → dcnum-0.15.0.dist-info}/WHEEL +1 -1
- dcnum-0.14.0.dist-info/RECORD +0 -40
- {dcnum-0.14.0.dist-info → dcnum-0.15.0.dist-info}/LICENSE +0 -0
- {dcnum-0.14.0.dist-info → dcnum-0.15.0.dist-info}/top_level.txt +0 -0
dcnum/_version.py
CHANGED
dcnum/feat/__init__.py
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
"""Feature computation: managing event extraction threads"""
|
|
1
2
|
import logging
|
|
2
3
|
import multiprocessing as mp
|
|
3
4
|
import threading
|
|
@@ -45,6 +46,8 @@ class EventExtractorManagerThread(threading.Thread):
|
|
|
45
46
|
"""
|
|
46
47
|
super(EventExtractorManagerThread, self).__init__(
|
|
47
48
|
name="EventExtractorManager", *args, **kwargs)
|
|
49
|
+
if debug:
|
|
50
|
+
fe_kwargs["close_queues"] = False
|
|
48
51
|
self.logger = logging.getLogger(
|
|
49
52
|
"dcnum.feat.EventExtractorManagerThread")
|
|
50
53
|
#: Keyword arguments for class:`.EventExtractor`
|
|
@@ -1,16 +1,6 @@
|
|
|
1
1
|
# flake8: noqa: F401
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
from .base import Background
|
|
2
|
+
"""Feature computation: background image data from image data"""
|
|
3
|
+
from .base import Background, get_available_background_methods
|
|
5
4
|
# Background methods are registered by importing them here.
|
|
6
5
|
from .bg_roll_median import BackgroundRollMed
|
|
7
6
|
from .bg_sparse_median import BackgroundSparseMed
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
@functools.cache
|
|
11
|
-
def get_available_background_methods():
|
|
12
|
-
"""Return dictionary of background computation methods"""
|
|
13
|
-
methods = {}
|
|
14
|
-
for cls in Background.__subclasses__():
|
|
15
|
-
methods[cls.key()] = cls
|
|
16
|
-
return methods
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import abc
|
|
2
|
+
import functools
|
|
2
3
|
import inspect
|
|
3
4
|
import multiprocessing as mp
|
|
4
5
|
import pathlib
|
|
5
6
|
import uuid
|
|
7
|
+
import warnings
|
|
6
8
|
|
|
7
9
|
import h5py
|
|
8
10
|
import hdf5plugin
|
|
@@ -131,37 +133,6 @@ class Background(abc.ABC):
|
|
|
131
133
|
if self.h5in is not self.h5out and self.h5out is not None:
|
|
132
134
|
self.h5out.close()
|
|
133
135
|
|
|
134
|
-
@staticmethod
|
|
135
|
-
def get_kwargs_from_ppid(bg_ppid):
|
|
136
|
-
"""Return keyword arguments for any subclass from a PPID string"""
|
|
137
|
-
name, pp_check_user_kwargs = bg_ppid.split(":")
|
|
138
|
-
for cls in Background.__subclasses__():
|
|
139
|
-
if cls.key() == name:
|
|
140
|
-
break
|
|
141
|
-
else:
|
|
142
|
-
raise ValueError(
|
|
143
|
-
f"Could not find background computation method '{name}'!")
|
|
144
|
-
kwargs = ppid.ppid_to_kwargs(cls=cls,
|
|
145
|
-
method="check_user_kwargs",
|
|
146
|
-
ppid=pp_check_user_kwargs)
|
|
147
|
-
return kwargs
|
|
148
|
-
|
|
149
|
-
@classmethod
|
|
150
|
-
def get_ppid_from_kwargs(cls, kwargs):
|
|
151
|
-
"""Return the PPID based on given keyword arguments for a subclass"""
|
|
152
|
-
key = cls.key()
|
|
153
|
-
cback = ppid.kwargs_to_ppid(cls, "check_user_kwargs", kwargs)
|
|
154
|
-
return ":".join([key, cback])
|
|
155
|
-
|
|
156
|
-
@classmethod
|
|
157
|
-
def key(cls):
|
|
158
|
-
if cls is Background:
|
|
159
|
-
raise ValueError("Cannot get `key` for `Background` base class!")
|
|
160
|
-
key = cls.__name__.lower()
|
|
161
|
-
if key.startswith("background"):
|
|
162
|
-
key = key[10:]
|
|
163
|
-
return key
|
|
164
|
-
|
|
165
136
|
@abc.abstractmethod
|
|
166
137
|
def check_user_kwargs(self, **kwargs):
|
|
167
138
|
"""Implement this to check the kwargs during init"""
|
|
@@ -186,11 +157,42 @@ class Background(abc.ABC):
|
|
|
186
157
|
|
|
187
158
|
k=100^b=10000
|
|
188
159
|
"""
|
|
189
|
-
return self.
|
|
160
|
+
return self.get_ppid_from_ppkw(self.kwargs)
|
|
161
|
+
|
|
162
|
+
@classmethod
|
|
163
|
+
def get_ppid_code(cls):
|
|
164
|
+
if cls is Background:
|
|
165
|
+
raise ValueError("Cannot get `key` for `Background` base class!")
|
|
166
|
+
key = cls.__name__.lower()
|
|
167
|
+
if key.startswith("background"):
|
|
168
|
+
key = key[10:]
|
|
169
|
+
return key
|
|
170
|
+
|
|
171
|
+
@classmethod
|
|
172
|
+
def get_ppid_from_ppkw(cls, kwargs):
|
|
173
|
+
"""Return the PPID based on given keyword arguments for a subclass"""
|
|
174
|
+
code = cls.get_ppid_code()
|
|
175
|
+
cback = ppid.kwargs_to_ppid(cls, "check_user_kwargs", kwargs)
|
|
176
|
+
return ":".join([code, cback])
|
|
177
|
+
|
|
178
|
+
@staticmethod
|
|
179
|
+
def get_ppkw_from_ppid(bg_ppid):
|
|
180
|
+
"""Return keyword arguments for any subclass from a PPID string"""
|
|
181
|
+
code, pp_check_user_kwargs = bg_ppid.split(":")
|
|
182
|
+
for bg_code in get_available_background_methods():
|
|
183
|
+
if bg_code == code:
|
|
184
|
+
cls = get_available_background_methods()[bg_code]
|
|
185
|
+
break
|
|
186
|
+
else:
|
|
187
|
+
raise ValueError(
|
|
188
|
+
f"Could not find background computation method '{code}'!")
|
|
189
|
+
kwargs = ppid.ppid_to_kwargs(cls=cls,
|
|
190
|
+
method="check_user_kwargs",
|
|
191
|
+
ppid=pp_check_user_kwargs)
|
|
192
|
+
return kwargs
|
|
190
193
|
|
|
191
194
|
def process(self):
|
|
192
195
|
self.process_approach()
|
|
193
|
-
|
|
194
196
|
bg_ppid = self.get_ppid()
|
|
195
197
|
# Store pipeline information in the image_bg feature
|
|
196
198
|
self.h5out["events/image_bg"].attrs["dcnum ppid background"] = bg_ppid
|
|
@@ -200,3 +202,19 @@ class Background(abc.ABC):
|
|
|
200
202
|
@abc.abstractmethod
|
|
201
203
|
def process_approach(self):
|
|
202
204
|
"""The actual background computation approach"""
|
|
205
|
+
|
|
206
|
+
@classmethod
|
|
207
|
+
def get_ppid_from_kwargs(cls, kwargs):
|
|
208
|
+
warnings.warn(
|
|
209
|
+
"Please use get_ppid_from_ppkw instead of get_ppid_from_kwargs.",
|
|
210
|
+
DeprecationWarning)
|
|
211
|
+
return cls.get_ppid_from_ppkw(kwargs)
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
@functools.cache
|
|
215
|
+
def get_available_background_methods():
|
|
216
|
+
"""Return dictionary of background computation methods"""
|
|
217
|
+
methods = {}
|
|
218
|
+
for cls in Background.__subclasses__():
|
|
219
|
+
methods[cls.get_ppid_code()] = cls
|
|
220
|
+
return methods
|
dcnum/feat/gate.py
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
|
+
"""Feature computation: gating after feature extraction"""
|
|
1
2
|
import copy
|
|
3
|
+
import warnings
|
|
2
4
|
|
|
3
5
|
import numpy as np
|
|
4
6
|
|
|
5
|
-
from ..meta.ppid import kwargs_to_ppid
|
|
7
|
+
from ..meta.ppid import kwargs_to_ppid, ppid_to_kwargs
|
|
6
8
|
|
|
7
9
|
|
|
8
10
|
class Gate:
|
|
@@ -84,42 +86,6 @@ class Gate:
|
|
|
84
86
|
|
|
85
87
|
return all_online_filters
|
|
86
88
|
|
|
87
|
-
def get_ppid(self):
|
|
88
|
-
"""Return a unique gating pipeline identifier
|
|
89
|
-
|
|
90
|
-
The pipeline identifier is universally applicable and must
|
|
91
|
-
be backwards-compatible (future versions of dcevent will
|
|
92
|
-
correctly acknowledge the ID).
|
|
93
|
-
|
|
94
|
-
The gating pipeline ID is defined as::
|
|
95
|
-
|
|
96
|
-
KEY:KW_GATE
|
|
97
|
-
|
|
98
|
-
Where KEY is e.g. "online_gates", and KW_GATE is
|
|
99
|
-
the corresponding value, e.g.::
|
|
100
|
-
|
|
101
|
-
online_gates=True^size_thresh_mask=5
|
|
102
|
-
"""
|
|
103
|
-
return self.get_ppid_from_kwargs(self.kwargs)
|
|
104
|
-
|
|
105
|
-
@classmethod
|
|
106
|
-
def get_ppid_from_kwargs(cls, kwargs):
|
|
107
|
-
# TODO:
|
|
108
|
-
# If polygon filters are used, the MD5sum should be used and
|
|
109
|
-
# they should be placed as a log to the output .rtdc file.
|
|
110
|
-
kwargs = copy.deepcopy(kwargs)
|
|
111
|
-
if kwargs.get("size_thresh_mask") is None:
|
|
112
|
-
# Set the default described in init
|
|
113
|
-
kwargs["size_thresh_mask"] = cls._default_size_thresh_mask
|
|
114
|
-
key = cls.key()
|
|
115
|
-
cback = kwargs_to_ppid(cls, "__init__", kwargs)
|
|
116
|
-
|
|
117
|
-
return ":".join([key, cback])
|
|
118
|
-
|
|
119
|
-
@property
|
|
120
|
-
def features(self):
|
|
121
|
-
return [kk.split()[0] for kk in list(self.box_gates.keys())]
|
|
122
|
-
|
|
123
89
|
def gate_feature(self, feat, data):
|
|
124
90
|
valid_left = True
|
|
125
91
|
valid_right = True
|
|
@@ -129,10 +95,6 @@ class Gate:
|
|
|
129
95
|
valid_right = data < self.box_gates[f"{feat} max"]
|
|
130
96
|
return np.logical_and(valid_left, valid_right)
|
|
131
97
|
|
|
132
|
-
@classmethod
|
|
133
|
-
def key(cls):
|
|
134
|
-
return "norm"
|
|
135
|
-
|
|
136
98
|
def gate_event(self, event):
|
|
137
99
|
"""Return None if the event should not be used, else `event`"""
|
|
138
100
|
if self.box_gates and event:
|
|
@@ -170,3 +132,62 @@ class Gate:
|
|
|
170
132
|
if mask_sum is None:
|
|
171
133
|
mask_sum = np.sum(mask)
|
|
172
134
|
return mask_sum > self.kwargs["size_thresh_mask"]
|
|
135
|
+
|
|
136
|
+
def get_ppid(self):
|
|
137
|
+
"""Return a unique gating pipeline identifier
|
|
138
|
+
|
|
139
|
+
The pipeline identifier is universally applicable and must
|
|
140
|
+
be backwards-compatible (future versions of dcevent will
|
|
141
|
+
correctly acknowledge the ID).
|
|
142
|
+
|
|
143
|
+
The gating pipeline ID is defined as::
|
|
144
|
+
|
|
145
|
+
KEY:KW_GATE
|
|
146
|
+
|
|
147
|
+
Where KEY is e.g. "online_gates", and KW_GATE is
|
|
148
|
+
the corresponding value, e.g.::
|
|
149
|
+
|
|
150
|
+
online_gates=True^size_thresh_mask=5
|
|
151
|
+
"""
|
|
152
|
+
return self.get_ppid_from_ppkw(self.kwargs)
|
|
153
|
+
|
|
154
|
+
@classmethod
|
|
155
|
+
def get_ppid_code(cls):
|
|
156
|
+
return "norm"
|
|
157
|
+
|
|
158
|
+
@classmethod
|
|
159
|
+
def get_ppid_from_ppkw(cls, kwargs):
|
|
160
|
+
"""return full pipeline identifier from the given keywords"""
|
|
161
|
+
# TODO:
|
|
162
|
+
# If polygon filters are used, the MD5sum should be used and
|
|
163
|
+
# they should be placed as a log to the output .rtdc file.
|
|
164
|
+
kwargs = copy.deepcopy(kwargs)
|
|
165
|
+
if kwargs.get("size_thresh_mask") is None:
|
|
166
|
+
# Set the default described in init
|
|
167
|
+
kwargs["size_thresh_mask"] = cls._default_size_thresh_mask
|
|
168
|
+
key = cls.get_ppid_code()
|
|
169
|
+
cback = kwargs_to_ppid(cls, "__init__", kwargs)
|
|
170
|
+
|
|
171
|
+
return ":".join([key, cback])
|
|
172
|
+
|
|
173
|
+
@staticmethod
|
|
174
|
+
def get_ppkw_from_ppid(gate_ppid):
|
|
175
|
+
code, pp_gate_kwargs = gate_ppid.split(":")
|
|
176
|
+
if code != Gate.get_ppid_code():
|
|
177
|
+
raise ValueError(
|
|
178
|
+
f"Could not find gating method '{code}'!")
|
|
179
|
+
kwargs = ppid_to_kwargs(cls=Gate,
|
|
180
|
+
method="__init__",
|
|
181
|
+
ppid=pp_gate_kwargs)
|
|
182
|
+
return kwargs
|
|
183
|
+
|
|
184
|
+
@property
|
|
185
|
+
def features(self):
|
|
186
|
+
return [kk.split()[0] for kk in list(self.box_gates.keys())]
|
|
187
|
+
|
|
188
|
+
@classmethod
|
|
189
|
+
def get_ppid_from_kwargs(cls, kwargs):
|
|
190
|
+
warnings.warn(
|
|
191
|
+
"Please use get_ppid_from_ppkw instead of get_ppid_from_kwargs.",
|
|
192
|
+
DeprecationWarning)
|
|
193
|
+
return cls.get_ppid_from_ppkw(kwargs)
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
"""Feature Extraction: event extractor worker"""
|
|
1
2
|
import collections
|
|
2
3
|
import logging
|
|
3
4
|
from logging.handlers import QueueHandler
|
|
@@ -6,10 +7,11 @@ import os
|
|
|
6
7
|
import queue
|
|
7
8
|
import threading
|
|
8
9
|
import traceback
|
|
10
|
+
import warnings
|
|
9
11
|
|
|
10
12
|
import numpy as np
|
|
11
13
|
|
|
12
|
-
from ..meta.ppid import kwargs_to_ppid
|
|
14
|
+
from ..meta.ppid import kwargs_to_ppid, ppid_to_kwargs
|
|
13
15
|
from ..read import HDF5Data
|
|
14
16
|
|
|
15
17
|
from .feat_brightness import brightness_features
|
|
@@ -27,14 +29,13 @@ class QueueEventExtractor:
|
|
|
27
29
|
def __init__(self,
|
|
28
30
|
data: HDF5Data,
|
|
29
31
|
gate: Gate,
|
|
30
|
-
preselect: bool,
|
|
31
|
-
ptp_median: float,
|
|
32
32
|
raw_queue: mp.Queue,
|
|
33
33
|
event_queue: mp.Queue,
|
|
34
34
|
log_queue: mp.Queue,
|
|
35
35
|
feat_nevents: mp.Array,
|
|
36
36
|
label_array: mp.Array,
|
|
37
37
|
finalize_extraction: mp.Value,
|
|
38
|
+
close_queues: bool = True,
|
|
38
39
|
extract_kwargs: dict = None,
|
|
39
40
|
*args, **kwargs):
|
|
40
41
|
"""Base class for event extraction from label images
|
|
@@ -44,15 +45,10 @@ class QueueEventExtractor:
|
|
|
44
45
|
|
|
45
46
|
Parameters
|
|
46
47
|
----------
|
|
47
|
-
data:
|
|
48
|
+
data: HDF5Data
|
|
48
49
|
Data source.
|
|
49
|
-
gate:
|
|
50
|
+
gate: Gate
|
|
50
51
|
Gating rules.
|
|
51
|
-
preselect:
|
|
52
|
-
Whether to perform data preselection based on peak-to-peak
|
|
53
|
-
values in the images.
|
|
54
|
-
ptp_median:
|
|
55
|
-
Median peak-to-peak value in the images for preselction.
|
|
56
52
|
raw_queue:
|
|
57
53
|
Queue from which the worker obtains the chunks and
|
|
58
54
|
indices of the labels to work on.
|
|
@@ -70,27 +66,26 @@ class QueueEventExtractor:
|
|
|
70
66
|
finalize_extraction:
|
|
71
67
|
Shared value indicating whether this worker should stop as
|
|
72
68
|
soon as the `raw_queue` is empty.
|
|
69
|
+
close_queues: bool
|
|
70
|
+
Whether to close event and logging queues
|
|
71
|
+
(set to False in debug mode)
|
|
73
72
|
extract_kwargs:
|
|
74
73
|
Keyword arguments for the extraction process. See the
|
|
75
74
|
keyword-only arguments in
|
|
76
75
|
:func:`QueueEventExtractor.get_events_from_masks`.
|
|
77
|
-
|
|
78
76
|
"""
|
|
79
77
|
super(QueueEventExtractor, self).__init__(*args, **kwargs)
|
|
80
78
|
#: Data instance
|
|
81
79
|
self.data = data
|
|
82
80
|
#: Gating information
|
|
83
81
|
self.gate = gate
|
|
84
|
-
#: Whether to perform Preselection
|
|
85
|
-
self.preselect = preselect
|
|
86
|
-
#: Peak-to-peak median for preselection
|
|
87
|
-
self.ptp_median = ptp_median
|
|
88
82
|
#: queue containing sub-indices for `label_array`
|
|
89
83
|
self.raw_queue = raw_queue
|
|
90
84
|
#: queue with event-wise feature dictionaries
|
|
91
85
|
self.event_queue = event_queue
|
|
92
86
|
#: queue for logging
|
|
93
87
|
self.log_queue = log_queue
|
|
88
|
+
self.close_queues = close_queues
|
|
94
89
|
#: Shared array of length `len(data)` into which the number of
|
|
95
90
|
#: events per frame is written.
|
|
96
91
|
self.feat_nevents = feat_nevents
|
|
@@ -111,24 +106,49 @@ class QueueEventExtractor:
|
|
|
111
106
|
self.logger = None
|
|
112
107
|
|
|
113
108
|
@staticmethod
|
|
114
|
-
def get_init_kwargs(data
|
|
115
|
-
|
|
109
|
+
def get_init_kwargs(data: HDF5Data,
|
|
110
|
+
gate: Gate,
|
|
111
|
+
log_queue: mp.Queue,
|
|
112
|
+
preselect: None = None,
|
|
113
|
+
ptp_median: None = None):
|
|
114
|
+
"""Get initialization arguments for :cass:`.QueueEventExtractor`
|
|
116
115
|
|
|
117
116
|
This method was created for convenience reasons:
|
|
118
117
|
- It makes sure that the order of arguments is correct, since it
|
|
119
118
|
is implemented in the same class.
|
|
120
119
|
- It simplifies testing.
|
|
120
|
+
|
|
121
|
+
Parameters
|
|
122
|
+
----------
|
|
123
|
+
data: HDF5Data
|
|
124
|
+
Input data
|
|
125
|
+
gate: HDF5Data
|
|
126
|
+
Gating class to use
|
|
127
|
+
log_queue: mp.Queue
|
|
128
|
+
Queue for sending log messages
|
|
129
|
+
preselect, ptp_median:
|
|
130
|
+
Deprecated
|
|
131
|
+
|
|
132
|
+
Returns
|
|
133
|
+
-------
|
|
134
|
+
args: dict
|
|
135
|
+
You can pass `*args.values()` directly to `__init__`
|
|
121
136
|
"""
|
|
122
137
|
# queue with the raw (unsegmented) image data
|
|
123
138
|
raw_queue = mp_spawn.Queue()
|
|
124
139
|
# queue with event-wise feature dictionaries
|
|
125
140
|
event_queue = mp_spawn.Queue()
|
|
126
141
|
|
|
142
|
+
if preselect is not None:
|
|
143
|
+
warnings.warn("The `preselect` argument is deprecated!",
|
|
144
|
+
DeprecationWarning)
|
|
145
|
+
if ptp_median is not None:
|
|
146
|
+
warnings.warn("The `ptp_median` argument is deprecated!",
|
|
147
|
+
DeprecationWarning)
|
|
148
|
+
|
|
127
149
|
args = collections.OrderedDict()
|
|
128
150
|
args["data"] = data
|
|
129
151
|
args["gate"] = gate
|
|
130
|
-
args["preselect"] = preselect
|
|
131
|
-
args["ptp_median"] = ptp_median
|
|
132
152
|
args["raw_queue"] = raw_queue
|
|
133
153
|
args["event_queue"] = event_queue
|
|
134
154
|
args["log_queue"] = log_queue
|
|
@@ -139,15 +159,9 @@ class QueueEventExtractor:
|
|
|
139
159
|
np.ctypeslib.ctypes.c_int16,
|
|
140
160
|
int(np.prod(data.image.chunk_shape)))
|
|
141
161
|
args["finalize_extraction"] = mp_spawn.Value("b", False)
|
|
162
|
+
args["close_queues"] = True
|
|
142
163
|
return args
|
|
143
164
|
|
|
144
|
-
@classmethod
|
|
145
|
-
def get_ppid_from_kwargs(cls, kwargs):
|
|
146
|
-
"""Return the pipeline ID for this event extractor"""
|
|
147
|
-
key = "legacy"
|
|
148
|
-
cback = kwargs_to_ppid(cls, "get_events_from_masks", kwargs)
|
|
149
|
-
return ":".join([key, cback])
|
|
150
|
-
|
|
151
165
|
def get_events_from_masks(self, masks, data_index, *,
|
|
152
166
|
brightness: bool = True,
|
|
153
167
|
haralick: bool = True,
|
|
@@ -236,7 +250,29 @@ class QueueEventExtractor:
|
|
|
236
250
|
|
|
237
251
|
b=1^h=1
|
|
238
252
|
"""
|
|
239
|
-
return self.
|
|
253
|
+
return self.get_ppid_from_ppkw(self.extract_kwargs)
|
|
254
|
+
|
|
255
|
+
@classmethod
|
|
256
|
+
def get_ppid_code(cls):
|
|
257
|
+
return "legacy"
|
|
258
|
+
|
|
259
|
+
@classmethod
|
|
260
|
+
def get_ppid_from_ppkw(cls, kwargs):
|
|
261
|
+
"""Return the pipeline ID for this event extractor"""
|
|
262
|
+
code = cls.get_ppid_code()
|
|
263
|
+
cback = kwargs_to_ppid(cls, "get_events_from_masks", kwargs)
|
|
264
|
+
return ":".join([code, cback])
|
|
265
|
+
|
|
266
|
+
@staticmethod
|
|
267
|
+
def get_ppkw_from_ppid(extr_ppid):
|
|
268
|
+
code, pp_extr_kwargs = extr_ppid.split(":")
|
|
269
|
+
if code != QueueEventExtractor.get_ppid_code():
|
|
270
|
+
raise ValueError(
|
|
271
|
+
f"Could not find extraction method '{code}'!")
|
|
272
|
+
kwargs = ppid_to_kwargs(cls=QueueEventExtractor,
|
|
273
|
+
method="get_events_from_masks",
|
|
274
|
+
ppid=pp_extr_kwargs)
|
|
275
|
+
return kwargs
|
|
240
276
|
|
|
241
277
|
def process_label(self, label, index):
|
|
242
278
|
"""Process one label image, extracting masks and features"""
|
|
@@ -245,11 +281,7 @@ class QueueEventExtractor:
|
|
|
245
281
|
# TODO: Do this before segmentation already?
|
|
246
282
|
# skip events that have been analyzed already
|
|
247
283
|
return None
|
|
248
|
-
|
|
249
|
-
# TODO: Do this before segmentation already?
|
|
250
|
-
ptp = np.ptp(self.data.image_corr[index])
|
|
251
|
-
if ptp < 0.1 * self.ptp_median:
|
|
252
|
-
return None
|
|
284
|
+
|
|
253
285
|
masks = self.get_masks_from_label(label)
|
|
254
286
|
if masks.size:
|
|
255
287
|
events = self.get_events_from_masks(
|
|
@@ -300,14 +332,22 @@ class QueueEventExtractor:
|
|
|
300
332
|
self.event_queue.put((index, events))
|
|
301
333
|
|
|
302
334
|
self.logger.debug(f"Finalizing `run` for PID {os.getpid()}, {self}")
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
335
|
+
if self.close_queues:
|
|
336
|
+
# Explicitly close the event queue and join it
|
|
337
|
+
self.event_queue.close()
|
|
338
|
+
self.event_queue.join_thread()
|
|
339
|
+
self.logger.debug(f"End of `run` for PID {os.getpid()}, {self}")
|
|
340
|
+
# Also close the logging queue. Note that not all messages might
|
|
341
|
+
# arrive in the logging queue, since we called `cancel_join_thread`
|
|
342
|
+
# earlier.
|
|
343
|
+
self.log_queue.close()
|
|
344
|
+
|
|
345
|
+
@classmethod
|
|
346
|
+
def get_ppid_from_kwargs(cls, kwargs):
|
|
347
|
+
warnings.warn(
|
|
348
|
+
"Please use get_ppid_from_ppkw instead of get_ppid_from_kwargs.",
|
|
349
|
+
DeprecationWarning)
|
|
350
|
+
return cls.get_ppid_from_ppkw(kwargs)
|
|
311
351
|
|
|
312
352
|
|
|
313
353
|
class EventExtractorProcess(QueueEventExtractor, mp_spawn.Process):
|
dcnum/logic/__init__.py
ADDED