pavank007 commited on
Commit
5d770da
·
verified ·
1 Parent(s): 23bfbfd

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +190 -265
app.py CHANGED
@@ -1,289 +1,214 @@
1
  import gradio as gr
2
- import numpy as np
3
  import torch
4
- from PIL import Image, ImageFilter
5
  import cv2
6
- import os
7
- import sys
8
- import traceback
9
- from transformers import pipeline
10
-
11
- # Configure logging to console
12
- import logging
13
- logging.basicConfig(level=logging.INFO,
14
- format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
15
- stream=sys.stdout)
16
- logger = logging.getLogger(__name__)
17
 
18
- # Set device to GPU if available
19
  device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
20
- logger.info(f"Using device: {device}")
21
-
22
- # Use smaller, more efficient models for Hugging Face Space
23
- SEGMENTATION_MODEL = "facebook/sam-vit-base"
24
- DEPTH_MODEL = "depth-anything/Depth-Anything-V2-Small-hf"
25
 
26
- # Global variables for models
27
- segmentation_pipe = None
28
- depth_pipe = None
 
29
 
30
- def load_segmentation_model():
31
- """Load the segmentation model on demand"""
32
- global segmentation_pipe
33
- if segmentation_pipe is None:
34
- try:
35
- logger.info("Loading segmentation model...")
36
- segmentation_pipe = pipeline("image-segmentation", model=SEGMENTATION_MODEL)
37
- logger.info("Segmentation model loaded successfully")
38
- except Exception as e:
39
- logger.error(f"Error loading segmentation model: {e}")
40
- logger.error(traceback.format_exc())
41
- return None
42
- return segmentation_pipe
43
 
44
- def load_depth_model():
45
- """Load the depth model on demand"""
46
- global depth_pipe
47
- if depth_pipe is None:
48
- try:
49
- logger.info("Loading depth estimation model...")
50
- depth_pipe = pipeline("depth-estimation", model=DEPTH_MODEL)
51
- logger.info("Depth estimation model loaded successfully")
52
- except Exception as e:
53
- logger.error(f"Error loading depth model: {e}")
54
- logger.error(traceback.format_exc())
55
- return None
56
- return depth_pipe
57
-
58
- def get_segmentation_mask(input_image):
59
- """Get segmentation mask using the segmentation pipeline"""
60
- try:
61
- # Load the model if not already loaded
62
- model = load_segmentation_model()
63
- if model is None:
64
- logger.warning("Failed to load segmentation model, returning empty mask")
65
- return Image.new('L', (512, 512), 0), input_image
66
-
67
- # Process the image
68
- input_pil = Image.fromarray(input_image).convert('RGB') if isinstance(input_image, np.ndarray) else input_image.convert('RGB')
69
- input_pil = input_pil.resize((512, 512))
70
-
71
- # Get segmentation results
72
- results = model(input_pil)
73
-
74
- # Extract mask (handling different model outputs)
75
- if isinstance(results, list) and len(results) > 0:
76
- if 'mask' in results[0]:
77
- mask = results[0]['mask']
78
- # Convert mask to proper format
79
- if isinstance(mask, Image.Image):
80
- mask_array = np.array(mask)
81
- else:
82
- mask_array = mask
83
- mask_array = (mask_array * 255).astype(np.uint8)
84
- else:
85
- # Create composite mask from segmentation results
86
- mask_array = np.zeros((512, 512), dtype=np.uint8)
87
- for segment in results:
88
- if 'segmentation' in segment:
89
- mask_array = np.logical_or(mask_array, segment['segmentation']).astype(np.uint8) * 255
90
- else:
91
- # Create blank mask as fallback
92
- mask_array = np.zeros((512, 512), dtype=np.uint8)
93
-
94
- # Convert to PIL Image
95
- mask_img = Image.fromarray(mask_array)
96
-
97
- return mask_img, input_pil
98
-
99
- except Exception as e:
100
- logger.error(f"Error in segmentation: {e}")
101
- logger.error(traceback.format_exc())
102
- # Return a blank mask in case of error
103
- if isinstance(input_image, np.ndarray):
104
- return Image.new('L', (512, 512), 0), Image.fromarray(input_image).resize((512, 512))
105
- else:
106
- return Image.new('L', (512, 512), 0), input_image.resize((512, 512))
107
 
