Skip to content

Commit 09d4db6

Browse files
committed
feat: Add cuslide2 plugin examples and testing tools
1 parent 4612258 commit 09d4db6

File tree

3 files changed

+591
-0
lines changed

3 files changed

+591
-0
lines changed
Lines changed: 311 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,311 @@
1+
#!/usr/bin/env python3
2+
"""
3+
cuslide2 Plugin Demo with nvImageCodec GPU Acceleration
4+
5+
This example demonstrates how to use the cuslide2 plugin for GPU-accelerated
6+
JPEG/JPEG2000 decoding in digital pathology images.
7+
8+
Features:
9+
- Automatic cuslide2 plugin configuration
10+
- GPU vs CPU performance comparison
11+
- Support for SVS, TIFF, and Philips formats
12+
- nvImageCodec integration validation
13+
"""
14+
15+
import os
16+
import sys
17+
import json
18+
import time
19+
import numpy as np
20+
from pathlib import Path
21+
from typing import Optional, Tuple, List
22+
23+
def setup_cuslide2_plugin():
24+
"""Configure cuCIM to use cuslide2 plugin with priority"""
25+
26+
print("🔧 Setting up cuslide2 plugin...")
27+
28+
# Set plugin root to build directory
29+
plugin_root = "/home/cdinea/cucim/cpp/plugins/cucim.kit.cuslide2/build/lib"
30+
31+
try:
32+
from cucim.clara import _set_plugin_root
33+
_set_plugin_root(plugin_root)
34+
print(f"✅ Plugin root set: {plugin_root}")
35+
except ImportError:
36+
print("❌ cuCIM not available - please install cuCIM")
37+
return False
38+
39+
# Create plugin configuration to prioritize cuslide2
40+
config = {
41+
"plugin": {
42+
"names": [
43+
"cucim.kit.cuslide2@25.10.00.so", # cuslide2 with nvImageCodec (highest priority)
44+
"cucim.kit.cuslide@25.10.00.so", # Original cuslide (fallback)
45+
"cucim.kit.cumed@25.10.00.so" # Medical imaging
46+
]
47+
}
48+
}
49+
50+
# Write config file
51+
config_path = "/tmp/.cucim_cuslide2_demo.json"
52+
with open(config_path, "w") as f:
53+
json.dump(config, f, indent=2)
54+
55+
# Set environment variable
56+
os.environ["CUCIM_CONFIG_PATH"] = config_path
57+
print(f"✅ Plugin configuration created: {config_path}")
58+
59+
return True
60+
61+
def check_nvimgcodec_availability() -> bool:
62+
"""Check if nvImageCodec is available for GPU acceleration"""
63+
64+
conda_prefix = os.environ.get('CONDA_PREFIX', '/home/cdinea/micromamba')
65+
nvimgcodec_lib = Path(conda_prefix) / "lib/libnvimgcodec.so.0"
66+
67+
if nvimgcodec_lib.exists():
68+
print(f"✅ nvImageCodec available: {nvimgcodec_lib}")
69+
return True
70+
else:
71+
print(f"⚠️ nvImageCodec not found: {nvimgcodec_lib}")
72+
print(" GPU acceleration will not be available")
73+
return False
74+
75+
def benchmark_decode_performance(img, region_sizes: List[int] = [1024, 2048, 4096]) -> dict:
76+
"""Benchmark CPU vs GPU decode performance"""
77+
78+
results = {}
79+
80+
print(f"\n📊 Performance Benchmarking")
81+
print("=" * 50)
82+
83+
for size in region_sizes:
84+
if img.shape[0] < size or img.shape[1] < size:
85+
print(f"⚠️ Skipping {size}x{size} - image too small")
86+
continue
87+
88+
print(f"\n🔍 Testing {size}x{size} region...")
89+
90+
# CPU benchmark
91+
print(" 🖥️ CPU decode...")
92+
try:
93+
start_time = time.time()
94+
cpu_region = img.read_region(
95+
location=[0, 0],
96+
size=[size, size],
97+
level=0,
98+
device="cpu"
99+
)
100+
cpu_time = time.time() - start_time
101+
print(f" Time: {cpu_time:.3f}s")
102+
print(f" Shape: {cpu_region.shape}")
103+
print(f" Device: {cpu_region.device}")
104+
except Exception as e:
105+
print(f" ❌ CPU decode failed: {e}")
106+
cpu_time = None
107+
108+
# GPU benchmark
109+
print(" 🚀 GPU decode...")
110+
try:
111+
start_time = time.time()
112+
gpu_region = img.read_region(
113+
location=[0, 0],
114+
size=[size, size],
115+
level=0,
116+
device="cuda"
117+
)
118+
gpu_time = time.time() - start_time
119+
print(f" Time: {gpu_time:.3f}s")
120+
print(f" Shape: {gpu_region.shape}")
121+
print(f" Device: {gpu_region.device}")
122+
123+
if cpu_time and gpu_time > 0:
124+
speedup = cpu_time / gpu_time
125+
print(f" 🎯 Speedup: {speedup:.2f}x")
126+
results[size] = {
127+
'cpu_time': cpu_time,
128+
'gpu_time': gpu_time,
129+
'speedup': speedup
130+
}
131+
132+
except Exception as e:
133+
print(f" ⚠️ GPU decode failed: {e}")
134+
print(f" (This is expected if CUDA is not available)")
135+
136+
return results
137+
138+
def analyze_image_format(img) -> dict:
139+
"""Analyze image format and compression details"""
140+
141+
info = {
142+
'dimensions': img.shape,
143+
'levels': img.level_count,
144+
'spacing': img.spacing() if hasattr(img, 'spacing') else None,
145+
'dtype': str(img.dtype),
146+
'device': str(img.device),
147+
'associated_images': []
148+
}
149+
150+
# Get associated images
151+
if hasattr(img, 'associated_images'):
152+
info['associated_images'] = list(img.associated_images)
153+
154+
# Get metadata
155+
if hasattr(img, 'metadata'):
156+
metadata = img.metadata
157+
if isinstance(metadata, dict):
158+
# Look for compression information
159+
if 'tiff' in metadata:
160+
tiff_info = metadata['tiff']
161+
if isinstance(tiff_info, dict) and 'compression' in tiff_info:
162+
info['compression'] = tiff_info['compression']
163+
164+
return info
165+
166+
def test_cuslide2_plugin(file_path: str):
167+
"""Test cuslide2 plugin with a specific file"""
168+
169+
print(f"\n🔍 Testing cuslide2 plugin with: {file_path}")
170+
print("=" * 60)
171+
172+
if not Path(file_path).exists():
173+
print(f"❌ File not found: {file_path}")
174+
return False
175+
176+
try:
177+
from cucim import CuImage
178+
179+
# Load image
180+
print("📁 Loading image...")
181+
start_time = time.time()
182+
img = CuImage(file_path)
183+
load_time = time.time() - start_time
184+
185+
print(f"✅ Image loaded in {load_time:.3f}s")
186+
187+
# Analyze image format
188+
print("\n📋 Image Analysis:")
189+
info = analyze_image_format(img)
190+
for key, value in info.items():
191+
print(f" {key}: {value}")
192+
193+
# Show level information
194+
print(f"\n📊 Level Information:")
195+
for level in range(img.level_count):
196+
level_shape = img.level_shape(level)
197+
level_spacing = img.level_spacing(level) if hasattr(img, 'level_spacing') else None
198+
print(f" Level {level}: {level_shape} (spacing: {level_spacing})")
199+
200+
# Performance benchmarking
201+
results = benchmark_decode_performance(img)
202+
203+
# Summary
204+
if results:
205+
print(f"\n🏆 Performance Summary:")
206+
avg_speedup = sum(r['speedup'] for r in results.values()) / len(results)
207+
print(f" Average GPU speedup: {avg_speedup:.2f}x")
208+
209+
best_speedup = max(r['speedup'] for r in results.values())
210+
best_size = max(results.keys(), key=lambda k: results[k]['speedup'])
211+
print(f" Best speedup: {best_speedup:.2f}x (at {best_size}x{best_size})")
212+
213+
return True
214+
215+
except Exception as e:
216+
print(f"❌ Error testing plugin: {e}")
217+
import traceback
218+
traceback.print_exc()
219+
return False
220+
221+
def find_test_images() -> List[str]:
222+
"""Find available test images"""
223+
224+
search_paths = [
225+
"/home/cdinea/cucim/test_data",
226+
"/home/cdinea/cucim/notebooks/input",
227+
"/home/cdinea/cucim/cpp/plugins/cucim.kit.cuslide2/test_data",
228+
"/tmp"
229+
]
230+
231+
extensions = ['.svs', '.tiff', '.tif', '.ndpi']
232+
found_images = []
233+
234+
for search_path in search_paths:
235+
if Path(search_path).exists():
236+
for ext in extensions:
237+
pattern = f"*{ext}"
238+
matches = list(Path(search_path).glob(pattern))
239+
found_images.extend([str(m) for m in matches])
240+
241+
return found_images
242+
243+
def demo_mode():
244+
"""Run demo mode without specific files"""
245+
246+
print("\n🎮 cuslide2 Plugin Demo Mode")
247+
print("=" * 40)
248+
249+
# Check for available test images
250+
test_images = find_test_images()
251+
252+
if test_images:
253+
print(f"📁 Found {len(test_images)} test image(s):")
254+
for img_path in test_images[:5]: # Show first 5
255+
print(f" • {img_path}")
256+
257+
# Test with first available image
258+
print(f"\n🧪 Testing with: {test_images[0]}")
259+
return test_cuslide2_plugin(test_images[0])
260+
else:
261+
print("📝 No test images found. To test cuslide2:")
262+
print(" 1. Place a .svs, .tiff, or .tif file in one of these locations:")
263+
print(" • /home/cdinea/cucim/test_data/")
264+
print(" • /home/cdinea/cucim/notebooks/input/")
265+
print(" • /tmp/")
266+
print(" 2. Run: python cuslide2_plugin_demo.py /path/to/your/image.svs")
267+
268+
print(f"\n✅ cuslide2 plugin is configured and ready!")
269+
print(f"🎯 Supported formats:")
270+
print(f" • Aperio SVS (JPEG/JPEG2000)")
271+
print(f" • Philips TIFF (JPEG/JPEG2000)")
272+
print(f" • Generic tiled TIFF (JPEG/JPEG2000)")
273+
274+
return True
275+
276+
def main():
277+
"""Main function"""
278+
279+
print("🚀 cuslide2 Plugin Demo with nvImageCodec")
280+
print("=" * 50)
281+
282+
# Setup plugin
283+
if not setup_cuslide2_plugin():
284+
return 1
285+
286+
# Check nvImageCodec
287+
nvimgcodec_available = check_nvimgcodec_availability()
288+
289+
# Get file path from command line or run demo
290+
if len(sys.argv) > 1:
291+
file_path = sys.argv[1]
292+
success = test_cuslide2_plugin(file_path)
293+
else:
294+
success = demo_mode()
295+
296+
# Final summary
297+
print(f"\n🎉 Demo completed!")
298+
print(f"✅ cuslide2 plugin: Ready")
299+
print(f"{'✅' if nvimgcodec_available else '⚠️ '} nvImageCodec: {'Available' if nvimgcodec_available else 'CPU fallback'}")
300+
301+
if nvimgcodec_available:
302+
print(f"\n🚀 GPU acceleration is active!")
303+
print(f" JPEG/JPEG2000 tiles will be decoded on GPU for faster performance")
304+
else:
305+
print(f"\n💡 To enable GPU acceleration:")
306+
print(f" micromamba install libnvimgcodec-dev libnvimgcodec0 -c conda-forge")
307+
308+
return 0 if success else 1
309+
310+
if __name__ == "__main__":
311+
sys.exit(main())

0 commit comments

Comments
 (0)