streamlit-design-toolkit / pages /3_Image_Compression_Tool.py
Wkatir
feat: adding converter
065cb1a
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()