Add AOI hotspot strict audit for 20nm compare
@@ -115,12 +115,18 @@ When changing the primary compare page:
|
|||||||
For the current 20nm visual baseline, prefer:
|
For the current 20nm visual baseline, prefer:
|
||||||
|
|
||||||
- [`navsea_strict_audit.py`](/root/sourceserver/pbf/navsea_strict_audit.py)
|
- [`navsea_strict_audit.py`](/root/sourceserver/pbf/navsea_strict_audit.py)
|
||||||
|
- [`strict_audit_hotspots_20nm.json`](/root/sourceserver/pbf/strict_audit_hotspots_20nm.json)
|
||||||
|
|
||||||
This script combines:
|
This script combines:
|
||||||
|
|
||||||
- browser-rendered screenshot diff from the fixed compare page
|
- browser-rendered screenshot diff from the fixed compare page
|
||||||
- backend render audit summary from the current 20nm audit JSON
|
- backend render audit summary from the current 20nm audit JSON
|
||||||
|
|
||||||
|
And now focuses on:
|
||||||
|
|
||||||
|
- small AOI hotspot crops that are chosen to calibrate backend audit blind spots
|
||||||
|
- not just one coarse full-screen diff number
|
||||||
|
|
||||||
Use it when you need one report that explains both:
|
Use it when you need one report that explains both:
|
||||||
|
|
||||||
- what the user really sees
|
- what the user really sees
|
||||||
|
|||||||
@@ -334,12 +334,23 @@ Remote: `ssh://git@nas:2222/tei/pbf.git`
|
|||||||
本轮已新增统一审计脚本:
|
本轮已新增统一审计脚本:
|
||||||
|
|
||||||
- [`navsea_strict_audit.py`](/root/sourceserver/pbf/navsea_strict_audit.py)
|
- [`navsea_strict_audit.py`](/root/sourceserver/pbf/navsea_strict_audit.py)
|
||||||
|
- [`strict_audit_hotspots_20nm.json`](/root/sourceserver/pbf/strict_audit_hotspots_20nm.json)
|
||||||
|
|
||||||
它会把两部分合并到同一份报告里:
|
它会把两部分合并到同一份报告里:
|
||||||
|
|
||||||
- 浏览器真实出图截图差异
|
- 浏览器真实出图截图差异
|
||||||
- 当前 20nm backend render audit 摘要
|
- 当前 20nm backend render audit 摘要
|
||||||
|
|
||||||
|
但当前实际工作方式已经进一步收紧为:
|
||||||
|
|
||||||
|
- 后台 render audit 是主审计
|
||||||
|
- 小范围 AOI 图像审计是校准审计
|
||||||
|
- 不再只看整屏大 diff
|
||||||
|
- 只盯后台最容易误判的热点区域:
|
||||||
|
- 航标 / 灯塔 / 红绿标识
|
||||||
|
- 鱼礁 / 碍航点
|
||||||
|
- 远处 symbol / 小灯
|
||||||
|
|
||||||
当前产物:
|
当前产物:
|
||||||
|
|
||||||
- [strict_audit_summary.md](/root/sourceserver/pbf/report/strict_audit/strict_audit_summary.md)
|
- [strict_audit_summary.md](/root/sourceserver/pbf/report/strict_audit/strict_audit_summary.md)
|
||||||
@@ -348,25 +359,36 @@ Remote: `ssh://git@nas:2222/tei/pbf.git`
|
|||||||
- [compare_left.png](/root/sourceserver/pbf/report/strict_audit/compare_left.png)
|
- [compare_left.png](/root/sourceserver/pbf/report/strict_audit/compare_left.png)
|
||||||
- [compare_right.png](/root/sourceserver/pbf/report/strict_audit/compare_right.png)
|
- [compare_right.png](/root/sourceserver/pbf/report/strict_audit/compare_right.png)
|
||||||
- [compare_diff.png](/root/sourceserver/pbf/report/strict_audit/compare_diff.png)
|
- [compare_diff.png](/root/sourceserver/pbf/report/strict_audit/compare_diff.png)
|
||||||
|
- [report/strict_audit/hotspots](/root/sourceserver/pbf/report/strict_audit/hotspots)
|
||||||
|
|
||||||
当前 `r7` strict audit 结论:
|
当前 `r7` strict audit 结论:
|
||||||
|
|
||||||
- compare version:
|
- compare version:
|
||||||
- `20nm-r7-20260331-2044`
|
- `20nm-r7-20260331-2044`
|
||||||
- visual changed ratio:
|
- visual changed ratio:
|
||||||
- `0.887847`
|
- `0.899848`
|
||||||
- changed pixels:
|
- changed pixels:
|
||||||
- `411961 / 464000`
|
- `230361 / 256000`
|
||||||
- backend render audit 总体仍是:
|
- backend render audit 总体仍是:
|
||||||
- `extra_in_engineering = 258317`
|
- `extra_in_engineering = 258317`
|
||||||
- `missing_in_engineering = 258317`
|
- `missing_in_engineering = 258317`
|
||||||
|
|
||||||
|
当前热点 AOI 排名:
|
||||||
|
|
||||||
|
1. `southwest_lighthouse_cluster`
|
||||||
|
- changed ratio: `0.972315`
|
||||||
|
2. `takashima_main_harbor_marks`
|
||||||
|
- changed ratio: `0.783759`
|
||||||
|
3. `takashima_inner_nearshore_hazards`
|
||||||
|
- changed ratio: `0.744246`
|
||||||
|
4. `east_breakwater_marks`
|
||||||
|
- changed ratio: `0.617833`
|
||||||
|
|
||||||
这份统一审计的意义是:
|
这份统一审计的意义是:
|
||||||
|
|
||||||
- 以后不再把“视觉变好了”与“后台数字没变”当成互相冲突
|
- 以后不再只靠整屏图像差异判断问题
|
||||||
- 可以一眼看出:
|
- 可以用少量热点小框去校准后台 render audit 的盲区
|
||||||
- 浏览器真实出图变没变
|
- 当用户肉眼说“这里还不对”时,可以直接把那个区域加入热点审计并复跑
|
||||||
- 后台 render audit 有没有跟上
|
|
||||||
|
|
||||||
线上已同步:
|
线上已同步:
|
||||||
|
|
||||||
@@ -701,21 +723,26 @@ Style 语义专项里仍最重要的是:
|
|||||||
|
|
||||||
下次继续时,建议按这个顺序推进:
|
下次继续时,建议按这个顺序推进:
|
||||||
|
|
||||||
- 先以固定页面
|
- 固定只用:
|
||||||
- `http://192.168.200.184/newpec/domain/navsea-compare-legacy-domain-compatible.html`
|
- `http://192.168.200.184/newpec/navsea-compare-karatsu-20nm.html`
|
||||||
作为唯一视觉基线
|
作为唯一视觉基线
|
||||||
- 以 [`src/Domain/style.domain-legacy-compatible-karatsu-10nm.deployed-2026-03-31.json`](/root/sourceserver/pbf/src/Domain/style.domain-legacy-compatible-karatsu-10nm.deployed-2026-03-31.json)
|
- 固定只用:
|
||||||
作为“最接近旧版”的右侧样式母版
|
- `/home/wwwroot/pbf-delivery-karatsu-20nm`
|
||||||
- 在 Domain compatible 线上逐层确认:
|
作为当前 delivery PBF
|
||||||
- 哪些 layer 仍依赖 legacy 日文字段
|
- 先围绕 strict audit 热点逐组修:
|
||||||
- 哪些可以直接切到标准字段而不改变视觉
|
- `southwest_lighthouse_cluster`
|
||||||
- 哪些需要加兼容映射才能保持视觉不变
|
- `takashima_main_harbor_marks`
|
||||||
|
- `takashima_inner_nearshore_hazards`
|
||||||
|
- `east_breakwater_marks`
|
||||||
|
- 每修一轮就重跑:
|
||||||
|
- [`navsea_strict_audit.py`](/root/sourceserver/pbf/navsea_strict_audit.py)
|
||||||
|
- 目的不是追求整屏 diff 立刻变小,而是让后台 render audit 对热点问题的判断逐步和肉眼一致
|
||||||
|
|
||||||
当前真正的下一步不再是继续调 `20nm compare` 页面,而是:
|
当前真正的下一步是:
|
||||||
|
|
||||||
- 把 deployed baseline style 中对 legacy 字段的依赖系统列出来
|
- 把用户指出的新问题优先落进热点 AOI
|
||||||
- 以固定 compare 页面做逐步替换验证
|
- 用热点截图和点击拾取结果对照右侧实际图层与属性
|
||||||
- 每次替换后只在这一个页面上检查视觉回归
|
- 再决定是继续修 [`style.navsea-delivery-karatsu-10nm.json`](/root/sourceserver/pbf/src/pbf/style.navsea-delivery-karatsu-10nm.json),还是回到 builder 补更细的 delivery 语义字段
|
||||||
|
|
||||||
## Important Notes
|
## Important Notes
|
||||||
|
|
||||||
@@ -729,4 +756,4 @@ Style 语义专项里仍最重要的是:
|
|||||||
|
|
||||||
下次开机如果要快速接上,可以先看这份文件,再按下面这句继续:
|
下次开机如果要快速接上,可以先看这份文件,再按下面这句继续:
|
||||||
|
|
||||||
`继续处理 STEP_RECORD.md 里记录的固定 compare 基线,围绕 navsea-compare-legacy-domain-compatible.html 这一个页面,在 pbf-domain-karatsu-10nm 线上逐步去掉日文字段依赖,同时保持视觉尽量不变。`
|
`继续处理 STEP_RECORD.md 里记录的 20nm 固定 compare 基线,围绕 navsea-compare-karatsu-20nm.html 这一个页面和 pbf-delivery-karatsu-20nm 这套 PBF,按 strict audit 的热点 AOI 逐组修视觉问题,并用小范围图像审计去校准后台 render audit。`
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import argparse
|
|||||||
import json
|
import json
|
||||||
import re
|
import re
|
||||||
import subprocess
|
import subprocess
|
||||||
import tempfile
|
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Any
|
from typing import Any
|
||||||
@@ -19,6 +18,7 @@ DEFAULT_BACKEND_AUDIT_JSON = Path(
|
|||||||
"/root/sourceserver/pbf/NavSea_Original_vs_Delivery_Render_Audit_Karatsu_20nm_2026-03-31.r7.json"
|
"/root/sourceserver/pbf/NavSea_Original_vs_Delivery_Render_Audit_Karatsu_20nm_2026-03-31.r7.json"
|
||||||
)
|
)
|
||||||
DEFAULT_OUTPUT_DIR = Path("/root/sourceserver/pbf/report/strict_audit")
|
DEFAULT_OUTPUT_DIR = Path("/root/sourceserver/pbf/report/strict_audit")
|
||||||
|
DEFAULT_HOTSPOTS_PATH = Path("/root/sourceserver/pbf/strict_audit_hotspots_20nm.json")
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
@@ -32,6 +32,15 @@ class CropBox:
|
|||||||
return (self.left, self.top, self.right, self.bottom)
|
return (self.left, self.top, self.right, self.bottom)
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(frozen=True)
|
||||||
|
class HotspotSpec:
|
||||||
|
id: str
|
||||||
|
label: str
|
||||||
|
notes: str
|
||||||
|
pane_box_norm: tuple[float, float, float, float]
|
||||||
|
backend_focus: tuple[str, ...]
|
||||||
|
|
||||||
|
|
||||||
def parse_args() -> argparse.Namespace:
|
def parse_args() -> argparse.Namespace:
|
||||||
parser = argparse.ArgumentParser(
|
parser = argparse.ArgumentParser(
|
||||||
description="Run strict audit by combining browser-rendered visual diff with backend render audit summary."
|
description="Run strict audit by combining browser-rendered visual diff with backend render audit summary."
|
||||||
@@ -39,11 +48,12 @@ def parse_args() -> argparse.Namespace:
|
|||||||
parser.add_argument("--compare-url", default=DEFAULT_COMPARE_URL)
|
parser.add_argument("--compare-url", default=DEFAULT_COMPARE_URL)
|
||||||
parser.add_argument("--backend-audit-json", type=Path, default=DEFAULT_BACKEND_AUDIT_JSON)
|
parser.add_argument("--backend-audit-json", type=Path, default=DEFAULT_BACKEND_AUDIT_JSON)
|
||||||
parser.add_argument("--output-dir", type=Path, default=DEFAULT_OUTPUT_DIR)
|
parser.add_argument("--output-dir", type=Path, default=DEFAULT_OUTPUT_DIR)
|
||||||
parser.add_argument("--width", type=int, default=1600)
|
parser.add_argument("--width", type=int, default=1280)
|
||||||
parser.add_argument("--height", type=int, default=900)
|
parser.add_argument("--height", type=int, default=720)
|
||||||
parser.add_argument("--toolbar-height", type=int, default=150)
|
parser.add_argument("--toolbar-height", type=int, default=150)
|
||||||
parser.add_argument("--bottom-trim", type=int, default=170)
|
parser.add_argument("--bottom-trim", type=int, default=170)
|
||||||
parser.add_argument("--timeout-sec", type=int, default=90)
|
parser.add_argument("--timeout-sec", type=int, default=120)
|
||||||
|
parser.add_argument("--hotspots-json", type=Path, default=DEFAULT_HOTSPOTS_PATH)
|
||||||
return parser.parse_args()
|
return parser.parse_args()
|
||||||
|
|
||||||
|
|
||||||
@@ -65,7 +75,7 @@ def run_browser_screenshot(compare_url: str, output_png: Path, width: int, heigh
|
|||||||
"--disable-gpu",
|
"--disable-gpu",
|
||||||
"--enable-unsafe-swiftshader",
|
"--enable-unsafe-swiftshader",
|
||||||
"--no-sandbox",
|
"--no-sandbox",
|
||||||
"--virtual-time-budget=20000",
|
"--virtual-time-budget=15000",
|
||||||
f"--window-size={width},{height}",
|
f"--window-size={width},{height}",
|
||||||
f"--screenshot={output_png}",
|
f"--screenshot={output_png}",
|
||||||
compare_url,
|
compare_url,
|
||||||
@@ -82,6 +92,30 @@ def build_crop_boxes(image_width: int, image_height: int, toolbar_height: int, b
|
|||||||
return left_box, right_box
|
return left_box, right_box
|
||||||
|
|
||||||
|
|
||||||
|
def load_hotspots(hotspots_json: Path) -> list[HotspotSpec]:
|
||||||
|
raw = json.loads(hotspots_json.read_text())
|
||||||
|
hotspots: list[HotspotSpec] = []
|
||||||
|
for item in raw:
|
||||||
|
hotspots.append(
|
||||||
|
HotspotSpec(
|
||||||
|
id=item["id"],
|
||||||
|
label=item["label"],
|
||||||
|
notes=item.get("notes", ""),
|
||||||
|
pane_box_norm=tuple(item["pane_box_norm"]),
|
||||||
|
backend_focus=tuple(item.get("backend_focus", [])),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return hotspots
|
||||||
|
|
||||||
|
|
||||||
|
def normalized_box_to_pixels(box_norm: tuple[float, float, float, float], pane_width: int, pane_height: int) -> CropBox:
|
||||||
|
left = max(0, min(pane_width, round(box_norm[0] * pane_width)))
|
||||||
|
top = max(0, min(pane_height, round(box_norm[1] * pane_height)))
|
||||||
|
right = max(left + 1, min(pane_width, round(box_norm[2] * pane_width)))
|
||||||
|
bottom = max(top + 1, min(pane_height, round(box_norm[3] * pane_height)))
|
||||||
|
return CropBox(left, top, right, bottom)
|
||||||
|
|
||||||
|
|
||||||
def compute_visual_metrics(left_img: Image.Image, right_img: Image.Image) -> dict[str, Any]:
|
def compute_visual_metrics(left_img: Image.Image, right_img: Image.Image) -> dict[str, Any]:
|
||||||
if left_img.size != right_img.size:
|
if left_img.size != right_img.size:
|
||||||
raise ValueError(f"image sizes differ: {left_img.size} vs {right_img.size}")
|
raise ValueError(f"image sizes differ: {left_img.size} vs {right_img.size}")
|
||||||
@@ -116,7 +150,16 @@ def compute_visual_metrics(left_img: Image.Image, right_img: Image.Image) -> dic
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def save_visual_outputs(full_png: Path, left_png: Path, right_png: Path, diff_png: Path, toolbar_height: int, bottom_trim: int) -> dict[str, Any]:
|
def save_visual_outputs(
|
||||||
|
full_png: Path,
|
||||||
|
left_png: Path,
|
||||||
|
right_png: Path,
|
||||||
|
diff_png: Path,
|
||||||
|
toolbar_height: int,
|
||||||
|
bottom_trim: int,
|
||||||
|
hotspots: list[HotspotSpec],
|
||||||
|
output_dir: Path,
|
||||||
|
) -> dict[str, Any]:
|
||||||
image = Image.open(full_png).convert("RGBA")
|
image = Image.open(full_png).convert("RGBA")
|
||||||
left_box, right_box = build_crop_boxes(image.width, image.height, toolbar_height, bottom_trim)
|
left_box, right_box = build_crop_boxes(image.width, image.height, toolbar_height, bottom_trim)
|
||||||
left = image.crop(left_box.as_tuple())
|
left = image.crop(left_box.as_tuple())
|
||||||
@@ -131,9 +174,49 @@ def save_visual_outputs(full_png: Path, left_png: Path, right_png: Path, diff_pn
|
|||||||
|
|
||||||
metrics["full_crop_left"] = list(left_box.as_tuple())
|
metrics["full_crop_left"] = list(left_box.as_tuple())
|
||||||
metrics["full_crop_right"] = list(right_box.as_tuple())
|
metrics["full_crop_right"] = list(right_box.as_tuple())
|
||||||
|
metrics["hotspots"] = save_hotspot_outputs(left, right, hotspots, output_dir)
|
||||||
return metrics
|
return metrics
|
||||||
|
|
||||||
|
|
||||||
|
def save_hotspot_outputs(
|
||||||
|
left_img: Image.Image,
|
||||||
|
right_img: Image.Image,
|
||||||
|
hotspots: list[HotspotSpec],
|
||||||
|
output_dir: Path,
|
||||||
|
) -> list[dict[str, Any]]:
|
||||||
|
hotspot_dir = output_dir / "hotspots"
|
||||||
|
hotspot_dir.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
|
results: list[dict[str, Any]] = []
|
||||||
|
for hotspot in hotspots:
|
||||||
|
box = normalized_box_to_pixels(hotspot.pane_box_norm, left_img.width, left_img.height)
|
||||||
|
left_crop = left_img.crop(box.as_tuple())
|
||||||
|
right_crop = right_img.crop(box.as_tuple())
|
||||||
|
metrics = compute_visual_metrics(left_crop, right_crop)
|
||||||
|
diff_img = metrics.pop("diff_image")
|
||||||
|
diff_boost = diff_img.convert("RGB").point(lambda value: min(255, value * 4))
|
||||||
|
|
||||||
|
left_path = hotspot_dir / f"{hotspot.id}_left.png"
|
||||||
|
right_path = hotspot_dir / f"{hotspot.id}_right.png"
|
||||||
|
diff_path = hotspot_dir / f"{hotspot.id}_diff.png"
|
||||||
|
left_crop.save(left_path)
|
||||||
|
right_crop.save(right_path)
|
||||||
|
diff_boost.save(diff_path)
|
||||||
|
|
||||||
|
metrics["id"] = hotspot.id
|
||||||
|
metrics["label"] = hotspot.label
|
||||||
|
metrics["notes"] = hotspot.notes
|
||||||
|
metrics["backend_focus"] = list(hotspot.backend_focus)
|
||||||
|
metrics["pane_box_norm"] = list(hotspot.pane_box_norm)
|
||||||
|
metrics["pane_box_pixels"] = list(box.as_tuple())
|
||||||
|
metrics["left_image"] = str(left_path)
|
||||||
|
metrics["right_image"] = str(right_path)
|
||||||
|
metrics["diff_image"] = str(diff_path)
|
||||||
|
results.append(metrics)
|
||||||
|
|
||||||
|
return sorted(results, key=lambda item: item["changed_ratio"], reverse=True)
|
||||||
|
|
||||||
|
|
||||||
def load_backend_summary(report_json_path: Path) -> dict[str, Any]:
|
def load_backend_summary(report_json_path: Path) -> dict[str, Any]:
|
||||||
data = json.loads(report_json_path.read_text())
|
data = json.loads(report_json_path.read_text())
|
||||||
|
|
||||||
@@ -200,6 +283,32 @@ def write_reports(
|
|||||||
f"- `{output_dir / 'compare_right.png'}`",
|
f"- `{output_dir / 'compare_right.png'}`",
|
||||||
f"- `{output_dir / 'compare_diff.png'}`",
|
f"- `{output_dir / 'compare_diff.png'}`",
|
||||||
"",
|
"",
|
||||||
|
"## Hotspot AOI Audit",
|
||||||
|
"",
|
||||||
|
]
|
||||||
|
|
||||||
|
for hotspot in visual_metrics.get("hotspots", []):
|
||||||
|
lines.extend(
|
||||||
|
[
|
||||||
|
f"### {hotspot['label']}",
|
||||||
|
"",
|
||||||
|
f"- id: `{hotspot['id']}`",
|
||||||
|
f"- notes: `{hotspot['notes']}`",
|
||||||
|
f"- backend focus: `{hotspot['backend_focus']}`",
|
||||||
|
f"- changed pixels: `{hotspot['changed_pixels']}` / `{hotspot['total_pixels']}`",
|
||||||
|
f"- changed ratio: `{hotspot['changed_ratio']}`",
|
||||||
|
f"- mean abs rgb: `{hotspot['mean_abs_rgb']}`",
|
||||||
|
f"- pane box norm: `{hotspot['pane_box_norm']}`",
|
||||||
|
f"- pane box pixels: `{hotspot['pane_box_pixels']}`",
|
||||||
|
f"- left image: `{hotspot['left_image']}`",
|
||||||
|
f"- right image: `{hotspot['right_image']}`",
|
||||||
|
f"- diff image: `{hotspot['diff_image']}`",
|
||||||
|
"",
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
lines.extend(
|
||||||
|
[
|
||||||
"## Backend Render Audit",
|
"## Backend Render Audit",
|
||||||
"",
|
"",
|
||||||
f"- original feature instances: `{backend_summary['original_feature_instances']}`",
|
f"- original feature instances: `{backend_summary['original_feature_instances']}`",
|
||||||
@@ -208,7 +317,8 @@ def write_reports(
|
|||||||
"",
|
"",
|
||||||
"Status counts:",
|
"Status counts:",
|
||||||
"",
|
"",
|
||||||
]
|
]
|
||||||
|
)
|
||||||
for key, value in backend_summary["status_counts"].items():
|
for key, value in backend_summary["status_counts"].items():
|
||||||
lines.append(f"- `{key}`: `{value}`")
|
lines.append(f"- `{key}`: `{value}`")
|
||||||
|
|
||||||
@@ -238,6 +348,7 @@ def main() -> None:
|
|||||||
args.output_dir.mkdir(parents=True, exist_ok=True)
|
args.output_dir.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
compare_version = fetch_compare_version(args.compare_url)
|
compare_version = fetch_compare_version(args.compare_url)
|
||||||
|
hotspots = load_hotspots(args.hotspots_json)
|
||||||
full_png = args.output_dir / "compare_full.png"
|
full_png = args.output_dir / "compare_full.png"
|
||||||
left_png = args.output_dir / "compare_left.png"
|
left_png = args.output_dir / "compare_left.png"
|
||||||
right_png = args.output_dir / "compare_right.png"
|
right_png = args.output_dir / "compare_right.png"
|
||||||
@@ -251,6 +362,8 @@ def main() -> None:
|
|||||||
diff_png=diff_png,
|
diff_png=diff_png,
|
||||||
toolbar_height=args.toolbar_height,
|
toolbar_height=args.toolbar_height,
|
||||||
bottom_trim=args.bottom_trim,
|
bottom_trim=args.bottom_trim,
|
||||||
|
hotspots=hotspots,
|
||||||
|
output_dir=args.output_dir,
|
||||||
)
|
)
|
||||||
backend_summary = load_backend_summary(args.backend_audit_json)
|
backend_summary = load_backend_summary(args.backend_audit_json)
|
||||||
report_md, report_json = write_reports(
|
report_md, report_json = write_reports(
|
||||||
@@ -270,6 +383,13 @@ def main() -> None:
|
|||||||
"report_json": str(report_json),
|
"report_json": str(report_json),
|
||||||
"changed_ratio": visual_metrics["changed_ratio"],
|
"changed_ratio": visual_metrics["changed_ratio"],
|
||||||
"changed_pixels": visual_metrics["changed_pixels"],
|
"changed_pixels": visual_metrics["changed_pixels"],
|
||||||
|
"hotspots": [
|
||||||
|
{
|
||||||
|
"id": item["id"],
|
||||||
|
"changed_ratio": item["changed_ratio"],
|
||||||
|
}
|
||||||
|
for item in visual_metrics.get("hotspots", [])
|
||||||
|
],
|
||||||
},
|
},
|
||||||
ensure_ascii=False,
|
ensure_ascii=False,
|
||||||
indent=2,
|
indent=2,
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 420 KiB After Width: | Height: | Size: 241 KiB |
|
Before Width: | Height: | Size: 1.0 MiB After Width: | Height: | Size: 533 KiB |
|
Before Width: | Height: | Size: 477 KiB After Width: | Height: | Size: 270 KiB |
|
Before Width: | Height: | Size: 372 KiB After Width: | Height: | Size: 191 KiB |
BIN
report/strict_audit/hotspots/east_breakwater_marks_diff.png
Normal file
|
After Width: | Height: | Size: 9.7 KiB |
BIN
report/strict_audit/hotspots/east_breakwater_marks_left.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
report/strict_audit/hotspots/east_breakwater_marks_right.png
Normal file
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 9.0 KiB |
|
After Width: | Height: | Size: 9.6 KiB |
|
After Width: | Height: | Size: 8.8 KiB |
|
After Width: | Height: | Size: 13 KiB |
|
After Width: | Height: | Size: 19 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 19 KiB |
|
After Width: | Height: | Size: 24 KiB |
|
After Width: | Height: | Size: 24 KiB |
@@ -3,38 +3,222 @@
|
|||||||
"compare_version": "20nm-r7-20260331-2044",
|
"compare_version": "20nm-r7-20260331-2044",
|
||||||
"backend_audit_json": "/root/sourceserver/pbf/NavSea_Original_vs_Delivery_Render_Audit_Karatsu_20nm_2026-03-31.r7.json",
|
"backend_audit_json": "/root/sourceserver/pbf/NavSea_Original_vs_Delivery_Render_Audit_Karatsu_20nm_2026-03-31.r7.json",
|
||||||
"visual_metrics": {
|
"visual_metrics": {
|
||||||
"image_width": 800,
|
"image_width": 640,
|
||||||
"image_height": 580,
|
"image_height": 400,
|
||||||
"total_pixels": 464000,
|
"total_pixels": 256000,
|
||||||
"changed_pixels": 411961,
|
"changed_pixels": 230361,
|
||||||
"changed_ratio": 0.887847,
|
"changed_ratio": 0.899848,
|
||||||
"mean_abs_rgb": [
|
"mean_abs_rgb": [
|
||||||
30.3034,
|
27.394,
|
||||||
31.6079,
|
28.0012,
|
||||||
61.2516
|
51.399
|
||||||
],
|
],
|
||||||
"rms_rgb": [
|
"rms_rgb": [
|
||||||
45.7879,
|
42.1614,
|
||||||
45.5806,
|
40.8117,
|
||||||
75.4396
|
66.9404
|
||||||
],
|
],
|
||||||
"diff_bbox": [
|
"diff_bbox": [
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
800,
|
640,
|
||||||
580
|
400
|
||||||
],
|
],
|
||||||
"full_crop_left": [
|
"full_crop_left": [
|
||||||
0,
|
0,
|
||||||
150,
|
150,
|
||||||
800,
|
640,
|
||||||
730
|
550
|
||||||
],
|
],
|
||||||
"full_crop_right": [
|
"full_crop_right": [
|
||||||
800,
|
640,
|
||||||
150,
|
150,
|
||||||
1600,
|
1280,
|
||||||
730
|
550
|
||||||
|
],
|
||||||
|
"hotspots": [
|
||||||
|
{
|
||||||
|
"image_width": 90,
|
||||||
|
"image_height": 120,
|
||||||
|
"total_pixels": 10800,
|
||||||
|
"changed_pixels": 10501,
|
||||||
|
"changed_ratio": 0.972315,
|
||||||
|
"mean_abs_rgb": [
|
||||||
|
35.1766,
|
||||||
|
36.7016,
|
||||||
|
83.6827
|
||||||
|
],
|
||||||
|
"rms_rgb": [
|
||||||
|
43.2452,
|
||||||
|
44.0282,
|
||||||
|
90.1318
|
||||||
|
],
|
||||||
|
"diff_bbox": [
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
90,
|
||||||
|
120
|
||||||
|
],
|
||||||
|
"id": "southwest_lighthouse_cluster",
|
||||||
|
"label": "西南灯塔区",
|
||||||
|
"notes": "左下角远处灯塔与标记点,用来校验远处 symbol 表现。",
|
||||||
|
"backend_focus": [
|
||||||
|
"p航路標識群",
|
||||||
|
"p陸上構造物"
|
||||||
|
],
|
||||||
|
"pane_box_norm": [
|
||||||
|
0.0,
|
||||||
|
0.7,
|
||||||
|
0.14,
|
||||||
|
1.0
|
||||||
|
],
|
||||||
|
"pane_box_pixels": [
|
||||||
|
0,
|
||||||
|
280,
|
||||||
|
90,
|
||||||
|
400
|
||||||
|
],
|
||||||
|
"left_image": "/root/sourceserver/pbf/report/strict_audit/hotspots/southwest_lighthouse_cluster_left.png",
|
||||||
|
"right_image": "/root/sourceserver/pbf/report/strict_audit/hotspots/southwest_lighthouse_cluster_right.png",
|
||||||
|
"diff_image": "/root/sourceserver/pbf/report/strict_audit/hotspots/southwest_lighthouse_cluster_diff.png"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"image_width": 147,
|
||||||
|
"image_height": 80,
|
||||||
|
"total_pixels": 11760,
|
||||||
|
"changed_pixels": 9217,
|
||||||
|
"changed_ratio": 0.783759,
|
||||||
|
"mean_abs_rgb": [
|
||||||
|
25.8832,
|
||||||
|
26.3819,
|
||||||
|
35.8165
|
||||||
|
],
|
||||||
|
"rms_rgb": [
|
||||||
|
40.7513,
|
||||||
|
43.6982,
|
||||||
|
55.4931
|
||||||
|
],
|
||||||
|
"diff_bbox": [
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
147,
|
||||||
|
80
|
||||||
|
],
|
||||||
|
"id": "takashima_main_harbor_marks",
|
||||||
|
"label": "高岛主港航标区",
|
||||||
|
"notes": "主港口灯塔、红绿标识、近岸障碍标记。",
|
||||||
|
"backend_focus": [
|
||||||
|
"p航路標識群",
|
||||||
|
"safety_icon_missing",
|
||||||
|
"text_missing_in_engineering"
|
||||||
|
],
|
||||||
|
"pane_box_norm": [
|
||||||
|
0.34,
|
||||||
|
0.05,
|
||||||
|
0.57,
|
||||||
|
0.25
|
||||||
|
],
|
||||||
|
"pane_box_pixels": [
|
||||||
|
218,
|
||||||
|
20,
|
||||||
|
365,
|
||||||
|
100
|
||||||
|
],
|
||||||
|
"left_image": "/root/sourceserver/pbf/report/strict_audit/hotspots/takashima_main_harbor_marks_left.png",
|
||||||
|
"right_image": "/root/sourceserver/pbf/report/strict_audit/hotspots/takashima_main_harbor_marks_right.png",
|
||||||
|
"diff_image": "/root/sourceserver/pbf/report/strict_audit/hotspots/takashima_main_harbor_marks_diff.png"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"image_width": 115,
|
||||||
|
"image_height": 68,
|
||||||
|
"total_pixels": 7820,
|
||||||
|
"changed_pixels": 5820,
|
||||||
|
"changed_ratio": 0.744246,
|
||||||
|
"mean_abs_rgb": [
|
||||||
|
26.0925,
|
||||||
|
28.3926,
|
||||||
|
32.3252
|
||||||
|
],
|
||||||
|
"rms_rgb": [
|
||||||
|
40.8552,
|
||||||
|
45.3891,
|
||||||
|
51.1523
|
||||||
|
],
|
||||||
|
"diff_bbox": [
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
115,
|
||||||
|
68
|
||||||
|
],
|
||||||
|
"id": "takashima_inner_nearshore_hazards",
|
||||||
|
"label": "高岛近岸碍航与鱼礁区",
|
||||||
|
"notes": "近岸鱼礁、碍航点、灯塔与小灯混合区。",
|
||||||
|
"backend_focus": [
|
||||||
|
"p投錨注意障害物",
|
||||||
|
"p航行危険障害物",
|
||||||
|
"p航路標識群"
|
||||||
|
],
|
||||||
|
"pane_box_norm": [
|
||||||
|
0.44,
|
||||||
|
0.14,
|
||||||
|
0.62,
|
||||||
|
0.31
|
||||||
|
],
|
||||||
|
"pane_box_pixels": [
|
||||||
|
282,
|
||||||
|
56,
|
||||||
|
397,
|
||||||
|
124
|
||||||
|
],
|
||||||
|
"left_image": "/root/sourceserver/pbf/report/strict_audit/hotspots/takashima_inner_nearshore_hazards_left.png",
|
||||||
|
"right_image": "/root/sourceserver/pbf/report/strict_audit/hotspots/takashima_inner_nearshore_hazards_right.png",
|
||||||
|
"diff_image": "/root/sourceserver/pbf/report/strict_audit/hotspots/takashima_inner_nearshore_hazards_diff.png"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"image_width": 109,
|
||||||
|
"image_height": 64,
|
||||||
|
"total_pixels": 6976,
|
||||||
|
"changed_pixels": 4310,
|
||||||
|
"changed_ratio": 0.617833,
|
||||||
|
"mean_abs_rgb": [
|
||||||
|
14.5257,
|
||||||
|
14.9903,
|
||||||
|
20.7008
|
||||||
|
],
|
||||||
|
"rms_rgb": [
|
||||||
|
30.1275,
|
||||||
|
32.1947,
|
||||||
|
39.6306
|
||||||
|
],
|
||||||
|
"diff_bbox": [
|
||||||
|
0,
|
||||||
|
4,
|
||||||
|
109,
|
||||||
|
64
|
||||||
|
],
|
||||||
|
"id": "east_breakwater_marks",
|
||||||
|
"label": "东侧防波堤灯台区",
|
||||||
|
"notes": "右上沿岸的防波堤灯台与港口标记。",
|
||||||
|
"backend_focus": [
|
||||||
|
"p航路標識群",
|
||||||
|
"safety_icon_missing"
|
||||||
|
],
|
||||||
|
"pane_box_norm": [
|
||||||
|
0.83,
|
||||||
|
0.0,
|
||||||
|
1.0,
|
||||||
|
0.16
|
||||||
|
],
|
||||||
|
"pane_box_pixels": [
|
||||||
|
531,
|
||||||
|
0,
|
||||||
|
640,
|
||||||
|
64
|
||||||
|
],
|
||||||
|
"left_image": "/root/sourceserver/pbf/report/strict_audit/hotspots/east_breakwater_marks_left.png",
|
||||||
|
"right_image": "/root/sourceserver/pbf/report/strict_audit/hotspots/east_breakwater_marks_right.png",
|
||||||
|
"diff_image": "/root/sourceserver/pbf/report/strict_audit/hotspots/east_breakwater_marks_diff.png"
|
||||||
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"backend_summary": {
|
"backend_summary": {
|
||||||
|
|||||||
@@ -8,12 +8,12 @@
|
|||||||
|
|
||||||
## Visual Audit
|
## Visual Audit
|
||||||
|
|
||||||
- changed pixels: `411961` / `464000`
|
- changed pixels: `230361` / `256000`
|
||||||
- changed ratio: `0.887847`
|
- changed ratio: `0.899848`
|
||||||
- mean abs rgb: `[30.3034, 31.6079, 61.2516]`
|
- mean abs rgb: `[27.394, 28.0012, 51.399]`
|
||||||
- rms rgb: `[45.7879, 45.5806, 75.4396]`
|
- rms rgb: `[42.1614, 40.8117, 66.9404]`
|
||||||
- left crop: `[0, 150, 800, 730]`
|
- left crop: `[0, 150, 640, 550]`
|
||||||
- right crop: `[800, 150, 1600, 730]`
|
- right crop: `[640, 150, 1280, 550]`
|
||||||
|
|
||||||
Artifacts:
|
Artifacts:
|
||||||
|
|
||||||
@@ -22,6 +22,64 @@ Artifacts:
|
|||||||
- `/root/sourceserver/pbf/report/strict_audit/compare_right.png`
|
- `/root/sourceserver/pbf/report/strict_audit/compare_right.png`
|
||||||
- `/root/sourceserver/pbf/report/strict_audit/compare_diff.png`
|
- `/root/sourceserver/pbf/report/strict_audit/compare_diff.png`
|
||||||
|
|
||||||
|
## Hotspot AOI Audit
|
||||||
|
|
||||||
|
### 西南灯塔区
|
||||||
|
|
||||||
|
- id: `southwest_lighthouse_cluster`
|
||||||
|
- notes: `左下角远处灯塔与标记点,用来校验远处 symbol 表现。`
|
||||||
|
- backend focus: `['p航路標識群', 'p陸上構造物']`
|
||||||
|
- changed pixels: `10501` / `10800`
|
||||||
|
- changed ratio: `0.972315`
|
||||||
|
- mean abs rgb: `[35.1766, 36.7016, 83.6827]`
|
||||||
|
- pane box norm: `[0.0, 0.7, 0.14, 1.0]`
|
||||||
|
- pane box pixels: `[0, 280, 90, 400]`
|
||||||
|
- left image: `/root/sourceserver/pbf/report/strict_audit/hotspots/southwest_lighthouse_cluster_left.png`
|
||||||
|
- right image: `/root/sourceserver/pbf/report/strict_audit/hotspots/southwest_lighthouse_cluster_right.png`
|
||||||
|
- diff image: `/root/sourceserver/pbf/report/strict_audit/hotspots/southwest_lighthouse_cluster_diff.png`
|
||||||
|
|
||||||
|
### 高岛主港航标区
|
||||||
|
|
||||||
|
- id: `takashima_main_harbor_marks`
|
||||||
|
- notes: `主港口灯塔、红绿标识、近岸障碍标记。`
|
||||||
|
- backend focus: `['p航路標識群', 'safety_icon_missing', 'text_missing_in_engineering']`
|
||||||
|
- changed pixels: `9217` / `11760`
|
||||||
|
- changed ratio: `0.783759`
|
||||||
|
- mean abs rgb: `[25.8832, 26.3819, 35.8165]`
|
||||||
|
- pane box norm: `[0.34, 0.05, 0.57, 0.25]`
|
||||||
|
- pane box pixels: `[218, 20, 365, 100]`
|
||||||
|
- left image: `/root/sourceserver/pbf/report/strict_audit/hotspots/takashima_main_harbor_marks_left.png`
|
||||||
|
- right image: `/root/sourceserver/pbf/report/strict_audit/hotspots/takashima_main_harbor_marks_right.png`
|
||||||
|
- diff image: `/root/sourceserver/pbf/report/strict_audit/hotspots/takashima_main_harbor_marks_diff.png`
|
||||||
|
|
||||||
|
### 高岛近岸碍航与鱼礁区
|
||||||
|
|
||||||
|
- id: `takashima_inner_nearshore_hazards`
|
||||||
|
- notes: `近岸鱼礁、碍航点、灯塔与小灯混合区。`
|
||||||
|
- backend focus: `['p投錨注意障害物', 'p航行危険障害物', 'p航路標識群']`
|
||||||
|
- changed pixels: `5820` / `7820`
|
||||||
|
- changed ratio: `0.744246`
|
||||||
|
- mean abs rgb: `[26.0925, 28.3926, 32.3252]`
|
||||||
|
- pane box norm: `[0.44, 0.14, 0.62, 0.31]`
|
||||||
|
- pane box pixels: `[282, 56, 397, 124]`
|
||||||
|
- left image: `/root/sourceserver/pbf/report/strict_audit/hotspots/takashima_inner_nearshore_hazards_left.png`
|
||||||
|
- right image: `/root/sourceserver/pbf/report/strict_audit/hotspots/takashima_inner_nearshore_hazards_right.png`
|
||||||
|
- diff image: `/root/sourceserver/pbf/report/strict_audit/hotspots/takashima_inner_nearshore_hazards_diff.png`
|
||||||
|
|
||||||
|
### 东侧防波堤灯台区
|
||||||
|
|
||||||
|
- id: `east_breakwater_marks`
|
||||||
|
- notes: `右上沿岸的防波堤灯台与港口标记。`
|
||||||
|
- backend focus: `['p航路標識群', 'safety_icon_missing']`
|
||||||
|
- changed pixels: `4310` / `6976`
|
||||||
|
- changed ratio: `0.617833`
|
||||||
|
- mean abs rgb: `[14.5257, 14.9903, 20.7008]`
|
||||||
|
- pane box norm: `[0.83, 0.0, 1.0, 0.16]`
|
||||||
|
- pane box pixels: `[531, 0, 640, 64]`
|
||||||
|
- left image: `/root/sourceserver/pbf/report/strict_audit/hotspots/east_breakwater_marks_left.png`
|
||||||
|
- right image: `/root/sourceserver/pbf/report/strict_audit/hotspots/east_breakwater_marks_right.png`
|
||||||
|
- diff image: `/root/sourceserver/pbf/report/strict_audit/hotspots/east_breakwater_marks_diff.png`
|
||||||
|
|
||||||
## Backend Render Audit
|
## Backend Render Audit
|
||||||
|
|
||||||
- original feature instances: `258317`
|
- original feature instances: `258317`
|
||||||
|
|||||||
44
strict_audit_hotspots_20nm.json
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"id": "takashima_main_harbor_marks",
|
||||||
|
"label": "高岛主港航标区",
|
||||||
|
"notes": "主港口灯塔、红绿标识、近岸障碍标记。",
|
||||||
|
"pane_box_norm": [0.34, 0.05, 0.57, 0.25],
|
||||||
|
"backend_focus": [
|
||||||
|
"p航路標識群",
|
||||||
|
"safety_icon_missing",
|
||||||
|
"text_missing_in_engineering"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "takashima_inner_nearshore_hazards",
|
||||||
|
"label": "高岛近岸碍航与鱼礁区",
|
||||||
|
"notes": "近岸鱼礁、碍航点、灯塔与小灯混合区。",
|
||||||
|
"pane_box_norm": [0.44, 0.14, 0.62, 0.31],
|
||||||
|
"backend_focus": [
|
||||||
|
"p投錨注意障害物",
|
||||||
|
"p航行危険障害物",
|
||||||
|
"p航路標識群"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "east_breakwater_marks",
|
||||||
|
"label": "东侧防波堤灯台区",
|
||||||
|
"notes": "右上沿岸的防波堤灯台与港口标记。",
|
||||||
|
"pane_box_norm": [0.83, 0.0, 1.0, 0.16],
|
||||||
|
"backend_focus": [
|
||||||
|
"p航路標識群",
|
||||||
|
"safety_icon_missing"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "southwest_lighthouse_cluster",
|
||||||
|
"label": "西南灯塔区",
|
||||||
|
"notes": "左下角远处灯塔与标记点,用来校验远处 symbol 表现。",
|
||||||
|
"pane_box_norm": [0.0, 0.70, 0.14, 1.0],
|
||||||
|
"backend_focus": [
|
||||||
|
"p航路標識群",
|
||||||
|
"p陸上構造物"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||