leonett commited on
Commit
9f2a016
·
verified ·
1 Parent(s): b3ef723

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +155 -49
app.py CHANGED
@@ -1,14 +1,14 @@
 
1
  import cv2
2
  import numpy as np
3
  from PIL import Image
4
  from PIL.ExifTags import TAGS, GPSTAGS
5
  import hashlib
6
  import gradio as gr
7
- import matplotlib.pyplot as plt
8
- import tempfile
9
  import os
10
  from datetime import datetime
11
 
 
12
  def obtener_metadatos(imagen):
13
  metadatos = {}
14
  try:
@@ -21,9 +21,11 @@ def obtener_metadatos(imagen):
21
  print(f"Error al obtener metadatos: {e}")
22
  return metadatos
23
 
 
24
  def obtener_coordenadas(exif_data):
25
  if not exif_data:
26
  return None
 
27
  gps_info = exif_data.get("GPSInfo", {})
28
  if not gps_info:
29
  return None
@@ -52,10 +54,12 @@ def obtener_coordenadas(exif_data):
52
 
53
  return None
54
 
 
55
  def calcular_hash(imagen):
56
  imagen_bytes = imagen.tobytes()
57
  return hashlib.md5(imagen_bytes).hexdigest()
58
 
 
59
  def analizar_manipulacion(imagen, metadatos):
60
  manipulada = False
61
  razones = []
@@ -80,17 +84,23 @@ def analizar_manipulacion(imagen, metadatos):
80
 
81
  return manipulada, razones
82
 
83
- def realizar_ela(imagen, quality=95, scale=100):
 
84
  imagen_cv = cv2.cvtColor(np.array(imagen), cv2.COLOR_RGB2BGR)
85
- temp_path = "temp_image.jpg"
86
- cv2.imwrite(temp_path, imagen_cv, [cv2.IMWRITE_JPEG_QUALITY, quality])
87
- imagen_comprimida = cv2.imread(temp_path)
88
  diferencia = cv2.absdiff(imagen_cv, imagen_comprimida)
89
- ela_imagen = scale * diferencia
90
- ela_imagen = cv2.cvtColor(ela_imagen, cv2.COLOR_BGR2GRAY)
91
- os.remove(temp_path)
 
 
 
 
92
  return ela_imagen
93
 
 
94
  def procesar_imagen(archivo_imagen):
95
  try:
96
  imagen = Image.open(archivo_imagen)
@@ -99,81 +109,177 @@ def procesar_imagen(archivo_imagen):
99
 
100
  nombre_archivo = os.path.basename(archivo_imagen)
101
  info_basica = f"""
102
- Información básica de la imagen:
103
- Nombre del archivo: {nombre_archivo}
104
- Formato: {imagen.format}
105
- Tamaño: {imagen.size} píxeles
106
- Modo: {imagen.mode}
 
107
  """
 
108
  stats = os.stat(archivo_imagen)
109
  tamaño_archivo = stats.st_size / 1024
110
  fecha_creacion = datetime.fromtimestamp(stats.st_ctime).strftime('%Y-%m-%d %H:%M:%S')
111
  fecha_modificacion = datetime.fromtimestamp(stats.st_mtime).strftime('%Y-%m-%d %H:%M:%S')
112
 
113
  info_basica += f"""
114
- Tamaño del archivo: {tamaño_archivo:.2f} KB
115
- Fecha de creación: {fecha_creacion}
116
- Fecha de modificación: {fecha_modificacion}
117
  """
118
 
119
  with open(archivo_imagen, "rb") as f:
120
  primeros_10_bytes = f.read(10)
121
  header_hex = " ".join(f"{byte:02x}" for byte in primeros_10_bytes)
122
-
123
- info_basica += f"\nPrimeros 10 bytes del archivo (hex): {header_hex}\n"
 
 
124
 
125
  metadatos = obtener_metadatos(imagen)
126
- info_metadatos = "\n---\nANÁLISIS FORENSE DE METADATOS:\n"
 
 
127
  if metadatos:
128
  for tag, value in metadatos.items():
129
  if tag == "DateTime":
130
- info_metadatos += f"Fecha y hora de la captura: {value}\n"
131
  elif tag == "Make":
132
- info_metadatos += f"Fabricante de la cámara: {value}\n"
133
  elif tag == "Model":
