-
-
Notifications
You must be signed in to change notification settings - Fork 228
Add medical image denoising demo for anisotropic diffusion #3997
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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:
|
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.