import io import os import tempfile import cv2 import gradio as gr import numpy as np from PIL import Image, ImageSequence def image_to_sketch_gif(input_image: Image.Image): # Convert PIL image to OpenCV format open_cv_image = np.array(input_image.convert("RGB")) open_cv_image = cv2.cvtColor(open_cv_image, cv2.COLOR_RGB2BGR) # Convert to grayscale grayscale_image = cv2.cvtColor(open_cv_image, cv2.COLOR_BGR2GRAY) # Apply Gaussian blur blurred_image = cv2.GaussianBlur(grayscale_image, (5, 5), 0) # Use Canny Edge Detection edges = cv2.Canny(blurred_image, threshold1=50, threshold2=150) # Ensure binary format _, binary_sketch = cv2.threshold(edges, 128, 255, cv2.THRESH_BINARY) # Find connected components num_labels, labels, stats, _ = cv2.connectedComponentsWithStats( binary_sketch, connectivity=8 ) # Sort components by size (excluding the background, which is label 0) components = sorted( [(i, stats[i, cv2.CC_STAT_AREA]) for i in range(1, num_labels)], key=lambda x: x[1], reverse=True, ) # Initialize an empty canvas for accumulation accumulated_image = np.zeros_like(binary_sketch, dtype=np.uint8) # Store frames frames = [] for label, _ in components: # Add the current component to the accumulation accumulated_image[labels == label] = 255 # Convert OpenCV image to PIL image and append to frames pil_frame = Image.fromarray(255 - accumulated_image) frames.append(pil_frame.copy()) # Save GIF to a temporary file tmp_dir = tempfile.gettempdir() # Get system temp directory tmp_gif_path = os.path.join(tmp_dir, "sketch_animation.gif") frames[0].save( tmp_gif_path, format="GIF", save_all=True, append_images=frames[1:], duration=100, loop=0, ) return ( frames[0], frames, gr.Slider( value=0, maximum=len(frames) - 1, ), gr.Button(interactive=False), )