leonett commited on
Commit
d4a2b4e
·
1 Parent(s): 2169c29

Agregué app.py y requirements.txt

Browse files
Files changed (2) hide show
  1. app.py +233 -0
  2. requirements.txt +5 -0
app.py ADDED
@@ -0,0 +1,233 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # -*- coding: utf-8 -*-
2
+
3
+ import cv2
4
+ import numpy as np
5
+ from PIL import Image
6
+ from PIL.ExifTags import TAGS, GPSTAGS
7
+ import hashlib
8
+ import gradio as gr
9
+ import matplotlib.pyplot as plt
10
+ import tempfile
11
+ import os
12
+ from datetime import datetime
13
+
14
+ def obtener_metadatos(imagen):
15
+ """
16
+ Obtiene los metadatos de la imagen.
17
+ """
18
+ metadatos = {}
19
+ try:
20
+ exif_data = imagen._getexif()
21
+ if exif_data:
22
+ for tag_id, value in exif_data.items():
23
+ tag = TAGS.get(tag_id, tag_id)
24
+ metadatos[tag] = value
25
+ except Exception as e:
26
+ print(f"Error al obtener metadatos: {e}")
27
+ return metadatos
28
+
29
+ def obtener_coordenadas(exif_data):
30
+ """
31
+ Obtiene las coordenadas GPS de los metadatos EXIF.
32
+ """
33
+ if not exif_data:
34
+ return None
35
+
36
+ gps_info = exif_data.get("GPSInfo", {})
37
+ if not gps_info:
38
+ return None
39
+
40
+ # Convertir las coordenadas GPS a grados decimales
41
+ def gps_to_degrees(value):
42
+ d, m, s = value
43
+ return d + (m / 60.0) + (s / 3600.0)
44
+
45
+ try:
46
+ latitud = gps_info.get(2) # Latitud
47
+ longitud = gps_info.get(4) # Longitud
48
+ latitud_ref = gps_info.get(1) # Norte/Sur
49
+ longitud_ref = gps_info.get(3) # Este/Oeste
50
+
51
+ if latitud and longitud and latitud_ref and longitud_ref:
52
+ latitud = gps_to_degrees(latitud)
53
+ longitud = gps_to_degrees(longitud)
54
+ if latitud_ref == "S": # Si es Sur, convertir a negativo
55
+ latitud = -latitud
56
+ if longitud_ref == "W": # Si es Oeste, convertir a negativo
57
+ longitud = -longitud
58
+ return latitud, longitud
59
+ except Exception as e:
60
+ print(f"Error al procesar coordenadas GPS: {e}")
61
+ return None
62
+
63
+ return None
64
+
65
+ def calcular_hash(imagen):
66
+ """
67
+ Calcula el hash MD5 de la imagen para verificar su integridad.
68
+ """
69
+ imagen_bytes = imagen.tobytes()
70
+ return hashlib.md5(imagen_bytes).hexdigest()
71
+
72
+ def analizar_manipulacion(imagen, metadatos):
73
+ """
74
+ Analiza si la imagen ha sido manipulada.
75
+ """
76
+ manipulada = False
77
+ razones = []
78
+
79
+ # Verificar si la imagen tiene marcas de agua
80
+ if imagen.mode == "P":
81
+ razones.append("La imagen tiene marcas de agua o es una imagen indexada.")
82
+ manipulada = True
83
+
84
+ # Verificar si los metadatos han sido alterados
85
+ if not metadatos:
86
+ razones.append("La imagen no tiene metadatos EXIF.")
87
+ manipulada = True
88
+ else:
89
+ if "Software" in metadatos:
90
+ razones.append(f"La imagen fue editada con: {metadatos['Software']}")
91
+ manipulada = True
92
+
93
+ # Verificar si el hash de la imagen coincide con un hash conocido (simulación)
94
+ hash_conocido = "1a79a4d60de6718e8e5b326e338ae533" # Hash de ejemplo
95
+ hash_actual = calcular_hash(imagen)
96
+ if hash_actual != hash_conocido:
97
+ razones.append("El hash de la imagen no coincide con el hash conocido.")
98
+ manipulada = True
99
+
100
+ return manipulada, razones
101
+
102
+ def realizar_ela(imagen, quality=95, scale=100):
103
+ """
104
+ Realiza el Error Level Analysis (ELA) en la imagen utilizando OpenCV.
105
+ """
106
+ # Convertir la imagen a formato compatible con OpenCV
107
+ imagen_cv = cv2.cvtColor(np.array(imagen), cv2.COLOR_RGB2BGR)
108
+
109
+ # Guardar la imagen comprimida
110
+ temp_path = "temp_image.jpg"
111
+ cv2.imwrite(temp_path, imagen_cv, [cv2.IMWRITE_JPEG_QUALITY, quality])
112
+
113
+ # Leer la imagen comprimida
114
+ imagen_comprimida = cv2.imread(temp_path)
115
+
116
+ # Calcular la diferencia absoluta entre la imagen original y la comprimida
117
+ diferencia = cv2.absdiff(imagen_cv, imagen_comprimida)
118
+
119
+ # Escalar la diferencia para resaltar las áreas modificadas
120
+ ela_imagen = scale * diferencia
121
+
122
+ # Convertir a escala de grises para mejorar la visualización
123
+ ela_imagen = cv2.cvtColor(ela_imagen, cv2.COLOR_BGR2GRAY)
124
+
125
+ # Eliminar el archivo temporal
126
+ os.remove(temp_path)
127
+
128
+ return ela_imagen
129
+
130
+ def procesar_imagen(archivo_imagen):
131
+ # Cargar la imagen
132
+ try:
133
+ imagen = Image.open(archivo_imagen)
134
+ except Exception as e:
135
+ return f"Error al cargar la imagen: {e}", None
136
+
137
+ # Obtener información básica de la imagen
138
+ nombre_archivo = os.path.basename(archivo_imagen)
139
+ info_basica = f"""
140
+ Información básica de la imagen:
141
+ Nombre del archivo: {nombre_archivo}
142
+ Formato: {imagen.format}
143
+ Tamaño: {imagen.size} píxeles
144
+ Modo: {imagen.mode}
145
+ """
146
+
147
+ # Obtener tamaño del archivo, fecha de creación y modificación
148
+ stats = os.stat(archivo_imagen)
149
+ tamaño_archivo = stats.st_size / 1024 # Tamaño en KB
150
+ fecha_creacion = datetime.fromtimestamp(stats.st_ctime).strftime('%Y-%m-%d %H:%M:%S')
151
+ fecha_modificacion = datetime.fromtimestamp(stats.st_mtime).strftime('%Y-%m-%d %H:%M:%S')
152
+
153
+ info_basica += f"""
154
+ Tamaño del archivo: {tamaño_archivo:.2f} KB
155
+ Fecha de creación: {fecha_creacion}
156
+ Fecha de modificación: {fecha_modificacion}
157
+ """
158
+
159
+ # Obtener los primeros 10 bytes del archivo en formato hexadecimal
160
+ with open(archivo_imagen, "rb") as f:
161
+ primeros_10_bytes = f.read(10)
162
+ header_hex = " ".join(f"{byte:02x}" for byte in primeros_10_bytes)
163
+
164
+ info_basica += f"""
165
+ Primeros 10 bytes del archivo (hex): {header_hex}
166
+ """
167
+
168
+ # Obtener metadatos
169
+ metadatos = obtener_metadatos(imagen)
170
+ info_metadatos = "\n" + "-" * 50 + "\nANÁLISIS FORENSE DE LOS METADATOS DE LA IMAGEN:\n"
171
+ if metadatos:
172
+ for tag, value in metadatos.items():
173
+ if tag == "DateTime":
174
+ info_metadatos += f"Fecha y hora de la captura: {value}\n"
175
+ elif tag == "Make":
176
+ info_metadatos += f"Fabricante de la cámara: {value}\n"
177
+ elif tag == "Model":
178
+ info_metadatos += f"Modelo de la cámara: {value}\n"
179
+ elif tag == "Software":
180
+ info_metadatos += f"Software utilizado para editar la imagen: {value}\n"
181
+ elif tag == "ExifImageWidth":
182
+ info_metadatos += f"Ancho de la imagen: {value} píxeles\n"
183
+ elif tag == "ExifImageHeight":
184
+ info_metadatos += f"Alto de la imagen: {value} píxeles\n"
185
+ elif tag == "GPSInfo":
186
+ info_metadatos += "Información de ubicación GPS:\n"
187
+ coordenadas = obtener_coordenadas(metadatos)
188
+ if coordenadas:
189
+ latitud, longitud = coordenadas
190
+ enlace_google_maps = f"https://www.google.com/maps?q={latitud},{longitud}"
191
+ info_metadatos += f"- Coordenadas GPS: {latitud}, {longitud}\n"
192
+ info_metadatos += f"- Enlace a Google Maps: {enlace_google_maps}\n"
193
+ else:
194
+ info_metadatos += "- No se encontraron coordenadas GPS.\n"
195
+ else:
196
+ info_metadatos += f"{tag}: {value}\n"
197
+ else:
198
+ info_metadatos += "No se encontraron metadatos.\n"
199
+
200
+ # Analizar si la imagen ha sido manipulada
201
+ manipulada, razones = analizar_manipulacion(imagen, metadatos)
202
+ info_manipulacion = "\nAnálisis de manipulación:\n"
203
+ if manipulada:
204
+ info_manipulacion += "La imagen ha sido manipulada.\n"
205
+ info_manipulacion += "Razones:\n"
206
+ for razon in razones:
207
+ info_manipulacion += f"- {razon}\n"
208
+ else:
209
+ info_manipulacion += "La imagen NO ha sido manipulada.\n"
210
+
211
+ # Realizar Error Level Analysis (ELA)
212
+ ela_imagen = realizar_ela(imagen)
213
+
214
+ # Guardar la imagen ELA en un archivo temporal
215
+ with tempfile.NamedTemporaryFile(suffix=".png", delete=False) as tmp_file:
216
+ cv2.imwrite(tmp_file.name, ela_imagen)
217
+ ela_image_path = tmp_file.name
218
+
219
+ return ela_image_path, info_basica + info_metadatos + info_manipulacion
220
+
221
+ # Interfaz de GRADIO
222
+ iface = gr.Interface(
223
+ fn=procesar_imagen,
224
+ inputs=gr.Image(type="filepath", label="Sube una imagen"),
225
+ outputs=[gr.Image(label="Error Level Analysis (ELA)"), gr.Textbox(label="Resultado del análisis")],
226
+ title="Análisis de metadatos y Error de ELA en imágenes digitales con Gradio",
227
+ description="""<div style="text-align: center;">
228
+ <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) y fue desarrollado por José R. Leonett para los peritos forenses digitales de Guatemala <a href="http://www.forensedigital.gt">www.forensedigital.gt</a>, analistas forenses o cualquier persona interesada en verificar la autenticidad de imágenes digitales.</p>
229
+ </div>"""
230
+ )
231
+
232
+ # Lanzar la interfaz de Gradio
233
+ iface.launch()
requirements.txt ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ gradio
2
+ pillow
3
+ opencv-python
4
+ numpy
5
+ matplotlib