rdsarjito commited on
Commit
16d78bc
Β·
1 Parent(s): 39e7123

Update kode untuk fitur baru

Browse files
Files changed (1) hide show
  1. app.py +157 -4
app.py CHANGED
@@ -2,6 +2,10 @@ import streamlit as st
2
  import pickle
3
  import requests
4
  from bs4 import BeautifulSoup
 
 
 
 
5
 
6
  # === Custom CSS for better styling ===
7
  def load_css():
@@ -40,6 +44,16 @@ def load_css():
40
  margin: 1rem 0;
41
  }
42
 
 
 
 
 
 
 
 
 
 
 
43
  /* Results styling - matching the image design */
44
  .allergen-result {
45
  padding: 1rem 1.5rem;
@@ -75,6 +89,23 @@ def load_css():
75
  text-align: center;
76
  }
77
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
78
  /* Button styling */
79
  .stButton > button {
80
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
@@ -92,6 +123,11 @@ def load_css():
92
  box-shadow: 0 6px 8px rgba(0, 0, 0, 0.15);
93
  }
94
 
 
 
 
 
 
95
  /* Radio button styling */
96
  .stRadio > div {
97
  background: white;
@@ -155,6 +191,17 @@ def load_css():
155
  </style>
156
  """, unsafe_allow_html=True)
157
 
 
 
 
 
 
 
 
 
 
 
 
158
  # === Load TF-IDF Vectorizer ===
159
  @st.cache_resource
160
  def load_vectorizer():
@@ -167,6 +214,37 @@ def load_model():
167
  with open("saved_models/XGBoost_model.pkl", "rb") as f:
168
  return pickle.load(f)
169
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
170
  # === Prediksi ===
171
  def predict_allergen(model, vectorizer, input_text):
172
  X_input = vectorizer.transform([input_text])
@@ -228,6 +306,27 @@ def get_ingredients_from_cookpad(url):
228
  except Exception as e:
229
  return None, f"Terjadi kesalahan: {str(e)}"
230
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
231
  # === Display results with custom styling matching the image ===
232
  def display_results(results, probabilities, labels):
233
  st.markdown("### 🎯 Hasil Analisis Alergen")
@@ -307,7 +406,7 @@ def main():
307
  st.markdown("""
308
  <div class="main-header">
309
  <h1>πŸ₯˜ Deteksi Alergen Makanan</h1>
310
- <p>Analisis kandungan alergen dalam resep makanan dengan teknologi AI</p>
311
  </div>
312
  """, unsafe_allow_html=True)
313
 
@@ -325,9 +424,17 @@ def main():
325
 
326
  st.markdown("### πŸ’‘ Tips Penggunaan")
327
  st.markdown("""
 
328
  - Masukkan bahan dengan detail
329
  - Gunakan nama bahan dalam bahasa Indonesia
330
- - Untuk URL Cookpad, pastikan link valid
 
 
 
 
 
 
 
331
  - Maksimal 20 URL per analisis
332
  """)
333
 
@@ -339,7 +446,7 @@ def main():
339
  st.markdown("### πŸ”§ Pilih Metode Input")
340
  input_mode = st.radio(
341
  "",
342
- ["πŸ“ Input Manual", "πŸ”— URL Cookpad"],
343
  horizontal=True
344
  )
345
 
@@ -384,6 +491,52 @@ def main():
384
  st.success("βœ… Analisis selesai!")
385
  display_results(results, probs, labels)
386
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
387
  elif input_mode == "πŸ”— URL Cookpad":
388
  st.markdown("### πŸ”— Analisis dari URL Cookpad")
389
 
@@ -458,7 +611,7 @@ def main():
458
  # Footer
459
  st.markdown("""
460
  <div class="footer">
461
- <p>πŸ”¬ Powered by XGBoost & TF-IDF | Made with ❀️ using Streamlit</p>
462
  </div>
463
  """, unsafe_allow_html=True)
464
 
 
2
  import pickle
3
  import requests
4
  from bs4 import BeautifulSoup
5
+ import easyocr
6
+ import numpy as np
7
+ from PIL import Image
8
+ import cv2
9
 
10
  # === Custom CSS for better styling ===
11
  def load_css():
 
44
  margin: 1rem 0;
45
  }
46
 
47
+ /* Camera card styling */
48
+ .camera-card {
49
+ background: linear-gradient(135deg, #f8f9fa, #e9ecef);
50
+ padding: 1.5rem;
51
+ border-radius: 10px;
52
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
53
+ border-left: 4px solid #28a745;
54
+ margin: 1rem 0;
55
+ }
56
+
57
  /* Results styling - matching the image design */
58
  .allergen-result {
59
  padding: 1rem 1.5rem;
 
89
  text-align: center;
90
  }
91
 
92
+ /* OCR result styling */
93
+ .ocr-result {
94
+ background: #f8f9fa;
95
+ padding: 1rem 1.5rem;
96
+ margin: 1rem 0;
97
+ border-radius: 10px;
98
+ border-left: 4px solid #17a2b8;
99
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
100
+ line-height: 1.6;
101
+ font-size: 1rem;
102
+ }
103
+
104
+ .ocr-result strong {
105
+ color: #495057;
106
+ font-weight: 600;
107
+ }
108
+
109
  /* Button styling */
110
  .stButton > button {
111
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
 
123
  box-shadow: 0 6px 8px rgba(0, 0, 0, 0.15);
124
  }
125
 
126
+ /* Camera button styling */
127
+ .camera-button {
128
+ background: linear-gradient(135deg, #28a745 0%, #20c997 100%) !important;
129
+ }
130
+
131
  /* Radio button styling */
132
  .stRadio > div {
133
  background: white;
 
191
  </style>
192
  """, unsafe_allow_html=True)
