Spaces:
Build error
Build error
Update app.py
Browse files
app.py
CHANGED
@@ -1,120 +1,72 @@
|
|
1 |
import gradio as gr
|
2 |
import torch
|
|
|
3 |
import pandas as pd
|
4 |
-
import numpy as np
|
5 |
from PIL import Image
|
6 |
-
import re
|
7 |
|
8 |
-
#
|
9 |
-
|
10 |
-
|
11 |
-
"feijão": ["feijão preto", "feijão carioca", "feijão vermelho", "feijões"],
|
12 |
-
"frango": ["frango grelhado", "peito de frango", "filé de frango", "frango assado", "chicken"],
|
13 |
-
"salada": ["alface", "tomate", "verduras", "legumes", "vegetais", "salada verde"],
|
14 |
-
"batata": ["batata cozida", "batata assada", "purê de batata", "potato"],
|
15 |
-
"carne": ["carne vermelha", "bife", "filé", "carne assada", "beef"],
|
16 |
-
"peixe": ["filé de peixe", "peixe grelhado", "peixe assado", "fish"],
|
17 |
-
"macarrão": ["espaguete", "massa", "pasta", "macarrão integral", "noodles"],
|
18 |
-
"ovo": ["ovo frito", "ovo cozido", "ovos", "omelete", "eggs"]
|
19 |
-
}
|
20 |
|
|
|
21 |
NUTRITION_DB = {
|
22 |
"arroz": {"calorias": 130, "proteinas": 2.7, "carboidratos": 28, "gorduras": 0.3},
|
23 |
"feijão": {"calorias": 77, "proteinas": 5.2, "carboidratos": 13.6, "gorduras": 0.5},
|
24 |
-
"frango": {"calorias": 165, "proteinas": 31, "carboidratos": 0, "gorduras": 3.6},
|
25 |
-
"salada": {"calorias": 15, "proteinas": 1.4, "carboidratos": 2.9, "gorduras": 0.2},
|
26 |
-
"batata": {"calorias": 93, "proteinas": 2.5, "carboidratos": 21, "gorduras": 0.1},
|
27 |
"carne": {"calorias": 250, "proteinas": 26, "carboidratos": 0, "gorduras": 17},
|
28 |
-
"
|
29 |
-
"
|
30 |
-
"ovo": {"calorias": 155, "proteinas": 13, "carboidratos": 1.1, "gorduras": 11},
|
31 |
}
|
32 |
|
33 |
-
def
|
34 |
-
"""
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
|
43 |
-
|
44 |
-
|
45 |
-
|
46 |
-
|
47 |
-
|
48 |
-
|
49 |
-
|
50 |
-
|
51 |
-
|
52 |
-
|
53 |
-
|
54 |
-
|
55 |
-
|
56 |
-
|
57 |
-
|
58 |
-
|
59 |
-
|
60 |
-
|
61 |
-
|
62 |
-
"
|
63 |
-
"refeição": 0.1,
|
64 |
-
"porção": 0.1,
|
65 |
-
"servido": 0.1,
|
66 |
-
"preparado": 0.1
|
67 |
}
|
68 |
|
69 |
-
|
70 |
-
confidence_boost = 0
|
71 |
-
for clue, value in context_clues.items():
|
72 |
-
if clue in description:
|
73 |
-
confidence_boost += value
|
74 |
-
|
75 |
-
if confidence_boost > 0:
|
76 |
-
found_foods[food]["confianca"] = "Alta" if confidence_boost > 0.2 else found_foods[food]["confianca"]
|
77 |
|
78 |
-
|
79 |
-
|
80 |
-
|
81 |
-
|
82 |
-
|
83 |
-
found_foods[food]["preparo"] = method
|
84 |
|
85 |
-
return found_foods
|
86 |
|
87 |
-
def
|
88 |
-
"""
|
89 |
try:
|
90 |
-
#
|
91 |
-
|
92 |
-
|
93 |
-
if not identified_foods:
|
94 |
-
return "Nenhum alimento conhecido identificado com confiança.", None, None
|
95 |
|
96 |
-
#
|
97 |
-
total_nutrients =
|
98 |
-
"calorias": 0,
|
99 |
-
"proteinas": 0,
|
100 |
-
"carboidratos": 0,
|
101 |
-
"gorduras": 0
|
102 |
-
}
|
103 |
-
|
104 |
-
# Lista detalhada de alimentos identificados
|
105 |
-
foods_detail = []
|
106 |
-
for food, info in identified_foods.items():
|
107 |
-
foods_detail.append(f"• {food.capitalize()}")
|
108 |
-
foods_detail.append(f" - Confiança: {info['confianca']}")
|
109 |
-
foods_detail.append(f" - Menções: {info['mencoes']}")
|
110 |
-
foods_detail.append(f" - Variação encontrada: {info['variacao']}")
|
111 |
-
if 'preparo' in info:
|
112 |
-
foods_detail.append(f" - Método de preparo: {info['preparo']}")
|
113 |
-
foods_detail.append("") # Linha em branco para separar
|
114 |
-
|
115 |
-
# Soma nutrientes
|
116 |
-
for nutrient, value in NUTRITION_DB[food].items():
|
117 |
-
total_nutrients[nutrient] += value
|
118 |
|
119 |
# Prepara dados para visualização
|
120 |
table_data = [
|
@@ -134,91 +86,59 @@ def analyze_foods(description):
|
|
134 |
]
|
135 |
})
|
136 |
|
137 |
-
|
|
|
138 |
{description}
|
139 |
|
140 |
-
###
|
141 |
-
{
|
142 |
|
143 |
-
### 📊
|
144 |
-
• Calorias
|
145 |
• Proteínas: {total_nutrients['proteinas']:.1f}g
|
146 |
• Carboidratos: {total_nutrients['carboidratos']:.1f}g
|
147 |
• Gorduras: {total_nutrients['gorduras']:.1f}g
|
148 |
|
149 |
-
### 💡
|
150 |
-
{"
|
151 |
-
{"
|
152 |
-
{"
|
153 |
"""
|
154 |
|
155 |
return analysis, table_data, plot_data
|
156 |
|
157 |
-
except Exception as e:
|
158 |
-
raise gr.Error(f"Erro na análise: {str(e)}")
|
159 |
-
|
160 |
-
def analyze_image(image):
|
161 |
-
"""Função principal que coordena o processo de análise"""
|
162 |
-
try:
|
163 |
-
# Simula a resposta do modelo (temporário até resolvermos o erro do DeepSeek)
|
164 |
-
description = "Na imagem é possível ver um prato contendo arroz branco cozido, feijão preto, um pedaço de frango grelhado servido com salada de alface e tomate. O prato parece uma refeição completa preparada recentemente."
|
165 |
-
|
166 |
-
# Analisa os alimentos
|
167 |
-
analysis, table_data, plot_data = analyze_foods(description)
|
168 |
-
|
169 |
-
return analysis, table_data, plot_data
|
170 |
-
|
171 |
except Exception as e:
|
172 |
return str(e), None, None
|
173 |
|
174 |
# Interface Gradio
|
175 |
with gr.Blocks(theme=gr.themes.Soft()) as iface:
|
176 |
-
gr.Markdown(""
|
177 |
-
# 🍽️ Análise Nutricional com IA
|
178 |
-
Faça upload de uma foto do seu prato para análise nutricional detalhada.
|
179 |
-
""")
|
180 |
|
181 |
with gr.Row():
|
182 |
-
# Coluna de Input
|
183 |
with gr.Column():
|
184 |
image_input = gr.Image(
|
185 |
type="pil",
|
186 |
label="Foto do Prato",
|
187 |
sources=["upload", "webcam"]
|
188 |
)
|
189 |
-
analyze_btn = gr.Button("📊 Analisar", variant="primary"
|
190 |
-
|
191 |
-
with gr.Accordion("📝 Dicas", open=False):
|
192 |
-
gr.Markdown("""
|
193 |
-
- Use fotos bem iluminadas
|
194 |
-
- Fotografe de cima para baixo
|
195 |
-
- Certifique-se que todos os alimentos estão visíveis
|
196 |
-
- Evite sombras ou reflexos fortes
|
197 |
-
""")
|
198 |
|
199 |
-
# Coluna de Output
|
200 |
with gr.Column():
|
201 |
-
# Análise textual
|
202 |
output_text = gr.Markdown()
|
203 |
|
204 |
with gr.Row():
|
205 |
-
# Tabela nutricional
|
206 |
output_table = gr.Dataframe(
|
207 |
headers=["Nutriente", "Quantidade"],
|
208 |
-
label="Informação Nutricional"
|
209 |
-
wrap=True
|
210 |
)
|
211 |
|
212 |
-
# Gráfico
|
213 |
output_plot = gr.BarPlot(
|
214 |
x="Nutriente",
|
215 |
y="Quantidade",
|
216 |
title="Macronutrientes (g)",
|
217 |
-
height=300
|
218 |
-
tooltip=["Nutriente", "Quantidade"]
|
219 |
)
|
220 |
|
221 |
-
# Eventos
|
222 |
analyze_btn.click(
|
223 |
fn=analyze_image,
|
224 |
inputs=[image_input],
|
|
|
1 |
import gradio as gr
|
2 |
import torch
|
3 |
+
from transformers import AutoModel, AutoProcessor
|
4 |
import pandas as pd
|
|
|
5 |
from PIL import Image
|
|
|
6 |
|
7 |
+
# Carrega o modelo
|
8 |
+
model = AutoModel.from_pretrained("openbmb/MiniCPM-o-2_6", trust_remote_code=True)
|
9 |
+
processor = AutoProcessor.from_pretrained("openbmb/MiniCPM-o-2_6", trust_remote_code=True)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
10 |
|
11 |
+
# Base de dados nutricional
|
12 |
NUTRITION_DB = {
|
13 |
"arroz": {"calorias": 130, "proteinas": 2.7, "carboidratos": 28, "gorduras": 0.3},
|
14 |
"feijão": {"calorias": 77, "proteinas": 5.2, "carboidratos": 13.6, "gorduras": 0.5},
|
|
|
|
|
|
|
15 |
"carne": {"calorias": 250, "proteinas": 26, "carboidratos": 0, "gorduras": 17},
|
16 |
+
"batata frita": {"calorias": 312, "proteinas": 3.4, "carboidratos": 41, "gorduras": 15},
|
17 |
+
"salada": {"calorias": 15, "proteinas": 1.4, "carboidratos": 2.9, "gorduras": 0.2}
|
|
|
18 |
}
|
19 |
|
20 |
+
def process_image(image, progress=gr.Progress()):
|
21 |
+
"""Processa a imagem usando o modelo MiniCPM"""
|
22 |
+
try:
|
23 |
+
progress(0.3, desc="Processando imagem...")
|
24 |
+
|
25 |
+
# Prepara a imagem
|
26 |
+
if isinstance(image, str):
|
27 |
+
image = Image.open(image)
|
28 |
+
|
29 |
+
# Processa a imagem com o modelo
|
30 |
+
inputs = processor(images=image, text="What foods are in this image?", return_tensors="pt")
|
31 |
+
|
32 |
+
progress(0.6, desc="Analisando conteúdo...")
|
33 |
+
# Gera a descrição
|
34 |
+
outputs = model.generate(**inputs, max_length=100)
|
35 |
+
description = processor.decode(outputs[0], skip_special_tokens=True)
|
36 |
+
|
37 |
+
progress(1.0, desc="Concluído!")
|
38 |
+
return description
|
39 |
+
|
40 |
+
except Exception as e:
|
41 |
+
raise gr.Error(f"Erro no processamento: {str(e)}")
|
42 |
+
|
43 |
+
def analyze_nutrition(foods_list):
|
44 |
+
"""Analisa nutrientes dos alimentos identificados"""
|
45 |
+
total_nutrients = {
|
46 |
+
"calorias": 0,
|
47 |
+
"proteinas": 0,
|
48 |
+
"carboidratos": 0,
|
49 |
+
"gorduras": 0
|
|
|
|
|
|
|
|
|
50 |
}
|
51 |
|
52 |
+
found_foods = []
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
53 |
|
54 |
+
for food in NUTRITION_DB.keys():
|
55 |
+
if food.lower() in foods_list.lower():
|
56 |
+
found_foods.append(food)
|
57 |
+
for nutrient, value in NUTRITION_DB[food].items():
|
58 |
+
total_nutrients[nutrient] += value
|
|
|
59 |
|
60 |
+
return total_nutrients, found_foods
|
61 |
|
62 |
+
def analyze_image(image):
|
63 |
+
"""Função principal de análise"""
|
64 |
try:
|
65 |
+
# Processa a imagem
|
66 |
+
description = process_image(image)
|
|
|
|
|
|
|
67 |
|
68 |
+
# Analisa nutrientes
|
69 |
+
total_nutrients, found_foods = analyze_nutrition(description)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
70 |
|
71 |
# Prepara dados para visualização
|
72 |
table_data = [
|
|
|
86 |
]
|
87 |
})
|
88 |
|
89 |
+
# Monta o relatório
|
90 |
+
analysis = f"""### 🔍 Análise da Imagem:
|
91 |
{description}
|
92 |
|
93 |
+
### 🍽️ Alimentos Identificados:
|
94 |
+
{', '.join(found_foods)}
|
95 |
|
96 |
+
### 📊 Informação Nutricional:
|
97 |
+
• Calorias: {total_nutrients['calorias']:.1f} kcal
|
98 |
• Proteínas: {total_nutrients['proteinas']:.1f}g
|
99 |
• Carboidratos: {total_nutrients['carboidratos']:.1f}g
|
100 |
• Gorduras: {total_nutrients['gorduras']:.1f}g
|
101 |
|
102 |
+
### 💡 Recomendações:
|
103 |
+
{"⚠️ Alto teor calórico" if total_nutrients['calorias'] > 800 else "✅ Calorias adequadas"}
|
104 |
+
{"⚠️ Considere reduzir carboidratos" if total_nutrients['carboidratos'] > 60 else "✅ Carboidratos adequados"}
|
105 |
+
{"⚠️ Alto teor de gorduras" if total_nutrients['gorduras'] > 20 else "✅ Gorduras adequadas"}
|
106 |
"""
|
107 |
|
108 |
return analysis, table_data, plot_data
|
109 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
110 |
except Exception as e:
|
111 |
return str(e), None, None
|
112 |
|
113 |
# Interface Gradio
|
114 |
with gr.Blocks(theme=gr.themes.Soft()) as iface:
|
115 |
+
gr.Markdown("# 🍽️ Análise Nutricional com IA")
|
|
|
|
|
|
|
116 |
|
117 |
with gr.Row():
|
|
|
118 |
with gr.Column():
|
119 |
image_input = gr.Image(
|
120 |
type="pil",
|
121 |
label="Foto do Prato",
|
122 |
sources=["upload", "webcam"]
|
123 |
)
|
124 |
+
analyze_btn = gr.Button("📊 Analisar", variant="primary")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
125 |
|
|
|
126 |
with gr.Column():
|
|
|
127 |
output_text = gr.Markdown()
|
128 |
|
129 |
with gr.Row():
|
|
|
130 |
output_table = gr.Dataframe(
|
131 |
headers=["Nutriente", "Quantidade"],
|
132 |
+
label="Informação Nutricional"
|
|
|
133 |
)
|
134 |
|
|
|
135 |
output_plot = gr.BarPlot(
|
136 |
x="Nutriente",
|
137 |
y="Quantidade",
|
138 |
title="Macronutrientes (g)",
|
139 |
+
height=300
|
|
|
140 |
)
|
141 |
|
|
|
142 |
analyze_btn.click(
|
143 |
fn=analyze_image,
|
144 |
inputs=[image_input],
|