leonett commited on
Commit
3f143c8
·
verified ·
1 Parent(s): b508a69

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +57 -41
app.py CHANGED
@@ -84,12 +84,17 @@ def analizar_manipulacion(imagen, metadatos):
84
  manipulada = True
85
  return manipulada, razones
86
 
87
- def realizar_ela(imagen, quality=95, scale=15, umbral=30):
88
- """Realiza ELA y resalta SOLO las áreas modificadas con color sobre fondo gris/negro."""
89
  try:
90
  img_np = np.array(imagen.convert("RGB"))
91
  img_cv = cv2.cvtColor(img_np, cv2.COLOR_RGB2BGR)
92
 
 
 
 
 
 
93
  # Guardar y recargar con compresión
94
  temp_path = "/tmp/temp_image.jpg"
95
  cv2.imwrite(temp_path, img_cv, [cv2.IMWRITE_JPEG_QUALITY, quality])
@@ -99,26 +104,30 @@ def realizar_ela(imagen, quality=95, scale=15, umbral=30):
99
 
100
  # Calcular diferencia absoluta
101
  diferencia = cv2.absdiff(img_cv, img_comprimida)
102
- ela_imagen = scale * diferencia
 
 
 
 
 
 
103
 
104
- # Convertir a escala de grises para crear máscara
105
  ela_gray = cv2.cvtColor(ela_imagen, cv2.COLOR_BGR2GRAY)
106
 
107
- # Crear máscara: solo áreas con error > umbral
108
  _, mask = cv2.threshold(ela_gray, umbral, 255, cv2.THRESH_BINARY)
109
  mask = mask.astype(np.uint8)
110
 
111
- # Aplicar mapa de color SOLO a las áreas de la máscara (COLORMAP_HOT = rojo/amarillo)
112
  ela_color = cv2.applyColorMap(ela_gray, cv2.COLORMAP_HOT)
113
 
114
- # Convertir imagen original a escala de grises (fondo)
115
  img_gray = cv2.cvtColor(img_cv, cv2.COLOR_BGR2GRAY)
116
- img_gray = cv2.cvtColor(img_gray, cv2.COLOR_GRAY2BGR) # Volver a 3 canales
 
117
 
118
- # Oscurecer fondo para mayor contraste
119
- img_gray = cv2.convertScaleAbs(img_gray, alpha=0.6, beta=0)
120
-
121
- # Combinar: fondo gris + áreas coloreadas donde la máscara es blanca
122
  result = np.where(mask[..., None] > 0, ela_color, img_gray)
123
 
124
  os.remove(temp_path)
@@ -148,56 +157,64 @@ def procesar_imagen(archivo_imagen):
148
  ela_result = realizar_ela(img)
149
  cv2.imwrite(ela_path, ela_result)
150
 
 
 
 
 
 
 
 
151
  # Información básica
152
- info_basica = f"**Nombre del archivo:** {nombre_original}\n"
153
- info_basica += f"**Formato:** {img.format}\n"
154
- info_basica += f"**Tamaño:** {img.size[0]} x {img.size[1]} píxeles\n"
155
- info_basica += f"**Modo:** {img.mode}\n\n"
 
156
 
157
  metadatos = obtener_metadatos(img)
158
- info_metadatos = "**ANÁLISIS FORENSE DE LOS METADATOS:**\n"
159
 
160
  google_maps_url = None
161
 
162
  if metadatos:
163
  for tag, value in metadatos.items():
164
  if tag == "DateTime":
165
- info_metadatos += f"- Fecha y hora de captura: {value}\n"
166
  elif tag == "Make":
167
- info_metadatos += f"- Fabricante de cámara: {value}\n"
168
  elif tag == "Model":
169
- info_metadatos += f"- Modelo de cámara: {value}\n"
170
  elif tag == "Software":
171
- info_metadatos += f"- Software de edición: {value}\n"
172
  elif tag == "GPSInfo":
173
  coords = obtener_coordenadas(metadatos)
174
  if coords:
175
  lat, lon = coords
176
- info_metadatos += f"- Coordenadas GPS: {lat:.6f}, {lon:.6f}\n"
177
  google_maps_url = f"https://www.google.com/maps?q={lat},{lon}"
178
- info_metadatos += f"- Enlace a Google Maps: {google_maps_url}\n"
179
  else:
180
- info_metadatos += "- No se encontraron coordenadas GPS válidas\n"
181
  else:
182
- info_metadatos += f"- {tag}: {value}\n"
183
  else:
184
- info_metadatos += "- No se encontraron metadatos EXIF\n"
185
 
186
  sha3_hash = calcular_hash(img)
187
- info_metadatos += f"\n- SHA3-256: {sha3_hash}\n\n"
188
 
189
  manipulada, razones = analizar_manipulacion(img, metadatos)
190
- info_manipulacion = "**ANÁLISIS DE MANIPULACIÓN:**\n"
191
  if manipulada:
