feat(perception): add recorded replay maturation labs

This commit is contained in:
DCCONSTRUCTIONS
2026-08-05 07:47:15 +03:00
parent c1b0f6f8a3
commit 3982256f08
101 changed files with 24009 additions and 4 deletions
@@ -0,0 +1,135 @@
[CmdletBinding()]
param(
[Parameter(Mandatory = $true)]
[string]$SourceRoot,
[Parameter(Mandatory = $true)]
[string]$OutputRoot,
[string]$LogPath = ''
)
$ErrorActionPreference = 'Stop'
$ProgressPreference = 'SilentlyContinue'
$sourceCommit = '581889df47d6181110c758c10b872ca833a835e3'
$developmentImage = 'nvcr.io/nvidia/deepstream:9.1-triton-multiarch@sha256:fd31f5b44ababdbdee8cd397a375e888191b49e402ac237254a4cdc239130f5b'
$runtimeImage = 'nvcr.io/nvidia/deepstream:9.1-samples-multiarch@sha256:10eca409b3894e91c1bac915c9f1346307e56695e552487cbe8cf2f58a3f998f'
$libraryName = 'libnvds_infercustomparser_tao.so'
$expectedSources = [ordered]@{
'Makefile' = '0265f470354e60c6d719bde68c7b74b1879eed9b4552b8fe7b39416af5ce6835'
'debug_logger_raii.cpp' = '1d388509e1ff9008de6ccd6451db9c6433273ed78a94e95a1df84585b8dc2915'
'debug_logger_raii.hpp' = '6efdce1874468848664a18ceb613f2384b8c079cb12baa888a433d6c0f81b7ec'
'debug_logger_tensor.hpp' = 'c9999fcf92536bbb36498ddd4485f213fc2f5ddc70d24f8d408195a48680b94f'
'nvdsinfer_custombboxparser_tao.cpp' = '1794e3ee5152f25eff31454c6181368676f6659c68fc25b4b1933f6cbb63158b'
}
function Get-Sha256([string]$Path) {
return (Get-FileHash -LiteralPath $Path -Algorithm SHA256).Hash.ToLowerInvariant()
}
function Invoke-Docker([string[]]$Arguments, [string]$Label) {
$previousErrorActionPreference = $ErrorActionPreference
$ErrorActionPreference = 'Continue'
try {
& docker @Arguments
$dockerExitCode = $LASTEXITCODE
}
finally {
$ErrorActionPreference = $previousErrorActionPreference
}
if ($dockerExitCode -ne 0) {
throw "$Label failed with exit code $dockerExitCode"
}
}
$source = (Resolve-Path -LiteralPath $SourceRoot).Path
New-Item -ItemType Directory -Force -Path $OutputRoot | Out-Null
$output = (Resolve-Path -LiteralPath $OutputRoot).Path
if ($LogPath) {
$logParent = Split-Path -Parent $LogPath
if ($logParent) { New-Item -ItemType Directory -Force -Path $logParent | Out-Null }
Start-Transcript -LiteralPath $LogPath -Append | Out-Null
}
try {
$sourceLibrary = Join-Path $source $libraryName
if (Test-Path -LiteralPath $sourceLibrary -PathType Leaf) {
Remove-Item -LiteralPath $sourceLibrary -Force
}
$actualSourceFiles = @(Get-ChildItem -LiteralPath $source -File | ForEach-Object { $_.Name })
if (@($actualSourceFiles | Where-Object { -not $expectedSources.Contains($_) }).Count -ne 0 -or
@($expectedSources.Keys | Where-Object { $actualSourceFiles -notcontains $_ }).Count -ne 0) {
throw 'NVIDIA TAO parser source inventory changed.'
}
foreach ($entry in $expectedSources.GetEnumerator()) {
$path = Join-Path $source $entry.Key
$actualSha = Get-Sha256 $path
if ($actualSha -ne $entry.Value) {
throw "NVIDIA TAO parser source changed: $($entry.Key)"
}
}
$previousErrorActionPreference = $ErrorActionPreference
$ErrorActionPreference = 'Continue'
try {
& docker image inspect $developmentImage *> $null
$developmentImageCached = $LASTEXITCODE -eq 0
}
finally {
$ErrorActionPreference = $previousErrorActionPreference
}
if (-not $developmentImageCached) {
Write-Host 'Pulling exact NVIDIA DeepStream 9.1 Triton development image...'
Invoke-Docker @('pull', $developmentImage) 'DeepStream development image pull'
}
$containerSource = '/opt/nvidia/deepstream/deepstream/sources/apps/sample_apps/deepstream_tao_apps/post_processor'
Invoke-Docker @(
'run', '--rm', '--gpus', 'all', '--network', 'none', '--cap-drop', 'ALL',
'--security-opt', 'no-new-privileges',
'--mount', "type=bind,src=$source,dst=$containerSource",
'--workdir', $containerSource,
'--entrypoint', 'make', $developmentImage, 'CUDA_VER=13.2'
) 'official NVIDIA TAO parser build'
if (-not (Test-Path -LiteralPath $sourceLibrary -PathType Leaf) -or
(Get-Item -LiteralPath $sourceLibrary).Length -eq 0) {
throw 'NVIDIA TAO parser build did not produce a library.'
}
$destination = Join-Path $output $libraryName
$temporary = "$destination.$([Guid]::NewGuid().ToString('N')).tmp"
Copy-Item -LiteralPath $sourceLibrary -Destination $temporary
Move-Item -LiteralPath $temporary -Destination $destination -Force
$librarySha = Get-Sha256 $destination
Invoke-Docker @(
'run', '--rm', '--gpus', 'all', '--network', 'none', '--read-only',
'--cap-drop', 'ALL', '--security-opt', 'no-new-privileges',
'--mount', "type=bind,src=$output,dst=/workspace/parser,readonly",
'--entrypoint', '/bin/bash', $runtimeImage, '-lc',
"ldd /workspace/parser/$libraryName && nm -D /workspace/parser/$libraryName | grep -q NvDsInferParseCustomDDETRTAO"
) 'NVIDIA TAO parser runtime verification'
$manifest = [ordered]@{
schema_version = 'missioncore.e46e-nvidia-tao-parser/v1'
status = 'completed'
source_repository = 'https://github.com/NVIDIA/DeepStream.git'
source_commit = $sourceCommit
source_files = @($expectedSources.GetEnumerator() | ForEach-Object {
[ordered]@{ path = $_.Key; sha256 = $_.Value }
})
development_image = $developmentImage
runtime_image = $runtimeImage
cuda_version = '13.2'
symbol = 'NvDsInferParseCustomDDETRTAO'
library_file = $libraryName
library_sha256 = $librarySha
completed_at_utc = (Get-Date).ToUniversalTime().ToString('o')
}
$manifest | ConvertTo-Json -Depth 8 | Set-Content -LiteralPath (Join-Path $output 'parser-runtime.json') -Encoding UTF8
Write-Host "E46E_NVIDIA_TAO_PARSER_COMPLETED sha256=$librarySha output=$output"
}
finally {
if ($LogPath) { Stop-Transcript | Out-Null }
}
@@ -0,0 +1,43 @@
[CmdletBinding()]
param(
[Parameter(Mandatory = $true)]
[string]$BuildScript,
[Parameter(Mandatory = $true)]
[string]$SourceRoot,
[Parameter(Mandatory = $true)]
[string]$OutputRoot
)
$ErrorActionPreference = 'Stop'
$taskName = 'MissionCore-E46ENvidiaTaoParser'
$script = (Resolve-Path -LiteralPath $BuildScript).Path
$source = (Resolve-Path -LiteralPath $SourceRoot).Path
New-Item -ItemType Directory -Force -Path $OutputRoot | Out-Null
$output = (Resolve-Path -LiteralPath $OutputRoot).Path
$existing = Get-ScheduledTask -TaskName $taskName -ErrorAction SilentlyContinue
if ($existing -and $existing.State -eq 'Running') {
throw "$taskName is already running."
}
$logsRoot = 'D:\NDC_MISSIONCORE\runtime\experiments\e46e\logs'
New-Item -ItemType Directory -Force -Path $logsRoot | Out-Null
$stamp = (Get-Date).ToUniversalTime().ToString('yyyyMMddTHHmmssfffZ')
$logPath = Join-Path $logsRoot "e46e-parser-build-$stamp.log"
$powerShell = "$env:SystemRoot\System32\WindowsPowerShell\v1.0\powershell.exe"
$arguments = @(
'-NoLogo', '-NoProfile', '-NonInteractive', '-ExecutionPolicy', 'Bypass',
'-File', "`"$script`"", '-SourceRoot', "`"$source`"",
'-OutputRoot', "`"$output`"", '-LogPath', "`"$logPath`""
) -join ' '
$userId = [System.Security.Principal.WindowsIdentity]::GetCurrent().Name
$action = New-ScheduledTaskAction -Execute $powerShell -Argument $arguments -WorkingDirectory $source
$principal = New-ScheduledTaskPrincipal -UserId $userId -LogonType Interactive -RunLevel Limited
$trigger = New-ScheduledTaskTrigger -Once -At ((Get-Date).AddMinutes(30))
$settings = New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries `
-StartWhenAvailable -ExecutionTimeLimit ([TimeSpan]::FromHours(3))
Register-ScheduledTask -TaskName $taskName -Action $action -Principal $principal `
-Trigger $trigger -Settings $settings `
-Description 'Build the pinned official NVIDIA DeepStream TAO RT-DETR parser.' -Force | Out-Null
Start-ScheduledTask -TaskName $taskName
Write-Host "E46E_PARSER_TASK_STARTED task=$taskName log=$logPath"
@@ -0,0 +1,319 @@
[CmdletBinding()]
param(
[Parameter(Mandatory = $true)]
[string]$PackageRoot,
[string]$SourceJobRoot = 'D:\NDC_MISSIONCORE\runtime\jobs\recorded-camera-602ac89026ed12978619801d',
[string]$RuntimeRoot = 'D:\NDC_MISSIONCORE\runtime\experiments\e46e',
[string]$LogPath = ''
)
$ErrorActionPreference = 'Stop'
$ProgressPreference = 'SilentlyContinue'
function Get-Sha256([string]$Path) {
return (Get-FileHash -LiteralPath $Path -Algorithm SHA256).Hash.ToLowerInvariant()
}
function Assert-Sha256([string]$Path, [string]$Expected, [string]$Label) {
if (-not (Test-Path -LiteralPath $Path -PathType Leaf)) {
throw "$Label is missing: $Path"
}
$actual = Get-Sha256 $Path
if ($actual -ne $Expected) {
throw "$Label SHA-256 changed: expected $Expected, got $actual"
}
}
function Invoke-Docker([string[]]$Arguments, [string]$Label) {
& docker @Arguments
if ($LASTEXITCODE -ne 0) {
throw "$Label failed with exit code $LASTEXITCODE"
}
}
$package = (Resolve-Path -LiteralPath $PackageRoot).Path
$sourceJob = (Resolve-Path -LiteralPath $SourceJobRoot).Path
$profilePath = Join-Path $package 'profile.json'
$manifestPath = Join-Path $package 'manifest.json'
if (-not (Test-Path -LiteralPath $profilePath -PathType Leaf) -or
-not (Test-Path -LiteralPath $manifestPath -PathType Leaf)) {
throw 'E46E package is incomplete.'
}
$manifest = Get-Content -LiteralPath $manifestPath -Raw | ConvertFrom-Json
if ($manifest.schema_version -ne 'missioncore.e46e-worker-package/v1' -or
$manifest.package_id -ne (Split-Path -Leaf $package)) {
throw 'E46E package identity is invalid.'
}
$expectedPaths = @($manifest.identity.artifact_paths)
foreach ($artifact in @($manifest.artifacts)) {
if ($expectedPaths -notcontains [string]$artifact.path) {
throw "Unexpected E46E package artifact: $($artifact.path)"
}
$artifactPath = Join-Path $package ([string]$artifact.path)
Assert-Sha256 $artifactPath ([string]$artifact.sha256) "package artifact $($artifact.path)"
if ((Get-Item -LiteralPath $artifactPath).Length -ne [int64]$artifact.byte_length) {
throw "Package artifact length changed: $($artifact.path)"
}
}
$actualPaths = @(Get-ChildItem -LiteralPath $package -Recurse -File | ForEach-Object {
$_.FullName.Substring($package.Length + 1).Replace('\', '/')
})
if (@($actualPaths | Where-Object { $_ -ne 'manifest.json' -and $expectedPaths -notcontains $_ }).Count -ne 0 -or
@($expectedPaths | Where-Object { $actualPaths -notcontains $_ }).Count -ne 0) {
throw 'E46E package file set changed.'
}
$profile = Get-Content -LiteralPath $profilePath -Raw | ConvertFrom-Json
if ($profile.schema_version -ne 'missioncore.e46e-ready-stack-profile/v1') {
throw 'E46E profile is incompatible.'
}
$image = [string]$profile.runtime.container_image
$imageDigestMatch = [regex]::Match($image, '@sha256:([0-9a-f]{64})$')
if (-not $imageDigestMatch.Success) {
throw 'E46E runtime image must be pinned by a full SHA-256 digest.'
}
$imageDigest = $imageDigestMatch.Groups[1].Value
$modelSha = [string]$profile.detector.model_sha256
$streamSha = [string]$profile.source.stream_sha256
$modelFile = [string]$profile.detector.model_file
$modelUrl = [string]$profile.detector.model_url
$parserFile = [string]$profile.parser.library_file
$parserSha = [string]$profile.parser.library_sha256
$parserLibraryPath = Join-Path $package "runtime\$parserFile"
New-Item -ItemType Directory -Force -Path $RuntimeRoot | Out-Null
$logsRoot = Join-Path $RuntimeRoot 'logs'
$modelsRoot = Join-Path $RuntimeRoot 'models\trafficcamnet_transformer_lite\deployable_resnet50_v2.0'
$inputsRoot = Join-Path $RuntimeRoot 'inputs'
$runsRoot = Join-Path $RuntimeRoot 'runs'
$resultsRoot = Join-Path $RuntimeRoot 'ready-stack-results'
foreach ($path in @($logsRoot, $modelsRoot, $inputsRoot, $runsRoot, $resultsRoot)) {
New-Item -ItemType Directory -Force -Path $path | Out-Null
}
if ($LogPath) {
$logParent = Split-Path -Parent $LogPath
if ($logParent) { New-Item -ItemType Directory -Force -Path $logParent | Out-Null }
Start-Transcript -LiteralPath $LogPath -Append | Out-Null
}
try {
Write-Host "E46E package: $($manifest.package_id)"
Write-Host "E46E source: $sourceJob"
Write-Host "E46E image: $image"
Assert-Sha256 $parserLibraryPath $parserSha 'official NVIDIA DeepStream TAO parser'
$modelPath = Join-Path $modelsRoot $modelFile
if (Test-Path -LiteralPath $modelPath -PathType Leaf) {
Assert-Sha256 $modelPath $modelSha 'TrafficCamNet Transformer Lite model'
}
else {
$modelTemp = "$modelPath.$([Guid]::NewGuid().ToString('N')).download"
Write-Host 'Downloading exact NVIDIA TrafficCamNet Transformer Lite model...'
& curl.exe --fail --location --retry 3 --output $modelTemp $modelUrl
if ($LASTEXITCODE -ne 0) {
throw "NVIDIA model download failed with exit code $LASTEXITCODE"
}
Assert-Sha256 $modelTemp $modelSha 'downloaded model'
Move-Item -LiteralPath $modelTemp -Destination $modelPath
}
$jobPath = Join-Path $sourceJob 'job.json'
$job = Get-Content -LiteralPath $jobPath -Raw | ConvertFrom-Json
if ($job.job_id -ne $profile.source.job_id -or
$job.input.archive_index_sha256 -ne $profile.source.archive_index_sha256 -or
$job.input.archive_summary_sha256 -ne $profile.source.archive_summary_sha256) {
throw 'Exact E46E source job binding changed.'
}
$cameraRoot = Join-Path $sourceJob 'input\camera\sensor.camera.right\epoch-1'
$indexPath = Join-Path $cameraRoot 'index.jsonl'
$summaryPath = Join-Path $cameraRoot 'summary.json'
Assert-Sha256 $indexPath ([string]$profile.source.archive_index_sha256) 'source index'
Assert-Sha256 $summaryPath ([string]$profile.source.archive_summary_sha256) 'source summary'
$summary = Get-Content -LiteralPath $summaryPath -Raw | ConvertFrom-Json
if ($summary.stream_sha256 -ne $streamSha -or
[int]$summary.segment_count -ne [int]$profile.source.segment_count) {
throw 'Source stream identity changed.'
}
$inputPath = Join-Path $inputsRoot "right-$streamSha.mp4"
if (Test-Path -LiteralPath $inputPath -PathType Leaf) {
Assert-Sha256 $inputPath $streamSha 'reconstructed RIGHT stream'
}
else {
$inputTemp = "$inputPath.$([Guid]::NewGuid().ToString('N')).tmp"
$destinationStream = [System.IO.File]::Open(
$inputTemp,
[System.IO.FileMode]::CreateNew,
[System.IO.FileAccess]::Write,
[System.IO.FileShare]::None
)
$incremental = [System.Security.Cryptography.IncrementalHash]::CreateHash(
[System.Security.Cryptography.HashAlgorithmName]::SHA256
)
try {
$sourceParts = [System.Collections.Generic.List[string]]::new()
$sourceParts.Add((Join-Path $cameraRoot 'init.mp4'))
foreach ($line in [System.IO.File]::ReadLines($indexPath)) {
$row = $line | ConvertFrom-Json
$sourceParts.Add((Join-Path $cameraRoot ([string]$row.path)))
}
if ($sourceParts.Count -ne ([int]$profile.source.segment_count + 1)) {
throw 'Source stream part count changed.'
}
$buffer = New-Object byte[] (4MB)
foreach ($part in $sourceParts) {
$inputStream = [System.IO.File]::OpenRead($part)
try {
while (($read = $inputStream.Read($buffer, 0, $buffer.Length)) -gt 0) {
$destinationStream.Write($buffer, 0, $read)
$incremental.AppendData($buffer, 0, $read)
}
}
finally {
$inputStream.Dispose()
}
}
$destinationStream.Flush($true)
$actualStreamSha = ([BitConverter]::ToString(
$incremental.GetHashAndReset()
)).Replace('-', '').ToLowerInvariant()
}
finally {
$incremental.Dispose()
$destinationStream.Dispose()
}
if ($actualStreamSha -ne $streamSha) {
throw "Reconstructed stream SHA-256 changed: $actualStreamSha"
}
Move-Item -LiteralPath $inputTemp -Destination $inputPath
}
$previousErrorActionPreference = $ErrorActionPreference
$ErrorActionPreference = 'Continue'
try {
& docker image inspect $image *> $null
$imageCached = $LASTEXITCODE -eq 0
}
finally {
$ErrorActionPreference = $previousErrorActionPreference
}
if ($imageCached) {
Write-Host 'Using the exact cached NVIDIA DeepStream image.'
}
else {
Write-Host 'Pulling exact NVIDIA DeepStream image in the interactive user session...'
Invoke-Docker @('pull', $image) 'DeepStream image pull'
}
$runId = (Get-Date).ToUniversalTime().ToString('yyyyMMddTHHmmssfffZ')
$runRoot = Join-Path $runsRoot $runId
$rawRoot = Join-Path $runRoot 'raw'
$inputMount = Join-Path $runRoot 'input'
New-Item -ItemType Directory -Force -Path $rawRoot | Out-Null
New-Item -ItemType Directory -Force -Path (Join-Path $rawRoot 'detections') | Out-Null
New-Item -ItemType Directory -Force -Path (Join-Path $rawRoot 'tracks') | Out-Null
New-Item -ItemType Directory -Force -Path $inputMount | Out-Null
Copy-Item -LiteralPath $inputPath -Destination (Join-Path $inputMount 'right.mp4')
$deepstreamLog = Join-Path $rawRoot 'deepstream.log'
$trackerCopy = Join-Path $rawRoot 'tracker-config.yml'
$startedAt = (Get-Date).ToUniversalTime().ToString('o')
$containerCommand = @"
set -euo pipefail
cp /opt/nvidia/deepstream/deepstream/samples/configs/deepstream-app/config_tracker_NvDCF_perf.yml /workspace/output/tracker-config.yml
deepstream-app -c /workspace/package/runtime/e46e_deepstream_app.txt
"@
$dockerArguments = @(
'run', '--rm', '--name', "ndc-mission-core-deepstream-e46e-$runId",
'--gpus', 'all', '--network', 'none', '--cap-drop', 'ALL',
'--security-opt', 'no-new-privileges', '--shm-size', '4g',
'--label', 'com.nodedc.product=mission-core',
'--label', 'com.nodedc.stack=perception',
'--label', 'com.nodedc.role=deepstream-ready-stack-e46e',
'--label', 'com.nodedc.managed-by=mission-core-worker',
'--mount', "type=bind,src=$inputMount,dst=/workspace/input,readonly",
'--mount', "type=bind,src=$package,dst=/workspace/package,readonly",
'--mount', "type=bind,src=$modelsRoot,dst=/workspace/model",
'--mount', "type=bind,src=$rawRoot,dst=/workspace/output",
'--entrypoint', '/bin/bash', $image, '-lc', $containerCommand
)
Write-Host 'Running full 4489-frame NVIDIA detector + NvDCF replay...'
$previousErrorActionPreference = $ErrorActionPreference
$ErrorActionPreference = 'Continue'
try {
& docker @dockerArguments 2>&1 | Tee-Object -LiteralPath $deepstreamLog
$deepstreamExit = $LASTEXITCODE
}
finally {
$ErrorActionPreference = $previousErrorActionPreference
}
if ($deepstreamExit -ne 0) {
throw "DeepStream replay failed with exit code $deepstreamExit"
}
$overlayPath = Join-Path $rawRoot 'overlay.mp4'
$enginePath = Join-Path $modelsRoot "$modelFile`_b1_gpu0_fp16.engine"
if (-not (Test-Path -LiteralPath $overlayPath -PathType Leaf) -or
(Get-Item -LiteralPath $overlayPath).Length -eq 0) {
throw 'DeepStream did not produce an overlay video.'
}
if (-not (Test-Path -LiteralPath $enginePath -PathType Leaf)) {
throw 'DeepStream did not produce the exact TensorRT engine.'
}
$detectionFiles = @(Get-ChildItem -LiteralPath (Join-Path $rawRoot 'detections') -File)
$trackFiles = @(Get-ChildItem -LiteralPath (Join-Path $rawRoot 'tracks') -File)
if ($detectionFiles.Count -ne [int]$profile.source.segment_count -or
$trackFiles.Count -ne [int]$profile.source.segment_count) {
throw "DeepStream frame coverage changed: detections=$($detectionFiles.Count), tracks=$($trackFiles.Count)"
}
$runtime = [ordered]@{
schema_version = 'missioncore.e46e-deepstream-runtime/v1'
status = 'completed'
worker_host = $env:COMPUTERNAME
gpu_name = ((& nvidia-smi --query-gpu=name --format=csv,noheader | Select-Object -First 1).Trim())
started_at_utc = $startedAt
completed_at_utc = (Get-Date).ToUniversalTime().ToString('o')
container_image = $image
container_image_digest = $imageDigest
model_sha256 = Get-Sha256 $modelPath
model_engine_sha256 = Get-Sha256 $enginePath
deepstream_config_sha256 = Get-Sha256 (Join-Path $package 'runtime\e46e_deepstream_app.txt')
detector_config_sha256 = Get-Sha256 (Join-Path $package 'runtime\e46e_trafficcamnet_rtdetr.txt')
parser_library_sha256 = Get-Sha256 $parserLibraryPath
tracker_config_sha256 = Get-Sha256 $trackerCopy
input_stream_sha256 = Get-Sha256 $inputPath
overlay_sha256 = Get-Sha256 $overlayPath
frame_count = [int]$profile.source.segment_count
deepstream_exit_code = $deepstreamExit
}
$runtimePath = Join-Path $rawRoot 'runtime.json'
$runtime | ConvertTo-Json -Depth 8 | Set-Content -LiteralPath $runtimePath -Encoding UTF8
$consolidatorImage = 'nvcr.io/nvidia/tritonserver:26.06-py3@sha256:58df7489c3f2276f9591d500a012dee03e23d35543ce3c390b4c001e6bf90794'
$consolidatorArguments = @(
'run', '--rm', '--name', "ndc-mission-core-e46e-consolidator-$runId",
'--network', 'none', '--read-only', '--cap-drop', 'ALL',
'--security-opt', 'no-new-privileges', '--tmpfs', '/tmp:rw,noexec,nosuid,size=64m',
'--mount', "type=bind,src=$package,dst=/workspace/package,readonly",
'--mount', "type=bind,src=$sourceJob,dst=/workspace/source-job,readonly",
'--mount', "type=bind,src=$rawRoot,dst=/workspace/raw,readonly",
'--mount', "type=bind,src=$resultsRoot,dst=/workspace/results",
'-e', 'PYTHONPATH=/workspace/package/runtime',
'-e', 'PYTHONDONTWRITEBYTECODE=1',
$consolidatorImage,
'python3', '/workspace/package/runtime/run_e46e_ready_stack.py',
'--source-job', '/workspace/source-job',
'--raw-root', '/workspace/raw',
'--profile', '/workspace/package/profile.json',
'--output-root', '/workspace/results'
)
Write-Host 'Freezing immutable E46E evidence...'
Invoke-Docker $consolidatorArguments 'E46E consolidation'
Write-Host "E46E_READY_STACK_COMPLETED run=$runId results=$resultsRoot"
}
finally {
if ($LogPath) { Stop-Transcript | Out-Null }
}
@@ -0,0 +1,53 @@
[CmdletBinding()]
param(
[Parameter(Mandatory = $true)]
[string]$PackageRoot,
[string]$SourceJobRoot = 'D:\NDC_MISSIONCORE\runtime\jobs\recorded-camera-602ac89026ed12978619801d',
[string]$RuntimeRoot = 'D:\NDC_MISSIONCORE\runtime\experiments\e46e'
)
$ErrorActionPreference = 'Stop'
$taskName = 'MissionCore-E46EReadyStack'
$package = (Resolve-Path -LiteralPath $PackageRoot).Path
$script = Join-Path $package 'runtime\Invoke-E46EReadyStack.ps1'
if (-not (Test-Path -LiteralPath $script -PathType Leaf)) {
throw "E46E runner is missing: $script"
}
$existing = Get-ScheduledTask -TaskName $taskName -ErrorAction SilentlyContinue
if ($existing -and $existing.State -eq 'Running') {
throw "$taskName is already running."
}
$logsRoot = Join-Path $RuntimeRoot 'logs'
New-Item -ItemType Directory -Force -Path $logsRoot | Out-Null
$stamp = (Get-Date).ToUniversalTime().ToString('yyyyMMddTHHmmssfffZ')
$logPath = Join-Path $logsRoot "e46e-ready-stack-$stamp.log"
$powerShell = "$env:SystemRoot\System32\WindowsPowerShell\v1.0\powershell.exe"
$arguments = @(
'-NoLogo', '-NoProfile', '-NonInteractive', '-ExecutionPolicy', 'Bypass',
'-File', "`"$script`"",
'-PackageRoot', "`"$package`"",
'-SourceJobRoot', "`"$SourceJobRoot`"",
'-RuntimeRoot', "`"$RuntimeRoot`"",
'-LogPath', "`"$logPath`""
) -join ' '
$userId = [System.Security.Principal.WindowsIdentity]::GetCurrent().Name
$action = New-ScheduledTaskAction -Execute $powerShell -Argument $arguments -WorkingDirectory $package
$principal = New-ScheduledTaskPrincipal -UserId $userId -LogonType Interactive -RunLevel Limited
$trigger = New-ScheduledTaskTrigger -Once -At ((Get-Date).AddMinutes(30))
$settings = New-ScheduledTaskSettingsSet `
-AllowStartIfOnBatteries `
-DontStopIfGoingOnBatteries `
-StartWhenAvailable `
-ExecutionTimeLimit ([TimeSpan]::FromHours(6))
Register-ScheduledTask `
-TaskName $taskName `
-Action $action `
-Principal $principal `
-Trigger $trigger `
-Settings $settings `
-Description 'One-shot Mission Core E46E stock NVIDIA RT-DETR plus NvDCF recorded RIGHT replay.' `
-Force | Out-Null
Start-ScheduledTask -TaskName $taskName
Write-Host "E46E_TASK_STARTED task=$taskName log=$logPath"
@@ -0,0 +1,329 @@
[CmdletBinding()]
param(
[Parameter(Mandatory = $true)]
[string]$PackageRoot,
[string]$SourceJobRoot = 'D:\NDC_MISSIONCORE\runtime\jobs\recorded-camera-602ac89026ed12978619801d',
[string]$RuntimeRoot = 'D:\NDC_MISSIONCORE\runtime\experiments\e46f',
[string]$LogPath = ''
)
$ErrorActionPreference = 'Stop'
$ProgressPreference = 'SilentlyContinue'
function Get-Sha256([string]$Path) {
return (Get-FileHash -LiteralPath $Path -Algorithm SHA256).Hash.ToLowerInvariant()
}
function Assert-Sha256([string]$Path, [string]$Expected, [string]$Label) {
if (-not (Test-Path -LiteralPath $Path -PathType Leaf)) {
throw "$Label is missing: $Path"
}
$actual = Get-Sha256 $Path
if ($actual -ne $Expected) {
throw "$Label SHA-256 changed: expected $Expected, got $actual"
}
}
function Invoke-Docker([string[]]$Arguments, [string]$Label) {
& docker @Arguments
if ($LASTEXITCODE -ne 0) {
throw "$Label failed with exit code $LASTEXITCODE"
}
}
$package = (Resolve-Path -LiteralPath $PackageRoot).Path
$sourceJob = (Resolve-Path -LiteralPath $SourceJobRoot).Path
$profilePath = Join-Path $package 'profile.json'
$manifestPath = Join-Path $package 'manifest.json'
if (-not (Test-Path -LiteralPath $profilePath -PathType Leaf) -or
-not (Test-Path -LiteralPath $manifestPath -PathType Leaf)) {
throw 'E46F package is incomplete.'
}
$manifest = Get-Content -LiteralPath $manifestPath -Raw | ConvertFrom-Json
if ($manifest.schema_version -ne 'missioncore.e46f-worker-package/v1' -or
$manifest.package_id -ne (Split-Path -Leaf $package)) {
throw 'E46F package identity is invalid.'
}
$expectedPaths = @($manifest.identity.artifact_paths)
foreach ($artifact in @($manifest.artifacts)) {
if ($expectedPaths -notcontains [string]$artifact.path) {
throw "Unexpected E46F package artifact: $($artifact.path)"
}
$artifactPath = Join-Path $package ([string]$artifact.path)
Assert-Sha256 $artifactPath ([string]$artifact.sha256) "package artifact $($artifact.path)"
if ((Get-Item -LiteralPath $artifactPath).Length -ne [int64]$artifact.byte_length) {
throw "Package artifact length changed: $($artifact.path)"
}
}
$actualPaths = @(Get-ChildItem -LiteralPath $package -Recurse -File | ForEach-Object {
$_.FullName.Substring($package.Length + 1).Replace('\', '/')
})
if (@($actualPaths | Where-Object { $_ -ne 'manifest.json' -and $expectedPaths -notcontains $_ }).Count -ne 0 -or
@($expectedPaths | Where-Object { $actualPaths -notcontains $_ }).Count -ne 0) {
throw 'E46F package file set changed.'
}
$profile = Get-Content -LiteralPath $profilePath -Raw | ConvertFrom-Json
if ($profile.schema_version -ne 'missioncore.e46f-dashcam-bakeoff-profile/v1' -or
$profile.comparison_contract.controlled_change -ne 'detector-only') {
throw 'E46F profile is incompatible.'
}
$image = [string]$profile.runtime.container_image
$imageDigestMatch = [regex]::Match($image, '@sha256:([0-9a-f]{64})$')
if (-not $imageDigestMatch.Success) {
throw 'E46F runtime image must be pinned by a full SHA-256 digest.'
}
$imageDigest = $imageDigestMatch.Groups[1].Value
$modelSha = [string]$profile.detector.model_sha256
$streamSha = [string]$profile.source.stream_sha256
$modelFile = [string]$profile.detector.model_file
$modelUrl = [string]$profile.detector.model_url
New-Item -ItemType Directory -Force -Path $RuntimeRoot | Out-Null
$logsRoot = Join-Path $RuntimeRoot 'logs'
$modelsRoot = Join-Path $RuntimeRoot "models\dashcamnet\$([string]$profile.detector.version)"
$inputsRoot = Join-Path $RuntimeRoot 'inputs'
$runsRoot = Join-Path $RuntimeRoot 'runs'
$resultsRoot = Join-Path $RuntimeRoot 'dashcam-bakeoff-results'
foreach ($path in @($logsRoot, $modelsRoot, $inputsRoot, $runsRoot, $resultsRoot)) {
New-Item -ItemType Directory -Force -Path $path | Out-Null
}
if ($LogPath) {
$logParent = Split-Path -Parent $LogPath
if ($logParent) { New-Item -ItemType Directory -Force -Path $logParent | Out-Null }
Start-Transcript -LiteralPath $LogPath -Append | Out-Null
}
try {
Write-Host "E46F package: $($manifest.package_id)"
Write-Host "E46F source: $sourceJob"
Write-Host "E46F image: $image"
$modelPath = Join-Path $modelsRoot $modelFile
if (Test-Path -LiteralPath $modelPath -PathType Leaf) {
Assert-Sha256 $modelPath $modelSha 'DashCamNet model'
}
else {
$modelTemp = "$modelPath.$([Guid]::NewGuid().ToString('N')).download"
Write-Host 'Downloading exact NVIDIA DashCamNet model...'
& curl.exe --fail --location --retry 3 --output $modelTemp $modelUrl
if ($LASTEXITCODE -ne 0) {
throw "NVIDIA model download failed with exit code $LASTEXITCODE"
}
Assert-Sha256 $modelTemp $modelSha 'downloaded DashCamNet model'
Move-Item -LiteralPath $modelTemp -Destination $modelPath
}
$jobPath = Join-Path $sourceJob 'job.json'
$job = Get-Content -LiteralPath $jobPath -Raw | ConvertFrom-Json
if ($job.job_id -ne $profile.source.job_id -or
$job.input.archive_index_sha256 -ne $profile.source.archive_index_sha256 -or
$job.input.archive_summary_sha256 -ne $profile.source.archive_summary_sha256) {
throw 'Exact E46F source job binding changed.'
}
$cameraRoot = Join-Path $sourceJob 'input\camera\sensor.camera.right\epoch-1'
$indexPath = Join-Path $cameraRoot 'index.jsonl'
$summaryPath = Join-Path $cameraRoot 'summary.json'
Assert-Sha256 $indexPath ([string]$profile.source.archive_index_sha256) 'source index'
Assert-Sha256 $summaryPath ([string]$profile.source.archive_summary_sha256) 'source summary'
$summary = Get-Content -LiteralPath $summaryPath -Raw | ConvertFrom-Json
if ($summary.stream_sha256 -ne $streamSha -or
[int]$summary.segment_count -ne [int]$profile.source.segment_count) {
throw 'Source stream identity changed.'
}
$inputPath = Join-Path $inputsRoot "right-$streamSha.mp4"
$e46eInputPath = "D:\NDC_MISSIONCORE\runtime\experiments\e46e\inputs\right-$streamSha.mp4"
if (Test-Path -LiteralPath $inputPath -PathType Leaf) {
Assert-Sha256 $inputPath $streamSha 'E46F reconstructed RIGHT stream'
}
elseif (Test-Path -LiteralPath $e46eInputPath -PathType Leaf) {
Assert-Sha256 $e46eInputPath $streamSha 'E46E controlled RIGHT stream'
Copy-Item -LiteralPath $e46eInputPath -Destination $inputPath
Assert-Sha256 $inputPath $streamSha 'E46F copied RIGHT stream'
}
else {
$inputTemp = "$inputPath.$([Guid]::NewGuid().ToString('N')).tmp"
$destinationStream = [System.IO.File]::Open(
$inputTemp,
[System.IO.FileMode]::CreateNew,
[System.IO.FileAccess]::Write,
[System.IO.FileShare]::None
)
$incremental = [System.Security.Cryptography.IncrementalHash]::CreateHash(
[System.Security.Cryptography.HashAlgorithmName]::SHA256
)
try {
$sourceParts = [System.Collections.Generic.List[string]]::new()
$sourceParts.Add((Join-Path $cameraRoot 'init.mp4'))
foreach ($line in [System.IO.File]::ReadLines($indexPath)) {
$row = $line | ConvertFrom-Json
$sourceParts.Add((Join-Path $cameraRoot ([string]$row.path)))
}
if ($sourceParts.Count -ne ([int]$profile.source.segment_count + 1)) {
throw 'Source stream part count changed.'
}
$buffer = New-Object byte[] (4MB)
foreach ($part in $sourceParts) {
$inputStream = [System.IO.File]::OpenRead($part)
try {
while (($read = $inputStream.Read($buffer, 0, $buffer.Length)) -gt 0) {
$destinationStream.Write($buffer, 0, $read)
$incremental.AppendData($buffer, 0, $read)
}
}
finally {
$inputStream.Dispose()
}
}
$destinationStream.Flush($true)
$actualStreamSha = ([BitConverter]::ToString(
$incremental.GetHashAndReset()
)).Replace('-', '').ToLowerInvariant()
}
finally {
$incremental.Dispose()
$destinationStream.Dispose()
}
if ($actualStreamSha -ne $streamSha) {
throw "Reconstructed stream SHA-256 changed: $actualStreamSha"
}
Move-Item -LiteralPath $inputTemp -Destination $inputPath
}
$previousErrorActionPreference = $ErrorActionPreference
$ErrorActionPreference = 'Continue'
try {
& docker image inspect $image *> $null
$imageCached = $LASTEXITCODE -eq 0
}
finally {
$ErrorActionPreference = $previousErrorActionPreference
}
if ($imageCached) {
Write-Host 'Using the exact cached NVIDIA DeepStream image.'
}
else {
Write-Host 'Pulling exact NVIDIA DeepStream image in the interactive user session...'
Invoke-Docker @('pull', $image) 'DeepStream image pull'
}
$runId = (Get-Date).ToUniversalTime().ToString('yyyyMMddTHHmmssfffZ')
$runRoot = Join-Path $runsRoot $runId
$rawRoot = Join-Path $runRoot 'raw'
$inputMount = Join-Path $runRoot 'input'
New-Item -ItemType Directory -Force -Path $rawRoot | Out-Null
New-Item -ItemType Directory -Force -Path (Join-Path $rawRoot 'detections') | Out-Null
New-Item -ItemType Directory -Force -Path (Join-Path $rawRoot 'tracks') | Out-Null
New-Item -ItemType Directory -Force -Path $inputMount | Out-Null
Copy-Item -LiteralPath $inputPath -Destination (Join-Path $inputMount 'right.mp4')
$deepstreamLog = Join-Path $rawRoot 'deepstream.log'
$trackerCopy = Join-Path $rawRoot 'tracker-config.yml'
$startedAt = (Get-Date).ToUniversalTime().ToString('o')
$containerCommand = @"
set -euo pipefail
cp /opt/nvidia/deepstream/deepstream/samples/configs/deepstream-app/config_tracker_NvDCF_perf.yml /workspace/output/tracker-config.yml
deepstream-app -c /workspace/package/runtime/e46f_deepstream_app.txt
"@
$dockerArguments = @(
'run', '--rm', '--name', "ndc-mission-core-deepstream-e46f-$runId",
'--gpus', 'all', '--network', 'none', '--cap-drop', 'ALL',
'--security-opt', 'no-new-privileges', '--shm-size', '4g',
'--label', 'com.nodedc.product=mission-core',
'--label', 'com.nodedc.stack=perception',
'--label', 'com.nodedc.role=deepstream-dashcam-bakeoff-e46f',
'--label', 'com.nodedc.managed-by=mission-core-worker',
'--mount', "type=bind,src=$inputMount,dst=/workspace/input,readonly",
'--mount', "type=bind,src=$package,dst=/workspace/package,readonly",
'--mount', "type=bind,src=$modelsRoot,dst=/workspace/model",
'--mount', "type=bind,src=$rawRoot,dst=/workspace/output",
'--entrypoint', '/bin/bash', $image, '-lc', $containerCommand
)
Write-Host 'Running full 4489-frame NVIDIA DashCamNet + NvDCF bake-off...'
$previousErrorActionPreference = $ErrorActionPreference
$ErrorActionPreference = 'Continue'
try {
& docker @dockerArguments 2>&1 | Tee-Object -LiteralPath $deepstreamLog
$deepstreamExit = $LASTEXITCODE
}
finally {
$ErrorActionPreference = $previousErrorActionPreference
}
if ($deepstreamExit -ne 0) {
throw "DeepStream replay failed with exit code $deepstreamExit"
}
$invalidOutputBinding = Select-String `
-LiteralPath $deepstreamLog `
-Pattern 'Could not find output layer','Given invalid tensor name' `
-SimpleMatch `
-Quiet
if ($invalidOutputBinding) {
throw 'DeepStream accepted the process but rejected the configured detector output bindings.'
}
$overlayPath = Join-Path $rawRoot 'overlay.mp4'
$enginePath = Join-Path $modelsRoot "$modelFile`_b1_gpu0_fp16.engine"
if (-not (Test-Path -LiteralPath $overlayPath -PathType Leaf) -or
(Get-Item -LiteralPath $overlayPath).Length -eq 0) {
throw 'DeepStream did not produce an overlay video.'
}
if (-not (Test-Path -LiteralPath $enginePath -PathType Leaf)) {
throw 'DeepStream did not produce the exact TensorRT engine.'
}
$detectionFiles = @(Get-ChildItem -LiteralPath (Join-Path $rawRoot 'detections') -File)
$trackFiles = @(Get-ChildItem -LiteralPath (Join-Path $rawRoot 'tracks') -File)
if ($detectionFiles.Count -ne [int]$profile.source.segment_count -or
$trackFiles.Count -ne [int]$profile.source.segment_count) {
throw "DeepStream frame coverage changed: detections=$($detectionFiles.Count), tracks=$($trackFiles.Count)"
}
$runtime = [ordered]@{
schema_version = 'missioncore.e46f-dashcam-deepstream-runtime/v1'
status = 'completed'
worker_host = $env:COMPUTERNAME
gpu_name = ((& nvidia-smi --query-gpu=name --format=csv,noheader | Select-Object -First 1).Trim())
started_at_utc = $startedAt
completed_at_utc = (Get-Date).ToUniversalTime().ToString('o')
container_image = $image
container_image_digest = $imageDigest
model_sha256 = Get-Sha256 $modelPath
model_engine_sha256 = Get-Sha256 $enginePath
deepstream_config_sha256 = Get-Sha256 (Join-Path $package 'runtime\e46f_deepstream_app.txt')
detector_config_sha256 = Get-Sha256 (Join-Path $package 'runtime\e46f_dashcamnet_detectnet.txt')
tracker_config_sha256 = Get-Sha256 $trackerCopy
input_stream_sha256 = Get-Sha256 $inputPath
overlay_sha256 = Get-Sha256 $overlayPath
frame_count = [int]$profile.source.segment_count
deepstream_exit_code = $deepstreamExit
}
$runtimePath = Join-Path $rawRoot 'runtime.json'
$runtime | ConvertTo-Json -Depth 8 | Set-Content -LiteralPath $runtimePath -Encoding UTF8
$consolidatorImage = 'nvcr.io/nvidia/tritonserver:26.06-py3@sha256:58df7489c3f2276f9591d500a012dee03e23d35543ce3c390b4c001e6bf90794'
$consolidatorArguments = @(
'run', '--rm', '--name', "ndc-mission-core-e46f-consolidator-$runId",
'--network', 'none', '--read-only', '--cap-drop', 'ALL',
'--security-opt', 'no-new-privileges', '--tmpfs', '/tmp:rw,noexec,nosuid,size=64m',
'--mount', "type=bind,src=$package,dst=/workspace/package,readonly",
'--mount', "type=bind,src=$sourceJob,dst=/workspace/source-job,readonly",
'--mount', "type=bind,src=$rawRoot,dst=/workspace/raw,readonly",
'--mount', "type=bind,src=$resultsRoot,dst=/workspace/results",
'-e', 'PYTHONPATH=/workspace/package/runtime',
'-e', 'PYTHONDONTWRITEBYTECODE=1',
$consolidatorImage,
'python3', '/workspace/package/runtime/run_e46f_dashcam_bakeoff.py',
'--source-job', '/workspace/source-job',
'--raw-root', '/workspace/raw',
'--profile', '/workspace/package/profile.json',
'--output-root', '/workspace/results'
)
Write-Host 'Freezing immutable E46F evidence...'
Invoke-Docker $consolidatorArguments 'E46F consolidation'
Write-Host "E46F_DASHCAM_BAKEOFF_COMPLETED run=$runId results=$resultsRoot"
}
finally {
if ($LogPath) { Stop-Transcript | Out-Null }
}
@@ -0,0 +1,53 @@
[CmdletBinding()]
param(
[Parameter(Mandatory = $true)]
[string]$PackageRoot,
[string]$SourceJobRoot = 'D:\NDC_MISSIONCORE\runtime\jobs\recorded-camera-602ac89026ed12978619801d',
[string]$RuntimeRoot = 'D:\NDC_MISSIONCORE\runtime\experiments\e46f'
)
$ErrorActionPreference = 'Stop'
$taskName = 'MissionCore-E46FDashCamBakeoff'
$package = (Resolve-Path -LiteralPath $PackageRoot).Path
$script = Join-Path $package 'runtime\Invoke-E46FDashCamBakeoff.ps1'
if (-not (Test-Path -LiteralPath $script -PathType Leaf)) {
throw "E46F runner is missing: $script"
}
$existing = Get-ScheduledTask -TaskName $taskName -ErrorAction SilentlyContinue
if ($existing -and $existing.State -eq 'Running') {
throw "$taskName is already running."
}
$logsRoot = Join-Path $RuntimeRoot 'logs'
New-Item -ItemType Directory -Force -Path $logsRoot | Out-Null
$stamp = (Get-Date).ToUniversalTime().ToString('yyyyMMddTHHmmssfffZ')
$logPath = Join-Path $logsRoot "e46f-dashcam-bakeoff-$stamp.log"
$powerShell = "$env:SystemRoot\System32\WindowsPowerShell\v1.0\powershell.exe"
$arguments = @(
'-NoLogo', '-NoProfile', '-NonInteractive', '-ExecutionPolicy', 'Bypass',
'-File', "`"$script`"",
'-PackageRoot', "`"$package`"",
'-SourceJobRoot', "`"$SourceJobRoot`"",
'-RuntimeRoot', "`"$RuntimeRoot`"",
'-LogPath', "`"$logPath`""
) -join ' '
$userId = [System.Security.Principal.WindowsIdentity]::GetCurrent().Name
$action = New-ScheduledTaskAction -Execute $powerShell -Argument $arguments -WorkingDirectory $package
$principal = New-ScheduledTaskPrincipal -UserId $userId -LogonType Interactive -RunLevel Limited
$trigger = New-ScheduledTaskTrigger -Once -At ((Get-Date).AddMinutes(30))
$settings = New-ScheduledTaskSettingsSet `
-AllowStartIfOnBatteries `
-DontStopIfGoingOnBatteries `
-StartWhenAvailable `
-ExecutionTimeLimit ([TimeSpan]::FromHours(6))
Register-ScheduledTask `
-TaskName $taskName `
-Action $action `
-Principal $principal `
-Trigger $trigger `
-Settings $settings `
-Description 'One-shot Mission Core E46F stock NVIDIA DashCamNet plus NvDCF detector-only bake-off.' `
-Force | Out-Null
Start-ScheduledTask -TaskName $taskName
Write-Host "E46F_TASK_STARTED task=$taskName log=$logPath"
@@ -0,0 +1,440 @@
[CmdletBinding()]
param(
[Parameter(Mandatory = $true)]
[string]$PackageRoot,
[string]$SourceJobRoot = 'D:\NDC_MISSIONCORE\runtime\jobs\recorded-camera-602ac89026ed12978619801d',
[string]$RuntimeRoot = 'D:\NDC_MISSIONCORE\runtime\experiments\e46g',
[string]$LogPath = ''
)
$ErrorActionPreference = 'Stop'
$ProgressPreference = 'SilentlyContinue'
function Get-Sha256([string]$Path) {
return (Get-FileHash -LiteralPath $Path -Algorithm SHA256).Hash.ToLowerInvariant()
}
function Assert-Sha256([string]$Path, [string]$Expected, [string]$Label) {
if (-not (Test-Path -LiteralPath $Path -PathType Leaf)) {
throw "$Label is missing: $Path"
}
$actual = Get-Sha256 $Path
if ($actual -ne $Expected) {
throw "$Label SHA-256 changed: expected $Expected, got $actual"
}
}
function Invoke-Docker([string[]]$Arguments, [string]$Label) {
& docker @Arguments
if ($LASTEXITCODE -ne 0) {
throw "$Label failed with exit code $LASTEXITCODE"
}
}
function Get-VideoFrameCount([string]$Path) {
$probe = & ffprobe -v error -select_streams v:0 -count_frames `
-show_entries stream=nb_read_frames -of json $Path | ConvertFrom-Json
if ($LASTEXITCODE -ne 0) {
throw "ffprobe failed: $Path"
}
return [int]@($probe.streams)[0].nb_read_frames
}
function Ensure-Model(
[pscustomobject]$Candidate,
[string]$ModelRoot,
[string]$Label
) {
New-Item -ItemType Directory -Force -Path $ModelRoot | Out-Null
$modelPath = Join-Path $ModelRoot ([string]$Candidate.model_file)
if (Test-Path -LiteralPath $modelPath -PathType Leaf) {
Assert-Sha256 $modelPath ([string]$Candidate.model_sha256) $Label
return $modelPath
}
$temporary = "$modelPath.$([Guid]::NewGuid().ToString('N')).download"
& curl.exe --fail --location --retry 3 --output $temporary ([string]$Candidate.model_url)
if ($LASTEXITCODE -ne 0) {
throw "$Label download failed with exit code $LASTEXITCODE"
}
Assert-Sha256 $temporary ([string]$Candidate.model_sha256) "downloaded $Label"
Move-Item -LiteralPath $temporary -Destination $modelPath
return $modelPath
}
$package = (Resolve-Path -LiteralPath $PackageRoot).Path
$sourceJob = (Resolve-Path -LiteralPath $SourceJobRoot).Path
$profilePath = Join-Path $package 'profile.json'
$manifestPath = Join-Path $package 'manifest.json'
if (-not (Test-Path -LiteralPath $profilePath -PathType Leaf) -or
-not (Test-Path -LiteralPath $manifestPath -PathType Leaf)) {
throw 'E46G package is incomplete.'
}
$manifest = Get-Content -LiteralPath $manifestPath -Raw | ConvertFrom-Json
if ($manifest.schema_version -ne 'missioncore.e46g-worker-package/v1' -or
$manifest.package_id -ne (Split-Path -Leaf $package)) {
throw 'E46G package identity is invalid.'
}
$expectedPaths = @($manifest.identity.artifact_paths)
foreach ($artifact in @($manifest.artifacts)) {
if ($expectedPaths -notcontains [string]$artifact.path) {
throw "Unexpected E46G package artifact: $($artifact.path)"
}
$artifactPath = Join-Path $package ([string]$artifact.path)
Assert-Sha256 $artifactPath ([string]$artifact.sha256) "package artifact $($artifact.path)"
if ((Get-Item -LiteralPath $artifactPath).Length -ne [int64]$artifact.byte_length) {
throw "Package artifact length changed: $($artifact.path)"
}
}
$actualPaths = @(Get-ChildItem -LiteralPath $package -Recurse -File | ForEach-Object {
$_.FullName.Substring($package.Length + 1).Replace('\', '/')
})
if (@($actualPaths | Where-Object { $_ -ne 'manifest.json' -and $expectedPaths -notcontains $_ }).Count -ne 0 -or
@($expectedPaths | Where-Object { $actualPaths -notcontains $_ }).Count -ne 0) {
throw 'E46G package file set changed.'
}
$profile = Get-Content -LiteralPath $profilePath -Raw | ConvertFrom-Json
if ($profile.schema_version -ne 'missioncore.e46g-rectified-detector-bakeoff-profile/v1' -or
$profile.source.camera_source_id -ne 'sensor.camera.right' -or
$profile.rectification.provider -ne 'NVIDIA Gst-nvdewarper') {
throw 'E46G profile is incompatible.'
}
$image = [string]$profile.runtime.container_image
$imageDigestMatch = [regex]::Match($image, '@sha256:([0-9a-f]{64})$')
if (-not $imageDigestMatch.Success) {
throw 'E46G runtime image must be pinned by a full SHA-256 digest.'
}
$imageDigest = $imageDigestMatch.Groups[1].Value
$streamSha = [string]$profile.source.stream_sha256
$sampleFrameCount = [int]$profile.selection.frame_count
$firstSourceFrame = [int]$profile.selection.first_source_frame_index
$lastSourceFrame = [int]$profile.selection.last_source_frame_index
$expectedFullFrameCount = [int]$profile.rectification.expected_full_frame_count
$retainedSourceRange = @($profile.rectification.retained_source_frame_index_range)
if ($retainedSourceRange.Count -ne 2 -or
$firstSourceFrame -lt [int]$retainedSourceRange[0] -or
$lastSourceFrame -gt [int]$retainedSourceRange[1]) {
throw 'E46G selection is outside the admitted NVIDIA-decoded source prefix.'
}
$parserPath = Join-Path $package "runtime\$([string]$profile.trafficcamnet_parser.library_file)"
Assert-Sha256 $parserPath ([string]$profile.trafficcamnet_parser.library_sha256) `
'official NVIDIA DeepStream TAO parser'
if (-not (Get-Command ffmpeg -ErrorAction SilentlyContinue) -or
-not (Get-Command ffprobe -ErrorAction SilentlyContinue)) {
throw 'E46G requires the existing Worker ffmpeg/ffprobe installation.'
}
New-Item -ItemType Directory -Force -Path $RuntimeRoot | Out-Null
$inputsRoot = Join-Path $RuntimeRoot 'inputs'
$runsRoot = Join-Path $RuntimeRoot 'runs'
$resultsRoot = Join-Path $RuntimeRoot 'rectified-detector-bakeoff-results'
foreach ($path in @($inputsRoot, $runsRoot, $resultsRoot)) {
New-Item -ItemType Directory -Force -Path $path | Out-Null
}
if ($LogPath) {
$logParent = Split-Path -Parent $LogPath
if ($logParent) { New-Item -ItemType Directory -Force -Path $logParent | Out-Null }
Start-Transcript -LiteralPath $LogPath -Append | Out-Null
}
try {
$jobPath = Join-Path $sourceJob 'job.json'
$job = Get-Content -LiteralPath $jobPath -Raw | ConvertFrom-Json
if ($job.job_id -ne $profile.source.job_id -or
$job.input.archive_index_sha256 -ne $profile.source.archive_index_sha256 -or
$job.input.archive_summary_sha256 -ne $profile.source.archive_summary_sha256) {
throw 'Exact E46G source job binding changed.'
}
$inputPath = Join-Path $inputsRoot "right-$streamSha.mp4"
$e46eInput = "D:\NDC_MISSIONCORE\runtime\experiments\e46e\inputs\right-$streamSha.mp4"
$e46fInput = "D:\NDC_MISSIONCORE\runtime\experiments\e46f\inputs\right-$streamSha.mp4"
if (Test-Path -LiteralPath $inputPath -PathType Leaf) {
Assert-Sha256 $inputPath $streamSha 'E46G controlled RIGHT stream'
}
elseif (Test-Path -LiteralPath $e46eInput -PathType Leaf) {
Assert-Sha256 $e46eInput $streamSha 'E46E controlled RIGHT stream'
Copy-Item -LiteralPath $e46eInput -Destination $inputPath
}
elseif (Test-Path -LiteralPath $e46fInput -PathType Leaf) {
Assert-Sha256 $e46fInput $streamSha 'E46F controlled RIGHT stream'
Copy-Item -LiteralPath $e46fInput -Destination $inputPath
}
else {
& (Join-Path $package 'runtime\Prepare-RectifiedCameraReplay.ps1') `
-JobRoot $sourceJob -OutputPath $inputPath
}
Assert-Sha256 $inputPath $streamSha 'E46G reconstructed RIGHT stream'
$trafficModelRoot = 'D:\NDC_MISSIONCORE\runtime\experiments\e46e\models\trafficcamnet_transformer_lite\deployable_resnet50_v2.0'
$dashModelRoot = 'D:\NDC_MISSIONCORE\runtime\experiments\e46f\models\dashcamnet\pruned_onnx_v1.0.4'
$trafficModel = Ensure-Model $profile.candidates.trafficcamnet $trafficModelRoot 'TrafficCamNet model'
$dashModel = Ensure-Model $profile.candidates.dashcamnet $dashModelRoot 'DashCamNet model'
$previousErrorActionPreference = $ErrorActionPreference
$ErrorActionPreference = 'Continue'
try {
& docker image inspect $image *> $null
$imageCached = $LASTEXITCODE -eq 0
}
finally {
$ErrorActionPreference = $previousErrorActionPreference
}
if (-not $imageCached) {
Invoke-Docker @('pull', $image) 'DeepStream image pull'
}
$runId = (Get-Date).ToUniversalTime().ToString('yyyyMMddTHHmmssfffZ')
$runRoot = Join-Path $runsRoot $runId
$rawRoot = Join-Path $runRoot 'raw'
$sourceInputMount = Join-Path $runRoot 'source-input'
$geometryRoot = Join-Path $rawRoot 'geometry'
$samplesRoot = Join-Path $rawRoot 'samples'
$comparisonRoot = Join-Path $rawRoot 'comparison'
foreach ($path in @(
$rawRoot,
$sourceInputMount,
$geometryRoot,
$samplesRoot,
$comparisonRoot
)) {
New-Item -ItemType Directory -Force -Path $path | Out-Null
}
Copy-Item -LiteralPath $inputPath -Destination (Join-Path $sourceInputMount 'right.mp4')
$workerLog = Join-Path $rawRoot 'worker.log'
"E46G run $runId`nsource=$streamSha`nselection=$firstSourceFrame..$lastSourceFrame" |
Set-Content -LiteralPath $workerLog -Encoding UTF8
$startedAt = (Get-Date).ToUniversalTime().ToString('o')
$geometryRuntime = [ordered]@{}
foreach ($view in @('left', 'front', 'right')) {
$viewProfile = $profile.rectification.views.$view
$configName = [string]$viewProfile.config_file
$configPath = Join-Path $package "runtime\$configName"
Assert-Sha256 $configPath ([string]$viewProfile.config_sha256) "$view dewarper config"
$outputPath = Join-Path $geometryRoot "$view.mp4"
$containerCommand = @"
set -euo pipefail
gst-launch-1.0 -e filesrc location=/workspace/input/right.mp4 ! qtdemux ! h264parse ! nvv4l2decoder ! nvvideoconvert ! 'video/x-raw(memory:NVMM),format=RGBA' ! nvdewarper config-file=/workspace/package/runtime/$configName source-id=0 num-batch-buffers=1 ! nvvideoconvert ! 'video/x-raw(memory:NVMM),format=NV12' ! nvv4l2h264enc bitrate=6000000 ! h264parse ! qtmux ! filesink location=/workspace/output/$view.mp4
"@
$dewarperLog = Join-Path $geometryRoot "$view.log"
$dockerArguments = @(
'run', '--rm', '--name', "ndc-mission-core-e46g-dewarper-$view-$runId",
'--gpus', 'all', '--network', 'none', '--cap-drop', 'ALL',
'--security-opt', 'no-new-privileges', '--shm-size', '4g',
'--label', 'com.nodedc.product=mission-core',
'--label', 'com.nodedc.stack=perception',
'--label', 'com.nodedc.role=nvdewarper-e46g',
'--mount', "type=bind,src=$sourceInputMount,dst=/workspace/input,readonly",
'--mount', "type=bind,src=$package,dst=/workspace/package,readonly",
'--mount', "type=bind,src=$geometryRoot,dst=/workspace/output",
'--entrypoint', '/bin/bash', $image, '-lc', $containerCommand
)
$previousErrorActionPreference = $ErrorActionPreference
$ErrorActionPreference = 'Continue'
try {
& docker @dockerArguments 2>&1 | Tee-Object -LiteralPath $dewarperLog
$dewarperExit = $LASTEXITCODE
}
finally {
$ErrorActionPreference = $previousErrorActionPreference
}
if ($dewarperExit -ne 0 -or -not (Test-Path -LiteralPath $outputPath -PathType Leaf)) {
throw "NVIDIA nvdewarper failed for $view with exit code $dewarperExit"
}
if ((Get-VideoFrameCount $outputPath) -ne $expectedFullFrameCount) {
throw "Full rectified $view frame coverage changed."
}
$samplePath = Join-Path $samplesRoot "$view.mp4"
$filter = "select='between(n\,$firstSourceFrame\,$lastSourceFrame)',setpts=N/(10*TB)"
& ffmpeg -hide_banner -loglevel error -y -i $outputPath -vf $filter -an `
-c:v libx264 -preset fast -crf 18 -pix_fmt yuv420p -r 10 $samplePath
if ($LASTEXITCODE -ne 0 -or (Get-VideoFrameCount $samplePath) -ne $sampleFrameCount) {
throw "Exact E46G sample extraction failed for $view."
}
$geometryRuntime[$view] = [ordered]@{
dewarper_config_sha256 = Get-Sha256 $configPath
dewarper_log_sha256 = Get-Sha256 $dewarperLog
full_rectified_video_path = "geometry/$view.mp4"
full_rectified_video_sha256 = Get-Sha256 $outputPath
sample_video_path = "samples/$view.mp4"
sample_video_sha256 = Get-Sha256 $samplePath
full_frame_count = $expectedFullFrameCount
retained_source_frame_index_range = @(
[int]$retainedSourceRange[0],
[int]$retainedSourceRange[1]
)
excluded_source_tail_frame_count = [int]$profile.rectification.excluded_source_tail_frame_count
sample_frame_count = $sampleFrameCount
}
}
$candidateRuntime = [ordered]@{}
$candidateDefinitions = @(
[pscustomobject]@{
Name = 'trafficcamnet'
ModelRoot = $trafficModelRoot
ModelPath = $trafficModel
},
[pscustomobject]@{
Name = 'dashcamnet'
ModelRoot = $dashModelRoot
ModelPath = $dashModel
}
)
foreach ($definition in $candidateDefinitions) {
$candidate = [string]$definition.Name
$candidateProfile = $profile.candidates.$candidate
$appConfigPath = Join-Path $package "runtime\$([string]$candidateProfile.deepstream_app_config)"
$detectorConfigPath = Join-Path $package "runtime\$([string]$candidateProfile.detector_config)"
Assert-Sha256 $appConfigPath ([string]$candidateProfile.deepstream_app_config_sha256) `
"$candidate DeepStream app config"
Assert-Sha256 $detectorConfigPath ([string]$candidateProfile.detector_config_sha256) `
"$candidate detector config"
$runs = [ordered]@{}
foreach ($view in @('left', 'front', 'right')) {
$viewRoot = Join-Path $rawRoot "runs\$candidate\$view"
$inputMount = Join-Path $viewRoot 'input'
foreach ($path in @(
$viewRoot,
$inputMount,
(Join-Path $viewRoot 'detections'),
(Join-Path $viewRoot 'tracks')
)) {
New-Item -ItemType Directory -Force -Path $path | Out-Null
}
Copy-Item -LiteralPath (Join-Path $samplesRoot "$view.mp4") `
-Destination (Join-Path $inputMount 'view.mp4')
$deepstreamLog = Join-Path $viewRoot 'deepstream.log'
$containerCommand = @"
set -euo pipefail
cp /opt/nvidia/deepstream/deepstream/samples/configs/deepstream-app/config_tracker_NvDCF_perf.yml /workspace/output/tracker-config.yml
deepstream-app -c /workspace/package/runtime/$([string]$candidateProfile.deepstream_app_config)
"@
$dockerArguments = @(
'run', '--rm', '--name', "ndc-mission-core-e46g-$candidate-$view-$runId",
'--gpus', 'all', '--network', 'none', '--cap-drop', 'ALL',
'--security-opt', 'no-new-privileges', '--shm-size', '4g',
'--label', 'com.nodedc.product=mission-core',
'--label', 'com.nodedc.stack=perception',
'--label', "com.nodedc.role=e46g-$candidate-$view",
'--mount', "type=bind,src=$inputMount,dst=/workspace/input,readonly",
'--mount', "type=bind,src=$package,dst=/workspace/package,readonly",
'--mount', "type=bind,src=$($definition.ModelRoot),dst=/workspace/model",
'--mount', "type=bind,src=$viewRoot,dst=/workspace/output",
'--entrypoint', '/bin/bash', $image, '-lc', $containerCommand
)
$previousErrorActionPreference = $ErrorActionPreference
$ErrorActionPreference = 'Continue'
try {
& docker @dockerArguments 2>&1 | Tee-Object -LiteralPath $deepstreamLog
$deepstreamExit = $LASTEXITCODE
}
finally {
$ErrorActionPreference = $previousErrorActionPreference
}
if ($deepstreamExit -ne 0) {
throw "DeepStream failed for $candidate/$view with exit code $deepstreamExit"
}
$overlayPath = Join-Path $viewRoot 'overlay.mp4'
$trackerPath = Join-Path $viewRoot 'tracker-config.yml'
$detections = @(Get-ChildItem -LiteralPath (Join-Path $viewRoot 'detections') -File)
$tracks = @(Get-ChildItem -LiteralPath (Join-Path $viewRoot 'tracks') -File)
if (-not (Test-Path -LiteralPath $overlayPath -PathType Leaf) -or
$detections.Count -ne $sampleFrameCount -or $tracks.Count -ne $sampleFrameCount) {
throw "DeepStream output coverage changed for $candidate/$view."
}
$enginePath = "$($definition.ModelPath)_b1_gpu0_fp16.engine"
if (-not (Test-Path -LiteralPath $enginePath -PathType Leaf)) {
throw "TensorRT engine is missing for $candidate."
}
$runs[$view] = [ordered]@{
overlay_path = "runs/$candidate/$view/overlay.mp4"
overlay_sha256 = Get-Sha256 $overlayPath
deepstream_log_path = "runs/$candidate/$view/deepstream.log"
deepstream_log_sha256 = Get-Sha256 $deepstreamLog
tracker_config_sha256 = Get-Sha256 $trackerPath
model_engine_sha256 = Get-Sha256 $enginePath
frame_count = $sampleFrameCount
deepstream_exit_code = $deepstreamExit
}
}
$candidateRuntime[$candidate] = [ordered]@{
model_sha256 = Get-Sha256 $definition.ModelPath
deepstream_app_config_sha256 = Get-Sha256 $appConfigPath
detector_config_sha256 = Get-Sha256 $detectorConfigPath
parser_library_sha256 = $(if ($candidate -eq 'trafficcamnet') {
Get-Sha256 $parserPath
} else { $null })
runs = $runs
}
}
$comparisonRuntime = [ordered]@{}
foreach ($candidate in @('trafficcamnet', 'dashcamnet')) {
$left = Join-Path $rawRoot "runs\$candidate\left\overlay.mp4"
$front = Join-Path $rawRoot "runs\$candidate\front\overlay.mp4"
$right = Join-Path $rawRoot "runs\$candidate\right\overlay.mp4"
$output = Join-Path $comparisonRoot "$candidate.mp4"
& ffmpeg -hide_banner -loglevel error -y -i $left -i $front -i $right `
-filter_complex '[0:v][1:v][2:v]hstack=inputs=3[v]' -map '[v]' -an `
-c:v libx264 -preset fast -crf 20 -pix_fmt yuv420p -movflags +faststart $output
if ($LASTEXITCODE -ne 0 -or (Get-VideoFrameCount $output) -ne $sampleFrameCount) {
throw "E46G synchronized comparison video failed for $candidate."
}
$comparisonRuntime[$candidate] = [ordered]@{
video_path = "comparison/$candidate.mp4"
video_sha256 = Get-Sha256 $output
frame_count = $sampleFrameCount
view_order = @('left', 'front', 'right')
}
}
$runtime = [ordered]@{
schema_version = 'missioncore.e46g-rectified-detector-runtime/v1'
status = 'completed'
worker_host = $env:COMPUTERNAME
gpu_name = ((& nvidia-smi --query-gpu=name --format=csv,noheader | Select-Object -First 1).Trim())
started_at_utc = $startedAt
completed_at_utc = (Get-Date).ToUniversalTime().ToString('o')
container_image = $image
container_image_digest = $imageDigest
source_stream_sha256 = Get-Sha256 $inputPath
first_source_frame_index = $firstSourceFrame
sample_frame_count = $sampleFrameCount
geometry = $geometryRuntime
candidates = $candidateRuntime
comparison = $comparisonRuntime
}
$runtime | ConvertTo-Json -Depth 16 |
Set-Content -LiteralPath (Join-Path $rawRoot 'runtime.json') -Encoding UTF8
Add-Content -LiteralPath $workerLog -Value "completed=$(Get-Date -Format o)"
$consolidatorImage = 'nvcr.io/nvidia/tritonserver:26.06-py3@sha256:58df7489c3f2276f9591d500a012dee03e23d35543ce3c390b4c001e6bf90794'
$consolidatorArguments = @(
'run', '--rm', '--name', "ndc-mission-core-e46g-consolidator-$runId",
'--network', 'none', '--read-only', '--cap-drop', 'ALL',
'--security-opt', 'no-new-privileges', '--tmpfs', '/tmp:rw,noexec,nosuid,size=64m',
'--mount', "type=bind,src=$package,dst=/workspace/package,readonly",
'--mount', "type=bind,src=$sourceJob,dst=/workspace/source-job,readonly",
'--mount', "type=bind,src=$rawRoot,dst=/workspace/raw,readonly",
'--mount', "type=bind,src=$resultsRoot,dst=/workspace/results",
'-e', 'PYTHONPATH=/workspace/package/runtime',
'-e', 'PYTHONDONTWRITEBYTECODE=1',
$consolidatorImage,
'python3', '/workspace/package/runtime/run_e46g_rectified_detector_bakeoff.py',
'--source-job', '/workspace/source-job',
'--raw-root', '/workspace/raw',
'--profile', '/workspace/package/profile.json',
'--output-root', '/workspace/results'
)
Invoke-Docker $consolidatorArguments 'E46G consolidation'
Write-Host "E46G_RECTIFIED_DETECTOR_BAKEOFF_COMPLETED run=$runId results=$resultsRoot"
}
finally {
if ($LogPath) { Stop-Transcript | Out-Null }
}
@@ -0,0 +1,53 @@
[CmdletBinding()]
param(
[Parameter(Mandatory = $true)]
[string]$PackageRoot,
[string]$SourceJobRoot = 'D:\NDC_MISSIONCORE\runtime\jobs\recorded-camera-602ac89026ed12978619801d',
[string]$RuntimeRoot = 'D:\NDC_MISSIONCORE\runtime\experiments\e46g'
)
$ErrorActionPreference = 'Stop'
$taskName = 'MissionCore-E46GRectifiedDetectorBakeoff'
$package = (Resolve-Path -LiteralPath $PackageRoot).Path
$script = Join-Path $package 'runtime\Invoke-E46GRectifiedDetectorBakeoff.ps1'
if (-not (Test-Path -LiteralPath $script -PathType Leaf)) {
throw "E46G runner is missing: $script"
}
$existing = Get-ScheduledTask -TaskName $taskName -ErrorAction SilentlyContinue
if ($existing -and $existing.State -eq 'Running') {
throw "$taskName is already running."
}
$logsRoot = Join-Path $RuntimeRoot 'logs'
New-Item -ItemType Directory -Force -Path $logsRoot | Out-Null
$stamp = (Get-Date).ToUniversalTime().ToString('yyyyMMddTHHmmssfffZ')
$logPath = Join-Path $logsRoot "e46g-rectified-detector-bakeoff-$stamp.log"
$powerShell = "$env:SystemRoot\System32\WindowsPowerShell\v1.0\powershell.exe"
$arguments = @(
'-NoLogo', '-NoProfile', '-NonInteractive', '-ExecutionPolicy', 'Bypass',
'-File', "`"$script`"",
'-PackageRoot', "`"$package`"",
'-SourceJobRoot', "`"$SourceJobRoot`"",
'-RuntimeRoot', "`"$RuntimeRoot`"",
'-LogPath', "`"$logPath`""
) -join ' '
$userId = [System.Security.Principal.WindowsIdentity]::GetCurrent().Name
$action = New-ScheduledTaskAction -Execute $powerShell -Argument $arguments -WorkingDirectory $package
$principal = New-ScheduledTaskPrincipal -UserId $userId -LogonType Interactive -RunLevel Limited
$trigger = New-ScheduledTaskTrigger -Once -At ((Get-Date).AddMinutes(30))
$settings = New-ScheduledTaskSettingsSet `
-AllowStartIfOnBatteries `
-DontStopIfGoingOnBatteries `
-StartWhenAvailable `
-ExecutionTimeLimit ([TimeSpan]::FromHours(6))
Register-ScheduledTask `
-TaskName $taskName `
-Action $action `
-Principal $principal `
-Trigger $trigger `
-Settings $settings `
-Description 'One-shot E46G factory-KB4 NVIDIA nvdewarper detector A/B.' `
-Force | Out-Null
Start-ScheduledTask -TaskName $taskName
Write-Host "E46G_TASK_STARTED task=$taskName log=$logPath"
@@ -0,0 +1,369 @@
[CmdletBinding()]
param(
[Parameter(Mandatory = $true)]
[string]$PackageRoot,
[string]$SourceJobRoot = 'D:\NDC_MISSIONCORE\runtime\jobs\recorded-camera-602ac89026ed12978619801d',
[string]$RuntimeRoot = 'D:\NDC_MISSIONCORE\runtime\experiments\e46h',
[string]$LogPath = ''
)
$ErrorActionPreference = 'Stop'
$ProgressPreference = 'SilentlyContinue'
function Get-Sha256([string]$Path) {
return (Get-FileHash -LiteralPath $Path -Algorithm SHA256).Hash.ToLowerInvariant()
}
function Assert-Sha256([string]$Path, [string]$Expected, [string]$Label) {
if (-not (Test-Path -LiteralPath $Path -PathType Leaf)) {
throw "$Label is missing: $Path"
}
$actual = Get-Sha256 $Path
if ($actual -ne $Expected) {
throw "$Label SHA-256 changed: expected $Expected, got $actual"
}
}
function Invoke-Docker([string[]]$Arguments, [string]$Label) {
& docker @Arguments
if ($LASTEXITCODE -ne 0) {
throw "$Label failed with exit code $LASTEXITCODE"
}
}
function Get-VideoFrameCount([string]$Path) {
$probe = & ffprobe -v error -select_streams v:0 -count_frames `
-show_entries stream=nb_read_frames -of json $Path | ConvertFrom-Json
if ($LASTEXITCODE -ne 0) {
throw "ffprobe failed: $Path"
}
return [int]@($probe.streams)[0].nb_read_frames
}
function Ensure-Model([pscustomobject]$Detector, [string]$ModelRoot) {
New-Item -ItemType Directory -Force -Path $ModelRoot | Out-Null
$modelPath = Join-Path $ModelRoot ([string]$Detector.model_file)
if (Test-Path -LiteralPath $modelPath -PathType Leaf) {
Assert-Sha256 $modelPath ([string]$Detector.model_sha256) 'TrafficCamNet model'
return $modelPath
}
$temporary = "$modelPath.$([Guid]::NewGuid().ToString('N')).download"
& curl.exe --fail --location --retry 3 --output $temporary ([string]$Detector.model_url)
if ($LASTEXITCODE -ne 0) {
throw "TrafficCamNet download failed with exit code $LASTEXITCODE"
}
Assert-Sha256 $temporary ([string]$Detector.model_sha256) 'downloaded TrafficCamNet model'
Move-Item -LiteralPath $temporary -Destination $modelPath
return $modelPath
}
$package = (Resolve-Path -LiteralPath $PackageRoot).Path
$sourceJob = (Resolve-Path -LiteralPath $SourceJobRoot).Path
$profilePath = Join-Path $package 'profile.json'
$manifestPath = Join-Path $package 'manifest.json'
if (-not (Test-Path -LiteralPath $profilePath -PathType Leaf) -or
-not (Test-Path -LiteralPath $manifestPath -PathType Leaf)) {
throw 'E46H package is incomplete.'
}
$manifest = Get-Content -LiteralPath $manifestPath -Raw | ConvertFrom-Json
if ($manifest.schema_version -ne 'missioncore.e46h-worker-package/v1' -or
$manifest.package_id -ne (Split-Path -Leaf $package)) {
throw 'E46H package identity is invalid.'
}
$expectedPaths = @($manifest.identity.artifact_paths)
foreach ($artifact in @($manifest.artifacts)) {
if ($expectedPaths -notcontains [string]$artifact.path) {
throw "Unexpected E46H package artifact: $($artifact.path)"
}
$artifactPath = Join-Path $package ([string]$artifact.path)
Assert-Sha256 $artifactPath ([string]$artifact.sha256) "package artifact $($artifact.path)"
if ((Get-Item -LiteralPath $artifactPath).Length -ne [int64]$artifact.byte_length) {
throw "Package artifact length changed: $($artifact.path)"
}
}
$actualPaths = @(Get-ChildItem -LiteralPath $package -Recurse -File | ForEach-Object {
$_.FullName.Substring($package.Length + 1).Replace('\', '/')
})
if (@($actualPaths | Where-Object { $_ -ne 'manifest.json' -and $expectedPaths -notcontains $_ }).Count -ne 0 -or
@($expectedPaths | Where-Object { $actualPaths -notcontains $_ }).Count -ne 0) {
throw 'E46H package file set changed.'
}
$profile = Get-Content -LiteralPath $profilePath -Raw | ConvertFrom-Json
if ($profile.schema_version -ne 'missioncore.e46h-full-rectified-front-replay-profile/v1' -or
$profile.source.camera_source_id -ne 'sensor.camera.right' -or
$profile.rectification.view -ne 'front' -or
$profile.detector.name -ne 'NVIDIA TrafficCamNet Transformer Lite') {
throw 'E46H profile is incompatible.'
}
$image = [string]$profile.runtime.container_image
$imageDigestMatch = [regex]::Match($image, '@sha256:([0-9a-f]{64})$')
if (-not $imageDigestMatch.Success) {
throw 'E46H runtime image must be pinned by a full SHA-256 digest.'
}
$imageDigest = $imageDigestMatch.Groups[1].Value
$streamSha = [string]$profile.source.stream_sha256
$frameCount = [int]$profile.selection.frame_count
if ($frameCount -ne 4488 -or
[int]$profile.selection.first_source_frame_index -ne 0 -or
[int]$profile.selection.last_source_frame_index -ne 4487) {
throw 'E46H retained route contract changed.'
}
$parserPath = Join-Path $package "runtime\$([string]$profile.parser.library_file)"
Assert-Sha256 $parserPath ([string]$profile.parser.library_sha256) `
'official NVIDIA DeepStream TAO parser'
if (-not (Get-Command ffmpeg -ErrorAction SilentlyContinue) -or
-not (Get-Command ffprobe -ErrorAction SilentlyContinue)) {
throw 'E46H requires the existing Worker ffmpeg/ffprobe installation.'
}
New-Item -ItemType Directory -Force -Path $RuntimeRoot | Out-Null
$inputsRoot = Join-Path $RuntimeRoot 'inputs'
$runsRoot = Join-Path $RuntimeRoot 'runs'
$resultsRoot = Join-Path $RuntimeRoot 'full-rectified-front-results'
foreach ($path in @($inputsRoot, $runsRoot, $resultsRoot)) {
New-Item -ItemType Directory -Force -Path $path | Out-Null
}
if ($LogPath) {
$logParent = Split-Path -Parent $LogPath
if ($logParent) { New-Item -ItemType Directory -Force -Path $logParent | Out-Null }
Start-Transcript -LiteralPath $LogPath -Append | Out-Null
}
try {
$jobPath = Join-Path $sourceJob 'job.json'
$job = Get-Content -LiteralPath $jobPath -Raw | ConvertFrom-Json
if ($job.job_id -ne $profile.source.job_id -or
$job.input.archive_index_sha256 -ne $profile.source.archive_index_sha256 -or
$job.input.archive_summary_sha256 -ne $profile.source.archive_summary_sha256) {
throw 'Exact E46H source job binding changed.'
}
$inputPath = Join-Path $inputsRoot "right-$streamSha.mp4"
$e46gInput = "D:\NDC_MISSIONCORE\runtime\experiments\e46g\inputs\right-$streamSha.mp4"
$e46eInput = "D:\NDC_MISSIONCORE\runtime\experiments\e46e\inputs\right-$streamSha.mp4"
if (Test-Path -LiteralPath $inputPath -PathType Leaf) {
Assert-Sha256 $inputPath $streamSha 'E46H controlled RIGHT stream'
}
elseif (Test-Path -LiteralPath $e46gInput -PathType Leaf) {
Assert-Sha256 $e46gInput $streamSha 'E46G controlled RIGHT stream'
Copy-Item -LiteralPath $e46gInput -Destination $inputPath
}
elseif (Test-Path -LiteralPath $e46eInput -PathType Leaf) {
Assert-Sha256 $e46eInput $streamSha 'E46E controlled RIGHT stream'
Copy-Item -LiteralPath $e46eInput -Destination $inputPath
}
else {
& (Join-Path $package 'runtime\Prepare-RectifiedCameraReplay.ps1') `
-JobRoot $sourceJob -OutputPath $inputPath
}
Assert-Sha256 $inputPath $streamSha 'E46H reconstructed RIGHT stream'
$modelRoot = 'D:\NDC_MISSIONCORE\runtime\experiments\e46e\models\trafficcamnet_transformer_lite\deployable_resnet50_v2.0'
$modelPath = Ensure-Model $profile.detector $modelRoot
$previousErrorActionPreference = $ErrorActionPreference
$ErrorActionPreference = 'Continue'
try {
& docker image inspect $image *> $null
$imageCached = $LASTEXITCODE -eq 0
}
finally {
$ErrorActionPreference = $previousErrorActionPreference
}
if (-not $imageCached) {
Invoke-Docker @('pull', $image) 'DeepStream image pull'
}
$runId = (Get-Date).ToUniversalTime().ToString('yyyyMMddTHHmmssfffZ')
$runRoot = Join-Path $runsRoot $runId
$rawRoot = Join-Path $runRoot 'raw'
$sourceInputMount = Join-Path $runRoot 'source-input'
$geometryRoot = Join-Path $rawRoot 'geometry'
$runOutput = Join-Path $rawRoot 'run'
$frontInputMount = Join-Path $runRoot 'front-input'
foreach ($path in @(
$rawRoot,
$sourceInputMount,
$geometryRoot,
$runOutput,
$frontInputMount,
(Join-Path $runOutput 'detections'),
(Join-Path $runOutput 'tracks')
)) {
New-Item -ItemType Directory -Force -Path $path | Out-Null
}
Copy-Item -LiteralPath $inputPath -Destination (Join-Path $sourceInputMount 'right.mp4')
$workerLog = Join-Path $rawRoot 'worker.log'
"E46H run $runId`nsource=$streamSha`nselection=0..4487`nview=front" |
Set-Content -LiteralPath $workerLog -Encoding UTF8
$startedAt = (Get-Date).ToUniversalTime().ToString('o')
$dewarperConfig = Join-Path $package "runtime\$([string]$profile.rectification.config_file)"
Assert-Sha256 $dewarperConfig ([string]$profile.rectification.config_sha256) `
'FRONT dewarper config'
$frontPath = Join-Path $geometryRoot 'front.mp4'
$dewarperLog = Join-Path $geometryRoot 'front.log'
$dewarperCommand = @"
set -euo pipefail
gst-launch-1.0 -e filesrc location=/workspace/input/right.mp4 ! qtdemux ! h264parse ! nvv4l2decoder ! nvvideoconvert ! 'video/x-raw(memory:NVMM),format=RGBA' ! nvdewarper config-file=/workspace/package/runtime/$([string]$profile.rectification.config_file) source-id=0 num-batch-buffers=1 ! nvvideoconvert ! 'video/x-raw(memory:NVMM),format=NV12' ! nvv4l2h264enc bitrate=6000000 ! h264parse ! qtmux ! filesink location=/workspace/output/front.mp4
"@
$dewarperArguments = @(
'run', '--rm', '--name', "ndc-mission-core-e46h-dewarper-$runId",
'--gpus', 'all', '--network', 'none', '--cap-drop', 'ALL',
'--security-opt', 'no-new-privileges', '--shm-size', '4g',
'--label', 'com.nodedc.product=mission-core',
'--label', 'com.nodedc.stack=perception',
'--label', 'com.nodedc.role=nvdewarper-e46h-front',
'--mount', "type=bind,src=$sourceInputMount,dst=/workspace/input,readonly",
'--mount', "type=bind,src=$package,dst=/workspace/package,readonly",
'--mount', "type=bind,src=$geometryRoot,dst=/workspace/output",
'--entrypoint', '/bin/bash', $image, '-lc', $dewarperCommand
)
$previousErrorActionPreference = $ErrorActionPreference
$ErrorActionPreference = 'Continue'
try {
& docker @dewarperArguments 2>&1 | Tee-Object -LiteralPath $dewarperLog
$dewarperExit = $LASTEXITCODE
}
finally {
$ErrorActionPreference = $previousErrorActionPreference
}
if ($dewarperExit -ne 0 -or -not (Test-Path -LiteralPath $frontPath -PathType Leaf)) {
throw "NVIDIA nvdewarper failed with exit code $dewarperExit"
}
if ((Get-VideoFrameCount $frontPath) -ne $frameCount) {
throw 'E46H FRONT frame coverage changed.'
}
$normalizedFront = Join-Path $geometryRoot 'front-normalized.mp4'
& ffmpeg -hide_banner -loglevel error -y -fflags +genpts -i $frontPath `
-vf 'setpts=N/(10*TB)' -an -c:v libx264 -preset fast -crf 18 `
-pix_fmt yuv420p -r 10 -movflags +faststart $normalizedFront
if ($LASTEXITCODE -ne 0 -or (Get-VideoFrameCount $normalizedFront) -ne $frameCount) {
throw 'E46H FRONT timestamp normalization failed.'
}
Move-Item -LiteralPath $normalizedFront -Destination $frontPath -Force
Copy-Item -LiteralPath $frontPath -Destination (Join-Path $frontInputMount 'view.mp4')
$appConfig = Join-Path $package "runtime\$([string]$profile.detector.deepstream_app_config)"
$detectorConfig = Join-Path $package "runtime\$([string]$profile.detector.detector_config)"
Assert-Sha256 $appConfig ([string]$profile.detector.deepstream_app_config_sha256) `
'TrafficCamNet DeepStream app config'
Assert-Sha256 $detectorConfig ([string]$profile.detector.detector_config_sha256) `
'TrafficCamNet detector config'
$deepstreamLog = Join-Path $runOutput 'deepstream.log'
$deepstreamCommand = @"
set -euo pipefail
cp /opt/nvidia/deepstream/deepstream/samples/configs/deepstream-app/config_tracker_NvDCF_perf.yml /workspace/output/tracker-config.yml
deepstream-app -c /workspace/package/runtime/$([string]$profile.detector.deepstream_app_config)
"@
$deepstreamArguments = @(
'run', '--rm', '--name', "ndc-mission-core-e46h-front-$runId",
'--gpus', 'all', '--network', 'none', '--cap-drop', 'ALL',
'--security-opt', 'no-new-privileges', '--shm-size', '4g',
'--label', 'com.nodedc.product=mission-core',
'--label', 'com.nodedc.stack=perception',
'--label', 'com.nodedc.role=e46h-front-trafficcamnet',
'--mount', "type=bind,src=$frontInputMount,dst=/workspace/input,readonly",
'--mount', "type=bind,src=$package,dst=/workspace/package,readonly",
'--mount', "type=bind,src=$modelRoot,dst=/workspace/model",
'--mount', "type=bind,src=$runOutput,dst=/workspace/output",
'--entrypoint', '/bin/bash', $image, '-lc', $deepstreamCommand
)
$previousErrorActionPreference = $ErrorActionPreference
$ErrorActionPreference = 'Continue'
try {
& docker @deepstreamArguments 2>&1 | Tee-Object -LiteralPath $deepstreamLog
$deepstreamExit = $LASTEXITCODE
}
finally {
$ErrorActionPreference = $previousErrorActionPreference
}
if ($deepstreamExit -ne 0) {
throw "DeepStream failed with exit code $deepstreamExit"
}
$overlayPath = Join-Path $runOutput 'overlay.mp4'
$trackerPath = Join-Path $runOutput 'tracker-config.yml'
$detections = @(Get-ChildItem -LiteralPath (Join-Path $runOutput 'detections') -File)
$tracks = @(Get-ChildItem -LiteralPath (Join-Path $runOutput 'tracks') -File)
if (-not (Test-Path -LiteralPath $overlayPath -PathType Leaf) -or
$detections.Count -ne $frameCount -or $tracks.Count -ne $frameCount) {
throw "DeepStream output coverage changed: detections=$($detections.Count), tracks=$($tracks.Count)"
}
$fastOverlay = Join-Path $runOutput 'overlay-faststart.mp4'
& ffmpeg -hide_banner -loglevel error -y -i $overlayPath -c copy -movflags +faststart $fastOverlay
if ($LASTEXITCODE -ne 0 -or (Get-VideoFrameCount $fastOverlay) -ne $frameCount) {
throw 'E46H fast-start overlay normalization failed.'
}
Move-Item -LiteralPath $fastOverlay -Destination $overlayPath -Force
$enginePath = "$modelPath`_b1_gpu0_fp16.engine"
if (-not (Test-Path -LiteralPath $enginePath -PathType Leaf)) {
throw 'TrafficCamNet TensorRT engine is missing.'
}
$runtime = [ordered]@{
schema_version = 'missioncore.e46h-full-rectified-front-runtime/v1'
status = 'completed'
worker_host = $env:COMPUTERNAME
gpu_name = ((& nvidia-smi --query-gpu=name --format=csv,noheader | Select-Object -First 1).Trim())
started_at_utc = $startedAt
completed_at_utc = (Get-Date).ToUniversalTime().ToString('o')
container_image = $image
container_image_digest = $imageDigest
source_stream_sha256 = Get-Sha256 $inputPath
frame_count = $frameCount
retained_source_frame_index_range = @(0, 4487)
geometry = [ordered]@{
video_path = 'geometry/front.mp4'
video_sha256 = Get-Sha256 $frontPath
log_path = 'geometry/front.log'
log_sha256 = Get-Sha256 $dewarperLog
config_sha256 = Get-Sha256 $dewarperConfig
frame_count = $frameCount
dewarper_exit_code = $dewarperExit
}
run = [ordered]@{
overlay_path = 'run/overlay.mp4'
overlay_sha256 = Get-Sha256 $overlayPath
deepstream_log_path = 'run/deepstream.log'
deepstream_log_sha256 = Get-Sha256 $deepstreamLog
model_sha256 = Get-Sha256 $modelPath
model_engine_sha256 = Get-Sha256 $enginePath
parser_library_sha256 = Get-Sha256 $parserPath
deepstream_app_config_sha256 = Get-Sha256 $appConfig
detector_config_sha256 = Get-Sha256 $detectorConfig
tracker_config_sha256 = Get-Sha256 $trackerPath
frame_count = $frameCount
deepstream_exit_code = $deepstreamExit
}
}
$runtime | ConvertTo-Json -Depth 12 |
Set-Content -LiteralPath (Join-Path $rawRoot 'runtime.json') -Encoding UTF8
Add-Content -LiteralPath $workerLog -Value "completed=$(Get-Date -Format o)"
$consolidatorImage = 'nvcr.io/nvidia/tritonserver:26.06-py3@sha256:58df7489c3f2276f9591d500a012dee03e23d35543ce3c390b4c001e6bf90794'
$consolidatorArguments = @(
'run', '--rm', '--name', "ndc-mission-core-e46h-consolidator-$runId",
'--network', 'none', '--read-only', '--cap-drop', 'ALL',
'--security-opt', 'no-new-privileges', '--tmpfs', '/tmp:rw,noexec,nosuid,size=64m',
'--mount', "type=bind,src=$package,dst=/workspace/package,readonly",
'--mount', "type=bind,src=$sourceJob,dst=/workspace/source-job,readonly",
'--mount', "type=bind,src=$rawRoot,dst=/workspace/raw,readonly",
'--mount', "type=bind,src=$resultsRoot,dst=/workspace/results",
'-e', 'PYTHONPATH=/workspace/package/runtime',
'-e', 'PYTHONDONTWRITEBYTECODE=1',
$consolidatorImage,
'python3', '/workspace/package/runtime/run_e46h_full_rectified_front_replay.py',
'--source-job', '/workspace/source-job',
'--raw-root', '/workspace/raw',
'--profile', '/workspace/package/profile.json',
'--output-root', '/workspace/results'
)
Invoke-Docker $consolidatorArguments 'E46H consolidation'
Write-Host "E46H_FULL_RECTIFIED_FRONT_COMPLETED run=$runId results=$resultsRoot"
}
finally {
if ($LogPath) { Stop-Transcript | Out-Null }
}
@@ -0,0 +1,53 @@
[CmdletBinding()]
param(
[Parameter(Mandatory = $true)]
[string]$PackageRoot,
[string]$SourceJobRoot = 'D:\NDC_MISSIONCORE\runtime\jobs\recorded-camera-602ac89026ed12978619801d',
[string]$RuntimeRoot = 'D:\NDC_MISSIONCORE\runtime\experiments\e46h'
)
$ErrorActionPreference = 'Stop'
$taskName = 'MissionCore-E46HFullRectifiedFrontReplay'
$package = (Resolve-Path -LiteralPath $PackageRoot).Path
$script = Join-Path $package 'runtime\Invoke-E46HFullRectifiedFrontReplay.ps1'
if (-not (Test-Path -LiteralPath $script -PathType Leaf)) {
throw "E46H runner is missing: $script"
}
$existing = Get-ScheduledTask -TaskName $taskName -ErrorAction SilentlyContinue
if ($existing -and $existing.State -eq 'Running') {
throw "$taskName is already running."
}
$logsRoot = Join-Path $RuntimeRoot 'logs'
New-Item -ItemType Directory -Force -Path $logsRoot | Out-Null
$stamp = (Get-Date).ToUniversalTime().ToString('yyyyMMddTHHmmssfffZ')
$logPath = Join-Path $logsRoot "e46h-full-rectified-front-$stamp.log"
$powerShell = "$env:SystemRoot\System32\WindowsPowerShell\v1.0\powershell.exe"
$arguments = @(
'-NoLogo', '-NoProfile', '-NonInteractive', '-ExecutionPolicy', 'Bypass',
'-File', "`"$script`"",
'-PackageRoot', "`"$package`"",
'-SourceJobRoot', "`"$SourceJobRoot`"",
'-RuntimeRoot', "`"$RuntimeRoot`"",
'-LogPath', "`"$logPath`""
) -join ' '
$userId = [System.Security.Principal.WindowsIdentity]::GetCurrent().Name
$action = New-ScheduledTaskAction -Execute $powerShell -Argument $arguments -WorkingDirectory $package
$principal = New-ScheduledTaskPrincipal -UserId $userId -LogonType Interactive -RunLevel Limited
$trigger = New-ScheduledTaskTrigger -Once -At ((Get-Date).AddMinutes(30))
$settings = New-ScheduledTaskSettingsSet `
-AllowStartIfOnBatteries `
-DontStopIfGoingOnBatteries `
-StartWhenAvailable `
-ExecutionTimeLimit ([TimeSpan]::FromHours(6))
Register-ScheduledTask `
-TaskName $taskName `
-Action $action `
-Principal $principal `
-Trigger $trigger `
-Settings $settings `
-Description 'One-shot E46H full retained FRONT TrafficCamNet + NvDCF replay.' `
-Force | Out-Null
Start-ScheduledTask -TaskName $taskName
Write-Host "E46H_TASK_STARTED task=$taskName log=$logPath"
@@ -0,0 +1,118 @@
[CmdletBinding()]
param(
[Parameter(Mandatory = $true)]
[string]$PackageRoot,
[string]$RuntimeRoot = 'D:\NDC_MISSIONCORE\runtime\experiments\e46j',
[string]$SourceVideo = 'D:\NDC_MISSIONCORE\runtime\experiments\e46e\inputs\right-cadd1696ff000904eb78633a0a8418104b8024f178b91f3421789021ccb160e8.mp4',
[string]$ValidFovMask = 'D:\NDC_MISSIONCORE\runtime\inputs\e2\valid-fov-mask-b4dd8ddf2b87c1d520ee8a0868c4fea062d7c14d1bae73ccabd3abe1f3acbac2\mask.png',
[int]$MaxFrames = 0,
[switch]$NoOverlay,
[string]$RunPrefix = 'full'
)
$ErrorActionPreference = 'Stop'
$ProgressPreference = 'SilentlyContinue'
function Get-Sha256([string]$Path) {
return (Get-FileHash -LiteralPath $Path -Algorithm SHA256).Hash.ToLowerInvariant()
}
$package = (Resolve-Path -LiteralPath $PackageRoot).Path
$source = (Resolve-Path -LiteralPath $SourceVideo).Path
$mask = (Resolve-Path -LiteralPath $ValidFovMask).Path
$profile = Join-Path $package 'e46j_raw_fisheye_yolox_profile.json'
$runner = Join-Path $package 'run_e46j_raw_fisheye_yolox.py'
if (-not (Test-Path -LiteralPath $profile -PathType Leaf) -or
-not (Test-Path -LiteralPath $runner -PathType Leaf)) {
throw 'E46J package is incomplete.'
}
if ((Get-Sha256 $source) -ne 'cadd1696ff000904eb78633a0a8418104b8024f178b91f3421789021ccb160e8') {
throw 'E46J source stream identity changed.'
}
$runsRoot = Join-Path $RuntimeRoot 'runs'
New-Item -ItemType Directory -Force -Path $runsRoot | Out-Null
$runId = "$RunPrefix-$((Get-Date).ToUniversalTime().ToString('yyyyMMddTHHmmssfffZ'))"
$image = 'nvcr.io/nvidia/tritonserver:26.06-py3@sha256:58df7489c3f2276f9591d500a012dee03e23d35543ce3c390b4c001e6bf90794'
$command = "python3 /workspace/package/run_e46j_raw_fisheye_yolox.py --input /workspace/input/right.mp4 --mask /workspace/valid-fov/mask.png --profile /workspace/package/e46j_raw_fisheye_yolox_profile.json --triton-url http://127.0.0.1:8000 --output /workspace/runs/$runId"
if ($MaxFrames -gt 0) {
$command += " --max-frames $MaxFrames"
}
if (-not $NoOverlay) {
$command += ' --overlay'
}
$dockerArguments = @(
'run', '--rm', '--name', "ndc-mission-core-e46j-$runId",
'--network', 'container:ndc-mission-core-triton',
'--gpus', 'all',
'--cap-drop', 'ALL',
'--security-opt', 'no-new-privileges',
'--shm-size', '1g',
'--label', 'com.nodedc.product=mission-core',
'--label', 'com.nodedc.stack=perception',
'--label', 'com.nodedc.role=e46j-raw-fisheye-realtime-gate',
'--label', 'com.nodedc.managed-by=mission-core-worker',
'--mount', "type=bind,src=$source,dst=/workspace/input/right.mp4,readonly",
'--mount', "type=bind,src=$mask,dst=/workspace/valid-fov/mask.png,readonly",
'--mount', "type=bind,src=$package,dst=/workspace/package,readonly",
'--mount', "type=bind,src=$runsRoot,dst=/workspace/runs",
'--mount', 'type=bind,src=D:\NDC_MISSIONCORE\runtime\derived\perception-p0-env-v1,dst=/opt/env,readonly',
'--mount', 'type=bind,src=D:\NDC_MISSIONCORE\runtime\derived\perception-e15-media-pyav180-lz445-v1,dst=/opt/media,readonly',
'--mount', 'type=bind,src=D:\NDC_MISSIONCORE\runtime\derived\perception-e3-opencv413092-v1,dst=/environment,readonly',
'-e', 'PYTHONPATH=/opt/env:/opt/media:/environment/packages',
'--entrypoint', '/bin/bash',
$image, '-lc', $command
)
& docker @dockerArguments
$runnerExitCode = $LASTEXITCODE
if ($runnerExitCode -ne 0 -and $runnerExitCode -ne 2) {
throw "E46J runner crashed with exit code $runnerExitCode"
}
$result = Join-Path $runsRoot $runId
$runtimePath = Join-Path $result 'runtime.json'
if (-not $NoOverlay) {
$intermediate = Join-Path $result 'raw-fisheye-yolox-overlay-intermediate.mp4'
$overlay = Join-Path $result 'raw-fisheye-yolox-overlay.mp4'
if (-not (Test-Path -LiteralPath $intermediate -PathType Leaf)) {
throw 'E46J intermediate overlay is missing.'
}
& ffmpeg.exe -hide_banner -loglevel error -y -i $intermediate `
-c:v libx264 -preset veryfast -crf 20 -r 4489000/448723 `
-movflags +faststart -an $overlay
if ($LASTEXITCODE -ne 0 -or
-not (Test-Path -LiteralPath $overlay -PathType Leaf) -or
(Get-Item -LiteralPath $overlay).Length -eq 0) {
throw 'E46J final H.264 overlay transcode failed.'
}
$runtime = Get-Content -LiteralPath $runtimePath -Raw | ConvertFrom-Json
$runtime.artifacts.PSObject.Properties.Remove('overlay_intermediate')
$runtime.artifacts | Add-Member -NotePropertyName overlay -NotePropertyValue ([pscustomobject]@{
file = 'raw-fisheye-yolox-overlay.mp4'
byte_length = (Get-Item -LiteralPath $overlay).Length
sha256 = Get-Sha256 $overlay
codec = 'H.264'
frame_rate = 10.003944527024467
})
$runtime | Add-Member -NotePropertyName visual_export -NotePropertyValue ([pscustomobject]@{
provider = ((& ffmpeg.exe -version | Select-Object -First 1).Trim())
source = 'MPEG-4 Part 2 intermediate produced by OpenCV VideoWriter'
output = 'H.264 MP4 with faststart'
excluded_from_core_latency = $true
})
$runtime | ConvertTo-Json -Depth 20 | Set-Content -LiteralPath $runtimePath -Encoding UTF8
Remove-Item -LiteralPath $intermediate -Force
}
Write-Output "E46J_RUN_ID=$runId"
Write-Output "E46J_RESULT_ROOT=$result"
Get-Content -LiteralPath $runtimePath -Raw
if ($runnerExitCode -eq 2) {
exit 2
}
@@ -0,0 +1,124 @@
[CmdletBinding()]
param(
[Parameter(Mandatory = $true)]
[string]$JobRoot,
[Parameter(Mandatory = $true)]
[string]$OutputPath,
[ValidateRange(1, 1000)]
[int]$FreeGiBFloor = 360
)
$ErrorActionPreference = "Stop"
$ProgressPreference = "SilentlyContinue"
function Assert-DDrivePath {
param([string]$Path, [string]$Label)
$fullPath = [IO.Path]::GetFullPath($Path)
if ([IO.Path]::GetPathRoot($fullPath).TrimEnd("\") -ine "D:") {
throw "$Label must be stored on D:"
}
return $fullPath
}
function Assert-FreeSpace {
param([string]$Phase, [int64]$RequiredAdditionalBytes = 0)
$freeBytes = [int64](Get-PSDrive -Name D).Free
$floorBytes = [int64]$FreeGiBFloor * 1GB
Write-Output (
"DISK_GUARD PHASE={0} FREE_GIB={1} FLOOR_GIB={2}" -f
$Phase,
[math]::Round($freeBytes / 1GB, 3),
$FreeGiBFloor
)
if ($freeBytes -lt ($floorBytes + $RequiredAdditionalBytes)) {
throw "D: does not have the guarded replay reserve during $Phase"
}
}
$jobDirectory = Assert-DDrivePath (
(Resolve-Path -LiteralPath $JobRoot).Path
) "Job root"
$output = Assert-DDrivePath $OutputPath "Output path"
if (Test-Path -LiteralPath $output) {
Write-Output "REPLAY_ALREADY_PRESENT=$output"
exit 0
}
$jobPath = Join-Path $jobDirectory "job.json"
$job = Get-Content -LiteralPath $jobPath -Raw | ConvertFrom-Json
if (
$job.schema_version -ne "missioncore.compute-job/v1" -or
$job.job_id -ne "recorded-camera-602ac89026ed12978619801d" -or
$job.input.session_id -ne "20260720T065719Z_viewer_live" -or
$job.input.source_id -ne "sensor.camera.right" -or
[int]$job.input.segment_count -ne 4489
) {
throw "The requested job is not the immutable RAVNOVES00 right-camera source"
}
$epochRoot = Join-Path $jobDirectory "input\camera\sensor.camera.right\epoch-1"
$initPath = Join-Path $epochRoot "init.mp4"
$segmentsRoot = Join-Path $epochRoot "segments"
if (-not (Test-Path -LiteralPath $initPath -PathType Leaf)) {
throw "Camera initialization segment is absent"
}
if (-not (Test-Path -LiteralPath $segmentsRoot -PathType Container)) {
throw "Camera segment directory is absent"
}
$parent = Split-Path $output -Parent
$null = New-Item -ItemType Directory -Path $parent -Force
$temporary = Join-Path $parent (".{0}.{1}.partial" -f (Split-Path $output -Leaf), [Guid]::NewGuid().ToString("N"))
$requiredBytes = [int64]$job.input.byte_length + 1GB
Assert-FreeSpace "preflight" $requiredBytes
try {
$destination = [IO.File]::Open(
$temporary,
[IO.FileMode]::CreateNew,
[IO.FileAccess]::Write,
[IO.FileShare]::None
)
try {
$source = [IO.File]::OpenRead($initPath)
try { $source.CopyTo($destination) } finally { $source.Dispose() }
for ($sequence = 1; $sequence -le 4489; $sequence++) {
$segment = Join-Path $segmentsRoot ("{0}.m4s" -f $sequence)
if (-not (Test-Path -LiteralPath $segment -PathType Leaf)) {
throw "Camera segment is absent: $sequence"
}
$source = [IO.File]::OpenRead($segment)
try { $source.CopyTo($destination) } finally { $source.Dispose() }
}
$destination.Flush($true)
}
finally {
$destination.Dispose()
}
$probe = & ffprobe -v error -select_streams v:0 -count_frames `
-show_entries stream=width,height,nb_read_frames `
-of json $temporary | ConvertFrom-Json
if ($LASTEXITCODE -ne 0) {
throw "ffprobe failed for the reconstructed camera source"
}
$stream = @($probe.streams)[0]
if (
[int]$stream.width -ne 800 -or
[int]$stream.height -ne 600 -or
[int]$stream.nb_read_frames -ne 4489
) {
throw "Reconstructed camera stream violates the immutable frame contract"
}
Move-Item -LiteralPath $temporary -Destination $output
Assert-FreeSpace "published"
Write-Output "REPLAY_PATH=$output"
Write-Output "FRAME_COUNT=4489"
}
finally {
if (Test-Path -LiteralPath $temporary) {
Remove-Item -LiteralPath $temporary -Force
}
}
@@ -0,0 +1,78 @@
[application]
enable-perf-measurement=1
perf-measurement-interval-sec=5
gie-kitti-output-dir=/workspace/output/detections
kitti-track-output-dir=/workspace/output/tracks
[tiled-display]
enable=0
rows=1
columns=1
width=800
height=600
gpu-id=0
[source0]
enable=1
type=3
num-sources=1
uri=file:///workspace/input/right.mp4
gpu-id=0
[streammux]
gpu-id=0
batch-size=1
batched-push-timeout=40000
width=800
height=600
live-source=0
[primary-gie]
enable=1
gpu-id=0
plugin-type=0
batch-size=1
gie-unique-id=1
config-file=/workspace/package/runtime/e46e_trafficcamnet_rtdetr.txt
bbox-border-color1=0.267;0.831;1.0;1.0
bbox-border-color2=0.243;0.973;0.553;1.0
bbox-border-color3=1.0;0.306;0.765;1.0
bbox-border-color4=1.0;0.741;0.153;1.0
[tracker]
enable=1
tracker-width=960
tracker-height=544
ll-lib-file=/opt/nvidia/deepstream/deepstream/lib/libnvds_nvmultiobjecttracker.so
ll-config-file=/opt/nvidia/deepstream/deepstream/samples/configs/deepstream-app/config_tracker_NvDCF_perf.yml
gpu-id=0
display-tracking-id=1
compute-hw=1
[osd]
enable=1
gpu-id=0
border-width=3
text-size=16
text-color=1;1;1;1
text-bg-color=0.08;0.08;0.08;0.9
font=Arial
display-bbox=1
display-text=1
[sink0]
enable=1
type=3
container=1
codec=1
enc-type=0
sync=0
qos=0
bitrate=8000000
profile=4
output-file=/workspace/output/overlay.mp4
source-id=0
gpu-id=0
[tests]
file-loop=0
@@ -0,0 +1,5 @@
background
bicycle
car
person
road_sign
@@ -0,0 +1,26 @@
[property]
gpu-id=0
onnx-file=/workspace/model/resnet50_trafficcamnet_rtdetr.fp16.onnx
model-engine-file=/workspace/model/resnet50_trafficcamnet_rtdetr.fp16.onnx_b1_gpu0_fp16.engine
labelfile-path=/workspace/package/runtime/e46e_trafficcamnet_labels.txt
custom-lib-path=/workspace/package/runtime/libnvds_infercustomparser_tao.so
parse-bbox-func-name=NvDsInferParseCustomDDETRTAO
output-blob-names=pred_logits;pred_boxes
infer-dims=3;544;960
maintain-aspect-ratio=1
net-scale-factor=0.00392156862745098
offsets=0;0;0
model-color-format=0
network-mode=2
network-type=0
num-detected-classes=5
cluster-mode=4
output-tensor-meta=1
workspace-size=1048576
batch-size=1
interval=0
gie-unique-id=1
[class-attrs-all]
pre-cluster-threshold=0.5
topk=20
@@ -0,0 +1,32 @@
[property]
gpu-id=0
onnx-file=/workspace/model/resnet18_dashcamnet_pruned.onnx
model-engine-file=/workspace/model/resnet18_dashcamnet_pruned.onnx_b1_gpu0_fp16.engine
labelfile-path=/workspace/package/runtime/e46f_dashcamnet_labels.txt
output-blob-names=output_bbox/BiasAdd:0;output_cov/Sigmoid:0
infer-dims=3;544;960
maintain-aspect-ratio=0
net-scale-factor=0.00392156862745098
offsets=0;0;0
model-color-format=0
network-mode=2
network-type=0
num-detected-classes=4
cluster-mode=2
output-tensor-meta=0
workspace-size=1048576
batch-size=1
interval=0
gie-unique-id=1
[class-attrs-all]
topk=20
nms-iou-threshold=0.5
pre-cluster-threshold=0.2
roi-top-offset=0
roi-bottom-offset=0
[class-attrs-0]
topk=20
nms-iou-threshold=0.5
pre-cluster-threshold=0.4
@@ -0,0 +1,4 @@
car
bicycle
person
road_sign
@@ -0,0 +1,78 @@
[application]
enable-perf-measurement=1
perf-measurement-interval-sec=5
gie-kitti-output-dir=/workspace/output/detections
kitti-track-output-dir=/workspace/output/tracks
[tiled-display]
enable=0
rows=1
columns=1
width=800
height=600
gpu-id=0
[source0]
enable=1
type=3
num-sources=1
uri=file:///workspace/input/right.mp4
gpu-id=0
[streammux]
gpu-id=0
batch-size=1
batched-push-timeout=40000
width=800
height=600
live-source=0
[primary-gie]
enable=1
gpu-id=0
plugin-type=0
batch-size=1
gie-unique-id=1
config-file=/workspace/package/runtime/e46f_dashcamnet_detectnet.txt
bbox-border-color0=0.243;0.973;0.553;1.0
bbox-border-color1=0.267;0.831;1.0;1.0
bbox-border-color2=1.0;0.306;0.765;1.0
bbox-border-color3=1.0;0.741;0.153;1.0
[tracker]
enable=1
tracker-width=960
tracker-height=544
ll-lib-file=/opt/nvidia/deepstream/deepstream/lib/libnvds_nvmultiobjecttracker.so
ll-config-file=/opt/nvidia/deepstream/deepstream/samples/configs/deepstream-app/config_tracker_NvDCF_perf.yml
gpu-id=0
display-tracking-id=1
compute-hw=1
[osd]
enable=1
gpu-id=0
border-width=3
text-size=16
text-color=1;1;1;1
text-bg-color=0.08;0.08;0.08;0.9
font=Arial
display-bbox=1
display-text=1
[sink0]
enable=1
type=3
container=1
codec=1
enc-type=0
sync=0
qos=0
bitrate=8000000
profile=4
output-file=/workspace/output/overlay.mp4
source-id=0
gpu-id=0
[tests]
file-loop=0
@@ -0,0 +1,78 @@
[application]
enable-perf-measurement=1
perf-measurement-interval-sec=5
gie-kitti-output-dir=/workspace/output/detections
kitti-track-output-dir=/workspace/output/tracks
[tiled-display]
enable=0
rows=1
columns=1
width=960
height=544
gpu-id=0
[source0]
enable=1
type=3
num-sources=1
uri=file:///workspace/input/view.mp4
gpu-id=0
[streammux]
gpu-id=0
batch-size=1
batched-push-timeout=40000
width=960
height=544
live-source=0
[primary-gie]
enable=1
gpu-id=0
plugin-type=0
batch-size=1
gie-unique-id=1
config-file=/workspace/package/runtime/e46f_dashcamnet_detectnet.txt
bbox-border-color0=0.243;0.973;0.553;1.0
bbox-border-color1=0.267;0.831;1.0;1.0
bbox-border-color2=1.0;0.306;0.765;1.0
bbox-border-color3=1.0;0.741;0.153;1.0
[tracker]
enable=1
tracker-width=960
tracker-height=544
ll-lib-file=/opt/nvidia/deepstream/deepstream/lib/libnvds_nvmultiobjecttracker.so
ll-config-file=/opt/nvidia/deepstream/deepstream/samples/configs/deepstream-app/config_tracker_NvDCF_perf.yml
gpu-id=0
display-tracking-id=1
compute-hw=1
[osd]
enable=1
gpu-id=0
border-width=3
text-size=16
text-color=1;1;1;1
text-bg-color=0.08;0.08;0.08;0.9
font=Arial
display-bbox=1
display-text=1
[sink0]
enable=1
type=3
container=1
codec=1
enc-type=0
sync=0
qos=0
bitrate=6000000
profile=4
output-file=/workspace/output/overlay.mp4
source-id=0
gpu-id=0
[tests]
file-loop=0
@@ -0,0 +1,22 @@
[property]
output-width=960
output-height=544
num-batch-buffers=1
cuda-memory-type=2
[surface0]
projection-type=4
surface-index=0
width=960
height=544
yaw=0.0
pitch=0.0
roll=0.0
rot-axes=YXZ
focal-length=194.59817287616025;194.57531427932872
distortion=-0.023164451386679667;-0.0014974198594105452;-0.001039213149441563;-0.000035237331915978814
src-x0=396.31861150187996
src-y0=301.49644357408005
dst-focal-length=402.76782296509447;402.76782296509447
dst-principal-point=479.5;271.5
cuda-address-mode=1
@@ -0,0 +1,22 @@
[property]
output-width=960
output-height=544
num-batch-buffers=1
cuda-memory-type=2
[surface0]
projection-type=4
surface-index=0
width=960
height=544
yaw=270.0
pitch=0.0
roll=0.0
rot-axes=YXZ
focal-length=194.59817287616025;194.57531427932872
distortion=-0.023164451386679667;-0.0014974198594105452;-0.001039213149441563;-0.000035237331915978814
src-x0=396.31861150187996
src-y0=301.49644357408005
dst-focal-length=402.76782296509447;402.76782296509447
dst-principal-point=479.5;271.5
cuda-address-mode=1
@@ -0,0 +1,22 @@
[property]
output-width=960
output-height=544
num-batch-buffers=1
cuda-memory-type=2
[surface0]
projection-type=4
surface-index=0
width=960
height=544
yaw=90.0
pitch=0.0
roll=0.0
rot-axes=YXZ
focal-length=194.59817287616025;194.57531427932872
distortion=-0.023164451386679667;-0.0014974198594105452;-0.001039213149441563;-0.000035237331915978814
src-x0=396.31861150187996
src-y0=301.49644357408005
dst-focal-length=402.76782296509447;402.76782296509447
dst-principal-point=479.5;271.5
cuda-address-mode=1
@@ -0,0 +1,78 @@
[application]
enable-perf-measurement=1
perf-measurement-interval-sec=5
gie-kitti-output-dir=/workspace/output/detections
kitti-track-output-dir=/workspace/output/tracks
[tiled-display]
enable=0
rows=1
columns=1
width=960
height=544
gpu-id=0
[source0]
enable=1
type=3
num-sources=1
uri=file:///workspace/input/view.mp4
gpu-id=0
[streammux]
gpu-id=0
batch-size=1
batched-push-timeout=40000
width=960
height=544
live-source=0
[primary-gie]
enable=1
gpu-id=0
plugin-type=0
batch-size=1
gie-unique-id=1
config-file=/workspace/package/runtime/e46e_trafficcamnet_rtdetr.txt
bbox-border-color1=0.267;0.831;1.0;1.0
bbox-border-color2=0.243;0.973;0.553;1.0
bbox-border-color3=1.0;0.306;0.765;1.0
bbox-border-color4=1.0;0.741;0.153;1.0
[tracker]
enable=1
tracker-width=960
tracker-height=544
ll-lib-file=/opt/nvidia/deepstream/deepstream/lib/libnvds_nvmultiobjecttracker.so
ll-config-file=/opt/nvidia/deepstream/deepstream/samples/configs/deepstream-app/config_tracker_NvDCF_perf.yml
gpu-id=0
display-tracking-id=1
compute-hw=1
[osd]
enable=1
gpu-id=0
border-width=3
text-size=16
text-color=1;1;1;1
text-bg-color=0.08;0.08;0.08;0.9
font=Arial
display-bbox=1
display-text=1
[sink0]
enable=1
type=3
container=1
codec=1
enc-type=0
sync=0
qos=0
bitrate=6000000
profile=4
output-file=/workspace/output/overlay.mp4
source-id=0
gpu-id=0
[tests]
file-loop=0
@@ -0,0 +1,41 @@
#!/usr/bin/env python3
"""Consolidate raw DeepStream/NvDCF output into immutable E46E evidence."""
from __future__ import annotations
import argparse
import json
from pathlib import Path
from k1link.compute.e46e_ready_stack import build_e46e_ready_stack
def main() -> int:
parser = argparse.ArgumentParser()
parser.add_argument("--source-job", type=Path, required=True)
parser.add_argument("--raw-root", type=Path, required=True)
parser.add_argument("--profile", type=Path, required=True)
parser.add_argument("--output-root", type=Path, required=True)
args = parser.parse_args()
result = build_e46e_ready_stack(
source_job_root=args.source_job,
raw_root=args.raw_root,
profile_path=args.profile,
output_root=args.output_root,
)
print(
json.dumps(
{
"result_id": result["result_id"],
"result_root": str(result["result_root"]),
"metrics": result["report"]["metrics"],
},
ensure_ascii=False,
indent=2,
)
)
return 0
if __name__ == "__main__":
raise SystemExit(main())
@@ -0,0 +1,41 @@
#!/usr/bin/env python3
"""Consolidate raw DashCamNet/NvDCF output into immutable E46F evidence."""
from __future__ import annotations
import argparse
import json
from pathlib import Path
from k1link.compute.e46f_dashcam_bakeoff import build_e46f_dashcam_bakeoff
def main() -> int:
parser = argparse.ArgumentParser()
parser.add_argument("--source-job", type=Path, required=True)
parser.add_argument("--raw-root", type=Path, required=True)
parser.add_argument("--profile", type=Path, required=True)
parser.add_argument("--output-root", type=Path, required=True)
args = parser.parse_args()
result = build_e46f_dashcam_bakeoff(
source_job_root=args.source_job,
raw_root=args.raw_root,
profile_path=args.profile,
output_root=args.output_root,
)
print(
json.dumps(
{
"result_id": result["result_id"],
"result_root": str(result["result_root"]),
"metrics": result["report"]["metrics"],
},
ensure_ascii=False,
indent=2,
)
)
return 0
if __name__ == "__main__":
raise SystemExit(main())
@@ -0,0 +1,32 @@
#!/usr/bin/env python3
"""Freeze raw NVIDIA E46G output as immutable Mission Core evidence."""
from __future__ import annotations
import argparse
from pathlib import Path
from k1link.compute.e46g_rectified_detector_bakeoff import (
build_e46g_rectified_detector_bakeoff,
)
def main() -> int:
parser = argparse.ArgumentParser()
parser.add_argument("--source-job", type=Path, required=True)
parser.add_argument("--raw-root", type=Path, required=True)
parser.add_argument("--profile", type=Path, required=True)
parser.add_argument("--output-root", type=Path, required=True)
args = parser.parse_args()
result = build_e46g_rectified_detector_bakeoff(
source_job_root=args.source_job,
raw_root=args.raw_root,
profile_path=args.profile,
output_root=args.output_root,
)
print(result["result_root"])
return 0
if __name__ == "__main__":
raise SystemExit(main())
@@ -0,0 +1,32 @@
#!/usr/bin/env python3
"""Freeze raw NVIDIA E46H output as immutable Mission Core evidence."""
from __future__ import annotations
import argparse
from pathlib import Path
from k1link.compute.e46h_full_rectified_front_replay import (
build_e46h_full_rectified_front_replay,
)
def main() -> int:
parser = argparse.ArgumentParser()
parser.add_argument("--source-job", type=Path, required=True)
parser.add_argument("--raw-root", type=Path, required=True)
parser.add_argument("--profile", type=Path, required=True)
parser.add_argument("--output-root", type=Path, required=True)
args = parser.parse_args()
result = build_e46h_full_rectified_front_replay(
source_job_root=args.source_job,
raw_root=args.raw_root,
profile_path=args.profile,
output_root=args.output_root,
)
print(result["result_root"])
return 0
if __name__ == "__main__":
raise SystemExit(main())
@@ -0,0 +1,706 @@
#!/usr/bin/env python3
"""Run the frozen one-pass YOLOX-S realtime gate on the full K1 RIGHT fisheye.
The runner deliberately performs exactly one detector request per decoded source
frame. It does not rectify, crop, tile, track, hold, stitch or use route-specific
filters. H.264 overlay encoding is measured separately from the detector path.
"""
from __future__ import annotations
import argparse
import hashlib
import http.client
import json
import math
import os
import platform
import statistics
import subprocess
import threading
import time
import urllib.parse
from collections import Counter
from datetime import UTC, datetime
from pathlib import Path
from typing import Any
import numpy as np
PROFILE_SCHEMA = "missioncore.e46j-raw-fisheye-realtime-profile/v1"
RUNTIME_SCHEMA = "missioncore.e46j-raw-fisheye-realtime-runtime/v1"
FRAME_SCHEMA = "missioncore.e46j-raw-fisheye-realtime-frame/v1"
COCO_CLASSES = (
"person", "bicycle", "car", "motorcycle", "airplane", "bus", "train",
"truck", "boat", "traffic light", "fire hydrant", "stop sign",
"parking meter", "bench", "bird", "cat", "dog", "horse", "sheep",
"cow", "elephant", "bear", "zebra", "giraffe", "backpack", "umbrella",
"handbag", "tie", "suitcase", "frisbee", "skis", "snowboard",
"sports ball", "kite", "baseball bat", "baseball glove", "skateboard",
"surfboard", "tennis racket", "bottle", "wine glass", "cup", "fork",
"knife", "spoon", "bowl", "banana", "apple", "sandwich", "orange",
"broccoli", "carrot", "hot dog", "pizza", "donut", "cake", "chair",
"couch", "potted plant", "bed", "dining table", "toilet", "tv",
"laptop", "mouse", "remote", "keyboard", "cell phone", "microwave",
"oven", "toaster", "sink", "refrigerator", "book", "clock", "vase",
"scissors", "teddy bear", "hair drier", "toothbrush",
)
def arguments() -> argparse.Namespace:
parser = argparse.ArgumentParser()
parser.add_argument("--input", type=Path, required=True)
parser.add_argument("--mask", type=Path, required=True)
parser.add_argument("--profile", type=Path, required=True)
parser.add_argument("--triton-url", required=True)
parser.add_argument("--output", type=Path, required=True)
parser.add_argument("--max-frames", type=int, default=0)
parser.add_argument("--overlay", action="store_true")
parser.add_argument("--telemetry-interval", type=float, default=0.5)
return parser.parse_args()
def read_object(path: Path) -> dict[str, Any]:
value = json.loads(path.resolve(strict=True).read_text(encoding="utf-8-sig"))
if not isinstance(value, dict):
raise RuntimeError(f"JSON object expected: {path}")
return value
def canonical_json(value: object) -> bytes:
return json.dumps(
value, sort_keys=True, separators=(",", ":"), allow_nan=False
).encode()
def sha256(path: Path) -> str:
digest = hashlib.sha256()
with path.open("rb") as stream:
while chunk := stream.read(1024 * 1024):
digest.update(chunk)
return digest.hexdigest()
def distribution(values: list[float]) -> dict[str, float]:
if not values:
return {"mean": 0.0, "p50": 0.0, "p95": 0.0, "maximum": 0.0}
ordered = sorted(values)
def percentile(fraction: float) -> float:
index = (len(ordered) - 1) * fraction
lower = math.floor(index)
upper = math.ceil(index)
if lower == upper:
return ordered[lower]
ratio = index - lower
return ordered[lower] * (1.0 - ratio) + ordered[upper] * ratio
return {
"mean": round(statistics.fmean(ordered), 6),
"p50": round(percentile(0.5), 6),
"p95": round(percentile(0.95), 6),
"maximum": round(max(ordered), 6),
}
def validate_profile(profile: dict[str, Any], source_sha256: str) -> None:
source = profile.get("source")
detector = profile.get("detector")
detection = profile.get("detection")
acceptance = profile.get("acceptance")
if (
profile.get("schema_version") != PROFILE_SCHEMA
or not isinstance(source, dict)
or source.get("camera_source_id") != "sensor.camera.right"
or source.get("stream_sha256") != source_sha256
or source.get("resolution") != [800, 600]
or source.get("calibration_model") != "KB4"
or not isinstance(detector, dict)
or detector.get("id") != "yolox_s"
or detector.get("input_shape") != [1, 3, 640, 640]
or detector.get("single_inference_per_source_frame") is not True
or not isinstance(detection, dict)
or detection.get("custom_detector_logic") is not False
or detection.get("route_specific_filtering") is not False
or not isinstance(acceptance, dict)
or acceptance.get("require_full_raw_fov") is not True
):
raise RuntimeError("E46J profile contract changed")
def load_mask(path: Path) -> np.ndarray:
from PIL import Image
mask = np.asarray(Image.open(path.resolve(strict=True)).convert("L")) > 0
if mask.shape != (600, 800) or not np.any(mask):
raise RuntimeError("E46J valid-FOV mask changed")
return mask
def preprocess(
image_bgr: np.ndarray, mask: np.ndarray, profile: dict[str, Any]
) -> np.ndarray:
import cv2
target_height = int(profile["detector"]["input_shape"][2])
target_width = int(profile["detector"]["input_shape"][3])
height, width = image_bgr.shape[:2]
ratio = min(target_height / height, target_width / width)
resized_width = int(width * ratio)
resized_height = int(height * ratio)
fill = int(profile["preprocessing"]["valid_fov_fill_value"])
masked = np.where(mask[..., None], image_bgr, fill).astype(np.uint8)
resized = cv2.resize(
masked, (resized_width, resized_height), interpolation=cv2.INTER_LINEAR
)
canvas = np.full((target_height, target_width, 3), fill, dtype=np.uint8)
canvas[:resized_height, :resized_width] = resized
return np.ascontiguousarray(canvas.transpose(2, 0, 1), dtype=np.float32)[None]
class TritonHttpClient:
"""Minimal persistent Triton HTTP client for one sequential camera stream."""
def __init__(self, url: str, model: dict[str, Any]) -> None:
parsed = urllib.parse.urlsplit(url)
if parsed.scheme != "http" or not parsed.hostname:
raise RuntimeError("E46J requires an explicit HTTP Triton endpoint")
self.model = model
self.path = f"{parsed.path.rstrip('/')}/v2/models/{model['id']}/infer"
self.connection = http.client.HTTPConnection(
parsed.hostname,
parsed.port or 80,
timeout=60,
)
def close(self) -> None:
self.connection.close()
def infer(self, tensor: np.ndarray) -> np.ndarray:
contiguous = np.ascontiguousarray(tensor, dtype=np.float32)
binary = contiguous.tobytes()
header = {
"inputs": [
{
"name": self.model["input_name"],
"shape": list(contiguous.shape),
"datatype": "FP32",
"parameters": {"binary_data_size": len(binary)},
}
],
"outputs": [
{
"name": self.model["output_name"],
"parameters": {"binary_data": True},
}
],
}
encoded = canonical_json(header)
self.connection.request(
"POST",
self.path,
body=encoded + binary,
headers={
"Content-Type": "application/octet-stream",
"Inference-Header-Content-Length": str(len(encoded)),
},
)
response = self.connection.getresponse()
payload = response.read()
if response.status != 200:
raise RuntimeError(
f"E46J Triton inference failed: HTTP {response.status}: "
f"{payload[:512]!r}"
)
header_length_value = response.getheader("Inference-Header-Content-Length")
if not header_length_value:
raise RuntimeError("E46J Triton output header length is missing")
header_length = int(header_length_value)
descriptor = json.loads(payload[:header_length])["outputs"][0]
if (
descriptor["name"] != self.model["output_name"]
or descriptor["datatype"] != "FP32"
):
raise RuntimeError("E46J Triton output descriptor changed")
shape = tuple(int(value) for value in descriptor["shape"])
array = np.frombuffer(payload[header_length:], dtype="<f4")
if array.size != math.prod(shape):
raise RuntimeError("E46J Triton output byte length changed")
return array.reshape(shape)
def decode_yolox(output: np.ndarray) -> np.ndarray:
predictions = output.copy()
grids: list[np.ndarray] = []
strides: list[np.ndarray] = []
for stride in (8, 16, 32):
height = 640 // stride
width = 640 // stride
yv, xv = np.meshgrid(np.arange(height), np.arange(width), indexing="ij")
grids.append(np.stack((xv, yv), axis=2).reshape(1, -1, 2))
strides.append(np.full((1, height * width, 1), stride))
grid = np.concatenate(grids, axis=1)
expanded_strides = np.concatenate(strides, axis=1)
predictions[..., :2] = (predictions[..., :2] + grid) * expanded_strides
predictions[..., 2:4] = np.exp(predictions[..., 2:4]) * expanded_strides
return predictions
def box_iou(one: np.ndarray, many: np.ndarray) -> np.ndarray:
if many.size == 0:
return np.zeros((0,), dtype=np.float32)
top_left = np.maximum(one[:2], many[:, :2])
bottom_right = np.minimum(one[2:], many[:, 2:])
intersection = np.prod(np.maximum(0.0, bottom_right - top_left), axis=1)
one_area = max(0.0, float(one[2] - one[0])) * max(
0.0, float(one[3] - one[1])
)
many_area = np.maximum(0.0, many[:, 2] - many[:, 0]) * np.maximum(
0.0, many[:, 3] - many[:, 1]
)
union = one_area + many_area - intersection
return np.divide(
intersection, union, out=np.zeros_like(intersection), where=union > 0
)
def nms(boxes: np.ndarray, scores: np.ndarray, threshold: float) -> list[int]:
order = scores.argsort()[::-1]
keep: list[int] = []
while order.size:
index = int(order[0])
keep.append(index)
overlaps = box_iou(boxes[index], boxes[order[1:]])
order = order[np.where(overlaps <= threshold)[0] + 1]
return keep
def valid_fraction(
box: np.ndarray, integral: np.ndarray
) -> tuple[float, bool, float]:
height = integral.shape[0] - 1
width = integral.shape[1] - 1
x1 = int(np.clip(math.floor(float(box[0])), 0, width))
y1 = int(np.clip(math.floor(float(box[1])), 0, height))
x2 = int(np.clip(math.ceil(float(box[2])), 0, width))
y2 = int(np.clip(math.ceil(float(box[3])), 0, height))
area = float(max(0, x2 - x1) * max(0, y2 - y1))
if area <= 0:
return 0.0, False, 0.0
inside = integral[y2, x2] - integral[y1, x2] - integral[y2, x1] + integral[
y1, x1
]
center_x = int(
np.clip(round((float(box[0]) + float(box[2])) / 2.0), 0, width - 1)
)
center_y = int(
np.clip(round((float(box[1]) + float(box[3])) / 2.0), 0, height - 1)
)
center_inside = bool(
integral[center_y + 1, center_x + 1]
- integral[center_y, center_x + 1]
- integral[center_y + 1, center_x]
+ integral[center_y, center_x]
)
return float(inside) / area, center_inside, area
def detections(
output: np.ndarray, profile: dict[str, Any], mask: np.ndarray
) -> tuple[list[dict[str, Any]], dict[str, int]]:
prediction = decode_yolox(output)[0]
boxes = prediction[:, :4]
boxes_xyxy = np.empty_like(boxes)
boxes_xyxy[:, 0] = boxes[:, 0] - boxes[:, 2] / 2.0
boxes_xyxy[:, 1] = boxes[:, 1] - boxes[:, 3] / 2.0
boxes_xyxy[:, 2] = boxes[:, 0] + boxes[:, 2] / 2.0
boxes_xyxy[:, 3] = boxes[:, 1] + boxes[:, 3] / 2.0
source_height, source_width = mask.shape
ratio = min(640 / source_height, 640 / source_width)
boxes_xyxy /= ratio
class_scores = prediction[:, 4:5] * prediction[:, 5:]
class_ids = class_scores.argmax(axis=1)
scores = class_scores[np.arange(class_scores.shape[0]), class_ids]
detection = profile["detection"]
target_ids = set(int(value) for value in detection["target_class_ids"])
candidate_mask = np.logical_and(
scores >= float(detection["minimum_score"]),
np.isin(class_ids, list(target_ids)),
)
candidate_boxes = boxes_xyxy[candidate_mask]
candidate_scores = scores[candidate_mask]
candidate_classes = class_ids[candidate_mask]
integral = np.pad(mask.astype(np.int64), ((1, 0), (1, 0))).cumsum(0).cumsum(1)
result: list[dict[str, Any]] = []
rejected: Counter[str] = Counter()
for class_id in sorted(target_ids):
indices = np.where(candidate_classes == class_id)[0]
if not indices.size:
continue
keep = nms(
candidate_boxes[indices],
candidate_scores[indices],
float(detection["nms_iou_threshold"]),
)
for selected in indices[keep]:
box = candidate_boxes[selected].copy()
box[[0, 2]] = np.clip(box[[0, 2]], 0, source_width)
box[[1, 3]] = np.clip(box[[1, 3]], 0, source_height)
fraction, center_inside, area = valid_fraction(box, integral)
if area < float(detection["minimum_box_area_pixels"]):
rejected["small_box"] += 1
continue
if area / float(source_width * source_height) > float(
detection["maximum_box_area_fraction"]
):
rejected["large_box"] += 1
continue
if fraction < float(detection["minimum_valid_fov_fraction"]):
rejected["outside_valid_fov"] += 1
continue
if detection["require_center_inside_valid_fov"] and not center_inside:
rejected["center_outside_valid_fov"] += 1
continue
result.append(
{
"class_id": int(class_id),
"label": COCO_CLASSES[int(class_id)],
"score": round(float(candidate_scores[selected]), 9),
"bbox_xyxy": [round(float(value), 6) for value in box],
"valid_fov_fraction": round(fraction, 6),
}
)
result.sort(key=lambda item: (-float(item["score"]), int(item["class_id"])))
return result, dict(rejected)
def draw_overlay(image: np.ndarray, rows: list[dict[str, Any]]) -> np.ndarray:
import cv2
for row in rows:
x1, y1, x2, y2 = (int(round(value)) for value in row["bbox_xyxy"])
label = f"{row['label']} {float(row['score']):.2f}"
cv2.rectangle(image, (x1, y1), (x2, y2), (248, 248, 248), 2)
(text_width, text_height), baseline = cv2.getTextSize(
label, cv2.FONT_HERSHEY_SIMPLEX, 0.45, 1
)
top = max(0, y1 - text_height - baseline - 6)
cv2.rectangle(
image,
(x1, top),
(min(image.shape[1] - 1, x1 + text_width + 8), y1),
(18, 18, 18),
-1,
)
cv2.putText(
image,
label,
(x1 + 4, max(text_height + 1, y1 - baseline - 3)),
cv2.FONT_HERSHEY_SIMPLEX,
0.45,
(248, 248, 248),
1,
cv2.LINE_AA,
)
return image
class Telemetry:
def __init__(self, interval: float) -> None:
self.interval = interval
self.samples: list[dict[str, float]] = []
self.stop_event = threading.Event()
self.thread = threading.Thread(target=self._run, daemon=True)
def __enter__(self) -> Telemetry:
self.thread.start()
return self
def __exit__(self, *_args: object) -> None:
self.stop_event.set()
self.thread.join(timeout=5)
def _run(self) -> None:
while not self.stop_event.is_set():
try:
completed = subprocess.run(
[
"nvidia-smi",
"--query-gpu=utilization.gpu,memory.used,power.draw,temperature.gpu",
"--format=csv,noheader,nounits",
],
check=True,
capture_output=True,
text=True,
timeout=10,
)
values = [float(value.strip()) for value in completed.stdout.split(",")]
self.samples.append(
{
"gpu_utilization_percent": values[0],
"gpu_memory_used_mib": values[1],
"gpu_power_watts": values[2],
"gpu_temperature_celsius": values[3],
}
)
except (OSError, ValueError, subprocess.SubprocessError):
pass
self.stop_event.wait(self.interval)
def telemetry_summary(samples: list[dict[str, float]]) -> dict[str, object]:
result: dict[str, object] = {"sample_count": len(samples)}
for key in (
"gpu_utilization_percent",
"gpu_memory_used_mib",
"gpu_power_watts",
"gpu_temperature_celsius",
):
result[key] = distribution([row[key] for row in samples])
return result
def run(args: argparse.Namespace) -> dict[str, Any]:
import av
source = args.input.resolve(strict=True)
profile_path = args.profile.resolve(strict=True)
mask_path = args.mask.resolve(strict=True)
output = args.output.expanduser().absolute()
output.mkdir(parents=True, exist_ok=False)
profile = read_object(profile_path)
source_sha = sha256(source)
validate_profile(profile, source_sha)
mask = load_mask(mask_path)
mask_sha = sha256(mask_path)
container = av.open(str(source))
video_stream = container.streams.video[0]
frame_rate = float(video_stream.average_rate)
if not math.isclose(frame_rate, float(profile["source"]["frame_rate"]), abs_tol=1e-6):
raise RuntimeError(
"E46J source frame rate changed: "
f"profile={profile['source']['frame_rate']}, actual={frame_rate}, "
f"average_rate={video_stream.average_rate}, time_base={video_stream.time_base}"
)
overlay_path = output / "raw-fisheye-yolox-overlay-intermediate.mp4"
overlay_writer = None
if args.overlay:
import cv2
overlay_writer = cv2.VideoWriter(
str(overlay_path),
cv2.VideoWriter_fourcc(*"mp4v"),
frame_rate,
(800, 600),
)
if not overlay_writer.isOpened():
raise RuntimeError("E46J intermediate overlay encoder is unavailable")
frame_path = output / "frames.jsonl"
frame_stream = frame_path.open("x", encoding="utf-8")
class_counts: Counter[str] = Counter()
rejected_counts: Counter[str] = Counter()
latencies: dict[str, list[float]] = {
"preprocess_ms": [],
"inference_request_ms": [],
"postprocess_ms": [],
"core_path_ms": [],
"overlay_encode_ms": [],
}
zero_detection_frames = 0
maximum_detections = 0
failed_frames = 0
processed_frames = 0
run_started_utc = datetime.now(UTC)
wall_started = time.perf_counter()
warm_tensor = np.full((1, 3, 640, 640), 114.0, dtype=np.float32)
triton = TritonHttpClient(args.triton_url, profile["detector"])
triton.infer(warm_tensor)
with Telemetry(args.telemetry_interval) as telemetry:
try:
for decoded in container.decode(video_stream):
if args.max_frames and processed_frames >= args.max_frames:
break
frame_index = processed_frames
image = decoded.to_ndarray(format="bgr24")
if image.shape != (600, 800, 3):
raise RuntimeError(f"E46J source raster changed at frame {frame_index}")
core_started = time.perf_counter()
preprocess_started = core_started
tensor = preprocess(image, mask, profile)
preprocess_ms = (time.perf_counter() - preprocess_started) * 1000.0
inference_started = time.perf_counter()
raw_output = triton.infer(tensor)
inference_ms = (time.perf_counter() - inference_started) * 1000.0
postprocess_started = time.perf_counter()
rows, rejected = detections(raw_output, profile, mask)
postprocess_ms = (time.perf_counter() - postprocess_started) * 1000.0
core_path_ms = (time.perf_counter() - core_started) * 1000.0
latencies["preprocess_ms"].append(preprocess_ms)
latencies["inference_request_ms"].append(inference_ms)
latencies["postprocess_ms"].append(postprocess_ms)
latencies["core_path_ms"].append(core_path_ms)
class_counts.update(str(row["label"]) for row in rows)
rejected_counts.update(rejected)
maximum_detections = max(maximum_detections, len(rows))
if not rows:
zero_detection_frames += 1
overlay_ms = 0.0
if overlay_writer is not None:
overlay_started = time.perf_counter()
overlay = draw_overlay(image.copy(), rows)
overlay_writer.write(overlay)
overlay_ms = (time.perf_counter() - overlay_started) * 1000.0
latencies["overlay_encode_ms"].append(overlay_ms)
frame_stream.write(
json.dumps(
{
"schema_version": FRAME_SCHEMA,
"frame_index": frame_index,
"session_seconds": round(frame_index / frame_rate, 6),
"detections": rows,
"rejected": rejected,
"latency_ms": {
"preprocess": round(preprocess_ms, 6),
"inference_request": round(inference_ms, 6),
"postprocess": round(postprocess_ms, 6),
"core_path": round(core_path_ms, 6),
"overlay_encode": round(overlay_ms, 6),
},
},
ensure_ascii=False,
separators=(",", ":"),
allow_nan=False,
)
+ "\n"
)
processed_frames += 1
except BaseException:
failed_frames += 1
raise
finally:
frame_stream.flush()
os.fsync(frame_stream.fileno())
frame_stream.close()
container.close()
triton.close()
if overlay_writer is not None:
overlay_writer.release()
wall_seconds = time.perf_counter() - wall_started
run_completed_utc = datetime.now(UTC)
core = distribution(latencies["core_path_ms"])
inference = distribution(latencies["inference_request_ms"])
acceptance = profile["acceptance"]
expected_frames = (
min(int(args.max_frames), int(profile["source"]["frame_count"]))
if args.max_frames
else int(profile["source"]["frame_count"])
)
checks = {
"frame_coverage": processed_frames == expected_frames,
"zero_failed_frames": failed_frames == 0,
"core_capacity_fps": (1000.0 / core["mean"])
>= float(acceptance["minimum_core_capacity_fps"]),
"core_path_p95_ms": core["p95"]
<= float(acceptance["maximum_core_path_p95_ms"]),
"inference_request_p95_ms": inference["p95"]
<= float(acceptance["maximum_inference_request_p95_ms"]),
"single_pass_full_raw_fov": True,
}
runtime: dict[str, Any] = {
"schema_version": RUNTIME_SCHEMA,
"status": "completed" if all(checks.values()) else "completed-gate-failed",
"worker_host": platform.node(),
"gpu_name": subprocess.run(
["nvidia-smi", "--query-gpu=name", "--format=csv,noheader"],
check=True,
capture_output=True,
text=True,
timeout=10,
).stdout.strip().splitlines()[0],
"started_at_utc": run_started_utc.isoformat(timespec="milliseconds").replace(
"+00:00", "Z"
),
"completed_at_utc": run_completed_utc.isoformat(
timespec="milliseconds"
).replace("+00:00", "Z"),
"source": {
"video_sha256": source_sha,
"frame_rate": frame_rate,
"resolution": [800, 600],
"decoded_frame_count": processed_frames,
"valid_fov_mask_sha256": mask_sha,
"preprocessing": "raw KB4 valid-FOV fill plus top-left letterbox; no crop/dewarp/tile",
},
"model": {
"id": profile["detector"]["id"],
"model_sha256": profile["detector"]["model_sha256"],
"config_sha256": profile["detector"]["config_sha256"],
"runtime": profile["detector"]["runtime"],
"inference_requests": processed_frames,
},
"profile_sha256": sha256(profile_path),
"metrics": {
"processed_frame_count": processed_frames,
"failed_frame_count": failed_frames,
"wall_seconds_including_overlay_export": round(wall_seconds, 6),
"export_throughput_fps": round(processed_frames / wall_seconds, 6),
"core_capacity_fps": round(1000.0 / core["mean"], 6),
"latency_ms": {key: distribution(values) for key, values in latencies.items()},
"detection_observation_count": sum(class_counts.values()),
"class_observation_counts": dict(sorted(class_counts.items())),
"mean_detections_per_frame": round(
sum(class_counts.values()) / max(processed_frames, 1), 6
),
"maximum_detections_per_frame": maximum_detections,
"zero_detection_frame_count": zero_detection_frames,
"zero_detection_frame_fraction": round(
zero_detection_frames / max(processed_frames, 1), 9
),
"rejected_counts": dict(sorted(rejected_counts.items())),
"gpu": telemetry_summary(telemetry.samples),
},
"acceptance": {
"thresholds": acceptance,
"checks": checks,
"passed": all(checks.values()),
},
"artifacts": {
"frames": {
"file": frame_path.name,
"byte_length": frame_path.stat().st_size,
"sha256": sha256(frame_path),
},
},
"authority": profile["authority"],
}
if overlay_path.is_file():
runtime["artifacts"]["overlay_intermediate"] = {
"file": overlay_path.name,
"byte_length": overlay_path.stat().st_size,
"sha256": sha256(overlay_path),
"codec": "MPEG-4 Part 2",
"frame_rate": frame_rate,
}
runtime_path = output / "runtime.json"
runtime_path.write_text(
json.dumps(runtime, ensure_ascii=False, indent=2, allow_nan=False) + "\n",
encoding="utf-8",
)
return runtime
def main() -> int:
runtime = run(arguments())
print(json.dumps(runtime, ensure_ascii=False, indent=2, allow_nan=False))
return 0 if runtime["acceptance"]["passed"] else 2
if __name__ == "__main__":
raise SystemExit(main())
@@ -0,0 +1,457 @@
#!/usr/bin/env python3
"""Run the canonical YOLOX detector on calibration-derived K1 perspective views.
This module is an adapter between the already qualified K1 KB4 rectification
from LAB E3 and the already qualified YOLOX/ByteTrack detector from LAB E8.
It deliberately does not introduce another detector, tracker or coordinate
system. Detections are projected back into the immutable right-camera frame
before class-aware NMS and tracking.
"""
from __future__ import annotations
import argparse
import copy
import concurrent.futures
import json
import math
import statistics
import time
from collections import Counter
from pathlib import Path
from typing import Any
import numpy as np
from PIL import Image, ImageDraw
from run_e3_rectified_segmentation import _clahe, _rectification_maps
from run_e5_instance_tracking import (
_detections,
_infer,
_load_valid_fov,
_nms,
_preprocess,
_valid_fraction,
)
def _arguments() -> argparse.Namespace:
parser = argparse.ArgumentParser()
parser.add_argument("--job", type=Path, required=True)
parser.add_argument("--frames", type=Path, required=True)
parser.add_argument("--rectification-profile", type=Path, required=True)
parser.add_argument("--detector-profile", type=Path, required=True)
parser.add_argument("--valid-fov-root", type=Path, required=True)
parser.add_argument("--triton-url", required=True)
parser.add_argument("--output", type=Path, required=True)
parser.add_argument("--contrast", choices=("none", "clahe"), default="none")
parser.add_argument(
"--active-tiles",
default="front,left,right,up,down",
help="Comma-separated E3 rectification tile names evaluated this pass",
)
parser.add_argument("--limit", type=int, default=0)
parser.add_argument(
"--tile-size",
type=int,
default=0,
help="Detector raster size; 0 preserves the E3 profile raster",
)
return parser.parse_args()
def _read_object(path: Path) -> dict[str, Any]:
value = json.loads(path.resolve(strict=True).read_text(encoding="utf-8-sig"))
if not isinstance(value, dict):
raise RuntimeError(f"JSON root is not an object: {path}")
return value
def _percentile(values: list[float], percentile: float) -> float:
if not values:
return 0.0
ordered = sorted(values)
index = (len(ordered) - 1) * percentile
lower = math.floor(index)
upper = math.ceil(index)
if lower == upper:
return ordered[lower]
fraction = index - lower
return ordered[lower] * (1.0 - fraction) + ordered[upper] * fraction
def _project_tile_box(
box: list[float],
tile: dict[str, Any],
selected_tile: np.ndarray,
tile_index: int,
) -> tuple[np.ndarray, tuple[float, float]] | None:
"""Project a perspective bbox perimeter into the raw fisheye frame."""
map_x = tile["raw_map_x"]
map_y = tile["raw_map_y"]
height, width = map_x.shape
x1, y1, x2, y2 = (float(value) for value in box)
x1 = float(np.clip(x1, 0.0, width - 1.0))
x2 = float(np.clip(x2, 0.0, width - 1.0))
y1 = float(np.clip(y1, 0.0, height - 1.0))
y2 = float(np.clip(y2, 0.0, height - 1.0))
if x2 <= x1 or y2 <= y1:
return None
samples = max(16, int(max(x2 - x1, y2 - y1) / 4.0))
horizontal = np.linspace(x1, x2, samples)
vertical = np.linspace(y1, y2, samples)
sample_x = np.concatenate(
(horizontal, horizontal, np.full(samples, x1), np.full(samples, x2))
)
sample_y = np.concatenate(
(np.full(samples, y1), np.full(samples, y2), vertical, vertical)
)
integer_x = np.clip(np.rint(sample_x).astype(np.int64), 0, width - 1)
integer_y = np.clip(np.rint(sample_y).astype(np.int64), 0, height - 1)
raw_x = map_x[integer_y, integer_x]
raw_y = map_y[integer_y, integer_x]
finite = np.isfinite(raw_x) & np.isfinite(raw_y)
if not np.any(finite):
return None
center_x = int(np.clip(round((x1 + x2) / 2.0), 0, width - 1))
center_y = int(np.clip(round((y1 + y2) / 2.0), 0, height - 1))
raw_center_x = float(map_x[center_y, center_x])
raw_center_y = float(map_y[center_y, center_x])
raw_height, raw_width = selected_tile.shape
if not (
math.isfinite(raw_center_x)
and math.isfinite(raw_center_y)
and 0.0 <= raw_center_x < raw_width
and 0.0 <= raw_center_y < raw_height
):
return None
owner_x = int(np.clip(round(raw_center_x), 0, raw_width - 1))
owner_y = int(np.clip(round(raw_center_y), 0, raw_height - 1))
if int(selected_tile[owner_y, owner_x]) != tile_index:
return None
projected = np.asarray(
[
float(np.min(raw_x[finite])),
float(np.min(raw_y[finite])),
float(np.max(raw_x[finite])),
float(np.max(raw_y[finite])),
],
dtype=np.float64,
)
projected[[0, 2]] = np.clip(projected[[0, 2]], 0.0, raw_width)
projected[[1, 3]] = np.clip(projected[[1, 3]], 0.0, raw_height)
return projected, (raw_center_x, raw_center_y)
def _merge_raw_detections(
candidates: list[dict[str, Any]],
detector_profile: dict[str, Any],
valid_mask: np.ndarray,
) -> tuple[list[dict[str, Any]], dict[str, int]]:
detection = detector_profile["detection"]
integral = np.pad(valid_mask.astype(np.int64), ((1, 0), (1, 0))).cumsum(0).cumsum(1)
raw_height, raw_width = valid_mask.shape
admitted: list[dict[str, Any]] = []
rejected = Counter()
for candidate in candidates:
box = np.asarray(candidate["bbox_xyxy"], dtype=np.float64)
valid_fraction, center_inside, area = _valid_fraction(box, integral)
if area < float(detection["minimum_box_area_pixels"]):
rejected["small_raw_box"] += 1
continue
if area / float(raw_width * raw_height) > float(
detection["maximum_box_area_fraction"]
):
rejected["large_raw_box"] += 1
continue
if valid_fraction < float(detection["minimum_valid_fov_fraction"]):
rejected["outside_raw_valid_fov"] += 1
continue
if detection["require_center_inside_valid_fov"] and not center_inside:
rejected["raw_center_outside_valid_fov"] += 1
continue
enriched = dict(candidate)
enriched["valid_fov_fraction"] = round(valid_fraction, 6)
admitted.append(enriched)
result: list[dict[str, Any]] = []
class_ids = sorted({int(item["class_id"]) for item in admitted})
for class_id in class_ids:
group = [item for item in admitted if int(item["class_id"]) == class_id]
boxes = np.asarray([item["bbox_xyxy"] for item in group], dtype=np.float64)
scores = np.asarray([item["score"] for item in group], dtype=np.float64)
keep = _nms(
boxes,
scores,
float(detection["nms_iou_threshold"]),
float(detection["nms_containment_threshold"]),
)
result.extend(group[index] for index in keep)
rejected["cross_tile_duplicate"] += len(group) - len(keep)
result.sort(key=lambda item: (-float(item["score"]), int(item["class_id"])))
return result, dict(rejected)
def detect_rectified(
image: np.ndarray,
*,
maps: dict[str, Any],
rectification_profile: dict[str, Any],
detector_profile: dict[str, Any],
valid_mask: np.ndarray,
triton_url: str,
contrast: str,
active_tiles: set[str],
cv2: Any,
) -> tuple[list[dict[str, Any]], dict[str, Any]]:
candidates: list[dict[str, Any]] = []
tile_metrics: list[dict[str, Any]] = []
tile_valid_mask = np.ones(
(
int(rectification_profile["rectification"]["tile_size"]),
int(rectification_profile["rectification"]["tile_size"]),
),
dtype=bool,
)
active_entries = [
(tile_index, tile)
for tile_index, tile in enumerate(maps["tiles"])
if tile["name"] in active_tiles
]
def prepare(entry: tuple[int, dict[str, Any]]) -> dict[str, Any]:
tile_index, tile = entry
started = time.perf_counter()
tile_image = cv2.remap(
image,
tile["raw_map_x"],
tile["raw_map_y"],
interpolation=cv2.INTER_LINEAR,
borderMode=cv2.BORDER_CONSTANT,
borderValue=(114, 114, 114),
)
if contrast == "clahe":
tile_image = _clahe(tile_image, rectification_profile, cv2)
preprocess_started = time.perf_counter()
tensor = _preprocess(tile_image, tile_valid_mask, detector_profile)
preprocess_ms = (time.perf_counter() - preprocess_started) * 1000.0
return {
"tile_index": tile_index,
"tile": tile,
"tensor": tensor,
"preprocess_ms": preprocess_ms,
"preparation_ms": (time.perf_counter() - started) * 1000.0,
}
# Rectification/remap is CPU work and independent for each calibrated view.
# Keep the pool bounded to the three operational core views; Triton requests
# below remain sequential against the single canonical model instance.
with concurrent.futures.ThreadPoolExecutor(
max_workers=len(active_entries), thread_name_prefix="k1-kb4-view"
) as executor:
prepared = list(executor.map(prepare, active_entries))
for item in prepared:
tile_index = int(item["tile_index"])
tile = item["tile"]
tile_started = time.perf_counter()
output, inference_ms = _infer(
triton_url, detector_profile["model"], item["tensor"]
)
tile_detections, tile_rejected = _detections(
output, detector_profile, tile_valid_mask
)
owned = 0
for detection in tile_detections:
projected = _project_tile_box(
detection["bbox_xyxy"], tile, maps["selected_tile"], tile_index
)
if projected is None:
continue
raw_box, raw_center = projected
candidate = dict(detection)
candidate["bbox_xyxy"] = [round(float(value), 6) for value in raw_box]
candidate["raw_center_xy"] = [round(value, 6) for value in raw_center]
candidate["rectification_tile"] = tile["name"]
candidates.append(candidate)
owned += 1
tile_metrics.append(
{
"tile": tile["name"],
"detections": len(tile_detections),
"owned_detections": owned,
"inference_ms": round(inference_ms, 6),
"preprocess_ms": round(float(item["preprocess_ms"]), 6),
"preparation_ms": round(float(item["preparation_ms"]), 6),
"serial_inference_postprocess_ms": round(
(time.perf_counter() - tile_started) * 1000.0, 6
),
"rejected": tile_rejected,
}
)
merged, rejected = _merge_raw_detections(candidates, detector_profile, valid_mask)
return merged, {
"tiles": tile_metrics,
"candidate_count": len(candidates),
"rejected": rejected,
}
def _draw_overlay(image: np.ndarray, detections: list[dict[str, Any]]) -> Image.Image:
output = Image.fromarray(image)
draw = ImageDraw.Draw(output)
palette = {
"person": "#8cff5d",
"bicycle": "#50d7ff",
"car": "#ffffff",
"motorcycle": "#ffcd57",
"bus": "#ff8b5d",
"truck": "#ff8b5d",
}
for detection in detections:
box = tuple(float(value) for value in detection["bbox_xyxy"])
color = palette.get(str(detection["label"]), "#ffffff")
draw.rectangle(box, outline=color, width=3)
label = (
f"{detection['label']} {float(detection['score']):.0%} "
f"[{detection['rectification_tile']}]"
)
text_box = draw.textbbox((box[0], max(0.0, box[1] - 18.0)), label)
draw.rectangle(text_box, fill="#0b0b0d")
draw.text((box[0], max(0.0, box[1] - 18.0)), label, fill=color)
return output
def main() -> None:
arguments = _arguments()
if arguments.limit < 0:
raise RuntimeError("--limit cannot be negative")
if arguments.tile_size and not 320 <= arguments.tile_size <= 1024:
raise RuntimeError("--tile-size must be zero or within [320, 1024]")
import cv2
rectification_profile = _read_object(arguments.rectification_profile)
if arguments.tile_size:
rectification_profile = copy.deepcopy(rectification_profile)
rectification_profile["rectification"]["tile_size"] = arguments.tile_size
detector_profile = _read_object(arguments.detector_profile)
job = _read_object(arguments.job)
active_tiles = {
value.strip() for value in arguments.active_tiles.split(",") if value.strip()
}
configured_tiles = {
str(tile["name"])
for tile in rectification_profile["rectification"]["tiles"]
}
if not active_tiles or not active_tiles <= configured_tiles:
raise RuntimeError("--active-tiles contains an unknown or empty tile set")
expected_resolution = tuple(rectification_profile["source"]["resolution"])
if expected_resolution != tuple(detector_profile["source"]["resolution"]):
raise RuntimeError("Rectification and detector source resolutions differ")
if (
rectification_profile["source"]["calibration_sha256"]
!= detector_profile["source"]["calibration_sha256"]
):
raise RuntimeError("Rectification and detector calibration identities differ")
valid_mask, _mask_metadata = _load_valid_fov(
arguments.valid_fov_root, job, detector_profile
)
maps_started = time.perf_counter()
maps = _rectification_maps(rectification_profile, valid_mask)
maps_ms = (time.perf_counter() - maps_started) * 1000.0
frame_paths = sorted(arguments.frames.glob("*.jpg"))
if arguments.limit:
frame_paths = frame_paths[: arguments.limit]
if not frame_paths:
raise RuntimeError("No JPEG benchmark frames were found")
arguments.output.mkdir(parents=True, exist_ok=False)
overlays = arguments.output / "overlays"
overlays.mkdir()
documents: list[dict[str, Any]] = []
frame_latencies: list[float] = []
inference_latencies: list[float] = []
detection_counts: list[int] = []
for frame_path in frame_paths:
image = np.asarray(Image.open(frame_path).convert("RGB"), dtype=np.uint8)
if (image.shape[1], image.shape[0]) != expected_resolution:
raise RuntimeError(f"Unexpected camera resolution: {frame_path}")
started = time.perf_counter()
detections, diagnostics = detect_rectified(
image,
maps=maps,
rectification_profile=rectification_profile,
detector_profile=detector_profile,
valid_mask=valid_mask,
triton_url=arguments.triton_url,
contrast=arguments.contrast,
active_tiles=active_tiles,
cv2=cv2,
)
elapsed_ms = (time.perf_counter() - started) * 1000.0
frame_latencies.append(elapsed_ms)
inference_latencies.extend(
float(tile["inference_ms"]) for tile in diagnostics["tiles"]
)
detection_counts.append(len(detections))
overlay_name = frame_path.name
_draw_overlay(image, detections).save(
overlays / overlay_name, format="JPEG", quality=92, optimize=True
)
documents.append(
{
"frame": frame_path.name,
"elapsed_ms": round(elapsed_ms, 6),
"detections": detections,
"diagnostics": diagnostics,
"overlay": f"overlays/{overlay_name}",
}
)
print(
f"FRAME={frame_path.name} DETECTIONS={len(detections)} "
f"ELAPSED_MS={elapsed_ms:.3f}",
flush=True,
)
summary = {
"schema_version": "missioncore.rectified-yolox-benchmark/v1",
"pipeline": "k1-kb4-cubemap5-yolox-raw-frame/v1",
"contrast": arguments.contrast,
"active_tiles": sorted(active_tiles),
"tile_size": int(rectification_profile["rectification"]["tile_size"]),
"frame_count": len(documents),
"rectification_map_build_ms": round(maps_ms, 6),
"latency_ms": {
"mean": round(statistics.fmean(frame_latencies), 6),
"p50": round(_percentile(frame_latencies, 0.5), 6),
"p95": round(_percentile(frame_latencies, 0.95), 6),
"maximum": round(max(frame_latencies), 6),
},
"tile_inference_ms": {
"mean": round(statistics.fmean(inference_latencies), 6),
"p95": round(_percentile(inference_latencies, 0.95), 6),
"maximum": round(max(inference_latencies), 6),
},
"detections_per_frame": {
"mean": round(statistics.fmean(detection_counts), 6),
"minimum": min(detection_counts),
"maximum": max(detection_counts),
"total": sum(detection_counts),
},
"coverage": maps["coverage"],
"frames": documents,
}
(arguments.output / "benchmark.json").write_text(
json.dumps(summary, ensure_ascii=False, sort_keys=True, indent=2) + "\n",
encoding="utf-8",
)
print(json.dumps({key: value for key, value in summary.items() if key != "frames"}))
if __name__ == "__main__":
main()
@@ -0,0 +1,326 @@
#!/usr/bin/env python3
"""Qualify the rectified YOLOX + existing ByteTrack path on full RAVNOVES00."""
from __future__ import annotations
import argparse
import copy
import json
import platform
import statistics
import time
from collections import Counter
from pathlib import Path
from typing import Any
import numpy as np
from PIL import Image
from run_e3_rectified_segmentation import _rectification_maps
from run_e5_instance_tracking import (
TwoStageTracker,
_duplicate_pairs,
_load_valid_fov,
_track_document,
)
from run_rectified_yolox_detector import (
_draw_overlay,
_percentile,
_read_object,
detect_rectified,
)
def _arguments() -> argparse.Namespace:
parser = argparse.ArgumentParser()
parser.add_argument("--job", type=Path, required=True)
parser.add_argument("--video", type=Path, required=True)
parser.add_argument("--rectification-profile", type=Path, required=True)
parser.add_argument("--detector-profile", type=Path, required=True)
parser.add_argument("--valid-fov-root", type=Path, required=True)
parser.add_argument("--triton-url", required=True)
parser.add_argument("--output", type=Path, required=True)
parser.add_argument("--active-tiles", default="front,left,right")
parser.add_argument("--tile-size", type=int, default=640)
parser.add_argument("--preview-stride", type=int, default=250)
parser.add_argument("--limit", type=int, default=0)
return parser.parse_args()
def _latency(values: list[float]) -> dict[str, float]:
return {
"mean": round(statistics.fmean(values), 6),
"p50": round(_percentile(values, 0.5), 6),
"p95": round(_percentile(values, 0.95), 6),
"p99": round(_percentile(values, 0.99), 6),
"maximum": round(max(values), 6),
}
def main() -> None:
arguments = _arguments()
if arguments.tile_size != 640:
raise RuntimeError("Operational qualification is fixed to detector-native 640 tiles")
if arguments.preview_stride < 1 or arguments.limit < 0:
raise RuntimeError("Preview stride or frame limit is invalid")
if not arguments.triton_url.startswith("http://"):
raise RuntimeError("Triton URL must use the internal HTTP endpoint")
import cv2
job = _read_object(arguments.job)
if (
job.get("job_id") != "recorded-camera-602ac89026ed12978619801d"
or job.get("input", {}).get("session_id") != "20260720T065719Z_viewer_live"
or job.get("input", {}).get("source_id") != "sensor.camera.right"
or job.get("input", {}).get("segment_count") != 4489
):
raise RuntimeError("Qualification is not bound to immutable RAVNOVES00")
rectification_profile = copy.deepcopy(_read_object(arguments.rectification_profile))
detector_profile = _read_object(arguments.detector_profile)
rectification_profile["rectification"]["tile_size"] = arguments.tile_size
if (
rectification_profile["source"]["calibration_sha256"]
!= detector_profile["source"]["calibration_sha256"]
):
raise RuntimeError("Calibration identity differs between detector stages")
active_tiles = {
value.strip() for value in arguments.active_tiles.split(",") if value.strip()
}
if active_tiles != {"front", "left", "right"}:
raise RuntimeError("Operational qualification requires front,left,right views")
valid_mask, valid_fov = _load_valid_fov(
arguments.valid_fov_root, job, detector_profile
)
maps_started = time.perf_counter()
maps = _rectification_maps(rectification_profile, valid_mask)
maps_ms = (time.perf_counter() - maps_started) * 1000.0
video = arguments.video.resolve(strict=True)
capture = cv2.VideoCapture(str(video))
if not capture.isOpened():
raise RuntimeError("OpenCV could not open the immutable camera replay")
width = int(capture.get(cv2.CAP_PROP_FRAME_WIDTH))
height = int(capture.get(cv2.CAP_PROP_FRAME_HEIGHT))
source_count = int(capture.get(cv2.CAP_PROP_FRAME_COUNT))
if (width, height, source_count) != (800, 600, 4489):
raise RuntimeError("Camera replay metadata violates the RAVNOVES00 contract")
output = arguments.output.resolve()
if output.exists():
raise RuntimeError("Qualification output must be absent")
output.mkdir(parents=True, mode=0o700)
previews = output / "previews"
previews.mkdir()
frames_path = output / "frames.jsonl"
tracker = TwoStageTracker(detector_profile["tracking"])
from scipy.optimize import linear_sum_assignment
linear_sum_assignment(np.zeros((1, 1), dtype=np.float64))
latency: list[float] = []
decode_latency: list[float] = []
detector_latency: list[float] = []
tracking_latency: list[float] = []
detections_by_label: Counter[str] = Counter()
tracks_by_label: Counter[str] = Counter()
rejections: Counter[str] = Counter()
unique_tracks: set[int] = set()
duplicate_pairs = 0
processed = 0
failures = 0
# One explicit warmup makes the source-rate metrics independent from model
# initialization while preserving the first source frame for the real run.
ok, warm_bgr = capture.read()
if not ok:
raise RuntimeError("Camera replay has no warmup frame")
warm_rgb = cv2.cvtColor(warm_bgr, cv2.COLOR_BGR2RGB)
detect_rectified(
warm_rgb,
maps=maps,
rectification_profile=rectification_profile,
detector_profile=detector_profile,
valid_mask=valid_mask,
triton_url=arguments.triton_url,
contrast="none",
active_tiles=active_tiles,
cv2=cv2,
)
capture.set(cv2.CAP_PROP_POS_FRAMES, 0)
run_started = time.perf_counter()
try:
with frames_path.open("x", encoding="utf-8", newline="\n") as stream:
while True:
if arguments.limit and processed >= arguments.limit:
break
decode_started = time.perf_counter()
ok, bgr = capture.read()
decoded = time.perf_counter()
if not ok:
break
image = cv2.cvtColor(bgr, cv2.COLOR_BGR2RGB)
frame_started = time.perf_counter()
try:
detections, diagnostics = detect_rectified(
image,
maps=maps,
rectification_profile=rectification_profile,
detector_profile=detector_profile,
valid_mask=valid_mask,
triton_url=arguments.triton_url,
contrast="none",
active_tiles=active_tiles,
cv2=cv2,
)
detected = time.perf_counter()
tracks = tracker.update(detections, processed)
tracked = time.perf_counter()
except Exception:
failures += 1
raise
frame_ms = (tracked - frame_started) * 1000.0
latency.append(frame_ms)
decode_latency.append((decoded - decode_started) * 1000.0)
detector_latency.append((detected - frame_started) * 1000.0)
tracking_latency.append((tracked - detected) * 1000.0)
detections_by_label.update(str(item["label"]) for item in detections)
tracks_by_label.update(track.label for track in tracks)
for track in tracks:
unique_tracks.add(track.track_id)
duplicate_pairs += _duplicate_pairs(tracks)
rejections.update(diagnostics["rejected"])
document = {
"schema_version": "missioncore.rectified-yolox-frame/v1",
"frame_index": processed,
"sequence": processed + 1,
"detections": detections,
"tracks": [_track_document(track) for track in tracks],
"processing_ms": round(frame_ms, 6),
"tile_inference_ms": round(
sum(float(tile["inference_ms"]) for tile in diagnostics["tiles"]),
6,
),
}
stream.write(
json.dumps(
document,
ensure_ascii=False,
sort_keys=True,
separators=(",", ":"),
)
+ "\n"
)
if processed % arguments.preview_stride == 0:
_draw_overlay(image, detections).save(
previews / f"frame-{processed:06d}.jpg",
format="JPEG",
quality=92,
optimize=True,
)
processed += 1
if processed % 100 == 0:
stream.flush()
print(
f"PHASE=rectified-yolox FRAMES={processed} "
f"LAST_MS={frame_ms:.3f}",
flush=True,
)
finally:
capture.release()
wall_seconds = time.perf_counter() - run_started
expected = arguments.limit or source_count
steady = latency[1:] if len(latency) > 1 else latency
effective_fps = processed / wall_seconds
steady_latency = _latency(steady)
checks = {
"complete_frame_accounting": processed == expected,
"zero_failures": failures == 0,
"minimum_effective_fps_9_5": effective_fps >= 9.5,
"maximum_steady_p95_ms_100": steady_latency["p95"] <= 100.0,
"same_class_duplicate_pairs_zero": duplicate_pairs == 0,
}
accepted = all(checks.values())
report: dict[str, Any] = {
"schema_version": "missioncore.rectified-yolox-qualification/v1",
"state": "accepted" if accepted else "rejected",
"source": {
"job_id": job["job_id"],
"session_id": job["input"]["session_id"],
"source_id": job["input"]["source_id"],
"frame_count": source_count,
"calibration_sha256": detector_profile["source"]["calibration_sha256"],
},
"pipeline": {
"id": "k1-kb4-core3-yolox-bytetrack/v1",
"rectification": "five-perspective-gnomonic/v1",
"active_tiles": sorted(active_tiles),
"tile_size": arguments.tile_size,
"detector": detector_profile["model"],
"tracker": detector_profile["tracking"],
"peripheral_views": "up/down remain on the existing 2 Hz semantic cadence",
},
"runtime": {
"hostname": platform.node(),
"python": platform.python_version(),
"opencv": cv2.__version__,
"rectification_map_build_ms": round(maps_ms, 6),
"wall_seconds": round(wall_seconds, 6),
"effective_fps": round(effective_fps, 6),
},
"metrics": {
"frames_processed": processed,
"failures": failures,
"latency_ms": _latency(latency),
"steady_latency_ms": steady_latency,
"decode_latency_ms": _latency(decode_latency),
"detector_latency_ms": _latency(detector_latency),
"tracking_latency_ms": _latency(tracking_latency),
"detections": int(sum(detections_by_label.values())),
"detections_by_label": dict(sorted(detections_by_label.items())),
"unique_confirmed_tracks": len(unique_tracks),
"track_observations": int(sum(tracks_by_label.values())),
"track_observations_by_label": dict(sorted(tracks_by_label.items())),
"rejections": dict(sorted(rejections.items())),
"same_class_duplicate_pairs_iou_ge_0_8": duplicate_pairs,
"tracker_tracks_created": tracker.created,
"tracker_tracks_retired": tracker.retired,
},
"acceptance": {"accepted": accepted, "checks": checks},
"valid_fov": valid_fov,
"limitations": [
"RAVNOVES00 has no independent exhaustive object ground truth.",
"This gate qualifies runtime and visual evidence, not safety accuracy.",
"LiDAR association, temporal world state and segmentation are existing downstream stages and are not recomputed by this detector-only gate.",
],
"artifacts": {
"frames": "frames.jsonl",
"previews": "previews/",
},
}
(output / "qualification.json").write_text(
json.dumps(report, ensure_ascii=False, sort_keys=True, indent=2) + "\n",
encoding="utf-8",
)
print(
json.dumps(
{
"accepted": accepted,
"frames_processed": processed,
"effective_fps": round(effective_fps, 6),
"steady_p95_ms": steady_latency["p95"],
"detections": report["metrics"]["detections"],
"unique_confirmed_tracks": len(unique_tracks),
},
sort_keys=True,
),
flush=True,
)
raise SystemExit(0 if accepted else 2)
if __name__ == "__main__":
main()