Advertisement
nBiker77

Redimensionar_borrar_fotos.py

Sep 16th, 2024 (edited)
66
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 17.55 KB | Source Code | 0 0
  1. import os
  2. import tkinter as tk
  3. from tkinter import filedialog, ttk, messagebox
  4. from PIL import Image, ImageOps
  5. import threading
  6. from rembg import remove
  7. import numpy as np
  8. import cv2
  9.  
  10. def ajustar_opacidad(imagen_original, imagen_sin_fondo, opacidad):
  11.     original = np.array(imagen_original.convert("RGBA"))
  12.     sin_fondo = np.array(imagen_sin_fondo)
  13.     mascara = sin_fondo[:, :, 3] / 255.0
  14.     mascara_ajustada = mascara * (1 - opacidad)
  15.     resultado = original * (1 - mascara_ajustada[:, :, np.newaxis]) + sin_fondo * mascara_ajustada[:, :, np.newaxis]
  16.     return Image.fromarray(resultado.astype('uint8'), 'RGBA')
  17.  
  18. # Método 1: Usar rembg (que internamente usa U²-Net)
  19. def quitar_fondo_rembg(imagen):
  20.     return remove(imagen)
  21.  
  22. # Método 2: Usar grabCut de OpenCV (para imágenes con fondo complejo)
  23. def quitar_fondo_grabcut(imagen):
  24.     if isinstance(imagen, Image.Image):
  25.         imagen = np.array(imagen)
  26.  
  27.     # Asegurarse de que la imagen esté en formato RGB
  28.     if imagen.shape[2] == 4:  # Si es RGBA, convertir a RGB
  29.         imagen = cv2.cvtColor(imagen, cv2.COLOR_RGBA2RGB)
  30.  
  31.     # Crear una máscara inicial (todo 0)
  32.     mask = np.zeros(imagen.shape[:2], np.uint8)
  33.  
  34.     # Modelos necesarios para grabCut
  35.     bgdModel = np.zeros((1, 65), np.float64)
  36.     fgdModel = np.zeros((1, 65), np.float64)
  37.  
  38.     # Rectángulo alrededor del área que contiene el primer plano (la parte interesante de la imagen)
  39.     height, width = imagen.shape[:2]
  40.     rect = (10, 10, width - 10, height - 10)
  41.  
  42.     # Aplicar grabCut
  43.     cv2.grabCut(imagen, mask, rect, bgdModel, fgdModel, 5, cv2.GC_INIT_WITH_RECT)
  44.  
  45.     # Crear una máscara donde 0 y 2 son background, 1 y 3 son foreground
  46.     mask2 = np.where((mask == 2) | (mask == 0), 0, 1).astype('uint8')
  47.  
  48.     # Aplicar la máscara a la imagen
  49.     imagen_sin_fondo = imagen * mask2[:, :, np.newaxis]
  50.  
  51.     # Convertir la imagen a RGBA para añadir el canal alfa (transparencia)
  52.     img_transparente = cv2.cvtColor(imagen_sin_fondo, cv2.COLOR_RGB2RGBA)
  53.  
  54.     # Establecer el canal alfa en base a la máscara generada
  55.     img_transparente[:, :, 3] = mask2 * 255
  56.  
  57.     return Image.fromarray(img_transparente)
  58.  
  59. # Método 3: Chroma key (para fondos sólidos)
  60. def quitar_fondo_chroma(imagen, color_fondo=(255, 255, 255), umbral=100):
  61.     if isinstance(imagen, Image.Image):
  62.         imagen = np.array(imagen)
  63.  
  64.     # Asegurarse de que la imagen tenga solo 3 canales (RGB)
  65.     if imagen.shape[2] == 4:  # Si es RGBA, convertir a RGB
  66.         imagen = cv2.cvtColor(imagen, cv2.COLOR_RGBA2RGB)
  67.  
  68.     # Crear un array del color de fondo (debe ser RGB)
  69.     fondo_rgb = np.full(imagen.shape, color_fondo, dtype=np.uint8)
  70.  
  71.     # Calcular la diferencia entre la imagen y el color de fondo
  72.     diferencia = cv2.absdiff(imagen, fondo_rgb)
  73.  
  74.     # Convertir la diferencia a escala de grises para crear la máscara
  75.     mascara = cv2.cvtColor(diferencia, cv2.COLOR_BGR2GRAY)
  76.  
  77.     # Aplicar un umbral a la máscara para obtener una máscara binaria
  78.     _, mascara_binaria = cv2.threshold(mascara, umbral, 255, cv2.THRESH_BINARY)
  79.  
  80.     # Convertir la imagen a RGBA
  81.     img_transparente = cv2.cvtColor(imagen, cv2.COLOR_RGB2BGRA)
  82.  
  83.     # Usar la máscara binaria para asignar transparencia
  84.     img_transparente[:, :, 3] = 255 - mascara_binaria
  85.  
  86.     return Image.fromarray(img_transparente)
  87.  
  88. # Método 4: Umbralizacion para eliminar el fondo
  89. def quitar_fondo_umbral(imagen, umbral=128):
  90.     # Asegurarse de que la imagen esté en el formato correcto
  91.     if not isinstance(imagen, np.ndarray):
  92.         # Convertir imagen PIL a formato NumPy array
  93.         imagen = np.array(imagen)
  94.  
  95.     # Convertir la imagen a escala de grises
  96.     gris = cv2.cvtColor(imagen, cv2.COLOR_RGBA2GRAY)
  97.  
  98.     # Aplicar el umbral para crear una máscara
  99.     _, mascara = cv2.threshold(gris, umbral, 255, cv2.THRESH_BINARY)
  100.  
  101.     # Invertir la máscara
  102.     mascara_invertida = cv2.bitwise_not(mascara)
  103.  
  104.     # Aplicar la máscara invertida para quitar el fondo
  105.     resultado = cv2.bitwise_and(imagen, imagen, mask=mascara_invertida)
  106.  
  107.     # Convertir de nuevo a formato PIL para el retorno
  108.     return Image.fromarray(resultado)
  109.  
  110. # Metodo 5: Segmentacion de color con Umbral
  111. def quitar_fondo_segmentacion(imagen, umbral=120):
  112.     # Convertir la imagen a formato RGB
  113.     imagen_rgb = imagen.convert('RGB')
  114.     imagen_np = np.array(imagen_rgb)
  115.  
  116.     # Convertir la imagen a formato BGR (OpenCV usa BGR por defecto)
  117.     imagen_bgr = cv2.cvtColor(imagen_np, cv2.COLOR_RGB2BGR)
  118.  
  119.     # Convertir la imagen a formato float32 para OpenCV
  120.     imagen_bgr = np.float32(imagen_bgr) / 255.0
  121.  
  122.     # Aplicar un desenfoque gaussiano
  123.     imagen_suavizada = cv2.GaussianBlur(imagen_bgr, (5, 5), 0)
  124.  
  125.     # Convertir la imagen suavizada a escala de grises
  126.     imagen_gris = cv2.cvtColor(imagen_suavizada, cv2.COLOR_BGR2GRAY)
  127.  
  128.     # Aplicar un umbral para segmentar la imagen
  129.     _, imagen_segmentada = cv2.threshold(imagen_gris, 0.5, 1.0, cv2.THRESH_BINARY)
  130.  
  131.     # Crear una máscara a partir de la imagen segmentada
  132.     mascara = (imagen_segmentada * 255).astype(np.uint8)
  133.  
  134.     # Aplicar la máscara a la imagen original
  135.     imagen_final = cv2.bitwise_and(imagen_bgr, imagen_bgr, mask=mascara)
  136.  
  137.     # Convertir la imagen final a formato uint8
  138.     imagen_final = np.uint8(imagen_final * 255)
  139.  
  140.     # Convertir la imagen final a formato RGB y de vuelta a PIL
  141.     imagen_final = cv2.cvtColor(imagen_final, cv2.COLOR_BGR2RGB)
  142.     imagen_final_pil = Image.fromarray(imagen_final)
  143.  
  144.     return imagen_final_pil
  145.  
  146. # Metodo 5A: Alternativa de Segmentacion por sistema K-means Clustering
  147. def quitar_fondo_kmeans(imagen, k=2):
  148.     # Asegurarse de que la imagen esté en el formato correcto (NumPy array)
  149.     if not isinstance(imagen, np.ndarray):
  150.         imagen = np.array(imagen)
  151.  
  152.     # Verificar si la imagen tiene un canal alfa (RGBA), en ese caso eliminarlo para el procesamiento
  153.     if imagen.shape[2] == 4:
  154.         imagen = cv2.cvtColor(imagen, cv2.COLOR_RGBA2RGB)
  155.  
  156.     # Convertir la imagen a un array 2D para K-means
  157.     Z = imagen.reshape((-1, 3))
  158.  
  159.     # Convertir a float32 para K-means
  160.     Z = np.float32(Z)
  161.  
  162.     # Criterios de K-means (máx. 10 iteraciones o precisión 1.0)
  163.     criterio = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 10, 1.0)
  164.  
  165.     # Aplicar K-means para segmentación
  166.     _, labels, centers = cv2.kmeans(Z, k, None, criterio, 10, cv2.KMEANS_RANDOM_CENTERS)
  167.  
  168.     # Convertir los centros de vuelta a 8 bits
  169.     centers = np.uint8(centers)
  170.  
  171.     # Etiquetar las imágenes usando los labels de K-means
  172.     resultado = centers[labels.flatten()]
  173.     resultado = resultado.reshape(imagen.shape)
  174.  
  175.     # Crear una máscara basada en el cluster que debería ser el fondo
  176.     fondo = np.min(centers, axis=0)
  177.     mascara = np.all(resultado == fondo, axis=-1).astype(np.uint8) * 255
  178.  
  179.     # Invertir la máscara
  180.     mascara_invertida = cv2.bitwise_not(mascara)
  181.  
  182.     # Aplicar la máscara para eliminar el fondo
  183.     resultado_con_fondo_eliminado = cv2.bitwise_and(imagen, imagen, mask=mascara_invertida)
  184.  
  185.     # Convertir de nuevo a formato PIL para el retorno
  186.     return Image.fromarray(resultado_con_fondo_eliminado)
  187.  
  188. # Metodo 6: Sustitucion de Color Simple
  189. def quitar_fondo_simple(imagen, color_fondo):
  190.     imagen = np.array(imagen.convert("RGBA"))
  191.     color_fondo = np.array(color_fondo + (255,))  # Añadir alfa al color de fondo
  192.  
  193.     # Crear una máscara donde el color de fondo coincide
  194.     mascara = np.all(imagen == color_fondo, axis=-1)
  195.  
  196.     # Aplicar la máscara
  197.     imagen[mascara] = [0, 0, 0, 0]
  198.  
  199.     return Image.fromarray(imagen, 'RGBA')
  200.  
  201. # Metodo 7: Metodo avanzado para quitar el fondo
  202. def quitar_fondo_avanzado(ruta_imagen_entrada):
  203.     # Leer la imagen con Pillow
  204.     imagen_pil = Image.open(ruta_imagen_entrada)
  205.  
  206.     # Convertir a formato OpenCV
  207.     imagen_cv = cv2.cvtColor(np.array(imagen_pil), cv2.COLOR_RGB2BGR)
  208.  
  209.     # Convertir a escala de grises
  210.     gris = cv2.cvtColor(imagen_cv, cv2.COLOR_BGR2GRAY)
  211.  
  212.     # Aplicar desenfoque gaussiano
  213.     desenfocado = cv2.GaussianBlur(gris, (5, 5), 0)
  214.  
  215.     # Aplicar umbral de Otsu
  216.     _, umbral = cv2.threshold(desenfocado, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)
  217.  
  218.     # Encontrar contornos
  219.     contornos, _ = cv2.findContours(umbral, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
  220.  
  221.     # Crear una máscara en blanco
  222.     mascara = np.zeros(imagen_cv.shape[:2], dtype=np.uint8)
  223.  
  224.     # Dibujar los contornos en la máscara
  225.     cv2.drawContours(mascara, contornos, -1, (255), thickness=cv2.FILLED)
  226.  
  227.     # Aplicar operaciones morfológicas para mejorar la máscara
  228.     kernel = np.ones((5,5), np.uint8)
  229.     mascara = cv2.morphologyEx(mascara, cv2.MORPH_CLOSE, kernel)
  230.     mascara = cv2.morphologyEx(mascara, cv2.MORPH_OPEN, kernel)
  231.  
  232.     # Suavizar los bordes de la máscara
  233.     mascara = cv2.GaussianBlur(mascara, (5, 5), 0)
  234.  
  235.     # Convertir la máscara a imagen PIL
  236.     mascara_pil = Image.fromarray(mascara)
  237.  
  238.     # Aplicar la máscara a la imagen original
  239.     imagen_resultado = imagen_pil.copy()
  240.     imagen_resultado.putalpha(mascara_pil)
  241.  
  242.     return imagen_resultado
  243.  
  244. # Metodo 8. quitar fondo de imagen por el sistema de IA Pi.
  245. def quitar_fondo_pi(ruta_entrada, ruta_salida):
  246.     # Selecciona la imagen original
  247.     image = cv2.imread(ruta_entrada)
  248.  
  249.     # Convertir la imagen al espacio de color BGR y a formato float32
  250.     image = cv2.cvtColor(image, cv2.COLOR_BGR2BGRA)
  251.     image = np.float32(image)
  252.  
  253.     # Utiliza el algoritmo de "eliminación de fondo" de OpenCV
  254.     bg_removal = cv2.createBackgroundSubtractorMOG2(detectShadows=False)
  255.     mask = bg_removal.apply(image)
  256.  
  257.     # Crea una máscara de transparencia
  258.     transparent_img = cv2.cvtColor(image, cv2.COLOR_BGR2BGRA)
  259.     transparent_img[:, :, 3] = mask * 255
  260.  
  261.     # Guarda la imagen con el fondo eliminado en formato PNG
  262.     cv2.imwrite(ruta_salida, transparent_img)
  263.     # Retornar la imagen con el fondo eliminado en formato PNG
  264.     return transparent_img
  265.  
  266. # Proceso de imágenes según el método seleccionado
  267. def procesar_imagen(img, borrar_fondo, opacidad, metodo_borrado, ruta_entrada, ruta_salida):
  268.     # Llamada a la función para detectar el color de fondo
  269.     color_fondo = obtener_color_fondo(img)
  270.  
  271.     if borrar_fondo:
  272.         if metodo_borrado == "1. Rembg":
  273.             img_sin_fondo = quitar_fondo_rembg(img)
  274.         elif metodo_borrado == "2. grabCut":
  275.             img_sin_fondo = quitar_fondo_grabcut(img)
  276.         elif metodo_borrado == "3. Chroma Key":
  277.             img_sin_fondo = quitar_fondo_chroma(img)
  278.         elif metodo_borrado == "4. Umbral":
  279.             img_sin_fondo = quitar_fondo_umbral(img, 120)
  280.         elif metodo_borrado == "5. Segmentacion":
  281.             img_sin_fondo = quitar_fondo_segmentacion(img, 120)
  282.         elif metodo_borrado == "5b. K-means Clustering":
  283.             img_sin_fondo = quitar_fondo_kmeans(img)
  284.         elif metodo_borrado == "6. Color Simple":
  285.             img_sin_fondo = quitar_fondo_simple(img, color_fondo)
  286.         elif metodo_borrado == "7. Advanced Plus":
  287.             img_sin_fondo = quitar_fondo_avanzado(ruta_entrada)
  288.         elif metodo_borrado == "8. Quitar fondo PI":
  289.             img_sin_fondo = quitar_fondo_pi(ruta_entrada, ruta_salida)
  290.         else:
  291.             raise ValueError("Método de borrado no reconocido")
  292.         return img
  293.     return img
  294.  
  295. # Detectar el color de fondo automaticamente
  296. def obtener_color_fondo(imagen):
  297.     # Convertir a un array numpy
  298.     imagen_np = np.array(imagen)
  299.  
  300.     # Tomar las esquinas (por ejemplo, las primeras 10 píxeles en cada esquina)
  301.     esquinas = [
  302.         imagen_np[0:10, 0:10],        # Esquina superior izquierda
  303.         imagen_np[0:10, -10:],        # Esquina superior derecha
  304.         imagen_np[-10:, 0:10],        # Esquina inferior izquierda
  305.         imagen_np[-10:, -10:]         # Esquina inferior derecha
  306.     ]
  307.  
  308.     # Combinar las esquinas y calcular el color promedio (usando la media)
  309.     color_fondo = np.mean(np.vstack(esquinas), axis=(0, 1))
  310.  
  311.     return tuple(color_fondo.astype(int))[:3]  # Solo devolver los 3 primeros valores (RGB)
  312.  
  313. # Redimensionar y procesar las imágenes
  314. def redimensionar_fotos(directorio_entrada, directorio_salida, progreso_var, nombre_archivo_var, ancho, alto, borrar_fondo, opacidad, metodo_borrado):
  315.     if not os.path.exists(directorio_salida):
  316.         os.makedirs(directorio_salida)
  317.  
  318.     archivos = [f for f in os.listdir(directorio_entrada) if f.lower().endswith(('.png', '.jpg', '.jpeg', '.gif', '.bmp'))]
  319.     total_archivos = len(archivos)
  320.  
  321.     for i, filename in enumerate(archivos):
  322.         ruta_entrada = os.path.join(directorio_entrada, filename)
  323.         nombre_base, extension = os.path.splitext(filename)
  324.         ruta_salida = os.path.join(directorio_salida, f"{nombre_base}.png" if borrar_fondo else f"{nombre_base}.png")
  325.  
  326.         with Image.open(ruta_entrada) as img:
  327.             img = procesar_imagen(img, borrar_fondo, opacidad, metodo_borrado, ruta_entrada, ruta_salida)
  328.  
  329.             if borrar_fondo:
  330.                 img = img.convert("RGBA")
  331.             else:
  332.                 img = img.convert("RGB")
  333.  
  334.             img.thumbnail((ancho, alto), Image.LANCZOS)
  335.             nueva_img = Image.new("RGBA" if borrar_fondo else "RGB", (ancho, alto), (0, 0, 0, 0) if borrar_fondo else (255, 255, 255))
  336.             posicion = ((ancho - img.width) // 2, (alto - img.height) // 2)
  337.             nueva_img.paste(img, posicion, img if borrar_fondo else None)
  338.             nueva_img.save(ruta_salida, format='PNG')
  339.  
  340.             # if borrar_fondo:
  341.             #    nueva_img.save(ruta_salida, format='PNG')
  342.             # else:
  343.             #    nueva_img = nueva_img.convert('RGB')
  344.             #    nueva_img.save(ruta_salida, format='JPEG', quality=95, optimize=True)
  345.  
  346.         progreso = (i + 1) / total_archivos * 100
  347.         progreso_var.set(progreso)
  348.         nombre_archivo_var.set(filename)
  349.         root.update_idletasks()
  350.  
  351.     nombre_archivo_var.set("¡Proceso completado!")
  352.  
  353. # Selector de directorios
  354. def seleccionar_directorio(entry):
  355.     directorio = filedialog.askdirectory()
  356.     entry.delete(0, tk.END)
  357.     entry.insert(0, directorio)
  358.  
  359. # Iniciar el proceso
  360. def iniciar_proceso():
  361.     directorio_entrada = entrada_dir.get()
  362.     directorio_salida = salida_dir.get()
  363.     resolucion = resolucion_var.get().split('x')
  364.     ancho, alto = map(int, resolucion)
  365.     borrar_fondo = borrar_fondo_var.get()
  366.     opacidad = opacidad_var.get() / 100.0
  367.     metodo_borrado = metodo_var.get()
  368.  
  369.     if not directorio_entrada or not directorio_salida:
  370.         messagebox.showerror("Error", "Por favor, selecciona los directorios de entrada y salida.")
  371.         return
  372.  
  373.     def proceso():
  374.         boton_inicio.config(state=tk.DISABLED)
  375.         progreso_var.set(0)
  376.         nombre_archivo_var.set("")
  377.         root.update_idletasks()
  378.         redimensionar_fotos(directorio_entrada, directorio_salida, progreso_var, nombre_archivo_var, ancho, alto, borrar_fondo, opacidad, metodo_borrado)
  379.         boton_inicio.config(state=tk.NORMAL)
  380.         messagebox.showinfo("Proceso Completado", "El procesamiento de imágenes ha finalizado.")
  381.  
  382.     threading.Thread(target=proceso, daemon=True).start()
  383.  
  384. # Configuración de la ventana principal
  385. root = tk.Tk()
  386. root.title("Gestión de Fotos - LADYTEJE")
  387. root.geometry("600x600")
  388.  
  389. # Variables
  390. progreso_var = tk.DoubleVar()
  391. nombre_archivo_var = tk.StringVar()
  392. resolucion_var = tk.StringVar()
  393. borrar_fondo_var = tk.BooleanVar()
  394. opacidad_var = tk.IntVar(value=0)  # Valor inicial: 0 (borrado completo)
  395. metodo_var = tk.StringVar()
  396.  
  397. # Widgets de la interfaz
  398. tk.Label(root, text="Directorio de entrada:").pack(pady=5)
  399. entrada_dir = tk.Entry(root, width=60)
  400. entrada_dir.pack(side=tk.TOP, pady=5)
  401. entrada_dir.insert(0, os.getcwd())  # Establece el valor inicial del Entry con el directorio actual
  402. tk.Button(root, text="Seleccionar", command=lambda: seleccionar_directorio(entrada_dir)).pack(pady=5)
  403.  
  404. tk.Label(root, text="Directorio de salida:").pack(pady=5)
  405. salida_dir = tk.Entry(root, width=60)
  406. salida_dir.pack(side=tk.TOP, pady=5)
  407. salida_dir.insert(0, os.getcwd())  # Establece el valor inicial del Entry con el directorio actual
  408. tk.Button(root, text="Seleccionar", command=lambda: seleccionar_directorio(salida_dir)).pack(pady=5)
  409.  
  410. tk.Label(root, text="Seleccionar resolución:").pack(pady=5)
  411. resoluciones = ['600x800', '800x600', '1024x768', '1280x720', '1920x1080']
  412. resolucion_var.set(resoluciones[0])
  413. resolucion_menu = tk.OptionMenu(root, resolucion_var, *resoluciones)
  414. resolucion_menu.pack(pady=5)
  415.  
  416. tk.Checkbutton(root, text="Borrar fondo", variable=borrar_fondo_var).pack(pady=5)
  417.  
  418. tk.Label(root, text="Opacidad del fondo:").pack(pady=5)
  419. opacidad_slider = tk.Scale(root, from_=0, to=100, orient=tk.HORIZONTAL, variable=opacidad_var)
  420. opacidad_slider.pack(pady=5)
  421.  
  422. tk.Label(root, text="Método de borrado de fondo:").pack(pady=5)
  423. 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"]
  424. metodo_var.set(metodos_borrado[0])
  425. metodo_menu = ttk.Combobox(root, textvariable=metodo_var, values=metodos_borrado)
  426. metodo_menu.pack(pady=5)
  427.  
  428. boton_inicio = tk.Button(root, text="Iniciar proceso", command=iniciar_proceso)
  429. boton_inicio.pack(side=tk.LEFT, padx=20, pady=10)
  430.  
  431. tk.Button(root, text="Salir", command=root.quit).pack(side=tk.RIGHT, padx=20, pady=20)
  432.  
  433. tk.Label(root, text="Progreso del procesamiento:").pack(pady=5)
  434. progreso_barra = ttk.Progressbar(root, variable=progreso_var, maximum=100)
  435. progreso_barra.pack(fill=tk.X, padx=10, pady=10)
  436.  
  437. tk.Label(root, textvariable=nombre_archivo_var).pack(pady=5)
  438.  
  439. # Iniciar la aplicación
  440. root.mainloop()
  441.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement