test(M00): calibrate paired renderer checkpoints
Work-Package: M00-QAR-VISUAL-DIFF-001 Agent: sindo-main-codex Tests: synthetic JPG/PNG diff, manifest, coordination and documentation gates Fidelity: five build 12340 references compared; coordinate and placement gaps recorded
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
extends SceneTree
|
||||
|
||||
## Compares paired PNG checkpoint directories without storing proprietary reference images.
|
||||
## Compares original-client JPG/PNG references with OpenWC checkpoint PNGs.
|
||||
## Usage: godot --headless --path . --script res://src/tools/compare_render_checkpoints.gd --
|
||||
## --reference <directory> --candidate <directory> [--output <report.json>]
|
||||
## [--pixel-threshold 0.04] [--mean-threshold 0.015] [--changed-ratio-threshold 0.01]
|
||||
@@ -73,22 +73,29 @@ func _parse_arguments(raw_arguments: PackedStringArray) -> Dictionary:
|
||||
|
||||
|
||||
func _compare_directories(reference_directory: String, candidate_directory: String, options: Dictionary) -> Dictionary:
|
||||
var reference_files := _png_file_names(reference_directory)
|
||||
reference_directory = ProjectSettings.globalize_path(reference_directory)
|
||||
candidate_directory = ProjectSettings.globalize_path(candidate_directory)
|
||||
var reference_files := _reference_image_file_names(reference_directory)
|
||||
var results: Array[Dictionary] = []
|
||||
var failed_count := 0
|
||||
var missing_count := 0
|
||||
for file_name in reference_files:
|
||||
var reference_path := reference_directory.path_join(file_name)
|
||||
var candidate_path := candidate_directory.path_join(file_name)
|
||||
if not FileAccess.file_exists(candidate_path):
|
||||
results.append({"name": file_name, "status": "missing_candidate"})
|
||||
var checkpoint_name := file_name.get_basename()
|
||||
var candidate_file_names := _candidate_file_names(candidate_directory, checkpoint_name)
|
||||
if candidate_file_names.is_empty():
|
||||
results.append({"checkpoint": checkpoint_name, "reference": file_name, "status": "missing_candidate"})
|
||||
missing_count += 1
|
||||
continue
|
||||
var comparison := _compare_images(reference_path, candidate_path, options)
|
||||
comparison.name = file_name
|
||||
results.append(comparison)
|
||||
if comparison.status != "passed":
|
||||
failed_count += 1
|
||||
for candidate_file_name in candidate_file_names:
|
||||
var candidate_path := candidate_directory.path_join(candidate_file_name)
|
||||
var comparison := _compare_images(reference_path, candidate_path, options)
|
||||
comparison.checkpoint = checkpoint_name
|
||||
comparison.reference = file_name
|
||||
comparison.candidate = candidate_file_name
|
||||
results.append(comparison)
|
||||
if comparison.status != "passed":
|
||||
failed_count += 1
|
||||
var no_reference_images := reference_files.is_empty()
|
||||
return {
|
||||
"schema_version": REPORT_SCHEMA_VERSION,
|
||||
@@ -149,18 +156,32 @@ func _perceptual_color_error(reference_color: Color, candidate_color: Color) ->
|
||||
return 0.2126 * red_delta + 0.7152 * green_delta + 0.0722 * blue_delta + 0.25 * alpha_delta
|
||||
|
||||
|
||||
func _png_file_names(directory_path: String) -> Array[String]:
|
||||
func _reference_image_file_names(directory_path: String) -> Array[String]:
|
||||
var directory := DirAccess.open(directory_path)
|
||||
if directory == null:
|
||||
return []
|
||||
var file_names: Array[String] = []
|
||||
for file_name in directory.get_files():
|
||||
if file_name.to_lower().ends_with(".png"):
|
||||
var lower_name := file_name.to_lower()
|
||||
if not lower_name.begins_with("diagnostic_") and (lower_name.ends_with(".png") or lower_name.ends_with(".jpg") or lower_name.ends_with(".jpeg")):
|
||||
file_names.append(file_name)
|
||||
file_names.sort()
|
||||
return file_names
|
||||
|
||||
|
||||
func _candidate_file_names(candidate_directory: String, checkpoint_name: String) -> Array[String]:
|
||||
var candidates: Array[String] = []
|
||||
for pass_name in ["cold_process", "warm_revisit"]:
|
||||
var file_name := "%s__%s.png" % [checkpoint_name, pass_name]
|
||||
if FileAccess.file_exists(candidate_directory.path_join(file_name)):
|
||||
candidates.append(file_name)
|
||||
if candidates.is_empty():
|
||||
var exact_file_name := "%s.png" % checkpoint_name
|
||||
if FileAccess.file_exists(candidate_directory.path_join(exact_file_name)):
|
||||
candidates.append(exact_file_name)
|
||||
return candidates
|
||||
|
||||
|
||||
func _write_report(output_path: String, report: Dictionary) -> bool:
|
||||
var absolute_path := ProjectSettings.globalize_path(output_path)
|
||||
var error := DirAccess.make_dir_recursive_absolute(absolute_path.get_base_dir())
|
||||
@@ -186,14 +207,13 @@ func _run_self_test() -> int:
|
||||
reference_image.fill(Color(0.25, 0.5, 0.75, 1.0))
|
||||
var changed_image := reference_image.duplicate()
|
||||
changed_image.set_pixel(0, 0, Color.WHITE)
|
||||
for pair in [
|
||||
[reference_directory, reference_image],
|
||||
[identical_directory, reference_image],
|
||||
[changed_directory, changed_image],
|
||||
]:
|
||||
var save_error: Error = pair[1].save_png(String(pair[0]).path_join("synthetic.png"))
|
||||
if save_error != OK:
|
||||
push_error("RENDER_CHECKPOINT_DIFF SELF_TEST: cannot save fixture")
|
||||
if reference_image.save_jpg(reference_directory.path_join("synthetic.jpg"), 1.0) != OK:
|
||||
push_error("RENDER_CHECKPOINT_DIFF SELF_TEST: cannot save JPG reference")
|
||||
return 1
|
||||
for pass_name in ["cold_process", "warm_revisit"]:
|
||||
if reference_image.save_png(identical_directory.path_join("synthetic__%s.png" % pass_name)) != OK:
|
||||
return 1
|
||||
if changed_image.save_png(changed_directory.path_join("synthetic__%s.png" % pass_name)) != OK:
|
||||
return 1
|
||||
var options := {
|
||||
"pixel_threshold": DEFAULT_PIXEL_THRESHOLD,
|
||||
|
||||
Reference in New Issue
Block a user