193
 
194
+ # === Load EasyOCR Reader ===
195
+ @st.cache_resource
196
+ def load_ocr_reader():
197
+ """Load EasyOCR reader with Indonesian and English language support"""
198
+ try:
199
+ reader = easyocr.Reader(['id', 'en'], gpu=False) # Indonesian and English
200
+ return reader
201
+ except Exception as e:
202
+ st.error(f"❌ Gagal memuat EasyOCR: {str(e)}")
203
+ return None
204
+
205
  # === Load TF-IDF Vectorizer ===
206
  @st.cache_resource
207
  def load_vectorizer():
 
214
  with open("saved_models/XGBoost_model.pkl", "rb") as f:
215
  return pickle.load(f)
216
 
217
+ # === OCR Text Extraction ===
218
+ def extract_text_from_image(image, reader):
219
+ """Extract text from image using EasyOCR"""
220
+ try:
221
+ # Convert PIL image to numpy array
222
+ if isinstance(image, Image.Image):
223
+ image_array = np.array(image)
224
+ else:
225
+ image_array = image
226
+
227
+ # Perform OCR
228
+ results = reader.readtext(image_array)
229
+
230
+ # Extract text from results
231
+ extracted_texts = []
232
+ confidence_scores = []
233
+
234
+ for (bbox, text, confidence) in results:
235
+ if confidence > 0.3: # Filter out low confidence text
236
+ extracted_texts.append(text)
237
+ confidence_scores.append(confidence)
238
+
239
+ # Join all extracted text
240
+ full_text = " ".join(extracted_texts)
241
+ avg_confidence = np.mean(confidence_scores) if confidence_scores else 0
242
+
243
+ return full_text, extracted_texts, avg_confidence
244
+
245
+ except Exception as e:
246
+ return "", [], 0
247
+
248
  # === Prediksi ===
249
  def predict_allergen(model, vectorizer, input_text):
250
  X_input = vectorizer.transform([input_text])
 
306
  except Exception as e:
307
  return None, f"Terjadi kesalahan: {str(e)}"
308
 
309
+ # === Display OCR Results ===
310
+ def display_ocr_results(extracted_text, text_list, confidence):
311
+ """Display OCR extraction results"""
312
+ st.markdown("### πŸ“– Hasil Ekstraksi Teks")
313
+
314
+ if extracted_text.strip():
315
+ st.markdown(f'''
316
+ <div class="ocr-result">
317
+ <strong>πŸ“ Teks yang Terdeteksi:</strong><br>
318
+ {extracted_text}
319
+ </div>
320
+ ''', unsafe_allow_html=True)
321
+
322
+ # Show confidence and individual text elements
323
+ with st.expander(f"πŸ“Š Detail OCR (Confidence: {confidence:.2f})", expanded=False):
324
+ st.markdown("**Teks Individual yang Terdeteksi:**")
325
+ for i, text in enumerate(text_list, 1):
326
+ st.write(f"{i}. {text}")
327
+ else:
328
+ st.warning("⚠️ Tidak ada teks yang dapat diekstrak dari gambar. Pastikan gambar memiliki teks yang jelas dan terbaca.")
329
+
330
  # === Display results with custom styling matching the image ===
