Spaces:
Sleeping
Sleeping
KabsiMontassar
commited on
Commit
·
d65f7e4
1
Parent(s):
d2354c3
Add application file
Browse files- .gitignore +2 -0
- app.py +115 -0
- chat_app.py +82 -0
- class_labels.json +41 -0
- classification.ipynb +0 -0
- detection.ipynb +0 -0
- images/classification/graph.png +0 -0
- images/detection/fitting.png +0 -0
- images/detection/loading.png +0 -0
- model_loader.py +88 -0
- requirements.txt +9 -0
- style.css +32 -0
- utils.py +88 -0
.gitignore
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
|
|
|
| 1 |
+
.env
|
| 2 |
+
assets
|
app.py
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
import json
|
| 3 |
+
import numpy as np
|
| 4 |
+
import gradio as gr
|
| 5 |
+
from PIL import Image
|
| 6 |
+
from model_loader import load_model, preprocess_image, predict_disease
|
| 7 |
+
from chat_app import groq_chatbot
|
| 8 |
+
|
| 9 |
+
# Load the disease diagnosis model
|
| 10 |
+
model_path = "attached_assets/mobilenetv2.h5"
|
| 11 |
+
model = load_model(model_path)
|
| 12 |
+
|
| 13 |
+
# Load class labels
|
| 14 |
+
with open("class_labels.json", "r") as f:
|
| 15 |
+
class_labels = json.load(f)
|
| 16 |
+
|
| 17 |
+
# Disease treatments dictionary
|
| 18 |
+
DEMO_TREATMENTS = {
|
| 19 |
+
"Apple - Apple Scab": "Rake and destroy fallen leaves, prune for good air circulation, apply fungicides like captan or sulfur before rainy periods, and plant resistant apple varieties.",
|
| 20 |
+
"Apple - Black Rot": "Prune and remove infected branches, destroy fallen leaves and fruit, apply copper-based fungicides, and ensure proper spacing between trees.",
|
| 21 |
+
"Apple - Cedar Apple Rust": "Remove nearby juniper or cedar trees if possible, apply fungicides like myclobutanil, and plant resistant apple varieties.",
|
| 22 |
+
"Apple - Healthy": "Your apple tree appears healthy! Continue regular maintenance, including pruning, watering, and monitoring for pests or disease symptoms.",
|
| 23 |
+
"Background without Leaves": "No plants detected. Please upload an image containing leaves to diagnose.",
|
| 24 |
+
"Blueberry - Healthy": "Your blueberry plant is healthy! Ensure consistent watering, proper mulching, and protect it from frost during early spring.",
|
| 25 |
+
"Cherry - Powdery Mildew": "Remove infected leaves, avoid overhead watering, ensure good air circulation, and apply fungicides containing sulfur or potassium bicarbonate.",
|
| 26 |
+
"Cherry - Healthy": "Your cherry tree is healthy! Continue providing proper care, including regular pruning, watering, and monitoring for pests or diseases.",
|
| 27 |
+
"Corn - Cercospora Leaf Spot (Gray Leaf Spot)": "Rotate crops annually, apply fungicides like strobilurins or triazoles, and ensure good field drainage and proper plant spacing.",
|
| 28 |
+
"Corn - Common Rust": "Apply fungicides containing propiconazole or azoxystrobin, plant resistant corn varieties, and avoid overhead irrigation.",
|
| 29 |
+
"Corn - Northern Leaf Blight": "Rotate crops, plant resistant varieties, and apply fungicides like mancozeb or strobilurin when symptoms first appear.",
|
| 30 |
+
"Corn - Healthy": "Your corn plant looks healthy! Continue to monitor for any signs of disease and ensure proper spacing for airflow.",
|
| 31 |
+
"Grape - Black Rot": "Remove mummified berries and infected leaves, prune for good air circulation, and apply fungicides such as myclobutanil or captan.",
|
| 32 |
+
"Grape - Esca (Black Measles)": "Prune and destroy infected parts, practice proper vineyard sanitation, and avoid mechanical injuries to the vines.",
|
| 33 |
+
"Grape - Leaf Blight (Isariopsis Leaf Spot)": "Remove and destroy infected leaves, apply fungicides containing copper, and ensure proper spacing for air circulation.",
|
| 34 |
+
"Grape - Healthy": "Your grapevine is healthy! Maintain regular pruning, proper watering, and monitoring for pests or diseases.",
|
| 35 |
+
"Orange - Huanglongbing (Citrus Greening)": "Unfortunately, there is no cure. Remove and destroy infected trees, control psyllid populations using insecticides, and plant disease-free certified saplings.",
|
| 36 |
+
"Peach - Bacterial Spot": "Apply copper-based bactericides, remove and destroy infected leaves and fruit, and plant resistant peach varieties.",
|
| 37 |
+
"Peach - Healthy": "Your peach tree looks healthy! Continue regular care, including pruning, fertilizing, and monitoring for pests or diseases.",
|
| 38 |
+
"Pepper (Bell) - Bacterial Spot": "Apply fixed copper sprays, avoid overhead irrigation, remove infected plants, and practice crop rotation.",
|
| 39 |
+
"Pepper (Bell) - Healthy": "Your bell pepper plant looks healthy! Ensure adequate sunlight, watering, and keep monitoring for any pests or diseases.",
|
| 40 |
+
"Potato - Early Blight": "Remove infected leaves, apply fungicides with active ingredients like chlorothalonil, and ensure proper spacing between plants.",
|
| 41 |
+
"Potato - Late Blight": "Apply fungicides like mancozeb or chlorothalonil, remove and destroy infected plants, and avoid overhead watering.",
|
| 42 |
+
"Potato - Healthy": "Your potato plant looks healthy! Maintain proper watering, ensure good soil drainage, and monitor for any signs of disease.",
|
| 43 |
+
"Raspberry - Healthy": "Your raspberry plant is healthy! Ensure proper support, regular pruning, and protect it from pests and harsh weather conditions.",
|
| 44 |
+
"Soybean - Healthy": "Your soybean crop is healthy! Monitor regularly for any signs of disease or pests, and maintain proper crop rotation.",
|
| 45 |
+
"Squash - Powdery Mildew": "Apply fungicides with sulfur or potassium bicarbonate, remove infected leaves, and ensure good air circulation.",
|
| 46 |
+
"Strawberry - Leaf Scorch": "Remove infected leaves, avoid overhead watering, and apply fungicides like captan or mancozeb.",
|
| 47 |
+
"Strawberry - Healthy": "Your strawberry plants are healthy! Maintain consistent watering, ensure good air circulation, and protect from frost.",
|
| 48 |
+
"Tomato - Bacterial Spot": "Remove infected leaves, apply fixed copper sprays, and avoid overhead irrigation. Ensure proper plant spacing.",
|
| 49 |
+
"Tomato - Early Blight": "Remove infected leaves, apply fungicides containing chlorothalonil or copper, and mulch around plants to prevent soil splashing.",
|
| 50 |
+
"Tomato - Late Blight": "Apply fungicides like chlorothalonil or mancozeb, remove infected plants, and ensure proper spacing for airflow.",
|
| 51 |
+
"Tomato - Leaf Mold": "Remove infected leaves, ensure proper air circulation, and apply fungicides containing chlorothalonil or copper.",
|
| 52 |
+
"Tomato - Septoria Leaf Spot": "Remove and destroy infected leaves, apply fungicides like mancozeb, and avoid overhead watering.",
|
| 53 |
+
"Tomato - Spider Mites (Two-Spotted Spider Mite)": "Spray the undersides of leaves with water, apply horticultural oils or insecticidal soaps, and maintain humidity around plants.",
|
| 54 |
+
"Tomato - Target Spot": "Remove infected leaves, apply fungicides like chlorothalonil, and ensure proper spacing between plants for air circulation.",
|
| 55 |
+
"Tomato - Tomato Yellow Leaf Curl Virus": "Remove infected plants, control whitefly populations with insecticides, and plant resistant tomato varieties.",
|
| 56 |
+
"Tomato - Tomato Mosaic Virus": "Remove infected plants, sterilize tools, and avoid handling plants when wet.",
|
| 57 |
+
"Tomato - Healthy": "Your tomato plant is healthy! Maintain regular watering, ensure adequate sunlight, and monitor for pests or diseases."
|
| 58 |
+
}
|
| 59 |
+
|
| 60 |
+
# Diagnosis function
|
| 61 |
+
def diagnose_image(image):
|
| 62 |
+
if image is None:
|
| 63 |
+
return "⚠️ Please upload an image for diagnosis."
|
| 64 |
+
|
| 65 |
+
try:
|
| 66 |
+
img_array = np.array(image)
|
| 67 |
+
preprocessed_img = preprocess_image(img_array)
|
| 68 |
+
disease_label, confidence = predict_disease(model, preprocessed_img, class_labels)
|
| 69 |
+
confidence_pct = f"{confidence:.1f}%"
|
| 70 |
+
|
| 71 |
+
treatment = DEMO_TREATMENTS.get(
|
| 72 |
+
disease_label,
|
| 73 |
+
"No specific treatment information available for this condition. Consult with an agricultural expert."
|
| 74 |
+
)
|
| 75 |
+
|
| 76 |
+
result = f"### 🌿 Diagnosis: {disease_label.replace('_', ' ')}\n"
|
| 77 |
+
result += f"**Confidence:** {confidence_pct}\n\n"
|
| 78 |
+
result += f"### 🛠 Recommended Treatment:\n{treatment}"
|
| 79 |
+
return result
|
| 80 |
+
except Exception as e:
|
| 81 |
+
return f"❌ Error during diagnosis: {e}"
|
| 82 |
+
|
| 83 |
+
# Build Gradio UI
|
| 84 |
+
with gr.Blocks(css="footer {visibility: hidden}") as app:
|
| 85 |
+
gr.Markdown("# 🌱 Plant Disease Diagnosis & Agricultural Chatbot")
|
| 86 |
+
gr.Markdown("Upload an image of a plant leaf for disease detection, or ask the chatbot for agricultural advice.")
|
| 87 |
+
|
| 88 |
+
with gr.Row():
|
| 89 |
+
# LEFT: Image Upload & Disease Diagnosis
|
| 90 |
+
with gr.Column(scale=1):
|
| 91 |
+
gr.Markdown("## 📸 Upload Image for Diagnosis")
|
| 92 |
+
image_input = gr.Image(type="numpy", label="Upload Leaf Image")
|
| 93 |
+
diagnose_button = gr.Button("🔍 Diagnose", variant="primary")
|
| 94 |
+
diagnosis_output = gr.Markdown(label="Diagnosis Results")
|
| 95 |
+
|
| 96 |
+
diagnose_button.click(fn=diagnose_image, inputs=[image_input], outputs=[diagnosis_output])
|
| 97 |
+
|
| 98 |
+
# RIGHT: Chatbot for Agricultural Advice
|
| 99 |
+
with gr.Column(scale=1):
|
| 100 |
+
gr.Markdown("## 🤖 Ask the Agricultural Chatbot")
|
| 101 |
+
chatbot = gr.Chatbot(height=400)
|
| 102 |
+
msg = gr.Textbox(placeholder="Ask a question about agriculture...", label="Your Question")
|
| 103 |
+
clear = gr.Button("🗑 Clear Chat")
|
| 104 |
+
chat_history_state = gr.State([])
|
| 105 |
+
|
| 106 |
+
msg.submit(fn=groq_chatbot, inputs=[msg, chat_history_state], outputs=[chatbot, msg])
|
| 107 |
+
clear.click(lambda: ([], ""), None, [chatbot, msg], queue=False)
|
| 108 |
+
|
| 109 |
+
gr.Markdown("---")
|
| 110 |
+
gr.Markdown("### ℹ️ About this Application")
|
| 111 |
+
gr.Markdown("This AI-powered tool helps diagnose plant diseases and provides treatment recommendations. It also includes a chatbot for general agricultural queries.")
|
| 112 |
+
|
| 113 |
+
# Launch Gradio app
|
| 114 |
+
if __name__ == "__main__":
|
| 115 |
+
app.launch(share=True)
|
chat_app.py
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
from groq import Groq
|
| 3 |
+
import gradio as gr
|
| 4 |
+
from dotenv import load_dotenv
|
| 5 |
+
load_dotenv()
|
| 6 |
+
# Load API key from environment variable
|
| 7 |
+
API_KEY = os.getenv("GROQ_API_KEY")
|
| 8 |
+
if not API_KEY:
|
| 9 |
+
raise ValueError("Missing GROQ_API_KEY environment variable. Set it in Hugging Face Spaces Secrets.")
|
| 10 |
+
|
| 11 |
+
client = Groq(api_key=API_KEY)
|
| 12 |
+
|
| 13 |
+
VALIDATION_PROMPT = "You are an intelligent assistant. Analyze the input question carefully. Respond with 'Yes' if the input is agriculture-related, and 'No' otherwise."
|
| 14 |
+
RESPONSE_PROMPT = "You are an agriculture expert. Provide a concise and accurate answer to the following agriculture-related question:"
|
| 15 |
+
|
| 16 |
+
def validate_input(input_text):
|
| 17 |
+
try:
|
| 18 |
+
validation_response = client.chat.completions.create(
|
| 19 |
+
model="llama-3.1-8b-instant",
|
| 20 |
+
messages=[
|
| 21 |
+
{"role": "system", "content": "You are a helpful assistant."},
|
| 22 |
+
{"role": "user", "content": VALIDATION_PROMPT},
|
| 23 |
+
{"role": "user", "content": input_text},
|
| 24 |
+
],
|
| 25 |
+
temperature=0,
|
| 26 |
+
max_completion_tokens=1,
|
| 27 |
+
)
|
| 28 |
+
return validation_response.choices[0].message.content.strip()
|
| 29 |
+
except Exception as e:
|
| 30 |
+
return f"Error: {e}"
|
| 31 |
+
|
| 32 |
+
def get_agriculture_response(input_text):
|
| 33 |
+
try:
|
| 34 |
+
detailed_response = client.chat.completions.create(
|
| 35 |
+
model="llama-3.1-8b-instant",
|
| 36 |
+
messages=[
|
| 37 |
+
{"role": "system", "content": "You are a helpful assistant."},
|
| 38 |
+
{"role": "user", "content": RESPONSE_PROMPT},
|
| 39 |
+
{"role": "user", "content": input_text},
|
| 40 |
+
],
|
| 41 |
+
temperature=0.5,
|
| 42 |
+
max_completion_tokens=700,
|
| 43 |
+
)
|
| 44 |
+
return detailed_response.choices[0].message.content.strip()
|
| 45 |
+
except Exception as e:
|
| 46 |
+
return f"Error: {e}"
|
| 47 |
+
|
| 48 |
+
def groq_chatbot(input_text, chat_history):
|
| 49 |
+
validation_result = validate_input(input_text)
|
| 50 |
+
|
| 51 |
+
if validation_result.lower() == "yes":
|
| 52 |
+
response = get_agriculture_response(input_text)
|
| 53 |
+
elif validation_result.lower() == "no":
|
| 54 |
+
response = "❌ This is not an agriculture-related question."
|
| 55 |
+
else:
|
| 56 |
+
response = f"⚠️ Unexpected response: {validation_result}"
|
| 57 |
+
|
| 58 |
+
# Append to chat history
|
| 59 |
+
chat_history.append((input_text, response))
|
| 60 |
+
return chat_history, "" # Clears input field after submission
|
| 61 |
+
|
| 62 |
+
def launch_gradio_interface():
|
| 63 |
+
with gr.Blocks() as demo:
|
| 64 |
+
gr.Markdown("### 🌱 Agriculture AI Assistant")
|
| 65 |
+
gr.Markdown("Ask questions about plant diseases, treatments, or general agricultural topics.")
|
| 66 |
+
|
| 67 |
+
chatbot = gr.Chatbot(height=400)
|
| 68 |
+
msg = gr.Textbox(placeholder="Ask a question about agriculture...", label="Your Question")
|
| 69 |
+
clear = gr.Button("Clear Chat")
|
| 70 |
+
|
| 71 |
+
chat_history_state = gr.State([]) # Stores chat history
|
| 72 |
+
|
| 73 |
+
# ✅ Enter key submits the question
|
| 74 |
+
msg.submit(fn=groq_chatbot, inputs=[msg, chat_history_state], outputs=[chatbot, msg])
|
| 75 |
+
|
| 76 |
+
# ✅ Clicking "Clear Chat" resets history
|
| 77 |
+
clear.click(lambda: ([], ""), None, [chatbot, msg], queue=False)
|
| 78 |
+
|
| 79 |
+
demo.launch(share=True)
|
| 80 |
+
|
| 81 |
+
if __name__ == "__main__":
|
| 82 |
+
launch_gradio_interface()
|
class_labels.json
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"0": "Apple - Apple Scab",
|
| 3 |
+
"1": "Apple - Black Rot",
|
| 4 |
+
"2": "Apple - Cedar Apple Rust",
|
| 5 |
+
"3": "Apple - Healthy",
|
| 6 |
+
"4": "Background without Leaves",
|
| 7 |
+
"5": "Blueberry - Healthy",
|
| 8 |
+
"6": "Cherry - Powdery Mildew",
|
| 9 |
+
"7": "Cherry - Healthy",
|
| 10 |
+
"8": "Corn - Cercospora Leaf Spot (Gray Leaf Spot)",
|
| 11 |
+
"9": "Corn - Common Rust",
|
| 12 |
+
"10": "Corn - Northern Leaf Blight",
|
| 13 |
+
"11": "Corn - Healthy",
|
| 14 |
+
"12": "Grape - Black Rot",
|
| 15 |
+
"13": "Grape - Esca (Black Measles)",
|
| 16 |
+
"14": "Grape - Leaf Blight (Isariopsis Leaf Spot)",
|
| 17 |
+
"15": "Grape - Healthy",
|
| 18 |
+
"16": "Orange - Huanglongbing (Citrus Greening)",
|
| 19 |
+
"17": "Peach - Bacterial Spot",
|
| 20 |
+
"18": "Peach - Healthy",
|
| 21 |
+
"19": "Pepper (Bell) - Bacterial Spot",
|
| 22 |
+
"20": "Pepper (Bell) - Healthy",
|
| 23 |
+
"21": "Potato - Early Blight",
|
| 24 |
+
"22": "Potato - Late Blight",
|
| 25 |
+
"23": "Potato - Healthy",
|
| 26 |
+
"24": "Raspberry - Healthy",
|
| 27 |
+
"25": "Soybean - Healthy",
|
| 28 |
+
"26": "Squash - Powdery Mildew",
|
| 29 |
+
"27": "Strawberry - Leaf Scorch",
|
| 30 |
+
"28": "Strawberry - Healthy",
|
| 31 |
+
"29": "Tomato - Bacterial Spot",
|
| 32 |
+
"30": "Tomato - Early Blight",
|
| 33 |
+
"31": "Tomato - Late Blight",
|
| 34 |
+
"32": "Tomato - Leaf Mold",
|
| 35 |
+
"33": "Tomato - Septoria Leaf Spot",
|
| 36 |
+
"34": "Tomato - Spider Mites (Two-Spotted Spider Mite)",
|
| 37 |
+
"35": "Tomato - Target Spot",
|
| 38 |
+
"36": "Tomato - Tomato Yellow Leaf Curl Virus",
|
| 39 |
+
"37": "Tomato - Tomato Mosaic Virus",
|
| 40 |
+
"38": "Tomato - Healthy"
|
| 41 |
+
}
|
classification.ipynb
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
detection.ipynb
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
images/classification/graph.png
ADDED
|
images/detection/fitting.png
ADDED
|
images/detection/loading.png
ADDED
|
model_loader.py
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
import tensorflow as tf
|
| 3 |
+
import numpy as np
|
| 4 |
+
import cv2
|
| 5 |
+
import logging
|
| 6 |
+
|
| 7 |
+
# Setup logging
|
| 8 |
+
logging.basicConfig(level=logging.DEBUG)
|
| 9 |
+
logger = logging.getLogger(__name__)
|
| 10 |
+
|
| 11 |
+
def load_model(model_path):
|
| 12 |
+
"""
|
| 13 |
+
Load the MobileNetV2 model from the specified path
|
| 14 |
+
|
| 15 |
+
Args:
|
| 16 |
+
model_path: Path to the .h5 model file
|
| 17 |
+
|
| 18 |
+
Returns:
|
| 19 |
+
Loaded TensorFlow model
|
| 20 |
+
"""
|
| 21 |
+
try:
|
| 22 |
+
logger.info(f"Loading model from {model_path}")
|
| 23 |
+
model = tf.keras.models.load_model(model_path)
|
| 24 |
+
logger.info("Model loaded successfully")
|
| 25 |
+
return model
|
| 26 |
+
except Exception as e:
|
| 27 |
+
logger.error(f"Error loading model: {str(e)}")
|
| 28 |
+
raise
|
| 29 |
+
|
| 30 |
+
def preprocess_image(image, target_size=(224, 224)):
|
| 31 |
+
"""
|
| 32 |
+
Preprocess the image for model input
|
| 33 |
+
|
| 34 |
+
Args:
|
| 35 |
+
image: Input image (numpy array from OpenCV)
|
| 36 |
+
target_size: Target size for model input (default: 224x224)
|
| 37 |
+
|
| 38 |
+
Returns:
|
| 39 |
+
Preprocessed image ready for model input
|
| 40 |
+
"""
|
| 41 |
+
try:
|
| 42 |
+
# Convert BGR to RGB if from OpenCV
|
| 43 |
+
if len(image.shape) == 3 and image.shape[2] == 3:
|
| 44 |
+
image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
|
| 45 |
+
|
| 46 |
+
# Resize image
|
| 47 |
+
image_resized = cv2.resize(image, target_size)
|
| 48 |
+
|
| 49 |
+
# Convert to float and normalize
|
| 50 |
+
image_normalized = image_resized.astype(np.float32) / 255.0
|
| 51 |
+
|
| 52 |
+
# Expand dimensions to create batch
|
| 53 |
+
image_batch = np.expand_dims(image_normalized, axis=0)
|
| 54 |
+
|
| 55 |
+
return image_batch
|
| 56 |
+
except Exception as e:
|
| 57 |
+
logger.error(f"Error preprocessing image: {str(e)}")
|
| 58 |
+
raise
|
| 59 |
+
|
| 60 |
+
def predict_disease(model, preprocessed_image, class_labels):
|
| 61 |
+
"""
|
| 62 |
+
Predict disease from preprocessed image
|
| 63 |
+
|
| 64 |
+
Args:
|
| 65 |
+
model: Loaded TensorFlow model
|
| 66 |
+
preprocessed_image: Preprocessed image batch
|
| 67 |
+
class_labels: Dictionary mapping class indices to labels
|
| 68 |
+
|
| 69 |
+
Returns:
|
| 70 |
+
Tuple of (predicted disease label, confidence percentage)
|
| 71 |
+
"""
|
| 72 |
+
try:
|
| 73 |
+
# Make prediction
|
| 74 |
+
predictions = model.predict(preprocessed_image)
|
| 75 |
+
|
| 76 |
+
# Get the predicted class index
|
| 77 |
+
predicted_class_idx = np.argmax(predictions[0])
|
| 78 |
+
|
| 79 |
+
# Get confidence score
|
| 80 |
+
confidence = float(predictions[0][predicted_class_idx] * 100)
|
| 81 |
+
|
| 82 |
+
# Convert to label
|
| 83 |
+
label = class_labels.get(str(predicted_class_idx), f"Unknown class {predicted_class_idx}")
|
| 84 |
+
|
| 85 |
+
return label, confidence
|
| 86 |
+
except Exception as e:
|
| 87 |
+
logger.error(f"Error making prediction: {str(e)}")
|
| 88 |
+
raise
|
requirements.txt
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
tensorflow==2.15.0
|
| 2 |
+
numpy>=1.22.0
|
| 3 |
+
pillow>=10.0.0
|
| 4 |
+
gradio>=4.0.0
|
| 5 |
+
groq
|
| 6 |
+
opencv-python
|
| 7 |
+
opencv-python-headless
|
| 8 |
+
bing_image_downloader
|
| 9 |
+
python-dotenv
|
style.css
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
body {
|
| 2 |
+
font-family: 'Arial', sans-serif;
|
| 3 |
+
background-color: #1e1e1e;
|
| 4 |
+
color: white;
|
| 5 |
+
}
|
| 6 |
+
|
| 7 |
+
#title {
|
| 8 |
+
margin-bottom: 20px;
|
| 9 |
+
}
|
| 10 |
+
|
| 11 |
+
#input-box textarea {
|
| 12 |
+
background-color: #2c2c2c !important;
|
| 13 |
+
color: white;
|
| 14 |
+
border-radius: 10px;
|
| 15 |
+
font-size: 16px;
|
| 16 |
+
}
|
| 17 |
+
|
| 18 |
+
#validate-button {
|
| 19 |
+
background-color: #4CAF50 !important;
|
| 20 |
+
color: white;
|
| 21 |
+
font-size: 18px;
|
| 22 |
+
border-radius: 8px;
|
| 23 |
+
padding: 10px 15px;
|
| 24 |
+
}
|
| 25 |
+
|
| 26 |
+
#validation-result, #output-box {
|
| 27 |
+
background-color: #2c2c2c !important;
|
| 28 |
+
color: white;
|
| 29 |
+
font-size: 16px;
|
| 30 |
+
border-radius: 8px;
|
| 31 |
+
padding: 10px;
|
| 32 |
+
}
|
utils.py
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import re
|
| 2 |
+
import logging
|
| 3 |
+
from difflib import get_close_matches
|
| 4 |
+
|
| 5 |
+
# Setup logging
|
| 6 |
+
logging.basicConfig(level=logging.DEBUG)
|
| 7 |
+
logger = logging.getLogger(__name__)
|
| 8 |
+
|
| 9 |
+
def extract_disease_name(prediction):
|
| 10 |
+
"""
|
| 11 |
+
Extract just the disease name from a prediction that might include plant name
|
| 12 |
+
|
| 13 |
+
Args:
|
| 14 |
+
prediction: Full prediction string (e.g., "Tomato_Late_blight")
|
| 15 |
+
|
| 16 |
+
Returns:
|
| 17 |
+
Extracted disease name
|
| 18 |
+
"""
|
| 19 |
+
try:
|
| 20 |
+
# Handle common formats like "Plant_Disease" or "Plant Disease"
|
| 21 |
+
if '_' in prediction:
|
| 22 |
+
parts = prediction.split('_')
|
| 23 |
+
else:
|
| 24 |
+
parts = prediction.split(' ')
|
| 25 |
+
|
| 26 |
+
if len(parts) <= 1:
|
| 27 |
+
return prediction # Return as is if can't split
|
| 28 |
+
|
| 29 |
+
# Skip the plant name (first part) and join the rest
|
| 30 |
+
disease_parts = parts[1:]
|
| 31 |
+
disease_name = ' '.join(disease_parts)
|
| 32 |
+
|
| 33 |
+
# Clean up the disease name
|
| 34 |
+
disease_name = disease_name.replace('_', ' ').strip()
|
| 35 |
+
|
| 36 |
+
return disease_name
|
| 37 |
+
except Exception as e:
|
| 38 |
+
logger.error(f"Error extracting disease name: {str(e)}")
|
| 39 |
+
return prediction # Return original on error
|
| 40 |
+
|
| 41 |
+
def normalize_disease_name(disease_name):
|
| 42 |
+
"""
|
| 43 |
+
Normalize disease names for better matching
|
| 44 |
+
|
| 45 |
+
Args:
|
| 46 |
+
disease_name: Disease name to normalize
|
| 47 |
+
|
| 48 |
+
Returns:
|
| 49 |
+
Normalized disease name
|
| 50 |
+
"""
|
| 51 |
+
# Convert to lowercase
|
| 52 |
+
normalized = disease_name.lower()
|
| 53 |
+
|
| 54 |
+
# Remove special characters
|
| 55 |
+
normalized = re.sub(r'[^a-z0-9\s]', '', normalized)
|
| 56 |
+
|
| 57 |
+
# Replace multiple spaces with single space
|
| 58 |
+
normalized = re.sub(r'\s+', ' ', normalized).strip()
|
| 59 |
+
|
| 60 |
+
return normalized
|
| 61 |
+
|
| 62 |
+
def find_similar_disease(disease_name, known_diseases):
|
| 63 |
+
"""
|
| 64 |
+
Find the most similar disease name in a list of known diseases
|
| 65 |
+
|
| 66 |
+
Args:
|
| 67 |
+
disease_name: Query disease name
|
| 68 |
+
known_diseases: List of known disease names
|
| 69 |
+
|
| 70 |
+
Returns:
|
| 71 |
+
Most similar disease name, or the original if no good match
|
| 72 |
+
"""
|
| 73 |
+
# Normalize the query
|
| 74 |
+
norm_query = normalize_disease_name(disease_name)
|
| 75 |
+
|
| 76 |
+
# Normalize all known diseases
|
| 77 |
+
norm_known = [normalize_disease_name(d) for d in known_diseases]
|
| 78 |
+
|
| 79 |
+
# Find close matches
|
| 80 |
+
matches = get_close_matches(norm_query, norm_known, n=1, cutoff=0.6)
|
| 81 |
+
|
| 82 |
+
if matches:
|
| 83 |
+
# Get the index of the match in the normalized list
|
| 84 |
+
match_idx = norm_known.index(matches[0])
|
| 85 |
+
# Return the original form of the matched disease
|
| 86 |
+
return known_diseases[match_idx]
|
| 87 |
+
|
| 88 |
+
return disease_name # Return original if no good matches
|