Skip to content

Commit 53d6177

Browse files
authored
Align Measure standard deviation with ImageJ (#59)
1 parent 0c91f03 commit 53d6177

3 files changed

Lines changed: 41 additions & 9 deletions

File tree

docs/imagej-measure-comparison-2026-05-16.md

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ ImageJCsharp Measure behavior.
1010
| ImageJ build | ImageJ 1.54s99 |
1111
| ImageJ source | `https://wsr.imagej.net/jars/ij.jar` |
1212
| Java | `C:\Java21\jdk-21.0.2\bin\java.exe` |
13-
| ImageJCsharp commit | `762eae3` |
13+
| ImageJCsharp commit compared initially | `762eae3` |
1414
| Operating system | Windows |
1515

1616
ImageJ was run through its Java API with `ij.plugin.filter.Analyzer` using these
@@ -53,7 +53,7 @@ The ROI covers these four pixels:
5353

5454
### Full Image
5555

56-
| Measurement | ImageJ 1.54s99 | ImageJCsharp | Status |
56+
| Measurement | ImageJ 1.54s99 | ImageJCsharp at `762eae3` | Initial Status |
5757
| --- | ---: | ---: | --- |
5858
| Area | 12 | 12 | Match |
5959
| Mean | 55 | 55 | Match |
@@ -63,7 +63,7 @@ The ROI covers these four pixels:
6363

6464
### Rectangle ROI
6565

66-
| Measurement | ImageJ 1.54s99 | ImageJCsharp | Status |
66+
| Measurement | ImageJ 1.54s99 | ImageJCsharp at `762eae3` | Initial Status |
6767
| --- | ---: | ---: | --- |
6868
| Area | 4 | 4 | Match |
6969
| Mean | 75 | 75 | Match |
@@ -82,13 +82,13 @@ deviation.
8282

8383
## Follow-Up
8484

85-
Follow-up issue:
85+
Follow-up issue created from this comparison:
8686

8787
```text
8888
https://github.com/yangfei/ImageJCsharp/issues/57
8989
```
9090

91-
That issue tracks the decision and implementation work for aligning
92-
ImageJCsharp standard deviation behavior with ImageJ, if that is the desired
93-
compatibility target.
94-
91+
Issue #57 aligned ImageJCsharp Measure with ImageJ for these StdDev cases by
92+
using sample standard deviation in `Measurements.Measure`. Automated Core tests
93+
now assert the ImageJ 1.54s99 full-image and rectangle-ROI StdDev values listed
94+
above.

src/ImageJCsharp.Core/Measurements.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,9 @@ private static MeasurementResult Measure(
138138
}
139139

140140
var mean = sum / count;
141-
var variance = Math.Max(0, (sumSquares / count) - (mean * mean));
141+
var variance = count > 1
142+
? Math.Max(0, (sumSquares - ((sum * sum) / count)) / (count - 1))
143+
: 0;
142144
var area = count * calibration.PixelWidth * calibration.PixelHeight;
143145

144146
return new MeasurementResult(count, area, mean, min, max, Math.Sqrt(variance));

tests/ImageJCsharp.Core.Tests/CoreImageTests.cs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,36 @@ public void MeasureComputesStatisticsInsideRectRoi()
104104
Assert.Equal(60, result.Max);
105105
}
106106

107+
[Fact]
108+
public void MeasureUsesImageJCompatibleSampleStandardDeviation()
109+
{
110+
var image = GrayImage.FromPixels(4, 3, new ushort[]
111+
{
112+
0, 10, 20, 30,
113+
40, 50, 60, 70,
114+
80, 90, 100, 110
115+
});
116+
117+
var result = Measurements.Measure(image, new RectRoi(0, 0, 4, 3), PixelCalibration.Identity);
118+
119+
Assert.Equal(36.05551275463989, result.StandardDeviation, 12);
120+
}
121+
122+
[Fact]
123+
public void MeasureRoiUsesImageJCompatibleSampleStandardDeviation()
124+
{
125+
var image = GrayImage.FromPixels(4, 3, new ushort[]
126+
{
127+
0, 10, 20, 30,
128+
40, 50, 60, 70,
129+
80, 90, 100, 110
130+
});
131+
132+
var result = Measurements.Measure(image, new RectRoi(1, 1, 2, 2), PixelCalibration.Identity);
133+
134+
Assert.Equal(23.804761428476166, result.StandardDeviation, 12);
135+
}
136+
107137
[Fact]
108138
public void MeasureUsesPixelCalibrationForArea()
109139
{

0 commit comments

Comments
 (0)