agerhund commited on
Commit
a6f3241
·
verified ·
1 Parent(s): 7336f3b

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +74 -71
app.py CHANGED
@@ -4,21 +4,20 @@ import numpy as np
4
  from PIL import Image
5
  import os
6
  from huggingface_hub import hf_hub_download
7
-
8
  import logic
9
 
10
- # --- CONFIGURACIÓN INICIAL DE LA PÁGINA ---
11
- st.set_page_config(page_title="DesignIA - Recomendador de Muebles inteligente", layout="wide")
12
-
13
  # --- CONSTANTES Y RUTAS DE RECURSOS ---
14
  csv_path = "data/furniture_data.csv"
15
- cache_path = "vectores_cache.pkl"
16
 
17
- # --- CONSTANTES DE DESCARGA DE MODELOS (USANDO TU REPOSITORIO) ---
18
  MODEL_REPO_ID = "agerhund/DesignIA_models"
19
  MODEL_FILE_BERT = "bert_style_encoder.pth"
20
  MODEL_FILE_HORIZON = "horizonnet_model.pth"
21
 
 
 
 
22
  # --- CARGAR ESTILOS CSS ---
23
  def cargar_estilo():
24
  """Define y aplica estilos CSS para la UI de Streamlit."""
@@ -51,7 +50,7 @@ def cargar_estilo():
51
  box-shadow: 0 2px 5px rgba(0,0,0,0.05);
52
  margin-bottom: 15px;
53
  border: 1px solid #eee;
54
- color: black !important; /* Forzar texto negro dentro de la tarjeta blanca */
55
  }
56
  </style>
