Skip to content

Slice Geometry: add moment weighting mode option#393

Draft
JonathanAWilliams wants to merge 4 commits intobonej-org:392-pixel-weighted-moments-in-slice-geometryfrom
JonathanAWilliams:392-pixel-weighted-moments-in-slice-geometry
Draft

Slice Geometry: add moment weighting mode option#393
JonathanAWilliams wants to merge 4 commits intobonej-org:392-pixel-weighted-moments-in-slice-geometryfrom
JonathanAWilliams:392-pixel-weighted-moments-in-slice-geometry

Conversation

@JonathanAWilliams
Copy link
Copy Markdown

Related to #392
Adds a “Moment weighting” dropdown and enum parsing in Slice Geometry. Default remains geometric (binary), so there is no behaviour change yet—this is scaffolding for the upcoming pixel-weighted (density/partial area) moment calculations.

Next: implement density-weighted centroid + density-weighted integrand

image

@mdoube mdoube linked an issue Jan 8, 2026 that may be closed by this pull request
Comment thread Legacy/bonej/src/main/java/org/bonej/plugins/SliceGeometry.java Outdated
Comment thread Legacy/bonej/src/main/java/org/bonej/plugins/SliceGeometry.java Outdated
@mdoube
Copy link
Copy Markdown
Member

mdoube commented Jan 8, 2026

@JonathanAWilliams thanks! Keep pushing your changes to your branch and let them accumulate here until we're happy that it's done and I can merge the PR.

The new drop-down makes the following Partial volume compensation checkbox redundant, so remove that. Its effect will now be triggered when the user chooses the corresponding option from the drop down.

@JonathanAWilliams
Copy link
Copy Markdown
Author

Removed the redundant checkbox as suggested and verified in ImageJ.
Partial-area behaviour is now controlled via the Moment weighting dropdown.
Will start on density-weighted implementaiton

image

mdoube
mdoube previously approved these changes Jan 8, 2026
@mdoube mdoube marked this pull request as draft January 8, 2026 12:25
@JonathanAWilliams
Copy link
Copy Markdown
Author

Added centroid selection driven by the new “Moment weighting” mode.

GEOMETRIC (1): existing behaviour
PARTIAL_AREA (2): reproduces previous partial volume centroid behaviour
DENSITY (3): uses a calibrated per-pixel weight (w = m·pixel + c) to compute a density-weighted centroid

image

In this PR the change is intentionally limited to centroid computation only; the second-moment (integrand) accumulation remains geometric/partial-area as before.

Next step: apply the selected weighting mode inside the second-moment accumulation.

@mdoube mdoube self-requested a review January 9, 2026 14:00
@mdoube mdoube dismissed their stale review January 9, 2026 14:00

premature, will review all changes once the pr is ready

@JonathanAWilliams
Copy link
Copy Markdown
Author

This PR adds density-weighted centroid and second-moment calculations to the Slice Geometry plugin via a new “Moment weighting” selector (Geometric / Partial area / Density). In density mode, per-pixel weights derived from calibrated greyscale values are applied directly inside the centroid and moment accumulations. Legacy geometric behaviour is preserved when density weighting is not selected. Also added J has outputted parameter.

Legacy geometric behaviour is preserved when density weighting is not selected.

Case A: 2x2 binary block
image
image
Expected behaviour:

  1. Geometric (1), partial-area (2), and density-weighted (3) modes should all reduce to the same result under uniform density. With density varied by y=mx+x (m=1, c=0 in this test).
  2. Geometric and weighted centroids should coincide at the centre of the block, and the principal second moments should be equal (Imin=Imax) in this symmetric case.
  3. J = Imin + Imax

Observed behaviour:

  1. All three weighting modes report identical centroids.
  2. Imin = Imax in all 3 modes
  3. J = Imin + Imax in all 3 modes.
    Two-pixel asymmetric block. g1 = 100, g2 = 200

Case B two-pixel asymmetric block
image
image

Expected behaviour:

  1. Density weighting should shift the centroid toward the higher-intensity pixel.
  2. Second moments should redistribute accordingly, while J remains invariant
  3. In the geometric mode, results should reduce to the symmetric, unweighted case.

