dclab 0.67.0__cp314-cp314t-macosx_10_13_x86_64.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 dclab might be problematic. Click here for more details.
- dclab/__init__.py +41 -0
- dclab/_version.py +34 -0
- dclab/cached.py +97 -0
- dclab/cli/__init__.py +10 -0
- dclab/cli/common.py +237 -0
- dclab/cli/task_compress.py +126 -0
- dclab/cli/task_condense.py +223 -0
- dclab/cli/task_join.py +229 -0
- dclab/cli/task_repack.py +98 -0
- dclab/cli/task_split.py +154 -0
- dclab/cli/task_tdms2rtdc.py +186 -0
- dclab/cli/task_verify_dataset.py +75 -0
- dclab/definitions/__init__.py +79 -0
- dclab/definitions/feat_const.py +202 -0
- dclab/definitions/feat_logic.py +182 -0
- dclab/definitions/meta_const.py +252 -0
- dclab/definitions/meta_logic.py +111 -0
- dclab/definitions/meta_parse.py +94 -0
- dclab/downsampling.cpython-314t-darwin.so +0 -0
- dclab/downsampling.pyx +230 -0
- dclab/external/__init__.py +4 -0
- dclab/external/packaging/LICENSE +3 -0
- dclab/external/packaging/LICENSE.APACHE +177 -0
- dclab/external/packaging/LICENSE.BSD +23 -0
- dclab/external/packaging/__init__.py +6 -0
- dclab/external/packaging/_structures.py +61 -0
- dclab/external/packaging/version.py +505 -0
- dclab/external/skimage/LICENSE +28 -0
- dclab/external/skimage/__init__.py +2 -0
- dclab/external/skimage/_find_contours.py +216 -0
- dclab/external/skimage/_find_contours_cy.cpython-314t-darwin.so +0 -0
- dclab/external/skimage/_find_contours_cy.pyx +188 -0
- dclab/external/skimage/_pnpoly.cpython-314t-darwin.so +0 -0
- dclab/external/skimage/_pnpoly.pyx +99 -0
- dclab/external/skimage/_shared/__init__.py +1 -0
- dclab/external/skimage/_shared/geometry.cpython-314t-darwin.so +0 -0
- dclab/external/skimage/_shared/geometry.pxd +6 -0
- dclab/external/skimage/_shared/geometry.pyx +55 -0
- dclab/external/skimage/measure.py +7 -0
- dclab/external/skimage/pnpoly.py +53 -0
- dclab/external/statsmodels/LICENSE +35 -0
- dclab/external/statsmodels/__init__.py +6 -0
- dclab/external/statsmodels/nonparametric/__init__.py +1 -0
- dclab/external/statsmodels/nonparametric/_kernel_base.py +203 -0
- dclab/external/statsmodels/nonparametric/kernel_density.py +165 -0
- dclab/external/statsmodels/nonparametric/kernels.py +36 -0
- dclab/features/__init__.py +9 -0
- dclab/features/bright.py +81 -0
- dclab/features/bright_bc.py +93 -0
- dclab/features/bright_perc.py +63 -0
- dclab/features/contour.py +161 -0
- dclab/features/emodulus/__init__.py +339 -0
- dclab/features/emodulus/load.py +252 -0
- dclab/features/emodulus/lut_HE-2D-FEM-22.txt +16432 -0
- dclab/features/emodulus/lut_HE-3D-FEM-22.txt +1276 -0
- dclab/features/emodulus/lut_LE-2D-FEM-19.txt +13082 -0
- dclab/features/emodulus/pxcorr.py +135 -0
- dclab/features/emodulus/scale_linear.py +247 -0
- dclab/features/emodulus/viscosity.py +260 -0
- dclab/features/fl_crosstalk.py +95 -0
- dclab/features/inert_ratio.py +377 -0
- dclab/features/volume.py +242 -0
- dclab/http_utils.py +322 -0
- dclab/isoelastics/__init__.py +468 -0
- dclab/isoelastics/iso_HE-2D-FEM-22-area_um-deform.txt +2440 -0
- dclab/isoelastics/iso_HE-2D-FEM-22-volume-deform.txt +2635 -0
- dclab/isoelastics/iso_HE-3D-FEM-22-area_um-deform.txt +1930 -0
- dclab/isoelastics/iso_HE-3D-FEM-22-volume-deform.txt +2221 -0
- dclab/isoelastics/iso_LE-2D-FEM-19-area_um-deform.txt +2151 -0
- dclab/isoelastics/iso_LE-2D-FEM-19-volume-deform.txt +2250 -0
- dclab/isoelastics/iso_LE-2D-ana-18-area_um-deform.txt +1266 -0
- dclab/kde/__init__.py +1 -0
- dclab/kde/base.py +459 -0
- dclab/kde/contours.py +222 -0
- dclab/kde/methods.py +313 -0
- dclab/kde_contours.py +10 -0
- dclab/kde_methods.py +11 -0
- dclab/lme4/__init__.py +5 -0
- dclab/lme4/lme4_template.R +94 -0
- dclab/lme4/rsetup.py +204 -0
- dclab/lme4/wrapr.py +386 -0
- dclab/polygon_filter.py +398 -0
- dclab/rtdc_dataset/__init__.py +15 -0
- dclab/rtdc_dataset/check.py +902 -0
- dclab/rtdc_dataset/config.py +533 -0
- dclab/rtdc_dataset/copier.py +353 -0
- dclab/rtdc_dataset/core.py +896 -0
- dclab/rtdc_dataset/export.py +867 -0
- dclab/rtdc_dataset/feat_anc_core/__init__.py +24 -0
- dclab/rtdc_dataset/feat_anc_core/af_basic.py +75 -0
- dclab/rtdc_dataset/feat_anc_core/af_emodulus.py +160 -0
- dclab/rtdc_dataset/feat_anc_core/af_fl_max_ctc.py +133 -0
- dclab/rtdc_dataset/feat_anc_core/af_image_contour.py +113 -0
- dclab/rtdc_dataset/feat_anc_core/af_ml_class.py +102 -0
- dclab/rtdc_dataset/feat_anc_core/ancillary_feature.py +320 -0
- dclab/rtdc_dataset/feat_anc_ml/__init__.py +32 -0
- dclab/rtdc_dataset/feat_anc_plugin/__init__.py +3 -0
- dclab/rtdc_dataset/feat_anc_plugin/plugin_feature.py +329 -0
- dclab/rtdc_dataset/feat_basin.py +762 -0
- dclab/rtdc_dataset/feat_temp.py +102 -0
- dclab/rtdc_dataset/filter.py +263 -0
- dclab/rtdc_dataset/fmt_dcor/__init__.py +7 -0
- dclab/rtdc_dataset/fmt_dcor/access_token.py +52 -0
- dclab/rtdc_dataset/fmt_dcor/api.py +173 -0
- dclab/rtdc_dataset/fmt_dcor/base.py +299 -0
- dclab/rtdc_dataset/fmt_dcor/basin.py +73 -0
- dclab/rtdc_dataset/fmt_dcor/logs.py +26 -0
- dclab/rtdc_dataset/fmt_dcor/tables.py +66 -0
- dclab/rtdc_dataset/fmt_dict.py +103 -0
- dclab/rtdc_dataset/fmt_hdf5/__init__.py +6 -0
- dclab/rtdc_dataset/fmt_hdf5/base.py +192 -0
- dclab/rtdc_dataset/fmt_hdf5/basin.py +30 -0
- dclab/rtdc_dataset/fmt_hdf5/events.py +276 -0
- dclab/rtdc_dataset/fmt_hdf5/feat_defect.py +164 -0
- dclab/rtdc_dataset/fmt_hdf5/logs.py +33 -0
- dclab/rtdc_dataset/fmt_hdf5/tables.py +60 -0
- dclab/rtdc_dataset/fmt_hierarchy/__init__.py +11 -0
- dclab/rtdc_dataset/fmt_hierarchy/base.py +278 -0
- dclab/rtdc_dataset/fmt_hierarchy/events.py +146 -0
- dclab/rtdc_dataset/fmt_hierarchy/hfilter.py +140 -0
- dclab/rtdc_dataset/fmt_hierarchy/mapper.py +134 -0
- dclab/rtdc_dataset/fmt_http.py +102 -0
- dclab/rtdc_dataset/fmt_s3.py +354 -0
- dclab/rtdc_dataset/fmt_tdms/__init__.py +476 -0
- dclab/rtdc_dataset/fmt_tdms/event_contour.py +264 -0
- dclab/rtdc_dataset/fmt_tdms/event_image.py +220 -0
- dclab/rtdc_dataset/fmt_tdms/event_mask.py +62 -0
- dclab/rtdc_dataset/fmt_tdms/event_trace.py +146 -0
- dclab/rtdc_dataset/fmt_tdms/exc.py +37 -0
- dclab/rtdc_dataset/fmt_tdms/naming.py +151 -0
- dclab/rtdc_dataset/load.py +77 -0
- dclab/rtdc_dataset/meta_table.py +25 -0
- dclab/rtdc_dataset/writer.py +1019 -0
- dclab/statistics.py +226 -0
- dclab/util.py +176 -0
- dclab/warn.py +15 -0
- dclab-0.67.0.dist-info/METADATA +153 -0
- dclab-0.67.0.dist-info/RECORD +142 -0
- dclab-0.67.0.dist-info/WHEEL +6 -0
- dclab-0.67.0.dist-info/entry_points.txt +8 -0
- dclab-0.67.0.dist-info/licenses/LICENSE +283 -0
- dclab-0.67.0.dist-info/top_level.txt +1 -0
dclab/polygon_filter.py
ADDED
|
@@ -0,0 +1,398 @@
|
|
|
1
|
+
|
|
2
|
+
import io
|
|
3
|
+
import pathlib
|
|
4
|
+
import warnings
|
|
5
|
+
|
|
6
|
+
import numpy as np
|
|
7
|
+
|
|
8
|
+
from .external.skimage.measure import points_in_poly
|
|
9
|
+
from .util import hashobj
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class FilterIdExistsWarning(UserWarning):
|
|
13
|
+
pass
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class PolygonFilter(object):
|
|
17
|
+
# Stuff that is done upon creation (not instantiation) of this class
|
|
18
|
+
instances = []
|
|
19
|
+
_instance_counter = 0
|
|
20
|
+
|
|
21
|
+
def __init__(self, axes=None, points=None, inverted=False,
|
|
22
|
+
name=None, filename=None, fileid=0,
|
|
23
|
+
unique_id=None):
|
|
24
|
+
"""An object for filtering RTDC data based on a polygonial area
|
|
25
|
+
|
|
26
|
+
Parameters
|
|
27
|
+
----------
|
|
28
|
+
axes: tuple of str or list of str
|
|
29
|
+
The axes/features on which the polygon is defined. The
|
|
30
|
+
first axis is the x-axis. Example: ("area_um", "deform").
|
|
31
|
+
points: array-like object of shape (N,2)
|
|
32
|
+
The N coordinates (x,y) of the polygon. The exact order is
|
|
33
|
+
important.
|
|
34
|
+
inverted: bool
|
|
35
|
+
Invert the polygon filter. This parameter is overridden
|
|
36
|
+
if `filename` is given.
|
|
37
|
+
name: str
|
|
38
|
+
A name for the polygon (optional).
|
|
39
|
+
filename : str
|
|
40
|
+
A path to a .poly file as created by this classes' `save`
|
|
41
|
+
method. If `filename` is given, all other parameters are
|
|
42
|
+
ignored.
|
|
43
|
+
fileid: int
|
|
44
|
+
Which filter to import from the file (starting at 0).
|
|
45
|
+
unique_id: int
|
|
46
|
+
An integer defining the unique id of the new instance.
|
|
47
|
+
|
|
48
|
+
Notes
|
|
49
|
+
-----
|
|
50
|
+
The minimal arguments to this class are either `filename` OR
|
|
51
|
+
(`axes`, `points`). If `filename` is set, all parameters are
|
|
52
|
+
taken from the given .poly file.
|
|
53
|
+
"""
|
|
54
|
+
self.inverted = inverted
|
|
55
|
+
self._points = None
|
|
56
|
+
# check if a filename was given
|
|
57
|
+
if filename is not None:
|
|
58
|
+
filename = pathlib.Path(filename)
|
|
59
|
+
if not isinstance(fileid, int):
|
|
60
|
+
raise ValueError("`fileid` must be an integer!")
|
|
61
|
+
if not filename.exists():
|
|
62
|
+
raise ValueError("Error, no such file: {}".format(filename))
|
|
63
|
+
self.fileid = fileid
|
|
64
|
+
# This also sets a unique id
|
|
65
|
+
self._load(filename, unique_id=unique_id)
|
|
66
|
+
else:
|
|
67
|
+
if len(axes) != 2:
|
|
68
|
+
raise ValueError("`axes` must have length 2, "
|
|
69
|
+
+ "got '{}'!".format(axes))
|
|
70
|
+
self.axes = axes
|
|
71
|
+
self.points = np.array(points, dtype=np.float64)
|
|
72
|
+
self.name = name
|
|
73
|
+
if unique_id is None:
|
|
74
|
+
# Force giving away a unique id
|
|
75
|
+
unique_id = self._instance_counter
|
|
76
|
+
|
|
77
|
+
# Set unique id
|
|
78
|
+
if unique_id is not None:
|
|
79
|
+
self._set_unique_id(unique_id)
|
|
80
|
+
|
|
81
|
+
self._check_data()
|
|
82
|
+
# if everything worked out, add to instances
|
|
83
|
+
PolygonFilter.instances.append(self)
|
|
84
|
+
|
|
85
|
+
def __eq__(self, pf):
|
|
86
|
+
if (isinstance(pf, PolygonFilter) and
|
|
87
|
+
self.inverted == pf.inverted and
|
|
88
|
+
np.allclose(self.points, pf.points) and
|
|
89
|
+
list(self.axes) == list(pf.axes)):
|
|
90
|
+
eq = True
|
|
91
|
+
else:
|
|
92
|
+
eq = False
|
|
93
|
+
return eq
|
|
94
|
+
|
|
95
|
+
def __getstate__(self):
|
|
96
|
+
state = {
|
|
97
|
+
"axis x": self.axes[0],
|
|
98
|
+
"axis y": self.axes[1],
|
|
99
|
+
"identifier": self.unique_id,
|
|
100
|
+
"inverted": self.inverted,
|
|
101
|
+
"name": self.name,
|
|
102
|
+
"points": self.points.tolist()
|
|
103
|
+
}
|
|
104
|
+
return state
|
|
105
|
+
|
|
106
|
+
def __setstate__(self, state):
|
|
107
|
+
if state["identifier"] != self.unique_id:
|
|
108
|
+
raise ValueError("Polygon filter identifier mismatch!")
|
|
109
|
+
self.axes = [state["axis x"], state["axis y"]]
|
|
110
|
+
self.inverted = state["inverted"]
|
|
111
|
+
self.name = state["name"]
|
|
112
|
+
self.points = state["points"]
|
|
113
|
+
|
|
114
|
+
def _check_data(self):
|
|
115
|
+
"""Check if the data given is valid"""
|
|
116
|
+
if self.axes is None:
|
|
117
|
+
raise PolygonFilterError("`axes` parm not set.")
|
|
118
|
+
if self.points is None:
|
|
119
|
+
raise PolygonFilterError("`points` parm not set.")
|
|
120
|
+
self.points = np.array(self.points)
|
|
121
|
+
if self.points.shape[1] != 2:
|
|
122
|
+
raise PolygonFilterError("data points' shape[1] must be 2.")
|
|
123
|
+
if self.name is None:
|
|
124
|
+
self.name = "polygon filter {}".format(self.unique_id)
|
|
125
|
+
if not isinstance(self.inverted, bool):
|
|
126
|
+
raise PolygonFilterError("`inverted` must be boolean.")
|
|
127
|
+
|
|
128
|
+
def _load(self, filename, unique_id=None):
|
|
129
|
+
"""Import all filters from a text file"""
|
|
130
|
+
filename = pathlib.Path(filename)
|
|
131
|
+
with filename.open("r", errors="replace") as fd:
|
|
132
|
+
data = fd.readlines()
|
|
133
|
+
|
|
134
|
+
# Get the strings that correspond to self.fileid
|
|
135
|
+
bool_head = [li.strip().startswith("[") for li in data]
|
|
136
|
+
|
|
137
|
+
int_head = np.squeeze(np.where(bool_head))
|
|
138
|
+
int_head = np.atleast_1d(int_head)
|
|
139
|
+
|
|
140
|
+
start = int_head[self.fileid]+1
|
|
141
|
+
|
|
142
|
+
if len(int_head) > self.fileid+1:
|
|
143
|
+
end = int_head[self.fileid+1]
|
|
144
|
+
else:
|
|
145
|
+
end = len(data)
|
|
146
|
+
|
|
147
|
+
subdata = data[start:end]
|
|
148
|
+
|
|
149
|
+
# separate all elements and strip them
|
|
150
|
+
subdata = [[it.strip() for it in li.split("=")] for li in subdata]
|
|
151
|
+
|
|
152
|
+
points = []
|
|
153
|
+
|
|
154
|
+
for var, val in subdata:
|
|
155
|
+
if var.lower() == "x axis":
|
|
156
|
+
xaxis = val.lower()
|
|
157
|
+
elif var.lower() == "y axis":
|
|
158
|
+
yaxis = val.lower()
|
|
159
|
+
elif var.lower() == "name":
|
|
160
|
+
self.name = val
|
|
161
|
+
elif var.lower() == "inverted":
|
|
162
|
+
if val == "True":
|
|
163
|
+
self.inverted = True
|
|
164
|
+
elif var.lower().startswith("point"):
|
|
165
|
+
val = np.array(val.strip("[]").split(), dtype=np.float64)
|
|
166
|
+
points.append([int(var[5:]), val])
|
|
167
|
+
else:
|
|
168
|
+
raise KeyError("Unknown variable: {} = {}".
|
|
169
|
+
format(var, val))
|
|
170
|
+
self.axes = (xaxis, yaxis)
|
|
171
|
+
# sort points
|
|
172
|
+
points.sort()
|
|
173
|
+
# get only coordinates from points
|
|
174
|
+
self.points = np.array([p[1] for p in points])
|
|
175
|
+
|
|
176
|
+
if unique_id is None:
|
|
177
|
+
# overwrite unique id
|
|
178
|
+
unique_id = int(data[start-1].strip().strip("Polygon []"))
|
|
179
|
+
self._set_unique_id(unique_id)
|
|
180
|
+
|
|
181
|
+
def _set_unique_id(self, unique_id):
|
|
182
|
+
"""Define a unique id"""
|
|
183
|
+
assert isinstance(unique_id, int), "unique_id must be an integer"
|
|
184
|
+
|
|
185
|
+
if PolygonFilter.instace_exists(unique_id):
|
|
186
|
+
newid = max(PolygonFilter._instance_counter, unique_id+1)
|
|
187
|
+
msg = "PolygonFilter with unique_id '{}' exists.".format(unique_id)
|
|
188
|
+
msg += " Using new unique id '{}'.".format(newid)
|
|
189
|
+
warnings.warn(msg, FilterIdExistsWarning)
|
|
190
|
+
unique_id = newid
|
|
191
|
+
|
|
192
|
+
ic = max(PolygonFilter._instance_counter, unique_id+1)
|
|
193
|
+
PolygonFilter._instance_counter = ic
|
|
194
|
+
self.unique_id = unique_id
|
|
195
|
+
|
|
196
|
+
@property
|
|
197
|
+
def hash(self):
|
|
198
|
+
"""Hash of `axes`, `points`, and `inverted`"""
|
|
199
|
+
return hashobj([self.axes, self.points, self.inverted])
|
|
200
|
+
|
|
201
|
+
@property
|
|
202
|
+
def points(self):
|
|
203
|
+
# make sure points always is an array (so we can use .tobytes())
|
|
204
|
+
return np.array(self._points)
|
|
205
|
+
|
|
206
|
+
@points.setter
|
|
207
|
+
def points(self, points):
|
|
208
|
+
self._points = points
|
|
209
|
+
|
|
210
|
+
@staticmethod
|
|
211
|
+
def clear_all_filters():
|
|
212
|
+
"""Remove all filters and reset instance counter"""
|
|
213
|
+
PolygonFilter.instances = []
|
|
214
|
+
PolygonFilter._instance_counter = 0
|
|
215
|
+
|
|
216
|
+
@staticmethod
|
|
217
|
+
def unique_id_exists(pid):
|
|
218
|
+
"""Whether or not a filter with this unique id exists"""
|
|
219
|
+
for instance in PolygonFilter.instances:
|
|
220
|
+
if instance.unique_id == pid:
|
|
221
|
+
exists = True
|
|
222
|
+
break
|
|
223
|
+
else:
|
|
224
|
+
exists = False
|
|
225
|
+
return exists
|
|
226
|
+
|
|
227
|
+
def copy(self, invert=False):
|
|
228
|
+
"""Return a copy of the current instance
|
|
229
|
+
|
|
230
|
+
Parameters
|
|
231
|
+
----------
|
|
232
|
+
invert: bool
|
|
233
|
+
The copy will be inverted w.r.t. the original
|
|
234
|
+
"""
|
|
235
|
+
if invert:
|
|
236
|
+
inverted = not self.inverted
|
|
237
|
+
else:
|
|
238
|
+
inverted = self.inverted
|
|
239
|
+
|
|
240
|
+
return PolygonFilter(axes=self.axes,
|
|
241
|
+
points=self.points,
|
|
242
|
+
name=self.name,
|
|
243
|
+
inverted=inverted)
|
|
244
|
+
|
|
245
|
+
def filter(self, datax, datay):
|
|
246
|
+
"""Filter a set of datax and datay according to `self.points`"""
|
|
247
|
+
points = np.zeros((datax.shape[0], 2), dtype=np.float64)
|
|
248
|
+
points[:, 0] = datax
|
|
249
|
+
points[:, 1] = datay
|
|
250
|
+
f = points_in_poly(points=points, verts=self.points)
|
|
251
|
+
|
|
252
|
+
if self.inverted:
|
|
253
|
+
np.invert(f, f)
|
|
254
|
+
|
|
255
|
+
return f
|
|
256
|
+
|
|
257
|
+
@staticmethod
|
|
258
|
+
def get_instance_from_id(unique_id):
|
|
259
|
+
"""Get an instance of the `PolygonFilter` using a unique id"""
|
|
260
|
+
for instance in PolygonFilter.instances:
|
|
261
|
+
if instance.unique_id == unique_id:
|
|
262
|
+
return instance
|
|
263
|
+
# if this does not work:
|
|
264
|
+
raise KeyError("PolygonFilter with unique_id {} not found.".
|
|
265
|
+
format(unique_id))
|
|
266
|
+
|
|
267
|
+
@staticmethod
|
|
268
|
+
def import_all(path):
|
|
269
|
+
"""Import all polygons from a .poly file.
|
|
270
|
+
|
|
271
|
+
Returns a list of the imported polygon filters
|
|
272
|
+
"""
|
|
273
|
+
plist = []
|
|
274
|
+
fid = 0
|
|
275
|
+
while True:
|
|
276
|
+
try:
|
|
277
|
+
p = PolygonFilter(filename=path, fileid=fid)
|
|
278
|
+
plist.append(p)
|
|
279
|
+
fid += 1
|
|
280
|
+
except IndexError:
|
|
281
|
+
break
|
|
282
|
+
return plist
|
|
283
|
+
|
|
284
|
+
@staticmethod
|
|
285
|
+
def instace_exists(unique_id):
|
|
286
|
+
"""Determine whether an instance with this unique id exists"""
|
|
287
|
+
try:
|
|
288
|
+
PolygonFilter.get_instance_from_id(unique_id)
|
|
289
|
+
except KeyError:
|
|
290
|
+
return False
|
|
291
|
+
else:
|
|
292
|
+
return True
|
|
293
|
+
|
|
294
|
+
@staticmethod
|
|
295
|
+
def point_in_poly(p, poly):
|
|
296
|
+
"""Determine whether a point is within a polygon area
|
|
297
|
+
|
|
298
|
+
Uses the ray casting algorithm.
|
|
299
|
+
|
|
300
|
+
Parameters
|
|
301
|
+
----------
|
|
302
|
+
p: tuple of floats
|
|
303
|
+
Coordinates of the point
|
|
304
|
+
poly: array_like of shape (N, 2)
|
|
305
|
+
Polygon (`PolygonFilter.points`)
|
|
306
|
+
|
|
307
|
+
Returns
|
|
308
|
+
-------
|
|
309
|
+
inside: bool
|
|
310
|
+
`True`, if point is inside.
|
|
311
|
+
|
|
312
|
+
Notes
|
|
313
|
+
-----
|
|
314
|
+
If `p` lies on a side of the polygon, it is defined as
|
|
315
|
+
|
|
316
|
+
- "inside" if it is on the lower or left
|
|
317
|
+
- "outside" if it is on the top or right
|
|
318
|
+
|
|
319
|
+
.. versionchanged:: 0.24.1
|
|
320
|
+
The new version uses the cython implementation from
|
|
321
|
+
scikit-image. In the old version, the inside/outside
|
|
322
|
+
definition was the other way around. In favor of not
|
|
323
|
+
having to modify upstram code, the scikit-image
|
|
324
|
+
version was adapted.
|
|
325
|
+
"""
|
|
326
|
+
points = np.array(p).reshape(1, 2)
|
|
327
|
+
f = points_in_poly(points=points, verts=np.array(poly))
|
|
328
|
+
return f.item()
|
|
329
|
+
|
|
330
|
+
@staticmethod
|
|
331
|
+
def remove(unique_id):
|
|
332
|
+
"""Remove a polygon filter from `PolygonFilter.instances`"""
|
|
333
|
+
for p in PolygonFilter.instances:
|
|
334
|
+
if p.unique_id == unique_id:
|
|
335
|
+
PolygonFilter.instances.remove(p)
|
|
336
|
+
|
|
337
|
+
def save(self, polyfile, ret_fobj=False):
|
|
338
|
+
"""Save all data to a text file (appends data if file exists).
|
|
339
|
+
|
|
340
|
+
Polyfile can be either a path to a file or a file object that
|
|
341
|
+
was opened with the write "w" parameter. By using the file
|
|
342
|
+
object, multiple instances of this class can write their data.
|
|
343
|
+
|
|
344
|
+
If `ret_fobj` is `True`, then the file object will not be
|
|
345
|
+
closed and returned.
|
|
346
|
+
"""
|
|
347
|
+
if isinstance(polyfile, io.IOBase):
|
|
348
|
+
fobj = polyfile
|
|
349
|
+
else:
|
|
350
|
+
fobj = pathlib.Path(polyfile).open("a")
|
|
351
|
+
|
|
352
|
+
# Who the hell would use more then 10 million polygons or
|
|
353
|
+
# polygon points? -> 08d (easier if other people want to import)
|
|
354
|
+
data2write = []
|
|
355
|
+
data2write.append("[Polygon {:08d}]".format(self.unique_id))
|
|
356
|
+
data2write.append("X Axis = {}".format(self.axes[0]))
|
|
357
|
+
data2write.append("Y Axis = {}".format(self.axes[1]))
|
|
358
|
+
data2write.append("Name = {}".format(self.name))
|
|
359
|
+
data2write.append("Inverted = {}".format(self.inverted))
|
|
360
|
+
for i, point in enumerate(self.points):
|
|
361
|
+
data2write.append("point{:08d} = {:.15e} {:.15e}".format(i,
|
|
362
|
+
point[0],
|
|
363
|
+
point[1]))
|
|
364
|
+
# Add new lines
|
|
365
|
+
for i in range(len(data2write)):
|
|
366
|
+
data2write[i] += "\n"
|
|
367
|
+
|
|
368
|
+
# begin writing to fobj
|
|
369
|
+
fobj.writelines(data2write)
|
|
370
|
+
|
|
371
|
+
if ret_fobj:
|
|
372
|
+
return fobj
|
|
373
|
+
else:
|
|
374
|
+
fobj.close()
|
|
375
|
+
|
|
376
|
+
@staticmethod
|
|
377
|
+
def save_all(polyfile):
|
|
378
|
+
"""Save all polygon filters"""
|
|
379
|
+
if len(PolygonFilter.instances) == 0:
|
|
380
|
+
raise PolygonFilterError("There are no polygon filters to save.")
|
|
381
|
+
for p in PolygonFilter.instances:
|
|
382
|
+
# we return the ret_obj, so we don't need to open and
|
|
383
|
+
# close the file multiple times.
|
|
384
|
+
polyobj = p.save(polyfile, ret_fobj=True)
|
|
385
|
+
# close the object after we are done saving all filters
|
|
386
|
+
polyobj.close()
|
|
387
|
+
|
|
388
|
+
|
|
389
|
+
class PolygonFilterError(BaseException):
|
|
390
|
+
pass
|
|
391
|
+
|
|
392
|
+
|
|
393
|
+
def get_polygon_filter_names():
|
|
394
|
+
"""Get the names of all polygon filters in the order of creation"""
|
|
395
|
+
names = []
|
|
396
|
+
for p in PolygonFilter.instances:
|
|
397
|
+
names.append(p.name)
|
|
398
|
+
return names
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# flake8: noqa: F401
|
|
2
|
+
from ..util import hashfile
|
|
3
|
+
|
|
4
|
+
from .check import IntegrityChecker, check_dataset
|
|
5
|
+
from .config import Configuration
|
|
6
|
+
from .copier import h5ds_copy, is_properly_compressed, rtdc_copy
|
|
7
|
+
from .core import RTDCBase
|
|
8
|
+
from .fmt_dcor import RTDC_DCOR
|
|
9
|
+
from .fmt_dict import RTDC_Dict
|
|
10
|
+
from .fmt_hdf5 import RTDC_HDF5
|
|
11
|
+
from .fmt_hierarchy import RTDC_Hierarchy
|
|
12
|
+
from .fmt_s3 import RTDC_S3
|
|
13
|
+
from .fmt_tdms import RTDC_TDMS
|
|
14
|
+
from .load import new_dataset
|
|
15
|
+
from .writer import RTDCWriter
|