Actualización del código Error ELA
Browse files
app.py
CHANGED
@@ -8,65 +8,39 @@ import hashlib
|
|
8 |
import gradio as gr
|
9 |
import os
|
10 |
from datetime import datetime
|
|
|
11 |
|
12 |
def obtener_metadatos(imagen):
|
13 |
"""
|
14 |
Obtiene los metadatos de la imagen.
|
15 |
"""
|
16 |
-
metadatos = {}
|
17 |
try:
|
18 |
exif_data = imagen._getexif()
|
19 |
-
if exif_data:
|
20 |
-
|
21 |
-
|
22 |
-
metadatos[tag] = value
|
23 |
except Exception as e:
|
24 |
print(f"Error al obtener metadatos: {e}")
|
25 |
-
|
26 |
|
27 |
-
def
|
28 |
"""
|
29 |
-
|
30 |
"""
|
31 |
-
if not exif_data:
|
32 |
-
return None
|
33 |
-
|
34 |
-
gps_info = exif_data.get("GPSInfo", {})
|
35 |
-
if not gps_info:
|
36 |
-
return None
|
37 |
-
|
38 |
-
# Convertir las coordenadas GPS a grados decimales
|
39 |
-
def gps_to_degrees(value):
|
40 |
-
d, m, s = value
|
41 |
-
return d + (m / 60.0) + (s / 3600.0)
|
42 |
-
|
43 |
try:
|
44 |
-
|
45 |
-
|
46 |
-
|
47 |
-
|
48 |
-
|
49 |
-
|
50 |
-
|
51 |
-
|
52 |
-
|
53 |
-
latitud = -latitud
|
54 |
-
if longitud_ref == "W": # Si es Oeste, convertir a negativo
|
55 |
-
longitud = -longitud
|
56 |
-
return latitud, longitud
|
57 |
except Exception as e:
|
58 |
-
print(f"Error al
|
59 |
return None
|
60 |
|
61 |
-
return None
|
62 |
-
|
63 |
-
def calcular_hash(imagen):
|
64 |
-
"""
|
65 |
-
Calcula el hash MD5 de la imagen para verificar su integridad.
|
66 |
-
"""
|
67 |
-
imagen_bytes = imagen.tobytes()
|
68 |
-
return hashlib.md5(imagen_bytes).hexdigest()
|
69 |
-
|
70 |
def analizar_manipulacion(imagen, metadatos, ela_imagen):
|
71 |
"""
|
72 |
Analiza si la imagen ha sido manipulada.
|
@@ -74,119 +48,78 @@ def analizar_manipulacion(imagen, metadatos, ela_imagen):
|
|
74 |
manipulada = False
|
75 |
razones = []
|
76 |
|
77 |
-
# Verificar si los metadatos están presentes
|
78 |
if not metadatos:
|
79 |
-
razones.append("La imagen no tiene metadatos EXIF
|
80 |
else:
|
81 |
if "Software" in metadatos:
|
82 |
razones.append(f"La imagen fue editada con: {metadatos['Software']}")
|
83 |
manipulada = True
|
84 |
|
85 |
-
|
86 |
-
|
87 |
-
|
88 |
-
|
89 |
-
|
90 |
-
|
91 |
-
|
92 |
-
promedio_ela = np.mean(ela_imagen)
|
93 |
-
if promedio_ela > 10: # Umbral arbitrario, ajustar según los datos
|
94 |
-
razones.append("El análisis ELA sugiere posibles alteraciones en la imagen.")
|
95 |
-
manipulada = True
|
96 |
|
|
|
|
|
|
|
97 |
return manipulada, razones
|
98 |
|
99 |
-
def realizar_ela(imagen, quality=95, scale=100):
|
100 |
-
"""
|
101 |
-
Realiza el Error Level Analysis (ELA) en la imagen utilizando OpenCV.
|
102 |
-
"""
|
103 |
-
# Convertir la imagen a formato compatible con OpenCV
|
104 |
-
imagen_cv = cv2.cvtColor(np.array(imagen), cv2.COLOR_RGB2BGR)
|
105 |
-
|
106 |
-
# Guardar la imagen comprimida
|
107 |
-
temp_path = "temp_image.jpg"
|
108 |
-
cv2.imwrite(temp_path, imagen_cv, [cv2.IMWRITE_JPEG_QUALITY, quality])
|
109 |
-
|
110 |
-
# Leer la imagen comprimida
|
111 |
-
imagen_comprimida = cv2.imread(temp_path)
|
112 |
-
|
113 |
-
# Calcular la diferencia absoluta entre la imagen original y la comprimida
|
114 |
-
diferencia = cv2.absdiff(imagen_cv, imagen_comprimida)
|
115 |
-
|
116 |
-
# Escalar la diferencia para resaltar las áreas modificadas
|
117 |
-
ela_imagen = scale * diferencia
|
118 |
-
|
119 |
-
# Convertir a escala de grises para mejorar la visualización
|
120 |
-
ela_imagen = cv2.cvtColor(ela_imagen, cv2.COLOR_BGR2GRAY)
|
121 |
-
|
122 |
-
# Eliminar el archivo temporal
|
123 |
-
os.remove(temp_path)
|
124 |
-
|
125 |
-
return ela_imagen
|
126 |
-
|
127 |
def procesar_imagen(archivo_imagen):
|
128 |
-
# Cargar la imagen
|
129 |
try:
|
130 |
imagen = Image.open(archivo_imagen)
|
131 |
-
|
132 |
-
|
133 |
-
|
134 |
-
|
135 |
-
|
136 |
-
|
137 |
-
|
138 |
-
|
139 |
-
|
140 |
-
|
141 |
-
|
142 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
143 |
|
144 |
-
|
145 |
-
|
146 |
-
|
147 |
-
|
148 |
-
fecha_modificacion = datetime.fromtimestamp(stats.st_mtime).strftime('%Y-%m-%d %H:%M:%S')
|
149 |
-
|
150 |
-
info_basica += f"""
|
151 |
-
Tamaño del archivo: {tamaño_archivo:.2f} KB
|
152 |
-
Fecha de creación: {fecha_creacion}
|
153 |
-
Fecha de modificación: {fecha_modificacion}
|
154 |
-
"""
|
155 |
-
|
156 |
-
# Obtener metadatos
|
157 |
-
metadatos = obtener_metadatos(imagen)
|
158 |
-
|
159 |
-
# Realizar Error Level Analysis (ELA)
|
160 |
-
ela_imagen = realizar_ela(imagen)
|
161 |
-
|
162 |
-
# Analizar si la imagen ha sido manipulada
|
163 |
-
manipulada, razones = analizar_manipulacion(imagen, metadatos, ela_imagen)
|
164 |
-
info_manipulacion = "\nAnálisis de manipulación:\n"
|
165 |
-
if manipulada:
|
166 |
-
info_manipulacion += "La imagen ha sido manipulada.\n"
|
167 |
-
info_manipulacion += "Razones:\n"
|
168 |
-
for razon in razones:
|
169 |
-
info_manipulacion += f"- {razon}\n"
|
170 |
-
else:
|
171 |
-
info_manipulacion += "La imagen NO ha sido manipulada.\n"
|
172 |
-
|
173 |
-
# Guardar la imagen ELA en un archivo temporal
|
174 |
-
with tempfile.NamedTemporaryFile(suffix=".png", delete=False) as tmp_file:
|
175 |
-
cv2.imwrite(tmp_file.name, ela_imagen)
|
176 |
-
ela_image_path = tmp_file.name
|
177 |
-
|
178 |
-
return ela_image_path, info_basica + info_manipulacion
|
179 |
|
180 |
# Interfaz de GRADIO
|
181 |
iface = gr.Interface(
|
182 |
fn=procesar_imagen,
|
183 |
inputs=gr.Image(type="filepath", label="Sube una imagen"),
|
184 |
outputs=[gr.Image(label="Error Level Analysis (ELA)"), gr.Textbox(label="Resultado del análisis")],
|
185 |
-
title="Análisis de
|
186 |
-
description="
|
187 |
-
<p>Este programa es una herramienta de computación forense diseñado para analizar imágenes en busca de evidencia de manipulación o edición. Utiliza la técnica de Error Level Analysis (ELA).</p>
|
188 |
-
</div>"""
|
189 |
)
|
190 |
|
191 |
-
# Lanzar la interfaz
|
192 |
iface.launch()
|
|
|
8 |
import gradio as gr
|
9 |
import os
|
10 |
from datetime import datetime
|
11 |
+
import tempfile
|
12 |
|
13 |
def obtener_metadatos(imagen):
|
14 |
"""
|
15 |
Obtiene los metadatos de la imagen.
|
16 |
"""
|
|
|
17 |
try:
|
18 |
exif_data = imagen._getexif()
|
19 |
+
if not exif_data:
|
20 |
+
return {}
|
21 |
+
return {TAGS.get(tag_id, tag_id): value for tag_id, value in exif_data.items()}
|
|
|
22 |
except Exception as e:
|
23 |
print(f"Error al obtener metadatos: {e}")
|
24 |
+
return {}
|
25 |
|
26 |
+
def realizar_ela(imagen, quality=95, scale=100):
|
27 |
"""
|
28 |
+
Realiza el Error Level Analysis (ELA) en la imagen.
|
29 |
"""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
30 |
try:
|
31 |
+
imagen_cv = cv2.cvtColor(np.array(imagen), cv2.COLOR_RGB2BGR)
|
32 |
+
temp_path = "temp_image.jpg"
|
33 |
+
cv2.imwrite(temp_path, imagen_cv, [cv2.IMWRITE_JPEG_QUALITY, quality])
|
34 |
+
imagen_comprimida = cv2.imread(temp_path)
|
35 |
+
diferencia = cv2.absdiff(imagen_cv, imagen_comprimida)
|
36 |
+
ela_imagen = scale * diferencia
|
37 |
+
ela_imagen = cv2.cvtColor(ela_imagen, cv2.COLOR_BGR2GRAY)
|
38 |
+
os.remove(temp_path)
|
39 |
+
return ela_imagen
|
|
|
|
|
|
|
|
|
40 |
except Exception as e:
|
41 |
+
print(f"Error al realizar ELA: {e}")
|
42 |
return None
|
43 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
44 |
def analizar_manipulacion(imagen, metadatos, ela_imagen):
|
45 |
"""
|
46 |
Analiza si la imagen ha sido manipulada.
|
|
|
48 |
manipulada = False
|
49 |
razones = []
|
50 |
|
|
|
51 |
if not metadatos:
|
52 |
+
razones.append("La imagen no tiene metadatos EXIF.")
|
53 |
else:
|
54 |
if "Software" in metadatos:
|
55 |
razones.append(f"La imagen fue editada con: {metadatos['Software']}")
|
56 |
manipulada = True
|
57 |
|
58 |
+
if ela_imagen is None:
|
59 |
+
razones.append("No se pudo realizar el análisis ELA.")
|
60 |
+
else:
|
61 |
+
promedio_ela = np.mean(ela_imagen)
|
62 |
+
if promedio_ela > 10:
|
63 |
+
razones.append("El análisis ELA sugiere posibles alteraciones.")
|
64 |
+
manipulada = True
|
|
|
|
|
|
|
|
|
65 |
|
66 |
+
if not razones:
|
67 |
+
razones.append("No se encontraron indicadores claros de manipulación.")
|
68 |
+
|
69 |
return manipulada, razones
|
70 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
71 |
def procesar_imagen(archivo_imagen):
|
|
|
72 |
try:
|
73 |
imagen = Image.open(archivo_imagen)
|
74 |
+
nombre_archivo = os.path.basename(archivo_imagen)
|
75 |
+
stats = os.stat(archivo_imagen)
|
76 |
+
tamaño_archivo = stats.st_size / 1024
|
77 |
+
fecha_creacion = datetime.fromtimestamp(stats.st_ctime).strftime('%Y-%m-%d %H:%M:%S')
|
78 |
+
fecha_modificacion = datetime.fromtimestamp(stats.st_mtime).strftime('%Y-%m-%d %H:%M:%S')
|
79 |
+
|
80 |
+
info_basica = f"""
|
81 |
+
Información básica de la imagen:
|
82 |
+
Nombre del archivo: {nombre_archivo}
|
83 |
+
Tamaño: {tamaño_archivo:.2f} KB
|
84 |
+
Fecha de creación: {fecha_creacion}
|
85 |
+
Fecha de modificación: {fecha_modificacion}
|
86 |
+
"""
|
87 |
+
|
88 |
+
metadatos = obtener_metadatos(imagen)
|
89 |
+
ela_imagen = realizar_ela(imagen)
|
90 |
+
manipulada, razones = analizar_manipulacion(imagen, metadatos, ela_imagen)
|
91 |
+
|
92 |
+
info_manipulacion = "\nAnálisis de manipulación:\n"
|
93 |
+
if manipulada:
|
94 |
+
info_manipulacion += "La imagen ha sido manipulada.\nRazones:\n"
|
95 |
+
for razon in razones:
|
96 |
+
info_manipulacion += f"- {razon}\n"
|
97 |
+
else:
|
98 |
+
info_manipulacion += "La imagen NO ha sido manipulada.\nRazones:\n"
|
99 |
+
for razon in razones:
|
100 |
+
info_manipulacion += f"- {razon}\n"
|
101 |
+
|
102 |
+
if ela_imagen is not None:
|
103 |
+
with tempfile.NamedTemporaryFile(suffix=".png", delete=False) as tmp_file:
|
104 |
+
cv2.imwrite(tmp_file.name, ela_imagen)
|
105 |
+
ela_image_path = tmp_file.name
|
106 |
+
return ela_image_path, info_basica + info_manipulacion
|
107 |
+
else:
|
108 |
+
return None, info_basica + info_manipulacion + "\n(No se generó ELA debido a un error técnico.)"
|
109 |
|
110 |
+
except Exception as e:
|
111 |
+
error_msg = f"Error procesando la imagen: {e}"
|
112 |
+
print(error_msg)
|
113 |
+
return None, error_msg
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
114 |
|
115 |
# Interfaz de GRADIO
|
116 |
iface = gr.Interface(
|
117 |
fn=procesar_imagen,
|
118 |
inputs=gr.Image(type="filepath", label="Sube una imagen"),
|
119 |
outputs=[gr.Image(label="Error Level Analysis (ELA)"), gr.Textbox(label="Resultado del análisis")],
|
120 |
+
title="Análisis de Manipulación de Imágenes",
|
121 |
+
description="Sube una imagen para analizar posibles manipulaciones usando metadatos y ELA."
|
|
|
|
|
122 |
)
|
123 |
|
124 |
+
# Lanzar la interfaz
|
125 |
iface.launch()
|