import os
import cv2
import clip
import time
import torch
import warnings
import numpy as np
import streamlit as st
from PIL import Image, ImageDraw
from segment_anything import build_sam, SamAutomaticMaskGenerator
warnings.filterwarnings("ignore")
st.set_page_config(
page_title="CLIP+SAM - WebApp",
page_icon="✨",
layout="wide",
initial_sidebar_state="auto",
)
MODEL_CHECKPOINT = "model/sam_vit_h_4b8939.pth"
upload_path = "uploads/"
@st.cache_resource()
def mask_generate(MODEL_CHECKPOINT):
# Download the model weights to load them here
model_start_time = time.time()
mask_generator = SamAutomaticMaskGenerator(build_sam(checkpoint=MODEL_CHECKPOINT))
model_end_time = time.time()
print("-" * 50)
print(
f"Model downloaded successfully in {model_end_time - model_start_time} seconds."
)
return mask_generator
def generate_image_masks(image_path, mask_generator):
img_mask_start_time = time.time()
image = cv2.imread(image_path)
image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
masks = mask_generator.generate(image)
img_mask_end_time = time.time()
print("-" * 50)
print(
f"Image mask generated successfully in {img_mask_end_time - img_mask_start_time} seconds."
)
return masks
def convert_box_xywh_to_xyxy(box):
x1 = box[0]
y1 = box[1]
x2 = box[0] + box[2]
y2 = box[1] + box[3]
return [x1, y1, x2, y2]
def segment_image(image, segmentation_mask):
image_array = np.array(image)
segmented_image_array = np.zeros_like(image_array)
segmented_image_array[segmentation_mask] = image_array[segmentation_mask]
segmented_image = Image.fromarray(segmented_image_array)
black_image = Image.new("RGB", image.size, (0, 0, 0))
transparency_mask = np.zeros_like(segmentation_mask, dtype=np.uint8)
transparency_mask[segmentation_mask] = 255
transparency_mask_image = Image.fromarray(transparency_mask, mode="L")
black_image.paste(segmented_image, mask=transparency_mask_image)
return black_image
def load_CLIP():
# Load CLIP
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
print(f"Using {device.type} for inference...")
clip_start_time = time.time()
model, preprocess = clip.load("ViT-B/32", device=device)
clip_end_time = time.time()
print("-" * 50)
print(f"CLIP loaded successfully in {clip_end_time - clip_start_time} seconds.")
return model, preprocess, device
@torch.no_grad()
def retriev(elements: list[Image.Image], search_text: str) -> int:
model, preprocess, device = load_CLIP()
preprocessed_images = [preprocess(image).to(device) for image in elements]
tokenized_text = clip.tokenize([search_text]).to(device)
stacked_images = torch.stack(preprocessed_images)
image_features = model.encode_image(stacked_images)
text_features = model.encode_text(tokenized_text)
image_features /= image_features.norm(dim=-1, keepdim=True)
text_features /= text_features.norm(dim=-1, keepdim=True)
probs = 100.0 * image_features @ text_features.T
return probs[:, 0].softmax(dim=0)
def get_indices_of_values_above_threshold(values, threshold):
return [i for i, v in enumerate(values) if v > threshold]
mask_generator = mask_generate(MODEL_CHECKPOINT)
st.title("✨ CLIP + SAM 🏜")
st.info(" Let me help perform open vocabulary image segmentation. 😉")
col_a, col_b = st.columns(2)
prompt = st.text_input("Enter your text", "pear")
image_path = st.file_uploader("Upload Image 🚀", type=["png", "jpg", "bmp", "jpeg"])
if image_path is not None and (prompt is not None or len(prompt.strip()) != 0):
with open(os.path.join(upload_path, image_path.name), "wb") as f:
f.write((image_path).getbuffer())
uploaded_image_path = os.path.abspath(os.path.join(upload_path, image_path.name))
with st.spinner("Working... 💫"):
# Cut out all masks
image = Image.open(uploaded_image_path)
cropped_boxes = []
masks = generate_image_masks(uploaded_image_path, mask_generator)
for mask in masks:
cropped_boxes.append(
segment_image(image, mask["segmentation"]).crop(
convert_box_xywh_to_xyxy(mask["bbox"])
)
)
scores = retriev(cropped_boxes, str(prompt))
indices = get_indices_of_values_above_threshold(scores, 0.05)
segmentation_masks = []
for seg_idx in indices:
segmentation_mask_image = Image.fromarray(
masks[seg_idx]["segmentation"].astype("uint8") * 255
)
segmentation_masks.append(segmentation_mask_image)
original_image = Image.open(uploaded_image_path)
overlay_image = Image.new("RGBA", image.size, (0, 0, 0, 0))
overlay_color = (255, 0, 0, 200)
draw = ImageDraw.Draw(overlay_image)
for segmentation_mask_image in segmentation_masks:
draw.bitmap((0, 0), segmentation_mask_image, fill=overlay_color)
result_image = Image.alpha_composite(
original_image.convert("RGBA"), overlay_image
)
np_image = np.array(result_image)
with st.container():
col1, col2 = st.columns(2)
with col1:
st.image(image, width=500)
st.success("Original Image")
with col2:
st.image(np_image, width=500)
st.success("Output based on CLIP+SAM")
else:
st.warning("⚠ Please upload your Image! 😯")
st.markdown(
"