108
- def apply_background_blur(original_image, mask_image, sigma=15):
109
- """Apply Gaussian blur to the background using a segmentation mask"""
110
- try:
111
- # Convert to PIL Image if needed
112
- if isinstance(original_image, np.ndarray):
113
- original_image = Image.fromarray(original_image)
114
- if isinstance(mask_image, np.ndarray):
115
- mask_image = Image.fromarray(mask_image)
116
-
117
- # Ensure consistent sizes
118
- original_image = original_image.resize((512, 512)).convert('RGB')
119
- mask_image = mask_image.resize((512, 512)).convert('L')
120
-
121
- # Ensure mask is binary
122
- mask_array = np.array(mask_image)
123
- _, binary_mask = cv2.threshold(mask_array, 127, 255, cv2.THRESH_BINARY)
124
-
125
- # Create blurred version of the original
126
- blurred_img = original_image.filter(ImageFilter.GaussianBlur(radius=sigma))
127
-
128
- # Convert to numpy for processing
129
- original_array = np.array(original_image)
130
- blurred_array = np.array(blurred_img)
131
-
132
- # Create mask for all 3 channels
133
- mask_3d = np.stack([binary_mask, binary_mask, binary_mask], axis=2)
134
-
135
- # Combine original foreground with blurred background
136
- result_array = np.where(mask_3d == 255, original_array, blurred_array)
137
-
138
- return Image.fromarray(result_array.astype(np.uint8))
139
 
140
- except Exception as e:
141
- logger.error(f"Error in background blur: {e}")
142
- logger.error(traceback.format_exc())
143
- # Return original image in case of error
144
- return original_image
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
145
 
146
- def get_depth_map(input_image):
147
- """Get depth map using the depth estimation pipeline"""
148
- try:
149
- # Load model if not already loaded
150
- model = load_depth_model()
151
- if model is None:
152
- logger.warning("Failed to load depth model, returning empty depth map")
153
- return Image.new('L', (512, 512), 128), np.ones((512, 512)) * 0.5
154
-
155
- # Convert to PIL if needed
156
- if isinstance(input_image, np.ndarray):
157
- input_image = Image.fromarray(input_image)
158
-
159
- # Ensure consistent format
160
- input_image = input_image.resize((512, 512)).convert('RGB')
161
-
162
- # Get depth estimation
163
- result = model(input_image)
164
- depth_map = result["depth"]
165
-
166
- # Convert to numpy for further processing
167
- depth_array = np.array(depth_map)
168
-
169
- return depth_map, depth_array
170
 
171
- except Exception as e:
172
- logger.error(f"Error in depth estimation: {e}")
173
- logger.error(traceback.format_exc())
174
- # Return default depth in case of error
175
- return Image.new('L', (512, 512), 128), np.ones((512, 512)) * 0.5
 
 
 
 
 
 
 
176
 
177
- def apply_depth_based_blur(original_image, depth_array, max_blur=30):
178
- """Apply variable Gaussian blur based on depth"""
179
- try:
180
- # Convert to PIL if needed
181
- if isinstance(original_image, np.ndarray):
182
- original_image = Image.fromarray(original_image)
183
-
184
- # Ensure consistent size
185
- original_image = original_image.resize((512, 512)).convert('RGB')
186
-
187
- # Handle depth array format
188
- if len(depth_array.shape) == 3 and depth_array.shape[2] > 1:
189
- depth_array = np.mean(depth_array, axis=2)
190
-
191
- # Normalize depth values
192
- depth_min = np.min(depth_array)
193
- depth_max = np.max(depth_array)
194
-
195
- if depth_max > depth_min:
196
- normalized_depth = (depth_array - depth_min) / (depth_max - depth_min)
197
- else:
198
- # Handle case where depth is constant
199
- normalized_depth = np.zeros_like(depth_array)
200
-
201
- # Create progressively blurred versions of the image
202
- blurred_images = []
203
- for blur_amount in range(max_blur + 1):
204
- blurred_images.append(original_image.filter(ImageFilter.GaussianBlur(radius=blur_amount)))
205
-
206
- # Create output array
207
- result_array = np.zeros((512, 512, 3), dtype=np.uint8)
208
-
209
- # Apply variable blur based on depth
210
- height, width = normalized_depth.shape
211
- for y in range(height):
212
- for x in range(width):
213
- blur_radius = int(normalized_depth[y, x] * max_blur)
214
- result_array[y, x] = np.array(blurred_images[blur_radius])[y, x]
215
-
216
- return Image.fromarray(result_array)
217
 
