"""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)