File size: 16,174 Bytes
e85e2ac
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1723af0
e85e2ac
 
 
 
1723af0
e85e2ac
 
 
 
 
 
 
 
 
fd26332
 
 
e85e2ac
 
 
 
 
 
f18cf78
 
e85e2ac
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
fd26332
 
e85e2ac
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
""" 
|Funci贸n para preprocesar una imagen antes de la predicci贸n------------------------------------------
|                                                                                         LIBRERIAS |
|___________________________________________________________________________________________________| 
"""

# Machine Learning
import tensorflow as tf  # TensorFlow para el aprendizaje autom谩tico
from tensorflow.keras.models import load_model  # load_model de Keras para cargar modelos guardados
from tensorflow.keras.preprocessing.image import img_to_array  #  img_to_array de Keras para convertir im谩genes en arrays
from tensorflow.keras.applications.mobilenet_v2 import preprocess_input  # preprocess_input de Keras para preprocesar im谩genes
 
# Procesamiento (im谩genes y operaciones)
import cv2  # OpenCV para el procesamiento de im谩genes
import numpy as np  # NumPy para manipulaci贸n de matrices y vectores
import os  # Importa el m贸dulo os para interactuar con el sistema operativo
from revelado.revelado import calculate_ela, calculate_difference_image, apply_auto_contrast, equalize_histogram_color  #  funciones personalizadas del m贸dulo revelado
 
#Interfaz y aplicaci贸n web
import gradio as gr  #  Biblioteca Gradio para crear la interfaz
from gradio import Image  #  Clase Image de Gradio para mostrar im谩genes en la interfaz
from flask import Flask, redirect  # Flask y redirect de Flask para la aplicaci贸n web
import threading  #Threading para ejecutar tareas en hilos separados
from gradio import Interface
import matplotlib.pyplot as plt
from gradio import Image as GrImage, Label, Dropdown, Image as GrImageOut
from tensorflow.keras.preprocessing.image import ImageDataGenerator
from tensorflow.keras.preprocessing.image import load_img, img_to_array
from tensorflow.keras.applications.mobilenet import preprocess_input
#Mapa de calor
from PIL import Image
import numpy as np
from tf_keras_vis.gradcam import GradcamPlusPlus
from tf_keras_vis.utils.model_modifiers import ReplaceToLinear
from tf_keras_vis.utils import normalize

import os
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2'
 
#_______________________________________________
# Importar la clase Flask de Flask para la aplicaci贸n web
app = Flask(__name__)

# T铆tulo y descripci贸n de la interfaz
title = "D2AS: Deep Synthetic Art Detector"
description = "Una aplicaci贸n para realizar predicciones sobre im谩genes art铆sticas generativas vs manuales"

# Cargar el modelo guardado
model_clase_unica = load_model("model.h5")
model_dos_clases = load_model("paper.h5")
 

""" 
|Funci贸n para preprocesar una imagen antes de la predicci贸n------------------------------------------
|                                                                                         FUNCIONES |
|___________________________________________________________________________________________________| 
"""
 

  # Funci贸n para preprocesar una imagen antes de la predicci贸n
def preprocess_image(image):
    """
    Tipo de funci贸n: Preprocesamiento de imagen
    Par谩metros de entrada: image (objeto imagen)
    Par谩metros de salida: imagen_preprocesada (arreglo numpy)
    Descripci贸n: Convierte una imagen en un arreglo numpy, expande las dimensiones
                 y aplica preprocesamiento necesario para el modelo.
    """
    # Convertir la imagen en un arreglo numpy
    image_array = img_to_array(image)
    
    # Expandir las dimensiones del arreglo para que coincida con la entrada esperada por el modelo
    image_array = np.expand_dims(image_array, axis=0)
    
    # Preprocesar la imagen
    imagen_preprocesada = preprocess_input(image_array)
    
    return imagen_preprocesada


""" 
|Funci贸n para guardar la imagen original en la carpeta de entrada------------------------------------
|                                                                                         FUNCIONES |
|___________________________________________________________________________________________________| 
"""