218
- except Exception as e:
219
- logger.error(f"Error in depth-based blur: {e}")
220
- logger.error(traceback.format_exc())
221
- # Return original image in case of error
222
- return original_image
 
 
 
 
 
223
 
224
- def process_image(input_image, blur_sigma=15, max_depth_blur=30):
225
- """Process the image through all steps with error handling"""
226
- try:
227
- if input_image is None:
228
- logger.warning("No input image provided")
229
- return None, None, None, None, None
230
-
231
- # Step 1: Get segmentation mask
232
- mask, resized_image = get_segmentation_mask(input_image)
233
-
234
- # Step 2: Apply background blur
235
- blurred_background = apply_background_blur(resized_image, mask, sigma=blur_sigma)
236
-
237
- # Step 3: Get depth map
238
- depth_map, depth_array = get_depth_map(resized_image)
239
-
240
- # Step 4: Apply depth-based blur
241
- depth_blur = apply_depth_based_blur(resized_image, depth_array, max_blur=max_depth_blur)
242
 
243
- # Convert to numpy arrays for Gradio
244
- input_np = np.array(resized_image)
245
- mask_np = np.array(mask)
246
- blurred_np = np.array(blurred_background)
247
- depth_map_np = np.array(depth_map)
248
- depth_blur_np = np.array(depth_blur)
249
 
250
- return input_np, mask_np, blurred_np, depth_map_np, depth_blur_np
251
 
252
- except Exception as e:
253
- logger.error(f"Error in image processing: {e}")
254
- logger.error(traceback.format_exc())
255
 
256
- # Create blank outputs in case of error
257
- empty = np.zeros((512, 512, 3), dtype=np.uint8)
258
- empty_mask = np.zeros((512, 512), dtype=np.uint8)
259
 
260
- if input_image is not None and isinstance(input_image, np.ndarray):
261
- img_resized = cv2.resize(input_image, (512, 512))
262
- return img_resized, empty_mask, empty, empty_mask, empty
263
- else:
264
- return empty, empty_mask, empty, empty_mask, empty
265
 
266
  # Create Gradio Interface
