import gradio as gr from PIL import Image, ImageFilter import numpy as np import torch import cv2 from transformers import AutoImageProcessor, AutoModelForDepthEstimation # Load depth estimation model image_processor = AutoImageProcessor.from_pretrained("depth-anything/Depth-Anything-V2-Small-hf") model = AutoModelForDepthEstimation.from_pretrained("depth-anything/Depth-Anything-V2-Small-hf") def apply_gaussian_blur(image, mask): """Applies Gaussian blur to the background based on a user-drawn mask.""" if mask is None: return image # If no mask is provided, return the original image # Ensure mask is grayscale and resized to match image dimensions mask_pil = Image.fromarray(mask).convert("L").resize(image.size) mask_array = np.array(mask_pil) # Create a blurred background blurred_background = image.filter(ImageFilter.GaussianBlur(radius=15)) # Convert images to NumPy arrays img_array = np.array(image) blurred_array = np.array(blurred_background) # Create a boolean mask (foreground = True, background = False) foreground_mask = mask_array > 0 foreground_mask_3d = np.stack([foreground_mask] * 3, axis=-1) # Blend the original image with the blurred background final_image_array = np.where(foreground_mask_3d, img_array, blurred_array) final_image = Image.fromarray(final_image_array.astype(np.uint8)) return final_image def apply_lens_blur(image): """Applies depth-based lens blur using a pre-trained model.""" # Resize image to 512x512 for processing resized_image = image.resize((512, 512)) image_np = np.array(resized_image) # Prepare image for the model inputs = image_processor(images=resized_image, return_tensors="pt") with torch.no_grad(): outputs = model(**inputs) predicted_depth = outputs.predicted_depth # Interpolate depth map to match the image size prediction = torch.nn.functional.interpolate( predicted_depth.unsqueeze(1), size=resized_image.size[::-1], mode="bicubic", align_corners=False, ).squeeze() # Convert prediction to a NumPy array depth_map = prediction.cpu().numpy() # Normalize the depth map depth_norm = (depth_map - np.min(depth_map)) / (np.max(depth_map) - np.min(depth_map)) num_blur_levels = 5 blurred_layers = [] for i in range(num_blur_levels): sigma = i * 0.5 if sigma == 0: blurred = image_np else: blurred = cv2.GaussianBlur(image_np, (15, 15), sigmaX=sigma, sigmaY=sigma, borderType=cv2.BORDER_REPLICATE) blurred_layers.append(blurred) depth_indices = ((1 - depth_norm) * (num_blur_levels - 1)).astype(np.uint8) final_blurred_image = np.zeros_like(image_np) for y in range(image_np.shape[0]): for x in range(image_np.shape[1]): depth_index = depth_indices[y, x] final_blurred_image[y, x] = blurred_layers[depth_index][y, x] # Convert the final blurred image back to a PIL Image final_blurred_pil_image = Image.fromarray(final_blurred_image) return final_blurred_pil_image def process_image(image, mask, blur_type): """Processes the image based on the selected blur type.""" if blur_type == "Gaussian Blur": return apply_gaussian_blur(image, mask) elif blur_type == "Lens Blur": return apply_lens_blur(image) else: return image interface = gr.Interface( fn=process_image, inputs=[ gr.Image(type="pil", label="Upload an Image"), gr.Sketchpad(height=256, width=256, label="Draw Mask (Only for Gaussian Blur)"), gr.Radio(["Gaussian Blur", "Lens Blur"], label="Choose Blur Effect") ], outputs=gr.Image(type="pil"), title="Gaussian & Lens Blur Effects", description="Upload an image and select either Gaussian blur (with mask) or depth-based lens blur." ) if __name__ == "__main__": interface.launch(share=True)