PaceOfAging / app.py
CelagenexResearch's picture
Update app.py
a3a3080 verified
import gradio as gr
from PIL import Image
import torch
import numpy as np
from transformers import CLIPProcessor, CLIPModel
# Load model and processor
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
model = CLIPModel.from_pretrained("openai/clip-vit-base-patch16").to(device)
processor = CLIPProcessor.from_pretrained("openai/clip-vit-base-patch16")
# (You can keep your STANFORD_BREEDS and BREED_LIFESPAN dictionaries as-is)
# -------- Helper Functions from your code --------
# Stanford Dogs Dataset - 120 Breeds (from search results [1][3][4])
STANFORD_BREEDS = [
"afghan hound", "african hunting dog", "airedale",
"american staffordshire terrier", "appenzeller", "australian terrier",
"basenji", "basset", "beagle", "bedlington terrier", "bernese mountain dog",
"black-and-tan coonhound", "blenheim spaniel", "bloodhound", "bluetick",
"border collie", "border terrier", "borzoi", "boston bull",
"bouvier des flandres", "boxer", "brabancon griffon", "briard",
"brittany spaniel", "bull mastiff", "cairn", "cardigan",
"chesapeake bay retriever", "chihuahua", "chow", "clumber",
"cocker spaniel", "collie", "curly-coated retriever", "dandie dinmont",
"dhole", "dingo", "doberman", "english foxhound", "english setter",
"english springer", "entlebucher", "eskimo dog", "flat-coated retriever",
"french bulldog", "german shepherd", "german short-haired pointer",
"giant schnauzer", "golden retriever", "gordon setter", "great dane",
"great pyrenees", "greater swiss mountain dog", "groenendael",
"ibizan hound", "irish setter", "irish terrier", "irish water spaniel",
"irish wolfhound", "italian greyhound", "japanese spaniel", "keeshond",
"kelpie", "kerry blue terrier", "komondor", "kuvasz", "labrador retriever",
"lakeland terrier", "leonberg", "lhasa", "malamute", "malinois",
"maltese dog", "mexican hairless", "miniature pinscher",
"miniature poodle", "miniature schnauzer", "newfoundland",
"norfolk terrier", "norwegian elkhound", "norwich terrier",
"old english sheepdog", "otterhound", "papillon", "pekinese", "pembroke",
"pomeranian", "pug", "redbone", "rhodesian ridgeback", "rottweiler",
"saint bernard", "saluki", "samoyed", "schipperke", "scotch terrier",
"scottish deerhound", "sealyham terrier", "shetland sheepdog", "shih tzu",
"siberian husky", "silky terrier", "soft-coated wheaten terrier",
"staffordshire bullterrier", "standard poodle", "standard schnauzer",
"sussex spaniel", "tibetan mastiff", "tibetan terrier", "toy poodle",
"toy terrier", "vizsla", "walker hound", "weimaraner",
"welsh springer spaniel", "west highland white terrier", "whippet",
"wire-haired fox terrier", "yorkshire terrier"
]
# Breed-specific lifespan data (years) for age prediction
BREED_LIFESPAN = {
# Sourced from Nature study (2022) and Scientific Reports (2024)
"afghan hound": 11.1,
"african hunting dog": 10.5, # Estimated (similar to other hounds)
"airedale": 11.5,
"american staffordshire terrier": 12.5,
"appenzeller": 13.0, # Estimated (similar to Swiss breeds)
"australian terrier": 13.5,
"basenji": 12.1,
"basset": 12.5,
"beagle": 12.5,
"bedlington terrier": 13.7,
"bernese mountain dog": 10.1,
"black-and-tan coonhound": 10.8,
"blenheim spaniel": 13.3, # Cavalier King Charles variant
"bloodhound": 9.3,
"bluetick": 11.0,
"border collie": 13.1,
"border terrier": 14.2,
"borzoi": 12.0,
"boston bull": 11.8,
"bouvier des flandres": 11.3,
"boxer": 11.3,
"brabancon griffon": 13.0,
"briard": 12.6,
"brittany spaniel": 13.5,
"bull mastiff": 10.2,
"cairn": 14.0,
"cardigan": 13.2, # Welsh Corgi
"chesapeake bay retriever": 11.6,
"chihuahua": 11.8,
"chow": 12.1,
"clumber": 12.3,
"cocker spaniel": 13.3,
"collie": 13.3,
"curly-coated retriever": 12.2,
"dandie dinmont": 12.8,
"dhole": 10.0, # Estimated (wild dog)
"dingo": 10.0, # Estimated (wild dog)
"doberman": 11.2,
"english foxhound": 13.0,
"english setter": 13.1,
"english springer": 13.5,
"entlebucher": 13.0,
"eskimo dog": 11.3,
"flat-coated retriever": 11.7,
"french bulldog": 9.8,
"german shepherd": 11.3,
"german short-haired pointer": 13.4,
"giant schnauzer": 12.1,
"golden retriever": 13.2,
"gordon setter": 12.4,
"great dane": 10.6,
"great pyrenees": 10.9,
"greater swiss mountain dog": 10.9,
"groenendael": 12.0, # Belgian Shepherd
"ibizan hound": 13.3,
"irish setter": 12.9,
"irish terrier": 13.5,
"irish water spaniel": 10.8,
"irish wolfhound": 9.9,
"italian greyhound": 14.0,
"japanese spaniel": 13.3, # Japanese Chin
"keeshond": 12.3,
"kelpie": 12.0,
"kerry blue terrier": 12.4,
"komondor": 10.5,
"kuvasz": 10.5,
"labrador retriever": 13.1,
"lakeland terrier": 14.2,
"leonberg": 10.0,
"lhasa": 14.0,
"malamute": 11.3,
"malinois": 12.0,
"maltese dog": 13.1,
"mexican hairless": 13.0, # Xoloitzcuintli
"miniature pinscher": 13.7,
"miniature poodle": 14.0,
"miniature schnauzer": 13.3,
"newfoundland": 11.0,
"norfolk terrier": 13.5,
"norwegian elkhound": 13.0,
"norwich terrier": 14.0,
"old english sheepdog": 12.1,
"otterhound": 12.0,
"papillon": 14.5,
"pekinese": 13.3,
"pembroke": 13.2, # Welsh Corgi
"pomeranian": 12.2,
"pug": 11.6,
"redbone": 12.0,
"rhodesian ridgeback": 12.0,
"rottweiler": 10.6,
"saint bernard": 9.3,
"saluki": 13.3,
"samoyed": 13.1,
"schipperke": 14.2,
"scotch terrier": 12.7, # Scottish Terrier
"scottish deerhound": 10.5,
"sealyham terrier": 13.1,
"shetland sheepdog": 13.4,
"shih tzu": 12.8,
"siberian husky": 11.9,
"silky terrier": 13.3,
"soft-coated wheaten terrier": 13.7,
"staffordshire bullterrier": 12.0,
"standard poodle": 14.0,
"standard schnauzer": 13.0,
"sussex spaniel": 13.5,
"tibetan mastiff": 13.3,
"tibetan terrier": 13.8,
"toy poodle": 14.0,
"toy terrier": 13.0,
"vizsla": 13.5,
"walker hound": 12.0,
"weimaraner": 12.8,
"welsh springer spaniel": 14.0,
"west highland white terrier": 13.4,
"whippet": 13.4,
"wire-haired fox terrier": 13.5,
"yorkshire terrier": 13.3
}
def predict_biological_age(image, breed):
avg_lifespan = BREED_LIFESPAN.get(breed.lower(), 12)
age_prompts = [f"a {age}-year-old {breed}" for age in range(1, int(avg_lifespan * 2) + 1)]
inputs = processor(text=age_prompts, images=image, return_tensors="pt", padding=True).to(device)
with torch.no_grad():
outputs = model(**inputs)
age_logits = outputs.logits_per_image
age_probs = age_logits.softmax(dim=1).cpu().numpy()[0]
return np.argmax(age_probs) + 1
def analyze_dog_health(image, user_breed=None, chronological_age=None):
image = image.convert("RGB")
inputs = processor(images=image, return_tensors="pt").to(device)
with torch.no_grad():
image_features = model.get_image_features(**inputs)
# 1. Breed classification
breed_inputs = processor(
text=[f"a photo of a {b}" for b in STANFORD_BREEDS],
return_tensors="pt",
padding=True
).to(device)
with torch.no_grad():
text_features = model.get_text_features(**breed_inputs)
breed_similarities = (image_features @ text_features.T).softmax(dim=-1)
top_breed_idx = breed_similarities.argmax().item()
predicted_breed = STANFORD_BREEDS[top_breed_idx]
breed_confidence = breed_similarities[0, top_breed_idx].item()
breed = user_breed if user_breed else predicted_breed
# 2. Health Aspects
health_aspects = {
"coat_health": ["shiny healthy coat", "dull patchy fur"],
"eye_clarity": ["bright clear eyes", "cloudy milky eyes"],
"body_condition": ["ideal muscle tone", "visible ribs or hip bones"],
"dental_health": ["clean white teeth", "yellow stained teeth"],
"energy_level": ["alert energetic posture", "lethargic tired appearance"],
"skin_health": ["smooth healthy skin", "red irritated skin"],
"weight_status": ["healthy weight", "overweight obese"],
"joint_health": ["strong stable joints", "stiff painful movement"]
}
health_report = {}
for aspect, (pos, neg) in health_aspects.items():
aspect_input = processor(text=[pos, neg], return_tensors="pt", padding=True).to(device)
with torch.no_grad():
aspect_features = model.get_text_features(**aspect_input)
similarity = (image_features @ aspect_features.T).softmax(dim=-1)
health_report[aspect] = {
"assessment": pos if similarity[0, 0] > similarity[0, 1] else neg,
"confidence": similarity[0, 0].item(),
"score": similarity[0, 0].item() - similarity[0, 1].item()
}
# 3. Biological age
bio_age = predict_biological_age(image, breed)
lifespan = BREED_LIFESPAN.get(breed.lower(), 12)
pace = round(bio_age / chronological_age, 2) if chronological_age else None
# 4. Final report
summary = f"Predicted Breed: {predicted_breed} ({breed_confidence:.1%})\n"
if user_breed:
summary += f"User Override Breed: {user_breed}\n"
summary += f"Biological Age: {bio_age} years\n"
summary += f"Chronological Age: {chronological_age if chronological_age else 'Not provided'}\n"
summary += f"Avg Lifespan: {lifespan} years\n"
if pace:
summary += f"Pace of Aging: {pace:.2f}x\n"
summary += "\n### Health Assessment\n"
for k, v in health_report.items():
summary += f"- {k.replace('_', ' ').title()}: {v['assessment']} (Confidence: {v['confidence']:.1%}, Score: {v['score']:.2f})\n"
return summary
# -------- Gradio Interface --------
iface = gr.Interface(
fn=analyze_dog_health,
inputs=[
gr.Image(type="pil", label="Upload Dog Image"),
gr.Textbox(label="(Optional) Override Breed"),
gr.Number(label="Chronological Age (in years)", precision=1)
],
outputs=gr.Markdown(),
title="🐶 Dog Health & Age Analyzer",
description="Upload a photo of your dog to predict its breed, biological age, health indicators, and compare with average lifespan.",
allow_flagging="never"
)
iface.launch()