cellprofiler-library-nightly 5.0.0.dev454__tar.gz → 5.0.0.dev466__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.
Files changed (84) hide show
  1. {cellprofiler_library_nightly-5.0.0.dev454 → cellprofiler_library_nightly-5.0.0.dev466}/PKG-INFO +1 -1
  2. {cellprofiler_library_nightly-5.0.0.dev454 → cellprofiler_library_nightly-5.0.0.dev466}/cellprofiler_library/_version.py +3 -3
  3. {cellprofiler_library_nightly-5.0.0.dev454 → cellprofiler_library_nightly-5.0.0.dev466}/cellprofiler_library/functions/image_processing.py +240 -3
  4. cellprofiler_library_nightly-5.0.0.dev466/cellprofiler_library/modules/_graytocolor.py +58 -0
  5. cellprofiler_library_nightly-5.0.0.dev466/cellprofiler_library/modules/_imagemath.py +64 -0
  6. cellprofiler_library_nightly-5.0.0.dev466/cellprofiler_library/opts/graytocolor.py +20 -0
  7. cellprofiler_library_nightly-5.0.0.dev466/cellprofiler_library/opts/imagemath.py +30 -0
  8. {cellprofiler_library_nightly-5.0.0.dev454 → cellprofiler_library_nightly-5.0.0.dev466}/cellprofiler_library_nightly.egg-info/PKG-INFO +1 -1
  9. {cellprofiler_library_nightly-5.0.0.dev454 → cellprofiler_library_nightly-5.0.0.dev466}/cellprofiler_library_nightly.egg-info/SOURCES.txt +4 -0
  10. {cellprofiler_library_nightly-5.0.0.dev454 → cellprofiler_library_nightly-5.0.0.dev466}/LICENSE +0 -0
  11. {cellprofiler_library_nightly-5.0.0.dev454 → cellprofiler_library_nightly-5.0.0.dev466}/README.md +0 -0
  12. {cellprofiler_library_nightly-5.0.0.dev454 → cellprofiler_library_nightly-5.0.0.dev466}/cellprofiler_library/__init__.py +0 -0
  13. {cellprofiler_library_nightly-5.0.0.dev454 → cellprofiler_library_nightly-5.0.0.dev466}/cellprofiler_library/functions/__init__.py +0 -0
  14. {cellprofiler_library_nightly-5.0.0.dev454 → cellprofiler_library_nightly-5.0.0.dev466}/cellprofiler_library/functions/file_processing.py +0 -0
  15. {cellprofiler_library_nightly-5.0.0.dev454 → cellprofiler_library_nightly-5.0.0.dev466}/cellprofiler_library/functions/measurement.py +0 -0
  16. {cellprofiler_library_nightly-5.0.0.dev454 → cellprofiler_library_nightly-5.0.0.dev466}/cellprofiler_library/functions/object_processing.py +0 -0
  17. {cellprofiler_library_nightly-5.0.0.dev454 → cellprofiler_library_nightly-5.0.0.dev466}/cellprofiler_library/functions/segmentation.py +0 -0
  18. {cellprofiler_library_nightly-5.0.0.dev454 → cellprofiler_library_nightly-5.0.0.dev466}/cellprofiler_library/modules/__init__.py +0 -0
  19. {cellprofiler_library_nightly-5.0.0.dev454 → cellprofiler_library_nightly-5.0.0.dev466}/cellprofiler_library/modules/_closing.py +0 -0
  20. {cellprofiler_library_nightly-5.0.0.dev454 → cellprofiler_library_nightly-5.0.0.dev466}/cellprofiler_library/modules/_colortogray.py +0 -0
  21. {cellprofiler_library_nightly-5.0.0.dev454 → cellprofiler_library_nightly-5.0.0.dev466}/cellprofiler_library/modules/_combineobjects.py +0 -0
  22. {cellprofiler_library_nightly-5.0.0.dev454 → cellprofiler_library_nightly-5.0.0.dev466}/cellprofiler_library/modules/_convertimagetoobjects.py +0 -0
  23. {cellprofiler_library_nightly-5.0.0.dev454 → cellprofiler_library_nightly-5.0.0.dev466}/cellprofiler_library/modules/_convertobjectstoimage.py +0 -0
  24. {cellprofiler_library_nightly-5.0.0.dev454 → cellprofiler_library_nightly-5.0.0.dev466}/cellprofiler_library/modules/_correctilluminationapply.py +0 -0
  25. {cellprofiler_library_nightly-5.0.0.dev454 → cellprofiler_library_nightly-5.0.0.dev466}/cellprofiler_library/modules/_crop.py +0 -0
  26. {cellprofiler_library_nightly-5.0.0.dev454 → cellprofiler_library_nightly-5.0.0.dev466}/cellprofiler_library/modules/_dilateimage.py +0 -0
  27. {cellprofiler_library_nightly-5.0.0.dev454 → cellprofiler_library_nightly-5.0.0.dev466}/cellprofiler_library/modules/_dilateobjects.py +0 -0
  28. {cellprofiler_library_nightly-5.0.0.dev454 → cellprofiler_library_nightly-5.0.0.dev466}/cellprofiler_library/modules/_enhanceedges.py +0 -0
  29. {cellprofiler_library_nightly-5.0.0.dev454 → cellprofiler_library_nightly-5.0.0.dev466}/cellprofiler_library/modules/_enhanceorsuppressfeatures.py +0 -0
  30. {cellprofiler_library_nightly-5.0.0.dev454 → cellprofiler_library_nightly-5.0.0.dev466}/cellprofiler_library/modules/_erodeimage.py +0 -0
  31. {cellprofiler_library_nightly-5.0.0.dev454 → cellprofiler_library_nightly-5.0.0.dev466}/cellprofiler_library/modules/_erodeobjects.py +0 -0
  32. {cellprofiler_library_nightly-5.0.0.dev454 → cellprofiler_library_nightly-5.0.0.dev466}/cellprofiler_library/modules/_expandorshrinkobjects.py +0 -0
  33. {cellprofiler_library_nightly-5.0.0.dev454 → cellprofiler_library_nightly-5.0.0.dev466}/cellprofiler_library/modules/_fillobjects.py +0 -0
  34. {cellprofiler_library_nightly-5.0.0.dev454 → cellprofiler_library_nightly-5.0.0.dev466}/cellprofiler_library/modules/_gaussianfilter.py +0 -0
  35. {cellprofiler_library_nightly-5.0.0.dev454 → cellprofiler_library_nightly-5.0.0.dev466}/cellprofiler_library/modules/_identifyprimaryobjects.py +0 -0
  36. {cellprofiler_library_nightly-5.0.0.dev454 → cellprofiler_library_nightly-5.0.0.dev466}/cellprofiler_library/modules/_identifysecondaryobjects.py +0 -0
  37. {cellprofiler_library_nightly-5.0.0.dev454 → cellprofiler_library_nightly-5.0.0.dev466}/cellprofiler_library/modules/_identifytertiaryobjects.py +0 -0
  38. {cellprofiler_library_nightly-5.0.0.dev454 → cellprofiler_library_nightly-5.0.0.dev466}/cellprofiler_library/modules/_measureimageoverlap.py +0 -0
  39. {cellprofiler_library_nightly-5.0.0.dev454 → cellprofiler_library_nightly-5.0.0.dev466}/cellprofiler_library/modules/_measureobjectsizeshape.py +0 -0
  40. {cellprofiler_library_nightly-5.0.0.dev454 → cellprofiler_library_nightly-5.0.0.dev466}/cellprofiler_library/modules/_medialaxis.py +0 -0
  41. {cellprofiler_library_nightly-5.0.0.dev454 → cellprofiler_library_nightly-5.0.0.dev466}/cellprofiler_library/modules/_medianfilter.py +0 -0
  42. {cellprofiler_library_nightly-5.0.0.dev454 → cellprofiler_library_nightly-5.0.0.dev466}/cellprofiler_library/modules/_morph.py +0 -0
  43. {cellprofiler_library_nightly-5.0.0.dev454 → cellprofiler_library_nightly-5.0.0.dev466}/cellprofiler_library/modules/_morphologicalskeleton.py +0 -0
  44. {cellprofiler_library_nightly-5.0.0.dev454 → cellprofiler_library_nightly-5.0.0.dev466}/cellprofiler_library/modules/_opening.py +0 -0
  45. {cellprofiler_library_nightly-5.0.0.dev454 → cellprofiler_library_nightly-5.0.0.dev466}/cellprofiler_library/modules/_overlayobjects.py +0 -0
  46. {cellprofiler_library_nightly-5.0.0.dev454 → cellprofiler_library_nightly-5.0.0.dev466}/cellprofiler_library/modules/_overlayoutlines.py +0 -0
  47. {cellprofiler_library_nightly-5.0.0.dev454 → cellprofiler_library_nightly-5.0.0.dev466}/cellprofiler_library/modules/_reducenoise.py +0 -0
  48. {cellprofiler_library_nightly-5.0.0.dev454 → cellprofiler_library_nightly-5.0.0.dev466}/cellprofiler_library/modules/_removeholes.py +0 -0
  49. {cellprofiler_library_nightly-5.0.0.dev454 → cellprofiler_library_nightly-5.0.0.dev466}/cellprofiler_library/modules/_resize.py +0 -0
  50. {cellprofiler_library_nightly-5.0.0.dev454 → cellprofiler_library_nightly-5.0.0.dev466}/cellprofiler_library/modules/_savecroppedobjects.py +0 -0
  51. {cellprofiler_library_nightly-5.0.0.dev454 → cellprofiler_library_nightly-5.0.0.dev466}/cellprofiler_library/modules/_shrinktoobjectcenters.py +0 -0
  52. {cellprofiler_library_nightly-5.0.0.dev454 → cellprofiler_library_nightly-5.0.0.dev466}/cellprofiler_library/modules/_threshold.py +0 -0
  53. {cellprofiler_library_nightly-5.0.0.dev454 → cellprofiler_library_nightly-5.0.0.dev466}/cellprofiler_library/modules/_watershed.py +0 -0
  54. {cellprofiler_library_nightly-5.0.0.dev454 → cellprofiler_library_nightly-5.0.0.dev466}/cellprofiler_library/opts/__init__.py +0 -0
  55. {cellprofiler_library_nightly-5.0.0.dev454 → cellprofiler_library_nightly-5.0.0.dev466}/cellprofiler_library/opts/colortogray.py +0 -0
  56. {cellprofiler_library_nightly-5.0.0.dev454 → cellprofiler_library_nightly-5.0.0.dev466}/cellprofiler_library/opts/convertimagetoobjects.py +0 -0
  57. {cellprofiler_library_nightly-5.0.0.dev454 → cellprofiler_library_nightly-5.0.0.dev466}/cellprofiler_library/opts/convertobjectstoimage.py +0 -0
  58. {cellprofiler_library_nightly-5.0.0.dev454 → cellprofiler_library_nightly-5.0.0.dev466}/cellprofiler_library/opts/correctilluminationapply.py +0 -0
  59. {cellprofiler_library_nightly-5.0.0.dev454 → cellprofiler_library_nightly-5.0.0.dev466}/cellprofiler_library/opts/crop.py +0 -0
  60. {cellprofiler_library_nightly-5.0.0.dev454 → cellprofiler_library_nightly-5.0.0.dev466}/cellprofiler_library/opts/dilateimage.py +0 -0
  61. {cellprofiler_library_nightly-5.0.0.dev454 → cellprofiler_library_nightly-5.0.0.dev466}/cellprofiler_library/opts/dilateobjects.py +0 -0
  62. {cellprofiler_library_nightly-5.0.0.dev454 → cellprofiler_library_nightly-5.0.0.dev466}/cellprofiler_library/opts/enhanceorsuppressfeatures.py +0 -0
  63. {cellprofiler_library_nightly-5.0.0.dev454 → cellprofiler_library_nightly-5.0.0.dev466}/cellprofiler_library/opts/erodeimage.py +0 -0
  64. {cellprofiler_library_nightly-5.0.0.dev454 → cellprofiler_library_nightly-5.0.0.dev466}/cellprofiler_library/opts/erodeobjects.py +0 -0
  65. {cellprofiler_library_nightly-5.0.0.dev454 → cellprofiler_library_nightly-5.0.0.dev466}/cellprofiler_library/opts/identifyprimaryobjects.py +0 -0
  66. {cellprofiler_library_nightly-5.0.0.dev454 → cellprofiler_library_nightly-5.0.0.dev466}/cellprofiler_library/opts/identifysecondaryobjects.py +0 -0
  67. {cellprofiler_library_nightly-5.0.0.dev454 → cellprofiler_library_nightly-5.0.0.dev466}/cellprofiler_library/opts/identifytertiaryobjects.py +0 -0
  68. {cellprofiler_library_nightly-5.0.0.dev454 → cellprofiler_library_nightly-5.0.0.dev466}/cellprofiler_library/opts/measureimageoverlap.py +0 -0
  69. {cellprofiler_library_nightly-5.0.0.dev454 → cellprofiler_library_nightly-5.0.0.dev466}/cellprofiler_library/opts/morph.py +0 -0
  70. {cellprofiler_library_nightly-5.0.0.dev454 → cellprofiler_library_nightly-5.0.0.dev466}/cellprofiler_library/opts/objectsizeshapefeatures.py +0 -0
  71. {cellprofiler_library_nightly-5.0.0.dev454 → cellprofiler_library_nightly-5.0.0.dev466}/cellprofiler_library/opts/overlayoutlines.py +0 -0
  72. {cellprofiler_library_nightly-5.0.0.dev454 → cellprofiler_library_nightly-5.0.0.dev466}/cellprofiler_library/opts/removeholes.py +0 -0
  73. {cellprofiler_library_nightly-5.0.0.dev454 → cellprofiler_library_nightly-5.0.0.dev466}/cellprofiler_library/opts/resize.py +0 -0
  74. {cellprofiler_library_nightly-5.0.0.dev454 → cellprofiler_library_nightly-5.0.0.dev466}/cellprofiler_library/opts/shrinktoobjectcenters.py +0 -0
  75. {cellprofiler_library_nightly-5.0.0.dev454 → cellprofiler_library_nightly-5.0.0.dev466}/cellprofiler_library/opts/structuring_elements.py +0 -0
  76. {cellprofiler_library_nightly-5.0.0.dev454 → cellprofiler_library_nightly-5.0.0.dev466}/cellprofiler_library/opts/threshold.py +0 -0
  77. {cellprofiler_library_nightly-5.0.0.dev454 → cellprofiler_library_nightly-5.0.0.dev466}/cellprofiler_library/py.typed +0 -0
  78. {cellprofiler_library_nightly-5.0.0.dev454 → cellprofiler_library_nightly-5.0.0.dev466}/cellprofiler_library/types.py +0 -0
  79. {cellprofiler_library_nightly-5.0.0.dev454 → cellprofiler_library_nightly-5.0.0.dev466}/cellprofiler_library_nightly.egg-info/dependency_links.txt +0 -0
  80. {cellprofiler_library_nightly-5.0.0.dev454 → cellprofiler_library_nightly-5.0.0.dev466}/cellprofiler_library_nightly.egg-info/requires.txt +0 -0
  81. {cellprofiler_library_nightly-5.0.0.dev454 → cellprofiler_library_nightly-5.0.0.dev466}/cellprofiler_library_nightly.egg-info/top_level.txt +0 -0
  82. {cellprofiler_library_nightly-5.0.0.dev454 → cellprofiler_library_nightly-5.0.0.dev466}/environment.yml +0 -0
  83. {cellprofiler_library_nightly-5.0.0.dev454 → cellprofiler_library_nightly-5.0.0.dev466}/pyproject.toml +0 -0
  84. {cellprofiler_library_nightly-5.0.0.dev454 → cellprofiler_library_nightly-5.0.0.dev466}/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.dev454