Observed behaviour:

  1. In geometric mode, the centroid lies midway between the two pixels
  2. In density-weighted mode, the centroid shifts toward the higher-intensity pixel by the analytically predicted amount.
  3. Imin and Imax change relative to geometric case, reflecting altered material distribution
  4. J matches analytically derived value (hand calculation) and is rotationally invariant.

Tasks Done

  • Density-weighted centroid
  • Density-weighted second-moment accumulation
  • Preservation of geometric & partial-area behaviour
  • Introduction/reporting of polar moment J
  • Analytical validation (Cases A & B)

Next set of changes

  1. Decided on centorid consistency.
    Currently report both geometric and weighted versions, this might be useful to keep, however, in the density case Xcent = wXcent, they are both density weighted. Would want Xcent to be the geometry case
  2. Add density calibration beyond linear
    Add ability to calibrated from average grayscale value of each phantom of known density used.
  3. Could add the two validated cases as automated tests

@mdoube
Copy link
Copy Markdown
Member

mdoube commented Jan 13, 2026

Thanks! Good validtion. Would be good to add them as unit tests, alongside these: https://github.com/bonej-org/BoneJ2/tree/master/Legacy/bonej/src/test/java/org/bonej/plugins

Also when testing originally, I did a stack with a rotating rectangular ROI, something like this macro, to test stability under rotation.

newImage("Untitled", "8-bit black", 512, 512, 180);

for (i = 0; i < nSlices; i++){
	slice = i + 1;
	setSlice(slice);
	run("Specify...", "width=256 height=128 x=278 y=264 slice="+slice+" centered");
	run("Rotate...", "  angle="+i);
	setForegroundColor(255, 255, 255);
	run("Fill", "slice");
	run("Select None");
}

run("Slice Geometry", "bone=unknown draw_axes draw_centroids annotated_copy_(2d) process_stack clear_results bone_min=128 bone_max=255 slope=0.0000 y_intercept=1.8000 moment=[Geometric (binary)] background=128 foreground=255");

@JonathanAWilliams
Copy link
Copy Markdown
Author

Slice Geometry: optional per-pixel contribution maps for second moments (Imin, Imax, J)

This PR adds an optional per-pixel contribution visualisation pathway to the Slice Geometry plugin, allowing users to generate spatial maps showing how individual pixels contribute to the second-moment metrics under the selected moment-weighting mode.

Contribution maps are implemented for Imin, Imax and J (polar moment invariant = Imin + Imax). Outputs are produced as 32-bit float stacks, one slice per input slice, suitable for inspection, LUT application, and optional 3D visualisation.

UI additions

Main dialog

A new checkbox has been added to the Slice Geometry dialog:

image

Contribution selection dialog

When enabled, a second dialog allows users to select one or more outputs:

  • Imin contribution
  • Imax contribution
  • J (polar invariant) contribution

At least one output must be selected to proceed.

image

Outputs

For each selected metric, a new ImagePlus stack is generated and shown:

  • Imin_contrib_<title>>
  • Imax_contrib_<title>
  • J_contrib_<title>

Pixel values are stored as floats.
Pixels outside the analysed region are set to NaN, so they do not influence colour scaling or LUT mapping.

Geometry mode example

image

Apply Fire LUT
image

3D Viewer Plugin
image

Density mode example

image

Apply Fire LUT
image

3D Viewer Plugin
image

Contribution definitions

J contribution

For each pixel, the J contribution consists of:

  1. Distance term about the slice centroid
    w · (dx² + dy²) · A

  2. Pixel self-term for a rectangular pixel
    w · A · (vW² + vH²) / 12

This mirrors the discretisation already used in Slice Geometry’s second-moment calculations.


Imin / Imax contributions

Imin and Imax contribution maps are computed via axis-specific decomposition using the slice’s principal orientation (theta[s]).

Each pixel’s contribution is projected onto the minimum and maximum principal axes, rather than derived from the final scalar Imin/Imax values.

This ensures that:

  • the spatial maps are mathematically consistent,
  • summing pixel contributions recovers the reported scalar Imin and Imax values.

Validation

Contribution maps were validated via internal consistency checks against Slice Geometry’s scalar outputs.

