Skip to content

Commit 3acb754

Browse files
authored
Merge pull request #2158 from jadh4v/add-numOfPixels-to-vtkImageData-computeHistogram
feat(vtkImageData.computeHistogram): added number of pixels involved
2 parents e8c8aec + 3e1e430 commit 3acb754

File tree

4 files changed

+113
-2
lines changed

4 files changed

+113
-2
lines changed

Sources/Common/DataModel/ImageData/index.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ interface IComputeHistogram {
2222
export interface vtkImageData extends vtkDataSet {
2323

2424
/**
25-
* Returns an object with `{ minimum, maximum, average, variance, sigma }`
25+
* Returns an object with `{ minimum, maximum, average, variance, sigma, count }`
2626
* of the imageData points found within the provided `worldBounds`.
2727
*
2828
* `voxelFunc(index, bounds)` is an optional function that is called with

Sources/Common/DataModel/ImageData/index.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -410,7 +410,9 @@ function vtkImageData(publicAPI, model) {
410410
}
411411

412412
const average = inum > 0 ? isum / inum : 0;
413-
const variance = sumOfSquares - average * average;
413+
const variance = inum
414+
? Math.abs(sumOfSquares / inum - average * average)
415+
: 0;
414416
const sigma = Math.sqrt(variance);
415417

416418
return {
@@ -419,6 +421,7 @@ function vtkImageData(publicAPI, model) {
419421
average,
420422
variance,
421423
sigma,
424+
count: inum,
422425
};
423426
};
424427

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
import test from 'tape-catch';
2+
import vtkImageData from 'vtk.js/Sources/Common/DataModel/ImageData';
3+
import vtkRTAnalyticSource from 'vtk.js/Sources/Filters/Sources/RTAnalyticSource';
4+
5+
test('Test vtkImageData instance', (t) => {
6+
t.ok(vtkImageData, 'Make sure the class definition exists');
7+
const instance = vtkImageData.newInstance();
8+
t.ok(instance);
9+
t.end();
10+
});
11+
12+
test('Test vtkImageData histogram', (t) => {
13+
const spacing = 0.7;
14+
const size = 50;
15+
const compareFloat = (a, b) => Math.abs(a - b) < Number.EPSILON;
16+
17+
const source = vtkRTAnalyticSource.newInstance();
18+
source.setWholeExtent([0, size, 0, size, 0, size]);
19+
source.update();
20+
21+
const image = source.getOutputData();
22+
image.setSpacing([spacing, spacing, spacing]);
23+
24+
const bounds = image.getBounds();
25+
const hist = image.computeHistogram(bounds);
26+
27+
const baseline1 = {
28+
minimum: 9,
29+
maximum: 185,
30+
average: 64.65,
31+
variance: 782.87,
32+
sigma: 27.98,
33+
count: 132651,
34+
};
35+
36+
t.ok(
37+
hist.minimum === baseline1.minimum,
38+
'computeHistogram return value test: minimum'
39+
);
40+
t.ok(
41+
hist.maximum === baseline1.maximum,
42+
'computeHistogram return value test: maximum'
43+
);
44+
t.ok(
45+
compareFloat(hist.average.toFixed(2), baseline1.average),
46+
'computeHistogram return value test: average'
47+
);
48+
t.ok(
49+
compareFloat(hist.variance.toFixed(2), baseline1.variance),
50+
'computeHistogram return value test: variance'
51+
);
52+
t.ok(
53+
compareFloat(hist.sigma.toFixed(2), baseline1.sigma),
54+
'computeHistogram return value test: sigma'
55+
);
56+
t.ok(
57+
hist.count === baseline1.count,
58+
'computeHistogram return value test: count'
59+
);
60+
61+
// masking function that ignores the bottom 10 and top 10 rows of voxels.
62+
const voxelFunc = (idx) => idx[0] > 9 && idx[0] < 40;
63+
64+
const baseline2 = {
65+
minimum: 9,
66+
maximum: 173,
67+
average: 65.29,
68+
variance: 676.51,
69+
sigma: 26.01,
70+
count: 78030,
71+
};
72+
73+
const histWithMask = image.computeHistogram(bounds, voxelFunc);
74+
75+
t.ok(
76+
histWithMask.minimum === baseline2.minimum &&
77+
histWithMask.maximum === baseline2.maximum &&
78+
compareFloat(histWithMask.average.toFixed(2), baseline2.average) &&
79+
compareFloat(histWithMask.variance.toFixed(2), baseline2.variance) &&
80+
compareFloat(histWithMask.sigma.toFixed(2), baseline2.sigma) &&
81+
histWithMask.count === baseline2.count,
82+
'computeHistogram test with masking function'
83+
);
84+
85+
const voxelFuncNone = (idx) => false;
86+
const baseline3 = {
87+
minimum: Infinity,
88+
maximum: -Infinity,
89+
average: 0,
90+
variance: 0,
91+
sigma: 0,
92+
count: 0,
93+
};
94+
const histNone = image.computeHistogram(bounds, voxelFuncNone);
95+
96+
t.ok(
97+
histNone.minimum === baseline3.minimum &&
98+
histNone.maximum === baseline3.maximum &&
99+
compareFloat(histNone.average.toFixed(2), baseline3.average) &&
100+
compareFloat(histNone.variance.toFixed(2), baseline3.variance) &&
101+
compareFloat(histNone.sigma.toFixed(2), baseline3.sigma) &&
102+
histNone.count === baseline3.count,
103+
'computeHistogram test with zero number of voxels that qualify'
104+
);
105+
106+
t.end();
107+
});

Sources/tests.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import './Common/DataModel/Box/test/testBox';
1717
import './Common/DataModel/Cell/test/testCell';
1818
import './Common/DataModel/Cone/test/testConeImplicitFunction';
1919
import './Common/DataModel/DataSetAttributes/test/testDataSetAttributes';
20+
import './Common/DataModel/ImageData/test/testImageData';
2021
import './Common/DataModel/Line/test/testLine';
2122
import './Common/DataModel/Plane/test/testPlane';
2223
import './Common/DataModel/PolyData/test/testPolyData';

0 commit comments

Comments
 (0)