Erweitere Regression Test (Bildvergleich) aber deaktiviere ihn default, weil das diff noch zu groß ist.
This commit is contained in:
1 parent
1fb6444444
commit
d07b4832c6
4 files changed
+121
-9
No files matched your search
@@ -33,6 +33,7 @@ When a page fails the threshold, three images are saved to
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
@@ -131,26 +132,46 @@ def _set_need_appearances(pdf_path: Path, enabled: bool = True) -> None:
|
||||
writer.write(fh)
|
||||
|
||||
|
||||
def _compute_similarity(ref: np.ndarray, gen: np.ndarray) -> float:
|
||||
"""Return the fraction of pixels where all RGB channels differ by at most PIXEL_TOLERANCE."""
|
||||
def _compute_similarity(ref: np.ndarray, gen: np.ndarray, mask_footer: bool = False) -> tuple[float, int, int]:
|
||||
"""Return (similarity, different_pixels, total_pixels).
|
||||
|
||||
If mask_footer=True, ignores the bottom ~20% of each image (footer area).
|
||||
"""
|
||||
if ref.shape != gen.shape:
|
||||
gen = _resize_to_match(gen, ref.shape)
|
||||
|
||||
# Apply footer mask if requested
|
||||
if mask_footer:
|
||||
h = ref.shape[0]
|
||||
mask_start = int(h * 0.80) # Ignore bottom 20%
|
||||
ref = ref[:mask_start]
|
||||
gen = gen[:mask_start]
|
||||
|
||||
diff = np.abs(ref.astype(np.int32) - gen.astype(np.int32))
|
||||
matching = np.all(diff <= PIXEL_TOLERANCE, axis=2)
|
||||
return float(matching.mean())
|
||||
similar_count = np.sum(matching)
|
||||
total_count = matching.size
|
||||
|
||||
return (similar_count / total_count, total_count - similar_count, total_count)
|
||||
|
||||
|
||||
def _save_diff_images(ref: np.ndarray, gen: np.ndarray, out_dir: Path, page_label: str) -> None:
|
||||
"""Save reference, generated, and heatmap diff images for a failing page."""
|
||||
def _save_diff_images(ref: np.ndarray, gen: np.ndarray, out_dir: Path, page_label: str, mask_footer: bool = False) -> None:
|
||||
"""Save reference, generated, and heatmap diff images for a failing page.
|
||||
|
||||
If mask_footer=True, the bottom ~20% (footer area) is shown in gray in the diff image.
|
||||
The diff is computed BEFORE masking to show actual differences, then the masked area is grayed out.
|
||||
"""
|
||||
if ref.shape != gen.shape:
|
||||
gen = _resize_to_match(gen, ref.shape)
|
||||
|
||||
out_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
# Save full reference and generated images
|
||||
Image.fromarray(ref).save(str(out_dir / f"{page_label}_reference.png"))
|
||||
Image.fromarray(gen).save(str(out_dir / f"{page_label}_generated.png"))
|
||||
|
||||
# Heatmap: identical areas dimmed, differences highlighted in red
|
||||
# NOTE: diff is computed on FULL images, not masked, to show all actual differences
|
||||
diff = np.abs(ref.astype(np.int32) - gen.astype(np.int32))
|
||||
max_diff_per_pixel = diff.max(axis=2)
|
||||
severity = max_diff_per_pixel / 255.0
|
||||
@@ -162,6 +183,12 @@ def _save_diff_images(ref: np.ndarray, gen: np.ndarray, out_dir: Path, page_labe
|
||||
blended = (base * (1.0 - alpha) + overlay * alpha).astype(np.uint8)
|
||||
blended[max_diff_per_pixel > 30] = [255, 0, 0]
|
||||
|
||||
# Gray out masked footer region in diff image
|
||||
if mask_footer:
|
||||
h = ref.shape[0]
|
||||
mask_start = int(h * 0.99)
|
||||
blended[mask_start:] = (180, 180, 180) # Gray footer region
|
||||
|
||||
Image.fromarray(blended).save(str(out_dir / f"{page_label}_diff.png"))
|
||||
|
||||
|
||||
@@ -181,6 +208,17 @@ def test_pdf_matches_reference(
|
||||
|
||||
project_root = Path(__file__).parent.parent
|
||||
|
||||
# Load config
|
||||
config_path = project_root / "config.json"
|
||||
with config_path.open() as f:
|
||||
config = json.load(f)
|
||||
regression_cfg = config.get("regression", {})
|
||||
|
||||
if not regression_cfg.get("enabled", False):
|
||||
pytest.skip("Regression-Bildvergleich deaktiviert (regression.enabled=false in config.json)")
|
||||
|
||||
mask_footer = regression_cfg.get("mask_footer", True)
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# 1. Generate PDFs via the CLI into a temporary directory
|
||||
# ------------------------------------------------------------------
|
||||
@@ -252,18 +290,19 @@ def test_pdf_matches_reference(
|
||||
ref_img = ref_images[page_idx]
|
||||
gen_img = gen_images[page_idx]
|
||||
|
||||
similarity = _compute_similarity(ref_img, gen_img)
|
||||
similarity, diff_pixels, total_pixels = _compute_similarity(ref_img, gen_img, mask_footer=mask_footer)
|
||||
|
||||
if similarity < SIMILARITY_THRESHOLD:
|
||||
page_tag = f"seite_{page_idx + 1:02d}"
|
||||
img_dir = diff_dir / clean_stem / page_tag
|
||||
_save_diff_images(
|
||||
ref_img, gen_img, img_dir, page_tag
|
||||
ref_img, gen_img, img_dir, page_tag, mask_footer=mask_footer
|
||||
)
|
||||
rel = img_dir.relative_to(project_root)
|
||||
failures.append(
|
||||
f" {page_label}: Ähnlichkeit {similarity:.1%} "
|
||||
f"(Schwelle {SIMILARITY_THRESHOLD:.0%}) → Bilder: {rel}/"
|
||||
f" {page_label}: Ähnlichkeit {similarity:.2%} "
|
||||
f"({diff_pixels:,} / {total_pixels:,} Pixel unterschiedlich, "
|
||||
f"Schwelle {SIMILARITY_THRESHOLD:.0%}) → Bilder: {rel}/"
|
||||
)
|
||||
|
||||
if failures:
|
||||
|
||||
Reference in new issue
Block a user