3
+ Version: 5.0.0.dev466
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>
@@ -28,7 +28,7 @@ version_tuple: VERSION_TUPLE
28
28
  commit_id: COMMIT_ID
29
29
  __commit_id__: COMMIT_ID
30
30
 
31
- __version__ = version = '5.0.0.dev454'
32
- __version_tuple__ = version_tuple = (5, 0, 0, 'dev454')
31
+ __version__ = version = '5.0.0.dev466'
32
+ __version_tuple__ = version_tuple = (5, 0, 0, 'dev466')
33
33
 
34
- __commit_id__ = commit_id = 'g957fa6e04'
34
+ __commit_id__ = commit_id = 'ge958a4dc2'
@@ -1,8 +1,10 @@
1
1
  import numpy
2
+ from numpy import isscalar as _isscalar
2
3
  import skimage.color
3
4
  import skimage.morphology
4
5
  import skimage.segmentation
5
6
  import skimage.util
7
+ from skimage.util import invert as _invert
6
8
  import skimage.transform
7
9
  import skimage
8
10
  import skimage.restoration
@@ -13,16 +15,20 @@ import scipy
13
15
  import scipy.interpolate
14
16
  import matplotlib
15
17
  import math
18
+ from typing import Any, Optional, Tuple, Callable, Union, List, cast, Dict, TypeVar
16
19
  from numpy.typing import NDArray
