Spaces:
Sleeping
Sleeping
| import os | |
| import gradio as gr | |
| import numpy as np | |
| import requests | |
| import json | |
| from dotenv import load_dotenv | |
| from tensorflow.keras.models import load_model | |
| from PIL import Image | |
| # Load environment variables | |
| load_dotenv() | |
| # ===== Groq API Key ===== | |
| GROQ_API_KEY = os.getenv("GROQ_API_KEY", "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") | |
| GROQ_MODEL = "llama3-70b-8192" # Using Llama 3 70B model | |
| print(f"Groq API key available: {'Yes' if GROQ_API_KEY else 'No'}") | |
| # ===== Fallback to Hugging Face API Token ===== | |
| HF_API_TOKEN = os.getenv("HF_API_TOKEN") | |
| print(f"HF API token available: {'Yes' if HF_API_TOKEN else 'No'}") | |
| # ===== 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") | |
| # ===== Disease Information Database (fallback if API fails) ===== | |
| 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" | |
| ] | |
| } | |
| } | |
| # ===== 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" | |
| # ===== Groq API Call ===== | |
| def call_groq_api(prompt): | |
| """Call Groq API for detailed disease analysis and advice""" | |
| print(f"Calling Groq API with prompt: {prompt[:50]}...") | |
| headers = { | |
| "Authorization": f"Bearer {GROQ_API_KEY}", | |
| "Content-Type": "application/json" | |
| } | |
| payload = { | |
| "model": GROQ_MODEL, | |
| "messages": [ | |
| {"role": "system", "content": "You are an expert agricultural advisor specializing in tomato farming and plant diseases."}, | |
| {"role": "user", "content": prompt} | |
| ], | |
| "max_tokens": 800, | |
| "temperature": 0.7 | |
| } | |
| try: | |
| response = requests.post( | |
| "https://api.groq.com/openai/v1/chat/completions", | |
| headers=headers, | |
| json=payload, | |
| timeout=30 | |
| ) | |
| print(f"Groq API response status: {response.status_code}") | |
| if response.status_code == 200: | |
| result = response.json() | |
| if "choices" in result and len(result["choices"]) > 0: | |
| content = result["choices"][0]["message"]["content"] | |
| print(f"Groq API response received: {len(content)} characters") | |
| return content | |
| print(f"Groq API error: {response.status_code} - {response.text}") | |
| return None | |
| except Exception as e: | |
| print(f"Error with Groq API: {str(e)}") | |
| return None | |
| # ===== Fallback to Hugging Face API ===== | |
| def call_hf_api(prompt, model_id="mistralai/Mistral-7B-Instruct-v0.2"): | |
| """Call Hugging Face API as fallback""" | |
| if not HF_API_TOKEN: | |
| return None | |
| 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": 500, | |
| "temperature": 0.7, | |
| "top_p": 0.95, | |
| "do_sample": True | |
| } | |
| } | |
| url = f"https://api-inference.huggingface.co/models/{model_id}" | |
| try: | |
| 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 | |
| return None | |
| except Exception as e: | |
| print(f"Error with Hugging Face API: {str(e)}") | |
| return None | |
| # ===== AI Assistant Functions ===== | |
| def ai_assistant_v1(image, prediction, confidence): | |
| """Use Groq API for Model A versions""" | |
| if "healthy" in prediction.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: {prediction} 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." | |
| ) | |
| # Call Groq API | |
| response = call_groq_api(prompt) | |
| # If Groq API fails, try Hugging Face API | |
| if not response: | |
| response = call_hf_api(prompt) | |
| # If both APIs fail, use fallback information | |
| if not response: | |
| # Get fallback information from our database | |
| info = disease_info.get(prediction, { | |
| "description": "Information not available for this disease.", | |
| "causes": "Unknown causes.", | |
| "treatment": ["Consult with a local agricultural extension service."] | |
| }) | |
| response = f""" | |
| # {prediction} | |
| ## Description | |
| {info.get('description', 'No description available.')} | |
| ## Causes | |
| {info.get('causes', 'Information not available.')} | |
| ## Recommended Treatment | |
| {chr(10).join(f"- {rec}" for rec in info.get('treatment', ['No specific treatment information available.']))} | |
| *Note: This is fallback information. For more detailed advice, please try again later when the AI service is available.* | |
| """ | |
| return response | |
| def ai_assistant_v2(image, prediction, confidence): | |
| """Use Groq API for Model B versions""" | |
| if "healthy" in prediction.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: {prediction} 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." | |
| ) | |
| # Call Groq API | |
| response = call_groq_api(prompt) | |
| # If Groq API fails, try Hugging Face API | |
| if not response: | |
| response = call_hf_api(prompt) | |
| # If both APIs fail, use fallback information | |
| if not response: | |
| # Get fallback information from our database | |
| info = disease_info.get(prediction, { | |
| "description": "Information not available for this disease.", | |
| "causes": "Unknown causes.", | |
| "treatment": ["Consult with a local agricultural extension service."] | |
| }) | |
| response = f""" | |
| # {prediction} | |
| ## Description | |
| {info.get('description', 'No description available.')} | |
| ## Causes | |
| {info.get('causes', 'Information not available.')} | |
| ## Recommended Treatment | |
| {chr(10).join(f"- {rec}" for rec in info.get('treatment', ['No specific treatment information available.']))} | |
| *Note: This is fallback information. For more detailed advice, please try again later when the AI service is available.* | |
| """ | |
| return response | |
| # ===== 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 = ai_assistant_v1(image, 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 = ai_assistant_v1(image, 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 = ai_assistant_v2(image, 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 = ai_assistant_v2(image, 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.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", sources=["upload", "webcam", "clipboard"]) | |
| submit = gr.Button("π Analyze", variant="primary") | |
| output = gr.Markdown(label="π Diagnosis & Advice") | |
| # 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 img, ver: process_version(img, ver), | |
| inputs=[image_input, version], | |
| outputs=output | |
| ) | |
| demo.launch() | |