Diagnostic Tools for 3D Reconstruction Troubleshooting#
This document describes diagnostic tools for identifying and fixing reconstruction artifacts in serial OCT microscopy data, particularly for obliquely-mounted samples.
Context: Oblique Sample Mounting#
Standard serial blockface histology acquisitions image samples in the three normal anatomical directions (coronal, sagittal, axial). However, for studying how contrast changes across different orientations, samples can be mounted at angles relative to these standard planes (e.g., 45° between sagittal and coronal).
While the imaging acquisition itself is standard serial blockface histology, the oblique mounting introduces additional challenges for 3D reconstruction:
Sample edges may not align with image borders
Tissue deformation patterns differ from standard orientations
Registration between slices may need rotation correction
Common Artifacts and Their Causes#
Edge Mismatches / “Overhangs”#
Symptoms: Visible discontinuities at slice boundaries, tissue edges don’t align in 3D view.
Possible Causes:
Cumulative rotation drift (mosaic-level): Small rotations between slices accumulate, causing edges to diverge
Tile dilation (tile-level): Physical positions from microscope motor don’t match actual image positions after tissue cutting/relaxation
Registration failures: Poor-quality slices cause incorrect transform estimation
Pipeline Diagnostic Options#
Master Switch: diagnostic_mode#
The simplest way to enable all diagnostics is with a single flag:
nextflow run soct_3d_reconst.nf \
--input /path/to/data \
--diagnostic_mode true \
--debug_slices "10-20" # Limit to subset for faster testing
When diagnostic_mode=true, all diagnostic analyses are enabled:
✓ Rotation drift analysis (mosaic-level)
✓ Tile dilation analysis (tile-level)
✓ Motor-only stitching
Granular Control#
If you only need specific diagnostics, keep diagnostic_mode=false and enable individual flags:
params {
// Enable specific diagnostics
analyze_rotation_drift = true // Analyze cumulative rotation between slices
motor_only_stitch = true // Create slices using only motor positions
// Configure diagnostics
diagnostic_rotation_threshold = 2.0 // Flag rotations above this (degrees)
motor_only_overlap = 0.1 // Expected tile overlap for motor-only stitch
}
Diagnostic Scripts#
1. Acquisition Rotation Analysis (linum_analyze_acquisition_rotation.py)#
NEW: Analyzes rotation patterns from raw acquisition shift data (before registration).
This examines the direction of shift vectors between consecutive slices to detect:
Consistent drift direction vs varying direction (rotation indicator)
Systematic angular drift (stage rotation)
Sudden direction changes (sample movement)
linum_analyze_acquisition_rotation.py \
shifts_xy.csv \
output_dir \
--registration_dir /path/to/register_pairwise # optional: for comparison
Output:
acquisition_rotation_data.csv: Shift angles and angular velocity per sliceacquisition_rotation_analysis.png: Vector plots and angle trendsacquisition_rotation_analysis.txt: Text report with interpretation
Interpretation:
High angle std (>90°): Shift directions vary widely - complex drift or sample movement
Low angle std (<30°): Consistent drift direction - minimal rotation
Systematic angular drift: Stage rotating during acquisition
Compare with registration rotation to see if registration is compensating
2. Registration Rotation Drift Analysis (linum_analyze_registration_transforms.py)#
Analyzes cumulative rotation between consecutive slices from pairwise registration outputs.
linum_analyze_registration_transforms.py \
/path/to/register_pairwise \
output_dir \
--rotation_threshold 2.0
Output:
rotation_data.csv: Per-slice rotation valuesrotation_analysis.png: Visualization plotsrotation_analysis.txt: Text report
Interpretation:
Mean rotation ≠ 0: Systematic bias, may indicate tilted sample or stage
High cumulative drift: Edges will diverge in 3D; consider
registration_transform='euler'Sudden large rotations: Check slice quality, may need exclusion
3. Tile Dilation Analysis (linum_analyze_tile_dilation.py)#
Compares expected motor positions to registration-derived positions.
linum_analyze_tile_dilation.py \
mosaic_grid_z10.ome.zarr \
transform_xy.npy \
output_dir \
--overlap_fraction 0.1
Output:
dilation_analysis.json: Quantitative metricsdilation_analysis.png: Vector field and heatmap visualizationsdilation_analysis.txt: Text report with interpretation
Interpretation:
Scale factor > 1: Tiles spread more than expected (tissue expansion after cutting)
Scale factor < 1: Tiles spread less than expected (stage calibration issue)
Anisotropic scaling: Different X/Y scales cause shearing in 3D
4. Motor-Only Stitching (linum_stitch_motor_only.py)#
Creates stitched mosaic using ONLY motor positions (bypassing image registration).
linum_stitch_motor_only.py \
mosaic_grid_z10.ome.zarr \
slice_z10_motor_only.ome.zarr \
--overlap_fraction 0.1
Interpretation: By comparing motor-only vs fully-registered stitches:
Large differences: Registration is compensating for motor errors (may indicate dilation)
Systematic offsets: Stage calibration issue
Random scatter: Normal registration refinement
5. Aggregated Dilation Analysis (linum_aggregate_dilation_analysis.py)#
NEW: Aggregates dilation analysis from multiple slices to compute recommended scale correction factors.
linum_aggregate_dilation_analysis.py \
/path/to/dilation_analysis \
output_dir
Output:
aggregated_dilation_analysis.json: Summary statistics and correction factorsper_slice_correction_factors.csv: Per-slice factors for advanced useaggregated_dilation_report.txt: Text report with recommendationsaggregated_dilation_analysis.png: Visualizations across slices
Interpretation:
Recommended scale_y, scale_x: Apply these to compensate for systematic dilation
Deviation from unity: How much mosaics contract/expand vs expected
Anisotropy: If X/Y differ, use separate correction factors
6. Comprehensive Diagnostics (linum_diagnose_reconstruction.py)#
Runs all analyses and generates a unified report.
linum_diagnose_reconstruction.py \
/path/to/pipeline_output \
output_dir \
--rotation_threshold 2.0 \
--slice_range "10-40"
Output:
diagnostic_report.txt: Comprehensive text reportdiagnostic_results.json: All metrics in JSON formatIndividual analysis plots
Troubleshooting Workflow#
flowchart TD
A[Reconstruction artifact:<br/>edge mismatch / overhang] --> B[linum_diagnose_reconstruction.py<br/>diagnostic_report.txt]
B --> C{Symptom?}
C -->|cumulative rotation drift| D[Rotation drift]
C -->|individual slice quality| E[Bad slice]
C -->|large inter-slice XY jumps| F[Motor / encoder issue]
C -->|tile-level mismatch| G[Tile dilation]
D --> D1[Set registration_transform=euler<br/>raise registration_max_rotation]
E --> E1[auto_assess_quality=true<br/>auto_exclude_enabled=true]
F --> F1[detect_rehoming=true<br/>tile_fov_mm=0.875<br/>common_space_refine_unreliable=true]
G --> G1[linum_analyze_tile_dilation.py<br/>then per-tile correction]
D1 --> R[Re-run with --debug_slices subset]
E1 --> R
F1 --> R
G1 --> R
R --> V[Re-run diagnostics<br/>verify improvement]
V -->|still bad| C
V -->|fixed| DONE([Done])
Step 1: Quick Assessment#
# Run diagnostics on existing output
linum_diagnose_reconstruction.py /path/to/sub-18 diagnostics
Review diagnostic_report.txt for issues flagged.
Step 2: Identify Root Cause#
If rotation drift is high:
// Ensure Euler transform is used
registration_transform = 'euler'
registration_max_rotation = 35.0 // Increase if needed for oblique cuts
If specific slices are problematic:
// Exclude degraded slices via slice_config.csv
// Or enable automatic quality assessment and exclusion:
auto_assess_quality = true
auto_assess_min_quality = 0.3
auto_exclude_enabled = true
If large inter-slice shifts are causing misalignment:
// Enable rehoming detection to correct encoder glitch spikes:
detect_rehoming = true
rehoming_max_shift_mm = 0.5
// For tile-column expansion events (legacy data):
tile_fov_mm = 0.875
// Enable image-based refinement for transitions flagged reliable=0:
common_space_refine_unreliable = true
common_space_refine_max_discrepancy_px = 0
Step 3: Reprocess with Fixes#
# Re-run with adjusted parameters
nextflow run soct_3d_reconst.nf \
--input /path/to/data \
--registration_transform euler \
--registration_max_rotation 40.0 \
--debug_slices "20-40" # Test on subset first
Step 4: Validate Fix#
# Re-run diagnostics to confirm improvement
linum_diagnose_reconstruction.py /path/to/new_output diagnostics_after_fix
Example: Troubleshooting sub-18 Oblique Brain#
Based on a 45° oblique-cut mouse brain with edge matching issues:
Run initial diagnostics:
linum_diagnose_reconstruction.py sub-18 sub-18/diagnostics
Check rotation analysis (
diagnostics/rotation_analysis.txt):If cumulative rotation > 5°, edges will misalign
Compare motor-only stitches with registered stitches:
Visual inspection of differences reveals registration contribution
Parameters Reference#
Diagnostic Parameters#
Parameter |
Default |
Description |
|---|---|---|
|
false |
Master switch: enables ALL diagnostics when true |
|
false |
Analyze inter-slice rotation (mosaic-level) |
|
false |
Create motor-position-only stitches |
|
false |
Create 3D stack using motor positions only |
|
2.0 |
Flag rotations above this (degrees) |
|
0.2 |
Expected tile overlap fraction |
Tile Stitching Parameters#
The pipeline uses motor positions for tile placement by default, which provides better alignment than registration-based positioning. Motor positions are precise and don’t introduce the systematic compression seen with image-based registration.
Parameter |
Default |
Description |
|---|---|---|
|
true |
Use motor positions for tile placement (recommended) |
|
0.2 |
Expected tile overlap fraction (must match acquisition) |
Why motor positions? Analysis of registration-based stitching showed systematic compression (scale_y ≈ 0.97, scale_x ≈ 0.99), meaning tiles were placed ~3% closer than motor positions indicated. This caused “overhang” artifacts when stacking slices. Motor positions are precise and don’t introduce this compression.
Note: When diagnostic_mode=true, the individual diagnostic flags (analyze_rotation_drift,
analyze_tile_dilation, motor_only_stitch, motor_only_stack) are automatically enabled.
Output Directory Structure#
When diagnostics are enabled, outputs appear in:
output/
├── diagnostics/
│ ├── rotation_analysis/
│ │ ├── rotation_data.csv
│ │ ├── rotation_analysis.png
│ │ └── rotation_analysis.txt
│ ├── acquisition_rotation/
│ ├── motor_only_stitch/
│ │ ├── slice_z02_motor_only.ome.zarr
│ │ ├── slice_z03_motor_only.ome.zarr
│ │ └── ...
│ ├── refined_stitch/
│ ├── motor_only_stack/
│ └── stitch_comparison/
└── ...