17
- import centrosome.filter
18
- from typing import Any, Optional, Tuple, Callable, Union, List, TypeVar
19
20
  from cellprofiler_library.types import ImageGrayscale, ImageGrayscaleMask, Image2DColor, Image2DGrayscale, ImageAny, ImageAnyMask, ObjectSegmentation, Image2D, Image2DMask, StructuringElement, ObjectLabelSet, ImageColor, ImageBinaryMask
20
21
  from cellprofiler_library.opts import threshold as Threshold
21
22
  from cellprofiler_library.opts.enhanceorsuppressfeatures import SpeckleAccuracy, NeuriteMethod
22
23
  from cellprofiler_library.opts.overlayoutlines import BrightnessMode
23
24
  from cellprofiler_library.opts.crop import RemovalMethod
24
25
  from cellprofiler_library.opts.structuring_elements import StructuringElementShape2D, StructuringElementShape3D
25
- from ..opts.resize import ResizingMethod, DimensionMethod, InterpolationMethod
26
+ from cellprofiler_library.opts.resize import ResizingMethod, DimensionMethod, InterpolationMethod
27
+ from cellprofiler_library.opts.imagemath import Operator
28
+
29
+ # TODO: python_latest - remove this if later skimage versions update types
30
+ invert = cast(Callable[[ImageAny], ImageAny], _invert)
31
+ isscalar = cast(Callable[[Optional[ImageAny]], bool], _isscalar)
26
32
 
