Coverage for openhcs/processing/backends/lib_registry/cupy_registry.py: 71.1%
45 statements
« prev ^ index » next coverage.py v7.10.3, created at 2025-08-14 05:57 +0000
« prev ^ index » next coverage.py v7.10.3, created at 2025-08-14 05:57 +0000
1"""
2Clean CuPy Registry Implementation
4Implements clean abstraction with internal library-specific logic.
5All CuPy-specific details (GPU handling, CuCIM integration, 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, ProcessingContract, FunctionMetadata
19cp = optional_import("cupy")
20cucim = optional_import("cucim")
21cucim_skimage = optional_import("cucim.skimage")
24class CupyRegistry(LibraryRegistryBase):
25 """Clean CuPy registry with internal GPU handling logic."""
27 # Library-specific exclusions (uses common ones)
28 EXCLUSIONS = LibraryRegistryBase.COMMON_EXCLUSIONS
30 # Modules to scan for functions
31 MODULES_TO_SCAN = ['filters', 'morphology', 'measure', 'segmentation',
32 'feature', 'restoration', 'transform', 'exposure',
33 'color', 'util']
35 # Memory type for this registry
36 MEMORY_TYPE = MemoryType.CUPY.value
38 # Float dtype for this registry
39 FLOAT_DTYPE = cp.float32
41 def __init__(self):
42 super().__init__("cupy")
44 # ===== ESSENTIAL ABC METHODS =====
45 def get_library_version(self) -> str:
46 return cucim.__version__
48 def is_library_available(self) -> bool:
49 return cp is not None and cucim_skimage is not None
51 def get_library_object(self):
52 return cucim_skimage
54 # ===== HOOK IMPLEMENTATIONS =====
55 def _create_array(self, shape: Tuple[int, ...], dtype):
56 return cp.random.rand(*shape).astype(dtype)
58 def _check_first_parameter(self, first_param, func_name: str) -> bool:
59 return first_param.name.lower() in {'image', 'input', 'array', 'img'}
61 def _preprocess_input(self, image, func_name: str):
62 return image # No preprocessing needed for CuPy
64 def _postprocess_output(self, result, original_image, func_name: str):
65 # ProcessingContract system handles dimensional behavior - no categorization needed
66 return result
68 # ===== LIBRARY-SPECIFIC IMPLEMENTATIONS =====
69 def _generate_function_name(self, name: str, module_name: str) -> str:
70 """Generate function name - original for filters, prefixed for others."""
71 return name if module_name == 'filters' else f"{module_name}_{name}"
73 def _generate_tags(self, func_name: str) -> List[str]:
74 """Generate tags with GPU tag."""
75 tags = func_name.lower().replace("_", " ").split()
76 tags.append("gpu")
77 return tags
79 def _stack_2d_results(self, func, test_3d):
80 """Stack 2D results using CuPy."""
81 results = [func(test_3d[z]) for z in range(test_3d.shape[0])]
82 return cp.stack(results)
84 def _arrays_close(self, arr1, arr2):
85 """Compare arrays using CuPy."""
86 return np.allclose(arr1.get(), arr2.get(), rtol=1e-5, atol=1e-8)
88 def _expand_2d_to_3d(self, array_2d):
89 """Expand 2D array to 3D using CuPy expansion."""
90 return array_2d[None, ...]