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

38 statements  

« prev     ^ index     » next       coverage.py v7.10.3, created at 2025-08-14 05:57 +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, ProcessingContract, FunctionMetadata 

18 

19skimage = optional_import("skimage") 

20 

21 

22class SkimageRegistry(LibraryRegistryBase): 

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 # ===== HOOK IMPLEMENTATIONS ===== 

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

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

54 

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

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

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

58 

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

60 return image # No preprocessing needed for scikit-image 

61 

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

63 # ProcessingContract system handles dimensional behavior - no categorization needed 

64 return result 

65 

66 # ===== LIBRARY-SPECIFIC IMPLEMENTATIONS ===== 

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

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

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

70 

71 def _stack_2d_results(self, func, test_3d): 

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

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

74 

75 def _arrays_close(self, arr1, arr2): 

76 """Compare arrays using NumPy.""" 

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

78 

79 def _expand_2d_to_3d(self, array_2d): 

80 """Expand 2D array to 3D using NumPy expansion.""" 

81 return np.expand_dims(array_2d, 0)