M3 β€” Dual-Branch CNN for Image Forgery Detection

Classifies an image as Authentic or Forged by fusing raw RGB pixels (frozen ResNet50) with an Error Level Analysis (ELA) map (custom CNN) that amplifies JPEG compression inconsistencies left by tampering.

  • For: NED University β€” PG Diploma in Generative AI (Deep Learning)
  • By: Mr. Salman Zaman and Mr. Muhammad Usama Alam Β· Coordinator: Mr. Sajid Majeed
  • File: M3_best_v2.h5 (~98 MB, full Keras 3 model) Β· License: MIT
  • Demo: salmanzaman777/digital-image-forgery-detector

Architecture Β· two inputs [rgb 224Γ—224Γ—3, ela 224Γ—224Γ—3]

  • RGB: frozen ResNet50 (ImageNet) β†’ GlobalAvgPool β†’ 2048-d
  • ELA: 3Γ— (Conv2D + BN + MaxPool, 32β†’64β†’128) β†’ GlobalAvgPool β†’ 128-d
  • Head: Concatenate (2176-d) β†’ Dense(256, ReLU) β†’ Dropout(0.5) β†’ Dense(1, Sigmoid) Β· ~24.24M params / ~652K trainable

Training & Evaluation

CASIA v2 (7,492 authentic + 5,124 tampered; 70/15/15 stratified split, SEED 42), Adam (lr 0.001), BCE loss, batch 32, Colab GPU. Decision threshold = 0.50.

Metric Value Ablation AUC-ROC
AUC-ROC (test) 0.9774 RGB only 0.5822
Test accuracy ~92% ELA only 0.9807
Error rate ~8% Dual (M3) 0.9774

The model is fundamentally ELA-driven (RGB alone β‰ˆ random); ResNet50 adds texture context.

Limitations

OOD: high-quality phone photos (HDR/multi-frame) yield large ELA residuals β†’ often flagged forged. Scope: CASIA-only β€” won't generalise to GANs, deepfakes, or inpainting; ResNet50 stays frozen (no fine-tuning).

Usage

ELA must match training exactly: JPEG q=90 β†’ diff β†’ 15Γ— brightness β†’ JPEG q=75 β†’ tf.image.decode_jpeg β†’ bilinear resize β†’ Γ·255.

import io, numpy as np, tensorflow as tf
from PIL import Image, ImageChops, ImageEnhance
from huggingface_hub import hf_hub_download

IMG = (224, 224)
model = tf.keras.models.load_model(hf_hub_download(
    repo_id="salmanzaman777/digital-image-forgery-detection-model",
    filename="M3_best_v2.h5"), compile=False)

def ela(img, q=90, scale=15):
    img = img.convert("RGB"); buf = io.BytesIO(); img.save(buf, "JPEG", quality=q); buf.seek(0)
    d = ImageEnhance.Brightness(ImageChops.difference(img, Image.open(buf).convert("RGB"))).enhance(scale)
    out = io.BytesIO(); d.save(out, "JPEG", quality=75)
    x = tf.image.resize(tf.image.decode_jpeg(out.getvalue(), channels=3), IMG)
    return (tf.cast(x, tf.float32) / 255.0).numpy()

image = Image.open("test.jpg").convert("RGB")
rgb = np.array(image.resize(IMG, Image.LANCZOS), np.float32)[np.newaxis] / 255.0
pred = float(model.predict([rgb, ela(image)[np.newaxis]], verbose=0)[0][0])
print("FORGED" if pred > 0.5 else "AUTHENTIC", f"(score={pred:.4f})")
Downloads last month
-
Inference Providers NEW
This model isn't deployed by any Inference Provider. πŸ™‹ Ask for provider support

Space using salmanzaman777/digital-image-forgery-detection-model 1