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]
Esempio n. 9
0
 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])