| """ |
| Gradio web UI wrapper for the Image Postprocess pipeline. |
| |
| This lightweight wrapper saves the uploaded image to a temporary file, |
| constructs a minimal args namespace expected by `process_image`, runs the |
| processing pipeline, and returns the result to the browser. |
| |
| Designed for quick deployment on Huggingface Spaces. |
| """ |
| from pathlib import Path |
| import tempfile |
| import os |
| from types import SimpleNamespace |
| from typing import Optional |
| from PIL import Image |
| import io |
| import matplotlib.pyplot as plt |
| import numpy as np |
| import json |
|
|
| |
| PRESETS_FILE = Path(__file__).parent / "presets.json" |
|
|
| |
| BUILTIN_PRESETS = { |
| "Default": {}, |
| "NovaNodes (reference)": { |
| "noise_std": 0.02, |
| "clahe_clip": 2.0, |
| "tile": 8, |
| "cutoff": 0.25, |
| "fstrength": 0.9, |
| "randomness": 0.05, |
| |
| "perturb": 0.01, |
| "phase_perturb": 0.08, |
| "radial_smooth": 5, |
| "jpeg_cycles": 1, |
| "jpeg_qmin": 88, |
| |
| "sim_camera": True, |
| "vignette_strength": 0.35, |
| "chroma_strength": 1.2, |
| "iso_scale": 1.0, |
| "read_noise": 2.0, |
| |
| "hot_pixel_prob": 1e-7, |
| "no_no_bayer": False, |
| |
| "lut_strength": 1.0, |
| }, |
| "High JPEG cycles": {"jpeg_cycles": 3, "jpeg_qmin": 70}, |
| "Aggressive": {"noise_std": 0.06, "fstrength": 1.0, "perturb": 0.02, "jpeg_cycles": 2}, |
| "Subtle": {"noise_std": 0.01, "fstrength": 0.6}, |
|
|
| |
| "Preview (no camera sim)": {"sim_camera": False, "noise_std": 0.01, "fstrength": 0.6}, |
| } |
|
|
|
|
| def load_custom_presets(): |
| if PRESETS_FILE.exists(): |
| try: |
| return json.loads(PRESETS_FILE.read_text()) |
| except Exception: |
| return {} |
| return {} |
|
|
|
|
| def save_custom_preset(name: str, data: dict): |
| presets = load_custom_presets() |
| presets[name] = data |
| PRESETS_FILE.write_text(json.dumps(presets, indent=2)) |
|
|
|
|
| def get_preset_overrides(name: str): |
| if name in BUILTIN_PRESETS: |
| return BUILTIN_PRESETS[name].copy() |
| customs = load_custom_presets() |
| return customs.get(name, {}).copy() |
|
|
|
|
| def preset_summary(name: str): |
| overrides = get_preset_overrides(name) |
| if not overrides: |
| return "(no overrides)" |
| return json.dumps(overrides, indent=2) |
|
|
| gr = None |
|
|
| try: |
| from image_postprocess import process_image |
| except Exception as e: |
| process_image = None |
| IMPORT_ERROR = str(e) |
| else: |
| IMPORT_ERROR = None |
|
|
|
|
| def _mk_temp_file(suffix: str = ".png") -> str: |
| f = tempfile.NamedTemporaryFile(suffix=suffix, delete=False) |
| f.close() |
| return f.name |
|
|
|
|
| def run_process( |
| img: Image.Image, |
| noise_std: float = 0.02, |
| clahe_clip: float = 2.0, |
| tile: int = 8, |
| cutoff: float = 0.25, |
| fstrength: float = 0.9, |
| awb: bool = True, |
| sim_camera: bool = True, |
| lut_file: Optional[Path] = None, |
| lut_strength: float = 0.1, |
| ): |
| """Run the repository's processing pipeline on a PIL image and return a PIL image. |
| |
| Returns (pil.Image or None, status string). |
| """ |
| if process_image is None: |
| return None, f"Backend import error: {IMPORT_ERROR}" |
|
|
| tmp_files = [] |
| try: |
| in_path = _mk_temp_file(suffix=".png") |
| img.save(in_path) |
| tmp_files.append(in_path) |
|
|
| out_path = _mk_temp_file(suffix=".jpg") |
| tmp_files.append(out_path) |
|
|
| lut_path = None |
| if lut_file is not None: |
| |
| lut_path = str(lut_file) |
|
|
| args = SimpleNamespace( |
| input=in_path, |
| output=out_path, |
| awb=bool(awb), |
| ref=None, |
| noise_std=float(noise_std), |
| clahe_clip=float(clahe_clip), |
| tile=int(tile), |
| cutoff=float(cutoff), |
| fstrength=float(fstrength), |
| randomness=0.05, |
| perturb=0.008, |
| seed=None, |
| fft_ref=None, |
| fft_mode="auto", |
| fft_alpha=1.0, |
| phase_perturb=0.08, |
| radial_smooth=5, |
| sim_camera=bool(sim_camera), |
| no_no_bayer=False, |
| jpeg_cycles=1, |
| jpeg_qmin=88, |
| jpeg_qmax=96, |
| vignette_strength=0.35, |
| chroma_strength=1.2, |
| iso_scale=1.0, |
| read_noise=2.0, |
| hot_pixel_prob=1e-6, |
| banding_strength=0.0, |
| motion_blur_kernel=1, |
| lut=(lut_path if lut_path else None), |
| lut_strength=float(lut_strength), |
| ) |
|
|
| |
| try: |
| print("process_image called with args:") |
| for k, v in vars(args).items(): |
| print(f" {k}: {v}") |
| except Exception: |
| pass |
|
|
| try: |
| process_image(in_path, out_path, args) |
| except Exception as e: |
| return None, f"Processing error: {e}" |
|
|
| out_img = Image.open(out_path).convert("RGB") |
| return out_img, "OK" |
|
|
| finally: |
| for p in tmp_files: |
| try: |
| os.unlink(p) |
| except Exception: |
| pass |
|
|
|
|
| def run_process_with_exif( |
| img: Image.Image, |
| noise_std: float = 0.02, |
| clahe_clip: float = 2.0, |
| tile: int = 8, |
| cutoff: float = 0.25, |
| fstrength: float = 0.9, |
| awb: bool = True, |
| sim_camera: bool = True, |
| lut_file: Optional[Path] = None, |
| lut_strength: float = 0.1, |
| awb_ref: Optional[Image.Image] = None, |
| fft_ref: Optional[Image.Image] = None, |
| seed: Optional[int] = None, |
| jpeg_cycles: int = 1, |
| jpeg_qmin: int = 88, |
| jpeg_qmax: int = 96, |
| vignette_strength: float = 0.35, |
| chroma_strength: float = 1.2, |
| iso_scale: float = 1.0, |
| read_noise: float = 2.0, |
| no_no_bayer: bool = False, |
| randomness: float = 0.05, |
| perturb: float = 0.008, |
| phase_perturb: float = 0.08, |
| radial_smooth: int = 5, |
| fft_mode: str = "auto", |
| fft_alpha: float = 1.0, |
| apply_exif: bool = True, |
| hot_pixel_prob: float = 1e-6, |
| banding_strength: float = 0.0, |
| motion_blur_kernel: int = 1, |
| ): |
| """Run pipeline like `run_process` but return (pil_img, status, exif_hex_or_empty). |
| |
| This function is used by the Gradio UI to expose EXIF metadata. |
| """ |
| if process_image is None: |
| return None, f"Backend import error: {IMPORT_ERROR}", "" |
|
|
| tmp_files = [] |
| try: |
| in_path = _mk_temp_file(suffix=".png") |
| img.save(in_path) |
| tmp_files.append(in_path) |
|
|
| |
| awb_ref_path = None |
| if awb_ref is not None: |
| p = _mk_temp_file(suffix=".png") |
| awb_ref.save(p) |
| awb_ref_path = p |
| tmp_files.append(p) |
|
|
| fft_ref_path = None |
| if fft_ref is not None: |
| p = _mk_temp_file(suffix=".png") |
| fft_ref.save(p) |
| fft_ref_path = p |
| tmp_files.append(p) |
|
|
| out_path = _mk_temp_file(suffix=".jpg") |
| tmp_files.append(out_path) |
|
|
| lut_path = None |
| if lut_file is not None: |
| lut_path = str(lut_file) |
|
|
| args = SimpleNamespace( |
| input=in_path, |
| output=out_path, |
| awb=bool(awb), |
| ref=awb_ref_path, |
| noise_std=float(noise_std), |
| clahe_clip=float(clahe_clip), |
| tile=int(tile), |
| cutoff=float(cutoff), |
| fstrength=float(fstrength), |
| randomness=float(randomness), |
| perturb=float(perturb), |
| seed=seed, |
| fft_ref=fft_ref_path, |
| fft_mode=fft_mode, |
| fft_alpha=float(fft_alpha), |
| phase_perturb=float(phase_perturb), |
| radial_smooth=int(radial_smooth), |
| sim_camera=bool(sim_camera), |
| no_no_bayer=bool(no_no_bayer), |
| jpeg_cycles=int(jpeg_cycles), |
| jpeg_qmin=int(jpeg_qmin), |
| jpeg_qmax=int(jpeg_qmax), |
| vignette_strength=float(vignette_strength), |
| chroma_strength=float(chroma_strength), |
| iso_scale=float(iso_scale), |
| read_noise=float(read_noise), |
| hot_pixel_prob=float(hot_pixel_prob), |
| banding_strength=float(banding_strength), |
| motion_blur_kernel=int(motion_blur_kernel), |
| lut=(lut_path if lut_path else None), |
| lut_strength=float(lut_strength), |
| ) |
|
|
| |
| try: |
| print("process_image called with args:") |
| for k, v in vars(args).items(): |
| print(f" {k}: {v}") |
| except Exception: |
| pass |
|
|
| try: |
| process_image(in_path, out_path, args) |
| except Exception as e: |
| return None, f"Processing error: {e}", "" |
|
|
| out_img = Image.open(out_path).convert("RGB") |
|
|
| |
| exif_hex = "" |
| try: |
| info = Image.open(out_path).info |
| exif_bytes = info.get('exif') |
| if exif_bytes: |
| exif_hex = exif_bytes.hex() |
| except Exception: |
| exif_hex = "" |
|
|
| return out_img, "OK", exif_hex |
|
|
| finally: |
| for p in tmp_files: |
| try: |
| os.unlink(p) |
| except Exception: |
| pass |
|
|
| |
| def pil_to_gray_array(pil_img: Image.Image): |
| arr = np.array(pil_img.convert('RGB')) |
| gray = (0.299 * arr[:, :, 0] + 0.587 * arr[:, :, 1] + 0.114 * arr[:, :, 2]).astype(np.float32) |
| return gray |
|
|
|
|
| def compute_fft_magnitude(gray_arr, eps=1e-8): |
| f = np.fft.fft2(gray_arr) |
| fshift = np.fft.fftshift(f) |
| mag = np.abs(fshift) |
| mag_log = np.log1p(mag) |
| return mag, mag_log |
|
|
|
|
| def radial_profile(mag, center=None, nbins=100): |
| h, w = mag.shape |
| if center is None: |
| center = (int(h / 2), int(w / 2)) |
| y, x = np.indices((h, w)) |
| r = np.sqrt((x - center[1]) ** 2 + (y - center[0]) ** 2) |
| r_flat = r.ravel() |
| mag_flat = mag.ravel() |
| max_r = np.max(r_flat) |
| if max_r <= 0: |
| return np.linspace(0, 1, nbins), np.zeros(nbins) |
| bins = np.linspace(0, max_r, nbins + 1) |
| inds = np.digitize(r_flat, bins) - 1 |
| radial_mean = np.zeros(nbins) |
| for i in range(nbins): |
| sel = inds == i |
| if np.any(sel): |
| radial_mean[i] = mag_flat[sel].mean() |
| else: |
| radial_mean[i] = 0.0 |
| centers = 0.5 * (bins[:-1] + bins[1:]) / max_r |
| return centers, radial_mean |
|
|
|
|
| def fig_to_pil(fig): |
| buf = io.BytesIO() |
| fig.savefig(buf, format='png', bbox_inches='tight') |
| plt.close(fig) |
| buf.seek(0) |
| return Image.open(buf).convert('RGB') |
|
|
|
|
| def make_analysis_images(pil_img: Image.Image): |
| """Return (hist_img, fft_img, radial_img) as PIL Images for the provided PIL image.""" |
| gray = pil_to_gray_array(pil_img) |
|
|
| |
| fig1 = plt.figure(figsize=(3, 2), dpi=100) |
| ax1 = fig1.add_subplot(111) |
| flat = gray.ravel() |
| if flat.dtype.kind == 'f' and flat.max() <= 1.0: |
| flat = (flat * 255.0).astype(np.uint8) |
| ax1.hist(flat, bins=256, range=(0, 255)) |
| ax1.set_title('Grayscale histogram') |
| ax1.set_xlabel('Intensity') |
| ax1.set_ylabel('Count') |
| hist_img = fig_to_pil(fig1) |
|
|
| |
| mag, mag_log = compute_fft_magnitude(gray) |
| fig2 = plt.figure(figsize=(3, 2), dpi=100) |
| ax2 = fig2.add_subplot(111) |
| ax2.imshow(mag_log, origin='lower', aspect='auto') |
| ax2.set_title('FFT magnitude (log)') |
| ax2.set_xticks([]) |
| ax2.set_yticks([]) |
| fft_img = fig_to_pil(fig2) |
|
|
| |
| centers, radial = radial_profile(mag) |
| fig3 = plt.figure(figsize=(3, 2), dpi=100) |
| ax3 = fig3.add_subplot(111) |
| ax3.plot(centers, radial) |
| ax3.set_title('Radial freq profile') |
| ax3.set_xlabel('Normalized radius') |
| ax3.set_ylabel('Mean magnitude') |
| radial_img = fig_to_pil(fig3) |
|
|
| return hist_img, fft_img, radial_img |
|
|
|
|
| def make_delta_image(orig: Image.Image, proc: Image.Image, max_size: int = 256): |
| """Return (diff_pil, mse, norm_diff) comparing orig vs proc. |
| |
| - diff_pil: absolute-difference thumbnail (RGB) |
| - mse: mean squared error (float) |
| - norm_diff: mean absolute difference normalized to [0..1] |
| """ |
| try: |
| |
| orig_small = orig.copy() |
| proc_small = proc.copy() |
| orig_small.thumbnail((max_size, max_size)) |
| proc_small.thumbnail((max_size, max_size)) |
|
|
| a = np.asarray(orig_small).astype(np.float32) |
| b = np.asarray(proc_small).astype(np.float32) |
| |
| if a.shape != b.shape: |
| |
| proc_small = proc_small.resize(orig_small.size) |
| b = np.asarray(proc_small).astype(np.float32) |
|
|
| diff = np.abs(a - b) |
| mse = float(((a - b) ** 2).mean()) |
| norm_diff = float(diff.mean() / 255.0) |
|
|
| |
| diff_vis = np.clip(diff * 4.0, 0, 255).astype(np.uint8) |
| diff_img = Image.fromarray(diff_vis) |
| metrics = f"MSE: {mse:.2f}\nMean abs diff (norm): {norm_diff:.4f}" |
| return diff_img, mse, metrics |
| except Exception as e: |
| return None, 0.0, f"delta error: {e}" |
|
|
|
|
| def build_interface(): |
| try: |
| import gradio as gr |
| except Exception: |
| raise RuntimeError("Gradio is not installed. Add 'gradio' to requirements.txt and install it.") |
|
|
| with gr.Blocks() as demo: |
| gr.Markdown("# Image Postprocess — Gradio frontend\nWraps the repository's `process_image` pipeline.") |
|
|
| with gr.Row(): |
| inp = gr.Image(type="pil", label="Input image") |
| out = gr.Image(type="pil", label="Processed image") |
|
|
| |
| customs = list(load_custom_presets().keys()) |
| preset_choices = [*BUILTIN_PRESETS.keys(), *customs] |
| with gr.Row(): |
| preset = gr.Dropdown(choices=preset_choices, value="Preview (no camera sim)", label="Preset") |
| preset_name = gr.Textbox(label="Save preset as (name)") |
| save_preset_btn = gr.Button("Save preset") |
| preset_summary_box = gr.Textbox(value=preset_summary("Default"), label="Preset summary", interactive=False) |
|
|
| with gr.Row(): |
| hist_out = gr.Image(type="pil", label="Processed hist") |
| fft_out = gr.Image(type="pil", label="Processed FFT") |
| radial_out = gr.Image(type="pil", label="Processed radial") |
| delta_out = gr.Image(type="pil", label="Diff (abs) thumb") |
| delta_metrics = gr.Textbox(label="Delta metrics", interactive=False) |
|
|
| |
| with gr.Row(): |
| awb_ref = gr.Image(type="pil", label="AWB reference (optional)") |
| fft_ref = gr.Image(type="pil", label="FFT reference (optional)") |
| exif_out = gr.Textbox(label="EXIF (hex)") |
|
|
| with gr.Row(): |
| noise_std = gr.Slider(0.0, 0.1, value=0.02, step=0.001, label="Noise STD (fraction)") |
| clahe_clip = gr.Slider(0.5, 10.0, value=2.0, step=0.1, label="CLAHE clip") |
| tile = gr.Slider(2, 32, value=8, step=1, label="CLAHE tile") |
|
|
| with gr.Row(): |
| cutoff = gr.Slider(0.0, 1.0, value=0.25, step=0.01, label="Fourier cutoff") |
| fstrength = gr.Slider(0.0, 1.0, value=0.9, step=0.01, label="Fourier strength") |
| awb = gr.Checkbox(label="Apply AWB (auto white balance)", value=True) |
|
|
| with gr.Row(): |
| sim_camera = gr.Checkbox(label="Simulate camera pipeline", value=False) |
| lut_file = gr.File(label="Optional LUT (png/npy/cube)") |
| lut_strength = gr.Slider(0.0, 1.0, value=0.1, step=0.01, label="LUT strength") |
|
|
| with gr.Row(): |
| seed = gr.Number(value=None, label="Seed (integer, optional)") |
| jpeg_cycles = gr.Slider(1, 5, value=1, step=1, label="JPEG cycles") |
| jpeg_qmin = gr.Slider(30, 100, value=88, step=1, label="JPEG quality (min)") |
|
|
| with gr.Row(): |
| vignette_strength = gr.Slider(0.0, 1.0, value=0.35, step=0.01, label="Vignette strength") |
| chroma_strength = gr.Slider(0.0, 5.0, value=1.2, step=0.1, label="Chroma strength") |
| iso_scale = gr.Slider(0.1, 16.0, value=1.0, step=0.1, label="ISO scale") |
|
|
| with gr.Row(): |
| read_noise = gr.Slider(0.0, 50.0, value=2.0, step=0.1, label="Read noise") |
| no_no_bayer = gr.Checkbox(label="Disable Bayer (no demosaic)", value=False) |
|
|
| |
| with gr.Accordion("Advanced parameters (expert)", open=False): |
| randomness = gr.Slider(0.0, 0.5, value=0.05, step=0.001, label="Fourier randomness") |
| perturb = gr.Slider(0.0, 0.05, value=0.008, step=0.001, label="Perturb magnitude") |
| phase_perturb = gr.Slider(0.0, 0.5, value=0.08, step=0.001, label="Phase perturb") |
| radial_smooth = gr.Slider(0, 50, value=5, step=1, label="Radial smooth") |
| fft_mode = gr.Dropdown(["auto", "ref", "model"], value="auto", label="FFT mode") |
| fft_alpha = gr.Slider(0.1, 4.0, value=1.0, step=0.1, label="FFT alpha (1/f)") |
|
|
| status = gr.Textbox(label="Status", interactive=False) |
|
|
| def _wrap(preset, inp_img, noise_std, clahe_clip, tile, cutoff, fstrength, awb, sim_camera, lut_file, lut_strength, awb_ref, fft_ref, seed, jpeg_cycles, jpeg_qmin, vignette_strength, chroma_strength, iso_scale, read_noise, no_no_bayer, randomness, perturb, phase_perturb, radial_smooth, fft_mode, fft_alpha): |
| jpeg_qmax = 96 |
| lut_path = lut_file.name if getattr(lut_file, 'name', None) else None |
|
|
| |
| params = { |
| "noise_std": float(noise_std), |
| "clahe_clip": float(clahe_clip), |
| "tile": int(tile), |
| "cutoff": float(cutoff), |
| "fstrength": float(fstrength), |
| "awb": bool(awb), |
| "sim_camera": bool(sim_camera), |
| "lut_file": lut_path, |
| "lut_strength": float(lut_strength), |
| "awb_ref": awb_ref, |
| "fft_ref": fft_ref, |
| "seed": int(seed) if (seed is not None and str(seed) != "") else None, |
| "jpeg_cycles": int(jpeg_cycles), |
| "jpeg_qmin": int(jpeg_qmin), |
| "jpeg_qmax": int(jpeg_qmax), |
| "vignette_strength": float(vignette_strength), |
| "chroma_strength": float(chroma_strength), |
| "iso_scale": float(iso_scale), |
| "read_noise": float(read_noise), |
| "no_no_bayer": bool(no_no_bayer), |
| "randomness": float(randomness), |
| "perturb": float(perturb), |
| "phase_perturb": float(phase_perturb), |
| "radial_smooth": int(radial_smooth), |
| "fft_mode": fft_mode, |
| "fft_alpha": float(fft_alpha), |
| } |
|
|
| |
| overrides = get_preset_overrides(preset) |
| for k, v in overrides.items(): |
| params[k] = v |
|
|
| |
| try: |
| result, msg, exif = run_process_with_exif( |
| inp_img, |
| noise_std=params.get("noise_std"), |
| clahe_clip=params.get("clahe_clip"), |
| tile=params.get("tile"), |
| cutoff=params.get("cutoff"), |
| fstrength=params.get("fstrength"), |
| awb=params.get("awb"), |
| sim_camera=params.get("sim_camera"), |
| lut_file=params.get("lut_file"), |
| lut_strength=params.get("lut_strength"), |
| awb_ref=params.get("awb_ref"), |
| fft_ref=params.get("fft_ref"), |
| seed=params.get("seed"), |
| jpeg_cycles=params.get("jpeg_cycles"), |
| jpeg_qmin=params.get("jpeg_qmin"), |
| jpeg_qmax=params.get("jpeg_qmax"), |
| vignette_strength=params.get("vignette_strength"), |
| chroma_strength=params.get("chroma_strength"), |
| iso_scale=params.get("iso_scale"), |
| read_noise=params.get("read_noise"), |
| no_no_bayer=params.get("no_no_bayer"), |
| randomness=params.get("randomness"), |
| perturb=params.get("perturb"), |
| phase_perturb=params.get("phase_perturb"), |
| radial_smooth=params.get("radial_smooth"), |
| fft_mode=params.get("fft_mode"), |
| fft_alpha=params.get("fft_alpha"), |
| ) |
| except Exception as e: |
| return None, None, None, None, None, "", f"Processing error: {e}" |
|
|
| if result is None: |
| return None, None, None, None, None, "", msg |
|
|
| try: |
| hist_img, fft_img, radial_img = make_analysis_images(result) |
| except Exception as e: |
| return result, None, None, None, None, exif, f"Analysis error: {e}" |
|
|
| |
| try: |
| diff_img, mse_val, metrics = make_delta_image(inp_img, result) |
| except Exception as e: |
| diff_img, mse_val, metrics = None, 0.0, f"delta error: {e}" |
|
|
| return result, hist_img, fft_img, radial_img, diff_img, metrics, exif, msg |
|
|
| btn = gr.Button("Run") |
| btn.click( |
| _wrap, |
| inputs=[preset, inp, noise_std, clahe_clip, tile, cutoff, fstrength, awb, sim_camera, lut_file, lut_strength, awb_ref, fft_ref, seed, jpeg_cycles, jpeg_qmin, vignette_strength, chroma_strength, iso_scale, read_noise, no_no_bayer, randomness, perturb, phase_perturb, radial_smooth, fft_mode, fft_alpha], |
| outputs=[out, hist_out, fft_out, radial_out, delta_out, delta_metrics, exif_out, status], |
| ) |
|
|
| def _save_preset(name, preset, noise_std, clahe_clip, tile, cutoff, fstrength, awb, sim_camera, lut_strength, seed, jpeg_cycles, jpeg_qmin, vignette_strength, chroma_strength, iso_scale, read_noise, no_no_bayer, randomness, perturb, phase_perturb, radial_smooth, fft_mode, fft_alpha): |
| if not name: |
| return "Provide a name to save the preset" |
| data = { |
| "noise_std": float(noise_std), |
| "clahe_clip": float(clahe_clip), |
| "tile": int(tile), |
| "cutoff": float(cutoff), |
| "fstrength": float(fstrength), |
| "randomness": float(randomness), |
| "perturb": float(perturb), |
| "phase_perturb": float(phase_perturb), |
| "radial_smooth": int(radial_smooth), |
| "jpeg_cycles": int(jpeg_cycles), |
| "jpeg_qmin": int(jpeg_qmin), |
| "vignette_strength": float(vignette_strength), |
| "chroma_strength": float(chroma_strength), |
| "iso_scale": float(iso_scale), |
| "read_noise": float(read_noise), |
| "no_no_bayer": bool(no_no_bayer), |
| } |
| save_custom_preset(name, data) |
| return f"Saved preset: {name}" |
|
|
| save_preset_btn.click(_save_preset, inputs=[preset_name, preset, noise_std, clahe_clip, tile, cutoff, fstrength, awb, sim_camera, lut_strength, seed, jpeg_cycles, jpeg_qmin, vignette_strength, chroma_strength, iso_scale, read_noise, no_no_bayer, randomness, perturb, phase_perturb, radial_smooth, fft_mode, fft_alpha], outputs=[preset_summary_box]) |
|
|
| def _update_summary(selected): |
| return preset_summary(selected) |
|
|
| preset.change(_update_summary, inputs=[preset], outputs=[preset_summary_box]) |
|
|
| return demo |
|
|
|
|
| if __name__ == "__main__": |
| iface = build_interface() |
| iface.launch(server_name="0.0.0.0", server_port=7860) |
|
|