def guardar_imagen_original(imagen, input_folder):
    """
    Tipo de funci贸n: Guardar imagen
    Par谩metros de entrada: imagen (objeto imagen), input_folder (ruta de la carpeta de entrada)
    Par谩metros de salida: Ninguno
    Descripci贸n: Guarda la imagen original en la carpeta de entrada con la m谩xima calidad.
    """
    # Obtener la extensi贸n de archivo original de la imagen
    filename = "original_image.jpg"
    
    # Guardar la imagen en la carpeta de entrada con la m谩xima calidad
    cv2.imwrite(os.path.join(input_folder, filename), imagen, [cv2.IMWRITE_JPEG_QUALITY, 100])
    

""" 
|Funci贸n para realizar predicciones sobre una imagenes-----------------------------------------------
|                                                                                           FUNCION |
|___________________________________________________________________________________________________| 
"""
#Funci贸n para crear el mapa de calor para una clase
def generate_heatmap(original_image, processed_image, model):
    # Convert the original image to the required format
    original_image_color = Image.fromarray(cv2.cvtColor(original_image, cv2.COLOR_BGR2RGBA)).resize((300, 300))
    original_image_gray = original_image_color.convert("L").convert("RGBA")

    # Prepare the processed image
    processed_image_resized = cv2.resize(processed_image, (300, 300))
    processed_image_preprocessed = preprocess_image(processed_image_resized)
      # Define the scoring function for the detected class
    def score_for_class(output):
        return output[:, 0]  # Score for the detected class  
    # Get the predicted probabilities
    probabilities = model.predict(processed_image_preprocessed)[0]
    percentage_class_1 = int(probabilities[0] * 100)
    percentage_class_0 = 100 - percentage_class_1

    # Create an instance of GradcamPlusPlus
    
    gradcam = GradcamPlusPlus(model, model_modifier=ReplaceToLinear(), clone=True)

 
    # Get the Grad-CAM heatmap for the detected class
 
    cam_class = gradcam(score_for_class, processed_image_preprocessed) # Use preprocessed image
    cam_class = normalize(cam_class)

    # Reduce the heatmap dimensionality to 2D and scale to 8-bit value莽s
    cam_class = np.uint8(255 * np.squeeze(cam_class))

    # Save the heatmap and load as an RGBA image
    plt.imsave('temporal.png', cam_class)
    cam_image = Image.open('temporal.png').convert("RGBA")
   
    # Superimpose the heatmap on the original grayscale image
    superimposed_image_class = Image.blend(original_image_gray, cam_image, alpha=0.5)
    # Tama帽o final deseado
    final_height, final_width = 300, 300
    # Concatenate the original image, processed image, and heatmaps
    # Redimensionar todas las im谩genes a la misma altura
    # Convertir la imagen PIL a una matriz NumPy
    original_image_color_array = np.array(original_image_color)

    if original_image_color_array.shape[2] == 4:
        original_image_color_array = cv2.cvtColor(original_image_color_array, cv2.COLOR_RGBA2RGB)

 
    processed_image_array = np.array(processed_image)
 
    superimposed_image_class_array = np.array(superimposed_image_class)
    if superimposed_image_class_array.shape[2] == 4:
        superimposed_image_class_array = cv2.cvtColor(superimposed_image_class_array, cv2.COLOR_RGBA2RGB)

    # Redimensionar todas las im谩genes a las mismas dimensiones
    original_image_color_resized = cv2.resize(original_image_color_array, (final_width, final_height))
    processed_image_resized = cv2.resize(processed_image_array, (final_width, final_height))
    superimposed_image_class_resized = cv2.resize(superimposed_image_class_array, (final_width, final_height))

    # Concatenar las im谩genes redimensionadas
    final_image = np.hstack([original_image_color_resized, processed_image_resized, superimposed_image_class_resized])
    # Verificar la imagen procesada
    print("Processed Image Resized Shape:", processed_image_resized.shape)
    # Verificar el modelo
    print("Model Summary:", model.summary())

    def score_for_class(output):
        score = output[:, 0]  # Score for the detected class
        print("Score Shape:", score.shape)
        return score

    # Return the final concatenated image
    return final_image

