File size: 5,627 Bytes
46ff125
 
 
 
3fb37bd
 
 
 
 
 
46ff125
 
 
3fb37bd
46ff125
 
 
 
 
 
 
 
 
3fb37bd
 
 
 
 
46ff125
 
3fb37bd
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ecb27c6
 
3fb37bd
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
46ff125
3fb37bd
46ff125
 
ecb27c6
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
a384894
 
 
ecb27c6
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3fb37bd
 
46ff125
 
ecb27c6
 
46ff125
 
ecb27c6
 
46ff125
 
 
a384894
 
 
3fb37bd
46ff125
3fb37bd
 
46ff125
ecb27c6
a384894
 
 
 
 
 
 
ecb27c6
3fb37bd
46ff125
ecb27c6
 
 
46ff125
ecb27c6
3fb37bd
ecb27c6
 
 
3fb37bd
 
ecb27c6
a384894
 
 
 
 
 
 
 
 
46ff125
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
import cv2
import numpy as np
import gradio as gr
from PIL import Image
from scipy.ndimage import gaussian_filter
from transformers import (
    AutoImageProcessor,
    AutoModelForDepthEstimation,
)
import torch


def resize_to_512(img: Image.Image) -> Image.Image:
    return img.resize((512, 512)) if img.size != (512, 512) else img


def gaussian_blur(img: Image.Image, kernel_size: int):
    img = resize_to_512(img)
    img_cv = cv2.cvtColor(np.array(img), cv2.COLOR_RGB2BGR)
    blurred = cv2.GaussianBlur(img_cv, (kernel_size | 1, kernel_size | 1), 0)
    return cv2.cvtColor(blurred, cv2.COLOR_BGR2RGB)


depth_model_id = "depth-anything/Depth-Anything-V2-Small-hf"
processor = AutoImageProcessor.from_pretrained(depth_model_id)
depth_model = AutoModelForDepthEstimation.from_pretrained(depth_model_id)


def lens_blur(img: Image.Image, max_blur_radius: int):
    img = resize_to_512(img)
    original = np.array(img).astype(np.float32)

    inputs = processor(images=img, return_tensors="pt")
    with torch.no_grad():
        outputs = depth_model(**inputs)
        predicted_depth = outputs.predicted_depth

    depth = (
        torch.nn.functional.interpolate(
            predicted_depth.unsqueeze(1),
            size=(512, 512),
            mode="bicubic",
            align_corners=False,
        )
        .squeeze()
        .cpu()
        .numpy()
    )

    depth_norm = (depth - depth.min()) / (depth.max() - depth.min())
    depth_inverted = 1.0 - depth_norm

    num_levels = 6
    max_sigma = max_blur_radius / 2.0
    blur_levels = np.linspace(0, max_sigma, num_levels)
    blurred_images = [gaussian_filter(original, sigma=(s, s, 0)) for s in blur_levels]

    blurred_final = np.zeros_like(original, dtype=np.float32)
    depth_scaled = depth_inverted * (num_levels - 1)
    depth_int = np.floor(depth_scaled).astype(int)
    depth_frac = depth_scaled - depth_int

    for i in range(num_levels - 1):
        mask = depth_int == i
        alpha = depth_frac[mask]
        for c in range(3):
            blended = (
                blurred_images[i][..., c][mask] * (1 - alpha)
                + blurred_images[i + 1][..., c][mask] * alpha
            )
            blurred_final[..., c][mask] = blended

    return np.clip(blurred_final, 0, 255).astype(np.uint8)


def synthetic_lens_blur(img: Image.Image, max_blur_radius: int):
    img = resize_to_512(img)
    original = cv2.cvtColor(np.array(img), cv2.COLOR_RGB2BGR)
    original_rgb = cv2.cvtColor(original, cv2.COLOR_BGR2RGB)

    depth_norm = np.zeros((original.shape[0], original.shape[1]), dtype=np.float32)
    cv2.circle(depth_norm, (original.shape[1] // 2, original.shape[0] // 2), 100, 1, -1)
    depth_norm = cv2.GaussianBlur(depth_norm, (21, 21), 0)

    blurred_image = np.zeros_like(original_rgb)

    for i in range(original.shape[0]):
        for j in range(original.shape[1]):
            blur_radius = int(depth_norm[i, j] * max_blur_radius)
            if blur_radius % 2 == 0:
                blur_radius += 1

            x_min = max(j - blur_radius, 0)
            x_max = min(j + blur_radius, original.shape[1])
            y_min = max(i - blur_radius, 0)
            y_max = min(i + blur_radius, original.shape[0])

            roi = original_rgb[y_min:y_max, x_min:x_max]

            if blur_radius > 1:
                blurred_roi = cv2.GaussianBlur(roi, (blur_radius, blur_radius), 0)
                try:
                    blurred_image[i, j] = blurred_roi[
                        blur_radius // 2, blur_radius // 2
                    ]
                except:
                    blurred_image[i, j] = original_rgb[i, j]
            else:
                blurred_image[i, j] = original_rgb[i, j]

    return blurred_image


def apply_all_blurs(img, g_kernel, lens_radius, synthetic_radius):
    g = gaussian_blur(img, g_kernel)
    l = lens_blur(img, lens_radius)
    s = synthetic_lens_blur(img, synthetic_radius)
    return g, l, s


def update_gaussian(img, kernel_size):
    return gaussian_blur(img, kernel_size)


def update_lens(img, radius):
    return lens_blur(img, radius)


def update_synthetic(img, radius):
    return synthetic_lens_blur(img, radius)


with gr.Blocks() as demo:
    gr.Markdown(
        "## 🌀 Blur Effects Comparison: Gaussian, Depth-Based, Synthetic (Depth Based Blur works with bottles)"
    )

    with gr.Row():
        image_input = gr.Image(type="pil", label="Upload Image")

    with gr.Row():
        g_slider = gr.Slider(1, 49, step=2, value=11, label="Gaussian Kernel Size")
        lens_slider = gr.Slider(
            1,
            50,
            step=1,
            value=15,
            label="Depth-Based Blur Intensity (Works with bottles)",
        )
        synth_slider = gr.Slider(1, 50, step=1, value=25, label="Synthetic Blur Radius")

    with gr.Row():
        g_output = gr.Image(label="Gaussian Blurred Image")
        l_output = gr.Image(label="Depth-Based Lens Blurred Image")
        s_output = gr.Image(label="Synthetic Depth Lens Blurred Image")

    # Initial image upload updates all three
    image_input.change(
        fn=apply_all_blurs,
        inputs=[image_input, g_slider, lens_slider, synth_slider],
        outputs=[g_output, l_output, s_output],
    )

    # Individual updates for each slider
    g_slider.change(
        fn=update_gaussian, inputs=[image_input, g_slider], outputs=g_output
    )
    lens_slider.change(
        fn=update_lens, inputs=[image_input, lens_slider], outputs=l_output
    )
    synth_slider.change(
        fn=update_synthetic, inputs=[image_input, synth_slider], outputs=s_output
    )

demo.launch()