YourAIEngineer commited on
Commit
ba8ca57
·
verified ·
1 Parent(s): 4d798a7

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +88 -23
app.py CHANGED
@@ -28,18 +28,78 @@ OPENROUTER_BASE_URL = "https://openrouter.ai/api/v1"
28
  def initialize_ocr():
29
  """Inisialisasi model PaddleOCR tanpa parameter GPU."""
30
  try:
31
- # Hanya tentukan language dan angle classifier
32
  return PaddleOCR(lang='en', use_angle_cls=True)
33
  except Exception as e:
34
  st.error(f"Gagal inisialisasi OCR: {e}")
35
  return None
36
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
37
  def reset_state():
38
- for k in ['ocr_done','data','calculated']:
39
- st.session_state.pop(k, None)
 
40
 
41
  # --- UI APLIKASI ---
42
 
 
43
  cr = initialize_ocr()
44
  if cr is None:
45
  st.error("Model OCR tidak tersedia.")
@@ -59,7 +119,7 @@ with st.expander("📋 Petunjuk Penggunaan"):
59
 
60
  # Langkah 1: Upload
61
  st.header("1. Upload Gambar")
62
- file = st.file_uploader("Pilih gambar tabel gizi", type=["jpg","jpeg","png"], on_change=reset_state)
63
 
64
  if file:
65
  arr = np.frombuffer(file.read(), np.uint8)
@@ -68,7 +128,7 @@ if file:
68
 
69
  if st.button("Analisis OCR"):
70
  with st.spinner("Mendeteksi teks..."):
71
- res = cr.ocr(img, cls=True)
72
  texts = [ln[1][0] for ln in (res[0] if res else [])]
73
  full = " ".join(texts).lower()
74
  patterns = {
@@ -77,7 +137,7 @@ if file:
77
  'fat': r"(lemak jenuh|saturated fat)[^\d]*(\d+\.?\d*)"
78
  }
79
  data = {}
80
- for k,p in patterns.items():
81
  m = re.search(p, full)
82
  if m:
83
  data[k] = m.group(2)
@@ -91,38 +151,43 @@ if st.session_state.get('ocr_done'):
91
  st.header("2. Koreksi & Hitung Grade")
92
  d = st.session_state.data
93
  with st.form("form2"):
94
- serving = st.text_input("Takaran Saji (g/ml)", value=d.get('serving','100'))
95
- sugar = st.text_input("Gula (g)", value=d.get('sugar','0'))
96
- fat = st.text_input("Lemak Jenuh (g)", value=d.get('fat','0'))
97
  ok = st.form_submit_button("Hitung Grade")
98
  if ok:
99
  sv = parse_numeric_value(serving)
100
  sg = parse_numeric_value(sugar)
101
  fg = parse_numeric_value(fat)
102
- sp = sg/sv*100 if sv>0 else 0
103
- fp = fg/sv*100 if sv>0 else 0
104
- st.session_state.calc = {'sv':sv,'sp':sp,'fp':fp}
105
  st.session_state.calculated = True
106
 
107
  # Langkah 3: Tampilkan Hasil
108
  if st.session_state.get('calculated'):
109
  c = st.session_state.calc
110
- gs = get_grade_from_value(c['sp'],{"A":1.0,"B":5.0,"C":10.0})
111
- gf = get_grade_from_value(c['fp'],{"A":0.7,"B":1.2,"C":2.8})
112
- gf_final = max(gs,gf, key=lambda x: ['Grade A','Grade B','Grade C','Grade D'].index(x))
 
113
  st.header("3. Hasil Grading")
114
  cols = st.columns(3)
115
- def show(col,title,val,unit,gr):
116
- bc,tc = get_grade_color(gr)
117
- col.markdown(f"<div style='background:{bc};padding:10px;border-radius:8px;text-align:center;color:{tc};'>"
118
- f"<strong>{title}</strong><p>{val:.2f} {unit}</p><h4>{gr}</h4></div>",unsafe_allow_html=True)
119
- show(cols[0],"Gula",c['sp'],"g/100ml",gs)
120
- show(cols[1],"Lemak Jenuh",c['fp'],"g/100ml",gf)
121
- show(cols[2],"Grade Akhir",0,"",gf_final)
 
 
 
 
122
  st.divider()
123
  st.header("4. Saran Nutrisi AI")
124
  with st.spinner("Meminta AI..."):
125
- advice = get_nutrition_advice(c['sv'],c['sp'],c['fp'],gs,gf,gf_final)
126
  st.info(advice)
127
 
128
  # Footer
 
28
  def initialize_ocr():
29
  """Inisialisasi model PaddleOCR tanpa parameter GPU."""
30
  try:
 
31
  return PaddleOCR(lang='en', use_angle_cls=True)
32
  except Exception as e:
33
  st.error(f"Gagal inisialisasi OCR: {e}")
34
  return None
35
 