def cargar_y_preprocesar_imagen(imagen_path, target_size=(300, 300)):
    # Crear una instancia de ImageDataGenerator con las mismas configuracion para el conjunto de entrenamiento
    datagen =  ImageDataGenerator(rescale=1.0/255.)  
    
    # Cargar la imagen
    img = load_img(imagen_path, target_size=target_size)

    # Convertir la imagen a un array y expandimos las dimensiones para que pueda ser procesada por el ImageDataGenerator
    img = img_to_array(img)
    img = np.expand_dims(img, axis=0)

    # Usar el ImageDataGenerator para procesar la imagen
    img = datagen.flow(img, batch_size=1)

    # El ImageDataGenerator retorna un generador, as铆 que usamos next() para obtener la imagen procesada
    img = next(img)

    # Devolver la imagen procesada
    return img[0] 
#Generamos el mapa de calor para dos clases
def generate_heatmap_2_classes(original_image, processed_image, model):
    # Convertir la imagen original al formato requerido y redimensionar
    # original_image_color = Image.fromarray(cv2.cvtColor(original_image, cv2.COLOR_BGR2RGBA)).resize((300, 300))
    original = Image.open("img.png").resize((300, 300)).convert("RGBA")
    original_image_color = Image.open("img.png").resize((300, 300)).convert("RGBA")
 
    # Leer la imagen procesada desde el archivo "img.png"
    processed_image = cargar_y_preprocesar_imagen("img.png")
    image = np.expand_dims(processed_image, axis=0)  # A帽ade una dimensi贸n extra para el batch

    # Obt茅n las probabilidades de pertenencia a cada clase
    probabilities = model.predict(image)[0]
 
    # Crear una instancia de GradcamPlusPlus
    gradcam = GradcamPlusPlus(model, model_modifier=ReplaceToLinear(), clone=True)
     # Obt茅n las probabilidades de pertenencia a cada clase
    probabilities = model.predict(image)[0]
    percentage_human = int(probabilities[0] * 100)
    percentage_synthetic = int(probabilities[1] * 100)
  
    # Definir funciones de puntuaci贸n para ambas clases
    def score_for_human(output):
        return output[:, 0]  # Puntuaci贸n para la clase "humana"

    def score_for_synthetic(output):
        return output[:, 1]  # Puntuaci贸n para la clase "sint茅tica"

    # Obt茅n los mapas de calor de Grad-CAM para cada clase
    cam_human = gradcam(score_for_human, image)
    cam_human = normalize(cam_human)
    cam_synthetic = gradcam(score_for_synthetic, image)
    cam_synthetic = normalize(cam_synthetic)

   # Reduce la dimensionalidad de los mapas de calor a 2D y esc谩lalos a valores de 8 bits
    cam_human = np.uint8(255 * np.squeeze(cam_human))
    cam_synthetic = np.uint8(255 * np.squeeze(cam_synthetic))

    images_combined = [original_image_color]
    for cam in [cam_human, cam_synthetic]:
        # Guardar el mapa de calor
        plt.imsave('temporal.png', cam)

        # Cargar el mapa de calor y convertirlo a RGBA
        cam_image = Image.open('temporal.png').convert("RGBA")

        # Superponer el mapa de calor a la imagen original
        superimposed_image = Image.blend(original, cam_image, alpha=0.5)

        # Guardar la imagen en la lista de im谩genes a combinarse
        images_combined.append(superimposed_image)

    # Concatenar las im谩genes y guardar la imagen final
    final_image = Image.fromarray(np.hstack([np.array(im) for im in images_combined]))

    return final_image

