Skip to content

Commit

Permalink
Merge pull request #714 from ANTsX/add_tests
Browse files Browse the repository at this point in the history
BUG: label_image_centroids only worked with sequential labels 1..N
  • Loading branch information
cookpa authored Oct 8, 2024
2 parents be10edc + ce8667d commit 5238b3c
Show file tree
Hide file tree
Showing 6 changed files with 86 additions and 48 deletions.
3 changes: 2 additions & 1 deletion ants/label/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@
from .label_overlap_measures import label_overlap_measures
from .label_stats import label_stats
from .labels_to_matrix import labels_to_matrix
from .multi_label_morphology import multi_label_morphology
from .make_points_image import make_points_image
from .multi_label_morphology import multi_label_morphology
26 changes: 13 additions & 13 deletions ants/label/label_image_centroids.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,14 @@ def label_image_centroids(image, physical=False, convex=True, verbose=False):
---------
image : ANTsImage
image of integer labels
physical : boolean
whether you want physical space coordinates or not
convex : boolean
if True, return centroid
if False return point with min average distance to other points with same label
Returns
-------
dictionary w/ following key-value pairs:
Expand Down Expand Up @@ -58,14 +58,14 @@ def label_image_centroids(image, physical=False, convex=True, verbose=False):
zc = np.zeros(n_labels)

if convex:
for i in mylabels:
idx = (labels == i).flatten()
xc[i-1] = np.mean(xcoords[idx])
yc[i-1] = np.mean(ycoords[idx])
zc[i-1] = np.mean(zcoords[idx])
for lab_idx, label_intensity in enumerate(mylabels):
idx = (labels == label_intensity).flatten()
xc[lab_idx] = np.mean(xcoords[idx])
yc[lab_idx] = np.mean(ycoords[idx])
zc[lab_idx] = np.mean(zcoords[idx])
else:
for i in mylabels:
idx = (labels == i).flatten()
for lab_idx, label_intensity in enumerate(mylabels):
idx = (labels == label_intensity).flatten()
xci = xcoords[idx]
yci = ycoords[idx]
zci = zcoords[idx]
Expand All @@ -75,9 +75,9 @@ def label_image_centroids(image, physical=False, convex=True, verbose=False):
dist[j] = np.mean(np.sqrt((xci[j] - xci)**2 + (yci[j] - yci)**2 + (zci[j] - zci)**2))

mid = np.where(dist==np.min(dist))
xc[i-1] = xci[mid]
yc[i-1] = yci[mid]
zc[i-1] = zci[mid]
xc[lab_idx] = xci[mid]
yc[lab_idx] = yci[mid]
zc[lab_idx] = zci[mid]

centroids = np.vstack([xc,yc,zc]).T

Expand Down
26 changes: 13 additions & 13 deletions ants/label/make_points_image.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

import ants

def make_points_image(pts, mask, radius=5):
def make_points_image(pts, target, radius=5):
"""
Create label image from physical space points
Expand All @@ -21,10 +21,10 @@ def make_points_image(pts, mask, radius=5):
Arguments
---------
pts : numpy.ndarray
input powers points
input points
mask : ANTsImage
mask defining target space
target : ANTsImage
Image defining target space
radius : integer
radius for the points
Expand All @@ -37,25 +37,25 @@ def make_points_image(pts, mask, radius=5):
-------
>>> import ants
>>> import pandas as pd
>>> mni = ants.image_read(ants.get_data('mni')).get_mask()
>>> mni = ants.image_read(ants.get_data('mni'))
>>> powers_pts = pd.read_csv(ants.get_data('powers_mni_itk'))
>>> powers_labels = ants.make_points_image(powers_pts.iloc[:,:3].values, mni, radius=3)
"""
powers_lblimg = mask * 0
lblimg = target * 0
npts = len(pts)
dim = mask.dimension
dim = target.dimension
if pts.shape[1] != dim:
raise ValueError('points dimensionality should match that of images')

