skin-lesion-api / app.py
Ranjith445's picture
Use demo.app for FastAPI routes, fix HF Space launch
28a2b47
Raw
History Blame Contribute Delete
3.54 kB
import os, sys
from dotenv import load_dotenv
load_dotenv()
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
from download_assets import download_assets
download_assets()
import gradio as gr
from fastapi.middleware.cors import CORSMiddleware
from fastapi import File, UploadFile, HTTPException
from fastapi.responses import JSONResponse
from pydantic import BaseModel
from typing import Optional
from PIL import Image
import io, base64, json
from src.predict import predict_from_bytes
from src.explain import explain_from_bytes
from src.llm_explain import explain_prediction, answer_question
from config import CLASS_LABELS
def run_predict(image_bytes, do_explain, do_llm):
result = predict_from_bytes(image_bytes)
heatmap_b64 = None
if do_explain:
try:
pred_idx = CLASS_LABELS[result["predicted_class"]]
heatmap_b64 = explain_from_bytes(image_bytes, pred_idx)
except: pass
explanation = None
if do_llm:
try:
explanation = explain_prediction(
predicted_class=result["predicted_class"],
confidence=result["confidence"],
probabilities=result["probabilities"],
)
except Exception as e:
explanation = str(e)
return result, heatmap_b64, explanation
def gradio_predict(image: Image.Image):
buf = io.BytesIO()
image.save(buf, format="JPEG")
result, heatmap_b64, explanation = run_predict(buf.getvalue(), True, True)
heatmap_img = Image.open(io.BytesIO(base64.b64decode(heatmap_b64))) if heatmap_b64 else None
return heatmap_img, {result["predicted_class"]: result["confidence"]}, explanation or ""
with gr.Blocks(title="DermAI") as demo:
gr.Markdown("# DermAI — Skin Lesion Classifier\nFrontend: [GitHub Pages](https://ranjithtkm445-blip.github.io/skin-lesion-ai)")
with gr.Row():
inp = gr.Image(type="pil", label="Upload Image")
with gr.Column():
heatmap = gr.Image(label="Grad-CAM")
label_out = gr.Label(label="Prediction")
explanation_out = gr.Textbox(label="Clinical Explanation", lines=6)
btn = gr.Button("Analyze", variant="primary")
btn.click(gradio_predict, inputs=inp, outputs=[heatmap, label_out, explanation_out])
# Mount custom API routes on Gradio internal FastAPI app
app = demo.app
app.add_middleware(CORSMiddleware, allow_origins=["*"], allow_methods=["*"], allow_headers=["*"])
@app.get("/health")
def health():
return {"status": "ok"}
@app.post("/predict")
async def predict(file: UploadFile = File(...), explain: bool = True, llm_explain: bool = True):
if not file.content_type.startswith("image/"):
raise HTTPException(status_code=400, detail="File must be an image.")
image_bytes = await file.read()
result, heatmap_b64, explanation = run_predict(image_bytes, explain, llm_explain)
return {
"predicted_class": result["predicted_class"],
"class_name": result["class_name"],
"confidence": result["confidence"],
"probabilities": result["probabilities"],
"heatmap_base64": heatmap_b64,
"explanation": explanation,
}
@app.post("/explain")
async def ask(question: str, predicted_class: Optional[str] = None):
try:
answer = answer_question(question, predicted_class)
return {"answer": answer}
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
if __name__ == "__main__":
demo.launch(server_name="0.0.0.0", server_port=7860)