57
  """, unsafe_allow_html=True)
@@ -64,8 +63,8 @@ if 'room_data' not in st.session_state: st.session_state.room_data = None
64
  if 'muebles_df' not in st.session_state: st.session_state.muebles_df = None
65
  if 'data_manager' not in st.session_state: st.session_state.data_manager = None
66
  if 'horizon_model_path' not in st.session_state: st.session_state.horizon_model_path = None
67
- if 'selected_example_path' not in st.session_state: st.session_state.selected_example_path = None
68
- if 'selected_example_name' not in st.session_state: st.session_state.selected_example_name = None
69
 
70
 
71
  # --- FUNCIONES DE CARGA CON CACHE ---
@@ -75,10 +74,7 @@ def download_models():
75
  """Descarga ambos modelos grandes desde el Model Repository y devuelve las rutas locales temporales."""
76
  st.info("Descargando modelos grandes desde Hugging Face Hub (¡Solo la primera vez!)...")
77
 
78
- # 1. Descarga del modelo BERT/Style (para DataManager)
79
  bert_path = hf_hub_download(repo_id=MODEL_REPO_ID, filename=MODEL_FILE_BERT)
80
-
81
- # 2. Descarga del modelo HorizonNet (para RoomLayoutDetector)
82
  horizon_path = hf_hub_download(repo_id=MODEL_REPO_ID, filename=MODEL_FILE_HORIZON)
83
 
84
  return bert_path, horizon_path
@@ -86,7 +82,6 @@ def download_models():
86
  @st.cache_resource
87
  def init_backend(csv, cache, bert_model_path):
88
  """Inicializa DataManager y carga el DataFrame de muebles, usando la ruta local del modelo BERT."""
89
- # El DataManager (logic.py) utiliza la ruta del modelo BERT para sus operaciones.
90
  dm = logic.DataManager(csv, cache, bert_model_path)
91
  df = dm.cargar_datos()
92
  return dm, df
@@ -95,15 +90,12 @@ def init_backend(csv, cache, bert_model_path):
95
  try:
96
  if st.session_state.data_manager is None:
97
 
98
- # 1. Descarga los modelos y obtiene las rutas locales temporales
99
  bert_path_downloaded, horizon_path_downloaded = download_models()
100
 
101
  with st.spinner("Cargando base de datos de muebles y modelos IA..."):
102
 
103
- # 2. Inicializa el DataManager (que solo necesita el modelo BERT)
104
  dm, df = init_backend(csv_path, cache_path, bert_path_downloaded)
105
 
106
- # 3. Guarda todos los objetos y la RUTA DE HORIZONNET para el PASO 1
107
  st.session_state.data_manager = dm
108
  st.session_state.muebles_df = df
109
  st.session_state.horizon_model_path = horizon_path_downloaded
@@ -115,7 +107,7 @@ except Exception as e:
115
  st.stop()
116
 
117
 
118
- # --- SIDEBAR: INFORMACIÓN DEL PROYECTO ---
119
  with st.sidebar:
120
  st.title("DesignIA - Asistente de Diseño")
121
  st.markdown("---")
@@ -126,7 +118,7 @@ with st.sidebar:
126
  st.markdown("Desarrollado por **Andrés Gerlotti Slusnys**")
127
  st.markdown("© 2025")
128
 
129
- # Indicador de estado para debugging
130
  with st.expander("Estado del Sistema", expanded=False):
131
  st.success("Motor Gráfico: Activo")
132
  st.success("Modelo NLP (BERT): Cargado")
@@ -134,43 +126,9 @@ with st.sidebar:
134
  st.success("HorizonNet: Conectado (Remoto)")
135
  else:
136
  st.warning("HorizonNet: Pendiente de descarga/inicialización")
137
-
138
- # Sección de Ejemplos
139
- st.markdown("---")
140
- st.subheader("📸 Imágenes de Ejemplo")
141
- st.caption("Descarga estas imágenes para probar la app:")
142
 
143
- examples_dir = os.path.join(os.path.dirname(__file__), "examples")
144
- if os.path.exists(examples_dir):
145
- example_files = [f for f in os.listdir(examples_dir) if f.endswith(('.jpg', '.png'))]
146
- example_files.sort()
147
-
148
- # Guardar el nombre del archivo seleccionado en la sesión
149
- selected_example = st.selectbox("Selecciona un ejemplo:", example_files, key='example_select_box')
150
-
151
- if selected_example:
152
- file_path = os.path.join(examples_dir, selected_example)
153
-
154
- # 🛑 Corrección: Guardar la ruta y el nombre del ejemplo seleccionado en session_state
155
- st.session_state.selected_example_path = file_path
156
- st.session_state.selected_example_name = selected_example
157
-
158
- with open(file_path, "rb") as file:
159
- btn = st.download_button(
160
- label="⬇️ Descargar Imagen",
161
- data=file,
162
- file_name=selected_example,
163
- mime="image/jpeg"
164
- )
165
-
166
- # Mostrar miniatura
167
- st.image(file_path, caption="Vista previa", use_container_width=True)
168
- else:
169
- # Limpiar la ruta si no hay nada seleccionado
170
- if 'selected_example_path' in st.session_state:
171
- del st.session_state.selected_example_path
172
- else:
173
- st.info("No hay ejemplos disponibles.")
174
 
175
 
176
  # --- INTERFAZ PRINCIPAL ---
@@ -180,49 +138,95 @@ st.markdown("Sube una panorámica, detecta el espacio y obtén el diseño ideal
180
  # --- PASO 1: CARGA DE IMAGEN Y DETECCIÓN ---
181
  st.header("1. Escaneo de Habitación")
182
 
183
- # 1. Selector de carga manual
184
- uploaded_file = st.file_uploader("Sube tu imagen panorámica (360)", type=['jpg', 'png', 'jpeg'])
185
 
186
- # Obtener rutas de archivos de ejemplo desde session_state (definidas en la barra lateral)
187
- selected_example_path = st.session_state.get('selected_example_path', None)
188
- selected_example_name = st.session_state.get('selected_example_name', None)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
189
 
190
  # Determinar qué archivo usar (la carga manual tiene prioridad)
 
 
 
191
  if uploaded_file is not None:
192
  source_file = uploaded_file
193
  file_caption = 'Imagen subida'
194
- elif selected_example_path is not None:
195
- # Usar el archivo de la carpeta 'examples' como fuente
196
- source_file = selected_example_path
197
- file_caption = f'Imagen de ejemplo: {selected_example_name}'
198
  else:
199
  source_file = None
200
 
201
 
202
  if source_file is not None:
203
-
204
- if isinstance(source_file, str):
205
  image = Image.open(source_file)
206
  else:
207
  image = Image.open(source_file)
208
 
209
- st.image(image, caption=file_caption, use_container_width=True)
 
 
210
 
211
  if st.button("Analizar la habitación"):
212
  with st.spinner("Detectando la geometría..."):
213
 
214
- if isinstance(source_file, str):
 
215
  temp_file_path = source_file
216
  else:
217
  temp_file_path = "temp_pano.jpg"
218
  with open(temp_file_path, "wb") as f:
219
  f.write(source_file.getbuffer())
220
-
221
 
222
 
223
  # Instanciar y ejecutar el detector de layout (HorizonNet)
224
  try:
225
- # 🛑 USAR LA RUTA TEMPORAL GUARDADA EN session_state
226
  detector = logic.RoomLayoutDetector(st.session_state.horizon_model_path)
227
  room_data = detector.detect_layout(temp_file_path)
228
 
@@ -394,7 +398,6 @@ if st.session_state.stage >= 2:
394
  st.error("No se pudo generar una distribución válida para este espacio (demasiado pequeño o muchos obstáculos).")
395
  else:
396
  # 5. Recomendar productos (Knapsack para optimización de precio/estilo)
397
- # Las 'constraints' se definen por los muebles que el layout PUDO colocar
398
  best_combo = recommender.buscar_combinacion(constraints, presupuesto, top_n=1)
399
 
400
  if not best_combo:
@@ -411,7 +414,7 @@ if st.session_state.stage == 3:
411
  st.divider()
412
  st.header("Tu salón ideal")
413
 
414
- # --- VISUALIZACIÓN 3D Interactiva ---
415
  st.subheader("Visualización 3D Interactiva")
416
 
417
  # Generar la figura 3D
 
4
  from PIL import Image
5
  import os
6
  from huggingface_hub import hf_hub_download
 
7
  import logic
8
 
 
 
 
9
  # --- CONSTANTES Y RUTAS DE RECURSOS ---
10
  csv_path = "data/furniture_data.csv"
11
+ cache_path = "vectores_cache.pkl"
12
 
13
+ # --- CONSTANTES DE DESCARGA DE MODELOS ---
14
  MODEL_REPO_ID = "agerhund/DesignIA_models"
15
  MODEL_FILE_BERT = "bert_style_encoder.pth"
16
  MODEL_FILE_HORIZON = "horizonnet_model.pth"
17
 
18
+ # --- CONFIGURACIÓN INICIAL DE LA PÁGINA ---
19
+ st.set_page_config(page_title="DesignIA - Recomendador de Muebles inteligente", layout="wide")
20
+
21
  # --- CARGAR ESTILOS CSS ---
22
  def cargar_estilo():
23
  """Define y aplica estilos CSS para la UI de Streamlit."""
 
50
  box-shadow: 0 2px 5px rgba(0,0,0,0.05);
51
  margin-bottom: 15px;
52
  border: 1px solid #eee;
53
+ color: black !important;
54
  }
55
  </style>
56
  """, unsafe_allow_html=True)
 
