NeuroHealth / app.py
Mikecode123's picture
Update app.py
95fecec verified
Raw
History Blame
5.08 kB
import torch
import torch.nn as nn
import torchvision.models as models
from fastapi import FastAPI, UploadFile, File
from PIL import Image
import io
import torchvision.transforms as transforms
# =========================
# APP
# =========================
app = FastAPI(title="Alzheimer DATSCAN Ensemble API")
# =========================
# DEVICE
# =========================
DEVICE = torch.device("cuda" if torch.cuda.is_available() else "cpu")
print("Using device:", DEVICE)
# =========================
# CLASSES (VERY IMPORTANT - YOUR ORIGINAL SETUP)
# =========================
CLASSES = [
"Mild Demented",
"Moderate Demented",
"Non Demented",
"Very Mild Demented"
]
# =========================
# IMAGE TRANSFORM
# =========================
transform = transforms.Compose([
transforms.Resize((224, 224)),
transforms.ToTensor(),
transforms.Normalize([0.5, 0.5, 0.5], [0.5, 0.5, 0.5])
])
# =========================
# MODEL BUILDER (MATCH TRAINING)
# =========================
def build_model(version="121"):
if version == "121":
model = models.densenet121(weights=None)
in_features = 1024
elif version == "169":
model = models.densenet169(weights=None)
in_features = 1664
else:
model = models.densenet201(weights=None)
in_features = 1920
model.classifier = nn.Sequential(
nn.Dropout(0.4),
nn.Linear(in_features, 4)
)
return model
# =========================
# SAFE MODEL LOADER (FIX ALL YOUR ERRORS)
# =========================
def load_model(path, version):
model = build_model(version)
try:
state = torch.load(path, map_location=DEVICE)
# handle different checkpoint formats
if isinstance(state, dict):
if "state_dict" in state:
state = state["state_dict"]
elif "model_state_dict" in state:
state = state["model_state_dict"]
model.load_state_dict(state, strict=False)
print(f"Loaded {path}")
except Exception as e:
print(f"Failed loading {path}: {e}")
model.to(DEVICE)
model.eval()
return model
# =========================
# LOAD MODELS
# =========================
model_121 = load_model("alzheimers_densenet121.pth", "121")
model_169 = load_model("alzheimers_densenet169.pth", "169")
model_201 = load_model("alzheimers_densenet201.pth", "201")
# =========================
# IMAGE PROCESSING
# =========================
def process_image(image_bytes):
img = Image.open(io.BytesIO(image_bytes)).convert("RGB")
img = transform(img).unsqueeze(0).to(DEVICE)
return img
# =========================
# PREDICT FUNCTION (FIXED)
# =========================
def predict(model, x):
with torch.no_grad():
out = model(x)
probs = torch.softmax(out, dim=1)[0]
conf, cls = torch.max(probs, 0)
return {
"class_id": int(cls.item()),
"class_name": CLASSES[int(cls.item())],
"confidence": float(conf.item()),
"probabilities": {
CLASSES[i]: float(probs[i].item())
for i in range(len(CLASSES))
}
}
# =========================
# ROOT
# =========================
@app.get("/")
def home():
return {
"status": "running",
"models": ["densenet121", "densenet169", "densenet201"],
"classes": CLASSES,
"endpoints": ["/predict/121", "/predict/169", "/predict/201", "/predict/ensemble"]
}
# =========================
# SINGLE MODEL ROUTES
# =========================
@app.post("/predict/121")
async def predict_121(file: UploadFile = File(...)):
img = process_image(await file.read())
return {"model": "121", **predict(model_121, img)}
@app.post("/predict/169")
async def predict_169(file: UploadFile = File(...)):
img = process_image(await file.read())
return {"model": "169", **predict(model_169, img)}
@app.post("/predict/201")
async def predict_201(file: UploadFile = File(...)):
img = process_image(await file.read())
return {"model": "201", **predict(model_201, img)}
# =========================
# ENSEMBLE (FINAL FIXED VERSION)
# =========================
@app.post("/predict/ensemble")
async def ensemble(file: UploadFile = File(...)):
img = process_image(await file.read())
r1 = predict(model_121, img)
r2 = predict(model_169, img)
r3 = predict(model_201, img)
avg = {}
for c in CLASSES:
avg[c] = (r1["probabilities"][c] +
r2["probabilities"][c] +
r3["probabilities"][c]) / 3
final_class = max(avg, key=avg.get)
return {
"final_prediction": final_class,
"final_confidence": avg[final_class],
"individual_models": {
"121": r1,
"169": r2,
"201": r3
},
"ensemble_probabilities": avg
}