Coverage for openhcs/processing/backends/lib_registry/scikit_image_registry.py: 85.0%
40 statements
« prev ^ index » next coverage.py v7.10.7, created at 2025-10-01 18:33 +0000
« prev ^ index » next coverage.py v7.10.7, created at 2025-10-01 18:33 +0000
1"""
2Clean Scikit-Image Registry Implementation
4Implements clean abstraction with internal library-specific logic.
5All scikit-image-specific details (array compliance, channel_axis, etc.)
6are handled internally without leaking into the ABC.
7"""
8from __future__ import annotations
10import inspect
11import numpy as np
12from functools import wraps
13from typing import Tuple, Callable, List, Any, Dict
15from openhcs.constants import MemoryType
16from openhcs.core.utils import optional_import
17from .unified_registry import LibraryRegistryBase, RuntimeTestingRegistryBase, ProcessingContract, FunctionMetadata
19skimage = optional_import("skimage")
22class SkimageRegistry(RuntimeTestingRegistryBase):
23 """Clean scikit-image registry with internal array compliance logic."""
25 # Library-specific exclusions (uses common ones)
26 EXCLUSIONS = LibraryRegistryBase.COMMON_EXCLUSIONS
28 # Modules to scan for functions
29 MODULES_TO_SCAN = ['filters', 'morphology', 'segmentation', 'feature',
30 'measure', 'transform', 'restoration', 'exposure']
32 # Memory type for this registry
33 MEMORY_TYPE = MemoryType.NUMPY.value
35 # Float dtype for this registry
36 FLOAT_DTYPE = np.float32
38 def __init__(self):
39 super().__init__("skimage")
41 # ===== ESSENTIAL ABC METHODS =====
42 def get_library_version(self) -> str:
43 return skimage.__version__
45 def is_library_available(self) -> bool:
46 return skimage is not None
48 def get_library_object(self):
49 return skimage
51 def get_module_patterns(self) -> List[str]:
52 """Get module patterns for scikit-image."""
53 return ['skimage']
55 def get_display_name(self) -> str:
56 """Get proper display name for scikit-image."""
57 return 'scikit-image'
59 # ===== HOOK IMPLEMENTATIONS =====
60 def _create_array(self, shape: Tuple[int, ...], dtype):
61 return np.random.rand(*shape).astype(dtype)
63 def _check_first_parameter(self, first_param, func_name: str) -> bool:
64 return (first_param.annotation in [np.ndarray, "np.ndarray", "ndarray"] or
65 first_param.name.lower() in {'image', 'input', 'array', 'img'})
67 def _preprocess_input(self, image, func_name: str):
68 return image # No preprocessing needed for scikit-image
70 def _postprocess_output(self, result, original_image, func_name: str):
71 # ProcessingContract system handles dimensional behavior - no categorization needed
72 return result
74 # ===== LIBRARY-SPECIFIC IMPLEMENTATIONS =====
75 def _generate_function_name(self, name: str, module_name: str) -> str:
76 """Generate function name with module prefix."""
77 return f"{module_name}.{name}"
79 def _stack_2d_results(self, func, test_3d):
80 """Stack 2D results using NumPy."""
81 return np.stack([func(test_3d[z]) for z in range(test_3d.shape[0])])
83 def _arrays_close(self, arr1, arr2):
84 """Compare arrays using NumPy."""
85 return np.allclose(arr1, arr2, rtol=1e-5, atol=1e-8)