27
33
  T = TypeVar("T", bound=ImageAny)
28
34
  MorphImageT = TypeVar("Union[ImageGrayscale, ImageGrayscaleMask]", bound=Union[ImageGrayscale, ImageGrayscaleMask])
@@ -1702,3 +1708,234 @@ def fill_holes(image: ImageAny, diameter: float) -> ImageAny:
1702
1708
  size = numpy.pi * factor
1703
1709
 
1704
1710
  return skimage.morphology.remove_small_holes(image, size)
1711
+
1712
+ ################################################################################
1713
+ # ImageMath
1714
+ ################################################################################
1715
+
1716
+ def imagemath_apply_on_image(
1717
+ output_pixel_data: ImageAny,
1718
+ pd: ImageAny,
1719
+ comparitor: ImageAny,
1720
+ op: Callable[[ImageAny, ImageAny], ImageAny],
1721
+ opval: Operator,
1722
+ ) -> ImageAny:
1723
+ assert isinstance(output_pixel_data, numpy.ndarray), "output_pixel_data must be a numpy array" # Pylance needs to understand this is a numpy array
1724
+ if not isscalar(pd) and output_pixel_data.ndim != pd.ndim:
1725
+ if output_pixel_data.ndim == 2:
1726
+ output_pixel_data = output_pixel_data[:, :, numpy.newaxis]
1727
+ if opval == Operator.EQUALS and not isscalar(comparitor):
1728
+ comparitor = comparitor[:, :, numpy.newaxis]
1729
+ if pd.ndim == 2:
1730
+ pd = pd[:, :, numpy.newaxis]
1731
+ if opval == Operator.EQUALS:
1732
+ output_pixel_data = output_pixel_data & (comparitor == pd)
1733
+ else:
1734
+ output_pixel_data = op(output_pixel_data, pd)
1735
+ return output_pixel_data
1736
+
1737
+
1738
+
1739
+ def imagemath_apply_binary_operation(
1740
+ opval: Operator,
1741
+ operands: List[Union[ImageAny, float]],
1742
+ masks: Optional[List[Optional[Union[ImageAnyMask, bool]]]],
1743
+ output_pixel_data: ImageAny,
1744
+ output_mask: Optional[ImageAnyMask],
1745
+ image_factors: Optional[List[float]], # TODO: can this be removed?
1746
+ ignore_mask: bool, # TODO: can this be removed?
1747
+ ) -> Tuple[
1748
+ ImageAny,
1749
+ Optional[ImageAnyMask],
1750
+ ]:
1751
+ #
1752
+ # Helper function to determine if logical operations should be used
1753
+ #
1754
+ def use_logical_operation(pixel_data: List[ImageAny]) -> bool:
1755
+ return all(
1756
+ [pd.dtype == bool for pd in pixel_data if not isscalar(pd)]
1757
+ )
1758
+
1759
+ #
1760
+ # Helper function for logical subtraction
1761
+ #
1762
+
1763
+ def logical_subtract(output_pixel_data: ImageAny, pd: ImageAny) -> ImageAny:
1764
+ output_pixel_data[pd] = False
1765
+ return output_pixel_data
1766
+
1767
+ comparitor = operands[0] # fix pylance error
1768
+ use_logical = use_logical_operation(operands)
1769
+ op_fn_dispatch: Dict[Operator, Callable[[ImageAny, ImageAny], ImageAny]] = {
1770
+ Operator.ADD: numpy.add,
1771
+ Operator.SUBTRACT: logical_subtract if use_logical else numpy.subtract,
1772
+ Operator.DIFFERENCE: numpy.logical_xor if use_logical else lambda x, y: numpy.abs(numpy.subtract(x, y)),
1773
+ Operator.MULTIPLY: numpy.logical_and if use_logical else numpy.multiply,
1774
+ Operator.MINIMUM: numpy.minimum,
1775
+ Operator.MAXIMUM: numpy.maximum,
1776
+ Operator.AVERAGE: numpy.add,
1777
+ Operator.MAXIMUM: numpy.maximum,
1778
+ Operator.AND: numpy.logical_and,
1779
+ Operator.OR: numpy.logical_or,
1780
+ Operator.EQUALS: numpy.equal,
1781
+ Operator.NONE: lambda x, y: x,
1782
+ Operator.DIVIDE: numpy.divide,
1783
+ }
1784
+ if opval not in op_fn_dispatch:
1785
+ raise NotImplementedError(f"Unimplemented operation: {opval}")
1786
+ #
1787
+ # Binary operations
1788
+ #
1789
+ op = op_fn_dispatch[opval]
1790
+
1791
+ #
1792
+ # Equals and Subtract operations need additional handling
1793
+ #
1794
+ if opval == Operator.EQUALS:
1795
+ output_pixel_data = numpy.ones(operands[0].shape, bool)
1796
+ comparitor = operands[0]
1797
+ elif opval == Operator.SUBTRACT and use_logical:
1798
+ output_pixel_data = operands[0].copy()
1799
+
1800
+
1801
+ # _masks is a list of Nones if masks is None. Fixes type warnings.
1802
+ if masks is None:
1803
+ masks = [None for _ in operands]
1804
+
1805
+ #
1806
+ # Apply the operation to each image in the list
1807
+ #
1808
+ for pd, mask in zip(operands[1:], masks[1:]):
1809
+ output_pixel_data = imagemath_apply_on_image(output_pixel_data, pd, comparitor, op, opval)
1810
+ if not ignore_mask:
1811
+ if output_mask is None:
1812
+ output_mask = mask
1813
+ elif mask is not None:
1814
+ output_mask = output_mask & mask
1815
+ #
1816
+ # Average operation needs additional handling
1817
+ #
1818
+ if opval == Operator.AVERAGE:
1819
+ if not use_logical:
1820
+ assert image_factors is not None, "image_factors must be provided for average operation"
1821
+ output_pixel_data /= sum(image_factors)
1822
+ return output_pixel_data, output_mask
1823
+
1824
+
1825
+ def imagemath_apply_unary_operation(
1826
+ opval: Operator,
1827
+ operands: List[Union[ImageAny, float]],
1828
+ masks: Optional[List[Optional[Union[ImageAnyMask, bool]]]],
1829
+ output_pixel_data: ImageAny,
1830
+ output_mask: Optional[ImageAnyMask],
1831
+ ignore_mask: bool,
1832
+ ) -> Tuple[
1833
+ ImageAny,
1834
+ Optional[ImageAnyMask],
1835
+ ]:
1836
+ if opval == Operator.STDEV:
1837
+ pixel_array = numpy.array(operands)
1838
+ output_pixel_data = numpy.std(pixel_array,axis=0)
1839
+ if not ignore_mask:
1840
+ mask_array = numpy.array(masks)
1841
+ output_mask = mask_array.all(axis=0)
1842
+ elif opval == Operator.INVERT:
1843
+ output_pixel_data = invert(output_pixel_data)
1844
+ elif opval == Operator.NOT:
1845
+ output_pixel_data = numpy.logical_not(output_pixel_data)
1846
+ elif opval == Operator.LOG_TRANSFORM:
1847
+ output_pixel_data = numpy.log2(output_pixel_data + 1)
1848
+ elif opval == Operator.LOG_TRANSFORM_LEGACY:
1849
+ output_pixel_data = numpy.log2(output_pixel_data)
1850
+ elif opval == Operator.NONE:
1851
+ output_pixel_data = output_pixel_data.copy()
1852
+ else:
1853
+ raise NotImplementedError(
1854
+ "The operation %s has not been implemented" % opval
1855
+ )
1856
+ return output_pixel_data, output_mask
1857
+
1858
+
1859
+ ###############################################################################
1860
+ # GrayToColor
1861
+ ###############################################################################
1862
+
1863
+ def gray_to_rgb(
1864
+ pixel_data_arr: List[Optional[Image2DGrayscale]],
1865
+ adjustment_factor_array: List[float],
1866
+ intensities: List[Tuple[float, ...]]=[(1.0, 0.0, 0.0), (0.0, 1.0, 0.0), (0.0, 0.0, 1.0)],
1867
+ wants_rescale: bool=True,
1868
+ ) -> Image2DColor:
1869
+ assert(len(pixel_data_arr) == len(adjustment_factor_array)), f"pixel_data_arr and adjustment_factor_array must be the same length. pixel_data_arr has {len(pixel_data_arr)} elements, adjustment_factor_array has {len(adjustment_factor_array)} elements."
1870
+ assert(len(pixel_data_arr) == len(intensities)), f"pixel_data_arr and intensities must be the same length. pixel_data_arr has {len(pixel_data_arr)} elements, intensities has {len(intensities)} elements."
1871
+
1872
+ parent_image = None
1873
+ rgb_pixel_data = None
1874
+ for pixel_data, adjustment_factor, intensity_triplet in zip(pixel_data_arr, adjustment_factor_array, intensities):
1875
+ if pixel_data is None:
1876
+ continue
1877
+ multiplier = numpy.array(intensity_triplet) * adjustment_factor
1878
+ if wants_rescale:
1879
+ pixel_data = pixel_data /numpy.max(pixel_data)
1880
+ if parent_image is not None:
1881
+ if parent_image.shape != pixel_data.shape:
1882
+ raise ValueError(
1883
+ "The input images have different sizes (%s vs %s)"
1884
+ % (
1885
+ parent_image.shape,
1886
+ pixel_data.shape,
1887
+ )
1888
+ )
1889
+ rgb_pixel_data += numpy.dstack([pixel_data] * 3) * multiplier
1890
+ else:
1891
+ parent_image = pixel_data
1892
+ rgb_pixel_data = numpy.dstack([pixel_data] * 3) * multiplier
1893
+ return rgb_pixel_data
1894
+
1895
+ def gray_to_cmyk(*args, **kwargs):
1896
+ # gray to cmyk has the same implementation as gray to rgb but with different intensities
1897
+ return gray_to_rgb(*args, **kwargs)
1898
+
1899
+
1900
+ def gray_to_composite_color(
1901
+ pixel_data_arr: List[Optional[Image2DGrayscale]],
1902
+ color_array: List[Tuple[int, int, int]],
1903
+ weight_array: List[float],
1904
+ wants_rescale: bool,
1905
+ ) -> Image2DColor:
1906
+ source_channels = pixel_data_arr
1907
+ parent_image = pixel_data_arr[0]
1908
+ for idx, pd in enumerate(source_channels):
1909
+ if pd is None:
1910
+ continue
1911
+ if pd.shape != source_channels[0].shape:
1912
+ raise ValueError(
1913
+ "The input images have different sizes (%s vs %s)"
1914
+ % (
1915
+ source_channels[0].shape,
1916
+ pd.shape,
1917
+ )
1918
+ )
1919
+
1920
+ colors: List[NDArray[numpy.float32]] = []
1921
+ pixel_data = parent_image
1922
+ if wants_rescale:
1923
+ pixel_data = pixel_data / numpy.max(pixel_data)
1924
+ for color_tuple, weight in zip(color_array, weight_array):
1925
+ color = (weight * numpy.array(color_tuple).astype(pixel_data.dtype) / 255)
1926
+ colors += [color[numpy.newaxis, numpy.newaxis, :]]
1927
+ rgb_pixel_data = pixel_data[:, :, numpy.newaxis] * colors[0]
1928
+ for image, color in zip(source_channels[1:], colors[1:]):
1929
+ if wants_rescale:
1930
+ image = image / numpy.max(image)
1931
+ rgb_pixel_data = rgb_pixel_data + image[:, :, numpy.newaxis] * color
1932
+
1933
+ return rgb_pixel_data
1934
+
1935
+
1936
+ def gray_to_stacked_color(
1937
+ pixel_data_arr: List[Optional[Image2DGrayscale]]
1938
+ ) -> Image2DColor:
1939
+ source_channels = pixel_data_arr
1940
+ rgb_pixel_data = numpy.dstack(source_channels)
1941
+ return rgb_pixel_data
@@ -0,0 +1,58 @@
1
+ import numpy
2
+ from numpy.typing import NDArray
3
+ from typing import List, Tuple, Optional
4
+ from pydantic import validate_call, ConfigDict
5
+ from cellprofiler_library.opts.graytocolor import Scheme
6
+ from cellprofiler_library.types import Image2DGrayscale, Image2DColor
7
+ from cellprofiler_library.functions.image_processing import gray_to_rgb, gray_to_cmyk, gray_to_composite_color, gray_to_stacked_color
8
+
9
+ @validate_call(config=ConfigDict(arbitrary_types_allowed=True))
10
+ def gray_to_color(
11
+ pixel_data_arr: List[Optional[Image2DGrayscale]],
12
+ scheme: Scheme,
13
+ color_array: Optional[List[Tuple[int, int, int]]] = None,
14
+ weight_array: Optional[List[float]] = None,
15
+ adjustment_factor_array: Optional[List[float]] = None,
16
+ intensities: List[Tuple[float, ...]]=[(1.0, 0.0, 0.0), (0.0, 1.0, 0.0), (0.0, 0.0, 1.0)],
17
+ wants_rescale: bool=True,
18
+ ) -> Image2DColor:
19
+ rgb_pixel_data = None
20
+ if scheme == Scheme.RGB:
21
+ assert adjustment_factor_array is not None, "adjustment_factor_array must be provided for RGB mode"
22
+ assert intensities is not None, "intensities must be provided for RGB mode"
23
+ rgb_pixel_data = gray_to_rgb(
24
+ pixel_data_arr = pixel_data_arr,
25
+ adjustment_factor_array = adjustment_factor_array,
26
+ intensities = intensities,
27
+ wants_rescale = wants_rescale,
28
+ )
29
+ elif scheme == Scheme.CMYK:
30
+ assert adjustment_factor_array is not None, "adjustment_factor_array must be provided for CMYK mode"
31
+ assert intensities is not None, "intensities must be provided for CMYK mode"
32
+ rgb_pixel_data = gray_to_cmyk(
33
+ pixel_data_arr = pixel_data_arr,
34
+ adjustment_factor_array = adjustment_factor_array,
35
+ intensities = intensities,
36
+ wants_rescale = wants_rescale,
37
+ )
38
+ elif scheme == Scheme.COMPOSITE:
39
+ assert color_array is not None, "color_array must be provided for composite mode"
40
+ assert weight_array is not None, "weight_array must be provided for composite mode"
41
+ rgb_pixel_data = gray_to_composite_color(
42
+ pixel_data_arr = pixel_data_arr,
43
+ color_array = color_array,
44
+ weight_array = weight_array,
45
+ wants_rescale = wants_rescale,
46
+ )
47
+ elif scheme == Scheme.STACK:
48
+ assert pixel_data_arr is not None, "pixel_data_arr must be provided for stack mode"
49
+ rgb_pixel_data = gray_to_stacked_color(
50
+ pixel_data_arr = pixel_data_arr,
51
+ )
52
+ else:
53
+ raise ValueError(f"Unimplemented scheme: {scheme}")
54
+ if scheme.value != Scheme.STACK and wants_rescale:
55
+ # If we rescaled, clip values that went out of range after multiplication
56
+ rgb_pixel_data[rgb_pixel_data > 1] = 1
57
+ return rgb_pixel_data
58
+
@@ -0,0 +1,64 @@
1
+ import numpy
2
+ from typing import Callable, Optional, List, Tuple, cast, Union, Annotated
3
+ from numpy import isscalar as _isscalar
4
+ from pydantic import validate_call, ConfigDict, Field
5
+ from cellprofiler_library.opts.imagemath import Operator, BINARY_OUTPUT_OPS, UNARY_INPUT_OPS, BINARY_INPUT_OPS
6
+ from cellprofiler_library.types import ImageAny, ImageAnyMask
7
+ from cellprofiler_library.functions.image_processing import imagemath_apply_binary_operation, imagemath_apply_unary_operation
8
+ isscalar = cast(Callable[[Optional[ImageAny]], bool], _isscalar)
9
+
10
+
11
+ @validate_call(config=ConfigDict(arbitrary_types_allowed=True))
12
+ def image_math(
13
+ opval: Annotated[Operator, Field(description=" Operator to apply to the images or measurements")],
14
+ operands: Annotated[List[Union[ImageAny, float]], Field(description=" List of images and/or measurements to apply the operator to")],
15
+ masks: Annotated[Optional[List[Optional[Union[ImageAnyMask, bool]]]], Field(description=" List of masks for the images. Use `True` for measurement operands")],
16
+ output_pixel_data: Annotated[ImageAny, Field(description=" Output image or measurement")],
17
+ output_mask: Annotated[Optional[ImageAnyMask], Field(description=" Output mask")],
18
+ ignore_mask: Annotated[bool, Field(description=" Ignore the masks")],
19
+ image_factors: Annotated[Optional[List[float]], Field(description=" List of image factors to multiply the images by")],
20
+ exponent: Annotated[Optional[float], Field(description=" Exponent to raise the images by")],
21
+ after_factor: Annotated[Optional[float], Field(description=" Multiply the images by this value after the operation")],
22
+ addend: Annotated[Optional[float], Field(description=" Add this value to the images")],
23
+ truncate_low: Annotated[Optional[bool], Field(description=" Truncate the images below 0")],
24
+ truncate_high: Annotated[Optional[bool], Field(description=" Truncate the images above 0")],
25
+ replace_nan: Annotated[Optional[bool], Field(description=" Replace invalid values with 0")],
26
+ ) -> Tuple[
27
+ ImageAny, Optional[ImageAnyMask]
28
+ ]:
29
+ if opval in BINARY_INPUT_OPS:
30
+ output_pixel_data, output_mask = imagemath_apply_binary_operation(opval, operands, masks, output_pixel_data, output_mask, image_factors, ignore_mask)
31
+
32
+ elif opval in UNARY_INPUT_OPS:
33
+ output_pixel_data, output_mask = imagemath_apply_unary_operation(opval, operands, masks, output_pixel_data, output_mask, ignore_mask)
34
+
35
+ else:
36
+ raise NotImplementedError(
37
+ "The operation %s has not been implemented" % opval
38
+ )
39
+
40
+ # Check to see if there was a measurement & image w/o mask. If so
41
+ # set mask to none
42
+ if isscalar(output_mask):
43
+ output_mask = None
44
+ if opval not in BINARY_OUTPUT_OPS:
45
+ #
46
+ # Post-processing: exponent, multiply, add
47
+ #
48
+ if exponent is not None and exponent != 1:
49
+ output_pixel_data **= exponent
50
+ if after_factor is not None and after_factor != 1:
51
+ output_pixel_data *= after_factor
52
+ if addend is not None and addend != 0:
53
+ output_pixel_data += addend
54
+
55
+ #
56
+ # truncate values
57
+ #
58
+ if truncate_low:
59
+ output_pixel_data[output_pixel_data < 0] = 0
60
+ if truncate_high:
61
+ output_pixel_data[output_pixel_data > 1] = 1
62
+ if replace_nan:
63
+ output_pixel_data[numpy.isnan(output_pixel_data)] = 0
64
+ return output_pixel_data, output_mask
@@ -0,0 +1,20 @@
1
+ from enum import Enum
2
+
3
+ class Scheme(str, Enum):
4
+ RGB = "RGB"
5
+ CMYK = "CMYK"
6
+ STACK = "Stack"
7
+ COMPOSITE = "Composite"
8
+ LEAVE_THIS_BLACK = "Leave this black"
9
+
10
+ DEFAULT_COLORS = [
11
+ "#%02x%02x%02x" % color
12
+ for color in (
13
+ (255, 0, 0),
14
+ (0, 255, 0),
15
+ (0, 0, 255),
16
+ (128, 128, 0),
17
+ (128, 0, 128),
18
+ (0, 128, 128),
19
+ )
20
+ ]
@@ -0,0 +1,30 @@
1
+ from enum import Enum
2
+
3
+ class Operator(str, Enum):
4
+ ADD = "Add"
5
+ SUBTRACT = "Subtract"
6
+ DIFFERENCE = "Absolute Difference"
7
+ MULTIPLY = "Multiply"
8
+ DIVIDE = "Divide"
9
+ AVERAGE = "Average"
10
+ MINIMUM = "Minimum"
11
+ MAXIMUM = "Maximum"
12
+ STDEV = "Standard Deviation"
13
+ INVERT = "Invert"
14
+ COMPLEMENT = "Complement"
15
+ LOG_TRANSFORM_LEGACY = "Log transform (legacy)"
16
+ LOG_TRANSFORM = "Log transform (base 2)"
17
+ NONE = "None"
18
+ OR = "Or"
19
+ AND = "And"
20
+ NOT = "Not"
21
+ EQUALS = "Equals"
22
+
23
+
24
+ class Operand(str, Enum):
25
+ IMAGE = "Image"
26
+ MEASUREMENT = "Measurement"
27
+
28
+ BINARY_OUTPUT_OPS = [Operator.AND, Operator.OR, Operator.NOT, Operator.EQUALS]
29
+ UNARY_INPUT_OPS = [Operator.STDEV, Operator.INVERT, Operator.NOT, Operator.LOG_TRANSFORM, Operator.LOG_TRANSFORM_LEGACY, Operator.NONE]
30
+ BINARY_INPUT_OPS = [Operator.ADD, Operator.SUBTRACT, Operator.DIFFERENCE, Operator.MULTIPLY, Operator.DIVIDE, Operator.AVERAGE, Operator.MAXIMUM, Operator.MINIMUM, Operator.AND, Operator.OR, Operator.EQUALS]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cellprofiler-library-nightly
3
- Version: 5.0.0.dev454
3
+ Version: 5.0.0.dev466
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>
@@ -29,9 +29,11 @@ cellprofiler_library/modules/_erodeobjects.py
29
29
  cellprofiler_library/modules/_expandorshrinkobjects.py
