Files
2026-07-17 17:45:56 +08:00

246 lines
8.4 KiB
PowerShell

param(
[Parameter(Mandatory = $true)]
[string]$TargetSignaturePath,
[Parameter(Mandatory = $true)]
[string]$CandidateExtractionRoot,
[string]$CandidateViewsJson = "",
[string]$OutputPath = "",
[double]$PrimitiveWeight = 0.15,
[double]$DirectionWeight = 0.15,
[double]$LineLengthWeight = 0.15,
[double]$CircleRadiusWeight = 0.03,
[double]$ArcRadiusWeight = 0.03,
[double]$ArcLengthWeight = 0.04,
[double]$RelativeMidpointWeight = 0.30,
[double]$RelativeFeatureWeight = 0.15
)
$ErrorActionPreference = "Stop"
function Read-JsonFile([string]$Path) {
if (-not (Test-Path -LiteralPath $Path)) {
throw "File does not exist: $Path"
}
return Get-Content -LiteralPath $Path -Raw | ConvertFrom-Json
}
function Get-PropertyValue($Object, [string]$Name) {
if ($null -eq $Object) {
return $null
}
$property = $Object.PSObject.Properties[$Name]
if ($null -eq $property) {
return $null
}
return $property.Value
}
function Convert-ToNumberMap($Object) {
$result = [ordered]@{}
if ($null -eq $Object) {
return $result
}
foreach ($property in $Object.PSObject.Properties) {
$value = 0.0
if ($null -ne $property.Value) {
$value = [double]$property.Value
}
$result[$property.Name] = $value
}
return $result
}
function Get-Map($Signature, [string]$Name) {
return Convert-ToNumberMap (Get-PropertyValue $Signature $Name)
}
function Get-RelativeMap($Signature, [string]$Name) {
$relative = Get-PropertyValue $Signature "relative_line_coordinates"
return Convert-ToNumberMap (Get-PropertyValue $relative $Name)
}
function Get-MapSum($Map) {
$sum = 0.0
foreach ($key in $Map.Keys) {
$sum += [double]$Map[$key]
}
return $sum
}
function Get-HistogramSimilarity($CandidateMap, $TargetMap) {
$keys = @($CandidateMap.Keys + $TargetMap.Keys | Select-Object -Unique)
if ($keys.Count -eq 0) {
return 1.0
}
$intersection = 0.0
$union = 0.0
foreach ($key in $keys) {
$candidateValue = 0.0
$targetValue = 0.0
if ($CandidateMap.Contains($key)) {
$candidateValue = [double]$CandidateMap[$key]
}
if ($TargetMap.Contains($key)) {
$targetValue = [double]$TargetMap[$key]
}
$intersection += [Math]::Min($candidateValue, $targetValue)
$union += [Math]::Max($candidateValue, $targetValue)
}
if ($union -le 0.0) {
return 1.0
}
return $intersection / $union
}
function Get-CountSimilarity([double]$CandidateCount, [double]$TargetCount) {
$maxValue = [Math]::Max($CandidateCount, $TargetCount)
if ($maxValue -le 0.0) {
return 1.0
}
return [Math]::Min($CandidateCount, $TargetCount) / $maxValue
}
function Get-CandidateMetadataMap([string]$Path) {
$map = @{}
if ([string]::IsNullOrWhiteSpace($Path) -or -not (Test-Path -LiteralPath $Path)) {
return $map
}
$rawItems = Read-JsonFile $Path
$items = if ($rawItems -is [array]) { $rawItems } else { @($rawItems) }
foreach ($item in $items) {
$index = [int](Get-PropertyValue $item "index")
$map[[string]$index] = $item
}
return $map
}
$target = Read-JsonFile $TargetSignaturePath
$candidateRoot = Resolve-Path -LiteralPath $CandidateExtractionRoot
if ([string]::IsNullOrWhiteSpace($OutputPath)) {
$OutputPath = Join-Path $candidateRoot.Path "candidate-ranking-relative.json"
}
$weights = [ordered]@{
primitive_counts = $PrimitiveWeight
line_direction_counts = $DirectionWeight
line_length_histogram = $LineLengthWeight
circle_radius_histogram = $CircleRadiusWeight
arc_radius_histogram = $ArcRadiusWeight
arc_length_histogram = $ArcLengthWeight
relative_midpoint_histogram = $RelativeMidpointWeight
relative_feature_histogram = $RelativeFeatureWeight
}
$weightSum = 0.0
foreach ($value in $weights.Values) {
$weightSum += [double]$value
}
if ([Math]::Abs($weightSum - 1.0) -gt 0.000001) {
throw "Weights must sum to 1.0. Current sum: $weightSum"
}
$metadata = Get-CandidateMetadataMap $CandidateViewsJson
$targetMaps = @{
primitive_counts = Get-Map $target "primitive_counts"
line_direction_counts = Get-Map $target "line_direction_counts"
line_length_histogram = Get-Map $target "line_length_histogram"
circle_radius_histogram = Get-Map $target "circle_radius_histogram"
arc_radius_histogram = Get-Map $target "arc_radius_histogram"
arc_length_histogram = Get-Map $target "arc_length_histogram"
relative_midpoint_histogram = Get-RelativeMap $target "relative_midpoint_histogram"
relative_feature_histogram = Get-RelativeMap $target "feature_histogram"
}
$signatureFiles = @(Get-ChildItem -LiteralPath $candidateRoot.Path -Recurse -Filter "main-view-signature.json" | Sort-Object FullName)
if ($signatureFiles.Count -eq 0) {
throw "No candidate signatures found under: $($candidateRoot.Path)"
}
$ranking = @()
foreach ($signatureFile in $signatureFiles) {
$candidate = Read-JsonFile $signatureFile.FullName
$directoryName = Split-Path -Leaf (Split-Path -Parent $signatureFile.FullName)
$index = $null
if ($directoryName -match '^(\d+)_') {
$index = [int]$Matches[1]
}
$candidateMaps = @{
primitive_counts = Get-Map $candidate "primitive_counts"
line_direction_counts = Get-Map $candidate "line_direction_counts"
line_length_histogram = Get-Map $candidate "line_length_histogram"
circle_radius_histogram = Get-Map $candidate "circle_radius_histogram"
arc_radius_histogram = Get-Map $candidate "arc_radius_histogram"
arc_length_histogram = Get-Map $candidate "arc_length_histogram"
relative_midpoint_histogram = Get-RelativeMap $candidate "relative_midpoint_histogram"
relative_feature_histogram = Get-RelativeMap $candidate "feature_histogram"
}
$componentScores = [ordered]@{}
$score = 0.0
foreach ($key in $weights.Keys) {
$component = Get-HistogramSimilarity $candidateMaps[$key] $targetMaps[$key]
$componentScores[$key] = $component
$score += $component * [double]$weights[$key]
}
$candidatePrimitiveCounts = $candidateMaps["primitive_counts"]
$targetPrimitiveCounts = $targetMaps["primitive_counts"]
$candidateLineCount = if ($candidatePrimitiveCounts.Contains("line")) { [double]$candidatePrimitiveCounts["line"] } else { 0.0 }
$targetLineCount = if ($targetPrimitiveCounts.Contains("line")) { [double]$targetPrimitiveCounts["line"] } else { 0.0 }
$meta = $null
if ($null -ne $index -and $metadata.ContainsKey([string]$index)) {
$meta = $metadata[[string]$index]
}
$ranking += [pscustomobject][ordered]@{
rank = 0
index = $index
name = $directoryName
candidate_view_name = Get-PropertyValue $meta "name"
direction_name = Get-PropertyValue $meta "direction_name"
roll_degrees = Get-PropertyValue $meta "roll_deg"
score = $score
component_scores = $componentScores
candidate_line_count = $candidateLineCount
target_line_count = $targetLineCount
line_count_score = Get-CountSimilarity $candidateLineCount $targetLineCount
candidate_relative_midpoint_bins = (Get-MapSum $candidateMaps["relative_midpoint_histogram"])
target_relative_midpoint_bins = (Get-MapSum $targetMaps["relative_midpoint_histogram"])
signature_path = $signatureFile.FullName
}
}
$sorted = @($ranking | Sort-Object -Property @{ Expression = { [double]$_.score }; Descending = $true }, @{ Expression = { [int]$_.index }; Ascending = $true })
for ($i = 0; $i -lt $sorted.Count; $i++) {
$sorted[$i].rank = $i + 1
}
$result = [ordered]@{
ok = $true
created_at = (Get-Date).ToString("o")
target_signature_path = (Resolve-Path -LiteralPath $TargetSignaturePath).Path
candidate_extraction_root = $candidateRoot.Path
candidate_count = $sorted.Count
weights = $weights
ranking = $sorted
}
$outputDirectory = Split-Path -Parent $OutputPath
if (-not [string]::IsNullOrWhiteSpace($outputDirectory)) {
New-Item -ItemType Directory -Force -Path $outputDirectory | Out-Null
}
$result | ConvertTo-Json -Depth 12 | Set-Content -LiteralPath $OutputPath -Encoding UTF8
$sorted | Select-Object -First 10 rank,index,name,score,candidate_line_count,target_line_count,line_count_score | Format-Table -AutoSize
Write-Host "Ranking written: $OutputPath"