63
  if 'muebles_df' not in st.session_state: st.session_state.muebles_df = None
64
  if 'data_manager' not in st.session_state: st.session_state.data_manager = None
65
  if 'horizon_model_path' not in st.session_state: st.session_state.horizon_model_path = None
66
+ if 'source_file_path' not in st.session_state: st.session_state.source_file_path = None
67
+ if 'is_example' not in st.session_state: st.session_state.is_example = False
68
 
69
 
70
  # --- FUNCIONES DE CARGA CON CACHE ---
 
74
  """Descarga ambos modelos grandes desde el Model Repository y devuelve las rutas locales temporales."""
75
  st.info("Descargando modelos grandes desde Hugging Face Hub (¡Solo la primera vez!)...")
76
 
 
77
  bert_path = hf_hub_download(repo_id=MODEL_REPO_ID, filename=MODEL_FILE_BERT)
 
 
78
  horizon_path = hf_hub_download(repo_id=MODEL_REPO_ID, filename=MODEL_FILE_HORIZON)
79
 
80
  return bert_path, horizon_path
 
82
  @st.cache_resource
83
  def init_backend(csv, cache, bert_model_path):
84
  """Inicializa DataManager y carga el DataFrame de muebles, usando la ruta local del modelo BERT."""
 
85
  dm = logic.DataManager(csv, cache, bert_model_path)
86
  df = dm.cargar_datos()
87
  return dm, df
 
90
  try:
91
  if st.session_state.data_manager is None:
92
 
 
93
  bert_path_downloaded, horizon_path_downloaded = download_models()
94
 
95
  with st.spinner("Cargando base de datos de muebles y modelos IA..."):
96
 
 
97
  dm, df = init_backend(csv_path, cache_path, bert_path_downloaded)
98
 
 
99
  st.session_state.data_manager = dm
100
  st.session_state.muebles_df = df
101
  st.session_state.horizon_model_path = horizon_path_downloaded
 
107
  st.stop()
108
 
109
 
110
+ # --- SIDEBAR: INFORMACIÓN DEL PROYECTO (SOLO INFORMACIÓN ESTÁTICA) ---
111
  with st.sidebar:
112
  st.title("DesignIA - Asistente de Diseño")
113
  st.markdown("---")
 
118
  st.markdown("Desarrollado por **Andrés Gerlotti Slusnys**")
119
  st.markdown("© 2025")
120
 
121
+ # Indicador de estado
122
  with st.expander("Estado del Sistema", expanded=False):
123
  st.success("Motor Gráfico: Activo")
124
  st.success("Modelo NLP (BERT): Cargado")
 
126
  st.success("HorizonNet: Conectado (Remoto)")
127
  else:
128
  st.warning("HorizonNet: Pendiente de descarga/inicialización")
 
 
 
 
 
129
 
130
+ st.markdown("---")
131
+ st.info("El selector de imágenes de ejemplo se encuentra en el Paso 1 de la página principal.")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
132
 
133
 
