Add printf-style numbering token support to Get-LastFrame.ps1 for -Image parameter (e.g. frame_%04d.png), resolving token to actual 0-based frame index before passing to ffmpeg to avoid image2 sequence counter behavior, with automatic output directory creation when path does not exist and updated documentation matching Get-FramesRange.ps1 functionality
This commit is contained in:
1 parent
c057f804bf
commit
9088022ef7
1 file changed
+41
-3
+41
-3
@@ -5,6 +5,10 @@
|
||||
Path to the input video file. Can be absolute or relative to current directory.
|
||||
.PARAMETER Image
|
||||
Path to the output PNG image file. Can be absolute or relative to current directory.
|
||||
Supports a printf-style numbering token (e.g. frame_%04d.png or C:\Output\frame_%04d.png), which is
|
||||
replaced with the extracted 0-based frame index, zero-padded to the width given in the token
|
||||
(%d = no padding, %04d = 4 digits). Without a token the path is used literally.
|
||||
The output directory is created when it does not exist.
|
||||
.PARAMETER Frame
|
||||
0-based frame index to extract. Negative values count from the end:
|
||||
-1 extracts the last frame (default), -2 extracts second-to-last, etc.
|
||||
@@ -12,6 +16,7 @@
|
||||
.DESCRIPTION
|
||||
Extracts a specific frame of a video file to a PNG image using ffmpeg.
|
||||
Supports negative frame indices to count from the end of the video.
|
||||
Supports a printf-style numbering token in -Image, like Get-FramesRange.ps1.
|
||||
Supports -WhatIf and -Confirm for safe execution.
|
||||
#>
|
||||
[CmdletBinding(SupportsShouldProcess)]
|
||||
@@ -64,9 +69,42 @@ $frameToExtract = if ($Frame -lt 0) {
|
||||
|
||||
Write-Host ('Total frames: {0} - extracting frame index {1}' -f $totalFramesInt, $frameToExtract)
|
||||
|
||||
if ($PSCmdlet.ShouldProcess($Image, ('Extract frame {0} to PNG' -f $frameToExtract))) {
|
||||
# Resolve any printf-style numbering token (%d, %04d) in -Image to the extracted frame
|
||||
# index. The substitution is done here rather than left to ffmpeg: ffmpeg would treat the
|
||||
# token as an image2 sequence counter starting at 1 instead of the actual video frame index.
|
||||
$ImagePath = $Image
|
||||
$tokenPattern = [regex]'%(\d*)d'
|
||||
while ($ImagePath -match $tokenPattern) {
|
||||
$padWidth = if ($Matches[1]) { [int]$Matches[1] } else { 0 }
|
||||
$ImagePath = $tokenPattern.Replace($ImagePath, ([string]$frameToExtract).PadLeft($padWidth, '0'), 1)
|
||||
}
|
||||
|
||||
if ($ImagePath -ne $Image) {
|
||||
Write-Host ('Output pattern resolved to: {0}' -f $ImagePath)
|
||||
}
|
||||
|
||||
$outputDir = Split-Path -Parent $ImagePath
|
||||
if ($outputDir -and -not (Test-Path -LiteralPath $outputDir)) {
|
||||
if ($PSCmdlet.ShouldProcess($outputDir, 'Create output directory')) {
|
||||
New-Item -ItemType Directory -Path $outputDir -Force | Out-Null
|
||||
Write-Host ('Created output directory: {0}' -f $outputDir)
|
||||
}
|
||||
}
|
||||
|
||||
if ($PSCmdlet.ShouldProcess($ImagePath, ('Extract frame {0} to PNG' -f $frameToExtract))) {
|
||||
$videoFilter = 'select=eq(n\,{0})' -f $frameToExtract
|
||||
$ffmpegStderr = & ffmpeg -y -i $VideoPath -vf $videoFilter -vsync vfr -frames:v 1 -q:v 1 $Image 2>&1
|
||||
|
||||
$ffmpegArgs = @(
|
||||
'-y'
|
||||
'-i', $VideoPath
|
||||
'-vf', $videoFilter
|
||||
'-vsync', 'vfr'
|
||||
'-frames:v', '1'
|
||||
'-q:v', '1'
|
||||
'--', $ImagePath
|
||||
)
|
||||
|
||||
$ffmpegStderr = & ffmpeg @ffmpegArgs 2>&1
|
||||
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
$ffmpegStderr | ForEach-Object { Write-Error $_ }
|
||||
@@ -74,5 +112,5 @@ if ($PSCmdlet.ShouldProcess($Image, ('Extract frame {0} to PNG' -f $frameToExtra
|
||||
exit 1
|
||||
}
|
||||
|
||||
Write-Host ('Frame saved to: {0}' -f $Image)
|
||||
Write-Host ('Frame saved to: {0}' -f $ImagePath)
|
||||
}
|
||||
Reference in new issue
Block a user