def test_some_to_many(self, dummy_signal): scope = MetricScope.SOME_TO_MANY assert scope.name == "SOME_TO_MANY" assert scope.value == "some_to_many" sig_shape = dummy_signal.axes_manager.signal_shape expt = dummy_signal.data.reshape((-1, ) + sig_shape) sim = expt[:3] dims = (expt.ndim, sim.ndim) assert dims == (3, 3) # Expansion of dimensions works ncc_metric = _SIMILARITY_METRICS["ncc"] ncc = ncc_metric(expt, sim) assert ncc.shape == (9, 3) assert np.allclose(np.diagonal(ncc), 1) def dot_product(a, b): norm_a = np.linalg.norm(a, axis=(1, 2))[:, np.newaxis, np.newaxis] norm_b = np.linalg.norm(b, axis=(1, 2))[:, np.newaxis, np.newaxis] return np.tensordot(a / norm_a, b / norm_b, axes=([1, 2], [2, 1])) metric = make_similarity_metric(metric_func=dot_product, scope=scope) assert metric._EXPT_SIM_NDIM_TO_SCOPE[dims] == scope assert metric._SCOPE_TO_EXPT_SIM_NDIM[scope] == dims ndp = metric(expt, sim) assert ndp.shape == (9, 3) assert np.allclose(np.sum(ndp), 19.92476)
def test_some_to_one(self, dummy_signal): scope = MetricScope.SOME_TO_ONE assert scope.name == "SOME_TO_ONE" assert scope.value == "some_to_one" sig_shape = dummy_signal.axes_manager.signal_shape expt = dummy_signal.data.reshape((-1, ) + sig_shape) sim = expt[0] dims = (expt.ndim, sim.ndim) assert dims == (3, 2) # Expansion of dimensions works ndp_metric = _SIMILARITY_METRICS["ndp"] ndp = ndp_metric(expt, sim) assert ndp.shape == (9, ) assert np.allclose(ndp[0], 1) def dot_product(a, b): norm_a = np.linalg.norm(a, axis=(1, 2))[:, np.newaxis, np.newaxis] norm_b = np.linalg.norm(b) return np.tensordot(a / norm_a, b / norm_b, axes=([1, 2], [1, 0])) metric = make_similarity_metric(metric_func=dot_product, scope=scope) assert metric._EXPT_SIM_NDIM_TO_SCOPE[dims] == scope assert metric._SCOPE_TO_EXPT_SIM_NDIM[scope] == dims ndp = metric(expt, sim) assert ndp.shape == (9, ) assert np.allclose(np.sum(ndp), 6.9578266)
def test_negative_metric(self, get_single_phase_xmap): def negative_sad(p, t): # pragma: no cover return -np.sum(np.abs(p - t), axis=(2, 3)) metric = make_similarity_metric(negative_sad, greater_is_better=False) map_shape = (5, 6) rot_per_point = 5 scores_prop = "scores" sim_idx_prop = "simulation_indices" xmap1 = get_single_phase_xmap(map_shape, rot_per_point, [scores_prop, sim_idx_prop], "a", 0) xmap2 = get_single_phase_xmap(map_shape, rot_per_point, [scores_prop, sim_idx_prop], "b", 1) xmap2[0, 3].prop[scores_prop] = 0 desired_phase_id = np.zeros(np.prod(map_shape)) desired_phase_id[3] = 1 merged_xmap = merge_crystal_maps( crystal_maps=[xmap1, xmap2], metric=metric, ) assert np.allclose(merged_xmap.phase_id, desired_phase_id)
def test_make_similarity_metric(self, flat, returned_class): assert (type( make_similarity_metric( lambda expt, sim: np.zeros((2, 4)) if flat else np.zeros( (2, 2, 2)), flat=flat, scope=MetricScope.MANY_TO_MANY, )) is returned_class)
def test_too_small_scoped_inputs(self): metric = make_similarity_metric( lambda expt, sim: np.zeros((2, 2, 2)), scope=MetricScope.MANY_TO_MANY, ) assert (metric._is_compatible( np.zeros((2, 2)).ndim, np.zeros((2, 2)).ndim) is False)
def test_not_supported_inputs(self): metric = make_similarity_metric( lambda expt, sim: 1.0, scope=MetricScope.MANY_TO_MANY, make_compatible_to_lower_scopes=True, ) assert (metric._is_compatible( np.zeros((2, 2, 2, 2, 2)).ndim, np.zeros((4, 2, 2)).ndim) is False)
def test_some_to_one_flat(self, dummy_signal): scope_in = MetricScope.SOME_TO_ONE metric = make_similarity_metric(metric_func=_zncc_einsum, scope=scope_in, flat=True) scope_out = metric.scope assert metric.flat assert scope_out.name == "MANY_TO_ONE"
def test_similarity_metric_representation(self): metrics = [ make_similarity_metric( metric_func=lambda expt, sim: np.zeros((2, 2, 2)), scope=MetricScope.MANY_TO_MANY, ), make_similarity_metric( metric_func=lambda expt, sim: np.zeros((2, 2, 2)), scope=MetricScope.ONE_TO_MANY, flat=True, ), _SIMILARITY_METRICS["ncc"], _SIMILARITY_METRICS["ndp"], ] desired_repr = [ "SimilarityMetric <lambda>, scope: many_to_many", "FlatSimilarityMetric <lambda>, scope: one_to_many", "SimilarityMetric _zncc_einsum, scope: many_to_many", "SimilarityMetric _ndp_einsum, scope: many_to_many", ] for i in range(len(desired_repr)): assert repr(metrics[i]) == desired_repr[i]
def test_flat_metric(self): expt = np.array( [ [[[1, 2], [3, 4]], [[5, 6], [7, 8]]], [[[9, 8], [1, 7]], [[5, 2], [2, 7]]], ], np.int8, ) sim = np.array([[[5, 3], [2, 7]], [[9, 8], [1, 7]]], np.int8) euclidean_metric = make_similarity_metric( lambda expt, sim: cdist(expt, sim, metric="euclidean"), greater_is_better=False, flat=True, scope=MetricScope.MANY_TO_MANY, make_compatible_to_lower_scopes=True, ) assert (euclidean_metric._is_compatible(expt.ndim, sim.ndim) is True and pytest.approx(euclidean_metric(expt, sim)[2, 1]) == 0)
def test_too_large_scoped_inputs(self): metric = make_similarity_metric(lambda expt, sim: 1.0, scope=MetricScope.ONE_TO_ONE) assert (metric._is_compatible( np.zeros((2, 2, 2, 2)).ndim, np.zeros((4, 2, 2)).ndim) is False)
class TestPatternMatching: zncc_flat_metric = make_similarity_metric( lambda p, t: cdist(p, t, metric="correlation"), greater_is_better=False, flat=True, ) dummy_metric = make_similarity_metric(lambda p, t: 1.0) def test_not_recognized_metric(self): with pytest.raises(ValueError): _pattern_match(np.zeros((2, 2)), np.zeros((2, 2)), metric="not_recognized") def test_mismatching_signal_shapes(self): self.dummy_metric.scope = MetricScope.MANY_TO_MANY with pytest.raises(OSError): _pattern_match(np.zeros((2, 2)), np.zeros((3, 3)), metric=self.dummy_metric) def test_metric_not_compatible_with_data(self): self.dummy_metric.scope = MetricScope.ONE_TO_MANY with pytest.raises(OSError): _pattern_match( np.zeros((2, 2, 2, 2)), np.zeros((2, 2)), metric=self.dummy_metric, ) @pytest.mark.parametrize("n_slices", [1, 2]) def test_pattern_match_compute_true(self, n_slices): # Four patterns p = np.array( [ [[[1, 2], [3, 4]], [[5, 6], [7, 8]]], [[[9, 8], [1, 7]], [[5, 2], [2, 7]]], ], np.int8, ) # Five templates t = np.array( [ [[5, 3], [2, 7]], [[9, 8], [1, 7]], [[10, 2], [5, 3]], [[8, 4], [6, 12]], [[43, 0], [5, 3]], ], np.int8, ) t_da = da.from_array(t) mr = _pattern_match(p, t_da, n_slices=n_slices, keep_n=1) assert mr[0][2] == 1 # Template index in t of perfect match assert np.allclose(mr[1][2], 1.0) # ZNCC of perfect match def test_pattern_match_compute_false(self): p = np.arange(16).reshape((2, 2, 2, 2)) t = np.arange(8).reshape((2, 2, 2)) mr = _pattern_match(p, t, compute=False) assert len(mr) == 2 assert isinstance(mr[0], da.Array) and isinstance(mr[1], da.Array) def test_pattern_match_slices_compute_false(self): p = np.arange(16).reshape((2, 2, 2, 2)) t = np.arange(8).reshape((2, 2, 2)) with pytest.raises(NotImplementedError): _pattern_match(p, t, n_slices=2, compute=False) def test_pattern_match_one_to_one(self): p = np.random.random(3 * 3).reshape((3, 3)) mr = _pattern_match(p, p) assert mr[0][0] == 0 def test_pattern_match_phase_name(self): """Ensure that the `phase_name` accepts different types.""" exp = nickel_ebsd_small().data sim = exp.reshape((-1, ) + exp.shape[-2:]) sim_idx1, scores1 = _pattern_match(exp, sim, n_slices=2) sim_idx2, scores2 = _pattern_match(exp, sim, phase_name="a", n_slices=2) sim_idx3, scores3 = _pattern_match(exp, sim, phase_name="", n_slices=2) assert np.allclose(sim_idx1[0], [0, 3, 6, 4, 7, 1, 8, 5, 2]) assert np.allclose(sim_idx2[0], [0, 3, 6, 4, 7, 1, 8, 5, 2]) assert np.allclose(sim_idx3[0], [0, 3, 6, 4, 7, 1, 8, 5, 2]) def test_passing_results_from_get_patterns(self): s = nickel_ebsd_small() print(s) s.remove_static_background() s.remove_dynamic_background() mp = nickel_ebsd_master_pattern_small(projection="lambert") r = Rotation([ [0.9522, -0.0151, -0.2883, 0.1001], # Left grain [0.9534, 0.0465, -0.2187, -0.2026], # Right grain [0.8876, -0.2312, 0.3364, -0.2131], # 50th best match ]) detector = EBSDDetector( shape=s.axes_manager.signal_shape[::-1], pc=[0.421, 0.7794, 0.5049], convention="tsl", sample_tilt=70, ) sim = mp.get_patterns(rotations=r, detector=detector, energy=20) xmap = s.match_patterns(sim, keep_n=1) scores = xmap.scores.reshape(xmap.size, ) sim_idx = xmap.simulation_indices.reshape(xmap.size, ) assert np.allclose( scores, [0.197, 0.188, 0.207, 0.198, 0.186, 0.225, 0.191, 0.191, 0.206], atol=1e-3, ) assert np.allclose(sim_idx, [0, 1, 1, 0, 1, 1, 0, 1, 1])