30
30
  cellprofiler_library/modules/_fillobjects.py
31
31
  cellprofiler_library/modules/_gaussianfilter.py
32
+ cellprofiler_library/modules/_graytocolor.py
32
33
  cellprofiler_library/modules/_identifyprimaryobjects.py
33
34
  cellprofiler_library/modules/_identifysecondaryobjects.py
34
35
  cellprofiler_library/modules/_identifytertiaryobjects.py
36
+ cellprofiler_library/modules/_imagemath.py
35
37
  cellprofiler_library/modules/_measureimageoverlap.py
36
38
  cellprofiler_library/modules/_measureobjectsizeshape.py
37
39
  cellprofiler_library/modules/_medialaxis.py
@@ -59,9 +61,11 @@ cellprofiler_library/opts/dilateobjects.py
59
61
  cellprofiler_library/opts/enhanceorsuppressfeatures.py
60
62
  cellprofiler_library/opts/erodeimage.py
61
63
  cellprofiler_library/opts/erodeobjects.py
64
+ cellprofiler_library/opts/graytocolor.py
62
65
  cellprofiler_library/opts/identifyprimaryobjects.py
63
66
  cellprofiler_library/opts/identifysecondaryobjects.py
64
67
  cellprofiler_library/opts/identifytertiaryobjects.py
68
+ cellprofiler_library/opts/imagemath.py
65
69
  cellprofiler_library/opts/measureimageoverlap.py
66
70
  cellprofiler_library/opts/morph.py
67
71
  cellprofiler_library/opts/objectsizeshapefeatures.py