|
import streamlit as st |
|
import numpy as np |
|
import cv2 |
|
from PIL import Image |
|
from io import BytesIO |
|
from ultralytics import YOLO |
|
from datetime import datetime |
|
from gtts import gTTS |
|
import tempfile |
|
import os |
|
import base64 |
|
import ollama |
|
import bcrypt |
|
import sqlite3 |
|
import time |
|
from deep_translator import GoogleTranslator |
|
|
|
|
|
|
|
import requests |
|
|
|
|
|
conn = sqlite3.connect('users.db') |
|
c = conn.cursor() |
|
c.execute('''CREATE TABLE IF NOT EXISTS users |
|
(id INTEGER PRIMARY KEY AUTOINCREMENT, |
|
username TEXT UNIQUE, |
|
password_hash TEXT)''') |
|
conn.commit() |
|
|
|
|
|
def hash_password(password): |
|
return bcrypt.hashpw(password.encode(), bcrypt.gensalt()) |
|
|
|
def verify_password(password, hashed_password): |
|
return bcrypt.checkpw(password.encode(), hashed_password) |
|
|
|
|
|
def add_user(username, password): |
|
|
|
c.execute("SELECT id FROM users WHERE username = ?", (username,)) |
|
result = c.fetchone() |
|
|
|
if result: |
|
return False |
|
|
|
|
|
password_hash = hash_password(password) |
|
c.execute("INSERT INTO users (username, password_hash) VALUES (?, ?)", |
|
(username, password_hash)) |
|
conn.commit() |
|
|
|
return True |
|
|
|
|
|
def verify_user(username, password): |
|
c.execute("SELECT password_hash FROM users WHERE username = ?", (username,)) |
|
result = c.fetchone() |
|
if result: |
|
return verify_password(password, result[0]) |
|
return False |
|
|
|
|
|
def login(username, password): |
|
if not username or not password: |
|
st.error("Username and password are required.") |
|
return False |
|
if verify_user(username, password): |
|
st.session_state['authenticated'] = True |
|
st.session_state['username'] = username |
|
st.session_state['last_activity'] = time.time() |
|
return True |
|
st.error("Invalid username or password.") |
|
return False |
|
|
|
def logout(): |
|
st.session_state['authenticated'] = False |
|
st.session_state['username'] = None |
|
|
|
|
|
def local_css(): |
|
st.markdown(""" |
|
<style> |
|
.stButton>button { |
|
width: 100%; |
|
border-radius: 5px; |
|
height: 3em; |
|
margin-top: 10px; |
|
} |
|
|
|
.auth-container { |
|
max-width: 400px; |
|
margin: auto; |
|
padding: 20px; |
|
border-radius: 10px; |
|
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); |
|
background-color: white; |
|
} |
|
|
|
.auth-title { |
|
text-align: center; |
|
font-size: 24px; |
|
margin-bottom: 20px; |
|
color: #1f1f1f; |
|
} |
|
|
|
.auth-subtitle { |
|
text-align: center; |
|
font-size: 16px; |
|
margin-bottom: 20px; |
|
color: #666; |
|
} |
|
|
|
.hero-section { |
|
text-align: center; |
|
padding: 40px 20px; |
|
background: linear-gradient(to right, #4f46e5, #3b82f6); |
|
color: white; |
|
margin-bottom: 30px; |
|
} |
|
|
|
.feature-container { |
|
max-width: 1200px; |
|
margin: auto; |
|
padding: 20px; |
|
display: grid; |
|
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); |
|
gap: 20px; |
|
margin-bottom: 40px; |
|
} |
|
|
|
.feature-card { |
|
background: white; |
|
padding: 20px; |
|
border-radius: 10px; |
|
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); |
|
transition: transform 0.3s ease, box-shadow 0.3s ease; |
|
} |
|
|
|
.feature-card:hover { |
|
transform: scale(1.05); |
|
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2); |
|
} |
|
|
|
.feature-title { |
|
color: #1f1f1f; |
|
font-size: 18px; |
|
margin-bottom: 10px; |
|
font-weight: bold; |
|
} |
|
|
|
.feature-text { |
|
color: #666; |
|
font-size: 14px; |
|
} |
|
|
|
.divider { |
|
text-align: center; |
|
margin: 20px 0; |
|
position: relative; |
|
} |
|
|
|
.divider:before { |
|
content: ""; |
|
position: absolute; |
|
top: 50%; |
|
left: 0; |
|
right: 0; |
|
height: 1px; |
|
background-color: #e0e0e0; |
|
z-index: -1; |
|
} |
|
|
|
.divider span { |
|
background-color: white; |
|
padding: 0 10px; |
|
color: #666; |
|
font-size: 14px; |
|
} |
|
|
|
@keyframes typing { |
|
0% { |
|
width: 0; |
|
} |
|
50% { |
|
width: 100%; |
|
} |
|
60% { |
|
width: 100%; |
|
} |
|
100% { |
|
width: 0; |
|
} |
|
} |
|
|
|
@keyframes blink { |
|
50% { |
|
border-color: transparent; |
|
} |
|
} |
|
|
|
.hero-title{ |
|
display: inline-block; |
|
font-size: 2.5em; |
|
white-space: nowrap; |
|
overflow: hidden; |
|
border-right: 2px solid white; |
|
width: 0; |
|
animation: typing 6s steps(40, end) infinite, blink 0.5s step-end infinite; |
|
} |
|
|
|
.hero-section { |
|
text-align: center; |
|
padding: 40px 20px; |
|
background: linear-gradient(45deg, #4f46e5, #3b82f6); |
|
background-size: 300% 300%; |
|
animation: gradientShift 8s ease infinite; |
|
color: white; |
|
margin-bottom: 30px; |
|
opacity: 0; |
|
animation: fadeIn 2s ease-in-out forwards; |
|
} |
|
|
|
@keyframes fadeIn { |
|
from { |
|
opacity: 0; |
|
} |
|
to { |
|
opacity: 1; |
|
} |
|
} |
|
|
|
@keyframes gradientShift { |
|
0% { |
|
background-position: 0% 50%; |
|
} |
|
50% { |
|
background-position: 100% 50%; |
|
} |
|
100% { |
|
background-position: 0% 50%; |
|
} |
|
} |
|
|
|
/*.feature-container { |
|
display: flex; |
|
justify-content: center; |
|
align-items: center; |
|
gap: 20px; |
|
position: relative; |
|
width: 100%; |
|
height: 300px; |
|
animation: rotate 20s linear infinite; /* Rotate the container */ |
|
} |
|
|
|
.feature-card { |
|
background: white; |
|
padding: 20px; |
|
border-radius: 10px; |
|
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); |
|
transition: transform 0.3s ease, box-shadow 0.3s ease; |
|
flex-shrink: 0; |
|
width: 250px; |
|
} |
|
|
|
.feature-card:hover { |
|
transform: scale(1.1); |
|
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2); |
|
} |
|
|
|
@keyframes rotate { |
|
from { |
|
transform: rotate(0deg); |
|
} |
|
to { |
|
transform: rotate(-360deg); |
|
} |
|
*/} |
|
|
|
/*.feature-container { |
|
display: flex; |
|
justify-content: center; |
|
align-items: center; |
|
overflow: hidden; |
|
position: relative; |
|
width: 100%; |
|
height: 300px; |
|
} |
|
|
|
.feature-track { |
|
display: flex; |
|
animation: circularMove 15s linear infinite; |
|
} |
|
|
|
.feature-card { |
|
flex: 0 0 300px; /* Fixed width for each card */ |
|
margin: 0 20px; |
|
background: white; |
|
color: #333; /* Text color */ |
|
padding: 20px; |
|
border-radius: 10px; |
|
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); |
|
text-align: center; /* Center-align the text */ |
|
overflow: hidden; /* Prevent overflow issues */ |
|
} |
|
|
|
.feature-card h3 { |
|
font-size: 1.2em; |
|
margin-bottom: 10px; |
|
text-align: center; |
|
} |
|
|
|
.feature-card p { |
|
font-size: 0.9em; |
|
line-height: 1.4; |
|
text-align: center; |
|
font-weight: bold; |
|
} |
|
|
|
|
|
.feature-card:hover { |
|
transform: scale(1.1); |
|
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2); |
|
} |
|
|
|
@keyframes circularMove { |
|
0% { |
|
transform: translateX(0); |
|
} |
|
100% { |
|
transform: translateX(-100%); |
|
} |
|
*/} |
|
.feature-container { |
|
display: flex; |
|
justify-content: center; |
|
align-items: center; |
|
height: 400px; |
|
perspective: 1000px; |
|
perspective-origin: 50% 50%; |
|
background: linear-gradient(to bottom, #1e293b, #0f172a); /* Dark blue gradient background */ |
|
overflow: hidden; |
|
position: relative; |
|
padding: 40px 0; |
|
} |
|
|
|
.feature-track { |
|
position: relative; |
|
width: 100%; |
|
height: 100%; |
|
display: flex; |
|
transform-style: preserve-3d; |
|
animation: carousel 15s linear infinite; |
|
} |
|
|
|
.feature-card { |
|
position: absolute; |
|
width: 300px; |
|
padding: 50px; |
|
background: white; |
|
border-radius: 15px; |
|
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3); /* Enhanced shadow for better contrast */ |
|
backface-visibility: hidden; |
|
transform-origin: center center; |
|
transition: all 0.5s ease; |
|
} |
|
|
|
.feature-card h3 { |
|
color: #1e293b; |
|
font-size: 1.5em; |
|
margin-bottom: 1rem; |
|
font-weight: bold; |
|
} |
|
|
|
.feature-card p { |
|
color: #475569; |
|
line-height: 1.6; |
|
} |
|
|
|
/* Position and animate cards */ |
|
.feature-card:nth-child(1) { |
|
transform: rotateY(0deg) translateZ(400px) translateX(0px); |
|
} |
|
|
|
.feature-card:nth-child(2) { |
|
transform: rotateY(60deg) translateZ(400px) translateX(0px); |
|
} |
|
|
|
.feature-card:nth-child(3) { |
|
transform: rotateY(120deg) translateZ(400px) translateX(0px); |
|
} |
|
|
|
.feature-card:nth-child(4) { |
|
transform: rotateY(180deg) translateZ(400px) translateX(0px); |
|
} |
|
|
|
.feature-card:nth-child(5) { |
|
transform: rotateY(240deg) translateZ(400px) translateX(0px); |
|
} |
|
|
|
.feature-card:nth-child(6) { |
|
transform: rotateY(300deg) translateZ(400px) translateX(0px); |
|
} |
|
|
|
@keyframes carousel { |
|
0% { |
|
transform: translateZ(-400px) rotateY(0deg); |
|
} |
|
100% { |
|
transform: translateZ(-400px) rotateY(-360deg); |
|
} |
|
} |
|
|
|
/* Enhanced hover effect with glow */ |
|
.feature-card:hover { |
|
transform: scale(1.1) translateZ(450px); |
|
box-shadow: 0 8px 30px rgba(255, 255, 255, 0.1); /* Glowing effect */ |
|
z-index: 1; |
|
} |
|
|
|
/* Gradient overlays for depth effect */ |
|
.feature-container::before, |
|
.feature-container::after { |
|
content: ''; |
|
position: absolute; |
|
width: 100%; |
|
height: 100px; |
|
z-index: 2; |
|
pointer-events: none; |
|
} |
|
|
|
.feature-container::before { |
|
top: 0; |
|
background: linear-gradient(to bottom, #1e293b, rgba(30, 41, 59, 0)); |
|
} |
|
|
|
.feature-container::after { |
|
bottom: 0; |
|
background: linear-gradient(to top, #1e293b, rgba(30, 41, 59, 0)); |
|
</style> |
|
""", unsafe_allow_html=True) |
|
|
|
|
|
if 'authenticated' in st.session_state and st.session_state['authenticated']: |
|
if time.time() - st.session_state['last_activity'] > 1800: |
|
logout() |
|
st.rerun() |
|
st.session_state['last_activity'] = time.time() |
|
|
|
|
|
if 'show_register_form' not in st.session_state: |
|
st.session_state['show_register_form'] = False |
|
|
|
|
|
if 'authenticated' not in st.session_state or not st.session_state['authenticated']: |
|
local_css() |
|
|
|
|
|
st.markdown(""" |
|
<div class="hero-section"> |
|
<h1 class="hero-title" style="font-size: 2.5em; margin-bottom: 20px;">Crop Disease Detection System</h1> |
|
<p style="font-size: 1.2em; max-width: 800px; margin: 0 auto;"> |
|
An advanced AI-powered system that helps farmers and agricultural experts identify and manage crop diseases effectively |
|
</p> |
|
</div> |
|
|
|
""", unsafe_allow_html=True) |
|
|
|
|
|
st.subheader("Key Features") |
|
col1, col2, col3 = st.columns(3) |
|
|
|
st.markdown(""" |
|
<div class="feature-container"> |
|
<div class="feature-track"> |
|
<div class="feature-card"> |
|
<h3>π Instant Detection</h3> |
|
<p>Upload images of your crops and get immediate disease detection results using state-of-the-art AI technology.</p> |
|
</div> |
|
<div class="feature-card"> |
|
<h3>π‘ Expert Analysis</h3> |
|
<p>Receive detailed analysis and recommendations from our plant pathology expert system.</p> |
|
</div> |
|
<div class="feature-card"> |
|
<h3>π Detailed Reports</h3> |
|
<p>Generate comprehensive reports with treatment recommendations and preventive measures.</p> |
|
</div> |
|
<div class="feature-card"> |
|
<h3>π Instant Detection</h3> |
|
<p>Upload images of your crops and get immediate disease detection results using state-of-the-art AI technology.</p> |
|
</div> |
|
<div class="feature-card"> |
|
<h3>π‘ Expert Analysis</h3> |
|
<p>Receive detailed analysis and recommendations from our plant pathology expert system.</p> |
|
</div> |
|
<div class="feature-card"> |
|
<h3>π Detailed Reports</h3> |
|
<p>Generate comprehensive reports with treatment recommendations and preventive measures.</p> |
|
</div> |
|
</div> |
|
</div> |
|
""", unsafe_allow_html=True) |
|
|
|
|
|
st.markdown(""" |
|
<div class="crop-carousel-container"> |
|
<div class="crop-carousel-track"> |
|
<div class="crop-card"> |
|
<img src="https://github.com/ROBERT-ADDO-ASANTE-DARKO/AI-powered-crop-disease-detection/blob/main/images/b034333ddcc732299d45abf753f3fa71f6ff48ffa3338bfecd615bc2.jpg?raw=true" alt="Crop 1"> |
|
<h4>Corn Leaf Blight</h4> |
|
<p>Corn leaf blight is a fungal disease caused primarily by Exserohilum turcicum (Northern corn leaf blight) and Bipolaris maydis (Southern corn leaf blight).</p> |
|
</div> |
|
<div class="crop-card"> |
|
<img src="https://github.com/ROBERT-ADDO-ASANTE-DARKO/AI-powered-crop-disease-detection/blob/main/images/apple.jpg?raw=true" alt="Crop 2"> |
|
<h4>Apple Scab Leaf</h4> |
|
<p>Apple scab is a fungal disease caused by Venturia inaequalis. It primarily affects apple and crabapple trees.</p> |
|
</div> |
|
<div class="crop-card"> |
|
<img src="https://github.com/ROBERT-ADDO-ASANTE-DARKO/AI-powered-crop-disease-detection/blob/main/images/tomato.jpg?raw=true" alt="Crop 3"> |
|
<h4>Tomato Leaf Late Blight</h4> |
|
<p>Late blight of tomato is caused by the oomycete pathogen Phytophthora infestans. It is characterized by dark, water-soaked lesions on leaves, stems, and fruit.</p> |
|
</div> |
|
<div class="crop-card"> |
|
<img src="https://github.com/ROBERT-ADDO-ASANTE-DARKO/AI-powered-crop-disease-detection/blob/main/images/918d1d7a3dda5ce8fbdabf92e5bf38f104efd129ee09adcc6d1ad46c.jpg?raw=true" alt="Crop 4"> |
|
<h4>Tomato Leaf Yellow Virus</h4> |
|
<p>Tomato leaf yellow virus (often referred to as Tomato yellow leaf curl virus, or TYLCV) is a viral disease transmitted by whiteflies. It causes yellowing and curling of tomato leaves.</p> |
|
</div> |
|
</div> |
|
</div> |
|
""", unsafe_allow_html=True) |
|
|
|
st.markdown(""" |
|
<style> |
|
.crop-carousel-container { |
|
width: 100%; |
|
max-width: 800px; |
|
margin: auto; |
|
overflow: hidden; |
|
position: relative; |
|
} |
|
|
|
.crop-carousel-track { |
|
display: flex; |
|
animation: moveLeft 20s linear infinite; /* Move right to left */ |
|
} |
|
|
|
.crop-card { |
|
flex: 0 0 300px; |
|
margin: 0 20px; |
|
background: white; |
|
color: #333; |
|
padding: 20px; |
|
border-radius: 10px; |
|
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); |
|
text-align: center; |
|
overflow: hidden; |
|
} |
|
|
|
.crop-card img { |
|
width: 100%; |
|
height: 150px; |
|
object-fit: cover; |
|
border-radius: 10px; |
|
margin-bottom: 10px; |
|
} |
|
|
|
.crop-card h4 { |
|
font-size: 1.2em; |
|
margin: 10px 0; |
|
} |
|
|
|
.crop-card p { |
|
font-size: 0.9em; |
|
line-height: 1.4; |
|
color: #555; |
|
} |
|
|
|
@keyframes moveLeft { |
|
0% { |
|
transform: translateX(100%); |
|
} |
|
100% { |
|
transform: translateX(-100%); |
|
} |
|
} |
|
</style> |
|
""", unsafe_allow_html=True) |
|
|
|
|
|
|
|
st.markdown("<br>", unsafe_allow_html=True) |
|
|
|
|
|
st.markdown('<div class="auth-container">', unsafe_allow_html=True) |
|
|
|
|
|
if 'show_reset_form' not in st.session_state: |
|
st.session_state['show_reset_form'] = False |
|
|
|
|
|
def update_password(username, new_password): |
|
conn = sqlite3.connect('users.db') |
|
c = conn.cursor() |
|
|
|
|
|
c.execute("SELECT id FROM users WHERE username = ?", (username,)) |
|
if not c.fetchone(): |
|
return False |
|
|
|
|
|
password_hash = bcrypt.hashpw(new_password.encode(), bcrypt.gensalt()) |
|
c.execute("UPDATE users SET password_hash = ? WHERE username = ?", |
|
(password_hash, username)) |
|
conn.commit() |
|
conn.close() |
|
return True |
|
|
|
|
|
|
|
if not st.session_state.get('authenticated', False): |
|
st.markdown('<div class="auth-container">', unsafe_allow_html=True) |
|
|
|
|
|
if st.session_state.get('show_reset_form', False): |
|
st.markdown('<h1 class="auth-title">Reset Password</h1>', unsafe_allow_html=True) |
|
st.markdown('<p class="auth-subtitle">Enter your username and new password</p>', unsafe_allow_html=True) |
|
|
|
with st.form("reset_form"): |
|
username = st.text_input("Username") |
|
new_password = st.text_input("New Password", type="password") |
|
confirm_password = st.text_input("Confirm Password", type="password") |
|
submit = st.form_submit_button("Reset Password") |
|
|
|
if submit: |
|
if not username or not new_password or not confirm_password: |
|
st.error("All fields are required.") |
|
elif new_password != confirm_password: |
|
st.error("Passwords do not match.") |
|
elif update_password(username, new_password): |
|
st.success("Password updated successfully!") |
|
st.session_state['show_reset_form'] = False |
|
time.sleep(1) |
|
st.rerun() |
|
else: |
|
st.error("Username not found.") |
|
|
|
if st.button("Back to Login"): |
|
st.session_state['show_reset_form'] = False |
|
st.rerun() |
|
|
|
|
|
elif st.session_state.get('show_register_form', False): |
|
st.markdown('<h1 class="auth-title">Create Account</h1>', unsafe_allow_html=True) |
|
st.markdown('<p class="auth-subtitle">Sign up to get started</p>', unsafe_allow_html=True) |
|
|
|
with st.form("register_form"): |
|
new_username = st.text_input("Username") |
|
new_password = st.text_input("Password", type="password") |
|
submit_button = st.form_submit_button("Create Account") |
|
|
|
if submit_button: |
|
if new_username and new_password: |
|
if add_user(new_username, new_password): |
|
st.success("Account created successfully!") |
|
st.session_state['show_register_form'] = False |
|
time.sleep(1) |
|
st.rerun() |
|
else: |
|
st.error("Username already exists.") |
|
else: |
|
st.error("Username and password are required.") |
|
|
|
st.markdown('<div class="divider"><span>OR</span></div>', unsafe_allow_html=True) |
|
if st.button("Back to Login"): |
|
st.session_state['show_register_form'] = False |
|
st.rerun() |
|
|
|
|
|
else: |
|
st.markdown('<h1 class="auth-title">Welcome Back</h1>', unsafe_allow_html=True) |
|
st.markdown('<p class="auth-subtitle">Sign in to your account</p>', unsafe_allow_html=True) |
|
|
|
with st.form("login_form"): |
|
username = st.text_input("Username") |
|
password = st.text_input("Password", type="password") |
|
cols = st.columns([1, 1]) |
|
submit_button = cols[0].form_submit_button("Sign In") |
|
forgot_password = cols[1].form_submit_button("Forgot Password?") |
|
|
|
if submit_button: |
|
if login(username, password): |
|
st.success("Logged in successfully!") |
|
time.sleep(1) |
|
st.rerun() |
|
elif forgot_password: |
|
st.session_state['show_reset_form'] = True |
|
st.rerun() |
|
|
|
st.markdown('<div class="divider"><span>OR</span></div>', unsafe_allow_html=True) |
|
if st.button("Create New Account"): |
|
st.session_state['show_register_form'] = True |
|
st.rerun() |
|
|
|
st.markdown('</div>', unsafe_allow_html=True) |
|
|
|
|
|
st.markdown(""" |
|
<div style="background: linear-gradient(to right, #1e293b, #334155); color: white; padding: 40px 0; margin-top: 40px;"> |
|
<div style="max-width: 1200px; margin: auto; padding: 0 20px;"> |
|
<div style="display: flex; flex-wrap: wrap; justify-content: space-between; gap: 40px;"> |
|
<!-- About Section --> |
|
<div style="flex: 1; min-width: 250px;"> |
|
<h3 style="color: #60a5fa; font-size: 1.5em; margin-bottom: 20px;">About Our Platform</h3> |
|
<p style="color: #e2e8f0; line-height: 1.6; margin-bottom: 20px;"> |
|
Our AI-powered platform revolutionizes crop disease detection and management. |
|
We combine cutting-edge technology with agricultural expertise to protect your crops |
|
and maximize your yield. |
|
</p> |
|
</div> |
|
<div style="flex: 1; min-width: 250px;"> |
|
<h3 style="color: #60a5fa; font-size: 1.5em; margin-bottom: 20px;">Key Features</h3> |
|
<ul style="list-style: none; padding: 0; color: #e2e8f0;"> |
|
<li style="margin-bottom: 10px; display: flex; align-items: center;"> |
|
<span style="color: #60a5fa; margin-right: 10px;">β</span> Real-time Disease Detection |
|
</li> |
|
<li style="margin-bottom: 10px; display: flex; align-items: center;"> |
|
<span style="color: #60a5fa; margin-right: 10px;">β</span> Multi-language Support |
|
</li> |
|
<li style="margin-bottom: 10px; display: flex; align-items: center;"> |
|
<span style="color: #60a5fa; margin-right: 10px;">β</span> Expert Analysis Reports |
|
</li> |
|
<li style="margin-bottom: 10px; display: flex; align-items: center;"> |
|
<span style="color: #60a5fa; margin-right: 10px;">β</span> Treatment Recommendations |
|
</li> |
|
</ul> |
|
</div> |
|
<div style="flex: 1; min-width: 250px;"> |
|
<h3 style="color: #60a5fa; font-size: 1.5em; margin-bottom: 20px;">Contact Us</h3> |
|
<p style="color: #e2e8f0; line-height: 1.6; margin-bottom: 10px;"> |
|
<span style="color: #60a5fa;">Email:</span> [email protected] |
|
</p> |
|
<p style="color: #e2e8f0; line-height: 1.6; margin-bottom: 20px;"> |
|
<span style="color: #60a5fa;">Phone:</span> +1 (234) 567-8900 |
|
</p> |
|
<div style="display: flex; gap: 15px; margin-top: 20px;"> |
|
<a href="#" style="color: #60a5fa; text-decoration: none; font-size: 1.2em;"> |
|
<span>π±</span> |
|
</a> |
|
<a href="#" style="color: #60a5fa; text-decoration: none; font-size: 1.2em;"> |
|
<span>π¬</span> |
|
</a> |
|
<a href="#" style="color: #60a5fa; text-decoration: none; font-size: 1.2em;"> |
|
<span>π¨</span> |
|
</a> |
|
</div> |
|
</div> |
|
</div> |
|
<div style="border-top: 1px solid #4b5563; margin-top: 40px; padding-top: 20px; text-align: center;"> |
|
<p style="color: #e2e8f0; font-size: 0.9em;"> |
|
Β© 2025 Crop Disease Detection System. All rights reserved. |
|
</p> |
|
<div style="margin-top: 10px;"> |
|
<a href="#" style="color: #e2e8f0; text-decoration: none; margin: 0 10px; font-size: 0.9em;">Privacy Policy</a> |
|
<a href="#" style="color: #e2e8f0; text-decoration: none; margin: 0 10px; font-size: 0.9em;">Terms of Service</a> |
|
<a href="#" style="color: #e2e8f0; text-decoration: none; margin: 0 10px; font-size: 0.9em;">FAQ</a> |
|
</div> |
|
</div> |
|
</div> |
|
</div> |
|
""", unsafe_allow_html=True) |
|
|
|
st.stop() |
|
|
|
|
|
def setup_feedback_db(): |
|
conn = sqlite3.connect('customer_feedback.db') |
|
c = conn.cursor() |
|
c.execute('''CREATE TABLE IF NOT EXISTS customer_feedback |
|
(id INTEGER PRIMARY KEY AUTOINCREMENT, |
|
question TEXT, |
|
response TEXT, |
|
feedback_type TEXT, |
|
comment_type TEXT, |
|
custom_comment TEXT, |
|
timestamp DATETIME DEFAULT CURRENT_TIMESTAMP)''') |
|
conn.commit() |
|
return conn, c |
|
|
|
def save_feedback(question, response, feedback_type, comment_type=None, custom_comment=None): |
|
conn, c = setup_feedback_db() |
|
try: |
|
c.execute("""INSERT INTO customer_feedback |
|
(question, response, feedback_type, comment_type, custom_comment) |
|
VALUES (?, ?, ?, ?, ?)""", |
|
(question, response, feedback_type, comment_type, custom_comment)) |
|
conn.commit() |
|
return True |
|
except Exception as e: |
|
st.error(f"Error saving feedback: {e}") |
|
return False |
|
finally: |
|
conn.close() |
|
|
|
|
|
def display_feedback_buttons(file_id, index, question, response): |
|
|
|
SUGGESTED_COMMENTS = [ |
|
"Inaccurate information", |
|
"Unclear explanation", |
|
"Missing details", |
|
"Not relevant to question", |
|
"Technical error", |
|
"Other" |
|
] |
|
|
|
|
|
if f"feedback_{file_id}_{index}" not in st.session_state: |
|
st.session_state[f"feedback_{file_id}_{index}"] = { |
|
"feedback_type": None, |
|
"comment": None, |
|
"submitted": False |
|
} |
|
|
|
col1, col2 = st.columns([1, 4]) |
|
with col1: |
|
if st.button("π", key=f"helpful_{file_id}_{index}"): |
|
|
|
save_feedback(question, response, "π") |
|
st.success("Feedback saved!") |
|
|
|
st.session_state[f"feedback_{file_id}_{index}"]["submitted"] = True |
|
return |
|
|
|
with col2: |
|
if st.button("π", key=f"not_helpful_{file_id}_{index}"): |
|
|
|
st.session_state[f"feedback_{file_id}_{index}"]["feedback_type"] = "π" |
|
|
|
|
|
if st.session_state[f"feedback_{file_id}_{index}"].get("feedback_type") == "π": |
|
|
|
selected_comment = st.selectbox( |
|
"What was the issue?", |
|
options=SUGGESTED_COMMENTS, |
|
key=f"suggested_comment_{file_id}_{index}" |
|
) |
|
|
|
|
|
custom_comment = None |
|
if selected_comment == "Other": |
|
custom_comment = st.text_area( |
|
"Please describe the issue:", |
|
key=f"custom_comment_{file_id}_{index}" |
|
) |
|
|
|
|
|
if st.button("Submit Feedback", key=f"submit_{file_id}_{index}"): |
|
|
|
save_feedback( |
|
question, |
|
response, |
|
st.session_state[f"feedback_{file_id}_{index}"]["feedback_type"], |
|
custom_comment if selected_comment == "Other" else selected_comment |
|
) |
|
st.success("Thank you for your feedback!") |
|
|
|
st.session_state[f"feedback_{file_id}_{index}"]["submitted"] = True |
|
return |
|
|
|
|
|
SUPPORTED_MODELS = { |
|
"llama3.2": { |
|
"name": "llama3.2", |
|
"system_prompt": "You are a helpful plant pathology expert assistant.", |
|
"supports_vision": False |
|
}, |
|
"llama3.1": { |
|
"name": "llama3.1", |
|
"system_prompt": "You are a helpful plant pathology expert assistant.", |
|
"supports_vision": False |
|
}, |
|
"llama2": { |
|
"name": "llama2", |
|
"system_prompt": "You are a helpful plant pathology expert assistant.", |
|
"supports_vision": False |
|
}, |
|
"llava": { |
|
"name": "llava", |
|
"system_prompt": "You are a helpful plant pathology expert assistant with vision capabilities.", |
|
"supports_vision": True, |
|
"vision_prompt": "Analyze the image and describe the diseases present." |
|
}, |
|
"mistral": { |
|
"name": "mistral", |
|
"system_prompt": "You are a helpful plant pathology expert assistant.", |
|
"supports_vision": False |
|
}, |
|
"gemma": { |
|
"name": "gemma", |
|
"system_prompt": "You are a helpful plant pathology expert assistant.", |
|
"supports_vision": False |
|
}, |
|
"jyan1/paligemma-mix-224": { |
|
"name": "jyan1/paligemma-mix-224", |
|
"system_prompt": "You are a helpful plant pathology expert assistant.", |
|
"supports_vision": True |
|
} |
|
} |
|
|
|
|
|
if 'conversation_history' not in st.session_state: |
|
st.session_state.conversation_history = {} |
|
|
|
|
|
yolo_model = YOLO("models/best.pt") |
|
|
|
def preprocess_image(image, target_size=(224, 224)): |
|
""" |
|
Preprocess the image for vision-capable models. |
|
""" |
|
image = Image.fromarray(image) |
|
image = image.resize(target_size) |
|
return image |
|
|
|
def text_to_speech(text, language='en'): |
|
"""Convert text to speech using gTTS""" |
|
try: |
|
|
|
with tempfile.NamedTemporaryFile(delete=False, suffix=".mp3") as temp_audio: |
|
|
|
tts = gTTS(text=text, lang=language, slow=False) |
|
tts.save(temp_audio.name) |
|
|
|
|
|
with open(temp_audio.name, 'rb') as audio_file: |
|
audio_bytes = audio_file.read() |
|
|
|
|
|
os.unlink(temp_audio.name) |
|
|
|
return audio_bytes |
|
except Exception as e: |
|
st.error(f"Error generating speech: {str(e)}") |
|
return None |
|
|
|
def check_ollama_connection(): |
|
try: |
|
response = requests.get("http://localhost:11434") |
|
return response.status_code == 200 |
|
except Exception as e: |
|
return False |
|
|
|
def generate_ollama_response(prompt, model_name="llama2", conversation_history=None, image_data=None): |
|
try: |
|
if model_name not in SUPPORTED_MODELS: |
|
return f"Error: Model {model_name} is not supported." |
|
|
|
model_config = SUPPORTED_MODELS[model_name] |
|
|
|
|
|
messages = [ |
|
{ |
|
"role": "system", |
|
"content": model_config["system_prompt"] |
|
} |
|
] |
|
|
|
|
|
if conversation_history: |
|
for entry in conversation_history: |
|
if len(entry) >= 2: |
|
question, response = entry[:2] |
|
messages.extend([ |
|
{"role": "user", "content": question}, |
|
{"role": "assistant", "content": response} |
|
]) |
|
|
|
|
|
if model_config["supports_vision"] and image_data is not None: |
|
if isinstance(image_data, np.ndarray): |
|
image = Image.fromarray(image_data) |
|
buffered = BytesIO() |
|
image.save(buffered, format="JPEG") |
|
img_str = base64.b64encode(buffered.getvalue()).decode() |
|
|
|
messages.append({ |
|
"role": "user", |
|
"content": [ |
|
{"type": "text", "text": prompt}, |
|
{"type": "image", "image": img_str} |
|
] |
|
}) |
|
else: |
|
messages.append({ |
|
"role": "user", |
|
"content": prompt |
|
}) |
|
|
|
|
|
api_url = "http://localhost:11434/api/generate" |
|
payload = { |
|
"model": model_config["name"], |
|
"prompt": prompt, |
|
"stream": False |
|
} |
|
|
|
|
|
response = requests.post(api_url, json=payload) |
|
|
|
|
|
if response.status_code != 200: |
|
return f"Error: API request failed with status code {response.status_code}. Response: {response.text}" |
|
|
|
|
|
response_data = response.json() |
|
|
|
|
|
if "response" in response_data: |
|
return response_data["response"] |
|
else: |
|
return f"Error: Unexpected response format: {response_data}" |
|
|
|
except Exception as e: |
|
return f"Error connecting to Ollama API: {str(e)}" |
|
|
|
def generate_improved_description(detected_classes, class_names, user_text, image_details=None, conversation_history=None): |
|
""" |
|
Generate a more detailed and contextual description using Ollama |
|
""" |
|
detected_objects = [class_names[cls] for cls in detected_classes] |
|
|
|
|
|
disease_context = f"Detected diseases: {', '.join(detected_objects)}" |
|
|
|
|
|
if not conversation_history: |
|
base_prompt = f"""As an expert plant pathologist, analyze the following crop diseases detected in the image: {', '.join(detected_objects)}. |
|
|
|
For each detected disease, provide a structured analysis following this format: |
|
|
|
1. Disease Name: [Name] |
|
- Pathogen: [Causative organism] |
|
- Severity Level: [Based on visual symptoms] |
|
- Key Symptoms: |
|
* [Symptom 1] |
|
* [Symptom 2] |
|
- Economic Impact: |
|
* [Brief description of potential crop losses] |
|
- Treatment Options: |
|
* Immediate actions: [Short-term solutions] |
|
* Long-term management: [Preventive measures] |
|
- Environmental Conditions: |
|
* Favorable conditions for disease development |
|
* Risk factors |
|
|
|
2. Recommendations: |
|
- Immediate Steps: |
|
* [Action items for immediate control] |
|
- Prevention Strategy: |
|
* [Long-term prevention measures] |
|
- Monitoring Protocol: |
|
* [What to watch for] |
|
|
|
Initial Question/Context: {user_text if user_text else "Provide a general analysis"} |
|
""" |
|
else: |
|
base_prompt = f"""Context: {disease_context} |
|
|
|
Previous conversation context has been provided above. Please address the following follow-up question while maintaining consistency with previous responses: |
|
|
|
{user_text} |
|
|
|
Provide a detailed response that builds upon the previous context and specifically addresses this question.""" |
|
|
|
|
|
selected_model = st.session_state.get('selected_model', 'llama2') |
|
|
|
return generate_ollama_response( |
|
base_prompt, |
|
model_name=selected_model, |
|
conversation_history=conversation_history, |
|
image_data=image_details.get("image_data") if image_details else None |
|
) |
|
|
|
def inference(image): |
|
""" |
|
Enhanced inference function with confidence scores and bounding box information |
|
""" |
|
results = yolo_model(image, conf=0.4) |
|
infer = np.zeros(image.shape, dtype=np.uint8) |
|
classes = dict() |
|
names_infer = [] |
|
confidence_scores = [] |
|
bounding_boxes = [] |
|
|
|
for r in results: |
|
infer = r.plot() |
|
classes = r.names |
|
names_infer = r.boxes.cls.tolist() |
|
confidence_scores = r.boxes.conf.tolist() |
|
bounding_boxes = r.boxes.xyxy.tolist() |
|
|
|
return infer, names_infer, classes, confidence_scores, bounding_boxes |
|
|
|
|
|
st.title("Interactive Crop Disease Detection and AnalysisπΎπΏπ₯¬βοΈ") |
|
st.write(f"Welcome, {st.session_state['username']}!π") |
|
|
|
|
|
if st.button("Logout"): |
|
logout() |
|
st.rerun() |
|
|
|
|
|
with st.sidebar: |
|
st.header("Settings") |
|
selected_model = st.selectbox( |
|
"Select LLM Model", |
|
list(SUPPORTED_MODELS.keys()), |
|
index=0, |
|
help="Choose the Ollama model to use for analysis" |
|
) |
|
|
|
st.session_state['selected_model'] = selected_model |
|
|
|
if SUPPORTED_MODELS[selected_model]["supports_vision"]: |
|
st.info("This model supports vision capabilities and can analyze images directly.") |
|
|
|
confidence_threshold = st.slider("Detection Confidence Threshold", 0.0, 1.0, 0.4) |
|
show_confidence = st.checkbox("Show Confidence Scores", value=True) |
|
show_bbox = st.checkbox("Show Bounding Boxes", value=True) |
|
|
|
|
|
st.header("Text-to-Speech Settings") |
|
tts_enabled = st.checkbox("Enable Text-to-Speech", value=True) |
|
if tts_enabled: |
|
language = st.selectbox("Speech Language", |
|
options=['en', 'es', 'fr', 'de'], |
|
format_func=lambda x: { |
|
'en': 'English', |
|
'es': 'Spanish', |
|
'fr': 'French', |
|
'de': 'German' |
|
}[x], |
|
help="Select speech language") |
|
|
|
|
|
if st.button("Clear All Conversations"): |
|
st.session_state.conversation_history = {} |
|
st.success("Conversation history cleared!") |
|
|
|
|
|
language = st.selectbox( |
|
"Select Language", |
|
options=['en', 'es', 'fr', 'de'], |
|
format_func=lambda x: { |
|
'en': 'English', |
|
'es': 'Spanish', |
|
'fr': 'French', |
|
'de': 'German' |
|
}[x], |
|
help="Select your preferred language" |
|
) |
|
|
|
|
|
uploaded_files = st.file_uploader("Upload images for disease detection", type=["jpg", "jpeg", "png"], accept_multiple_files=True) |
|
|
|
if uploaded_files: |
|
for uploaded_file in uploaded_files: |
|
file_id = uploaded_file.name |
|
|
|
|
|
if file_id not in st.session_state.conversation_history: |
|
st.session_state.conversation_history[file_id] = [] |
|
|
|
st.header(f"Analysis for {file_id}") |
|
|
|
|
|
col1, col2 = st.columns(2) |
|
|
|
|
|
file_bytes = np.asarray(bytearray(uploaded_file.read()), dtype=np.uint8) |
|
image = cv2.imdecode(file_bytes, 1) |
|
image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB) |
|
|
|
|
|
with col1: |
|
st.subheader("Original Image") |
|
st.image(image, use_container_width=True) |
|
|
|
|
|
with st.spinner("Processing image..."): |
|
infer_image, classes_in_image, classes_in_dataset, confidences, boxes = inference(image) |
|
|
|
with col2: |
|
st.subheader("Detected Diseases") |
|
st.image(infer_image, use_container_width=True) |
|
|
|
|
|
if show_confidence: |
|
st.subheader("Detection Details") |
|
for cls, conf in zip(classes_in_image, confidences): |
|
st.write(f"- {classes_in_dataset[cls]}: {conf:.2%} confidence") |
|
|
|
|
|
if st.session_state.conversation_history[file_id]: |
|
st.subheader("Conversation History") |
|
for i, entry in enumerate(st.session_state.conversation_history[file_id]): |
|
question, response = entry[:2] |
|
|
|
with st.expander(f"Q{i+1}: {question[:50]}...", expanded=False): |
|
st.write("**Question:**", question) |
|
st.write("**Response:**", response) |
|
|
|
|
|
display_feedback_buttons(file_id, i, question, response) |
|
|
|
|
|
if tts_enabled: |
|
if st.button("π Listen", key=f"listen_history_{file_id}_{i}"): |
|
with st.spinner("Generating audio..."): |
|
audio_bytes = text_to_speech(response, language) |
|
if audio_bytes: |
|
st.audio(audio_bytes, format='audio/mp3') |
|
|
|
|
|
|
|
st.subheader("Ask Questions") |
|
user_text = st.text_area( |
|
"Enter your question about the detected diseases:", |
|
placeholder="Example: What are the best treatment options for these diseases? What preventive measures should I take?", |
|
key=f"question_{file_id}" |
|
) |
|
|
|
def translate_text(text, target_lang='en'): |
|
translator = GoogleTranslator(source='auto', target=target_lang) |
|
return translator.translate(text) |
|
|
|
|
|
if st.button("Get Analysis", key=f"analyze_{file_id}"): |
|
with st.spinner(f"Generating analysis using {selected_model}..."): |
|
|
|
translated_input = translate_text(user_text, target_lang='en') |
|
st.write(f"Translated Input (to English): {translated_input}") |
|
|
|
|
|
image_details = { |
|
"confidence_scores": confidences, |
|
"bounding_boxes": boxes, |
|
"image_dimensions": image.shape, |
|
"image_data": image |
|
} |
|
|
|
|
|
response = generate_improved_description( |
|
classes_in_image, |
|
classes_in_dataset, |
|
translated_input, |
|
image_details, |
|
st.session_state.conversation_history[file_id] |
|
) |
|
|
|
|
|
translated_response = translate_text(response, target_lang=language) |
|
|
|
|
|
st.session_state.conversation_history[file_id].append((user_text, translated_response, None)) |
|
st.markdown("### Latest Response") |
|
st.markdown(translated_response) |
|
|
|
|
|
if tts_enabled: |
|
col1, col2 = st.columns([1, 4]) |
|
with col1: |
|
if st.button("π Listen", key=f"listen_latest_{file_id}"): |
|
with st.spinner("Generating audio..."): |
|
audio_bytes = text_to_speech(translated_response, language) |
|
if audio_bytes: |
|
st.audio(audio_bytes, format='audio/mp3') |
|
|
|
|
|
if st.button("Export Conversation", key=f"export_{file_id}"): |
|
conversation_text = f""" |
|
# Crop Disease Analysis Report |
|
|
|
## Image Information |
|
- Filename: {file_id} |
|
- Analysis Date: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')} |
|
|
|
## Detected Diseases |
|
{', '.join([classes_in_dataset[cls] for cls in classes_in_image])} |
|
|
|
## Conversation History |
|
""" |
|
|
|
for i, entry in enumerate(st.session_state.conversation_history[file_id]): |
|
if len(entry) == 2: |
|
question, response = entry |
|
feedback = "No feedback" |
|
else: |
|
question, response, feedback = entry |
|
|
|
conversation_text += f"\n### Question {i+1}:\n{question}\n\n### Answer {i+1}:\n{response}\n\n### Feedback {i+1}:\n{feedback}\n" |
|
|
|
st.download_button( |
|
label="Download Conversation", |
|
data=conversation_text, |
|
file_name=f"disease_analysis_{file_id}.md", |
|
mime="text/markdown" |
|
) |
|
|
|
|
|
st.markdown(""" |
|
--- |
|
### How to Use |
|
1. Upload one or more images of crops with potential diseases |
|
2. View the detected diseases and their confidence scores |
|
3. Ask questions about the diseases, treatments, or prevention |
|
4. Use the π Listen button to hear the responses |
|
5. View previous questions and answers in the conversation history |
|
6. Export the entire conversation for future reference |
|
7. Use the sidebar to adjust settings or clear conversation history |
|
""") |