Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
@@ -16,242 +16,6 @@ seg_model = AutoModelForSemanticSegmentation.from_pretrained("nvidia/segformer-b
|
|
16 |
depth_processor = DPTImageProcessor.from_pretrained("Intel/dpt-large")
|
17 |
depth_model = DPTForDepthEstimation.from_pretrained("Intel/dpt-large")
|
18 |
|
19 |
-
def apply_gaussian_blur(image, mask, sigma=15):
|
20 |
-
"""Apply Gaussian blur to the background of an image based on a mask."""
|
21 |
-
# Convert mask to binary (0 and 255)
|
22 |
-
if mask.max() <= 1.0:
|
23 |
-
binary_mask = (mask * 255).astype(np.uint8)
|
24 |
-
else:
|
25 |
-
binary_mask = mask.astype(np.uint8)
|
26 |
-
|
27 |
-
# Create a blurred version of the entire image
|
28 |
-
blurred = cv2.GaussianBlur(image, (0, 0), sigma)
|
29 |
-
|
30 |
-
# Resize mask to match image dimensions if needed
|
31 |
-
if binary_mask.shape[:2] != image.shape[:2]:
|
32 |
-
binary_mask = cv2.resize(binary_mask, (image.shape[1], image.shape[0]))
|
33 |
-
|
34 |
-
# Create a 3-channel mask if the input mask is single-channel
|
35 |
-
if len(binary_mask.shape) == 2:
|
36 |
-
mask_3ch = np.stack([binary_mask, binary_mask, binary_mask], axis=2)
|
37 |
-
else:
|
38 |
-
mask_3ch = binary_mask
|
39 |
-
|
40 |
-
# Normalize mask to range [0, 1]
|
41 |
-
mask_3ch = mask_3ch / 255.0
|
42 |
-
|
43 |
-
# Combine original image (foreground) with blurred image (background) using the mask
|
44 |
-
result = image * mask_3ch + blurred * (1 - mask_3ch)
|
45 |
-
|
46 |
-
return result.astype(np.uint8)
|
47 |
-
|
48 |
-
def apply_depth_blur(image, depth_map, max_sigma=25):
|
49 |
-
"""Apply variable Gaussian blur based on depth map."""
|
50 |
-
# Normalize depth map to range [0, 1]
|
51 |
-
if depth_map.max() > 1.0:
|
52 |
-
depth_norm = depth_map / depth_map.max()
|
53 |
-
else:
|
54 |
-
depth_norm = depth_map
|
55 |
-
|
56 |
-
# Resize depth map to match image dimensions if needed
|
57 |
-
if depth_norm.shape[:2] != image.shape[:2]:
|
58 |
-
depth_norm = cv2.resize(depth_norm, (image.shape[1], image.shape[0]))
|
59 |
-
|
60 |
-
# Create output image
|
61 |
-
result = np.zeros_like(image)
|
62 |
-
|
63 |
-
# Apply different blur levels based on depth
|
64 |
-
for sigma in range(1, int(max_sigma) + 1, 2):
|
65 |
-
# Create a mask for pixels at this depth level
|
66 |
-
lower_bound = (sigma - 1) / max_sigma
|
67 |
-
upper_bound = (sigma + 1) / max_sigma
|
68 |
-
mask = np.logical_and(depth_norm >= lower_bound, depth_norm <= upper_bound).astype(np.float32)
|
69 |
-
|
70 |
-
# Skip if no pixels at this depth
|
71 |
-
if not np.any(mask):
|
72 |
-
continue
|
73 |
-
|
74 |
-
# Blur the image with current sigma
|
75 |
-
blurred = cv2.GaussianBlur(image, (0, 0), sigma)
|
76 |
-
|
77 |
-
# Create a 3-channel mask if the input mask is single-channel
|
78 |
-
if len(mask.shape) == 2:
|
79 |
-
mask_3ch = np.stack([mask, mask, mask], axis=2)
|
80 |
-
else:
|
81 |
-
mask_3ch = mask
|
82 |
-
|
83 |
-
# Add the blurred pixels at this depth to the result
|
84 |
-
result += (blurred * mask_3ch).astype(np.uint8)
|
85 |
-
|
86 |
-
# Fill in any missing pixels (where sum of all masks < 1)
|
87 |
-
total_mask = np.zeros_like(depth_norm)
|
88 |
-
for sigma in range(1, int(max_sigma) + 1, 2):
|
89 |
-
lower_bound = (sigma - 1) / max_sigma
|
90 |
-
upper_bound = (sigma + 1) / max_sigma
|
91 |
-
mask = np.logical_and(depth_norm >= lower_bound, depth_norm <= upper_bound).astype(np.float32)
|
92 |
-
total_mask += mask
|
93 |
-
|
94 |
-
missing_mask = (total_mask < 0.5).astype(np.float32)
|
95 |
-
if np.any(missing_mask):
|
96 |
-
missing_mask_3ch = np.stack([missing_mask, missing_mask, missing_mask], axis=2)
|
97 |
-
result += (image * missing_mask_3ch).astype(np.uint8)
|
98 |
-
|
99 |
-
return result
|
100 |
-
|
101 |
-
def get_segmentation_mask(image_pil):
|
102 |
-
"""Get segmentation mask for person/foreground from an image."""
|
103 |
-
# Resize the image to the size expected by the segmentation model
|
104 |
-
width, height = image_pil.size
|
105 |
-
image_pil_resized = image_pil.resize((512, 512))
|
106 |
-
|
107 |
-
# Process the image with the segmentation model
|
108 |
-
inputs = seg_processor(images=image_pil_resized, return_tensors="pt")
|
109 |
-
with torch.no_grad():
|
110 |
-
outputs = seg_model(**inputs)
|
111 |
-
|
112 |
-
# Get the predicted segmentation mask
|
113 |
-
logits = outputs.logits
|
114 |
-
upsampled_logits = torch.nn.functional.interpolate(
|
115 |
-
logits,
|
116 |
-
size=(512, 512),
|
117 |
-
mode="bilinear",
|
118 |
-
align_corners=False,
|
119 |
-
)
|
120 |
-
|
121 |
-
# Get the predicted segmentation mask
|
122 |
-
predicted_mask = upsampled_logits.argmax(dim=1)[0]
|
123 |
-
|
124 |
-
# Convert the mask to a numpy array
|
125 |
-
mask_np = predicted_mask.cpu().numpy()
|
126 |
-
|
127 |
-
# Create a foreground mask - considering classes that are likely to be foreground
|
128 |
-
# The ADE20K dataset has 150 classes, so we need to choose which ones to consider as foreground
|
129 |
-
# Common foreground classes: person (12), animal classes, and objects like furniture
|
130 |
-
# This is a simplified approach - you may need to adjust based on your needs
|
131 |
-
foreground_classes = [12, 13, 14, 15, 16, 17, 18, 19, 20] # Person and some objects
|
132 |
-
foreground_mask = np.zeros_like(mask_np)
|
133 |
-
for cls in foreground_classes:
|
134 |
-
foreground_mask[mask_np == cls] = 1
|
135 |
-
|
136 |
-
# Resize back to original image size
|
137 |
-
foreground_mask = cv2.resize(foreground_mask, (width, height))
|
138 |
-
|
139 |
-
return foreground_mask
|
140 |
-
|
141 |
-
def get_depth_map(image_pil):
|
142 |
-
"""Get depth map from an image."""
|
143 |
-
# Process the image with the depth estimation model
|
144 |
-
inputs = depth_processor(images=image_pil, return_tensors="pt")
|
145 |
-
with torch.no_grad():
|
146 |
-
outputs = depth_model(**inputs)
|
147 |
-
predicted_depth = outputs.predicted_depth
|
148 |
-
|
149 |
-
# Interpolate to original size
|
150 |
-
prediction = torch.nn.functional.interpolate(
|
151 |
-
predicted_depth.unsqueeze(1),
|
152 |
-
size=image_pil.size[::-1],
|
153 |
-
mode="bicubic",
|
154 |
-
align_corners=False,
|
155 |
-
)
|
156 |
-
|
157 |
-
# Convert to numpy array
|
158 |
-
depth_map = prediction.squeeze().cpu().numpy()
|
159 |
-
|
160 |
-
# Normalize depth map
|
161 |
-
depth_map = (depth_map - depth_map.min()) / (depth_map.max() - depth_map.min())
|
162 |
-
|
163 |
-
return depth_map
|
164 |
-
|
165 |
-
def process_image(input_image, blur_sigma=15, depth_blur_sigma=25):
|
166 |
-
"""Main function to process the input image."""
|
167 |
-
try:
|
168 |
-
# Convert to PIL Image if needed
|
169 |
-
if isinstance(input_image, np.ndarray):
|
170 |
-
pil_image = Image.fromarray(input_image)
|
171 |
-
else:
|
172 |
-
pil_image = input_image
|
173 |
-
input_image = np.array(pil_image)
|
174 |
-
|
175 |
-
# Get segmentation mask
|
176 |
-
seg_mask = get_segmentation_mask(pil_image)
|
177 |
-
|
178 |
-
# Get depth map
|
179 |
-
depth_map = get_depth_map(pil_image)
|
180 |
-
|
181 |
-
# Apply gaussian blur to background
|
182 |
-
gaussian_result = apply_gaussian_blur(input_image, seg_mask, sigma=blur_sigma)
|
183 |
-
|
184 |
-
# Apply depth-based blur
|
185 |
-
depth_result = apply_depth_blur(input_image, depth_map, max_sigma=depth_blur_sigma)
|
186 |
-
|
187 |
-
# Display depth map as an image
|
188 |
-
depth_visualization = (depth_map * 255).astype(np.uint8)
|
189 |
-
depth_visualization = cv2.applyColorMap(depth_visualization, cv2.COLORMAP_INFERNO)
|
190 |
-
|
191 |
-
# Display segmentation mask
|
192 |
-
seg_visualization = (seg_mask * 255).astype(np.uint8)
|
193 |
-
|
194 |
-
return [
|
195 |
-
input_image,
|
196 |
-
seg_visualization,
|
197 |
-
gaussian_result,
|
198 |
-
depth_visualization,
|
199 |
-
depth_result
|
200 |
-
]
|
201 |
-
except Exception as e:
|
202 |
-
print(f"Error processing image: {e}")
|
203 |
-
return [None, None, None, None, None]
|
204 |
-
|
205 |
-
# Create Gradio interface
|
206 |
-
with gr.Blocks(title="Image Blur Effects with Segmentation and Depth Estimation") as demo:
|
207 |
-
gr.Markdown("# Image Blur Effects App")
|
208 |
-
gr.Markdown("This app demonstrates two types of blur effects: background blur using segmentation and depth-based lens blur.")
|
209 |
-
|
210 |
-
with gr.Row():
|
211 |
-
with gr.Column():
|
212 |
-
input_image = gr.Image(label="Upload an image", type="pil")
|
213 |
-
blur_sigma = gr.Slider(minimum=1, maximum=50, value=15, step=1, label="Background Blur Intensity")
|
214 |
-
depth_blur_sigma = gr.Slider(minimum=1, maximum=50, value=25, step=1, label="Depth Blur Max Intensity")
|
215 |
-
process_btn = gr.Button("Process Image")
|
216 |
-
|
217 |
-
with gr.Column():
|
218 |
-
with gr.Tab("Original Image"):
|
219 |
-
output_original = gr.Image(label="Original Image")
|
220 |
-
with gr.Tab("Segmentation Mask"):
|
221 |
-
output_segmentation = gr.Image(label="Segmentation Mask")
|
222 |
-
with gr.Tab("Background Blur"):
|
223 |
-
output_gaussian = gr.Image(label="Background Blur Result")
|
224 |
-
with gr.Tab("Depth Map"):
|
225 |
-
output_depth = gr.Image(label="Depth Map")
|
226 |
-
with gr.Tab("Depth-based Lens Blur"):
|
227 |
-
output_depth_blur = gr.Image(label="Depth-based Lens Blur Result")
|
228 |
-
|
229 |
-
process_btn.click(
|
230 |
-
fn=process_image,
|
231 |
-
inputs=[input_image, blur_sigma, depth_blur_sigma],
|
232 |
-
outputs=[output_original, output_segmentation, output_gaussian, output_depth, output_depth_blur]
|
233 |
-
)
|
234 |
-
|
235 |
-
gr.Markdown("""
|
236 |
-
## How it works
|
237 |
-
import gradio as gr
|
238 |
-
import torch
|
239 |
-
import numpy as np
|
240 |
-
import cv2
|
241 |
-
from PIL import Image
|
242 |
-
from transformers import AutoImageProcessor, AutoModelForSemanticSegmentation
|
243 |
-
from transformers import DPTImageProcessor, DPTForDepthEstimation
|
244 |
-
import warnings
|
245 |
-
warnings.filterwarnings("ignore")
|
246 |
-
|
247 |
-
# Load segmentation model - using SegFormer which is compatible with AutoModelForSemanticSegmentation
|
248 |
-
seg_processor = AutoImageProcessor.from_pretrained("nvidia/segformer-b0-finetuned-ade-512-512")
|
249 |
-
seg_model = AutoModelForSemanticSegmentation.from_pretrained("nvidia/segformer-b0-finetuned-ade-512-512")
|
250 |
-
|
251 |
-
# Load depth estimation model
|
252 |
-
depth_processor = DPTImageProcessor.from_pretrained("Intel/dpt-large")
|
253 |
-
depth_model = DPTForDepthEstimation.from_pretrained("Intel/dpt-large")
|
254 |
-
|
255 |
def safe_resize(image, target_size, interpolation=cv2.INTER_LINEAR):
|
256 |
"""Safely resize an image with validation checks."""
|
257 |
if image is None:
|
@@ -535,11 +299,4 @@ with gr.Blocks(title="Image Blur Effects with Segmentation and Depth Estimation"
|
|
535 |
Try uploading a photo of a person against a background to see the effects!
|
536 |
""")
|
537 |
|
538 |
-
demo.launch()
|
539 |
-
1. **Background Blur**: Uses a segmentation model to identify foreground objects and blurs only the background
|
540 |
-
2. **Depth-based Lens Blur**: Uses a depth estimation model to apply variable blur based on estimated distance
|
541 |
-
|
542 |
-
Try uploading a photo of a person or object against a background to see the effects!
|
543 |
-
""")
|
544 |
-
|
545 |
demo.launch()
|
|
|
16 |
depth_processor = DPTImageProcessor.from_pretrained("Intel/dpt-large")
|
17 |
depth_model = DPTForDepthEstimation.from_pretrained("Intel/dpt-large")
|
18 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
19 |
def safe_resize(image, target_size, interpolation=cv2.INTER_LINEAR):
|
20 |
"""Safely resize an image with validation checks."""
|
21 |
if image is None:
|
|
|
299 |
Try uploading a photo of a person against a background to see the effects!
|
300 |
""")
|
301 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
302 |
demo.launch()
|