cellprofiler-library-nightly 5.0.0.dev650__tar.gz → 5.0.0.dev669__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.
- {cellprofiler_library_nightly-5.0.0.dev650/cellprofiler_library_nightly.egg-info → cellprofiler_library_nightly-5.0.0.dev669}/PKG-INFO +1 -1
- cellprofiler_library_nightly-5.0.0.dev669/cellprofiler_library/_version.py +24 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/functions/image_processing.py +214 -11
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/functions/object_processing.py +346 -1
- cellprofiler_library_nightly-5.0.0.dev669/cellprofiler_library/modules/_filterobjects.py +88 -0
- cellprofiler_library_nightly-5.0.0.dev669/cellprofiler_library/modules/_rescaleintensity.py +60 -0
- cellprofiler_library_nightly-5.0.0.dev669/cellprofiler_library/opts/filterobjects.py +38 -0
- cellprofiler_library_nightly-5.0.0.dev669/cellprofiler_library/opts/rescaleintensity.py +39 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/types.py +14 -5
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669/cellprofiler_library_nightly.egg-info}/PKG-INFO +1 -1
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library_nightly.egg-info/SOURCES.txt +6 -0
- cellprofiler_library_nightly-5.0.0.dev669/cellprofiler_library_nightly.egg-info/scm_file_list.json +118 -0
- cellprofiler_library_nightly-5.0.0.dev669/cellprofiler_library_nightly.egg-info/scm_version.json +8 -0
- cellprofiler_library_nightly-5.0.0.dev650/cellprofiler_library/_version.py +0 -34
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/LICENSE +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/README.md +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/__init__.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/functions/__init__.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/functions/file_processing.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/functions/measurement.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/functions/segmentation.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/measurement_model.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/modules/__init__.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/modules/_align.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/modules/_closing.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/modules/_colortogray.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/modules/_combineobjects.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/modules/_convertimagetoobjects.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/modules/_convertobjectstoimage.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/modules/_correctilluminationapply.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/modules/_crop.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/modules/_dilateimage.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/modules/_dilateobjects.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/modules/_enhanceedges.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/modules/_enhanceorsuppressfeatures.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/modules/_erodeimage.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/modules/_erodeobjects.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/modules/_expandorshrinkobjects.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/modules/_fillobjects.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/modules/_flipandrotate.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/modules/_gaussianfilter.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/modules/_graytocolor.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/modules/_identifydeadworms.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/modules/_identifyprimaryobjects.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/modules/_identifysecondaryobjects.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/modules/_identifytertiaryobjects.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/modules/_imagemath.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/modules/_measurecolocalization.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/modules/_measuregranularity.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/modules/_measureimageareaoccupied.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/modules/_measureimageintensity.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/modules/_measureimageoverlap.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/modules/_measureimagequality.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/modules/_measureimageskeleton.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/modules/_measureobjectintensity.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/modules/_measureobjectintensitydistribution.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/modules/_measureobjectneighbors.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/modules/_measureobjectoverlap.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/modules/_measureobjectsizeshape.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/modules/_measureobjectskeleton.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/modules/_measuretexture.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/modules/_medialaxis.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/modules/_medianfilter.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/modules/_morph.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/modules/_morphologicalskeleton.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/modules/_opening.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/modules/_overlayobjects.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/modules/_overlayoutlines.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/modules/_reducenoise.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/modules/_removeholes.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/modules/_resize.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/modules/_savecroppedobjects.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/modules/_shrinktoobjectcenters.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/modules/_smooth.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/modules/_threshold.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/modules/_watershed.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/opts/__init__.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/opts/align.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/opts/colortogray.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/opts/convertimagetoobjects.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/opts/convertobjectstoimage.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/opts/correctilluminationapply.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/opts/crop.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/opts/dilateimage.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/opts/dilateobjects.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/opts/enhanceedges.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/opts/enhanceorsuppressfeatures.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/opts/erodeimage.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/opts/erodeobjects.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/opts/flipandrotate.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/opts/graytocolor.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/opts/identifydeadworms.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/opts/identifyprimaryobjects.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/opts/identifysecondaryobjects.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/opts/identifytertiaryobjects.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/opts/imagemath.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/opts/measurecolocalization.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/opts/measuregranularity.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/opts/measureimageareaoccupied.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/opts/measureimageintensity.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/opts/measureimageoverlap.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/opts/measureimagequality.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/opts/measureimageskeleton.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/opts/measureobjectintensity.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/opts/measureobjectintensitydistribution.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/opts/measureobjectneighbors.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/opts/measureobjectoverlap.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/opts/measureobjectskeleton.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/opts/measuretexture.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/opts/morph.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/opts/objectsizeshapefeatures.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/opts/overlayoutlines.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/opts/removeholes.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/opts/resize.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/opts/shrinktoobjectcenters.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/opts/smooth.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/opts/structuring_elements.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/opts/threshold.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/py.typed +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library_nightly.egg-info/dependency_links.txt +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library_nightly.egg-info/requires.txt +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library_nightly.egg-info/top_level.txt +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/environment.yml +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/pyproject.toml +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: cellprofiler-library-nightly
|
|
3
|
-
Version: 5.0.0.
|
|
3
|
+
Version: 5.0.0.dev669
|
|
4
4
|
Summary: cellprofiler-library implements CellProfiler's image processing and mathematical code, and is usable as a standalone library
|
|
5
5
|
Author: Anne Carpenter, Thouis (Ray) Jones, Lee Kamentsky, Vebjorn Ljosa, David Logan, Mark Bray, Madison Swain-Bowden, Allen Goodman, Claire McQuinn, Alice Lucas, Callum Tromans-Coia
|
|
6
6
|
Author-email: Beth Cimini <bcimini@broadinstitute.org>, David Stirling <dstirling@glencoesoftware.com>, Nodar Gogoberidze <ngogober@broadinstitute.org>
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# file generated by vcs-versioning
|
|
2
|
+
# don't change, don't track in version control
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
__all__ = [
|
|
6
|
+
"__version__",
|
|
7
|
+
"__version_tuple__",
|
|
8
|
+
"version",
|
|
9
|
+
"version_tuple",
|
|
10
|
+
"__commit_id__",
|
|
11
|
+
"commit_id",
|
|
12
|
+
]
|
|
13
|
+
|
|
14
|
+
version: str
|
|
15
|
+
__version__: str
|
|
16
|
+
__version_tuple__: tuple[int | str, ...]
|
|
17
|
+
version_tuple: tuple[int | str, ...]
|
|
18
|
+
commit_id: str | None
|
|
19
|
+
__commit_id__: str | None
|
|
20
|
+
|
|
21
|
+
__version__ = version = '5.0.0.dev669'
|
|
22
|
+
__version_tuple__ = version_tuple = (5, 0, 0, 'dev669')
|
|
23
|
+
|
|
24
|
+
__commit_id__ = commit_id = 'g51250caf3'
|
|
@@ -8,6 +8,7 @@ from skimage.util import invert as _invert
|
|
|
8
8
|
import skimage.transform
|
|
9
9
|
import skimage
|
|
10
10
|
import skimage.restoration
|
|
11
|
+
import skimage.exposure
|
|
11
12
|
import centrosome
|
|
12
13
|
import centrosome.threshold
|
|
13
14
|
import centrosome.filter
|
|
@@ -31,7 +32,7 @@ from cellprofiler_library.types import (
|
|
|
31
32
|
ImageAny, ImageAnyMask,
|
|
32
33
|
ObjectSegmentation,
|
|
33
34
|
Image2D, Image2DMask,
|
|
34
|
-
StructuringElement, ObjectLabelSet, ImageColor, ImageBinaryMask, Image2DBinary, ObjectLabel, ImageBinary, Pixel
|
|
35
|
+
StructuringElement, ObjectLabelSet, ImageColor, ImageBinaryMask, Image2DBinary, ObjectLabel, ImageBinary, Pixel, PixelAny
|
|
35
36
|
)
|
|
36
37
|
from cellprofiler_library.opts import threshold as Threshold
|
|
37
38
|
from cellprofiler_library.opts.enhanceorsuppressfeatures import SpeckleAccuracy, NeuriteMethod
|
|
@@ -42,7 +43,7 @@ from cellprofiler_library.opts.resize import ResizingMethod, DimensionMethod, In
|
|
|
42
43
|
from cellprofiler_library.opts.imagemath import Operator
|
|
43
44
|
from cellprofiler_library.opts.flipandrotate import RotationCoordinateAlignmnet
|
|
44
45
|
from cellprofiler_library.opts.enhanceedges import EdgeDirection
|
|
45
|
-
|
|
46
|
+
from cellprofiler_library.opts.rescaleintensity import MaximumIntensityMethod, MinimumIntensityMethod
|
|
46
47
|
invert = cast(Callable[[ImageAny], ImageAny], _invert)
|
|
47
48
|
isscalar = cast(Callable[[Optional[ImageAny]], bool], _isscalar)
|
|
48
49
|
|
|
@@ -1769,21 +1770,21 @@ def fill_holes(image: ImageAny, diameter: float) -> ImageAny:
|
|
|
1769
1770
|
|
|
1770
1771
|
def imagemath_apply_on_image(
|
|
1771
1772
|
output_pixel_data: ImageAny,
|
|
1772
|
-
pd: ImageAny,
|
|
1773
|
-
|
|
1774
|
-
op: Callable[[ImageAny, ImageAny], ImageAny],
|
|
1773
|
+
pd: ImageAny,
|
|
1774
|
+
comparator: ImageAny,
|
|
1775
|
+
op: Callable[[ImageAny, ImageAny], ImageAny],
|
|
1775
1776
|
opval: Operator,
|
|
1776
1777
|
) -> ImageAny:
|
|
1777
1778
|
assert isinstance(output_pixel_data, numpy.ndarray), "output_pixel_data must be a numpy array" # Pylance needs to understand this is a numpy array
|
|
1778
1779
|
if not isscalar(pd) and output_pixel_data.ndim != pd.ndim:
|
|
1779
1780
|
if output_pixel_data.ndim == 2:
|
|
1780
1781
|
output_pixel_data = output_pixel_data[:, :, numpy.newaxis]
|
|
1781
|
-
if opval == Operator.EQUALS and not isscalar(
|
|
1782
|
-
|
|
1782
|
+
if opval == Operator.EQUALS and not isscalar(comparator):
|
|
1783
|
+
comparator = comparator[:, :, numpy.newaxis]
|
|
1783
1784
|
if pd.ndim == 2:
|
|
1784
1785
|
pd = pd[:, :, numpy.newaxis]
|
|
1785
1786
|
if opval == Operator.EQUALS:
|
|
1786
|
-
output_pixel_data = output_pixel_data & (
|
|
1787
|
+
output_pixel_data = output_pixel_data & (comparator == pd)
|
|
1787
1788
|
else:
|
|
1788
1789
|
output_pixel_data = op(output_pixel_data, pd)
|
|
1789
1790
|
return output_pixel_data
|
|
@@ -1818,7 +1819,7 @@ def imagemath_apply_binary_operation(
|
|
|
1818
1819
|
output_pixel_data[pd] = False
|
|
1819
1820
|
return output_pixel_data
|
|
1820
1821
|
|
|
1821
|
-
|
|
1822
|
+
comparator = operands[0] # fix pylance error
|
|
1822
1823
|
use_logical = use_logical_operation(operands)
|
|
1823
1824
|
op_fn_dispatch: Dict[Operator, Callable[[ImageAny, ImageAny], ImageAny]] = {
|
|
1824
1825
|
Operator.ADD: numpy.add,
|
|
@@ -1847,7 +1848,7 @@ def imagemath_apply_binary_operation(
|
|
|
1847
1848
|
#
|
|
1848
1849
|
if opval == Operator.EQUALS:
|
|
1849
1850
|
output_pixel_data = numpy.ones(operands[0].shape, bool)
|
|
1850
|
-
|
|
1851
|
+
comparator = operands[0]
|
|
1851
1852
|
elif opval == Operator.SUBTRACT and use_logical:
|
|
1852
1853
|
output_pixel_data = operands[0].copy()
|
|
1853
1854
|
|
|
@@ -1860,7 +1861,7 @@ def imagemath_apply_binary_operation(
|
|
|
1860
1861
|
# Apply the operation to each image in the list
|
|
1861
1862
|
#
|
|
1862
1863
|
for pd, mask in zip(operands[1:], masks[1:]):
|
|
1863
|
-
output_pixel_data = imagemath_apply_on_image(output_pixel_data, pd,
|
|
1864
|
+
output_pixel_data = imagemath_apply_on_image(output_pixel_data, pd, comparator, op, opval)
|
|
1864
1865
|
if not ignore_mask:
|
|
1865
1866
|
if output_mask is None:
|
|
1866
1867
|
output_mask = mask
|
|
@@ -2870,3 +2871,205 @@ def reshape_image(
|
|
|
2870
2871
|
result[: source.shape[0], : source.shape[1]] = source
|
|
2871
2872
|
return result
|
|
2872
2873
|
|
|
2874
|
+
|
|
2875
|
+
###############################################################################
|
|
2876
|
+
# RescaleIntensity
|
|
2877
|
+
###############################################################################
|
|
2878
|
+
|
|
2879
|
+
def rescale(
|
|
2880
|
+
image_pixel_data: ImageAny,
|
|
2881
|
+
in_range: Tuple[float, float],
|
|
2882
|
+
out_range: Tuple[float, float] = (0.0, 1.0)
|
|
2883
|
+
) -> ImageAny:
|
|
2884
|
+
data = 1.0 * image_pixel_data
|
|
2885
|
+
|
|
2886
|
+
rescaled = skimage.exposure.rescale_intensity(
|
|
2887
|
+
data, in_range=in_range, out_range=out_range
|
|
2888
|
+
)
|
|
2889
|
+
|
|
2890
|
+
return rescaled
|
|
2891
|
+
|
|
2892
|
+
|
|
2893
|
+
def stretch_rescale(
|
|
2894
|
+
data: ImageAny,
|
|
2895
|
+
mask: ImageAnyMask,
|
|
2896
|
+
multichannel: bool = False
|
|
2897
|
+
) -> ImageAny:
|
|
2898
|
+
if multichannel:
|
|
2899
|
+
splitaxis = data.ndim - 1
|
|
2900
|
+
singlechannels = numpy.split(data, data.shape[-1], splitaxis)
|
|
2901
|
+
newchannels = []
|
|
2902
|
+
for channel in singlechannels:
|
|
2903
|
+
channel = numpy.squeeze(channel, axis=splitaxis)
|
|
2904
|
+
if (masked_channel := channel[mask]).size == 0:
|
|
2905
|
+
in_range = (0, 1)
|
|
2906
|
+
else:
|
|
2907
|
+
in_range = (min(masked_channel), max(masked_channel))
|
|
2908
|
+
|
|
2909
|
+
rescaled = rescale(channel, in_range)
|
|
2910
|
+
|
|
2911
|
+
newchannels.append(rescaled)
|
|
2912
|
+
full_rescaled = numpy.stack(newchannels, axis=-1)
|
|
2913
|
+
return full_rescaled
|
|
2914
|
+
if (masked_data := data[mask]).size == 0:
|
|
2915
|
+
in_range = (0, 1)
|
|
2916
|
+
else:
|
|
2917
|
+
in_range = (min(masked_data), max(masked_data))
|
|
2918
|
+
return rescale(data, in_range)
|
|
2919
|
+
|
|
2920
|
+
|
|
2921
|
+
def manual_input_range(
|
|
2922
|
+
data: ImageAny,
|
|
2923
|
+
mask: Optional[ImageAnyMask],
|
|
2924
|
+
source_high: float,
|
|
2925
|
+
source_low: float,
|
|
2926
|
+
source_scale_min: float,
|
|
2927
|
+
source_scale_max: float,
|
|
2928
|
+
auto_high: MaximumIntensityMethod,
|
|
2929
|
+
auto_low: MinimumIntensityMethod,
|
|
2930
|
+
global_min: Optional[PixelAny],
|
|
2931
|
+
global_max: Optional[PixelAny],
|
|
2932
|
+
) -> ImageAny:
|
|
2933
|
+
return manual_io_range(
|
|
2934
|
+
data,
|
|
2935
|
+
mask,
|
|
2936
|
+
source_high,
|
|
2937
|
+
source_low,
|
|
2938
|
+
source_scale_min,
|
|
2939
|
+
source_scale_max,
|
|
2940
|
+
auto_high,
|
|
2941
|
+
auto_low,
|
|
2942
|
+
global_min,
|
|
2943
|
+
global_max
|
|
2944
|
+
)
|
|
2945
|
+
|
|
2946
|
+
|
|
2947
|
+
def manual_io_range(
|
|
2948
|
+
data: ImageAny,
|
|
2949
|
+
mask: Optional[ImageAnyMask],
|
|
2950
|
+
source_high: float,
|
|
2951
|
+
source_low: float,
|
|
2952
|
+
source_scale_min: float,
|
|
2953
|
+
source_scale_max: float,
|
|
2954
|
+
auto_high: MaximumIntensityMethod,
|
|
2955
|
+
auto_low: MinimumIntensityMethod,
|
|
2956
|
+
global_min: Optional[PixelAny],
|
|
2957
|
+
global_max: Optional[PixelAny],
|
|
2958
|
+
dest_scale_min: Optional[float]=None,
|
|
2959
|
+
dest_scale_max: Optional[float]=None
|
|
2960
|
+
) -> ImageAny:
|
|
2961
|
+
in_range = get_source_range(data, mask, source_high, source_low, source_scale_min, source_scale_max, auto_high, auto_low, global_min, global_max)
|
|
2962
|
+
if dest_scale_min is None and dest_scale_max is None:
|
|
2963
|
+
return rescale(data, in_range)
|
|
2964
|
+
else:
|
|
2965
|
+
out_range = (dest_scale_min, dest_scale_max)
|
|
2966
|
+
return rescale(data, in_range, out_range)
|
|
2967
|
+
|
|
2968
|
+
|
|
2969
|
+
def divide(data: ImageAny, value: float) -> ImageAny:
|
|
2970
|
+
if value == 0.0:
|
|
2971
|
+
raise ZeroDivisionError("Cannot divide pixel intensity by 0.")
|
|
2972
|
+
|
|
2973
|
+
return data / value
|
|
2974
|
+
|
|
2975
|
+
|
|
2976
|
+
def divide_by_image_minimum(data: ImageAny, mask: ImageAnyMask) -> ImageAny:
|
|
2977
|
+
if (masked_data := data[mask]).size == 0:
|
|
2978
|
+
src_min = numpy.float64(0)
|
|
2979
|
+
else:
|
|
2980
|
+
src_min = numpy.min(masked_data)
|
|
2981
|
+
|
|
2982
|
+
return divide(data, float(src_min))
|
|
2983
|
+
|
|
2984
|
+
|
|
2985
|
+
def divide_by_image_maximum(data: ImageAny, mask: ImageAnyMask) -> ImageAny:
|
|
2986
|
+
if (masked_data := data[mask]).size == 0:
|
|
2987
|
+
src_max = numpy.float64(1)
|
|
2988
|
+
else:
|
|
2989
|
+
src_max = numpy.max(masked_data)
|
|
2990
|
+
|
|
2991
|
+
return divide(data, float(src_max))
|
|
2992
|
+
|
|
2993
|
+
|
|
2994
|
+
def divide_by_value(data: ImageAny, divisor_value: float):
|
|
2995
|
+
return divide(data, divisor_value)
|
|
2996
|
+
|
|
2997
|
+
|
|
2998
|
+
def scale_by_image_maximum(
|
|
2999
|
+
data: ImageAny,
|
|
3000
|
+
mask: ImageAnyMask,
|
|
3001
|
+
reference_data: ImageAny,
|
|
3002
|
+
reference_mask: ImageGrayscaleMask
|
|
3003
|
+
) -> ImageAny:
|
|
3004
|
+
###
|
|
3005
|
+
# Scale the image by the maximum of another image
|
|
3006
|
+
#
|
|
3007
|
+
# Find the maximum value within the unmasked region of the input
|
|
3008
|
+
# and reference image. Multiply by the reference maximum, divide
|
|
3009
|
+
# by the input maximum to scale the input image to the same
|
|
3010
|
+
# range as the reference image
|
|
3011
|
+
###
|
|
3012
|
+
if (masked_input := data[mask]).size == 0:
|
|
3013
|
+
return data
|
|
3014
|
+
else:
|
|
3015
|
+
image_max = numpy.max(masked_input)
|
|
3016
|
+
|
|
3017
|
+
if image_max == 0:
|
|
3018
|
+
return data
|
|
3019
|
+
|
|
3020
|
+
|
|
3021
|
+
if (masked_ref := reference_data[reference_mask]).size == 0:
|
|
3022
|
+
reference_max = 1
|
|
3023
|
+
else:
|
|
3024
|
+
reference_max = numpy.max(masked_ref)
|
|
3025
|
+
|
|
3026
|
+
return divide(data * reference_max, float(image_max))
|
|
3027
|
+
|
|
3028
|
+
|
|
3029
|
+
def get_source_range(
|
|
3030
|
+
data: ImageAny,
|
|
3031
|
+
mask: Optional[ImageAnyMask],
|
|
3032
|
+
source_high: float,
|
|
3033
|
+
source_low: float,
|
|
3034
|
+
source_scale_min: float,
|
|
3035
|
+
source_scale_max: float,
|
|
3036
|
+
auto_high: MaximumIntensityMethod,
|
|
3037
|
+
auto_low: MinimumIntensityMethod,
|
|
3038
|
+
global_min: Optional[PixelAny],
|
|
3039
|
+
global_max: Optional[PixelAny],
|
|
3040
|
+
) -> Tuple[float, float]:
|
|
3041
|
+
"""Get the source range, accounting for automatically computed values"""
|
|
3042
|
+
input_pixels = None
|
|
3043
|
+
if (
|
|
3044
|
+
auto_high == MaximumIntensityMethod.CUSTOM_VALUE.value
|
|
3045
|
+
and auto_low == MinimumIntensityMethod.CUSTOM_VALUE.value
|
|
3046
|
+
):
|
|
3047
|
+
return source_scale_min, source_scale_max
|
|
3048
|
+
|
|
3049
|
+
if (
|
|
3050
|
+
auto_low == MinimumIntensityMethod.EACH_IMAGE.value
|
|
3051
|
+
or auto_high == MaximumIntensityMethod.EACH_IMAGE.value
|
|
3052
|
+
):
|
|
3053
|
+
input_pixels = data
|
|
3054
|
+
if mask is not None:
|
|
3055
|
+
input_pixels = input_pixels[mask]
|
|
3056
|
+
if input_pixels.size == 0:
|
|
3057
|
+
return 0, 1
|
|
3058
|
+
|
|
3059
|
+
if auto_low == MinimumIntensityMethod.ALL_IMAGES.value:
|
|
3060
|
+
src_min = float(global_min or 0)
|
|
3061
|
+
elif auto_low == MinimumIntensityMethod.EACH_IMAGE.value:
|
|
3062
|
+
assert input_pixels is not None, "Invalid settings for automatic minimum, please check your settings and data"
|
|
3063
|
+
src_min = float(numpy.min(input_pixels))
|
|
3064
|
+
else:
|
|
3065
|
+
src_min = source_low
|
|
3066
|
+
|
|
3067
|
+
if auto_high == MaximumIntensityMethod.ALL_IMAGES.value:
|
|
3068
|
+
src_max = float(global_max or 0)
|
|
3069
|
+
elif auto_high == MaximumIntensityMethod.EACH_IMAGE.value:
|
|
3070
|
+
assert input_pixels is not None, "Invalid settings for automatic maximum, please check your settings and data"
|
|
3071
|
+
src_max = float(numpy.max(input_pixels))
|
|
3072
|
+
else:
|
|
3073
|
+
src_max = source_high
|
|
3074
|
+
|
|
3075
|
+
return src_min, src_max
|
|
@@ -5,10 +5,11 @@ import skimage.morphology
|
|
|
5
5
|
import matplotlib.cm
|
|
6
6
|
import mahotas
|
|
7
7
|
import centrosome.cpmorphology
|
|
8
|
-
from typing import Optional, Literal, Tuple, Union
|
|
8
|
+
from typing import Optional, Literal, Tuple, Union, List, Dict, Any
|
|
9
9
|
from cellprofiler_library.types import ImageAnyMask, ObjectLabel, ImageColor, ImageGrayscale, Image2DGrayscale, Image2DGrayscaleMask, ImageBinary, ImageAny, Image2DBinary
|
|
10
10
|
from cellprofiler_library.functions.image_processing import crop_image_similarly, ObjectSegmentation, StructuringElement
|
|
11
11
|
from cellprofiler_library.opts.identifyprimaryobjects import UnclumpMethod, WatershedMethod, FillHolesMethod
|
|
12
|
+
from cellprofiler_library.opts.filterobjects import FilterMethod, OverlapAssignment
|
|
12
13
|
|
|
13
14
|
|
|
14
15
|
def shrink_to_point(labels, fill):
|
|
@@ -1250,3 +1251,347 @@ def object_crop_image_similarly(
|
|
|
1250
1251
|
raise ValueError("Images are of different size and no parent image")
|
|
1251
1252
|
return crop_image_similarly(obj_parent_image, other_image, obj_parent_crop_mask)
|
|
1252
1253
|
|
|
1254
|
+
|
|
1255
|
+
################################################################################
|
|
1256
|
+
# MeasureColocalization
|
|
1257
|
+
################################################################################
|
|
1258
|
+
|
|
1259
|
+
|
|
1260
|
+
def keep_one(values: NDArray[numpy.float64], filter_choice: FilterMethod) -> NDArray[numpy.int_]:
|
|
1261
|
+
"""
|
|
1262
|
+
Return an array containing the single object to keep
|
|
1263
|
+
|
|
1264
|
+
values - measurement value per object
|
|
1265
|
+
filter_choice - FilterMethod.MINIMAL or FilterMethod.MAXIMAL
|
|
1266
|
+
"""
|
|
1267
|
+
if len(values) == 0:
|
|
1268
|
+
return numpy.array([], int)
|
|
1269
|
+
best_idx = (
|
|
1270
|
+
numpy.argmax(values)
|
|
1271
|
+
if filter_choice == FilterMethod.MAXIMAL.value
|
|
1272
|
+
else numpy.argmin(values)
|
|
1273
|
+
) + 1
|
|
1274
|
+
return numpy.array([best_idx], int)
|
|
1275
|
+
|
|
1276
|
+
|
|
1277
|
+
def keep_per_object(
|
|
1278
|
+
src_labels: ObjectSegmentation,
|
|
1279
|
+
enclosing_labels: ObjectSegmentation,
|
|
1280
|
+
enclosing_max: int,
|
|
1281
|
+
per_object_assignment: OverlapAssignment,
|
|
1282
|
+
filter_choice: FilterMethod,
|
|
1283
|
+
values: NDArray[numpy.float64],
|
|
1284
|
+
) -> Union[NDArray[numpy.int_], List[int]]:
|
|
1285
|
+
"""
|
|
1286
|
+
Return an array containing the best object per enclosing object
|
|
1287
|
+
|
|
1288
|
+
src_labels - segmentation of the objects being filtered
|
|
1289
|
+
enclosing_labels - segmentation of the enclosing (parent) objects
|
|
1290
|
+
enclosing_max - number of enclosing objects
|
|
1291
|
+
per_object_assignment - OverlapAssignment strategy for matching objects to enclosing objects
|
|
1292
|
+
filter_choice - FilterMethod.MINIMAL_PER_OBJECT or FilterMethod.MAXIMAL_PER_OBJECT
|
|
1293
|
+
values - measurement value per object
|
|
1294
|
+
"""
|
|
1295
|
+
if enclosing_max == 0:
|
|
1296
|
+
return numpy.array([], int)
|
|
1297
|
+
enclosing_range = numpy.arange(1, enclosing_max + 1)
|
|
1298
|
+
|
|
1299
|
+
#
|
|
1300
|
+
# Make a vector of the value of the measurement per label index.
|
|
1301
|
+
# We can then label each pixel in the image with the measurement
|
|
1302
|
+
# value for the object at that pixel.
|
|
1303
|
+
# For unlabeled pixels, put the minimum value if looking for the
|
|
1304
|
+
# maximum value and vice-versa
|
|
1305
|
+
#
|
|
1306
|
+
wants_max = filter_choice == FilterMethod.MAXIMAL_PER_OBJECT.value
|
|
1307
|
+
if per_object_assignment == OverlapAssignment.PARENT_WITH_MOST_OVERLAP.value:
|
|
1308
|
+
#
|
|
1309
|
+
# Find the number of overlapping pixels in enclosing
|
|
1310
|
+
# and source objects
|
|
1311
|
+
#
|
|
1312
|
+
mask = enclosing_labels * src_labels != 0
|
|
1313
|
+
enclosing_labels = enclosing_labels[mask]
|
|
1314
|
+
src_labels = src_labels[mask]
|
|
1315
|
+
order = numpy.lexsort((enclosing_labels, src_labels))
|
|
1316
|
+
src_labels = src_labels[order]
|
|
1317
|
+
enclosing_labels = enclosing_labels[order]
|
|
1318
|
+
firsts = numpy.hstack(
|
|
1319
|
+
(
|
|
1320
|
+
[0],
|
|
1321
|
+
numpy.where(
|
|
1322
|
+
(src_labels[:-1] != src_labels[1:])
|
|
1323
|
+
| (enclosing_labels[:-1] != enclosing_labels[1:])
|
|
1324
|
+
)[0]
|
|
1325
|
+
+ 1,
|
|
1326
|
+
[len(src_labels)],
|
|
1327
|
+
)
|
|
1328
|
+
)
|
|
1329
|
+
areas = firsts[1:] - firsts[:-1]
|
|
1330
|
+
enclosing_labels = enclosing_labels[firsts[:-1]]
|
|
1331
|
+
src_labels = src_labels[firsts[:-1]]
|
|
1332
|
+
#
|
|
1333
|
+
# Re-sort by source label value and area descending
|
|
1334
|
+
#
|
|
1335
|
+
if wants_max:
|
|
1336
|
+
svalues = -values
|
|
1337
|
+
else:
|
|
1338
|
+
svalues = values
|
|
1339
|
+
order = numpy.lexsort((-areas, svalues[src_labels - 1]))
|
|
1340
|
+
src_labels, enclosing_labels, areas = [
|
|
1341
|
+
x[order] for x in (src_labels, enclosing_labels, areas)
|
|
1342
|
+
]
|
|
1343
|
+
firsts = numpy.hstack(
|
|
1344
|
+
(
|
|
1345
|
+
[0],
|
|
1346
|
+
numpy.where(src_labels[:-1] != src_labels[1:])[0] + 1,
|
|
1347
|
+
src_labels.shape[:1],
|
|
1348
|
+
)
|
|
1349
|
+
)
|
|
1350
|
+
counts = firsts[1:] - firsts[:-1]
|
|
1351
|
+
#
|
|
1352
|
+
# Process them in order. The maximal or minimal child
|
|
1353
|
+
# will be assigned to the most overlapping parent and that
|
|
1354
|
+
# parent will be excluded.
|
|
1355
|
+
#
|
|
1356
|
+
best_src_label = numpy.zeros(enclosing_max + 1, int)
|
|
1357
|
+
for idx, count in zip(firsts[:-1], counts):
|
|
1358
|
+
for i in range(count):
|
|
1359
|
+
enclosing_object_number = enclosing_labels[idx + i]
|
|
1360
|
+
if best_src_label[enclosing_object_number] == 0:
|
|
1361
|
+
best_src_label[enclosing_object_number] = src_labels[idx]
|
|
1362
|
+
break
|
|
1363
|
+
#
|
|
1364
|
+
# Remove best source labels = 0 and sort to get the list
|
|
1365
|
+
#
|
|
1366
|
+
best_src_label = best_src_label[best_src_label != 0]
|
|
1367
|
+
best_src_label.sort()
|
|
1368
|
+
return best_src_label
|
|
1369
|
+
else:
|
|
1370
|
+
tricky_values = numpy.zeros((len(values) + 1,))
|
|
1371
|
+
tricky_values[1:] = values
|
|
1372
|
+
if wants_max:
|
|
1373
|
+
tricky_values[0] = -numpy.Inf
|
|
1374
|
+
else:
|
|
1375
|
+
tricky_values[0] = numpy.Inf
|
|
1376
|
+
src_values = tricky_values[src_labels]
|
|
1377
|
+
#
|
|
1378
|
+
# Now find the location of the best for each of the enclosing objects
|
|
1379
|
+
#
|
|
1380
|
+
fn = (
|
|
1381
|
+
scipy.ndimage.maximum_position
|
|
1382
|
+
if wants_max
|
|
1383
|
+
else scipy.ndimage.minimum_position
|
|
1384
|
+
)
|
|
1385
|
+
best_pos = fn(src_values, enclosing_labels, enclosing_range)
|
|
1386
|
+
best_pos = numpy.array(
|
|
1387
|
+
(best_pos,) if isinstance(best_pos, tuple) else best_pos
|
|
1388
|
+
)
|
|
1389
|
+
best_pos = best_pos.astype(numpy.uint32)
|
|
1390
|
+
#
|
|
1391
|
+
# Get the label of the pixel at each location
|
|
1392
|
+
#
|
|
1393
|
+
# Multidimensional indexing with non-tuple values is not allowed as of numpy 1.23
|
|
1394
|
+
best_pos = tuple(map(tuple, best_pos.transpose()))
|
|
1395
|
+
indexes = src_labels[best_pos]
|
|
1396
|
+
indexes = set(indexes)
|
|
1397
|
+
indexes = list(indexes)
|
|
1398
|
+
indexes.sort()
|
|
1399
|
+
return indexes[1:] if len(indexes) > 0 and indexes[0] == 0 else indexes
|
|
1400
|
+
|
|
1401
|
+
|
|
1402
|
+
def keep_within_limits(limit_groups: List[Dict[str, Any]]) -> NDArray[numpy.int_]:
|
|
1403
|
+
"""Return an array containing the indices of objects to keep
|
|
1404
|
+
|
|
1405
|
+
limit_groups - a list of {"values": ndarray, "min_limit": float or None, "max_limit": float or None}
|
|
1406
|
+
"""
|
|
1407
|
+
hits = None
|
|
1408
|
+
MIN_LIM = "min_limit"
|
|
1409
|
+
MAX_LIM = "max_limit"
|
|
1410
|
+
VALUES = "values"
|
|
1411
|
+
for group in limit_groups:
|
|
1412
|
+
values = group[VALUES]
|
|
1413
|
+
|
|
1414
|
+
if hits is None:
|
|
1415
|
+
hits = numpy.ones(len(values), bool)
|
|
1416
|
+
elif len(hits) < len(values):
|
|
1417
|
+
temp = numpy.ones(len(values), bool)
|
|
1418
|
+
temp[~hits] = False
|
|
1419
|
+
hits = temp
|
|
1420
|
+
low_limit = group[MIN_LIM]
|
|
1421
|
+
high_limit = group[MAX_LIM]
|
|
1422
|
+
if low_limit is not None:
|
|
1423
|
+
hits[values < low_limit] = False
|
|
1424
|
+
if high_limit is not None:
|
|
1425
|
+
hits[values > high_limit] = False
|
|
1426
|
+
assert hits is not None
|
|
1427
|
+
indexes = numpy.argwhere(hits)[:, 0]
|
|
1428
|
+
indexes = indexes + 1
|
|
1429
|
+
return indexes
|
|
1430
|
+
|
|
1431
|
+
|
|
1432
|
+
def keep_by_rules(scores: NDArray[numpy.float64], rules_class: int) -> NDArray[numpy.int_]:
|
|
1433
|
+
"""Return the indexes (base 1) of objects whose highest-scoring class is rules_class
|
|
1434
|
+
|
|
1435
|
+
scores - an MxN matrix as returned by Rules.score(): M objects x N classes.
|
|
1436
|
+
The Rules object itself is never passed into the library, only
|
|
1437
|
+
the plain scores it produces.
|
|
1438
|
+
rules_class - the 0-based class index to keep
|
|
1439
|
+
"""
|
|
1440
|
+
if len(scores) == 0:
|
|
1441
|
+
return numpy.array([], int)
|
|
1442
|
+
is_not_nan = numpy.any(~numpy.isnan(scores), 1)
|
|
1443
|
+
best_class = numpy.argmax(scores[is_not_nan], 1).flatten()
|
|
1444
|
+
hits = numpy.zeros(scores.shape[0], bool)
|
|
1445
|
+
hits[is_not_nan] = best_class == rules_class
|
|
1446
|
+
return numpy.argwhere(hits).flatten() + 1
|
|
1447
|
+
|
|
1448
|
+
|
|
1449
|
+
def keep_by_hits(hits: NDArray[numpy.bool_]) -> NDArray[numpy.int_]:
|
|
1450
|
+
"""Return the indexes (base 1) of objects for which hits is True
|
|
1451
|
+
|
|
1452
|
+
Used for classifier predictions (predicted_classes == target_class) -
|
|
1453
|
+
the classifier object itself is never passed into the library, only the
|
|
1454
|
+
resulting boolean hits it produces.
|
|
1455
|
+
"""
|
|
1456
|
+
return numpy.argwhere(hits).flatten() + 1
|
|
1457
|
+
|
|
1458
|
+
|
|
1459
|
+
def discard_border_objects(labels: ObjectSegmentation, parent_image_mask: Optional[NDArray[numpy.bool_]]) -> List[int]:
|
|
1460
|
+
"""
|
|
1461
|
+
Return an array containing the object numbers to keep
|
|
1462
|
+
|
|
1463
|
+
labels - segmentation of the objects being filtered
|
|
1464
|
+
parent_image_mask - mask of the parent image (or None); objects touching its border are discarded
|
|
1465
|
+
"""
|
|
1466
|
+
|
|
1467
|
+
if parent_image_mask is not None:
|
|
1468
|
+
mask = parent_image_mask
|
|
1469
|
+
interior_pixels = scipy.ndimage.binary_erosion(mask)
|
|
1470
|
+
|
|
1471
|
+
else:
|
|
1472
|
+
interior_pixels = scipy.ndimage.binary_erosion(numpy.ones_like(labels))
|
|
1473
|
+
|
|
1474
|
+
border_pixels = numpy.logical_not(interior_pixels)
|
|
1475
|
+
border_labels = set(labels[border_pixels])
|
|
1476
|
+
if (border_labels == {0} and parent_image_mask):
|
|
1477
|
+
# The assumption here is that, if nothing touches the border,
|
|
1478
|
+
# the mask is a large, elliptical mask that tells you where the
|
|
1479
|
+
# well is. That's the way the old Matlab code works and it's duplicated here
|
|
1480
|
+
#
|
|
1481
|
+
# The operation below gets the mask pixels that are on the border of the mask
|
|
1482
|
+
# The erosion turns all pixels touching an edge to zero. The not of this
|
|
1483
|
+
# is the border + formerly masked-out pixels.
|
|
1484
|
+
|
|
1485
|
+
mask = parent_image_mask
|
|
1486
|
+
interior_pixels = scipy.ndimage.binary_erosion(mask)
|
|
1487
|
+
border_pixels = numpy.logical_not(interior_pixels)
|
|
1488
|
+
border_labels = set(labels[border_pixels])
|
|
1489
|
+
|
|
1490
|
+
return list(set(labels.ravel()).difference(border_labels))
|
|
1491
|
+
|
|
1492
|
+
|
|
1493
|
+
def get_filtered_object(
|
|
1494
|
+
src_objects_segmented: ObjectSegmentation,
|
|
1495
|
+
indexes: Union[NDArray[numpy.int_], List[int]],
|
|
1496
|
+
label_indexes: Optional[NDArray[numpy.int_]],
|
|
1497
|
+
max_label: int,
|
|
1498
|
+
parent_objects: Optional[NDArray[numpy.int_]],
|
|
1499
|
+
keep_unassociated_objects: bool,
|
|
1500
|
+
) -> ObjectSegmentation:
|
|
1501
|
+
"""
|
|
1502
|
+
Relabel a segmentation so it keeps only the filtered objects
|
|
1503
|
+
|
|
1504
|
+
src_objects_segmented - segmentation to filter and relabel
|
|
1505
|
+
indexes - object numbers (base 1) to keep
|
|
1506
|
+
label_indexes - mapping from old label to new label, or None to build it from indexes
|
|
1507
|
+
max_label - highest label value in src_objects_segmented
|
|
1508
|
+
parent_objects - parent object number per object, or None if unrelated to a parent
|
|
1509
|
+
keep_unassociated_objects - whether to keep objects that have no parent
|
|
1510
|
+
"""
|
|
1511
|
+
if label_indexes is None:
|
|
1512
|
+
new_object_count = len(indexes)
|
|
1513
|
+
label_indexes = numpy.zeros((max_label + 1,), int)
|
|
1514
|
+
label_indexes[indexes] = numpy.arange(1, new_object_count + 1)
|
|
1515
|
+
|
|
1516
|
+
#
|
|
1517
|
+
# Reindex the labels of the old source image
|
|
1518
|
+
#
|
|
1519
|
+
target_objects_segmented = reindex_labels(src_objects_segmented, max_label, label_indexes, parent_objects, keep_unassociated_objects)
|
|
1520
|
+
|
|
1521
|
+
return target_objects_segmented
|
|
1522
|
+
|
|
1523
|
+
def reindex_labels(
|
|
1524
|
+
src_objects_segmented: ObjectSegmentation,
|
|
1525
|
+
max_label: int,
|
|
1526
|
+
label_indexes: NDArray[numpy.int_],
|
|
1527
|
+
parent_objects: Optional[NDArray[numpy.int_]],
|
|
1528
|
+
keep_unassociated_objects: bool,
|
|
1529
|
+
) -> ObjectSegmentation:
|
|
1530
|
+
"""
|
|
1531
|
+
Reindex a segmentation, dropping objects whose new label is 0
|
|
1532
|
+
|
|
1533
|
+
src_objects_segmented - segmentation to relabel
|
|
1534
|
+
max_label - highest label value of the filtered object
|
|
1535
|
+
label_indexes - mapping from old label to new label (0 removes the object)
|
|
1536
|
+
parent_objects - parent object number per object, or None if unrelated to a parent
|
|
1537
|
+
keep_unassociated_objects - whether to keep objects that have no parent
|
|
1538
|
+
"""
|
|
1539
|
+
target_labels = src_objects_segmented.copy()
|
|
1540
|
+
if parent_objects is None:
|
|
1541
|
+
target_labels[target_labels > max_label] = 0
|
|
1542
|
+
target_labels = label_indexes[target_labels]
|
|
1543
|
+
else:
|
|
1544
|
+
# Initialize target labels to keep all child objects
|
|
1545
|
+
target_label_numbers = numpy.arange(1, target_labels.max() + 1)
|
|
1546
|
+
|
|
1547
|
+
orphan_children = target_label_numbers[parent_objects == 0]
|
|
1548
|
+
|
|
1549
|
+
# label == 0 indicates parent object has to be removed
|
|
1550
|
+
objects_to_remove = numpy.arange(max_label+1)[label_indexes == 0][1:] # ignore the first zero as it is the background
|
|
1551
|
+
|
|
1552
|
+
# object is removed by setting its new label to zero
|
|
1553
|
+
target_label_numbers = target_label_numbers*~numpy.isin(parent_objects, objects_to_remove)
|
|
1554
|
+
|
|
1555
|
+
new_child_object_count = sum(target_label_numbers != 0)
|
|
1556
|
+
|
|
1557
|
+
# orphan children get new labels. Labels are always continuous and start at 1
|
|
1558
|
+
target_label_numbers[target_label_numbers != 0] = numpy.arange(1, new_child_object_count + 1)
|
|
1559
|
+
|
|
1560
|
+
# Add zero for background label
|
|
1561
|
+
target_label_numbers = numpy.pad(target_label_numbers, (1, 0))
|
|
1562
|
+
|
|
1563
|
+
# Overwrite orphan children new labels with 0 to remove unassociated objects
|
|
1564
|
+
if not keep_unassociated_objects:
|
|
1565
|
+
target_label_numbers[orphan_children] = 0
|
|
1566
|
+
|
|
1567
|
+
# Numpy fancy indexing to relabel
|
|
1568
|
+
target_labels = target_label_numbers[target_labels]
|
|
1569
|
+
|
|
1570
|
+
return target_labels
|
|
1571
|
+
|
|
1572
|
+
def get_removed_objects(
|
|
1573
|
+
indexes: Union[NDArray[numpy.int_], List[int]],
|
|
1574
|
+
max_label: int,
|
|
1575
|
+
src_objects_segmented: ObjectSegmentation,
|
|
1576
|
+
) -> ObjectSegmentation:
|
|
1577
|
+
"""
|
|
1578
|
+
Return a segmentation containing only the objects removed by the filter
|
|
1579
|
+
|
|
1580
|
+
indexes - object numbers (base 1) that were kept
|
|
1581
|
+
max_label - highest label value in src_objects_segmented
|
|
1582
|
+
src_objects_segmented - the original, unfiltered segmentation
|
|
1583
|
+
"""
|
|
1584
|
+
removed_labels = src_objects_segmented.copy()
|
|
1585
|
+
# Isolate objects removed by the filter
|
|
1586
|
+
removed_indexes = [x for x in range(1, max_label+1) if x not in indexes]
|
|
1587
|
+
removed_object_count = len(removed_indexes)
|
|
1588
|
+
removed_label_indexes = numpy.zeros((max_label + 1,), int)
|
|
1589
|
+
removed_label_indexes[removed_indexes] = numpy.arange(1, removed_object_count + 1)
|
|
1590
|
+
|
|
1591
|
+
#
|
|
1592
|
+
# Reindex the labels of the old source image
|
|
1593
|
+
#
|
|
1594
|
+
removed_labels[removed_labels > max_label] = 0
|
|
1595
|
+
removed_labels = removed_label_indexes[removed_labels]
|
|
1596
|
+
|
|
1597
|
+
return removed_labels
|