コード例 #1
0
    def test_signal_varying_dimensions(self, dummy_signal, slices,
                                       desired_xmap_shape):
        s = dummy_signal.inav[slices]
        sig_shape = dummy_signal.axes_manager.signal_shape
        s_dict1 = EBSD(dummy_signal.data.reshape((-1, ) + sig_shape))
        n_sim = s_dict1.axes_manager.navigation_size
        s_dict1._xmap = CrystalMap(Rotation(np.zeros((n_sim, 4))))
        sd = StaticPatternMatching(s_dict1)
        res = sd(s)

        assert res.shape == desired_xmap_shape
コード例 #2
0
    def test_keep_n(self, n_rot_in, n_rot_out, keep_n):
        s = nickel_ebsd_small()
        s_dict = EBSD(np.random.random((n_rot_in, 60, 60)).astype(np.float32))
        s_dict._xmap = CrystalMap(Rotation(np.zeros((n_rot_in, 4))))
        sd = StaticPatternMatching(s_dict)
        xmap = sd(s)

        assert xmap.rotations_per_point == n_rot_out

        xmap2 = sd(s, keep_n=keep_n)

        assert xmap2.rotations_per_point == keep_n
コード例 #3
0
    def test_bands_as_markers_1d_nav(
        self, nickel_ebsd_simulation_generator, nickel_rlp
    ):
        """1D nav shape band markers work."""
        simgen = nickel_ebsd_simulation_generator[0]
        assert simgen.navigation_shape == (1,)
        sim = simgen.geometrical_simulation(nickel_rlp.symmetrise())
        assert sim.bands.navigation_shape == (1,)

        se = EBSD(np.ones((60, 60), dtype=np.uint8))

        se.add_marker(marker=sim.bands_as_markers(), permanent=False, plot_marker=True)
        plt.close("all")
コード例 #4
0
    def test_get_rgb_1d(self):
        s = EBSD(np.random.random(9 * 3600).reshape((9, 60, 60)))
        vbse_gen = VirtualBSEGenerator(s)

        with pytest.raises(ValueError,
                           match="The signal dimension cannot be "):
            _ = vbse_gen.get_rgb_image(r=(0, 0), g=(0, 1), b=(0, 2))
コード例 #5
0
    def test_n_slices_input(self, dummy_signal):
        sig_shape = dummy_signal.axes_manager.signal_shape
        n_px = np.prod(sig_shape)
        n_sim = 13500 + 1
        rand_data = (np.random.randint(
            0, 255,
            n_sim * n_px).reshape((n_sim, ) + sig_shape).astype(np.uint8))
        s_dict1 = EBSD(rand_data)
        s_dict1._xmap = CrystalMap(Rotation(np.zeros((n_sim, 4))))
        sd = StaticPatternMatching(s_dict1)

        with replace_stdin(io.StringIO("y")):
            res = sd(dummy_signal, n_slices=1)
            assert isinstance(res, CrystalMap)

        with replace_stdin(io.StringIO("n")):
            res = sd(dummy_signal, n_slices=1)
            assert res is None
コード例 #6
0
ファイル: conftest.py プロジェクト: friedkitteh/kikuchipy
def dummy_signal():
    """Dummy signal of shape <(3, 3)|(3, 3)>. If this is changed, all
    tests using this signal will fail since they compare the output from
    methods using this signal (as input) to hard-coded outputs.
    """
    # fmt: off
    dummy_array = np.array([
        5, 6, 5, 7, 6, 5, 6, 1, 0, 9, 7, 8, 7, 0, 8, 8, 7, 6, 0, 3, 3, 5, 2, 9,
        3, 3, 9, 8, 1, 7, 6, 4, 8, 8, 2, 2, 4, 0, 9, 0, 1, 0, 2, 2, 5, 8, 6, 0,
        4, 7, 7, 7, 6, 0, 4, 1, 6, 3, 4, 0, 1, 1, 0, 5, 9, 8, 4, 6, 0, 2, 9, 2,
        9, 4, 3, 6, 5, 6, 2, 5, 9
    ],
                           dtype=np.uint8).reshape((3, 3, 3, 3))
    # fmt: on
    return EBSD(dummy_array)
