246 lines
8.4 KiB
PowerShell
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"
|