for r in range(npts):
pt = pts[r,:]
idx = ants.transform_physical_point_to_index(mask, pt.tolist() ).astype(int)
idx = ants.transform_physical_point_to_index(target, pt.tolist() ).astype(int)
in_image=True
for kk in range(mask.dimension):
in_image = in_image and idx[kk] >= 0 and idx[kk] < mask.shape[kk]
for kk in range(target.dimension):
in_image = in_image and idx[kk] >= 0 and idx[kk] < target.shape[kk]
if ( in_image == True ):
if (dim == 3):
powers_lblimg[idx[0],idx[1],idx[2]] = r + 1
lblimg[idx[0],idx[1],idx[2]] = r + 1
elif (dim == 2):
powers_lblimg[idx[0],idx[1]] = r + 1
return ants.morphology( powers_lblimg, 'dilate', radius, 'grayscale' )
lblimg[idx[0],idx[1]] = r + 1
return ants.morphology( lblimg, 'dilate', radius, 'grayscale' )
11 changes: 5 additions & 6 deletions ants/registration/affine_initializer.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,21 @@


def affine_initializer(fixed_image, moving_image, search_factor=20,
radian_fraction=0.1, use_principal_axis=False,
radian_fraction=0.1, use_principal_axis=False,
local_search_iterations=10, mask=None, txfn=None ):
"""
A multi-start optimizer for affine registration
Searches over the sphere to find a good initialization for further
registration refinement, if needed. This is a wrapper for the ANTs
function antsAffineInitializer.
ANTsR function: `affineInitializer`
Arguments
---------
fixed_image : ANTsImage
the fixed reference image
moving_image : ANTsImage
moving_image : ANTsImage
the moving image to be mapped to the fixed space
search_factor : scalar
degree of increments on the sphere to search
Expand All @@ -41,7 +41,7 @@ def affine_initializer(fixed_image, moving_image, search_factor=20,
-------
ndarray
transformation matrix
Example
-------
>>> import ants
Expand All @@ -66,6 +66,5 @@ def affine_initializer(fixed_image, moving_image, search_factor=20,

if retval != 0:
warnings.warn('ERROR: Non-zero exit status!')

return txfn

return txfn
2 changes: 1 addition & 1 deletion ants/utils/mni2tal.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ def mni2tal(xin):
References
----------
http://bioimagesuite.yale.edu/mni2tal/501_95733_More\%20Accurate\%20Talairach\%20Coordinates\%20SLIDES.pdf
http://bioimagesuite.yale.edu/mni2tal/501_95733_More\\%20Accurate\\%20Talairach\\%20Coordinates\\%20SLIDES.pdf
http://imaging.mrc-cbu.cam.ac.uk/imaging/MniTalairach
"""
if (not isinstance(xin, (tuple,list))) or (len(xin) != 3):
Expand Down
66 changes: 52 additions & 14 deletions tests/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -209,13 +209,13 @@ def test_crop_image_example(self):

# label image not float
cropped = ants.crop_image(fi, fi.clone("unsigned int"), 100)

# channel image
fi = ants.image_read( ants.get_ants_data('r16') )
cropped = ants.crop_image(fi)
fi2 = ants.merge_channels([fi,fi])
cropped2 = ants.crop_image(fi2)

self.assertEqual(cropped.shape, cropped2.shape)

def test_crop_indices_example(self):
Expand Down Expand Up @@ -583,11 +583,27 @@ def setUp(self):
def tearDown(self):
pass

def test_label_clusters_example(self):
def test_label_image_centroids(self):
image = ants.from_numpy(
np.asarray([[[0, 2], [1, 3]], [[4, 6], [5, 7]]]).astype("float32")
)
labels = ants.label_image_centroids(image)
self.assertEqual(len(labels['labels']), 7)

# Test non-sequential labels
image = ants.from_numpy(
np.asarray([[[0, 2], [2, 2]], [[2, 0], [5, 0]]]).astype("float32")
)

labels = ants.label_image_centroids(image)
self.assertTrue(len(labels['labels']) == 2)
self.assertTrue(labels['labels'][1] == 5)
self.assertTrue(np.allclose(labels['vertices'][0], [0.5 , 0.5 , 0.25], atol=1e-5))
# With convex = False, the centroid position should change
labels = ants.label_image_centroids(image, convex=False)
self.assertTrue(np.allclose(labels['vertices'][0], [1.0, 1.0, 0.0], atol=1e-5))
# single point unchanged
self.assertTrue(np.allclose(labels['vertices'][1], [0.0, 1.0, 1.0], atol=1e-5))


class TestModule_label_overlap_measures(unittest.TestCase):
Expand Down Expand Up @@ -633,6 +649,28 @@ def test_labels_to_matrix_example(self):
labmat = ants.labels_to_matrix(labs, mask)


class TestModule_make_points_image(unittest.TestCase):
def setUp(self):
pass

