Coverage for openhcs/processing/backends/lib_registry/pyclesperanto_registry.py: 48.5%

54 statements  

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

1""" 

2Clean Pyclesperanto Registry Implementation 

3 

4Implements clean abstraction with internal library-specific logic. 

5All pyclesperanto-specific details (dtype conversions, Z-parameters, etc.) 

6are handled internally without leaking into the ABC. 

7""" 

8from __future__ import annotations 

9 

10import inspect 

11import numpy as np 

12from typing import Tuple, List 

13from openhcs.constants import MemoryType 

14from openhcs.core.utils import optional_import 

15from .unified_registry import LibraryRegistryBase, RuntimeTestingRegistryBase 

16 

17cle = optional_import("pyclesperanto") 

18 

19 

20class PyclesperantoRegistry(RuntimeTestingRegistryBase): 

21 """Clean pyclesperanto registry with internal library-specific logic.""" 

22 

23 # Registry name for auto-registration 

24 _registry_name = 'pyclesperanto' 

25 

26 # Library-specific exclusions extending common ones 

27 EXCLUSIONS = LibraryRegistryBase.COMMON_EXCLUSIONS | { 

28 'push_zyx', 'pull_zyx', 'create_zyx', 'set_wait_for_kernel_finish', 

29 'get_device', 'select_device', 'list_available_devices' 

30 } 

31 

32 # Modules to scan for functions 

33 MODULES_TO_SCAN = [""] # Pyclesperanto functions are in main namespace 

34 

35 # Memory type for this registry 

36 MEMORY_TYPE = MemoryType.PYCLESPERANTO.value 

37 

38 # Float dtype for this registry 

39 FLOAT_DTYPE = np.float32 

40 

41 def __init__(self): 

42 super().__init__("pyclesperanto") 

43 # Internal constants for dtype handling 

44 self._BINARY_FUNCTIONS = {'binary_infsup', 'binary_supinf'} 

45 self._UINT8_FUNCTIONS = {'mode', 'mode_box', 'mode_sphere'} 

46 self._IMAGE_PARAM_NAMES = {"src", "source", "image", "input", "src1", "input_image", "input_image0"} 

47 

48 # ===== ESSENTIAL ABC METHODS ===== 

49 def get_library_version(self) -> str: 

50 return cle.__version__ 

51 

52 def is_library_available(self) -> bool: 

53 return bool(cle) 

54 

55 def get_library_object(self): 

56 return cle 

57 

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

59 """Get module patterns for pyclesperanto (includes 'cle' alternative).""" 

60 return ['pyclesperanto', 'cle'] 

61 

62 # ===== HOOK IMPLEMENTATIONS ===== 

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

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

65 

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

67 return (first_param.name.lower() in self._IMAGE_PARAM_NAMES and 

68 first_param.kind in (inspect.Parameter.POSITIONAL_ONLY, 

69 inspect.Parameter.POSITIONAL_OR_KEYWORD)) 

70 

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

72 return self._convert_input_dtype(image, func_name) 

73 

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

75 return self._convert_output_dtype(result, original_image.dtype, func_name) 

76 

77 # ===== LIBRARY-SPECIFIC HELPER METHODS ===== 

78 def _convert_input_dtype(self, image, func_name): 

79 """Internal dtype conversion logic.""" 

80 if func_name in self._BINARY_FUNCTIONS: 

81 return ((image > 0.5) * 255).astype(np.uint8) 

82 elif func_name in self._UINT8_FUNCTIONS: 

83 return (np.clip(image, 0, 1) * 255).astype(np.uint8) 

84 return image 

85 

86 def _convert_output_dtype(self, result, original_dtype, func_name): 

87 """Internal output dtype conversion.""" 

88 if func_name in self._BINARY_FUNCTIONS or func_name in self._UINT8_FUNCTIONS: 

89 if result.dtype != original_dtype: 

90 if result.dtype == np.uint8 and original_dtype == np.float32: 

91 return result.astype(np.float32) / 255.0 

92 elif result.dtype == np.bool_ and original_dtype == np.float32: 

93 return result.astype(np.float32) 

94 return result 

95 

96 # ===== LIBRARY-SPECIFIC IMPLEMENTATIONS ===== 

97 def _stack_2d_results(self, func, test_3d): 

98 """Stack 2D results using CLE.""" 

99 results = [func(test_3d[z]) for z in range(test_3d.shape[0])] 

100 return cle.concatenate_along_z(*results) 

101 

102 def _arrays_close(self, arr1, arr2): 

103 """Compare arrays using CLE.""" 

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

105 

106