Spaces:
Running
Running
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() | |