192
- info_manipulacion += "⚠️ **LA IMAGEN HA SIDO MANIPULADA.**\nRazones:\n"
193
  for r in razones:
194
- info_manipulacion += f"- {r}\n"
195
  else:
196
- info_manipulacion += "✅ **LA IMAGEN NO HA SIDO MANIPULADA.**\n"
197
 
198
  analysis_text = info_basica + info_metadatos + info_manipulacion
199
 
200
- with open(text_path, "w") as f:
201
  f.write(analysis_text)
202
 
203
  with zipfile.ZipFile(zip_path, "w") as zipf:
@@ -227,6 +244,8 @@ with gr.Blocks(title="Análisis Forense de Imágenes con ELA", theme=theme) as d
227
  # 📸 Análisis Forense de Imágenes con Error Level Analysis (ELA)
228
  Programa de computación forense para analizar imágenes en busca de evidencia de manipulación o edición.
229
  """)
 
 
230
 
231
  with gr.Row():
232
  with gr.Column():
@@ -256,11 +275,11 @@ with gr.Blocks(title="Análisis Forense de Imágenes con ELA", theme=theme) as d
256
  # Función para resetear todo al cargar nueva imagen
257
  def reset_on_upload():
258
  return (
259
- gr.update(value=None), # Limpiar texto
260
- gr.update(value=None), # Limpiar imagen ELA
261
- gr.update(visible=False), # Ocultar ZIP
262
- gr.update(visible=False), # Ocultar botón Google Maps
263
- "" # Limpiar URL
264
  )
265
 
266
  # Evento de análisis
@@ -294,9 +313,6 @@ with gr.Blocks(title="Análisis Forense de Imágenes con ELA", theme=theme) as d
294
  outputs=[analysis_text, ela_image, download_zip, google_maps_btn, google_maps_url_state]
295
  )
296
 
297
- # 👇👇👇 LÍNEA DE CRÉDITO (¡correctamente indentada!)
298
- gr.Markdown("Desarrollado por José R. Leonett para el Grupo de Peritos Forenses Digitales de Guatemala - [www.forensedigital.gt](https://www.forensedigital.gt)")
299
-
300
  # ▶️ Ejecución
301
  if __name__ == "__main__":
302
  demo.launch(
@@ -305,4 +321,4 @@ if __name__ == "__main__":
305
  share=True,
306
  inbrowser=True,
307
  favicon_path="https://huggingface.co/datasets/huggingface/logo/resolve/main/hf-logo.png"
308
- )
 
84
  manipulada = True
85
  return manipulada, razones
86
 
87
+ def realizar_ela(imagen):
88
+ """Realiza ELA con parámetros automáticos optimizados para máxima detección forense."""
89
  try:
90
  img_np = np.array(imagen.convert("RGB"))
91
  img_cv = cv2.cvtColor(img_np, cv2.COLOR_RGB2BGR)
92
 
93
+ # ✅ Parámetros automáticos optimizados
94
+ quality = 90 # Compresión media-alta para resaltar diferencias
95
+ scale = 20 # Amplificación del error
96
+ umbral = 25 # Umbral bajo para máxima sensibilidad
97
+
98
  # Guardar y recargar con compresión
99
  temp_path = "/tmp/temp_image.jpg"
100
  cv2.imwrite(temp_path, img_cv, [cv2.IMWRITE_JPEG_QUALITY, quality])
 
104
 
105
  # Calcular diferencia absoluta
106
  diferencia = cv2.absdiff(img_cv, img_comprimida)
107
+ ela_imagen = scale * diferencia.astype(np.float32)
108
+
109
+ # ✅ Normalizar para mejorar contraste automáticamente
110
+ ela_imagen = cv2.normalize(ela_imagen, None, 0, 255, cv2.NORM_MINMAX).astype(np.uint8)
111
+
112
+ # ✅ Suavizado gaussiano para reducir ruido y resaltar bordes reales
113
+ ela_imagen = cv2.GaussianBlur(ela_imagen, (3, 3), 0)
114
 
115
+ # Convertir a escala de grises
116
  ela_gray = cv2.cvtColor(ela_imagen, cv2.COLOR_BGR2GRAY)
117
 
118
+ # Crear máscara binaria
119
  _, mask = cv2.threshold(ela_gray, umbral, 255, cv2.THRESH_BINARY)
120
  mask = mask.astype(np.uint8)
121
 
122
+ # Aplicar mapa de color (rojo/amarillo = alto error)
123
  ela_color = cv2.applyColorMap(ela_gray, cv2.COLORMAP_HOT)
124
 
125
+ # Fondo: imagen original en escala de grises oscurecida
126
  img_gray = cv2.cvtColor(img_cv, cv2.COLOR_BGR2GRAY)
127
+ img_gray = cv2.cvtColor(img_gray, cv2.COLOR_GRAY2BGR)
128
+ img_gray = cv2.convertScaleAbs(img_gray, alpha=0.5, beta=0) # Más oscuro para mayor contraste
129
 
130
+ # Combinar: fondo gris + áreas coloreadas
 
 
 
131
  result = np.where(mask[..., None] > 0, ela_color, img_gray)
132
 
133
  os.remove(temp_path)
 
157
  ela_result = realizar_ela(img)
158
  cv2.imwrite(ela_path, ela_result)
159
 
160
+ # ✅ Obtener tamaño del archivo
161
+ file_size_bytes = os.path.getsize(archivo_imagen)
162
+ if file_size_bytes < 1024 * 1024:
163
+ file_size = f"{file_size_bytes / 1024:.2f} KB"
164
+ else:
165
+ file_size = f"{file_size_bytes / (1024 * 1024):.2f} MB"
166
+
167
  # Información básica
168
+ info_basica = f"**Nombre del archivo:** {nombre_original}\r\n"
169
+ info_basica += f"**Tamaño del archivo:** {file_size}\r\n"
170
+ info_basica += f"**Dimensiones:** {img.size[0]} x {img.size[1]} píxeles\r\n"
171
+ info_basica += f"**Formato:** {img.format}\r\n"
172
+ info_basica += f"**Modo:** {img.mode}\r\n\r\n"
173
 
174
  metadatos = obtener_metadatos(img)
175
+ info_metadatos = "**ANÁLISIS FORENSE DE LOS METADATOS:**\r\n\r\n"
176
 
177
  google_maps_url = None
178
 
179
  if metadatos:
180
  for tag, value in metadatos.items():
181
  if tag == "DateTime":
182
+ info_metadatos += f"- Fecha y hora de captura: {value}\r\n"
183
  elif tag == "Make":
184
+ info_metadatos += f"- Fabricante de cámara: {value}\r\n"
185
  elif tag == "Model":
186
+ info_metadatos += f"- Modelo de cámara: {value}\r\n"
187
  elif tag == "Software":
188
+ info_metadatos += f"- Software de edición: {value}\r\n"
189
  elif tag == "GPSInfo":
190
  coords = obtener_coordenadas(metadatos)
191
  if coords:
192
  lat, lon = coords
193
+ info_metadatos += f"- Coordenadas GPS: {lat:.6f}, {lon:.6f}\r\n"
194
  google_maps_url = f"https://www.google.com/maps?q={lat},{lon}"
195
+ info_metadatos += f"- Enlace a Google Maps: {google_maps_url}\r\n"
196
  else:
197
+ info_metadatos += "- No se encontraron coordenadas GPS válidas\r\n"
198
  else:
199
+ info_metadatos += f"- {tag}: {value}\r\n"
200
  else:
201
+ info_metadatos += "- No se encontraron metadatos EXIF\r\n"
202
 
203
  sha3_hash = calcular_hash(img)
204
+ info_metadatos += f"\r\n- SHA3-256: {sha3_hash}\r\n\r\n"
205
 
206
  manipulada, razones = analizar_manipulacion(img, metadatos)
207
+ info_manipulacion = "**ANÁLISIS DE MANIPULACIÓN:**\r\n\r\n"
208
  if manipulada:
209
+ info_manipulacion += "⚠️ **LA IMAGEN HA SIDO MANIPULADA.**\r\nRazones:\r\n"
210
  for r in razones:
211
+ info_manipulacion += f"- {r}\r\n"
212
  else:
213
+ info_manipulacion += "✅ **LA IMAGEN NO HA SIDO MANIPULADA.**\r\n"
214
 
215
  analysis_text = info_basica + info_metadatos + info_manipulacion
216
 
217
+ with open(text_path, "w", encoding="utf-8", newline="\r\n") as f:
218
  f.write(analysis_text)
219
 
220
  with zipfile.ZipFile(zip_path, "w") as zipf:
 
244
  # 📸 Análisis Forense de Imágenes con Error Level Analysis (ELA)
245
  Programa de computación forense para analizar imágenes en busca de evidencia de manipulación o edición.
246
  """)
247
+ # ✅ Línea de crédito correctamente indentada y con URL limpia
248
+ gr.Markdown("Desarrollado por José R. Leonett para el Grupo de Peritos Forenses Digitales de Guatemala - [www.forensedigital.gt](https://www.forensedigital.gt)")
249
 
250
  with gr.Row():
251
  with gr.Column():
 
275
  # Función para resetear todo al cargar nueva imagen
276
  def reset_on_upload():
277
  return (
278
+ gr.update(value=None),
279
+ gr.update(value=None),
280
+ gr.update(visible=False),
281
+ gr.update(visible=False),
282
+ ""
283
  )
284
 
285
  # Evento de análisis
 
313
  outputs=[analysis_text, ela_image, download_zip, google_maps_btn, google_maps_url_state]
314
  )
315
 
 
 
 
316
  # ▶️ Ejecución
317
  if __name__ == "__main__":
318
  demo.launch(
 
321
  share=True,
322
  inbrowser=True,
323
  favicon_path="https://huggingface.co/datasets/huggingface/logo/resolve/main/hf-logo.png"
324
+ )