Tdanyim/Kspace_App
0
1import gradio as gr2import pydicom3import numpy as np4import matplotlib.pyplot as plt5 6# Image to K-space Conversion via FFT Functions & Vice-versa7def fft2c(image):8 """ Computes the centered 2D FFT of an image in NumPy. """9 return np.fft.fftshift(np.fft.fft2(np.fft.ifftshift(image, axes=(-2, -1))), axes=(-2, -1))10 11def ifft2c(kspace):12 """ Computes the centered 2D IFFT of k-space data using NumPy. """13 return np.fft.ifftshift(np.fft.ifft2(np.fft.fftshift(kspace, axes=(-2, -1))), axes=(-2, -1))14 15# Function to process the DICOM file16def dicomMatrix(path, apply_rescale=True):17 ds = pydicom.dcmread(path)18 arr = ds.pixel_array.astype(np.float32) # 2D for single-slice images19 20 # Apply HU/intensity rescale if present (common for CT)21 if apply_rescale and hasattr(ds, "RescaleSlope") and hasattr(ds, "RescaleIntercept"):22 arr = arr * float(ds.RescaleSlope) + float(ds.RescaleIntercept)23 return arr, ds24 25# Function to display the DICOM image26def show_dicom_image(dicom_file):27 # Load and process the DICOM file28 M, ds = dicomMatrix(dicom_file.name)29 30 # Display the DICOM image31 plt.figure(figsize=(8, 8))32 plt.imshow(M, cmap='gray')33 plt.title("DICOM Image")34 plt.axis("off")35 36 # Return the image plot as output37 return plt38 39# Function to create the low-pass filter40def createLowPass(shape, cutoff_radius):41 """ Create a 2D low-pass filter mask based on the cutoff radius. """42 y, x = np.indices(shape)43 center = np.array(shape) // 244 dist_sq = (x - center[1])**2 + (y - center[0])**245 lowPassFilter = np.where(dist_sq <= cutoff_radius**2, 1, 0)46 return lowPassFilter47 48# Low-pass filter function49def lowFilter(MRIsam, cutoff_radius):50 """Computes the 2D FFT, applies a low-pass filter, and reconstructs the image."""51 lowPassFilter = createLowPass(MRIsam.shape, cutoff_radius)52 kspMRI = fft2c(MRIsam)53 kspMRI_L = kspMRI * lowPassFilter54 spaMRI_L = np.abs(ifft2c(kspMRI_L))55 return kspMRI, kspMRI_L, spaMRI_L56 57# High-pass filter function58def highFilter(MRIsam, cutoff_radius):59 """Computes the 2D FFT, applies a high-pass filter, and reconstructs the image."""60 lowPassFilter = createLowPass(MRIsam.shape, cutoff_radius)61 highPassFilter = 1 - lowPassFilter62 kspMRI = fft2c(MRIsam)63 kspMRI_H = kspMRI * highPassFilter64 spaMRI_H = np.abs(ifft2c(kspMRI_H))65 return kspMRI, kspMRI_H, spaMRI_H66 67# Function to apply the undersampling mask to the k-space data68def undersample(kspace: np.ndarray, factor: int, compress: bool):69 """ Skipping every nth kspace line, starting from the midline.70 Simulates acquiring every nth (where n is the acceleration factor) line71 of kspace, common in SENSE algorithm.72 73 Parameters:74 kspace: Complex k-space numpy.ndarray75 factor: Only scan every nth line (n=factor) starting from midline76 compress: compress kspace by removing empty lines (rectangular FOV)77 """78 if factor > 1:79 mask = np.ones(kspace.shape, dtype=bool)80 midline = kspace.shape[0] // 281 mask[midline::factor] = 082 mask[midline::-factor] = 083 84 if compress:85 # Remove empty lines and compress86 q = kspace[~mask]87 q = q.reshape(q.size // kspace.shape[1], kspace.shape[1])88 kspace[:] = q[:]89 else:90 kspace[mask] = 091 return kspace92 93# Function to convert to k-space and apply undersampling94def undersampling_function(dicom_file, undersampling_factor, compress=False):95 # Load and process the DICOM file96 M, ds = dicomMatrix(dicom_file.name)97 98 # Perform K-space conversion99 kspMRI = fft2c(M)100 101 # Apply undersampling102 kspMRI_us = undersample(kspMRI.copy(), undersampling_factor, compress)103 spaMRI_us = np.abs(ifft2c(kspMRI_us))104 105 # Plot the original k-space, image, undersampled k-space, and undersampled image106 fig, axs = plt.subplots(1, 4, figsize=(20, 6))107 108 # Original Image109 axs[0].imshow(M, cmap='gray')110 axs[0].set_title("Original Image")111 axs[0].axis("off")112 113 # Original K-space (Magnitude)114 axs[1].imshow(np.log(np.abs(kspMRI) + 1e-9), cmap='gray')115 axs[1].set_title("Original K-space")116 axs[1].axis("off")117 118 # Undersampled K-space (Magnitude)119 axs[2].imshow(np.log(np.abs(kspMRI_us) + 1e-9), cmap='gray')120 axs[2].set_title(f"Undersampled K-space (R={undersampling_factor})")121 axs[2].axis("off")122 123 # Reconstructed Image from Undersampled K-space124 axs[3].imshow(spaMRI_us, cmap='gray')125 axs[3].set_title("Undersampled Image")126 axs[3].axis("off")127 128 plt.tight_layout()129 return fig130 131# Function to convert to k-space and plot the K-space visualization132def convert_to_kspace_and_plot(dicom_file):133 # Load and process the DICOM file134 M, ds = dicomMatrix(dicom_file.name)135 136 # Perform the K-space conversion137 kspMRI = fft2c(M)138 spaMRI = np.abs(ifft2c(kspMRI))139 140 # Plot original image, k-space, and reconstructed image141 fig, axs = plt.subplots(1, 3, figsize=(20, 6))142 143 # Original Image144 axs[0].imshow(M, cmap='gray')145 axs[0].set_title("Original Image")146 axs[0].axis("off")147 148 # K-space (Magnitude)149 axs[1].imshow(np.log(np.abs(kspMRI) + 1e-9), cmap='gray')150 axs[1].set_title("K-space (Original)")151 axs[1].axis("off")152 153 # Reconstructed Image from K-space154 axs[2].imshow(spaMRI, cmap='gray')155 axs[2].set_title("Reconstructed Image")156 axs[2].axis("off")157 158 plt.tight_layout()159 return fig160 161# Function to apply the filter (low-pass or high-pass) to K-space and plot162def convert_to_kspace_with_filter(dicom_file, filter_type, cutoff_radius):163 # Load and process the DICOM file164 M, ds = dicomMatrix(dicom_file.name)165 166 # Apply either low-pass or high-pass filtering based on user selection167 if filter_type == "Low-pass":168 kspMRI, kspMRI_L, spaMRI_L = lowFilter(M, cutoff_radius)169 elif filter_type == "High-pass":170 kspMRI, kspMRI_H, spaMRI_H = highFilter(M, cutoff_radius)171 172 # Plot original image, k-space, filtered k-space, and reconstructed image173 fig, axs = plt.subplots(1, 4, figsize=(20, 6))174 175 # Original Image176 axs[0].imshow(M, cmap='gray')177 axs[0].set_title("Original Image")178 axs[0].axis("off")179 180 # Original K-space (Magnitude)181 axs[1].imshow(np.log(np.abs(kspMRI) + 1e-9), cmap='gray')182 axs[1].set_title("K-space (Original)")183 axs[1].axis("off")184 185 # Filtered K-space (Magnitude)186 if filter_type == "Low-pass":187 axs[2].imshow(np.log(np.abs(kspMRI_L) + 1e-9), cmap='gray')188 axs[2].set_title(f"K-space ({filter_type} Filtered)")189 elif filter_type == "High-pass":190 axs[2].imshow(np.log(np.abs(kspMRI_H) + 1e-9), cmap='gray')191 axs[2].set_title(f"K-space ({filter_type} Filtered)")192 axs[2].axis("off")193 194 # Reconstructed Image from Filtered K-space195 if filter_type == "Low-pass":196 axs[3].imshow(spaMRI_L, cmap='gray')197 axs[3].set_title(f"Image ({filter_type} Reconstructed)")198 elif filter_type == "High-pass":199 axs[3].imshow(spaMRI_H, cmap='gray')200 axs[3].set_title(f"Image ({filter_type} Reconstructed)")201 axs[3].axis("off")202 203 plt.tight_layout()204 return fig205 206# Build the Gradio interface with improved aesthetics and navigation207def build_dicom_interface():208 with gr.Blocks() as demo:209 gr.Markdown("""210 # ๐ง MRI Processing Tool ๐งโ๐ฌ211 Welcome to the MRI processing interface! 212 Use the tabs below to upload a DICOM file and explore different MRI processing features:213 - **MRI Visualization** ๐ท214 - **K-space Conversion** โ๏ธ215 - **Filtering** ๐งฐ216 - **Undersampling** ๐ฉป217 """)218 219 with gr.Tab("MRI Visualization ๐ท"):220 with gr.Row():221 dicom_input = gr.File(label="Upload DICOM File")222 example_button = gr.Button("๐ก Use Example DICOM File", variant="primary")223 224 with gr.Row():225 show_image_button = gr.Button("Show Image")226 227 with gr.Row():228 image_output = gr.Plot(label="DICOM Image Visualization")229 230 example_button.click(fn=lambda: "13660", inputs=[], outputs=[dicom_input])231 show_image_button.click(fn=show_dicom_image, inputs=dicom_input, outputs=image_output)232 233 with gr.Tab("K-space Conversion โ๏ธ"):234 with gr.Row():235 view_kspace_button = gr.Button("View K-space Conversion")236 237 with gr.Row():238 kspace_output = gr.Plot(label="K-space and Reconstructed Image")239 240 view_kspace_button.click(fn=convert_to_kspace_and_plot, inputs=dicom_input, outputs=kspace_output)241 242 with gr.Tab("Filtering ๐งฐ"):243 with gr.Row():244 filter_type = gr.Radio(["Low-pass", "High-pass"], value="Low-pass", label="Select Filter Type")245 cutoff_radius_slider = gr.Slider(minimum=0, maximum=50, step=1, value=10, label="Cut-off Radius")246 247 with gr.Row():248 convert_button = gr.Button("Convert to K-space with Filtering")249 250 with gr.Row():251 filter_output = gr.Plot(label="Filtered K-space and Reconstructed Image")252 253 convert_button.click(fn=convert_to_kspace_with_filter, inputs=[dicom_input, filter_type, cutoff_radius_slider], outputs=filter_output)254 255 with gr.Tab("Undersampling ๐ฉป"):256 with gr.Row():257 undersampling_factor_slider = gr.Slider(minimum=1, maximum=10, step=1, value=2, label="Undersampling Factor")258 259 with gr.Row():260 undersample_button = gr.Button("Apply Undersampling")261 262 with gr.Row():263 undersample_output = gr.Plot(label="Undersampled K-space and Image")264 265 undersample_button.click(fn=undersampling_function, inputs=[dicom_input, undersampling_factor_slider], outputs=undersample_output)266 267 # Corrected link placement at the bottom, outside of the tabs268 with gr.Row():269 gr.Markdown("""270 ---271 ## For a detailed tutorial and more code examples:272 [Explore the Jupyter Notebook Tutorial](https://www.kaggle.com/code/danieltweneboaha/imageformation)273 """)274 275 return demo276 277# Launch the interface278demo = build_dicom_interface()279demo.launch(share=True)