phenotypic 0.6.0__py3-none-any.whl
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.
- phenotypic/__init__.py +38 -0
- phenotypic/abstract/__init__.py +27 -0
- phenotypic/abstract/_docstring_metaclass.py +22 -0
- phenotypic/abstract/_feature_measure.py +43 -0
- phenotypic/abstract/_grid_corrector.py +25 -0
- phenotypic/abstract/_grid_finder.py +27 -0
- phenotypic/abstract/_grid_map_modifier.py +19 -0
- phenotypic/abstract/_grid_measure.py +23 -0
- phenotypic/abstract/_grid_operation.py +2 -0
- phenotypic/abstract/_image_corrector.py +35 -0
- phenotypic/abstract/_image_enhancer.py +45 -0
- phenotypic/abstract/_image_operation.py +47 -0
- phenotypic/abstract/_map_modifier.py +51 -0
- phenotypic/abstract/_object_detector.py +49 -0
- phenotypic/abstract/_threshold_detector.py +8 -0
- phenotypic/core/__init__.py +7 -0
- phenotypic/core/_grid_image.py +6 -0
- phenotypic/core/_image.py +16 -0
- phenotypic/core/_imread.py +16 -0
- phenotypic/core/accessors/__init__.py +122 -0
- phenotypic/core/accessors/_grid_accessor.py +362 -0
- phenotypic/core/accessors/_hsv_accessor.py +224 -0
- phenotypic/core/accessors/_image_accessor.py +147 -0
- phenotypic/core/accessors/_image_array_accessor.py +324 -0
- phenotypic/core/accessors/_image_data_accessor.py +24 -0
- phenotypic/core/accessors/_image_enh_matrix_accessor.py +220 -0
- phenotypic/core/accessors/_image_matrix_accessor.py +231 -0
- phenotypic/core/accessors/_image_objects_accessor.py +114 -0
- phenotypic/core/accessors/_measurement_accessor.py +106 -0
- phenotypic/core/accessors/_metadata_accessor.py +94 -0
- phenotypic/core/accessors/_objmap_accessor.py +140 -0
- phenotypic/core/accessors/_objmask_accessor.py +107 -0
- phenotypic/core/handlers/__init__.py +5 -0
- phenotypic/core/handlers/_image_grid_handler.py +174 -0
- phenotypic/core/handlers/_image_handler.py +854 -0
- phenotypic/core/handlers/_image_hsv_handler.py +57 -0
- phenotypic/core/handlers/_image_io_handler.py +111 -0
- phenotypic/correction/__init__.py +5 -0
- phenotypic/data/__init__.py +12 -0
- phenotypic/data/_sample_image_data.py +47 -0
- phenotypic/detection/__init__.py +9 -0
- phenotypic/detection/_otsu_detector.py +41 -0
- phenotypic/detection/_triangle_detector.py +39 -0
- phenotypic/enhancement/__init__.py +37 -0
- phenotypic/enhancement/_clahe.py +34 -0
- phenotypic/enhancement/_contrast_streching.py +32 -0
- phenotypic/enhancement/_gaussian_preprocessor.py +39 -0
- phenotypic/enhancement/_laplace_preprocessor.py +33 -0
- phenotypic/enhancement/_median_preprocessor.py +31 -0
- phenotypic/enhancement/_rank_median_preprocessor.py +42 -0
- phenotypic/enhancement/_rolling_ball_preprocessor.py +21 -0
- phenotypic/enhancement/_white_tophat_preprocessor.py +39 -0
- phenotypic/grid/__init__.py +18 -0
- phenotypic/grid/_grid_aligner.py +106 -0
- phenotypic/grid/_grid_apply.py +39 -0
- phenotypic/grid/_grid_linreg_stats_extractor.py +73 -0
- phenotypic/grid/_grid_oversized_object_remover.py +61 -0
- phenotypic/grid/_linreg_residual_outlier_modifier.py +116 -0
- phenotypic/grid/_min_residual_error_reducer.py +60 -0
- phenotypic/grid/_object_spread_extractor.py +32 -0
- phenotypic/grid/_optimal_center_grid_finder.py +330 -0
- phenotypic/measure/__init__.py +6 -0
- phenotypic/measure/_boundary_extractor.py +34 -0
- phenotypic/measure/_color_extractor.py +144 -0
- phenotypic/measure/_measure_geometry.py +51 -0
- phenotypic/measure/_measure_intensity.py +32 -0
- phenotypic/morphology/__init__.py +5 -0
- phenotypic/morphology/_morphology_filler.py +23 -0
- phenotypic/morphology/_morphology_opener.py +14 -0
- phenotypic/morphology/_white_tophat_modifier.py +36 -0
- phenotypic/objects/__init__.py +11 -0
- phenotypic/objects/_border_object_modifier.py +36 -0
- phenotypic/objects/_circularity_modifier.py +61 -0
- phenotypic/objects/_reduction_by_center_deviation_modifier.py +45 -0
- phenotypic/objects/_small_object_modifier.py +14 -0
- phenotypic/pipeline/__init__.py +3 -0
- phenotypic/pipeline/_image_pipeline.py +139 -0
- phenotypic/transform/__init__.py +4 -0
- phenotypic/transform/_image_padder.py +37 -0
- phenotypic/transform/_image_resizer.py +174 -0
- phenotypic/util/__init__.py +9 -0
- phenotypic/util/constants_.py +120 -0
- phenotypic/util/exceptions_.py +277 -0
- phenotypic/util/funcs.py +21 -0
- phenotypic/util/labels.py +80 -0
- phenotypic-0.6.0.dist-info/METADATA +99 -0
- phenotypic-0.6.0.dist-info/RECORD +90 -0
- phenotypic-0.6.0.dist-info/WHEEL +5 -0
- phenotypic-0.6.0.dist-info/licenses/LICENSE +674 -0
- phenotypic-0.6.0.dist-info/top_level.txt +1 -0
phenotypic/__init__.py
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
__version__ = "0.6.0"
|
|
2
|
+
|
|
3
|
+
from .core._image import Image
|
|
4
|
+
from .core._imread import imread
|
|
5
|
+
from .core._grid_image import GridImage
|
|
6
|
+
|
|
7
|
+
from . import (
|
|
8
|
+
data,
|
|
9
|
+
detection,
|
|
10
|
+
measure,
|
|
11
|
+
grid,
|
|
12
|
+
abstract,
|
|
13
|
+
objects,
|
|
14
|
+
morphology,
|
|
15
|
+
pipeline,
|
|
16
|
+
correction,
|
|
17
|
+
enhancement,
|
|
18
|
+
transform,
|
|
19
|
+
util,
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
__all__ = [
|
|
23
|
+
"Image", # Class imported from core
|
|
24
|
+
"imread", # Function imported from core
|
|
25
|
+
"GridImage", # Class imported from core
|
|
26
|
+
"data",
|
|
27
|
+
"detection",
|
|
28
|
+
"measure",
|
|
29
|
+
"grid",
|
|
30
|
+
"abstract",
|
|
31
|
+
"objects",
|
|
32
|
+
"morphology",
|
|
33
|
+
"pipeline",
|
|
34
|
+
"correction",
|
|
35
|
+
"enhancement",
|
|
36
|
+
"transform",
|
|
37
|
+
"util",
|
|
38
|
+
]
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
from ._feature_measure import FeatureMeasure
|
|
2
|
+
from ._image_operation import ImageOperation
|
|
3
|
+
from ._image_enhancer import ImageEnhancer
|
|
4
|
+
from ._image_corrector import ImageCorrector
|
|
5
|
+
from ._object_detector import ObjectDetector
|
|
6
|
+
from ._map_modifier import MapModifier
|
|
7
|
+
from ._threshold_detector import ThresholdDetector
|
|
8
|
+
from ._grid_operation import GridOperation
|
|
9
|
+
from ._grid_finder import GridFinder
|
|
10
|
+
from ._grid_corrector import GridCorrector
|
|
11
|
+
from ._grid_map_modifier import GridMapModifier
|
|
12
|
+
from ._grid_measure import GridFeatureMeasure
|
|
13
|
+
|
|
14
|
+
__all__ = [
|
|
15
|
+
"FeatureMeasure",
|
|
16
|
+
"ImageOperation",
|
|
17
|
+
"ImageEnhancer",
|
|
18
|
+
"ImageCorrector",
|
|
19
|
+
"ObjectDetector",
|
|
20
|
+
"MapModifier",
|
|
21
|
+
"ThresholdDetector",
|
|
22
|
+
"GridOperation",
|
|
23
|
+
"GridFinder",
|
|
24
|
+
"GridCorrector",
|
|
25
|
+
"GridMapModifier",
|
|
26
|
+
"GridFeatureMeasure",
|
|
27
|
+
]
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
|
|
2
|
+
class MeasureDocstringMeta(type):
|
|
3
|
+
"""
|
|
4
|
+
Metaclass to automatically set the docstring of measure() to match _operate()
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
def __new__(cls, name, bases, dct):
|
|
8
|
+
# Set the docstring of measure() to match _operate()
|
|
9
|
+
if '_operate' in dct and 'measure' in dct:
|
|
10
|
+
dct['measure'].__doc__ = dct['_operate'].__doc__
|
|
11
|
+
return super().__new__(cls, name, bases, dct)
|
|
12
|
+
|
|
13
|
+
class ApplyDocstringMeta(type):
|
|
14
|
+
"""
|
|
15
|
+
Metaclass to automatically set the docstring of apply() to match _operate()
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
def __new__(cls, name, bases, dct):
|
|
19
|
+
# Set the docstring of measure() to match _operate()
|
|
20
|
+
if '_operate' in dct and 'measure' in dct:
|
|
21
|
+
dct['measure'].__doc__ = dct['_operate'].__doc__
|
|
22
|
+
return super().__new__(cls, name, bases, dct)
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
from typing import TYPE_CHECKING
|
|
3
|
+
|
|
4
|
+
if TYPE_CHECKING: from phenotypic import Image
|
|
5
|
+
|
|
6
|
+
import numpy as np
|
|
7
|
+
import pandas as pd
|
|
8
|
+
|
|
9
|
+
from ._docstring_metaclass import MeasureDocstringMeta
|
|
10
|
+
from ._image_operation import ImageOperation
|
|
11
|
+
from phenotypic.util.exceptions_ import OperationFailedError, InterfaceError
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
# <<Interface>>
|
|
15
|
+
class FeatureMeasure:
|
|
16
|
+
"""
|
|
17
|
+
A FeatureExtractor is an abstract object intended to calculate measurements on the values within detected objects of
|
|
18
|
+
the image array. The __init__ constructor & _operate method are meant to be the only parts overloaded in inherited classes. This is so
|
|
19
|
+
that the main measure method call can contain all the necessary type validation and output validation checks to streamline development.
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
def measure(self, image: Image) -> pd.DataFrame:
|
|
23
|
+
try:
|
|
24
|
+
imcopy: Image = image.copy()
|
|
25
|
+
|
|
26
|
+
measurement = self._operate(image)
|
|
27
|
+
|
|
28
|
+
# TODO: Fix checks
|
|
29
|
+
# if not np.array_equal(imcopy.matrix[:], image.matrix[:]): raise ValueError(ARRAY_CHANGE_ERROR_MSG)
|
|
30
|
+
# if not np.array_equal(imcopy.enh_matrix[:], image.enh_matrix[:]): raise ValueError(ENHANCED_ARRAY_CHANGE_ERROR_MSG)
|
|
31
|
+
# if not np.array_equal(imcopy.omask[:], image.omask[:]): raise ValueError(MASK_CHANGE_ERROR_MSG)
|
|
32
|
+
# if not np.array_equal(imcopy.omap[:], image.omap[:]): raise ValueError(MAP_CHANGE_ERROR_MSG)
|
|
33
|
+
|
|
34
|
+
return measurement
|
|
35
|
+
except Exception as e:
|
|
36
|
+
raise OperationFailedError(operation=self.__class__.__name__,
|
|
37
|
+
image_name=image.name,
|
|
38
|
+
err_type=type(e),
|
|
39
|
+
message=str(e)
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
def _operate(self, image: Image) -> pd.DataFrame:
|
|
43
|
+
raise InterfaceError
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
from typing import TYPE_CHECKING
|
|
3
|
+
|
|
4
|
+
import pandas as pd
|
|
5
|
+
|
|
6
|
+
if TYPE_CHECKING: from phenotypic import GridImage
|
|
7
|
+
|
|
8
|
+
from phenotypic.abstract import ImageCorrector
|
|
9
|
+
from phenotypic.abstract import GridOperation
|
|
10
|
+
from phenotypic.util.exceptions_ import GridImageInputError, OutputValueError
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class GridCorrector(ImageCorrector, GridOperation):
|
|
14
|
+
def __init__(self, n_rows: int, n_cols: int):
|
|
15
|
+
self.n_rows = n_rows
|
|
16
|
+
self.n_cols = n_cols
|
|
17
|
+
|
|
18
|
+
def apply(self, image: GridImage, inplace=False) -> GridImage:
|
|
19
|
+
from phenotypic import GridImage
|
|
20
|
+
|
|
21
|
+
if not isinstance(image, GridImage): raise GridImageInputError
|
|
22
|
+
output = super().apply(image, inplace=inplace)
|
|
23
|
+
if not isinstance(output, GridImage): raise OutputValueError("GridImage")
|
|
24
|
+
return output
|
|
25
|
+
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
from typing import TYPE_CHECKING
|
|
3
|
+
|
|
4
|
+
if TYPE_CHECKING: from phenotypic import Image
|
|
5
|
+
|
|
6
|
+
import pandas as pd
|
|
7
|
+
|
|
8
|
+
from phenotypic.abstract import FeatureMeasure
|
|
9
|
+
from phenotypic.abstract import GridOperation
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class GridFinder(FeatureMeasure, GridOperation):
|
|
13
|
+
"""
|
|
14
|
+
GridFinder measures grid information from the objects in various ways. Using the names here allow for streamlined integration.
|
|
15
|
+
Unlike other Grid series interfaces, GridExtractors can work on regular images, and should not be dependent on the GridImage class.
|
|
16
|
+
|
|
17
|
+
Parameters:
|
|
18
|
+
nrows (int): Number of rows in the grid.
|
|
19
|
+
ncols (int): Number of columns in the grid.
|
|
20
|
+
|
|
21
|
+
"""
|
|
22
|
+
def __init__(self, nrows: int, ncols: int):
|
|
23
|
+
self.nrows = nrows
|
|
24
|
+
self.ncols = ncols
|
|
25
|
+
|
|
26
|
+
def _operate(self, image: Image) -> pd.DataFrame:
|
|
27
|
+
pass
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
from typing import TYPE_CHECKING
|
|
3
|
+
|
|
4
|
+
if TYPE_CHECKING: from phenotypic import GridImage
|
|
5
|
+
|
|
6
|
+
from phenotypic.abstract import MapModifier
|
|
7
|
+
from phenotypic.abstract import GridOperation
|
|
8
|
+
from phenotypic.util.exceptions_ import GridImageInputError, InterfaceError
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class GridMapModifier(MapModifier, GridOperation):
|
|
12
|
+
def apply(self, image: GridImage, inplace: bool = False) -> GridImage:
|
|
13
|
+
from phenotypic import GridImage
|
|
14
|
+
if not isinstance(image, GridImage): raise GridImageInputError()
|
|
15
|
+
output = super().apply(image=image, inplace=inplace)
|
|
16
|
+
return output
|
|
17
|
+
|
|
18
|
+
def _operate(self, image: GridImage) -> GridImage:
|
|
19
|
+
raise InterfaceError()
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
from typing import TYPE_CHECKING
|
|
3
|
+
if TYPE_CHECKING: from phenotypic import GridImage
|
|
4
|
+
|
|
5
|
+
import pandas as pd
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
from phenotypic.abstract import FeatureMeasure
|
|
9
|
+
from phenotypic.abstract import GridOperation
|
|
10
|
+
from phenotypic.util.exceptions_ import GridImageInputError, OutputValueError
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class GridFeatureMeasure(FeatureMeasure, GridOperation):
|
|
14
|
+
def __init__(self, n_rows: int, n_cols: int):
|
|
15
|
+
self.n_rows = n_rows
|
|
16
|
+
self.n_cols = n_cols
|
|
17
|
+
|
|
18
|
+
def measure(self, image: GridImage) -> pd.DataFrame:
|
|
19
|
+
from phenotypic import GridImage
|
|
20
|
+
if not isinstance(image, GridImage): raise GridImageInputError()
|
|
21
|
+
output = super().measure(image)
|
|
22
|
+
if not isinstance(output, pd.DataFrame): raise OutputValueError("pandas.DataFrame")
|
|
23
|
+
return output
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
from typing import TYPE_CHECKING
|
|
3
|
+
|
|
4
|
+
if TYPE_CHECKING: from phenotypic import Image
|
|
5
|
+
|
|
6
|
+
from typing import Union, Dict
|
|
7
|
+
|
|
8
|
+
from ._image_operation import ImageOperation
|
|
9
|
+
from phenotypic.util.exceptions_ import InterfaceError, OperationFailedError
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class ImageCorrector(ImageOperation):
|
|
13
|
+
"""ImageCorrectors are for general operations that alter every image component such as rotating. These have no integrity checks.
|
|
14
|
+
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
def __init__(self):
|
|
18
|
+
pass
|
|
19
|
+
|
|
20
|
+
def apply(self, image: Image, inplace=False) -> Union[Image, Dict[str, Image]]:
|
|
21
|
+
try:
|
|
22
|
+
if inplace:
|
|
23
|
+
output = self._operate(image)
|
|
24
|
+
else:
|
|
25
|
+
output = self._operate(image.copy())
|
|
26
|
+
return output
|
|
27
|
+
except Exception as e:
|
|
28
|
+
raise OperationFailedError(operation=self.__class__.__name__,
|
|
29
|
+
image_name=image.name,
|
|
30
|
+
err_type=type(e),
|
|
31
|
+
message=str(e)
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
def _operate(self, image: Image) -> Image:
|
|
35
|
+
raise InterfaceError
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
from typing import TYPE_CHECKING
|
|
3
|
+
|
|
4
|
+
if TYPE_CHECKING: from phenotypic import Image
|
|
5
|
+
|
|
6
|
+
import numpy as np
|
|
7
|
+
from ._image_operation import ImageOperation
|
|
8
|
+
from phenotypic.util.exceptions_ import InterfaceError, DataIntegrityError, OperationFailedError
|
|
9
|
+
from phenotypic.util.constants_ import IMAGE_FORMATS
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class ImageEnhancer(ImageOperation):
|
|
13
|
+
def __init__(self):
|
|
14
|
+
pass
|
|
15
|
+
|
|
16
|
+
def apply(self, image: Image, inplace: bool = False) -> Image:
|
|
17
|
+
try:
|
|
18
|
+
# Make a copy for post checking
|
|
19
|
+
imcopy = image.copy()
|
|
20
|
+
|
|
21
|
+
# Reset the object map in case the inherited class forgets to do so
|
|
22
|
+
image.objmap.reset()
|
|
23
|
+
|
|
24
|
+
if inplace:
|
|
25
|
+
output = self._operate(image)
|
|
26
|
+
else:
|
|
27
|
+
output = self._operate(image.copy())
|
|
28
|
+
|
|
29
|
+
if image._image_format.is_array():
|
|
30
|
+
if not np.array_equal(output.array[:], imcopy.array[:]):
|
|
31
|
+
raise DataIntegrityError(component='array', operation=self.__class__.__name__)
|
|
32
|
+
|
|
33
|
+
if not np.array_equal(output.matrix[:], imcopy.matrix[:]):
|
|
34
|
+
raise DataIntegrityError(component='matrix', operation=self.__class__.__name__)
|
|
35
|
+
|
|
36
|
+
return output
|
|
37
|
+
except Exception as e:
|
|
38
|
+
raise OperationFailedError(operation=self.__class__.__name__,
|
|
39
|
+
image_name=image.name,
|
|
40
|
+
err_type=type(e),
|
|
41
|
+
message=str(e)
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
def _operate(self, image: Image) -> Image:
|
|
45
|
+
raise InterfaceError
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
from typing import TYPE_CHECKING
|
|
3
|
+
|
|
4
|
+
if TYPE_CHECKING: from phenotypic import Image
|
|
5
|
+
|
|
6
|
+
from ..util.exceptions_ import InterfaceError
|
|
7
|
+
|
|
8
|
+
class ImageOperation:
|
|
9
|
+
"""
|
|
10
|
+
Represents an abstract base class for image operations.
|
|
11
|
+
|
|
12
|
+
This class provides a common abstract for applying transformations or
|
|
13
|
+
operations to images. It defines a method to apply the operation and
|
|
14
|
+
enforces the implementation of the specific operation in a subclass.
|
|
15
|
+
Users can apply operations either in-place or on a copy of the image.
|
|
16
|
+
|
|
17
|
+
"""
|
|
18
|
+
def apply(self, image, inplace=False) -> Image:
|
|
19
|
+
"""
|
|
20
|
+
Applies a certain operation to an image, either in-place or on a copy.
|
|
21
|
+
|
|
22
|
+
Args:
|
|
23
|
+
image (Image): The input image to apply the operation on.
|
|
24
|
+
inplace (bool): If True, modifies the image in place; otherwise,
|
|
25
|
+
operates on a copy of the image.
|
|
26
|
+
|
|
27
|
+
Returns:
|
|
28
|
+
Image: The modified image after applying the operation.
|
|
29
|
+
"""
|
|
30
|
+
if inplace:
|
|
31
|
+
return self._operate(image)
|
|
32
|
+
else:
|
|
33
|
+
return self._operate(image.copy())
|
|
34
|
+
|
|
35
|
+
def _operate(self, image: Image) -> Image:
|
|
36
|
+
"""
|
|
37
|
+
A placeholder for the subfunction for an image operator for processing image objects.
|
|
38
|
+
|
|
39
|
+
This method is called from apply and must be implemented in a subclass. This allows for checks for data integrity to be made.
|
|
40
|
+
|
|
41
|
+
Args:
|
|
42
|
+
image (Image): The image object to be processed by internal operations.
|
|
43
|
+
|
|
44
|
+
Raises:
|
|
45
|
+
InterfaceError: Raised if the method is not implemented in a subclass.
|
|
46
|
+
"""
|
|
47
|
+
raise InterfaceError
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
from typing import TYPE_CHECKING
|
|
3
|
+
|
|
4
|
+
if TYPE_CHECKING: from phenotypic import Image
|
|
5
|
+
|
|
6
|
+
import numpy as np
|
|
7
|
+
|
|
8
|
+
from ._image_operation import ImageOperation
|
|
9
|
+
from ..util.constants_ import IMAGE_FORMATS
|
|
10
|
+
from phenotypic.util.exceptions_ import OperationFailedError, InterfaceError, DataIntegrityError
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
# <<Interface>>
|
|
14
|
+
class MapModifier(ImageOperation):
|
|
15
|
+
"""Map modifiers edit the object map and are used for removing, combining, and re-ordering objects."""
|
|
16
|
+
|
|
17
|
+
def apply(self, image: Image, inplace: bool = False) -> Image:
|
|
18
|
+
try:
|
|
19
|
+
imcopy = image.copy()
|
|
20
|
+
|
|
21
|
+
if inplace:
|
|
22
|
+
output = self._operate(image)
|
|
23
|
+
else:
|
|
24
|
+
output = self._operate(image.copy())
|
|
25
|
+
|
|
26
|
+
# TODO: Fix this check
|
|
27
|
+
if output._image_format.is_array():
|
|
28
|
+
if not np.array_equal(imcopy.array[:], output.array[:]): raise DataIntegrityError(
|
|
29
|
+
component='array', operation=self.__class__.__name__, image_name=image.name
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
# TODO: Fix this check
|
|
33
|
+
# if not np.array_equal(imcopy.matrix[:], output.matrix[:]): raise DataIntegrityError(
|
|
34
|
+
# component='matrix', operation=self.__class__.__name__, image_name=image.name
|
|
35
|
+
# )
|
|
36
|
+
|
|
37
|
+
if not np.array_equal(imcopy.enh_matrix[:], output.enh_matrix[:]): raise DataIntegrityError(
|
|
38
|
+
component='enh_matrix', operation=self.__class__.__name__, image_name=image.name
|
|
39
|
+
)
|
|
40
|
+
return output
|
|
41
|
+
except DataIntegrityError as e:
|
|
42
|
+
raise e
|
|
43
|
+
except Exception as e:
|
|
44
|
+
raise OperationFailedError(operation=self.__class__.__name__,
|
|
45
|
+
image_name=image.name,
|
|
46
|
+
err_type=type(e),
|
|
47
|
+
message=str(e)
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
def _operate(self, image: Image) -> Image:
|
|
51
|
+
raise InterfaceError
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
from typing import TYPE_CHECKING
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
if TYPE_CHECKING: from phenotypic import Image
|
|
6
|
+
|
|
7
|
+
import numpy as np
|
|
8
|
+
from ._image_operation import ImageOperation
|
|
9
|
+
from phenotypic.util.exceptions_ import OperationFailedError, DataIntegrityError, InterfaceError
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
# <<Interface>>
|
|
13
|
+
class ObjectDetector(ImageOperation):
|
|
14
|
+
"""ObjectDetectors are for detecting objects in an image. They change the image object mask and map."""
|
|
15
|
+
|
|
16
|
+
def __init__(self):
|
|
17
|
+
pass
|
|
18
|
+
|
|
19
|
+
def apply(self, image: Image, inplace: bool = False) -> Image:
|
|
20
|
+
try:
|
|
21
|
+
imcopy = image.copy()
|
|
22
|
+
|
|
23
|
+
if inplace:
|
|
24
|
+
output = self._operate(image)
|
|
25
|
+
else:
|
|
26
|
+
output = self._operate(image.copy())
|
|
27
|
+
|
|
28
|
+
# Post Operation Checks
|
|
29
|
+
if not np.array_equal(imcopy.matrix[:], output.matrix[:]): raise DataIntegrityError(component='matrix',
|
|
30
|
+
operation=self.__class__.__name__,
|
|
31
|
+
image_name=image.name
|
|
32
|
+
)
|
|
33
|
+
if not np.array_equal(imcopy.enh_matrix[:], output.enh_matrix[:]): raise DataIntegrityError(component='enh_matrix',
|
|
34
|
+
operation=self.__class__.__name__,
|
|
35
|
+
image_name=image.name
|
|
36
|
+
)
|
|
37
|
+
output.objmap.relabel()
|
|
38
|
+
return output
|
|
39
|
+
except DataIntegrityError as e:
|
|
40
|
+
raise e
|
|
41
|
+
except Exception as e:
|
|
42
|
+
raise OperationFailedError(operation=self.__class__.__name__,
|
|
43
|
+
image_name=image.name,
|
|
44
|
+
err_type=type(e),
|
|
45
|
+
message=str(e)
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
def _operate(self, image: Image) -> Image:
|
|
49
|
+
raise InterfaceError
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
from ._object_detector import ObjectDetector
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
# <<Interface>>
|
|
6
|
+
class ThresholdDetector(ObjectDetector):
|
|
7
|
+
"""ThresholdDetectors are a type of ObjectDetector that use a threshold, such as Otsu's threshold, to detect objects in an image. They change the image object mask and map."""
|
|
8
|
+
pass
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
from .handlers._image_hsv_handler import ImageHsvHandler
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class Image(ImageHsvHandler):
|
|
5
|
+
"""A comprehensive class for handling image processing, including manipulation, information sync, metadata management, and format conversion.
|
|
6
|
+
|
|
7
|
+
The `Image` class is designed to load, process, and manage image data using different
|
|
8
|
+
representation formats (e.g., arrays and matrices). This class allows for metadata editing,
|
|
9
|
+
schema definition, and subcomponent handling to streamline image processing tasks.
|
|
10
|
+
|
|
11
|
+
Note:
|
|
12
|
+
- If the input_image is 2-D, the ImageHandler leave the array form as None
|
|
13
|
+
- If the input_image is 3-D, the ImageHandler will automatically set the matrix component to the grayscale representation.
|
|
14
|
+
- Added in v0.5.0, HSV handling support
|
|
15
|
+
"""
|
|
16
|
+
pass
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
from ._image import Image
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def imread(filepath: str):
|
|
6
|
+
"""
|
|
7
|
+
Reads an image from the specified file path using the `Image().imread` method.
|
|
8
|
+
|
|
9
|
+
Args:
|
|
10
|
+
filepath (str): The path to the image file to be read.
|
|
11
|
+
|
|
12
|
+
Returns:
|
|
13
|
+
Image: An `Image` object containing the data read from the specified file.
|
|
14
|
+
"""
|
|
15
|
+
filepath = Path(filepath)
|
|
16
|
+
return Image().imread(filepath)
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Overview
|
|
3
|
+
========
|
|
4
|
+
|
|
5
|
+
The ``accessors`` submodule provides a comprehensive set of tools and interfaces for accessing and interacting with the various data components of an image. These components range from raw pixel data and matrix representations to object masks, metadata, and high-level measurements. Additionally, it includes utilities that streamline development workflows involving image processing and analysis.
|
|
6
|
+
|
|
7
|
+
The goal is to provide efficient, standardized access to image data while enabling intuitive and flexible integration for advanced image manipulation and analysis tasks.
|
|
8
|
+
|
|
9
|
+
Image Data Containers
|
|
10
|
+
=====================
|
|
11
|
+
|
|
12
|
+
This category includes classes designed to offer fundamental abstractions for accessing image data in various representations:
|
|
13
|
+
|
|
14
|
+
1. :class:`ImageArray`
|
|
15
|
+
Represents the multichannel pixel data of an image when provided. Useful for direct manipulation and pixel-wise operations.
|
|
16
|
+
|
|
17
|
+
2. :class:`ImageMatrix`
|
|
18
|
+
Provides structured access to the image in its matrix form, offering methods suited for mathematical or analytical operations on image data. Automatically converted from RGB using weighted luminance conversion.
|
|
19
|
+
|
|
20
|
+
3. :class:`ImageEnhancedMatrix`
|
|
21
|
+
An enhanceable copy of the image matrix to improve detection while maintaining the original image data integrity.
|
|
22
|
+
|
|
23
|
+
Objects and Object Mapping
|
|
24
|
+
==========================
|
|
25
|
+
|
|
26
|
+
These classes manage object-level abstractions and their corresponding data mappings:
|
|
27
|
+
|
|
28
|
+
1. :class:`ObjectMap`
|
|
29
|
+
Represents a mapping of detected objects or regions in the image to their associated IDs. Useful for:
|
|
30
|
+
|
|
31
|
+
- Object identification
|
|
32
|
+
- Bounding box management
|
|
33
|
+
- Spatial relationships between objects.
|
|
34
|
+
- Region-based calculations
|
|
35
|
+
|
|
36
|
+
2. :class:`ObjectMask`
|
|
37
|
+
An abstract specialized for working with binary masks of objects. Useful for morphological operations such as erosion, dilation, and closing.
|
|
38
|
+
|
|
39
|
+
Note:
|
|
40
|
+
Changes to the object mask will cause relabeling of the object map
|
|
41
|
+
|
|
42
|
+
High-Level Object Interfaces
|
|
43
|
+
============================
|
|
44
|
+
|
|
45
|
+
These classes provide consolidated access to manage multiple object-level and metadata components.
|
|
46
|
+
|
|
47
|
+
1. :class:`ObjectsAccessor`
|
|
48
|
+
Facilitates high-level interaction with detected objects within an image. Features include:
|
|
49
|
+
|
|
50
|
+
- Feature extraction
|
|
51
|
+
- Object comparison
|
|
52
|
+
- Visualization of detected objects
|
|
53
|
+
|
|
54
|
+
2. :class:`MeasurementContainer` *(Coming Soon!)*
|
|
55
|
+
Encapsulates measurements or attributes associated with objects, such as size, shape, or intensity. Organizes measurements for reproducibility and easier analysis.
|
|
56
|
+
|
|
57
|
+
3. :class:`MetadataContainer` *(Coming Soon!)*
|
|
58
|
+
Responsible for storing and accessing metadata associated with the image. Examples of metadata include:
|
|
59
|
+
|
|
60
|
+
- Acquisition details
|
|
61
|
+
- Resolution
|
|
62
|
+
- Additional contextual information.
|
|
63
|
+
|
|
64
|
+
HSV (Hue, Saturation, Brightness) Interface
|
|
65
|
+
===========================================
|
|
66
|
+
|
|
67
|
+
1. :class:`HsvAccessor`
|
|
68
|
+
Provides detailed access to the HSV (Hue, Saturation, Brightness) color space components of an image. This abstract includes support for:
|
|
69
|
+
|
|
70
|
+
- Direct pixel access via ``__getitem__`` and ``__setitem__``.
|
|
71
|
+
- Advanced utilities for image visualization and object-specific manipulation in the HSV domain.
|
|
72
|
+
|
|
73
|
+
**Key Methods:**
|
|
74
|
+
- ``shape``: Returns the dimensions of the HSV representation.
|
|
75
|
+
- ``copy``: Creates and returns a full copy of the HSV data structure.
|
|
76
|
+
- ``histogram``: Computes a histogram for the HSV image or parts of it.
|
|
77
|
+
- ``show``: Displays the HSV image.
|
|
78
|
+
- ``show_objects``: Visualizes objects overlaid on the HSV image.
|
|
79
|
+
- ``extract_obj_hue``, ``extract_obj_saturation``, ``extract_obj_brightness``, ``extract_obj``:
|
|
80
|
+
Extract specific HSV component data for individual image objects, aiding in targeted color-based analysis.
|
|
81
|
+
|
|
82
|
+
Purpose and Use Cases
|
|
83
|
+
=====================
|
|
84
|
+
|
|
85
|
+
The ``accessors`` submodule is designed for developers and researchers working on advanced image processing tasks. It is particularly suited for:
|
|
86
|
+
|
|
87
|
+
- Object detection and feature extraction
|
|
88
|
+
- HSV color-space analysis
|
|
89
|
+
- Grid Analysis
|
|
90
|
+
- Metadata association for image datasets
|
|
91
|
+
- Advanced mathematical and matrix operations on image data.
|
|
92
|
+
|
|
93
|
+
"""
|
|
94
|
+
from ._image_accessor import ImageAccessor
|
|
95
|
+
from ._image_data_accessor import ImageDataAccessor
|
|
96
|
+
from ._image_array_accessor import ImageArray
|
|
97
|
+
from ._image_matrix_accessor import ImageMatrix
|
|
98
|
+
from ._image_enh_matrix_accessor import ImageEnhancedMatrix
|
|
99
|
+
from ._objmap_accessor import ObjectMap
|
|
100
|
+
from ._objmask_accessor import ObjectMask
|
|
101
|
+
|
|
102
|
+
from ._image_objects_accessor import ObjectsAccessor
|
|
103
|
+
# from ._measurement_container_interface import MeasurementAccessor
|
|
104
|
+
from ._metadata_accessor import MetadataAccessor
|
|
105
|
+
|
|
106
|
+
from ._hsv_accessor import HsvAccessor
|
|
107
|
+
from ._grid_accessor import GridAccessor
|
|
108
|
+
|
|
109
|
+
# Define __all__ to include all imported objects
|
|
110
|
+
__all__ = [
|
|
111
|
+
"ImageAccessor",
|
|
112
|
+
"ImageDataAccessor",
|
|
113
|
+
"ImageArray",
|
|
114
|
+
"ImageMatrix",
|
|
115
|
+
"ImageEnhancedMatrix",
|
|
116
|
+
"ObjectMap",
|
|
117
|
+
"ObjectMask",
|
|
118
|
+
"ObjectsAccessor",
|
|
119
|
+
"HsvAccessor",
|
|
120
|
+
"GridAccessor",
|
|
121
|
+
"MetadataAccessor",
|
|
122
|
+
]
|