DermaScan-AI / api /app.py
Meet Radadiya
DermaScan
782e635
"""
=================================================================
DERMASCAN-AI β€” FastAPI Application
=================================================================
"""
import io
import time
import numpy as np
from PIL import Image
from pathlib import Path
from fastapi import FastAPI, File, UploadFile, HTTPException, Query
from fastapi.middleware.cors import CORSMiddleware
from contextlib import asynccontextmanager
from src.inference.predictor import SkinPredictor
from src.response.response_engine import ResponseEngine
from src.response.hospital_finder import HospitalFinder
from api.schemas import HealthResponse
# ── Global objects ──
predictor = None
response_engine = None
hospital_finder = None
@asynccontextmanager
async def lifespan(app: FastAPI):
global predictor, response_engine, hospital_finder
print("πŸš€ Starting DermaScan-AI...")
predictor = SkinPredictor(
model_path="checkpoints/best_model.pth",
class_config_path="configs/class_config.json",
device="cpu",
)
response_engine = ResponseEngine(
class_config_path="configs/class_config.json",
response_templates_path="configs/response_templates.json",
)
hospital_finder = HospitalFinder()
print("βœ… DermaScan-AI ready!")
yield
print("πŸ›‘ Shutting down...")
app = FastAPI(
title="πŸ”¬ DermaScan-AI",
description="AI-powered skin disease detection with clinical guidance",
version="1.0.0",
lifespan=lifespan,
)
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_methods=["*"],
allow_headers=["*"],
)
def convert_numpy(obj):
"""Convert numpy types to Python native for JSON serialization."""
if isinstance(obj, dict):
return {k: convert_numpy(v) for k, v in obj.items()}
elif isinstance(obj, list):
return [convert_numpy(v) for v in obj]
elif isinstance(obj, (np.bool_,)):
return bool(obj)
elif isinstance(obj, (np.integer,)):
return int(obj)
elif isinstance(obj, (np.floating,)):
return float(obj)
elif isinstance(obj, np.ndarray):
return obj.tolist()
return obj
@app.get("/health", response_model=HealthResponse)
async def health():
return HealthResponse(
status="healthy",
model_loaded=predictor is not None,
model_name="EfficientNet-B3",
version="1.0.0",
)
@app.post("/predict")
async def predict(
file: UploadFile = File(...),
city: str = Query("Delhi", description="City in India"),
state: str = Query("Delhi", description="State in India"),
):
if file.content_type not in ["image/jpeg", "image/png", "image/jpg"]:
raise HTTPException(400, "Only JPG/PNG images supported")
contents = await file.read()
if len(contents) > 10 * 1024 * 1024:
raise HTTPException(400, "File too large (max 10MB)")
try:
image = Image.open(io.BytesIO(contents)).convert('RGB')
except Exception:
raise HTTPException(400, "Invalid image file")
start = time.time()
prediction = predictor.predict(image)
inference_time = time.time() - start
response = response_engine.generate_response(
predicted_class=prediction['predicted_class'],
confidence=prediction['confidence'],
all_probabilities=prediction['all_probabilities'],
)
hospital_result = hospital_finder.search(
query=response['hospital_search_query'],
city=city,
state=state,
)
response['maps_url'] = hospital_result['maps_url']
response['maps_embed_url'] = hospital_result['embed_url']
response['hospital_location'] = hospital_result['location']
response['inference_time'] = round(inference_time, 3)
response['emergency_numbers'] = hospital_finder.get_emergency_numbers()
return convert_numpy(response)
if __name__ == "__main__":
import uvicorn
uvicorn.run("api.app:app", host="0.0.0.0", port=8000, reload=True)