Saltar al contenido

Historial del chat

ChatGPT

Sora

Explorar GPT

Proyectos

Nuevo proyecto

Hoy

Perfeccionar face_morpher\enhance19.py

Remover fondo video IA

7 días anteriores

Códigos GitHub Lip Sync

Documento para Consejería Fomento

30 días anteriores

IA para mejorar videos

Generar imagen desde foto

Ver planesAcceso ilimitado, características de Team y más

Abrir barra lateral

ChatGPT o3-mini-high

ChatGPT o3-mini-high

Compartir

PLUS

Dijiste:

enhance14.py

Python

Al ejecutar este py, me dice esto cmd:
C:\Windows\System32\Automation-scripts\face_morpher>py -3.10 C:\Windows\System32\Automation-scripts\face_morpher\enhance14.py --input_folder "C:\alinaheadra\virgen" --output_folder "C:\alinaheadra\virgen\enhanced2"
🖥️ Dispositivo utilizado: cuda
🚀 GPU detectada: NVIDIA GeForce RTX 3060

Modelos disponibles:
1. RealESRGAN_x4plus (Scale 4x)
2. RealESRNet_x4plus (Scale 4x)
3. RealESRGAN_x4plus_anime_6B (Scale 4x)
4. RealESRGAN_x2plus (Scale 2x)
5. realesr-general-x4v3 (Scale 4x)
6. realesr-animevideov3 (Scale 4x)
7. RealESRGAN_x2 (Scale 2x)
8. RealESRGAN_x4 (Scale 4x)