コード例 #7
0
    def test_return_merged_crystal_map(self, return_merged_xmap,
                                       desired_n_xmaps_out):
        s = nickel_ebsd_small()
        s_dict1 = EBSD(s.data.reshape(-1, 60, 60))
        s_dict2 = s_dict1.deepcopy()
        n_patterns = s_dict1.axes_manager.navigation_size
        s_dict1._xmap = CrystalMap(Rotation(np.zeros((n_patterns, 4))))
        s_dict2._xmap = s_dict1.xmap.deepcopy()
        s_dict1.xmap.phases[0].name = "a"
        s_dict2.xmap.phases[0].name = "b"

        sd = StaticPatternMatching([s_dict1, s_dict2])
        res1 = sd(s, return_merged_crystal_map=return_merged_xmap)

        assert len(res1) == desired_n_xmaps_out

        sd.dictionaries.pop(-1)
        res2 = sd(s, return_merged_crystal_map=True)

        assert isinstance(res2, CrystalMap)

        res3 = sd(s)

        assert isinstance(res3, CrystalMap)
コード例 #8
0
    def test_bands_as_markers(
        self, nickel_ebsd_simulation_generator, nickel_rlp, nav_shape
    ):
        """Line markers work."""
        simgen = nickel_ebsd_simulation_generator
        simgen.navigation_shape = nav_shape
        sim = simgen.geometrical_simulation(nickel_rlp.symmetrise())

        se = EBSD(np.ones(nav_shape + (60, 60), dtype=np.uint8))

        se.add_marker(marker=sim.bands_as_markers(), permanent=True, plot_marker=False)
        assert isinstance(se.metadata.Markers.line_segment, line_segment)
        se.plot()
        plt.close("all")
コード例 #9
0
    def test_pc_as_markers(
        self, nickel_ebsd_simulation_generator, nickel_rlp, nav_shape
    ):
        """Projection center markers work."""
        simgen = nickel_ebsd_simulation_generator
        simgen.navigation_shape = nav_shape
        sim = simgen.geometrical_simulation(nickel_rlp.symmetrise())

        se = EBSD(np.ones(nav_shape + (60, 60), dtype=np.uint8))

        se.add_marker(marker=sim.pc_as_markers(), permanent=True, plot_marker=False)
        assert isinstance(se.metadata.Markers.point, point)
        assert se.metadata.Markers.point.marker_properties["marker"] == "*"
        se.plot()
        plt.close("all")
コード例 #10
0
    def test_as_markers(self, nickel_ebsd_simulation_generator, nickel_rlp):
        """All markers work."""
        simgen = nickel_ebsd_simulation_generator
        sim = simgen.geometrical_simulation(nickel_rlp.symmetrise())

        se = EBSD(np.ones(simgen.navigation_shape + (60, 60), dtype=np.uint8))

        se.add_marker(
            marker=sim.as_markers(
                bands=True, zone_axes=True, zone_axes_labels=True, pc=True
            ),
            permanent=True,
            plot_marker=False,
        )
        se.plot()
        plt.close("all")
コード例 #11
0
    def test_get_orientation_similarity_map(self):
        s = nickel_ebsd_small()

        s_dict1 = EBSD(s.data.reshape(-1, 60, 60))
        s_dict2 = EBSD(s.data.reshape(-1, 60, 60))
        n_patterns = s_dict1.axes_manager.navigation_size
        s_dict1._xmap = CrystalMap(Rotation(np.zeros((n_patterns, 4))))
        s_dict2._xmap = CrystalMap(Rotation(np.zeros((n_patterns, 4))))
        s_dict1.xmap.phases[0].name = "a"
        s_dict2.xmap.phases[0].name = "b"

        sd = StaticPatternMatching([s_dict1, s_dict2])
        res = sd(s, keep_n=1, get_orientation_similarity_map=True)
        xmap1, _ = res

        assert np.allclose(xmap1.scores, 1)
        assert np.all(["osm" in xmap.prop for xmap in res])
コード例 #12
0
    def test_plot_zone_axes_labels_warns(self,
                                         nickel_ebsd_simulation_generator,
                                         nickel_rlp):
        """Matplotlib warns when plotting text with NaN coordinates."""
        simgen = nickel_ebsd_simulation_generator
        sim = simgen.geometrical_simulation(nickel_rlp.symmetrise())

        se = EBSD(np.ones(simgen.navigation_shape + (60, 60), dtype=np.uint8))

        with pytest.warns(UserWarning, match="Matplotlib will print"):
            se.add_marker(
                marker=sim.zone_axes_labels_as_markers(),
                permanent=False,
                plot_marker=True,
            )
            se.plot()
            se.axes_manager[0].index = 1
        plt.close("all")
