|
|
import os
|
|
|
|
|
|
|
|
|
os.environ.setdefault("TF_CPP_MIN_LOG_LEVEL", "2")
|
|
|
os.environ.setdefault("TF_ENABLE_ONEDNN_OPTS", "0")
|
|
|
|
|
|
import gradio as gr
|
|
|
import numpy as np
|
|
|
import cv2
|
|
|
from tensorflow.keras.models import load_model
|
|
|
from skimage.metrics import structural_similarity as ssim
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
MODEL_PATH = "image_denoising_autoencoder.h5"
|
|
|
model = load_model(MODEL_PATH, compile=False)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def preprocess_gray(image):
|
|
|
image = cv2.resize(image, (128, 128))
|
|
|
image = image.astype("float32") / 255.0
|
|
|
return image.reshape(128, 128, 1)
|
|
|
|
|
|
def add_noise(image, noise_factor):
|
|
|
noisy = image + noise_factor * np.random.normal(
|
|
|
loc=0.0, scale=1.0, size=image.shape
|
|
|
)
|
|
|
return np.clip(noisy, 0.0, 1.0)
|
|
|
|
|
|
def psnr(original, denoised):
|
|
|
mse = np.mean((original - denoised) ** 2)
|
|
|
if mse == 0:
|
|
|
return 100
|
|
|
return 10 * np.log10(1.0 / mse)
|
|
|
|
|
|
def interpret_psnr(value):
|
|
|
if value < 15:
|
|
|
return "Poor β"
|
|
|
elif value < 25:
|
|
|
return "Moderate β οΈ"
|
|
|
else:
|
|
|
return "Good β
"
|
|
|
|
|
|
def interpret_ssim(value):
|
|
|
if value < 0.5:
|
|
|
return "Low β"
|
|
|
elif value < 0.8:
|
|
|
return "Moderate β οΈ"
|
|
|
else:
|
|
|
return "High β
"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def enhance_clarity(image, strength=1.0):
|
|
|
image_uint8 = (image * 255).astype(np.uint8)
|
|
|
blurred = cv2.GaussianBlur(image_uint8, (5, 5), 0)
|
|
|
sharpened = cv2.addWeighted(
|
|
|
image_uint8, 1 + strength, blurred, -strength, 0
|
|
|
)
|
|
|
return np.clip(sharpened.astype("float32") / 255.0, 0, 1)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def denoise_image(input_image, noise_level, mode, clarity_strength):
|
|
|
if input_image is None:
|
|
|
return None, None, None, "Upload an image to start."
|
|
|
|
|
|
|
|
|
if mode == "Grayscale":
|
|
|
if input_image.ndim == 3:
|
|
|
gray = cv2.cvtColor(input_image, cv2.COLOR_RGB2GRAY)
|
|
|
else:
|
|
|
gray = input_image
|
|
|
|
|
|
clean = preprocess_gray(gray)
|
|
|
noisy = add_noise(clean, noise_level)
|
|
|
denoised = model.predict(noisy.reshape(1, 128, 128, 1))[0]
|
|
|
|
|
|
enhanced = enhance_clarity(
|
|
|
denoised.reshape(128, 128),
|
|
|
strength=clarity_strength
|
|
|
)
|
|
|
|
|
|
psnr_val = psnr(clean.reshape(128, 128), denoised.reshape(128, 128))
|
|
|
ssim_val = ssim(
|
|
|
clean.reshape(128, 128),
|
|
|
denoised.reshape(128, 128),
|
|
|
data_range=1.0
|
|
|
)
|
|
|
|
|
|
metrics = (
|
|
|
f"PSNR: {psnr_val:.2f} dB ({interpret_psnr(psnr_val)})\n"
|
|
|
f"SSIM: {ssim_val:.3f} ({interpret_ssim(ssim_val)})\n"
|
|
|
f"Clarity Strength: {clarity_strength:.2f}"
|
|
|
)
|
|
|
|
|
|
return (
|
|
|
clean.reshape(128, 128),
|
|
|
noisy.reshape(128, 128),
|
|
|
enhanced,
|
|
|
metrics
|
|
|
)
|
|
|
|
|
|
|
|
|
resized = cv2.resize(input_image, (128, 128))
|
|
|
resized = resized.astype("float32") / 255.0
|
|
|
|
|
|
noisy_rgb = add_noise(resized, noise_level)
|
|
|
denoised_rgb = np.zeros_like(noisy_rgb)
|
|
|
|
|
|
for c in range(3):
|
|
|
channel = noisy_rgb[:, :, c].reshape(128, 128, 1)
|
|
|
denoised_rgb[:, :, c] = model.predict(
|
|
|
channel.reshape(1, 128, 128, 1)
|
|
|
)[0].reshape(128, 128)
|
|
|
|
|
|
enhanced_rgb = np.zeros_like(denoised_rgb)
|
|
|
for c in range(3):
|
|
|
enhanced_rgb[:, :, c] = enhance_clarity(
|
|
|
denoised_rgb[:, :, c],
|
|
|
strength=clarity_strength
|
|
|
)
|
|
|
|
|
|
psnr_val = psnr(resized, denoised_rgb)
|
|
|
ssim_val = ssim(
|
|
|
resized,
|
|
|
denoised_rgb,
|
|
|
channel_axis=2,
|
|
|
data_range=1.0
|
|
|
)
|
|
|
|
|
|
metrics = (
|
|
|
f"PSNR: {psnr_val:.2f} dB ({interpret_psnr(psnr_val)})\n"
|
|
|
f"SSIM: {ssim_val:.3f} ({interpret_ssim(ssim_val)})\n"
|
|
|
f"Clarity Strength: {clarity_strength:.2f}"
|
|
|
)
|
|
|
|
|
|
return resized, noisy_rgb, enhanced_rgb, metrics
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
with gr.Blocks(title="Image Denoising Autoencoder") as demo:
|
|
|
gr.Markdown(
|
|
|
"""
|
|
|
# π§ Image Denoising using U-Net Autoencoder
|
|
|
π Sliders update output automatically
|
|
|
"""
|
|
|
)
|
|
|
|
|
|
with gr.Row():
|
|
|
input_image = gr.Image(label="Upload Image", type="numpy", height=320)
|
|
|
|
|
|
with gr.Column():
|
|
|
noise_slider = gr.Slider(0.05, 0.5, 0.3, 0.05, label="Noise Level")
|
|
|
clarity_slider = gr.Slider(0.0, 2.0, 1.0, 0.1, label="Clarity Strength")
|
|
|
mode_selector = gr.Radio(
|
|
|
["Grayscale", "RGB"], value="Grayscale", label="Mode"
|
|
|
)
|
|
|
|
|
|
denoise_btn = gr.Button("Denoise Image π")
|
|
|
|
|
|
with gr.Row():
|
|
|
original_out = gr.Image(label="Original", height=260)
|
|
|
noisy_out = gr.Image(label="Noisy", height=260)
|
|
|
denoised_out = gr.Image(label="Denoised", height=260)
|
|
|
|
|
|
metrics_box = gr.Textbox(label="Quality Metrics", lines=4)
|
|
|
|
|
|
inputs = [input_image, noise_slider, mode_selector, clarity_slider]
|
|
|
outputs = [original_out, noisy_out, denoised_out, metrics_box]
|
|
|
|
|
|
|
|
|
noise_slider.change(denoise_image, inputs, outputs)
|
|
|
clarity_slider.change(denoise_image, inputs, outputs)
|
|
|
mode_selector.change(denoise_image, inputs, outputs)
|
|
|
|
|
|
|
|
|
denoise_btn.click(denoise_image, inputs, outputs)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
demo.launch(debug=True)
|
|
|
|