skills/cv/ct-z-stack-orientation-flip/SKILL.md
Detect inverted CT slice ordering by comparing ImagePositionPatient[2] (the Z coordinate) of the first and last DICOM slice in a series, flipping the volume along axis 0 when needed so every patient ends up in canonical head→feet order
npx skillsauth add wenmin-wu/ds-skills cv-ct-z-stack-orientation-flipInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
DICOM file names are not a reliable proxy for slice order. The same scanner can write 1.dcm as the topmost slice on one acquisition and the bottommost on the next, depending on protocol and reconstruction direction. If you train on a mix and don't normalize, augmentations like flip(axis=0) become silently inconsistent and the model learns a much fuzzier through-plane signal than it could. The single-line fix: read ImagePositionPatient[2] from the first and last DICOM in the series; if the last has a larger Z value than the first, the stack is inverted relative to the canonical patient frame and you flip it.
import dicomsdl
import numpy as np
def canonicalize_z_order(image, dcm_dir, slice_min, slice_max):
dcm0 = dicomsdl.open(f'{dcm_dir}/{slice_min}.dcm')
dcmN = dicomsdl.open(f'{dcm_dir}/{slice_max - 1}.dcm')
z0 = dcm0.ImagePositionPatient[2]
zN = dcmN.ImagePositionPatient[2]
if zN > z0: # inverted: flip into head→feet order
image = image[::-1]
dz = abs((zN - z0) / max(slice_max - slice_min - 1, 1))
return np.ascontiguousarray(image), dz
(D, H, W) array (in filename order), open just the first and last DICOM headersImagePositionPatient[2] — the third element is the Z coordinate in patient spacezN > z0, the stack is in feet→head order; reverse it with image[::-1]dz as the absolute difference divided by (num_slices - 1) and store it for the resampling stepnp.ascontiguousarray after the slice-reverse to avoid downstream RuntimeError: non-contiguous from torchdz: after flipping, the sign is meaningless — what the resampler needs is the magnitude.np.ascontiguousarray after [::-1]: numpy reverse-views are non-contiguous and break torch tensor zero-copy paths.data-ai
Scaled Pinball Loss (SPL) metric for evaluating quantile forecasts, normalized by mean absolute successive differences of training data
data-ai
Walk backward through a time series and multiplicatively rescale segments when jumps exceed a fraction of the running mean to correct data collection anomalies
testing
Transform forecasting target to next/current ratio minus one so that optimizing MAE or squared error implicitly minimizes SMAPE
tools
Convert point forecasts to prediction intervals by scaling with logit-transformed quantile ratios passed through a Normal CDF