denoise-app / core /synth.py
katospiegel's picture
Deploy denoise-app as an imaging-plaza Gradio Space (SDSC)
753375e verified
Raw
History Blame Contribute Delete
1.7 kB
"""Synthetic clean microscopy-style image + a noisy version (shot + read noise).
Bright blobs (cells) and thin filaments on a smooth background. The clean image is
the ground truth, so denoisers can be scored with PSNR / SSIM.
"""
from __future__ import annotations
import numpy as np
def clean_image(H: int = 256, W: int = 256, seed: int = 0) -> np.ndarray:
rng = np.random.default_rng(seed)
yy, xx = np.mgrid[0:H, 0:W]
img = 0.08 + 0.05 * np.sin(xx / W * np.pi) * np.cos(yy / H * np.pi)
for _ in range(rng.integers(14, 22)): # cells / blobs
cy, cx = rng.uniform(12, H - 12), rng.uniform(12, W - 12)
a, b = rng.uniform(5, 12), rng.uniform(5, 12)
img += rng.uniform(0.4, 0.9) * np.exp(-(((xx - cx) ** 2) / (2 * a ** 2) +
((yy - cy) ** 2) / (2 * b ** 2)))
for _ in range(rng.integers(4, 8)): # filaments (thin lines)
x0, y0 = rng.uniform(0, W), rng.uniform(0, H)
ang = rng.uniform(0, np.pi)
t = np.linspace(0, rng.uniform(40, 120), 200)
for s in t:
px, py = x0 + s * np.cos(ang), y0 + s * np.sin(ang)
if 1 <= px < W - 1 and 1 <= py < H - 1:
img[int(py), int(px)] += 0.5
return np.clip(img, 0, 1).astype(np.float32)
def add_noise(clean: np.ndarray, sigma: float = 0.12, peak: float = 40.0,
seed: int = 0) -> np.ndarray:
rng = np.random.default_rng(seed)
shot = rng.poisson(np.clip(clean, 0, None) * peak) / peak # Poisson (shot)
noisy = shot + rng.normal(0, sigma, clean.shape) # Gaussian (read)
return np.clip(noisy, 0, 1).astype(np.float32)