大概2024年,我就转换了很多本地图片到AVIF格式,主要目的是节省空间。不要小看这点空间,因为很多以前的游戏截图一张图片就高达3-16MB,这是非常夸张的。当然这么大的占用还得感谢英伟达自带游戏截图。
更小的空间占用代表你可以保存更多的图片。在偶尔的回顾中,看到这些图片,还能让你想起往日的感动。
我选择AVIF格式作为目标格式,是同时考虑过格式先进性,开放性,兼容性,还有寿命。我相信AVIF在未来几十年应该都不会被淘汰的,是比较理想的目标格式。
不过比较奇葩的一点是我在2025年发现不少人竟然反过来转换AVIF到JPG格式,让我大受震撼。
转换目标格式,有个隐藏的要点,就是一定要保留原来的文件时间属性,这样才可以说是保留了原汁原味的图片。
通常的转换软件都会忘记这点,导致2018年的图片被转换之后就成了2025年的文件了。
CPU预览转换方案
经常压缩图片的都知道,Google有个Squoosh工具可以可视化且快速极致压缩图片,通常能减少90%以上的空间,同时可以在线调节参数。在以前,Squoosh是有官方命令行工具的,不过后来不出意外的被废弃了。
Squoosh目前有第三方维护的CLI版本,我这里就简单写下。
npm install -g @frostoven/squoosh-cli
foreach ($file in (Get-ChildItem -Path *.jpg,*.jpeg,*.png)) { $outputFile = Join-Path $file.DirectoryName ($file.BaseName + ".avif"); squoosh-cli --avif '{"cqLevel":20,"cqAlphaLevel":20,"subsample":3,"tileColsLog2":0,"tileRowsLog2":0,"speed":3,"chromaDeltaQ":false,"sharpness":0,"denoiseLevel":0,"tune":0}' $file.FullName; Start-Sleep -Seconds 1; (Get-Item $outputFile).CreationTime = $file.CreationTime; (Get-Item $outputFile).LastWriteTime = $file.LastWriteTime }
请在你想要转换图片的文件夹位置打开powershell运行。该脚本功能很简单,建议只用来测试,或者只连续运行。
这个powershell脚本会快速检查当前所在目录下的所有jpg,jpeg和png图片,然后转换它们到AVIF,而且保留时间戳。
你试试就知道了,这个东西的速度非常慢。我之前用10400 CPU(6核心12线程12M L3缓存),每张4K图片大概需要2-3分钟,慢到不可思议。
后来我当然不再忍受这个速度了,决定调用显卡的硬件加速功能。不过,这个方案有个好处就是对图片支持很专业,可以作为硬件加速有兼容问题时候的回退。下面也会用到它。
显卡硬件加速转换AVIF
你可能会说了,我怎么不知道啥显卡支持AVIF? 其实只要你的显卡支持AV1编码器,就能用来加速AVIF的转换。
我用的是Intel Arc750显卡,自带AV1编码器,转换速度是非常理想的。而调用硬件加速也不需要特别的收费软件,只需要FFmpeg就行了。FFmpeg说自己老二谁敢说第一?
在我的这张显卡上,通常转换一张4K图片到AVIF,需要1秒钟左右的时间,快了起码100倍。所以凡是用过硬件加速的都不太可能回去用CPU编码。
如果你有需要,可以直接用我自己用的脚本。
我的自用脚本使用概况
最新代码会更新在这里: https://github.com/riverai/Auto-JPGPNGtoAVIF-Compressor
先安装好基本软件包
首先应该安装Node.js环境,然后使用
npm install -g @frostoven/squoosh-cli
以安装CPU转码软件包。
然后安装FFmpeg并且正确配置环境变量,确保在命令行可以直接调用FFmpeg。目前我用的是FFmpeg 8.
使用方法
下载.ps1直接在需要处理图片的目录打开命令行,然后执行
注意: 如果提示“禁止运行脚本”,请先执行 Set-ExecutionPolicy RemoteSigned 解除限制。
或者可以从下方直接复制所有代码直接粘贴到powershell命令行窗口。
其他说明
这个脚本尽可能兼容三家显卡,尽可能采用YUV444和10bit颜色编码。如果显卡不兼容该格式,FFmpeg会自动回退到更差的版本,一般不用担心报错。
如果图片包含透明通道,因为严格的兼容性设置,脚本会直接调用Squoosh来处理,避免兼容性问题出现差错。
包含识别文件名,自动跳过已经转换过的图片,可以分多次完成任务。
同时,自动完成复制原始文件的时间属性到新文件,准确保留你的记忆。
如果执行有报错自己问问AI吧,现在时代变了,没必要自己死磕。
一般以前占用3-16M的PNG图片转换之后只需要占用50-200KB的空间,算是比较理想的了。
最后我使用Jpgview专业看图软件进行对比,我从肉眼上无法看到质量差距。
如果不方便访问GitHub,可以将代码直接复制(保持格式正确),直接粘贴到powershell窗口运行,或者保存为.ps1脚本然后执行。
# -----------------------------------------------------------------# 1. 自动硬件检测与配置区域# -----------------------------------------------------------------Write-Host "正在检测显卡硬件..." -ForegroundColor Cyan$videoControllers = Get-CimInstance Win32_VideoController$encoderType = $null# 优先级检测逻辑:NVIDIA -> AMD -> Intelforeach ($gpu in $videoControllers) { if ($gpu.Name -match "NVIDIA") { $encoderType = "nvenc"; Write-Host " -> 检测到 NVIDIA 显卡: $($gpu.Name)" -ForegroundColor Green break } elseif ($gpu.Name -match "AMD" -or $gpu.Name -match "Radeon") { $encoderType = "amf"; Write-Host " -> 检测到 AMD 显卡: $($gpu.Name)" -ForegroundColor Green break } elseif ($gpu.Name -match "Intel" -or $gpu.Name -match "Arc" -or $gpu.Name -match "Iris") { $encoderType = "qsv"; Write-Host " -> 检测到 Intel 显卡: $($gpu.Name)" -ForegroundColor Green }}if (-not $encoderType) { Write-Host "警告: 未检测到支持的显卡厂商,将全部使用 CPU (Squoosh) 处理。" -ForegroundColor Yellow}# --- 编码质量设置 ---$globalQuality = 25 # 通用质量参数 (0-51, 越低越好)# --- Squoosh (CPU) 设置 ---# "subsample":3 代表 YUV 4:4:4$squooshArgs = '{"cqLevel":20,"cqAlphaLevel":20,"subsample":3,"tileColsLog2":0,"tileRowsLog2":0,"speed":3,"chromaDeltaQ":false,"sharpness":0,"denoiseLevel":0,"tune":0}'# -----------------------------------------------------------------# 2. 目标格式 (给显卡用的)$targetPixFmt = "yuv444p10le" # 预检查 Squooshif (-not (Get-Command squoosh-cli -ErrorAction SilentlyContinue)) { Write-Host "错误: 未找到 squoosh-cli。请运行 npm install -g @frostoven/squoosh-cli" -ForegroundColor Red exit}# 定义一个函数方便调用 CPU 处理 (恢复了原生输出)functionRun-Squoosh { param($reason, $fileObj) Write-Host " -> $reason 切换到 CPU (Squoosh) 引擎..." -ForegroundColor Yellow # 这里去掉了 | Out-Null,保留 Squoosh 的原生进度条输出 $cmd = "squoosh-cli --avif '$squooshArgs' -d `"$($fileObj.DirectoryName)`" `"$($fileObj.FullName)`"" Invoke-Expression $cmd Start-Sleep -Seconds 1}# 3. 遍历所有图片foreach ($file in (Get-ChildItem -Path *.jpg,*.jpeg,*.png)) { $outputFile = Join-Path $file.DirectoryName ($file.BaseName + ".avif") if (Test-Path $outputFile) { Write-Host "跳过: $outputFile (已存在)" -ForegroundColor Gray continue } Write-Host "开始处理: $($file.Name)" -ForegroundColor Cyan # 4. 探测格式 try { $pixFmt = (ffprobe -v error -select_streams v:0 -show_entries stream=pix_fmt -of csv=p=0 $file.FullName).Trim() Write-Host " -> 探测到格式: $pixFmt" } catch { Write-Host " -> 错误: ffprobe 失败,跳过文件" -ForegroundColor Red continue } # 5. 智能分流逻辑 # 情况 A: 包含透明通道 -> 直接 CPU if ($pixFmt -match "a") { Run-Squoosh -reason "检测到透明通道" -fileObj $file # 检查 Squoosh 结果 if (Test-Path $outputFile) { $outputItem = Get-Item $outputFile $outputItem.CreationTime = $file.CreationTime $outputItem.LastWriteTime = $file.LastWriteTime Write-Host "成功: $($outputFile) (Squoosh/已同步时间戳)" -ForegroundColor Green } else { Write-Host "失败: Squoosh 未生成文件" -ForegroundColor Red } } # 情况 B: 没显卡 -> 直接 CPU elseif (-not $encoderType) { Run-Squoosh -reason "未检测到硬件加速" -fileObj $file if (Test-Path $outputFile) { $outputItem = Get-Item $outputFile $outputItem.CreationTime = $file.CreationTime $outputItem.LastWriteTime = $file.LastWriteTime Write-Host "成功: $($outputFile) (Squoosh/已同步时间戳)" -ForegroundColor Green } else { Write-Host "失败: Squoosh 未生成文件" -ForegroundColor Red } } else { # 情况 C: 尝试 GPU 硬件加速 Write-Host " -> 纯色图。尝试使用 $encoderType 硬件加速..." $encoderArgs = "" switch ($encoderType) { "qsv" { $encoderArgs = "-c:v av1_qsv -global_quality $globalQuality -preset 4" } "nvenc" { $encoderArgs = "-c:v av1_nvenc -cq $globalQuality" } "amf" { $encoderArgs = "-c:v av1_amf -rc cqp -qp $globalQuality" } } # 尝试执行 FFmpeg (恢复了原生输出,去掉了 -hide_banner -loglevel error) $cmdString = "ffmpeg -strict -1 -i `"$($file.FullName)`" -pix_fmt $targetPixFmt -color_range 2 $encoderArgs -frames:v 1 -y `"$outputFile`"" Invoke-Expression $cmdString # 【智能回退机制】 if ($LASTEXITCODE -ne 0) { Write-Host " -> [警告] 硬件编码失败 (退出代码 $LASTEXITCODE),尝试自动回退到 CPU..." -ForegroundColor Red # 删除可能生成的 0kb 坏文件 if (Test-Path $outputFile) { Remove-Item $outputFile } # 调用 CPU Run-Squoosh -reason "自动回退机制" -fileObj $file # 检查 CPU 结果 if (Test-Path $outputFile) { $outputItem = Get-Item $outputFile $outputItem.CreationTime = $file.CreationTime $outputItem.LastWriteTime = $file.LastWriteTime Write-Host "成功: $($outputFile) (回退Squoosh/已同步时间戳)" -ForegroundColor Green } else { Write-Host "失败: CPU 回退处理也失败了" -ForegroundColor Red } } else { # GPU 成功 $outputItem = Get-Item $outputFile $outputItem.CreationTime = $file.CreationTime $outputItem.LastWriteTime = $file.LastWriteTime Write-Host "成功: $($outputFile) (FFmpeg/已同步时间戳)" -ForegroundColor Green } }}Write-Host "全部处理完毕。"