
.. DO NOT EDIT.
.. THIS FILE WAS AUTOMATICALLY GENERATED BY SPHINX-GALLERY.
.. TO MAKE CHANGES, EDIT THE SOURCE PYTHON FILE:
.. "_auto_examples/plot_active_contours.py"
.. LINE NUMBERS ARE GIVEN BELOW.

.. only:: html

    .. note::
        :class: sphx-glr-download-link-note

        :ref:`Go to the end <sphx_glr_download__auto_examples_plot_active_contours.py>`
        to download the full example code.

.. rst-class:: sphx-glr-example-title

.. _sphx_glr__auto_examples_plot_active_contours.py:


Active Contours
===============

This example demonstrates a basic active contours (snakes) implementation using splinebox. The goal is to segment the astronaut's head in an image by iteratively refining a spline contour.

.. GENERATED FROM PYTHON SOURCE LINES 7-17

.. code-block:: Python



    import matplotlib
    import matplotlib.pyplot as plt
    import numpy as np
    import scipy
    import skimage
    import splinebox.basis_functions
    import splinebox.spline_curves








.. GENERATED FROM PYTHON SOURCE LINES 19-20

Let's load the astronaut example image from skimage.

.. GENERATED FROM PYTHON SOURCE LINES 20-25

.. code-block:: Python

    img = skimage.data.astronaut()

    plt.imshow(img)
    plt.show()




.. image-sg:: /_auto_examples/images/sphx_glr_plot_active_contours_001.png
   :alt: plot active contours
   :srcset: /_auto_examples/images/sphx_glr_plot_active_contours_001.png
   :class: sphx-glr-single-img





.. GENERATED FROM PYTHON SOURCE LINES 26-28

To make the contour stick to the edges of the image, we compute an edge map.
First, convert the image to grayscale, apply Gaussian smoothing to reduce noise, and then apply the Sobel filter for edge detection.

.. GENERATED FROM PYTHON SOURCE LINES 28-36

.. code-block:: Python

    gray = skimage.color.rgb2gray(img)
    smooth = skimage.filters.gaussian(gray, 3, preserve_range=False)
    edge = skimage.filters.sobel(smooth)

    plt.imshow(edge)
    plt.show()





.. image-sg:: /_auto_examples/images/sphx_glr_plot_active_contours_002.png
   :alt: plot active contours
   :srcset: /_auto_examples/images/sphx_glr_plot_active_contours_002.png
   :class: sphx-glr-single-img





.. GENERATED FROM PYTHON SOURCE LINES 37-39

To make calculating the edge energy at non-integer locations easier, we use a surface spline on a regular grid.
This returns a callable object that can be queried for edge energy values.

.. GENERATED FROM PYTHON SOURCE LINES 39-44

.. code-block:: Python

    edge_energy = scipy.interpolate.RectBivariateSpline(
        np.arange(edge.shape[1]), np.arange(edge.shape[0]), edge, kx=2, ky=2, s=1
    )









.. GENERATED FROM PYTHON SOURCE LINES 45-47

We define an internal energy function to regularize the curvature of the spline.
The internal energy depends on the first and second derivatives of the spline.

.. GENERATED FROM PYTHON SOURCE LINES 47-51

.. code-block:: Python

    def internal_energy(spline, t, alpha, beta):
        return 0.5 * (alpha * spline(t, derivative=1) ** 2 + beta * spline(t, derivative=2) ** 2)









.. GENERATED FROM PYTHON SOURCE LINES 52-53

Initialize the spline with 30 knots that form a circle around the astronaut's head.

.. GENERATED FROM PYTHON SOURCE LINES 53-59

.. code-block:: Python

    M = 30
    s = np.linspace(0, 2 * np.pi, M + 1)[:-1]
    y = 100 + 100 * np.sin(s)
    x = 220 + 100 * np.cos(s)
    knots = np.array([y, x]).T








.. GENERATED FROM PYTHON SOURCE LINES 60-61

We keep a copy of the initial knots so we can plot them later.

.. GENERATED FROM PYTHON SOURCE LINES 61-63

.. code-block:: Python

    initial_knots = knots.copy()








.. GENERATED FROM PYTHON SOURCE LINES 64-66

Now, we can construct a B3 spline and set its control points (coefficients)
using the knots we generated above.

.. GENERATED FROM PYTHON SOURCE LINES 66-77

.. code-block:: Python

    spline = splinebox.spline_curves.Spline(M=M, basis_function=splinebox.basis_functions.B3(), closed=True)
    spline.knots = knots

    t = np.linspace(0, M, 400)
    contour = spline(t)
    plt.imshow(img)
    plt.scatter(knots[:, 1], knots[:, 0])
    plt.plot(contour[:, 1], contour[:, 0])
    plt.show()