134
- info_metadatos += f"Modelo de la cámara: {value}\n"
135
  elif tag == "Software":
136
- info_metadatos += f"Software utilizado para editar la imagen: {value}\n"
137
  elif tag == "ExifImageWidth":
138
- info_metadatos += f"Ancho de la imagen: {value} píxeles\n"
139
  elif tag == "ExifImageHeight":
140
- info_metadatos += f"Alto de la imagen: {value} píxeles\n"
141
  elif tag == "GPSInfo":
 
142
  coordenadas = obtener_coordenadas(metadatos)
143
  if coordenadas:
144
  latitud, longitud = coordenadas
145
- enlace_google = f"https://www.google.com/maps?q={latitud},{longitud}"
146
- info_metadatos += f"Coordenadas GPS: {latitud}, {longitud}\nGoogle Maps: {enlace_google}\n"
 
147
  else:
148
- info_metadatos += "No se encontraron coordenadas GPS.\n"
149
- else:
150
- info_metadatos += f"{tag}: {value}\n"
151
  else:
152
- info_metadatos += "No se encontraron metadatos.\n"
 
153
 
154
  manipulada, razones = analizar_manipulacion(imagen, metadatos)
155
- info_manipulacion = "\n---\nAnálisis de manipulación:\n"
 
 
156
  if manipulada:
157
- info_manipulacion += "La imagen ha sido manipulada.\nRazones:\n"
 
158
  for razon in razones:
159
- info_manipulacion += f"- {razon}\n"
 
160
  else:
161
- info_manipulacion += "La imagen NO ha sido manipulada.\n"
 
162
 
163
  ela_imagen = realizar_ela(imagen)
 
 
164
 
165
- with tempfile.NamedTemporaryFile(suffix=".png", delete=False) as tmp_file:
166
- cv2.imwrite(tmp_file.name, ela_imagen)
167
- ela_image_path = tmp_file.name
168
-
169
- return ela_image_path, info_basica + info_metadatos + info_manipulacion
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
170
 
