Berzelius255 commited on
Commit
fbedb17
·
verified ·
1 Parent(s): f54b8cd

Uploaded 3 files

Browse files
Files changed (3) hide show
  1. app.py +1323 -0
  2. startup.sh +27 -0
  3. supervisord.conf +16 -0
app.py ADDED
@@ -0,0 +1,1323 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ import numpy as np
3
+ import cv2
4
+ from PIL import Image
5
+ from io import BytesIO
6
+ from ultralytics import YOLO
7
+ from datetime import datetime
8
+ from gtts import gTTS
9
+ import tempfile
10
+ import os
11
+ import base64
12
+ import ollama
13
+ import bcrypt
14
+ import sqlite3
15
+ import time
16
+ from deep_translator import GoogleTranslator
17
+ #from transformers import AutoTokenizer, AutoModelForCausalLM, AutoImageProcessor, pipeline
18
+ #import torch
19
+ #from huggingface_hub import from_pretrained_keras
20
+ import requests
21
+
22
+ # Database setup
23
+ conn = sqlite3.connect('users.db')
24
+ c = conn.cursor()
25
+ c.execute('''CREATE TABLE IF NOT EXISTS users
26
+ (id INTEGER PRIMARY KEY AUTOINCREMENT,
27
+ username TEXT UNIQUE,
28
+ password_hash TEXT)''')
29
+ conn.commit()
30
+
31
+ # Password hashing and verification
32
+ def hash_password(password):
33
+ return bcrypt.hashpw(password.encode(), bcrypt.gensalt())
34
+
35
+ def verify_password(password, hashed_password):
36
+ return bcrypt.checkpw(password.encode(), hashed_password)
37
+
38
+ # Add a user
39
+ def add_user(username, password):
40
+ # Check if username already exists
41
+ c.execute("SELECT id FROM users WHERE username = ?", (username,))
42
+ result = c.fetchone()
43
+
44
+ if result:
45
+ return False # Username already exists
46
+
47
+ # Hash the password and insert the new user
48
+ password_hash = hash_password(password)
49
+ c.execute("INSERT INTO users (username, password_hash) VALUES (?, ?)",
50
+ (username, password_hash))
51
+ conn.commit()
52
+
53
+ return True
54
+
55
+ # Verify a user
56
+ def verify_user(username, password):
57
+ c.execute("SELECT password_hash FROM users WHERE username = ?", (username,))
58
+ result = c.fetchone()
59
+ if result:
60
+ return verify_password(password, result[0])
61
+ return False
62
+
63
+ # Login and logout
64
+ def login(username, password):
65
+ if not username or not password:
66
+ st.error("Username and password are required.")
67
+ return False
68
+ if verify_user(username, password):
69
+ st.session_state['authenticated'] = True
70
+ st.session_state['username'] = username
71
+ st.session_state['last_activity'] = time.time()
72
+ return True
73
+ st.error("Invalid username or password.")
74
+ return False
75
+
76
+ def logout():
77
+ st.session_state['authenticated'] = False
78
+ st.session_state['username'] = None
79
+
80
+ # Add this at the top of your file
81
+ def local_css():
82
+ st.markdown("""
83
+ <style>
84
+ .stButton>button {
85
+ width: 100%;
86
+ border-radius: 5px;
87
+ height: 3em;
88
+ margin-top: 10px;
89
+ }
90
+
91
+ .auth-container {
92
+ max-width: 400px;
93
+ margin: auto;
94
+ padding: 20px;
95
+ border-radius: 10px;
96
+ box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
97
+ background-color: white;
98
+ }
99
+
100
+ .auth-title {
101
+ text-align: center;
102
+ font-size: 24px;
103
+ margin-bottom: 20px;
104
+ color: #1f1f1f;
105
+ }
106
+
107
+ .auth-subtitle {
108
+ text-align: center;
109
+ font-size: 16px;
110
+ margin-bottom: 20px;
111
+ color: #666;
112
+ }
113
+
114
+ .hero-section {
115
+ text-align: center;
116
+ padding: 40px 20px;
117
+ background: linear-gradient(to right, #4f46e5, #3b82f6);
118
+ color: white;
119
+ margin-bottom: 30px;
120
+ }
121
+
122
+ .feature-container {
123
+ max-width: 1200px;
124
+ margin: auto;
125
+ padding: 20px;
126
+ display: grid;
127
+ grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
128
+ gap: 20px;
129
+ margin-bottom: 40px;
130
+ }
131
+
132
+ .feature-card {
133
+ background: white;
134
+ padding: 20px;
135
+ border-radius: 10px;
136
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
137
+ transition: transform 0.3s ease, box-shadow 0.3s ease;
138
+ }
139
+
140
+ .feature-card:hover {
141
+ transform: scale(1.05);
142
+ box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
143
+ }
144
+
145
+ .feature-title {
146
+ color: #1f1f1f;
147
+ font-size: 18px;
148
+ margin-bottom: 10px;
149
+ font-weight: bold;
150
+ }
151
+
152
+ .feature-text {
153
+ color: #666;
154
+ font-size: 14px;
155
+ }
156
+
157
+ .divider {
158
+ text-align: center;
159
+ margin: 20px 0;
160
+ position: relative;
161
+ }
162
+
163
+ .divider:before {
164
+ content: "";
165
+ position: absolute;
166
+ top: 50%;
167
+ left: 0;
168
+ right: 0;
169
+ height: 1px;
170
+ background-color: #e0e0e0;
171
+ z-index: -1;
172
+ }
173
+
174
+ .divider span {
175
+ background-color: white;
176
+ padding: 0 10px;
177
+ color: #666;
178
+ font-size: 14px;
179
+ }
180
+
181
+ @keyframes typing {
182
+ 0% {
183
+ width: 0;
184
+ }
185
+ 50% {
186
+ width: 100%;
187
+ }
188
+ 60% {
189
+ width: 100%;
190
+ }
191
+ 100% {
192
+ width: 0;
193
+ }
194
+ }
195
+
196
+ @keyframes blink {
197
+ 50% {
198
+ border-color: transparent;
199
+ }
200
+ }
201
+
202
+ .hero-title{
203
+ display: inline-block;
204
+ font-size: 2.5em;
205
+ white-space: nowrap;
206
+ overflow: hidden;
207
+ border-right: 2px solid white;
208
+ width: 0;
209
+ animation: typing 6s steps(40, end) infinite, blink 0.5s step-end infinite;
210
+ }
211
+
212
+ .hero-section {
213
+ text-align: center;
214
+ padding: 40px 20px;
215
+ background: linear-gradient(45deg, #4f46e5, #3b82f6);
216
+ background-size: 300% 300%;
217
+ animation: gradientShift 8s ease infinite;
218
+ color: white;
219
+ margin-bottom: 30px;
220
+ opacity: 0;
221
+ animation: fadeIn 2s ease-in-out forwards;
222
+ }
223
+
224
+ @keyframes fadeIn {
225
+ from {
226
+ opacity: 0;
227
+ }
228
+ to {
229
+ opacity: 1;
230
+ }
231
+ }
232
+
233
+ @keyframes gradientShift {
234
+ 0% {
235
+ background-position: 0% 50%;
236
+ }
237
+ 50% {
238
+ background-position: 100% 50%;
239
+ }
240
+ 100% {
241
+ background-position: 0% 50%;
242
+ }
243
+ }
244
+
245
+ /*.feature-container {
246
+ display: flex;
247
+ justify-content: center;
248
+ align-items: center;
249
+ gap: 20px;
250
+ position: relative;
251
+ width: 100%;
252
+ height: 300px;
253
+ animation: rotate 20s linear infinite; /* Rotate the container */
254
+ }
255
+
256
+ .feature-card {
257
+ background: white;
258
+ padding: 20px;
259
+ border-radius: 10px;
260
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
261
+ transition: transform 0.3s ease, box-shadow 0.3s ease;
262
+ flex-shrink: 0;
263
+ width: 250px;
264
+ }
265
+
266
+ .feature-card:hover {
267
+ transform: scale(1.1);
268
+ box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
269
+ }
270
+
271
+ @keyframes rotate {
272
+ from {
273
+ transform: rotate(0deg);
274
+ }
275
+ to {
276
+ transform: rotate(-360deg);
277
+ }
278
+ */}
279
+
280
+ /*.feature-container {
281
+ display: flex;
282
+ justify-content: center;
283
+ align-items: center;
284
+ overflow: hidden;
285
+ position: relative;
286
+ width: 100%;
287
+ height: 300px;
288
+ }
289
+
290
+ .feature-track {
291
+ display: flex;
292
+ animation: circularMove 15s linear infinite;
293
+ }
294
+
295
+ .feature-card {
296
+ flex: 0 0 300px; /* Fixed width for each card */
297
+ margin: 0 20px;
298
+ background: white;
299
+ color: #333; /* Text color */
300
+ padding: 20px;
301
+ border-radius: 10px;
302
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
303
+ text-align: center; /* Center-align the text */
304
+ overflow: hidden; /* Prevent overflow issues */
305
+ }
306
+
307
+ .feature-card h3 {
308
+ font-size: 1.2em;
309
+ margin-bottom: 10px;
310
+ text-align: center;
311
+ }
312
+
313
+ .feature-card p {
314
+ font-size: 0.9em;
315
+ line-height: 1.4;
316
+ text-align: center;
317
+ font-weight: bold;
318
+ }
319
+
320
+
321
+ .feature-card:hover {
322
+ transform: scale(1.1);
323
+ box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
324
+ }
325
+
326
+ @keyframes circularMove {
327
+ 0% {
328
+ transform: translateX(0);
329
+ }
330
+ 100% {
331
+ transform: translateX(-100%);
332
+ }
333
+ */}
334
+ .feature-container {
335
+ display: flex;
336
+ justify-content: center;
337
+ align-items: center;
338
+ height: 400px;
339
+ perspective: 1000px;
340
+ perspective-origin: 50% 50%;
341
+ background: linear-gradient(to bottom, #1e293b, #0f172a); /* Dark blue gradient background */
342
+ overflow: hidden;
343
+ position: relative;
344
+ padding: 40px 0;
345
+ }
346
+
347
+ .feature-track {
348
+ position: relative;
349
+ width: 100%;
350
+ height: 100%;
351
+ display: flex;
352
+ transform-style: preserve-3d;
353
+ animation: carousel 15s linear infinite;
354
+ }
355
+
356
+ .feature-card {
357
+ position: absolute;
358
+ width: 300px;
359
+ padding: 50px;
360
+ background: white;
361
+ border-radius: 15px;
362
+ box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3); /* Enhanced shadow for better contrast */
363
+ backface-visibility: hidden;
364
+ transform-origin: center center;
365
+ transition: all 0.5s ease;
366
+ }
367
+
368
+ .feature-card h3 {
369
+ color: #1e293b;
370
+ font-size: 1.5em;
371
+ margin-bottom: 1rem;
372
+ font-weight: bold;
373
+ }
374
+
375
+ .feature-card p {
376
+ color: #475569;
377
+ line-height: 1.6;
378
+ }
379
+
380
+ /* Position and animate cards */
381
+ .feature-card:nth-child(1) {
382
+ transform: rotateY(0deg) translateZ(400px) translateX(0px);
383
+ }
384
+
385
+ .feature-card:nth-child(2) {
386
+ transform: rotateY(60deg) translateZ(400px) translateX(0px);
387
+ }
388
+
389
+ .feature-card:nth-child(3) {
390
+ transform: rotateY(120deg) translateZ(400px) translateX(0px);
391
+ }
392
+
393
+ .feature-card:nth-child(4) {
394
+ transform: rotateY(180deg) translateZ(400px) translateX(0px);
395
+ }
396
+
397
+ .feature-card:nth-child(5) {
398
+ transform: rotateY(240deg) translateZ(400px) translateX(0px);
399
+ }
400
+
401
+ .feature-card:nth-child(6) {
402
+ transform: rotateY(300deg) translateZ(400px) translateX(0px);
403
+ }
404
+
405
+ @keyframes carousel {
406
+ 0% {
407
+ transform: translateZ(-400px) rotateY(0deg);
408
+ }
409
+ 100% {
410
+ transform: translateZ(-400px) rotateY(-360deg);
411
+ }
412
+ }
413
+
414
+ /* Enhanced hover effect with glow */
415
+ .feature-card:hover {
416
+ transform: scale(1.1) translateZ(450px);
417
+ box-shadow: 0 8px 30px rgba(255, 255, 255, 0.1); /* Glowing effect */
418
+ z-index: 1;
419
+ }
420
+
421
+ /* Gradient overlays for depth effect */
422
+ .feature-container::before,
423
+ .feature-container::after {
424
+ content: '';
425
+ position: absolute;
426
+ width: 100%;
427
+ height: 100px;
428
+ z-index: 2;
429
+ pointer-events: none;
430
+ }
431
+
432
+ .feature-container::before {
433
+ top: 0;
434
+ background: linear-gradient(to bottom, #1e293b, rgba(30, 41, 59, 0));
435
+ }
436
+
437
+ .feature-container::after {
438
+ bottom: 0;
439
+ background: linear-gradient(to top, #1e293b, rgba(30, 41, 59, 0));
440
+ </style>
441
+ """, unsafe_allow_html=True)
442
+
443
+ # Check session expiry
444
+ if 'authenticated' in st.session_state and st.session_state['authenticated']:
445
+ if time.time() - st.session_state['last_activity'] > 1800: # 30 minutes
446
+ logout()
447
+ st.rerun()
448
+ st.session_state['last_activity'] = time.time()
449
+
450
+ # Initialize session state for registration form visibility
451
+ if 'show_register_form' not in st.session_state:
452
+ st.session_state['show_register_form'] = False
453
+
454
+ # Replace your login/registration section with this:
455
+ if 'authenticated' not in st.session_state or not st.session_state['authenticated']:
456
+ local_css()
457
+
458
+ # Landing page hero section
459
+ st.markdown("""
460
+ <div class="hero-section">
461
+ <h1 class="hero-title" style="font-size: 2.5em; margin-bottom: 20px;">Crop Disease Detection System</h1>
462
+ <p style="font-size: 1.2em; max-width: 800px; margin: 0 auto;">
463
+ An advanced AI-powered system that helps farmers and agricultural experts identify and manage crop diseases effectively
464
+ </p>
465
+ </div>
466
+
467
+ """, unsafe_allow_html=True)
468
+
469
+ # Features section using Streamlit columns
470
+ st.subheader("Key Features")
471
+ col1, col2, col3 = st.columns(3)
472
+
473
+ st.markdown("""
474
+ <div class="feature-container">
475
+ <div class="feature-track">
476
+ <div class="feature-card">
477
+ <h3>🔍 Instant Detection</h3>
478
+ <p>Upload images of your crops and get immediate disease detection results using state-of-the-art AI technology.</p>
479
+ </div>
480
+ <div class="feature-card">
481
+ <h3>💡 Expert Analysis</h3>
482
+ <p>Receive detailed analysis and recommendations from our plant pathology expert system.</p>
483
+ </div>
484
+ <div class="feature-card">
485
+ <h3>📊 Detailed Reports</h3>
486
+ <p>Generate comprehensive reports with treatment recommendations and preventive measures.</p>
487
+ </div>
488
+ <div class="feature-card">
489
+ <h3>🔍 Instant Detection</h3>
490
+ <p>Upload images of your crops and get immediate disease detection results using state-of-the-art AI technology.</p>
491
+ </div>
492
+ <div class="feature-card">
493
+ <h3>💡 Expert Analysis</h3>
494
+ <p>Receive detailed analysis and recommendations from our plant pathology expert system.</p>
495
+ </div>
496
+ <div class="feature-card">
497
+ <h3>📊 Detailed Reports</h3>
498
+ <p>Generate comprehensive reports with treatment recommendations and preventive measures.</p>
499
+ </div>
500
+ </div>
501
+ </div>
502
+ """, unsafe_allow_html=True)
503
+
504
+ # Crop carousel section
505
+ st.markdown("""
506
+ <div class="crop-carousel-container">
507
+ <div class="crop-carousel-track">
508
+ <div class="crop-card">
509
+ <img src="https://github.com/ROBERT-ADDO-ASANTE-DARKO/AI-powered-crop-disease-detection/blob/main/images/b034333ddcc732299d45abf753f3fa71f6ff48ffa3338bfecd615bc2.jpg?raw=true" alt="Crop 1">
510
+ <h4>Corn Leaf Blight</h4>
511
+ <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>
512
+ </div>
513
+ <div class="crop-card">
514
+ <img src="https://github.com/ROBERT-ADDO-ASANTE-DARKO/AI-powered-crop-disease-detection/blob/main/images/apple.jpg?raw=true" alt="Crop 2">
515
+ <h4>Apple Scab Leaf</h4>
516
+ <p>Apple scab is a fungal disease caused by Venturia inaequalis. It primarily affects apple and crabapple trees.</p>
517
+ </div>
518
+ <div class="crop-card">
519
+ <img src="https://github.com/ROBERT-ADDO-ASANTE-DARKO/AI-powered-crop-disease-detection/blob/main/images/tomato.jpg?raw=true" alt="Crop 3">
520
+ <h4>Tomato Leaf Late Blight</h4>
521
+ <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>
522
+ </div>
523
+ <div class="crop-card">
524
+ <img src="https://github.com/ROBERT-ADDO-ASANTE-DARKO/AI-powered-crop-disease-detection/blob/main/images/918d1d7a3dda5ce8fbdabf92e5bf38f104efd129ee09adcc6d1ad46c.jpg?raw=true" alt="Crop 4">
525
+ <h4>Tomato Leaf Yellow Virus</h4>
526
+ <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>
527
+ </div>
528
+ </div>
529
+ </div>
530
+ """, unsafe_allow_html=True)
531
+
532
+ st.markdown("""
533
+ <style>
534
+ .crop-carousel-container {
535
+ width: 100%;
536
+ max-width: 800px;
537
+ margin: auto;
538
+ overflow: hidden;
539
+ position: relative;
540
+ }
541
+
542
+ .crop-carousel-track {
543
+ display: flex;
544
+ animation: moveLeft 20s linear infinite; /* Move right to left */
545
+ }
546
+
547
+ .crop-card {
548
+ flex: 0 0 300px;
549
+ margin: 0 20px;
550
+ background: white;
551
+ color: #333;
552
+ padding: 20px;
553
+ border-radius: 10px;
554
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
555
+ text-align: center;
556
+ overflow: hidden;
557
+ }
558
+
559
+ .crop-card img {
560
+ width: 100%;
561
+ height: 150px;
562
+ object-fit: cover;
563
+ border-radius: 10px;
564
+ margin-bottom: 10px;
565
+ }
566
+
567
+ .crop-card h4 {
568
+ font-size: 1.2em;
569
+ margin: 10px 0;
570
+ }
571
+
572
+ .crop-card p {
573
+ font-size: 0.9em;
574
+ line-height: 1.4;
575
+ color: #555;
576
+ }
577
+
578
+ @keyframes moveLeft {
579
+ 0% {
580
+ transform: translateX(100%);
581
+ }
582
+ 100% {
583
+ transform: translateX(-100%);
584
+ }
585
+ }
586
+ </style>
587
+ """, unsafe_allow_html=True)
588
+
589
+
590
+ # Add some spacing
591
+ st.markdown("<br>", unsafe_allow_html=True)
592
+
593
+ # Authentication container
594
+ st.markdown('<div class="auth-container">', unsafe_allow_html=True)
595
+
596
+ # Initialize password reset state
597
+ if 'show_reset_form' not in st.session_state:
598
+ st.session_state['show_reset_form'] = False
599
+
600
+ # Update password function
601
+ def update_password(username, new_password):
602
+ conn = sqlite3.connect('users.db')
603
+ c = conn.cursor()
604
+
605
+ # Check if username exists
606
+ c.execute("SELECT id FROM users WHERE username = ?", (username,))
607
+ if not c.fetchone():
608
+ return False
609
+
610
+ # Update password
611
+ password_hash = bcrypt.hashpw(new_password.encode(), bcrypt.gensalt())
612
+ c.execute("UPDATE users SET password_hash = ? WHERE username = ?",
613
+ (password_hash, username))
614
+ conn.commit()
615
+ conn.close()
616
+ return True
617
+
618
+
619
+ # Update the authentication container section
620
+ if not st.session_state.get('authenticated', False):
621
+ st.markdown('<div class="auth-container">', unsafe_allow_html=True)
622
+
623
+ # Reset Password Form
624
+ if st.session_state.get('show_reset_form', False):
625
+ st.markdown('<h1 class="auth-title">Reset Password</h1>', unsafe_allow_html=True)
626
+ st.markdown('<p class="auth-subtitle">Enter your username and new password</p>', unsafe_allow_html=True)
627
+
628
+ with st.form("reset_form"):
629
+ username = st.text_input("Username")
630
+ new_password = st.text_input("New Password", type="password")
631
+ confirm_password = st.text_input("Confirm Password", type="password")
632
+ submit = st.form_submit_button("Reset Password")
633
+
634
+ if submit:
635
+ if not username or not new_password or not confirm_password:
636
+ st.error("All fields are required.")
637
+ elif new_password != confirm_password:
638
+ st.error("Passwords do not match.")
639
+ elif update_password(username, new_password):
640
+ st.success("Password updated successfully!")
641
+ st.session_state['show_reset_form'] = False
642
+ time.sleep(1)
643
+ st.rerun()
644
+ else:
645
+ st.error("Username not found.")
646
+
647
+ if st.button("Back to Login"):
648
+ st.session_state['show_reset_form'] = False
649
+ st.rerun()
650
+
651
+ # Registration Form
652
+ elif st.session_state.get('show_register_form', False):
653
+ st.markdown('<h1 class="auth-title">Create Account</h1>', unsafe_allow_html=True)
654
+ st.markdown('<p class="auth-subtitle">Sign up to get started</p>', unsafe_allow_html=True)
655
+
656
+ with st.form("register_form"):
657
+ new_username = st.text_input("Username")
658
+ new_password = st.text_input("Password", type="password")
659
+ submit_button = st.form_submit_button("Create Account")
660
+
661
+ if submit_button:
662
+ if new_username and new_password:
663
+ if add_user(new_username, new_password):
664
+ st.success("Account created successfully!")
665
+ st.session_state['show_register_form'] = False
666
+ time.sleep(1)
667
+ st.rerun()
668
+ else:
669
+ st.error("Username already exists.")
670
+ else:
671
+ st.error("Username and password are required.")
672
+
673
+ st.markdown('<div class="divider"><span>OR</span></div>', unsafe_allow_html=True)
674
+ if st.button("Back to Login"):
675
+ st.session_state['show_register_form'] = False
676
+ st.rerun()
677
+
678
+ # Login Form (default)
679
+ else:
680
+ st.markdown('<h1 class="auth-title">Welcome Back</h1>', unsafe_allow_html=True)
681
+ st.markdown('<p class="auth-subtitle">Sign in to your account</p>', unsafe_allow_html=True)
682
+
683
+ with st.form("login_form"):
684
+ username = st.text_input("Username")
685
+ password = st.text_input("Password", type="password")
686
+ cols = st.columns([1, 1])
687
+ submit_button = cols[0].form_submit_button("Sign In")
688
+ forgot_password = cols[1].form_submit_button("Forgot Password?")
689
+
690
+ if submit_button:
691
+ if login(username, password):
692
+ st.success("Logged in successfully!")
693
+ time.sleep(1)
694
+ st.rerun()
695
+ elif forgot_password:
696
+ st.session_state['show_reset_form'] = True
697
+ st.rerun()
698
+
699
+ st.markdown('<div class="divider"><span>OR</span></div>', unsafe_allow_html=True)
700
+ if st.button("Create New Account"):
701
+ st.session_state['show_register_form'] = True
702
+ st.rerun()
703
+
704
+ st.markdown('</div>', unsafe_allow_html=True)
705
+
706
+ # Update the footer section (replace the existing footer with this)
707
+ st.markdown("""
708
+ <div style="background: linear-gradient(to right, #1e293b, #334155); color: white; padding: 40px 0; margin-top: 40px;">
709
+ <div style="max-width: 1200px; margin: auto; padding: 0 20px;">
710
+ <div style="display: flex; flex-wrap: wrap; justify-content: space-between; gap: 40px;">
711
+ <!-- About Section -->
712
+ <div style="flex: 1; min-width: 250px;">
713
+ <h3 style="color: #60a5fa; font-size: 1.5em; margin-bottom: 20px;">About Our Platform</h3>
714
+ <p style="color: #e2e8f0; line-height: 1.6; margin-bottom: 20px;">
715
+ Our AI-powered platform revolutionizes crop disease detection and management.
716
+ We combine cutting-edge technology with agricultural expertise to protect your crops
717
+ and maximize your yield.
718
+ </p>
719
+ </div>
720
+ <div style="flex: 1; min-width: 250px;">
721
+ <h3 style="color: #60a5fa; font-size: 1.5em; margin-bottom: 20px;">Key Features</h3>
722
+ <ul style="list-style: none; padding: 0; color: #e2e8f0;">
723
+ <li style="margin-bottom: 10px; display: flex; align-items: center;">
724
+ <span style="color: #60a5fa; margin-right: 10px;">✓</span> Real-time Disease Detection
725
+ </li>
726
+ <li style="margin-bottom: 10px; display: flex; align-items: center;">
727
+ <span style="color: #60a5fa; margin-right: 10px;">✓</span> Multi-language Support
728
+ </li>
729
+ <li style="margin-bottom: 10px; display: flex; align-items: center;">
730
+ <span style="color: #60a5fa; margin-right: 10px;">✓</span> Expert Analysis Reports
731
+ </li>
732
+ <li style="margin-bottom: 10px; display: flex; align-items: center;">
733
+ <span style="color: #60a5fa; margin-right: 10px;">✓</span> Treatment Recommendations
734
+ </li>
735
+ </ul>
736
+ </div>
737
+ <div style="flex: 1; min-width: 250px;">
738
+ <h3 style="color: #60a5fa; font-size: 1.5em; margin-bottom: 20px;">Contact Us</h3>
739
+ <p style="color: #e2e8f0; line-height: 1.6; margin-bottom: 10px;">
740
+ <span style="color: #60a5fa;">Email:</span> [email protected]
741
+ </p>
742
+ <p style="color: #e2e8f0; line-height: 1.6; margin-bottom: 20px;">
743
+ <span style="color: #60a5fa;">Phone:</span> +1 (234) 567-8900
744
+ </p>
745
+ <div style="display: flex; gap: 15px; margin-top: 20px;">
746
+ <a href="#" style="color: #60a5fa; text-decoration: none; font-size: 1.2em;">
747
+ <span>📱</span>
748
+ </a>
749
+ <a href="#" style="color: #60a5fa; text-decoration: none; font-size: 1.2em;">
750
+ <span>💬</span>
751
+ </a>
752
+ <a href="#" style="color: #60a5fa; text-decoration: none; font-size: 1.2em;">
753
+ <span>📨</span>
754
+ </a>
755
+ </div>
756
+ </div>
757
+ </div>
758
+ <div style="border-top: 1px solid #4b5563; margin-top: 40px; padding-top: 20px; text-align: center;">
759
+ <p style="color: #e2e8f0; font-size: 0.9em;">
760
+ © 2025 Crop Disease Detection System. All rights reserved.
761
+ </p>
762
+ <div style="margin-top: 10px;">
763
+ <a href="#" style="color: #e2e8f0; text-decoration: none; margin: 0 10px; font-size: 0.9em;">Privacy Policy</a>
764
+ <a href="#" style="color: #e2e8f0; text-decoration: none; margin: 0 10px; font-size: 0.9em;">Terms of Service</a>
765
+ <a href="#" style="color: #e2e8f0; text-decoration: none; margin: 0 10px; font-size: 0.9em;">FAQ</a>
766
+ </div>
767
+ </div>
768
+ </div>
769
+ </div>
770
+ """, unsafe_allow_html=True)
771
+
772
+ st.stop()
773
+
774
+ # Update database schema to include comments
775
+ def setup_feedback_db():
776
+ conn = sqlite3.connect('customer_feedback.db')
777
+ c = conn.cursor()
778
+ c.execute('''CREATE TABLE IF NOT EXISTS customer_feedback
779
+ (id INTEGER PRIMARY KEY AUTOINCREMENT,
780
+ question TEXT,
781
+ response TEXT,
782
+ feedback_type TEXT,
783
+ comment_type TEXT,
784
+ custom_comment TEXT,
785
+ timestamp DATETIME DEFAULT CURRENT_TIMESTAMP)''')
786
+ conn.commit()
787
+ return conn, c
788
+
789
+ def save_feedback(question, response, feedback_type, comment_type=None, custom_comment=None):
790
+ conn, c = setup_feedback_db()
791
+ try:
792
+ c.execute("""INSERT INTO customer_feedback
793
+ (question, response, feedback_type, comment_type, custom_comment)
794
+ VALUES (?, ?, ?, ?, ?)""",
795
+ (question, response, feedback_type, comment_type, custom_comment))
796
+ conn.commit()
797
+ return True
798
+ except Exception as e:
799
+ st.error(f"Error saving feedback: {e}")
800
+ return False
801
+ finally:
802
+ conn.close()
803
+
804
+ # Update the conversation display section
805
+ def display_feedback_buttons(file_id, index, question, response):
806
+ # Suggested comments
807
+ SUGGESTED_COMMENTS = [
808
+ "Inaccurate information",
809
+ "Unclear explanation",
810
+ "Missing details",
811
+ "Not relevant to question",
812
+ "Technical error",
813
+ "Other"
814
+ ]
815
+
816
+ # Initialize session state for feedback if it doesn't exist
817
+ if f"feedback_{file_id}_{index}" not in st.session_state:
818
+ st.session_state[f"feedback_{file_id}_{index}"] = {
819
+ "feedback_type": None, # Stores "👍" or "👎"
820
+ "comment": None, # Stores the user's comment
821
+ "submitted": False # Tracks whether feedback has been submitted
822
+ }
823
+
824
+ col1, col2 = st.columns([1, 4])
825
+ with col1:
826
+ if st.button("👍", key=f"helpful_{file_id}_{index}"):
827
+ # Save positive feedback immediately
828
+ save_feedback(question, response, "👍")
829
+ st.success("Feedback saved!")
830
+ # Update session state to indicate feedback has been submitted
831
+ st.session_state[f"feedback_{file_id}_{index}"]["submitted"] = True
832
+ return
833
+
834
+ with col2:
835
+ if st.button("👎", key=f"not_helpful_{file_id}_{index}"):
836
+ # Store the feedback type in session state
837
+ st.session_state[f"feedback_{file_id}_{index}"]["feedback_type"] = "👎"
838
+
839
+ # Check if feedback_type is "👎" before showing the comment input field
840
+ if st.session_state[f"feedback_{file_id}_{index}"].get("feedback_type") == "👎":
841
+ # Display suggested comments in a dropdown menu
842
+ selected_comment = st.selectbox(
843
+ "What was the issue?",
844
+ options=SUGGESTED_COMMENTS,
845
+ key=f"suggested_comment_{file_id}_{index}"
846
+ )
847
+
848
+ # If the user selects "Other", allow them to provide a custom comment
849
+ custom_comment = None
850
+ if selected_comment == "Other":
851
+ custom_comment = st.text_area(
852
+ "Please describe the issue:",
853
+ key=f"custom_comment_{file_id}_{index}"
854
+ )
855
+
856
+ # Submit Feedback button
857
+ if st.button("Submit Feedback", key=f"submit_{file_id}_{index}"):
858
+ # Save feedback to the database
859
+ save_feedback(
860
+ question,
861
+ response,
862
+ st.session_state[f"feedback_{file_id}_{index}"]["feedback_type"],
863
+ custom_comment if selected_comment == "Other" else selected_comment
864
+ )
865
+ st.success("Thank you for your feedback!")
866
+ # Update session state to indicate feedback has been submitted
867
+ st.session_state[f"feedback_{file_id}_{index}"]["submitted"] = True
868
+ return
869
+
870
+ # Model configuration
871
+ SUPPORTED_MODELS = {
872
+ "llama3.2": {
873
+ "name": "llama3.2",
874
+ "system_prompt": "You are a helpful plant pathology expert assistant.",
875
+ "supports_vision": False
876
+ },
877
+ "llama3.1": {
878
+ "name": "llama3.1",
879
+ "system_prompt": "You are a helpful plant pathology expert assistant.",
880
+ "supports_vision": False
881
+ },
882
+ "llama2": {
883
+ "name": "llama2",
884
+ "system_prompt": "You are a helpful plant pathology expert assistant.",
885
+ "supports_vision": False
886
+ },
887
+ "llava": {
888
+ "name": "llava",
889
+ "system_prompt": "You are a helpful plant pathology expert assistant with vision capabilities.",
890
+ "supports_vision": True,
891
+ "vision_prompt": "Analyze the image and describe the diseases present."
892
+ },
893
+ "mistral": {
894
+ "name": "mistral",
895
+ "system_prompt": "You are a helpful plant pathology expert assistant.",
896
+ "supports_vision": False
897
+ },
898
+ "gemma": {
899
+ "name": "gemma",
900
+ "system_prompt": "You are a helpful plant pathology expert assistant.",
901
+ "supports_vision": False
902
+ },
903
+ "jyan1/paligemma-mix-224": {
904
+ "name": "jyan1/paligemma-mix-224",
905
+ "system_prompt": "You are a helpful plant pathology expert assistant.",
906
+ "supports_vision": True
907
+ }
908
+ }
909
+
910
+ # Initialize session state for conversation history if it doesn't exist
911
+ if 'conversation_history' not in st.session_state:
912
+ st.session_state.conversation_history = {}
913
+
914
+ # Load YOLOv8 model
915
+ yolo_model = YOLO("models/best.pt")
916
+
917
+ def preprocess_image(image, target_size=(224, 224)):
918
+ """
919
+ Preprocess the image for vision-capable models.
920
+ """
921
+ image = Image.fromarray(image)
922
+ image = image.resize(target_size)
923
+ return image
924
+
925
+ def text_to_speech(text, language='en'):
926
+ """Convert text to speech using gTTS"""
927
+ try:
928
+ # Create temporary file
929
+ with tempfile.NamedTemporaryFile(delete=False, suffix=".mp3") as temp_audio:
930
+ # Generate audio file
931
+ tts = gTTS(text=text, lang=language, slow=False)
932
+ tts.save(temp_audio.name)
933
+
934
+ # Read the audio file
935
+ with open(temp_audio.name, 'rb') as audio_file:
936
+ audio_bytes = audio_file.read()
937
+
938
+ # Clean up
939
+ os.unlink(temp_audio.name)
940
+
941
+ return audio_bytes
942
+ except Exception as e:
943
+ st.error(f"Error generating speech: {str(e)}")
944
+ return None
945
+
946
+ def check_ollama_connection():
947
+ try:
948
+ response = requests.get("http://localhost:11434")
949
+ return response.status_code == 200
950
+ except Exception as e:
951
+ return False
952
+
953
+ def generate_ollama_response(prompt, model_name="llama2", conversation_history=None, image_data=None):
954
+ try:
955
+ if model_name not in SUPPORTED_MODELS:
956
+ return f"Error: Model {model_name} is not supported."
957
+
958
+ model_config = SUPPORTED_MODELS[model_name]
959
+
960
+ # Build the messages array
961
+ messages = [
962
+ {
963
+ "role": "system",
964
+ "content": model_config["system_prompt"]
965
+ }
966
+ ]
967
+
968
+ # Add conversation history
969
+ if conversation_history:
970
+ for entry in conversation_history:
971
+ if len(entry) >= 2: # Handle tuples with 2 or 3 values
972
+ question, response = entry[:2]
973
+ messages.extend([
974
+ {"role": "user", "content": question},
975
+ {"role": "assistant", "content": response}
976
+ ])
977
+
978
+ # Handle vision models differently
979
+ if model_config["supports_vision"] and image_data is not None:
980
+ if isinstance(image_data, np.ndarray):
981
+ image = Image.fromarray(image_data)
982
+ buffered = BytesIO()
983
+ image.save(buffered, format="JPEG")
984
+ img_str = base64.b64encode(buffered.getvalue()).decode()
985
+
986
+ messages.append({
987
+ "role": "user",
988
+ "content": [
989
+ {"type": "text", "text": prompt},
990
+ {"type": "image", "image": img_str}
991
+ ]
992
+ })
993
+ else:
994
+ messages.append({
995
+ "role": "user",
996
+ "content": prompt
997
+ })
998
+
999
+ # Make an API call to Ollama
1000
+ api_url = "http://localhost:11434/api/generate" # Ollama API endpoint
1001
+ payload = {
1002
+ "model": model_config["name"],
1003
+ "prompt": prompt, # Use the prompt directly
1004
+ "stream": False # Set to True if you want streaming responses
1005
+ }
1006
+
1007
+ # Send the request
1008
+ response = requests.post(api_url, json=payload)
1009
+
1010
+ # Check for errors
1011
+ if response.status_code != 200:
1012
+ return f"Error: API request failed with status code {response.status_code}. Response: {response.text}"
1013
+
1014
+ # Parse the response
1015
+ response_data = response.json()
1016
+
1017
+ # Check if the response contains the expected key
1018
+ if "response" in response_data:
1019
+ return response_data["response"]
1020
+ else:
1021
+ return f"Error: Unexpected response format: {response_data}"
1022
+
1023
+ except Exception as e:
1024
+ return f"Error connecting to Ollama API: {str(e)}"
1025
+
1026
+ def generate_improved_description(detected_classes, class_names, user_text, image_details=None, conversation_history=None):
1027
+ """
1028
+ Generate a more detailed and contextual description using Ollama
1029
+ """
1030
+ detected_objects = [class_names[cls] for cls in detected_classes]
1031
+
1032
+ # Create base context about detected diseases
1033
+ disease_context = f"Detected diseases: {', '.join(detected_objects)}"
1034
+
1035
+ # Different prompt structure for initial vs. follow-up questions
1036
+ if not conversation_history:
1037
+ base_prompt = f"""As an expert plant pathologist, analyze the following crop diseases detected in the image: {', '.join(detected_objects)}.
1038
+
1039
+ For each detected disease, provide a structured analysis following this format:
1040
+
1041
+ 1. Disease Name: [Name]
1042
+ - Pathogen: [Causative organism]
1043
+ - Severity Level: [Based on visual symptoms]
1044
+ - Key Symptoms:
1045
+ * [Symptom 1]
1046
+ * [Symptom 2]
1047
+ - Economic Impact:
1048
+ * [Brief description of potential crop losses]
1049
+ - Treatment Options:
1050
+ * Immediate actions: [Short-term solutions]
1051
+ * Long-term management: [Preventive measures]
1052
+ - Environmental Conditions:
1053
+ * Favorable conditions for disease development
1054
+ * Risk factors
1055
+
1056
+ 2. Recommendations:
1057
+ - Immediate Steps:
1058
+ * [Action items for immediate control]
1059
+ - Prevention Strategy:
1060
+ * [Long-term prevention measures]
1061
+ - Monitoring Protocol:
1062
+ * [What to watch for]
1063
+
1064
+ Initial Question/Context: {user_text if user_text else "Provide a general analysis"}
1065
+ """
1066
+ else:
1067
+ base_prompt = f"""Context: {disease_context}
1068
+
1069
+ Previous conversation context has been provided above. Please address the following follow-up question while maintaining consistency with previous responses:
1070
+
1071
+ {user_text}
1072
+
1073
+ Provide a detailed response that builds upon the previous context and specifically addresses this question."""
1074
+
1075
+ # Get the selected model from session state or default to llama2
1076
+ selected_model = st.session_state.get('selected_model', 'llama2')
1077
+
1078
+ return generate_ollama_response(
1079
+ base_prompt,
1080
+ model_name=selected_model,
1081
+ conversation_history=conversation_history,
1082
+ image_data=image_details.get("image_data") if image_details else None
1083
+ )
1084
+
1085
+ def inference(image):
1086
+ """
1087
+ Enhanced inference function with confidence scores and bounding box information
1088
+ """
1089
+ results = yolo_model(image, conf=0.4)
1090
+ infer = np.zeros(image.shape, dtype=np.uint8)
1091
+ classes = dict()
1092
+ names_infer = []
1093
+ confidence_scores = []
1094
+ bounding_boxes = []
1095
+
1096
+ for r in results:
1097
+ infer = r.plot()
1098
+ classes = r.names
1099
+ names_infer = r.boxes.cls.tolist()
1100
+ confidence_scores = r.boxes.conf.tolist()
1101
+ bounding_boxes = r.boxes.xyxy.tolist()
1102
+
1103
+ return infer, names_infer, classes, confidence_scores, bounding_boxes
1104
+
1105
+ # Streamlit application
1106
+ st.title("Interactive Crop Disease Detection and Analysis🌾🌿🥬☘️")
1107
+ st.write(f"Welcome, {st.session_state['username']}!😊")
1108
+
1109
+ # Logout button
1110
+ if st.button("Logout"):
1111
+ logout()
1112
+ st.rerun()
1113
+
1114
+ # Add sidebar for configuration
1115
+ with st.sidebar:
1116
+ st.header("Settings")
1117
+ selected_model = st.selectbox(
1118
+ "Select LLM Model",
1119
+ list(SUPPORTED_MODELS.keys()),
1120
+ index=0, # Default to first model (bart-large-cnn)
1121
+ help="Choose the Ollama model to use for analysis"
1122
+ )
1123
+ # Store the selected model in session state
1124
+ st.session_state['selected_model'] = selected_model
1125
+
1126
+ if SUPPORTED_MODELS[selected_model]["supports_vision"]:
1127
+ st.info("This model supports vision capabilities and can analyze images directly.")
1128
+
1129
+ confidence_threshold = st.slider("Detection Confidence Threshold", 0.0, 1.0, 0.4)
1130
+ show_confidence = st.checkbox("Show Confidence Scores", value=True)
1131
+ show_bbox = st.checkbox("Show Bounding Boxes", value=True)
1132
+
1133
+ # TTS Settings
1134
+ st.header("Text-to-Speech Settings")
1135
+ tts_enabled = st.checkbox("Enable Text-to-Speech", value=True)
1136
+ if tts_enabled:
1137
+ language = st.selectbox("Speech Language",
1138
+ options=['en', 'es', 'fr', 'de'],
1139
+ format_func=lambda x: {
1140
+ 'en': 'English',
1141
+ 'es': 'Spanish',
1142
+ 'fr': 'French',
1143
+ 'de': 'German'
1144
+ }[x],
1145
+ help="Select speech language")
1146
+
1147
+ # Add option to clear conversation history
1148
+ if st.button("Clear All Conversations"):
1149
+ st.session_state.conversation_history = {}
1150
+ st.success("Conversation history cleared!")
1151
+
1152
+ # Language selection
1153
+ language = st.selectbox(
1154
+ "Select Language",
1155
+ options=['en', 'es', 'fr', 'de'], # Add more languages as needed
1156
+ format_func=lambda x: {
1157
+ 'en': 'English',
1158
+ 'es': 'Spanish',
1159
+ 'fr': 'French',
1160
+ 'de': 'German'
1161
+ }[x],
1162
+ help="Select your preferred language"
1163
+ )
1164
+
1165
+ # Main content
1166
+ uploaded_files = st.file_uploader("Upload images for disease detection", type=["jpg", "jpeg", "png"], accept_multiple_files=True)
1167
+
1168
+ if uploaded_files:
1169
+ for uploaded_file in uploaded_files:
1170
+ file_id = uploaded_file.name
1171
+
1172
+ # Initialize conversation history for this image if it doesn't exist
1173
+ if file_id not in st.session_state.conversation_history:
1174
+ st.session_state.conversation_history[file_id] = []
1175
+
1176
+ st.header(f"Analysis for {file_id}")
1177
+
1178
+ # Create columns for side-by-side display
1179
+ col1, col2 = st.columns(2)
1180
+
1181
+ # Process image
1182
+ file_bytes = np.asarray(bytearray(uploaded_file.read()), dtype=np.uint8)
1183
+ image = cv2.imdecode(file_bytes, 1)
1184
+ image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
1185
+
1186
+ # Display original image
1187
+ with col1:
1188
+ st.subheader("Original Image")
1189
+ st.image(image, use_container_width=True)
1190
+
1191
+ # Process and display results
1192
+ with st.spinner("Processing image..."):
1193
+ infer_image, classes_in_image, classes_in_dataset, confidences, boxes = inference(image)
1194
+
1195
+ with col2:
1196
+ st.subheader("Detected Diseases")
1197
+ st.image(infer_image, use_container_width=True)
1198
+
1199
+ # Display detection details
1200
+ if show_confidence:
1201
+ st.subheader("Detection Details")
1202
+ for cls, conf in zip(classes_in_image, confidences):
1203
+ st.write(f"- {classes_in_dataset[cls]}: {conf:.2%} confidence")
1204
+
1205
+ # Display conversation history
1206
+ if st.session_state.conversation_history[file_id]:
1207
+ st.subheader("Conversation History")
1208
+ for i, entry in enumerate(st.session_state.conversation_history[file_id]):
1209
+ question, response = entry[:2]
1210
+
1211
+ with st.expander(f"Q{i+1}: {question[:50]}...", expanded=False):
1212
+ st.write("**Question:**", question)
1213
+ st.write("**Response:**", response)
1214
+
1215
+ # Display feedback buttons and handle comment collection
1216
+ display_feedback_buttons(file_id, i, question, response)
1217
+
1218
+ # Audio playback option
1219
+ if tts_enabled:
1220
+ if st.button("🔊 Listen", key=f"listen_history_{file_id}_{i}"):
1221
+ with st.spinner("Generating audio..."):
1222
+ audio_bytes = text_to_speech(response, language)
1223
+ if audio_bytes:
1224
+ st.audio(audio_bytes, format='audio/mp3')
1225
+
1226
+
1227
+ # User input for questions
1228
+ st.subheader("Ask Questions")
1229
+ user_text = st.text_area(
1230
+ "Enter your question about the detected diseases:",
1231
+ placeholder="Example: What are the best treatment options for these diseases? What preventive measures should I take?",
1232
+ key=f"question_{file_id}"
1233
+ )
1234
+
1235
+ def translate_text(text, target_lang='en'):
1236
+ translator = GoogleTranslator(source='auto', target=target_lang)
1237
+ return translator.translate(text)
1238
+
1239
+ # Use the async function in your Streamlit app
1240
+ if st.button("Get Analysis", key=f"analyze_{file_id}"):
1241
+ with st.spinner(f"Generating analysis using {selected_model}..."):
1242
+ # Perform translation
1243
+ translated_input = translate_text(user_text, target_lang='en')
1244
+ st.write(f"Translated Input (to English): {translated_input}")
1245
+
1246
+ # Create detailed image information dictionary
1247
+ image_details = {
1248
+ "confidence_scores": confidences,
1249
+ "bounding_boxes": boxes,
1250
+ "image_dimensions": image.shape,
1251
+ "image_data": image # Add the image data for vision models
1252
+ }
1253
+
1254
+ # Generate response
1255
+ response = generate_improved_description(
1256
+ classes_in_image,
1257
+ classes_in_dataset,
1258
+ translated_input,
1259
+ image_details,
1260
+ st.session_state.conversation_history[file_id]
1261
+ )
1262
+
1263
+ # Translate LLM response
1264
+ translated_response = translate_text(response, target_lang=language)
1265
+
1266
+ # Add to conversation history and display the response
1267
+ st.session_state.conversation_history[file_id].append((user_text, translated_response, None))
1268
+ st.markdown("### Latest Response")
1269
+ st.markdown(translated_response)
1270
+
1271
+ # Add audio playback option for the latest response
1272
+ if tts_enabled:
1273
+ col1, col2 = st.columns([1, 4])
1274
+ with col1:
1275
+ if st.button("🔊 Listen", key=f"listen_latest_{file_id}"):
1276
+ with st.spinner("Generating audio..."):
1277
+ audio_bytes = text_to_speech(translated_response, language)
1278
+ if audio_bytes:
1279
+ st.audio(audio_bytes, format='audio/mp3')
1280
+
1281
+ # Export conversation
1282
+ if st.button("Export Conversation", key=f"export_{file_id}"):
1283
+ conversation_text = f"""
1284
+ # Crop Disease Analysis Report
1285
+
1286
+ ## Image Information
1287
+ - Filename: {file_id}
1288
+ - Analysis Date: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}
1289
+
1290
+ ## Detected Diseases
1291
+ {', '.join([classes_in_dataset[cls] for cls in classes_in_image])}
1292
+
1293
+ ## Conversation History
1294
+ """
1295
+
1296
+ for i, entry in enumerate(st.session_state.conversation_history[file_id]):
1297
+ if len(entry) == 2: # Handle legacy entries
1298
+ question, response = entry
1299
+ feedback = "No feedback"
1300
+ else:
1301
+ question, response, feedback = entry
1302
+
1303
+ conversation_text += f"\n### Question {i+1}:\n{question}\n\n### Answer {i+1}:\n{response}\n\n### Feedback {i+1}:\n{feedback}\n"
1304
+
1305
+ st.download_button(
1306
+ label="Download Conversation",
1307
+ data=conversation_text,
1308
+ file_name=f"disease_analysis_{file_id}.md",
1309
+ mime="text/markdown"
1310
+ )
1311
+
1312
+ # Add a footer with clear instructions
1313
+ st.markdown("""
1314
+ ---
1315
+ ### How to Use
1316
+ 1. Upload one or more images of crops with potential diseases
1317
+ 2. View the detected diseases and their confidence scores
1318
+ 3. Ask questions about the diseases, treatments, or prevention
1319
+ 4. Use the 🔊 Listen button to hear the responses
1320
+ 5. View previous questions and answers in the conversation history
1321
+ 6. Export the entire conversation for future reference
1322
+ 7. Use the sidebar to adjust settings or clear conversation history
1323
+ """)
startup.sh ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/bin/bash
2
+
3
+ # Start Ollama in the background
4
+ ollama serve &
5
+
6
+ # Wait for Ollama to initialize
7
+ echo "Waiting for Ollama to start..."
8
+ sleep 5
9
+
10
+ # Pull required models
11
+ echo "Pulling Ollama models..."
12
+ ollama pull llama2
13
+ #ollama pull llama3.1
14
+ #ollama pull llama3.2
15
+ #ollama pull mistral
16
+ ollama list
17
+
18
+ curl -X POST http://localhost:11434/api/generate \
19
+ -H "Content-Type: application/json" \
20
+ -d '{
21
+ "model": "llama2",
22
+ "prompt": "Hello, world!"
23
+ }'
24
+
25
+ # Start supervisord to manage processes
26
+ echo "Starting supervisord..."
27
+ exec /usr/bin/supervisord -c /etc/supervisor/conf.d/supervisord.conf
supervisord.conf ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [supervisord]
2
+ nodaemon=true
3
+
4
+ [program:ollama]
5
+ command=ollama serve
6
+ autostart=true
7
+ autorestart=true
8
+ stderr_logfile=/dev/stderr
9
+ stdout_logfile=/dev/stdout
10
+
11
+ [program:streamlit]
12
+ command=streamlit run app.py --server.port=8501 --server.address=0.0.0.0
13
+ autostart=true
14
+ autorestart=true
15
+ stderr_logfile=/dev/stderr
16
+ stdout_logfile=/dev/stdout