Spaces:
Build error
Build error
Update app.py
Browse files
app.py
CHANGED
@@ -1,13 +1,23 @@
|
|
1 |
import gradio as gr
|
2 |
import torch
|
3 |
-
from transformers import AutoModelForCausalLM, AutoTokenizer
|
4 |
import pandas as pd
|
5 |
import numpy as np
|
6 |
from PIL import Image
|
7 |
-
|
8 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
9 |
|
10 |
-
# Base de dados nutricional
|
11 |
NUTRITION_DB = {
|
12 |
"arroz": {"calorias": 130, "proteinas": 2.7, "carboidratos": 28, "gorduras": 0.3},
|
13 |
"feijão": {"calorias": 77, "proteinas": 5.2, "carboidratos": 13.6, "gorduras": 0.5},
|
@@ -20,17 +30,68 @@ NUTRITION_DB = {
|
|
20 |
"ovo": {"calorias": 155, "proteinas": 13, "carboidratos": 1.1, "gorduras": 11},
|
21 |
}
|
22 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
23 |
def analyze_foods(description):
|
24 |
"""Analisa a descrição e retorna informações nutricionais"""
|
25 |
try:
|
26 |
-
#
|
27 |
-
|
28 |
-
for food in NUTRITION_DB.keys():
|
29 |
-
if food in description.lower():
|
30 |
-
found_foods.append(food)
|
31 |
|
32 |
-
if not
|
33 |
-
return "Nenhum alimento conhecido identificado.", None, None
|
34 |
|
35 |
# Calcula nutrientes
|
36 |
total_nutrients = {
|
@@ -40,7 +101,17 @@ def analyze_foods(description):
|
|
40 |
"gorduras": 0
|
41 |
}
|
42 |
|
43 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
44 |
for nutrient, value in NUTRITION_DB[food].items():
|
45 |
total_nutrients[nutrient] += value
|
46 |
|
@@ -66,7 +137,7 @@ def analyze_foods(description):
|
|
66 |
{description}
|
67 |
|
68 |
### 🔍 Alimentos Identificados:
|
69 |
-
|
70 |
|
71 |
### 📊 Análise Nutricional:
|
72 |
• Calorias Totais: {total_nutrients['calorias']:.1f} kcal
|
@@ -89,7 +160,7 @@ def analyze_image(image):
|
|
89 |
"""Função principal que coordena o processo de análise"""
|
90 |
try:
|
91 |
# Simula a resposta do modelo (temporário até resolvermos o erro do DeepSeek)
|
92 |
-
description = "Na imagem é possível ver um prato contendo arroz branco cozido, feijão, um pedaço de frango grelhado
|
93 |
|
94 |
# Analisa os alimentos
|
95 |
analysis, table_data, plot_data = analyze_foods(description)
|
@@ -99,59 +170,9 @@ def analyze_image(image):
|
|
99 |
except Exception as e:
|
100 |
return str(e), None, None
|
101 |
|
102 |
-
# Interface Gradio
|
103 |
with gr.Blocks(theme=gr.themes.Soft()) as iface:
|
104 |
-
|
105 |
-
# 🍽️ Análise Nutricional com IA
|
106 |
-
Faça upload de uma foto do seu prato para análise nutricional detalhada.
|
107 |
-
""")
|
108 |
-
|
109 |
-
with gr.Row():
|
110 |
-
# Coluna de Input
|
111 |
-
with gr.Column():
|
112 |
-
image_input = gr.Image(
|
113 |
-
type="pil",
|
114 |
-
label="Foto do Prato",
|
115 |
-
sources=["upload", "webcam"]
|
116 |
-
)
|
117 |
-
analyze_btn = gr.Button("📊 Analisar", variant="primary", size="lg")
|
118 |
-
|
119 |
-
with gr.Accordion("📝 Dicas", open=False):
|
120 |
-
gr.Markdown("""
|
121 |
-
- Use fotos bem iluminadas
|
122 |
-
- Fotografe de cima para baixo
|
123 |
-
- Certifique-se que todos os alimentos estão visíveis
|
124 |
-
- Evite sombras ou reflexos fortes
|
125 |
-
""")
|
126 |
-
|
127 |
-
# Coluna de Output
|
128 |
-
with gr.Column():
|
129 |
-
# Análise textual
|
130 |
-
output_text = gr.Markdown()
|
131 |
-
|
132 |
-
with gr.Row():
|
133 |
-
# Tabela nutricional
|
134 |
-
output_table = gr.Dataframe(
|
135 |
-
headers=["Nutriente", "Quantidade"],
|
136 |
-
label="Informação Nutricional",
|
137 |
-
wrap=True
|
138 |
-
)
|
139 |
-
|
140 |
-
# Gráfico
|
141 |
-
output_plot = gr.BarPlot(
|
142 |
-
x="Nutriente",
|
143 |
-
y="Quantidade",
|
144 |
-
title="Macronutrientes (g)",
|
145 |
-
height=300,
|
146 |
-
tooltip=["Nutriente", "Quantidade"]
|
147 |
-
)
|
148 |
-
|
149 |
-
# Eventos
|
150 |
-
analyze_btn.click(
|
151 |
-
fn=analyze_image,
|
152 |
-
inputs=[image_input],
|
153 |
-
outputs=[output_text, output_table, output_plot]
|
154 |
-
)
|
155 |
|
156 |
if __name__ == "__main__":
|
157 |
print(f"Usando dispositivo: {'CUDA' if torch.cuda.is_available() else 'CPU'}")
|
|
|
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 |
+
# Base de dados nutricional expandida com variações e sinônimos
|
9 |
+
FOOD_ALIASES = {
|
10 |
+
"arroz": ["arroz branco", "arroz integral", "arroz cozido", "arroz solto"],
|
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},
|
|
|
30 |
"ovo": {"calorias": 155, "proteinas": 13, "carboidratos": 1.1, "gorduras": 11},
|
31 |
}
|
32 |
|
33 |
+
def identify_foods(description):
|
34 |
+
"""
|
35 |
+
Identifica alimentos na descrição usando múltiplas verificações
|
36 |
+
"""
|
37 |
+
found_foods = {}
|
38 |
+
description = description.lower()
|
39 |
+
|
40 |
+
# Primeira verificação: busca direta
|
41 |
+
for base_food in NUTRITION_DB.keys():
|
42 |
+
if base_food in description:
|
43 |
+
found_foods[base_food] = {
|
44 |
+
"confianca": "Alta",
|
45 |
+
"mencoes": description.count(base_food),
|
46 |
+
"variacao": base_food
|
47 |
+
}
|
48 |
+
|
49 |
+
# Segunda verificação: sinônimos e variações
|
50 |
+
for base_food, aliases in FOOD_ALIASES.items():
|
51 |
+
if base_food not in found_foods: # Só verifica se ainda não encontrou
|
52 |
+
for alias in aliases:
|
53 |
+
if alias in description:
|
54 |
+
found_foods[base_food] = {
|
55 |
+
"confianca": "Média",
|
56 |
+
"mencoes": description.count(alias),
|
57 |
+
"variacao": alias
|
58 |
+
}
|
59 |
+
|
60 |
+
# Terceira verificação: análise de contexto
|
61 |
+
context_clues = {
|
62 |
+
"prato": 0.1,
|
63 |
+
"refeição": 0.1,
|
64 |
+
"porção": 0.1,
|
65 |
+
"servido": 0.1,
|
66 |
+
"preparado": 0.1
|
67 |
+
}
|
68 |
+
|
69 |
+
for food in found_foods:
|
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 |
+
# Quarta verificação: validação de preparação
|
79 |
+
cooking_methods = ["cozido", "assado", "grelhado", "frito", "cru"]
|
80 |
+
for food in found_foods:
|
81 |
+
for method in cooking_methods:
|
82 |
+
if method in description and food in FOOD_ALIASES:
|
83 |
+
found_foods[food]["preparo"] = method
|
84 |
+
|
85 |
+
return found_foods
|
86 |
+
|
87 |
def analyze_foods(description):
|
88 |
"""Analisa a descrição e retorna informações nutricionais"""
|
89 |
try:
|
90 |
+
# Identificação detalhada dos alimentos
|
91 |
+
identified_foods = identify_foods(description)
|
|
|
|
|
|
|
92 |
|
93 |
+
if not identified_foods:
|
94 |
+
return "Nenhum alimento conhecido identificado com confiança.", None, None
|
95 |
|
96 |
# Calcula nutrientes
|
97 |
total_nutrients = {
|
|
|
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 |
+
|
114 |
+
# Soma nutrientes
|
115 |
for nutrient, value in NUTRITION_DB[food].items():
|
116 |
total_nutrients[nutrient] += value
|
117 |
|
|
|
137 |
{description}
|
138 |
|
139 |
### 🔍 Alimentos Identificados:
|
140 |
+
{chr(10).join(foods_detail)}
|
141 |
|
142 |
### 📊 Análise Nutricional:
|
143 |
• Calorias Totais: {total_nutrients['calorias']:.1f} kcal
|
|
|
160 |
"""Função principal que coordena o processo de análise"""
|
161 |
try:
|
162 |
# Simula a resposta do modelo (temporário até resolvermos o erro do DeepSeek)
|
163 |
+
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."
|
164 |
|
165 |
# Analisa os alimentos
|
166 |
analysis, table_data, plot_data = analyze_foods(description)
|
|
|
170 |
except Exception as e:
|
171 |
return str(e), None, None
|
172 |
|
173 |
+
# Interface Gradio (resto do código permanece o mesmo)
|
174 |
with gr.Blocks(theme=gr.themes.Soft()) as iface:
|
175 |
+
# ... (resto do código da interface permanece igual)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
176 |
|
177 |
if __name__ == "__main__":
|
178 |
print(f"Usando dispositivo: {'CUDA' if torch.cuda.is_available() else 'CPU'}")
|