Spaces:
Running
Running
Update to add synthetic blur
Browse files
.DS_Store
CHANGED
Binary files a/.DS_Store and b/.DS_Store differ
|
|
app.py
CHANGED
@@ -21,7 +21,6 @@ def gaussian_blur(img: Image.Image, kernel_size: int):
|
|
21 |
return cv2.cvtColor(blurred, cv2.COLOR_BGR2RGB)
|
22 |
|
23 |
|
24 |
-
# Load model once globally
|
25 |
depth_model_id = "depth-anything/Depth-Anything-V2-Small-hf"
|
26 |
processor = AutoImageProcessor.from_pretrained(depth_model_id)
|
27 |
depth_model = AutoModelForDepthEstimation.from_pretrained(depth_model_id)
|
@@ -31,7 +30,6 @@ def lens_blur(img: Image.Image, max_blur_radius: int):
|
|
31 |
img = resize_to_512(img)
|
32 |
original = np.array(img).astype(np.float32)
|
33 |
|
34 |
-
# Get depth map
|
35 |
inputs = processor(images=img, return_tensors="pt")
|
36 |
with torch.no_grad():
|
37 |
outputs = depth_model(**inputs)
|
@@ -49,19 +47,14 @@ def lens_blur(img: Image.Image, max_blur_radius: int):
|
|
49 |
.numpy()
|
50 |
)
|
51 |
|
52 |
-
# Normalize and invert depth
|
53 |
depth_norm = (depth - depth.min()) / (depth.max() - depth.min())
|
54 |
depth_inverted = 1.0 - depth_norm
|
55 |
|
56 |
-
|
57 |
-
|
58 |
-
max_sigma = (
|
59 |
-
max_blur_radius / 2.0
|
60 |
-
) # Scale down to reasonable range (e.g. 0β25 β 0β12.5 sigma)
|
61 |
blur_levels = np.linspace(0, max_sigma, num_levels)
|
62 |
blurred_images = [gaussian_filter(original, sigma=(s, s, 0)) for s in blur_levels]
|
63 |
|
64 |
-
# Blend based on depth
|
65 |
blurred_final = np.zeros_like(original, dtype=np.float32)
|
66 |
depth_scaled = depth_inverted * (num_levels - 1)
|
67 |
depth_int = np.floor(depth_scaled).astype(int)
|
@@ -80,56 +73,87 @@ def lens_blur(img: Image.Image, max_blur_radius: int):
|
|
80 |
return np.clip(blurred_final, 0, 255).astype(np.uint8)
|
81 |
|
82 |
|
83 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
84 |
def update_gaussian(img, kernel_size):
|
85 |
return gaussian_blur(img, kernel_size)
|
86 |
|
87 |
|
88 |
-
def update_lens(img,
|
89 |
-
return lens_blur(img,
|
90 |
|
91 |
|
92 |
-
def
|
93 |
-
|
94 |
-
l_blurred = lens_blur(img, max_blur_radius)
|
95 |
-
return g_blurred, l_blurred
|
96 |
|
97 |
|
98 |
with gr.Blocks() as demo:
|
99 |
-
gr.Markdown("## π
|
100 |
|
101 |
with gr.Row():
|
102 |
image_input = gr.Image(type="pil", label="Upload Image")
|
103 |
|
104 |
with gr.Row():
|
105 |
-
|
106 |
-
lens_slider = gr.Slider(
|
107 |
-
|
108 |
-
)
|
109 |
|
110 |
with gr.Row():
|
111 |
-
|
112 |
-
|
|
|
113 |
|
114 |
-
#
|
115 |
image_input.change(
|
116 |
-
fn=
|
117 |
-
inputs=[image_input,
|
118 |
-
outputs=[
|
119 |
-
)
|
120 |
-
|
121 |
-
# Trigger only gaussian blur
|
122 |
-
kernel_slider.change(
|
123 |
-
fn=update_gaussian,
|
124 |
-
inputs=[image_input, kernel_slider],
|
125 |
-
outputs=gaussian_output,
|
126 |
)
|
127 |
|
128 |
-
#
|
129 |
-
|
130 |
-
|
131 |
-
|
132 |
-
outputs=lens_output,
|
133 |
-
)
|
134 |
|
135 |
demo.launch()
|
|
|
21 |
return cv2.cvtColor(blurred, cv2.COLOR_BGR2RGB)
|
22 |
|
23 |
|
|
|
24 |
depth_model_id = "depth-anything/Depth-Anything-V2-Small-hf"
|
25 |
processor = AutoImageProcessor.from_pretrained(depth_model_id)
|
26 |
depth_model = AutoModelForDepthEstimation.from_pretrained(depth_model_id)
|
|
|
30 |
img = resize_to_512(img)
|
31 |
original = np.array(img).astype(np.float32)
|
32 |
|
|
|
33 |
inputs = processor(images=img, return_tensors="pt")
|
34 |
with torch.no_grad():
|
35 |
outputs = depth_model(**inputs)
|
|
|
47 |
.numpy()
|
48 |
)
|
49 |
|
|
|
50 |
depth_norm = (depth - depth.min()) / (depth.max() - depth.min())
|
51 |
depth_inverted = 1.0 - depth_norm
|
52 |
|
53 |
+
num_levels = 6
|
54 |
+
max_sigma = max_blur_radius / 2.0
|
|
|
|
|
|
|
55 |
blur_levels = np.linspace(0, max_sigma, num_levels)
|
56 |
blurred_images = [gaussian_filter(original, sigma=(s, s, 0)) for s in blur_levels]
|
57 |
|
|
|
58 |
blurred_final = np.zeros_like(original, dtype=np.float32)
|
59 |
depth_scaled = depth_inverted * (num_levels - 1)
|
60 |
depth_int = np.floor(depth_scaled).astype(int)
|
|
|
73 |
return np.clip(blurred_final, 0, 255).astype(np.uint8)
|
74 |
|
75 |
|
76 |
+
def synthetic_lens_blur(img: Image.Image, max_blur_radius: int):
|
77 |
+
img = resize_to_512(img)
|
78 |
+
original = cv2.cvtColor(np.array(img), cv2.COLOR_RGB2BGR)
|
79 |
+
original_rgb = cv2.cvtColor(original, cv2.COLOR_BGR2RGB)
|
80 |
+
|
81 |
+
depth_norm = np.zeros((original.shape[0], original.shape[1]), dtype=np.float32)
|
82 |
+
cv2.circle(depth_norm, (original.shape[1] // 2, original.shape[0] // 2), 100, 1, -1)
|
83 |
+
depth_norm = cv2.GaussianBlur(depth_norm, (21, 21), 0)
|
84 |
+
|
85 |
+
blurred_image = np.zeros_like(original_rgb)
|
86 |
+
|
87 |
+
for i in range(original.shape[0]):
|
88 |
+
for j in range(original.shape[1]):
|
89 |
+
blur_radius = int(depth_norm[i, j] * max_blur_radius)
|
90 |
+
if blur_radius % 2 == 0:
|
91 |
+
blur_radius += 1
|
92 |
+
|
93 |
+
x_min = max(j - blur_radius, 0)
|
94 |
+
x_max = min(j + blur_radius, original.shape[1])
|
95 |
+
y_min = max(i - blur_radius, 0)
|
96 |
+
y_max = min(i + blur_radius, original.shape[0])
|
97 |
+
|
98 |
+
roi = original_rgb[y_min:y_max, x_min:x_max]
|
99 |
+
|
100 |
+
if blur_radius > 1:
|
101 |
+
blurred_roi = cv2.GaussianBlur(roi, (blur_radius, blur_radius), 0)
|
102 |
+
try:
|
103 |
+
blurred_image[i, j] = blurred_roi[blur_radius // 2, blur_radius // 2]
|
104 |
+
except:
|
105 |
+
blurred_image[i, j] = original_rgb[i, j]
|
106 |
+
else:
|
107 |
+
blurred_image[i, j] = original_rgb[i, j]
|
108 |
+
|
109 |
+
return blurred_image
|
110 |
+
|
111 |
+
|
112 |
+
def apply_all_blurs(img, g_kernel, lens_radius, synthetic_radius):
|
113 |
+
g = gaussian_blur(img, g_kernel)
|
114 |
+
l = lens_blur(img, lens_radius)
|
115 |
+
s = synthetic_lens_blur(img, synthetic_radius)
|
116 |
+
return g, l, s
|
117 |
+
|
118 |
+
|
119 |
def update_gaussian(img, kernel_size):
|
120 |
return gaussian_blur(img, kernel_size)
|
121 |
|
122 |
|
123 |
+
def update_lens(img, radius):
|
124 |
+
return lens_blur(img, radius)
|
125 |
|
126 |
|
127 |
+
def update_synthetic(img, radius):
|
128 |
+
return synthetic_lens_blur(img, radius)
|
|
|
|
|
129 |
|
130 |
|
131 |
with gr.Blocks() as demo:
|
132 |
+
gr.Markdown("## π Blur Effects Comparison: Gaussian, Depth-Based, Synthetic")
|
133 |
|
134 |
with gr.Row():
|
135 |
image_input = gr.Image(type="pil", label="Upload Image")
|
136 |
|
137 |
with gr.Row():
|
138 |
+
g_slider = gr.Slider(1, 49, step=2, value=11, label="Gaussian Kernel Size")
|
139 |
+
lens_slider = gr.Slider(1, 50, step=1, value=15, label="Depth-Based Blur Intensity")
|
140 |
+
synth_slider = gr.Slider(1, 50, step=1, value=25, label="Synthetic Blur Radius")
|
|
|
141 |
|
142 |
with gr.Row():
|
143 |
+
g_output = gr.Image(label="Gaussian Blurred Image")
|
144 |
+
l_output = gr.Image(label="Depth-Based Lens Blurred Image")
|
145 |
+
s_output = gr.Image(label="Synthetic Depth Lens Blurred Image")
|
146 |
|
147 |
+
# Initial image upload updates all three
|
148 |
image_input.change(
|
149 |
+
fn=apply_all_blurs,
|
150 |
+
inputs=[image_input, g_slider, lens_slider, synth_slider],
|
151 |
+
outputs=[g_output, l_output, s_output],
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
152 |
)
|
153 |
|
154 |
+
# Individual updates for each slider
|
155 |
+
g_slider.change(fn=update_gaussian, inputs=[image_input, g_slider], outputs=g_output)
|
156 |
+
lens_slider.change(fn=update_lens, inputs=[image_input, lens_slider], outputs=l_output)
|
157 |
+
synth_slider.change(fn=update_synthetic, inputs=[image_input, synth_slider], outputs=s_output)
|
|
|
|
|
158 |
|
159 |
demo.launch()
|