Spaces:
Runtime error
Runtime error
| """CV Lab Camera: Gradio scientific camera and image filtering lab.""" | |
| from __future__ import annotations | |
| import json | |
| import tempfile | |
| from pathlib import Path | |
| from typing import Any | |
| import gradio as gr | |
| import numpy as np | |
| from PIL import Image | |
| from batch.dataset_processor import process_dataset | |
| from cv_ops.analysis import histogram_figure, low_resolution_pair, pixel_preview, stats | |
| from cv_ops.morphology import apply_morphology | |
| from cv_ops.transforms import reflect, rotate, scale_image, translate | |
| from filters.builtin import BUILTIN_FILTERS, ensure_rgb | |
| from filters.registry import apply_definition, apply_step, delete_filter, load_definition, names, operation_to_definition, save_filter | |
| from models.face_filters import apply_face_filter, ar_filter_names, delete_ar_filter, load_ar_definition, save_ar_filter | |
| from models.style_transfer import stylize | |
| import warnings | |
| warnings.filterwarnings("ignore", category=DeprecationWarning) | |
| ROOT = Path(__file__).resolve().parent | |
| STYLE_SAMPLES = sorted(str(p) for p in (ROOT / "assets").glob("style_*.png")) | |
| DEFAULT_CUSTOM_AR_JSON = json.dumps( | |
| { | |
| "elements": [ | |
| {"landmark": "forehead", "shape": "crown", "color": [255, 215, 0], "scale": 1.0, "offset_y": -0.15}, | |
| {"landmark": "eyes", "shape": "visor", "color": [0, 255, 255], "scale": 1.0}, | |
| {"landmark": "mouth", "shape": "mustache", "color": [40, 20, 20], "scale": 0.9, "offset_y": -0.05}, | |
| ] | |
| }, | |
| indent=2, | |
| ) | |
| def export_image(image: np.ndarray | None) -> str | None: | |
| if image is None: | |
| return None | |
| arr = np.asarray(image) | |
| if arr.ndim == 2: | |
| img_obj = Image.fromarray(arr) | |
| else: | |
| img_obj = Image.fromarray(arr.astype(np.uint8)) | |
| out_dir = Path(tempfile.mkdtemp(prefix="cv_lab_dl_")) | |
| filepath = out_dir / "cv_lab_result.png" | |
| img_obj.save(filepath) | |
| return str(filepath) | |
| def set_global_image(image: np.ndarray | None) -> tuple[np.ndarray | None, np.ndarray | None, str]: | |
| if image is None: | |
| return None, None, "No image loaded." | |
| img = ensure_rgb(image) | |
| return img, img, f"Loaded image: {img.shape[1]} x {img.shape[0]}" | |
| def make_code_snippet(operation: str, params: dict[str, Any]) -> str: | |
| params_str = json.dumps(params, indent=2) | |
| return f"""import numpy as np | |
| from PIL import Image | |
| from filters.registry import apply_step | |
| image = np.array(Image.open("input.png").convert("RGB")) | |
| params = {params_str} | |
| result = apply_step(image, "{operation}", params) | |
| Image.fromarray(result).save("output.png") | |
| """ | |
| def run_builtin(image: np.ndarray | None, filter_name: str, blur_method: str, kernel_size: int, edge_method: str, low: int, high: int, brightness: int, contrast: float, saturation: float, hue_shift: int, channel: str) -> tuple[np.ndarray | None, dict[str, Any], str]: | |
| if image is None: | |
| raise gr.Error("Load or capture an image first.") | |
| params = { | |
| "method": blur_method if filter_name == "Blur" else edge_method, | |
| "kernel_size": kernel_size, | |
| "low": low, | |
| "high": high, | |
| "brightness": brightness, | |
| "contrast": contrast, | |
| "saturation": saturation, | |
| "hue_shift": hue_shift, | |
| "channel": channel, | |
| } | |
| result = apply_step(image, filter_name, params) | |
| definition = operation_to_definition(filter_name, params) | |
| code = make_code_snippet(filter_name, params) | |
| return result, definition, code | |
| def preview_kernel(image: np.ndarray | None, kernel_text: str) -> tuple[np.ndarray | None, dict[str, Any], str]: | |
| if image is None: | |
| raise gr.Error("Load or capture an image first.") | |
| kernel = parse_kernel(kernel_text) | |
| definition = {"type": "kernel", "kernel": kernel} | |
| code = f"""import numpy as np | |
| from PIL import Image | |
| from filters.builtin import custom_kernel | |
| image = np.array(Image.open("input.png").convert("RGB")) | |
| kernel = {json.dumps(kernel)} | |
| result = custom_kernel(image, kernel=kernel) | |
| Image.fromarray(result).save("output.png") | |
| """ | |
| return apply_definition(image, definition), definition, code | |
| def preview_pipeline(image: np.ndarray | None, pipeline_text: str) -> tuple[np.ndarray | None, dict[str, Any], str]: | |
| if image is None: | |
| raise gr.Error("Load or capture an image first.") | |
| definition = parse_pipeline(pipeline_text) | |
| code = f"""import numpy as np | |
| from PIL import Image | |
| from filters.registry import apply_definition | |
| image = np.array(Image.open("input.png").convert("RGB")) | |
| pipeline = {json.dumps(definition, indent=2)} | |
| result = apply_definition(image, pipeline) | |
| Image.fromarray(result).save("output.png") | |
| """ | |
| return apply_definition(image, definition), definition, code | |
| def save_current_filter(name: str | None, definition: dict[str, Any] | None) -> tuple[str, gr.Dropdown]: | |
| if not name or not name.strip(): | |
| raise gr.Error("Enter a filter name before saving.") | |
| if not definition: | |
| raise gr.Error("Preview a built-in, kernel, or pipeline filter before saving.") | |
| saved = save_filter(name, definition) | |
| return f"Saved filter '{saved['name']}'.", gr.Dropdown(choices=names(False), value=saved["name"]) | |
| def load_saved_filter(image: np.ndarray | None, name: str | None) -> tuple[np.ndarray | None, dict[str, Any], str]: | |
| if not name: | |
| raise gr.Error("Select a saved filter from the dropdown to load.") | |
| if image is None: | |
| raise gr.Error("Load or capture an image first.") | |
| try: | |
| definition = load_definition(name) | |
| except Exception as exc: | |
| raise gr.Error(str(exc)) | |
| return apply_definition(image, definition), definition, json.dumps(definition, indent=2) | |
| def delete_saved(name: str | None) -> tuple[str, gr.Dropdown]: | |
| if not name: | |
| raise gr.Error("Select a saved filter from the dropdown to delete.") | |
| delete_filter(name) | |
| return f"Deleted '{name}'.", gr.Dropdown(choices=names(False), value=None) | |
| def analyze(image: np.ndarray | None, percent: int, x: int, y: int): | |
| if image is None: | |
| raise gr.Error("Load or capture an image first.") | |
| high_rgb, low_rgb, high_gray, low_gray = low_resolution_pair(image, percent) | |
| return high_rgb, low_rgb, high_gray, low_gray, pixel_preview(high_rgb, x, y), pixel_preview(low_rgb, x, y), histogram_figure(high_rgb, low_rgb), {"high_rgb": stats(high_rgb), "low_rgb": stats(low_rgb), "high_gray": stats(high_gray), "low_gray": stats(low_gray)} | |
| def transform(image: np.ndarray | None, op: str, tx: int, ty: int, border: str, angle: float, scale: float, cx: float, cy: float, expand: bool, sx: float, sy: float, interp: str, flip: str): | |
| if image is None: | |
| raise gr.Error("Load or capture an image first.") | |
| if op == "Translation": | |
| return translate(image, tx, ty, border) | |
| if op == "Rotation": | |
| return rotate(image, angle, scale, cx, cy, expand) | |
| if op == "Scaling": | |
| return scale_image(image, sx, sy, interp) | |
| return reflect(image, flip) | |
| def morph(image: np.ndarray | None, operation: str, threshold_method: str, threshold: int, shape: str, size: int, iterations: int): | |
| if image is None: | |
| raise gr.Error("Load or capture an image first.") | |
| result, kernel = apply_morphology(image, operation, shape, size, iterations, threshold_method, threshold) | |
| return result, kernel.tolist() | |
| def run_style(image: np.ndarray | None, style_upload: np.ndarray | None, style_path: str | None, max_size: int): | |
| if image is None: | |
| raise gr.Error("Load or capture a content image first.") | |
| if style_upload is None and not style_path: | |
| raise gr.Error("Upload a style image or choose a sample style.") | |
| style = style_upload if style_upload is not None else np.asarray(Image.open(style_path).convert("RGB")) | |
| result, seconds, message = stylize(image, style, max_size) | |
| return result, f"{message} Inference time: {seconds:.2f}s" | |
| def run_face(image: np.ndarray | None, filter_name: str): | |
| if image is None: | |
| raise gr.Error("Load or capture an image first.") | |
| return apply_face_filter(image, filter_name) | |
| def preview_ar_custom(image: np.ndarray | None, custom_json: str | None) -> tuple[np.ndarray | None, str]: | |
| if image is None: | |
| raise gr.Error("Load or capture an image first.") | |
| if not custom_json or not custom_json.strip(): | |
| raise gr.Error("Enter a custom AR filter JSON definition.") | |
| try: | |
| definition = json.loads(custom_json) | |
| except Exception as exc: | |
| raise gr.Error(f"Invalid AR filter JSON: {exc}") | |
| return apply_face_filter(image, filter_name="Custom", custom_def=definition) | |
| def save_custom_ar_filter(name: str | None, custom_json: str | None) -> tuple[str, gr.Dropdown, gr.Dropdown]: | |
| if not name or not name.strip(): | |
| raise gr.Error("Enter an AR filter name before saving.") | |
| if not custom_json or not custom_json.strip(): | |
| raise gr.Error("Enter a custom AR filter JSON definition.") | |
| try: | |
| definition = json.loads(custom_json) | |
| except Exception as exc: | |
| raise gr.Error(f"Invalid AR filter JSON: {exc}") | |
| saved = save_ar_filter(name, definition) | |
| return f"Saved AR filter '{saved['name']}'.", gr.Dropdown(choices=ar_filter_names(True), value=saved["name"]), gr.Dropdown(choices=ar_filter_names(False), value=saved["name"]) | |
| def load_saved_ar_filter(image: np.ndarray | None, name: str | None) -> tuple[np.ndarray | None, str, str]: | |
| if not name: | |
| raise gr.Error("Select a saved AR filter from the dropdown to load.") | |
| if image is None: | |
| raise gr.Error("Load or capture an image first.") | |
| try: | |
| definition = load_ar_definition(name) | |
| except Exception as exc: | |
| raise gr.Error(str(exc)) | |
| result, msg = apply_face_filter(image, filter_name=name) | |
| return result, json.dumps(definition, indent=2), msg | |
| def delete_saved_ar_filter(name: str | None) -> tuple[str, gr.Dropdown, gr.Dropdown]: | |
| if not name: | |
| raise gr.Error("Select a saved AR filter from the dropdown to delete.") | |
| delete_ar_filter(name) | |
| return f"Deleted AR filter '{name}'.", gr.Dropdown(choices=ar_filter_names(True), value="Glasses"), gr.Dropdown(choices=ar_filter_names(False), value=None) | |
| def run_batch(files: list[str] | None, directory: str, filter_name: str, progress=gr.Progress()): | |
| if not filter_name: | |
| raise gr.Error("Choose a built-in or saved filter.") | |
| return process_dataset(files, directory or None, filter_name, progress) | |
| custom_theme = gr.themes.Soft( | |
| primary_hue="indigo", | |
| secondary_hue="cyan", | |
| neutral_hue="slate", | |
| font=[gr.themes.GoogleFont("Inter"), "ui-sans-serif", "system-ui", "sans-serif"], | |
| ) | |
| with gr.Blocks(title="CV Lab Camera Studio") as demo: | |
| current_image = gr.State() | |
| current_definition = gr.State() | |
| gr.Markdown( | |
| """# πΈ CV Lab Camera Studio | |
| ### Comprehensive Computer Vision & AR Laboratory for Photo Editors, Scientists & Developers | |
| *Capture with your webcam or upload an image to use seamlessly across all processing modules below.* | |
| """ | |
| ) | |
| with gr.Row(): | |
| global_input = gr.Image(label="Global Image Input", sources=["webcam", "upload"], type="numpy") | |
| global_preview = gr.Image(label="Active Image Canvas", type="numpy") | |
| status = gr.Markdown("β¨ Load or capture an image above to start processing across all tabs.") | |
| global_input.change(set_global_image, global_input, [current_image, global_preview, status]) | |
| with gr.Tabs(): | |
| with gr.Tab("π¨ Photo Filters & Pipelines"): | |
| gr.Markdown("Apply built-in visual filters, adjust parameters, or create reusable custom matrix kernels and multi-step JSON pipelines.") | |
| with gr.Row(): | |
| before = gr.Image(value=None, label="Original Input", type="numpy") | |
| after = gr.Image(label="Filtered Result", type="numpy") | |
| current_image.change(lambda x: x, current_image, before) | |
| with gr.Group(): | |
| filter_name = gr.Dropdown(list(BUILTIN_FILTERS.keys())[:-1], value="Sepia", label="Built-in Filter Selection") | |
| with gr.Row(): | |
| blur_method = gr.Radio(["Gaussian", "Median", "Bilateral"], value="Gaussian", label="Blur Method", info="Smoothing algorithm") | |
| kernel_size = gr.Slider(1, 31, value=7, step=2, label="Kernel Size", info="Must be an odd integer") | |
| edge_method = gr.Radio(["Canny", "Sobel"], value="Canny", label="Edge Detection Method") | |
| with gr.Row(): | |
| low = gr.Slider(0, 255, value=80, step=1, label="Canny Low Threshold", info="Lower hysteresis bound") | |
| high = gr.Slider(0, 255, value=160, step=1, label="Canny High Threshold", info="Upper hysteresis bound") | |
| brightness = gr.Slider(-100, 100, value=0, step=1, label="Brightness Shift") | |
| contrast = gr.Slider(0.1, 3.0, value=1.0, step=0.05, label="Contrast Multiplier") | |
| with gr.Row(): | |
| saturation = gr.Slider(0, 3, value=1, step=0.05, label="Saturation Factor") | |
| hue_shift = gr.Slider(-90, 90, value=0, step=1, label="Hue Shift Degrees") | |
| channel = gr.Radio(["R", "G", "B"], value="R", label="Channel Isolation") | |
| apply_builtin = gr.Button("β‘ Apply Built-in Filter", variant="primary") | |
| with gr.Accordion("βοΈ Custom Kernel & Pipeline JSON Editor", open=False): | |
| with gr.Row(): | |
| with gr.Column(): | |
| kernel_text = gr.Textbox(value="[[0,-1,0],[-1,5,-1],[0,-1,0]]", lines=4, label="Custom 3x3 Matrix Kernel JSON") | |
| apply_kernel = gr.Button("Preview Custom Kernel") | |
| with gr.Column(): | |
| pipeline_text = gr.Textbox(value='[{"operation":"Grayscale","params":{}},{"operation":"Sharpen","params":{"amount":1.4}}]', lines=5, label="Multi-Step Pipeline JSON") | |
| apply_pipeline = gr.Button("Preview Pipeline") | |
| with gr.Row(): | |
| save_name = gr.Textbox(label="Filter Name to Save") | |
| save_btn = gr.Button("πΎ Save Current Filter") | |
| saved_dropdown = gr.Dropdown(choices=names(False), label="Load Saved Filter Preset") | |
| load_btn = gr.Button("π Load Filter") | |
| delete_btn = gr.Button("ποΈ Delete Filter", variant="stop") | |
| filter_msg = gr.Markdown() | |
| with gr.Accordion("π Filter JSON Definition & Python Code Snippet", open=False): | |
| filter_json = gr.JSON(label="Current Filter Definition JSON") | |
| dev_code = gr.Code(label="Python Code Snippet for Developers", language="python") | |
| download_filter = gr.DownloadButton("π₯ Download Filtered Result", value=None) | |
| apply_builtin.click(run_builtin, [current_image, filter_name, blur_method, kernel_size, edge_method, low, high, brightness, contrast, saturation, hue_shift, channel], [after, current_definition, dev_code]).then(export_image, after, download_filter).then(lambda d: d, current_definition, filter_json) | |
| apply_kernel.click(preview_kernel, [current_image, kernel_text], [after, current_definition, dev_code]).then(export_image, after, download_filter).then(lambda d: d, current_definition, filter_json) | |
| apply_pipeline.click(preview_pipeline, [current_image, pipeline_text], [after, current_definition, dev_code]).then(export_image, after, download_filter).then(lambda d: d, current_definition, filter_json) | |
| save_btn.click(save_current_filter, [save_name, current_definition], [filter_msg, saved_dropdown]) | |
| load_btn.click(load_saved_filter, [current_image, saved_dropdown], [after, current_definition, pipeline_text]).then(export_image, after, download_filter).then(lambda d: d, current_definition, filter_json) | |
| delete_btn.click(delete_saved, saved_dropdown, [filter_msg, saved_dropdown]) | |
| with gr.Tab("π¬ Resolution & Color Analysis"): | |
| gr.Markdown("Compare high/low resolution RGB and grayscale representations, analyze pixel crops, and inspect intensity histograms.") | |
| with gr.Row(): | |
| percent = gr.Slider(5, 100, value=25, step=5, label="Low Resolution Percent", info="Downsample scale percentage") | |
| px = gr.Number(value=0, precision=0, label="Pixel Crop X Coordinate") | |
| py = gr.Number(value=0, precision=0, label="Pixel Crop Y Coordinate") | |
| analyze_btn = gr.Button("π¬ Run Analysis", variant="primary") | |
| with gr.Row(): | |
| high_rgb = gr.Image(label="High RGB Original", type="numpy") | |
| low_rgb = gr.Image(label="Low RGB Upsampled", type="numpy") | |
| with gr.Row(): | |
| high_gray = gr.Image(label="High Grayscale", type="numpy") | |
| low_gray = gr.Image(label="Low Grayscale", type="numpy") | |
| with gr.Row(): | |
| pix_high = gr.Dataframe(label="High RGB Pixel Values", row_count=5) | |
| pix_low = gr.Dataframe(label="Low RGB Pixel Values", row_count=5) | |
| hist = gr.Plot(label="Channel Intensity Histograms") | |
| stat_json = gr.JSON(label="Detailed Image Statistics") | |
| analyze_btn.click(analyze, [current_image, percent, px, py], [high_rgb, low_rgb, high_gray, low_gray, pix_high, pix_low, hist, stat_json]) | |
| with gr.Tab("π Geometric Transformations"): | |
| gr.Markdown("Apply affine geometric transformations including translation, rotation, scaling, and reflection while inspecting matrix parameters.") | |
| op = gr.Radio(["Translation", "Rotation", "Scaling", "Reflection"], value="Rotation", label="Transformation Operation") | |
| with gr.Group(): | |
| with gr.Row(): | |
| tx = gr.Slider(-300, 300, value=30, step=1, label="Translation X Offset (px)") | |
| ty = gr.Slider(-300, 300, value=30, step=1, label="Translation Y Offset (px)") | |
| border = gr.Radio(["constant", "reflect", "replicate"], value="constant", label="Border Extrapolation") | |
| with gr.Row(): | |
| angle = gr.Slider(0, 360, value=30, step=1, label="Rotation Angle (deg)") | |
| rot_scale = gr.Slider(0.1, 3, value=1, step=0.05, label="Rotation Scale Factor") | |
| cx = gr.Slider(0, 1, value=0.5, step=0.05, label="Center X Ratio") | |
| cy = gr.Slider(0, 1, value=0.5, step=0.05, label="Center Y Ratio") | |
| expand = gr.Checkbox(value=True, label="Expand Canvas Bounds") | |
| with gr.Row(): | |
| sx = gr.Slider(0.1, 4, value=1.2, step=0.05, label="Scale X Factor") | |
| sy = gr.Slider(0.1, 4, value=1.2, step=0.05, label="Scale Y Factor") | |
| interp = gr.Radio(["nearest", "linear", "cubic", "area"], value="linear", label="Interpolation Mode") | |
| flip = gr.Radio(["horizontal", "vertical", "both"], value="horizontal", label="Reflection Axis") | |
| trans_btn = gr.Button("π Apply Transformation", variant="primary") | |
| with gr.Row(): | |
| trans_before = gr.Image(label="Original Canvas", type="numpy") | |
| trans_after = gr.Image(label="Transformed Canvas", type="numpy") | |
| matrix = gr.JSON(label="Affine Transformation Matrix 2x3") | |
| trans_dl = gr.DownloadButton("π₯ Download Transformed Result", value=None) | |
| current_image.change(lambda x: x, current_image, trans_before) | |
| trans_btn.click(transform, [current_image, op, tx, ty, border, angle, rot_scale, cx, cy, expand, sx, sy, interp, flip], [trans_after, matrix]).then(export_image, trans_after, trans_dl) | |
| with gr.Tab("π§ͺ Morphological Operations"): | |
| gr.Markdown("Apply thresholding and mathematical morphology operations with custom structuring element kernels.") | |
| with gr.Group(): | |
| with gr.Row(): | |
| morph_op = gr.Dropdown(["Erosion", "Dilation", "Opening", "Closing", "Gradient", "Top-Hat", "Black-Hat"], value="Opening", label="Morphological Operation") | |
| thresh_method = gr.Radio(["Otsu", "Manual"], value="Otsu", label="Binarization Threshold Method") | |
| thresh_value = gr.Slider(0, 255, value=128, step=1, label="Manual Threshold Value") | |
| with gr.Row(): | |
| shape = gr.Radio(["rect", "ellipse", "cross"], value="rect", label="Kernel Structuring Shape") | |
| morph_size = gr.Slider(1, 31, value=5, step=2, label="Kernel Size", info="Must be an odd integer") | |
| iterations = gr.Slider(1, 10, value=1, step=1, label="Iteration Count") | |
| morph_btn = gr.Button("π§ͺ Apply Morphology", variant="primary") | |
| with gr.Row(): | |
| morph_before = gr.Image(label="Original Input", type="numpy") | |
| morph_after = gr.Image(label="Morphology Output", type="numpy") | |
| kernel_view = gr.JSON(label="Structuring Element Kernel Matrix") | |
| morph_dl = gr.DownloadButton("π₯ Download Result", value=None) | |
| current_image.change(lambda x: x, current_image, morph_before) | |
| morph_btn.click(morph, [current_image, morph_op, thresh_method, thresh_value, shape, morph_size, iterations], [morph_after, kernel_view]).then(export_image, morph_after, morph_dl) | |
| with gr.Tab("π Neural Style Transfer"): | |
| gr.Markdown("Transfer artistic textures from a style reference image to your content image using the TensorFlow Hub Magenta deep neural model.") | |
| with gr.Row(): | |
| style_upload = gr.Image(label="Custom Style Image Upload", type="numpy") | |
| style_choice = gr.Dropdown(choices=STYLE_SAMPLES, label="Synthetic Sample Style Presets") | |
| max_size = gr.Slider(128, 1024, value=512, step=64, label="Inference Resolution Max Size (px)", info="Higher resolution takes longer") | |
| style_btn = gr.Button("π Run Style Transfer", variant="primary") | |
| style_out = gr.Image(label="Stylized Result", type="numpy") | |
| style_msg = gr.Markdown() | |
| style_dl = gr.DownloadButton("π₯ Download Stylized Result", value=None) | |
| style_btn.click(run_style, [current_image, style_upload, style_choice, max_size], [style_out, style_msg]).then(export_image, style_out, style_dl) | |
| with gr.Tab("π Face AR Filters"): | |
| gr.Markdown("Apply landmark-anchored AR face overlays or design, preview, and save custom JSON AR filters.") | |
| with gr.Row(): | |
| ar_choice = gr.Dropdown(choices=ar_filter_names(True), value="Glasses", label="AR Filter Presets") | |
| ar_btn = gr.Button("π Apply AR Filter", variant="primary") | |
| with gr.Row(): | |
| ar_before = gr.Image(label="Original Face Input", type="numpy") | |
| ar_out = gr.Image(label="AR Overlay Result", type="numpy") | |
| current_image.change(lambda x: x, current_image, ar_before) | |
| with gr.Accordion("π¨ Custom AR Filter Designer & JSON Editor", open=False): | |
| ar_custom_text = gr.Textbox(value=DEFAULT_CUSTOM_AR_JSON, lines=8, label="Custom AR Filter JSON Definition") | |
| ar_preview_btn = gr.Button("ποΈ Preview Custom AR Filter") | |
| with gr.Row(): | |
| ar_save_name = gr.Textbox(label="AR Filter Name to Save") | |
| ar_save_btn = gr.Button("πΎ Save Custom AR Filter") | |
| ar_saved_dropdown = gr.Dropdown(choices=ar_filter_names(False), label="Load Saved AR Preset") | |
| ar_load_btn = gr.Button("π Load AR Filter") | |
| ar_delete_btn = gr.Button("ποΈ Delete AR Filter", variant="stop") | |
| ar_msg = gr.Markdown() | |
| ar_dl = gr.DownloadButton("π₯ Download AR Result", value=None) | |
| ar_btn.click(run_face, [current_image, ar_choice], [ar_out, ar_msg]).then(export_image, ar_out, ar_dl) | |
| ar_preview_btn.click(preview_ar_custom, [current_image, ar_custom_text], [ar_out, ar_msg]).then(export_image, ar_out, ar_dl) | |
| ar_save_btn.click(save_custom_ar_filter, [ar_save_name, ar_custom_text], [ar_msg, ar_choice, ar_saved_dropdown]) | |
| ar_load_btn.click(load_saved_ar_filter, [current_image, ar_saved_dropdown], [ar_out, ar_custom_text, ar_msg]).then(export_image, ar_out, ar_dl) | |
| ar_delete_btn.click(delete_saved_ar_filter, ar_saved_dropdown, [ar_msg, ar_choice, ar_saved_dropdown]) | |
| with gr.Tab("π¦ Batch Dataset Processing"): | |
| gr.Markdown("Apply built-in or custom filter pipelines to multiple images, folders, or zip dataset archives with a reproducible manifest.") | |
| batch_files = gr.File(label="Upload Images or Zip Archive", file_count="multiple", type="filepath") | |
| batch_dir = gr.Textbox(label="Optional Local Dataset Directory Path") | |
| batch_filter = gr.Dropdown(choices=names(True), value="Grayscale", label="Filter / Pipeline Selection") | |
| refresh_filters = gr.Button("π Refresh Filter List") | |
| batch_btn = gr.Button("π Process Batch Dataset", variant="primary") | |
| batch_zip = gr.File(label="Processed Output Zip + manifest.json") | |
| refresh_filters.click(lambda: gr.Dropdown(choices=names(True)), None, batch_filter) | |
| batch_btn.click(run_batch, [batch_files, batch_dir, batch_filter], batch_zip) | |
| with gr.Tab("π Guide & Documentation"): | |
| gr.Markdown( | |
| """## Persona & Feature Guide | |
| ### π¨ Photo Editors | |
| - Use **Photo Filters & Pipelines** for instant visual edits like Sepia, Vignette, Emboss, Cartoonify, or Channel Isolation. | |
| - Experiment with **Neural Style Transfer** to stylize portraits or landscapes with synthetic or custom reference artwork. | |
| - Have fun with **Face AR Filters** for accessories (Glasses, Visors, Crown, Pirate Eyepatch, Dog Ears). | |
| ### π¬ Scientists & Researchers | |
| - Use **Resolution & Color Analysis** to downsample images and evaluate RGB vs Grayscale degradation, intensity histograms, and pixel crop tables. | |
| - Perform reproducible **Morphological Operations** (Opening, Closing, Top-Hat, Black-Hat) with explicit structuring element kernels. | |
| - Process large experiment datasets with **Batch Dataset Processing** to export processed images alongside `manifest.json`. | |
| ### π» Developers | |
| - Use the **Python SDK** (`developer_api.py`) or **CLI** (`cli.py`) for command line processing. | |
| - Build multi-step JSON filter pipelines or custom AR landmark overlays in the Web UI, save them to disk, and export auto-generated Python code snippets. | |
| """ | |
| ) | |
| if __name__ == "__main__": | |
| demo.launch(ssr_mode=False, share=False) | |