Spaces:
Sleeping
Sleeping
Upload 2 files
Browse files
best.pt
CHANGED
|
@@ -1,3 +1,3 @@
|
|
| 1 |
version https://git-lfs.github.com/spec/v1
|
| 2 |
-
oid sha256:
|
| 3 |
-
size
|
|
|
|
| 1 |
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:7f2e775168f7ce664b7abe08dbd5a7c903f5f3487c0711fe180fa8836015c312
|
| 3 |
+
size 77368442
|
main.py
CHANGED
|
@@ -1,9 +1,7 @@
|
|
| 1 |
from fastapi import FastAPI, File, UploadFile, Form
|
| 2 |
from fastapi.middleware.cors import CORSMiddleware
|
| 3 |
-
from typing import
|
| 4 |
-
from
|
| 5 |
-
from sahi.predict import get_sliced_prediction
|
| 6 |
-
from ultralytics import YOLO # π Import YOLO for classification
|
| 7 |
import uvicorn
|
| 8 |
import shutil
|
| 9 |
import os
|
|
@@ -19,80 +17,79 @@ app.add_middleware(
|
|
| 19 |
)
|
| 20 |
|
| 21 |
# ==========================================
|
| 22 |
-
# π§ LOAD MODELS
|
| 23 |
# ==========================================
|
| 24 |
print("β³ Loading Models...")
|
| 25 |
|
| 26 |
-
# 1. Disease Detection Model (Your Custom Model)
|
| 27 |
try:
|
| 28 |
-
|
| 29 |
-
|
| 30 |
-
|
| 31 |
-
confidence_threshold=0.25,
|
| 32 |
-
device='cpu'
|
| 33 |
-
)
|
| 34 |
-
print("β
Disease Model loaded!")
|
| 35 |
except Exception as e:
|
| 36 |
print(f"β Error loading Disease Model: {e}")
|
| 37 |
|
| 38 |
-
# 2. Plant Gatekeeper Model (Generic Classifier)
|
| 39 |
-
# This downloads automatically the first time (only ~6MB)
|
| 40 |
try:
|
| 41 |
-
|
|
|
|
| 42 |
print("β
Plant Gatekeeper loaded!")
|
| 43 |
except Exception as e:
|
| 44 |
print(f"β Error loading Gatekeeper: {e}")
|
| 45 |
|
| 46 |
|
| 47 |
# ==========================================
|
| 48 |
-
# π‘οΈ THE
|
| 49 |
# ==========================================
|
| 50 |
def is_likely_plant(image_path):
|
| 51 |
"""
|
| 52 |
-
|
| 53 |
-
|
|
|
|
| 54 |
"""
|
| 55 |
try:
|
| 56 |
-
|
| 57 |
-
results = classifier(image_path, verbose=False)
|
| 58 |
-
top_probs = results[0].probs.top1
|
| 59 |
-
top_name = results[0].names[top_probs].lower()
|
| 60 |
|
| 61 |
-
|
| 62 |
-
|
| 63 |
-
|
| 64 |
-
|
| 65 |
-
|
| 66 |
-
|
| 67 |
-
|
| 68 |
-
|
| 69 |
-
'
|
|
|
|
|
|
|
| 70 |
]
|
| 71 |
|
| 72 |
-
# Check if
|
| 73 |
-
for
|
| 74 |
-
|
| 75 |
-
|
| 76 |
-
|
|
|
|
| 77 |
|
| 78 |
-
#
|
| 79 |
-
|
| 80 |
-
return
|
| 81 |
|
| 82 |
except Exception as e:
|
| 83 |
print(f"β οΈ Gatekeeper Error: {e}")
|
| 84 |
-
return True #
|
| 85 |
|
| 86 |
|
| 87 |
def get_recommendation(disease_name):
|
| 88 |
recommendations = {
|
| 89 |
-
"Blast": "Use Tricyclazole 75 WP. Avoid excess nitrogen.",
|
| 90 |
-
"Blight": "Drain field. Apply
|
| 91 |
-
"Brown Spot": "Improve soil fertility.
|
| 92 |
-
"
|
| 93 |
-
"Healthy": "Keep maintaining good water levels!"
|
| 94 |
}
|
| 95 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 96 |
|
| 97 |
@app.get("/")
|
| 98 |
def home():
|
|
@@ -111,57 +108,38 @@ async def predict(
|
|
| 111 |
with open(temp_filename, "wb") as buffer:
|
| 112 |
shutil.copyfileobj(file.file, buffer)
|
| 113 |
|
| 114 |
-
# π STEP 1: RUN THE GATEKEEPER
|
| 115 |
if not is_likely_plant(temp_filename):
|
| 116 |
-
print("π« Rejected: Image does not look like a plant.")
|
| 117 |
return {
|
| 118 |
"filename": file.filename,
|
| 119 |
"disease": "Invalid Image",
|
| 120 |
"confidence": "0%",
|
| 121 |
-
"recommendation": "This image does not appear to be a plant. Please upload a clear photo of
|
| 122 |
"latitude": float(latitude) if latitude else None,
|
| 123 |
"longitude": float(longitude) if longitude else None
|
| 124 |
}
|
| 125 |
|
| 126 |
-
# β
STEP 2: RUN DISEASE
|
| 127 |
-
|
| 128 |
-
temp_filename,
|
| 129 |
-
detection_model,
|
| 130 |
-
slice_height=640,
|
| 131 |
-
slice_width=640,
|
| 132 |
-
overlap_height_ratio=0.2,
|
| 133 |
-
overlap_width_ratio=0.2
|
| 134 |
-
)
|
| 135 |
|
| 136 |
-
|
|
|
|
|
|
|
|
|
|
| 137 |
|
| 138 |
response_data = {
|
| 139 |
"filename": file.filename,
|
|
|
|
|
|
|
|
|
|
| 140 |
"latitude": float(latitude) if latitude else None,
|
| 141 |
"longitude": float(longitude) if longitude else None
|
| 142 |
}
|
| 143 |
|
| 144 |
-
if len(predictions) > 0:
|
| 145 |
-
best_pred = max(predictions, key=lambda x: x.score.value)
|
| 146 |
-
detected_name = best_pred.category.name
|
| 147 |
-
confidence_score = best_pred.score.value
|
| 148 |
-
|
| 149 |
-
response_data.update({
|
| 150 |
-
"disease": detected_name,
|
| 151 |
-
"confidence": f"{int(confidence_score * 100)}%",
|
| 152 |
-
"recommendation": get_recommendation(detected_name)
|
| 153 |
-
})
|
| 154 |
-
else:
|
| 155 |
-
response_data.update({
|
| 156 |
-
"disease": "Healthy / No Detection",
|
| 157 |
-
"confidence": "0%",
|
| 158 |
-
"recommendation": get_recommendation("Healthy")
|
| 159 |
-
})
|
| 160 |
-
|
| 161 |
return response_data
|
| 162 |
|
| 163 |
except Exception as e:
|
| 164 |
-
print(f"Error: {e}")
|
| 165 |
return {"error": str(e)}
|
| 166 |
|
| 167 |
finally:
|
|
|
|
| 1 |
from fastapi import FastAPI, File, UploadFile, Form
|
| 2 |
from fastapi.middleware.cors import CORSMiddleware
|
| 3 |
+
from typing import Optional
|
| 4 |
+
from ultralytics import YOLO
|
|
|
|
|
|
|
| 5 |
import uvicorn
|
| 6 |
import shutil
|
| 7 |
import os
|
|
|
|
| 17 |
)
|
| 18 |
|
| 19 |
# ==========================================
|
| 20 |
+
# π§ LOAD MODELS (Classification Only!)
|
| 21 |
# ==========================================
|
| 22 |
print("β³ Loading Models...")
|
| 23 |
|
|
|
|
| 24 |
try:
|
| 25 |
+
# 1. Disease Classification Model (Your ultra-smart 96.8% brain)
|
| 26 |
+
disease_model = YOLO('best.pt')
|
| 27 |
+
print("β
Goyam Disease Classifier loaded!")
|
|
|
|
|
|
|
|
|
|
|
|
|
| 28 |
except Exception as e:
|
| 29 |
print(f"β Error loading Disease Model: {e}")
|
| 30 |
|
|
|
|
|
|
|
| 31 |
try:
|
| 32 |
+
# 2. Plant Gatekeeper (ImageNet 1000-class classifier)
|
| 33 |
+
gatekeeper_model = YOLO('yolov8n-cls.pt')
|
| 34 |
print("β
Plant Gatekeeper loaded!")
|
| 35 |
except Exception as e:
|
| 36 |
print(f"β Error loading Gatekeeper: {e}")
|
| 37 |
|
| 38 |
|
| 39 |
# ==========================================
|
| 40 |
+
# π‘οΈ THE BULLETPROOF GATEKEEPER (Allowlist)
|
| 41 |
# ==========================================
|
| 42 |
def is_likely_plant(image_path):
|
| 43 |
"""
|
| 44 |
+
STRICT ALLOWLIST LOGIC:
|
| 45 |
+
We check the top 5 things the AI thinks it sees.
|
| 46 |
+
If NONE of them are related to nature, agriculture, or plants, we reject the photo.
|
| 47 |
"""
|
| 48 |
try:
|
| 49 |
+
results = gatekeeper_model(image_path, verbose=False)
|
|
|
|
|
|
|
|
|
|
| 50 |
|
| 51 |
+
# Get the top 5 predicted classes
|
| 52 |
+
top5_indices = results[0].probs.top5
|
| 53 |
+
top5_names = [results[0].names[i].lower() for i in top5_indices]
|
| 54 |
+
|
| 55 |
+
print(f"π§ Gatekeeper sees: {top5_names}")
|
| 56 |
+
|
| 57 |
+
# β
ALLOW LIST: Botanical, agricultural, and nature terms
|
| 58 |
+
allowed_keywords = [
|
| 59 |
+
'plant', 'leaf', 'grass', 'flower', 'tree', 'fern', 'moss', 'weed',
|
| 60 |
+
'crop', 'agriculture', 'field', 'greenhouse', 'pot', 'earth', 'soil',
|
| 61 |
+
'vegetation', 'forest', 'valley', 'daisy', 'corn', 'acorn', 'paddy'
|
| 62 |
]
|
| 63 |
|
| 64 |
+
# Check if ANY of the top 5 predictions contain our allowed keywords
|
| 65 |
+
for predicted_item in top5_names:
|
| 66 |
+
for good_word in allowed_keywords:
|
| 67 |
+
if good_word in predicted_item:
|
| 68 |
+
print(f"β
Passed: Gatekeeper verified plant matter ('{predicted_item}')")
|
| 69 |
+
return True
|
| 70 |
|
| 71 |
+
# If the loop finishes and didn't find a single nature word, reject it!
|
| 72 |
+
print(f"π« Blocked: No agricultural or plant features detected.")
|
| 73 |
+
return False
|
| 74 |
|
| 75 |
except Exception as e:
|
| 76 |
print(f"β οΈ Gatekeeper Error: {e}")
|
| 77 |
+
return True # Fail-safe: let it pass if the gatekeeper crashes
|
| 78 |
|
| 79 |
|
| 80 |
def get_recommendation(disease_name):
|
| 81 |
recommendations = {
|
| 82 |
+
"Leaf Blast": "Use Tricyclazole 75 WP. Avoid applying excess nitrogen fertilizer.",
|
| 83 |
+
"Sheath Blight": "Drain the field immediately. Apply validamycin or carbendazim.",
|
| 84 |
+
"Brown Spot": "Improve soil fertility. Apply potassium and phosphorus.",
|
| 85 |
+
"Healthy Rice Leaf": "No disease detected. Keep maintaining optimal water levels!"
|
|
|
|
| 86 |
}
|
| 87 |
+
# Fallback just in case class names differ slightly
|
| 88 |
+
for key, value in recommendations.items():
|
| 89 |
+
if key.lower() in disease_name.lower():
|
| 90 |
+
return value
|
| 91 |
+
return "Consult your local agricultural extension officer for treatment."
|
| 92 |
+
|
| 93 |
|
| 94 |
@app.get("/")
|
| 95 |
def home():
|
|
|
|
| 108 |
with open(temp_filename, "wb") as buffer:
|
| 109 |
shutil.copyfileobj(file.file, buffer)
|
| 110 |
|
| 111 |
+
# π STEP 1: RUN THE STRICT GATEKEEPER
|
| 112 |
if not is_likely_plant(temp_filename):
|
|
|
|
| 113 |
return {
|
| 114 |
"filename": file.filename,
|
| 115 |
"disease": "Invalid Image",
|
| 116 |
"confidence": "0%",
|
| 117 |
+
"recommendation": "This image does not appear to be a plant or paddy field. Please upload a clear photo of a rice leaf.",
|
| 118 |
"latitude": float(latitude) if latitude else None,
|
| 119 |
"longitude": float(longitude) if longitude else None
|
| 120 |
}
|
| 121 |
|
| 122 |
+
# β
STEP 2: RUN DISEASE CLASSIFICATION
|
| 123 |
+
results = disease_model(temp_filename, verbose=False)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 124 |
|
| 125 |
+
# Extract the highest probability prediction
|
| 126 |
+
top_idx = results[0].probs.top1
|
| 127 |
+
confidence_score = float(results[0].probs.top1conf)
|
| 128 |
+
detected_name = results[0].names[top_idx]
|
| 129 |
|
| 130 |
response_data = {
|
| 131 |
"filename": file.filename,
|
| 132 |
+
"disease": detected_name,
|
| 133 |
+
"confidence": f"{int(confidence_score * 100)}%",
|
| 134 |
+
"recommendation": get_recommendation(detected_name),
|
| 135 |
"latitude": float(latitude) if latitude else None,
|
| 136 |
"longitude": float(longitude) if longitude else None
|
| 137 |
}
|
| 138 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 139 |
return response_data
|
| 140 |
|
| 141 |
except Exception as e:
|
| 142 |
+
print(f"β API Error: {e}")
|
| 143 |
return {"error": str(e)}
|
| 144 |
|
| 145 |
finally:
|