267
- demo = gr.Interface(
268
- fn=process_image,
269
- inputs=[
270
- gr.Image(type="numpy", label="Upload Image"),
271
- gr.Slider(minimum=1, maximum=30, value=15, step=1, label="Background Blur Strength (σ)"),
272
- gr.Slider(minimum=1, maximum=50, value=30, step=1, label="Max Depth Blur Strength")
273
- ],
274
- outputs=[
275
- gr.Image(type="numpy", label="Original Image"),
276
- gr.Image(type="numpy", label="Segmentation Mask"),
277
- gr.Image(type="numpy", label="Background Blur"),
278
- gr.Image(type="numpy", label="Depth Map"),
279
- gr.Image(type="numpy", label="Depth-Based Lens Blur")
280
- ],
281
- title="Image Blur Effects - EEE 515 Assignment 3",
282
- description="Upload an image to apply segmentation-based blur and depth-based lens blur effects",
283
- examples=[["beach.jpeg", 15, 30]],
284
- allow_flagging="never"
285
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
286
 
287
  # Launch the app
288
- if __name__ == "__main__":
289
- demo.launch()
 
1
  import gradio as gr
 
2
  import torch
 
3
  import cv2
4
+ import numpy as np
5
+ from PIL import Image
6
+ import requests
7
+ from io import BytesIO
8
+ from transformers import AutoFeatureExtractor, AutoModelForSemanticSegmentation
9
+ from transformers import AutoImageProcessor, AutoModelForDepthEstimation
10
+ import torch.nn.functional as F
 
 
 
 
11
 
12
+ # Define device
13
  device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
 
 
 
 
 
14
 
15
+ # Load segmentation model
16
+ segmentation_model_name = "facebook/mask2former-swin-tiny-coco-instance"
17
+ seg_feature_extractor = AutoFeatureExtractor.from_pretrained(segmentation_model_name)
18
+ seg_model = AutoModelForSemanticSegmentation.from_pretrained(segmentation_model_name).to(device)
19
 
20
+ # Load depth estimation model
21
+ depth_model_name = "intel-isl/MiDaS-small"
22
+ depth_processor = AutoImageProcessor.from_pretrained(depth_model_name)
23
+ depth_model = AutoModelForDepthEstimation.from_pretrained(depth_model_name).to(device)
 
 
 
 
 
 
 
 
 
24
 
25
+ def apply_segmentation(input_image):
26
+ # Convert to PIL Image if needed
27
+ if not isinstance(input_image, Image.Image):
28
+ input_image = Image.fromarray(input_image)
29
+
30
+ # Resize to 512x512 for consistent processing
31
+ input_image = input_image.resize((512, 512))
32
+
33
+ # Prepare image for the model
34
+ inputs = seg_feature_extractor(images=input_image, return_tensors="pt").to(device)
35
+
36
+ # Forward pass
37
+ with torch.no_grad():
38
+ outputs = seg_model(**inputs)
39
+
40
+ # Process output to get binary mask (foreground=1, background=0)
41
+ logits = outputs.logits
42
+ predicted_mask = torch.argmax(logits, dim=1)
43
+
44
+ # Convert to numpy for processing
45
+ mask = predicted_mask[0].cpu().numpy()
46
+
47
+ # Focus on person class (typically class 0 or 1 depending on the model)
48
+ mask = (mask > 0).astype(np.uint8) * 255
49
+
50
+ return np.array(input_image), mask
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
51
 
52
+ def apply_depth_estimation(input_image):
53
+ # Convert to PIL Image if needed
54
+ if not isinstance(input_image, Image.Image):
55
+ input_image = Image.fromarray(input_image)
56
+
57
+ # Resize to 512x512 for consistent processing
58
+ input_image = input_image.resize((512, 512))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
59
 
60
+ # Prepare image for the model
61
+ inputs = depth_processor(images=input_image, return_tensors="pt").to(device)
62
+
63
+ # Forward pass
64
+ with torch.no_grad():
65
+ outputs = depth_model(**inputs)
66
+
67
+ # Process depth map
68
+ depth_map = outputs.predicted_depth
69
+ depth_map = torch.nn.functional.interpolate(
70
+ depth_map.unsqueeze(1),
71
+ size=(512, 512),
72
+ mode="bicubic",
73
+ align_corners=False,
74
+ ).squeeze()
75
+
76
+ # Normalize depth map to 0-1 range
77
+ depth_min = torch.min(depth_map)
78
+ depth_max = torch.max(depth_map)
79
+ depth_map = (depth_map - depth_min) / (depth_max - depth_min)
80
+
81
+ # Convert to numpy
82
+ depth_map = depth_map.cpu().numpy()
83
+
84
+ # Convert depth to heatmap for visualization
85
+ depth_map_vis = (depth_map * 255).astype(np.uint8)
86
+ depth_map_vis = cv2.applyColorMap(depth_map_vis, cv2.COLORMAP_INFERNO)
87
+
88
+ return np.array(input_image), depth_map, depth_map_vis
89
 
90
+ def apply_gaussian_blur(image, mask, sigma=15):
91
+ # Make a copy of the image
92
+ result = image.copy()
93
+
94
+ # Ensure mask is binary (0 or 1)
95
+ if mask.max() > 1:
96
+ mask = mask / 255.0
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
97
 
98
+ # Expand mask to 3 channels if needed
99
+ if len(mask.shape) == 2:
100
+ mask = np.expand_dims(mask, axis=2)
101
+ mask = np.repeat(mask, 3, axis=2)
102
+
103
+ # Blur the entire image
104
+ blurred = cv2.GaussianBlur(image, (0, 0), sigma)
105
+
106
+ # Combine original image (foreground) with blurred image (background) using the mask
107
+ result = image * mask + blurred * (1 - mask)
108
+
109
+ return result.astype(np.uint8)
110
 
111
+ def apply_depth_blur(image, depth_map, max_sigma=30):
112
+ # Make a copy of the image
113
+ result = np.zeros_like(image)
114
+
115
+ # Ensure depth map values are between 0-1
116
+ depth_map = (depth_map - depth_map.min()) / (depth_map.max() - depth_map.min())
117
+
118
+ # Apply variable blur based on depth
119
+ for sigma in range(1, max_sigma + 1):
120
+ # Create a mask for this depth level
121
+ depth_mask = ((depth_map >= (sigma - 1) / max_sigma) &
122
+ (depth_map < sigma / max_sigma)).astype(np.float32)
123
+
124
+ # Expand mask to 3 channels if needed
125
+ if len(depth_mask.shape) == 2:
126
+ depth_mask = np.expand_dims(depth_mask, axis=2)
127
+ depth_mask = np.repeat(depth_mask, 3, axis=2)
128
+
129
+ # Apply blur with current sigma
130
+ current_blur = cv2.GaussianBlur(image, (0, 0), sigma)
131
+
132
+ # Add to result
133
+ result += (current_blur * depth_mask).astype(np.uint8)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
134
 
135
+ # Handle remaining pixels (if any)
136
+ remaining_mask = (depth_map >= 1.0).astype(np.float32)
137
+ if len(remaining_mask.shape) == 2:
138
+ remaining_mask = np.expand_dims(remaining_mask, axis=2)
139
+ remaining_mask = np.repeat(remaining_mask, 3, axis=2)
140
+
141
+ max_blur = cv2.GaussianBlur(image, (0, 0), max_sigma)
142
+ result += (max_blur * remaining_mask).astype(np.uint8)
143
+
144
+ return result
145
 
146
+ def process_image(input_image, blur_type, blur_strength):
147
+ # Convert to numpy array if needed
148
+ if isinstance(input_image, str):
149
+ # Load from URL if it's a string
150
+ response = requests.get(input_image)
151
+ input_image = Image.open(BytesIO(response.content))
152
+
153
+ # Resize to 512x512 for consistent processing
154
+ input_image = Image.fromarray(input_image).resize((512, 512))
155
+ input_image_np = np.array(input_image)
156
+
157
+ # Process based on selected blur type
158
+ if blur_type == "Gaussian Background Blur":
159
+ # Apply segmentation
160
+ _, mask = apply_segmentation(input_image)
 
 
 
161
 
162
+ # Apply Gaussian blur with chosen strength
163
+ result = apply_gaussian_blur(input_image_np, mask, sigma=blur_strength)
 
 
 
 
164
 
165
+ return input_image_np, result
166
 
167
+ elif blur_type == "Depth-based Lens Blur":
168
+ # Apply depth estimation
169
+ _, depth_map, depth_vis = apply_depth_estimation(input_image)
170
 
171
+ # Apply depth-based blur with chosen max strength
172
+ result = apply_depth_blur(input_image_np, depth_map, max_sigma=blur_strength)
 
173
 
174
+ return input_image_np, result
 
 
 
 
175
 
176
  # Create Gradio Interface
177
+ with gr.Blocks(title="Image Blur Effects Demo") as app:
178
+ gr.Markdown("# Image Blur Effects Demo")
179
+ gr.Markdown("Upload an image to apply different blur effects using deep learning models")
180
+
181
+ with gr.Row():
182
+ input_image = gr.Image(label="Input Image", type="numpy")
183
+ output_image = gr.Image(label="Output Image", type="numpy")
184
+
185
+ with gr.Row():
186
+ blur_type = gr.Radio(
187
+ choices=["Gaussian Background Blur", "Depth-based Lens Blur"],
188
+ label="Blur Effect Type",
189
+ value="Gaussian Background Blur"
190
+ )
191
+ blur_strength = gr.Slider(
192
+ minimum=1, maximum=50, value=15, step=1,
193
+ label="Blur Strength"
194
+ )
195
+
196
+ submit_button = gr.Button("Apply Effect")
197
+
198
+ submit_button.click(
199
+ fn=process_image,
200
+ inputs=[input_image, blur_type, blur_strength],
201
+ outputs=[input_image, output_image]
202
+ )
203
+
204
+ gr.Markdown("""
205
+ ## How it works
206
+
207
+ 1. **Gaussian Background Blur**: Uses a segmentation model to detect the foreground object and applies blur to the background
208
+ 2. **Depth-based Lens Blur**: Uses a depth estimation model to create a variable blur effect where objects further away are more blurred
209
+
210
+ Both models are from Hugging Face Transformers library.
211
+ """)
212
 
213
  # Launch the app
214
+ app.launch()