Tutorial
This tutorial demonstrates how to use BM3DORNL for removing ring artifacts from tomography sinograms using synthetic phantom data.
For an interactive version of this tutorial, see the Jupyter notebook: notebooks/tutorial.ipynb
Overview
Ring artifacts are a common problem in neutron and X-ray tomography caused by detector pixel response variations. In sinogram space, these appear as vertical streaks. BM3DORNL provides specialized algorithms to remove these artifacts while preserving image structure.
Generating Test Data
BM3DORNL includes a phantom module for generating synthetic test data:
import numpy as np
from bm3dornl.phantom import (
shepp_logan_phantom,
generate_sinogram,
simulate_detector_gain_error,
get_synthetic_noise,
)
# Generate Shepp-Logan phantom
phantom = shepp_logan_phantom(size=256)
# Create sinogram via Radon transform
clean_sinogram, angles = generate_sinogram(phantom, scan_step=0.5)
# Add detector gain errors (causes ring artifacts)
noisy_sinogram, gain = simulate_detector_gain_error(
clean_sinogram,
detector_gain_range=(0.95, 1.05),
detector_gain_error=0.02,
)
# Add synthetic noise
noise = get_synthetic_noise(
image_size=noisy_sinogram.shape,
streak_kernel_width=1,
streak_kernel_length=100,
white_noise_intensity=0.02,
streak_noise_intensity=0.03,
)
noisy_sinogram = noisy_sinogram + noise.astype(np.float32)
Basic Usage
The main function is bm3d_ring_artifact_removal:
from bm3dornl import bm3d_ring_artifact_removal
# For ring artifacts, always use mode="streak"
denoised = bm3d_ring_artifact_removal(
noisy_sinogram,
mode="streak",
sigma_random=0.05,
)
Generic vs Streak Mode
BM3DORNL provides two modes:
generic: Standard BM3D for white (random) noise
streak: Specialized mode for vertical streak artifacts
For ring artifact removal, always use mode="streak". It specifically targets
vertical structures while preserving horizontal (angular) information.
# Compare both modes
denoised_generic = bm3d_ring_artifact_removal(
noisy_sinogram,
mode="generic",
sigma_random=0.05,
)
denoised_streak = bm3d_ring_artifact_removal(
noisy_sinogram,
mode="streak",
sigma_random=0.05,
)
# Streak mode will show better artifact removal with less structure loss
Parameter Tuning
The most important parameter is sigma_random, which controls denoising strength:
# Light denoising (preserves more detail)
denoised_light = bm3d_ring_artifact_removal(
sinogram, mode="streak", sigma_random=0.02
)
# Moderate denoising (balanced)
denoised_moderate = bm3d_ring_artifact_removal(
sinogram, mode="streak", sigma_random=0.05
)
# Aggressive denoising (removes more artifacts, may over-smooth)
denoised_heavy = bm3d_ring_artifact_removal(
sinogram, mode="streak", sigma_random=0.15
)
For quality vs speed tradeoffs, adjust step_size:
# Higher quality (slower)
denoised = bm3d_ring_artifact_removal(
sinogram, mode="streak", sigma_random=0.05, step_size=2
)
# Faster processing (slightly lower quality)
denoised = bm3d_ring_artifact_removal(
sinogram, mode="streak", sigma_random=0.05, step_size=6
)
Processing 3D Stacks
BM3DORNL handles 3D sinogram stacks automatically:
# stack_3d has shape (N, H, W) - N slices
denoised_stack = bm3d_ring_artifact_removal(
stack_3d,
mode="streak",
sigma_random=0.05,
batch_size=32, # Control memory usage
)
Evaluating Results
Always check the difference image to verify you’re removing artifacts, not signal:
import matplotlib.pyplot as plt
difference = noisy_sinogram - denoised
fig, axes = plt.subplots(1, 3, figsize=(15, 5))
axes[0].imshow(noisy_sinogram, cmap='gray')
axes[0].set_title('Input')
axes[1].imshow(denoised, cmap='gray')
axes[1].set_title('Denoised')
axes[2].imshow(difference, cmap='bwr')
axes[2].set_title('Removed (should show vertical streaks)')
plt.show()
The difference image should show primarily vertical streaks (the artifacts being removed).
If you see horizontal structure, reduce sigma_random.
Best Practices
Always use streak mode for ring artifacts in sinograms
Normalize your data to [0, 1] range for best results
Start with low sigma_random and increase gradually
Check the difference image to ensure you’re removing artifacts, not signal
Use batch_size to control memory for large 3D stacks
Compare with generic mode to verify streak mode is providing benefit