.. image-sg:: /_auto_examples/images/sphx_glr_plot_active_contours_003.png
   :alt: plot active contours
   :srcset: /_auto_examples/images/sphx_glr_plot_active_contours_003.png
   :class: sphx-glr-single-img





.. GENERATED FROM PYTHON SOURCE LINES 78-82

Here, we set the necessary paramters for the active contours algorithm:

* :math:`\alpha` controls the contribution of the first derivative (smoothness) to the internal energy
* :math:`\beta` controls the contribution of the second derivative (curvature) to the internal energy

.. GENERATED FROM PYTHON SOURCE LINES 82-91

.. code-block:: Python


    alpha = 0
    beta = 0.001

    # Store intermediate contours
    contours = []
    # Store the energy values
    external_energies = []








.. GENERATED FROM PYTHON SOURCE LINES 92-94

Nex, we define the energy function to be minimized. It combines the external energy from the edge map
and the internal energy based on spline smoothness and curvature.

.. GENERATED FROM PYTHON SOURCE LINES 94-113

.. code-block:: Python



    def energy_function(control_points, spline, t, alpha, beta):
        control_points = control_points.reshape((spline.M, -1))
        spline.control_points = control_points
        contour = spline(t)
        contours.append(contour.copy())

        # Compute external energy from the edge map
        edge_energy_value = np.sum(edge_energy(contour[:, 0], contour[:, 1], grid=False))
        external_energies.append(-edge_energy_value)

        # Compute internal energy
        internal_energy_value = np.sum(internal_energy(spline, t, alpha, beta))

        # Total energy to minimize
        return -edge_energy_value + internal_energy_value









.. GENERATED FROM PYTHON SOURCE LINES 114-116

The active contours approach consists of iteratively updating our control points (coefficients)
to minimize the energy and find the optimal contour.

.. GENERATED FROM PYTHON SOURCE LINES 116-121

.. code-block:: Python

    initial_control_points = spline.control_points.copy()
    result = scipy.optimize.minimize(
        energy_function, initial_control_points.flatten(), method="Powell", args=(spline, t, alpha, beta)
    )








.. GENERATED FROM PYTHON SOURCE LINES 122-124

Inorder to plot the spline as a smooth line, we have to evaluate it
more densly than just at the knots.

.. GENERATED FROM PYTHON SOURCE LINES 124-128

.. code-block:: Python

    samples = spline(t)

    final_knots = spline(np.arange(M))








.. GENERATED FROM PYTHON SOURCE LINES 129-130

Finaly, we can plot the result.

.. GENERATED FROM PYTHON SOURCE LINES 130-140

.. code-block:: Python

    plt.imshow(img)
    plt.scatter(initial_knots[:, 1], initial_knots[:, 0], marker="x", color="black", label="initial knots")
    contours = contours[::2000]
    colors = matplotlib.colormaps["viridis"](np.linspace(0, 1, len(contours)))
    for contour, color in zip(contours, colors):
        plt.plot(contour[:, 1], contour[:, 0], color=color, alpha=0.2)
    plt.scatter(final_knots[:, 1], final_knots[:, 0], marker="o", label="final final_knots")
    plt.plot(samples[:, 1], samples[:, 0], label="spline")
    plt.legend()
    plt.show()



.. image-sg:: /_auto_examples/images/sphx_glr_plot_active_contours_004.png
   :alt: plot active contours
   :srcset: /_auto_examples/images/sphx_glr_plot_active_contours_004.png
   :class: sphx-glr-single-img






.. rst-class:: sphx-glr-timing

   **Total running time of the script:** (0 minutes 10.643 seconds)


.. _sphx_glr_download__auto_examples_plot_active_contours.py:

.. only:: html

  .. container:: sphx-glr-footer sphx-glr-footer-example

    .. container:: sphx-glr-download sphx-glr-download-jupyter

      :download:`Download Jupyter notebook: plot_active_contours.ipynb <plot_active_contours.ipynb>`

    .. container:: sphx-glr-download sphx-glr-download-python

      :download:`Download Python source code: plot_active_contours.py <plot_active_contours.py>`

    .. container:: sphx-glr-download sphx-glr-download-zip

      :download:`Download zipped: plot_active_contours.zip <plot_active_contours.zip>`


.. only:: html

 .. rst-class:: sphx-glr-signature

    `Gallery generated by Sphinx-Gallery <https://sphinx-gallery.github.io>`_
