|
from fastapi import FastAPI, File, UploadFile, Form |
|
from fastapi.responses import HTMLResponse |
|
from model import predict, get_advice |
|
from io import BytesIO |
|
from PIL import Image |
|
import base64 |
|
|
|
app = FastAPI() |
|
|
|
@app.get("/", response_class=HTMLResponse) |
|
|
|
async def index(): |
|
html_content = """ |
|
<!DOCTYPE html> |
|
<html> |
|
<head> |
|
<title>Détection de Race</title> |
|
<meta charset="UTF-8"> |
|
<style> |
|
body { |
|
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; |
|
background: linear-gradient(135deg, #e8eafa 0%, #dfecf2 100%); |
|
display: flex; |
|
flex-direction: column; |
|
align-items: center; |
|
justify-content: start; |
|
min-height: 100vh; |
|
padding-top: 60px; |
|
margin: 0; |
|
} |
|
h1 { |
|
color: #333; |
|
font-size: 2rem; |
|
margin-bottom: 30px; |
|
} |
|
form { |
|
background-color: #ffffff; |
|
width: 400px; |
|
padding: 30px; |
|
border-radius: 15px; |
|
box-shadow: 0 8px 16px rgba(0, 0, 0, 0.1); |
|
} |
|
label { |
|
font-size: 1rem; |
|
font-weight: bold; |
|
color: #444; |
|
} |
|
input[type="file"], select { |
|
margin-top: 10px; |
|
margin-bottom: 20px; |
|
padding: 8px; |
|
width: 100%; |
|
border: 1px solid #ccc; |
|
border-radius: 6px; |
|
background-color: #f9f9f9; |
|
} |
|
button { |
|
background: linear-gradient(135deg, #4d6bb0, #5a5e99); |
|
color: white; |
|
padding: 10px 20px; |
|
border: none; |
|
border-radius: 8px; |
|
font-size: 1rem; |
|
cursor: pointer; |
|
} |
|
img#preview { |
|
margin-top: 20px; |
|
max-width: 300px; |
|
border-radius: 12px; |
|
display: none; |
|
} |
|
</style> |
|
<script> |
|
document.addEventListener("DOMContentLoaded", function () { |
|
const form = document.getElementById("uploadForm"); |
|
const fileInput = document.getElementById("fileInput"); |
|
const langSelect = document.getElementById("lang"); |
|
const preview = document.getElementById("preview"); |
|
const labelInput = document.getElementById("labelinput"); |
|
const labelLang = document.getElementById("labelLang"); |
|
const title = document.getElementById("title"); |
|
|
|
// Appliquer la langue depuis l'URL |
|
const urlParams = new URLSearchParams(window.location.search); |
|
const langFromURL = urlParams.get("lang"); |
|
if (langFromURL) { |
|
langSelect.value = langFromURL; |
|
labelInput.textContent = langFromURL === "en" ? "Choose Picture :" : "Choisir une image :"; |
|
labelLang.textContent = langFromURL === "en" ? "Choose the language :" : "Choisir la langue :"; |
|
title.textContent = langFromURL === "en" ? "Race Detection from Image" : "Détection de Race à partir de l'Image"; |
|
} |
|
|
|
|
|
langSelect.addEventListener("change", function () { |
|
const currentUrl = new URL(window.location.href); |
|
currentUrl.searchParams.set("lang", langSelect.value); |
|
window.location.href = currentUrl.toString(); |
|
}); |
|
|
|
|
|
fileInput.addEventListener("change", function () { |
|
if (this.files.length > 0) { |
|
const reader = new FileReader(); |
|
reader.onload = function (e) { |
|
preview.src = e.target.result; |
|
preview.style.display = "block"; |
|
}; |
|
reader.readAsDataURL(this.files[0]); |
|
if (langSelect.value !== "") { |
|
setTimeout(() => form.submit(), 500); |
|
} |
|
} |
|
}); |
|
}); |
|
</script> |
|
</head> |
|
<body> |
|
<h1 id="title">Détection de Race à partir de l'Image</h1> |
|
<form id="uploadForm" action="/predict/" method="post" enctype="multipart/form-data"> |
|
<label id="labelLang" for="lang">Choisir la langue :</label> |
|
<select name="lang" id="lang" required> |
|
<option value="">-- Sélectionner --</option> |
|
<option value="fr">Français</option> |
|
<option value="en">English</option> |
|
</select><br><br> |
|
<label id="labelinput" for="fileInput">Choisir une image :</label><br><br> |
|
<input type="file" name="file" id="fileInput" accept="image/*" required><br><br> |
|
<center> <img id="preview" src="#" alt="Aperçu de l'image"><br></center> |
|
<center><button type="submit">Analyse</button></center> |
|
</form> |
|
</body> |
|
</html> |
|
""" |
|
return HTMLResponse(content=html_content) |
|
@app.post("/predict/") |
|
async def predict_race(file: UploadFile = File(...), lang: str = Form("fr")): |
|
image_data = await file.read() |
|
image = Image.open(BytesIO(image_data)).convert("RGB") |
|
|
|
|
|
buffered = BytesIO() |
|
image.save(buffered, format="JPEG") |
|
img_str = base64.b64encode(buffered.getvalue()).decode("utf-8") |
|
|
|
label, confidence = predict(image) |
|
advice = get_advice(label) |
|
|
|
temperament = taille = activite = esperance = "" |
|
|
|
if isinstance(advice, str): |
|
lines = advice.split("\n") |
|
for line in lines: |
|
if line.lower().startswith("tempérament") or line.lower().startswith("temperament"): |
|
temperament = line |
|
elif line.lower().startswith("taille"): |
|
taille = line |
|
elif line.lower().startswith("activité") or line.lower().startswith("activity"): |
|
activite = line |
|
elif line.lower().startswith("espérance") or line.lower().startswith("life"): |
|
esperance = line |
|
advice = "" |
|
else: |
|
advice = "Aucune information disponible." if lang == "fr" else "No information available." |
|
|
|
return HTMLResponse(content=f""" |
|
<html> |
|
<head> |
|
<title>Résultats</title> |
|
<style> |
|
body {{ |
|
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; |
|
background: linear-gradient(135deg, #e8eafa, #dfecf2); |
|
text-align: center; |
|
padding: 60px 20px; |
|
margin: 0; |
|
}} |
|
.result {{ |
|
background-color: #ffffff; |
|
padding: 40px; |
|
border-radius: 16px; |
|
box-shadow: 0 12px 24px rgba(0, 0, 0, 0.1); |
|
display: inline-block; |
|
max-width: 600px; |
|
width: 90%; |
|
}} |
|
h1, h2, h3 {{ |
|
color: #333; |
|
margin: 10px 0; |
|
}} |
|
p {{ |
|
font-size: 1rem; |
|
color: #555; |
|
margin: 15px 0; |
|
}} |
|
img {{ |
|
margin-top: 20px; |
|
max-width: 100%; |
|
border-radius: 12px; |
|
box-shadow: 0 8px 16px rgba(0, 0, 0, 0.1); |
|
}} |
|
a {{ |
|
display: inline-block; |
|
margin-top: 30px; |
|
background: linear-gradient(135deg, #66bb6a, #43a047); |
|
color: white; |
|
padding: 12px 24px; |
|
border-radius: 8px; |
|
text-decoration: none; |
|
font-size: 1rem; |
|
}} |
|
</style> |
|
</head> |
|
<body> |
|
<div class="result"> |
|
<h1>{'Résultat de la Détection' if lang == 'fr' else 'Detection Result'}</h1> |
|
<h2>{'Race détectée' if lang == 'fr' else 'Detected Breed'} : {label}</h2> |
|
<h3>{'Confiance' if lang == 'fr' else 'Confidence'} : {confidence * 100:.2f}%</h3> |
|
<h3>{'Fiche descriptive' if lang == 'fr' else 'Breed Info'} :</h3> |
|
<p>{advice}</p> |
|
<p>{temperament}</p> |
|
<p>{taille}</p> |
|
<p>{activite}</p> |
|
<p>{esperance}</p> |
|
<img src="data:image/jpeg;base64,{img_str}" alt="Image Uploadée"> |
|
<br> |
|
<a href="/">🔙 {'Retour' if lang == 'fr' else 'Back'}</a> |
|
</div> |
|
</body> |
|
</html> |
|
""") |
|
|
|
|