def test_feature_vector_of_endpoints(): # Test subclassing Feature class VectorOfEndpointsFeature(dipymetric.Feature): def __init__(self): super(VectorOfEndpointsFeature, self).__init__(False) def infer_shape(self, streamline): return (1, streamline.shape[1]) def extract(self, streamline): return streamline[[-1]] - streamline[[0]] feature_types = [dipymetric.VectorOfEndpointsFeature(), VectorOfEndpointsFeature()] for feature in feature_types: for s in [s1, s2, s3, s4]: # Test method infer_shape assert_equal(feature.infer_shape(s), (1, s.shape[1])) # Test method extract features = feature.extract(s) assert_equal(features.shape, (1, s.shape[1])) assert_array_almost_equal(features, s[[-1]] - s[[0]]) # This feature type is not order invariant assert_false(feature.is_order_invariant) for s in [s1, s2, s3, s4]: features = feature.extract(s) features_flip = feature.extract(s[::-1]) # The flip features are simply the negative of the features. assert_array_almost_equal(features, -features_flip)
def test_metric_cosine(): feature = dipymetric.VectorOfEndpointsFeature() class CosineMetric(dipymetric.Metric): def __init__(self, feature): super(CosineMetric, self).__init__(feature=feature) def are_compatible(self, shape1, shape2): # Cosine metric works on vectors. return shape1 == shape2 and shape1[0] == 1 def dist(self, v1, v2): # Check if we have null vectors if norm(v1) == 0: return 0. if norm(v2) == 0 else 1. v1_normed = v1.astype(np.float64) / norm(v1.astype(np.float64)) v2_normed = v2.astype(np.float64) / norm(v2.astype(np.float64)) cos_theta = np.dot(v1_normed, v2_normed.T) # Make sure it's in [-1, 1], i.e. within domain of arccosine cos_theta = np.minimum(cos_theta, 1.) cos_theta = np.maximum(cos_theta, -1.) return np.arccos(cos_theta) / np.pi # Normalized cosine distance for metric in [CosineMetric(feature), dipymetric.CosineMetric(feature)]: # Test special cases of the cosine distance. v0 = np.array([[0, 0, 0]], dtype=np.float32) v1 = np.array([[1, 2, 3]], dtype=np.float32) v2 = np.array([[1, -1. / 2, 0]], dtype=np.float32) v3 = np.array([[-1, -2, -3]], dtype=np.float32) assert_equal(metric.dist(v0, v0), 0.) # dot-dot assert_equal(metric.dist(v0, v1), 1.) # dot-line assert_equal(metric.dist(v1, v1), 0.) # collinear assert_equal(metric.dist(v1, v2), 0.5) # orthogonal assert_equal(metric.dist(v1, v3), 1.) # opposite # All possible pairs for s1, s2 in itertools.product(*[streamlines] * 2): # Extract features since metric doesn't # work directly on streamlines f1 = metric.feature.extract(s1) f2 = metric.feature.extract(s2) # Test method are_compatible are_vectors = f1.shape[0] == 1 and f2.shape[0] == 1 same_dimension = f1.shape[1] == f2.shape[1] assert_equal(metric.are_compatible(f1.shape, f2.shape), are_vectors and same_dimension) # Test method dist if features are compatible if metric.are_compatible(f1.shape, f2.shape): distance = metric.dist(f1, f2) if np.all(f1 == f2): assert_almost_equal(distance, 0.) assert_almost_equal(distance, dipymetric.dist(metric, s1, s2)) assert_true(distance >= 0.) assert_true(distance <= 1.) # This metric type is not order invariant assert_false(metric.is_order_invariant) # All possible pairs for s1, s2 in itertools.product(*[streamlines] * 2): f1 = metric.feature.extract(s1) f2 = metric.feature.extract(s2) if not metric.are_compatible(f1.shape, f2.shape): continue f1_flip = metric.feature.extract(s1[::-1]) f2_flip = metric.feature.extract(s2[::-1]) distance = metric.dist(f1, f2) assert_almost_equal(metric.dist(f1_flip, f2_flip), distance) if not np.all(f1_flip == f2_flip): assert_false(metric.dist(f1, f2_flip) == distance) assert_false(metric.dist(f1_flip, f2) == distance)