ohmydog / app.py
๊น€์ˆ˜์ •
represent score in percent format
5b89d77
import gradio as gr
from fastapi import FastAPI, File, UploadFile
from fastapi.responses import JSONResponse
from PIL import Image
import numpy as np
import io
import torch
import torch.nn.functional as F
from transformers import AutoImageProcessor, AutoModelForImageClassification
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
image_processor = AutoImageProcessor.from_pretrained("wesleyacheng/dog-breeds-multiclass-image-classification-with-vit")
model = AutoModelForImageClassification.from_pretrained("wesleyacheng/dog-breeds-multiclass-image-classification-with-vit").to(device)
labels = {int(k): v for k, v in model.config.id2label.items()}
print(labels)
# FastAPI ์•ฑ ์ƒ์„ฑ
app = FastAPI()
# ํ’ˆ์ข… ํ•œ๊ธ€ ๋ฒˆ์—ญ
BREED_TRANSLATIONS = {
"otterhound": "์˜คํ„ฐํ•˜์šด๋“œ", "cocker_spaniel": "์ฝ”์ปค ์ŠคํŒจ๋‹ˆ์–ผ", "brittany_spaniel": "๋ธŒ๋ฆฌํƒ€๋‹ˆ ์ŠคํŒจ๋‹ˆ์–ผ",
"afghan_hound": "์•„ํ”„๊ฐ„ ํ•˜์šด๋“œ", "maltese_dog": "๋ชฐํ‹ฐ์ฆˆ", "schipperke": "์Šคํ‚คํผํ‚ค",
"irish_setter": "์•„์ด๋ฆฌ์‹œ ์„ธํ„ฐ", "pekinese": "ํŽ˜ํ‚ค๋‹ˆ์ฆˆ", "golden_retriever": "๊ณจ๋“  ๋ฆฌํŠธ๋ฆฌ๋ฒ„",
"vizsla": "๋น„์ฆ๋ผ", "welsh_springer_spaniel": "์›ฐ์‹œ ์Šคํ”„๋ง๊ฑฐ ์ŠคํŒจ๋‹ˆ์–ผ",
"staffordshire_bullterrier": "์Šคํƒœํผ๋“œ์…” ๋ถˆํ…Œ๋ฆฌ์–ด", "border_collie": "๋ณด๋” ์ฝœ๋ฆฌ",
"irish_terrier": "์•„์ด๋ฆฌ์‹œ ํ…Œ๋ฆฌ์–ด", "eskimo_dog": "์—์Šคํ‚ค๋ชจ๊ฒฌ", "pug": "ํผ๊ทธ",
"kelpie": "์ผˆํ”ผ", "yorkshire_terrier": "์š”ํฌ์…” ํ…Œ๋ฆฌ์–ด", "tibetan_terrier": "ํ‹ฐ๋ฒ ํƒ„ ํ…Œ๋ฆฌ์–ด",
"walker_hound": "์›Œ์ปค ํ•˜์šด๋“œ", "affenpinscher": "์•„ํŽœํ•€์…”", "cardigan": "์นด๋””๊ฑด ์ฝ”๊ธฐ",
"english_springer": "์ž‰๊ธ€๋ฆฌ์‹œ ์Šคํ”„๋ง๊ฑฐ", "english_foxhound": "์ž‰๊ธ€๋ฆฌ์‹œ ํญ์Šคํ•˜์šด๋“œ",
"west_highland_white_terrier": "์›จ์ŠคํŠธ ํ•˜์ด๋žœ๋“œ ํ™”์ดํŠธ ํ…Œ๋ฆฌ์–ด", "lakeland_terrier": "๋ ˆ์ดํด๋žœ๋“œ ํ…Œ๋ฆฌ์–ด",
"rhodesian_ridgeback": "๋กœ๋””์ง€์•ˆ ๋ฆฌ์ง€๋ฐฑ", "gordon_setter": "๊ณ ๋“  ์„ธํ„ฐ", "lhasa": "๋ผ์‚ฌ์••์†Œ",
"curly": "์ปฌ๋ฆฌ ์ฝ”ํ‹ฐ๋“œ ๋ฆฌํŠธ๋ฆฌ๋ฒ„", "beagle": "๋น„๊ธ€", "tibetan_mastiff": "ํ‹ฐ๋ฒ ํƒ„ ๋งˆ์Šคํ‹ฐํ”„",
"sussex_spaniel": "์„œ์‹์Šค ์ŠคํŒจ๋‹ˆ์–ผ", "saint_bernard": "์„ธ์ธํŠธ ๋ฒ„๋‚˜๋“œ", "toy_terrier": "ํ† ์ด ํ…Œ๋ฆฌ์–ด",
"standard_poodle": "์Šคํƒ ๋‹ค๋“œ ํ‘ธ๋“ค", "bernese_mountain_dog": "๋ฒ„๋‹ˆ์ฆˆ ๋งˆ์šดํ‹ด ๋…", "pomeranian": "ํฌ๋ฉ”๋ผ๋‹ˆ์•ˆ",
"ibizan_hound": "์ด๋น„์ž” ํ•˜์šด๋“œ", "redbone": "๋ ˆ๋“œ๋ณธ ์ฟคํ•˜์šด๋“œ", "toy_poodle": "ํ† ์ด ํ‘ธ๋“ค",
"basset": "๋ฐ”์…‹ ํ•˜์šด๋“œ", "scottish_deerhound": "์Šค์ฝ”ํ‹ฐ์‹œ ๋””์–ดํ•˜์šด๋“œ", "miniature_pinscher": "๋ฏธ๋‹ˆ์–ด์ฒ˜ ํ•€์…”",
"basenji": "๋ฐ”์„ผ์ง€", "border_terrier": "๋ณด๋” ํ…Œ๋ฆฌ์–ด", "bedlington_terrier": "๋ฒ ๋“ค๋งํ„ด ํ…Œ๋ฆฌ์–ด",
"kerry_blue_terrier": "์ผ€๋ฆฌ ๋ธ”๋ฃจ ํ…Œ๋ฆฌ์–ด", "weimaraner": "์™€์ด๋งˆ๋ผ๋„ˆ", "english_setter": "์ž‰๊ธ€๋ฆฌ์‹œ ์„ธํ„ฐ",
"bluetick": "๋ธ”๋ฃจํ‹ฑ ์ฟคํ•˜์šด๋“œ", "boston_bull": "๋ณด์Šคํ„ด ํ…Œ๋ฆฌ์–ด", "italian_greyhound": "์ดํƒˆ๋ฆฌ์•ˆ ๊ทธ๋ ˆ์ดํ•˜์šด๋“œ",
"dandie_dinmont": "๋Œ„๋”” ๋”˜๋ชฌํŠธ ํ…Œ๋ฆฌ์–ด", "airedale": "์—์–ด๋ฐ์ผ ํ…Œ๋ฆฌ์–ด", "irish_water_spaniel": "์•„์ด๋ฆฌ์‹œ ์›Œํ„ฐ ์ŠคํŒจ๋‹ˆ์–ผ",
"norfolk_terrier": "๋…ธํฝ ํ…Œ๋ฆฌ์–ด", "wire": "์™€์ด์–ด ํญ์Šค ํ…Œ๋ฆฌ์–ด", "french_bulldog": "ํ”„๋ Œ์น˜ ๋ถˆ๋…",
"soft": "์†Œํ”„ํŠธ ์ฝ”ํ‹ฐ๋“œ ํœ˜ํŠผ ํ…Œ๋ฆฌ์–ด", "komondor": "์ฝ”๋ชฌ๋„๋ฅด", "african_hunting_dog": "์•„ํ”„๋ฆฌ์นด ๋“ค๊ฐœ",
"siberian_husky": "์‹œ๋ฒ ๋ฆฌ์•ˆ ํ—ˆ์Šคํ‚ค", "newfoundland": "๋‰ดํŽ€๋“ค๋žœ๋“œ", "bouvier_des_flandres": "๋ถ€๋น„์— ๋ฐ ํ”Œ๋ž‘๋“œ๋ฅด",
"saluki": "์‚ด๋ฃจํ‚ค", "shetland_sheepdog": "์…ฐํ‹€๋žœ๋“œ ์‹œํ”„๋…", "collie": "์ฝœ๋ฆฌ", "rottweiler": "๋กœํŠธ์™€์ผ๋Ÿฌ",
"silky_terrier": "์‹คํ‚ค ํ…Œ๋ฆฌ์–ด", "norwegian_elkhound": "๋…ธ๋ฅด์›จ์ด ์—˜ํฌํ•˜์šด๋“œ", "chihuahua": "์น˜์™€์™€",
"leonberg": "๋ ˆ์˜จ๋ฒ ๋ฅด๊ฑฐ", "norwich_terrier": "๋…ธ๋ฆฌ์น˜ ํ…Œ๋ฆฌ์–ด", "cairn": "์ผ€์–ธ ํ…Œ๋ฆฌ์–ด", "boxer": "๋ณต์„œ",
"borzoi": "๋ณด๋ฅด์กฐ์ด", "dhole": "์Šน๋ƒฅ์ด", "samoyed": "์‚ฌ๋ชจ์˜ˆ๋“œ", "german_shepherd": "์ €๋จผ ์…ฐํผ๋“œ",
"labrador_retriever": "๋ž˜๋ธŒ๋ผ๋„ ๋ฆฌํŠธ๋ฆฌ๋ฒ„", "blenheim_spaniel": "๋ธ”๋ ˆ๋„˜ ์ŠคํŒจ๋‹ˆ์–ผ", "groenendael": "๊ทธ๋กœ๋„จ๋‹ฌ",
"doberman": "๋„๋ฒ ๋ฅด๋งŒ", "great_dane": "๊ทธ๋ ˆ์ดํŠธ ๋ฐ์ธ", "flat": "ํ”Œ๋žซ ์ฝ”ํ‹ฐ๋“œ ๋ฆฌํŠธ๋ฆฌ๋ฒ„",
"appenzeller": "์•„ํŽœ์ ค๋Ÿฌ", "shih": "์‹œ์ถ”", "japanese_spaniel": "์žฌํŒจ๋‹ˆ์ฆˆ ์ŠคํŒจ๋‹ˆ์–ผ",
"greater_swiss_mountain_dog": "๊ทธ๋ ˆ์ดํ„ฐ ์Šค์œ„์Šค ๋งˆ์šดํ‹ด ๋…", "black": "๋ธ”๋ž™ ์•ค ํƒ„ ์ฟคํ•˜์šด๋“œ",
"dingo": "๋”ฉ๊ณ ", "great_pyrenees": "๊ทธ๋ ˆ์ดํŠธ ํ”ผ๋ ˆ๋‹ˆ์ฆˆ", "whippet": "ํœ˜ํŽซ", "keeshond": "ํ‚ค์Šคํ˜ผํŠธ",
"malinois": "๋ง๋ฆฌ๋…ธ์ด์ฆˆ", "american_staffordshire_terrier": "์•„๋ฉ”๋ฆฌ์นธ ์Šคํƒœํผ๋“œ์…” ํ…Œ๋ฆฌ์–ด",
"mexican_hairless": "๋ฉ•์‹œ์นธ ํ—ค์–ด๋ฆฌ์Šค", "giant_schnauzer": "์ž์ด์–ธํŠธ ์Šˆ๋‚˜์šฐ์ €",
"brabancon_griffon": "๋ธŒ๋ผ๋ฐฉ์†ก ๊ทธ๋ฆฌํŽ‘", "kuvasz": "์ฟ ๋ฐ”์Šค", "miniature_poodle": "๋ฏธ๋‹ˆ์–ด์ฒ˜ ํ‘ธ๋“ค",
"irish_wolfhound": "์•„์ด๋ฆฌ์‹œ ์šธํ”„ํ•˜์šด๋“œ", "briard": "๋ธŒ๋ฆฌ์•„๋“œ", "clumber": "ํด๋Ÿผ๋ฒ„ ์ŠคํŒจ๋‹ˆ์–ผ",
"standard_schnauzer": "์Šคํƒ ๋‹ค๋“œ ์Šˆ๋‚˜์šฐ์ €", "bull_mastiff": "๋ถˆ ๋งˆ์Šคํ‹ฐํ”„", "malamute": "๋ง๋ผ๋ฎคํŠธ",
"sealyham_terrier": "์‹ค๋ฆฌ์—„ ํ…Œ๋ฆฌ์–ด", "entlebucher": "์—”ํ‹€๋ถ€์ฒ˜", "chow": "์ฐจ์šฐ์ฐจ์šฐ",
"papillon": "ํŒŒํ”ผ์šฉ", "pembroke": "ํŽจ๋ธŒ๋กœํฌ ์ฝ”๊ธฐ", "german_short": "์ €๋จผ ์‡ผํŠธํ—ค์–ด๋“œ ํฌ์ธํ„ฐ",
"old_english_sheepdog": "์˜ฌ๋“œ ์ž‰๊ธ€๋ฆฌ์‹œ ์‹œํ”„๋…", "chesapeake_bay_retriever": "์ฒด์„œํ”ผํฌ ๋ฒ ์ด ๋ฆฌํŠธ๋ฆฌ๋ฒ„",
"scotch_terrier": "์Šค์นด์น˜ ํ…Œ๋ฆฌ์–ด", "australian_terrier": "์˜ค์ŠคํŠธ๋ ˆ์ผ๋ฆฌ์•ˆ ํ…Œ๋ฆฌ์–ด",
"miniature_schnauzer": "๋ฏธ๋‹ˆ์–ด์ฒ˜ ์Šˆ๋‚˜์šฐ์ €", "bloodhound": "๋ธ”๋Ÿฌ๋“œํ•˜์šด๋“œ"
}
def predict(image: Image.Image, k=10):
inputs = image_processor(images=image, return_tensors="pt").to(device)
with torch.no_grad():
outputs = model(**inputs)
probs = F.softmax(outputs.logits, dim=-1)[0]
top_probs, top_idxs = torch.topk(probs, k=k)
top_probs = top_probs.cpu().tolist()
top_idxs = top_idxs.cpu().tolist()
# ์˜๋ฌธ ํ’ˆ์ข…๋ช…์„ ํ•œ๊ธ€๋กœ ๋ฒˆ์—ญํ•˜์—ฌ ๋ฐ˜ํ™˜ (ํ™•๋ฅ ์— 100 ๊ณฑํ•ด์„œ ํผ์„ผํŠธ๋กœ)
results = {}
for i, prob in zip(top_idxs, top_probs):
breed_name = labels[i]
korean_name = BREED_TRANSLATIONS.get(breed_name, breed_name)
results[korean_name] = round(float(prob) * 100, 3)
return results
def classify(profileImage: Image.Image):
result = predict(profileImage)
return result
# FastAPI ์—”๋“œํฌ์ธํŠธ: FormData๋กœ ์ด๋ฏธ์ง€ ๋ฐ›๊ธฐ
@app.post("/api/classify")
async def classify_api(profileImage: UploadFile = File(...)):
try:
# ์ด๋ฏธ์ง€ ํŒŒ์ผ ์ฝ๊ธฐ
contents = await profileImage.read()
image = Image.open(io.BytesIO(contents))
# ์˜ˆ์ธก ์ˆ˜ํ–‰
result = predict(image)
return JSONResponse(content={"success": True, "data": result})
except Exception as e:
return JSONResponse(
content={"success": False, "error": str(e)},
status_code=400
)
# Gradio ์ธํ„ฐํŽ˜์ด์Šค
demo = gr.Interface(fn=classify, inputs=gr.Image(type="pil", label="profileImage"), outputs="label")
# Gradio๋ฅผ FastAPI์— ๋งˆ์šดํŠธ
app = gr.mount_gradio_app(app, demo, path="/")
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=7860)