Skip to content

cleanup, fixes, refactor#10

Merged
m-reuter merged 6 commits intomainfrom
fixes
Apr 25, 2026
Merged

cleanup, fixes, refactor#10
m-reuter merged 6 commits intomainfrom
fixes

Conversation

@m-reuter
Copy link
Copy Markdown
Member

@m-reuter m-reuter commented Apr 25, 2026

  • fixes import of .img files that have a separate .ifh header (Nibabel cannot do it)
  • merge and unify tkRAS conversion to avoid duplications
  • make all project imports local
  • add consistent masking (ignore masked voxels)

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR refactors image/geometry handling to better match FreeSurfer conventions, adds support for 4dfp .img + .ifh sidecars, and extends registration pipelines/CLIs to accept masks (excluding masked voxels rather than zero-filling).

Changes:

  • Add a shared load_image() helper that applies .ifh geometry overrides for Analyze-style images and migrate call sites to use it.
  • Centralize tkRAS/voxel/RAS conversion helpers in neuroreg.image.geometry and reuse them in transform conversions and BBR paths.
  • Add mask support across IRLS (robreg), Powell coreg, and GD registration (plus corresponding CLI flags and tests).

Reviewed changes

Copilot reviewed 34 out of 34 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
tests/test_robreg.py Adds tests for .ifh affine loading and mask / singleton-4D handling in robreg wrapper.
tests/test_lta.py Adds tests validating tkRAS/vox2tkRAS helpers for NIfTI volume info.
tests/test_irls.py Adds tests for mask filtering in IRLS linear system and empty mask intersections.
tests/test_coreg.py Adds tests ensuring masks are forwarded through coreg (Powell/GD) and 4D singleton handling.
tests/test_cli.py Adds CLI tests verifying --mov-mask/--ref-mask forwarding for robreg/coreg CLIs.
tests/test_bbreg.py Updates bbreg prealignment tests to use mask exclusion instead of zero-filling.
neuroreg/transforms/regdat.py Reuses centralized vox2tkras_from_volume_info() for regdat conversions.
neuroreg/transforms/lta.py Switches header loading from nib.load to load_image() for .ifh support.
neuroreg/transforms/fsl.py Removes duplicated tkRAS computation and reuses centralized geometry helper.
neuroreg/segreg/register.py Replaces local image loader with shared load_image().
neuroreg/segreg/centroids.py Removes duplicated load_spatial_image in favor of shared load_image().
neuroreg/imreg/robreg.py Adds mask plumbing + singleton-4D coercion; uses shared load_image().
neuroreg/imreg/reg_model.py Converts imports to local/relative project imports.
neuroreg/imreg/powell.py Adds optional masks to NMI evaluator + load via load_image/load_mask; supports singleton-4D coercion.
neuroreg/imreg/optimize.py Adds mask-aware loss evaluation via masked_mean and masked NCC/MI/NMI.
neuroreg/imreg/losses.py Adds masked_mean and mask support to NCC/MI/NMI losses.
neuroreg/imreg/irls.py Adds mask-aware voxel filtering in construct_Ab/register_irls and empty-system handling.
neuroreg/imreg/gd.py Adds mask pyramids + forwards masks through GD optimizer/training loop; uses load_image().
neuroreg/imreg/coreg.py Extends public coreg API to accept and forward masks to backends.
neuroreg/image/segmentation.py Migrates image loading and tkRAS accessors to new shared geometry + loader.
neuroreg/image/masking.py New shared mask loading/coercion/pyramid utilities.
neuroreg/image/map.py Adds coerce_image_data_3d() and uses it in resampling.
neuroreg/image/io.py New shared load_image() with optional 4dfp .ifh affine override.
neuroreg/image/geometry.py New centralized tkRAS/voxel/RAS conversion helpers.
neuroreg/image/init.py Exports new geometry and io helpers.
neuroreg/cli/segreg.py Migrates CLI to local imports + load_image().
neuroreg/cli/robreg.py Adds --mov-mask/--ref-mask and forwards masks to robreg.
neuroreg/cli/lta.py Migrates to local imports.
neuroreg/cli/coreg.py Adds --mov-mask/--ref-mask and forwards masks to coreg.
neuroreg/cli/bbreg.py Migrates to load_image() and updates prealignment to pass target mask instead of masked reference image.
neuroreg/bbreg/register.py Migrates to load_image() and uses centralized tkRAS geometry helper.
neuroreg/bbreg/optimize.py Updates tkRAS-related error guidance text; minor formatting.
neuroreg/bbreg/io.py Removes duplicated tkRAS helper (now centralized) and adjusts formatting/imports.
neuroreg/init.py Converts package-level imports to relative form.
Comments suppressed due to low confidence (2)

neuroreg/imreg/irls.py:600

  • When A.shape[0] == 0, w_sqrt is empty, so (w_sqrt == 0).float().mean() produces NaN for zero_pct. This can leak into logs and into adaptive_sat logic. Guard this case explicitly (e.g., set zero_pct=100.0 or 0.0 when w_sqrt.numel()==0).
        # Solve IRLS on normalised images
        if A.shape[0] == 0:
            p = torch.zeros(6, dtype=src.dtype, device=src.device)
            w_sqrt = torch.zeros(0, dtype=src.dtype, device=src.device)
            sigma_val = 0.0
            err_val = float('inf')
        else:
            p, w_sqrt, sigma_val, err_val = irls_inner_loop(
                A, b, sat=current_sat, max_iterations=max_irls, verbose=verbose)
        info['sigma_hist'].append(sigma_val)

        # Check outlier percentage for adaptive sat adjustment
        zero_pct = (w_sqrt == 0).float().mean().item() * 100

neuroreg/imreg/losses.py:98

  • ncc_loss allows 1-D/2-D tensors (preds.dim() > 3 check) but then unconditionally calls F.avg_pool3d, which requires 3-D spatial inputs. Either enforce preds.dim() == target.dim() == 3 up front (with a clearer error) or add a 2-D path using avg_pool2d.
    if preds.dim() > 3 or target.dim() > 3:
        raise ValueError(
            f"ncc_loss expects ≤3-D tensors; got shapes {tuple(preds.shape)} and {tuple(target.shape)}."
        )

    mask_bool = _mask_to_bool(mask, preds)

    # Add batch + channel dims for pooling: (1, 1, D, H, W)
    src = preds.unsqueeze(0).unsqueeze(0)
    trg = target.unsqueeze(0).unsqueeze(0)


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread neuroreg/image/segmentation.py Outdated
Comment thread neuroreg/image/segmentation.py Outdated
Comment thread neuroreg/imreg/optimize.py
Comment thread neuroreg/image/masking.py Outdated
Comment thread neuroreg/imreg/robreg.py Outdated
Comment thread neuroreg/image/segmentation.py Outdated
@m-reuter m-reuter merged commit c9af62d into main Apr 25, 2026
38 of 39 checks passed
@m-reuter m-reuter deleted the fixes branch April 25, 2026 20:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants