Spaces:
Build error
Build error
Update app.py
Browse files
app.py
CHANGED
@@ -1,198 +1,180 @@
|
|
1 |
import gradio as gr
|
2 |
-
|
3 |
-
import torch
|
4 |
from PIL import Image
|
5 |
-
import
|
|
|
6 |
|
7 |
-
|
8 |
-
model_name = "Salesforce/blip2-opt-2.7b"
|
9 |
-
processor = Blip2Processor.from_pretrained(model_name)
|
10 |
-
model = Blip2ForConditionalGeneration.from_pretrained(model_name, torch_dtype=torch.float16, device_map="auto")
|
11 |
-
|
12 |
-
# Base de dados nutricional
|
13 |
-
NUTRITIONAL_DB = {
|
14 |
-
"arroz": {
|
15 |
-
"calorias": 130,
|
16 |
-
"proteinas": 2.7,
|
17 |
-
"carboidratos": 28,
|
18 |
-
"gorduras": 0.3,
|
19 |
-
"fibras": 0.4,
|
20 |
-
"vitamina_b1": 0.02,
|
21 |
-
"vitamina_b3": 0.4,
|
22 |
-
"ferro": 0.2,
|
23 |
-
},
|
24 |
-
"feijão": {
|
25 |
-
"calorias": 77,
|
26 |
-
"proteinas": 5.2,
|
27 |
-
"carboidratos": 13.6,
|
28 |
-
"gorduras": 0.5,
|
29 |
-
"fibras": 5.4,
|
30 |
-
"ferro": 1.5,
|
31 |
-
"potássio": 255,
|
32 |
-
"magnésio": 42,
|
33 |
-
},
|
34 |
-
"frango": {
|
35 |
-
"calorias": 165,
|
36 |
-
"proteinas": 31,
|
37 |
-
"carboidratos": 0,
|
38 |
-
"gorduras": 3.6,
|
39 |
-
"vitamina_b6": 0.5,
|
40 |
-
"niacina": 13.7,
|
41 |
-
"fósforo": 200,
|
42 |
-
},
|
43 |
-
"salada": {
|
44 |
-
"calorias": 15,
|
45 |
-
"proteinas": 1.4,
|
46 |
-
"carboidratos": 2.9,
|
47 |
-
"gorduras": 0.2,
|
48 |
-
"fibras": 1.3,
|
49 |
-
"vitamina_a": 370,
|
50 |
-
"vitamina_k": 126,
|
51 |
-
"folato": 38,
|
52 |
-
}
|
53 |
-
}
|
54 |
-
|
55 |
-
def get_food_description(image):
|
56 |
-
"""
|
57 |
-
Usa o modelo BLIP2 para analisar a imagem e descrever os alimentos
|
58 |
-
"""
|
59 |
-
prompt = "Descreva detalhadamente todos os alimentos visíveis nesta imagem. Liste cada item separadamente."
|
60 |
-
|
61 |
-
# Processar imagem
|
62 |
-
inputs = processor(image, text=prompt, return_tensors="pt").to(model.device)
|
63 |
-
|
64 |
-
# Gerar descrição
|
65 |
-
outputs = model.generate(
|
66 |
-
**inputs,
|
67 |
-
max_new_tokens=100,
|
68 |
-
do_sample=False,
|
69 |
-
num_beams=5,
|
70 |
-
temperature=1.0,
|
71 |
-
top_p=0.9,
|
72 |
-
)
|
73 |
-
|
74 |
-
# Decodificar e retornar descrição
|
75 |
-
description = processor.decode(outputs[0], skip_special_tokens=True)
|
76 |
-
return description
|
77 |
-
|
78 |
-
def parse_food_description(description):
|
79 |
-
"""
|
80 |
-
Processa a descrição do modelo para identificar alimentos
|
81 |
-
"""
|
82 |
-
foods = []
|
83 |
-
description = description.lower()
|
84 |
-
|
85 |
-
for food in NUTRITIONAL_DB.keys():
|
86 |
-
if food in description:
|
87 |
-
# Quantidade padrão em gramas
|
88 |
-
qty = 100
|
89 |
-
foods.append((food, qty))
|
90 |
-
|
91 |
-
return foods
|
92 |
-
|
93 |
-
def analyze_food(image):
|
94 |
"""
|
95 |
-
|
96 |
"""
|
97 |
try:
|
98 |
-
#
|
99 |
-
|
100 |
-
|
101 |
-
|
102 |
-
|
103 |
-
|
104 |
-
if not identified_foods:
|
105 |
-
return "Nenhum alimento foi identificado com confiança suficiente na imagem. Por favor, tente outra foto."
|
106 |
|
107 |
-
#
|
108 |
-
|
109 |
-
|
110 |
-
|
111 |
|
112 |
-
|
113 |
-
|
114 |
-
|
115 |
-
|
116 |
-
"gorduras": 0,
|
117 |
-
"fibras": 0,
|
118 |
-
}
|
119 |
|
120 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
121 |
|
122 |
-
|
123 |
-
|
124 |
-
|
|
|
125 |
|
126 |
-
|
127 |
-
|
128 |
-
|
129 |
-
|
130 |
-
|
131 |
-
|
132 |
-
|
133 |
-
|
134 |
-
|
135 |
-
|
136 |
-
vitamins_minerals[nutrient] = 0
|
137 |
-
vitamins_minerals[nutrient] += adjusted_value
|
138 |
-
|
139 |
-
# Adiciona seção de macronutrientes
|
140 |
-
report += "\n📊 MACRONUTRIENTES:\n"
|
141 |
-
report += f"• Calorias: {total_nutrients['calorias']:.1f} kcal\n"
|
142 |
-
report += f"• Proteínas: {total_nutrients['proteinas']:.1f}g\n"
|
143 |
-
report += f"• Carboidratos: {total_nutrients['carboidratos']:.1f}g\n"
|
144 |
-
report += f"• Gorduras: {total_nutrients['gorduras']:.1f}g\n"
|
145 |
-
report += f"• Fibras: {total_nutrients['fibras']:.1f}g\n"
|
146 |
|
147 |
-
#
|
148 |
-
|
149 |
-
|
150 |
-
|
151 |
-
|
|
|
|
|
152 |
|
153 |
-
#
|
154 |
-
|
155 |
|
156 |
-
|
157 |
-
|
158 |
-
|
159 |
-
|
160 |
-
|
161 |
-
|
|
|
|
|
|
|
162 |
|
163 |
-
#
|
164 |
-
|
165 |
-
|
166 |
-
|
167 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
168 |
|
169 |
-
|
170 |
-
|
171 |
-
|
172 |
-
|
|
|
|
|
|
|
|
|
173 |
|
174 |
-
|
175 |
-
report += f"• Proteínas: {protein_pct:.1f}%\n"
|
176 |
-
report += f"• Carboidratos: {carb_pct:.1f}%\n"
|
177 |
-
report += f"• Gorduras: {fat_pct:.1f}%\n"
|
178 |
|
179 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
180 |
|
181 |
except Exception as e:
|
182 |
-
return
|
183 |
|
184 |
# Interface Gradio
|
185 |
-
|
186 |
-
|
187 |
-
|
188 |
-
outputs=gr.Textbox(label="Relatório Nutricional", lines=20),
|
189 |
-
title="🍽️ Análise Nutricional com IA",
|
190 |
-
description="""
|
191 |
Faça upload de uma foto do seu prato para receber uma análise nutricional detalhada.
|
192 |
-
|
193 |
-
|
194 |
-
|
195 |
-
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
196 |
|
|
|
197 |
if __name__ == "__main__":
|
198 |
iface.launch(share=True)
|
|
|
1 |
import gradio as gr
|
2 |
+
import json
|
|
|
3 |
from PIL import Image
|
4 |
+
import requests
|
5 |
+
from io import BytesIO
|
6 |
|
7 |
+
def process_image(image, api_key=None):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
8 |
"""
|
9 |
+
Processa a imagem usando a API de análise nutricional
|
10 |
"""
|
11 |
try:
|
12 |
+
# Prepara a imagem
|
13 |
+
if isinstance(image, str):
|
14 |
+
img = Image.open(image)
|
15 |
+
else:
|
16 |
+
img = Image.fromarray(image)
|
|
|
|
|
|
|
17 |
|
18 |
+
# Converte para formato adequado
|
19 |
+
buffered = BytesIO()
|
20 |
+
img.save(buffered, format="JPEG")
|
21 |
+
img_bytes = buffered.getvalue()
|
22 |
|
23 |
+
# Headers da requisição
|
24 |
+
headers = {}
|
25 |
+
if api_key:
|
26 |
+
headers['Authorization'] = f'Bearer {api_key}'
|
|
|
|
|
|
|
27 |
|
28 |
+
# Faz a requisição para a API
|
29 |
+
files = {'image': ('image.jpg', img_bytes, 'image/jpeg')}
|
30 |
+
response = requests.post(
|
31 |
+
'http://localhost:7860/api/predict', # Ajuste a URL conforme necessário
|
32 |
+
files=files,
|
33 |
+
headers=headers
|
34 |
+
)
|
35 |
|
36 |
+
if response.status_code == 200:
|
37 |
+
return response.json()
|
38 |
+
else:
|
39 |
+
return f"Erro na API: {response.status_code}"
|
40 |
|
41 |
+
except Exception as e:
|
42 |
+
return f"Erro no processamento: {str(e)}"
|
43 |
+
|
44 |
+
def analyze_nutrition(image, api_key=""):
|
45 |
+
"""
|
46 |
+
Função principal que processa a imagem e retorna os resultados formatados
|
47 |
+
"""
|
48 |
+
try:
|
49 |
+
# Processa a imagem
|
50 |
+
result = process_image(image, api_key)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
51 |
|
52 |
+
if isinstance(result, str): # Se for mensagem de erro
|
53 |
+
return (
|
54 |
+
result, # Texto do erro
|
55 |
+
None, # Gráfico de macronutrientes
|
56 |
+
None, # Tabela nutricional
|
57 |
+
None # Recomendações
|
58 |
+
)
|
59 |
|
60 |
+
# Extrai informações do resultado
|
61 |
+
nutrients = result.get('nutrients', {})
|
62 |
|
63 |
+
# Prepara dados para o gráfico
|
64 |
+
macro_data = {
|
65 |
+
'labels': ['Proteínas', 'Carboidratos', 'Gorduras'],
|
66 |
+
'values': [
|
67 |
+
nutrients.get('proteinas', 0),
|
68 |
+
nutrients.get('carboidratos', 0),
|
69 |
+
nutrients.get('gorduras', 0)
|
70 |
+
]
|
71 |
+
}
|
72 |
|
73 |
+
# Prepara tabela nutricional
|
74 |
+
nutri_table = {
|
75 |
+
'Nutriente': ['Calorias', 'Proteínas', 'Carboidratos', 'Gorduras', 'Fibras'],
|
76 |
+
'Quantidade': [
|
77 |
+
f"{nutrients.get('calorias', 0):.1f} kcal",
|
78 |
+
f"{nutrients.get('proteinas', 0):.1f}g",
|
79 |
+
f"{nutrients.get('carboidratos', 0):.1f}g",
|
80 |
+
f"{nutrients.get('gorduras', 0):.1f}g",
|
81 |
+
f"{nutrients.get('fibras', 0):.1f}g"
|
82 |
+
]
|
83 |
+
}
|
84 |
|
85 |
+
# Prepara recomendações
|
86 |
+
recommendations = []
|
87 |
+
if nutrients.get('calorias', 0) > 800:
|
88 |
+
recommendations.append("⚠️ Alto valor calórico - considere reduzir as porções")
|
89 |
+
if nutrients.get('proteinas', 0) < 15:
|
90 |
+
recommendations.append("⚠️ Baixo teor de proteínas - considere adicionar fontes proteicas")
|
91 |
+
if nutrients.get('fibras', 0) < 6:
|
92 |
+
recommendations.append("⚠️ Baixo teor de fibras - adicione mais vegetais")
|
93 |
|
94 |
+
recommendations = "\n".join(recommendations) if recommendations else "✅ Valores nutricionais dentro das recomendações!"
|
|
|
|
|
|
|
95 |
|
96 |
+
# Retorna todos os componentes
|
97 |
+
return (
|
98 |
+
json.dumps(result, indent=2), # Resultado completo em JSON
|
99 |
+
gr.BarPlot(
|
100 |
+
macro_data['values'],
|
101 |
+
macro_data['labels'],
|
102 |
+
title="Distribuição de Macronutrientes",
|
103 |
+
y="Gramas"
|
104 |
+
), # Gráfico
|
105 |
+
gr.Dataframe(
|
106 |
+
headers=['Nutriente', 'Quantidade'],
|
107 |
+
data=list(zip(nutri_table['Nutriente'], nutri_table['Quantidade']))
|
108 |
+
), # Tabela
|
109 |
+
recommendations # Texto de recomendações
|
110 |
+
)
|
111 |
|
112 |
except Exception as e:
|
113 |
+
return str(e), None, None, None
|
114 |
|
115 |
# Interface Gradio
|
116 |
+
with gr.Blocks(theme=gr.themes.Soft()) as iface:
|
117 |
+
gr.Markdown("""
|
118 |
+
# 🍽️ Análise Nutricional com IA
|
|
|
|
|
|
|
119 |
Faça upload de uma foto do seu prato para receber uma análise nutricional detalhada.
|
120 |
+
""")
|
121 |
+
|
122 |
+
with gr.Row():
|
123 |
+
with gr.Column():
|
124 |
+
# Inputs
|
125 |
+
image_input = gr.Image(
|
126 |
+
type="pil",
|
127 |
+
label="Foto do Prato",
|
128 |
+
sources=['upload', 'webcam']
|
129 |
+
)
|
130 |
+
api_key = gr.Textbox(
|
131 |
+
label="API Key (opcional)",
|
132 |
+
placeholder="Digite sua API key aqui...",
|
133 |
+
type="password"
|
134 |
+
)
|
135 |
+
analyze_btn = gr.Button("📊 Analisar", variant="primary")
|
136 |
+
|
137 |
+
with gr.Column():
|
138 |
+
# Outputs
|
139 |
+
with gr.Tab("Resumo"):
|
140 |
+
recommendations = gr.Textbox(
|
141 |
+
label="Recomendações",
|
142 |
+
lines=3
|
143 |
+
)
|
144 |
+
nutri_table = gr.Dataframe(
|
145 |
+
label="Tabela Nutricional",
|
146 |
+
headers=['Nutriente', 'Quantidade']
|
147 |
+
)
|
148 |
+
macro_plot = gr.Plot(
|
149 |
+
label="Macronutrientes"
|
150 |
+
)
|
151 |
+
|
152 |
+
with gr.Tab("Detalhes"):
|
153 |
+
json_output = gr.JSON(
|
154 |
+
label="Resultado Detalhado"
|
155 |
+
)
|
156 |
+
|
157 |
+
# Eventos
|
158 |
+
analyze_btn.click(
|
159 |
+
fn=analyze_nutrition,
|
160 |
+
inputs=[image_input, api_key],
|
161 |
+
outputs=[json_output, macro_plot, nutri_table, recommendations]
|
162 |
+
)
|
163 |
+
|
164 |
+
gr.Markdown("""
|
165 |
+
### 📝 Instruções
|
166 |
+
1. Faça upload de uma foto do seu prato ou tire uma foto com a webcam
|
167 |
+
2. Se tiver uma API key, insira-a (opcional)
|
168 |
+
3. Clique em "Analisar"
|
169 |
+
4. Veja os resultados nas abas "Resumo" e "Detalhes"
|
170 |
+
|
171 |
+
### 🎯 Dicas para melhores resultados
|
172 |
+
- Use fotos bem iluminadas
|
173 |
+
- Fotografe de cima para baixo
|
174 |
+
- Certifique-se que todos os alimentos estão visíveis
|
175 |
+
- Evite sombras ou reflexos fortes
|
176 |
+
""")
|
177 |
|
178 |
+
# Inicia a interface
|
179 |
if __name__ == "__main__":
|
180 |
iface.launch(share=True)
|