Add medical image denoising demo for anisotropic diffusion#3997
Add medical image denoising demo for anisotropic diffusion#3997m-wisell wants to merge 3 commits intoFEniCS:mainfrom
Conversation
|
Updated to fix CI failures. All tests now passing locally. Ready for review. |
| def function_to_image(u, image_shape): | ||
| height, width = image_shape | ||
|
|
||
| mesh_obj = u.function_space.mesh | ||
| coords = mesh_obj.geometry.x[:, :2] | ||
| values = u.x.array | ||
|
|
||
| x_pixels = np.linspace(0, width - 1, width) | ||
| y_pixels = np.linspace(0, height - 1, height) | ||
| X, Y = np.meshgrid(x_pixels, y_pixels) | ||
|
|
||
| Y_flipped = height - 1 - Y | ||
|
|
||
| image = griddata( | ||
| coords, | ||
| values, | ||
| (X, Y_flipped), | ||
| method='linear', | ||
| fill_value=0.0 | ||
| ) | ||
|
|
||
| image = np.nan_to_num(image, nan=0.0) | ||
| return image |
There was a problem hiding this comment.
This function will only work in serial, no?
| def image_to_function(image, V): | ||
| u = fem.Function(V) | ||
| height, width = image.shape | ||
|
|
||
| def image_values(x): | ||
| values = np.zeros(x.shape[1]) | ||
|
|
||
| for i in range(x.shape[1]): | ||
| x_coord = x[0, i] | ||
| y_coord = x[1, i] | ||
|
|
||
| px = int(np.clip(np.round(x_coord), 0, width - 1)) | ||
| py = int(np.clip(np.round(height - 1 - y_coord), 0, height - 1)) | ||
|
|
||
| values[i] = image[py, px] | ||
|
|
||
| return values | ||
|
|
||
| u.interpolate(image_values) | ||
| return u |
There was a problem hiding this comment.
Could you vectorize this? Something along the lines of:
from mpi4py import MPI
import dolfinx
import numpy as np
width = 3
height = 2
mesh = dolfinx.mesh.create_rectangle(MPI.COMM_WORLD, [[0,0],[width, height]], [int(width), int(height)])
V = dolfinx.fem.functionspace(mesh, ("DG", 0))
u = dolfinx.fem.Function(V)
a = np.linspace(0.5, width-0.5, width, endpoint=True)
b = np.linspace(0.5, height-0.5, height, endpoint=True)
mg = np.meshgrid(a, b)
def f(x, y):
return x**2 + y**2
z = f(*mg)
def image_values(x):
values = np.zeros(x.shape[1])
for i in range(x.shape[1]):
x_coord = x[0, i]
y_coord = x[1, i]
px = int(np.clip(np.round(x_coord), 0, width - 1))
py = int(np.clip(np.round(height - y_coord), 0, height - 1))
values[i] = z[py,px]
return values
def images_values_vectorized(x):
px = np.clip(np.round(x[0]), 0, width - 1).astype(np.int64)
py = np.clip(np.round(height - x[1]), 0, height-1).astype(np.int64)
return z[py, px]
u.interpolate(image_values)
u2 = dolfinx.fem.Function(V)
u2.interpolate(images_values_vectorized)
np.testing.assert_allclose(u.x.array, u2.x.array)| return u_new | ||
|
|
||
| def denoise(self, image, n_iterations=30, verbose=True): | ||
| self.u_n = image_to_function(image, self.V) |
There was a problem hiding this comment.
As your mesh aligns with the image grid, using interpolation to move data from a voxel grid (DG-0) to a P1 Lagrange space is not well defined. I would suggest that you for the first time step use u_n in DG-0, and then after solving, replace this function with a P1 function.
|
|
||
| return a, L | ||
|
|
||
| def solve_timestep(self, a, L): |
There was a problem hiding this comment.
This function needs to be fully rewritten.
- you call
fem.formrepeatedly, even if the form does not change over time. (with my suggestion above, it would change once, but it wouldn't modify the sparsity pattern of point 2, thus those structures can be created once) - The same holds for the matrix and vector. They should only be created once.
- As you are not using any advanced PETSc features here, why not use
dolfinx.fem.petsc.LinearProblem. (even if one changes the form once, by overwritingLinearProblem._aandLinearProblem._bwith the form switching from DG-0 to P-1 for u_n)
|
I'm cautiously positive about the underlying idea for this demo (showing the use of a PDE solve on a problem from outside mechanics), but the execution is totally different to our existing demos:
|
|
This is a nice example, but a bit heavyweight for our demo suite. And the more dependencies we have the more often we have test system problems. Happy to discuss how we can highlight this (and other) interesting applications examples. |
Summary
This PR adds medical image denoising using anisotropic diffusion.
Addresses: Issue #3973
Implementation
Mathematical Model:
Perona-Malik anisotropic diffusion for edge-preserving denoising
Numerical Methods:
Features:
What Changed (Latest Update)
Quality Metrics (Real Mammogram 512x512)
Usage
Ready for review. CI check should now pass.