Update app.py
Browse files
app.py
CHANGED
@@ -14,13 +14,14 @@ from transformers import AutoModelForImageSegmentation, pipeline
|
|
14 |
# Set device (GPU if available, else CPU)
|
15 |
device = "cuda" if torch.cuda.is_available() else "cpu"
|
16 |
|
17 |
-
# Retrieve Hugging Face token from environment
|
|
|
18 |
hf_token = os.environ.get("HF_ACCESS_TOKEN")
|
19 |
if hf_token is None:
|
20 |
-
print("Warning: HF_ACCESS_TOKEN is not set.
|
21 |
|
22 |
# Load the segmentation model (RMBG-2.0)
|
23 |
-
#
|
24 |
segmentation_model = AutoModelForImageSegmentation.from_pretrained(
|
25 |
'briaai/RMBG-2.0',
|
26 |
trust_remote_code=True,
|
@@ -29,7 +30,7 @@ segmentation_model = AutoModelForImageSegmentation.from_pretrained(
|
|
29 |
segmentation_model.to(device)
|
30 |
segmentation_model.eval()
|
31 |
|
32 |
-
# Define the
|
33 |
image_size = (512, 512)
|
34 |
segmentation_transform = transforms.Compose([
|
35 |
transforms.Resize(image_size),
|
@@ -50,11 +51,11 @@ def segment_and_blur_background(input_image: Image.Image, blur_radius: int = 15,
|
|
50 |
then composites a Gaussian-blurred background with the sharp foreground.
|
51 |
The segmentation threshold is adjustable.
|
52 |
"""
|
53 |
-
# Ensure the image is in RGB and
|
54 |
image = input_image.convert("RGB")
|
55 |
orig_width, orig_height = image.size
|
56 |
|
57 |
-
# Preprocess image for segmentation
|
58 |
input_tensor = segmentation_transform(image).unsqueeze(0).to(device)
|
59 |
|
60 |
# Run inference on the segmentation model
|
@@ -65,75 +66,63 @@ def segment_and_blur_background(input_image: Image.Image, blur_radius: int = 15,
|
|
65 |
# Create a binary mask using the adjustable threshold
|
66 |
binary_mask = (pred > threshold).float()
|
67 |
mask_pil = transforms.ToPILImage()(binary_mask).convert("L")
|
68 |
-
# Convert grayscale mask to pure binary (0 or 255)
|
69 |
mask_pil = mask_pil.point(lambda p: 255 if p > 128 else 0)
|
70 |
-
# Resize mask back to the original image dimensions
|
71 |
mask_pil = mask_pil.resize((orig_width, orig_height), resample=Image.BILINEAR)
|
72 |
|
73 |
-
# Apply Gaussian blur to the
|
74 |
blurred_image = image.filter(ImageFilter.GaussianBlur(blur_radius))
|
75 |
-
# Composite the
|
76 |
final_image = Image.composite(image, blurred_image, mask_pil)
|
77 |
return final_image
|
78 |
|
79 |
def depth_based_lens_blur(input_image: Image.Image, max_blur: float = 2, num_bands: int = 40, invert_depth: bool = False) -> Image.Image:
|
80 |
"""
|
81 |
Applies a depth-based blur effect using a depth map from Depth-Anything.
|
82 |
-
The
|
83 |
"""
|
84 |
-
# Resize
|
85 |
image_resized = input_image.resize((512, 512))
|
86 |
|
87 |
-
#
|
88 |
results = depth_pipeline(image_resized)
|
89 |
depth_map_image = results['depth']
|
90 |
|
91 |
-
#
|
92 |
depth_array = np.array(depth_map_image, dtype=np.float32)
|
93 |
d_min, d_max = depth_array.min(), depth_array.max()
|
94 |
depth_norm = (depth_array - d_min) / (d_max - d_min + 1e-8)
|
95 |
if invert_depth:
|
96 |
depth_norm = 1.0 - depth_norm
|
97 |
|
98 |
-
# Convert the resized image to RGBA for compositing
|
99 |
orig_rgba = image_resized.convert("RGBA")
|
100 |
final_image = orig_rgba.copy()
|
101 |
|
102 |
-
# Divide the
|
103 |
band_edges = np.linspace(0, 1, num_bands + 1)
|
104 |
for i in range(num_bands):
|
105 |
band_min = band_edges[i]
|
106 |
band_max = band_edges[i + 1]
|
107 |
-
# Use the midpoint of the band to determine the blur strength.
|
108 |
mid = (band_min + band_max) / 2.0
|
109 |
blur_radius_band = (1 - mid) * max_blur
|
110 |
|
111 |
-
# Create a blurred version of the image for this band.
|
112 |
blurred_version = orig_rgba.filter(ImageFilter.GaussianBlur(blur_radius_band))
|
113 |
-
|
114 |
-
# Create a mask for pixels whose normalized depth falls within this band.
|
115 |
band_mask = ((depth_norm >= band_min) & (depth_norm < band_max)).astype(np.uint8) * 255
|
116 |
band_mask_pil = Image.fromarray(band_mask, mode="L")
|
117 |
|
118 |
-
# Composite the blurred version with the current final image using the band mask.
|
119 |
final_image = Image.composite(blurred_version, final_image, band_mask_pil)
|
120 |
|
121 |
-
# Return the final composited image as RGB.
|
122 |
return final_image.convert("RGB")
|
123 |
|
124 |
def process_image(input_image: Image.Image, effect: str, threshold: float, blur_intensity: float) -> Image.Image:
|
125 |
"""
|
126 |
-
|
127 |
-
-
|
128 |
-
-
|
129 |
-
The threshold slider is used only for the segmentation effect.
|
130 |
-
The blur_intensity slider controls the blur strength in both effects.
|
131 |
"""
|
132 |
if effect == "Gaussian Blur Background":
|
133 |
-
# For segmentation, use the threshold and blur_intensity (as blur_radius)
|
134 |
return segment_and_blur_background(input_image, blur_radius=int(blur_intensity), threshold=threshold)
|
135 |
elif effect == "Depth-based Lens Blur":
|
136 |
-
# For depth-based blur, use the blur_intensity as the max blur value.
|
137 |
return depth_based_lens_blur(input_image, max_blur=blur_intensity)
|
138 |
else:
|
139 |
return input_image
|
|
|
14 |
# Set device (GPU if available, else CPU)
|
15 |
device = "cuda" if torch.cuda.is_available() else "cpu"
|
16 |
|
17 |
+
# Retrieve the Hugging Face access token from environment variables.
|
18 |
+
# In your Space, you will set this in the Secrets panel.
|
19 |
hf_token = os.environ.get("HF_ACCESS_TOKEN")
|
20 |
if hf_token is None:
|
21 |
+
print("Warning: HF_ACCESS_TOKEN environment variable is not set. Model access might fail.")
|
22 |
|
23 |
# Load the segmentation model (RMBG-2.0)
|
24 |
+
# Make sure that you have been granted access to this gated model.
|
25 |
segmentation_model = AutoModelForImageSegmentation.from_pretrained(
|
26 |
'briaai/RMBG-2.0',
|
27 |
trust_remote_code=True,
|
|
|
30 |
segmentation_model.to(device)
|
31 |
segmentation_model.eval()
|
32 |
|
33 |
+
# Define the transformation for segmentation (resize to 512x512, convert to tensor, and normalize)
|
34 |
image_size = (512, 512)
|
35 |
segmentation_transform = transforms.Compose([
|
36 |
transforms.Resize(image_size),
|
|
|
51 |
then composites a Gaussian-blurred background with the sharp foreground.
|
52 |
The segmentation threshold is adjustable.
|
53 |
"""
|
54 |
+
# Ensure the image is in RGB and record original dimensions
|
55 |
image = input_image.convert("RGB")
|
56 |
orig_width, orig_height = image.size
|
57 |
|
58 |
+
# Preprocess the image for segmentation
|
59 |
input_tensor = segmentation_transform(image).unsqueeze(0).to(device)
|
60 |
|
61 |
# Run inference on the segmentation model
|
|
|
66 |
# Create a binary mask using the adjustable threshold
|
67 |
binary_mask = (pred > threshold).float()
|
68 |
mask_pil = transforms.ToPILImage()(binary_mask).convert("L")
|
|
|
69 |
mask_pil = mask_pil.point(lambda p: 255 if p > 128 else 0)
|
|
|
70 |
mask_pil = mask_pil.resize((orig_width, orig_height), resample=Image.BILINEAR)
|
71 |
|
72 |
+
# Apply Gaussian blur to create the background
|
73 |
blurred_image = image.filter(ImageFilter.GaussianBlur(blur_radius))
|
74 |
+
# Composite the foreground with the blurred background using the mask
|
75 |
final_image = Image.composite(image, blurred_image, mask_pil)
|
76 |
return final_image
|
77 |
|
78 |
def depth_based_lens_blur(input_image: Image.Image, max_blur: float = 2, num_bands: int = 40, invert_depth: bool = False) -> Image.Image:
|
79 |
"""
|
80 |
Applies a depth-based blur effect using a depth map from Depth-Anything.
|
81 |
+
The blur intensity is controlled by the max_blur parameter.
|
82 |
"""
|
83 |
+
# Resize input image to 512x512 for the depth estimation model
|
84 |
image_resized = input_image.resize((512, 512))
|
85 |
|
86 |
+
# Obtain the depth map using the depth estimation pipeline
|
87 |
results = depth_pipeline(image_resized)
|
88 |
depth_map_image = results['depth']
|
89 |
|
90 |
+
# Normalize the depth map to [0, 1]
|
91 |
depth_array = np.array(depth_map_image, dtype=np.float32)
|
92 |
d_min, d_max = depth_array.min(), depth_array.max()
|
93 |
depth_norm = (depth_array - d_min) / (d_max - d_min + 1e-8)
|
94 |
if invert_depth:
|
95 |
depth_norm = 1.0 - depth_norm
|
96 |
|
97 |
+
# Convert the resized image to RGBA for proper compositing
|
98 |
orig_rgba = image_resized.convert("RGBA")
|
99 |
final_image = orig_rgba.copy()
|
100 |
|
101 |
+
# Divide the depth range into bands and apply variable blur
|
102 |
band_edges = np.linspace(0, 1, num_bands + 1)
|
103 |
for i in range(num_bands):
|
104 |
band_min = band_edges[i]
|
105 |
band_max = band_edges[i + 1]
|
|
|
106 |
mid = (band_min + band_max) / 2.0
|
107 |
blur_radius_band = (1 - mid) * max_blur
|
108 |
|
|
|
109 |
blurred_version = orig_rgba.filter(ImageFilter.GaussianBlur(blur_radius_band))
|
|
|
|
|
110 |
band_mask = ((depth_norm >= band_min) & (depth_norm < band_max)).astype(np.uint8) * 255
|
111 |
band_mask_pil = Image.fromarray(band_mask, mode="L")
|
112 |
|
|
|
113 |
final_image = Image.composite(blurred_version, final_image, band_mask_pil)
|
114 |
|
|
|
115 |
return final_image.convert("RGB")
|
116 |
|
117 |
def process_image(input_image: Image.Image, effect: str, threshold: float, blur_intensity: float) -> Image.Image:
|
118 |
"""
|
119 |
+
Dispatches the image processing based on the chosen effect:
|
120 |
+
- 'Gaussian Blur Background' uses segmentation with adjustable threshold and blur radius.
|
121 |
+
- 'Depth-based Lens Blur' uses a depth-based approach with adjustable blur intensity.
|
|
|
|
|
122 |
"""
|
123 |
if effect == "Gaussian Blur Background":
|
|
|
124 |
return segment_and_blur_background(input_image, blur_radius=int(blur_intensity), threshold=threshold)
|
125 |
elif effect == "Depth-based Lens Blur":
|
|
|
126 |
return depth_based_lens_blur(input_image, max_blur=blur_intensity)
|
127 |
else:
|
128 |
return input_image
|