This document describes the directory structure for the HiP-CT / LabCT / Histology / Xenium spatial transcriptomics pipeline, organised around the sample (whole organ) as the root entity. The path to any file encodes its full physical provenance.
| Principle | Detail |
|---|---|
| Sample-centric root | Each organ is the top-level entity; everything hangs from it |
| Path = provenance | organ → chunks → blocks → cores mirrors the physical cutting chain |
| Modality at each level | HiP-CT, LabCT, Histology, Xenium appear wherever they apply |
| Registration alongside data | Each physical level has its own registration/ folder |
| Xenium runs at project level | Run data + overview image live outside all sample folders |
| Blocks at two levels | Under organ/blocks/ (small samples) OR organ/chunks/chunk/blocks/ (large organs) |
| Cores are fully equipped | Cores support labct, histology, xenium, and registration — same as blocks |
<project_root>/
│
├── xenium_runs/ # ALL Xenium run data — outside every sample
│ └── xenium_run_<run_id>/
│ ├── run_overview_image.ome.tif # whole-slide overview (all ROIs on this slide)
│ ├── run_metadata.json
│ └── roi_<r01>/ # one folder per sample ROI on the slide
│ ├── raw/
│ ├── processed/
│ ├── he_image/
│ └── metadata/
│
├── organ_<donor_id>_<organ>/ # one folder per sample
└── organ_<donor_id>_<organ>/
A single Xenium run places multiple samples on one slide. The overview image captures the whole slide and belongs to no single sample. Each block/xenium/ and core/xenium/ folder holds only a JSON reference file pointing to the relevant ROI in xenium_runs/. Registration transforms (Xenium → histology) remain inside the block's or core's own registration/ folder.
organ_<donor_id>_<organ>/
├── metadata/
│ ├── donor_metadata.json
│ └── organ_processing_metadata.json
├── hipct/
│ ├── whole_organ_scan/
│ │ ├── raw/ ├── processed/ └── metadata/
│ └── zoom_scan_<z01>/
│ ├── raw/ ├── processed/ └── metadata/
├── labct/ # whole-organ LabCT — small organs only
│ ├── raw/ ├── processed/ └── metadata/
├── blocks/ # blocks cut DIRECTLY from organ (small samples)
│ └── block_<b01>/ ...
├── chunks/ # ~5 cm cuts — large organs only
│ └── chunk_<c01>/ ...
└── registration/
├── hipct_whole_to_labct_organ/
└── hipct_organ_to_chunk/
Blocks (~1–2 cm paraffin-embedded) exist at two points in the hierarchy:
organ_.../blocks/block_<id>/— cut directly from the organ (small samples)organ_.../chunks/chunk_<id>/blocks/block_<id>/— cut from a chunk
The internal structure is identical. The registration anchor differs: small-organ blocks use labct_block_to_hipct_organ/; chunk-level blocks use labct_block_to_hipct_chunk/.
block_<b01>/
├── metadata/
├── labct/
│ ├── raw/ ├── processed/ └── metadata/
├── histology/
│ ├── single_sections/
│ │ └── section_<s01>/
│ │ ├── raw/ ├── processed/ └── metadata/
│ └── serial_sections/
│ ├── metadata/
│ └── section_<s001>/
│ ├── raw/ └── processed/
├── xenium/
│ └── run_ref.json # JSON pointer to xenium_runs/…/roi_<r>/
├── cores/
│ └── core_<k01>/ ... # see Core Structure below
└── registration/
├── labct_block_to_hipct_chunk/ # (or _to_hipct_organ/ for small samples)
├── histology_to_labct_block/
├── xenium_roi_to_histology/
└── serial_sections_to_labct_block/
Cores (~1 mm TMA-style) are punched from blocks. They support the same modalities as blocks — including histology (single or serial sections) and Xenium ST if the core is independently sectioned and placed on a Xenium slide.
core_<k01>/
├── labct/
│ ├── raw/ ├── processed/ └── metadata/
├── histology/
│ ├── single_sections/
│ │ └── section_<s01>/
│ │ ├── raw/ ├── processed/ └── metadata/
│ └── serial_sections/
│ ├── metadata/
│ └── section_<s001>/
│ ├── raw/ └── processed/
├── xenium/
│ └── run_ref.json # JSON pointer if core was Xenium-profiled
└── registration/
├── labct_core_to_labct_block/
├── histology_to_labct_core/
└── xenium_roi_to_histology_core/
Each physical level has a registration/ folder. Sub-directories are named <source>_to_<target>/ and contain:
transform.h5 # affine or B-spline (ITK HDF5)
deformation_field.nii.gz # dense deformation field (non-rigid)
reg_metadata.json # software, metric, date
Transforms chain across levels to achieve full-pipeline registration, e.g.:
histology section
→ histology_to_labct_block
→ labct_block_to_hipct_chunk
→ hipct_chunk_to_hipct_organ
| Entity | Pattern | Example |
|---|---|---|
| Organ / sample | organ_<donor_id>_<organ> |
organ_D001_adult_heart |
| Chunk | chunk_<organ_id>_<cNN> |
chunk_D001_adult_heart_c01 |
| Block | block_<organ_id>_<bNN> |
block_D001_adult_heart_b01 |
| Core | core_<organ_id>_<kNN> |
core_D001_adult_heart_k01 |
| HiP-CT zoom | zoom_scan_<zNN> |
zoom_scan_z01 |
| Section | section_<sNNN> |
section_s001 |
| Xenium run | xenium_run_<run_id> |
xenium_run_XR001 |
| Xenium ROI | roi_<rNN> |
roi_r01 |
An adult heart is scanned whole with HiP-CT, then physically cut. One chunk (left ventricular free wall) is scanned with HiP-CT and LabCT. A block is cut from the chunk and processed for serial-section histology and Xenium. A core from the block is also sectioned for histology and profiled independently with Xenium.
project_root/
│
├── xenium_runs/
│ └── xenium_run_XR005/
│ ├── run_overview_image.ome.tif # adult heart block + fetal heart block on same slide
│ ├── run_metadata.json
│ ├── roi_r01/ # adult heart block_b01 ROI
│ ├── roi_r02/ # adult heart core_k01 ROI (core on same slide)
│ └── roi_r03/ # fetal heart block ROI (different organ, same run)
│
└── organ_D001_adult_heart/
├── metadata/
├── hipct/
│ ├── whole_organ_scan/ # full adult heart volume
│ └── zoom_scan_z01/ # zoom into myocardium
├── labct/ # EMPTY — adult heart too large for whole-organ LabCT
├── blocks/ # EMPTY — adult heart uses chunks; blocks under chunks/
├── chunks/
│ └── chunk_D001_adult_heart_c01/ # left ventricular free wall
│ ├── metadata/
│ ├── hipct/
│ │ ├── whole_chunk_scan/
│ │ └── zoom_scan_z01/
│ ├── labct/
│ ├── blocks/
│ │ └── block_D001_adult_heart_b01/
│ │ ├── labct/
│ │ ├── histology/
│ │ │ └── serial_sections/ # serial sections for 3-D reconstruction
│ │ ├── xenium/
│ │ │ └── run_ref.json # → xenium_runs/XR005/roi_r01/
│ │ ├── cores/
│ │ │ └── core_D001_adult_heart_k01/
│ │ │ ├── labct/ # high-res core LabCT
│ │ │ ├── histology/ # sections from the core
│ │ │ ├── xenium/
│ │ │ │ └── run_ref.json # → xenium_runs/XR005/roi_r02/
│ │ │ └── registration/
│ │ │ ├── labct_core_to_labct_block/
│ │ │ ├── histology_to_labct_core/
│ │ │ └── xenium_roi_to_histology_core/
│ │ └── registration/
│ │ ├── labct_block_to_hipct_chunk/
│ │ ├── histology_to_labct_block/
│ │ └── xenium_roi_to_histology/
│ └── registration/
│ ├── hipct_chunk_to_labct_chunk/
│ └── hipct_chunk_to_hipct_organ/
└── registration/
├── hipct_whole_to_labct_organ/
└── hipct_organ_to_chunk/
A fetal heart is small enough to scan whole with both HiP-CT and LabCT. No chunking step is needed. Two blocks are cut directly from the intact organ. One block is processed for Xenium (sharing the same run slide as the adult heart block above). A core from that block is independently sectioned for histology but not Xenium-profiled.
project_root/
│
├── xenium_runs/
│ └── xenium_run_XR005/
│ └── roi_r03/ # fetal heart block ROI on shared slide
│
└── organ_F001_fetal_heart/
├── metadata/
├── hipct/
│ └── whole_organ_scan/ # whole fetal heart HiP-CT
├── labct/
│ ├── raw/ # POPULATED — fetal heart scanned whole
│ ├── processed/
│ └── metadata/
├── blocks/ # POPULATED — blocks cut directly from organ
│ ├── block_F001_fetal_heart_b01/
│ │ ├── labct/
│ │ ├── histology/
│ │ │ └── single_sections/
│ │ ├── xenium/
│ │ │ └── run_ref.json # → xenium_runs/XR005/roi_r03/
│ │ ├── cores/
│ │ │ └── core_F001_fetal_heart_k01/
│ │ │ ├── labct/
│ │ │ ├── histology/ # core sectioned independently
│ │ │ ├── xenium/ # run_ref.json left blank (no Xenium for this core)
│ │ │ └── registration/
│ │ │ ├── labct_core_to_labct_block/
│ │ │ └── histology_to_labct_core/
│ │ └── registration/
│ │ ├── labct_block_to_hipct_organ/ # note: _organ not _chunk
│ │ ├── histology_to_labct_block/
│ │ └── xenium_roi_to_histology/
│ └── block_F001_fetal_heart_b02/ # second block — histology only, no Xenium
│ ├── histology/
│ └── registration/
├── chunks/ # EMPTY — no chunking for fetal heart
└── registration/
└── hipct_whole_to_labct_organ/
{
"_comment": "Edit run_id and roi_id with actual Xenium run values.",
"run_id": "xenium_run_XR005",
"roi_id": "roi_r01",
"project_xenium_runs_path": "../../../../../../xenium_runs/"
}python create_pipeline_structure.py \
--root /path/to/project \
--sample organ_D001_adult_heartpython create_pipeline_structure.py \
--root /path/to/project \
--populate-existingpython create_pipeline_structure.py \
--root /path/to/project \
--sample organ_F001_fetal_heart \
--dry-run| Flag | Description |
|---|---|
--root <path> |
Project root directory (required) |
--sample <name> |
Create structure for a single named sample |
--populate-existing |
Scaffold all existing top-level folders under --root |
--dry-run |
Print what would be created; do not write to disk |
--quiet |
Suppress per-folder output |
| Data type | Location |
|---|---|
| Whole-organ HiP-CT | organ_.../hipct/whole_organ_scan/ |
| HiP-CT zoom | organ_.../hipct/zoom_scan_<z>/ |
| Whole-organ LabCT (small organs) | organ_.../labct/ |
| Chunk HiP-CT | organ_.../chunks/chunk_<c>/hipct/ |
| Chunk LabCT | organ_.../chunks/chunk_<c>/labct/ |
| Block LabCT | .../blocks/block_<b>/labct/ |
| Block histology | .../blocks/block_<b>/histology/ |
| Core LabCT | .../blocks/block_<b>/cores/core_<k>/labct/ |
| Core histology | .../blocks/block_<b>/cores/core_<k>/histology/ |
| Core Xenium | .../blocks/block_<b>/cores/core_<k>/xenium/run_ref.json |
| Xenium ROI data | xenium_runs/xenium_run_<r>/roi_<roi>/ |
| Xenium overview image | xenium_runs/xenium_run_<r>/run_overview_image.ome.tif |
| Registration transforms | registration/ at each physical level |