331
  def display_results(results, probabilities, labels):
332
  st.markdown("### 🎯 Hasil Analisis Alergen")
 
406
  st.markdown("""
407
  <div class="main-header">
408
  <h1>πŸ₯˜ Deteksi Alergen Makanan</h1>
409
+ <p>Analisis kandungan alergen dalam resep makanan dengan teknologi AI & OCR</p>
410
  </div>
411
  """, unsafe_allow_html=True)
412
 
 
424
 
425
  st.markdown("### πŸ’‘ Tips Penggunaan")
426
  st.markdown("""
427
+ **Input Manual:**
428
  - Masukkan bahan dengan detail
429
  - Gunakan nama bahan dalam bahasa Indonesia
430
+
431
+ **Kamera OCR:**
432
+ - Pastikan teks terlihat jelas
433
+ - Gunakan pencahayaan yang baik
434
+ - Hindari blur atau teks terpotong
435
+
436
+ **URL Cookpad:**
437
+ - Pastikan link valid
438
  - Maksimal 20 URL per analisis
439
  """)
440
 
 
446
  st.markdown("### πŸ”§ Pilih Metode Input")
447
  input_mode = st.radio(
448
  "",
449
+ ["πŸ“ Input Manual", "πŸ“· Kamera OCR", "πŸ”— URL Cookpad"],
450
  horizontal=True
451
  )
452
 
 
491
  st.success("βœ… Analisis selesai!")
492
  display_results(results, probs, labels)
493
 
494
+ elif input_mode == "πŸ“· Kamera OCR":
495
+ st.markdown("### πŸ“· Deteksi Alergen dari Gambar")
496
+
497
+ # Info card for camera
498
+ st.markdown("""
499
+ <div class="camera-card">
500
+ <strong>πŸ“· Petunjuk Kamera:</strong> Ambil foto langsung dari daftar bahan, kemasan makanan,
501
+ atau resep. Pastikan teks terlihat jelas dan pencahayaan memadai untuk hasil OCR terbaik.
502
+ </div>
503
+ """, unsafe_allow_html=True)
504
+
505
+ # Camera input
506
+ camera_image = st.camera_input("πŸ“Έ Ambil foto dengan kamera")
507
+
508
+ if camera_image is not None:
509
+ # Display the captured image
510
+ st.image(camera_image, caption="πŸ“· Gambar yang diambil", use_column_width=True)
511
+
512
+ # Load OCR reader
513
+ with st.spinner("πŸ”„ Memuat OCR engine..."):
514
+ reader = load_ocr_reader()
515
+
516
+ if reader is None:
517
+ st.error("❌ Gagal memuat OCR engine. Pastikan EasyOCR telah terinstall.")
518
+ else:
519
+ col_btn1, col_btn2, col_btn3 = st.columns([2, 2, 2])
520
+ with col_btn2:
521
+ if st.button("πŸ” Ekstrak Teks & Analisis", use_container_width=True, key="ocr_analyze"):
522
+ # Extract text from image
523
+ with st.spinner("πŸ“– Mengekstrak teks dari gambar..."):
524
+ extracted_text, text_list, confidence = extract_text_from_image(camera_image, reader)
525
+
526
+ if extracted_text.strip():
527
+ # Display OCR results
528
+ display_ocr_results(extracted_text, text_list, confidence)
529
+
530
+ # Analyze allergens
531
+ with st.spinner("πŸ”„ Menganalisis alergen..."):
532
+ pred, probs = predict_allergen(model, vectorizer, extracted_text)
533
+ results = dict(zip(labels, pred))
534
+
535
+ st.success("βœ… Analisis selesai!")
536
+ display_results(results, probs, labels)
537
+ else:
538
+ st.warning("⚠️ Tidak dapat mengekstrak teks dari gambar. Coba ambil foto dengan pencahayaan yang lebih baik dan pastikan teks terlihat jelas.")
539
+
540
  elif input_mode == "πŸ”— URL Cookpad":
541
  st.markdown("### πŸ”— Analisis dari URL Cookpad")
542
 
 
611
  # Footer
612
  st.markdown("""
613
  <div class="footer">
614
+ <p>πŸ”¬ Powered by XGBoost, TF-IDF & EasyOCR | Made with ❀️ using Streamlit</p>
615
  </div>
616
  """, unsafe_allow_html=True)
617