Skip to content

New commands: peaksconvert, peakscheck#2918

Merged
Lestropie merged 17 commits intodevfrom
new_peaks_cmds
Sep 18, 2025
Merged

New commands: peaksconvert, peakscheck#2918
Lestropie merged 17 commits intodevfrom
new_peaks_cmds

Conversation

@Lestropie
Copy link
Member

@Lestropie Lestropie commented Jun 6, 2024

Draft PR.

Depends on #2917 (uses that as base branch).

This is work that I deemed necessary for BIDS BEP016 DWI models (bids-standard/bids-bep016#24).

To have any kind of confidence around robust encoding of the outcomes of diffusion model fits on the filesystem, in my mind there are two requirements:

  • Prove that the purported coordinate system is correct.
    This necessitates a broader set of data. Showing that for one data instance, the purported coordinate system seems reasonable, is not evidence that in some other use case the interpretation will be incorrect.
  • Have the capability to convert data between different encodings or reference frames.

Here I've created two new commands, that are requisite for later parts of this validation tool.

  • peaksconvert converts both between different reference frames, and between 3-vector versus spherical coordinate encodings. This for instance should allow loading the outcomes of diffusion model fits from FSL and MRtrix in the converse software.

  • peakscheck does something similar to dwigradcheck, just using peaks images and tckgen -algorithm fact. Similar to dwigradcheck: Enhancements #2902 (which happened during the course of generating this changeset) the set of possible errors in encoding that are evaluated can be more complex than just axis permutations and flips.


Outstanding TODOs, many of which are plucked from code comments:

  • Deep in the spherical coordinates handling, the prospect of a triplet of spherical coefficients appears, where the third element is inferred to be a radius. This is however contrary to ISO convention, where the first element should be the radius. Would need to audit what code uses it, but I would like to consider changing that behaviour. Potentially to avoid unexpected errors, revert those functions to only consider unit spherical coordinates, and have newly named functions that deal with the prospect of triplets?

  • For spherical coordinates, should support for both physics and mathematics conventions be implemented?
    peakscheck tests for the prospect of swapping the order of the two angles; but permitting explicit specification of which of the two is in use may be preferable (relates to Spherical coordinates convention bids-standard/bids-bep016#96).
    Edit: This has been made out of scope for BEP016, probably makes sense here also.

  • Give peaksconvert ability to change fill value of absent fixels

  • Give peaksconvert ability to reduce maximal number of fixels
    Hypothetically, this could be done either by culling volumes, or by first sorting by the value per fixel (as 3-vector norm or spherical radius) and then removing

  • Multi-thread peaksconvert
    Edit: Command is very fast because operations are pretty trivial, so won't bother.

  • Check whether peaksconvert works across bootstrap realisations, if those realisations appear along the fifth image axis
    (In BEP016 I want to define explicitly if an image axis is used to encode orientation information vs. if an image axis is used to encode bootstrap realisations; see Bootstrapping bids-standard/bids-bep016#38. But that may not necessarily propagate to MRtrix)

  • peakscheck: Try to catch case where upstream there has been an erroneous transformation; eg. someone has orientations in IJK, they erroneously applied an XYZ2IJK transformation, and so now they need to apply the IJK2XYZ transformation twice to get their orientations in MRtrix's expected XYZ
    Edit: Too obtuse, put out of scope.

  • peakscheck: Reduce doubling-up between ijk and bvec reference frames.
    Ideally evaluate each unique transformation only once; there may however be multiple interpretations to a unique transformation.
    Edit: Not crucial for functionality; leave for now.

  • peakscheck: Add -in_reference option; the variant that includes the corresponding transformation to XYZ should then be considered the "expected" interpretation of the input data, used to determine whether the return code should be non-zero.

  • Add ability to disable internal transform realignment on image load via environment variable; make use of this in peakscheck
    Edit: Don't think this should be done. The problem is that if this setting is controlled both in a config file and in an environment variable, but the two values are not identical, then it's not clear which of the two should take precedence. While erroring out with a suitable message might make intuitive sense, the intent behind implementing this was to be able to use run.command(, env=) to disable transform realignment across the whole command, but if someone were to set RealignTransform: true in their config file (even though that's the default) then that would preclude command execution.

@Lestropie Lestropie self-assigned this Jun 6, 2024
github-actions[bot]

This comment was marked as outdated.

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
github-actions[bot]

This comment was marked as outdated.

Conflicts:
	core/file/nifti_utils.h
	python/mrtrix3/commands/peakscheck
- Resolves addition of peaksconvert command in #2918 with API changes in #2911.
- Resolves with clang-format added in #2652.
Resolves addition of peakscheck in #2918 with Python CLI changes in #2678.
Lestropie added a commit that referenced this pull request Aug 26, 2025
- Resolves addition of peaksconvert command in #2918 with API changes in #2911.
- Resolves with clang-format added in #2652.
Lestropie added a commit that referenced this pull request Aug 26, 2025
Resolves addition of peakscheck in #2918 with Python CLI changes in #2678.
Base automatically changed from dwi_metadata to dev September 4, 2025 00:01
Lestropie and others added 5 commits September 17, 2025 13:51
Conflicts:
	cpp/cmd/mrcalc.cpp
	cpp/cmd/peaksconvert.cpp
	cpp/core/file/nifti_utils.h
This reference frame appears to be an intrinsic feature of the FSL software and not specific to the encoding of diffusion MRI sensitisation directions in the .bvec format.
- All calculations observed throughout the code base that relate to relationships between cartesian and spherical coordinates, as well as spherical harmonics, make reference to the cosine of the second angle as its component relative to the zenith. This would make that angle an inclination rather than an elevation.
@Lestropie Lestropie marked this pull request as ready for review September 17, 2025 13:21
github-actions[bot]

This comment was marked as outdated.

github-actions[bot]

This comment was marked as outdated.

Resolves original implementation created in #2918 against post-cmake Python changes.
Copy link

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

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

clang-tidy made some suggestions

info.lightpos[1] = -cos(elevation);
const float inclination = inclination_slider->value() * (Math::pi / 1000.0);
const float azimuth = azimuth_slider->value() * (Math::pi / 1000.0);
info.lightpos[2] = sin(inclination) * cos(azimuth);

Choose a reason for hiding this comment

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

warning: narrowing conversion from 'double' to 'float' [bugprone-narrowing-conversions]

  info.lightpos[2] = sin(inclination) * cos(azimuth);
                     ^

const float inclination = inclination_slider->value() * (Math::pi / 1000.0);
const float azimuth = azimuth_slider->value() * (Math::pi / 1000.0);
info.lightpos[2] = sin(inclination) * cos(azimuth);
info.lightpos[0] = sin(inclination) * sin(azimuth);

Choose a reason for hiding this comment

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

warning: call to 'sin' promotes float to double [performance-type-promotion-in-math-fn]

Suggested change
info.lightpos[0] = sin(inclination) * sin(azimuth);
info.lightpos[0] = sin(inclination) * std::sin(azimuth);

const float inclination = inclination_slider->value() * (Math::pi / 1000.0);
const float azimuth = azimuth_slider->value() * (Math::pi / 1000.0);
info.lightpos[2] = sin(inclination) * cos(azimuth);
info.lightpos[0] = sin(inclination) * sin(azimuth);

Choose a reason for hiding this comment

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

warning: call to 'sin' promotes float to double [performance-type-promotion-in-math-fn]

Suggested change
info.lightpos[0] = sin(inclination) * sin(azimuth);
info.lightpos[0] = std::sin(inclination) * sin(azimuth);

const float inclination = inclination_slider->value() * (Math::pi / 1000.0);
const float azimuth = azimuth_slider->value() * (Math::pi / 1000.0);
info.lightpos[2] = sin(inclination) * cos(azimuth);
info.lightpos[0] = sin(inclination) * sin(azimuth);

Choose a reason for hiding this comment

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

warning: narrowing conversion from 'double' to 'float' [bugprone-narrowing-conversions]

  info.lightpos[0] = sin(inclination) * sin(azimuth);
                     ^

const float azimuth = azimuth_slider->value() * (Math::pi / 1000.0);
info.lightpos[2] = sin(inclination) * cos(azimuth);
info.lightpos[0] = sin(inclination) * sin(azimuth);
info.lightpos[1] = -cos(inclination);

Choose a reason for hiding this comment

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

warning: call to 'cos' promotes float to double [performance-type-promotion-in-math-fn]

Suggested change
info.lightpos[1] = -cos(inclination);
info.lightpos[1] = -std::cos(inclination);

const float azimuth = azimuth_slider->value() * (Math::pi / 1000.0);
info.lightpos[2] = sin(inclination) * cos(azimuth);
info.lightpos[0] = sin(inclination) * sin(azimuth);
info.lightpos[1] = -cos(inclination);

Choose a reason for hiding this comment

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

warning: narrowing conversion from 'double' to 'float' [bugprone-narrowing-conversions]

  info.lightpos[1] = -cos(inclination);
                     ^

protected:
GL::Lighting &info;
QSlider *elevation_slider, *azimuth_slider;
QSlider *inclination_slider, *azimuth_slider;

Choose a reason for hiding this comment

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

warning: member variable 'azimuth_slider' has protected visibility [cppcoreguidelines-non-private-member-variables-in-classes]

  QSlider *inclination_slider, *azimuth_slider;
                                ^

protected:
GL::Lighting &info;
QSlider *elevation_slider, *azimuth_slider;
QSlider *inclination_slider, *azimuth_slider;

Choose a reason for hiding this comment

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

warning: member variable 'inclination_slider' has protected visibility [cppcoreguidelines-non-private-member-variables-in-classes]

  QSlider *inclination_slider, *azimuth_slider;
           ^

Option facilitates nominating which references axis set the input dataset is expected a priori to conform to; the terminal feedback & command error code are modulated accordingly.
@Lestropie Lestropie mentioned this pull request Sep 18, 2025
4 tasks
@Lestropie Lestropie merged commit 6be6e3d into dev Sep 18, 2025
6 checks passed
@Lestropie Lestropie deleted the new_peaks_cmds branch September 18, 2025 07:02
Lestropie added a commit that referenced this pull request Sep 30, 2025
Rename "[az el]" to "[az in]"; this was missed in the mass renaming from elevation to inclination performed in 3ddf174 as part of #2918.
- De-duplicate implementation of command-line option -cartesian for direction-set-related commands.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

2 participants