enh: address problematic decision and document IntensityClip core function [test masks]
@@ -474,6 +474,20 @@ def _gen_reference(
def _advanced_clip(
in_file, p_min=35, p_max=99.98, nonnegative=True, dtype="int16", newpath=None
):
+ """
+ Remove outliers at both ends of the intensity distribution and fit into a given dtype.
+
+ This interface tries to emulate ANTs workflows' massaging that truncate images into
+ the 0-255 range, and applies percentiles for clipping images.
+ For image registration, normalizing the intensity into a compact range (e.g., uint8)
+ is generally advised.
+
+ To more robustly determine the clipping thresholds, data are removed of spikes
+ with a median filter.
+ Once the thresholds are calculated, the denoised data are thrown away and the thresholds
+ are applied on the original image.
+
+ """
from pathlib import Path
import nibabel as nb
import numpy as np
@@ -489,12 +503,16 @@ def _advanced_clip(
# Calculate stats on denoised version, to preempt outliers from biasing
denoised = ndimage.median_filter(data, footprint=ball(3))
- # Clip and cast
- a_min = np.percentile(denoised[denoised > 0], p_min)
- a_max = np.percentile(denoised[denoised > 0], p_max)
- if nonnegative:
- a_min = max(a_min, 0.0)
+ a_min = np.percentile(
+ denoised[denoised > 0] if nonnegative else denoised,
+ p_min
+ )
+ a_max = np.percentile(
+ denoised[denoised > 0] if nonnegative else denoised,
+ p_max
+ )
+ # Clip and cast
data = np.clip(data, a_min=a_min, a_max=a_max)
data -= data.min()
data /= data.max()
