Spaces:
Sleeping
Sleeping
Create app.py
Browse files
app.py
ADDED
@@ -0,0 +1,216 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import gradio as gr
|
2 |
+
import torch
|
3 |
+
import numpy as np
|
4 |
+
from PIL import Image
|
5 |
+
import cv2
|
6 |
+
from transformers import pipeline
|
7 |
+
import os
|
8 |
+
|
9 |
+
# Load models
|
10 |
+
def load_models():
|
11 |
+
# Load segmentation model
|
12 |
+
segmenter = pipeline("image-segmentation", model="facebook/maskformer-swin-base-ade")
|
13 |
+
|
14 |
+
# Load depth estimation model
|
15 |
+
depth_estimator = pipeline("depth-estimation", model="intel/dpt-large")
|
16 |
+
|
17 |
+
return segmenter, depth_estimator
|
18 |
+
|
19 |
+
# Create binary mask
|
20 |
+
def create_binary_mask(segmentation_results, image_np, target_class="person"):
|
21 |
+
# Initialize empty mask with black background
|
22 |
+
mask = np.zeros((image_np.shape[0], image_np.shape[1]), dtype=np.uint8)
|
23 |
+
|
24 |
+
# Look for segments with target class
|
25 |
+
found = False
|
26 |
+
for segment in segmentation_results:
|
27 |
+
if target_class.lower() in segment['label'].lower():
|
28 |
+
# Convert segment mask to numpy array
|
29 |
+
segment_mask = np.array(segment['mask'])
|
30 |
+
# Convert grayscale to binary (255 for white)
|
31 |
+
binary_mask = np.where(segment_mask > 0.5, 255, 0).astype(np.uint8)
|
32 |
+
# Add to overall mask
|
33 |
+
mask = cv2.bitwise_or(mask, binary_mask)
|
34 |
+
found = True
|
35 |
+
|
36 |
+
# If target class not found, use the largest segment
|
37 |
+
if not found:
|
38 |
+
largest_area = 0
|
39 |
+
largest_mask = None
|
40 |
+
|
41 |
+
for segment in segmentation_results:
|
42 |
+
segment_mask = np.array(segment['mask'])
|
43 |
+
binary_mask = np.where(segment_mask > 0.5, 255, 0).astype(np.uint8)
|
44 |
+
area = np.sum(binary_mask > 0)
|
45 |
+
|
46 |
+
if area > largest_area:
|
47 |
+
largest_area = area
|
48 |
+
largest_mask = binary_mask
|
49 |
+
|
50 |
+
if largest_mask is not None:
|
51 |
+
mask = largest_mask
|
52 |
+
|
53 |
+
return mask
|
54 |
+
|
55 |
+
# Apply Gaussian blur to background
|
56 |
+
def apply_gaussian_blur_to_background(image_np, mask, sigma=15):
|
57 |
+
# Create a blurred version of the entire image
|
58 |
+
blurred_image = cv2.GaussianBlur(image_np, (0, 0), sigma)
|
59 |
+
|
60 |
+
# Ensure mask is in correct format
|
61 |
+
if len(mask.shape) == 3 and mask.shape[2] == 3:
|
62 |
+
mask_gray = cv2.cvtColor(mask, cv2.COLOR_RGB2GRAY)
|
63 |
+
else:
|
64 |
+
mask_gray = mask.copy()
|
65 |
+
|
66 |
+
# Normalize mask to range 0-1
|
67 |
+
if mask_gray.max() > 1:
|
68 |
+
mask_gray = mask_gray / 255.0
|
69 |
+
|
70 |
+
# Expand mask dimensions for elementwise multiplication
|
71 |
+
mask_3channel = np.stack([mask_gray] * 3, axis=2)
|
72 |
+
|
73 |
+
# Combine original foreground with blurred background
|
74 |
+
result = image_np * mask_3channel + blurred_image * (1 - mask_3channel)
|
75 |
+
result = result.astype(np.uint8)
|
76 |
+
|
77 |
+
return result
|
78 |
+
|
79 |
+
# Normalize depth map
|
80 |
+
def normalize_depth_map(depth_map):
|
81 |
+
depth_min = depth_map.min()
|
82 |
+
depth_max = depth_map.max()
|
83 |
+
normalized_depth = (depth_map - depth_min) / (depth_max - depth_min)
|
84 |
+
return normalized_depth
|
85 |
+
|
86 |
+
# Apply depth-based blur
|
87 |
+
def apply_depth_based_blur(image, depth_map, max_blur=25):
|
88 |
+
# Create output image
|
89 |
+
result = np.zeros_like(image)
|
90 |
+
|
91 |
+
# Apply blur with intensity proportional to depth
|
92 |
+
for blur_size in range(1, max_blur + 1, 2): # Odd numbers for kernel size
|
93 |
+
# Create a mask for pixels that should receive this blur level
|
94 |
+
if blur_size == 1:
|
95 |
+
mask = (depth_map <= blur_size / max_blur).astype(np.float32)
|
96 |
+
else:
|
97 |
+
lower_bound = (blur_size - 2) / max_blur
|
98 |
+
upper_bound = blur_size / max_blur
|
99 |
+
mask = ((depth_map > lower_bound) & (depth_map <= upper_bound)).astype(np.float32)
|
100 |
+
|
101 |
+
# Skip if no pixels in this range
|
102 |
+
if not np.any(mask):
|
103 |
+
continue
|
104 |
+
|
105 |
+
# Apply Gaussian blur with current kernel size
|
106 |
+
if blur_size > 1:
|
107 |
+
blurred = cv2.GaussianBlur(image, (blur_size, blur_size), 0)
|
108 |
+
mask_3d = np.stack([mask] * 3, axis=2)
|
109 |
+
result += (blurred * mask_3d).astype(np.uint8)
|
110 |
+
else:
|
111 |
+
mask_3d = np.stack([mask] * 3, axis=2)
|
112 |
+
result += (image * mask_3d).astype(np.uint8)
|
113 |
+
|
114 |
+
return result
|
115 |
+
|
116 |
+
# Process function for Gradio
|
117 |
+
def process_image(input_image, blur_effect_type, blur_strength, target_class):
|
118 |
+
# Load models if not already loaded
|
119 |
+
if not hasattr(process_image, "models_loaded"):
|
120 |
+
process_image.segmenter, process_image.depth_estimator = load_models()
|
121 |
+
process_image.models_loaded = True
|
122 |
+
|
123 |
+
# Convert to numpy array
|
124 |
+
image_np = np.array(input_image)
|
125 |
+
|
126 |
+
# Process based on selected effect
|
127 |
+
if blur_effect_type == "Gaussian Background Blur":
|
128 |
+
# Segment the image
|
129 |
+
segmentation_results = process_image.segmenter(input_image)
|
130 |
+
|
131 |
+
# Create binary mask
|
132 |
+
binary_mask = create_binary_mask(segmentation_results, image_np, target_class)
|
133 |
+
|
134 |
+
# Apply Gaussian blur to background
|
135 |
+
result = apply_gaussian_blur_to_background(image_np, binary_mask, sigma=blur_strength)
|
136 |
+
|
137 |
+
return result
|
138 |
+
|
139 |
+
elif blur_effect_type == "Depth-Based Lens Blur":
|
140 |
+
# Resize for depth estimation
|
141 |
+
depth_input = cv2.resize(image_np, (512, 512))
|
142 |
+
|
143 |
+
# Get depth map
|
144 |
+
depth_result = process_image.depth_estimator(depth_input)
|
145 |
+
depth_map = np.array(depth_result["depth"])
|
146 |
+
|
147 |
+
# Normalize depth map
|
148 |
+
normalized_depth = normalize_depth_map(depth_map)
|
149 |
+
|
150 |
+
# Apply depth-based blur
|
151 |
+
result = apply_depth_based_blur(depth_input, normalized_depth, max_blur=blur_strength)
|
152 |
+
|
153 |
+
# Resize back to original dimensions if needed
|
154 |
+
if image_np.shape[:2] != (512, 512):
|
155 |
+
result = cv2.resize(result, (image_np.shape[1], image_np.shape[0]))
|
156 |
+
|
157 |
+
return result
|
158 |
+
|
159 |
+
else:
|
160 |
+
return image_np # Return original if no effect selected
|
161 |
+
|
162 |
+
# Create Gradio interface
|
163 |
+
demo = gr.Blocks(title="Image Blur Effects")
|
164 |
+
|
165 |
+
with demo:
|
166 |
+
gr.Markdown("# Image Blur Effects using Segmentation and Depth Estimation")
|
167 |
+
gr.Markdown("Upload an image to apply different blur effects. For best results, use an image with a clear foreground subject.")
|
168 |
+
|
169 |
+
with gr.Row():
|
170 |
+
input_image = gr.Image(label="Input Image", type="pil")
|
171 |
+
output_image = gr.Image(label="Output Image")
|
172 |
+
|
173 |
+
with gr.Row():
|
174 |
+
blur_effect_type = gr.Radio(
|
175 |
+
["Gaussian Background Blur", "Depth-Based Lens Blur"],
|
176 |
+
label="Blur Effect Type",
|
177 |
+
value="Gaussian Background Blur"
|
178 |
+
)
|
179 |
+
|
180 |
+
blur_strength = gr.Slider(
|
181 |
+
minimum=5,
|
182 |
+
maximum=45,
|
183 |
+
step=2,
|
184 |
+
value=15,
|
185 |
+
label="Blur Strength"
|
186 |
+
)
|
187 |
+
|
188 |
+
target_class = gr.Textbox(
|
189 |
+
label="Target Class (for segmentation)",
|
190 |
+
value="person",
|
191 |
+
placeholder="e.g., person, cat, dog"
|
192 |
+
)
|
193 |
+
|
194 |
+
process_btn = gr.Button("Apply Effect")
|
195 |
+
process_btn.click(
|
196 |
+
fn=process_image,
|
197 |
+
inputs=[input_image, blur_effect_type, blur_strength, target_class],
|
198 |
+
outputs=output_image
|
199 |
+
)
|
200 |
+
|
201 |
+
gr.Markdown("""
|
202 |
+
## How to use:
|
203 |
+
1. Upload an image with a clear foreground subject
|
204 |
+
2. Choose a blur effect type:
|
205 |
+
- **Gaussian Background Blur**: Blurs the background while keeping the foreground sharp
|
206 |
+
- **Depth-Based Lens Blur**: Creates a realistic lens blur effect based on depth estimation
|
207 |
+
3. Adjust the blur strength
|
208 |
+
4. For Gaussian Background Blur, specify the target class to identify the foreground (e.g., person, cat, dog)
|
209 |
+
5. Click "Apply Effect"
|
210 |
+
""")
|
211 |
+
|
212 |
+
# Initialize models
|
213 |
+
segmenter, depth_estimator = load_models()
|
214 |
+
|
215 |
+
# Launch the app
|
216 |
+
demo.launch()
|