sketch2pnml / ui /image_processor.py
sam0ed
Initial commit with Git LFS support
8eb0b3e
import gradio as gr
import os
import shutil
from typing import Optional
from .base import BaseUI
from config.path_config import (
DEMOS_DIR as DEMO_IMAGES_DIR, VISUALIZATIONS_DIR,
SUPPORTED_IMAGE_EXTENSIONS, SUPPORTED_CONFIG_EXTENSIONS,
ensure_directories_exist, get_working_image_path, get_working_config_path
)
from endpoints.converter import run_and_save_pipeline
class ImageProcessor(BaseUI):
"""Image Processor UI component"""
def __init__(self):
super().__init__()
self.demo_images = self._load_demo_images()
# UI components
self.demo_gallery = None
self.image_upload = None
self.yaml_upload = None
self.process_btn = None
self.output_gallery = None
self.output_message = None
# State variables
self.current_image = None
self.working_image = None
self.current_yaml = None
self.working_yaml = None
self.status_text = None
def _load_demo_images(self):
"""Load demo images from the demos directory"""
try:
demo_images = [os.path.join(DEMO_IMAGES_DIR, f) for f in os.listdir(DEMO_IMAGES_DIR)
if f.endswith(SUPPORTED_IMAGE_EXTENSIONS)]
if not demo_images:
print("No demo images found in the demos directory. Please add some images to", DEMO_IMAGES_DIR)
return []
return demo_images
except Exception as e:
print(f"Error loading demo images: {e}")
return []
def create_interface(self) -> gr.TabItem:
"""Create the Image Processor interface"""
with gr.TabItem("Image Processor") as tab:
gr.Markdown("## Sketch to PNML Converter")
# State variables for the app
self.current_image = self.create_state_var("current_image", None)
self.working_image = self.create_state_var("working_image", None)
self.current_yaml = self.create_state_var("current_yaml", None)
self.working_yaml = self.create_state_var("working_yaml", None)
self.status_text = self.create_state_var("status_text", "")
# Demo section at the top
with gr.Row():
with gr.Column():
gr.Markdown("### Demo Images")
if self.demo_images:
self.demo_gallery = gr.Gallery(
value=self.demo_images,
columns=5,
object_fit="contain",
height="150px",
show_label=False
)
else:
gr.Markdown(f"*No demo images available. Place image files in the '{DEMO_IMAGES_DIR}' directory.*")
# Input and output sections below
with gr.Row():
# Left column for inputs
with gr.Column():
# Image upload section
gr.Markdown("### Or Upload Your Own Image")
self.image_upload = self.create_file_upload(
"Upload Image",
["image"]
)
# YAML config upload section
gr.Markdown("### Upload YAML Configuration")
self.yaml_upload = self.create_file_upload(
"Upload YAML Config",
list(SUPPORTED_CONFIG_EXTENSIONS)
)
# Process button
self.process_btn = self.create_button("Process Files", variant="primary", size="lg")
# Right column for outputs
with gr.Column():
gr.Markdown("### Output Visualizations")
self.output_gallery = gr.Gallery(
label="Processing Results",
columns=1,
height=400,
show_label=True
)
self.output_message = gr.Markdown("")
self.setup_event_handlers()
return tab
def setup_event_handlers(self):
"""Setup event handlers for the Image Processor"""
# Event handlers
if self.demo_images and self.demo_gallery:
self.demo_gallery.select(
self._select_demo_image,
inputs=[self.current_yaml, self.working_yaml],
outputs=[self.current_image, self.working_image, self.current_yaml, self.working_yaml, self.status_text]
)
self.image_upload.change(
self._upload_image,
inputs=[self.image_upload, self.current_yaml, self.working_yaml],
outputs=[self.current_image, self.working_image, self.current_yaml, self.working_yaml, self.status_text]
)
self.yaml_upload.change(
self._upload_yaml,
inputs=[self.yaml_upload, self.current_image, self.working_image],
outputs=[self.current_image, self.working_image, self.current_yaml, self.working_yaml, self.status_text]
)
self.process_btn.click(
self._process_files,
inputs=[self.working_image, self.working_yaml],
outputs=[self.output_gallery, self.output_message]
)
# Update status message when it changes
self.status_text.change(lambda x: x, inputs=[self.status_text], outputs=[self.output_message])
def _save_as_working_image(self, image_path):
"""Save the selected image as the working image"""
# Ensure the extension is preserved when copying
_, ext = os.path.splitext(image_path)
working_image_path = get_working_image_path(ext)
shutil.copy(image_path, working_image_path)
return working_image_path
def _save_as_working_yaml(self, yaml_path):
"""Save the uploaded YAML as the working configuration"""
_, ext = os.path.splitext(yaml_path)
working_yaml_path = get_working_config_path(ext)
shutil.copy(yaml_path, working_yaml_path)
return working_yaml_path
def _clear_visualizations(self):
"""Clear the visualizations directory"""
# Create the directory if it doesn't exist
if not os.path.exists(VISUALIZATIONS_DIR):
os.makedirs(VISUALIZATIONS_DIR, exist_ok=True)
# Remove existing files
for file in os.listdir(VISUALIZATIONS_DIR):
file_path = os.path.join(VISUALIZATIONS_DIR, file)
if os.path.isfile(file_path):
os.remove(file_path)
def _process_files(self, image_path, yaml_path):
"""Process the uploaded files and run the pipeline"""
if image_path is None or yaml_path is None:
return None, "Both image and YAML configuration file are required."
# Clear previous visualization results
self._clear_visualizations()
# Run the pipeline
try:
run_and_save_pipeline(config_path=yaml_path, image_path=image_path)
# Get visualization results
vis_files = [os.path.join(VISUALIZATIONS_DIR, f) for f in os.listdir(VISUALIZATIONS_DIR) if f.endswith('.png')]
if not vis_files:
return None, "No visualization results were generated."
return vis_files, "Processing completed successfully!"
except Exception as e:
return None, self.handle_error(e, "during processing")
def _select_demo_image(self, evt: gr.SelectData, current_yaml=None, working_yaml=None):
"""Handle selection of a demo image"""
image_path = self.demo_images[evt.index]
if image_path:
working_image_path = self._save_as_working_image(image_path)
status = "Demo image selected."
if working_yaml:
status += " Ready to process!"
else:
status += " Please upload a YAML configuration file."
return image_path, working_image_path, current_yaml, working_yaml, status
return None, None, current_yaml, working_yaml, ""
def _upload_image(self, file_obj, current_yaml=None, working_yaml=None):
"""Handle upload of a custom image"""
if file_obj is not None:
# Save the uploaded file to working image
_, ext = os.path.splitext(file_obj.name)
working_image_path = get_working_image_path(ext)
shutil.copy(file_obj.name, working_image_path)
status = "Custom image uploaded."
if working_yaml:
status += " Ready to process!"
else:
status += " Please upload a YAML configuration file."
return file_obj.name, working_image_path, current_yaml, working_yaml, status
return None, None, current_yaml, working_yaml, ""
def _upload_yaml(self, file_obj, current_image, working_image):
"""Handle upload of a YAML file"""
if file_obj is not None:
# Save the uploaded YAML as the working config
yaml_path = file_obj.name
working_yaml_path = self._save_as_working_yaml(yaml_path)
if working_image is not None:
return current_image, working_image, yaml_path, working_yaml_path, "Both files uploaded. Ready to process!"
else:
return None, None, yaml_path, working_yaml_path, "YAML file uploaded. Please select or upload an image."
return current_image, working_image, None, None, ""