36
+
37
+ def parse_numeric_value(text: str) -> float:
38
+ """Parse string menjadi float dengan menghapus karakter non-numerik."""
39
+ cleaned = re.sub(r"[^\d\.\-]", "", str(text))
40
+ if cleaned in ['', '.', '-']:
41
+ return 0.0
42
+ try:
43
+ return float(cleaned)
44
+ except:
45
+ return 0.0
46
+
47
+
48
+ def get_nutrition_advice(serving_size, sugar_norm, fat_norm, sugar_grade, fat_grade, final_grade):
49
+ """Memanggil API OpenRouter untuk mendapatkan saran nutrisi."""
50
+ prompt = f"""
51
+ Anda adalah ahli gizi dari Indonesia yang ramah.
52
+ - Takaran Saji: {serving_size} g/ml
53
+ - Gula (per 100): {sugar_norm:.2f} g (Grade {sugar_grade.replace('Grade ', '')})
54
+ - Lemak Jenuh (per 100): {fat_norm:.2f} g (Grade {fat_grade.replace('Grade ', '')})
55
+ - Grade Akhir: {final_grade.replace('Grade ', '')}
56
+
57
+ Berikan saran nutrisi singkat 50-80 kata, fokus pada dampak kesehatan dan tips praktis.
58
+ """
59
+ headers = {"Authorization": f"Bearer {OPENROUTER_API_KEY}", "Content-Type": "application/json"}
60
+ payload = {
61
+ "model": "mistralai/mistral-7b-instruct:free",
62
+ "messages": [{"role": "user", "content": prompt}],
63
+ "max_tokens": 250,
64
+ "temperature": 0.7
65
+ }
66
+ try:
67
+ r = requests.post(f"{OPENROUTER_BASE_URL}/chat/completions", headers=headers, json=payload, timeout=30)
68
+ r.raise_for_status()
69
+ data = r.json()
70
+ return data["choices"][0]["message"]["content"].strip()
71
+ except Exception as e:
72
+ return f"Error: {e}"
73
+
74
+
75
+ def get_grade_from_value(value, thresholds):
76
+ if value <= thresholds["A"]:
77
+ return "Grade A"
78
+ if value <= thresholds["B"]:
79
+ return "Grade B"
80
+ if value <= thresholds["C"]:
81
+ return "Grade C"
82
+ return "Grade D"
83
+
84
+
85
+ def get_grade_color(grade):
86
+ colors = {
87
+ "Grade A": ("#2ecc71", "white"),
88
+ "Grade B": ("#f1c40f", "black"),
89
+ "Grade C": ("#e67e22", "white"),
90
+ "Grade D": ("#e74c3c", "white")
91
+ }
92
+ return colors.get(grade, ("#bdc3c7", "black"))
93
+
94
+
95
  def reset_state():
96
+ for k in ['ocr_done', 'data', 'calculated', 'calc']:
97
+ if k in st.session_state:
98
+ del st.session_state[k]
99
 
100
  # --- UI APLIKASI ---
101
 
102
+ # Inisialisasi OCR
103
  cr = initialize_ocr()
104
  if cr is None:
105
  st.error("Model OCR tidak tersedia.")
 
119
 
120
  # Langkah 1: Upload
121
  st.header("1. Upload Gambar")
122
+ file = st.file_uploader("Pilih gambar tabel gizi", type=["jpg", "jpeg", "png"], on_change=reset_state)
123
 
124
  if file:
125
  arr = np.frombuffer(file.read(), np.uint8)
 
128
 
129
  if st.button("Analisis OCR"):
130
  with st.spinner("Mendeteksi teks..."):
131
+ res = cr.ocr(img)
132
  texts = [ln[1][0] for ln in (res[0] if res else [])]
133
  full = " ".join(texts).lower()
134
  patterns = {
 
137
  'fat': r"(lemak jenuh|saturated fat)[^\d]*(\d+\.?\d*)"
138
  }
139
  data = {}
140
+ for k, p in patterns.items():
141
  m = re.search(p, full)
142
  if m:
143
  data[k] = m.group(2)
 
151
  st.header("2. Koreksi & Hitung Grade")
152
  d = st.session_state.data
153
  with st.form("form2"):
154
+ serving = st.text_input("Takaran Saji (g/ml)", value=d.get('serving', '100'))
155
+ sugar = st.text_input("Gula (g)", value=d.get('sugar', '0'))
156
+ fat = st.text_input("Lemak Jenuh (g)", value=d.get('fat', '0'))
157
  ok = st.form_submit_button("Hitung Grade")
158
  if ok:
159
  sv = parse_numeric_value(serving)
160
  sg = parse_numeric_value(sugar)
161
  fg = parse_numeric_value(fat)
162
+ sp = (sg / sv) * 100 if sv > 0 else 0
163
+ fp = (fg / sv) * 100 if sv > 0 else 0
164
+ st.session_state.calc = {'sv': sv, 'sp': sp, 'fp': fp}
165
  st.session_state.calculated = True
166
 
167
  # Langkah 3: Tampilkan Hasil
168
  if st.session_state.get('calculated'):
169
  c = st.session_state.calc
170
+ gs = get_grade_from_value(c['sp'], {"A": 1.0, "B": 5.0, "C": 10.0})
171
+ gf = get_grade_from_value(c['fp'], {"A": 0.7, "B": 1.2, "C": 2.8})
172
+ final_grade = max(gs, gf, key=lambda x: ['Grade A', 'Grade B', 'Grade C', 'Grade D'].index(x))
173
+
174
  st.header("3. Hasil Grading")
175
  cols = st.columns(3)
176
+ def show(col, title, value, unit, grade):
177
+ bg, textc = get_grade_color(grade)
178
+ col.markdown(
179
+ f"<div style='background:{bg};padding:10px;border-radius:8px;text-align:center;color:{textc};'>"
180
+ f"<strong>{title}</strong><p>{value:.2f} {unit}</p><h4>{grade}</h4></div>",
181
+ unsafe_allow_html=True
182
+ )
183
+ show(cols[0], "Gula", c['sp'], "g/100ml", gs)
184
+ show(cols[1], "Lemak Jenuh", c['fp'], "g/100ml", gf)
185
+ show(cols[2], "Grade Akhir", 0, "", final_grade)
186
+
187
  st.divider()
188
  st.header("4. Saran Nutrisi AI")
189
  with st.spinner("Meminta AI..."):
190
+ advice = get_nutrition_advice(c['sv'], c['sp'], c['fp'], gs, gf, final_grade)
191
  st.info(advice)
192
 
193
  # Footer