File size: 4,007 Bytes
1ce6f57
 
 
dd452ee
 
1ce6f57
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
dd452ee
 
 
 
1ce6f57
 
 
dd452ee
1ce6f57
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
from fastapi import FastAPI, File, UploadFile, Query, HTTPException
from fastapi.middleware.cors import CORSMiddleware
from plantdoc_predictor import Predictor
from PIL import Image
import io

app = FastAPI(title="PlantDoc API", version="1.0.0")

app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],
    allow_methods=["*"],
    allow_headers=["*"],
)

MODELS = [
    {"name": "mobilenetv2_v1",          "accuracy": 96.8,  "description": "Mobile-optimized, fastest inference"},
    {"name": "densenet169_v1",           "accuracy": 99.68, "description": "Best classical CNN"},
    {"name": "rec_add_attention_v1",     "accuracy": 99.70, "description": "Best overall — Recursive Additive Attention (research model)"},
    {"name": "convnext_small_v1",        "accuracy": 99.50, "description": "High accuracy + efficient"},
    {"name": "convnext_tiny_v1",         "accuracy": 99.23, "description": "Lightweight ConvNeXt"},
    {"name": "convnext_base_v1",         "accuracy": 99.10, "description": "ConvNeXt base"},
    {"name": "swin_tiny_patch4_window7", "accuracy": 99.10, "description": "Lightweight Swin Transformer"},
    {"name": "swin_base_patch4_window7", "accuracy": 99.10, "description": "Swin Transformer"},
    {"name": "vit_tiny_16_v1",           "accuracy": 99.10, "description": "Lightweight Vision Transformer"},
    {"name": "vit_small_16_v1",          "accuracy": 99.10, "description": "Small Vision Transformer"},
    {"name": "vit_base_16_v1",           "accuracy": 99.10, "description": "Vision Transformer base"},
    {"name": "vit_large_16_v1",          "accuracy": 99.10, "description": "Vision Transformer large"},
    {"name": "densenet121_v1",           "accuracy": 98.68, "description": "Efficient DenseNet"},
    {"name": "vgg19_v1",                 "accuracy": 98.98, "description": "Deep VGG"},
    {"name": "inceptionv3_v1",           "accuracy": 98.20, "description": "Multi-scale InceptionV3"},
    {"name": "resnet50_v1",              "accuracy": 97.80, "description": "Residual learning CNN"},
    {"name": "efficientnetb50_v1",       "accuracy": 97.80, "description": "EfficientNet balanced"},
    {"name": "densenet210_v1",           "accuracy": 97.00, "description": "Deep DenseNet"},
    {"name": "vgg16_v1",                 "accuracy": 96.80, "description": "Classic CNN"},
    {"name": "alexnet_v1",               "accuracy": 92.80, "description": "Baseline CNN"},
]

MODEL_NAMES = [m["name"] for m in MODELS]

_predictor_cache: dict[str, Predictor] = {}

def get_predictor(model_name: str) -> Predictor:
    if model_name not in _predictor_cache:
        _predictor_cache[model_name] = Predictor(model_name=model_name, verbose=False)
    return _predictor_cache[model_name]


@app.on_event("startup")
def warmup():
    get_predictor("mobilenetv2_v1")


@app.get("/health")
def health():
    return {"status": "ok", "loaded_models": list(_predictor_cache.keys())}


@app.get("/models")
def list_models():
    return {"models": MODELS}


@app.post("/predict")
async def predict(
    file: UploadFile = File(...),
    model: str = Query(default="mobilenetv2_v1"),
):
    if model not in MODEL_NAMES:
        raise HTTPException(status_code=400, detail=f"Unknown model '{model}'. Call /models for the list.")

    content = await file.read()
    if len(content) == 0:
        raise HTTPException(status_code=400, detail="Empty file uploaded.")

    try:
        image = Image.open(io.BytesIO(content)).convert("RGB")
    except Exception:
        raise HTTPException(status_code=400, detail="Invalid image file.")

    try:
        predictor = get_predictor(model)
        result = predictor.predict(image)
        plant, disease = (result["label"].split("___") + [""])[:2]
        result["plant"] = plant.replace("_", " ")
        result["disease"] = disease.replace("_", " ")
        result["is_healthy"] = "healthy" in result["label"].lower()
        return result
    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))