Tighten cutter phase span validation

This commit is contained in:
Melbar
2026-05-08 14:56:44 +02:00
parent 10e27afc8d
commit acafe538b2
8 changed files with 52 additions and 13 deletions
+1 -1
View File
File diff suppressed because one or more lines are too long
+11 -3
View File
@@ -135,6 +135,13 @@ Dauer erneut geschätzt; läuft die Source am Ende in eine sichtbar andere
Aktionsphase, wird der Clip gekürzt und der Rest bleibt Placeholder/Fade Aktionsphase, wird der Clip gekürzt und der Rest bleibt Placeholder/Fade
statt einen falschen Bewegungsmoment zu zeigen. statt einen falschen Bewegungsmoment zu zeigen.
Diese Span-Schätzung ist strenger als der grobe Suchscore: Ein fast stehender
Anfang darf einen Match nicht retten, wenn spätere Frames sichtbar in eine
andere Gestik, Körperposition oder eintretende Figur driften. Stabile
Score-Plateaus dürfen nur verlängern, wenn sie noch nah genug am Anfangsniveau
liegen; sonst bleibt der Rest bewusst offen, statt eine falsche Phase als
Source-Material auszugeben.
## Vision-Seeds vs. Vollscan ## Vision-Seeds vs. Vollscan
Der gewichtete Vision-Seed-Pfad ersetzt standardmäßig keinen normalen Der gewichtete Vision-Seed-Pfad ersetzt standardmäßig keinen normalen
@@ -171,9 +178,10 @@ Inpoint und übernimmt nur klare Verbesserungen. Er darf keine ganze lange
Dialogszene nach ähnlichen Layouts durchsuchen, weil sonst dieselbe Location Dialogszene nach ähnlichen Layouts durchsuchen, weil sonst dieselbe Location
mit anderer Gestik als falsche Phase gewinnen kann und die Laufzeit explodiert. mit anderer Gestik als falsche Phase gewinnen kann und die Laufzeit explodiert.
Report-Clips werden zusätzlich an den bekannten Source-Szenenstart plus eine Report-Clips werden zusätzlich an den bekannten Source-Szenenstart plus eine
kurze Guard-Zone geklemmt, damit ein knapp vor oder direkt auf der Schnittkante sehr kurze Ein-Frame-Guard-Zone geklemmt, damit ein knapp vor oder direkt auf
liegender Inpoint nicht mit Frames der vorherigen Einstellung oder einer zu der Schnittkante liegender Inpoint nicht mit Frames der vorherigen Einstellung
frühen Übergangsphase beginnt. beginnt. Die Guard-Zone bleibt bewusst klein, weil eine längere Korrektur die
sichtbare Bewegungsphase innerhalb derselben Einstellung verschieben würde.
## Multi-Shot-Beats ## Multi-Shot-Beats
Binary file not shown.
Binary file not shown.
Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 14 KiB

