Inital push
This commit is contained in:
@@ -0,0 +1,231 @@
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Generates interpolated frames between two images using RIFE and creates a video.
|
||||
|
||||
.DESCRIPTION
|
||||
This script uses rife-ncnn-vulkan to generate intermediate frames between a first
|
||||
and last image, then encodes the result into an H.264 video using ffmpeg.
|
||||
|
||||
The script:
|
||||
- Validates input image files exist
|
||||
- Creates a dedicated temporary directory for input images
|
||||
- Copies the first and last images to the dedicated temp directory
|
||||
- Runs rife-ncnn-vulkan to generate interpolated frames
|
||||
- Filters generated frames to keep only the desired sequence
|
||||
- Encodes final video with specified FPS
|
||||
|
||||
.PARAMETER First
|
||||
Full or relative path to the first image file (e.g., "First.png" or "D:\Images\First.png").
|
||||
|
||||
.PARAMETER Last
|
||||
Full or relative path to the last image file (e.g., "Last.png" or "D:\Images\Last.png").
|
||||
|
||||
.PARAMETER TempDir
|
||||
Full absolute path to a temporary directory for intermediate PNG frames.
|
||||
Must end with a backslash (\). Defaults to a system temp subdirectory.
|
||||
|
||||
.PARAMETER Frames
|
||||
Number of frames to generate in the output video. Must be greater than 0.
|
||||
|
||||
.PARAMETER Video
|
||||
Output video filename. If relative, will be resolved against current directory.
|
||||
|
||||
.PARAMETER FPS
|
||||
Frames per second for the output video. Default is 24.
|
||||
|
||||
.EXAMPLE
|
||||
.\Stich-InBetween.ps1 -First "frame1.png" -Last "frame2.png" -Frames 30 -Video "output.mp4"
|
||||
|
||||
.EXAMPLE
|
||||
.\Stich-InBetween.ps1 -First "D:\Images\start.png" -Last "D:\Images\end.png" -Frames 60 -Video "D:\Output\video.mp4" -FPS 30
|
||||
|
||||
.NOTES
|
||||
Requires:
|
||||
- rife-ncnn-vulkan.exe in the system PATH
|
||||
- ffmpeg in the system PATH
|
||||
- PowerShell 7 or later
|
||||
|
||||
The script automatically locates rife-ncnn-vulkan.exe from PATH and expects
|
||||
the rife-v4 model directory to be in the same directory as the executable.
|
||||
|
||||
Supports -WhatIf and -Confirm for safe execution.
|
||||
#>
|
||||
|
||||
[CmdletBinding(SupportsShouldProcess)]
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$First,
|
||||
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$Last,
|
||||
|
||||
[Parameter(Mandatory = $false)]
|
||||
[string]$TempDir,
|
||||
|
||||
[Parameter(Mandatory = $true)]
|
||||
[ValidateScript({ $_ -gt 0 })]
|
||||
[int]$Frames,
|
||||
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$Video,
|
||||
|
||||
[Parameter(Mandatory = $false)]
|
||||
[int]$FPS = 24
|
||||
)
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
|
||||
# --- Validate and expand parameters ---
|
||||
|
||||
# First and Last - resolve to absolute paths
|
||||
if (-not [System.IO.Path]::IsPathRooted($First)) {
|
||||
$First = Join-Path (Get-Location).Path $First
|
||||
}
|
||||
if (-not [System.IO.Path]::IsPathRooted($Last)) {
|
||||
$Last = Join-Path (Get-Location).Path $Last
|
||||
}
|
||||
|
||||
# TempDir
|
||||
if (-not $TempDir) {
|
||||
$TempDir = [System.IO.Path]::GetTempPath() + [System.Guid]::NewGuid().ToString() + '\'
|
||||
} else {
|
||||
if (-not [System.IO.Path]::IsPathRooted($TempDir)) {
|
||||
throw "TempDir must be a full absolute path, not a relative path: '$TempDir'"
|
||||
}
|
||||
if (-not $TempDir.EndsWith('\')) {
|
||||
throw "TempDir must end with a backslash (\): '$TempDir'"
|
||||
}
|
||||
}
|
||||
|
||||
# Video - if relative, resolve against current directory
|
||||
if (-not [System.IO.Path]::IsPathRooted($Video)) {
|
||||
$Video = Join-Path (Get-Location).Path $Video
|
||||
}
|
||||
|
||||
# --- Compute derived variables ---
|
||||
|
||||
$Count = 4 + (($Frames - 1) * 2)
|
||||
$EndsAt = [int]($Count / 2)
|
||||
|
||||
# --- Validate source files ---
|
||||
|
||||
if (-not (Test-Path -LiteralPath $First -PathType Leaf)) {
|
||||
throw "First image does not exist: $First"
|
||||
}
|
||||
if (-not (Test-Path -LiteralPath $Last -PathType Leaf)) {
|
||||
throw "Last image does not exist: $Last"
|
||||
}
|
||||
|
||||
# --- Delete output video if it exists ---
|
||||
|
||||
if (Test-Path -LiteralPath $Video -PathType Leaf) {
|
||||
if ($PSCmdlet.ShouldProcess($Video, 'Delete existing output video')) {
|
||||
Remove-Item -LiteralPath $Video -Force
|
||||
Write-Host "Deleted existing output video: $Video"
|
||||
}
|
||||
}
|
||||
|
||||
# --- Create dedicated temp directory for input images ---
|
||||
|
||||
$InputTempDir = [System.IO.Path]::GetTempPath() + [System.Guid]::NewGuid().ToString() + '\'
|
||||
$inputTempDirPath = $InputTempDir.TrimEnd('\')
|
||||
|
||||
if ($PSCmdlet.ShouldProcess($InputTempDir, 'Create dedicated input temp directory')) {
|
||||
New-Item -ItemType Directory -Path $inputTempDirPath | Out-Null
|
||||
Write-Host "Created input temp directory: $InputTempDir"
|
||||
}
|
||||
|
||||
# --- Copy input images to dedicated temp directory ---
|
||||
|
||||
$FirstFilename = [System.IO.Path]::GetFileName($First)
|
||||
$LastFilename = [System.IO.Path]::GetFileName($Last)
|
||||
|
||||
$InputFirstPath = Join-Path $inputTempDirPath $FirstFilename
|
||||
$InputLastPath = Join-Path $inputTempDirPath $LastFilename
|
||||
|
||||
if ($PSCmdlet.ShouldProcess($First, "Copy to $InputFirstPath")) {
|
||||
Copy-Item -LiteralPath $First -Destination $InputFirstPath -Force
|
||||
Write-Host "Copied $First -> $InputFirstPath"
|
||||
}
|
||||
|
||||
if ($PSCmdlet.ShouldProcess($Last, "Copy to $InputLastPath")) {
|
||||
Copy-Item -LiteralPath $Last -Destination $InputLastPath -Force
|
||||
Write-Host "Copied $Last -> $InputLastPath"
|
||||
}
|
||||
|
||||
# --- Locate rife-ncnn-vulkan.exe ---
|
||||
|
||||
$rifeExe = Get-Command -Name "rife-ncnn-vulkan.exe" -ErrorAction SilentlyContinue
|
||||
if (-not $rifeExe) {
|
||||
throw "rife-ncnn-vulkan.exe not found in PATH. Please ensure it is installed and added to the system PATH."
|
||||
}
|
||||
|
||||
$rifeExePath = $rifeExe.Source
|
||||
$rifeDir = Split-Path -Parent $rifeExePath
|
||||
$rifeModelPath = Join-Path $rifeDir "rife-v4"
|
||||
|
||||
if (-not (Test-Path -LiteralPath $rifeModelPath -PathType Container)) {
|
||||
throw "RIFE model directory not found at: $rifeModelPath"
|
||||
}
|
||||
|
||||
Write-Host "Found rife-ncnn-vulkan.exe at: $rifeExePath"
|
||||
Write-Host "Using model path: $rifeModelPath"
|
||||
|
||||
# --- Prepare TempDir ---
|
||||
|
||||
$tempDirPath = $TempDir.TrimEnd('\')
|
||||
if (Test-Path -LiteralPath $tempDirPath) {
|
||||
if ($PSCmdlet.ShouldProcess($TempDir, 'Clear temporary directory')) {
|
||||
Get-ChildItem -LiteralPath $tempDirPath -Force | Remove-Item -Recurse -Force
|
||||
Write-Host "Cleared TempDir: $TempDir"
|
||||
}
|
||||
} else {
|
||||
if ($PSCmdlet.ShouldProcess($TempDir, 'Create temporary directory')) {
|
||||
New-Item -ItemType Directory -Path $tempDirPath | Out-Null
|
||||
Write-Host "Created TempDir: $TempDir"
|
||||
}
|
||||
}
|
||||
|
||||
# --- Execute rife-ncnn-vulkan ---
|
||||
|
||||
if ($PSCmdlet.ShouldProcess($TempDir, "Generate interpolated frames with rife-ncnn-vulkan (Count=$Count)")) {
|
||||
Write-Host "Executing rife-ncnn-vulkan.exe (Count=$Count, EndsAt=$EndsAt)..."
|
||||
& $rifeExePath -0 $FirstFilename -1 $LastFilename -i $InputTempDir -o $TempDir -n $Count -m $rifeModelPath
|
||||
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
throw "rife-ncnn-vulkan.exe failed with exit code $LASTEXITCODE"
|
||||
}
|
||||
|
||||
# --- Clean up input temp directory ---
|
||||
if (Test-Path -LiteralPath $inputTempDirPath) {
|
||||
Remove-Item -LiteralPath $inputTempDirPath -Recurse -Force
|
||||
Write-Host "Cleaned up input temp directory: $InputTempDir"
|
||||
}
|
||||
|
||||
# --- Delete unwanted frames from TempDir ---
|
||||
# Always delete 00000001.png; delete all files with numeric index > $EndsAt
|
||||
|
||||
$filesToDelete = Get-ChildItem -LiteralPath $tempDirPath -Filter "*.png" | Where-Object {
|
||||
$index = [int]($_.BaseName)
|
||||
$index -eq 1 -or $index -gt $EndsAt
|
||||
}
|
||||
foreach ($file in $filesToDelete) {
|
||||
if ($PSCmdlet.ShouldProcess($file.FullName, 'Delete unwanted intermediate frame')) {
|
||||
Remove-Item -LiteralPath $file.FullName -Force
|
||||
Write-Host "Deleted: $($file.Name)"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# --- Execute ffmpeg ---
|
||||
|
||||
if ($PSCmdlet.ShouldProcess($Video, 'Create output video with ffmpeg')) {
|
||||
Write-Host "Executing ffmpeg (FPS=$FPS)..."
|
||||
& ffmpeg -y -framerate $FPS -i "${TempDir}%08d.png" -crf 8 -c:v libx264 -pix_fmt yuv420p "$Video"
|
||||
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
throw "ffmpeg failed with exit code $LASTEXITCODE"
|
||||
}
|
||||
|
||||
Write-Host "Video created successfully: $Video" -ForegroundColor Green
|
||||
}
|
||||
Reference in New Issue
Block a user