コード例 #13
0
    def test_bands_as_markers_family_colors(
        self, nickel_ebsd_simulation_generator, nickel_rlp
    ):
        """Line markers work when specifying colors."""
        simgen = nickel_ebsd_simulation_generator
        sim = simgen.geometrical_simulation(nickel_rlp[:2])

        se = EBSD(np.ones(simgen.navigation_shape + (60, 60), dtype=np.uint8))

        colors = ["lime", "tab:blue"]
        se.add_marker(
            marker=sim.bands_as_markers(family_colors=colors),
            permanent=True,
            plot_marker=False,
        )
        assert se.metadata.Markers.line_segment.marker_properties["color"] == colors[0]
        assert se.metadata.Markers.line_segment1.marker_properties["color"] == colors[1]
        se.plot()
        plt.close("all")
コード例 #14
0
    def test_zone_axes_labels_as_markers(self,
                                         nickel_ebsd_simulation_generator,
                                         nickel_rlp, nav_shape):
        """Text markers work."""
        simgen = nickel_ebsd_simulation_generator
        simgen.navigation_shape = nav_shape
        sim = simgen.geometrical_simulation(nickel_rlp.symmetrise())

        se = EBSD(np.ones(nav_shape + (60, 60), dtype=np.uint8))

        matplotlib.set_loglevel("error")
        se.add_marker(
            marker=sim.zone_axes_labels_as_markers(),
            permanent=True,
            plot_marker=False,
        )
        assert isinstance(se.metadata.Markers.text, text)
        se.plot()
        plt.close("all")
        matplotlib.set_loglevel("warning")