171
- iface = gr.Interface(
172
- fn=procesar_imagen,
173
- inputs=gr.Image(type="filepath", label="Sube una imagen"),
174
- outputs=[gr.Image(label="Imagen con ELA"), gr.Textbox(label="Informe Forense")],
175
- title="Análisis de ELA y Metadatos en Imágenes Digitales",
176
- description="Herramienta forense para verificar autenticidad de imágenes. Desarrollado por José R. Leonett - [forensedigital.gt](https://forensedigital.gt)"
177
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
178
 
179
- iface.launch()
 
 
 
1
+ # -*- coding: utf-8 -*-
2
  import cv2
3
  import numpy as np
4
  from PIL import Image
5
  from PIL.ExifTags import TAGS, GPSTAGS
6
  import hashlib
7
  import gradio as gr
 
 
8
  import os
9
  from datetime import datetime
10
 
11
+ # Función para obtener metadatos
12
  def obtener_metadatos(imagen):
13
  metadatos = {}
14
  try:
 
21
  print(f"Error al obtener metadatos: {e}")
22
  return metadatos
23
 
24
+ # Función para obtener coordenadas GPS
25
  def obtener_coordenadas(exif_data):
26
  if not exif_data:
27
  return None
28
+
29
  gps_info = exif_data.get("GPSInfo", {})
30
  if not gps_info:
31
  return None
 
54
 
55
  return None
56
 
57
+ # Función para calcular hash
58
  def calcular_hash(imagen):
59
  imagen_bytes = imagen.tobytes()
60
  return hashlib.md5(imagen_bytes).hexdigest()
61
 
62
+ # Función para analizar manipulación
63
  def analizar_manipulacion(imagen, metadatos):
64
  manipulada = False
65
  razones = []
 
84
 
85
  return manipulada, razones
86
 
87
+ # Función para realizar ELA
88
+ def realizar_ela(imagen, quality=95, scale=15):
89
  imagen_cv = cv2.cvtColor(np.array(imagen), cv2.COLOR_RGB2BGR)
90
+ _, buffer = cv2.imencode(".jpg", imagen_cv, [cv2.IMWRITE_JPEG_QUALITY, quality])
91
+ imagen_comprimida = cv2.imdecode(buffer, cv2.IMREAD_COLOR)
92
+
93
  diferencia = cv2.absdiff(imagen_cv, imagen_comprimida)
94
+ diferencia = np.max(diferencia, axis=2)
95
+ diferencia = scale * diferencia
96
+ diferencia = np.clip(diferencia, 0, 255).astype(np.uint8)
97
+
98
+ ela_imagen = cv2.applyColorMap(diferencia, cv2.COLORMAP_HOT)
99
+ ela_imagen = cv2.cvtColor(ela_imagen, cv2.COLOR_BGR2RGB)
100
+
101
  return ela_imagen
102
 
103
+ # Función principal de procesamiento
104
  def procesar_imagen(archivo_imagen):
105
  try:
106
  imagen = Image.open(archivo_imagen)
 
109
 
110
  nombre_archivo = os.path.basename(archivo_imagen)
111
  info_basica = f"""
112
+ <div style='text-align: left;'>
113
+ <h3>Información básica</h3>
114
+ <b>Nombre del archivo:</b> {nombre_archivo}<br>
115
+ <b>Formato:</b> {imagen.format}<br>
116
+ <b>Tamaño:</b> {imagen.size[0]}x{imagen.size[1]} píxeles<br>
117
+ <b>Modo:</b> {imagen.mode}<br>
118
  """
119
+
120
  stats = os.stat(archivo_imagen)
121
  tamaño_archivo = stats.st_size / 1024
122
  fecha_creacion = datetime.fromtimestamp(stats.st_ctime).strftime('%Y-%m-%d %H:%M:%S')
123
  fecha_modificacion = datetime.fromtimestamp(stats.st_mtime).strftime('%Y-%m-%d %H:%M:%S')
124
 
125
  info_basica += f"""
126
+ <b>Tamaño del archivo:</b> {tamaño_archivo:.2f} KB<br>
127
+ <b>Fecha de creación:</b> {fecha_creacion}<br>
128
+ <b>Fecha de modificación:</b> {fecha_modificacion}<br>
129
  """
130
 
131
  with open(archivo_imagen, "rb") as f:
132
  primeros_10_bytes = f.read(10)
133
  header_hex = " ".join(f"{byte:02x}" for byte in primeros_10_bytes)
134
+ info_basica += f"""
135
+ <b>Primeros 10 bytes (hex):</b> {header_hex}<br>
136
+ </div>
137
+ """
138
 
139
  metadatos = obtener_metadatos(imagen)
140
+ info_metadatos = "<div style='text-align: left;'>"
141
+ info_metadatos += "<h3>ANÁLISIS FORENSE DE METADATOS</h3>"
142
+
143
  if metadatos:
144
  for tag, value in metadatos.items():
145
  if tag == "DateTime":
146
+ info_metadatos += f"<b>Fecha y hora de captura:</b> {value}<br>"
147
  elif tag == "Make":
148
+ info_metadatos += f"<b>Fabricante:</b> {value}<br>"
149
  elif tag == "Model":
150
+ info_metadatos += f"<b>Modelo:</b> {value}<br>"
151
  elif tag == "Software":
152
+ info_metadatos += f"<b>Software de edición:</b> {value}<br>"
153
  elif tag == "ExifImageWidth":
154
+ info_metadatos += f"<b>Ancho EXIF:</b> {value} píxeles<br>"
155
  elif tag == "ExifImageHeight":
156
+ info_metadatos += f"<b>Alto EXIF:</b> {value} píxeles<br>"
157
  elif tag == "GPSInfo":
158
+ info_metadatos += "<b>Información GPS:</b><br>"
159
  coordenadas = obtener_coordenadas(metadatos)
160
  if coordenadas:
161
  latitud, longitud = coordenadas
162
+ enlace_google_maps = f"https://www.google.com/maps?q={latitud},{longitud}"
163
+ info_metadatos += f"- <b>Coordenadas:</b> {latitud}, {longitud}<br>"
164
+ info_metadatos += f"- <b>Enlace Maps:</b> <a href='{enlace_google_maps}' target='_blank'>{enlace_google_maps}</a><br>"
165
  else:
166
+ info_metadatos += "- No se encontraron coordenadas GPS<br>"
 
 
167
  else:
168
+ info_metadatos += "No se encontraron metadatos EXIF<br>"
169
+ info_metadatos += "</div>"
170
 
171
  manipulada, razones = analizar_manipulacion(imagen, metadatos)
172
+ info_manipulacion = "<div style='text-align: left;'>"
173
+ info_manipulacion += "<h3>ANÁLISIS DE MANIPULACIÓN</h3>"
174
+
175
  if manipulada:
176
+ info_manipulacion += "<span style='color: red; font-weight: bold;'>LA IMAGEN HA SIDO MANIPULADA</span><br>"
177
+ info_manipulacion += "<b>Razones:</b><ul>"
178
  for razon in razones:
179
+ info_manipulacion += f"<li>{razon}</li>"
180
+ info_manipulacion += "</ul>"
181
  else:
182
+ info_manipulacion += "<span style='color: green; font-weight: bold;'>NO SE DETECTÓ MANIPULACIÓN</span><br>"
183
+ info_manipulacion += "</div>"
184
 
185
  ela_imagen = realizar_ela(imagen)
186
+
187
+ return ela_imagen, info_basica + info_metadatos + info_manipulacion
188
 
189
+ # CSS personalizado para mejorar el diseño
190
+ css = """
191
+ .centered-image {
192
+ display: block;
193
+ margin-left: auto;
194
+ margin-right: auto;
195
+ max-width: 100%;
196
+ max-height: 400px;
197
+ }
198
+ .panel {
199
+ border: 1px solid #e0e0e0;
200
+ border-radius: 10px;
201
+ padding: 15px;
202
+ margin-bottom: 15px;
203
+ background-color: #f9f9f9;
204
+ }
205
+ .header {
206
+ text-align: center;
207
+ background: linear-gradient(135deg, #6a11cb 0%, #2575fc 100%);
208
+ color: white;
209
+ padding: 20px;
210
+ border-radius: 10px;
211
+ margin-bottom: 20px;
212
+ }
213
+ .footer {
214
+ text-align: center;
215
+ padding: 10px;
216
+ color: #666;
217
+ font-size: 0.9em;
218
+ }
219
+ .analysis-btn {
220
+ background: linear-gradient(135deg, #6a11cb 0%, #2575fc 100%) !important;
221
+ color: white !important;
222
+ border: none !important;
223
+ font-weight: bold !important;
224
+ padding: 12px 24px !important;
225
+ border-radius: 50px !important;
226
+ margin-top: 10px;
227
+ }
228
+ """
229
 
230
+ # Interfaz Gradio con diseño mejorado
231
+ with gr.Blocks(css=css) as demo:
232
+ with gr.Column():
233
+ gr.Markdown("""
234
+ <div class="header">
235
+ <h1>Análisis Forense de Imágenes Digitales</h1>
236
+ <p>Detección de manipulación mediante Error Level Analysis (ELA) y análisis de metadatos</p>
237
+ </div>
238
+ """)
239
+
240
+ with gr.Row():
241
+ with gr.Column(scale=1):
242
+ gr.Markdown("### Imagen Original")
243
+ with gr.Column(elem_classes="panel"):
244
+ input_image = gr.Image(
245
+ type="filepath",
246
+ label="",
247
+ show_label=False,
248
+ height=400,
249
+ elem_classes="centered-image"
250
+ )
251
+ analyze_btn = gr.Button("Analizar Imagen", elem_classes="analysis-btn")
252
+
253
+ with gr.Column(scale=1):
254
+ gr.Markdown("### Análisis ELA")
255
+ with gr.Column(elem_classes="panel"):
256
+ output_ela = gr.Image(
257
+ label="",
258
+ show_label=False,
259
+ height=400,
260
+ elem_classes="centered-image",
261
+ interactive=False
262
+ )
263
+
264
+ with gr.Row():
265
+ with gr.Column():
266
+ gr.Markdown("### Resultados del Análisis")
267
+ with gr.Column(elem_classes="panel"):
268
+ metadata_output = gr.HTML()
269
+
270
+ gr.Markdown("""
271
+ <div class="footer">
272
+ <p>Desarrollado por José R. Leonett | Perito Forense Digital | www.forensedigital.gt</p>
273
+ <p>Este sistema analiza imágenes mediante Error Level Analysis (ELA) para detectar posibles manipulaciones</p>
274
+ </div>
275
+ """)
276
+
277
+ analyze_btn.click(
278
+ fn=procesar_imagen,
279
+ inputs=[input_image],
280
+ outputs=[output_ela, metadata_output]
281
+ )
282
 
283
+ # Iniciar la aplicación
284
+ if __name__ == "__main__":
285
+ demo.launch()