134
  # --- INTERFAZ PRINCIPAL ---
 
138
  # --- PASO 1: CARGA DE IMAGEN Y DETECCIÓN ---
139
  st.header("1. Escaneo de Habitación")
140
 
141
+ # --- LÓGICA DE SELECCIÓN DE EJEMPLOS EN EL CUERPO PRINCIPAL ---
142
+ examples_dir = os.path.join(os.path.dirname(__file__), "examples")
143
 
144
+ if os.path.exists(examples_dir):
145
+ example_files = [f for f in os.listdir(examples_dir) if f.endswith(('.jpg', '.png'))]
146
+ example_files.sort()
147
+
148
+ col_select, col_download = st.columns([3, 1])
149
+
150
+ with col_select:
151
+ # Añadimos la opción de carga manual como la principal
152
+ display_files = ["--- Cargar imagen manualmente (prioridad) ---"] + example_files
153
+
154
+ selected_example_name = st.selectbox(
155
+ "O usa una imagen de ejemplo:",
156
+ display_files,
157
+ key='example_select_box'
158
+ )
159
+
160
+ # Manejo de la selección del ejemplo
161
+ if selected_example_name != display_files[0]:
162
+ file_path = os.path.join(examples_dir, selected_example_name)
163
+ st.session_state.source_file_path = file_path
164
+ st.session_state.is_example = True
165
+
166
+ with col_download:
167
+ st.markdown(" ")
168
+ with open(file_path, "rb") as file:
169
+ st.download_button(
170
+ label=f"⬇️ Descargar: {selected_example_name}",
171
+ data=file,
172
+ file_name=selected_example_name,
173
+ mime="image/jpeg"
174
+ )
175
+
176
+ # Mostrar miniatura del ejemplo
177
+ st.image(file_path, caption="Vista previa del ejemplo", use_container_width=True)
178
+
179
+ else:
180
+ # Limpiar las variables si se selecciona la opción nula
181
+ if 'source_file_path' in st.session_state:
182
+ del st.session_state.source_file_path
183
+ st.session_state.is_example = False
184
+
185
+
186
+ # 1. Selector de carga manual (Se mantiene aquí, pero solo para subir el archivo)
187
+ uploaded_file = st.file_uploader("Sube tu imagen panorámica (360)", type=['jpg', 'png', 'jpeg'])
188
 
189
  # Determinar qué archivo usar (la carga manual tiene prioridad)
190
+ source_file_path = st.session_state.get('source_file_path', None)
191
+ is_example = st.session_state.get('is_example', False)
192
+
193
  if uploaded_file is not None:
194
  source_file = uploaded_file
195
  file_caption = 'Imagen subida'
196
+ is_example = False
197
+ elif source_file_path is not None and is_example == True:
198
+ source_file = source_file_path
199
+ file_caption = f'Imagen de ejemplo: {source_file_path.split(os.sep)[-1]}'
200
  else:
201
  source_file = None
202
 
203
 
204
  if source_file is not None:
205
+ # Abrir la imagen
206
+ if is_example:
207
  image = Image.open(source_file)
208
  else:
209
  image = Image.open(source_file)
210
 
211
+ # Mostrar la imagen subida (solo si es subida, ya que el ejemplo se mostró arriba)
212
+ if uploaded_file is not None:
213
+ st.image(image, caption=file_caption, use_container_width=True)
214
 
215
  if st.button("Analizar la habitación"):
216
  with st.spinner("Detectando la geometría..."):
217
 
218
+ # --- MANEJO DEL ARCHIVO TEMPORAL PARA EL DETECTOR ---
219
+ if is_example:
220
  temp_file_path = source_file
221
  else:
222
  temp_file_path = "temp_pano.jpg"
223
  with open(temp_file_path, "wb") as f:
224
  f.write(source_file.getbuffer())
225
+ # --- FIN MANEJO ARCHIVO TEMPORAL ---
226
 
227
 
228
  # Instanciar y ejecutar el detector de layout (HorizonNet)
229
  try:
 
230
  detector = logic.RoomLayoutDetector(st.session_state.horizon_model_path)
231
  room_data = detector.detect_layout(temp_file_path)
232
 
 
398
  st.error("No se pudo generar una distribución válida para este espacio (demasiado pequeño o muchos obstáculos).")
399
  else:
400
  # 5. Recomendar productos (Knapsack para optimización de precio/estilo)
 
401
  best_combo = recommender.buscar_combinacion(constraints, presupuesto, top_n=1)
402
 
403
  if not best_combo:
 
414
  st.divider()
415
  st.header("Tu salón ideal")
416
 
417
+ # --- VISUALIZACIÓN 3D Interactiva (Plotly) ---
418
  st.subheader("Visualización 3D Interactiva")
419
 
420
  # Generar la figura 3D