コード例 #15
0
    def get_patterns(
        self,
        rotations: Rotation,
        detector: EBSDDetector,
        energy: Union[int, float],
        dtype_out: type = np.float32,
        compute: bool = False,
        **kwargs,
    ) -> Union[EBSD, LazyEBSD]:
        """Return a dictionary of EBSD patterns projected onto a
        detector from a master pattern in the square Lambert
        projection :cite:`callahan2013dynamical`, for a set of crystal
        rotations relative to the EDAX TSL sample reference frame (RD,
        TD, ND) and a fixed detector-sample geometry.

        Parameters
        ----------
        rotations
            Set of crystal rotations to get patterns from. The shape of
            this object, a maximum of two dimensions, determines the
            navigation shape of the output signal.
        detector
            EBSD detector describing the detector dimensions and the
            detector-sample geometry with a single, fixed
            projection/pattern center.
        energy
            Acceleration voltage, in kV, used to simulate the desired
            master pattern to create a dictionary from.
        dtype_out
            Data type of the returned patterns, by default np.float32.
        compute
            Whether to return a lazy result, by default False. For more
            information see :func:`~dask.array.Array.compute`.
        kwargs
            Keyword arguments passed to
            :func:`~kikuchipy.signals.util.get_chunking` to control the
            number of chunks the dictionary creation and the output data
            array is split into. Only `chunk_shape`, `chunk_bytes` and
            `dtype_out` (to `dtype`) are passed on.

        Returns
        -------
        EBSD or LazyEBSD
            Signal with navigation and signal shape equal to the
            rotation object's and detector shape, respectively.

        Notes
        -----
        If the master pattern phase has a non-centrosymmetric point
        group, both the northern and southern hemispheres must be
        provided. For more details regarding the reference frame visit
        the reference frame user guide at:
        https://kikuchipy.org/en/latest/reference_frames.html.
        """
        if self.projection != "lambert":
            raise NotImplementedError(
                "Master pattern must be in the square Lambert projection"
            )
        if len(detector.pc) > 1:
            raise NotImplementedError(
                "Detector must have exactly one projection center"
            )

        # Get suitable chunks when iterating over the rotations. The
        # signal axes are not chunked.
        nav_shape = rotations.shape
        nav_dim = len(nav_shape)
        if nav_dim > 2:
            raise ValueError(
                "The rotations object can only have one or two dimensions, but "
                f"an object with {nav_dim} was passed"
            )
        data_shape = nav_shape + detector.shape
        chunks = get_chunking(
            data_shape=data_shape,
            nav_dim=nav_dim,
            sig_dim=len(detector.shape),
            chunk_shape=kwargs.pop("chunk_shape", None),
            chunk_bytes=kwargs.pop("chunk_bytes", None),
            dtype=dtype_out,
        )

        # Get the master pattern arrays created by a desired energy
        north_slice = ()
        if "energy" in [i.name for i in self.axes_manager.navigation_axes]:
            energies = self.axes_manager["energy"].axis
            north_slice += ((np.abs(energies - energy)).argmin(),)
        south_slice = north_slice
        if self.hemisphere == "both":
            north_slice = (0,) + north_slice
            south_slice = (1,) + south_slice
        elif not self.phase.point_group.contains_inversion:
            raise AttributeError(
                "For crystals of point groups without inversion symmetry, like "
                f"the current {self.phase.point_group.name}, both hemispheres "
                "must be present in the master pattern signal"
            )
        master_north = self.data[north_slice]
        master_south = self.data[south_slice]

        # Whether to rescale pattern intensities after projection
        rescale = False
        if dtype_out != np.float32:
            rescale = True

        # Get direction cosines for each detector pixel relative to the
        # source point
        dc = _get_direction_cosines(detector)

        # Get dask array from rotations
        r_da = da.from_array(rotations.data, chunks=chunks[:nav_dim] + (-1,))

        # Which axes to drop and add when iterating over the rotations
        # dask array to produce the EBSD signal array, i.e. drop the
        # (4,)-shape quaternion axis and add detector shape axes, e.g.
        # (60, 60)
        if nav_dim == 1:
            drop_axis = 1
            new_axis = (1, 2)
        else:  # nav_dim == 2
            drop_axis = 2
            new_axis = (2, 3)

        # Project simulated patterns onto detector
        npx, npy = self.axes_manager.signal_shape
        scale = (npx - 1) / 2
        simulated = r_da.map_blocks(
            _get_patterns_chunk,
            dc=dc,
            master_north=master_north,
            master_south=master_south,
            npx=npx,
            npy=npy,
            scale=scale,
            rescale=rescale,
            dtype_out=dtype_out,
            drop_axis=drop_axis,
            new_axis=new_axis,
            chunks=chunks,
            dtype=dtype_out,
        )

        # Add crystal map and detector to keyword arguments
        kwargs = dict(
            xmap=CrystalMap(
                phase_list=PhaseList(self.phase), rotations=rotations,
            ),
            detector=detector,
        )

        # Specify navigation and signal axes for signal initialization
        names = ["y", "x", "dy", "dx"]
        scales = np.ones(4)
        ndim = simulated.ndim
        if ndim == 3:
            names = names[1:]
            scales = scales[1:]
        axes = [
            dict(
                size=data_shape[i],
                index_in_array=i,
                name=names[i],
                scale=scales[i],
                offset=0.0,
                units="px",
            )
            for i in range(ndim)
        ]

        if compute:
            with ProgressBar():
                print(
                    f"Creating a dictionary of {nav_shape} simulated patterns:",
                    file=sys.stdout,
                )
                patterns = simulated.compute()
            out = EBSD(patterns, axes=axes, **kwargs)
        else:
            out = LazyEBSD(simulated, axes=axes, **kwargs)

        return out
