Skip to content

Commit 3f962d8

Browse files
authored
Merge pull request #853 from ANTsX/labelstats
BUG: label_geometry_measures with uint32 labels
2 parents cdf373f + 2506b12 commit 3f962d8

File tree

4 files changed

+21
-12
lines changed

4 files changed

+21
-12
lines changed

ants/label/label_geometry_measures.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ def label_geometry_measures(label_image, intensity_image=None):
4343
if not np.all(label_image.numpy() == label_image_int.numpy()):
4444
raise ValueError('Input label values must be representable as uint32.')
4545
label_image = label_image.clone('unsigned int')
46+
else:
47+
label_image_int = label_image
4648
veccer = [label_image.dimension, label_image_int, intensity_image, outcsv]
4749
veccer_processed = process_arguments(veccer)
4850
libfn = get_lib_fn('LabelGeometryMeasures')

ants/label/label_stats.py

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
__all__ = ["label_stats"]
22

3+
import numpy as np
34
import pandas as pd
45

56
from ants.internal import get_lib_fn
@@ -8,7 +9,7 @@
89
@image_method
910
def label_stats(image, label_image):
1011
"""
11-
Get label statistics from image
12+
Get label statistics from an image. The labels must be representable as uint32.
1213
1314
ANTsR function: `labelStats`
1415
@@ -22,7 +23,7 @@ def label_stats(image, label_image):
2223
2324
Returns
2425
-------
25-
ndarray ?
26+
pandas.DataFrame
2627
2728
Example
2829
-------
@@ -34,10 +35,16 @@ def label_stats(image, label_image):
3435
>>> stats = ants.label_stats(image, segs1['segmentation'])
3536
"""
3637
image_float = image.clone("float")
37-
label_image_int = label_image.clone("unsigned int")
38+
39+
label_image_int = label_image.clone('unsigned int')
40+
41+
if label_image.pixeltype != 'unsigned int':
42+
if not np.all(label_image.numpy() == label_image_int.numpy()):
43+
raise ValueError('Input label values must be representable as uint32.')
3844

3945
libfn = get_lib_fn("labelStats%iD" % image.dimension)
4046
df = libfn(image_float.pointer, label_image_int.pointer)
4147
df = pd.DataFrame(df)
4248
df.sort_values(by=["LabelValue"], inplace=True)
49+
df = df.reset_index(drop=True)
4350
return df

src/labelStats.cxx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,12 +40,12 @@ nb::dict labelStatsHelper(
4040

4141
long nlabs = labelStatisticsImageFilter->GetNumberOfLabels();
4242

43-
std::vector<double> labelvals(nlabs);
43+
std::vector<LabelType> labelvals(nlabs);
4444
std::vector<double> means(nlabs);
4545
std::vector<double> mins(nlabs);
4646
std::vector<double> maxes(nlabs);
4747
std::vector<double> variances(nlabs);
48-
std::vector<double> counts(nlabs);
48+
std::vector<LabelType> counts(nlabs);
4949
std::vector<double> volumes(nlabs);
5050
std::vector<double> x(nlabs);
5151
std::vector<double> y(nlabs);
@@ -137,7 +137,7 @@ nb::dict labelStatsHelper(
137137
template <unsigned int Dimension>
138138
nb::dict labelStats(AntsImage<itk::Image<float, Dimension>> & py_image,
139139
AntsImage<itk::Image<unsigned int, Dimension>> & py_labelImage)
140-
{
140+
{
141141
typedef itk::Image<float, Dimension> FloatImageType;
142142
typedef itk::Image<unsigned int, Dimension> IntImageType;
143143
typedef typename FloatImageType::Pointer FloatImagePointerType;

tests/test_utils.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -996,7 +996,7 @@ def test_channels_first(self):
996996
img4 = ants.merge_channels([image,image2], channels_first=True)
997997
self.assertTrue(np.allclose(img3.numpy()[:,:,0], img4.numpy()[0,:,:]))
998998
self.assertTrue(np.allclose(img3.numpy()[:,:,1], img4.numpy()[1,:,:]))
999-
999+
10001000
def test_polar_decomposition(self):
10011001
# Helper functions for creating known matrices
10021002
def make_known_rotation(theta_deg):
@@ -1006,16 +1006,16 @@ def make_known_rotation(theta_deg):
10061006
[np.sin(theta), np.cos(theta)]]
10071007
return R
10081008
def make_known_scaling_matrix(sx, sy, sz):
1009-
return np.diag([sx, sy, sz])
1009+
return np.diag([sx, sy, sz])
10101010

1011-
# 1. Setup: Create a matrix from a known P and Z.
1011+
# 1. Setup: Create a matrix from a known P and Z.
10121012
# The key is to multiply them in the order P @ Z.
10131013
P_known = make_known_scaling_matrix(2.5, 1.0, 1.5)
1014-
Z_known = make_known_rotation(42) # Use Z for "orthogonal" part
1014+
Z_known = make_known_rotation(42) # Use Z for "orthogonal" part
10151015
# Construct X using the left decomposition structure: X = P @ Z
10161016
X = P_known @ Z_known
10171017

1018-
# 2. Decompose the matrix using the function
1018+
# 2. Decompose the matrix using the function
10191019
result = ants.polar_decomposition(X)
10201020
P_est = result["P"]
10211021
Z_est = result["Z"]
@@ -1028,7 +1028,7 @@ def make_known_scaling_matrix(sx, sy, sz):
10281028
nptest.assert_almost_equal(P_known, P_est)
10291029
# c. Check if the estimated orthogonal part is close to the known one.
10301030
nptest.assert_almost_equal(Z_known, Z_est)
1031-
1031+
10321032
def test_convergence_monitoring(self):
10331033
f = [1 / i for i in range(1, 100)]
10341034
convergence = ants.convergence_monitoring(f, window_size=10)

0 commit comments

Comments
 (0)