Differentiate Module#

In this example, we demonstrate how to use the differentiate module to compute different differential operations on an image.

Imports#

import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.axes_grid1 import make_axes_locatable
import requests
from io import BytesIO
from PIL import Image

# Import the Differentials class from your module (adjust import path as needed)
from splineops.differentiate.differentials import differentials

Data Preparation#

We retrieve an example color image, convert it to grayscale, and normalize its intensities to the [0,1] range.

url = 'https://r0k.us/graphics/kodak/kodak/kodim15.png'
response = requests.get(url)
img = Image.open(BytesIO(response.content))
image = np.array(img, dtype=np.float64)

# Convert to [0,1]
image_normalized = image / 255.0

# Convert to grayscale via simple weighting
image_gray = (
    image_normalized[:, :, 0] * 0.2989 +
    image_normalized[:, :, 1] * 0.5870 +
    image_normalized[:, :, 2] * 0.1140
)

Helper Visualization Functions#

def show_result_with_colorbar(title, result, units="Value", percentile_range=(5, 95)):
    """
    Displays a 2D result with a colorbar scaled using the given percentile range.

    Parameters
    ----------
    title : str
        Title for the plot.
    result : ndarray
        2D array representing the image or field to display.
    units : str
        Label for the colorbar (e.g., 'Intensity', 'Radians', etc.).
    percentile_range : tuple or None
        Percentiles to use for scaling the colormap. If None, use the min and max of the data.
    """
    h, w = result.shape
    aspect_ratio = h / float(w)
    fig_width = 6.0
    fig_height = fig_width * aspect_ratio
    fig, ax = plt.subplots(figsize=(fig_width, fig_height))

    # Determine vmin and vmax based on percentiles if provided
    if percentile_range is not None:
        pmin, pmax = np.percentile(result, percentile_range)
        im = ax.imshow(result, cmap='gray', aspect='equal', vmin=pmin, vmax=pmax)
        cbar_label = f"{units} range [{pmin:.3f}, {pmax:.3f}]"
    else:
        vmin, vmax = result.min(), result.max()
        im = ax.imshow(result, cmap='gray', aspect='equal', vmin=vmin, vmax=vmax)
        cbar_label = f"{units} range [{vmin:.3f}, {vmax:.3f}]"

    ax.set_title(title)
    ax.axis('off')

    # Create a colorbar with matching height using make_axes_locatable
    divider = make_axes_locatable(ax)
    cax = divider.append_axes("right", size="5%", pad=0.05)
    cbar = plt.colorbar(im, cax=cax)
    cbar.set_label(cbar_label)

    plt.tight_layout()
    plt.show()


def show_angle_result(title, angle_data, vmin, vmax, units="Radians"):
    """
    Displays angle data in a cyclical color map (hsv), with vmin and vmax specifying
    the circular range.

    Parameters
    ----------
    title : str
        Title for the plot.
    angle_data : ndarray
        2D array of angles (in radians).
    vmin : float
        Minimum of angle range (e.g., 0).
    vmax : float
        Maximum of angle range (e.g., 2*pi).
    units : str
        Label for the colorbar (e.g., 'Direction (radians)', etc.).
    """
    h, w = angle_data.shape
    aspect_ratio = h / float(w)
    fig_width = 6.0
    fig_height = fig_width * aspect_ratio
    fig, ax = plt.subplots(figsize=(fig_width, fig_height))

    # Plot with an HSV cyclical colormap
    im = ax.imshow(angle_data, cmap='hsv', aspect='equal', vmin=vmin, vmax=vmax)
    ax.set_title(title)
    ax.axis('off')

    # Add colorbar
    divider = make_axes_locatable(ax)
    cax = divider.append_axes("right", size="5%", pad=0.05)
    cbar = plt.colorbar(im, cax=cax, ticks=[vmin, (vmin+vmax)/2, vmax])
    cbar.set_label(f"{units} range [{vmin:.2f}, {vmax:.2f}]")

    plt.tight_layout()
    plt.show()

Show the original grayscale image with a colorbar

show_result_with_colorbar("Original Image", image_gray, units="Intensity")
Original Image

Gradient Magnitude#

diff = differentials(image_gray.copy())
diff.run(differentials.GRADIENT_MAGNITUDE)
grad_magnitude_result = diff.image

show_result_with_colorbar("Gradient Magnitude", grad_magnitude_result, units="Value")
Gradient Magnitude
Completed in 1.16 seconds

Gradient Direction#

diff = differentials(image_gray.copy())
diff.run(differentials.GRADIENT_DIRECTION)
grad_direction_result = diff.image

# Shift from [-π, π] to [0, 2π]
grad_direction_result_0_2pi = (grad_direction_result + 2.0*np.pi) % (2.0*np.pi)

# Visualize with HSV colormap, removing percentile clipping
show_angle_result(
    "Gradient Direction",
    grad_direction_result_0_2pi,
    vmin=0.0, vmax=2.0*np.pi,
    units="Direction (radians)"
)
Gradient Direction
Completed in 1.14 seconds

Laplacian#

diff = differentials(image_gray.copy())
diff.run(differentials.LAPLACIAN)
laplacian_result = diff.image

show_result_with_colorbar("Laplacian", laplacian_result, units="Value")
Laplacian
Completed in 1.35 seconds

Largest Hessian#

diff = differentials(image_gray.copy())
diff.run(differentials.LARGEST_HESSIAN)
largest_hessian_result = diff.image

show_result_with_colorbar("Largest Hessian Eigenvalue", largest_hessian_result, units="Value")
Largest Hessian Eigenvalue
Completed in 2.52 seconds

Smallest Hessian#

diff = differentials(image_gray.copy())
diff.run(differentials.SMALLEST_HESSIAN)
smallest_hessian_result = diff.image

show_result_with_colorbar("Smallest Hessian Eigenvalue", smallest_hessian_result, units="Value")
Smallest Hessian Eigenvalue
Completed in 2.45 seconds

Hessian Orientation#

diff = differentials(image_gray.copy())
diff.run(differentials.HESSIAN_ORIENTATION)
hessian_orientation_result = diff.image

show_angle_result(
    "Hessian Orientation",
    hessian_orientation_result,
    vmin=-np.pi/2.0, vmax=np.pi/2.0,
    units="Orientation (radians)"
)
Hessian Orientation
Completed in 2.50 seconds

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

Gallery generated by Sphinx-Gallery