|
import gradio as gr |
|
import torch |
|
import numpy as np |
|
from PIL import Image |
|
from scipy.ndimage import gaussian_filter |
|
from transformers import pipeline |
|
|
|
def preprocess_image(image): |
|
"""Resize and convert image to PIL format if needed.""" |
|
if isinstance(image, np.ndarray): |
|
image = Image.fromarray(image) |
|
|
|
|
|
image = image.resize((512, 512)) |
|
return image |
|
|
|
def segment_image(image, model_name="yolov8n-seg"): |
|
""" |
|
Perform instance segmentation on the input image using YOLO segmentation model. |
|
|
|
Args: |
|
image (PIL.Image): Input image |
|
model_name (str): Name of the YOLO segmentation model |
|
|
|
Returns: |
|
numpy.ndarray: Segmentation mask with instance segmentation |
|
""" |
|
from ultralytics import YOLO |
|
import numpy as np |
|
import torch |
|
|
|
|
|
model = YOLO(model_name) |
|
|
|
|
|
results = model(image) |
|
|
|
|
|
mask = np.zeros(image.size[::-1], dtype=np.uint8) |
|
|
|
|
|
for result in results: |
|
|
|
masks = result.masks |
|
|
|
if masks is not None: |
|
|
|
for single_mask in masks: |
|
|
|
mask_array = single_mask.data.cpu().numpy().squeeze() |
|
mask_array = (mask_array > 0.5).astype(np.uint8) |
|
|
|
|
|
if mask_array.shape != mask.shape: |
|
from PIL import Image |
|
mask_array = np.array( |
|
Image.fromarray(mask_array).resize( |
|
image.size[::-1], |
|
Image.NEAREST |
|
) |
|
) |
|
|
|
|
|
mask = np.maximum(mask, mask_array) |
|
|
|
return mask |
|
|
|
def process_image(image, blur_type, sigma=15): |
|
"""Process image based on blur type.""" |
|
|
|
pil_image = preprocess_image(image) |
|
|
|
|
|
if blur_type == "Gaussian Background Blur": |
|
|
|
segmentation_mask = segment_image(pil_image) |
|
|
|
|
|
mask_3d = np.stack([segmentation_mask] * 3, axis=2) |
|
|
|
|
|
image_array = np.array(pil_image) |
|
blurred = np.zeros_like(image_array) |
|
for channel in range(3): |
|
blurred[:, :, channel] = gaussian_filter(image_array[:, :, channel], sigma=sigma) |
|
|
|
|
|
result = image_array * mask_3d + blurred * (1 - mask_3d) |
|
result = Image.fromarray(result.astype(np.uint8)) |
|
|
|
elif blur_type == "Depth-Aware Lens Blur": |
|
result = apply_depth_aware_blur(pil_image, max_sigma=sigma) |
|
else: |
|
result = pil_image |
|
|
|
return result |
|
|
|
def apply_gaussian_blur(image, sigma=15): |
|
"""Apply Gaussian blur to the background.""" |
|
|
|
image_array = np.array(image) |
|
|
|
|
|
segmentation_mask = segment_image(image) |
|
|
|
|
|
foreground_mask = (segmentation_mask == 24).astype(np.uint8) |
|
|
|
|
|
blurred = np.zeros_like(image_array) |
|
for channel in range(3): |
|
blurred[:, :, channel] = gaussian_filter(image_array[:, :, channel], sigma=sigma) |
|
|
|
|
|
mask_3d = np.stack([foreground_mask] * 3, axis=2) |
|
result = image_array * mask_3d + blurred * (1 - mask_3d) |
|
|
|
return Image.fromarray(result.astype(np.uint8)) |
|
|
|
def estimate_depth(image, model_name="depth-anything/Depth-Anything-V2-Small-hf"): |
|
"""Estimate depth of the image.""" |
|
depth_estimator = pipeline( |
|
task="depth-estimation", |
|
model=model_name, |
|
torch_dtype=torch.float16 if torch.cuda.is_available() else torch.float32 |
|
) |
|
|
|
depth_output = depth_estimator(image) |
|
depth_map = np.array(depth_output["depth"]) |
|
|
|
|
|
depth_map = (depth_map - depth_map.min()) / (depth_map.max() - depth_map.min()) |
|
|
|
return depth_map |
|
|
|
def apply_depth_aware_blur(image, max_sigma=10, min_sigma=0): |
|
"""Apply depth-aware blur to the image (REVERSED version).""" |
|
|
|
depth_map = estimate_depth(image) |
|
|
|
image_array = np.array(image) |
|
blurred = np.zeros_like(image_array, dtype=np.float32) |
|
|
|
|
|
sigmas = np.interp(depth_map, [0, 1], [min_sigma, max_sigma]) |
|
|
|
|
|
blur_stack = {} |
|
for sigma in np.unique(sigmas): |
|
if sigma > 0: |
|
blurred_layer = np.zeros_like(image_array, dtype=np.float32) |
|
for channel in range(3): |
|
blurred_layer[:, :, channel] = gaussian_filter( |
|
image_array[:, :, channel].astype(np.float32), |
|
sigma=sigma |
|
) |
|
blur_stack[sigma] = blurred_layer |
|
|
|
|
|
for sigma in np.unique(sigmas): |
|
if sigma > 0: |
|
mask = (sigmas == sigma) |
|
mask_3d = np.stack([mask] * 3, axis=2) |
|
blurred += mask_3d * blur_stack[sigma] |
|
else: |
|
mask = (sigmas == 0) |
|
mask_3d = np.stack([mask] * 3, axis=2) |
|
blurred += mask_3d * image_array |
|
|
|
return Image.fromarray(blurred.astype(np.uint8)) |
|
|
|
|
|
|
|
|
|
def create_blur_app(): |
|
with gr.Blocks() as demo: |
|
gr.Markdown("# Image Blur Effects") |
|
|
|
with gr.Row(): |
|
input_image = gr.Image(label="Input Image", type="pil") |
|
output_image = gr.Image(label="Processed Image") |
|
|
|
with gr.Row(): |
|
blur_type = gr.Dropdown( |
|
choices=[ |
|
"Gaussian Background Blur", |
|
"Depth-Aware Lens Blur" |
|
], |
|
label="Blur Type" |
|
) |
|
sigma = gr.Slider( |
|
minimum=0, |
|
maximum=30, |
|
value=15, |
|
label="Blur Intensity" |
|
) |
|
|
|
process_btn = gr.Button("Apply Blur Effect") |
|
|
|
process_btn.click( |
|
fn=process_image, |
|
inputs=[input_image, blur_type, sigma], |
|
outputs=output_image |
|
) |
|
|
|
return demo |
|
|
|
|
|
if __name__ == "__main__": |
|
demo = create_blur_app() |
|
demo.launch() |