File size: 7,996 Bytes
b875fc4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
c9eaea0
 
 
 
 
 
 
 
b875fc4
 
80a40ad
c9eaea0
b875fc4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
from fastapi import FastAPI, File, UploadFile, HTTPException
from fastapi.middleware.cors import CORSMiddleware
import uvicorn
import torch
from torchvision import transforms
from torchvision.models import mobilenet_v3_large, MobileNet_V3_Large_Weights
from PIL import Image
import io
import os

app = FastAPI(title="AgroVision API")
app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],
    allow_methods=["*"],
    allow_headers=["*"],
)

device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

# Load models safely
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
VERIFY_MODEL_PATH = os.path.join(SCRIPT_DIR, "Jverify_model.pth")
DISEASE_MODEL_PATH = os.path.join(SCRIPT_DIR, "Jmodel.pth")

# Global dict for caching
MODELS = {
    "verify": None,
    "verify_classes": [],
    "disease": None,
    "disease_classes": []
}

def load_model(path):
    if not os.path.exists(path):
        return None, []
    checkpoint = torch.load(path, map_location=device)
    classes = checkpoint['classes']
    model = mobilenet_v3_large(weights=MobileNet_V3_Large_Weights.IMAGENET1K_V1)
    model.classifier[3] = torch.nn.Linear(model.classifier[3].in_features, len(classes))
    model.load_state_dict(checkpoint['mobilenet'])
    model = model.to(device)
    model.eval()
    return model, classes

@app.on_event("startup")
def startup_event():
    print("Loading models...")
    MODELS["verify"], MODELS["verify_classes"] = load_model(VERIFY_MODEL_PATH)
    MODELS["disease"], MODELS["disease_classes"] = load_model(DISEASE_MODEL_PATH)
    print("Models loaded successfully!")

test_transform = transforms.Compose([
    transforms.Resize((224, 224)),
    transforms.ToTensor(),
    transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
])

@app.post("/analyze")
async def analyze_image(file: UploadFile = File(...)):
    if MODELS["verify"] is None or MODELS["disease"] is None:
        raise HTTPException(status_code=500, detail="Models not loaded")

    try:
        contents = await file.read()
        if not contents:
            raise ValueError("The server received an empty file (0 bytes). This is usually caused by a proxy stripping the file.")
            
        try:
            image = Image.open(io.BytesIO(contents)).convert('RGB')
        except Exception as pil_err:
            raise ValueError(f"Server received {len(contents)} bytes, but PIL failed to read it as an image. Details: {str(pil_err)}")
            
        image_tensor = test_transform(image).unsqueeze(0).to(device)
    except Exception as e:
        print(f"FAILED TO LOAD IMAGE: {e}")
        raise HTTPException(status_code=400, detail=str(e))

    with torch.no_grad():
        # First verify if it's a tomato leaf
        v_out = MODELS["verify"](image_tensor)
        v_probs = torch.softmax(v_out, dim=1).cpu().numpy()[0]
        v_idx = v_probs.argmax()
        is_tomato = MODELS["verify_classes"][v_idx] == "Tomatoes"
        
        if not is_tomato:
            return {
                "success": True,
                "is_tomato": False,
                "confidence": float(v_probs[v_idx]),
                "message": "This doesn't seem to be a tomato leaf. Please upload a clear image of a tomato leaf for best results.",
                "suggestion": "Make sure the leaf fills most of the frame, is well-lit, and in focus."
            }

        # It's a tomato, check for diseases
        d_out = MODELS["disease"](image_tensor)
        d_probs = torch.softmax(d_out, dim=1).cpu().numpy()[0]
        d_idx = d_probs.argmax()
        disease_name = MODELS["disease_classes"][d_idx]
        d_confidence = float(d_probs[d_idx])

        # Prepare highly professional insights based on common disease labels
        insights = {
            "Late blight": (
                "Overview: A highly destructive fungal disease (Phytophthora infestans) that thrives in cool, wet weather. "
                "Symptoms: Dark, water-soaked spots on leaves that rapidly enlarge, white fungal growth on undersides.\n\n"
                "🛡️ Treatment & Solutions:\n"
                "• Immediate Action: Entirely remove and destroy infected plant parts immediately. Do not compost.\n"
                "• Chemical Control: Apply fungicides containing chlorothalonil, copper fungicide, or mancozeb as scheduled sprays.\n"
                "• Preventative Care: Maintain excellent air circulation; water at the base of the plant avoiding foliage; space plants properly."
            ),
            "Early blight": (
                "Overview: A common fungal disease (Alternaria solani) appearing on older foliage first.\n"
                "Symptoms: Brown or black spots with characteristic concentric rings (target-board appearance) surrounded by yellow halos.\n\n"
                "🛡️ Treatment & Solutions:\n"
                "• Organic Control: Apply copper-based or sulfur-based fungicides organically approved. Neem oil can be used as a preventative measure.\n"
                "• Chemical Control: Use chlorothalonil or azoxystrobin early in the infection cycle.\n"
                "• Preventative Care: Crop rotation (3-4 years without nightshades), mulch to prevent soil splashing, and remove bottom leaves touching the soil."
            ),
            "Bacterial spot": (
                "Overview: Caused by Xanthomonas bacteria, severely impacting yield in warm, humid conditions.\n"
                "Symptoms: Small, dark, water-soaked, scabby spots on fruit and leaves.\n\n"
                "🛡️ Treatment & Solutions:\n"
                "• Immediate Action: Bacteria are notoriously difficult to control once established. Remove infected crop debris post-harvest.\n"
                "• Chemical Control: Preventative copper fungicide sprays combined with mancozeb can suppress the spread.\n"
                "• Preventative Care: Use certified disease-free seeds/transplants; avoid working among wet plants to prevent spreading bacteria on hands and tools."
            ),
            "Spider Mites": (
                "Overview: Tiny arachnids that congregate on the undersides of leaves, thriving in hot, dry conditions.\n"
                "Symptoms: Stippling (tiny yellow or white dots on leaves), fine webbing, and eventual leaf yellowing/dropping.\n\n"
                "🛡️ Treatment & Solutions:\n"
                "• Organic Control: Knock them off with a strong blast of water. Apply insecticidal soap, Neem oil, or introduce predatory mites (Phytoseiulus persimilis).\n"
                "• Chemical Control: Use targeted miticides (abamectin or spiromesifen) if the infestation is severe.\n"
                "• Preventative Care: Keep plants well-watered (stress increases susceptibility) and maintain higher localized humidity."
            ),
            "Healthy": (
                "Overview: Your tomato plant exhibits strong, vigorous growth with no current signs of widespread pathogens or pests.\n\n"
                "🌱 Ongoing Best Practices:\n"
                "• Nutrition: Ensure a balanced supply of Phosphorus and Potassium during fruiting.\n"
                "• Watering: Maintain consistent soil moisture (avoiding wet-dry extremes to prevent blossom end rot).\n"
                "• Maintenance: Continue pruning suckers for indeterminate varieties to maximize airflow and sunlight penetration."
            )
        }
        
        suggestion = insights.get(disease_name, "Consult a local agricultural expert for treatment options tailored to your region.")

        return {
            "success": True,
            "is_tomato": True,
            "prediction": disease_name,
            "confidence": d_confidence,
            "suggestion": suggestion,
            "all_probabilities": {str(cls): float(prob) for cls, prob in zip(MODELS["disease_classes"], d_probs)}
        }

if __name__ == "__main__":
    uvicorn.run("api:app", host="0.0.0.0", port=8000, reload=True)