File size: 5,980 Bytes
3fc013c 3da52e9 3fc013c 0a9b7fb 3fc013c 8c565e5 3da52e9 3fc013c 0a9b7fb 3fc013c 0a9b7fb 3fc013c 3da52e9 8c565e5 3da52e9 3fc013c 8c565e5 3da52e9 0a9b7fb 3da52e9 8c565e5 3da52e9 8c565e5 3da52e9 8c565e5 3da52e9 8c565e5 3da52e9 8c565e5 3da52e9 0a9b7fb 3da52e9 0a9b7fb 3da52e9 3fc013c 0a9b7fb 59d403e 0a9b7fb 8c565e5 3da52e9 0a9b7fb 3da52e9 59d403e 0a9b7fb 3fc013c 0a9b7fb | 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 | """
CLIP Image & Video Embedding API - Lightweight version for HF Spaces free tier
Supports URL, base64 image input, and video URLs (extracts frames)
"""
import gradio as gr
import torch
from PIL import Image
from transformers import CLIPProcessor, CLIPModel
import requests
from io import BytesIO
import base64
import tempfile
import os
# Use CPU and smaller memory footprint
model = None
processor = None
def load_model():
global model, processor
if model is None:
model = CLIPModel.from_pretrained("openai/clip-vit-base-patch32")
processor = CLIPProcessor.from_pretrained("openai/clip-vit-base-patch32")
model.eval()
return model, processor
def extract_video_frames(video_url: str, num_frames: int = 3):
"""Extract frames from video URL using cv2"""
try:
import cv2
import numpy as np
# Download video to temp file
response = requests.get(video_url, timeout=60, stream=True)
with tempfile.NamedTemporaryFile(suffix='.mp4', delete=False) as tmp:
for chunk in response.iter_content(chunk_size=8192):
tmp.write(chunk)
tmp_path = tmp.name
# Open video
cap = cv2.VideoCapture(tmp_path)
total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
if total_frames == 0:
os.unlink(tmp_path)
return []
# Calculate frame positions (start, middle, end)
if num_frames == 1:
positions = [0]
elif num_frames == 2:
positions = [0, total_frames - 1]
else:
positions = [0, total_frames // 2, max(0, total_frames - 10)]
frames = []
for pos in positions[:num_frames]:
cap.set(cv2.CAP_PROP_POS_FRAMES, pos)
ret, frame = cap.read()
if ret:
# Convert BGR to RGB
frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
pil_image = Image.fromarray(frame_rgb)
frames.append(pil_image)
cap.release()
os.unlink(tmp_path)
return frames
except Exception as e:
print(f"Video frame extraction error: {e}")
return []
def is_video_url(url: str) -> bool:
"""Check if URL is a video"""
video_extensions = ['.mp4', '.mov', '.avi', '.webm', '.mkv']
url_lower = url.lower()
return any(ext in url_lower for ext in video_extensions) or '/video/' in url_lower
def get_embedding(image_input: str):
"""Get CLIP embedding from image URL, base64 string, or video URL"""
try:
if not image_input:
return {"success": False, "error": "Please provide an image/video URL or base64 string"}
# Load model on first use
model, processor = load_model()
images = []
is_video = False
# Check if it's a video URL
if image_input.startswith('http') and is_video_url(image_input):
is_video = True
frames = extract_video_frames(image_input, num_frames=3)
if not frames:
return {"success": False, "error": "Could not extract frames from video"}
images = frames
# Check if it's base64 (data:image/... or raw base64)
elif image_input.startswith('data:image'):
base64_data = image_input.split(',')[1] if ',' in image_input else image_input
image_bytes = base64.b64decode(base64_data)
images = [Image.open(BytesIO(image_bytes)).convert('RGB')]
elif not image_input.startswith('http'):
# Try as raw base64
try:
image_bytes = base64.b64decode(image_input)
images = [Image.open(BytesIO(image_bytes)).convert('RGB')]
except:
return {"success": False, "error": "Invalid input: provide URL or base64"}
else:
# It's an image URL - download it
response = requests.get(image_input, timeout=30)
images = [Image.open(BytesIO(response.content)).convert('RGB')]
# Get embeddings for all images/frames
all_embeddings = []
for img in images:
inputs = processor(images=img, return_tensors="pt")
with torch.no_grad():
features = model.get_image_features(**inputs)
# Normalize
embedding = features / features.norm(dim=-1, keepdim=True)
all_embeddings.append(embedding[0].tolist())
# For single image, return single embedding
# For video, return array of frame embeddings
if len(all_embeddings) == 1:
return {
"success": True,
"embedding": all_embeddings[0],
"dimensions": 512,
"type": "image"
}
else:
return {
"success": True,
"embeddings": all_embeddings,
"embedding": all_embeddings[0], # First frame as default
"dimensions": 512,
"frames": len(all_embeddings),
"type": "video"
}
except Exception as e:
return {"success": False, "error": str(e)}
# Gradio interface with API enabled
demo = gr.Interface(
fn=get_embedding,
inputs=gr.Textbox(
label="Image/Video (URL or base64)",
placeholder="https://example.com/image.jpg or video.mp4 or data:image/jpeg;base64,..."
),
outputs=gr.JSON(label="Result"),
title="CLIP Embedding API",
description="Get 512-dim CLIP embeddings from image URL, base64, or video URL (extracts 3 frames)",
api_name="predict"
)
if __name__ == "__main__":
demo.launch()
|