API / api_v1.py
Stroke-ia's picture
Rename api.py to api_v1.py
3c9ae2f verified
from fastapi import FastAPI, UploadFile, File, HTTPException, Security, Depends
from fastapi.security.api_key import APIKeyHeader
from fastapi.responses import JSONResponse, StreamingResponse
import uvicorn
import io
import numpy as np
from PIL import Image
import cv2
from ultralytics import YOLO
import requests
import os
# ==========================
# 🔑 Sécurité : API Key
# ==========================
API_KEY = "1234" # <-- Change ici avant de partager
api_key_header = APIKeyHeader(name="X-API-Key")
def verify_api_key(api_key: str = Security(api_key_header)):
if api_key != API_KEY:
raise HTTPException(status_code=403, detail="Forbidden")
return api_key
# ==========================
# 🚀 Application
# ==========================
app = FastAPI(
title="Stroke Detection API",
version="1.0.0",
description="""
🚑 Stroke Detection API using YOLOv8
⚠️ **Disclaimer**: This API is for **research/demo purposes only**.
It is **not a certified medical tool**. Do not use for medical decisions.
"""
)
# Charger ton modèle YOLOv8
model = YOLO("best.pt")
# ==========================
# 📦 Endpoint JSON
# ==========================
@app.post("/v1/predict/")
async def predict(
file: UploadFile = File(...),
api_key: str = Depends(verify_api_key)
):
try:
# Lire directement en mémoire
contents = await file.read()
image = Image.open(io.BytesIO(contents)).convert("RGB")
np_image = np.array(image)
# Prédiction YOLO
results = model.predict(np_image, conf=0.5, verbose=False)
output = []
for r in results:
for box in r.boxes:
output.append({
"class": r.names[int(box.cls[0].item())],
"confidence": float(box.conf[0].item()),
"bbox": box.xyxy[0].tolist()
})
return JSONResponse(content={"predictions": output})
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
# ==========================
# 📦 Endpoint Image
# ==========================
@app.post("/v1/predict_image/")
async def predict_image(
file: UploadFile = File(...),
api_key: str = Depends(verify_api_key)
):
try:
# Lire directement en mémoire
contents = await file.read()
image = Image.open(io.BytesIO(contents)).convert("RGB")
np_image = np.array(image)
# Prédiction YOLO + image annotée
results = model.predict(np_image, conf=0.5, verbose=False)
annotated = results[0].plot()
# Convertir en bytes pour StreamingResponse
annotated_pil = Image.fromarray(cv2.cvtColor(annotated, cv2.COLOR_BGR2RGB))
img_byte_arr = io.BytesIO()
annotated_pil.save(img_byte_arr, format="PNG")
img_byte_arr.seek(0)
return StreamingResponse(img_byte_arr, media_type="image/png")
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
# ==========================
# 🧪 Endpoint Test interne
# ==========================
@app.get("/test_request/")
async def test_request():
"""
Test interne de l'API déployée sur Hugging Face.
Utilise une image locale 'test.jpg' (⚠️ à placer dans ton repo Space).
"""
try:
file_path = "test.jpg" # ⚠️ Mets une image dans ton Space
base_url = "https://stroke-ia-api.hf.space" # ⚠️ adapte au nom exact de ton Space
if not os.path.exists(file_path):
return {"error": f"{file_path} introuvable dans le Space."}
# Test JSON
url_predict = f"{base_url}/v1/predict/"
files = {"file": open(file_path, "rb")}
headers = {"X-API-Key": API_KEY}
response = requests.post(url_predict, files=files, headers=headers)
json_result = response.json()
# Test image annotée
url_img = f"{base_url}/v1/predict_image/"
files = {"file": open(file_path, "rb")}
response_img = requests.post(url_img, files=files, headers=headers)
with open("result.png", "wb") as f:
f.write(response_img.content)
return {
"message": "✅ Test request exécuté sur Hugging Face API. Résultats sauvegardés.",
"json_result": json_result,
"saved_image": "result.png"
}
except Exception as e:
return {"error": str(e)}
# ==========================
# 🚀 Lancement local
# ==========================
if __name__ == "__main__":
uvicorn.run(app, host="0.0.0.0", port=7860)