Skill Tracer
Skills › Scientific Agent Skills › optimize-for-gpu › cucim.md

cucim.md

688 lines · skills/optimize-for-gpu/references/cucim.md at 5f2bc03

Open on GitHub

Most marks come from matching words in the file, so treat them as pointers.

1# cuCIM Reference
2
3> Review: 2026-10-01. Code below is illustrative unless explicitly described as CPU-tested.
4> GPU execution, performance, GDS, and multi-GPU behavior require validation on target hardware.
5
6cuCIM (CUDA Clara IMage) is NVIDIA's GPU-accelerated computer vision and image processing library
7within the RAPIDS ecosystem. Its `cucim.skimage` module mirrors a substantial part of scikit-image
8on CuPy arrays, and `cucim.CuImage` reads tiled whole-slide images. Verify function coverage and
9benchmark the actual image sizes, storage path, and processing chain.
11> **Full documentation:** https://docs.nvidia.com/cucim/26.08/
12> **GitHub:** https://github.com/rapidsai/cucim

cuCIM Reference

Review: 2026-10-01. Code below is illustrative unless explicitly described as CPU-tested. GPU execution, performance, GDS, and multi-GPU behavior require validation on target hardware.

cuCIM (CUDA Clara IMage) is NVIDIA's GPU-accelerated computer vision and image processing library within the RAPIDS ecosystem. Its cucim.skimage module mirrors a substantial part of scikit-image on CuPy arrays, and cucim.CuImage reads tiled whole-slide images. Verify function coverage and benchmark the actual image sizes, storage path, and processing chain.

