.. DO NOT EDIT. .. THIS FILE WAS AUTOMATICALLY GENERATED BY SPHINX-GALLERY. .. TO MAKE CHANGES, EDIT THE SOURCE PYTHON FILE: .. "auto_examples\06_binary_operations.py" .. LINE NUMBERS ARE GIVEN BELOW. .. only:: html .. note:: :class: sphx-glr-download-link-note :ref:`Go to the end ` to download the full example code. .. rst-class:: sphx-glr-example-title .. _sphx_glr_auto_examples_06_binary_operations.py: ======================= Binary Image Operations ======================= In this section, we will learn some basic operations that are usually done on a binary image, which I think are especially helpful for processing fluorescence images. To demonstrate the concept, we will use an 8x8 binary image. .. GENERATED FROM PYTHON SOURCE LINES 11-27 .. code-block:: Python import numpy as np import matplotlib.pyplot as plt import matplotlib.patheffects as path_effects from skimage.morphology import binary_erosion, binary_dilation, disk binary_image = np.array([[0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 1, 1, 0, 0, 0], [0, 0, 1, 1, 1, 1, 0, 0], [0, 1, 1, 1, 1, 1, 1, 0], [0, 1, 1, 1, 1, 1, 1, 0], [0, 0, 1, 1, 1, 1, 0, 0], [0, 0, 0, 1, 1, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0]]) .. GENERATED FROM PYTHON SOURCE LINES 28-31 First, let's create a function that displays the image which has an option that lets you put a pixel value on top of each pixel. .. GENERATED FROM PYTHON SOURCE LINES 31-51 .. code-block:: Python def display_image(ax, img, title, show_value=False): ax.imshow(img, cmap='gray', vmin=0, vmax=1) ax.set_title(title) ax.axis("off") if show_value: for row in range(img.shape[0]): for col in range(img.shape[1]): text = ax.text(col, row, int(img[row, col]), ha='center', va='center', fontsize=10, color='black', fontweight='bold') text.set_path_effects([path_effects.Stroke(linewidth=2, foreground='white'), path_effects.Normal()]) fig, axes = plt.subplots(1, 2, figsize=(6, 3)) display_image(axes[0], binary_image, "Binary Image") display_image(axes[1], binary_image, "with Pixel Values", show_value=True) plt.tight_layout() plt.show() .. image-sg:: /auto_examples/images/sphx_glr_06_binary_operations_001.png :alt: Binary Image, with Pixel Values :srcset: /auto_examples/images/sphx_glr_06_binary_operations_001.png :class: sphx-glr-single-img .. GENERATED FROM PYTHON SOURCE LINES 52-60 Morphological Operations ======================== Morphological operations are used to change the shape (or morphology) of a binary object. The most common ones are `erosion `_ and `dilation `_. As the name suggests, **erosion** erode a binary object by removing pixels from its outer boundary. Conversely, **dilation** dilates the object by adding pixels to its outer area. .. GENERATED FROM PYTHON SOURCE LINES 60-72 .. code-block:: Python dilated = binary_dilation(binary_image) eroded = binary_erosion(binary_image) # Display original, dilated, and eroded images side by side fig, axes = plt.subplots(1, 3, figsize=(9, 3)) display_image(axes[0], binary_image, "Original Image", show_value=True) display_image(axes[1], dilated, "Dilated", show_value=True) display_image(axes[2], eroded, "Eroded", show_value=True) plt.tight_layout() plt.show() .. image-sg:: /auto_examples/images/sphx_glr_06_binary_operations_002.png :alt: Original Image, Dilated, Eroded :srcset: /auto_examples/images/sphx_glr_06_binary_operations_002.png :class: sphx-glr-single-img .. GENERATED FROM PYTHON SOURCE LINES 73-77 The above operations were done using a single pixel, which means they used a matrix of size 1 (`[[1]]`). This matrix used for morphological operations is also called a "structuring element". We can also use a structuring element of a different size, such as a 3×3 matrix, for example. .. GENERATED FROM PYTHON SOURCE LINES 77-81 .. code-block:: Python matrix_3x3 = np.ones((3,3)) print(matrix_3x3) .. rst-class:: sphx-glr-script-out .. code-block:: none [[1. 1. 1.] [1. 1. 1.] [1. 1. 1.]] .. GENERATED FROM PYTHON SOURCE LINES 82-85 Now, let's apply erosion using this **3×3 structuring element** and compare the results. .. GENERATED FROM PYTHON SOURCE LINES 85-96 .. code-block:: Python eroded_3x3 = binary_erosion(binary_image, matrix_3x3) # Compare default erosion (1x1) vs erosion with (3x3) structuring element fig, axes = plt.subplots(1, 3, figsize=(9, 3)) display_image(axes[0], binary_image, "Original Image", show_value=True) display_image(axes[1], eroded, "Eroded (1×1)", show_value=True) display_image(axes[2], eroded_3x3, "Eroded (3×3)", show_value=True) plt.tight_layout() plt.show() .. image-sg:: /auto_examples/images/sphx_glr_06_binary_operations_003.png :alt: Original Image, Eroded (1×1), Eroded (3×3) :srcset: /auto_examples/images/sphx_glr_06_binary_operations_003.png :class: sphx-glr-single-img .. GENERATED FROM PYTHON SOURCE LINES 97-99 We can also use structuring element of different shapes .. GENERATED FROM PYTHON SOURCE LINES 99-115 .. code-block:: Python square_3 = np.ones((3,3)) # Square (3x3) square_5 = np.ones((5,5)) # Square (5x5) disk_1 = disk(1) # Disk with radius 1 disk_2 = disk(2) # Disk with radius 2 # Display structuring elements fig, axes = plt.subplots(1, 4, figsize=(8,2)) display_image(axes[0], square_3, "Square (3×3)", show_value=True) display_image(axes[1], square_5, "Square (5×5)", show_value=True) display_image(axes[2], disk_1, "Disk (r=1)", show_value=True) display_image(axes[3], disk_2, "Disk (r=2)", show_value=True) plt.tight_layout() plt.show() .. image-sg:: /auto_examples/images/sphx_glr_06_binary_operations_004.png :alt: Square (3×3), Square (5×5), Disk (r=1), Disk (r=2) :srcset: /auto_examples/images/sphx_glr_06_binary_operations_004.png :class: sphx-glr-single-img .. GENERATED FROM PYTHON SOURCE LINES 116-118 Let's apply erosion on the binary image of our blobs image. .. GENERATED FROM PYTHON SOURCE LINES 118-152 .. code-block:: Python from PIL import Image # Load the blobs image blob_path = "images/blobs.jpeg" blobs = Image.open(blob_path) blobs = np.array(blobs) # Convert grayscale image to binary using thresholding # The threshold value is arbitrary (but not really, can you guess why?) blobs_bin = blobs > 180 # Apply erosion with square structuring elements (3x3, 5x5, 7x7, 9x9) fig, axes = plt.subplots(1, 5, figsize=(15,3)) plt.suptitle("Erosion with Square structuring element") display_image(axes[0], blobs_bin, "Binary Image") for i, size in enumerate([3, 5, 7, 9]): eroded_img = binary_erosion(blobs_bin, np.ones((size, size))) display_image(axes[i+1], eroded_img, f"Square ({size}×{size})") plt.tight_layout() plt.show() # Apply erosion with disk structuring elements (r=1, 2, 3, 4) fig, axes = plt.subplots(1, 5, figsize=(15,3)) plt.suptitle("Erosion with Disk structuring element") display_image(axes[0], blobs_bin, "Binary Image") for i, radius in enumerate([1, 2, 3, 4]): eroded_img = binary_erosion(blobs_bin, disk(radius)) # Apply erosion display_image(axes[i+1], eroded_img, f"disk (r={radius})") plt.tight_layout() plt.show() .. rst-class:: sphx-glr-horizontal * .. image-sg:: /auto_examples/images/sphx_glr_06_binary_operations_005.png :alt: Erosion with Square structuring element, Binary Image, Square (3×3), Square (5×5), Square (7×7), Square (9×9) :srcset: /auto_examples/images/sphx_glr_06_binary_operations_005.png :class: sphx-glr-multi-img * .. image-sg:: /auto_examples/images/sphx_glr_06_binary_operations_006.png :alt: Erosion with Disk structuring element, Binary Image, disk (r=1), disk (r=2), disk (r=3), disk (r=4) :srcset: /auto_examples/images/sphx_glr_06_binary_operations_006.png :class: sphx-glr-multi-img .. GENERATED FROM PYTHON SOURCE LINES 153-162 .. hint:: Can you think of any useful applications of erosion, dilation, or the combination of both? .. seealso:: - `Opening `_: erosion followed by dilation. - `Closing `_: dilation followed by erosion. .. GENERATED FROM PYTHON SOURCE LINES 164-173 Logical Operations ================== You may have heard about logical operations like **OR** and **AND**: - **True AND False** results in **False**. - **True OR False** results in **True**. Since binary images contain only binary values (0 and 1), we can apply these logical operations to them. .. GENERATED FROM PYTHON SOURCE LINES 173-188 .. code-block:: Python # Convert the binary image to boolean binary_image = binary_image.astype(bool) # Show different types of logical operations on the binary image fig, axes = plt.subplots(1, 6, figsize=(18, 3)) display_image(axes[0], binary_image, "Binary Image (A)") display_image(axes[1], eroded, "Eroded (B)") display_image(axes[2], binary_image | eroded, "A | B (OR)") display_image(axes[3], binary_image ^ eroded, "A ^ B (XOR)") display_image(axes[4], binary_image & eroded, "A & B (AND)") display_image(axes[5], ~binary_image, "~A (NOT)") plt.tight_layout() plt.show() .. image-sg:: /auto_examples/images/sphx_glr_06_binary_operations_007.png :alt: Binary Image (A), Eroded (B), A | B (OR), A ^ B (XOR), A & B (AND), ~A (NOT) :srcset: /auto_examples/images/sphx_glr_06_binary_operations_007.png :class: sphx-glr-single-img .. GENERATED FROM PYTHON SOURCE LINES 189-203 So essentially, logical operations on binary images work like this: - **OR (`|`)** merges both binary images. - **XOR (`^`)** keeps only the non-overlapping areas. - **AND (`&`)** keeps only the overlapping regions. - **NOT (`~`)** inverts the image. .. note:: There are different ways to perform logical operations on a binary image. The operators we used so far (`|`, `^`, `&`, `~`) are called **bitwise operators**. These work correctly for boolean values (`True`, `False`). When using integers (`0`, `1`), the result may not be as expected. In such cases, it is better to use `numpy.logical `_ operations. .. GENERATED FROM PYTHON SOURCE LINES 203-208 .. code-block:: Python print(~np.array([False, True, True, False])) print(~np.array([0, 1, 1, 0])) print(np.logical_not(np.array([0, 1, 1, 0]))) .. rst-class:: sphx-glr-script-out .. code-block:: none [ True False False True] [-1 -2 -2 -1] [ True False False True] .. GENERATED FROM PYTHON SOURCE LINES 209-211 Let's apply logical operations to the binary image of the blobs and its eroded version. .. GENERATED FROM PYTHON SOURCE LINES 211-224 .. code-block:: Python blobs_eroded = binary_erosion(blobs_bin, disk(1)) fig, axes = plt.subplots(1, 6, figsize=(18, 3)) display_image(axes[0], blobs_bin, "Binary Image (A)") display_image(axes[1], blobs_eroded, "Eroded (B)") display_image(axes[2], blobs_bin | blobs_eroded, "A | B (OR)") display_image(axes[3], blobs_bin ^ blobs_eroded, "A ^ B (XOR)") display_image(axes[4], blobs_bin & blobs_eroded, "A & B (AND)") display_image(axes[5], ~blobs_bin, "~A (NOT)") plt.tight_layout() plt.show() .. image-sg:: /auto_examples/images/sphx_glr_06_binary_operations_008.png :alt: Binary Image (A), Eroded (B), A | B (OR), A ^ B (XOR), A & B (AND), ~A (NOT) :srcset: /auto_examples/images/sphx_glr_06_binary_operations_008.png :class: sphx-glr-single-img .. GENERATED FROM PYTHON SOURCE LINES 225-234 We can see that applying **erosion** followed by the **XOR** operation gives us the boundary of the object. .. hint:: Can you think of any combination of morphological operations and logical operations that might be useful? .. seealso:: - **Subtract (A - B)**: The name explains itself, and is similar to **XOR (A ^ B)** when A >= B. - **Add (A + B)**: Equivalent to **OR (A | B)**. .. rst-class:: sphx-glr-timing **Total running time of the script:** (0 minutes 1.661 seconds) .. _sphx_glr_download_auto_examples_06_binary_operations.py: .. only:: html .. container:: sphx-glr-footer sphx-glr-footer-example .. container:: sphx-glr-download sphx-glr-download-jupyter :download:`Download Jupyter notebook: 06_binary_operations.ipynb <06_binary_operations.ipynb>` .. container:: sphx-glr-download sphx-glr-download-python :download:`Download Python source code: 06_binary_operations.py <06_binary_operations.py>` .. container:: sphx-glr-download sphx-glr-download-zip :download:`Download zipped: 06_binary_operations.zip <06_binary_operations.zip>` .. only:: html .. rst-class:: sphx-glr-signature `Gallery generated by Sphinx-Gallery `_