test(M00): add camera pose sweep

This commit is contained in:
2026-07-12 01:46:02 +04:00
parent 8d4641a43b
commit b48195d08d
5 changed files with 255 additions and 2 deletions
@@ -0,0 +1,118 @@
[CmdletBinding()]
param(
[Parameter(Mandatory = $true)]
[string]$ReferenceDirectory,
[Parameter(Mandatory = $true)]
[string]$Checkpoint,
[string]$GodotPath,
[string]$Output = "user://render_camera_pose_sweep",
[double[]]$YawOffsets = @(-10.0, 0.0, 10.0),
[double[]]$PitchOffsets = @(-10.0, 0.0, 10.0),
[double]$CameraFov = 62.0,
[double]$WaitSeconds = 2.0,
[double]$MeasureSeconds = 0.1,
[switch]$PlanOnly
)
$ErrorActionPreference = "Stop"
$repoRoot = Split-Path -Parent $PSScriptRoot
if (-not $GodotPath) {
$godotCommand = Get-Command godot -ErrorAction SilentlyContinue
if ($godotCommand) {
$GodotPath = $godotCommand.Source
} else {
$GodotPath = Join-Path $env:TEMP "godot-4.6.1-openwc\Godot_v4.6.1-stable_win64.exe"
}
}
if (-not (Test-Path -LiteralPath $GodotPath)) {
throw "Godot executable not found: $GodotPath"
}
if (-not (Test-Path -LiteralPath $ReferenceDirectory)) {
throw "Reference directory not found: $ReferenceDirectory"
}
$invariantCulture = [Globalization.CultureInfo]::InvariantCulture
$cameraFovText = $CameraFov.ToString($invariantCulture)
$waitSecondsText = $WaitSeconds.ToString($invariantCulture)
$measureSecondsText = $MeasureSeconds.ToString($invariantCulture)
$absoluteOutput = if ($Output.StartsWith("user://")) {
Join-Path $env:APPDATA "Godot\app_userdata\OpenWC\$($Output.Substring(7))"
} else {
[IO.Path]::GetFullPath((Join-Path $repoRoot $Output))
}
$ranking = [Collections.Generic.List[object]]::new()
Push-Location $repoRoot
try {
foreach ($yawOffset in $YawOffsets) {
foreach ($pitchOffset in $PitchOffsets) {
$yawText = $yawOffset.ToString("0.###", $invariantCulture)
$pitchText = $pitchOffset.ToString("0.###", $invariantCulture)
$candidateName = "yaw_$($yawText.Replace('-', 'n').Replace('.', '_'))__pitch_$($pitchText.Replace('-', 'n').Replace('.', '_'))"
$candidateOutput = "$Output/$candidateName"
$comparisonReport = Join-Path $absoluteOutput "$candidateName.json"
if ($PlanOnly) {
$ranking.Add([pscustomobject]@{
yaw_offset_degrees = $yawOffset
pitch_offset_degrees = $pitchOffset
camera_fov_degrees = $CameraFov
candidate_directory = $candidateOutput
})
continue
}
& $GodotPath --path . --script res://src/tools/capture_render_checkpoints.gd -- `
--output $candidateOutput --only $Checkpoint --single-pass `
--camera-fov $cameraFovText `
--camera-yaw-offset $yawText --camera-pitch-offset $pitchText `
--wait $waitSecondsText --measure $measureSecondsText
if ($LASTEXITCODE -ne 0) {
throw "Capture failed for yaw=$yawText pitch=$pitchText"
}
& $GodotPath --headless --path . --script res://src/tools/compare_render_checkpoints.gd -- `
--reference $ReferenceDirectory --candidate $candidateOutput `
--only $Checkpoint --output $comparisonReport
if ($LASTEXITCODE -notin @(0, 1)) {
throw "Comparison failed for yaw=$yawText pitch=$pitchText"
}
$comparison = Get-Content -Raw -LiteralPath $comparisonReport | ConvertFrom-Json
if ($comparison.compared_count -lt 1) {
throw "No paired image was compared for yaw=$yawText pitch=$pitchText"
}
$meanError = ($comparison.results | Measure-Object -Property mean_perceptual_error -Average).Average
$changedRatio = ($comparison.results | Measure-Object -Property changed_pixel_ratio -Average).Average
$ranking.Add([pscustomobject]@{
yaw_offset_degrees = $yawOffset
pitch_offset_degrees = $pitchOffset
camera_fov_degrees = $CameraFov
mean_perceptual_error = $meanError
changed_pixel_ratio = $changedRatio
candidate_directory = $candidateOutput
comparison_report = $comparisonReport
})
}
}
$rankedCandidates = if ($PlanOnly) {
@($ranking)
} else {
@($ranking | Sort-Object mean_perceptual_error, changed_pixel_ratio)
}
$rankingPath = Join-Path $absoluteOutput "ranking.json"
$rankingDocument = [ordered]@{
schema_version = 1
checkpoint = $Checkpoint
reference_directory = [IO.Path]::GetFullPath($ReferenceDirectory)
candidates = $rankedCandidates
}
New-Item -ItemType Directory -Force -Path $absoluteOutput | Out-Null
$rankingDocument | ConvertTo-Json -Depth 6 | Set-Content -Encoding UTF8 -LiteralPath $rankingPath
$rankedCandidates | Format-Table yaw_offset_degrees, pitch_offset_degrees, mean_perceptual_error, changed_pixel_ratio
$completionKind = if ($PlanOnly) { "plan" } else { "ranking" }
Write-Output "Camera pose sweep $completionKind completed: $rankingPath"
} finally {
Pop-Location
}