跳过正文

将老照片如截图使用显卡加速转换到AVIF

大概2024年,我就转换了很多本地图片到AVIF格式,主要目的是节省空间。不要小看这点空间,因为很多以前的游戏截图一张图片就高达3-16MB,这是非常夸张的。当然这么大的占用还得感谢英伟达自带游戏截图。

更小的空间占用代表你可以保存更多的图片。在偶尔的回顾中,看到这些图片,还能让你想起往日的感动。

我选择AVIF格式作为目标格式,是同时考虑过格式先进性,开放性,兼容性,还有寿命。我相信AVIF在未来几十年应该都不会被淘汰的,是比较理想的目标格式。

不过比较奇葩的一点是我在2025年发现不少人竟然反过来转换AVIF到JPG格式,让我大受震撼。

转换目标格式,有个隐藏的要点,就是一定要保留原来的文件时间属性,这样才可以说是保留了原汁原味的图片。通常的转换软件都会忘记这点,导致2018年的图片被转换之后就成了2025年的文件了。

CPU预览转换方案
#

经常压缩图片的都知道,谷歌有个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,然后在需要处理图片的目录打开命令行,然后执行

./jtavif.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 -> Intel
foreach ($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" 

# 预检查 Squoosh
if (-not (Get-Command squoosh-cli -ErrorAction SilentlyContinue)) {
    Write-Host "错误: 未找到 squoosh-cli。请运行 npm install -g @frostoven/squoosh-cli" -ForegroundColor Red
    exit
}

# 定义一个函数方便调用 CPU 处理 (恢复了原生输出)
function Run-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 "全部处理完毕。"

代码由人类(我)提供思路,Ai辅助编写。