Spaces:
Running
Running
File size: 6,334 Bytes
cfbce04 065cb1a cfbce04 065cb1a cfbce04 065cb1a cfbce04 065cb1a cfbce04 065cb1a cfbce04 065cb1a cfbce04 065cb1a cfbce04 065cb1a cfbce04 065cb1a cfbce04 065cb1a cfbce04 065cb1a cfbce04 dded47d 065cb1a cfbce04 dded47d cfbce04 065cb1a cfbce04 dded47d 065cb1a cfbce04 dded47d 065cb1a dded47d cfbce04 dded47d cfbce04 dded47d cfbce04 dded47d cfbce04 dded47d 065cb1a dded47d 065cb1a cfbce04 dded47d 065cb1a dded47d cfbce04 065cb1a cfbce04 dded47d cfbce04 dded47d |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 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 |
import streamlit as st
from PIL import Image
import io
import zipfile
from datetime import datetime
from pathlib import Path
import logging
# Configuración de la página y del logging
st.set_page_config(
page_title="📁 Image Compression Tool",
page_icon="📁",
layout="wide"
)
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
def optimize_image(image_file):
"""
Optimiza la imagen automáticamente manteniendo la máxima calidad visual.
Parámetros:
- image_file: Archivo de imagen (stream) subido por el usuario.
Retorna:
- Tuple con los datos de la imagen optimizada (bytes) y el formato usado.
- Si no se logra una reducción de tamaño, retorna (None, None).
"""
try:
img = Image.open(image_file)
output_buffer = io.BytesIO()
# Configuración de parámetros según el formato y modo de la imagen
if img.format == 'PNG':
if img.mode in ('RGBA', 'LA'):
img_to_save = img
save_params = {'format': 'PNG', 'optimize': True}
else:
img_to_save = img.convert('RGB')
save_params = {'format': 'JPEG', 'quality': 85, 'optimize': True}
else:
img_to_save = img.convert('RGB')
save_params = {'format': 'JPEG', 'quality': 85, 'optimize': True}
# Guardar la imagen optimizada en un buffer
img_to_save.save(output_buffer, **save_params)
output_buffer.seek(0)
optimized_size = len(output_buffer.getvalue())
# Se valida que la imagen optimizada sea menor que la original
if optimized_size >= image_file.size:
logging.info("No se logró reducir el tamaño de la imagen.")
return None, None
logging.info("Imagen optimizada exitosamente.")
return output_buffer.getvalue(), save_params['format']
except Exception as e:
st.error(f"Error optimizando imagen: {e}")
logging.error(f"Error en optimize_image: {e}")
return None, None
def process_filename(original_name, optimized_format):
"""
Genera un nuevo nombre de archivo para la imagen optimizada.
Parámetros:
- original_name: Nombre original del archivo.
- optimized_format: Formato de la imagen optimizada.
Retorna:
- String con el nuevo nombre (por ejemplo, 'foto_optimizado.jpg').
"""
path = Path(original_name)
new_suffix = f".{optimized_format.lower()}" if optimized_format else '.jpg'
return f"{path.stem}_optimizado{new_suffix}"
def main():
st.title("📁 Image Compression Tool")
with st.expander("📌 Instrucciones de uso", expanded=True):
st.markdown(
"""
**Optimización automática de imágenes con máxima calidad visual**
**Características:**
- 🔍 Compresión inteligente automática
- 🖼️ Mantiene transparencia en PNG
- 📉 Reducción de tamaño garantizada
- 🚀 Procesamiento por lotes
- 📥 Descarga múltiple en ZIP
"""
)
uploaded_files = st.file_uploader(
"Sube tus imágenes (máx. 50MB por archivo)",
type=['png', 'jpg', 'jpeg'],
accept_multiple_files=True
)
if uploaded_files and st.button("🚀 Optimizar Imágenes"):
processed_images = []
total_reduction = 0
progress_bar = st.progress(0)
total_files = len(uploaded_files)
st.info("Optimizando imágenes...")
logging.info(f"Se subieron {total_files} archivos para optimización.")
for idx, file in enumerate(uploaded_files):
try:
if file.size > 50 * 1024 * 1024:
st.error(f"El archivo {file.name} excede 50MB")
logging.warning(f"El archivo {file.name} excede el tamaño máximo permitido.")
continue
original_size = file.size
optimized_data, format_used = optimize_image(file)
if optimized_data and format_used:
new_size = len(optimized_data)
reduction = original_size - new_size
total_reduction += reduction
new_name = process_filename(file.name, format_used)
processed_images.append((new_name, optimized_data, original_size, new_size))
st.write(f"✅ {file.name} optimizado ({reduction / 1024:.1f} KB ahorrados)")
logging.info(f"{file.name} optimizado. Ahorro: {reduction / 1024:.1f} KB.")
else:
st.write(f"⚠️ {file.name} no se optimizó porque no se logró reducir el tamaño.")
logging.info(f"{file.name} no se optimizó, no hubo reducción de tamaño.")
progress_bar.progress((idx + 1) / total_files)
except Exception as e:
st.error(f"Error procesando {file.name}: {e}")
logging.error(f"Error procesando {file.name}: {e}")
st.success(f"¡Optimización completada! (Ahorro total: {total_reduction / 1024:.1f} KB)")
if processed_images:
st.subheader("Resultados de Optimización")
cols = st.columns(3)
with cols[0]:
st.metric("Archivos procesados", len(processed_images))
with cols[1]:
st.metric("Espacio ahorrado", f"{total_reduction / 1024:.1f} KB")
with cols[2]:
avg_reduction = (total_reduction / len(processed_images)) / 1024
st.metric("Reducción promedio", f"{avg_reduction:.1f} KB")
# Crear archivo ZIP con las imágenes optimizadas
zip_buffer = io.BytesIO()
with zipfile.ZipFile(zip_buffer, 'w', zipfile.ZIP_DEFLATED) as zip_file:
for name, data, _, _ in processed_images:
zip_file.writestr(name, data)
st.download_button(
label="📥 Descargar Todas las Imágenes",
data=zip_buffer.getvalue(),
file_name=f"imagenes_optimizadas_{datetime.now().strftime('%Y%m%d_%H%M')}.zip",
mime="application/zip"
)
if __name__ == "__main__":
main()
|