コード例 #16
0
    def get_patterns(
        self,
        rotations: Rotation,
        detector: EBSDDetector,
        energy: Union[int, float],
        dtype_out: Union[type, np.dtype] = np.float32,
        compute: bool = False,
        **kwargs,
    ) -> Union[EBSD, LazyEBSD]:
        """Return a dictionary of EBSD patterns projected onto a
        detector from a master pattern in the square Lambert
        projection :cite:`callahan2013dynamical`, for a set of crystal
        rotations relative to the EDAX TSL sample reference frame (RD,
        TD, ND) and a fixed detector-sample geometry.

        Parameters
        ----------
        rotations
            Crystal rotations to get patterns from. The shape of this
            instance, a maximum of two dimensions, determines the
            navigation shape of the output signal.
        detector
            EBSD detector describing the detector dimensions and the
            detector-sample geometry with a single, fixed
            projection/pattern center.
        energy
            Acceleration voltage, in kV, used to simulate the desired
            master pattern to create a dictionary from. If only a single
            energy is present in the signal, this will be returned no
            matter its energy.
        dtype_out
            Data type of the returned patterns, by default np.float32.
        compute
            Whether to return a lazy result, by default False. For more
            information see :func:`~dask.array.Array.compute`.
        kwargs
            Keyword arguments passed to
            :func:`~kikuchipy.signals.util.get_chunking` to control the
            number of chunks the dictionary creation and the output data
            array is split into. Only `chunk_shape`, `chunk_bytes` and
            `dtype_out` (to `dtype`) are passed on.

        Returns
        -------
        EBSD or LazyEBSD
            Signal with navigation and signal shape equal to the
            rotation instance and detector shape, respectively.

        Notes
        -----
        If the master pattern phase has a non-centrosymmetric point
        group, both the northern and southern hemispheres must be
        provided. For more details regarding the reference frame visit
        the reference frame user guide.
        """
        self._is_suitable_for_projection(raise_if_not=True)

        if len(detector.pc) > 1:
            raise NotImplementedError(
                "Detector must have exactly one projection center")

        # Get suitable chunks when iterating over the rotations. Signal
        # axes are not chunked.
        nav_shape = rotations.shape
        nav_dim = len(nav_shape)
        if nav_dim > 2:
            raise ValueError(
                "`rotations` can only have one or two dimensions, but an instance with "
                f"{nav_dim} dimensions was passed")
        data_shape = nav_shape + detector.shape
        chunks = get_chunking(
            data_shape=data_shape,
            nav_dim=nav_dim,
            sig_dim=len(detector.shape),
            chunk_shape=kwargs.pop("chunk_shape", None),
            chunk_bytes=kwargs.pop("chunk_bytes", None),
            dtype=dtype_out,
        )

        # Whether to rescale pattern intensities after projection
        if dtype_out != self.data.dtype:
            rescale = True
            if isinstance(dtype_out, np.dtype):
                dtype_out = dtype_out.type
            out_min, out_max = dtype_range[dtype_out]
        else:
            rescale = False
            # Cannot be None due to Numba, so they are set to something
            # here. Values aren't used unless `rescale` is True.
            out_min, out_max = 1, 2

        # Get direction cosines for each detector pixel relative to the
        # source point
        direction_cosines = _get_direction_cosines_for_single_pc_from_detector(
            detector)

        # Get dask array from rotations
        rot_da = da.from_array(rotations.data,
                               chunks=chunks[:nav_dim] + (-1, ))

        # Which axes to drop and add when iterating over the rotations
        # dask array to produce the EBSD signal array, i.e. drop the
        # (4,)-shape quaternion axis and add detector shape axes, e.g.
        # (60, 60)
        if nav_dim == 1:
            drop_axis = 1
            new_axis = (1, 2)
        else:  # nav_dim == 2
            drop_axis = 2
            new_axis = (2, 3)

        master_north, master_south = self._get_master_pattern_arrays_from_energy(
            energy)

        # Project simulated patterns onto detector
        npx, npy = self.axes_manager.signal_shape
        scale = (npx - 1) / 2
        # TODO: Use dask.delayed instead?
        simulated = rot_da.map_blocks(
            _project_patterns_from_master_pattern,
            direction_cosines=direction_cosines,
            master_north=master_north,
            master_south=master_south,
            npx=int(npx),
            npy=int(npy),
            scale=float(scale),
            dtype_out=dtype_out,
            rescale=rescale,
            out_min=out_min,
            out_max=out_max,
            drop_axis=drop_axis,
            new_axis=new_axis,
            chunks=chunks,
            dtype=dtype_out,
        )

        # Add crystal map and detector to keyword arguments
        kwargs = dict(
            xmap=CrystalMap(phase_list=PhaseList(self.phase),
                            rotations=rotations),
            detector=detector,
        )

        # Specify navigation and signal axes for signal initialization
        names = ["y", "x", "dy", "dx"]
        scales = np.ones(4)
        ndim = simulated.ndim
        if ndim == 3:
            names = names[1:]
            scales = scales[1:]
        axes = [
            dict(
                size=data_shape[i],
                index_in_array=i,
                name=names[i],
                scale=scales[i],
                offset=0.0,
                units="px",
            ) for i in range(ndim)
        ]

        if compute:
            patterns = np.zeros(shape=simulated.shape, dtype=simulated.dtype)
            with ProgressBar():
                print(
                    f"Creating a dictionary of {nav_shape} simulated patterns:",
                    file=sys.stdout,
                )
                simulated.store(patterns, compute=True)
            out = EBSD(patterns, axes=axes, **kwargs)
        else:
            out = LazyEBSD(simulated, axes=axes, **kwargs)
        gc.collect()

        return out