User Guide
This guide covers both basic and advanced usage of the distance_transforms
library.
Getting Started
Installation
pip install distance_transforms
For GPU support, make sure you have PyTorch with CUDA installed.
Basic Usage
The primary function in distance_transforms
is transform
. This function takes a binary array (with 0s and 1s) and calculates the squared Euclidean distance from each 0 pixel to the nearest 1 pixel.
import numpy as np
import matplotlib.pyplot as plt
import distance_transforms as dts
# Create a random binary array
= np.random.choice([0, 1], size=(10, 10)).astype(np.float32)
arr
# Apply distance transform
= dts.transform(arr)
result
# Visualize
= plt.subplots(1, 2, figsize=(10, 5))
fig, (ax1, ax2) ='gray')
ax1.imshow(arr, cmap'Original')
ax1.set_title(='gray')
ax2.imshow(result, cmap'Distance Transform')
ax2.set_title(
plt.tight_layout() plt.show()
Real-World Example
import numpy as np
import matplotlib.pyplot as plt
import distance_transforms as dts
from skimage import io, color, filters
# Load image and process
= io.imread("sample.jpg")
img = color.rgb2gray(img)
img_gray = img_gray > filters.threshold_otsu(img_gray)
img_binary = dts.transform(img_binary.astype(np.float32))
img_dist
# Visualize
= plt.subplots(1, 3, figsize=(15, 5))
fig, axes 0].imshow(img)
axes[0].set_title('Original Image')
axes[1].imshow(img_binary, cmap='gray')
axes[1].set_title('Binary Image')
axes[2].imshow(img_dist, cmap='viridis')
axes[2].set_title('Distance Transform')
axes[
plt.tight_layout() plt.show()
Advanced Features
GPU Acceleration
distance_transforms
provides a specific function for GPU acceleration using PyTorch tensors: transform_cuda
.
import torch
import distance_transforms as dts
# Create a tensor on GPU
= torch.rand((100, 100), device='cuda')
x_gpu = (x_gpu > 0.5).float() # Convert to binary (0s and 1s)
x_gpu
# Apply transform on GPU
= dts.transform_cuda(x_gpu) result_gpu
Working with 3D Data
Both transform
and transform_cuda
support 3D arrays as well:
import numpy as np
import distance_transforms as dts
# Create a 3D binary array
= np.zeros((20, 20, 20), dtype=np.float32)
arr_3d 5, 5, 5] = 1
arr_3d[15, 15, 15] = 1
arr_3d[
# Apply distance transform
= dts.transform(arr_3d) result_3d
Performance Benchmarks
distance_transforms
generally outperforms other Python implementations, especially for large arrays and when using GPU acceleration:
Implementation | 100×100 | 1000×1000 | 100×100×100 |
---|---|---|---|
SciPy | 2.5 ms | 250 ms | 1200 ms |
distance_transforms (CPU) | 1.2 ms | 120 ms | 500 ms |
distance_transforms (GPU) | 0.8 ms | 25 ms | 120 ms |
Times are approximate and may vary based on hardware.
Benchmarking Code Example
You can benchmark the performance yourself:
import numpy as np
import torch
import time
import distance_transforms as dts
from scipy.ndimage import distance_transform_edt
# Create test data
= (224, 224)
size = np.random.choice([0, 1], size=size).astype(np.float32)
arr = torch.tensor(arr, device='cuda')
tensor
# Benchmark distance_transforms (CPU)
= time.time()
start = dts.transform(arr)
result_cpu = time.time() - start
cpu_time
# Benchmark distance_transforms (GPU)
= time.time()
start = dts.transform_cuda(tensor)
result_gpu = time.time() - start
gpu_time
# Benchmark SciPy
= time.time()
start = distance_transform_edt(arr == 0) ** 2
result_scipy = time.time() - start
scipy_time
print(f"CPU time: {cpu_time:.4f}s")
print(f"GPU time: {gpu_time:.4f}s")
print(f"SciPy time: {scipy_time:.4f}s")
print(f"GPU speedup vs. CPU: {cpu_time/gpu_time:.1f}x")
print(f"GPU speedup vs. SciPy: {scipy_time/gpu_time:.1f}x")
Integration with Deep Learning
distance_transforms
can be integrated with deep learning workflows, particularly for tasks like computing Hausdorff distance loss:
import torch
import torch.nn.functional as F
import distance_transforms as dts
def hausdorff_loss(pred, target):
# Convert predictions to binary
= (pred > 0.5).float()
pred_binary
# Calculate distance transforms
= dts.transform_cuda(pred_binary)
pred_dt = dts.transform_cuda(target)
target_dt
# Compute Hausdorff distances
= torch.mean(target * pred_dt)
d_pt = torch.mean(pred_binary * target_dt)
d_tp
return d_pt + d_tp
Example in a PyTorch Training Loop
import torch
import torch.nn as nn
import torch.optim as optim
import distance_transforms as dts
# Define a simple model
= nn.Sequential(
model 1, 16, 3, padding=1),
nn.Conv2d(
nn.ReLU(),16, 1, 3, padding=1),
nn.Conv2d(
nn.Sigmoid()
)
= optim.Adam(model.parameters(), lr=0.001)
optimizer
# Training loop with Hausdorff loss
def train(model, dataloader, epochs=10):
= torch.device('cuda' if torch.cuda.is_available() else 'cpu')
device
model.to(device)
for epoch in range(epochs):
for inputs, targets in dataloader:
= inputs.to(device), targets.to(device)
inputs, targets
# Forward pass
= model(inputs)
outputs
# Calculate loss
= F.binary_cross_entropy(outputs, targets)
dice_loss
# Add Hausdorff distance loss
= (outputs > 0.5).float()
outputs_binary = (targets > 0.5).float()
targets_binary
= dts.transform_cuda(outputs_binary)
pred_dt = dts.transform_cuda(targets_binary)
target_dt
= torch.mean(targets_binary * pred_dt) + torch.mean(outputs_binary * target_dt)
hausdorff
# Combine losses
= dice_loss + 0.5 * hausdorff
loss
# Backward pass and optimize
optimizer.zero_grad()
loss.backward()
optimizer.step()
print(f"Epoch {epoch+1}, Loss: {loss.item():.4f}")
First-Time Import Note
The first time you import distance_transforms
, it may take a while (up to 8 minutes) as it sets up the Julia environment and precompiles the necessary Julia packages. Subsequent imports will be much faster.
# First import may be slow
import distance_transforms as dts # May take up to 8 minutes
# Create a small test array to verify everything works
import numpy as np
= np.random.choice([0, 1], size=(5, 5)).astype(np.float32)
test_arr = dts.transform(test_arr)
result print(result)
Implementation Details
How it Works
distance_transforms
is a Python wrapper around the Julia package DistanceTransforms.jl
. The package uses the following approach:
- For CPU operations:
- Takes a NumPy array and converts it to a Julia array
- Applies the boolean indicator function to prepare the data
- Computes the distance transform in Julia
- Converts the result back to a NumPy array
- For GPU operations:
- Takes a PyTorch CUDA tensor and shares it with Julia using DLPack (no copying)
- Applies the boolean indicator function in Julia
- Computes the distance transform in Julia using GPU acceleration
- Shares the result back to PyTorch using DLPack
Next Steps
For detailed API information and function signatures, refer to the API Reference.