blur_background / app.py
Jeevan-HM's picture
Modify lens blur
3fb37bd
raw
history blame
4.06 kB
import cv2
import numpy as np
import gradio as gr
from PIL import Image
from scipy.ndimage import gaussian_filter
from transformers import (
AutoImageProcessor,
AutoModelForDepthEstimation,
)
import torch
def resize_to_512(img: Image.Image) -> Image.Image:
return img.resize((512, 512)) if img.size != (512, 512) else img
def gaussian_blur(img: Image.Image, kernel_size: int):
img = resize_to_512(img)
img_cv = cv2.cvtColor(np.array(img), cv2.COLOR_RGB2BGR)
blurred = cv2.GaussianBlur(img_cv, (kernel_size | 1, kernel_size | 1), 0)
return cv2.cvtColor(blurred, cv2.COLOR_BGR2RGB)
# Load model once globally
depth_model_id = "depth-anything/Depth-Anything-V2-Small-hf"
processor = AutoImageProcessor.from_pretrained(depth_model_id)
depth_model = AutoModelForDepthEstimation.from_pretrained(depth_model_id)
def lens_blur(img: Image.Image, max_blur_radius: int):
img = resize_to_512(img)
original = np.array(img).astype(np.float32)
# Get depth map
inputs = processor(images=img, return_tensors="pt")
with torch.no_grad():
outputs = depth_model(**inputs)
predicted_depth = outputs.predicted_depth
depth = (
torch.nn.functional.interpolate(
predicted_depth.unsqueeze(1),
size=(512, 512),
mode="bicubic",
align_corners=False,
)
.squeeze()
.cpu()
.numpy()
)
# Normalize and invert depth
depth_norm = (depth - depth.min()) / (depth.max() - depth.min())
depth_inverted = 1.0 - depth_norm
# Dynamically scale blur strength using the slider
num_levels = 6 # More levels for smoother transitions
max_sigma = (
max_blur_radius / 2.0
) # Scale down to reasonable range (e.g. 0–25 β†’ 0–12.5 sigma)
blur_levels = np.linspace(0, max_sigma, num_levels)
blurred_images = [gaussian_filter(original, sigma=(s, s, 0)) for s in blur_levels]
# Blend based on depth
blurred_final = np.zeros_like(original, dtype=np.float32)
depth_scaled = depth_inverted * (num_levels - 1)
depth_int = np.floor(depth_scaled).astype(int)
depth_frac = depth_scaled - depth_int
for i in range(num_levels - 1):
mask = depth_int == i
alpha = depth_frac[mask]
for c in range(3):
blended = (
blurred_images[i][..., c][mask] * (1 - alpha)
+ blurred_images[i + 1][..., c][mask] * alpha
)
blurred_final[..., c][mask] = blended
return np.clip(blurred_final, 0, 255).astype(np.uint8)
# Separate update functions
def update_gaussian(img, kernel_size):
return gaussian_blur(img, kernel_size)
def update_lens(img, max_blur_radius):
return lens_blur(img, max_blur_radius)
def apply_blurs(img, kernel_size, max_blur_radius):
g_blurred = gaussian_blur(img, kernel_size)
l_blurred = lens_blur(img, max_blur_radius)
return g_blurred, l_blurred
with gr.Blocks() as demo:
gr.Markdown("## πŸŒ€ Apply Gaussian and Depth-Based Lens Blur")
with gr.Row():
image_input = gr.Image(type="pil", label="Upload Image")
with gr.Row():
kernel_slider = gr.Slider(1, 49, step=2, value=11, label="Gaussian Kernel Size")
lens_slider = gr.Slider(
1, 50, step=1, value=15, label="Max Lens Blur Intensity"
)
with gr.Row():
gaussian_output = gr.Image(label="Gaussian Blurred Image")
lens_output = gr.Image(label="Depth-Based Lens Blurred Image")
# Trigger both when image changes
image_input.change(
fn=apply_blurs,
inputs=[image_input, kernel_slider, lens_slider],
outputs=[gaussian_output, lens_output],
)
# Trigger only gaussian blur
kernel_slider.change(
fn=update_gaussian,
inputs=[image_input, kernel_slider],
outputs=gaussian_output,
)
# Trigger only lens blur
lens_slider.change(
fn=update_lens,
inputs=[image_input, lens_slider],
outputs=lens_output,
)
demo.launch()