FaiqAHM commited on
Commit
499e520
Β·
verified Β·
1 Parent(s): 932d9e9

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +273 -0
app.py ADDED
@@ -0,0 +1,273 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ from transformers import AutoImageProcessor, AutoModelForImageClassification
3
+ from groq import Groq
4
+ import torch
5
+ from PIL import Image
6
+ from gtts import gTTS
7
+ import os
8
+ import re
9
+ import tempfile
10
+
11
+ # Set page config for a wide, user-friendly layout
12
+ st.set_page_config(page_title="Farmer's Friend", page_icon="🌱", layout="wide", initial_sidebar_state="expanded")
13
+
14
+ # Initialize Groq client using environment variable (set as a secret in Hugging Face Spaces)
15
+ groq_api_key = os.getenv("GROQ_API_KEY")
16
+ if not groq_api_key:
17
+ st.error("Groq API key not found. Please set the GROQ_API_KEY environment variable in Hugging Face Spaces secrets.")
18
+ st.stop()
19
+ groq_client = Groq(api_key=groq_api_key)
20
+
21
+ # Load and cache the model and processor
22
+ @st.cache_resource
23
+ def load_model_and_processor():
24
+ image_processor = AutoImageProcessor.from_pretrained("wambugu71/crop_leaf_diseases_vit", use_fast=True)
25
+ image_model = AutoModelForImageClassification.from_pretrained("wambugu71/crop_leaf_diseases_vit")
26
+ image_model.eval() # Set model to evaluation mode for faster inference
27
+ if torch.cuda.is_available():
28
+ image_model.cuda() # Use GPU if available
29
+ return image_processor, image_model
30
+
31
+ image_processor, image_model = load_model_and_processor()
32
+
33
+ # Check for wide-field image
34
+ def is_wide_field_image(image):
35
+ width, height = image.size
36
+ return width > 1000 or height > 1000
37
+
38
+ # Generate audio in English using temporary files
39
+ @st.cache_data
40
+ def text_to_audio(text, filename="output.mp3"):
41
+ try:
42
+ # Use a temporary file to store the audio
43
+ with tempfile.NamedTemporaryFile(delete=False, suffix=".mp3") as tmp_file:
44
+ tts = gTTS(text=text, lang='en', slow=False, tld='com')
45
+ tts.save(tmp_file.name)
46
+ return tmp_file.name
47
+ except Exception as e:
48
+ st.error(f"Error generating audio: {e}")
49
+ return None
50
+
51
+ # Smart voice-over generation (removing special characters)
52
+ @st.cache_data
53
+ def generate_voice_over(predicted_label, recommendation):
54
+ clean_label = re.sub(r'[_*]', ' ', predicted_label).strip()
55
+ if "healthy" in clean_label.lower():
56
+ voice_text = "Your crop looks healthy. Keep up the good work!"
57
+ else:
58
+ voice_text = f"Issue detected: {clean_label}. Recommendation: {recommendation}"
59
+ return text_to_audio(voice_text, "result.mp3")
60
+
61
+ # Analyze the crop (no caching due to PIL.Image.Image serialization issues)
62
+ def analyze_crop(image, crop_type):
63
+ if image is None:
64
+ msg = "Please upload a photo of your crop leaf."
65
+ return msg, text_to_audio(msg, "no_image.mp3")
66
+
67
+ if is_wide_field_image(image):
68
+ msg = "This image is too large. Please upload a close-up photo of the leaf."
69
+ return msg, text_to_audio(msg, "large_image.mp3")
70
+
71
+ # Convert image to RGB and resize (optimized)
72
+ image = image.convert("RGB")
73
+ image = image.resize((224, 224), Image.Resampling.LANCZOS)
74
+ inputs = image_processor(images=image, return_tensors="pt")
75
+
76
+ # Move inputs to GPU if available
77
+ if torch.cuda.is_available():
78
+ inputs = {k: v.cuda() for k, v in inputs.items()}
79
+
80
+ # Run inference
81
+ with torch.no_grad():
82
+ outputs = image_model(**inputs)
83
+ predicted_class_idx = outputs.logits.argmax(-1).item()
84
+ predicted_label = image_model.config.id2label[predicted_class_idx]
85
+
86
+ # Prepare result
87
+ if "healthy" in predicted_label.lower():
88
+ issue = "Healthy"
89
+ recommendation = "Your crop looks healthy. Keep up the good work!"
90
+ else:
91
+ issue = predicted_label
92
+ try:
93
+ prompt = f"Detected issue: {predicted_label} in {crop_type}. Suggest general remedies for this crop problem suitable for farmers worldwide in English."
94
+ response = groq_client.chat.completions.create(
95
+ model='llama3-8b-8192',
96
+ messages=[{'role': 'user', 'content': prompt}],
97
+ timeout=5 # Add timeout to avoid long waits
98
+ ).choices[0].message.content
99
+ recommendation = response
100
+ except Exception as e:
101
+ recommendation = "Sorry, there was an issue retrieving recommendations. Please consult a local agricultural expert."
102
+
103
+ result_text = f"**Issue:** {issue}. **Recommendation:** {recommendation}"
104
+ audio_file = generate_voice_over(predicted_label, recommendation)
105
+ return result_text, audio_file
106
+
107
+ # Custom CSS for a Grok-inspired design
108
+ st.markdown("""
109
+ <style>
110
+ .stApp {
111
+ background-color: #F5F7F5;
112
+ font-family: 'Arial', sans-serif;
113
+ }
114
+ .main-header {
115
+ color: #1A3C34;
116
+ font-size: 40px;
117
+ font-weight: 700;
118
+ text-align: center;
119
+ margin-bottom: 10px;
120
+ }
121
+ .sub-header {
122
+ color: #2E7D32;
123
+ font-size: 20px;
124
+ text-align: center;
125
+ margin-bottom: 20px;
126
+ }
127
+ .info-box {
128
+ background-color: #E8F5E9;
129
+ padding: 20px;
130
+ border-radius: 12px;
131
+ font-size: 16px;
132
+ box-shadow: 0 2px 5px rgba(0,0,0,0.1);
133
+ margin-bottom: 20px;
134
+ }
135
+ .css-1d391kg {
136
+ background-color: #FFFFFF;
137
+ border-right: 1px solid #E0E0E0;
138
+ }
139
+ .stButton>button {
140
+ background-color: #2E7D32;
141
+ color: white;
142
+ border-radius: 12px;
143
+ padding: 12px 24px;
144
+ font-size: 16px;
145
+ font-weight: 600;
146
+ border: none;
147
+ transition: all 0.3s ease;
148
+ }
149
+ .stButton>button:hover {
150
+ background-color: #1A3C34;
151
+ transform: scale(1.05);
152
+ }
153
+ .stTabs [data-baseweb="tab-list"] {
154
+ gap: 20px;
155
+ }
156
+ .stTabs [data-baseweb="tab"] {
157
+ background-color: #FFFFFF;
158
+ border-radius: 8px;
159
+ padding: 10px 20px;
160
+ font-weight: 500;
161
+ color: #1A3C34;
162
+ }
163
+ .stTabs [data-baseweb="tab"][aria-selected="true"] {
164
+ background-color: #2E7D32;
165
+ color: white;
166
+ }
167
+ .stImage > div > div > div > div > p {
168
+ font-size: 14px;
169
+ color: #4A4A4A;
170
+ text-align: center;
171
+ }
172
+ .stMarkdown {
173
+ font-size: 16px;
174
+ color: #1A3C34;
175
+ }
176
+ </style>
177
+ """, unsafe_allow_html=True)
178
+
179
+ # Main header with emoji
180
+ st.markdown('<div class="main-header">🌱 Farmer\'s Friend 🌱</div>', unsafe_allow_html=True)
181
+ st.markdown('<div class="sub-header">Helping farmers worldwide keep crops healthy!</div>', unsafe_allow_html=True)
182
+
183
+ # Instruction section
184
+ st.markdown('<div class="info-box">πŸ“Έ Upload a clear photo of your crop leaf to check for issues. We provide simple recommendations in English with English audio.</div>', unsafe_allow_html=True)
185
+
186
+ # Sidebar for settings
187
+ with st.sidebar:
188
+ st.markdown("### 🌾 Crop Settings")
189
+ crop_type = st.selectbox(
190
+ "Select your crop type:",
191
+ ["Wheat", "Maize", "Rice", "Corn", "Soybean", "Barley", "Cotton", "Millet", "Sorghum"],
192
+ help="Choose the type of crop you're analyzing."
193
+ )
194
+ st.markdown("### ℹ️ About")
195
+ st.write("This app helps farmers worldwide identify crop issues and get practical solutions. Upload a leaf photo to get started!")
196
+
197
+ # Tabs for organized layout
198
+ tab1, tab2 = st.tabs(["πŸ“€ Upload & Analyze", "πŸ“Š Results"])
199
+
200
+ with tab1:
201
+ # Use columns for a clean layout
202
+ col1, col2 = st.columns([1, 1])
203
+ with col1:
204
+ uploaded_image = st.file_uploader("Upload Photo:", type=["jpg", "jpeg", "png"], help="Upload a clear image of your crop leaf.")
205
+ if uploaded_image:
206
+ image = Image.open(uploaded_image)
207
+ st.image(image, caption="Your Uploaded Photo", use_container_width=True)
208
+ else:
209
+ image = None
210
+
211
+ with col2:
212
+ st.markdown("### πŸ“· Tips for Best Results")
213
+ st.write("- Use a close-up photo of the leaf.")
214
+ st.write("- Ensure good lighting.")
215
+ st.write("- Avoid blurry images.")
216
+
217
+ # Analyze button
218
+ if st.button("πŸ” Check"):
219
+ if image:
220
+ with st.spinner("Analyzing your crop..."):
221
+ result_text, audio_file = analyze_crop(image, crop_type)
222
+ st.session_state['result_text'] = result_text
223
+ st.session_state['audio_file'] = audio_file
224
+ st.success("Analysis Complete! Check the Results tab.")
225
+ else:
226
+ st.warning("Please upload a photo first.")
227
+
228
+ with tab2:
229
+ if 'result_text' in st.session_state:
230
+ st.markdown("### πŸ“Š Analysis Results")
231
+ st.write(st.session_state['result_text'])
232
+ if 'audio_file' in st.session_state and st.session_state['audio_file'] and os.path.exists(st.session_state['audio_file']):
233
+ with open(st.session_state['audio_file'], 'rb') as f:
234
+ audio_bytes = f.read()
235
+ st.audio(audio_bytes, format='audio/mp3')
236
+ # Clean up the temporary audio file after use
237
+ try:
238
+ os.remove(st.session_state['audio_file'])
239
+ except:
240
+ pass
241
+ else:
242
+ st.error("Audio not generated. Please try again.")
243
+ else:
244
+ st.info("No results yet. Please upload a photo and click 'Check' in the Upload tab.")
245
+
246
+ # Clear button at the bottom
247
+ if st.button("πŸ” Clear"):
248
+ # Clean up any existing audio files
249
+ if 'audio_file' in st.session_state and st.session_state['audio_file'] and os.path.exists(st.session_state['audio_file']):
250
+ try:
251
+ os.remove(st.session_state['audio_file'])
252
+ except:
253
+ pass
254
+ st.session_state.clear()
255
+ st.rerun()
256
+
257
+ # Initial instruction audio
258
+ if not uploaded_image:
259
+ instruction = "Upload a photo of your crop leaf to check for issues."
260
+ instruction_audio = text_to_audio(instruction, "instruction.mp3")
261
+ if instruction_audio and os.path.exists(instruction_audio):
262
+ with open(instruction_audio, 'rb') as f:
263
+ audio_bytes = f.read()
264
+ st.audio(audio_bytes, format='audio/mp3')
265
+ # Clean up the temporary audio file
266
+ try:
267
+ os.remove(instruction_audio)
268
+ except:
269
+ pass
270
+
271
+ # Footer
272
+ st.markdown("---")
273
+ st.markdown('<div style="text-align: center; color: #666;">Made with ❀️ for farmers worldwide | Contact support: [email protected]</div>', unsafe_allow_html=True)