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.
Files changed (125) hide show
  1. {cellprofiler_library_nightly-5.0.0.dev650/cellprofiler_library_nightly.egg-info → cellprofiler_library_nightly-5.0.0.dev669}/PKG-INFO +1 -1
  2. cellprofiler_library_nightly-5.0.0.dev669/cellprofiler_library/_version.py +24 -0
  3. {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/functions/image_processing.py +214 -11
  4. {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/functions/object_processing.py +346 -1
  5. cellprofiler_library_nightly-5.0.0.dev669/cellprofiler_library/modules/_filterobjects.py +88 -0
  6. cellprofiler_library_nightly-5.0.0.dev669/cellprofiler_library/modules/_rescaleintensity.py +60 -0
  7. cellprofiler_library_nightly-5.0.0.dev669/cellprofiler_library/opts/filterobjects.py +38 -0
  8. cellprofiler_library_nightly-5.0.0.dev669/cellprofiler_library/opts/rescaleintensity.py +39 -0
  9. {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/types.py +14 -5
  10. {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669/cellprofiler_library_nightly.egg-info}/PKG-INFO +1 -1
  11. {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library_nightly.egg-info/SOURCES.txt +6 -0
  12. cellprofiler_library_nightly-5.0.0.dev669/cellprofiler_library_nightly.egg-info/scm_file_list.json +118 -0
  13. cellprofiler_library_nightly-5.0.0.dev669/cellprofiler_library_nightly.egg-info/scm_version.json +8 -0
  14. cellprofiler_library_nightly-5.0.0.dev650/cellprofiler_library/_version.py +0 -34
  15. {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/LICENSE +0 -0
  16. {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/README.md +0 -0
  17. {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/__init__.py +0 -0
  18. {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/functions/__init__.py +0 -0
  19. {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/functions/file_processing.py +0 -0
  20. {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/functions/measurement.py +0 -0
  21. {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/functions/segmentation.py +0 -0
  22. {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/measurement_model.py +0 -0
  23. {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/modules/__init__.py +0 -0
  24. {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/modules/_align.py +0 -0
  25. {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/modules/_closing.py +0 -0
  26. {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/modules/_colortogray.py +0 -0
  27. {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/modules/_combineobjects.py +0 -0
  28. {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/modules/_convertimagetoobjects.py +0 -0
  29. {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/modules/_convertobjectstoimage.py +0 -0
  30. {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/modules/_correctilluminationapply.py +0 -0
  31. {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/modules/_crop.py +0 -0
  32. {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/modules/_dilateimage.py +0 -0
  33. {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/modules/_dilateobjects.py +0 -0
  34. {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/modules/_enhanceedges.py +0 -0
  35. {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/modules/_enhanceorsuppressfeatures.py +0 -0
  36. {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/modules/_erodeimage.py +0 -0
  37. {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/modules/_erodeobjects.py +0 -0
  38. {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/modules/_expandorshrinkobjects.py +0 -0
  39. {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/modules/_fillobjects.py +0 -0
  40. {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/modules/_flipandrotate.py +0 -0
  41. {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/modules/_gaussianfilter.py +0 -0
  42. {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/modules/_graytocolor.py +0 -0
  43. {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/modules/_identifydeadworms.py +0 -0
  44. {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/modules/_identifyprimaryobjects.py +0 -0
  45. {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/modules/_identifysecondaryobjects.py +0 -0
  46. {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/modules/_identifytertiaryobjects.py +0 -0
  47. {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/modules/_imagemath.py +0 -0
  48. {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/modules/_measurecolocalization.py +0 -0
  49. {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/modules/_measuregranularity.py +0 -0
  50. {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/modules/_measureimageareaoccupied.py +0 -0
  51. {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/modules/_measureimageintensity.py +0 -0
  52. {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/modules/_measureimageoverlap.py +0 -0
  53. {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/modules/_measureimagequality.py +0 -0
  54. {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/modules/_measureimageskeleton.py +0 -0
  55. {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/modules/_measureobjectintensity.py +0 -0
  56. {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/modules/_measureobjectintensitydistribution.py +0 -0
  57. {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/modules/_measureobjectneighbors.py +0 -0
  58. {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/modules/_measureobjectoverlap.py +0 -0
  59. {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/modules/_measureobjectsizeshape.py +0 -0
  60. {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/modules/_measureobjectskeleton.py +0 -0
  61. {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/modules/_measuretexture.py +0 -0
  62. {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/modules/_medialaxis.py +0 -0
  63. {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/modules/_medianfilter.py +0 -0
  64. {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/modules/_morph.py +0 -0
  65. {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/modules/_morphologicalskeleton.py +0 -0
  66. {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/modules/_opening.py +0 -0
  67. {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/modules/_overlayobjects.py +0 -0
  68. {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/modules/_overlayoutlines.py +0 -0
  69. {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/modules/_reducenoise.py +0 -0
  70. {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/modules/_removeholes.py +0 -0
  71. {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/modules/_resize.py +0 -0
  72. {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/modules/_savecroppedobjects.py +0 -0
  73. {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/modules/_shrinktoobjectcenters.py +0 -0
  74. {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/modules/_smooth.py +0 -0
  75. {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/modules/_threshold.py +0 -0
  76. {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/modules/_watershed.py +0 -0
  77. {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/opts/__init__.py +0 -0
  78. {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/opts/align.py +0 -0
  79. {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/opts/colortogray.py +0 -0
  80. {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/opts/convertimagetoobjects.py +0 -0
  81. {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/opts/convertobjectstoimage.py +0 -0
  82. {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/opts/correctilluminationapply.py +0 -0
  83. {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/opts/crop.py +0 -0
  84. {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/opts/dilateimage.py +0 -0
  85. {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/opts/dilateobjects.py +0 -0
  86. {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/opts/enhanceedges.py +0 -0
  87. {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/opts/enhanceorsuppressfeatures.py +0 -0
  88. {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/opts/erodeimage.py +0 -0
  89. {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/opts/erodeobjects.py +0 -0
  90. {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/opts/flipandrotate.py +0 -0
  91. {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/opts/graytocolor.py +0 -0
  92. {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/opts/identifydeadworms.py +0 -0
  93. {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/opts/identifyprimaryobjects.py +0 -0
  94. {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/opts/identifysecondaryobjects.py +0 -0
  95. {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/opts/identifytertiaryobjects.py +0 -0
  96. {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/opts/imagemath.py +0 -0
  97. {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/opts/measurecolocalization.py +0 -0
  98. {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/opts/measuregranularity.py +0 -0
  99. {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/opts/measureimageareaoccupied.py +0 -0
  100. {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/opts/measureimageintensity.py +0 -0
  101. {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/opts/measureimageoverlap.py +0 -0
  102. {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/opts/measureimagequality.py +0 -0
  103. {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/opts/measureimageskeleton.py +0 -0
  104. {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/opts/measureobjectintensity.py +0 -0
  105. {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/opts/measureobjectintensitydistribution.py +0 -0
  106. {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/opts/measureobjectneighbors.py +0 -0
  107. {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/opts/measureobjectoverlap.py +0 -0
  108. {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/opts/measureobjectskeleton.py +0 -0
  109. {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/opts/measuretexture.py +0 -0
  110. {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/opts/morph.py +0 -0
  111. {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/opts/objectsizeshapefeatures.py +0 -0
  112. {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/opts/overlayoutlines.py +0 -0
  113. {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/opts/removeholes.py +0 -0
  114. {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/opts/resize.py +0 -0
  115. {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/opts/shrinktoobjectcenters.py +0 -0
  116. {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/opts/smooth.py +0 -0
  117. {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/opts/structuring_elements.py +0 -0
  118. {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/opts/threshold.py +0 -0
  119. {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library/py.typed +0 -0
  120. {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library_nightly.egg-info/dependency_links.txt +0 -0
  121. {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library_nightly.egg-info/requires.txt +0 -0
  122. {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/cellprofiler_library_nightly.egg-info/top_level.txt +0 -0
  123. {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/environment.yml +0 -0
  124. {cellprofiler_library_nightly-5.0.0.dev650 → cellprofiler_library_nightly-5.0.0.dev669}/pyproject.toml +0 -0
  125. {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.dev650
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
- comparitor: ImageAny,
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(comparitor):
1782
- comparitor = comparitor[:, :, numpy.newaxis]
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 & (comparitor == pd)
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
- comparitor = operands[0] # fix pylance error
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
- comparitor = operands[0]
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, comparitor, op, opval)
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