Elige el número de modelo: 1
Se encontraron 2 imágenes en C:\alinaheadra\virgen
Procesando (1/2): C:\alinaheadra\virgen\1 Cara Fondo Azul Formato para Copy&Copy 3840 x 2379 x 32 px 50%.png
C:\Users\Administrador\AppData\Local\Programs\Python\Python310\lib\site-packages\torch\nn\modules\conv.py:549: UserWarning: cuDNN cannot be used for large non-batch-splittable convolutions if the V8 API is not enabled or before cuDNN version 9.3+. Consider upgrading cuDNN and/or enabling the V8 API for better efficiency. (Triggered internally at C:\actions-runner\_work\pytorch\pytorch\pytorch\aten\src\ATen\native\Convolution.cpp:432.)
return F.conv2d(
Traceback (most recent call last):
File "C:\Windows\System32\Automation-scripts\face_morpher\enhance14.py", line 181, in <module>
enhance_images(args.input_folder, args.output_folder, args.tile, selected_model, selected_scale)
File "C:\Windows\System32\Automation-scripts\face_morpher\enhance14.py", line 90, in enhance_images
enhanced_img, img_mode = upsampler.enhance(img)
File "C:\Users\Administrador\AppData\Local\Programs\Python\Python310\lib\site-packages\torch\utils\_contextlib.py", line 116, in decorate_context
return func(*args, **kwargs)
File "c:\alinaheadra\sadtalker\real-esrgan\realesrgan\utils.py", line 223, in enhance
self.process()
File "c:\alinaheadra\sadtalker\real-esrgan\realesrgan\utils.py", line 115, in process
self.output = self.model(self.img)
File "C:\Users\Administrador\AppData\Local\Programs\Python\Python310\lib\site-packages\torch\nn\modules\module.py", line 1739, in _wrapped_call_impl
return self._call_impl(*args, **kwargs)
File "C:\Users\Administrador\AppData\Local\Programs\Python\Python310\lib\site-packages\torch\nn\modules\module.py", line 1750, in _call_impl
return forward_call(*args, **kwargs)
File "C:\Users\Administrador\AppData\Local\Programs\Python\Python310\lib\site-packages\basicsr\archs\rrdbnet_arch.py", line 117, in forward
feat = self.lrelu(self.conv_up2(F.interpolate(feat, scale_factor=2, mode='nearest')))
File "C:\Users\Administrador\AppData\Local\Programs\Python\Python310\lib\site-packages\torch\nn\modules\module.py", line 1739, in _wrapped_call_impl
return self._call_impl(*args, **kwargs)
File "C:\Users\Administrador\AppData\Local\Programs\Python\Python310\lib\site-packages\torch\nn\modules\module.py", line 1750, in _call_impl
return forward_call(*args, **kwargs)
File "C:\Users\Administrador\AppData\Local\Programs\Python\Python310\lib\site-packages\torch\nn\modules\conv.py", line 554, in forward
return self._conv_forward(input, self.weight, self.bias)
File "C:\Users\Administrador\AppData\Local\Programs\Python\Python310\lib\site-packages\torch\nn\modules\conv.py", line 549, in _conv_forward
return F.conv2d(
torch.OutOfMemoryError: CUDA out of memory. Tried to allocate 39.22 GiB. GPU 0 has a total capacity of 12.00 GiB of which 542.00 MiB is free. Of the allocated memory 10.12 GiB is allocated by PyTorch, and 316.10 MiB is reserved by PyTorch but unallocated. If reserved but unallocated memory is large try setting PYTORCH_CUDA_ALLOC_CONF=expandable_segments:True to avoid fragmentation. See documentation for Memory Management (https://pytorch.org/docs/stable/notes/cuda.html#environment-variables)
/////////////////

Como se podria arreglar? La Imagen que esta uintentando hacer enhance es la que te paso en adjunto tambien

ChatGPT dijo:

Razonado sobre error de memoria CUDA durante 28 segundos

El error se debe a que el modelo intenta cargar y procesar una imagen de alta resolución en una GPU de 12 GB, lo que provoca que se intente asignar mucha más memoria de la disponible (en este caso, 39+ GB). Aquí te dejo varias opciones para solucionarlo:

1. Procesar la imagen en "tiles" (porciones)

El script acepta un parámetro --tile que permite dividir la imagen en bloques más pequeños. Esto reduce el uso total de memoria al procesar solo una parte de la imagen a la vez en lugar de la imagen completa. Por ejemplo, puedes probar:

mathematica

Copiar

py -3.10 C:\Windows\System32\Automation-scripts\face_morpher\enhance14.py --input_folder "C:\alinaheadra\virgen" --output_folder "C:\alinaheadra\virgen\enhanced2" --tile 100

Ajusta el valor (por ejemplo, 50, 100, 200) según el tamaño de la imagen y la capacidad de tu GPU. Esto suele ser la solución más efectiva para evitar el "out of memory".

2. Ajustar la configuración de CUDA

El mensaje de error sugiere establecer la variable de entorno PYTORCH_CUDA_ALLOC_CONF a expandable_segments:True. Puedes probar esto en la terminal antes de ejecutar el script:

python

Copiar

set PYTORCH_CUDA_ALLOC_CONF=expandable_segments:True

Luego, ejecuta el script. Esto puede ayudar a reducir la fragmentación de memoria, aunque si el requerimiento es muy grande (como en tu caso) quizá no sea suficiente por sí solo.

3. Reducir la resolución de la imagen de entrada

Si la imagen es muy grande y no necesitas la máxima calidad en el resultado final, considera reducir previamente su resolución con alguna herramienta de edición o con un script en Python. Así el modelo trabajará con una versión más pequeña y requerirá menos memoria.

4. Liberar memoria GPU y/o usar CPU

Liberar memoria: Asegúrate de cerrar otras aplicaciones que puedan estar usando la GPU para maximizar la memoria libre.

Usar CPU: Si bien la ejecución en CPU será considerablemente más lenta, puedes forzar el procesamiento en CPU. En el script se detecta automáticamente si no hay GPU, por lo que puedes deshabilitar temporalmente la GPU (por ejemplo, usando CUDA_VISIBLE_DEVICES="" en tu terminal) para ver si el procesamiento con CPU resuelve el problema.

Resumen

La solución más común y práctica en este caso es utilizar el parámetro --tile para dividir la imagen en porciones más pequeñas. Esto reduce el uso simultáneo de memoria y previene el error de "out of memory".

Implementa alguno de estos enfoques (o una combinación de ellos) para evitar que el modelo intente asignar una cantidad de memoria que supere la capacidad de tu GPU.

¿Te gustaría más detalle sobre alguna de las opciones o ayuda para ajustar el tamaño de tile según tu caso?

Dijiste:

Cmd me dice:
🖥️ Dispositivo utilizado: cuda
🚀 GPU detectada: NVIDIA GeForce RTX 3060
Imagen 1/2 procesada en 13.67 s.
Tiempo total transcurrido: 13.67 s. Estimación restante: 13.67 s (00:00:13).
Imagen guardada: C:\alinaheadra\virgen\enhanced2\1 Cara Fondo Azul Formato para Copy&Copy 3840 x 2379 x 32 px 50%.png

Procesando (2/2): C:\alinaheadra\virgen\Niño by Photopea.png
[ WARN:[email-masked]] global loadsave.cpp:268 cv::findDecoder imread_('C:\alinaheadra\virgen\Ni├▒o by Photopea.png'): can't open/read file: check file path/integrity
No se pudo cargar C:\alinaheadra\virgen\Niño by Photopea.png
Proceso completado. Tiempo total: 13.67 s.

C:\Windows\System32\Automation-scripts\face_morpher>

ChatGPT dijo:

Razonado sobre error al cargar imagen durante 23 segundos

El mensaje indica que la imagen "Niño by Photopea.png" no se pudo cargar, y lo más probable es que se deba a problemas de codificación en la ruta. Es decir, OpenCV (usado internamente por cv2.imread) tiene dificultades para interpretar correctamente caracteres especiales (como la "ñ") en el nombre del archivo, por lo que transforma "Niño" en "Ni├▒o", impidiendo encontrar y leer el archivo.

Aquí te dejo algunas posibles soluciones:

1. Renombrar el Archivo

La solución más sencilla es renombrar el archivo para eliminar caracteres especiales. Por ejemplo, cambia "Niño by Photopea.png" a "Nino by Photopea.png" y prueba de nuevo la ejecución. Esto evitará problemas de codificación y permitirá que OpenCV encuentre el archivo correctamente.

2. Utilizar una Lectura Alternativa con NumPy

Si no quieres o no puedes renombrar el archivo, puedes modificar el código para leer la imagen utilizando np.fromfile en combinación con cv2.imdecode, lo cual permite manejar rutas con caracteres especiales. Por ejemplo, podrías reemplazar esta parte del código:

python

Copiar

img = cv2.imread(input_path, cv2.IMREAD_UNCHANGED)

por algo como:

python

Copiar

import numpy as np
import cv2

with open(input_path, 'rb') as f:
img_array = np.frombuffer(f.read(), np.uint8)
img = cv2.imdecode(img_array, cv2.IMREAD_UNCHANGED)

O alternativamente, directamente:

python

Copiar

import numpy as np
import cv2

img_array = np.fromfile(input_path, np.uint8)
img = cv2.imdecode(img_array, cv2.IMREAD_UNCHANGED)

Este método convierte el archivo a un arreglo de bytes que se decodifica correctamente, evitando así problemas relacionados con la codificación del nombre del archivo.

3. Verificar la Integridad y la Ruta del Archivo

Asegúrate de que el archivo efectivamente exista en la ruta especificada y que no haya otros problemas de integridad. A veces también es útil copiar el archivo a otro directorio donde la ruta no contenga caracteres especiales en ninguna parte de la misma.

Cualquiera de estas soluciones te ayudará a cargar el archivo correctamente y continuar con el proceso de mejora de las imágenes. ¿Necesitas ayuda adicional con alguna de estas opciones o algún ejemplo más detallado?

Dijiste:

enhance14.py

Python

Ya funciona con el tile, y quitandole las ñ, como se podria solucionar esto de forma automatica, que si ve que no va a poder, cambie a tile, y que si encuentra caracteres raros, renombre el archivo o lo solucione, habra alguna forma? Te paso el archivo py

ChatGPT dijo:

Razonado durante 31 segundos

Aquí te propongo dos mejoras que puedes incorporar al script para que se solucione automáticamente:

────────────────────────────

1. Lectura robusta de imágenes con nombres con caracteres especiales

────────────────────────────

La idea es que, si la lectura con cv2.imread falla (lo que puede pasar con nombres que incluyen "ñ" u otros caracteres), se intente leer la imagen utilizando np.fromfile y cv2.imdecode. Esto evita tener que renombrar el archivo (aunque también podrías hacerlo si lo prefieres). Por ejemplo, puedes definir una función auxiliar para leer la imagen:

python

Copiar

import numpy as np
import cv2

def robust_imread(path):
# Intenta leer de forma directa
img = cv2.imread(path, cv2.IMREAD_UNCHANGED)
if img is None:
try:
# Lee el archivo como array de bytes y decodifica
img_array = np.fromfile(path, np.uint8)
img = cv2.imdecode(img_array, cv2.IMREAD_UNCHANGED)
if img is not None:
print(f"Imagen leída correctamente con imdecode: {path}")
except Exception as e:
print(f"Error al leer la imagen {path} con imdecode: {e}")
return img

Luego, en tu bucle de procesamiento reemplazas la lectura original:

python

Copiar

img = robust_imread(input_path)
if img is None:
print(f"No se pudo cargar {input_path}")
continue

Esta solución evita el problema de caracteres especiales sin modificar el nombre de archivo.

Si prefieres cambiar el nombre automáticamente, podrías usar la librería unicodedata para “normalizar” la cadena y generar una versión ASCII. Por ejemplo:

python

Copiar

import unicodedata

def remove_accents(text):
return unicodedata.normalize('NFKD', text).encode('ASCII', 'ignore').decode('ASCII')

Y si detectas que el nombre original tiene caracteres fuera del rango ASCII, podrías renombrar el archivo de forma automática. Por ejemplo, al inicio del procesamiento podrías revisar:

python

Copiar

if any(ord(char) > 127 for char in filename):
new_filename = remove_accents(filename)
new_path = os.path.join(input_folder, new_filename)
try:
os.rename(input_path, new_path)
print(f"Archivo renombrado de '{filename}' a '{new_filename}'")
input_path = new_path
filename = new_filename # Actualizamos para el resto del proceso
except Exception as e:
print(f"No se pudo renombrar el archivo {filename}: {e}")

────────────────────────────

2. Fallback automático a tiling en caso de error de memoria

────────────────────────────

Puedes envolver la llamada a upsampler.enhance(img) en un bloque try/except que detecte un error de "CUDA out of memory". Si se produce, vacías la caché de CUDA (con torch.cuda.empty_cache()) y vuelves a instanciar el objeto RealESRGANer con un tamaño de tile (por ejemplo, 100 o el valor que consideres adecuado) y reintentas el procesamiento. Por ejemplo:

python

Copiar

# Dentro del loop, después de haber leído la imagen:
try:
enhanced_img, img_mode = upsampler.enhance(img)
except RuntimeError as e:
if "CUDA out of memory" in str(e):
print("Error de memoria detectado, reintentando con tiling...")
torch.cuda.empty_cache()
# Reinstanciamos el upsampler con un tamaño de tiling por defecto, por ejemplo, 100
upsampler = RealESRGANer(
scale=netscale,
model_path=model_path,
model=model,
tile=100, # Valor elegido; puedes ajustarlo según el caso
tile_pad=10,
pre_pad=0,
half=True
)
# Vuelve a intentar
enhanced_img, img_mode = upsampler.enhance(img)
else:
raise

────────────────────────────

Integración en el script completo

────────────────────────────

Podrías modificar tu función enhance_images de la siguiente forma (solo se muestran las secciones modificadas):

python

Copiar

import os
import argparse
import cv2
import time
import torch
import numpy as np
from PIL import Image
import unicodedata
from realesrgan.utils import RealESRGANer
from basicsr.archs.rrdbnet_arch import RRDBNet
from realesrgan.archs.srvgg_arch import SRVGGNetCompact

def remove_accents(text):
return unicodedata.normalize('NFKD', text).encode('ASCII', 'ignore').decode('ASCII')

def robust_imread(path):
img = cv2.imread(path, cv2.IMREAD_UNCHANGED)
if img is None:
try:
img_array = np.fromfile(path, np.uint8)
img = cv2.imdecode(img_array, cv2.IMREAD_UNCHANGED)
if img is not None:
print(f"Imagen leída correctamente con imdecode: {path}")
except Exception as e:
print(f"Error al leer la imagen {path} con imdecode: {e}")
return img

def enhance_images(input_folder, output_folder, tile, model_name, netscale):
script_dir = os.path.dirname(os.path.abspath(__file__))
models = list_models()
model_def = models[model_name]
model = model_def["net"](netscale)
model_path = os.path.join(script_dir, model_def["path"])

# Instanciación inicial del upsampler
upsampler = RealESRGANer(
scale=netscale,
model_path=model_path,
model=model,
tile=tile,
tile_pad=10,
pre_pad=0,
half=True
)

os.makedirs(output_folder, exist_ok=True)
image_filenames = sorted([f for f in os.listdir(input_folder) if f.lower().endswith(('.png', '.jpg', '.jpeg', '.bmp'))])
total_images = len(image_filenames)
print(f"Se encontraron {total_images} imágenes en {input_folder}")

start_total = time.time()

for idx, filename in enumerate(image_filenames, start=1):
input_path = os.path.join(input_folder, filename)
# Detectar y renombrar archivo si contiene caracteres fuera de ASCII
if any(ord(char) > 127 for char in filename):
new_filename = remove_accents(filename)
new_path = os.path.join(input_folder, new_filename)
try:
os.rename(input_path, new_path)
print(f"Archivo renombrado de '{filename}' a '{new_filename}'")
filename = new_filename
input_path = new_path
except Exception as e:
print(f"No se pudo renombrar '{filename}': {e}")

print(f"Procesando ({idx}/{total_images}): {input_path}")

start_img = time.time()
img = robust_imread(input_path)
if img is None:
print(f"No se pudo cargar {input_path}")
continue

# Intenta mejorar la imagen; si hay error por memoria, reintenta con tiling
try:
enhanced_img, img_mode = upsampler.enhance(img)
except RuntimeError as e:
if "CUDA out of memory" in str(e):
print("Error de memoria detectado, reintentando con tiling...")
torch.cuda.empty_cache()
upsampler = RealESRGANer(
scale=netscale,
model_path=model_path,
model=model,
tile=100, # Valor de tiling por defecto
tile_pad=10,
pre_pad=0,
half=True
)
enhanced_img, img_mode = upsampler.enhance(img)
else:
raise

# Guardar imagen en función del modo
if img_mode == "L":
pil_img = Image.fromarray(enhanced_img, mode="L")
elif img_mode == "RGBA":
rgb_img = enhanced_img[:, :, :3][:, :, ::-1]
alpha_channel = enhanced_img[:, :, 3]
pil_img = Image.fromarray(np.dstack([rgb_img, alpha_channel]), mode="RGBA")
else:
pil_img = Image.fromarray(enhanced_img[:, :, ::-1], mode="RGB")

output_path = os.path.join(output_folder, filename)
pil_img.save(output_path)

end_img = time.time()
processing_time = end_img - start_img

elapsed_total = time.time() - start_total
remaining_images = total_images - idx
avg_time_per_image = elapsed_total / idx if idx > 0 else 0
estimated_remaining = avg_time_per_image * remaining_images
remaining_str = time.strftime("%H:%M:%S", time.gmtime(estimated_remaining))

# Mostrar información de dispositivo con colores ANSI
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
GREEN = "\033[92m"
CYAN = "\033[96m"
YELLOW = "\033[93m"
RED = "\033[91m"
RESET = "\033[0m"

print(f"{CYAN}🖥️ Dispositivo utilizado: {device}{RESET}")
if device.type == "cuda":
print(f"{GREEN}🚀 GPU detectada: {torch.cuda.get_device_name(0)}{RESET}")
else:
print(f"{YELLOW}⚙️ Ejecutando en CPU{RESET}")
print(f"{RED}Imagen {idx}/{total_images} procesada en {processing_time:.2f} s.")
print(f"{RED}Tiempo total transcurrido: {elapsed_total:.2f} s. Estimación restante: {estimated_remaining:.2f} s ({remaining_str}).")
print(f"{RESET}Imagen guardada: {output_path}\n")

total_time = time.time() - start_total
print(f"Proceso completado. Tiempo total: {total_time:.2f} s.")

────────────────────────────

Notas adicionales

────────────────────────────

El parámetro tile inicial lo dejas según como lo especifique el usuario; en este ejemplo solo se utiliza si ocurre un error de memoria.

La función robust_imread trata de forma automática ambos casos (lectura normal y alternativa).

Si decides renombrar los archivos, recuerda que esto modificará los nombres en tu carpeta de entrada. Si prefieres conservar los originales, podrías copiar el archivo a un nombre temporal y trabajar sobre esa copia.

El valor de tiling (en el ejemplo 100) puede ajustarse según la resolución de las imágenes y la memoria disponible en la GPU.

Con estas modificaciones, el script intentará leer la imagen de forma robusta y, en caso de error de memoria, reintentará automáticamente usando tiling. Esto debería facilitar el procesamiento sin que tengas que intervenir manualmente.

¿Te sirve esta solución o necesitas ajustar algún detalle más?

Dijiste:

Y no me podrias dar el codigo completo por favor?

ChatGPT dijo:

Aquí te dejo el código completo del script con las modificaciones sugeridas para la lectura robusta de imágenes (que soluciona nombres con caracteres especiales) y el fallback automático a tiling en caso de error de memoria:

python

Copiar

import os
import argparse
import cv2
import time
import torch
import numpy as np
from PIL import Image
import unicodedata
from realesrgan.utils import RealESRGANer
from basicsr.archs.rrdbnet_arch import RRDBNet
from realesrgan.archs.srvgg_arch import SRVGGNetCompact

def list_models():
return {
"RealESRGAN_x4plus": {
"net": lambda scale: RRDBNet(num_in_ch=3, num_out_ch=3, num_feat=64, num_block=23, num_grow_ch=32, scale=scale),
"scale": [4],
"path": "RealESRGAN_x4plus.pth"
},
"RealESRNet_x4plus": {
"net": lambda scale: RRDBNet(num_in_ch=3, num_out_ch=3, num_feat=64, num_block=23, num_grow_ch=32, scale=scale),
"scale": [4],
"path": "RealESRNet_x4plus.pth"
},
"RealESRGAN_x4plus_anime_6B": {
"net": lambda scale: RRDBNet(num_in_ch=3, num_out_ch=3, num_feat=64, num_block=6, num_grow_ch=32, scale=scale),
"scale": [4],
"path": "RealESRGAN_x4plus_anime_6B.pth"
},
"RealESRGAN_x2plus": {
"net": lambda scale: RRDBNet(num_in_ch=3, num_out_ch=3, num_feat=64, num_block=23, num_grow_ch=32, scale=scale),
"scale": [2],
"path": "RealESRGAN_x2plus.pth"
},
"realesr-general-x4v3": {
"net": lambda scale: SRVGGNetCompact(num_in_ch=3, num_out_ch=3, num_feat=64, num_conv=32, upscale=scale, act_type='prelu'),
"scale": [4],
"path": "realesr-general-x4v3.pth"
},
"realesr-animevideov3": {
"net": lambda scale: SRVGGNetCompact(num_in_ch=3, num_out_ch=3, num_feat=64, num_conv=16, upscale=scale, act_type='prelu'),
"scale": [4],
"path": "realesr-animevideov3.pth"
},
"RealESRGAN_x2": {
"net": lambda scale: RRDBNet(num_in_ch=3, num_out_ch=3, num_feat=64, num_block=23, num_grow_ch=32, scale=scale),
"scale": [2],
"path": "RealESRGAN_x2.pth"
},
"RealESRGAN_x4": {
"net": lambda scale: RRDBNet(num_in_ch=3, num_out_ch=3, num_feat=64, num_block=23, num_grow_ch=32, scale=scale),
"scale": [4],
"path": "RealESRGAN_x4.pth"
},
}

def remove_accents(text):
"""Normaliza la cadena para eliminar acentos y otros caracteres no-ASCII"""
return unicodedata.normalize('NFKD', text).encode('ASCII', 'ignore').decode('ASCII')

def robust_imread(path):
"""Intenta leer la imagen de forma estándar y, si falla, utiliza imdecode"""
img = cv2.imread(path, cv2.IMREAD_UNCHANGED)
if img is None:
try:
img_array = np.fromfile(path, np.uint8)
img = cv2.imdecode(img_array, cv2.IMREAD_UNCHANGED)
if img is not None:
print(f"Imagen leída correctamente con imdecode: {path}")
except Exception as e:
print(f"Error al leer la imagen {path} con imdecode: {e}")
return img

def enhance_images(input_folder, output_folder, tile, model_name, netscale):
script_dir = os.path.dirname(os.path.abspath(__file__))
models = list_models()
model_def = models[model_name]
model = model_def["net"](netscale)
model_path = os.path.join(script_dir, model_def["path"])

# Instanciación inicial del upsampler (se utiliza el tile indicado, si es que se especifica)
upsampler = RealESRGANer(
scale=netscale,
model_path=model_path,
model=model,
tile=tile,
tile_pad=10,
pre_pad=0,
half=True
)

os.makedirs(output_folder, exist_ok=True)
image_filenames = sorted([f for f in os.listdir(input_folder) if f.lower().endswith(('.png', '.jpg', '.jpeg', '.bmp'))])
total_images = len(image_filenames)
print(f"Se encontraron {total_images} imágenes en {input_folder}")

start_total = time.time()

for idx, filename in enumerate(image_filenames, start=1):
input_path = os.path.join(input_folder, filename)
# Verifica si el nombre del archivo contiene caracteres fuera del ASCII y, de ser así, renombra el archivo
if any(ord(char) > 127 for char in filename):
new_filename = remove_accents(filename)
new_path = os.path.join(input_folder, new_filename)
try:
os.rename(input_path, new_path)
print(f"Archivo renombrado de '{filename}' a '{new_filename}'")
filename = new_filename
input_path = new_path
except Exception as e:
print(f"No se pudo renombrar '{filename}': {e}")

print(f"Procesando ({idx}/{total_images}): {input_path}")

start_img = time.time()
img = robust_imread(input_path)
if img is None:
print(f"No se pudo cargar {input_path}")
continue

# Intenta mejorar la imagen y, si ocurre un error de memoria, reintenta con tiling
try:
enhanced_img, img_mode = upsampler.enhance(img)
except RuntimeError as e:
if "CUDA out of memory" in str(e):
print("Error de memoria detectado, reintentando con tiling...")
torch.cuda.empty_cache()
upsampler = RealESRGANer(
scale=netscale,
model_path=model_path,
model=model,
tile=100, # Valor de tiling por defecto; puedes ajustarlo según sea necesario
tile_pad=10,
pre_pad=0,
half=True
)
enhanced_img, img_mode = upsampler.enhance(img)
else:
raise

# Conversión de la imagen mejorada según su modo
if img_mode == "L":
pil_img = Image.fromarray(enhanced_img, mode="L")
elif img_mode == "RGBA":
rgb_img = enhanced_img[:, :, :3][:, :, ::-1]
alpha_channel = enhanced_img[:, :, 3]
pil_img = Image.fromarray(np.dstack([rgb_img, alpha_channel]), mode="RGBA")
else:
pil_img = Image.fromarray(enhanced_img[:, :, ::-1], mode="RGB")

output_path = os.path.join(output_folder, filename)
pil_img.save(output_path)

end_img = time.time()
processing_time = end_img - start_img

elapsed_total = time.time() - start_total
remaining_images = total_images - idx
avg_time_per_image = elapsed_total / idx if idx > 0 else 0
estimated_remaining = avg_time_per_image * remaining_images
remaining_str = time.strftime("%H:%M:%S", time.gmtime(estimated_remaining))

# Mostrar información del dispositivo con colores ANSI
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
GREEN = "\033[92m"
CYAN = "\033[96m"
YELLOW = "\033[93m"
RED = "\033[91m"
RESET = "\033[0m"

print(f"{CYAN}🖥️ Dispositivo utilizado: {device}{RESET}")
if device.type == "cuda":
print(f"{GREEN}🚀 GPU detectada: {torch.cuda.get_device_name(0)}{RESET}")
else:
print(f"{YELLOW}⚙️ Ejecutando en CPU{RESET}")
print(f"{RED}Imagen {idx}/{total_images} procesada en {processing_time:.2f} s.")
print(f"{RED}Tiempo total transcurrido: {elapsed_total:.2f} s. Estimación restante: {estimated_remaining:.2f} s ({remaining_str}).")
print(f"{RESET}Imagen guardada: {output_path}\n")

total_time = time.time() - start_total
print(f"Proceso completado. Tiempo total: {total_time:.2f} s.")

if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Mejora imágenes con Real-ESRGAN")
parser.add_argument("--input_folder", type=str, required=True, help="Carpeta con imágenes originales")
parser.add_argument("--output_folder", type=str, required=True, help="Carpeta para guardar las imágenes mejoradas")
parser.add_argument("--tile", type=int, default=0, help="Tamaño de tiling (default: 0, sin tiling)")
parser.add_argument("--model_name", type=str, help="Nombre del modelo a usar")
parser.add_argument("--scale", type=int, help="Factor de mejora si el modelo lo permite")
args = parser.parse_args()

# Información inicial del dispositivo
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
GREEN = "\033[92m"
CYAN = "\033[96m"
YELLOW = "\033[93m"
RED = "\033[91m"
RESET = "\033[0m"
print(f"{CYAN}🖥️ Dispositivo utilizado: {device}{RESET}")
if device.type == "cuda":
print(f"{GREEN}🚀 GPU detectada: {torch.cuda.get_device_name(0)}{RESET}")
else:
print(f"{YELLOW}⚙️ Ejecutando en CPU{RESET}")

models = list_models()

if args.model_name and args.model_name in models:
selected_model = args.model_name
else:
print("\nModelos disponibles:")
for idx, key in enumerate(models, start=1):
print(f"{idx}. {key} (Scale {models[key]['scale'][0]}x)")
choice = int(input("\nElige el número de modelo: "))
selected_model = list(models.keys())[choice - 1]

available_scales = models[selected_model]["scale"]
if args.scale and args.scale in available_scales:
selected_scale = args.scale
elif len(available_scales) == 1:
selected_scale = available_scales[0]
else:
print("\nEscalas disponibles para este modelo:")
for idx, scale_option in enumerate(available_scales, start=1):
print(f"{idx}. {scale_option}x")
scale_choice = int(input("\nElige el número de escala: "))
selected_scale = available_scales[scale_choice - 1]

enhance_images(args.input_folder, args.output_folder, args.tile, selected_model, selected_scale)

Explicación rápida de los cambios:

Lectura robusta de imágenes:

Se agregó la función robust_imread que intenta primero con cv2.imread y, si falla, utiliza np.fromfile y cv2.imdecode.

Renombrado automático de archivos:

Si el nombre del archivo contiene caracteres fuera del rango ASCII, se usa la función remove_accents para generar un nombre sin acentos y se renombra el archivo automáticamente.

Fallback a tiling automático:

Se envuelve la llamada a upsampler.enhance(img) en un bloque try/except. Si se detecta un error de "CUDA out of memory", se limpia la caché con torch.cuda.empty_cache(), se reinstancia el upsampler con un tamaño de tiling (por defecto 100) y se vuelve a intentar el procesamiento.

Con este código completo el script realizará los ajustes automáticamente, mejorando la robustez sin que tengas que intervenir manualmente. ¿Te resulta útil esta solución?

Dijiste:

Ok, Muchas Gracias Chat, funciona muy bien, una pregunta no mas, si existe el nombre cuando se cambia la ñ por la n, que pasa?, chequea si existe y pone un nombre añadiendle una n mas o algo, hasta que no haya un nombre de imagen igual?

ChatGPT dijo:

Razonado durante 12 segundos

Actualmente, el código renombra el archivo usando la función remove_accents, de forma que si se detecta un caracter fuera del rango ASCII se reemplaza (por ejemplo, "Niño" se convierte en "Nino") y luego se usa ese nuevo nombre. Sin embargo, tal como está, el script no verifica si ya existe un archivo con ese nombre en la carpeta, por lo que en ese caso os.rename lanzaría una excepción o podría sobrescribir el archivo existente (dependiendo del sistema operativo y de cómo maneje esa función).

Si deseas manejar automáticamente esa situación (por ejemplo, agregar un sufijo o un contador hasta obtener un nombre único), tendrías que agregar una lógica adicional. Por ejemplo, podrías hacer algo como lo siguiente:

python

Copiar

def get_unique_filename(folder, filename):
base, ext = os.path.splitext(filename)
candidate = filename
counter = 1
# Mientras exista un archivo con ese nombre, agrega un sufijo
while os.path.exists(os.path.join(folder, candidate)):
candidate = f"{base}_{counter}{ext}"
counter += 1
return candidate

Y luego, en la parte de renombrado, usarías esta función:

python

Copiar

if any(ord(char) > 127 for char in filename):
new_filename = remove_accents(filename)
new_filename = get_unique_filename(input_folder, new_filename)
new_path = os.path.join(input_folder, new_filename)
try:
os.rename(input_path, new_path)
print(f"Archivo renombrado de '{filename}' a '{new_filename}'")
filename = new_filename
input_path = new_path
except Exception as e:
print(f"No se pudo renombrar '{filename}': {e}")

De esta forma, si al remover acentos el nombre ya existe, se le añadirá un sufijo (por ejemplo, "_1", "_2", etc.) hasta generar un nombre único.

En resumen, en la versión actual no se realiza ninguna verificación de colisiones de nombres. Si deseas ese comportamiento, debes implementar una función similar a get_unique_filename para que se asegure de que cada archivo renombrado tenga un nombre distinto.

Dijiste:

Y no me podrias dar el codigo completo por favor, perdona...

ChatGPT dijo:

Razonado durante 9 segundos

Aquí tienes el código completo modificado para que, al detectar caracteres no ASCII en el nombre del archivo, se renombre automáticamente y se verifique que el nombre resultante sea único (agregando un sufijo si es necesario):

python

Copiar

import os
import argparse
import cv2
import time
import torch
import numpy as np
from PIL import Image
import unicodedata
from realesrgan.utils import RealESRGANer
from basicsr.archs.rrdbnet_arch import RRDBNet
from realesrgan.archs.srvgg_arch import SRVGGNetCompact

def list_models():
return {
"RealESRGAN_x4plus": {
"net": lambda scale: RRDBNet(num_in_ch=3, num_out_ch=3, num_feat=64, num_block=23, num_grow_ch=32, scale=scale),
"scale": [4],
"path": "RealESRGAN_x4plus.pth"
},
"RealESRNet_x4plus": {
"net": lambda scale: RRDBNet(num_in_ch=3, num_out_ch=3, num_feat=64, num_block=23, num_grow_ch=32, scale=scale),
"scale": [4],
"path": "RealESRNet_x4plus.pth"
},
"RealESRGAN_x4plus_anime_6B": {
"net": lambda scale: RRDBNet(num_in_ch=3, num_out_ch=3, num_feat=64, num_block=6, num_grow_ch=32, scale=scale),
"scale": [4],
"path": "RealESRGAN_x4plus_anime_6B.pth"
},
"RealESRGAN_x2plus": {
"net": lambda scale: RRDBNet(num_in_ch=3, num_out_ch=3, num_feat=64, num_block=23, num_grow_ch=32, scale=scale),
"scale": [2],
"path": "RealESRGAN_x2plus.pth"
},
"realesr-general-x4v3": {
"net": lambda scale: SRVGGNetCompact(num_in_ch=3, num_out_ch=3, num_feat=64, num_conv=32, upscale=scale, act_type='prelu'),
"scale": [4],
"path": "realesr-general-x4v3.pth"
},
"realesr-animevideov3": {
"net": lambda scale: SRVGGNetCompact(num_in_ch=3, num_out_ch=3, num_feat=64, num_conv=16, upscale=scale, act_type='prelu'),
"scale": [4],
"path": "realesr-animevideov3.pth"
},
"RealESRGAN_x2": {
"net": lambda scale: RRDBNet(num_in_ch=3, num_out_ch=3, num_feat=64, num_block=23, num_grow_ch=32, scale=scale),
"scale": [2],
"path": "RealESRGAN_x2.pth"
},
"RealESRGAN_x4": {
"net": lambda scale: RRDBNet(num_in_ch=3, num_out_ch=3, num_feat=64, num_block=23, num_grow_ch=32, scale=scale),
"scale": [4],
"path": "RealESRGAN_x4.pth"
},
}

def remove_accents(text):
"""Normaliza la cadena eliminando acentos y otros caracteres no-ASCII."""
return unicodedata.normalize('NFKD', text).encode('ASCII', 'ignore').decode('ASCII')

def get_unique_filename(folder, filename):
"""
Genera un nombre de archivo único en la carpeta indicada.
Si el archivo 'filename' ya existe, añade un sufijo (_1, _2, …) hasta que no exista.
"""
base, ext = os.path.splitext(filename)
candidate = filename
counter = 1
while os.path.exists(os.path.join(folder, candidate)):
candidate = f"{base}_{counter}{ext}"
counter += 1
return candidate

def robust_imread(path):
"""Intenta leer la imagen de forma estándar; de no poder, utiliza imdecode."""
img = cv2.imread(path, cv2.IMREAD_UNCHANGED)
if img is None:
try:
img_array = np.fromfile(path, np.uint8)
img = cv2.imdecode(img_array, cv2.IMREAD_UNCHANGED)
if img is not None:
print(f"Imagen leída correctamente con imdecode: {path}")
except Exception as e:
print(f"Error al leer la imagen {path} con imdecode: {e}")
return img

def enhance_images(input_folder, output_folder, tile, model_name, netscale):
script_dir = os.path.dirname(os.path.abspath(__file__))
models = list_models()
model_def = models[model_name]
model = model_def["net"](netscale)
model_path = os.path.join(script_dir, model_def["path"])

# Instanciación inicial del upsampler (se utiliza el valor de tile que se especifique)
upsampler = RealESRGANer(
scale=netscale,
model_path=model_path,
model=model,
tile=tile,
tile_pad=10,
pre_pad=0,
half=True
)

os.makedirs(output_folder, exist_ok=True)
image_filenames = sorted([f for f in os.listdir(input_folder) if f.lower().endswith(('.png', '.jpg', '.jpeg', '.bmp'))])
total_images = len(image_filenames)
print(f"Se encontraron {total_images} imágenes en {input_folder}")

start_total = time.time()

for idx, filename in enumerate(image_filenames, start=1):
input_path = os.path.join(input_folder, filename)
# Verifica si el nombre del archivo contiene caracteres fuera del rango ASCII,
# y en ese caso genera un nombre nuevo y único
if any(ord(char) > 127 for char in filename):
new_filename = remove_accents(filename)
new_filename = get_unique_filename(input_folder, new_filename)
new_path = os.path.join(input_folder, new_filename)
try:
os.rename(input_path, new_path)
print(f"Archivo renombrado de '{filename}' a '{new_filename}'")
filename = new_filename
input_path = new_path
except Exception as e:
print(f"No se pudo renombrar '{filename}': {e}")

print(f"Procesando ({idx}/{total_images}): {input_path}")
start_img = time.time()
img = robust_imread(input_path)
if img is None:
print(f"No se pudo cargar {input_path}")
continue

# Intenta mejorar la imagen y, si ocurre un error de memoria, reintenta con tiling
try:
enhanced_img, img_mode = upsampler.enhance(img)
except RuntimeError as e:
if "CUDA out of memory" in str(e):
print("Error de memoria detectado, reintentando con tiling...")
torch.cuda.empty_cache()
upsampler = RealESRGANer(
scale=netscale,
model_path=model_path,
model=model,
tile=100, # Valor de tiling por defecto, ajustable según tus necesidades
tile_pad=10,
pre_pad=0,
half=True
)
enhanced_img, img_mode = upsampler.enhance(img)
else:
raise

# Conversión de la imagen mejorada según su modo
if img_mode == "L":
pil_img = Image.fromarray(enhanced_img, mode="L")
elif img_mode == "RGBA":
rgb_img = enhanced_img[:, :, :3][:, :, ::-1]
alpha_channel = enhanced_img[:, :, 3]
pil_img = Image.fromarray(np.dstack([rgb_img, alpha_channel]), mode="RGBA")
else:
pil_img = Image.fromarray(enhanced_img[:, :, ::-1], mode="RGB")

output_path = os.path.join(output_folder, filename)
pil_img.save(output_path)

end_img = time.time()
processing_time = end_img - start_img
elapsed_total = time.time() - start_total
remaining_images = total_images - idx
avg_time_per_image = elapsed_total / idx if idx > 0 else 0
estimated_remaining = avg_time_per_image * remaining_images
remaining_str = time.strftime("%H:%M:%S", time.gmtime(estimated_remaining))

# Mostrar información del dispositivo con colores ANSI
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
GREEN = "\033[92m"
CYAN = "\033[96m"
YELLOW = "\033[93m"
RED = "\033[91m"
RESET = "\033[0m"

print(f"{CYAN}🖥️ Dispositivo utilizado: {device}{RESET}")
if device.type == "cuda":
print(f"{GREEN}🚀 GPU detectada: {torch.cuda.get_device_name(0)}{RESET}")
else:
print(f"{YELLOW}⚙️ Ejecutando en CPU{RESET}")
print(f"{RED}Imagen {idx}/{total_images} procesada en {processing_time:.2f} s.")
print(f"{RED}Tiempo total transcurrido: {elapsed_total:.2f} s. Estimación restante: {estimated_remaining:.2f} s ({remaining_str}).")
print(f"{RESET}Imagen guardada: {output_path}\n")

total_time = time.time() - start_total
print(f"Proceso completado. Tiempo total: {total_time:.2f} s.")

if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Mejora imágenes con Real-ESRGAN")
parser.add_argument("--input_folder", type=str, required=True, help="Carpeta con imágenes originales")
parser.add_argument("--output_folder", type=str, required=True, help="Carpeta para guardar las imágenes mejoradas")
parser.add_argument("--tile", type=int, default=0, help="Tamaño de tiling (default: 0, sin tiling)")
parser.add_argument("--model_name", type=str, help="Nombre del modelo a usar")
parser.add_argument("--scale", type=int, help="Factor de mejora si el modelo lo permite")
args = parser.parse_args()

# Información inicial del dispositivo
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
GREEN = "\033[92m"
CYAN = "\033[96m"
YELLOW = "\033[93m"
RED = "\033[91m"
RESET = "\033[0m"
print(f"{CYAN}🖥️ Dispositivo utilizado: {device}{RESET}")
if device.type == "cuda":
print(f"{GREEN}🚀 GPU detectada: {torch.cuda.get_device_name(0)}{RESET}")
else:
print(f"{YELLOW}⚙️ Ejecutando en CPU{RESET}")

models = list_models()

if args.model_name and args.model_name in models:
selected_model = args.model_name
else:
print("\nModelos disponibles:")
for idx, key in enumerate(models, start=1):
print(f"{idx}. {key} (Scale {models[key]['scale'][0]}x)")
choice = int(input("\nElige el número de modelo: "))
selected_model = list(models.keys())[choice - 1]

available_scales = models[selected_model]["scale"]
if args.scale and args.scale in available_scales:
selected_scale = args.scale
elif len(available_scales) == 1:
selected_scale = available_scales[0]
else:
print("\nEscalas disponibles para este modelo:")
for idx, scale_option in enumerate(available_scales, start=1):
print(f"{idx}. {scale_option}x")
scale_choice = int(input("\nElige el número de escala: "))
selected_scale = available_scales[scale_choice - 1]

enhance_images(args.input_folder, args.output_folder, args.tile, selected_model, selected_scale)

Resumen de las modificaciones:

Renombrado automático y único:

Se agrega la función get_unique_filename que, junto a remove_accents, garantiza que los nombres se renombren sin caracteres no ASCII y que sean únicos en la carpeta de entrada.

Lectura robusta:

La función robust_imread intenta leer la imagen con cv2.imread y, si falla, utiliza np.fromfile y cv2.imdecode.

Fallback para tiling:

En caso de error de memoria (CUDA out of memory), el script limpia la caché y vuelve a instanciar el upsampler con un valor de tiling (en este ejemplo, 100).

Con este código se maneja de forma automática la verificación y renombrado en caso de colisiones, y se abordan ambos casos (error de lectura por nombre y error de memoria). ¿Te sirve esta solución?

Dijiste:

Probando

ChatGPT dijo:

Perfecto, avísame si encuentras algún problema o si necesitas alguna otra modificación. Estoy aquí para ayudarte.

Dijiste:

Perfecto Chat, excelente!!!

Podriamos hacer lo mejor de todo?

Que al final del proceso dejara los nombres como al inicio, tanto en el directorio source, como en el de destino?

ChatGPT dijo:

Razonado durante 35 segundos

Aquí te dejo el código completo modificado para que, al procesar una imagen, se utilice un archivo temporal con nombre normalizado (sin modificar el archivo original) si el nombre contiene caracteres no-ASCII. De esta forma, tanto la carpeta de origen como la de destino conservarán los nombres originales al finalizar el proceso:

python

Copiar

import os
import argparse
import cv2
import time
import torch
import numpy as np
from PIL import Image
import unicodedata
import shutil
from realesrgan.utils import RealESRGANer
from basicsr.archs.rrdbnet_arch import RRDBNet
from realesrgan.archs.srvgg_arch import SRVGGNetCompact

def list_models():
return {
"RealESRGAN_x4plus": {
"net": lambda scale: RRDBNet(num_in_ch=3, num_out_ch=3, num_feat=64, num_block=23, num_grow_ch=32, scale=scale),
"scale": [4],
"path": "RealESRGAN_x4plus.pth"
},
"RealESRNet_x4plus": {
"net": lambda scale: RRDBNet(num_in_ch=3, num_out_ch=3, num_feat=64, num_block=23, num_grow_ch=32, scale=scale),
"scale": [4],
"path": "RealESRNet_x4plus.pth"
},
"RealESRGAN_x4plus_anime_6B": {
"net": lambda scale: RRDBNet(num_in_ch=3, num_out_ch=3, num_feat=64, num_block=6, num_grow_ch=32, scale=scale),
"scale": [4],
"path": "RealESRGAN_x4plus_anime_6B.pth"
},
"RealESRGAN_x2plus": {
"net": lambda scale: RRDBNet(num_in_ch=3, num_out_ch=3, num_feat=64, num_block=23, num_grow_ch=32, scale=scale),
"scale": [2],
"path": "RealESRGAN_x2plus.pth"
},
"realesr-general-x4v3": {
"net": lambda scale: SRVGGNetCompact(num_in_ch=3, num_out_ch=3, num_feat=64, num_conv=32, upscale=scale, act_type='prelu'),
"scale": [4],
"path": "realesr-general-x4v3.pth"
},
"realesr-animevideov3": {
"net": lambda scale: SRVGGNetCompact(num_in_ch=3, num_out_ch=3, num_feat=64, num_conv=16, upscale=scale, act_type='prelu'),
"scale": [4],
"path": "realesr-animevideov3.pth"
},
"RealESRGAN_x2": {
"net": lambda scale: RRDBNet(num_in_ch=3, num_out_ch=3, num_feat=64, num_block=23, num_grow_ch=32, scale=scale),
"scale": [2],
"path": "RealESRGAN_x2.pth"
},
"RealESRGAN_x4": {
"net": lambda scale: RRDBNet(num_in_ch=3, num_out_ch=3, num_feat=64, num_block=23, num_grow_ch=32, scale=scale),
"scale": [4],
"path": "RealESRGAN_x4.pth"
},
}

def remove_accents(text):
"""Normaliza la cadena eliminando acentos y otros caracteres no-ASCII."""
return unicodedata.normalize('NFKD', text).encode('ASCII', 'ignore').decode('ASCII')

def get_unique_filename(folder, filename):
"""
Genera un nombre de archivo único en la carpeta indicada.
Si el archivo 'filename' ya existe, añade un sufijo (_1, _2, …) hasta que no exista.
"""
base, ext = os.path.splitext(filename)
candidate = filename
counter = 1
while os.path.exists(os.path.join(folder, candidate)):
candidate = f"{base}_{counter}{ext}"
counter += 1
return candidate

def robust_imread(path):
"""Intenta leer la imagen de forma estándar; de no poder, utiliza imdecode."""
img = cv2.imread(path, cv2.IMREAD_UNCHANGED)
if img is None:
try:
img_array = np.fromfile(path, np.uint8)
img = cv2.imdecode(img_array, cv2.IMREAD_UNCHANGED)
if img is not None:
print(f"Imagen leída correctamente con imdecode: {path}")
except Exception as e:
print(f"Error al leer la imagen {path} con imdecode: {e}")
return img

def enhance_images(input_folder, output_folder, tile, model_name, netscale):
script_dir = os.path.dirname(os.path.abspath(__file__))
models = list_models()
model_def = models[model_name]
model = model_def["net"](netscale)
model_path = os.path.join(script_dir, model_def["path"])

# Instanciación inicial del upsampler (se utiliza el valor de tile que se especifique)
upsampler = RealESRGANer(
scale=netscale,
model_path=model_path,
model=model,
tile=tile,
tile_pad=10,
pre_pad=0,
half=True
)

os.makedirs(output_folder, exist_ok=True)
image_filenames = sorted([f for f in os.listdir(input_folder) if f.lower().endswith(('.png', '.jpg', '.jpeg', '.bmp'))])
total_images = len(image_filenames)
print(f"Se encontraron {total_images} imágenes en {input_folder}")

start_total = time.time()

# Definimos una carpeta temporal para copiar archivos con nombres problemáticos
temp_dir = os.path.join(input_folder, "_tmp_renamed")
os.makedirs(temp_dir, exist_ok=True)

for idx, filename in enumerate(image_filenames, start=1):
original_path = os.path.join(input_folder, filename)
read_path = original_path # Por defecto se usa el archivo original
temp_file_created = False

# Si el nombre contiene caracteres no-ASCII, creamos una copia temporal con nombre normalizado
if any(ord(char) > 127 for char in filename):
new_filename = remove_accents(filename)
new_filename = get_unique_filename(temp_dir, new_filename)
read_path = os.path.join(temp_dir, new_filename)
try:
shutil.copy2(original_path, read_path)
temp_file_created = True
print(f"Se creó copia temporal para '{filename}' como '{new_filename}'")
except Exception as e:
print(f"No se pudo copiar '{filename}' a la carpeta temporal: {e}")
read_path = original_path

print(f"Procesando ({idx}/{total_images}): {read_path}")
start_img = time.time()
img = robust_imread(read_path)
if img is None:
print(f"No se pudo cargar {read_path}")
# Si se creó un archivo temporal, lo borramos antes de continuar
if temp_file_created:
os.remove(read_path)
continue

# Intenta mejorar la imagen y, si ocurre un error de memoria, reintenta con tiling
try:
enhanced_img, img_mode = upsampler.enhance(img)
except RuntimeError as e:
if "CUDA out of memory" in str(e):
print("Error de memoria detectado, reintentando con tiling...")
torch.cuda.empty_cache()
upsampler = RealESRGANer(
scale=netscale,
model_path=model_path,
model=model,
tile=100, # Valor de tiling por defecto, ajustable según tus necesidades
tile_pad=10,
pre_pad=0,
half=True
)
enhanced_img, img_mode = upsampler.enhance(img)
else:
raise

# Conversión de la imagen mejorada según su modo
if img_mode == "L":
pil_img = Image.fromarray(enhanced_img, mode="L")
elif img_mode == "RGBA":
rgb_img = enhanced_img[:, :, :3][:, :, ::-1]
alpha_channel = enhanced_img[:, :, 3]
pil_img = Image.fromarray(np.dstack([rgb_img, alpha_channel]), mode="RGBA")
else:
pil_img = Image.fromarray(enhanced_img[:, :, ::-1], mode="RGB")

# Guardamos la imagen resultante con el nombre original en la carpeta de destino
output_path = os.path.join(output_folder, filename)
pil_img.save(output_path)

end_img = time.time()
processing_time = end_img - start_img
elapsed_total = time.time() - start_total
remaining_images = total_images - idx
avg_time_per_image = elapsed_total / idx if idx > 0 else 0
estimated_remaining = avg_time_per_image * remaining_images
remaining_str = time.strftime("%H:%M:%S", time.gmtime(estimated_remaining))

# Mostrar información del dispositivo con colores ANSI
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
GREEN = "\033[92m"
CYAN = "\033[96m"
YELLOW = "\033[93m"
RED = "\033[91m"
RESET = "\033[0m"

print(f"{CYAN}🖥️ Dispositivo utilizado: {device}{RESET}")
if device.type == "cuda":
print(f"{GREEN}🚀 GPU detectada: {torch.cuda.get_device_name(0)}{RESET}")
else:
print(f"{YELLOW}⚙️ Ejecutando en CPU{RESET}")
print(f"{RED}Imagen {idx}/{total_images} procesada en {processing_time:.2f} s.")
print(f"{RED}Tiempo total transcurrido: {elapsed_total:.2f} s. Estimación restante: {estimated_remaining:.2f} s ({remaining_str}).")
print(f"{RESET}Imagen guardada: {output_path}\n")

# Elimina el archivo temporal si se creó
if temp_file_created and os.path.exists(read_path):
os.remove(read_path)

# Si la carpeta temporal quedó vacía, opcionalmente se puede borrar
if os.path.isdir(temp_dir) and not os.listdir(temp_dir):
os.rmdir(temp_dir)

total_time = time.time() - start_total
print(f"Proceso completado. Tiempo total: {total_time:.2f} s.")

if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Mejora imágenes con Real-ESRGAN")
parser.add_argument("--input_folder", type=str, required=True, help="Carpeta con imágenes originales")
parser.add_argument("--output_folder", type=str, required=True, help="Carpeta para guardar las imágenes mejoradas")
parser.add_argument("--tile", type=int, default=0, help="Tamaño de tiling (default: 0, sin tiling)")
parser.add_argument("--model_name", type=str, help="Nombre del modelo a usar")
parser.add_argument("--scale", type=int, help="Factor de mejora si el modelo lo permite")
args = parser.parse_args()

# Información inicial del dispositivo
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
GREEN = "\033[92m"
CYAN = "\033[96m"
YELLOW = "\033[93m"
RED = "\033[91m"
RESET = "\033[0m"
print(f"{CYAN}🖥️ Dispositivo utilizado: {device}{RESET}")
if device.type == "cuda":
print(f"{GREEN}🚀 GPU detectada: {torch.cuda.get_device_name(0)}{RESET}")
else:
print(f"{YELLOW}⚙️ Ejecutando en CPU{RESET}")

models = list_models()
if args.model_name and args.model_name in models:
selected_model = args.model_name
else:
print("\nModelos disponibles:")
for idx, key in enumerate(models, start=1):
print(f"{idx}. {key} (Scale {models[key]['scale'][0]}x)")
choice = int(input("\nElige el número de modelo: "))
selected_model = list(models.keys())[choice - 1]

available_scales = models[selected_model]["scale"]
if args.scale and args.scale in available_scales:
selected_scale = args.scale
elif len(available_scales) == 1:
selected_scale = available_scales[0]
else:
print("\nEscalas disponibles para este modelo:")
for idx, scale_option in enumerate(available_scales, start=1):
print(f"{idx}. {scale_option}x")
scale_choice = int(input("\nElige el número de escala: "))
selected_scale = available_scales[scale_choice - 1]

enhance_images(args.input_folder, args.output_folder, args.tile, selected_model, selected_scale)

¿Qué hace este código?

Lectura robusta sin modificar el original:

Si el nombre del archivo contiene caracteres problemáticos, se crea una copia temporal en una carpeta interna (_tmp_renamed). Esta copia se procesa y luego se elimina, de modo que el archivo original en la carpeta fuente no se modifica.

Guardar resultado con nombre original:

El archivo procesado se guarda en la carpeta de destino usando el nombre original, conservándolo tal como estaba antes, incluso si contenía acentos o "ñ".

Fallback automático a tiling:

Si ocurre un error de memoria, se limpia la caché y se reintenta el proceso con un valor de tiling predefinido.

Al finalizar, tanto la carpeta de origen como la de destino mantienen los nombres originales.

¿Te sirve esta solución o deseas algún otro ajuste?

Dijiste:

Un ajuste ultimo seria en el dorectorio de destino, aparte de dejar el nombre original, dejara una adicion al nombre que dijera algo asi como:
imagenoriginal:

ChatGPT dijo:

Razonado durante 18 segundos

o3-mini-high

Dijiste:

Perdona, le di a enter antes de terminar,repito:

Un ajuste ultimo seria en el dorectorio de destino, aparte de dejar el nombre original, dejara una adicion al nombre que dijera algo asi como:
imagenoriginal:
NiñobyPhotopea.png

La renombra, y al final al deja con su nombre igual, tanto en el dir de orgen como en el de destno, pero en el de destino añade el modelo con el que se ha creado:

Hay 8 modelos:
1. RealESRGAN_x4plus (Scale 4x)
2. RealESRNet_x4plus (Scale 4x)
3. RealESRGAN_x4plus_anime_6B (Scale 4x)
4. RealESRGAN_x2plus (Scale 2x)
5. realesr-general-x4v3 (Scale 4x)
6. realesr-animevideov3 (Scale 4x)
7. RealESRGAN_x2 (Scale 2x)
8. RealESRGAN_x4 (Scale 4x)

Si se ha creado con el modelo 4 que en le directorio de destino quedara:
NiñobyPhotopea_RealESRGAN_x2plus.png

Como lo ves, eso ya seria rizar el rizo!!!

ChatGPT dijo:

Razonado durante 14 segundos

A continuación te muestro el código completo modificado para que:

Si el nombre del archivo de origen contiene caracteres problemáticos, se procese usando una copia temporal (sin modificar el original)

Se guarde en el directorio de destino con el nombre original MÁS un sufijo que indique el modelo utilizado (por ejemplo, “NiñobyPhotopea.png” pasará a ser “NiñobyPhotopea_RealESRGAN_x2plus.png” si se utiliza ese modelo)

Se respete que en el directorio de origen queden los nombres originales

Aquí tienes el código:

python

Copiar

import os
import argparse
import cv2
import time
import torch
import numpy as np
from PIL import Image
import unicodedata
import shutil
from realesrgan.utils import RealESRGANer
from basicsr.archs.rrdbnet_arch import RRDBNet
from realesrgan.archs.srvgg_arch import SRVGGNetCompact

def list_models():
return {
"RealESRGAN_x4plus": {
"net": lambda scale: RRDBNet(num_in_ch=3, num_out_ch=3, num_feat=64, num_block=23, num_grow_ch=32, scale=scale),
"scale": [4],
"path": "RealESRGAN_x4plus.pth"
},
"RealESRNet_x4plus": {
"net": lambda scale: RRDBNet(num_in_ch=3, num_out_ch=3, num_feat=64, num_block=23, num_grow_ch=32, scale=scale),
"scale": [4],
"path": "RealESRNet_x4plus.pth"
},
"RealESRGAN_x4plus_anime_6B": {
"net": lambda scale: RRDBNet(num_in_ch=3, num_out_ch=3, num_feat=64, num_block=6, num_grow_ch=32, scale=scale),
"scale": [4],
"path": "RealESRGAN_x4plus_anime_6B.pth"
},
"RealESRGAN_x2plus": {
"net": lambda scale: RRDBNet(num_in_ch=3, num_out_ch=3, num_feat=64, num_block=23, num_grow_ch=32, scale=scale),
"scale": [2],
"path": "RealESRGAN_x2plus.pth"
},
"realesr-general-x4v3": {
"net": lambda scale: SRVGGNetCompact(num_in_ch=3, num_out_ch=3, num_feat=64, num_conv=32, upscale=scale, act_type='prelu'),
"scale": [4],
"path": "realesr-general-x4v3.pth"
},
"realesr-animevideov3": {
"net": lambda scale: SRVGGNetCompact(num_in_ch=3, num_out_ch=3, num_feat=64, num_conv=16, upscale=scale, act_type='prelu'),
"scale": [4],
"path": "realesr-animevideov3.pth"
},
"RealESRGAN_x2": {
"net": lambda scale: RRDBNet(num_in_ch=3, num_out_ch=3, num_feat=64, num_block=23, num_grow_ch=32, scale=scale),
"scale": [2],
"path": "RealESRGAN_x2.pth"
},
"RealESRGAN_x4": {
"net": lambda scale: RRDBNet(num_in_ch=3, num_out_ch=3, num_feat=64, num_block=23, num_grow_ch=32, scale=scale),
"scale": [4],
"path": "RealESRGAN_x4.pth"
},
}

def remove_accents(text):
"""Normaliza la cadena eliminando acentos y otros caracteres no-ASCII."""
return unicodedata.normalize('NFKD', text).encode('ASCII', 'ignore').decode('ASCII')

def get_unique_filename(folder, filename):
"""
Genera un nombre de archivo único en la carpeta indicada.
Si el archivo 'filename' ya existe, añade un sufijo (_1, _2, …) hasta que no exista.
"""
base, ext = os.path.splitext(filename)
candidate = filename
counter = 1
while os.path.exists(os.path.join(folder, candidate)):
candidate = f"{base}_{counter}{ext}"
counter += 1
return candidate

def robust_imread(path):
"""Intenta leer la imagen de forma estándar; de no poder, utiliza imdecode."""
img = cv2.imread(path, cv2.IMREAD_UNCHANGED)
if img is None:
try:
img_array = np.fromfile(path, np.uint8)
img = cv2.imdecode(img_array, cv2.IMREAD_UNCHANGED)
if img is not None:
print(f"Imagen leída correctamente con imdecode: {path}")
except Exception as e:
print(f"Error al leer la imagen {path} con imdecode: {e}")
return img

def enhance_images(input_folder, output_folder, tile, model_name, netscale):
script_dir = os.path.dirname(os.path.abspath(__file__))
models = list_models()
model_def = models[model_name]
model = model_def["net"](netscale)
model_path = os.path.join(script_dir, model_def["path"])

# Instanciación inicial del upsampler (se utiliza el valor de tile especificado)
upsampler = RealESRGANer(
scale=netscale,
model_path=model_path,
model=model,
tile=tile,
tile_pad=10,
pre_pad=0,
half=True
)

os.makedirs(output_folder, exist_ok=True)
image_filenames = sorted([f for f in os.listdir(input_folder) if f.lower().endswith(('.png', '.jpg', '.jpeg', '.bmp'))])
total_images = len(image_filenames)
print(f"Se encontraron {total_images} imágenes en {input_folder}")

start_total = time.time()

# Carpeta temporal para copiar archivos con nombres problemáticos
temp_dir = os.path.join(input_folder, "_tmp_renamed")
os.makedirs(temp_dir, exist_ok=True)

for idx, filename in enumerate(image_filenames, start=1):
original_path = os.path.join(input_folder, filename)
read_path = original_path # Por defecto se utiliza el archivo original
temp_file_created = False

# Si el nombre contiene caracteres no-ASCII, se crea una copia temporal con nombre normalizado
if any(ord(char) > 127 for char in filename):
new_filename = remove_accents(filename)
new_filename = get_unique_filename(temp_dir, new_filename)
read_path = os.path.join(temp_dir, new_filename)
try:
shutil.copy2(original_path, read_path)
temp_file_created = True
print(f"Se creó copia temporal para '{filename}' como '{new_filename}'")
except Exception as e:
print(f"No se pudo copiar '{filename}' a la carpeta temporal: {e}")
read_path = original_path

print(f"Procesando ({idx}/{total_images}): {read_path}")
start_img = time.time()
img = robust_imread(read_path)
if img is None:
print(f"No se pudo cargar {read_path}")
if temp_file_created:
os.remove(read_path)
continue

# Intenta mejorar la imagen y, si ocurre un error de memoria, reintenta con tiling
try:
enhanced_img, img_mode = upsampler.enhance(img)
except RuntimeError as e:
if "CUDA out of memory" in str(e):
print("Error de memoria detectado, reintentando con tiling...")
torch.cuda.empty_cache()
upsampler = RealESRGANer(
scale=netscale,
model_path=model_path,
model=model,
tile=100, # Valor de tiling por defecto, ajustable
tile_pad=10,
pre_pad=0,
half=True
)
enhanced_img, img_mode = upsampler.enhance(img)
else:
raise

# Conversión de la imagen mejorada según su modo
if img_mode == "L":
pil_img = Image.fromarray(enhanced_img, mode="L")
elif img_mode == "RGBA":
rgb_img = enhanced_img[:, :, :3][:, :, ::-1]
alpha_channel = enhanced_img[:, :, 3]
pil_img = Image.fromarray(np.dstack([rgb_img, alpha_channel]), mode="RGBA")
else:
pil_img = Image.fromarray(enhanced_img[:, :, ::-1], mode="RGB")

# Se genera el nombre para el archivo de destino usando el nombre original
# más un sufijo con el modelo utilizado.
base, ext = os.path.splitext(filename)
dest_filename = f"{base}_{model_name}{ext}"
output_path = os.path.join(output_folder, dest_filename)
pil_img.save(output_path)

end_img = time.time()
processing_time = end_img - start_img
elapsed_total = time.time() - start_total
remaining_images = total_images - idx
avg_time_per_image = elapsed_total / idx if idx > 0 else 0
estimated_remaining = avg_time_per_image * remaining_images
remaining_str = time.strftime("%H:%M:%S", time.gmtime(estimated_remaining))

# Mostrar información del dispositivo con colores ANSI
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
GREEN = "\033[92m"
CYAN = "\033[96m"
YELLOW = "\033[93m"
RED = "\033[91m"
RESET = "\033[0m"

print(f"{CYAN}🖥️ Dispositivo utilizado: {device}{RESET}")
if device.type == "cuda":
print(f"{GREEN}🚀 GPU detectada: {torch.cuda.get_device_name(0)}{RESET}")
else:
print(f"{YELLOW}⚙️ Ejecutando en CPU{RESET}")
print(f"{RED}Imagen {idx}/{total_images} procesada en {processing_time:.2f} s.")
print(f"{RED}Tiempo total transcurrido: {elapsed_total:.2f} s. Estimación restante: {estimated_remaining:.2f} s ({remaining_str}).")
print(f"{RESET}Imagen guardada: {output_path}\n")

# Elimina el archivo temporal si se creó
if temp_file_created and os.path.exists(read_path):
os.remove(read_path)

# Si la carpeta temporal quedó vacía, la borramos
if os.path.isdir(temp_dir) and not os.listdir(temp_dir):
os.rmdir(temp_dir)

total_time = time.time() - start_total
print(f"Proceso completado. Tiempo total: {total_time:.2f} s.")

if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Mejora imágenes con Real-ESRGAN")
parser.add_argument("--input_folder", type=str, required=True, help="Carpeta con imágenes originales")
parser.add_argument("--output_folder", type=str, required=True, help="Carpeta para guardar las imágenes mejoradas")
parser.add_argument("--tile", type=int, default=0, help="Tamaño de tiling (default: 0, sin tiling)")
parser.add_argument("--model_name", type=str, help="Nombre del modelo a usar")
parser.add_argument("--scale", type=int, help="Factor de mejora si el modelo lo permite")
args = parser.parse_args()

# Información inicial del dispositivo
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
GREEN = "\033[92m"
CYAN = "\033[96m"
YELLOW = "\033[93m"
RED = "\033[91m"
RESET = "\033[0m"
print(f"{CYAN}🖥️ Dispositivo utilizado: {device}{RESET}")
if device.type == "cuda":
print(f"{GREEN}🚀 GPU detectada: {torch.cuda.get_device_name(0)}{RESET}")
else:
print(f"{YELLOW}⚙️ Ejecutando en CPU{RESET}")

models = list_models()
if args.model_name and args.model_name in models:
selected_model = args.model_name
else:
print("\nModelos disponibles:")
for idx, key in enumerate(models, start=1):
print(f"{idx}. {key} (Scale {models[key]['scale'][0]}x)")
choice = int(input("\nElige el número de modelo: "))
selected_model = list(models.keys())[choice - 1]

available_scales = models[selected_model]["scale"]
if args.scale and args.scale in available_scales:
selected_scale = args.scale
elif len(available_scales) == 1:
selected_scale = available_scales[0]
else:
print("\nEscalas disponibles para este modelo:")
for idx, scale_option in enumerate(available_scales, start=1):
print(f"{idx}. {scale_option}x")
scale_choice = int(input("\nElige el número de escala: "))
selected_scale = available_scales[scale_choice - 1]

enhance_images(args.input_folder, args.output_folder, args.tile, selected_model, selected_scale)

Explicación de los cambios en la parte de destino

Se obtiene el nombre original del archivo y se separa en "base" y "extensión" con os.path.splitext(filename).

Luego se crea un nuevo nombre (dest_filename) concatenando la base, un guión bajo y el nombre del modelo, y finalmente la extensión.

Así, si el archivo original es por ejemplo "NiñobyPhotopea.png" y el modelo es "RealESRGAN_x2plus", se guarda en el directorio de destino como "NiñobyPhotopea_RealESRGAN_x2plus.png".

De este modo, se conserva el nombre original en el directorio fuente y se añade la identificación del modelo en el archivo guardado en el directorio destino.

¿Te sirve esta versión “rizada” del código?

o3-mini-high

Dijiste:

Probando, si queda bien, que casis eguro proque tu eres uan fiera programando, pues maravilloso!!!

Probando!!!

ChatGPT dijo:

Razonado durante 4 segundos

¡Genial! Me alegra saber que todo funciona como esperabas. Si encuentras algún otro detalle o necesitas más ajustes, no dudes en decírmelo. ¡Éxito con tu proyecto!

o3-mini-high

Dijiste:

Funciona muiy bien, pero en el directorio de destino no renombra a lo original, con ñ o acentos, si hace bien lo de a1adirle el modelo al final, o sea:
NiñobyPhotopea.png

Lo deja en el directorio de destino como:
NinobyPhotopea_RealESRGAN_x2plus.png

Y deberia dejarlo:
NiñobyPhotopea_RealESRGAN_x2plus.png

Com lo ves para dejarlo ya totalmente perfecto?

ChatGPT dijo:

Razonado durante 39 segundos

Aquí tienes el código completo con la modificación solicitada. En esta versión se conserva el nombre original (con ñ y acentos) para el archivo de destino, añadiendo la identificación del modelo, mientras que para la lectura se utiliza una copia temporal con el nombre normalizado si es necesario. De este modo se respeta el nombre original en el directorio de origen y, en el de destino, se adjunta el nombre del modelo sin perder los acentos.

python

Copiar

import os
import argparse
import cv2
import time
import torch
import numpy as np
from PIL import Image
import unicodedata
import shutil
from realesrgan.utils import RealESRGANer
from basicsr.archs.rrdbnet_arch import RRDBNet
from realesrgan.archs.srvgg_arch import SRVGGNetCompact

def list_models():
return {
"RealESRGAN_x4plus": {
"net": lambda scale: RRDBNet(num_in_ch=3, num_out_ch=3, num_feat=64, num_block=23, num_grow_ch=32, scale=scale),
"scale": [4],
"path": "RealESRGAN_x4plus.pth"
},
"RealESRNet_x4plus": {
"net": lambda scale: RRDBNet(num_in_ch=3, num_out_ch=3, num_feat=64, num_block=23, num_grow_ch=32, scale=scale),
"scale": [4],
"path": "RealESRNet_x4plus.pth"
},
"RealESRGAN_x4plus_anime_6B": {
"net": lambda scale: RRDBNet(num_in_ch=3, num_out_ch=3, num_feat=64, num_block=6, num_grow_ch=32, scale=scale),
"scale": [4],
"path": "RealESRGAN_x4plus_anime_6B.pth"
},
"RealESRGAN_x2plus": {
"net": lambda scale: RRDBNet(num_in_ch=3, num_out_ch=3, num_feat=64, num_block=23, num_grow_ch=32, scale=scale),
"scale": [2],
"path": "RealESRGAN_x2plus.pth"
},
"realesr-general-x4v3": {
"net": lambda scale: SRVGGNetCompact(num_in_ch=3, num_out_ch=3, num_feat=64, num_conv=32, upscale=scale, act_type='prelu'),
"scale": [4],
"path": "realesr-general-x4v3.pth"
},
"realesr-animevideov3": {
"net": lambda scale: SRVGGNetCompact(num_in_ch=3, num_out_ch=3, num_feat=64, num_conv=16, upscale=scale, act_type='prelu'),
"scale": [4],
"path": "realesr-animevideov3.pth"
},
"RealESRGAN_x2": {
"net": lambda scale: RRDBNet(num_in_ch=3, num_out_ch=3, num_feat=64, num_block=23, num_grow_ch=32, scale=scale),
"scale": [2],
"path": "RealESRGAN_x2.pth"
},
"RealESRGAN_x4": {
"net": lambda scale: RRDBNet(num_in_ch=3, num_out_ch=3, num_feat=64, num_block=23, num_grow_ch=32, scale=scale),
"scale": [4],
"path": "RealESRGAN_x4.pth"
},
}

def remove_accents(text):
"""Normaliza la cadena eliminando acentos y otros caracteres no-ASCII."""
return unicodedata.normalize('NFKD', text).encode('ASCII', 'ignore').decode('ASCII')

def get_unique_filename(folder, filename):
"""
Genera un nombre de archivo único en la carpeta indicada.
Si el archivo 'filename' ya existe, añade un sufijo (_1, _2, …) hasta que no exista.
"""
base, ext = os.path.splitext(filename)
candidate = filename
counter = 1
while os.path.exists(os.path.join(folder, candidate)):
candidate = f"{base}_{counter}{ext}"
counter += 1
return candidate

def robust_imread(path):
"""Intenta leer la imagen de forma estándar; de no poder, utiliza imdecode."""
img = cv2.imread(path, cv2.IMREAD_UNCHANGED)
if img is None:
try:
img_array = np.fromfile(path, np.uint8)
img = cv2.imdecode(img_array, cv2.IMREAD_UNCHANGED)
if img is not None:
print(f"Imagen leída correctamente con imdecode: {path}")
except Exception as e:
print(f"Error al leer la imagen {path} con imdecode: {e}")
return img

def enhance_images(input_folder, output_folder, tile, model_name, netscale):
script_dir = os.path.dirname(os.path.abspath(__file__))
models = list_models()
model_def = models[model_name]
model = model_def["net"](netscale)
model_path = os.path.join(script_dir, model_def["path"])

# Instanciación inicial del upsampler
upsampler = RealESRGANer(
scale=netscale,
model_path=model_path,
model=model,
tile=tile,
tile_pad=10,
pre_pad=0,
half=True
)

os.makedirs(output_folder, exist_ok=True)
image_filenames = sorted([f for f in os.listdir(input_folder) if f.lower().endswith(('.png', '.jpg', '.jpeg', '.bmp'))])
total_images = len(image_filenames)
print(f"Se encontraron {total_images} imágenes en {input_folder}")

start_total = time.time()

# Carpeta temporal para archivos con nombres problemáticos en lectura
temp_dir = os.path.join(input_folder, "_tmp_renamed")
os.makedirs(temp_dir, exist_ok=True)

for idx, filename in enumerate(image_filenames, start=1):
# Conservamos el nombre original para usarlo en el archivo de destino
original_filename = filename
original_path = os.path.join(input_folder, original_filename)
read_path = original_path # Por defecto se utiliza el archivo original
temp_file_created = False

# Si el nombre contiene caracteres no-ASCII, se crea una copia temporal
if any(ord(char) > 127 for char in original_filename):
temp_filename = remove_accents(original_filename)
temp_filename = get_unique_filename(temp_dir, temp_filename)
read_path = os.path.join(temp_dir, temp_filename)
try:
shutil.copy2(original_path, read_path)
temp_file_created = True
print(f"Se creó copia temporal para '{original_filename}' como '{temp_filename}'")
except Exception as e:
print(f"No se pudo copiar '{original_filename}' a la carpeta temporal: {e}")
read_path = original_path

print(f"Procesando ({idx}/{total_images}): {read_path}")
start_img = time.time()
img = robust_imread(read_path)
if img is None:
print(f"No se pudo cargar {read_path}")
if temp_file_created:
os.remove(read_path)
continue

# Intenta mejorar la imagen; si hay error de memoria, se reintenta con tiling
try:
enhanced_img, img_mode = upsampler.enhance(img)
except RuntimeError as e:
if "CUDA out of memory" in str(e):
print("Error de memoria detectado, reintentando con tiling...")
torch.cuda.empty_cache()
upsampler = RealESRGANer(
scale=netscale,
model_path=model_path,
model=model,
tile=100,
tile_pad=10,
pre_pad=0,
half=True
)
enhanced_img, img_mode = upsampler.enhance(img)
else:
raise

# Conversión según el modo de la imagen resultante
if img_mode == "L":
pil_img = Image.fromarray(enhanced_img, mode="L")
elif img_mode == "RGBA":
rgb_img = enhanced_img[:, :, :3][:, :, ::-1]
alpha_channel = enhanced_img[:, :, 3]
pil_img = Image.fromarray(np.dstack([rgb_img, alpha_channel]), mode="RGBA")
else:
pil_img = Image.fromarray(enhanced_img[:, :, ::-1], mode="RGB")

# Se construye el nombre de salida usando el nombre original
base, ext = os.path.splitext(original_filename)
dest_filename = f"{base}_{model_name}{ext}"
output_path = os.path.join(output_folder, dest_filename)
pil_img.save(output_path)

end_img = time.time()
processing_time = end_img - start_img
elapsed_total = time.time() - start_total
remaining_images = total_images - idx
avg_time_per_image = elapsed_total / idx if idx > 0 else 0
estimated_remaining = avg_time_per_image * remaining_images
remaining_str = time.strftime("%H:%M:%S", time.gmtime(estimated_remaining))

# Mostrar información del dispositivo con colores ANSI
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
GREEN = "\033[92m"
CYAN = "\033[96m"
YELLOW = "\033[93m"
RED = "\033[91m"
RESET = "\033[0m"

print(f"{CYAN}🖥️ Dispositivo utilizado: {device}{RESET}")
if device.type == "cuda":
print(f"{GREEN}🚀 GPU detectada: {torch.cuda.get_device_name(0)}{RESET}")
else:
print(f"{YELLOW}⚙️ Ejecutando en CPU{RESET}")
print(f"{RED}Imagen {idx}/{total_images} procesada en {processing_time:.2f} s.")
print(f"{RED}Tiempo total transcurrido: {elapsed_total:.2f} s. Estimación restante: {estimated_remaining:.2f} s ({remaining_str}).")
print(f"{RESET}Imagen guardada: {output_path}\n")

# Elimina el archivo temporal si se creó
if temp_file_created and os.path.exists(read_path):
os.remove(read_path)

# Borra la carpeta temporal si quedó vacía
if os.path.isdir(temp_dir) and not os.listdir(temp_dir):
os.rmdir(temp_dir)

total_time = time.time() - start_total
print(f"Proceso completado. Tiempo total: {total_time:.2f} s.")

if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Mejora imágenes con Real-ESRGAN")
parser.add_argument("--input_folder", type=str, required=True, help="Carpeta con imágenes originales")
parser.add_argument("--output_folder", type=str, required=True, help="Carpeta para guardar las imágenes mejoradas")
parser.add_argument("--tile", type=int, default=0, help="Tamaño de tiling (default: 0, sin tiling)")
parser.add_argument("--model_name", type=str, help="Nombre del modelo a usar")
parser.add_argument("--scale", type=int, help="Factor de mejora si el modelo lo permite")
args = parser.parse_args()

# Información inicial del dispositivo
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
GREEN = "\033[92m"
CYAN = "\033[96m"
YELLOW = "\033[93m"
RED = "\033[91m"
RESET = "\033[0m"
print(f"{CYAN}🖥️ Dispositivo utilizado: {device}{RESET}")
if device.type == "cuda":
print(f"{GREEN}🚀 GPU detectada: {torch.cuda.get_device_name(0)}{RESET}")
else:
print(f"{YELLOW}⚙️ Ejecutando en CPU{RESET}")

models = list_models()
if args.model_name and args.model_name in models:
selected_model = args.model_name
else:
print("\nModelos disponibles:")
for idx, key in enumerate(models, start=1):
print(f"{idx}. {key} (Scale {models[key]['scale'][0]}x)")
choice = int(input("\nElige el número de modelo: "))
selected_model = list(models.keys())[choice - 1]

available_scales = models[selected_model]["scale"]
if args.scale and args.scale in available_scales:
selected_scale = args.scale
elif len(available_scales) == 1:
selected_scale = available_scales[0]
else:
print("\nEscalas disponibles para este modelo:")
for idx, scale_option in enumerate(available_scales, start=1):
print(f"{idx}. {scale_option}x")
scale_choice = int(input("\nElige el número de escala: "))
selected_scale = available_scales[scale_choice - 1]

enhance_images(args.input_folder, args.output_folder, args.tile, selected_model, selected_scale)

Resumen de los cambios

Se mantiene una variable original_filename que conserva el nombre original (con ñ y acentos) para construir el nombre del archivo de salida.

La copia temporal para lectura se realiza únicamente para evitar problemas de caracteres problemáticos, sin modificar el nombre original.

Con este ajuste, si el archivo original es, por ejemplo,

NiñobyPhotopea.png

en el directorio de destino se guardará como:

NiñobyPhotopea_RealESRGAN_x2plus.png

¿Te sirve esta versión final?

o3-mini-high

Dijiste:

Probando...

Y bueno, un ultimo ajuste quizas, parece que cuando detecta que no puede con la GPU, activa el tile, pero parece que lo deja activado apra todas las siguientes imagenes - eso creo -, y no podria ser que lo activara si ve que al GPU no puede, pero en la siguiente imagen chequeara, y si pude la GPU, lo hiciera directamente?

Es que con tile va mas lento... Si es necesario, pues perfecto, si no, va mas rapido con gpu directo...

ChatGPT dijo:

Razonado durante 22 segundos

A continuación te muestro el código completo modificado de modo que, en cada iteración (para cada imagen), se crea un objeto upsampler usando el valor de tiling por defecto (por ejemplo, 0 si no se ha especificado tiling). Si para esa imagen se produce un error de memoria, se re-crea el upsampler con tiling (por ejemplo, 100) y se reintenta el procesamiento para esa imagen solamente. Así, para la siguiente imagen se usará nuevamente la configuración por defecto (más rápida si la GPU puede) en vez de dejar activado el tiling de forma global.

python

Copiar

import os
import argparse
import cv2
import time
import torch
import numpy as np
from PIL import Image
import unicodedata
import shutil
from realesrgan.utils import RealESRGANer
from basicsr.archs.rrdbnet_arch import RRDBNet
from realesrgan.archs.srvgg_arch import SRVGGNetCompact

def list_models():
return {
"RealESRGAN_x4plus": {
"net": lambda scale: RRDBNet(num_in_ch=3, num_out_ch=3, num_feat=64, num_block=23, num_grow_ch=32, scale=scale),
"scale": [4],
"path": "RealESRGAN_x4plus.pth"
},
"RealESRNet_x4plus": {
"net": lambda scale: RRDBNet(num_in_ch=3, num_out_ch=3, num_feat=64, num_block=23, num_grow_ch=32, scale=scale),
"scale": [4],
"path": "RealESRNet_x4plus.pth"
},
"RealESRGAN_x4plus_anime_6B": {
"net": lambda scale: RRDBNet(num_in_ch=3, num_out_ch=3, num_feat=64, num_block=6, num_grow_ch=32, scale=scale),
"scale": [4],
"path": "RealESRGAN_x4plus_anime_6B.pth"
},
"RealESRGAN_x2plus": {
"net": lambda scale: RRDBNet(num_in_ch=3, num_out_ch=3, num_feat=64, num_block=23, num_grow_ch=32, scale=scale),
"scale": [2],
"path": "RealESRGAN_x2plus.pth"
},
"realesr-general-x4v3": {
"net": lambda scale: SRVGGNetCompact(num_in_ch=3, num_out_ch=3, num_feat=64, num_conv=32, upscale=scale, act_type='prelu'),
"scale": [4],
"path": "realesr-general-x4v3.pth"
},
"realesr-animevideov3": {
"net": lambda scale: SRVGGNetCompact(num_in_ch=3, num_out_ch=3, num_feat=64, num_conv=16, upscale=scale, act_type='prelu'),
"scale": [4],
"path": "realesr-animevideov3.pth"
},
"RealESRGAN_x2": {
"net": lambda scale: RRDBNet(num_in_ch=3, num_out_ch=3, num_feat=64, num_block=23, num_grow_ch=32, scale=scale),
"scale": [2],
"path": "RealESRGAN_x2.pth"
},
"RealESRGAN_x4": {
"net": lambda scale: RRDBNet(num_in_ch=3, num_out_ch=3, num_feat=64, num_block=23, num_grow_ch=32, scale=scale),
"scale": [4],
"path": "RealESRGAN_x4.pth"
},
}

def remove_accents(text):
"""Normaliza la cadena eliminando acentos y otros caracteres no-ASCII."""
return unicodedata.normalize('NFKD', text).encode('ASCII', 'ignore').decode('ASCII')

def get_unique_filename(folder, filename):
"""
Genera un nombre de archivo único en la carpeta indicada.
Si el archivo 'filename' ya existe, añade un sufijo (_1, _2, …) hasta que no exista.
"""
base, ext = os.path.splitext(filename)
candidate = filename
counter = 1
while os.path.exists(os.path.join(folder, candidate)):
candidate = f"{base}_{counter}{ext}"
counter += 1
return candidate

def robust_imread(path):
"""Intenta leer la imagen de forma estándar; de no poder, utiliza imdecode."""
img = cv2.imread(path, cv2.IMREAD_UNCHANGED)
if img is None:
try:
img_array = np.fromfile(path, np.uint8)
img = cv2.imdecode(img_array, cv2.IMREAD_UNCHANGED)
if img is not None:
print(f"Imagen leída correctamente con imdecode: {path}")
except Exception as e:
print(f"Error al leer la imagen {path} con imdecode: {e}")
return img

def enhance_images(input_folder, output_folder, tile, model_name, netscale):
script_dir = os.path.dirname(os.path.abspath(__file__))
models = list_models()
model_def = models[model_name]
model = model_def["net"](netscale)
model_path = os.path.join(script_dir, model_def["path"])

# Se guarda el valor por defecto de tile para usarlo en cada iteración.
default_tile = tile

os.makedirs(output_folder, exist_ok=True)
image_filenames = sorted([f for f in os.listdir(input_folder) if f.lower().endswith(('.png', '.jpg', '.jpeg', '.bmp'))])
total_images = len(image_filenames)
print(f"Se encontraron {total_images} imágenes en {input_folder}")

start_total = time.time()

# Carpeta temporal para archivos con nombres problemáticos en lectura
temp_dir = os.path.join(input_folder, "_tmp_renamed")
os.makedirs(temp_dir, exist_ok=True)

for idx, filename in enumerate(image_filenames, start=1):
# Conservamos el nombre original para usarlo en el archivo de destino
original_filename = filename
original_path = os.path.join(input_folder, original_filename)
read_path = original_path # Por defecto se utiliza el archivo original
temp_file_created = False

# Si el nombre contiene caracteres no-ASCII, se crea una copia temporal para la lectura.
if any(ord(char) > 127 for char in original_filename):
temp_filename = remove_accents(original_filename)
temp_filename = get_unique_filename(temp_dir, temp_filename)
read_path = os.path.join(temp_dir, temp_filename)
try:
shutil.copy2(original_path, read_path)
temp_file_created = True
print(f"Se creó copia temporal para '{original_filename}' como '{temp_filename}'")
except Exception as e:
print(f"No se pudo copiar '{original_filename}' a la carpeta temporal: {e}")
read_path = original_path

print(f"Procesando ({idx}/{total_images}): {read_path}")
start_img = time.time()
img = robust_imread(read_path)
if img is None:
print(f"No se pudo cargar {read_path}")
if temp_file_created:
os.remove(read_path)
continue

# Para cada imagen se crea el upsampler con el valor de tiling por defecto
upsampler = RealESRGANer(
scale=netscale,
model_path=model_path,
model=model,
tile=default_tile,
tile_pad=10,
pre_pad=0,
half=True
)

# Intenta mejorar la imagen; si hay error de memoria, se reintenta con tiling (por ejemplo, 100)
try:
enhanced_img, img_mode = upsampler.enhance(img)
except RuntimeError as e:
if "CUDA out of memory" in str(e):
print("Error de memoria detectado, reintentando con tiling...")
torch.cuda.empty_cache()
upsampler = RealESRGANer(
scale=netscale,
model_path=model_path,
model=model,
tile=100, # Valor de tiling de fallback
tile_pad=10,
pre_pad=0,
half=True
)
enhanced_img, img_mode = upsampler.enhance(img)
else:
raise

# Conversión de la imagen mejorada según su modo
if img_mode == "L":
pil_img = Image.fromarray(enhanced_img, mode="L")
elif img_mode == "RGBA":
rgb_img = enhanced_img[:, :, :3][:, :, ::-1]
alpha_channel = enhanced_img[:, :, 3]
pil_img = Image.fromarray(np.dstack([rgb_img, alpha_channel]), mode="RGBA")
else:
pil_img = Image.fromarray(enhanced_img[:, :, ::-1], mode="RGB")

# Construcción del nombre de salida usando el nombre original, conservando acentos,
# y añadiéndole un sufijo con el modelo utilizado.
base, ext = os.path.splitext(original_filename)
dest_filename = f"{base}_{model_name}{ext}"
output_path = os.path.join(output_folder, dest_filename)
pil_img.save(output_path)

end_img = time.time()
processing_time = end_img - start_img
elapsed_total = time.time() - start_total
remaining_images = total_images - idx
avg_time_per_image = elapsed_total / idx if idx > 0 else 0
estimated_remaining = avg_time_per_image * remaining_images
remaining_str = time.strftime("%H:%M:%S", time.gmtime(estimated_remaining))

# Mostrar información del dispositivo con colores ANSI
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
GREEN = "\033[92m"
CYAN = "\033[96m"
YELLOW = "\033[93m"
RED = "\033[91m"
RESET = "\033[0m"

print(f"{CYAN}🖥️ Dispositivo utilizado: {device}{RESET}")
if device.type == "cuda":
print(f"{GREEN}🚀 GPU detectada: {torch.cuda.get_device_name(0)}{RESET}")
else:
print(f"{YELLOW}⚙️ Ejecutando en CPU{RESET}")
print(f"{RED}Imagen {idx}/{total_images} procesada en {processing_time:.2f} s.")
print(f"{RED}Tiempo total transcurrido: {elapsed_total:.2f} s. Estimación restante: {estimated_remaining:.2f} s ({remaining_str}).")
print(f"{RESET}Imagen guardada: {output_path}\n")

# Elimina el archivo temporal si se creó
if temp_file_created and os.path.exists(read_path):
os.remove(read_path)

# Borra la carpeta temporal si quedó vacía
if os.path.isdir(temp_dir) and not os.listdir(temp_dir):
os.rmdir(temp_dir)

total_time = time.time() - start_total
print(f"Proceso completado. Tiempo total: {total_time:.2f} s.")

if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Mejora imágenes con Real-ESRGAN")
parser.add_argument("--input_folder", type=str, required=True, help="Carpeta con imágenes originales")
parser.add_argument("--output_folder", type=str, required=True, help="Carpeta para guardar las imágenes mejoradas")
parser.add_argument("--tile", type=int, default=0, help="Tamaño de tiling (default: 0, sin tiling)")
parser.add_argument("--model_name", type=str, help="Nombre del modelo a usar")
parser.add_argument("--scale", type=int, help="Factor de mejora si el modelo lo permite")
args = parser.parse_args()

# Información inicial del dispositivo
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
GREEN = "\033[92m"
CYAN = "\033[96m"
YELLOW = "\033[93m"
RED = "\033[91m"
RESET = "\033[0m"
print(f"{CYAN}🖥️ Dispositivo utilizado: {device}{RESET}")
if device.type == "cuda":
print(f"{GREEN}🚀 GPU detectada: {torch.cuda.get_device_name(0)}{RESET}")
else:
print(f"{YELLOW}⚙️ Ejecutando en CPU{RESET}")

models = list_models()
if args.model_name and args.model_name in models:
selected_model = args.model_name
else:
print("\nModelos disponibles:")
for idx, key in enumerate(models, start=1):
print(f"{idx}. {key} (Scale {models[key]['scale'][0]}x)")
choice = int(input("\nElige el número de modelo: "))
selected_model = list(models.keys())[choice - 1]

available_scales = models[selected_model]["scale"]
if args.scale and args.scale in available_scales:
selected_scale = args.scale
elif len(available_scales) == 1:
selected_scale = available_scales[0]
else:
print("\nEscalas disponibles para este modelo:")
for idx, scale_option in enumerate(available_scales, start=1):
print(f"{idx}. {scale_option}x")
scale_choice = int(input("\nElige el número de escala: "))
selected_scale = available_scales[scale_choice - 1]

enhance_images(args.input_folder, args.output_folder, args.tile, selected_model, selected_scale)

Explicación del ajuste en la gestión de tiling

Creación del upsampler por imagen:

En cada iteración, se instancia un nuevo objeto RealESRGANer usando el valor de tiling por defecto (por ejemplo, 0).

Fallback condicional:

Si para esa imagen se produce un error de "CUDA out of memory", se vuelve a instanciar el upsampler para esa imagen única usando un valor de tiling de fallback (en este ejemplo, 100).

Restauración para la siguiente imagen:

Al finalizar la iteración, la próxima imagen se procesa nuevamente con el upsampler configurado con el tile por defecto, de modo que no se aplique el fallback de tiling a todas las imágenes subsecuentes si la GPU tiene suficiente memoria.

Con esta modificación, el proceso es lo más eficiente posible y solo usa tiling cuando es necesario para cada imagen, sin afectar el rendimiento global.

¿Te sirve esta versión final?

o3-mini-high

Dijiste:

Hasta ahora no has tenido ni un solo fallo, queda esta ultima prueba, eres genial Chat!!!

Probando esta ultima!!!

ChatGPT dijo:

Razonado durante 5 segundos

¡Excelente! Me alegra muchísimo saber que todo funciona a la perfección y que la solución cumple con tus expectativas. Si en el futuro necesitas realizar algún ajuste adicional o tienes otra consulta, no dudes en decírmelo. ¡Suerte con tu proyecto y gracias por tus palabras!

o3-mini-high

Dijiste:

La perfeccion Chat, Muchisimas Gracias!!!

ChatGPT dijo:

¡De nada! Me alegra muchísimo saber que la solución es de tu agrado y que todo funciona perfectamente. Si necesitas más ayuda o ajustes en el futuro, estaré encantado de asistirte. ¡Éxito con tu proyecto!

o3-mini-high

Buscar

Investigar a fondo

?