HotOrNotBot / app.py
DopeyFace's picture
Update app.py
fb8e591 verified
Raw
History Blame Contribute Delete
3.41 kB
import sys
import selectors
import asyncio
import torch
import clip
from PIL import Image
import gradio as gr
# =====================================================================
# 1. PYTHON 3.13 + GRADIO HOT RELOAD PATCH
# =====================================================================
# Prevent 'ValueError: Invalid file descriptor: -1' during dev reloads.
_orig_fileobj_to_fd = selectors._fileobj_to_fd
def _safe_fileobj_to_fd(fileobj):
try:
return _orig_fileobj_to_fd(fileobj)
except ValueError as e:
if "Invalid file descriptor: -1" in str(e):
return -1
raise e
selectors._fileobj_to_fd = _safe_fileobj_to_fd
def cleanup_filter(exctype, value, traceback):
if issubclass(exctype, ValueError) and "Invalid file descriptor" in str(value):
return
sys.__excepthook__(exctype, value, traceback)
sys.excepthook = cleanup_filter
# =====================================================================
# 2. MODEL INITIALIZATION
device = "cuda" if torch.cuda.is_available() else "cpu"
model, preprocess = clip.load("ViT-B/32", device=device)
# 3. MAIN EVALUATION LOGIC
def hotornot(image, gender):
if image is None:
return "N/A", "N/A", "N/A", "N/A"
# Safely convert Gradio NumPy array back to PIL Image
image_pil = Image.fromarray(image.astype("uint8"), "RGB")
image_tensor = preprocess(image_pil).unsqueeze(0).to(device)
# Flattened text prompt structure for single-pass GPU batching
all_terms = [
f'a hot {gender}', f'a gross {gender}',
f'a beautiful {gender}', f'an ugly {gender}',
f'an attractive {gender}', f'a hideous {gender}'
]
text_tokens = clip.tokenize(all_terms).to(device)
with torch.no_grad():
# Complete all evaluations simultaneously in a single forward pass
logits_per_image, _ = model(image_tensor, text_tokens)
probs = logits_per_image.softmax(dim=-1).cpu().numpy()[0]
# Deconstruct batch pairs
p1, n1 = probs[0], probs[1]
p2, n2 = probs[2], probs[3]
p3, n3 = probs[4], probs[5]
# Math safely normalized between 0 and 100
hotness_score = round(((p1 - n1) + 1) * 50, 2)
beauty_score = round(((p2 - n2) + 1) * 50, 2)
attractiveness_score = round(((p3 - n3) + 1) * 50, 2)
# Compute a balanced composite score across all traits
avg_positive = (p1 + p2 + p3) / 3
avg_negative = (n1 + n2 + n3) / 3
composite = round(((avg_positive - avg_negative) + 1) * 50, 2)
return composite, hotness_score, beauty_score, attractiveness_score
# 4. GRADIO INTERFACE CONFIGURATION
iface = gr.Interface(
fn=hotornot,
inputs=[
gr.Image(label="Image"),
gr.Dropdown(
choices=['person', 'man', 'woman'],
value='person',
label="Gender/Identity Type"
)
],
outputs=[
gr.Textbox(label="Total Hot or Not™ Score"),
gr.Textbox(label="Hotness Score"),
gr.Textbox(label="Beauty Score"),
gr.Textbox(label="Attractiveness Score"),
],
title="Hot or Not",
description="A simple hot or not app using OpenAI's CLIP model. The input image is passed to CLIP and evaluated against relative contrasting semantic descriptions.",
)
if __name__ == "__main__":
# Explicitly enforce share=False to mitigate proxy collisions on local SSR setups
iface.launch(share=False)