Update app.py
Browse files
app.py
CHANGED
|
@@ -85,18 +85,22 @@ def analizar_manipulacion(imagen, metadatos):
|
|
| 85 |
return manipulada, razones
|
| 86 |
|
| 87 |
|
|
|
|
| 88 |
def realizar_ela(imagen):
|
| 89 |
-
"""Realiza ELA con parámetros
|
| 90 |
try:
|
|
|
|
| 91 |
img_np = np.array(imagen.convert("RGB"))
|
| 92 |
img_cv = cv2.cvtColor(img_np, cv2.COLOR_RGB2BGR)
|
| 93 |
|
| 94 |
-
# Parámetros
|
| 95 |
-
quality = 95
|
| 96 |
-
|
| 97 |
-
|
| 98 |
-
|
| 99 |
-
|
|
|
|
|
|
|
| 100 |
temp_path = "/tmp/temp_image.jpg"
|
| 101 |
cv2.imwrite(temp_path, img_cv, [cv2.IMWRITE_JPEG_QUALITY, quality])
|
| 102 |
img_comprimida = cv2.imread(temp_path)
|
|
@@ -104,31 +108,42 @@ def realizar_ela(imagen):
|
|
| 104 |
raise ValueError("Error al leer la imagen comprimida")
|
| 105 |
|
| 106 |
# Calcular diferencia absoluta
|
| 107 |
-
diferencia = cv2.absdiff(img_cv, img_comprimida)
|
| 108 |
-
ela_imagen = scale * diferencia.astype(np.float32)
|
| 109 |
|
| 110 |
-
#
|
| 111 |
-
|
| 112 |
-
|
|
|
|
| 113 |
|
| 114 |
-
#
|
| 115 |
-
|
| 116 |
|
| 117 |
-
# Crear máscara
|
| 118 |
-
|
|
|
|
| 119 |
mask = mask.astype(np.uint8)
|
| 120 |
|
| 121 |
-
# ✅
|
| 122 |
-
|
| 123 |
-
|
| 124 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 125 |
|
| 126 |
# Fondo: imagen original en escala de grises oscurecida
|
| 127 |
img_gray = cv2.cvtColor(img_cv, cv2.COLOR_BGR2GRAY)
|
| 128 |
img_gray = cv2.cvtColor(img_gray, cv2.COLOR_GRAY2BGR)
|
| 129 |
-
img_gray = cv2.convertScaleAbs(img_gray, alpha=0.5, beta=0)
|
| 130 |
|
| 131 |
-
# Combinar:
|
| 132 |
result = np.where(mask[..., None] > 0, ela_color, img_gray)
|
| 133 |
|
| 134 |
os.remove(temp_path)
|
|
@@ -142,6 +157,9 @@ def realizar_ela(imagen):
|
|
| 142 |
|
| 143 |
|
| 144 |
|
|
|
|
|
|
|
|
|
|
| 145 |
def procesar_imagen(archivo_imagen):
|
| 146 |
"""Procesa la imagen y devuelve ZIP + texto de análisis + imagen ELA + URL de Google Maps (si aplica)."""
|
| 147 |
try:
|
|
|
|
| 85 |
return manipulada, razones
|
| 86 |
|
| 87 |
|
| 88 |
+
|
| 89 |
def realizar_ela(imagen):
|
| 90 |
+
"""Realiza ELA con los parámetros y algoritmo exactos del código JavaScript, valores por defecto."""
|
| 91 |
try:
|
| 92 |
+
# Convertir imagen a array numpy y a BGR para OpenCV
|
| 93 |
img_np = np.array(imagen.convert("RGB"))
|
| 94 |
img_cv = cv2.cvtColor(img_np, cv2.COLOR_RGB2BGR)
|
| 95 |
|
| 96 |
+
# ✅ Parámetros por defecto del código JS
|
| 97 |
+
quality = 95 # Calidad JPG (0-100)
|
| 98 |
+
noise_level = 3 # Amplitud del ruido (1-30)
|
| 99 |
+
error_scale = 57 / 100.0 # Escala de error (0-100 → 0.0-1.0)
|
| 100 |
+
brightness_pct = 100 # Brillo (0-150%)
|
| 101 |
+
equalize_histogram = False # Por defecto desactivado
|
| 102 |
+
|
| 103 |
+
# Guardar como JPG con calidad especificada
|
| 104 |
temp_path = "/tmp/temp_image.jpg"
|
| 105 |
cv2.imwrite(temp_path, img_cv, [cv2.IMWRITE_JPEG_QUALITY, quality])
|
| 106 |
img_comprimida = cv2.imread(temp_path)
|
|
|
|
| 108 |
raise ValueError("Error al leer la imagen comprimida")
|
| 109 |
|
| 110 |
# Calcular diferencia absoluta
|
| 111 |
+
diferencia = cv2.absdiff(img_cv.astype(np.float32), img_comprimida.astype(np.float32))
|
|
|
|
| 112 |
|
| 113 |
+
# Aplicar "noiseLevel" y "errorScale" como en el código JS
|
| 114 |
+
# En JS: diff = Math.max(diffR, diffG, diffB) * errorScale * 20;
|
| 115 |
+
# Y cada canal se multiplica por (noiseLevel / 10)
|
| 116 |
+
scaled_diff = diferencia * (noise_level / 10.0) * error_scale * 20.0
|
| 117 |
|
| 118 |
+
# Limitar valores a 0-255 y convertir a uint8
|
| 119 |
+
scaled_diff = np.clip(scaled_diff, 0, 255).astype(np.uint8)
|
| 120 |
|
| 121 |
+
# Crear máscara: áreas con error significativo (simulando elaData.data[i + 3] > 0)
|
| 122 |
+
gray_diff = cv2.cvtColor(scaled_diff, cv2.COLOR_BGR2GRAY)
|
| 123 |
+
_, mask = cv2.threshold(gray_diff, 30, 255, cv2.THRESH_BINARY) # Umbral arbitrario para "diff > 0.3"
|
| 124 |
mask = mask.astype(np.uint8)
|
| 125 |
|
| 126 |
+
# ✅ Aplicar ecualización de histograma si estuviera activada (aunque por defecto es False)
|
| 127 |
+
if equalize_histogram:
|
| 128 |
+
# Convertir a escala de grises, ecualizar, y volver a BGR
|
| 129 |
+
gray = cv2.cvtColor(scaled_diff, cv2.COLOR_BGR2GRAY)
|
| 130 |
+
gray_eq = cv2.equalizeHist(gray)
|
| 131 |
+
scaled_diff = cv2.cvtColor(gray_eq, cv2.COLOR_GRAY2BGR)
|
| 132 |
+
|
| 133 |
+
# ✅ Aplicar brillo (brightness)
|
| 134 |
+
brightness_factor = min(1.5, brightness_pct / 100.0)
|
| 135 |
+
scaled_diff = cv2.convertScaleAbs(scaled_diff, alpha=brightness_factor, beta=0)
|
| 136 |
+
|
| 137 |
+
# ✅ Crear imagen final: fondo negro, áreas detectadas en blanco/amarillo
|
| 138 |
+
# Convertimos la diferencia escalada a una imagen en color
|
| 139 |
+
ela_color = scaled_diff.copy()
|
| 140 |
|
| 141 |
# Fondo: imagen original en escala de grises oscurecida
|
| 142 |
img_gray = cv2.cvtColor(img_cv, cv2.COLOR_BGR2GRAY)
|
| 143 |
img_gray = cv2.cvtColor(img_gray, cv2.COLOR_GRAY2BGR)
|
| 144 |
+
img_gray = cv2.convertScaleAbs(img_gray, alpha=0.5, beta=0) # Fondo oscuro
|
| 145 |
|
| 146 |
+
# Combinar: donde hay máscara, mostrar color; donde no, fondo gris
|
| 147 |
result = np.where(mask[..., None] > 0, ela_color, img_gray)
|
| 148 |
|
| 149 |
os.remove(temp_path)
|
|
|
|
| 157 |
|
| 158 |
|
| 159 |
|
| 160 |
+
|
| 161 |
+
|
| 162 |
+
|
| 163 |
def procesar_imagen(archivo_imagen):
|
| 164 |
"""Procesa la imagen y devuelve ZIP + texto de análisis + imagen ELA + URL de Google Maps (si aplica)."""
|
| 165 |
try:
|