Stacks uniformly sampled slices from multiple MRI series (e.g., Sagittal T1, T2, Axial) into a single multi-channel tensor for one-pass inference.
Scanned 9/12/2026
Install to Claude Code
npx -y skills add wenmin-wu/ds-skills --skill multi-series-channel-stacking --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Multi Series Channel Stacking?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/wenmin-wu-multi-series-channel-stacking)More formats (shields.io, HTML) on the badges page.
---
name: cv-multi-series-channel-stacking
description: >
Stacks uniformly sampled slices from multiple MRI series (e.g., Sagittal T1, T2, Axial) into a single multi-channel tensor for one-pass inference.
---
# Multi-Series Channel Stacking
## Overview
Medical imaging studies often contain multiple series (Sagittal T1, Sagittal T2/STIR, Axial T2) that provide complementary information. Instead of running separate models, stack a fixed number of uniformly sampled slices from each series into one multi-channel input tensor. A 3-series setup with 10 slices each produces a 30-channel input — any CNN backbone accepts this via `in_chans` override. This fuses cross-series information at the input level, letting the model learn which modality matters for each condition.
## Quick Start
```python
import numpy as np
import pydicom
def sample_slices(dicom_paths, n_slices=10):
"""Uniformly sample n_slices from center of a DICOM series."""
total = len(dicom_paths)
step = total / n_slices
start = total / 2.0 - (n_slices / 2.0 - 0.5) * step
indices = [max(0, int(round(start + i * step))) for i in range(n_slices)]
indices = [min(i, total - 1) for i in indices]
return [dicom_paths[i] for i in indices]
def build_multichannel(series_dict, img_size=256, n_per_series=10):
"""Stack slices from multiple series into (H, W, C) array."""
series_keys = sorted(series_dict.keys()) # deterministic order
total_channels = len(series_keys) * n_per_series
x = np.zeros((img_size, img_size, total_channels), dtype=np.uint8)
for idx, key in enumerate(series_keys):
paths = sorted(series_dict[key])
sampled = sample_slices(paths, n_per_series)
for j, path in enumerate(sampled):
img = pydicom.dcmread(path).pixel_array
img = cv2.resize(img, (img_size, img_size))
img = np.clip(img / img.max() * 255, 0, 255) if img.max() > 0 else img
x[..., idx * n_per_series + j] = img.astype(np.uint8)
return x
```
## Workflow
1. Group DICOM files by series description (Sagittal T1, Axial T2, etc.)
2. Sort slices within each series by instance number
3. Uniformly sample N slices from the center of each series
4. Stack into a single `(H, W, N_series × N_slices)` tensor
5. Feed to any timm model with `in_chans=total_channels`
## Key Decisions
- **Slices per series**: 10 is common; more captures detail but increases memory
- **Sampling strategy**: Center-biased uniform sampling avoids edge slices with less anatomy
- **Missing series**: Fill with zeros — the model learns to ignore empty channels
- **Normalization**: Min-max per slice to [0, 255] uint8 before stacking
## References
- [RSNA2024 LSDC DenseNet Submission](https://www.kaggle.com/code/hugowjd/rsna2024-lsdc-densenet-submission)
- [RSNA2024 LSDC Submission Baseline](https://www.kaggle.com/code/itsuki9180/rsna2024-lsdc-submission-baseline)
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!