Automatic Dtype Conversion
OpenHCS provides transparent automatic data type conversion to handle the diverse requirements of different GPU libraries while maintaining pipeline consistency.
Overview
Different GPU libraries have specific data type requirements that can conflict with OpenHCS’s standardized float32 [0,1] pipeline. OpenHCS automatically handles these conversions behind the scenes, eliminating warnings and ensuring correct function behavior.
Why Dtype Conversion is Needed
GPU libraries often have strict data type requirements:
# pyclesperanto binary functions expect binary (0/1) input
import pyclesperanto as cle
image = np.random.rand(100, 100).astype(np.float32) # [0,1] range
result = cle.binary_infsup(image) # ❌ Warning: "expected binary, float given"
# pyclesperanto mode functions require uint8 input
result = cle.mode(image) # ❌ Warning: "mode only support uint8 pixel type"
Without automatic conversion, users would need to manually handle these conversions:
# Manual conversion (what you DON'T need to do in OpenHCS)
binary_image = ((image > 0.5) * 255).astype(np.uint8) # Manual binary conversion
result = cle.binary_infsup(binary_image)
result = result.astype(np.float32) / 255.0 # Manual conversion back
How It Works
OpenHCS automatically detects function requirements and applies appropriate conversions:
from openhcs.processing.func_registry import get_function_by_name
# Get registered functions (these have automatic dtype conversion)
binary_infsup = get_function_by_name('binary_infsup', 'pyclesperanto')
mode = get_function_by_name('mode', 'pyclesperanto')
# Use with standard OpenHCS float32 input
image = np.random.rand(100, 100).astype(np.float32) # [0,1] range
# These work without warnings or manual conversion
result1 = binary_infsup(image) # ✅ Automatic binary conversion
result2 = mode(image) # ✅ Automatic uint8 conversion
# Results are automatically converted back to float32 [0,1]
print(result1.dtype) # float32
print(result2.dtype) # float32
Supported Conversions
Binary Functions
Functions that require binary (0/1) input:
# Affected functions:
- binary_infsup
- binary_supinf
# Conversion process:
# 1. Input: float32 [0,1]
# 2. Threshold: values > 0.5 become 1, others become 0
# 3. Scale: binary {0,1} → uint8 {0,255}
# 4. Execute function with uint8 binary input
# 5. Convert result back to float32 [0,1]
Example:
input_image = np.array([[0.2, 0.7], [0.4, 0.9]], dtype=np.float32)
# Internal conversion:
# [[0.2, 0.7], [0.4, 0.9]] → threshold at 0.5 → [[0, 1], [0, 1]]
# [[0, 1], [0, 1]] → scale to uint8 → [[0, 255], [0, 255]]
result = binary_infsup(input_image)
# Result is converted back to float32 [0,1] range
UINT8 Functions
Functions that require 8-bit unsigned integer input:
# Affected functions:
- mode
- mode_box
- mode_sphere
# Conversion process:
# 1. Input: float32 [0,1]
# 2. Clip: ensure values are in [0,1] range
# 3. Scale: [0,1] → [0,255] and convert to uint8
# 4. Execute function with uint8 input
# 5. Convert result back to float32 [0,1]
Example:
input_image = np.array([[0.2, 0.7], [0.4, 0.9]], dtype=np.float32)
# Internal conversion:
# [[0.2, 0.7], [0.4, 0.9]] → scale to uint8 → [[51, 178], [102, 229]]
result = mode(input_image)
# Result is converted back to float32 [0,1] range
Pipeline Integration
Dtype conversion integrates seamlessly with OpenHCS pipelines:
from openhcs.processing.func_registry import get_function_by_name
from openhcs.processing.step import FunctionStep
# Get functions with automatic dtype conversion
gaussian = get_function_by_name('gaussian_filter', 'skimage')
binary_op = get_function_by_name('binary_infsup', 'pyclesperanto')
mode_filter = get_function_by_name('mode', 'pyclesperanto')
# Create pipeline steps
steps = [
FunctionStep(gaussian, sigma=1.0), # float32 → float32
FunctionStep(binary_op), # float32 → auto convert → float32
FunctionStep(mode_filter), # float32 → auto convert → float32
]
# All conversions happen automatically
# Pipeline maintains float32 [0,1] consistency throughout
Best Practices
Input Data Preparation
# ✅ Good: Use standard OpenHCS format
image = load_image().astype(np.float32)
if image.max() > 1.0:
image = image / image.max() # Normalize to [0,1]
# ❌ Avoid: Manual dtype conversion for specific functions
# OpenHCS handles this automatically
Function Selection
# ✅ Good: Use registered functions for automatic conversion
from openhcs.processing.func_registry import get_function_by_name
binary_infsup = get_function_by_name('binary_infsup', 'pyclesperanto')
# ❌ Avoid: Direct library calls (no automatic conversion)
import pyclesperanto as cle
result = cle.binary_infsup(image) # May show warnings
Performance Considerations
# Dtype conversion has minimal performance impact:
✅ Only applied to functions that need it (small subset of functions)
✅ Conversion operations are fast (simple scaling/thresholding)
✅ No impact on functions that don't require conversion
✅ GPU memory transfers remain optimized
Troubleshooting
If you encounter dtype-related issues:
# Check if function has automatic conversion
from openhcs.processing.func_registry import get_function_by_name
func = get_function_by_name('function_name', 'library_name')
if func is None:
print("Function not found in registry")
else:
print("Function has automatic dtype conversion")
# Verify input data format
print(f"Input dtype: {image.dtype}")
print(f"Input range: [{image.min():.3f}, {image.max():.3f}]")
# Expected: dtype=float32, range=[0.0, 1.0]
For functions not yet covered by automatic conversion, you can request support by filing an issue with the specific function name and library.