"""Gradio based ui for Copaint pdf generator. """ import gradio as gr import shutil import tempfile from gradio_pdf import PDF from importlib.resources import files from pathlib import Path from copaint.copaint import image_to_pdf import torchvision import torch fromPIltoTensor = torchvision.transforms.ToTensor() fromTensortoPIL = torchvision.transforms.ToPILImage() def add_grid_to_image(image, h_cells, w_cells): # image is a torch tensor of shape (3, h, w) image = image.convert("RGB") image = fromPIltoTensor(image) grid_color = torch.tensor([16,15,46]).unsqueeze(1).unsqueeze(1) / 255.0 h,w = image.shape[1:] thickness = max(min(1, int(min(h,w)/100)), 1) print("thickness, h, w", thickness, h, w) for i in range(h_cells+1): idx_i = int(i*h/h_cells) image[:, idx_i-thickness:idx_i+thickness, :] = grid_color for j in range(w_cells+1): idx_j = int(j*w/w_cells) image[:, :, idx_j-thickness:idx_j+thickness] = grid_color image = fromTensortoPIL(image) return image def canvas_ratio(image, h_cells, w_cells): w,h = image.size aspect_ratio = w/h if aspect_ratio > 1: aspect_ratio = 1/aspect_ratio # find nearest aspect ratio in the list of predefined ones predefined_aspect_ratios = [2/3, 1/2, 1/1, 5/6, 4/5, 5/7] predefined_aspect_ratios_str = ["2:3", "1:2", "1:1", "5:6", "4:5", "5:7"] min_diff = float('inf') closest_ratio_idx = None for idx, ratio in enumerate(predefined_aspect_ratios): diff = abs(aspect_ratio - ratio) if diff < min_diff: min_diff = diff closest_ratio_idx = idx closest_ratio_str = predefined_aspect_ratios_str[closest_ratio_idx] if min_diff > 0.1: return None else: example_str = "" if closest_ratio_str == "1:1": if h_cells == 2 and h_cells == 2: example_str = " (for example, a 6” by 6” canvas)" if h_cells == 3 and h_cells == 3: example_str = " (for example, an 8” by 8” canvas)" elif closest_ratio_str == "5:6": example_str = " (for example, a 10” by 12” canvas)" elif closest_ratio_str == "3:4": if (h_cells == 3 and w_cells == 4) or (h_cells == 4 and w_cells == 3): example_str = " (for example, a 6” by 8” canvas)" if (h_cells == 4 and w_cells == 6) or (h_cells == 6 and w_cells == 4): example_str = " (for example, an 18” by 24”, or a 12” by 16” canvas)" elif closest_ratio_str == "2:3" and ((h_cells == 6 and w_cells == 9) or (h_cells == 9 and w_cells == 6)): example_str = " (for example, a 24” by 36” canvas)" return f"Preparing your canvas: *choose a canvas with a {closest_ratio_str} ratio to respect your design’s size{example_str}.*" def add_grid_and_display_ratio(image, h_cells, w_cells): if image is None: return None, gr.update(visible=False) return add_grid_to_image(image, h_cells, w_cells), gr.update(visible=True, value=canvas_ratio(image, h_cells, w_cells)) def process_copaint( input_image, h_cells=None, w_cells=None, a4=False, high_res=False, cell_size_in_cm=None, min_cell_size_in_cm=2, copaint_name="", copaint_logo=None, ): """Process the input and generate CoPaint PDF""" # Create temporary directories for processing temp_input_dir = tempfile.mkdtemp() temp_output_dir = tempfile.mkdtemp() try: # Save uploaded images to temp directory input_path = Path(temp_input_dir) / "input_image.png" input_image.save(input_path) logo_path = None if copaint_logo is not None: logo_path = Path(temp_input_dir) / "logo.png" copaint_logo.save(logo_path) else: # Use default logo path from the package logo_path = files("copaint.static") / "logo_copaint.png" if copaint_name == "" or copaint_name is None: from copaint.cli import default_identifier copaint_name = default_identifier() if a4 == "A4": a4 = True else: a4 = False # Generate the PDF pdf_path = image_to_pdf( input_image=str(input_path), logo_image=str(logo_path), outputfolder=temp_output_dir, h_cells=h_cells, w_cells=w_cells, unique_identifier=copaint_name, cell_size_in_cm=cell_size_in_cm, a4=a4, high_res=high_res, min_cell_size_in_cm=min_cell_size_in_cm ) return pdf_path, None # Return path and no error except Exception as e: # Return error message return None, f"Error generating PDF: {str(e)}" finally: # Clean up temporary input directory shutil.rmtree(temp_input_dir) def build_gradio_ui(): # Create Gradio Interface with gr.Blocks(title="CoPaint Generator", theme='NoCrypt/miku') as demo: gr.Markdown("# 🤖 CoPaint Generator") gr.Markdown("Upload an image with your painting design and set grid parameters to generate a CoPaint PDF template 🖨️📄✂️ for your next collaborative painting activities. 🎨🖌️") # --- inputs --- with gr.Row(equal_height=True): # Upload Design Template with gr.Column(scale=2): input_image = gr.Image(type="pil", label="Upload Your Design") with gr.Column(scale=1): # Grid with gr.Tab("Grid Layout"): gr.Markdown("