1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 | <# 视频均匀分段批量截图|强制处理所有视频,无短片过滤 均匀划分时间段截图,避免画面重复;兼容手机MP4、MKV 修复:文件名含[]特殊字符导致Test-Path报错 #> # ========== 参数配置 ========== $FramePrefix = "_frame_" $Width = 480 $TotalFrameNum = 10 # 一共生成10张截图 $SkipEndRatio = 0.05 # 跳过最后5%,规避片尾黑屏 # ============================== $ScanRoot = $PSScriptRoot Write-Host "扫描目录:$ScanRoot`n" -ForegroundColor Cyan if (-not (Get-Command ffmpeg -ErrorAction SilentlyContinue)) { Write-Host "【错误】未找到ffmpeg,请配置系统环境变量" -ForegroundColor Red Read-Host; exit 1 } $videoPattern = "\.(mp4|mkv|mov|avi|flv|webm|m4v|ts|mpeg|mpg)$" $videoFiles = Get-ChildItem -Path $ScanRoot -File -Recurse -ErrorAction SilentlyContinue | Where-Object { $_.Name -match $videoPattern } if (-not $videoFiles) { Write-Host "未检索到任何视频文件" -ForegroundColor Yellow Read-Host; exit } Write-Host "总计找到 $($videoFiles.Count) 个视频`n" foreach ($file in $videoFiles) { $videoFull = $file.FullName $baseName = $file.BaseName Write-Host "正在处理:$($file.Name)" # 获取视频总时长 $info = ffmpeg -i "$videoFull" 2>&1 $durMatch = $info | Select-String 'Duration: (\d{2}):(\d{2}):(\d{2})\.(\d{2})' if (-not $durMatch) { Write-Warning " 无法读取视频时长,跳过`n" continue } $h = [int]$durMatch.Matches.Groups[1].Value $m = [int]$durMatch.Matches.Groups[2].Value $s = [double]"$($durMatch.Matches.Groups[3].Value).$($durMatch.Matches.Groups[3].Value)" $totalSec = $h * 3600 + $m * 60 + $s $usableEndSec = $totalSec * (1 - $SkipEndRatio) $segmentLen = $usableEndSec / $TotalFrameNum $success = 0 for ($i = 0; $i -lt $TotalFrameNum; $i++) { $outFile = Join-Path $file.Directory "$($baseName)$FramePrefix$($i+1).jpg" # 重点修复:-LiteralPath 关闭通配符解析,支持文件名带[] if (Test-Path -LiteralPath $outFile) { Write-Host " frame$($i+1) 已存在,跳过" $success++ continue } # 每一段区间中间位置截取 $captureSec = ($i * $segmentLen) + ($segmentLen / 2) ffmpeg ` -ss $captureSec ` -i "$videoFull" ` -vframes 1 ` -vf "scale=${Width}:-1" ` -y ` "`"$outFile`"" 2>&1 | Out-Null if (Test-Path -LiteralPath $outFile) { $success++ } } Write-Host " 成功生成 $success / $TotalFrameNum 张截图`n" -ForegroundColor Green } Write-Host "==================================" Write-Host "全部视频处理完成!" -ForegroundColor Cyan Read-Host "回车关闭窗口" |
基础信息
脚本名称:视频均匀分段批量截图工具
脚本文件:Video_Multi_Screenshot.ps1
脚本类型:PowerShell 可执行脚本(.ps1)
适用系统:Windows 10 / Windows 11、Windows Server 2019+
依赖程序:FFmpeg(必须提前配置系统环境变量)
权限要求:普通用户权限即可;存放路径不要设置系统只读/加密目录
编码格式:UTF-8
功能简述 自动递归扫描脚本所在目录所有视频,读取视频总时长,均匀划分区间截取10张独立截图,画面分布在前、中、后各时段,避免截图画面高度重复;不拼接、不生成九宫格,单张独立输出,最大限度兼容手机MP4录像、MKV电影等各类视频。无短片过滤,无论视频长短强制执行截图任务。
参数配置说明(脚本头部可自定义)
1 2 3 4 5 | $FramePrefix = "_frame_" # 截图文件名后缀标识 $Width = 480 # 截图宽度,高度自动等比例缩放 $TotalFrameNum = 10 # 每个视频生成截图数量 $SkipEndRatio = 0.05 # 自动跳过视频末尾百分比,规避片尾黑屏/字幕 |
支持视频格式
mp4、mkv、mov、avi、flv、webm、m4v、ts、mpeg、mpg
使用说明
- 将脚本文件
Video_Multi_Screenshot.ps1放入视频根目录(脚本会自动以自身所在目录作为扫描起点,递归遍历所有子文件夹视频) - 确认FFmpeg已解压,
ffmpeg.exe路径加入系统PATH环境变量;新开终端输入ffmpeg -version测试是否生效 - 运行方式二选一:
- 右键脚本 → 使用PowerShell运行
- 在文件夹地址栏输入
powershell,执行命令:.\Video_Multi_Screenshot.ps1
- 执行后自动处理所有视频,输出截图格式:
原文件名_frame_1.jpg ~ 原文件名_frame_10.jpg - 脚本具备缓存机制:已存在的截图自动跳过,不会重复生成
注意事项
- Windows默认策略限制ps1脚本直接运行,首次运行报错需要开启执行策略: 以管理员打开PowerShell执行:12Set-ExecutionPolicy RemoteSigned
- FFmpeg版本建议使用官方完整版,精简绿色版容易缺失解码器,造成截图失败
- 手机录制VID_*.mp4关键帧稀疏,脚本采用
-ss前置快速seek提升兼容性,但尾部时段仍有概率截取失败,可适当调大$SkipEndRatio = 0.10 - 不要存放路径包含特殊emoji、超长中文、全角特殊符号,可能导致FFmpeg读取文件失败
- 大量视频批量处理建议关闭杀毒软件实时扫描,避免大量jpg新建触发拦截
- 短片(几秒短视频)受编码限制,末尾几张截图大概率捕获失败,属于视频封装特性,非脚本BUG
常见错误 & 解决方案
错误1:无法将“ffmpeg”项识别为 cmdlet、函数、脚本文件
✅ 原因:FFmpeg未配置环境变量 ✅ 方案:将ffmpeg解压目录添加系统Path,重启PowerShell窗口测试
错误2:无法加载文件 xxx.ps1,因为在此系统上禁止运行脚本
✅ 原因:PowerShell默认执行策略阻止本地脚本 ✅ 方案:管理员终端执行 Set-ExecutionPolicy RemoteSigned,输入Y确认
错误3:无法读取视频时长,跳过
✅ 原因:视频损坏、加密视频、不支持的编码、文件被占用 ✅ 方案:检查视频能否正常播放器打开;关闭占用视频的软件
错误4:部分frame截图生成失败,成功数量低于设定值
✅ 原因:MP4关键帧间隔巨大、视频尾部无关键帧、流损坏 ✅ 优化方案:修改 $SkipEndRatio = 0.10 跳过更多片尾区间
错误5:变量引用无效。':' 后面的变量名称字符无效
✅ 原因:旧版脚本未使用 ${变量} 写法 ✅ 方案:使用本文提供最终完整版脚本,已修复全部PowerShell语法冲突
错误6:权限不足 / 拒绝访问
✅ 原因:目录只读、文件被播放器/网盘锁定 ✅ 方案:移动脚本到普通文件夹,关闭占用视频的程序
进阶优化建议(可选修改)
- 需要更大截图尺寸:修改
$Width = 640/720 - 想要每个视频只截取8张:修改
$TotalFrameNum = 8 - 几乎全部截图捕获失败:将
$SkipEndRatio提升至0.15
附完整成品脚本(展示区块,网站可折叠展示)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 | <# 视频均匀分段批量截图|强制处理所有视频,无短片过滤 均匀划分时间段截图,避免画面重复;兼容手机MP4、MKV #> # ========== 参数配置 ========== $FramePrefix = "_frame_" $Width = 480 $TotalFrameNum = 10 # 一共生成10张截图 $SkipEndRatio = 0.05 # 跳过最后5%,规避片尾黑屏 # ============================== $ScanRoot = $PSScriptRoot Write-Host "扫描目录:$ScanRoot`n" -ForegroundColor Cyan if (-not (Get-Command ffmpeg -ErrorAction SilentlyContinue)) { Write-Host "【错误】未找到ffmpeg,请配置系统环境变量" -ForegroundColor Red Read-Host; exit 1 } $videoPattern = "\.(mp4|mkv|mov|avi|flv|webm|m4v|ts|mpeg|mpg)$" $videoFiles = Get-ChildItem -Path $ScanRoot -File -Recurse -ErrorAction SilentlyContinue | Where-Object { $_.Name -match $videoPattern } if (-not $videoFiles) { Write-Host "未检索到任何视频文件" -ForegroundColor Yellow Read-Host; exit } Write-Host "总计找到 $($videoFiles.Count) 个视频`n" foreach ($file in $videoFiles) { $videoFull = $file.FullName $baseName = $file.BaseName Write-Host "正在处理:$($file.Name)" # 获取视频总时长 $info = ffmpeg -i "$videoFull" 2>&1 $durMatch = $info | Select-String 'Duration: (\d{2}):(\d{2}):(\d{2})\.(\d{2})' if (-not $durMatch) { Write-Warning " 无法读取视频时长,跳过`n" continue } $h = [int]$durMatch.Matches.Groups[1].Value $m = [int]$durMatch.Matches.Groups[2].Value $s = [double]"$($durMatch.Matches.Groups[3].Value).$($durMatch.Matches.Groups[4].Value)" $totalSec = $h * 3600 + $m * 60 + $s $usableEndSec = $totalSec * (1 - $SkipEndRatio) $segmentLen = $usableEndSec / $TotalFrameNum $success = 0 for ($i = 0; $i -lt $TotalFrameNum; $i++) { $outFile = Join-Path $file.Directory "$($baseName)$FramePrefix$($i+1).jpg" if (Test-Path $outFile) { Write-Host " frame$($i+1) 已存在,跳过" $success++ continue } # 每一段区间中间位置截取 $captureSec = ($i * $segmentLen) + ($segmentLen / 2) ffmpeg ` -ss $captureSec ` -i "$videoFull" ` -vframes 1 ` -vf "scale=${Width}:-1" ` -y ` "$outFile" 2>&1 | Out-Null if (Test-Path $outFile) { $success++ } } Write-Host " 成功生成 $success / $TotalFrameNum 张截图`n" -ForegroundColor Green } Write-Host "==================================" Write-Host "全部视频处理完成!" -ForegroundColor Cyan Read-Host "回车关闭窗口" |
Markdown 精简版
视频均匀分段批量截图脚本
基础信息
| 项目 | 内容 |
|---|---|
| 脚本名称 | Video_Multi_Screenshot.ps1 |
| 脚本类型 | PowerShell 脚本(.ps1) |
| 运行平台 | Windows10 / Windows11 / Windows Server 2019+ |
| 依赖软件 | FFmpeg(需要加入系统环境变量) |
| 权限需求 | 普通用户权限;无需管理员(目录无只读锁定前提下) |
| 文件编码 | UTF-8 |
| 功能简介 | 递归扫描脚本所在目录及子目录视频;自动解析视频时长,均匀分割时间段截取10张独立截图;画面分散在前中后时段,避免截图画面高度重复;不区分视频长短,全部强制处理;已有截图自动跳过,防止重复生成。 |
| 支持格式 | mp4、mkv、mov、avi、flv、webm、m4v、ts、mpeg、mpg |
参数说明(脚本头部可自定义)
1 2 3 4 5 | $FramePrefix = "_frame_" # 截图文件名后缀 $Width = 480 # 截图宽度,高度自动等比例 $TotalFrameNum = 10 # 单个视频生成截图数量 $SkipEndRatio = 0.05 # 跳过视频末尾百分比,规避片尾黑屏/无关键帧 |
使用教程
- 将脚本放入视频根目录,程序自动以脚本所在位置作为扫描起点,递归遍历所有子文件夹。
- 配置 FFmpeg 环境变量,新开 PowerShell 输入
ffmpeg -version验证。 - 运行方式二选一:
- 右键脚本 → 使用 PowerShell 运行
- 当前文件夹打开 PowerShell,执行:
.\Video_Multi_Screenshot.ps1
- 输出命名规则:
原文件名_frame_1.jpg ~ 原文件名_frame_10.jpg
⚠️ 注意事项
- Windows 默认策略阻止 PS1 运行,首次运行报错,管理员PowerShell执行:
1 2 | Set-ExecutionPolicy RemoteSigned |
- 建议使用完整官方FFmpeg,精简绿色版容易缺失解码器。
- 手机录制
VID_*.mp4关键帧稀疏,视频末尾截图存在概率失败;可增大$SkipEndRatio=0.10。 - 文件路径避免 emoji、特殊全角符号,防止FFmpeg读取失败。
- 短片受视频编码限制,后半段截图极易失败,属于视频封装特性,非脚本BUG。
- 批量处理大量文件时,杀毒软件实时扫描可能造成轻微卡顿。
❌ 常见错误与解决办法
- 无法将“ffmpeg”识别为命令 FFmpeg未配置系统环境变量,配置后重启PowerShell终端。
- 禁止运行脚本 执行策略限制,使用上方
Set-ExecutionPolicy RemoteSigned命令。 - 无法读取视频时长,跳过 视频文件损坏、加密、被其他软件占用;使用播放器测试视频能否正常打开。
- 部分截图生成失败 MP4关键帧分布稀疏,末尾时段无法seek;调高
$SkipEndRatio。 - 拒绝访问 目标目录设置只读,或者视频被播放器、网盘进程锁定。
完整脚本代码
点击展开查看脚本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 | <# 视频均匀分段批量截图|强制处理所有视频,无短片过滤 均匀划分时间段截图,避免画面重复;兼容手机MP4、MKV #> # ========== 参数配置 ========== $FramePrefix = "_frame_" $Width = 480 $TotalFrameNum = 10 # 一共生成10张截图 $SkipEndRatio = 0.05 # 跳过最后5%,规避片尾黑屏 # ============================== $ScanRoot = $PSScriptRoot Write-Host "扫描目录:$ScanRoot`n" -ForegroundColor Cyan if (-not (Get-Command ffmpeg -ErrorAction SilentlyContinue)) { Write-Host "【错误】未找到ffmpeg,请配置系统环境变量" -ForegroundColor Red Read-Host; exit 1 } $videoPattern = "\.(mp4|mkv|mov|avi|flv|webm|m4v|ts|mpeg|mpg)$" $videoFiles = Get-ChildItem -Path $ScanRoot -File -Recurse -ErrorAction SilentlyContinue | Where-Object { $_.Name -match $videoPattern } if (-not $videoFiles) { Write-Host "未检索到任何视频文件" -ForegroundColor Yellow Read-Host; exit } Write-Host "总计找到 $($videoFiles.Count) 个视频`n" foreach ($file in $videoFiles) { $videoFull = $file.FullName $baseName = $file.BaseName Write-Host "正在处理:$($file.Name)" # 获取视频总时长 $info = ffmpeg -i "$videoFull" 2>&1 $durMatch = $info | Select-String 'Duration: (\d{2}):(\d{2}):(\d{2})\.(\d{2})' if (-not $durMatch) { Write-Warning " 无法读取视频时长,跳过`n" continue } $h = [int]$durMatch.Matches.Groups[1].Value $m = [int]$durMatch.Matches.Groups[2].Value $s = [double]"$($durMatch.Matches.Groups[3].Value).$($durMatch.Matches.Groups[4].Value)" $totalSec = $h * 3600 + $m * 60 + $s $usableEndSec = $totalSec * (1 - $SkipEndRatio) $segmentLen = $usableEndSec / $TotalFrameNum $success = 0 for ($i = 0; $i -lt $TotalFrameNum; $i++) { $outFile = Join-Path $file.Directory "$($baseName)$FramePrefix$($i+1).jpg" if (Test-Path $outFile) { Write-Host " frame$($i+1) 已存在,跳过" $success++ continue } # 每一段区间中间位置截取 $captureSec = ($i * $segmentLen) + ($segmentLen / 2) ffmpeg ` -ss $captureSec ` -i "$videoFull" ` -vframes 1 ` -vf "scale=${Width}:-1" ` -y ` "$outFile" 2>&1 | Out-Null if (Test-Path $outFile) { $success++ } } Write-Host " 成功生成 $success / $TotalFrameNum 张截图`n" -ForegroundColor Green } Write-Host "==================================" Write-Host "全部视频处理完成!" -ForegroundColor Cyan Read-Host "回车关闭窗口" |
改正版本:
报错根源
部分视频文件名包含 [ ]、*、? 这类字符,PowerShell 的 Test-Path 默认把参数当成通配符路径,中括号会被解析失败。
修复方案:给 Test-Path 增加 -LiteralPath
-LiteralPath:字面路径,不解析任何通配符符号,彻底解决该报错。
修复完成的最终可用脚本(直接替换)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 | <# 视频均匀分段批量截图|强制处理所有视频,无短片过滤 均匀划分时间段截图,避免画面重复;兼容手机MP4、MKV 修复:文件名含[]特殊字符导致Test-Path报错 #> # ========== 参数配置 ========== $FramePrefix = "_frame_" $Width = 480 $TotalFrameNum = 10 # 一共生成10张截图 $SkipEndRatio = 0.05 # 跳过最后5%,规避片尾黑屏 # ============================== $ScanRoot = $PSScriptRoot Write-Host "扫描目录:$ScanRoot`n" -ForegroundColor Cyan if (-not (Get-Command ffmpeg -ErrorAction SilentlyContinue)) { Write-Host "【错误】未找到ffmpeg,请配置系统环境变量" -ForegroundColor Red Read-Host; exit 1 } $videoPattern = "\.(mp4|mkv|mov|avi|flv|webm|m4v|ts|mpeg|mpg)$" $videoFiles = Get-ChildItem -Path $ScanRoot -File -Recurse -ErrorAction SilentlyContinue | Where-Object { $_.Name -match $videoPattern } if (-not $videoFiles) { Write-Host "未检索到任何视频文件" -ForegroundColor Yellow Read-Host; exit } Write-Host "总计找到 $($videoFiles.Count) 个视频`n" foreach ($file in $videoFiles) { $videoFull = $file.FullName $baseName = $file.BaseName Write-Host "正在处理:$($file.Name)" # 获取视频总时长 $info = ffmpeg -i "$videoFull" 2>&1 $durMatch = $info | Select-String 'Duration: (\d{2}):(\d{2}):(\d{2})\.(\d{2})' if (-not $durMatch) { Write-Warning " 无法读取视频时长,跳过`n" continue } $h = [int]$durMatch.Matches.Groups[1].Value $m = [int]$durMatch.Matches.Groups[2].Value $s = [double]"$($durMatch.Matches.Groups[3].Value).$($durMatch.Matches.Groups[3].Value)" $totalSec = $h * 3600 + $m * 60 + $s $usableEndSec = $totalSec * (1 - $SkipEndRatio) $segmentLen = $usableEndSec / $TotalFrameNum $success = 0 for ($i = 0; $i -lt $TotalFrameNum; $i++) { $outFile = Join-Path $file.Directory "$($baseName)$FramePrefix$($i+1).jpg" # 重点修复:-LiteralPath 关闭通配符解析,支持文件名带[] if (Test-Path -LiteralPath $outFile) { Write-Host " frame$($i+1) 已存在,跳过" $success++ continue } # 每一段区间中间位置截取 $captureSec = ($i * $segmentLen) + ($segmentLen / 2) ffmpeg ` -ss $captureSec ` -i "$videoFull" ` -vframes 1 ` -vf "scale=${Width}:-1" ` -y ` "`"$outFile`"" 2>&1 | Out-Null if (Test-Path -LiteralPath $outFile) { $success++ } } Write-Host " 成功生成 $success / $TotalFrameNum 张截图`n" -ForegroundColor Green } Write-Host "==================================" Write-Host "全部视频处理完成!" -ForegroundColor Cyan Read-Host "回车关闭窗口" |
新增一条:
错误:Test-Path 指定的通配符模式无效 原因:视频文件名包含
[ ]、*等特殊符号,Test-Path 默认识别为通配符。 解决:使用修复版脚本,内部增加-LiteralPath参数,按字面路径匹配。
修改关键点回顾
两处 Test-Path $outFile 统一改为 Test-Path -LiteralPath $outFile
现在哪怕视频文件名带有 [1080P]、[HEVC] 这类标记,也不会再抛出这条异常。