前言
相机、手机等设备拍摄的照片通常输出为JPEG格式,而直出的JPEG图片不仅压缩率低,而且还为了保存图片上的无用噪点而浪费了大量的存储空间。笔者经常需要将这些JPEG图片转码为WebP格式,以节省存储空间。
笔者以前通常使用bash、zsh或fish的for循环,调用各编解码器的命令行工具来实现批量转码,但是WebP等图片往往无法多线程编码,在转码时只有一个核实际参与计算,导致电脑性能不能得到充分利用。
parallel
的使用
笔者最近开始使用parallel这个工具进行并行的多张图片批量转码,它可以将多个命令行工具的输出进行并行处理,从而充分利用电脑的性能。
首先需要安装parallel
,在Arch Linux下可以使用pacman
安装:
sudo pacman -S parallel
parallel
的基本使用方法非常简单,既可以使用:::
或<
来传递并行处理的参数,也可以使用管道符|
来传递并行处理的参数,例如:
parallel echo ::: 1 2 3 4 5
parallel echo < <(seq 1 5)
seq 1 5 | parallel echo
这三条命令的输出均为:
1
2
3
4
5
parallel
还可以接受一些选项来调整并行处理的参数,例如:
-j
:指定并行处理的最大任务数,例如-j 4
表示最多同时处理4个任务;默认值为CPU核心数。-k
:保持输出的顺序,即使任务完成的顺序不同。--bar
:显示进度条。
将parallel
用于并行图片转码
在有多张图片的情况下,使用parallel
可以弥补图片编码器本身不支持多线程编码的问题,从而充分利用电脑的性能。
可以使用以下命令将当前目录下的所有JPEG图片转码为WebP格式,保留所有元数据信息,并显示进度条:
parallel --bar "cwebp -metadata all {} -o {.}.webp" ::: *.jpg
这里的{}
表示parallel
接受的参数,{.}
表示parallel
接受的参数的文件名部分,:::
表示parallel
接受的参数来自于命令行,*.jpg
表示当前目录下的所有JPEG图片。
对于较大的WebP图片,则需要对cwebp
命令的-segments
选项调整为1
,还可以将-partition_limit
选项调整为100
,从而提高编码速度:
parallel --bar "cwebp -metadata all -segments 1 -partition_limit 100 {} -o {.}.webp" ::: *.jpg
如果需要同时删除原图,可以使用以下命令:
parallel --bar "cwebp -metadata all -segments 1 -partition_limit 100 {} -o {.}.webp && rm {}" ::: *.jpg
类似地,如果需要调用ffmpeg
或者convert
其他命令行工具来批量转码图片,也可以使用parallel
来实现:
parallel --bar "ffmpeg -hwaccel auto -i {} -c:v libsvtav1 {.}.avif" ::: *.jpg
parallel --bar "convert {} {.}.avif" ::: *.jpg
对于音频转码,可以考虑类似的方法,但是对于视频转码则没有必要,因为视频编码器一般有良好的多线程支持。
parallel
的字符串替换
GNU parallel中的替换字符串的说明:
{}
:输入行。这个替换字符串将被从输入源读取的完整行替换。输入源通常是stdin
(标准输入),但也可以通过-a
,:::
, 或::::
给出。如果命令行不包含替换字符串,那么{}
将被添加到命令行的末尾。{.}
:无扩展名的输入行。这个替换字符串将被去掉扩展名的输入替换。例如,foo.jpg
变为foo
,subdir/foo.jpg
变为subdir/foo
。{/}
:输入行的基本名。这个替换字符串将被去掉目录部分的输入替换。{//}
:输入行的目录名。这个替换字符串将被输入行的目录部分替换。{/.}
:无扩展名的输入行的基本名。这个替换字符串将被去掉目录和扩展名部分的输入替换。{#}
:要运行的作业的序列号。这个替换字符串将被正在运行的作业的序列号替换。{%}
:作业插槽号。这个替换字符串将被作业的插槽号替换,插槽号在1和并行运行的作业数量之间。{n}
:来自输入源n的参数或第n个参数。这个位置替换字符串将被输入源n的输入或第n个参数替换。{n.}
:来自输入源n的参数或第n个参数,但是没有扩展名。{n/}
:来自输入源n的参数或第n个参数的基本名。{n//}
:来自输入源n的参数或第n个参数的目录名。{n/.}
:来自输入源n的参数或第n个参数的基本名,但是没有扩展名。{=perl expression=}
:用计算的perl表达式替换。$_
将包含与{}
相同的内容。计算perl表达式后,$_
将被用作值。{=n perl expression=}
:等同于{= perl expression =}
的位置替换字符串。
:::
与::::
的组合使用
多个:::
或::::
可以组合使用,其中,每个:::
或::::
所跟的参数组都将视为输入源,并生成所有输入源的组合,例如:
parallel echo ::: 1 2 ::: a b c
这条命令的输出为:
1 a
1 b
1 c
2 a
2 b
2 c
:::
与::::
的区别在于,:::
是在命令中直接读取输入源,而::::
是从文件中读取输入源,例如:
parallel echo :::: input.txt
如果这里的input.txt
文件内容为:
abcd
efgh
ijkl
那么这条命令的输出为:
abcd
efgh
ijkl