Full documentation: https://docs.nvidia.com/cucim/26.08/ GitHub: https://github.com/rapidsai/cucim
Raw lines 1 to 13
14## Table of Contents
161. [Installation and Setup](#installation-and-setup)
172. [Core Concept: CuPy Arrays](#core-concept-cupy-arrays)
183. [cucim.skimage — GPU scikit-image](#cucimskimage)
194. [Color Operations](#color-operations)
205. [Exposure and Histogram](#exposure-and-histogram)
216. [Feature Detection](#feature-detection)
227. [Filters](#filters)
238. [Measure and Region Properties](#measure-and-region-properties)
249. [Morphology](#morphology)
2510. [Segmentation](#segmentation)
2611. [Registration](#registration)
2712. [Restoration](#restoration)
2813. [Transform](#transform)
2914. [Metrics](#metrics)
3015. [Utility Functions](#utility-functions)
3116. [cucim.core.operations — NVIDIA-Specific](#cucimcoreoperations)
3217. [Whole-Slide Image Reading (cucim.clara)](#whole-slide-image-reading)
3318. [Performance Characteristics](#performance-characteristics)
3419. [Interoperability](#interoperability)
3520. [Known Limitations vs scikit-image](#known-limitations-vs-scikit-image)
3621. [Common Migration Patterns](#common-migration-patterns)
38---

Table of Contents

  1. Installation and Setup
  2. Core Concept: CuPy Arrays
  3. cucim.skimage — GPU scikit-image
  4. Color Operations
  5. Exposure and Histogram
  6. Feature Detection
  7. Filters
  8. Measure and Region Properties
  9. Morphology
  10. Segmentation
  11. Registration
  12. Restoration
  13. Transform
  14. Metrics
  15. Utility Functions
  16. cucim.core.operations — NVIDIA-Specific
  17. Whole-Slide Image Reading (cucim.clara)
  18. Performance Characteristics
  19. Interoperability
  20. Known Limitations vs scikit-image
  21. Common Migration Patterns

Raw lines 14 to 39
40## Installation and Setup
42Use `uv add` in standalone examples; follow the user's existing project package manager when one
43is already configured.
45```bash
46uv add --extra-index-url=https://pypi.nvidia.com "cucim-cu12==26.8.*" # For CUDA 12.x
47uv add --extra-index-url=https://pypi.nvidia.com "cucim-cu13==26.8.*" # For CUDA 13.x
48```
50cuCIM wheels are also published directly to PyPI, so the extra index is optional.
52**Platform:** Linux only (x86-64 and aarch64) — no Windows or macOS GPU support.
53**Requires:** NVIDIA GPU with CUDA 12.x or 13.x, Python 3.11+, CuPy, NumPy, SciPy, scikit-image.
55Verify:
56```python
57import cucim
58print(cucim.__version__)
60import cupy as cp
61from cucim.skimage.filters import gaussian
62img = cp.random.rand(512, 512).astype(cp.float32)
63result = gaussian(img, sigma=3)
64print(f"Filtered image shape: {result.shape}") # Should work on GPU
65```
67---

Installation and Setup

Use uv add in standalone examples; follow the user's existing project package manager when one is already configured.

uv add --extra-index-url=https://pypi.nvidia.com "cucim-cu12==26.8.*"    # For CUDA 12.x
uv add --extra-index-url=https://pypi.nvidia.com "cucim-cu13==26.8.*"    # For CUDA 13.x

cuCIM wheels are also published directly to PyPI, so the extra index is optional.

Platform: Linux only (x86-64 and aarch64) — no Windows or macOS GPU support. Requires: NVIDIA GPU with CUDA 12.x or 13.x, Python 3.11+, CuPy, NumPy, SciPy, scikit-image.

Verify:

import cucim
print(cucim.__version__)

import cupy as cp
from cucim.skimage.filters import gaussian
img = cp.random.rand(512, 512).astype(cp.float32)
result = gaussian(img, sigma=3)
print(f"Filtered image shape: {result.shape}")  # Should work on GPU

Raw lines 40 to 68
69## Core Concept: CuPy Arrays
71cuCIM operates natively on **CuPy arrays**. Many image operations consume and return CuPy arrays. Some return scalars, tuples, dictionaries, or region objects and can synchronize; device residency does not imply zero allocation or zero host work.
73```python
74import cupy as cp
75import numpy as np
76from cucim.skimage.filters import gaussian
78# Transfer image to GPU once
79image_gpu = cp.asarray(numpy_image)
81# All processing stays on GPU — zero-copy between cuCIM calls
82blurred = gaussian(image_gpu, sigma=3)
83# ... more processing on GPU ...
85# Transfer back to CPU only when needed (for display, save, etc.)
86result_cpu = cp.asnumpy(blurred)
87```
89**Best practice:** Move data to GPU once at the start, chain all cuCIM operations on GPU, then transfer back to CPU only at the end.
91---

Core Concept: CuPy Arrays

cuCIM operates natively on CuPy arrays. Many image operations consume and return CuPy arrays. Some return scalars, tuples, dictionaries, or region objects and can synchronize; device residency does not imply zero allocation or zero host work.

import cupy as cp
import numpy as np
from cucim.skimage.filters import gaussian

# Transfer image to GPU once
image_gpu = cp.asarray(numpy_image)

# All processing stays on GPU — zero-copy between cuCIM calls
blurred = gaussian(image_gpu, sigma=3)
# ... more processing on GPU ...

# Transfer back to CPU only when needed (for display, save, etc.)
result_cpu = cp.asnumpy(blurred)

Best practice: Move data to GPU once at the start, chain all cuCIM operations on GPU, then transfer back to CPU only at the end.


Raw lines 69 to 92
93## cucim.skimage
95The `cucim.skimage` module mirrors scikit-image's module structure. In most cases, replace `from skimage` with `from cucim.skimage` and pass CuPy arrays instead of NumPy arrays.
97```python
98# Before (CPU — scikit-image)
99from skimage.filters import gaussian
100import numpy as np
101result = gaussian(numpy_image, sigma=3)
103# After (GPU — cuCIM)
104from cucim.skimage.filters import gaussian
105import cupy as cp
106result = gaussian(cp.asarray(numpy_image), sigma=3)
107```
109---

cucim.skimage

The cucim.skimage module mirrors scikit-image's module structure. In most cases, replace from skimage with from cucim.skimage and pass CuPy arrays instead of NumPy arrays.

# Before (CPU — scikit-image)
from skimage.filters import gaussian
import numpy as np
result = gaussian(numpy_image, sigma=3)

# After (GPU — cuCIM)
from cucim.skimage.filters import gaussian
import cupy as cp
result = gaussian(cp.asarray(numpy_image), sigma=3)

Raw lines 93 to 110
111## Color Operations
113`cucim.skimage.color` — 42 GPU-accelerated color space conversion functions.
115```python
116from cucim.skimage.color import rgb2gray, rgb2hsv, rgb2lab, label2rgb
117from cucim.skimage.color import separate_stains, combine_stains
119# Color space conversions
120gray = rgb2gray(rgb_image_gpu)
121hsv = rgb2hsv(rgb_image_gpu)
122lab = rgb2lab(rgb_image_gpu)
124# Stain separation (for H&E histology)
125stains = separate_stains(rgb_image_gpu, stain_matrix)
126```
128**Available conversions:** `rgb2gray`, `rgb2hsv`, `hsv2rgb`, `rgb2lab`, `lab2rgb`, `rgb2xyz`, `xyz2rgb`, `rgb2luv`, `luv2rgb`, `rgb2ycbcr`, `ycbcr2rgb`, `rgb2yuv`, `yuv2rgb`, `rgb2yiq`, `yiq2rgb`, `rgb2hed`, `hed2rgb`, `rgb2rgbcie`, `rgbcie2rgb`, `gray2rgb`, `gray2rgba`, `rgba2rgb`, `convert_colorspace`, `label2rgb`
130**Color difference:** `deltaE_cie76`, `deltaE_ciede94`, `deltaE_ciede2000`, `deltaE_cmc`
132---

Color Operations

cucim.skimage.color — 42 GPU-accelerated color space conversion functions.

from cucim.skimage.color import rgb2gray, rgb2hsv, rgb2lab, label2rgb
from cucim.skimage.color import separate_stains, combine_stains

# Color space conversions
gray = rgb2gray(rgb_image_gpu)
hsv = rgb2hsv(rgb_image_gpu)
lab = rgb2lab(rgb_image_gpu)

# Stain separation (for H&E histology)
stains = separate_stains(rgb_image_gpu, stain_matrix)

Available conversions: rgb2gray, rgb2hsv, hsv2rgb, rgb2lab, lab2rgb, rgb2xyz, xyz2rgb, rgb2luv, luv2rgb, rgb2ycbcr, ycbcr2rgb, rgb2yuv, yuv2rgb, rgb2yiq, yiq2rgb, rgb2hed, hed2rgb, rgb2rgbcie, rgbcie2rgb, gray2rgb, gray2rgba, rgba2rgb, convert_colorspace, label2rgb

Color difference: deltaE_cie76, deltaE_ciede94, deltaE_ciede2000, deltaE_cmc


Raw lines 111 to 133
134## Exposure and Histogram
136`cucim.skimage.exposure` — histogram equalization, contrast adjustment.
138```python
139from cucim.skimage.exposure import (
140 equalize_hist, equalize_adapthist,
141 rescale_intensity, adjust_gamma, adjust_log, adjust_sigmoid,
142 histogram, match_histograms, is_low_contrast
145# CLAHE (Contrast Limited Adaptive Histogram Equalization)
146enhanced = equalize_adapthist(image_gpu, clip_limit=0.03)
148# Gamma correction
149brightened = adjust_gamma(image_gpu, gamma=0.5)
151# Rescale intensity to [0, 1]
152normalized = rescale_intensity(image_gpu)
154# Histogram matching between two images
155matched = match_histograms(source_gpu, reference_gpu)
156```
158---

Exposure and Histogram

cucim.skimage.exposure — histogram equalization, contrast adjustment.

from cucim.skimage.exposure import (
    equalize_hist, equalize_adapthist,
    rescale_intensity, adjust_gamma, adjust_log, adjust_sigmoid,
    histogram, match_histograms, is_low_contrast
)

# CLAHE (Contrast Limited Adaptive Histogram Equalization)
enhanced = equalize_adapthist(image_gpu, clip_limit=0.03)

# Gamma correction
brightened = adjust_gamma(image_gpu, gamma=0.5)

# Rescale intensity to [0, 1]
normalized = rescale_intensity(image_gpu)

# Histogram matching between two images
matched = match_histograms(source_gpu, reference_gpu)

Raw lines 134 to 159
160## Feature Detection
162`cucim.skimage.feature` — edge, corner, and blob detection.
164```python
165from cucim.skimage.feature import (
166 canny, corner_harris, corner_peaks,
167 blob_dog, blob_doh, blob_log,
168 structure_tensor, hessian_matrix, hessian_matrix_det,
169 match_template, peak_local_max, daisy, multiscale_basic_features
172# Canny edge detection
173edges = canny(gray_image_gpu, sigma=2.0)
175# Harris corner detection
176corners = corner_harris(gray_image_gpu)
177corner_coords = corner_peaks(corners, min_distance=5)
179# Blob detection (Difference of Gaussian)
180blobs = blob_dog(gray_image_gpu, max_sigma=30, threshold=0.1)
182# Template matching
183result = match_template(image_gpu, template_gpu)
184```
186---

Feature Detection

cucim.skimage.feature — edge, corner, and blob detection.

from cucim.skimage.feature import (
    canny, corner_harris, corner_peaks,
    blob_dog, blob_doh, blob_log,
    structure_tensor, hessian_matrix, hessian_matrix_det,
    match_template, peak_local_max, daisy, multiscale_basic_features
)

# Canny edge detection
edges = canny(gray_image_gpu, sigma=2.0)

# Harris corner detection
corners = corner_harris(gray_image_gpu)
corner_coords = corner_peaks(corners, min_distance=5)

# Blob detection (Difference of Gaussian)
blobs = blob_dog(gray_image_gpu, max_sigma=30, threshold=0.1)

# Template matching
result = match_template(image_gpu, template_gpu)

Raw lines 160 to 187
188## Filters
190`cucim.skimage.filters` — 47 GPU-accelerated filter functions. This is one of the most commonly used modules.
192```python
193from cucim.skimage.filters import (
194 gaussian, median, sobel, laplace, unsharp_mask,
195 frangi, hessian, meijering, sato,
196 threshold_otsu, threshold_multiotsu, threshold_sauvola,
197 gabor, difference_of_gaussians, butterworth
200# Gaussian blur
201blurred = gaussian(image_gpu, sigma=3)
203# Sobel edge detection
204edges = sobel(gray_image_gpu)
206# Unsharp mask (sharpening)
207sharpened = unsharp_mask(image_gpu, radius=5, amount=2.0)
209# Vessel/ridge detection (for medical imaging)
210vessels = frangi(gray_image_gpu, sigmas=range(1, 10))
212# Otsu thresholding
213threshold = threshold_otsu(gray_image_gpu)
214binary = gray_image_gpu > threshold
216# Multi-level Otsu
217thresholds = threshold_multiotsu(gray_image_gpu, classes=3)
218```
220**Edge detection:** `sobel`, `scharr`, `prewitt`, `roberts`, `farid`, `laplace` (plus `_h`/`_v` variants)
222**Smoothing:** `gaussian`, `median`, `unsharp_mask`
224**Ridge/vessel detection:** `frangi`, `hessian`, `meijering`, `sato`
226**Thresholding (10 methods):** `threshold_otsu`, `threshold_isodata`, `threshold_li`, `threshold_mean`, `threshold_minimum`, `threshold_multiotsu`, `threshold_niblack`, `threshold_sauvola`, `threshold_triangle`, `threshold_yen`
228**Frequency domain:** `butterworth`, `wiener`
230---

Filters

cucim.skimage.filters — 47 GPU-accelerated filter functions. This is one of the most commonly used modules.

from cucim.skimage.filters import (
    gaussian, median, sobel, laplace, unsharp_mask,
    frangi, hessian, meijering, sato,
    threshold_otsu, threshold_multiotsu, threshold_sauvola,
    gabor, difference_of_gaussians, butterworth
)

# Gaussian blur
blurred = gaussian(image_gpu, sigma=3)

# Sobel edge detection
edges = sobel(gray_image_gpu)

# Unsharp mask (sharpening)
sharpened = unsharp_mask(image_gpu, radius=5, amount=2.0)

# Vessel/ridge detection (for medical imaging)
vessels = frangi(gray_image_gpu, sigmas=range(1, 10))

# Otsu thresholding
threshold = threshold_otsu(gray_image_gpu)
binary = gray_image_gpu > threshold

# Multi-level Otsu
thresholds = threshold_multiotsu(gray_image_gpu, classes=3)

Edge detection: sobel, scharr, prewitt, roberts, farid, laplace (plus _h/_v variants)

Smoothing: gaussian, median, unsharp_mask

Ridge/vessel detection: frangi, hessian, meijering, sato

Thresholding (10 methods): threshold_otsu, threshold_isodata, threshold_li, threshold_mean, threshold_minimum, threshold_multiotsu, threshold_niblack, threshold_sauvola, threshold_triangle, threshold_yen

Frequency domain: butterworth, wiener


Raw lines 188 to 231
232## Measure and Region Properties
234`cucim.skimage.measure` — labeling, region properties, and shape metrics.
236```python
237from cucim.skimage.measure import label, regionprops, regionprops_table
238from cucim.skimage.measure import moments, moments_central, moments_hu
239from cucim.skimage.measure import block_reduce, shannon_entropy
241# Connected component labeling
242labels = label(binary_image_gpu)
244# Region properties (area, centroid, bounding box, etc.)
245props = regionprops(labels)
246table = regionprops_table(labels, intensity_image=gray_gpu,
247 properties=['area', 'centroid', 'mean_intensity'])
249# Block reduce (downsampling)
250downsampled = block_reduce(image_gpu, block_size=(2, 2), func=cp.mean)
251```
253**Colocalization metrics** (for microscopy): `manders_coloc_coeff`, `manders_overlap_coeff`, `pearson_corr_coeff`, `intersection_coeff`
255---

Measure and Region Properties

cucim.skimage.measure — labeling, region properties, and shape metrics.

from cucim.skimage.measure import label, regionprops, regionprops_table
from cucim.skimage.measure import moments, moments_central, moments_hu
from cucim.skimage.measure import block_reduce, shannon_entropy

# Connected component labeling
labels = label(binary_image_gpu)

# Region properties (area, centroid, bounding box, etc.)
props = regionprops(labels)
table = regionprops_table(labels, intensity_image=gray_gpu,
                          properties=['area', 'centroid', 'mean_intensity'])

# Block reduce (downsampling)
downsampled = block_reduce(image_gpu, block_size=(2, 2), func=cp.mean)

Colocalization metrics (for microscopy): manders_coloc_coeff, manders_overlap_coeff, pearson_corr_coeff, intersection_coeff


Raw lines 232 to 256
257## Morphology
259`cucim.skimage.morphology` — 30 GPU-accelerated morphological operations.
261```python
262from cucim.skimage.morphology import (
263 binary_erosion, binary_dilation, binary_opening, binary_closing,
264 erosion, dilation, opening, closing,
265 white_tophat, black_tophat,
266 disk, diamond, ball, star,
267 remove_small_objects, remove_small_holes,
268 reconstruction, medial_axis, thin
271# Create structuring element
272selem = disk(5)
274# Binary morphological operations
275cleaned = binary_opening(binary_image_gpu, footprint=selem)
276cleaned = binary_closing(cleaned, footprint=selem)
278# Remove small objects/holes
279cleaned = remove_small_objects(labels_gpu, min_size=100)
280filled = remove_small_holes(binary_gpu, area_threshold=50)
282# Grayscale morphology
283tophat = white_tophat(gray_image_gpu, footprint=disk(10))
284```
286**Structuring elements:** `disk`, `diamond`, `ball`, `octagon`, `octahedron`, `star`, `ellipse`, `footprint_rectangle`
288**Isotropic operations:** `isotropic_erosion`, `isotropic_dilation`, `isotropic_opening`, `isotropic_closing`
290**Extrema (added in 26.06):** `h_maxima`, `h_minima`, `local_maxima`, `local_minima`
292---

Morphology

cucim.skimage.morphology — 30 GPU-accelerated morphological operations.

from cucim.skimage.morphology import (
    binary_erosion, binary_dilation, binary_opening, binary_closing,
    erosion, dilation, opening, closing,
    white_tophat, black_tophat,
    disk, diamond, ball, star,
    remove_small_objects, remove_small_holes,
    reconstruction, medial_axis, thin
)

# Create structuring element
selem = disk(5)

# Binary morphological operations
cleaned = binary_opening(binary_image_gpu, footprint=selem)
cleaned = binary_closing(cleaned, footprint=selem)

# Remove small objects/holes
cleaned = remove_small_objects(labels_gpu, min_size=100)
filled = remove_small_holes(binary_gpu, area_threshold=50)

# Grayscale morphology
tophat = white_tophat(gray_image_gpu, footprint=disk(10))

Structuring elements: disk, diamond, ball, octagon, octahedron, star, ellipse, footprint_rectangle

Isotropic operations: isotropic_erosion, isotropic_dilation, isotropic_opening, isotropic_closing

Extrema (added in 26.06): h_maxima, h_minima, local_maxima, local_minima


Raw lines 257 to 293
294## Segmentation
296`cucim.skimage.segmentation` — level-set methods, boundary detection, label operations.
298```python
299from cucim.skimage.segmentation import (
300 chan_vese, morphological_chan_vese, morphological_geodesic_active_contour,
301 find_boundaries, mark_boundaries, clear_border,
302 expand_labels, relabel_sequential, random_walker,
303 inverse_gaussian_gradient, checkerboard_level_set
306# Chan-Vese segmentation
307segmented = chan_vese(gray_image_gpu, mu=0.25, max_num_iter=200)
309# Active contours (geodesic)
310gimage = inverse_gaussian_gradient(gray_image_gpu)
311init_ls = checkerboard_level_set(gray_image_gpu.shape)
312seg = morphological_geodesic_active_contour(gimage, num_iter=200, init_level_set=init_ls)
314# Find and mark boundaries
315boundaries = find_boundaries(labels_gpu, mode='thick')
316```
318---

Segmentation

cucim.skimage.segmentation — level-set methods, boundary detection, label operations.

from cucim.skimage.segmentation import (
    chan_vese, morphological_chan_vese, morphological_geodesic_active_contour,
    find_boundaries, mark_boundaries, clear_border,
    expand_labels, relabel_sequential, random_walker,
    inverse_gaussian_gradient, checkerboard_level_set
)

# Chan-Vese segmentation
segmented = chan_vese(gray_image_gpu, mu=0.25, max_num_iter=200)

# Active contours (geodesic)
gimage = inverse_gaussian_gradient(gray_image_gpu)
init_ls = checkerboard_level_set(gray_image_gpu.shape)
seg = morphological_geodesic_active_contour(gimage, num_iter=200, init_level_set=init_ls)

# Find and mark boundaries
boundaries = find_boundaries(labels_gpu, mode='thick')

Raw lines 294 to 319
320## Registration
322`cucim.skimage.registration` — image alignment.
324```python
325from cucim.skimage.registration import (
326 phase_cross_correlation,
327 optical_flow_tvl1,
328 optical_flow_ilk
331# Subpixel image registration
332shift, error, diffphase = phase_cross_correlation(reference_gpu, moving_gpu)
334# Optical flow
335flow = optical_flow_tvl1(frame1_gpu, frame2_gpu)
336```
338---

Registration

cucim.skimage.registration — image alignment.

from cucim.skimage.registration import (
    phase_cross_correlation,
    optical_flow_tvl1,
    optical_flow_ilk
)

# Subpixel image registration
shift, error, diffphase = phase_cross_correlation(reference_gpu, moving_gpu)

# Optical flow
flow = optical_flow_tvl1(frame1_gpu, frame2_gpu)

Raw lines 320 to 339
340## Restoration
342`cucim.skimage.restoration` — denoising and deconvolution.
344```python
345from cucim.skimage.restoration import (
346 denoise_tv_chambolle,
347 richardson_lucy,
348 wiener, unsupervised_wiener,
349 rolling_ball
352# Total variation denoising
353denoised = denoise_tv_chambolle(noisy_image_gpu, weight=0.1)
355# Richardson-Lucy deconvolution
356restored = richardson_lucy(blurred_image_gpu, psf_gpu, num_iter=30)
358# Rolling-ball background subtraction (added in 26.04)
359background = rolling_ball(image_gpu, radius=100)
360```
362---

Restoration

cucim.skimage.restoration — denoising and deconvolution.

from cucim.skimage.restoration import (
    denoise_tv_chambolle,
    richardson_lucy,
    wiener, unsupervised_wiener,
    rolling_ball
)

# Total variation denoising
denoised = denoise_tv_chambolle(noisy_image_gpu, weight=0.1)

# Richardson-Lucy deconvolution
restored = richardson_lucy(blurred_image_gpu, psf_gpu, num_iter=30)

# Rolling-ball background subtraction (added in 26.04)
background = rolling_ball(image_gpu, radius=100)

Raw lines 340 to 363
364## Transform
366`cucim.skimage.transform` — geometric transforms, resizing, pyramids.
368```python
369from cucim.skimage.transform import (
370 resize, rescale, rotate, warp, swirl, warp_polar,
371 pyramid_gaussian, pyramid_laplacian,
372 downscale_local_mean, integral_image,
373 AffineTransform, EuclideanTransform, SimilarityTransform
376# Resize
377resized = resize(image_gpu, (256, 256))
379# Rescale
380half = rescale(image_gpu, 0.5)
382# Rotate
383rotated = rotate(image_gpu, angle=45, resize=True)
385# Gaussian pyramid
386pyramid = list(pyramid_gaussian(image_gpu, max_layer=4, downscale=2))
388# Affine transform
389tform = AffineTransform(rotation=0.3, translation=(50, 50))
390warped = warp(image_gpu, tform.inverse)
391```
393---

Transform

cucim.skimage.transform — geometric transforms, resizing, pyramids.

from cucim.skimage.transform import (
    resize, rescale, rotate, warp, swirl, warp_polar,
    pyramid_gaussian, pyramid_laplacian,
    downscale_local_mean, integral_image,
    AffineTransform, EuclideanTransform, SimilarityTransform
)

# Resize
resized = resize(image_gpu, (256, 256))

# Rescale
half = rescale(image_gpu, 0.5)

# Rotate
rotated = rotate(image_gpu, angle=45, resize=True)

# Gaussian pyramid
pyramid = list(pyramid_gaussian(image_gpu, max_layer=4, downscale=2))

# Affine transform
tform = AffineTransform(rotation=0.3, translation=(50, 50))
warped = warp(image_gpu, tform.inverse)

Raw lines 364 to 394
395## Metrics
397`cucim.skimage.metrics` — image quality assessment.
399```python
400from cucim.skimage.metrics import (
401 mean_squared_error,
402 peak_signal_noise_ratio,
403 structural_similarity,
404 normalized_root_mse
407mse = mean_squared_error(original_gpu, processed_gpu)
408psnr = peak_signal_noise_ratio(original_gpu, processed_gpu, data_range=1.0)
409ssim = structural_similarity(original_gpu, processed_gpu, data_range=1.0)
410# Here both images are grayscale floating-point intensities on [0, 1].
411```
413---

Metrics

cucim.skimage.metrics — image quality assessment.

from cucim.skimage.metrics import (
    mean_squared_error,
    peak_signal_noise_ratio,
    structural_similarity,
    normalized_root_mse
)

mse = mean_squared_error(original_gpu, processed_gpu)
psnr = peak_signal_noise_ratio(original_gpu, processed_gpu, data_range=1.0)
ssim = structural_similarity(original_gpu, processed_gpu, data_range=1.0)
# Here both images are grayscale floating-point intensities on [0, 1].

Raw lines 395 to 414
415## Utility Functions
417`cucim.skimage.util` — type conversion, array manipulation.
419```python
420from cucim.skimage.util import (
421 img_as_float, img_as_float32, img_as_ubyte,
422 invert, crop, random_noise, montage
425# Convert to float32 [0, 1]
426float_img = img_as_float32(uint8_image_gpu)
428# Add noise for testing
429noisy = random_noise(image_gpu, mode='gaussian', var=0.01)
430```
432---

Utility Functions

cucim.skimage.util — type conversion, array manipulation.

from cucim.skimage.util import (
    img_as_float, img_as_float32, img_as_ubyte,
    invert, crop, random_noise, montage
)

# Convert to float32 [0, 1]
float_img = img_as_float32(uint8_image_gpu)

# Add noise for testing
noisy = random_noise(image_gpu, mode='gaussian', var=0.01)

Raw lines 415 to 433
434## cucim.core.operations
436NVIDIA-specific operations not found in scikit-image. Especially useful for digital pathology.

cucim.core.operations

NVIDIA-specific operations not found in scikit-image. Especially useful for digital pathology.

Raw lines 434 to 437
438### Pathology-Specific
440```python
441from cucim.core.operations.color import (
442 color_jitter,
443 image_to_absorbance,
444 stain_extraction_pca,
445 normalize_colors_pca
448# H&E stain normalization (digital pathology)
449normalized = normalize_colors_pca(he_image_gpu)
451# Color augmentation
452image_chw = cp.moveaxis(image_gpu, -1, 0) # This operation expects CHW/NCHW
453augmented = cp.moveaxis(color_jitter(image_chw, brightness=0.2, contrast=0.2,
454 saturation=0.2, hue=0.1), 0, -1)
455```

Pathology-Specific

from cucim.core.operations.color import (
    color_jitter,
    image_to_absorbance,
    stain_extraction_pca,
    normalize_colors_pca
)

# H&E stain normalization (digital pathology)
normalized = normalize_colors_pca(he_image_gpu)

# Color augmentation
image_chw = cp.moveaxis(image_gpu, -1, 0)  # This operation expects CHW/NCHW
augmented = cp.moveaxis(color_jitter(image_chw, brightness=0.2, contrast=0.2,
                                    saturation=0.2, hue=0.1), 0, -1)
Raw lines 438 to 456
457### Intensity Operations
459```python
460from cucim.core.operations.intensity import normalize_data, scale_intensity_range, zoom
462normalized = normalize_data(image_gpu, norm_constant=1.0, min_value=0.0, max_value=255.0)
463scaled = scale_intensity_range(image_gpu, a_min=0, a_max=255, b_min=0.0, b_max=1.0)
464```

Intensity Operations

from cucim.core.operations.intensity import normalize_data, scale_intensity_range, zoom

normalized = normalize_data(image_gpu, norm_constant=1.0, min_value=0.0, max_value=255.0)
scaled = scale_intensity_range(image_gpu, a_min=0, a_max=255, b_min=0.0, b_max=1.0)
Raw lines 457 to 465
466### Spatial Augmentation
468```python
469from cucim.core.operations.spatial import image_flip, image_rotate_90, rand_image_flip
471flipped = image_flip(image_gpu, spatial_axis=(1,))
472rotated = image_rotate_90(image_gpu, k=1, spatial_axis=(0, 1)) # 90 degrees
473randomly_flipped = rand_image_flip(image_gpu, spatial_axis=(0, 1), prob=0.5)
474```

Spatial Augmentation

from cucim.core.operations.spatial import image_flip, image_rotate_90, rand_image_flip

flipped = image_flip(image_gpu, spatial_axis=(1,))
rotated = image_rotate_90(image_gpu, k=1, spatial_axis=(0, 1))  # 90 degrees
randomly_flipped = rand_image_flip(image_gpu, spatial_axis=(0, 1), prob=0.5)
Raw lines 466 to 475
476### Distance Transform
478```python
479from cucim.core.operations.morphology import distance_transform_edt
481# Exact Euclidean distance transform (faster than scipy.ndimage on GPU)
482distances = distance_transform_edt(binary_image_gpu)
483```
485---

Distance Transform

from cucim.core.operations.morphology import distance_transform_edt

# Exact Euclidean distance transform (faster than scipy.ndimage on GPU)
distances = distance_transform_edt(binary_image_gpu)

Raw lines 476 to 486
487## Whole-Slide Image Reading
489`cucim.CuImage` is the public whole-slide image reader:
491```python
492from cucim import CuImage
494# Open a whole-slide image
495img = CuImage("slide.svs")
497# Inspect metadata
498print(f"Dimensions: {img.shape}")
499print(f"Resolution levels: {img.resolutions}")
500print(f"Spacing: {img.spacing()}")
502# Read a region (returns a CuImage object)
503region = img.read_region(location=(1000, 2000), size=(256, 256), level=0)
505# Convert to CuPy array for processing
506import cupy as cp
507tile_gpu = cp.asarray(region)
509# Process with cucim.skimage
510from cucim.skimage.color import rgb2gray
511gray_tile = rgb2gray(tile_gpu)
512```
514**Supported formats:** Aperio SVS, Philips TIFF, generic tiled multi-resolution RGB TIFF (JPEG, JPEG2000, LZW, Deflate compression).

Whole-Slide Image Reading

cucim.CuImage is the public whole-slide image reader:

from cucim import CuImage

# Open a whole-slide image
img = CuImage("slide.svs")

# Inspect metadata
print(f"Dimensions: {img.shape}")
print(f"Resolution levels: {img.resolutions}")
print(f"Spacing: {img.spacing()}")

# Read a region (returns a CuImage object)
region = img.read_region(location=(1000, 2000), size=(256, 256), level=0)

# Convert to CuPy array for processing
import cupy as cp
tile_gpu = cp.asarray(region)

# Process with cucim.skimage
from cucim.skimage.color import rgb2gray
gray_tile = rgb2gray(tile_gpu)

Supported formats: Aperio SVS, Philips TIFF, generic tiled multi-resolution RGB TIFF (JPEG, JPEG2000, LZW, Deflate compression).

Raw lines 487 to 515
516### Tile Caching
518```python
519from cucim import CuImage
521# Configure the global reader cache; capacity argument is in MiB, not bytes.
522cache = CuImage.cache("per_process", memory_capacity=2048) # 2 GiB
523```

Tile Caching

from cucim import CuImage

# Configure the global reader cache; capacity argument is in MiB, not bytes.
cache = CuImage.cache("per_process", memory_capacity=2048)  # 2 GiB
Raw lines 516 to 524
525### GPUDirect Storage
527GPUDirect Storage can reduce CPU staging on a supported Linux, driver, filesystem, storage, and
528container configuration. Treat it as a deployment capability, not an automatic size-based
529optimization. Confirm that GDS is active and compare end-to-end tile throughput; otherwise use
530cuCIM's normal reader path. Use KvikIO for explicit raw-buffer I/O rather than depending on
531cuCIM-internal filesystem classes.
533---

GPUDirect Storage

GPUDirect Storage can reduce CPU staging on a supported Linux, driver, filesystem, storage, and container configuration. Treat it as a deployment capability, not an automatic size-based optimization. Confirm that GDS is active and compare end-to-end tile throughput; otherwise use cuCIM's normal reader path. Use KvikIO for explicit raw-buffer I/O rather than depending on cuCIM-internal filesystem classes.


Raw lines 525 to 534
535## Performance Characteristics
537Benchmark each operation and the complete pipeline. Image dimensions alone do not predict a
538speedup: kernel type, footprint size, channels, dtype, storage, tiling, transfer, and downstream
539reuse all matter.
5411. Warm the CUDA context and lazy kernels before timed repetitions.
5422. Compare equivalent border modes, interpolation, connectivity, dtype, and output semantics.
5433. Report decode/read, host-device transfer, processing, and end-to-end times separately.
5444. For WSI workloads, record tile size, level, compression, access pattern, worker count, cache
545 state, storage device, and whether GDS was active.
5465. Transfer an image to the GPU once, chain compatible operations, and transfer only the result
547 required by the next CPU consumer.
549---

Performance Characteristics

Benchmark each operation and the complete pipeline. Image dimensions alone do not predict a speedup: kernel type, footprint size, channels, dtype, storage, tiling, transfer, and downstream reuse all matter.

  1. Warm the CUDA context and lazy kernels before timed repetitions.
  2. Compare equivalent border modes, interpolation, connectivity, dtype, and output semantics.
  3. Report decode/read, host-device transfer, processing, and end-to-end times separately.
  4. For WSI workloads, record tile size, level, compression, access pattern, worker count, cache state, storage device, and whether GDS was active.
  5. Transfer an image to the GPU once, chain compatible operations, and transfer only the result required by the next CPU consumer.

Raw lines 535 to 550
551## Interoperability
553- **CuPy:** Native image-array format; result container types depend on the function.
554- **NumPy:** Convert with `cp.asarray()` / `cp.asnumpy()`.
555- **PyTorch:** `torch.from_dlpack(cupy_array)` can share device memory; handle layout and lifetime explicitly. TensorFlow has a separate DLPack interface.
556- **MONAI:** Medical imaging framework with direct cuCIM integration for pathology transforms.
557- **Albumentations:** Do not assume its NumPy/CPU transforms dispatch to cuCIM; explicit adaptation and equivalence checks are needed.
558- **NVIDIA DALI:** Data loading pipeline integration.
559- **Numba CUDA:** CuPy arrays interoperable with Numba GPU kernels.
560- **cuDF:** Use for tabular operations on `regionprops_table` output.

Interoperability

  • CuPy: Native image-array format; result container types depend on the function.
  • NumPy: Convert with cp.asarray() / cp.asnumpy().
  • PyTorch: torch.from_dlpack(cupy_array) can share device memory; handle layout and lifetime explicitly. TensorFlow has a separate DLPack interface.
  • MONAI: Medical imaging framework with direct cuCIM integration for pathology transforms.
  • Albumentations: Do not assume its NumPy/CPU transforms dispatch to cuCIM; explicit adaptation and equivalence checks are needed.
  • NVIDIA DALI: Data loading pipeline integration.
  • Numba CUDA: CuPy arrays interoperable with Numba GPU kernels.
  • cuDF: Use for tabular operations on regionprops_table output.
Raw lines 551 to 561
Starts an agent dispatch :557
562### CPU/GPU Agnostic Code
564```python
565# Switch between CPU and GPU by changing the array module
566import cupy as cp # or: import numpy as cp
567from cucim.skimage.filters import gaussian # or: from skimage.filters import gaussian
569result = gaussian(cp.asarray(image), sigma=5)
570```
572---

CPU/GPU Agnostic Code

# Switch between CPU and GPU by changing the array module
import cupy as cp  # or: import numpy as cp
from cucim.skimage.filters import gaussian  # or: from skimage.filters import gaussian

result = gaussian(cp.asarray(image), sigma=5)

Raw lines 562 to 573
574## Known Limitations vs scikit-image
5761. **Incomplete API coverage:** Check the selected function in the pinned cuCIM API, including parameters and output semantics, instead of relying on a coverage percentage.
5782. **Linux only.** No Windows or macOS GPU support.
5803. **NVIDIA GPU required.** No AMD/Intel GPU support.
5824. **Data must be explicitly moved to GPU.** cuCIM does not auto-transfer; you must call `cp.asarray()`.
5845. **Small image penalty.** Small or one-shot operations may not amortize context, launch, and
585transfer overhead. Benchmark the real tile size and batching strategy.
5876. **GPU memory constraints.** Very large images must be tiled. GPU memory is typically smaller than system RAM.
5897. **WSI format support is limited.** Supports TIFF/SVS/Philips TIFF only. DICOM, NIFTI, Zarr not yet in stable release.
5918. **JIT compilation overhead** on first call per session (cached thereafter).
593---

Known Limitations vs scikit-image

  1. Incomplete API coverage: Check the selected function in the pinned cuCIM API, including parameters and output semantics, instead of relying on a coverage percentage.
  1. Linux only. No Windows or macOS GPU support.
  1. NVIDIA GPU required. No AMD/Intel GPU support.
  1. Data must be explicitly moved to GPU. cuCIM does not auto-transfer; you must call cp.asarray().
  1. Small image penalty. Small or one-shot operations may not amortize context, launch, and

transfer overhead. Benchmark the real tile size and batching strategy.

  1. GPU memory constraints. Very large images must be tiled. GPU memory is typically smaller than system RAM.
  1. WSI format support is limited. Supports TIFF/SVS/Philips TIFF only. DICOM, NIFTI, Zarr not yet in stable release.
  1. JIT compilation overhead on first call per session (cached thereafter).

Raw lines 574 to 594
595## Common Migration Patterns

Common Migration Patterns

Raw lines 595 to 596
597### Pattern 1: Direct scikit-image Replacement
599```python
600# Before (CPU)
601from skimage.filters import gaussian, sobel, threshold_otsu
602from skimage.morphology import binary_opening, disk
603from skimage.measure import label, regionprops_table
604import numpy as np
606image = np.array(...) # Load image
607blurred = gaussian(image, sigma=3)
608edges = sobel(blurred)
609binary = blurred > threshold_otsu(blurred)
610cleaned = binary_opening(binary, footprint=disk(3))
611labels = label(cleaned)
612props = regionprops_table(labels, image, properties=['area', 'centroid'])
614# After (GPU) — change imports, wrap input with cp.asarray
615from cucim.skimage.filters import gaussian, sobel, threshold_otsu
616from cucim.skimage.morphology import binary_opening, disk
617from cucim.skimage.measure import label, regionprops_table
618import cupy as cp
620image_gpu = cp.asarray(image) # Transfer once
621blurred = gaussian(image_gpu, sigma=3)
622edges = sobel(blurred)
623binary = blurred > threshold_otsu(blurred)
624cleaned = binary_opening(binary, footprint=disk(3))
625labels = label(cleaned)
626props = regionprops_table(labels, image_gpu, properties=['area', 'centroid'])
627```

Pattern 1: Direct scikit-image Replacement

# Before (CPU)
from skimage.filters import gaussian, sobel, threshold_otsu
from skimage.morphology import binary_opening, disk
from skimage.measure import label, regionprops_table
import numpy as np

image = np.array(...)  # Load image
blurred = gaussian(image, sigma=3)
edges = sobel(blurred)
binary = blurred > threshold_otsu(blurred)
cleaned = binary_opening(binary, footprint=disk(3))
labels = label(cleaned)
props = regionprops_table(labels, image, properties=['area', 'centroid'])

# After (GPU) — change imports, wrap input with cp.asarray
from cucim.skimage.filters import gaussian, sobel, threshold_otsu
from cucim.skimage.morphology import binary_opening, disk
from cucim.skimage.measure import label, regionprops_table
import cupy as cp

image_gpu = cp.asarray(image)  # Transfer once
blurred = gaussian(image_gpu, sigma=3)
edges = sobel(blurred)
binary = blurred > threshold_otsu(blurred)
cleaned = binary_opening(binary, footprint=disk(3))
labels = label(cleaned)
props = regionprops_table(labels, image_gpu, properties=['area', 'centroid'])
Raw lines 597 to 628
629### Pattern 2: Digital Pathology Pipeline
631```python
632from cucim import CuImage
633from cucim.skimage.color import rgb2gray, separate_stains
634from cucim.skimage.filters import threshold_otsu
635from cucim.skimage.morphology import binary_opening, remove_small_objects, disk
636from cucim.skimage.measure import label, regionprops_table
637from cucim.core.operations.color import normalize_colors_pca
638import cupy as cp
640# Read whole-slide image tile
641slide = CuImage("tissue.svs")
642tile = cp.asarray(slide.read_region(location=(1000, 2000), size=(512, 512), level=0))
644# Normalize staining
645normalized = normalize_colors_pca(tile)
647# Segment nuclei
648gray = rgb2gray(normalized)
649binary = gray < threshold_otsu(gray)
650cleaned = binary_opening(binary, footprint=disk(2))
651cleaned = remove_small_objects(label(cleaned), min_size=50)
652labels = label(cleaned)
654# Extract properties
655props = regionprops_table(labels, gray, properties=['area', 'centroid', 'mean_intensity'])
656```

Pattern 2: Digital Pathology Pipeline

from cucim import CuImage
from cucim.skimage.color import rgb2gray, separate_stains
from cucim.skimage.filters import threshold_otsu
from cucim.skimage.morphology import binary_opening, remove_small_objects, disk
from cucim.skimage.measure import label, regionprops_table
from cucim.core.operations.color import normalize_colors_pca
import cupy as cp

# Read whole-slide image tile
slide = CuImage("tissue.svs")
tile = cp.asarray(slide.read_region(location=(1000, 2000), size=(512, 512), level=0))

# Normalize staining
normalized = normalize_colors_pca(tile)

# Segment nuclei
gray = rgb2gray(normalized)
binary = gray < threshold_otsu(gray)
cleaned = binary_opening(binary, footprint=disk(2))
cleaned = remove_small_objects(label(cleaned), min_size=50)
labels = label(cleaned)

# Extract properties
props = regionprops_table(labels, gray, properties=['area', 'centroid', 'mean_intensity'])
Raw lines 629 to 657
658### Pattern 3: Deep Learning Preprocessing Pipeline
660```python
661import cupy as cp
662from cucim.skimage.transform import resize
663from cucim.skimage.exposure import equalize_adapthist
664from cucim.skimage.util import img_as_float32
665from cucim.core.operations.spatial import rand_image_flip
666from cucim.core.operations.color import color_jitter
667import torch
669# Load batch of images to GPU
670images_gpu = cp.asarray(numpy_batch) # (N, H, W, C)
672# Process each image on GPU
673processed = []
674for img in images_gpu:
675 img = img_as_float32(img)
676 img = resize(img, (224, 224))
677 img = equalize_adapthist(img)
678 img = rand_image_flip(img, spatial_axis=(0, 1), prob=0.5)
679 img = cp.moveaxis(img, -1, 0) # HWC -> CHW for color_jitter
680 img = color_jitter(img, brightness=0.2, contrast=0.2)
681 img = cp.moveaxis(img, 0, -1)
682 processed.append(img)
684batch_gpu = cp.stack(processed)
686# Zero-copy to PyTorch for model inference
687batch_torch = torch.from_dlpack(batch_gpu).permute(0, 3, 1, 2) # NHWC → NCHW
688```

Pattern 3: Deep Learning Preprocessing Pipeline

import cupy as cp
from cucim.skimage.transform import resize
from cucim.skimage.exposure import equalize_adapthist
from cucim.skimage.util import img_as_float32
from cucim.core.operations.spatial import rand_image_flip
from cucim.core.operations.color import color_jitter
import torch

# Load batch of images to GPU
images_gpu = cp.asarray(numpy_batch)  # (N, H, W, C)

# Process each image on GPU
processed = []
for img in images_gpu:
    img = img_as_float32(img)
    img = resize(img, (224, 224))
    img = equalize_adapthist(img)
    img = rand_image_flip(img, spatial_axis=(0, 1), prob=0.5)
    img = cp.moveaxis(img, -1, 0)  # HWC -> CHW for color_jitter
    img = color_jitter(img, brightness=0.2, contrast=0.2)
    img = cp.moveaxis(img, 0, -1)
    processed.append(img)

batch_gpu = cp.stack(processed)

# Zero-copy to PyTorch for model inference
batch_torch = torch.from_dlpack(batch_gpu).permute(0, 3, 1, 2)  # NHWC → NCHW
Raw lines 658 to 688