Coverage for openhcs/processing/backends/lib_registry/scikit_image_registry.py: 84.6%

39 statements  

« prev     ^ index     » next       coverage.py v7.11.0, created at 2025-11-04 02:09 +0000

1""" 

2Clean Scikit-Image Registry Implementation 

3 

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 

9 

10import numpy as np 

11from typing import Tuple, List 

12 

13from openhcs.constants import MemoryType 

14from openhcs.core.utils import optional_import 

15from .unified_registry import LibraryRegistryBase, RuntimeTestingRegistryBase 

16 

17skimage = optional_import("skimage") 

18 

19 

20class SkimageRegistry(RuntimeTestingRegistryBase): 

21 """Clean scikit-image registry with internal array compliance logic.""" 

22 

23 # Registry name for auto-registration 

24 _registry_name = 'skimage' 

25 

26 # Library-specific exclusions (uses common ones) 

27 EXCLUSIONS = LibraryRegistryBase.COMMON_EXCLUSIONS 

28 

29 # Modules to scan for functions 

30 MODULES_TO_SCAN = ['filters', 'morphology', 'segmentation', 'feature', 

31 'measure', 'transform', 'restoration', 'exposure'] 

32 

33 # Memory type for this registry 

34 MEMORY_TYPE = MemoryType.NUMPY.value 

35 

36 # Float dtype for this registry 

37 FLOAT_DTYPE = np.float32 

38 

39 def __init__(self): 

40 super().__init__("skimage") 

41 

42 # ===== ESSENTIAL ABC METHODS ===== 

43 def get_library_version(self) -> str: 

44 return skimage.__version__ 

45 

46 def is_library_available(self) -> bool: 

47 return skimage is not None 

48 

49 def get_library_object(self): 

50 return skimage 

51 

52 def get_module_patterns(self) -> List[str]: 

53 """Get module patterns for scikit-image.""" 

54 return ['skimage'] 

55 

56 def get_display_name(self) -> str: 

57 """Get proper display name for scikit-image.""" 

58 return 'scikit-image' 

59 

60 # ===== HOOK IMPLEMENTATIONS ===== 

61 def _create_array(self, shape: Tuple[int, ...], dtype): 

62 return np.random.rand(*shape).astype(dtype) 

63 

64 def _check_first_parameter(self, first_param, func_name: str) -> bool: 

65 return (first_param.annotation in [np.ndarray, "np.ndarray", "ndarray"] or 

66 first_param.name.lower() in {'image', 'input', 'array', 'img'}) 

67 

68 def _preprocess_input(self, image, func_name: str): 

69 return image # No preprocessing needed for scikit-image 

70 

71 def _postprocess_output(self, result, original_image, func_name: str): 

72 # ProcessingContract system handles dimensional behavior - no categorization needed 

73 return result 

74 

75 # ===== LIBRARY-SPECIFIC IMPLEMENTATIONS ===== 

76 def _generate_function_name(self, name: str, module_name: str) -> str: 

77 """Generate function name with module prefix.""" 

78 return f"{module_name}.{name}" 

79 

80 def _stack_2d_results(self, func, test_3d): 

81 """Stack 2D results using NumPy.""" 

82 return np.stack([func(test_3d[z]) for z in range(test_3d.shape[0])]) 

83 

84 def _arrays_close(self, arr1, arr2): 

85 """Compare arrays using NumPy.""" 

86 return np.allclose(arr1, arr2, rtol=1e-5, atol=1e-8) 

87 

88