For a single slice and weighting mode:

  1. Slice Geometry was run with contribution visualisation enabled, generating:
  • Imin, Imax, and J contribution images
  1. The analysed region was selected using ImageJ thresholding
  2. Integrated Density (IntDen) was measured via Analyze → Measure
  3. The summed per-pixel contributions were compared to the reported scalar outputs

Geometry mode

  1. Slice Geometry was run with contribution visualisation enabled, generating Imin, Imax and J contribution images
image 2. For each contribution images the analysed region was selected using ImageJ thresholding. image 3. Integrated Density (IntDen) was measured via Analyze > Measure. image J (3), Imin (4), Imax(5). Slice Geometry scalar output: image The summed per-pixel contributions closely matched the reported scalar second moments. Difference explained by issue noted below.

Density mode

image image image image

These checks confirm that:

  • each contribution map is mathematically consistent with Slice Geometry’s second-moment formulation,
  • per-pixel contributions sum to the corresponding scalar outputs,
  • the spatial maps accurately reflect second-moment distributions under the selected weighting mode.

Known issues / follow-up work

1. Output name collisions across weighting modes

If Slice Geometry is run multiple times on the same image using different weighting modes (e.g. geometric then density-weighted), contribution stacks from different runs share identical titles. This causes the ImageJ 3D Viewer to silently reuse or overwrite datasets.

Resolution:
Contribution stack titles to include the weighting mode to ensure uniqueness.


2. Dynamic range in Imin / Imax maps

Imin and Imax contribution maps naturally span several orders of magnitude:

  • Pixels near the principal axis contribute very small (but non-zero) values
  • Pixels far from the axis dominate the second moment

Under linear LUT scaling, near-axis contributions may appear black and be difficult to threshold. As seen in binarised Imin and Imax imaes

This is expected behaviour and can be confirmed by inspecting pixel values (e.g. values ≈ 20 near the axis vs ≈ 10⁷ at the cortex periphery).

Potential future improvement:
Optional log-scaled display for contribution maps to improve interpretability without altering stored values. Something like
image


Summary

This PR adds a optional contribution visualisation capability to Slice Geometry that:

  • exposes the spatial structure underlying Imin, Imax, and J,
  • remains mathematically consistent with existing second-moment calculations,
  • supports multiple weighting modes,
  • enables intuitive inspection, validation, and downstream visualisation.

@mdoube mdoube changed the title Slice Geometry: add moment weighting mode option (no behaviour change) Slice Geometry: add moment weighting mode option Feb 4, 2026
@mdoube
Copy link
Copy Markdown
Member

mdoube commented Feb 4, 2026

Here is a test macro that makes a rectangular ramp and rotates it. Please check that the results tally up with expectation:

newImage("rotating_ramp", "8-bit black", 512, 512, 360);

setBatchMode(true);

for (i = 1; i <= 360; i++) {
    newImage("ramp", "8-bit ramp", 256, 128, 1);
    run("Rotate... ", "angle=" + i + " grid=1 interpolation=None fill enlarge");
    run("Select All");
    run("Copy");
    selectImage("rotating_ramp");
    setSlice(i);
    run("Paste");
    run("Select None");
    selectImage("ramp");
    close;
}

setBatchMode("exit and display");

run("Slice Geometry", "bone=unknown draw_axes draw_centroids annotated_copy_(2d) 3d_annotation process_stack clear_results bone_min=1 bone_max=255 slope=0.01 y_intercept=0 moment=[Density-weighted (experimental)] background=0 foreground=255");
Screenshot from 2026-02-04 11-12-39

@mdoube
Copy link
Copy Markdown
Member

mdoube commented Feb 4, 2026

When enabled, a second dialog allows users to select one or more outputs:

  • Imin contribution
  • Imax contribution
  • J (polar invariant) contribution

At least one output must be selected to proceed.

Better to have all the options in one dialog. Multiple dependent dialogs create a headache for macro command harvesting and for headless operation. The old dialog style allows you to grey out options if other options are not active, or they can just be there and have no effect if the parent option is not selected.

I don't see your code changes that introduce this option, could you point me to them please?

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.

Pixel-weighted moments in Slice Geometry

2 participants