File size: 7,868 Bytes
a42c30f
00a78a6
 
00b55a6
a3d8190
00b55a6
a45d2d7
00a78a6
a45d2d7
 
 
 
 
 
 
 
 
 
 
 
 
3575b55
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
a45d2d7
 
a3d8190
 
 
a45d2d7
 
 
 
 
a3d8190
a45d2d7
 
a3d8190
a45d2d7
a3d8190
 
 
 
 
 
 
a45d2d7
 
 
 
 
 
 
 
 
a3d8190
 
 
 
 
 
 
 
 
a45d2d7
 
 
a3d8190
 
 
3575b55
 
 
a45d2d7
3575b55
a3d8190
00a78a6
a3d8190
a45d2d7
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3575b55
 
9d2d3c1
a45d2d7
 
3575b55
a45d2d7
 
 
 
 
 
 
 
 
 
 
 
00a78a6
 
a45d2d7
 
 
 
 
00a78a6
3575b55
 
a45d2d7
 
 
 
00a78a6
 
 
a45d2d7
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
import gradio as gr
import tensorflow as tf
import numpy as np
import cv2
from huggingface_hub import hf_hub_download
import time
import os

# Configuración de GPU para TensorFlow
physical_devices = tf.config.list_physical_devices('GPU')
if physical_devices:
    print("GPU disponible. Configurando...")
    try:
        for gpu in physical_devices:
            tf.config.experimental.set_memory_growth(gpu, True)
        print("Configuración de GPU completada")
    except Exception as e:
        print(f"Error en configuración de GPU: {e}")
else:
    print("No se detectó GPU. El procesamiento será más lento.")

# Definir funciones personalizadas para las transformadas de Fourier
def fourier_transform(x):
    fourier = tf.signal.fft2d(tf.cast(x, tf.complex64))
    fourier = tf.complex(tf.math.real(fourier), tf.math.imag(fourier))
    fourier = tf.abs(fourier)
    return tf.concat([tf.math.real(fourier), tf.math.imag(fourier)], axis=-1)

def inverse_fourier_transform(x):
    real_part, imag_part = tf.split(x, num_or_size_splits=2, axis=-1)
    complex_fourier = tf.complex(real_part, imag_part)
    return tf.abs(tf.signal.ifft2d(complex_fourier))

# Construir modelo manualmente
def build_autoencoder(input_shape=(256, 256, 3)):
    """Reconstruir el modelo autoencoder manualmente"""
    
    # Definir entradas
    inputs = tf.keras.layers.Input(shape=input_shape)
    
    # Aplicar transformada de Fourier (opcional)
    # x = tf.keras.layers.Lambda(fourier_transform)(inputs)
    x = inputs  # Skip Fourier transform for now
    
    # Encoder
    x = tf.keras.layers.Conv2D(32, (3, 3), activation='relu', padding='same')(x)
    x = tf.keras.layers.MaxPooling2D((2, 2), padding='same')(x)
    x = tf.keras.layers.Conv2D(64, (3, 3), activation='relu', padding='same')(x)
    x = tf.keras.layers.MaxPooling2D((2, 2), padding='same')(x)
    x = tf.keras.layers.Conv2D(128, (3, 3), activation='relu', padding='same')(x)
    encoded = tf.keras.layers.MaxPooling2D((2, 2), padding='same')(x)
    
    # Decoder
    x = tf.keras.layers.Conv2D(128, (3, 3), activation='relu', padding='same')(encoded)
    x = tf.keras.layers.UpSampling2D((2, 2))(x)
    x = tf.keras.layers.Conv2D(64, (3, 3), activation='relu', padding='same')(x)
    x = tf.keras.layers.UpSampling2D((2, 2))(x)
    x = tf.keras.layers.Conv2D(32, (3, 3), activation='relu', padding='same')(x)
    x = tf.keras.layers.UpSampling2D((2, 2))(x)
    
    # Output
    # x = tf.keras.layers.Lambda(inverse_fourier_transform)(x)  # Skip inverse transform
    outputs = tf.keras.layers.Conv2D(3, (3, 3), activation='sigmoid', padding='same')(x)
    
    # Crear modelo
    model = tf.keras.models.Model(inputs, outputs)
    
    return model

# Opción alternativa: Crear un modelo simplificado
def build_simple_autoencoder(input_shape=(256, 256, 3)):
    """Crear un autoencoder simple (sin Fourier)"""
    
    inputs = tf.keras.layers.Input(shape=input_shape)
    
    # Encoder
    x = tf.keras.layers.Conv2D(32, (3, 3), activation='relu', padding='same')(inputs)
    x = tf.keras.layers.MaxPooling2D((2, 2), padding='same')(x)
    x = tf.keras.layers.Conv2D(64, (3, 3), activation='relu', padding='same')(x)
    x = tf.keras.layers.MaxPooling2D((2, 2), padding='same')(x)
    
    # Decoder
    x = tf.keras.layers.Conv2D(64, (3, 3), activation='relu', padding='same')(x)
    x = tf.keras.layers.UpSampling2D((2, 2))(x)
    x = tf.keras.layers.Conv2D(32, (3, 3), activation='relu', padding='same')(x)
    x = tf.keras.layers.UpSampling2D((2, 2))(x)
    
    # Output
    outputs = tf.keras.layers.Conv2D(3, (3, 3), activation='sigmoid', padding='same')(x)
    
    # Crear modelo
    model = tf.keras.models.Model(inputs, outputs)
    
    return model