def hacer_predicciones(modelo_seleccionado, imagen):
    """
    Tipo de funci贸n: Predicci贸n
    Par谩metros de entrada: imagen (objeto imagen)
    Par谩metros de salida: diccionario de predicciones, imagen procesada (objeto imagen)
    Descripci贸n: Realiza predicciones sobre la imagen utilizando el modelo cargado.
                 Devuelve un diccionario con las probabilidades de cada clase y la imagen procesada.
    """
    if modelo_seleccionado == "Modelo 1 Clase":
        model = model_clase_unica
    else:
        model = model_dos_clases

    print(modelo_seleccionado)
 
    original_image = cv2.cvtColor(imagen, cv2.COLOR_RGB2BGR)

    num = 1
    difference_image = calculate_difference_image(imagen, num)
    difference_image2 = apply_auto_contrast(difference_image)
    cv2.imwrite("img.png", difference_image2)
    processed_image = cv2.imread("img.png")
    imagen_resized = cv2.resize(processed_image, (300, 300))
    imagen_preprocesada = preprocess_image(imagen_resized)
    
    if modelo_seleccionado == "Modelo 1 Clase":
        predicciones = model.predict(imagen_preprocesada).tolist()[0]
        probabilidad_clase_1 = predicciones[0]
        probabilidad_clase_0 = 1 - probabilidad_clase_1
        final_image = generate_heatmap(original_image, processed_image, model)
    else:
        # Usar la funci贸n cargar_y_preprocesar_imagen para el preprocesamiento correcto
        #processed_image_preprocessed = cargar_y_preprocesar_imagen("img.jpg")
        gradcam = GradcamPlusPlus(model, model_modifier=ReplaceToLinear(), clone=True)
        imagen_path = "img.png"
        # Convertir la imagen PIL en una matriz NumPy
        imagen = cargar_y_preprocesar_imagen(imagen_path)
        image = np.expand_dims(imagen, axis=0)  # A帽ade una dimensi贸n extra para el batch

        probabilities = model.predict(image)[0]
        print (probabilities)
        probabilidad_clase_0 = int(probabilities[0] * 100)
        probabilidad_clase_1 = int(probabilities[1] * 100)
        predicted_label = np.argmax(probabilities)
        print(predicted_label)
        
        def score_for_human(output):
            return output[:, 0]

        def score_for_synthetic(output):
            return output[:, 1]
         
        gradcam = GradcamPlusPlus(model, model_modifier=ReplaceToLinear(), clone=True)
        cams = [gradcam(score_func, image) for score_func in [score_for_human, score_for_synthetic]]
        cams = [normalize(cam) for cam in cams]
        cams = [np.uint8(255 * np.squeeze(cam)) for cam in cams]
        # Convertir la imagen PIL a una matriz NumPy
     
 
        final_image = generate_heatmap_2_classes(original_image, image, model)

    return {"Arte Manual": probabilidad_clase_0/100, "Arte Sintetico": probabilidad_clase_1/100}, final_image
"""
|Funci贸n para generar gradio en un hilo separado----------------------------------------------------
|                                                                                         FUNCIONES |
|___________________________________________________________________________________________________| 
"""

def run_gradio():
    """
    Tipo de funci贸n: Ejecuci贸n Gradio
    Par谩metros de entrada: Ninguno
    Par谩metros de salida: Ninguno
    Descripci贸n: Ejecuta la interfaz Gradio en un hilo separado.
    """
    output = iface.process([imagen])  # Ejecutar el procesamiento de la imagen
    predicciones = output[0]
    final_image = output[1]

    # Guardar la imagen en una ubicaci贸n accesible
    cv2.imwrite("output_img.jpg", final_image)
#___________________________________________________________________________________________________

# Crear una entrada de imagen en Gradio
#im = GrImage(shape=(None, None), image_mode='RGB', invert_colors=False, source="upload")
im = GrImage(image_mode='RGB', invert_colors=False, source="upload")
model_selector = Dropdown(choices=["Modelo 1 Clase", "Modelo 2 Clases"], label="Selecciona un modelo")

# Crear una interfaz de Gradio con una entrada de imagen
iface = Interface(
    fn=hacer_predicciones,
    inputs=[model_selector, im],
    outputs=[Label(), GrImageOut(type="numpy")],
    title=title,
    description=description,
    allow_flagging="never"
)

# Lanzar la aplicaci贸n Gradio en un hilo separado
threading.Thread(target=run_gradio).start()

# Lanzar la interfaz Gradio
iface.launch(share=False)

# Definir la funci贸n de ruta en Flask
@app.route('/')
def index():
    return redirect(iface.launch(share=True))

# Ejecutar la aplicaci贸n Flask
if __name__ == '__main__':
    app.run()