LPX55's picture
Update forensics/ela_hybrid.py
97ac331 verified
# ela_hybrid.py
import numpy as np
import cv2 as cv
from PIL import Image
import matplotlib.pyplot as plt
def compress_jpg(image, quality=75):
"""Compress image using JPEG compression (shared from ela.py)."""
encode_param = [int(cv.IMWRITE_JPEG_QUALITY), quality]
_, buffer = cv.imencode('.jpg', image, encode_param)
return cv.imdecode(buffer, cv.IMREAD_COLOR)
def generate_ela_hybrid(image_path: str, quality: int = 95, scale_factor: int = 150):
"""
Generate a 6-channel hybrid image combining RGB and ELA (3 channels each).
Args:
image_path (str): Path to the input image.
quality (int): JPEG compression quality (1-100).
scale_factor (int): Scale factor for ELA contrast.
Returns:
np.ndarray: 6-channel (RGB + ELA) image with shape (H, W, 6), dtype float32, normalized [0,1]
"""
# Load original image
original = cv.imread(image_path, cv.IMREAD_COLOR)
original = original.astype(np.float32) / 255 # Normalize to [0, 1]
# Compress and reload image
compressed = compress_jpg(original, quality)
compressed = compressed.astype(np.float32) / 255 # Normalize to [0, 1]
# Generate ELA as absolute difference between original and compressed
ela = cv.absdiff(original, compressed)
# Apply scale factor to enhance ELA differences
ela = cv.convertScaleAbs(ela, alpha=scale_factor / 100, beta=0)
ela = ela.astype(np.float32) / 255 # Normalize back to [0, 1]
# Stack RGB and ELA (3 channels each) into 6-channel input
hybrid_image = np.concatenate([original, ela], axis=-1) # Shape: H×W×6
return hybrid_image
def save_hybrid_image(hybrid_array, save_path: str):
"""Save the 6-channel hybrid image as a .npy file for model input."""
np.save(save_path, hybrid_array)
print(f"Saved hybrid ELA image to {save_path}")
def visualize_hybrid(hybrid_array: np.ndarray):
"""Return a list of 2 images: [RGB Image, ELA Map (grayscale)], as PIL Images."""
from PIL import Image
# Extract RGB (3 channels at front)
rgb_image = Image.fromarray((hybrid_array[:, :, :3] * 255).astype(np.uint8))
# Extract ELA channels (last 3)
ela_image = Image.fromarray((hybrid_array[:, :, 3:] * 255).astype(np.uint8))
return [rgb_image, ela_image]
# ela_hybrid.py
def generate_hybrid_ela_func(img_input):
"""
Returns a list of 2 PIL Images: [Original RGB Image, Hybrid ELA Output].
"""
from PIL import Image
import numpy as np
# Convert input to numpy array if needed
if not isinstance(img_input, np.ndarray):
img_input = np.array(img_input) # Ensure numpy format
# Generate ELA
hybrid_array = generate_ela_hybrid(img_input, quality=75, scale_factor=100)
visualizations = visualize_hybrid(hybrid_array)
return list(visualizations) # Returns [RGB PIL Image, ELA PIL Image]
def generate_concatenated_hybrid(img_array):
"""
Concatenate original RGB and ELA map into a single image.
Returns a single PIL Image (side-by-side).
"""
# Extract RGB and ELA channels
original_rgb = Image.fromarray((img_array[:, :, :3] * 255).astype(np.uint8)) # 3 channels
ela_map = Image.fromarray((img_array[:, :, 3:] * 255).astype(np.uint8)) # 3 channels
# Resize to match height
ela_map = ela_map.resize(original_rgb.size)
# Concatenate and return as a single PIL Image
combined = Image.new("RGB", (ela_map.width + original_rgb.width, original_rgb.height)) # Single row
combined.paste(original_rgb, (0, 0))
combined.paste(ela_map, (original_rgb.width, 0))
return combined