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

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 inspect 

11import numpy as np 

12from functools import wraps 

13from typing import Tuple, Callable, List, Any, Dict 

14 

15from openhcs.constants import MemoryType 

16from openhcs.core.utils import optional_import 

17from .unified_registry import LibraryRegistryBase, RuntimeTestingRegistryBase, ProcessingContract, FunctionMetadata 

18 

19skimage = optional_import("skimage") 

20 

21 

22class SkimageRegistry(RuntimeTestingRegistryBase): 

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

24 

25 # Library-specific exclusions (uses common ones) 

26 EXCLUSIONS = LibraryRegistryBase.COMMON_EXCLUSIONS 

27 

28 # Modules to scan for functions 

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

30 'measure', 'transform', 'restoration', 'exposure'] 

31 

32 # Memory type for this registry 

33 MEMORY_TYPE = MemoryType.NUMPY.value 

34 

35 # Float dtype for this registry 

36 FLOAT_DTYPE = np.float32 

37 

38 def __init__(self): 

39 super().__init__("skimage") 

40 

41 # ===== ESSENTIAL ABC METHODS ===== 

42 def get_library_version(self) -> str: 

43 return skimage.__version__ 

44 

45 def is_library_available(self) -> bool: 

46 return skimage is not None 

47 

48 def get_library_object(self): 

49 return skimage 

50 

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

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

53 return ['skimage'] 

54 

55 def get_display_name(self) -> str: 

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

57 return 'scikit-image' 

58 

59 # ===== HOOK IMPLEMENTATIONS ===== 

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

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

62 

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'}) 

66 

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

68 return image # No preprocessing needed for scikit-image 

69 

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

71 # ProcessingContract system handles dimensional behavior - no categorization needed 

72 return result 

73 

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}" 

78 

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])]) 

82 

83 def _arrays_close(self, arr1, arr2): 

84 """Compare arrays using NumPy.""" 

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

86 

87