def tearDown(self):
pass

def test_make_points_image_example(self):
image = ants.image_read(ants.get_ants_data("r16"))
points = np.array([[102, 76],[134, 129]])
points_image = ants.make_points_image(points, image, radius=5)
stats = ants.label_stats(image, points_image)
self.assertTrue(np.allclose(stats['Volume'].to_numpy()[1:3], 97.0, atol=1e-5))
self.assertTrue(np.allclose(stats['x'].to_numpy()[1:3], points[:,0], atol=1e-5))
self.assertTrue(np.allclose(stats['y'].to_numpy()[1:3], points[:,1], atol=1e-5))

points = np.array([[102, 76, 50],[134, 129, 50]])
# Shouldn't allow 3D points on a 2D image
with self.assertRaises(Exception):
points_image = ants.make_points_image(image, points, radius=3)


class TestModule_mask_image(unittest.TestCase):
def setUp(self):
pass
Expand Down Expand Up @@ -846,7 +884,7 @@ def setUp(self):
pass
def tearDown(self):
pass

def test_bspline_field(self):
points = np.array([[-50, -50]])
deltas = np.array([[10, 10]])
Expand Down Expand Up @@ -874,29 +912,29 @@ def test_ilr(self):
result = ants.ilr( df, vlist, myform)
myform = " mat2 ~ covar + mat1 "
result = ants.ilr( df, vlist, myform)

def test_quantile(self):
img = ants.image_read(ants.get_data('r16'))
ants.quantile(img, 0.5)
ants.quantile(img, (0.5, 0.75))

def test_bandpass(self):
brainSignal = np.random.randn( 400, 1000 )
tr = 1
filtered = ants.bandpass_filter_matrix( brainSignal, tr = tr )

def test_compcorr(self):
cc = ants.compcor( ants.image_read(ants.get_ants_data("ch2")) )

def test_histogram_match(self):
src_img = ants.image_read(ants.get_data('r16'))
ref_img = ants.image_read(ants.get_data('r64'))
src_ref = ants.histogram_match_image(src_img, ref_img)

src_img = ants.image_read(ants.get_data('r16'))
ref_img = ants.image_read(ants.get_data('r64'))
src_ref = ants.histogram_match_image2(src_img, ref_img)

def test_averaging(self):
x0=[ ants.get_data('r16'), ants.get_data('r27'), ants.get_data('r62'), ants.get_data('r64') ]
x1=[]
Expand All @@ -906,7 +944,7 @@ def test_averaging(self):
avg1=ants.average_images(x1)
avg2=ants.average_images(x1,mask=0)
avg3=ants.average_images(x1,mask=1,normalize=True)

def test_n3_2(self):
image = ants.image_read( ants.get_ants_data('r16') )
image_n3 = ants.n3_bias_field_correction2(image)
Expand All @@ -929,29 +967,29 @@ def test_thin_plate_spline(self):
displacement_origins=points, displacements=deltas,
origin=[0.0, 0.0], spacing=[1.0, 1.0], size=[100, 100],
direction=np.array([[-1, 0], [0, -1]]))

def test_multi_label_morph(self):
img = ants.image_read(ants.get_data('r16'))
labels = ants.get_mask(img,1,150) + ants.get_mask(img,151,225) * 2
labels_dilated = ants.multi_label_morphology(labels, 'MD', 2)
# should see original label regions preserved in dilated version
# label N should have mean N and 0 variance
print(ants.label_stats(labels_dilated, labels))

def test_hausdorff_distance(self):
r16 = ants.image_read( ants.get_ants_data('r16') )
r64 = ants.image_read( ants.get_ants_data('r64') )
s16 = ants.kmeans_segmentation( r16, 3 )['segmentation']
s64 = ants.kmeans_segmentation( r64, 3 )['segmentation']
stats = ants.hausdorff_distance(s16, s64)

def test_channels_first(self):
import ants
image = ants.image_read(ants.get_ants_data('r16'))
image2 = ants.image_read(ants.get_ants_data('r16'))
img3 = ants.merge_channels([image,image2])
img4 = ants.merge_channels([image,image2], channels_first=True)

self.assertTrue(np.allclose(img3.numpy()[:,:,0], img4.numpy()[0,:,:]))
self.assertTrue(np.allclose(img3.numpy()[:,:,1], img4.numpy()[1,:,:]))

Expand Down

0 comments on commit 5238b3c

Please sign in to comment.