前言
相机、手机等设备拍摄的照片通常输出为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