Derivative Operation¶
Here we show you examples of derivative/laplacian matrices and how they work on the example image.
Please see the derivative theory page if you want to know the details of the theory.
[1]:
import numpy as np
import ultraplot as uplt
from matplotlib import pyplot as plt
from matplotlib.cbook import get_sample_data
from PIL import Image
from cherab.inversion.tools import derivative_matrix, laplacian_matrix
Visualize derivative matrix¶
Let us compute the simple derivative matrix applying a 3 x 3 image array.
Firstly we show derivative matrices along to vertical direction (axis=0 direction) with different schemes. The derivative matrices are shown by plotting the matrix elements in 2-D image.
[2]:
fig, axes = uplt.subplots(ncols=3)
for ax, scheme in zip(axes, ["forward", "backward", "central"], strict=True):
# compute derivative matrix
dmat = derivative_matrix((3, 3), axis=0, scheme=scheme).toarray()
dmat[dmat == 0] = np.nan
# plot derivative matrix
ax.heatmap(
dmat,
cmap="ColdHot",
vmin=-1,
vmax=1,
lw=0.1,
ec="k",
labels=True,
precision=2,
clip_on=False,
)
ax.format(title=scheme)
axes.format(
xloc="top",
yloc="left",
yreverse=True,
ticklabelweight="bold",
linewidth=0,
tickpad=4,
)
Also we show the horizontal derivative matrix (axis=1 direction) case.
[3]:
fig, axes = uplt.subplots(ncols=3)
for ax, scheme in zip(axes, ["forward", "backward", "central"], strict=True):
# compute derivative matrix
dmat = derivative_matrix((3, 3), axis=1, scheme=scheme).toarray()
dmat[dmat == 0] = np.nan
# plot derivative matrix
ax.heatmap(
dmat,
cmap="ColdHot",
vmin=-1,
vmax=1,
lw=0.1,
ec="k",
labels=True,
precision=2,
clip_on=False,
)
ax.format(title=scheme)
axes.format(
xloc="top",
yloc="left",
yreverse=True,
ticklabelweight="bold",
linewidth=0,
tickpad=4,
)
We can see that some elements are zero due to the boundary condition.
Visualize laplacian matrix¶
Let us compute the simple laplacian matrix applying a 3 x 3 image array and show it by printing the matrix elements and plotting the matrix elements in 2-D image. There are two types of laplacian matrix: difference between only orthogonal neighbors or all neighbors including diagonal neighbors. The step size along diagonal direction is \(\sqrt{2}h\).
[4]:
fig, axes = uplt.subplots(ncols=2)
for ax, diagonal in zip(axes, [False, True], strict=True):
# compute laplacian matrix
lmat = laplacian_matrix((3, 3), diagonal=diagonal).toarray()
lmat[lmat == 0] = np.nan
# plot laplacian matrix
ax.heatmap(
lmat,
cmap="ColdHot",
vmin=-6,
vmax=1,
lw=0.1,
ec="k",
labels=True,
precision=2,
clip_on=False,
)
title = "diagonal" if diagonal else "no diagonal"
ax.format(title=title)
axes.format(
xloc="top",
yloc="left",
yreverse=True,
ticklabelweight="bold",
linewidth=0,
tickpad=4,
)
Apply the derivative/laplacian matrix to a sample image¶
Next, let us to apply derivative/laplacian matrices to pixels of a sample image. We use sample image stored in matplotlib library.
Let us preview the sample image converted to monochrome image.
[5]:
with get_sample_data("grace_hopper.jpg") as file:
arr_image = plt.imread(file)
# resize the image to 1/4 of the original size
with Image.fromarray(arr_image, mode="RGB") as im:
(width, height) = (im.width // 4, im.height // 4)
arr_image = np.array(im.resize((width, height)))
# convert RGB image to monotonic one
arr_image = arr_image.mean(axis=2)
# show image
print(f"image array shape: {arr_image.shape}")
fig, ax = uplt.subplots()
ax.imshow(arr_image, cmap="gray")
ax.format(
xlabel="$x$",
ylabel="$y$",
)
image array shape: (150, 128)
Then create the derivative/laplacian matrices. Note that the \(x\), \(y\) directions correspond to the 1, 0 axis of the image array, respectively, and the positive direction of the \(y\) axis is downward.
[6]:
# compute each derivative matrix along each axis with forward scheme
dmat_x = derivative_matrix(arr_image.shape, axis=1, scheme="forward")
dmat_y = derivative_matrix(arr_image.shape, axis=0, scheme="forward")
# compute derivative matrices along y direction with different schemes
dmat_y_backward = derivative_matrix(arr_image.shape, axis=0, scheme="backward")
dmat_y_central = derivative_matrix(arr_image.shape, axis=0, scheme="central")
# compute laplacian matrices with and without diagonal
lmat_diag = laplacian_matrix(arr_image.shape, diagonal=True)
lmat_no_diag = laplacian_matrix(arr_image.shape, diagonal=False)
Check the difference effect of the derivative matrix between \(x\) and \(y\) directions¶
[7]:
arr_derivatives = []
for dmat in [dmat_x, dmat_y]:
arr_derivative = dmat @ arr_image.flatten()
arr_derivatives.append(arr_derivative.reshape(arr_image.shape))
fig, axes = uplt.subplots(ncols=2, spanx=False)
for ax, arr, title in zip(axes, arr_derivatives, ["$x$", "$y$"], strict=True):
im = ax.imshow(arr, colorbar="b")
ax.format(title=f"{title} direction")
axes.format(
xlabel="$x$",
ylabel="$y$",
)
We can see that each derivative image varies depending on each image’s gradient direction.
Check the difference derivative scheme (forward, backward, central)¶
Let us check the difference between derivative schemes in the y direction.
[8]:
arr_derivatives = []
for dmat in [dmat_y, dmat_y_backward, dmat_y_central]:
arr_derivative = dmat @ arr_image.flatten()
arr_derivatives.append(arr_derivative.reshape(arr_image.shape))
fig, axes = uplt.subplots(ncols=3, spanx=False)
for ax, arr, title in zip(
axes,
arr_derivatives,
["forward", "backward", "central"],
strict=True,
):
im = ax.imshow(arr, colorbar="b")
ax.format(title=f"{title} direction")
axes.format(
xlabel="$x$",
ylabel="$y$",
)
There is no apparent difference between the derivative schemes. The difference seems be more apparent when the gradient is large or the area near the boundary because each scheme has different boundary condition.
Check the difference effect of the laplacian matrix w/ or w/o diagonal neighbors¶
[9]:
fig, axes = uplt.subplots(ncols=2, spanx=False)
for ax, lmat, title in zip(
axes,
[lmat_no_diag, lmat_diag],
["w/o diagonal", "w/ diagonal"],
strict=True,
):
# compute laplacian of image
arr_laplacian = lmat @ arr_image.flatten()
arr_laplacian = arr_laplacian.reshape(arr_image.shape)
# plot laplacian of image
im = ax.imshow(
arr_laplacian,
colorbar="b",
)
ax.format(title=title)
axes.format(
xlabel="$x$",
ylabel="$y$",
)
Both laplacian matrices can detect the edge of the image. The laplacian with diagonal neighbors generates larger values than that without diagonal neighbors.
In image processing, the both derivative and laplacian matrices are used for edge detection. In the inversion problem, specific to the tomography, these matrices make a reconstructed image smoother by adding the derivative/laplacian regularization term to the objective function.