|
|
|
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] |
|
""" |
|
|
|
original = cv.imread(image_path, cv.IMREAD_COLOR) |
|
original = original.astype(np.float32) / 255 |
|
|
|
|
|
compressed = compress_jpg(original, quality) |
|
compressed = compressed.astype(np.float32) / 255 |
|
|
|
|
|
ela = cv.absdiff(original, compressed) |
|
|
|
|
|
ela = cv.convertScaleAbs(ela, alpha=scale_factor / 100, beta=0) |
|
ela = ela.astype(np.float32) / 255 |
|
|
|
|
|
hybrid_image = np.concatenate([original, ela], axis=-1) |
|
|
|
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 |
|
|
|
|
|
rgb_image = Image.fromarray((hybrid_array[:, :, :3] * 255).astype(np.uint8)) |
|
|
|
|
|
ela_image = Image.fromarray((hybrid_array[:, :, 3:] * 255).astype(np.uint8)) |
|
|
|
return [rgb_image, ela_image] |
|
|
|
|
|
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 |
|
|
|
|
|
if not isinstance(img_input, np.ndarray): |
|
img_input = np.array(img_input) |
|
|
|
|
|
hybrid_array = generate_ela_hybrid(img_input, quality=75, scale_factor=100) |
|
visualizations = visualize_hybrid(hybrid_array) |
|
return list(visualizations) |
|
|
|
|
|
def generate_concatenated_hybrid(img_array): |
|
""" |
|
Concatenate original RGB and ELA map into a single image. |
|
Returns a single PIL Image (side-by-side). |
|
""" |
|
|
|
original_rgb = Image.fromarray((img_array[:, :, :3] * 255).astype(np.uint8)) |
|
ela_map = Image.fromarray((img_array[:, :, 3:] * 255).astype(np.uint8)) |
|
|
|
|
|
ela_map = ela_map.resize(original_rgb.size) |
|
|
|
|
|
combined = Image.new("RGB", (ela_map.width + original_rgb.width, original_rgb.height)) |
|
combined.paste(original_rgb, (0, 0)) |
|
combined.paste(ela_map, (original_rgb.width, 0)) |
|
return combined |