cellprofiler-library-nightly 5.0.0.dev650__tar.gz → 5.0.0.dev661__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.dev661}/PKG-INFO +1 -1
- cellprofiler_library_nightly-5.0.0.dev661/cellprofiler_library/_version.py +24 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev661}/cellprofiler_library/functions/image_processing.py +9 -9
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev661}/cellprofiler_library/functions/object_processing.py +346 -1
- cellprofiler_library_nightly-5.0.0.dev661/cellprofiler_library/modules/_filterobjects.py +88 -0
- cellprofiler_library_nightly-5.0.0.dev661/cellprofiler_library/opts/filterobjects.py +38 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev661/cellprofiler_library_nightly.egg-info}/PKG-INFO +1 -1
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev661}/cellprofiler_library_nightly.egg-info/SOURCES.txt +4 -0
- cellprofiler_library_nightly-5.0.0.dev661/cellprofiler_library_nightly.egg-info/scm_file_list.json +116 -0
- cellprofiler_library_nightly-5.0.0.dev661/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.dev661}/LICENSE +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev661}/README.md +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev661}/cellprofiler_library/__init__.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev661}/cellprofiler_library/functions/__init__.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev661}/cellprofiler_library/functions/file_processing.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev661}/cellprofiler_library/functions/measurement.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev661}/cellprofiler_library/functions/segmentation.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev661}/cellprofiler_library/measurement_model.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev661}/cellprofiler_library/modules/__init__.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev661}/cellprofiler_library/modules/_align.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev661}/cellprofiler_library/modules/_closing.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev661}/cellprofiler_library/modules/_colortogray.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev661}/cellprofiler_library/modules/_combineobjects.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev661}/cellprofiler_library/modules/_convertimagetoobjects.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev661}/cellprofiler_library/modules/_convertobjectstoimage.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev661}/cellprofiler_library/modules/_correctilluminationapply.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev661}/cellprofiler_library/modules/_crop.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev661}/cellprofiler_library/modules/_dilateimage.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev661}/cellprofiler_library/modules/_dilateobjects.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev661}/cellprofiler_library/modules/_enhanceedges.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev661}/cellprofiler_library/modules/_enhanceorsuppressfeatures.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev661}/cellprofiler_library/modules/_erodeimage.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev661}/cellprofiler_library/modules/_erodeobjects.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev661}/cellprofiler_library/modules/_expandorshrinkobjects.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev661}/cellprofiler_library/modules/_fillobjects.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev661}/cellprofiler_library/modules/_flipandrotate.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev661}/cellprofiler_library/modules/_gaussianfilter.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev661}/cellprofiler_library/modules/_graytocolor.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev661}/cellprofiler_library/modules/_identifydeadworms.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev661}/cellprofiler_library/modules/_identifyprimaryobjects.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev661}/cellprofiler_library/modules/_identifysecondaryobjects.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev661}/cellprofiler_library/modules/_identifytertiaryobjects.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev661}/cellprofiler_library/modules/_imagemath.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev661}/cellprofiler_library/modules/_measurecolocalization.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev661}/cellprofiler_library/modules/_measuregranularity.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev661}/cellprofiler_library/modules/_measureimageareaoccupied.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev661}/cellprofiler_library/modules/_measureimageintensity.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev661}/cellprofiler_library/modules/_measureimageoverlap.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev661}/cellprofiler_library/modules/_measureimagequality.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev661}/cellprofiler_library/modules/_measureimageskeleton.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev661}/cellprofiler_library/modules/_measureobjectintensity.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev661}/cellprofiler_library/modules/_measureobjectintensitydistribution.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev661}/cellprofiler_library/modules/_measureobjectneighbors.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev661}/cellprofiler_library/modules/_measureobjectoverlap.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev661}/cellprofiler_library/modules/_measureobjectsizeshape.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev661}/cellprofiler_library/modules/_measureobjectskeleton.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev661}/cellprofiler_library/modules/_measuretexture.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev661}/cellprofiler_library/modules/_medialaxis.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev661}/cellprofiler_library/modules/_medianfilter.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev661}/cellprofiler_library/modules/_morph.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev661}/cellprofiler_library/modules/_morphologicalskeleton.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev661}/cellprofiler_library/modules/_opening.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev661}/cellprofiler_library/modules/_overlayobjects.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev661}/cellprofiler_library/modules/_overlayoutlines.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev661}/cellprofiler_library/modules/_reducenoise.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev661}/cellprofiler_library/modules/_removeholes.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev661}/cellprofiler_library/modules/_resize.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev661}/cellprofiler_library/modules/_savecroppedobjects.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev661}/cellprofiler_library/modules/_shrinktoobjectcenters.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev661}/cellprofiler_library/modules/_smooth.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev661}/cellprofiler_library/modules/_threshold.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev661}/cellprofiler_library/modules/_watershed.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev661}/cellprofiler_library/opts/__init__.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev661}/cellprofiler_library/opts/align.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev661}/cellprofiler_library/opts/colortogray.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev661}/cellprofiler_library/opts/convertimagetoobjects.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev661}/cellprofiler_library/opts/convertobjectstoimage.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev661}/cellprofiler_library/opts/correctilluminationapply.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev661}/cellprofiler_library/opts/crop.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev661}/cellprofiler_library/opts/dilateimage.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev661}/cellprofiler_library/opts/dilateobjects.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev661}/cellprofiler_library/opts/enhanceedges.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev661}/cellprofiler_library/opts/enhanceorsuppressfeatures.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev661}/cellprofiler_library/opts/erodeimage.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev661}/cellprofiler_library/opts/erodeobjects.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev661}/cellprofiler_library/opts/flipandrotate.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev661}/cellprofiler_library/opts/graytocolor.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev661}/cellprofiler_library/opts/identifydeadworms.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev661}/cellprofiler_library/opts/identifyprimaryobjects.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev661}/cellprofiler_library/opts/identifysecondaryobjects.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev661}/cellprofiler_library/opts/identifytertiaryobjects.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev661}/cellprofiler_library/opts/imagemath.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev661}/cellprofiler_library/opts/measurecolocalization.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev661}/cellprofiler_library/opts/measuregranularity.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev661}/cellprofiler_library/opts/measureimageareaoccupied.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev661}/cellprofiler_library/opts/measureimageintensity.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev661}/cellprofiler_library/opts/measureimageoverlap.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev661}/cellprofiler_library/opts/measureimagequality.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev661}/cellprofiler_library/opts/measureimageskeleton.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev661}/cellprofiler_library/opts/measureobjectintensity.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev661}/cellprofiler_library/opts/measureobjectintensitydistribution.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev661}/cellprofiler_library/opts/measureobjectneighbors.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev661}/cellprofiler_library/opts/measureobjectoverlap.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev661}/cellprofiler_library/opts/measureobjectskeleton.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev661}/cellprofiler_library/opts/measuretexture.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev661}/cellprofiler_library/opts/morph.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev661}/cellprofiler_library/opts/objectsizeshapefeatures.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev661}/cellprofiler_library/opts/overlayoutlines.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev661}/cellprofiler_library/opts/removeholes.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev661}/cellprofiler_library/opts/resize.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev661}/cellprofiler_library/opts/shrinktoobjectcenters.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev661}/cellprofiler_library/opts/smooth.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev661}/cellprofiler_library/opts/structuring_elements.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev661}/cellprofiler_library/opts/threshold.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev661}/cellprofiler_library/py.typed +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev661}/cellprofiler_library/types.py +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev661}/cellprofiler_library_nightly.egg-info/dependency_links.txt +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev661}/cellprofiler_library_nightly.egg-info/requires.txt +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev661}/cellprofiler_library_nightly.egg-info/top_level.txt +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev661}/environment.yml +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev661}/pyproject.toml +0 -0
- {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev661}/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.dev661
|
|
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.dev661'
|
|
22
|
+
__version_tuple__ = version_tuple = (5, 0, 0, 'dev661')
|
|
23
|
+
|
|
24
|
+
__commit_id__ = commit_id = 'g222155826'
|
|
@@ -1769,21 +1769,21 @@ def fill_holes(image: ImageAny, diameter: float) -> ImageAny:
|
|
|
1769
1769
|
|
|
1770
1770
|
def imagemath_apply_on_image(
|
|
1771
1771
|
output_pixel_data: ImageAny,
|
|
1772
|
-
pd: ImageAny,
|
|
1773
|
-
|
|
1774
|
-
op: Callable[[ImageAny, ImageAny], ImageAny],
|
|
1772
|
+
pd: ImageAny,
|
|
1773
|
+
comparator: ImageAny,
|
|
1774
|
+
op: Callable[[ImageAny, ImageAny], ImageAny],
|
|
1775
1775
|
opval: Operator,
|
|
1776
1776
|
) -> ImageAny:
|
|
1777
1777
|
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
1778
|
if not isscalar(pd) and output_pixel_data.ndim != pd.ndim:
|
|
1779
1779
|
if output_pixel_data.ndim == 2:
|
|
1780
1780
|
output_pixel_data = output_pixel_data[:, :, numpy.newaxis]
|
|
1781
|
-
if opval == Operator.EQUALS and not isscalar(
|
|
1782
|
-
|
|
1781
|
+
if opval == Operator.EQUALS and not isscalar(comparator):
|
|
1782
|
+
comparator = comparator[:, :, numpy.newaxis]
|
|
1783
1783
|
if pd.ndim == 2:
|
|
1784
1784
|
pd = pd[:, :, numpy.newaxis]
|
|
1785
1785
|
if opval == Operator.EQUALS:
|
|
1786
|
-
output_pixel_data = output_pixel_data & (
|
|
1786
|
+
output_pixel_data = output_pixel_data & (comparator == pd)
|
|
1787
1787
|
else:
|
|
1788
1788
|
output_pixel_data = op(output_pixel_data, pd)
|
|
1789
1789
|
return output_pixel_data
|
|
@@ -1818,7 +1818,7 @@ def imagemath_apply_binary_operation(
|
|
|
1818
1818
|
output_pixel_data[pd] = False
|
|
1819
1819
|
return output_pixel_data
|
|
1820
1820
|
|
|
1821
|
-
|
|
1821
|
+
comparator = operands[0] # fix pylance error
|
|
1822
1822
|
use_logical = use_logical_operation(operands)
|
|
1823
1823
|
op_fn_dispatch: Dict[Operator, Callable[[ImageAny, ImageAny], ImageAny]] = {
|
|
1824
1824
|
Operator.ADD: numpy.add,
|
|
@@ -1847,7 +1847,7 @@ def imagemath_apply_binary_operation(
|
|
|
1847
1847
|
#
|
|
1848
1848
|
if opval == Operator.EQUALS:
|
|
1849
1849
|
output_pixel_data = numpy.ones(operands[0].shape, bool)
|
|
1850
|
-
|
|
1850
|
+
comparator = operands[0]
|
|
1851
1851
|
elif opval == Operator.SUBTRACT and use_logical:
|
|
1852
1852
|
output_pixel_data = operands[0].copy()
|
|
1853
1853
|
|
|
@@ -1860,7 +1860,7 @@ def imagemath_apply_binary_operation(
|
|
|
1860
1860
|
# Apply the operation to each image in the list
|
|
1861
1861
|
#
|
|
1862
1862
|
for pd, mask in zip(operands[1:], masks[1:]):
|
|
1863
|
-
output_pixel_data = imagemath_apply_on_image(output_pixel_data, pd,
|
|
1863
|
+
output_pixel_data = imagemath_apply_on_image(output_pixel_data, pd, comparator, op, opval)
|
|
1864
1864
|
if not ignore_mask:
|
|
1865
1865
|
if output_mask is None:
|
|
1866
1866
|
output_mask = mask
|
|
@@ -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
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import numpy
|
|
2
|
+
from typing import Annotated, Any, Dict, List, Optional, Tuple
|
|
3
|
+
|
|
4
|
+
from numpy.typing import NDArray
|
|
5
|
+
from pydantic import ConfigDict, Field, validate_call
|
|
6
|
+
|
|
7
|
+
from cellprofiler_library.opts.filterobjects import FilterMethod, FilterMode, OverlapAssignment
|
|
8
|
+
from cellprofiler_library.types import ObjectSegmentation
|
|
9
|
+
from cellprofiler_library.functions.object_processing import (
|
|
10
|
+
keep_one,
|
|
11
|
+
keep_per_object,
|
|
12
|
+
keep_within_limits,
|
|
13
|
+
discard_border_objects,
|
|
14
|
+
keep_by_rules,
|
|
15
|
+
keep_by_hits,
|
|
16
|
+
get_filtered_object,
|
|
17
|
+
get_removed_objects
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@validate_call(config=ConfigDict(arbitrary_types_allowed=True))
|
|
22
|
+
def filter_objects(
|
|
23
|
+
src_labels: Annotated[ObjectSegmentation, Field(description="Segmentation of the object being filtered")],
|
|
24
|
+
mode: Annotated[FilterMode, Field(description="Which filtering mode is active")],
|
|
25
|
+
keep_removed_objects: Annotated[bool, Field(description="Also compute and return the objects removed by the filter")] = False,
|
|
26
|
+
additional_objects: Annotated[
|
|
27
|
+
List[Tuple[ObjectSegmentation, Optional[NDArray[numpy.int_]], bool]],
|
|
28
|
+
Field(description="Additional objects to relabel to match the filtered object, as (labels, parent_objects, keep_unassociated_objects) tuples"),
|
|
29
|
+
] = [],
|
|
30
|
+
|
|
31
|
+
# mode == Measurements
|
|
32
|
+
filter_choice: Annotated[Optional[FilterMethod], Field(description="Used only if mode is Measurements: which measurement-filtering method")] = None,
|
|
33
|
+
values: Annotated[Optional[NDArray[numpy.float64]], Field(description="Used only for Minimal/Maximal or per-object filtering: measurement value per object")] = None,
|
|
34
|
+
limit_groups: Annotated[Optional[List[Dict[str, Any]]], Field(description="Used only for Limits filtering: list of {'values', 'min_limit', 'max_limit'}")] = None,
|
|
35
|
+
enclosing_labels: Annotated[Optional[ObjectSegmentation], Field(description="Used only for per-object filtering: enclosing/parent object segmentation")] = None,
|
|
36
|
+
enclosing_count: Annotated[Optional[int], Field(description="Used only for per-object filtering: number of enclosing objects")] = None,
|
|
37
|
+
per_object_assignment: Annotated[Optional[OverlapAssignment], Field(description="Used only for per-object filtering")] = None,
|
|
38
|
+
|
|
39
|
+
# mode == Border
|
|
40
|
+
parent_image_mask: Annotated[Optional[NDArray[numpy.bool_]], Field(description="Used only if mode is Border: parent image mask")] = None,
|
|
41
|
+
|
|
42
|
+
# mode in (Rules, Classifiers)
|
|
43
|
+
scores: Annotated[Optional[NDArray[numpy.float64]], Field(description="Used only for rules-based filtering: per-object x per-class scores from Rules.score()")] = None,
|
|
44
|
+
rules_class: Annotated[Optional[int], Field(description="Used only for rules-based filtering: 0-based class index to keep")] = None,
|
|
45
|
+
hits: Annotated[Optional[NDArray[numpy.bool_]], Field(description="Used only for classifier-prediction filtering: precomputed pass/fail per object")] = None,
|
|
46
|
+
) -> Tuple[ObjectSegmentation, List[ObjectSegmentation], Optional[ObjectSegmentation]]:
|
|
47
|
+
max_label = int(numpy.max(src_labels))
|
|
48
|
+
|
|
49
|
+
if mode == FilterMode.MEASUREMENTS.value:
|
|
50
|
+
if filter_choice in (FilterMethod.MINIMAL.value, FilterMethod.MAXIMAL.value):
|
|
51
|
+
indexes = keep_one(values, filter_choice)
|
|
52
|
+
elif filter_choice in (FilterMethod.MINIMAL_PER_OBJECT.value, FilterMethod.MAXIMAL_PER_OBJECT.value):
|
|
53
|
+
indexes = keep_per_object(
|
|
54
|
+
src_labels, enclosing_labels, enclosing_count, per_object_assignment, filter_choice, values,
|
|
55
|
+
)
|
|
56
|
+
elif filter_choice == FilterMethod.LIMITS.value:
|
|
57
|
+
indexes = keep_within_limits(limit_groups)
|
|
58
|
+
else:
|
|
59
|
+
raise ValueError(f"Unknown filter choice: {filter_choice} for mode {mode}")
|
|
60
|
+
elif mode == FilterMode.BORDER.value:
|
|
61
|
+
indexes = discard_border_objects(src_labels, parent_image_mask)
|
|
62
|
+
# keep_by_class
|
|
63
|
+
elif mode in (FilterMode.RULES.value, FilterMode.CLASSIFIERS.value):
|
|
64
|
+
if scores is not None:
|
|
65
|
+
indexes = keep_by_rules(scores, rules_class)
|
|
66
|
+
elif hits is not None:
|
|
67
|
+
indexes = keep_by_hits(hits)
|
|
68
|
+
else:
|
|
69
|
+
raise ValueError(f"mode {mode} requires either 'scores' or 'hits'")
|
|
70
|
+
else:
|
|
71
|
+
raise ValueError(f"Unknown filter mode: {mode}")
|
|
72
|
+
|
|
73
|
+
new_object_count = len(indexes)
|
|
74
|
+
label_indexes = numpy.zeros((max_label + 1,), int)
|
|
75
|
+
label_indexes[indexes] = numpy.arange(1, new_object_count + 1)
|
|
76
|
+
|
|
77
|
+
target_segmented = get_filtered_object(src_labels, indexes, label_indexes, max_label, None, False)
|
|
78
|
+
|
|
79
|
+
additional_segmented = [
|
|
80
|
+
get_filtered_object(labels, indexes, label_indexes, max_label, parent_objects, keep_unassociated_objects)
|
|
81
|
+
for labels, parent_objects, keep_unassociated_objects in additional_objects
|
|
82
|
+
]
|
|
83
|
+
|
|
84
|
+
removed_segmented = (
|
|
85
|
+
get_removed_objects(indexes, max_label, src_labels) if keep_removed_objects else None
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
return target_segmented, additional_segmented, removed_segmented
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
from enum import Enum
|
|
2
|
+
|
|
3
|
+
class FilterMethod(str, Enum):
|
|
4
|
+
"""Minimal filter - pick a single object per image by minimum measured value"""
|
|
5
|
+
MINIMAL = "Minimal"
|
|
6
|
+
|
|
7
|
+
"""Maximal filter - pick a single object per image by maximum measured value"""
|
|
8
|
+
MAXIMAL = "Maximal"
|
|
9
|
+
|
|
10
|
+
"""Pick one object per containing object by minimum measured value"""
|
|
11
|
+
MINIMAL_PER_OBJECT = "Minimal per object"
|
|
12
|
+
|
|
13
|
+
"""Pick one object per containing object by maximum measured value"""
|
|
14
|
+
MAXIMAL_PER_OBJECT = "Maximal per object"
|
|
15
|
+
|
|
16
|
+
"""Keep all objects whose values fall between set limits"""
|
|
17
|
+
LIMITS = "Limits"
|
|
18
|
+
|
|
19
|
+
FI_ALL = [
|
|
20
|
+
FilterMethod.MINIMAL,
|
|
21
|
+
FilterMethod.MAXIMAL,
|
|
22
|
+
FilterMethod.MINIMAL_PER_OBJECT,
|
|
23
|
+
FilterMethod.MAXIMAL_PER_OBJECT,
|
|
24
|
+
FilterMethod.LIMITS,
|
|
25
|
+
]
|
|
26
|
+
|
|
27
|
+
class FilterMode(str, Enum):
|
|
28
|
+
RULES = "Rules"
|
|
29
|
+
CLASSIFIERS = "Classifiers"
|
|
30
|
+
MEASUREMENTS = "Measurements"
|
|
31
|
+
BORDER = "Image or mask border"
|
|
32
|
+
|
|
33
|
+
DIR_CUSTOM = "Custom folder"
|
|
34
|
+
|
|
35
|
+
class OverlapAssignment(str, Enum):
|
|
36
|
+
BOTH = "Both parents"
|
|
37
|
+
PARENT_WITH_MOST_OVERLAP = "Parent with most overlap"
|
|
38
|
+
PO_ALL = [OverlapAssignment.BOTH, OverlapAssignment.PARENT_WITH_MOST_OVERLAP]
|
|
@@ -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.dev661
|
|
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>
|
|
@@ -30,6 +30,7 @@ cellprofiler_library/modules/_erodeimage.py
|
|
|
30
30
|
cellprofiler_library/modules/_erodeobjects.py
|
|
31
31
|
cellprofiler_library/modules/_expandorshrinkobjects.py
|
|
32
32
|
cellprofiler_library/modules/_fillobjects.py
|
|
33
|
+
cellprofiler_library/modules/_filterobjects.py
|
|
33
34
|
cellprofiler_library/modules/_flipandrotate.py
|
|
34
35
|
cellprofiler_library/modules/_gaussianfilter.py
|
|
35
36
|
cellprofiler_library/modules/_graytocolor.py
|
|
@@ -80,6 +81,7 @@ cellprofiler_library/opts/enhanceedges.py
|
|
|
80
81
|
cellprofiler_library/opts/enhanceorsuppressfeatures.py
|
|
81
82
|
cellprofiler_library/opts/erodeimage.py
|
|
82
83
|
cellprofiler_library/opts/erodeobjects.py
|
|
84
|
+
cellprofiler_library/opts/filterobjects.py
|
|
83
85
|
cellprofiler_library/opts/flipandrotate.py
|
|
84
86
|
cellprofiler_library/opts/graytocolor.py
|
|
85
87
|
cellprofiler_library/opts/identifydeadworms.py
|
|
@@ -113,4 +115,6 @@ cellprofiler_library_nightly.egg-info/PKG-INFO
|
|
|
113
115
|
cellprofiler_library_nightly.egg-info/SOURCES.txt
|
|
114
116
|
cellprofiler_library_nightly.egg-info/dependency_links.txt
|
|
115
117
|
cellprofiler_library_nightly.egg-info/requires.txt
|
|
118
|
+
cellprofiler_library_nightly.egg-info/scm_file_list.json
|
|
119
|
+
cellprofiler_library_nightly.egg-info/scm_version.json
|
|
116
120
|
cellprofiler_library_nightly.egg-info/top_level.txt
|