Add -Upscale parameter to Clone-Image.ps1 for AI upscaling with upscayl-bin before ffmpeg processing, add -Model and -Keep parameters for model selection and output preservation, and update Stich-InBetween.ps1 to use RIFE_HOME and NCNN_GPU_ID environment variables for TNTwise fork compatibility

This commit is contained in:
2026-06-10 17:36:45 -04:00
parent 1e58558308
commit fb4f371450
2 changed files with 205 additions and 8 deletions
+169 -1
View File
@@ -27,6 +27,23 @@
(0-6, higher means smaller files but slower encoding). Ignored for jpeg, avif, bmp and tiff. (0-6, higher means smaller files but slower encoding). Ignored for jpeg, avif, bmp and tiff.
.PARAMETER Metadata .PARAMETER Metadata
When present, copies available metadata from the source image file to the target image file. When present, copies available metadata from the source image file to the target image file.
.PARAMETER Upscale
Optional upscaling factor (2, 3 or 4) applied to the source image with upscayl-bin.exe
before ffmpeg runs. The upscaled image is written to a Windows temp PNG and used as the
ffmpeg input, so any -Size / -LTX cover-scale-then-crop is applied to the upscaled image.
Requires upscayl-bin.exe in PATH, the UPSCAYL_HOME environment variable pointing to the
upscayl-bin install directory, and the NCNN_GPU_ID environment variable set to the desired
GPU device ID (-1 for CPU, 0/1/2/... for GPU).
.PARAMETER Model
Upscayl model name (without extension) to use when -Upscale is specified. Defaults to
'4xNomos8kSC'. The model files (<Model>.bin and <Model>.param) must exist under
%UPSCAYL_HOME%\models. Ignored when -Upscale is not specified.
.PARAMETER Keep
Optional path to save a copy of the upscayl-bin output (i.e. the upscaled image, before
ffmpeg's cover-scale-then-crop). The file extension determines the format; if it differs
from .png (the upscayl output format) the temp PNG is converted with ffmpeg using the
appropriate per-format encoder. If the extension is missing or unrecognised, .png is
appended. Only valid when -Upscale is specified.
.PARAMETER LTX .PARAMETER LTX
When present, overrides the dimensions selected by -Size with the closest LTX-2 (Wan2GP by DeepBeepMeep) When present, overrides the dimensions selected by -Size with the closest LTX-2 (Wan2GP by DeepBeepMeep)
canonical resolution for the source's aspect ratio. Only valid when -Size is one of HD, 1080p, 720p or 480p. canonical resolution for the source's aspect ratio. Only valid when -Size is one of HD, 1080p, 720p or 480p.
@@ -61,7 +78,17 @@ param(
[switch]$Metadata, [switch]$Metadata,
[Parameter(Mandatory = $false)] [Parameter(Mandatory = $false)]
[switch]$LTX [switch]$LTX,
[Parameter(Mandatory = $false)]
[ValidateSet(0, 2, 3, 4)]
[int]$Upscale = 0,
[Parameter(Mandatory = $false)]
[string]$Model = '4xNomos8kSC',
[Parameter(Mandatory = $false)]
[string]$Keep = $null
) )
Set-StrictMode -Version Latest Set-StrictMode -Version Latest
@@ -259,6 +286,142 @@ switch ($targetFormat) {
} }
} }
# --- Optional upscayl-bin upscaling step (runs before ffmpeg). ---
# When -Upscale is given (2/3/4) the source image is upscaled with upscayl-bin
# into a temp PNG; that temp PNG then becomes the ffmpeg input so any -Size /
# -LTX cover-scale-then-crop is applied to the already-upscaled image.
$upscaledTempPath = $null
if ($Upscale -gt 0) {
$upscaylExe = Get-Command -Name 'upscayl-bin.exe' -ErrorAction SilentlyContinue
if (-not $upscaylExe) {
Write-Error 'upscayl-bin.exe not found in PATH. Install upscayl-bin and add it to the system PATH (see mkdocs/Upscalers.md).'
exit 1
}
$upscaylExePath = $upscaylExe.Source
$upscaylHome = $env:UPSCAYL_HOME
if (-not $upscaylHome) {
Write-Error 'UPSCAYL_HOME environment variable is not set. Set it to the upscayl-bin install directory.'
exit 1
}
$upscaylHome = $upscaylHome.TrimEnd('\', '/')
if (-not (Test-Path -LiteralPath $upscaylHome -PathType Container)) {
Write-Error ('UPSCAYL_HOME directory does not exist: {0}' -f $upscaylHome)
exit 1
}
$modelsDir = Join-Path $upscaylHome 'models'
if (-not (Test-Path -LiteralPath $modelsDir -PathType Container)) {
Write-Error ('Upscayl models directory not found: {0}' -f $modelsDir)
exit 1
}
$modelBin = Join-Path $modelsDir ('{0}.bin' -f $Model)
$modelParam = Join-Path $modelsDir ('{0}.param' -f $Model)
if (-not (Test-Path -LiteralPath $modelBin -PathType Leaf) -or -not (Test-Path -LiteralPath $modelParam -PathType Leaf)) {
Write-Error ('Upscayl model files not found for model ''{0}'' under {1} (expected {0}.bin and {0}.param).' -f $Model, $modelsDir)
exit 1
}
$gpuId = $env:NCNN_GPU_ID
if (-not $gpuId) {
Write-Error 'NCNN_GPU_ID environment variable is not set. Set it to -1 (CPU) or 0/1/2/... (GPU device index).'
exit 1
}
if ($gpuId -notmatch '^-?\d+$') {
Write-Error ('NCNN_GPU_ID must be an integer (-1 for CPU, 0+ for GPU). Current value: {0}' -f $gpuId)
exit 1
}
$upscaledTempPath = Join-Path ([System.IO.Path]::GetTempPath()) (('upscayl-{0}.png') -f ([System.Guid]::NewGuid().ToString()))
Write-Host ('Upscaling source with upscayl-bin (x{0}, model={1}, gpu={2})...' -f $Upscale, $Model, $gpuId)
Write-Host (' Upscayl temp file: {0}' -f $upscaledTempPath)
if ($PSCmdlet.ShouldProcess($SourcePath, ('Upscale x{0} with upscayl-bin into temp file' -f $Upscale))) {
$upscaylOutput = & $upscaylExePath -i $SourcePath -o $upscaledTempPath -s $Upscale.ToString() -n $Model -m $modelsDir -g $gpuId 2>&1
$upscaylExit = $LASTEXITCODE
if ($upscaylExit -ne 0) {
Write-Host 'upscayl-bin error output:' -ForegroundColor Red
$upscaylOutput | ForEach-Object { Write-Host $_ }
Write-Error ('upscayl-bin failed with exit code: {0}' -f $upscaylExit)
if (Test-Path -LiteralPath $upscaledTempPath) { Remove-Item -LiteralPath $upscaledTempPath -Force -ErrorAction SilentlyContinue }
exit 1
}
# Switch ffmpeg's input to the upscaled image.
$SourcePath = $upscaledTempPath
# Refresh informational source dimensions from the upscaled file.
$ffprobeDimOutput2 = & ffprobe -v error -select_streams v:0 -show_entries stream=width,height -of csv=p=0:s=x -- $SourcePath 2>&1
if ($LASTEXITCODE -eq 0) {
$dimLine2 = $ffprobeDimOutput2 | Select-Object -First 1
if ($dimLine2) {
$sourceDimensions = ([string]$dimLine2).Trim() + (' (upscaled x{0})' -f $Upscale)
}
}
# --- Optional -Keep: save a copy of the upscaled image. ---
if (-not [string]::IsNullOrWhiteSpace($Keep)) {
$KeepPath = $Keep
$keepExt = [System.IO.Path]::GetExtension($KeepPath).ToLowerInvariant()
if ($supportedTargetExts -notcontains $keepExt) {
$KeepPath += '.png'
$keepExt = '.png'
}
$KeepDir = Split-Path -Parent $KeepPath
if ($KeepDir -and -not (Test-Path -LiteralPath $KeepDir)) {
Write-Error ('Keep target directory does not exist: {0}' -f $KeepDir)
if (Test-Path -LiteralPath $upscaledTempPath) { Remove-Item -LiteralPath $upscaledTempPath -Force -ErrorAction SilentlyContinue }
exit 1
}
if ($keepExt -eq '.png') {
# Upscayl already wrote a PNG; just copy.
if ($PSCmdlet.ShouldProcess($KeepPath, 'Copy upscaled image (PNG)')) {
Copy-Item -LiteralPath $upscaledTempPath -Destination $KeepPath -Force
Write-Host ('Upscaled image saved to: {0}' -f $KeepPath)
}
} else {
# Convert PNG -> requested format with ffmpeg, using sane defaults.
switch ($keepExt) {
'.jpg' { $keepFormat = 'jpeg' }
'.jpeg' { $keepFormat = 'jpeg' }
'.tif' { $keepFormat = 'tiff' }
'.tiff' { $keepFormat = 'tiff' }
default { $keepFormat = $keepExt.TrimStart('.') }
}
$keepEncoderArgs = @()
switch ($keepFormat) {
'jpeg' { $keepEncoderArgs += @('-c:v', 'mjpeg', '-pix_fmt', 'yuvj420p', '-q:v', '2') }
'webp' { $keepEncoderArgs += @('-c:v', 'libwebp', '-quality', '90') }
'avif' { $keepEncoderArgs += @('-c:v', 'libaom-av1', '-still-picture', '1', '-cpu-used', '4', '-crf', '25') }
'bmp' { $keepEncoderArgs += @('-c:v', 'bmp') }
'tiff' { $keepEncoderArgs += @('-c:v', 'tiff') }
'png' { $keepEncoderArgs += @('-c:v', 'png') }
}
Write-Host ('Converting upscaled image to {0}: {1}' -f $keepFormat, $KeepPath)
if ($PSCmdlet.ShouldProcess($KeepPath, ('Save upscaled image as {0}' -f $keepFormat))) {
$keepFfmpegArgs = @('-y', '-i', $upscaledTempPath) + $keepEncoderArgs + @('-frames:v', '1', '-map_metadata', '-1', '--', $KeepPath)
$keepFfmpegOutput = & ffmpeg @keepFfmpegArgs 2>&1
$keepFfmpegExit = $LASTEXITCODE
if ($keepFfmpegExit -ne 0) {
Write-Host 'ffmpeg error output (Keep step):' -ForegroundColor Red
$keepFfmpegOutput | ForEach-Object { Write-Host $_ }
Write-Error ('ffmpeg failed to save upscaled image (-Keep) with exit code: {0}' -f $keepFfmpegExit)
if (Test-Path -LiteralPath $upscaledTempPath) { Remove-Item -LiteralPath $upscaledTempPath -Force -ErrorAction SilentlyContinue }
exit 1
}
Write-Host ('Upscaled image saved to: {0}' -f $KeepPath)
}
}
}
}
}
if (-not [string]::IsNullOrWhiteSpace($Keep) -and $Upscale -le 0) {
Write-Warning '-Keep was specified without -Upscale; ignoring (nothing to save).'
}
Write-Host ('Cloning image: {0}' -f $SourcePath) Write-Host ('Cloning image: {0}' -f $SourcePath)
Write-Host (' Target format: {0}' -f $targetFormat) Write-Host (' Target format: {0}' -f $targetFormat)
if ($sourceDimensions) { if ($sourceDimensions) {
@@ -316,3 +479,8 @@ if ($PSCmdlet.ShouldProcess($TargetPath, 'Clone image file')) {
Write-Host ('Image cloned successfully to: {0}' -f $TargetPath) Write-Host ('Image cloned successfully to: {0}' -f $TargetPath)
} }
# --- Clean up the upscayl temp file (if any). ---
if ($upscaledTempPath -and (Test-Path -LiteralPath $upscaledTempPath)) {
Remove-Item -LiteralPath $upscaledTempPath -Force -ErrorAction SilentlyContinue
}
+36 -7
View File
@@ -41,12 +41,17 @@
.NOTES .NOTES
Requires: Requires:
- rife-ncnn-vulkan.exe in the system PATH - rife-ncnn-vulkan.exe (TNTwise fork, RIFE 4.26) in the system PATH
- The RIFE_HOME environment variable set to the rife-ncnn-vulkan install directory
(the same directory that is on PATH; it must contain the rife-v4.26 model folder)
- The NCNN_GPU_ID environment variable set to the desired GPU device ID
(-1 for CPU, 0/1/2/etc. for GPU) passed to rife-ncnn-vulkan via the -g flag
- ffmpeg in the system PATH - ffmpeg in the system PATH
- PowerShell 7 or later - PowerShell 7 or later
The script automatically locates rife-ncnn-vulkan.exe from PATH and expects The script locates rife-ncnn-vulkan.exe from PATH and resolves the rife-v4.26
the rife-v4 model directory to be in the same directory as the executable. model directory from %RIFE_HOME%\rife-v4.26. The GPU device is selected from
%NCNN_GPU_ID%.
Supports -WhatIf and -Confirm for safe execution. Supports -WhatIf and -Confirm for safe execution.
#> #>
@@ -161,15 +166,39 @@ if (-not $rifeExe) {
} }
$rifeExePath = $rifeExe.Source $rifeExePath = $rifeExe.Source
$rifeDir = Split-Path -Parent $rifeExePath
$rifeModelPath = Join-Path $rifeDir "rife-v4" # Resolve the model directory via RIFE_HOME (required by the TNTwise fork's
# install layout, see mkdocs/RIFE.md). RIFE_HOME must point to the install
# folder containing the rife-v4.26 model directory.
$rifeHome = $env:RIFE_HOME
if (-not $rifeHome) {
throw "RIFE_HOME environment variable is not set. Set it to the rife-ncnn-vulkan install directory (the same folder that is on PATH)."
}
$rifeHome = $rifeHome.TrimEnd('\', '/')
if (-not (Test-Path -LiteralPath $rifeHome -PathType Container)) {
throw "RIFE_HOME directory does not exist: $rifeHome"
}
$rifeModelPath = Join-Path $rifeHome "rife-v4.26"
if (-not (Test-Path -LiteralPath $rifeModelPath -PathType Container)) { if (-not (Test-Path -LiteralPath $rifeModelPath -PathType Container)) {
throw "RIFE model directory not found at: $rifeModelPath" throw "RIFE model directory not found at: $rifeModelPath. Ensure the rife-v4.26 model folder ships under %RIFE_HOME%."
}
# Resolve the GPU device ID for the -g flag from NCNN_GPU_ID.
$gpuId = $env:NCNN_GPU_ID
if (-not $gpuId) {
throw "NCNN_GPU_ID environment variable is not set. Set it to -1 (CPU) or 0/1/2/etc. (GPU device index)."
}
if ($gpuId -notmatch '^-?\d+$') {
throw "NCNN_GPU_ID must be an integer (-1 for CPU, 0+ for GPU). Current value: $gpuId"
} }
Write-Host "Found rife-ncnn-vulkan.exe at: $rifeExePath" Write-Host "Found rife-ncnn-vulkan.exe at: $rifeExePath"
Write-Host "Using RIFE_HOME: $rifeHome"
Write-Host "Using model path: $rifeModelPath" Write-Host "Using model path: $rifeModelPath"
Write-Host "Using GPU device (-g): $gpuId"
# --- Prepare TempDir --- # --- Prepare TempDir ---
@@ -190,7 +219,7 @@ if (Test-Path -LiteralPath $tempDirPath) {
if ($PSCmdlet.ShouldProcess($TempDir, "Generate interpolated frames with rife-ncnn-vulkan (Count=$Count)")) { if ($PSCmdlet.ShouldProcess($TempDir, "Generate interpolated frames with rife-ncnn-vulkan (Count=$Count)")) {
Write-Host "Executing rife-ncnn-vulkan.exe (Count=$Count, EndsAt=$EndsAt)..." Write-Host "Executing rife-ncnn-vulkan.exe (Count=$Count, EndsAt=$EndsAt)..."
& $rifeExePath -0 $FirstFilename -1 $LastFilename -i $InputTempDir -o $TempDir -n $Count -m $rifeModelPath & $rifeExePath -0 $FirstFilename -1 $LastFilename -i $InputTempDir -o $TempDir -n $Count -m $rifeModelPath -g $gpuId
if ($LASTEXITCODE -ne 0) { if ($LASTEXITCODE -ne 0) {
throw "rife-ncnn-vulkan.exe failed with exit code $LASTEXITCODE" throw "rife-ncnn-vulkan.exe failed with exit code $LASTEXITCODE"