Skip to content
57 changes: 57 additions & 0 deletions docs-samples/data-science/gpu-accelerated-samples/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# GPU-Accelerated Samples

Jupyter notebooks demonstrating GPU acceleration using NVIDIA RAPIDS libraries on Microsoft Fabric and Azure VMs with NVIDIA GPUs.

> Based on examples from [NVIDIA RAPIDS](https://github.com/rapidsai) (Apache 2.0 License, Copyright NVIDIA Corporation). Modified and tested for Azure GPU VM environment.

## Requirements

- NVIDIA GPU (Tesla T4 or better)
- CUDA 12.x
- Python 3.12+
- RAPIDS 25.x (cuDF, cuML, cuCIM)
- Conda environment recommended

## Notebooks

| Notebook | Description |
|----------|-------------|
| [cudf-pandas-accelerator-demo.ipynb](cudf-pandas-accelerator-demo.ipynb) | Drop-in GPU acceleration for pandas with `cudf.pandas` — no code changes needed |
| [cudf-pandas-stock-analysis.ipynb](cudf-pandas-stock-analysis.ipynb) | Stock market data analysis using GPU-accelerated pandas (read, merge, resample, plot) |
| [cuml-scikit-learn-accelerator-demo.ipynb](cuml-scikit-learn-accelerator-demo.ipynb) | Drop-in GPU acceleration for scikit-learn (PCA, UMAP, KNN, HDBSCAN on activity recognition data) |
| [gpu-vs-cpu-compute-benchmark.ipynb](gpu-vs-cpu-compute-benchmark.ipynb) | Side-by-side GPU vs CPU benchmark for common compute operations |
| [rapids-dataframe-gpu-vs-cpu.ipynb](rapids-dataframe-gpu-vs-cpu.ipynb) | RAPIDS cuDF DataFrame operations compared to pandas on larger datasets |
| [multi-gpu-embedding-and-knn-search.ipynb](multi-gpu-embedding-and-knn-search.ipynb) | Multi-GPU text embedding generation and KNN similarity search using Ray + RAPIDS |

## cuCIM Medical Imaging

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

these notebooks seem to be a little bit heavy as sample notebooks, I'd suggest we combine them into one notebook and keep core components in the notebooks.


GPU-accelerated image processing for digital pathology and microscopy using [cuCIM](https://github.com/rapidsai/cucim).

> Based on examples from [rapidsai/cucim](https://github.com/rapidsai/cucim) (Apache 2.0 License, Copyright NVIDIA Corporation). Modified for Azure GPU VM environment.

| Notebook | Description |
|----------|-------------|
| [01-whole-slide-image-reading.ipynb](cucim_medical_imaging/01-whole-slide-image-reading.ipynb) | Reading and displaying multi-resolution whole-slide pathology images |
| [02-image-cache-performance.ipynb](cucim_medical_imaging/02-image-cache-performance.ipynb) | Tile caching strategies for faster repeated region access |
| [03-gabor-texture-classification.ipynb](cucim_medical_imaging/03-gabor-texture-classification.ipynb) | GPU vs CPU Gabor filter bank for texture classification |
| [04-random-walker-segmentation.ipynb](cucim_medical_imaging/04-random-walker-segmentation.ipynb) | GPU-accelerated random walker image segmentation |
| [05-vesselness-filter.ipynb](cucim_medical_imaging/05-vesselness-filter.ipynb) | GPU-accelerated vessel detection using Frangi/Sato filters |

### Setup for cuCIM notebooks

**Notebooks 01 and 02** require a whole-slide image file. Before running them, create the `input/` folder and download a sample image:

```bash
cd cucim_medical_imaging
mkdir -p input
# Download a sample Aperio SVS whole-slide image (~170MB)
wget -O input/image.tif https://openslide.cs.cmu.edu/download/openslide-testdata/Aperio/CMU-1.svs
```

**Notebooks 03, 04, and 05** are self-contained — they use synthetic data from scikit-image and do not require any additional files.

### Notes

- **multi-gpu-embedding-and-knn-search.ipynb** requires an Azure OpenAI API key and endpoint. Update the `api_key` and `azure_endpoint` values in Step 1 before running.
- **cuml-scikit-learn-accelerator-demo.ipynb** downloads the UCI HAR dataset to `/tmp/HAR_data/` on first run (requires internet access).
- **cudf-pandas-stock-analysis.ipynb** downloads stock price data from Yahoo Finance (requires internet access).
Original file line number Diff line number Diff line change
@@ -0,0 +1,314 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "eb420212",
"metadata": {},
"source": [
"# Basic Usage\n",
"\n",
"This notebook shows basic usage of cuCIM with a whole-slide pathology image."
]
},
{
"cell_type": "markdown",
"id": "39389384",
"metadata": {},
"source": [
"## Prerequisites"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "93157b57",
"metadata": {},
"outputs": [],
"source": [
"# !conda install -c conda-forge pillow"
]
},
{
"cell_type": "markdown",
"id": "6378c4a3",
"metadata": {},
"source": [
"## Read image"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "c02bf93e",
"metadata": {},
"outputs": [],
"source": [
"from cucim import CuImage\n",
"\n",
"img = CuImage(\"input/image.tif\")\n",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Better to auto download the data from a blob storage to local path.

"print(f\"Image loaded: {img.path}\")\n",
"print(f\"Dimensions (XY): {img.size('XY')}\")"
]
},
{
"cell_type": "markdown",
"id": "2b371170",
"metadata": {},
"source": [
"### See metadata"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "4ff52186",
"metadata": {},
"outputs": [],
"source": [
"import json\n",
"\n",
"metadata = img.metadata\n",
"print(json.dumps(metadata, indent=2)[:2000])"
]
},
{
"cell_type": "markdown",
"id": "5ffeeeed",
"metadata": {},
"source": [
"### Read region\n",
"\n",
"Let's read the whole slide at the lowest resolution and save as a thumbnail."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "ec613ae8",
"metadata": {},
"outputs": [],
"source": [
"from PIL import Image\n",
"\n",
"resolutions = img.resolutions\n",
"level_dimensions = resolutions[\"level_dimensions\"]\n",
"level_count = resolutions[\"level_count\"]\n",
"\n",
"print(f\"Number of levels: {level_count}\")\n",
"print(f\"Level dimensions: {level_dimensions}\")\n",
"\n",
"# Read the whole slide at lowest resolution\n",
"region = img.read_region(\n",
" location=[0, 0], size=level_dimensions[level_count - 1], level=level_count - 1\n",
")\n",
"\n",
"# Save and display\n",
"region.save(\"input/thumbnail.ppm\")\n",
"Image.open(\"input/thumbnail.ppm\")"
]
},
{
"cell_type": "markdown",
"id": "2f6da447",
"metadata": {},
"source": [
"Now let's read a 512x512 region at full resolution (level 0)."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "daacf8cf",
"metadata": {},
"outputs": [],
"source": [
"from PIL import Image\n",
"\n",
"region = img.read_region([10000, 20000], [512, 512], 0)\n",
"region.save(\"input/test.ppm\")\n",
"Image.open(\"input/test.ppm\")"
]
},
{
"cell_type": "markdown",
"id": "6c367337",
"metadata": {},
"source": [
"### `__array_interface__` support\n",
"\n",
"A NumPy array has the `__array_interface__` property. Here is a simple example:"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "e09b53d6",
"metadata": {},
"outputs": [],
"source": [
"import numpy as np\n",
"\n",
"np_arr = np.array([1, 2, 3])\n",
"print(np_arr.__array_interface__)"
]
},
{
"cell_type": "markdown",
"id": "f7a3f798",
"metadata": {},
"source": [
"As you can see from the above result, a NumPy array has `__array_interface__` property which describes the memory layout.\n",
"\n",
"**cuCIM also supports `__array_interface__`**, so you can directly convert a cuCIM region to a NumPy array and display it with PIL:"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "0804834e",
"metadata": {},
"outputs": [],
"source": [
"import numpy as np\n",
"from PIL import Image\n",
"\n",
"region = img.read_region([10000, 20000], [512, 512], 0)\n",
"np_img_arr = np.asarray(region)\n",
"\n",
"print(f\"Array shape: {np_img_arr.shape}, dtype: {np_img_arr.dtype}\")\n",
"arr_iface = np_img_arr.__array_interface__\n",
"print(f\"__array_interface__ shape: {arr_iface['shape']}\")\n",
"\n",
"Image.fromarray(np_img_arr)"
]
},
{
"cell_type": "markdown",
"id": "0b702b4e",
"metadata": {},
"source": [
"### `__cuda_array_interface__` support\n",
"\n",
"A CuPy array has `__cuda_array_interface__` property. Here is a simple example:"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "0990d4d4",
"metadata": {},
"outputs": [],
"source": [
"import cupy as cp\n",
"\n",
"cp_arr = cp.array([1, 2, 3])\n",
"print(cp_arr.__cuda_array_interface__)"
]
},
{
"cell_type": "markdown",
"id": "a130d04f",
"metadata": {},
"source": [
"A Python object that has `__cuda_array_interface__` property is considered as a CUDA array-like object and can be converted to CuPy array through `cp.asarray(obj)` method.\n",
"\n",
"**`__cuda_array_interface__` is also supported in CuImage** if you specify `device='cuda'` in `read_region()`. The following code loads image data directly to GPU memory and visualizes it in the Jupyter notebook:"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "5a43d245",
"metadata": {},
"outputs": [],
"source": [
"import cupy as cp\n",
"from cucim import CuImage\n",
"from PIL import Image\n",
"\n",
"img = CuImage(\"input/image.tif\")\n",
"resolutions = img.resolutions\n",
"level_dimensions = resolutions[\"level_dimensions\"]\n",
"level_count = resolutions[\"level_count\"]\n",
"\n",
"# Read region directly to GPU memory\n",
"region_gpu = img.read_region(\n",
" [0, 0], level_dimensions[level_count - 1], level_count - 1, device=\"cuda\"\n",
")\n",
"\n",
"print(f\"Device: {region_gpu.device}\")\n",
"cuda_iface = region_gpu.__cuda_array_interface__\n",
"print(f\"__cuda_array_interface__ shape: {cuda_iface['shape']}\")\n",
"\n",
"# Convert to CuPy array, then to CPU for display\n",
"cupy_arr = cp.asarray(region_gpu)\n",
"print(f\"CuPy array shape: {cupy_arr.shape}\")\n",
"Image.fromarray(cupy_arr.get())"
]
},
{
"cell_type": "markdown",
"id": "499f2e79",
"metadata": {},
"source": [
"### Associated images\n",
"\n",
"Some image formats such as Philips TIFF and Aperio SVS have associated images (Macro or Label images) in addition to the multi-resolution images.\n",
"\n",
"Let's check what associated images are available:"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "b9714d55",
"metadata": {},
"outputs": [],
"source": [
"import numpy as np\n",
"from PIL import Image\n",
"\n",
"print(f\"Associated images: {img.associated_images}\")\n",
"\n",
"for name in img.associated_images:\n",
" assoc_img = img.associated_image(name)\n",
" np_arr = np.asarray(assoc_img)\n",
" print(f\" '{name}': shape={np_arr.shape}\")\n",
"\n",
"# Display the macro image if available\n",
"if 'macro' in img.associated_images:\n",
" macro_image = img.associated_image('macro')\n",
" np_img_arr = np.asarray(macro_image)\n",
" print(f\"\\nDisplaying macro image: {np_img_arr.shape}\")\n",
" display(Image.fromarray(np_img_arr))\n",
"\n",
"# Check for label image\n",
"if 'label' in img.associated_images:\n",
" print(\"Label image exists\")\n",
"else:\n",
" print(\"\\nThere is no associated image named 'label'!\")"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.13.11"
}
},
"nbformat": 4,
"nbformat_minor": 5
}
Loading