Spaces:
Sleeping
Sleeping
| import os | |
| import gradio as gr | |
| import numpy as np | |
| import requests | |
| import json | |
| import time | |
| from tensorflow.keras.models import load_model | |
| from PIL import Image | |
| # ===== API Configuration ===== | |
| # Try to get API tokens from environment variables | |
| HF_API_TOKEN = os.getenv("HUGGINGFACE_TOKEN") # Hugging Face API token | |
| GROQ_API_KEY = os.getenv("GROQ_API_KEY") # Groq API key | |
| OPENAI_API_KEY = os.getenv("OPENAI_API_KEY") # OpenAI API key (fallback) | |
| print(f"API tokens available: HF={'Yes' if HF_API_TOKEN else 'No'}, Groq={'Yes' if GROQ_API_KEY else 'No'}, OpenAI={'Yes' if OPENAI_API_KEY else 'No'}") | |
| # ===== Disease Information Database ===== | |
| disease_info = { | |
| "Tomato Bacterial Spot": { | |
| "description": "A bacterial disease that causes small, dark spots on leaves, stems, and fruits.", | |
| "causes": "Caused by Xanthomonas bacteria, spread by water splash, contaminated tools, and seeds.", | |
| "treatment": [ | |
| "Remove and destroy infected plants", | |
| "Rotate crops with non-solanaceous plants", | |
| "Use copper-based fungicides", | |
| "Avoid overhead irrigation" | |
| ] | |
| }, | |
| "Tomato Early Blight": { | |
| "description": "A fungal disease that causes dark spots with concentric rings on lower leaves first.", | |
| "causes": "Caused by Alternaria solani fungus, favored by warm, humid conditions.", | |
| "treatment": [ | |
| "Remove infected leaves promptly", | |
| "Improve air circulation around plants", | |
| "Apply fungicides preventatively", | |
| "Mulch around plants to prevent soil splash" | |
| ] | |
| }, | |
| "Tomato Late Blight": { | |
| "description": "A devastating fungal disease that causes dark, water-soaked lesions on leaves and fruits.", | |
| "causes": "Caused by Phytophthora infestans, favored by cool, wet conditions.", | |
| "treatment": [ | |
| "Remove and destroy infected plants immediately", | |
| "Apply fungicides preventatively in humid conditions", | |
| "Improve drainage and air circulation", | |
| "Plant resistant varieties when available" | |
| ] | |
| }, | |
| "Tomato Mosaic Virus": { | |
| "description": "A viral disease that causes mottled green/yellow patterns on leaves and stunted growth.", | |
| "causes": "Caused by tobacco mosaic virus (TMV), spread by handling, tools, and sometimes seeds.", | |
| "treatment": [ | |
| "Remove and destroy infected plants", | |
| "Wash hands and tools after handling infected plants", | |
| "Control insect vectors like aphids", | |
| "Plant resistant varieties" | |
| ] | |
| }, | |
| "Tomato Yellow Leaf Curl Virus": { | |
| "description": "A viral disease transmitted by whiteflies that causes yellowing and curling of leaves.", | |
| "causes": "Caused by a begomovirus, transmitted primarily by whiteflies.", | |
| "treatment": [ | |
| "Use whitefly control measures", | |
| "Remove and destroy infected plants", | |
| "Use reflective mulches to repel whiteflies", | |
| "Plant resistant varieties" | |
| ] | |
| }, | |
| "Tomato___Target_Spot": { | |
| "description": "A fungal disease causing circular lesions with concentric rings on leaves, stems, and fruits.", | |
| "causes": "Caused by Corynespora cassiicola fungus, favored by warm, humid conditions.", | |
| "treatment": [ | |
| "Remove infected plant parts", | |
| "Improve air circulation", | |
| "Apply fungicides at first sign of disease", | |
| "Avoid overhead irrigation" | |
| ] | |
| }, | |
| "Tomato___Bacterial_spot": { | |
| "description": "A bacterial disease causing small, dark, water-soaked spots on leaves, stems, and fruits.", | |
| "causes": "Caused by Xanthomonas species, spread by water splash and contaminated tools.", | |
| "treatment": [ | |
| "Remove infected plant debris", | |
| "Use copper-based bactericides", | |
| "Rotate crops", | |
| "Use disease-free seeds and transplants" | |
| ] | |
| }, | |
| "Tomato___healthy": { | |
| "description": "The plant shows no signs of disease and appears to be in good health.", | |
| "causes": "Proper growing conditions, good management practices, and disease prevention.", | |
| "treatment": [ | |
| "Continue regular watering and fertilization", | |
| "Monitor for early signs of disease", | |
| "Maintain good air circulation", | |
| "Practice crop rotation" | |
| ] | |
| } | |
| } | |
| # ===== Load Trained Models ===== | |
| model_a = load_model("Tomato_Leaf_Disease_Model.h5") | |
| model_b = load_model("tomato_leaf_model_final(77%).h5") | |
| classifier_model = load_model("tomato_leaf_classifier_optimized.h5") | |
| # ===== Preprocessing Function ===== | |
| def preprocess_image(image, target_size=(224, 224)): | |
| # Ensure the image is resized and normalized. | |
| if isinstance(image, Image.Image): | |
| img = image.resize(target_size) | |
| else: | |
| img = Image.fromarray(image).resize(target_size) | |
| img_array = np.array(img) / 255.0 | |
| img_array = np.expand_dims(img_array, axis=0) | |
| return img_array | |
| # ===== Disease Label Mappings ===== | |
| # Model A labels | |
| disease_labels_a = { | |
| 0: "Tomato Bacterial Spot", | |
| 1: "Tomato Early Blight", | |
| 2: "Tomato Late Blight", | |
| 3: "Tomato Mosaic Virus", | |
| 4: "Tomato Yellow Leaf Curl Virus" | |
| } | |
| # Model B labels | |
| disease_labels_b = { | |
| 0: "Tomato___Target_Spot", | |
| 1: "Tomato___Bacterial_spot", | |
| 2: "Tomato___Early_blight", | |
| 3: "Tomato___healthy", | |
| 4: "Tomato___Late_blight" | |
| } | |
| # ===== Prediction Functions ===== | |
| def predict_model_a(image): | |
| img = preprocess_image(image) | |
| pred = model_a.predict(img) | |
| predicted_class = np.argmax(pred) | |
| confidence = float(np.max(pred) * 100) | |
| return disease_labels_a.get(predicted_class, "Unknown result"), confidence | |
| def predict_model_b(image): | |
| img = preprocess_image(image) | |
| pred = model_b.predict(img) | |
| predicted_class = np.argmax(pred) | |
| confidence = float(np.max(pred) * 100) | |
| return disease_labels_b.get(predicted_class, "Unknown result"), confidence | |
| def predict_classifier(image): | |
| img = preprocess_image(image) | |
| pred = classifier_model.predict(img) | |
| # Here we assume the classifier returns class 1 for "Tomato Leaf" | |
| return "Tomato Leaf" if np.argmax(pred) == 1 else "Not Tomato Leaf" | |
| # ===== AI Model API Calls ===== | |
| def get_ai_advice(prompt, retries=2): | |
| """Try multiple AI models with fallback mechanisms""" | |
| # Try Groq API first (if key available) | |
| if GROQ_API_KEY: | |
| try: | |
| headers = { | |
| "Authorization": f"Bearer {GROQ_API_KEY}", | |
| "Content-Type": "application/json" | |
| } | |
| payload = { | |
| "model": "llama3-8b-8192", # Using Llama 3 8B model | |
| "messages": [ | |
| {"role": "system", "content": "You are an expert agricultural advisor specializing in tomato farming."}, | |
| {"role": "user", "content": prompt} | |
| ], | |
| "max_tokens": 800, | |
| "temperature": 0.7 | |
| } | |
| response = requests.post( | |
| "https://api.groq.com/openai/v1/chat/completions", | |
| headers=headers, | |
| json=payload, | |
| timeout=30 | |
| ) | |
| if response.status_code == 200: | |
| result = response.json() | |
| if "choices" in result and len(result["choices"]) > 0: | |
| return result["choices"][0]["message"]["content"] | |
| print(f"Groq API error: {response.status_code} - {response.text}") | |
| except Exception as e: | |
| print(f"Error with Groq API: {str(e)}") | |
| # Try Hugging Face Inference API as first fallback (if token available) | |
| if HF_API_TOKEN: | |
| try: | |
| headers = {"Authorization": f"Bearer {HF_API_TOKEN}"} | |
| # Format prompt for instruction-tuned models | |
| formatted_prompt = f"""<s>[INST] {prompt} [/INST]""" | |
| payload = { | |
| "inputs": formatted_prompt, | |
| "parameters": { | |
| "max_new_tokens": 800, | |
| "temperature": 0.7, | |
| "top_p": 0.95, | |
| "do_sample": True | |
| } | |
| } | |
| # Try Mistral model first | |
| url = "https://api-inference.huggingface.co/models/mistralai/Mistral-7B-Instruct-v0.2" | |
| response = requests.post(url, headers=headers, json=payload, timeout=30) | |
| if response.status_code == 200: | |
| result = response.json() | |
| if isinstance(result, list) and len(result) > 0: | |
| if "generated_text" in result[0]: | |
| # Extract just the response part (after the prompt) | |
| generated_text = result[0]["generated_text"] | |
| # Remove the prompt from the response | |
| response_text = generated_text.split("[/INST]")[-1].strip() | |
| return response_text | |
| # If Mistral fails, try Llama 3 | |
| url = "https://api-inference.huggingface.co/models/meta-llama/Meta-Llama-3-8B-Instruct" | |
| response = requests.post(url, headers=headers, json=payload, timeout=30) | |
| if response.status_code == 200: | |
| result = response.json() | |
| if isinstance(result, list) and len(result) > 0: | |
| if "generated_text" in result[0]: | |
| generated_text = result[0]["generated_text"] | |
| response_text = generated_text.split("[/INST]")[-1].strip() | |
| return response_text | |
| except Exception as e: | |
| print(f"Error with Hugging Face API: {str(e)}") | |
| # Try OpenAI API as final fallback (if key available) | |
| if OPENAI_API_KEY: | |
| try: | |
| headers = { | |
| "Authorization": f"Bearer {OPENAI_API_KEY}", | |
| "Content-Type": "application/json" | |
| } | |
| payload = { | |
| "model": "gpt-3.5-turbo", | |
| "messages": [ | |
| {"role": "system", "content": "You are an expert agricultural advisor specializing in tomato farming."}, | |
| {"role": "user", "content": prompt} | |
| ], | |
| "max_tokens": 800, | |
| "temperature": 0.7 | |
| } | |
| response = requests.post( | |
| "https://api.openai.com/v1/chat/completions", | |
| headers=headers, | |
| json=payload, | |
| timeout=30 | |
| ) | |
| if response.status_code == 200: | |
| result = response.json() | |
| if "choices" in result and len(result["choices"]) > 0: | |
| return result["choices"][0]["message"]["content"] | |
| except Exception as e: | |
| print(f"Error with OpenAI API: {str(e)}") | |
| # If all API calls fail, use the fallback information from our database | |
| disease_name = prompt.split("disease has been detected: ")[-1].split(" with")[0] if "disease has been detected:" in prompt else "" | |
| if disease_name and disease_name in disease_info: | |
| info = disease_info[disease_name] | |
| return f""" | |
| # {disease_name} | |
| ## Description | |
| {info['description']} | |
| ## Causes | |
| {info['causes']} | |
| ## Recommended Treatment | |
| {chr(10).join(f"- {rec}" for rec in info['treatment'])} | |
| *Note: This is fallback information as our AI service is currently unavailable.* | |
| """ | |
| else: | |
| # Generic fallback response | |
| return """ | |
| # Agricultural Advice | |
| I apologize, but I'm currently unable to connect to our AI service. Here are some general tips for tomato plant care: | |
| ## General Tomato Care Tips | |
| - Water consistently, aiming for 1-2 inches per week | |
| - Provide support with stakes or cages | |
| - Fertilize regularly with balanced fertilizer | |
| - Remove suckers for indeterminate varieties | |
| - Monitor for pests and diseases regularly | |
| - Ensure good air circulation between plants | |
| - Mulch to retain moisture and prevent soil-borne diseases | |
| Please try again later for more specific advice. | |
| """ | |
| # ===== AI Assistant Functions ===== | |
| def generate_disease_advice(disease_name, confidence): | |
| """Generate advice for a specific disease with confidence level.""" | |
| if "healthy" in disease_name.lower(): | |
| prompt = ( | |
| "You are an agricultural advisor speaking to a farmer. " | |
| "The tomato crop appears healthy. " | |
| "Provide detailed preventive tips and best practices for maintaining tomato crop health. " | |
| "Include information about watering, fertilization, pest prevention, and optimal growing conditions. " | |
| "Format your response in clear sections with bullet points where appropriate." | |
| ) | |
| else: | |
| prompt = ( | |
| f"You are an agricultural advisor speaking to a farmer. " | |
| f"A disease has been detected in their tomato crop: {disease_name} with {confidence:.1f}% confidence. " | |
| f"Provide detailed advice on how to identify, manage and treat this disease. " | |
| f"Include information about: " | |
| f"1) What causes this disease " | |
| f"2) How it spreads " | |
| f"3) Specific treatments (both organic and chemical options) " | |
| f"4) Preventive measures for the future " | |
| f"Format your response in clear sections with bullet points where appropriate." | |
| ) | |
| return get_ai_advice(prompt) | |
| def chat_with_farmer(message, chat_history): | |
| """Handle chat interactions with farmers about agricultural topics.""" | |
| if not message.strip(): | |
| return "", chat_history | |
| # Prepare context from chat history | |
| context = "\n".join([f"Farmer: {q}\nAdvisor: {a}" for q, a in chat_history[-3:]]) # Use last 3 exchanges for context | |
| prompt = ( | |
| f"You are FarmAssist, an expert agricultural advisor specializing in tomato farming and plant diseases. " | |
| f"You provide helpful, accurate, and practical advice to farmers. " | |
| f"Always be respectful and considerate of farmers' knowledge while providing expert guidance. " | |
| f"If you're unsure about something, acknowledge it and provide the best information you can. " | |
| f"Previous conversation:\n{context}\n\n" | |
| f"Farmer's new question: {message}\n\n" | |
| f"Provide a helpful, informative response about farming, focusing on tomatoes if relevant." | |
| ) | |
| response = get_ai_advice(prompt) | |
| chat_history.append((message, response)) | |
| return "", chat_history | |
| # ===== Process Function Based on Version ===== | |
| def process_version(image, version): | |
| if image is None: | |
| return "No image provided." | |
| # --- Version 1.x (Model A) --- | |
| if version == "1.1": | |
| result, confidence = predict_model_a(image) | |
| return f"Model A Prediction: {result} (Confidence: {confidence:.1f}%)\n\nView Model A Training Notebook: https://colab.research.google.com/drive/1FMjs7JmdO6WVoXbzLA-ymwnIKq-GaV6w?usp=sharing" | |
| elif version == "1.2": | |
| result, confidence = predict_model_a(image) | |
| advice = generate_disease_advice(result, confidence) | |
| return f"Model A Prediction: {result} (Confidence: {confidence:.1f}%)\n\nExpert Advice:\n{advice}" | |
| elif version == "1.3": | |
| cls_result = predict_classifier(image) | |
| if cls_result != "Tomato Leaf": | |
| return "Classifier: The image is not a tomato leaf. Please try again with a tomato leaf image." | |
| result, confidence = predict_model_a(image) | |
| advice = generate_disease_advice(result, confidence) | |
| return ( | |
| f"Classifier: {cls_result}\n" | |
| f"Model A Prediction: {result} (Confidence: {confidence:.1f}%)\n\n" | |
| f"Expert Advice:\n{advice}\n\n" | |
| f"[View Model A & Classifier Training Notebook](https://colab.research.google.com/drive/1FMjs7JmdO6WVoXbzLA-ymwnIKq-GaV6w?usp=sharing)" | |
| ) | |
| # --- Version 2.x (Model B) --- | |
| elif version == "2.1": | |
| result, confidence = predict_model_b(image) | |
| return f"Model B Prediction: {result} (Confidence: {confidence:.1f}%)\n\n[View Model B Training Notebook](https://colab.research.google.com/drive/1CvoQY40gK2YsMgt4wq9kM2ZSO2c4lzFU?usp=sharing)" | |
| elif version == "2.2": | |
| result, confidence = predict_model_b(image) | |
| advice = generate_disease_advice(result, confidence) | |
| return f"Model B Prediction: {result} (Confidence: {confidence:.1f}%)\n\nExpert Advice:\n{advice}" | |
| elif version == "2.3": | |
| cls_result = predict_classifier(image) | |
| if cls_result != "Tomato Leaf": | |
| return "Classifier: The image is not a tomato leaf. Please try again with a tomato leaf image." | |
| result, confidence = predict_model_b(image) | |
| advice = generate_disease_advice(result, confidence) | |
| return ( | |
| f"Classifier: {cls_result}\n" | |
| f"Model B Prediction: {result} (Confidence: {confidence:.1f}%)\n\n" | |
| f"Expert Advice:\n{advice}\n\n" | |
| f"[View Model B & Classifier Training Notebook](https://colab.research.google.com/drive/1CvoQY40gK2YsMgt4wq9kM2ZSO2c4lzFU?usp=sharing)" | |
| ) | |
| else: | |
| return "Invalid version selected." | |
| # ===== Helper Function to Choose Between Uploaded & Camera Image ===== | |
| def combine_images(uploaded, camera): | |
| return camera if camera is not None else uploaded | |
| # ===== CSS for Theme Switching ===== | |
| light_css = """ | |
| <style> | |
| body { background-color: white; color: black; } | |
| .gr-button { background-color: #4CAF50; color: white; } | |
| .gr-input, .gr-textbox, .gr-dropdown, .gr-radio, .gr-markdown, .gr-container { background-color: white; color: black; } | |
| </style> | |
| """ | |
| dark_css = """ | |
| <style> | |
| body { background-color: #121212 !important; color: #e0e0e0 !important; } | |
| .gr-button { background-color: #555 !important; color: white !important; } | |
| .gr-input, .gr-textbox, .gr-dropdown, .gr-radio, .gr-markdown, .gr-container { background-color: #333 !important; color: #e0e0e0 !important; } | |
| </style> | |
| """ | |
| def update_css(theme): | |
| if theme == "Dark": | |
| return dark_css | |
| else: | |
| return light_css | |
| # ===== Gradio Interface ===== | |
| with gr.Blocks() as demo: | |
| # Hidden element for CSS injection (initially Light theme) | |
| css_injector = gr.HTML(update_css("Light")) | |
| gr.Markdown("# πΏ FarMVi8ioN β AI-powered Crop Monitoring") | |
| gr.Markdown("Detect tomato leaf diseases and get actionable advice on how to curb them.") | |
| with gr.Tabs(): | |
| # === Disease Detection Tab === | |
| with gr.TabItem("Disease Detection"): | |
| with gr.Row(): | |
| # ----- Left Column (β30%) ----- | |
| with gr.Column(scale=1): | |
| version = gr.Dropdown( | |
| choices=["1.1", "1.2", "1.3", "2.1", "2.2", "2.3"], | |
| label="Select Version", | |
| value="1.3", | |
| info="Versions 1.x use Model A; Versions 2.x use Model B." | |
| ) | |
| theme_choice = gr.Radio( | |
| choices=["Light", "Dark"], | |
| label="Select Theme", | |
| value="Light" | |
| ) | |
| gr.Markdown("### Notebook Links") | |
| gr.Markdown( | |
| """ | |
| **For Model A:** | |
| - Model A Only: [Training Notebook](https://colab.research.google.com/drive/1FMjs7JmdO6WVoXbzLA-ymwnIKq-GaV6w?usp=sharing) | |
| - Model A & Classifier: [Training Notebook](https://colab.research.google.com/drive/1CvoQY40gK2YsMgt4wq9kM2ZSO2c4lzFU?usp=sharing) | |
| **For Model B:** | |
| - Model B Only: [Training Notebook](https://colab.research.google.com/drive/1CvoQY40gK2YsMgt4wq9kM2ZSO2c4lzFU?usp=sharing) | |
| - Model B & Classifier: [Training Notebook](https://colab.research.google.com/drive/1CvoQY40gK2YsMgt4wq9kM2ZSO2c4lzFU?usp=sharing) | |
| """ | |
| ) | |
| # ----- Right Column (β70%) ----- | |
| with gr.Column(scale=2): | |
| image_input = gr.Image(label="π Upload Tomato Leaf Image", type="pil") | |
| camera_input = gr.Image(label="πΈ Use Camera (Live Preview)", type="pil", sources=["webcam"]) | |
| submit = gr.Button("π Analyze", variant="primary") | |
| output = gr.Markdown(label="π Diagnosis & Advice") | |
| # === Farmer Chat Tab === | |
| with gr.TabItem("Chat with Farm Assistant"): | |
| gr.Markdown("# π¬ Chat with Farm Assistant") | |
| gr.Markdown("Ask any questions about farming, crop diseases, or agricultural practices.") | |
| chatbot = gr.Chatbot( | |
| label="Chat History", | |
| height=400, | |
| bubble_full_width=False, | |
| show_copy_button=True | |
| ) | |
| with gr.Row(): | |
| chat_input = gr.Textbox( | |
| label="Your Question", | |
| placeholder="Ask about tomato farming, diseases, or agricultural practices...", | |
| lines=2 | |
| ) | |
| chat_button = gr.Button("Send", variant="primary") | |
| gr.Markdown(""" | |
| ### Example Questions: | |
| - How often should I water my tomato plants? | |
| - What's the best fertilizer for tomatoes? | |
| - How do I prevent early blight? | |
| - What are the signs of nutrient deficiency in tomatoes? | |
| """) | |
| # Update CSS dynamically based on theme selection | |
| theme_choice.change(fn=update_css, inputs=theme_choice, outputs=css_injector) | |
| # When submit is clicked, combine image inputs and process the selected version | |
| submit.click( | |
| fn=lambda uploaded, camera, ver: process_version(combine_images(uploaded, camera), ver), | |
| inputs=[image_input, camera_input, version], | |
| outputs=output | |
| ) | |
| # Chat functionality | |
| chat_button.click( | |
| fn=chat_with_farmer, | |
| inputs=[chat_input, chatbot], | |
| outputs=[chat_input, chatbot] | |
| ) | |
| # Also allow pressing Enter to send chat | |
| chat_input.submit( | |
| fn=chat_with_farmer, | |
| inputs=[chat_input, chatbot], | |
| outputs=[chat_input, chatbot] | |
| ) | |
| # Launch the app | |
| demo.launch() | |