# Funciones de preprocesamiento optimizadas
def degrade_image(image, downscale_factor=4):
    """Reduce la calidad de la imagen reduciendo su tamaño y volviéndola a escalar."""
    h, w = image.shape[:2]
    
    # Verificar tamaño mínimo
    if h < downscale_factor*4 or w < downscale_factor*4:
        return image  # Evitar downscaling excesivo en imágenes pequeñas
    
    # Reducir tamaño (forzando pérdida de calidad)
    small_img = cv2.resize(image, (w // downscale_factor, h // downscale_factor), 
                          interpolation=cv2.INTER_AREA)

    # Volver a escalarla al tamaño original
    degraded_img = cv2.resize(small_img, (w, h), interpolation=cv2.INTER_NEAREST)

    return degraded_img

def preprocess_image(image, std_dev=0.1, downscale_factor=4, target_size=(256, 256)):
    """Recibe una imagen en numpy array, la degrada en calidad, le agrega ruido y la normaliza."""
    
    # Verificar si la imagen es a color o en escala de grises
    if len(image.shape) == 2:
        # Convertir a RGB si es escala de grises
        image = cv2.cvtColor(image, cv2.COLOR_GRAY2RGB)
    elif image.shape[2] == 4:
        # Convertir de RGBA a RGB si tiene canal alpha
        image = cv2.cvtColor(image, cv2.COLOR_RGBA2RGB)
        
    # Reducir calidad
    degraded_img = degrade_image(image, downscale_factor)

    # Redimensionar a tamaño esperado por el modelo
    resized_img = cv2.resize(degraded_img, target_size, interpolation=cv2.INTER_AREA)
    
    # Normalizar
    resized_img = resized_img.astype(np.float32) / 255.0

    # Agregar ruido gaussiano
    noise = np.random.normal(0, std_dev, resized_img.shape)
    noisy_img = resized_img + noise
    noisy_img = np.clip(noisy_img, 0, 1)

    return noisy_img

print("Crear modelo simplificado...")
model = build_simple_autoencoder()
print("Modelo creado correctamente")

# Función de denoising
def Denoiser(imagen):
    """Aplica el modelo autoencoder para eliminar ruido de la imagen."""
    
    # Verificar que la imagen no sea None
    if imagen is None:
        return None, None
    
    # Convertir imagen de entrada a array NumPy
    imagen = np.array(imagen)
    
    # Verificar dimensiones de la imagen
    if len(imagen.shape) < 2:
        return None, None
    
    try:
        # Preprocesar imagen (degradarla y agregar ruido)
        noisy_image = preprocess_image(imagen)
        
        # Expandir dimensiones para el formato del modelo
        noisy_image_input = np.expand_dims(noisy_image, axis=0)
        
        # Medir el tiempo de la predicción
        start_time = time.time()
        
        # Para la primera versión usamos el modelo simple sin Fourier
        # Esto resolverá el problema de lambda layers
        reconstructed = model(noisy_image_input, training=False).numpy()[0]
        
        prediction_time = time.time() - start_time
        print(f"Tiempo de predicción: {prediction_time:.2f} segundos")
        
        # Asegurarse de que las imágenes estén en el rango [0, 255]
        noisy_image = np.uint8(noisy_image * 255)
        reconstructed = np.uint8(reconstructed * 255)
        
        return noisy_image, reconstructed
        
    except Exception as e:
        print(f"Error en el procesamiento: {e}")
        return None, None

# Crear interfaz en Gradio
demo = gr.Interface(
    fn=Denoiser, 
    inputs=gr.Image(type="numpy"),
    outputs=[
        gr.Image(type="numpy", label="Imagen con Ruido"), 
        gr.Image(type="numpy", label="Imagen Restaurada")
    ],
    title="Autoencoder para Denoising",
    description="""Este modelo de autoencoder reduce el ruido en imágenes. Sube una imagen para ver el resultado.
                  Nota: Esta es una versión simplificada que no utiliza el modelo pre-entrenado debido a limitaciones técnicas.""",
    examples=[
        "https://raw.githubusercontent.com/gradio-app/gradio/main/demo/english_htr/images/Create%20a%20free%20Gradio%20account%20to%20access%20our%20most%20powerful%20features.jpeg"
    ],
    cache_examples=True
)

# Lanzar la aplicación en Gradio
if __name__ == "__main__":
    demo.launch(share=False)