{ "cells": [ { "cell_type": "markdown", "metadata": { "nbsphinx": "hidden" }, "source": [ "This notebook is part of the `kikuchipy` documentation https://kikuchipy.org.\n", "Links to the documentation won't work from the notebook." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Pattern processing\n", "\n", "The raw EBSD signal can be characterized as a superposition of a Kikuchi diffraction pattern and a smooth background intensity.\n", "For pattern indexing, the latter intensity is undesirable, while for [virtual backscatter electron VBSE) imaging](virtual_backscatter_electron_imaging.rst), this intensity can reveal topographical, compositional or diffraction contrast.\n", "\n", "This tutorial details methods to enhance the Kikuchi diffraction pattern and manipulate detector intensities in patterns in an [EBSD](../reference/generated/kikuchipy.signals.EBSD.rst) signal.\n", "\n", "Most of the methods operating on EBSD objects use functions that operate on the individual patterns (`numpy.ndarray`).\n", "Some of these single pattern functions are available in the [kikuchipy.pattern](../reference/generated/kikuchipy.pattern.rst) module.\n", "\n", "Let's import the necessary libraries and read the Nickel EBSD test data set" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Exchange inline for notebook or qt5 (from pyqt) for interactive plotting\n", "%matplotlib inline\n", "\n", "import matplotlib.pyplot as plt\n", "import numpy as np\n", "\n", "import hyperspy.api as hs\n", "import kikuchipy as kp" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Use kp.load(\"data.h5\") to load your own data\n", "s = kp.data.nickel_ebsd_small()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Most methods operate inplace (indicated in their docstrings), meaning they overwrite the patterns in the EBSD signal.\n", "If we instead want to keep the original signal and operate on a new signal, we can either create a [deepcopy()](../reference/generated/kikuchipy.signals.EBSD.deepcopy.rst) of the original signal..." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "s2 = s.deepcopy()\n", "np.may_share_memory(s.data, s2.data)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "... or pass `inplace=False` to return a new signal and keep the original signal unaffected (new in version 0.8)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let's make a convenience function to plot a pattern before and after processing with the intensity distributions below.\n", "We'll also globally silence progressbars" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def plot_pattern_processing(patterns, titles):\n", " \"\"\"Plot two patterns side by side with intensity histograms below.\n", "\n", " Parameters\n", " ----------\n", " patterns : list of numpy.ndarray\n", " titles : list of str\n", " \"\"\"\n", " fig, axes = plt.subplots(2, 2, height_ratios=[3, 1.5])\n", " for ax, pattern, title in zip(axes[0], patterns, titles):\n", " ax.imshow(pattern, cmap=\"gray\")\n", " ax.set_title(title)\n", " ax.axis(\"off\")\n", " for ax, pattern in zip(axes[1], patterns):\n", " ax.hist(pattern.ravel(), bins=100)\n", " fig.tight_layout()\n", "\n", "\n", "hs.preferences.General.show_progressbar = False" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Background correction\n", "\n", "### Remove the static background\n", "\n", "Effects which are constant, like hot pixels or dirt on the detector, can be removed by subtracting or dividing by a static background wit [remove_static_background()](../reference/generated/kikuchipy.signals.EBSD.remove_static_background.rst)" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "nbsphinx-thumbnail": { "tooltip": "Enhancement of Kikuchi bands in EBSD patterns" }, "tags": [ "nbsphinx-thumbnail" ] }, "outputs": [], "source": [ "s2 = s.remove_static_background(inplace=False)\n", "\n", "plot_pattern_processing(\n", " [s.inav[0, 0].data, s2.inav[0, 0].data], [\"Raw\", \"Static\"]\n", ")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We didn't have to pass a background pattern since it is stored with the currenct signal in the [static_background](../reference/generated/kikuchipy.signals.EBSD.static_background.rst) attribute.\n", "We could instead pass the background pattern in the `static_bg` parameter.\n", "\n", "The static background pattern intensity range can be scaled to each individual pattern's range prior to removal with ``scale_bg=True``." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Remove the dynamic background\n", "\n", "Uneven intensity in a static background subtracted pattern can be corrected by subtraction or division of a dynamic background pattern obtained by Gaussian blurring with [remove_dynamic_background()](../reference/generated/kikuchipy.signals.EBSD.remove_dynamic_background.rst).\n", "A Gaussian window with a standard deviation set by `std` is used to blur each pattern individually (dynamic) either in the spatial or frequency domain, set by `filter_domain`.\n", "Blurring in the frequency domain uses a low-pass [Fast Fourier Transform (FFT) filter](#Filtering-in-the-frequency-domain).\n", "Each pattern is then subtracted or divided by the individual dynamic background pattern depending on the `operation`" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "s3 = s2.remove_dynamic_background(\n", " operation=\"subtract\", # Default\n", " filter_domain=\"frequency\", # Default\n", " std=8, # Default is 1/8 of the pattern width\n", " truncate=4, # Default\n", " inplace=False,\n", ")\n", "\n", "plot_pattern_processing(\n", " [s2.inav[0, 0].data, s3.inav[0, 0].data], [\"Static\", \"Static + dynamic\"]\n", ")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The width of the Gaussian window is truncated at the `truncated` number of standard deviations.\n", "Output patterns are rescaled to fill the input patterns' data type range." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Get the dynamic background\n", "\n", "The Gaussian blurred pattern removed during dynamic background correction can be obtained as an EBSD signal by calling [get_dynamic_background()](../reference/generated/kikuchipy.signals.EBSD.get_dynamic_background.rst)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "bg = s.get_dynamic_background(filter_domain=\"frequency\", std=8, truncate=4)\n", "\n", "plot_pattern_processing(\n", " [s.inav[0, 0].data, bg.inav[0, 0].data], [\"Raw\", \"Dynamic background\"]\n", ")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Average neighbour patterns\n", "\n", "The signal-to-noise ratio in patterns in a scan can be improved by averaging patterns with their closest neighbours within a window/kernel/mask using [average_neighbour_patterns()](../reference/generated/kikuchipy.signals.EBSD.average_neighbour_patterns.rst)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "s4 = s3.average_neighbour_patterns(window=\"gaussian\", std=1, inplace=False)\n", "\n", "plot_pattern_processing(\n", " [s3.inav[0, 0].data, s4.inav[0, 0].data],\n", " [\"Static + dynamic\", \"Static + dynamic + averaged\"],\n", ")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The array of averaged patterns $g(n_{\\mathrm{x}}, n_{\\mathrm{y}})$ is obtained by spatially correlating a window $w(s, t)$ with the array of patterns $f(n_{\\mathrm{x}}, n_{\\mathrm{y}})$, here 4D, which is padded with zeros at the edges.\n", "As coordinates $n_{\\mathrm{x}}$ and $n_{\\mathrm{y}}$ are varied, the window origin moves from pattern to pattern, computing the sum of products of the window coefficients with the neighbour pattern intensities, defined by the window shape, followed by normalizing by the sum of the window coefficients.\n", "For a symmetrical window of shape $m \\times n$, this becomes Gonzalez and Woods (2017)\n", "\n", "\\begin{equation}\n", "g(n_{\\mathrm{x}}, n_{\\mathrm{y}}) =\n", "\\frac{\\sum_{s=-a}^a\\sum_{t=-b}^b{w(s, t)\n", "f(n_{\\mathrm{x}} + s, n_{\\mathrm{y}} + t)}}\n", "{\\sum_{s=-a}^a\\sum_{t=-b}^b{w(s, t)}},\n", "\\end{equation}\n", "\n", "where $a = (m - 1)/2$ and $b = (n - 1)/2$.\n", "The window $w$, a [Window](../reference/generated/kikuchipy.filters.Window.rst) object, can be plotted" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "w = kp.filters.Window(window=\"gaussian\", shape=(3, 3), std=1)\n", "w.plot()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Any 1D or 2D window with desired coefficients can be used.\n", "This custom window can be passed to the `window` parameter in [average_neighbour_patterns()](../reference/generated/kikuchipy.signals.EBSD.average_neighbour_patterns.rst) or [Window](../reference/generated/kikuchipy.filters.Window.rst) as a `numpy.ndarray` or a `dask.array.Array`.\n", "Additionally, any window in [scipy.signal.windows.get_window()](https://docs.scipy.org/doc/scipy/reference/generated/scipy.signal.get_window.html) passed as a string via `window` with the necessary parameters as keyword arguments (like `std=1` for `window=\"gaussian\"`) can be used.\n", "To demonstrate the creation and use of an asymmetrical circular window (and the use of [make_circular()](../reference/generated/kikuchipy.filters.Window.make_circular.rst), although we could create a circular window directly by calling `window=\"circular\"` upon window initialization)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "w = kp.filters.Window(window=\"rectangular\", shape=(5, 4))\n", "w" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "w.make_circular()\n", "w.plot()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "But this `(5, 4)` averaging window cannot be used with our `(3, 3)` navigation shape signal.\n", "\n", "