PyOPIA 2.16.1__tar.gz → 2.16.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.
- {pyopia-2.16.1 → pyopia-2.16.3}/PKG-INFO +1 -1
- pyopia-2.16.3/pyopia/__init__.py +1 -0
- {pyopia-2.16.1 → pyopia-2.16.3}/pyopia/cli.py +15 -0
- {pyopia-2.16.1 → pyopia-2.16.3}/pyopia/instrument/holo.py +2 -1
- {pyopia-2.16.1 → pyopia-2.16.3}/pyopia/instrument/silcam.py +2 -1
- {pyopia-2.16.1 → pyopia-2.16.3}/pyopia/instrument/uvp.py +2 -1
- {pyopia-2.16.1 → pyopia-2.16.3}/pyopia/process.py +17 -4
- pyopia-2.16.3/pyopia/tests/test_process.py +64 -0
- pyopia-2.16.1/pyopia/__init__.py +0 -1
- {pyopia-2.16.1 → pyopia-2.16.3}/.gitignore +0 -0
- {pyopia-2.16.1 → pyopia-2.16.3}/LICENSE +0 -0
- {pyopia-2.16.1 → pyopia-2.16.3}/README.md +0 -0
- {pyopia-2.16.1 → pyopia-2.16.3}/pyopia/auxillarydata.py +0 -0
- {pyopia-2.16.1 → pyopia-2.16.3}/pyopia/background.py +0 -0
- {pyopia-2.16.1 → pyopia-2.16.3}/pyopia/cf_metadata.json +0 -0
- {pyopia-2.16.1 → pyopia-2.16.3}/pyopia/classify.py +0 -0
- {pyopia-2.16.1 → pyopia-2.16.3}/pyopia/classify_torch.py +0 -0
- {pyopia-2.16.1 → pyopia-2.16.3}/pyopia/dataexport/__init__.py +0 -0
- {pyopia-2.16.1 → pyopia-2.16.3}/pyopia/dataexport/ecotaxa.py +0 -0
- {pyopia-2.16.1 → pyopia-2.16.3}/pyopia/exampledata.py +0 -0
- {pyopia-2.16.1 → pyopia-2.16.3}/pyopia/instrument/__init__.py +0 -0
- {pyopia-2.16.1 → pyopia-2.16.3}/pyopia/instrument/common.py +0 -0
- {pyopia-2.16.1 → pyopia-2.16.3}/pyopia/io.py +0 -0
- {pyopia-2.16.1 → pyopia-2.16.3}/pyopia/metadata.py +0 -0
- {pyopia-2.16.1 → pyopia-2.16.3}/pyopia/pipeline.py +0 -0
- {pyopia-2.16.1 → pyopia-2.16.3}/pyopia/plotting.py +0 -0
- {pyopia-2.16.1 → pyopia-2.16.3}/pyopia/realtime.py +0 -0
- {pyopia-2.16.1 → pyopia-2.16.3}/pyopia/simulator/__init__.py +0 -0
- {pyopia-2.16.1 → pyopia-2.16.3}/pyopia/simulator/silcam.py +0 -0
- {pyopia-2.16.1 → pyopia-2.16.3}/pyopia/statistics.py +0 -0
- {pyopia-2.16.1 → pyopia-2.16.3}/pyopia/tests/__init__.py +0 -0
- {pyopia-2.16.1 → pyopia-2.16.3}/pyopia/tests/test_auxillarydata.py +0 -0
- {pyopia-2.16.1 → pyopia-2.16.3}/pyopia/tests/test_classify.py +0 -0
- {pyopia-2.16.1 → pyopia-2.16.3}/pyopia/tests/test_io.py +0 -0
- {pyopia-2.16.1 → pyopia-2.16.3}/pyopia/tests/test_notebooks.py +0 -0
- {pyopia-2.16.1 → pyopia-2.16.3}/pyopia/tests/test_pipeline.py +0 -0
- {pyopia-2.16.1 → pyopia-2.16.3}/pyopia/tests/test_realtime.py +0 -0
- {pyopia-2.16.1 → pyopia-2.16.3}/pyproject.toml +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "2.16.3"
|
|
@@ -371,6 +371,21 @@ def process_realtime(config_filename: str, watch_folder: str = None):
|
|
|
371
371
|
# Load config and setup logging
|
|
372
372
|
pipeline_config = pyopia.io.load_toml(config_filename)
|
|
373
373
|
setup_logging(pipeline_config)
|
|
374
|
+
|
|
375
|
+
# Create output folders
|
|
376
|
+
if "output" not in pipeline_config["steps"]:
|
|
377
|
+
raise RuntimeError(
|
|
378
|
+
'The given config file is missing an "output" step.\n'
|
|
379
|
+
+ "This is needed to setup how to save data to disc."
|
|
380
|
+
)
|
|
381
|
+
if "output_datafile" not in pipeline_config["steps"]["output"]:
|
|
382
|
+
raise RuntimeError(
|
|
383
|
+
'The given config file is missing "output_datafile" option in the "output" step.\n'
|
|
384
|
+
+ "This is needed to setup how to save data to disc."
|
|
385
|
+
)
|
|
386
|
+
output_datafile = pipeline_config["steps"]["output"]["output_datafile"]
|
|
387
|
+
os.makedirs(os.path.split(output_datafile)[:-1][0], exist_ok=True)
|
|
388
|
+
|
|
374
389
|
pyopia.realtime.run_realtime(pipeline_config, watch_folder=watch_folder)
|
|
375
390
|
|
|
376
391
|
|
|
@@ -668,7 +668,8 @@ def generate_config(raw_files: str, model_path: str, outfolder: str, output_pref
|
|
|
668
668
|
'segmentation': {
|
|
669
669
|
'pipeline_class': 'pyopia.process.Segment',
|
|
670
670
|
'threshold': 0.9,
|
|
671
|
-
'segment_source': 'im_focussed'
|
|
671
|
+
'segment_source': 'im_focussed',
|
|
672
|
+
'segmentation_method': 'fast'
|
|
672
673
|
},
|
|
673
674
|
'statextract': {
|
|
674
675
|
'pipeline_class': 'pyopia.process.CalculateStats',
|
|
@@ -341,7 +341,8 @@ def generate_config(raw_files: str, model_path: str, outfolder: str, output_pref
|
|
|
341
341
|
'segmentation': {
|
|
342
342
|
'pipeline_class': 'pyopia.process.Segment',
|
|
343
343
|
'threshold': 0.85,
|
|
344
|
-
'segment_source': 'im_minimum'
|
|
344
|
+
'segment_source': 'im_minimum',
|
|
345
|
+
'segmentation_method': 'fast'
|
|
345
346
|
},
|
|
346
347
|
'statextract': {
|
|
347
348
|
'pipeline_class': 'pyopia.process.CalculateStats',
|
|
@@ -110,7 +110,8 @@ def generate_config(raw_files: str, model_path: str, outfolder: str, output_pref
|
|
|
110
110
|
'segmentation': {
|
|
111
111
|
'pipeline_class': 'pyopia.process.Segment',
|
|
112
112
|
'threshold': 0.95,
|
|
113
|
-
'segment_source': 'imraw'
|
|
113
|
+
'segment_source': 'imraw',
|
|
114
|
+
'segmentation_method': 'fast'
|
|
114
115
|
},
|
|
115
116
|
'statextract': {
|
|
116
117
|
'pipeline_class': 'pyopia.process.CalculateStats',
|
|
@@ -376,7 +376,7 @@ def measure_particles(imbw, max_particles=5000):
|
|
|
376
376
|
return region_properties
|
|
377
377
|
|
|
378
378
|
|
|
379
|
-
def segment(img, threshold=0.98, minimum_area=12, fill_holes=True):
|
|
379
|
+
def segment(img, threshold=0.98, minimum_area=12, fill_holes=True, segmentation_method='fast'):
|
|
380
380
|
'''Create a binary image from a background-corrected image.
|
|
381
381
|
|
|
382
382
|
Parameters
|
|
@@ -389,6 +389,9 @@ def segment(img, threshold=0.98, minimum_area=12, fill_holes=True):
|
|
|
389
389
|
minimum number of pixels to be considered a particle, by default 12
|
|
390
390
|
fill_holes : bool, optional
|
|
391
391
|
runs ndi.binary_fill_holes if True, by default True
|
|
392
|
+
segmentation_method : str, optional
|
|
393
|
+
Segmentation function to use. Supports ``'fast'`` and ``'accurate'``.
|
|
394
|
+
By default ``'fast'``.
|
|
392
395
|
|
|
393
396
|
Returns
|
|
394
397
|
-------
|
|
@@ -397,7 +400,12 @@ def segment(img, threshold=0.98, minimum_area=12, fill_holes=True):
|
|
|
397
400
|
'''
|
|
398
401
|
logger.info('segment')
|
|
399
402
|
|
|
400
|
-
|
|
403
|
+
if segmentation_method == 'fast':
|
|
404
|
+
imbw = image2blackwhite_fast(img, threshold)
|
|
405
|
+
elif segmentation_method == 'accurate':
|
|
406
|
+
imbw = image2blackwhite_accurate(img, threshold)
|
|
407
|
+
else:
|
|
408
|
+
raise ValueError(f"Unknown segmentation_method '{segmentation_method}'. Supported values are 'fast' and 'accurate'.")
|
|
401
409
|
|
|
402
410
|
logger.info('clean')
|
|
403
411
|
|
|
@@ -493,6 +501,9 @@ class Segment():
|
|
|
493
501
|
segment_source: (str, optional)
|
|
494
502
|
The key in Pipeline.data of the image to be segmented.
|
|
495
503
|
Defaults to 'im_corrected'
|
|
504
|
+
segmentation_method: (str, optional)
|
|
505
|
+
Segmentation method to use: ``'fast'`` or ``'accurate'``.
|
|
506
|
+
Defaults to ``'fast'``.
|
|
496
507
|
|
|
497
508
|
Returns
|
|
498
509
|
-------
|
|
@@ -506,16 +517,18 @@ class Segment():
|
|
|
506
517
|
minimum_area=12,
|
|
507
518
|
threshold=0.98,
|
|
508
519
|
fill_holes=True,
|
|
509
|
-
segment_source='im_corrected'
|
|
520
|
+
segment_source='im_corrected',
|
|
521
|
+
segmentation_method='fast'):
|
|
510
522
|
|
|
511
523
|
self.minimum_area = minimum_area
|
|
512
524
|
self.threshold = threshold
|
|
513
525
|
self.fill_holes = fill_holes
|
|
514
526
|
self.segment_source = segment_source
|
|
527
|
+
self.segmentation_method = segmentation_method
|
|
515
528
|
|
|
516
529
|
def __call__(self, data):
|
|
517
530
|
data['imbw'] = segment(data[self.segment_source], threshold=self.threshold, fill_holes=self.fill_holes,
|
|
518
|
-
minimum_area=self.minimum_area)
|
|
531
|
+
minimum_area=self.minimum_area, segmentation_method=self.segmentation_method)
|
|
519
532
|
return data
|
|
520
533
|
|
|
521
534
|
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
import pytest
|
|
3
|
+
|
|
4
|
+
from pyopia.pipeline import Pipeline
|
|
5
|
+
import pyopia.process
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def test_segment_selects_accurate_method(monkeypatch):
|
|
9
|
+
img = np.ones((4, 4), dtype=float)
|
|
10
|
+
call_state = {"fast": 0, "accurate": 0}
|
|
11
|
+
|
|
12
|
+
def fake_fast(_img, _threshold):
|
|
13
|
+
call_state["fast"] += 1
|
|
14
|
+
return np.zeros_like(_img, dtype=bool)
|
|
15
|
+
|
|
16
|
+
def fake_accurate(_img, _threshold):
|
|
17
|
+
call_state["accurate"] += 1
|
|
18
|
+
return np.ones_like(_img, dtype=bool)
|
|
19
|
+
|
|
20
|
+
monkeypatch.setattr(pyopia.process, "image2blackwhite_fast", fake_fast)
|
|
21
|
+
monkeypatch.setattr(pyopia.process, "image2blackwhite_accurate", fake_accurate)
|
|
22
|
+
monkeypatch.setattr(pyopia.process, "clean_bw", lambda imbw, _minimum_area: imbw)
|
|
23
|
+
|
|
24
|
+
pyopia.process.segment(img, segmentation_method="accurate")
|
|
25
|
+
|
|
26
|
+
assert call_state["fast"] == 0
|
|
27
|
+
assert call_state["accurate"] == 1
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def test_segment_raises_on_unknown_method():
|
|
31
|
+
img = np.ones((4, 4), dtype=float)
|
|
32
|
+
|
|
33
|
+
with pytest.raises(ValueError, match="Unknown segmentation_method"):
|
|
34
|
+
pyopia.process.segment(img, segmentation_method="unknown")
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def test_pipeline_segmentation_step_accepts_segmentation_method_config(monkeypatch):
|
|
38
|
+
captured = {}
|
|
39
|
+
|
|
40
|
+
def fake_segment(img, threshold, minimum_area, fill_holes, segmentation_method):
|
|
41
|
+
captured["segmentation_method"] = segmentation_method
|
|
42
|
+
return np.zeros_like(img, dtype=bool)
|
|
43
|
+
|
|
44
|
+
monkeypatch.setattr(pyopia.process, "segment", fake_segment)
|
|
45
|
+
|
|
46
|
+
config = {
|
|
47
|
+
"general": {"raw_files": []},
|
|
48
|
+
"steps": {
|
|
49
|
+
"segmentation": {
|
|
50
|
+
"pipeline_class": "pyopia.process.Segment",
|
|
51
|
+
"threshold": 0.9,
|
|
52
|
+
"minimum_area": 5,
|
|
53
|
+
"fill_holes": True,
|
|
54
|
+
"segment_source": "im_corrected",
|
|
55
|
+
"segmentation_method": "accurate",
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
pipeline = Pipeline(config, initial_steps=[])
|
|
61
|
+
pipeline.data["im_corrected"] = np.ones((4, 4), dtype=float)
|
|
62
|
+
pipeline.run_step("segmentation")
|
|
63
|
+
|
|
64
|
+
assert captured["segmentation_method"] == "accurate"
|
pyopia-2.16.1/pyopia/__init__.py
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "2.16.1"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|