Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- import os
- import tkinter as tk
- from tkinter import filedialog, ttk, messagebox
- from PIL import Image, ImageOps
- import threading
- from rembg import remove
- import numpy as np
- import cv2
- def ajustar_opacidad(imagen_original, imagen_sin_fondo, opacidad):
- original = np.array(imagen_original.convert("RGBA"))
- sin_fondo = np.array(imagen_sin_fondo)
- mascara = sin_fondo[:, :, 3] / 255.0
- mascara_ajustada = mascara * (1 - opacidad)
- resultado = original * (1 - mascara_ajustada[:, :, np.newaxis]) + sin_fondo * mascara_ajustada[:, :, np.newaxis]
- return Image.fromarray(resultado.astype('uint8'), 'RGBA')
- # Método 1: Usar rembg (que internamente usa U²-Net)
- def quitar_fondo_rembg(imagen):
- return remove(imagen)
- # Método 2: Usar grabCut de OpenCV (para imágenes con fondo complejo)
- def quitar_fondo_grabcut(imagen):
- if isinstance(imagen, Image.Image):
- imagen = np.array(imagen)
- # Asegurarse de que la imagen esté en formato RGB
- if imagen.shape[2] == 4: # Si es RGBA, convertir a RGB
- imagen = cv2.cvtColor(imagen, cv2.COLOR_RGBA2RGB)
- # Crear una máscara inicial (todo 0)
- mask = np.zeros(imagen.shape[:2], np.uint8)
- # Modelos necesarios para grabCut
- bgdModel = np.zeros((1, 65), np.float64)
- fgdModel = np.zeros((1, 65), np.float64)
- # Rectángulo alrededor del área que contiene el primer plano (la parte interesante de la imagen)
- height, width = imagen.shape[:2]
- rect = (10, 10, width - 10, height - 10)
- # Aplicar grabCut
- cv2.grabCut(imagen, mask, rect, bgdModel, fgdModel, 5, cv2.GC_INIT_WITH_RECT)
- # Crear una máscara donde 0 y 2 son background, 1 y 3 son foreground
- mask2 = np.where((mask == 2) | (mask == 0), 0, 1).astype('uint8')
- # Aplicar la máscara a la imagen
- imagen_sin_fondo = imagen * mask2[:, :, np.newaxis]
- # Convertir la imagen a RGBA para añadir el canal alfa (transparencia)
- img_transparente = cv2.cvtColor(imagen_sin_fondo, cv2.COLOR_RGB2RGBA)
- # Establecer el canal alfa en base a la máscara generada
- img_transparente[:, :, 3] = mask2 * 255
- return Image.fromarray(img_transparente)
- # Método 3: Chroma key (para fondos sólidos)
- def quitar_fondo_chroma(imagen, color_fondo=(255, 255, 255), umbral=100):
- if isinstance(imagen, Image.Image):
- imagen = np.array(imagen)
- # Asegurarse de que la imagen tenga solo 3 canales (RGB)
- if imagen.shape[2] == 4: # Si es RGBA, convertir a RGB
- imagen = cv2.cvtColor(imagen, cv2.COLOR_RGBA2RGB)
- # Crear un array del color de fondo (debe ser RGB)
- fondo_rgb = np.full(imagen.shape, color_fondo, dtype=np.uint8)
- # Calcular la diferencia entre la imagen y el color de fondo
- diferencia = cv2.absdiff(imagen, fondo_rgb)
- # Convertir la diferencia a escala de grises para crear la máscara
- mascara = cv2.cvtColor(diferencia, cv2.COLOR_BGR2GRAY)
- # Aplicar un umbral a la máscara para obtener una máscara binaria
- _, mascara_binaria = cv2.threshold(mascara, umbral, 255, cv2.THRESH_BINARY)
- # Convertir la imagen a RGBA
- img_transparente = cv2.cvtColor(imagen, cv2.COLOR_RGB2BGRA)
- # Usar la máscara binaria para asignar transparencia
- img_transparente[:, :, 3] = 255 - mascara_binaria
- return Image.fromarray(img_transparente)
- # Método 4: Umbralizacion para eliminar el fondo
- def quitar_fondo_umbral(imagen, umbral=128):
- # Asegurarse de que la imagen esté en el formato correcto
- if not isinstance(imagen, np.ndarray):
- # Convertir imagen PIL a formato NumPy array
- imagen = np.array(imagen)
- # Convertir la imagen a escala de grises
- gris = cv2.cvtColor(imagen, cv2.COLOR_RGBA2GRAY)
- # Aplicar el umbral para crear una máscara
- _, mascara = cv2.threshold(gris, umbral, 255, cv2.THRESH_BINARY)
- # Invertir la máscara
- mascara_invertida = cv2.bitwise_not(mascara)
- # Aplicar la máscara invertida para quitar el fondo
- resultado = cv2.bitwise_and(imagen, imagen, mask=mascara_invertida)
- # Convertir de nuevo a formato PIL para el retorno
- return Image.fromarray(resultado)
- # Metodo 5: Segmentacion de color con Umbral
- def quitar_fondo_segmentacion(imagen, umbral=120):
- # Convertir la imagen a formato RGB
- imagen_rgb = imagen.convert('RGB')
- imagen_np = np.array(imagen_rgb)
- # Convertir la imagen a formato BGR (OpenCV usa BGR por defecto)
- imagen_bgr = cv2.cvtColor(imagen_np, cv2.COLOR_RGB2BGR)
- # Convertir la imagen a formato float32 para OpenCV
- imagen_bgr = np.float32(imagen_bgr) / 255.0
- # Aplicar un desenfoque gaussiano
- imagen_suavizada = cv2.GaussianBlur(imagen_bgr, (5, 5), 0)
- # Convertir la imagen suavizada a escala de grises
- imagen_gris = cv2.cvtColor(imagen_suavizada, cv2.COLOR_BGR2GRAY)
- # Aplicar un umbral para segmentar la imagen
- _, imagen_segmentada = cv2.threshold(imagen_gris, 0.5, 1.0, cv2.THRESH_BINARY)
- # Crear una máscara a partir de la imagen segmentada
- mascara = (imagen_segmentada * 255).astype(np.uint8)
- # Aplicar la máscara a la imagen original
- imagen_final = cv2.bitwise_and(imagen_bgr, imagen_bgr, mask=mascara)
- # Convertir la imagen final a formato uint8
- imagen_final = np.uint8(imagen_final * 255)
- # Convertir la imagen final a formato RGB y de vuelta a PIL
- imagen_final = cv2.cvtColor(imagen_final, cv2.COLOR_BGR2RGB)
- imagen_final_pil = Image.fromarray(imagen_final)
- return imagen_final_pil
- # Metodo 5A: Alternativa de Segmentacion por sistema K-means Clustering
- def quitar_fondo_kmeans(imagen, k=2):
- # Asegurarse de que la imagen esté en el formato correcto (NumPy array)
- if not isinstance(imagen, np.ndarray):
- imagen = np.array(imagen)
- # Verificar si la imagen tiene un canal alfa (RGBA), en ese caso eliminarlo para el procesamiento
- if imagen.shape[2] == 4:
- imagen = cv2.cvtColor(imagen, cv2.COLOR_RGBA2RGB)
- # Convertir la imagen a un array 2D para K-means
- Z = imagen.reshape((-1, 3))
- # Convertir a float32 para K-means
- Z = np.float32(Z)
- # Criterios de K-means (máx. 10 iteraciones o precisión 1.0)
- criterio = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 10, 1.0)
- # Aplicar K-means para segmentación
- _, labels, centers = cv2.kmeans(Z, k, None, criterio, 10, cv2.KMEANS_RANDOM_CENTERS)
- # Convertir los centros de vuelta a 8 bits
- centers = np.uint8(centers)
- # Etiquetar las imágenes usando los labels de K-means
- resultado = centers[labels.flatten()]
- resultado = resultado.reshape(imagen.shape)
- # Crear una máscara basada en el cluster que debería ser el fondo
- fondo = np.min(centers, axis=0)
- mascara = np.all(resultado == fondo, axis=-1).astype(np.uint8) * 255
- # Invertir la máscara
- mascara_invertida = cv2.bitwise_not(mascara)
- # Aplicar la máscara para eliminar el fondo
- resultado_con_fondo_eliminado = cv2.bitwise_and(imagen, imagen, mask=mascara_invertida)
- # Convertir de nuevo a formato PIL para el retorno
- return Image.fromarray(resultado_con_fondo_eliminado)
- # Metodo 6: Sustitucion de Color Simple
- def quitar_fondo_simple(imagen, color_fondo):
- imagen = np.array(imagen.convert("RGBA"))
- color_fondo = np.array(color_fondo + (255,)) # Añadir alfa al color de fondo
- # Crear una máscara donde el color de fondo coincide
- mascara = np.all(imagen == color_fondo, axis=-1)
- # Aplicar la máscara
- imagen[mascara] = [0, 0, 0, 0]
- return Image.fromarray(imagen, 'RGBA')
- # Metodo 7: Metodo avanzado para quitar el fondo
- def quitar_fondo_avanzado(ruta_imagen_entrada):
- # Leer la imagen con Pillow
- imagen_pil = Image.open(ruta_imagen_entrada)
- # Convertir a formato OpenCV
- imagen_cv = cv2.cvtColor(np.array(imagen_pil), cv2.COLOR_RGB2BGR)
- # Convertir a escala de grises
- gris = cv2.cvtColor(imagen_cv, cv2.COLOR_BGR2GRAY)
- # Aplicar desenfoque gaussiano
- desenfocado = cv2.GaussianBlur(gris, (5, 5), 0)
- # Aplicar umbral de Otsu
- _, umbral = cv2.threshold(desenfocado, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)
- # Encontrar contornos
- contornos, _ = cv2.findContours(umbral, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
- # Crear una máscara en blanco
- mascara = np.zeros(imagen_cv.shape[:2], dtype=np.uint8)
- # Dibujar los contornos en la máscara
- cv2.drawContours(mascara, contornos, -1, (255), thickness=cv2.FILLED)
- # Aplicar operaciones morfológicas para mejorar la máscara
- kernel = np.ones((5,5), np.uint8)
- mascara = cv2.morphologyEx(mascara, cv2.MORPH_CLOSE, kernel)
- mascara = cv2.morphologyEx(mascara, cv2.MORPH_OPEN, kernel)
- # Suavizar los bordes de la máscara
- mascara = cv2.GaussianBlur(mascara, (5, 5), 0)
- # Convertir la máscara a imagen PIL
- mascara_pil = Image.fromarray(mascara)
- # Aplicar la máscara a la imagen original
- imagen_resultado = imagen_pil.copy()
- imagen_resultado.putalpha(mascara_pil)
- return imagen_resultado
- # Metodo 8. quitar fondo de imagen por el sistema de IA Pi.
- def quitar_fondo_pi(ruta_entrada, ruta_salida):
- # Selecciona la imagen original
- image = cv2.imread(ruta_entrada)
- # Convertir la imagen al espacio de color BGR y a formato float32
- image = cv2.cvtColor(image, cv2.COLOR_BGR2BGRA)
- image = np.float32(image)
- # Utiliza el algoritmo de "eliminación de fondo" de OpenCV
- bg_removal = cv2.createBackgroundSubtractorMOG2(detectShadows=False)
- mask = bg_removal.apply(image)
- # Crea una máscara de transparencia
- transparent_img = cv2.cvtColor(image, cv2.COLOR_BGR2BGRA)
- transparent_img[:, :, 3] = mask * 255
- # Guarda la imagen con el fondo eliminado en formato PNG
- cv2.imwrite(ruta_salida, transparent_img)
- # Retornar la imagen con el fondo eliminado en formato PNG
- return transparent_img
- # Proceso de imágenes según el método seleccionado
- def procesar_imagen(img, borrar_fondo, opacidad, metodo_borrado, ruta_entrada, ruta_salida):
- # Llamada a la función para detectar el color de fondo
- color_fondo = obtener_color_fondo(img)
- if borrar_fondo:
- if metodo_borrado == "1. Rembg":
- img_sin_fondo = quitar_fondo_rembg(img)
- elif metodo_borrado == "2. grabCut":
- img_sin_fondo = quitar_fondo_grabcut(img)
- elif metodo_borrado == "3. Chroma Key":
- img_sin_fondo = quitar_fondo_chroma(img)
- elif metodo_borrado == "4. Umbral":
- img_sin_fondo = quitar_fondo_umbral(img, 120)
- elif metodo_borrado == "5. Segmentacion":
- img_sin_fondo = quitar_fondo_segmentacion(img, 120)
- elif metodo_borrado == "5b. K-means Clustering":
- img_sin_fondo = quitar_fondo_kmeans(img)
- elif metodo_borrado == "6. Color Simple":
- img_sin_fondo = quitar_fondo_simple(img, color_fondo)
- elif metodo_borrado == "7. Advanced Plus":
- img_sin_fondo = quitar_fondo_avanzado(ruta_entrada)
- elif metodo_borrado == "8. Quitar fondo PI":
- img_sin_fondo = quitar_fondo_pi(ruta_entrada, ruta_salida)
- else:
- raise ValueError("Método de borrado no reconocido")
- return img
- return img
- # Detectar el color de fondo automaticamente
- def obtener_color_fondo(imagen):
- # Convertir a un array numpy
- imagen_np = np.array(imagen)
- # Tomar las esquinas (por ejemplo, las primeras 10 píxeles en cada esquina)
- esquinas = [
- imagen_np[0:10, 0:10], # Esquina superior izquierda
- imagen_np[0:10, -10:], # Esquina superior derecha
- imagen_np[-10:, 0:10], # Esquina inferior izquierda
- imagen_np[-10:, -10:] # Esquina inferior derecha
- ]
- # Combinar las esquinas y calcular el color promedio (usando la media)
- color_fondo = np.mean(np.vstack(esquinas), axis=(0, 1))
- return tuple(color_fondo.astype(int))[:3] # Solo devolver los 3 primeros valores (RGB)
- # Redimensionar y procesar las imágenes
- def redimensionar_fotos(directorio_entrada, directorio_salida, progreso_var, nombre_archivo_var, ancho, alto, borrar_fondo, opacidad, metodo_borrado):
- if not os.path.exists(directorio_salida):
- os.makedirs(directorio_salida)
- archivos = [f for f in os.listdir(directorio_entrada) if f.lower().endswith(('.png', '.jpg', '.jpeg', '.gif', '.bmp'))]
- total_archivos = len(archivos)
- for i, filename in enumerate(archivos):
- ruta_entrada = os.path.join(directorio_entrada, filename)
- nombre_base, extension = os.path.splitext(filename)
- ruta_salida = os.path.join(directorio_salida, f"{nombre_base}.png" if borrar_fondo else f"{nombre_base}.png")
- with Image.open(ruta_entrada) as img:
- img = procesar_imagen(img, borrar_fondo, opacidad, metodo_borrado, ruta_entrada, ruta_salida)
- if borrar_fondo:
- img = img.convert("RGBA")
- else:
- img = img.convert("RGB")
- img.thumbnail((ancho, alto), Image.LANCZOS)
- nueva_img = Image.new("RGBA" if borrar_fondo else "RGB", (ancho, alto), (0, 0, 0, 0) if borrar_fondo else (255, 255, 255))
- posicion = ((ancho - img.width) // 2, (alto - img.height) // 2)
- nueva_img.paste(img, posicion, img if borrar_fondo else None)
- nueva_img.save(ruta_salida, format='PNG')
- # if borrar_fondo:
- # nueva_img.save(ruta_salida, format='PNG')
- # else:
- # nueva_img = nueva_img.convert('RGB')
- # nueva_img.save(ruta_salida, format='JPEG', quality=95, optimize=True)
- progreso = (i + 1) / total_archivos * 100
- progreso_var.set(progreso)
- nombre_archivo_var.set(filename)
- root.update_idletasks()
- nombre_archivo_var.set("¡Proceso completado!")
- # Selector de directorios
- def seleccionar_directorio(entry):
- directorio = filedialog.askdirectory()
- entry.delete(0, tk.END)
- entry.insert(0, directorio)
- # Iniciar el proceso
- def iniciar_proceso():
- directorio_entrada = entrada_dir.get()
- directorio_salida = salida_dir.get()
- resolucion = resolucion_var.get().split('x')
- ancho, alto = map(int, resolucion)
- borrar_fondo = borrar_fondo_var.get()
- opacidad = opacidad_var.get() / 100.0
- metodo_borrado = metodo_var.get()
- if not directorio_entrada or not directorio_salida:
- messagebox.showerror("Error", "Por favor, selecciona los directorios de entrada y salida.")
- return
- def proceso():
- boton_inicio.config(state=tk.DISABLED)
- progreso_var.set(0)
- nombre_archivo_var.set("")
- root.update_idletasks()
- redimensionar_fotos(directorio_entrada, directorio_salida, progreso_var, nombre_archivo_var, ancho, alto, borrar_fondo, opacidad, metodo_borrado)
- boton_inicio.config(state=tk.NORMAL)
- messagebox.showinfo("Proceso Completado", "El procesamiento de imágenes ha finalizado.")
- threading.Thread(target=proceso, daemon=True).start()
- # Configuración de la ventana principal
- root = tk.Tk()
- root.title("Gestión de Fotos - LADYTEJE")
- root.geometry("600x600")
- # Variables
- progreso_var = tk.DoubleVar()
- nombre_archivo_var = tk.StringVar()
- resolucion_var = tk.StringVar()
- borrar_fondo_var = tk.BooleanVar()
- opacidad_var = tk.IntVar(value=0) # Valor inicial: 0 (borrado completo)
- metodo_var = tk.StringVar()
- # Widgets de la interfaz
- tk.Label(root, text="Directorio de entrada:").pack(pady=5)
- entrada_dir = tk.Entry(root, width=60)
- entrada_dir.pack(side=tk.TOP, pady=5)
- entrada_dir.insert(0, os.getcwd()) # Establece el valor inicial del Entry con el directorio actual
- tk.Button(root, text="Seleccionar", command=lambda: seleccionar_directorio(entrada_dir)).pack(pady=5)
- tk.Label(root, text="Directorio de salida:").pack(pady=5)
- salida_dir = tk.Entry(root, width=60)
- salida_dir.pack(side=tk.TOP, pady=5)
- salida_dir.insert(0, os.getcwd()) # Establece el valor inicial del Entry con el directorio actual
- tk.Button(root, text="Seleccionar", command=lambda: seleccionar_directorio(salida_dir)).pack(pady=5)
- tk.Label(root, text="Seleccionar resolución:").pack(pady=5)
- resoluciones = ['600x800', '800x600', '1024x768', '1280x720', '1920x1080']
- resolucion_var.set(resoluciones[0])
- resolucion_menu = tk.OptionMenu(root, resolucion_var, *resoluciones)
- resolucion_menu.pack(pady=5)
- tk.Checkbutton(root, text="Borrar fondo", variable=borrar_fondo_var).pack(pady=5)
- tk.Label(root, text="Opacidad del fondo:").pack(pady=5)
- opacidad_slider = tk.Scale(root, from_=0, to=100, orient=tk.HORIZONTAL, variable=opacidad_var)
- opacidad_slider.pack(pady=5)
- tk.Label(root, text="Método de borrado de fondo:").pack(pady=5)
- metodos_borrado = ["1. Rembg", "2. grabCut", "3. Chroma Key", "4. Umbral", "5. Segmentacion", "5b. K-means Clustering", "6. Color Simple", "7. Advanced Plus", "8. Quitar fondo PI"]
- metodo_var.set(metodos_borrado[0])
- metodo_menu = ttk.Combobox(root, textvariable=metodo_var, values=metodos_borrado)
- metodo_menu.pack(pady=5)
- boton_inicio = tk.Button(root, text="Iniciar proceso", command=iniciar_proceso)
- boton_inicio.pack(side=tk.LEFT, padx=20, pady=10)
- tk.Button(root, text="Salir", command=root.quit).pack(side=tk.RIGHT, padx=20, pady=20)
- tk.Label(root, text="Progreso del procesamiento:").pack(pady=5)
- progreso_barra = ttk.Progressbar(root, variable=progreso_var, maximum=100)
- progreso_barra.pack(fill=tk.X, padx=10, pady=10)
- tk.Label(root, textvariable=nombre_archivo_var).pack(pady=5)
- # Iniciar la aplicación
- root.mainloop()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement