Color Remapping

This commit is contained in:
Zeecka
2026-01-14 21:22:41 +01:00
parent 828ab62c5c
commit 04c332900f
3 changed files with 113 additions and 6 deletions
+102
View File
@@ -0,0 +1,102 @@
# flake8: noqa: E203,E501,W503
# pylint: disable=C0413,W0718,R0903,R0801,W0612
# mypy: disable-error-code=unused-awaitable
"""Color Remapping Analyzer for Image Submissions."""
from pathlib import Path
from typing import Any, Optional
import numpy as np
from PIL import Image
from .base_analyzer import SubprocessAnalyzer
class ColorRemappingAnalyzer(SubprocessAnalyzer):
"""Analyzer for color remapping."""
def __init__(self, input_img: Path, output_dir: Path) -> None:
super().__init__("color_remapping", input_img, output_dir)
def get_results(self, _: Optional[str] = None) -> dict[str, Any]:
"""Analyze an image submission using color remapping."""
img = Image.open(self.input_img)
converted = False
if img.mode == "P":
img = img.convert("RGB")
converted = True
img_np = np.array(img)
# Handle grayscale, RGB, RGBA, etc.
if len(img_np.shape) == 2:
channels = 1
img_np = np.expand_dims(img_np, axis=-1)
else:
channels = img_np.shape[2]
# Ensure we have at least 3 channels (RGB)
if channels < 3:
# Grayscale to RGB
if channels == 1:
img_np = np.repeat(img_np, 3, axis=2)
# Add alpha channel if needed
if channels == 3:
alpha = np.full((img_np.shape[0], img_np.shape[1], 1), 255, dtype=np.uint8)
img_np = np.concatenate([img_np, alpha], axis=2)
elif channels == 4:
# RGBA is fine
pass
else:
# More than 4 channels, take first 3
img_np = img_np[..., :3]
image_json = []
# Generate 8 random color remappings
for i in range(8):
# Create a random permutation of color values (0-255)
# For each original color value, map it to a new random value
color_map = np.random.randint(0, 256, size=256, dtype=np.uint8)
# Apply the color remapping to each channel
remapped = np.zeros_like(img_np)
for c in range(min(3, channels)): # Apply to RGB channels
remapped[..., c] = color_map[img_np[..., c]]
# Keep alpha channel if present
if channels == 4:
remapped[..., 3] = img_np[..., 3]
# Create the remapped image
if channels == 4:
remapped_img = Image.fromarray(remapped.astype(np.uint8), mode="RGBA")
else:
remapped_img = Image.fromarray(remapped[..., :3].astype(np.uint8), mode="RGB")
# Save the remapped image
img_name = f"color_remapping_{i:02d}.png"
out_path = self.output_dir / img_name
dl_path = Path(self.output_dir.name) / img_name
image_json.append("/image/" + str(dl_path))
remapped_img.save(out_path)
output = {
"status": "ok",
"images": {
"Color Remapping": image_json,
},
}
if converted:
output["note"] = (
"Image contains a color palette and was converted to RGB for processing."
)
return output
def analyze_color_remapping(input_img: Path, output_dir: Path) -> None:
"""Analyze an image submission using color remapping."""
analyzer = ColorRemappingAnalyzer(input_img, output_dir)
analyzer.analyze()
+4 -1
View File
@@ -1,5 +1,6 @@
const TOOL_ORDER = [
"decomposer",
"color_remapping",
"file",
"exiftool",
"binwalk",
@@ -515,13 +516,14 @@ function parseResult(result) {
// Parse image output
var channels = ["Superimposed", "Red", "Green", "Blue", "Alpha"];
if (Object.keys(result[tool]["images"]).length == 1) {
channels = ["Grayscale"];
channels = Object.keys(result[tool]["images"]);
} else if (Object.keys(result[tool]["images"]).length == 4) {
channels = ["Superimposed", "Red", "Green", "Blue"];
}
for (const channel of channels) {
const images = result[tool]["images"][channel];
if (images) {
analyzer.innerHTML += `<h3>${capitalize(escapeHtml(channel))}</h3>`;
for (const image of images) {
analyzer.innerHTML += `<div class='results_img'><img src='${escapeHtml(
@@ -530,6 +532,7 @@ function parseResult(result) {
}
}
}
}
if ("image" in result[tool]) {
// Parse image output
+2
View File
@@ -14,6 +14,7 @@ from .utils.sentry import initialize_sentry
initialize_sentry()
from .analyzers.binwalk import analyze_binwalk # pylint: disable=C0413
from .analyzers.color_remapping import analyze_color_remapping
from .analyzers.decomposer import analyze_decomposer
from .analyzers.exiftool import analyze_exiftool
from .analyzers.file import analyze_file
@@ -103,6 +104,7 @@ def analyze_image(submission_hash: str) -> None:
analyzers = [
(analyze_binwalk, img_path, result_path),
(analyze_color_remapping, img_path, result_path),
(analyze_decomposer, img_path, result_path),
(analyze_exiftool, img_path, result_path),
(analyze_file, img_path, result_path),