File size: 5,836 Bytes
06eb7dd
 
 
 
 
5eda14f
06eb7dd
84d05bd
06eb7dd
0609674
cf380e1
06eb7dd
5eda14f
06eb7dd
5eda14f
06eb7dd
 
 
 
 
 
 
 
 
 
 
 
 
0609674
73709e3
0609674
 
 
 
 
 
73709e3
0609674
 
73709e3
 
0609674
 
 
1baf335
73709e3
 
 
 
5eda14f
 
 
06eb7dd
5eda14f
06eb7dd
 
5eda14f
06eb7dd
 
 
 
 
 
 
 
 
 
 
 
5eda14f
73709e3
cf380e1
 
0609674
 
 
 
 
 
 
 
 
 
 
 
 
 
 
73709e3
1baf335
73709e3
0609674
 
73709e3
 
5eda14f
0717775
5eda14f
73709e3
5eda14f
 
1baf335
5eda14f
 
 
 
 
 
73709e3
 
 
 
 
 
5eda14f
 
 
 
 
 
 
 
0717775
 
 
 
 
 
 
73709e3
cf380e1
5eda14f
0717775
73709e3
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
06eb7dd
73709e3
1baf335
73709e3
06eb7dd
5eda14f
73709e3
5eda14f
0609674
06eb7dd
 
0717775
06eb7dd
 
 
 
 
 
 
 
 
cf380e1
06eb7dd
1baf335
cf380e1
06eb7dd
 
 
cf380e1
06eb7dd
 
cf380e1
73709e3
06eb7dd
 
 
5eda14f
0609674
5eda14f
 
cf380e1
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
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
import os
import io
import logging
import numpy as np
import onnxruntime as ort
import requests
from PIL import Image
from fastapi import FastAPI, UploadFile, File, Form
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import HTMLResponse
import uvicorn

# -------------------------------------------------------
# App Setup
# -------------------------------------------------------
app = FastAPI(title="AgriCare Disease Detection API")

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

logging.basicConfig(level=logging.INFO)
logger = logging.getLogger("agricare_api")

# -------------------------------------------------------
# Root → Docs
# -------------------------------------------------------
@app.get("/", response_class=HTMLResponse)
async def root():
    return """
    <html>
      <head>
        <meta http-equiv="refresh" content="0; url=/docs">
        <title>AgriCare API</title>
      </head>
      <body>
        <p>Redirecting to <a href="/docs">/docs</a>...</p>
      </body>
    </html>
    """

@app.get("/health")
def health():
    return {"status": "ok"}

# -------------------------------------------------------
# Model Setup
# -------------------------------------------------------
MODEL_PATH = "cassava_efficientnetb3_fp16.onnx"
sess = ort.InferenceSession(MODEL_PATH, providers=["CPUExecutionProvider"])

INPUT_NAME = sess.get_inputs()[0].name
MODEL_DTYPE = np.float16 if "float16" in sess.get_inputs()[0].type else np.float32

CLASS_NAMES = [
    "Cassava Bacterial Blight",
    "Cassava Brown Streak Disease",
    "Cassava Green Mottle",
    "Cassava Mosaic Disease",
    "Healthy Leaf"
]

IMG_SIZE = 300
LOW_CONF_THRESHOLD = 0.60

# -------------------------------------------------------
# English Source of Truth
# -------------------------------------------------------
DISEASE_RECOMMENDATIONS = {
    "Cassava Bacterial Blight":
        "Cassava Bacterial Blight was detected. Remove and destroy infected plants. "
        "Use clean disease-free planting materials. Apply copper-based bactericides "
        "such as Copper Oxychloride. Avoid overhead irrigation.",

    "Cassava Brown Streak Disease":
        "Cassava Brown Streak Disease was detected. There is no chemical cure. "
        "Control whiteflies using Imidacloprid or Thiamethoxam. "
        "Plant resistant varieties and remove infected plants early.",

    "Cassava Green Mottle":
        "Cassava Green Mottle was detected. Control aphids and whiteflies using "
        "Lambda-cyhalothrin or Cypermethrin. Maintain field hygiene.",

    "Cassava Mosaic Disease":
        "Cassava Mosaic Disease was detected. There is no direct chemical cure. "
        "Control whiteflies using Imidacloprid or Acetamiprid. "
        "Uproot and destroy infected plants immediately.",

    "Healthy Leaf":
        "The cassava leaf is healthy. No treatment is required. "
        "Continue regular monitoring and good farm hygiene."
}

# -------------------------------------------------------
# Hugging Face – N-ATLaS
# -------------------------------------------------------
HF_TOKEN = os.getenv("HF_TOKEN")
NATLAS_URL = "https://router.huggingface.co/hf-inference/models/NCAIR1/N-ATLaS"

HEADERS = {
    "Authorization": f"Bearer {HF_TOKEN}",
    "Content-Type": "application/json"
}

LANGUAGE_MAP = {
    "yoruba": "Yoruba language",
    "hausa": "Hausa language",
    "igbo": "Igbo language"
}

# -------------------------------------------------------
# Utilities
# -------------------------------------------------------
def softmax(x):
    e = np.exp(x - np.max(x))
    return e / e.sum()

def preprocess(image_bytes):
    img = Image.open(io.BytesIO(image_bytes)).convert("RGB")
    img = img.resize((IMG_SIZE, IMG_SIZE))
    arr = np.array(img).astype("float32") / 255.0
    arr = (arr - [0.485, 0.456, 0.406]) / [0.229, 0.224, 0.225]
    arr = np.transpose(arr, (2, 0, 1))
    return arr[np.newaxis, :].astype(MODEL_DTYPE)

def translate_text(text: str, language: str) -> str:
    if language.lower() == "english":
        return text

    target_lang = LANGUAGE_MAP.get(language.lower(), language)

    prompt = (
        f"Translate the following agricultural advice into {target_lang}. "
        f"Do NOT answer in English.\n\n{text}"
    )

    r = requests.post(
        NATLAS_URL,
        headers=HEADERS,
        json={"inputs": prompt},
        timeout=30
    )

    r.raise_for_status()
    data = r.json()

    translated = data[0].get("generated_text", text)

    return translated.strip()

# -------------------------------------------------------
# Prediction Endpoint
# -------------------------------------------------------
@app.post("/predict")
async def predict(
    file: UploadFile = File(...),
    language: str = Form("english")
):
    image_bytes = await file.read()
    arr = preprocess(image_bytes)

    logits = np.squeeze(sess.run(None, {INPUT_NAME: arr})[0])
    probs = softmax(logits)

    idx = int(np.argmax(probs))
    confidence = float(probs[idx])
    predicted = CLASS_NAMES[idx]

    base_text = DISEASE_RECOMMENDATIONS[predicted]
    final_text = translate_text(base_text, language)

    return {
        "status": "low_confidence" if confidence < LOW_CONF_THRESHOLD else "ok",
        "predicted_class": predicted,
        "confidence": round(confidence, 4),
        "language": language,
        "recommendation_text": final_text,
        "route_to_expert": confidence < LOW_CONF_THRESHOLD,
        "probabilities": probs.tolist()
    }

# -------------------------------------------------------
# Run (HF Compatible)
# -------------------------------------------------------
if __name__ == "__main__":
    uvicorn.run("app_fastapi:app", host="0.0.0.0", port=7860)