+1 -1
View File
@@ -120,7 +120,7 @@ STILL_WIDTH = 480
STILL_QUALITY = 4 STILL_QUALITY = 4
CLIP_WIDTH = 480 CLIP_WIDTH = 480
CLIP_MAX_DURATION_S = 30.0 CLIP_MAX_DURATION_S = 30.0
SCENE_START_GUARD_S = 0.32 SCENE_START_GUARD_S = 0.04
# Each half of the side-by-side compare strip # Each half of the side-by-side compare strip
COMPARE_HALF_W = 480 COMPARE_HALF_W = 480
COMPARE_H = 270 # 16:9 COMPARE_H = 270 # 16:9
+33 -5
View File
@@ -7,6 +7,34 @@
$ErrorActionPreference = "Stop" $ErrorActionPreference = "Stop"
$VENV_DIR = ".venv" $VENV_DIR = ".venv"
function Invoke-CapturedProcess {
param(
[Parameter(Mandatory = $true)][string]$FilePath,
[Parameter(Mandatory = $false)][string[]]$Arguments = @()
)
$psi = [System.Diagnostics.ProcessStartInfo]::new()
$psi.FileName = $FilePath
foreach ($arg in $Arguments) {
[void]$psi.ArgumentList.Add($arg)
}
$psi.UseShellExecute = $false
$psi.RedirectStandardOutput = $true
$psi.RedirectStandardError = $true
$psi.CreateNoWindow = $true
$process = [System.Diagnostics.Process]::Start($psi)
$stdout = $process.StandardOutput.ReadToEnd()
$stderr = $process.StandardError.ReadToEnd()
$process.WaitForExit()
$combined = (($stdout + "`n" + $stderr).Trim())
if ($process.ExitCode -ne 0) {
throw "Command failed ($($process.ExitCode)): $FilePath $($Arguments -join ' ')`n$combined"
}
return $combined
}
function Resolve-ProjectPython { function Resolve-ProjectPython {
$cmd = Get-Command python -ErrorAction SilentlyContinue $cmd = Get-Command python -ErrorAction SilentlyContinue
if ($cmd) { if ($cmd) {
@@ -35,7 +63,7 @@ Write-Host ""
# ---- 1. Check Python version ------------------------------------------------ # ---- 1. Check Python version ------------------------------------------------
$PROJECT_PYTHON = Resolve-ProjectPython $PROJECT_PYTHON = Resolve-ProjectPython
$pythonVersion = & $PROJECT_PYTHON --version 2>&1 $pythonVersion = Invoke-CapturedProcess $PROJECT_PYTHON @("--version")
Write-Host "Python: $pythonVersion" Write-Host "Python: $pythonVersion"
if ($pythonVersion -notmatch "3\.(1[1-9]|[2-9]\d)") { if ($pythonVersion -notmatch "3\.(1[1-9]|[2-9]\d)") {
Write-Error "Python 3.11+ required. Found: $pythonVersion" Write-Error "Python 3.11+ required. Found: $pythonVersion"
@@ -48,8 +76,8 @@ if (Test-Path $VENV_DIR) {
$venvOk = $false $venvOk = $false
if (Test-Path $existingVenvPython) { if (Test-Path $existingVenvPython) {
try { try {
$existingVersion = & $existingVenvPython --version 2>&1 $existingVersion = Invoke-CapturedProcess $existingVenvPython @("--version")
$venvOk = $LASTEXITCODE -eq 0 -and $existingVersion -match "3\.(1[1-9]|[2-9]\d)" $venvOk = $existingVersion -match "3\.(1[1-9]|[2-9]\d)"
} catch { } catch {
$venvOk = $false $venvOk = $false
} }
@@ -60,12 +88,12 @@ if (Test-Path $VENV_DIR) {
} else { } else {
Write-Host "Existing virtual environment is not usable. Recreating '$VENV_DIR' ..." -ForegroundColor Yellow Write-Host "Existing virtual environment is not usable. Recreating '$VENV_DIR' ..." -ForegroundColor Yellow
Remove-Item -LiteralPath $VENV_DIR -Recurse -Force Remove-Item -LiteralPath $VENV_DIR -Recurse -Force
& $PROJECT_PYTHON -m venv $VENV_DIR Invoke-CapturedProcess $PROJECT_PYTHON @("-m", "venv", $VENV_DIR) | Out-Null
Write-Host "Done." -ForegroundColor Green Write-Host "Done." -ForegroundColor Green
} }
} else { } else {
Write-Host "Creating virtual environment in '$VENV_DIR' ..." -ForegroundColor Green Write-Host "Creating virtual environment in '$VENV_DIR' ..." -ForegroundColor Green
& $PROJECT_PYTHON -m venv $VENV_DIR Invoke-CapturedProcess $PROJECT_PYTHON @("-m", "venv", $VENV_DIR) | Out-Null
Write-Host "Done." -ForegroundColor Green Write-Host "Done." -ForegroundColor Green
} }
+6 -3
View File
@@ -1330,7 +1330,10 @@ def estimate_usable_source_duration(
warmup_scores = [score for offset, score in scores if offset <= min(1.0, beat.duration_s * 0.35)] warmup_scores = [score for offset, score in scores if offset <= min(1.0, beat.duration_s * 0.35)]
baseline = max(warmup_scores) if warmup_scores else max(score for _, score in scores) baseline = max(warmup_scores) if warmup_scores else max(score for _, score in scores)
min_score = max(0.34, baseline * 0.48) # Keep the usable span tied to the same action phase, not just the same room
# or actors. A loose cutoff hides "same scene, wrong moment" drift in long
# dialogue shots where the background remains highly correlated.
min_score = max(0.42, baseline * 0.62)
last_good = 0.0 last_good = 0.0
bad_run = 0 bad_run = 0
@@ -1363,8 +1366,8 @@ def estimate_usable_source_duration(
if ( if (
len(tail_scores) >= 3 len(tail_scores) >= 3
and float(np.std(tail_scores)) < 0.025 and float(np.std(tail_scores)) < 0.025
and float(np.mean(tail_scores)) > 0.20 and float(np.mean(tail_scores)) > max(0.30, baseline * 0.58)
and baseline >= 0.30 and baseline >= 0.42
): ):
logger.debug( logger.debug(
'Beat %d: stable plateau detected at offset %.3fs ' 'Beat %d: stable plateau detected at offset %.3fs '