import streamlit as st import cloudinary import cloudinary.uploader import cloudinary.api import requests import io import zipfile import logging # Configuración de la página y del logging st.set_page_config( page_title="Cloudinary AI Background Generator", page_icon="🤖", layout="wide" ) logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s') def init_cloudinary(): """ Inicializa Cloudinary utilizando las credenciales definidas en st.secrets. Registra en session_state si se inicializó correctamente y realiza una limpieza inicial. """ if 'cloudinary_initialized' not in st.session_state: try: cloudinary.config(url=st.secrets['CLOUDINARY_URL']) st.session_state.cloudinary_initialized = True cleanup_cloudinary() # Limpieza de recursos al iniciar logging.info("Cloudinary inicializado correctamente.") except Exception as e: st.error("Error: No se encontraron las credenciales de Cloudinary en secrets.toml") st.session_state.cloudinary_initialized = False logging.error(f"Error al inicializar Cloudinary: {e}") def check_file_size(file, max_size_mb=10): """ Verifica que el tamaño del archivo no exceda el límite especificado (en MB). """ file.seek(0, io.SEEK_END) file_size = file.tell() / (1024 * 1024) file.seek(0) return file_size <= max_size_mb def cleanup_cloudinary(): """ Limpia todos los recursos almacenados en Cloudinary. """ if not st.session_state.get('cloudinary_initialized', False): return try: result = cloudinary.api.resources() if 'resources' in result and result['resources']: public_ids = [resource['public_id'] for resource in result['resources']] if public_ids: cloudinary.api.delete_resources(public_ids) logging.info("Recursos de Cloudinary limpiados correctamente.") except Exception as e: st.error(f"Error al limpiar recursos: {e}") logging.error(f"Error al limpiar recursos: {e}") def process_image(image, width, height, dpr): """ Procesa la imagen utilizando Cloudinary aplicando una transformación definida. Parámetros: - image: Objeto tipo BytesIO con la imagen a procesar. - width, height: Dimensiones deseadas. - dpr: Escalado de resolución. Retorna: - Tuple con la imagen procesada en bytes y la extensión del archivo. """ if not st.session_state.get('cloudinary_initialized', False): st.error("Cloudinary no está inicializado correctamente") return None, None try: image.seek(0) if not check_file_size(image, 10): st.error("La imagen excede el límite de 10MB") return None, None image_content = image.read() response = cloudinary.uploader.upload( image_content, transformation=[{ "width": width, "height": height, "crop": "pad", "background": "gen_fill", "quality": 100, "dpr": dpr, "flags": "preserve_transparency" }] ) processed_url = response['secure_url'] processed_image = requests.get(processed_url).content file_format = response.get('format', 'jpg') logging.info("Imagen procesada correctamente.") return processed_image, file_format except Exception as e: st.error(f"Error procesando imagen: {e}") logging.error(f"Error en process_image: {e}") return None, None def main(): """ Función principal que configura la interfaz de la aplicación y coordina el flujo de procesamiento. """ init_cloudinary() st.title("🤖 Cloudinary AI Background Generator") with st.expander("📌 ¿Cómo usar esta herramienta?", expanded=True): st.markdown( """ **Transforma tus imágenes automáticamente con IA:** - 🔄 Redimensiona manteniendo la relación de aspecto - 🎨 Genera fondos coherentes usando IA - 📥 Descarga múltiples imágenes en un ZIP **Formatos soportados:** - PNG, JPG, JPEG, WEBP **Pasos para usar:** 1. Define las dimensiones deseadas (ancho y alto) 2. Sube tus imágenes (hasta 10MB cada una) 3. Haz clic en "Procesar Imágenes" 4. Descarga los resultados finales **Características clave:** - Preserva transparencia en PNGs - Soporte para formatos modernos (WEBP) - Calidad ultra HD (DPR configurable entre 1 y 3) - Procesamiento por lotes - Fondo generado por IA adaptado al contexto **Notas:** - Las imágenes subidas se borran automáticamente después del procesamiento - Para mejores resultados, usa imágenes con sujetos bien definidos - El tiempo de procesamiento varía según el tamaño y cantidad de imágenes """ ) # Sección de parámetros de configuración col1, col2, col3 = st.columns(3) with col1: width = st.number_input("Ancho (px)", value=1000, min_value=100, max_value=3000) with col2: height = st.number_input("Alto (px)", value=460, min_value=100, max_value=3000) with col3: dpr = st.number_input("DPR", value=3, min_value=1, max_value=3, step=1) # Carga de archivos de imagen uploaded_files = st.file_uploader( "Sube tus imágenes (máx. 10MB por archivo)", type=['png', 'jpg', 'jpeg', 'webp'], accept_multiple_files=True ) if uploaded_files: st.header("Imágenes Originales") cols = st.columns(3) original_images = [] for idx, file in enumerate(uploaded_files): file_bytes = file.getvalue() original_images.append((file.name, file_bytes)) with cols[idx % 3]: st.image(file_bytes, caption=file.name, use_column_width=True) if st.button("Procesar Imágenes"): if not st.session_state.get('cloudinary_initialized', False): st.error("Por favor, asegúrate de que Cloudinary esté correctamente configurado") return processed_images = [] progress_bar = st.progress(0) for idx, (name, img_bytes) in enumerate(original_images): img_io = io.BytesIO(img_bytes) with st.spinner(f'Procesando imagen {idx + 1}/{len(original_images)}...'): processed, file_format = process_image(img_io, width, height, dpr) if processed: processed_images.append((processed, file_format)) progress_bar.progress((idx + 1) / len(original_images)) if processed_images: st.header("Imágenes Procesadas") cols = st.columns(3) for idx, (img_bytes, file_format) in enumerate(processed_images): with cols[idx % 3]: st.image(img_bytes, caption=f"Formato: {file_format}", use_column_width=True) # Empaquetado en ZIP zip_buffer = io.BytesIO() with zipfile.ZipFile(zip_buffer, 'w') as zip_file: for idx, (img_bytes, file_format) in enumerate(processed_images): zip_file.writestr(f'imagen_procesada_{idx}.{file_format}', img_bytes) st.download_button( label="Descargar todas las imágenes", data=zip_buffer.getvalue(), file_name="imagenes_procesadas.zip", mime="application/zip" ) if __name__ == "__main__": main()