05 | 图像处理库:如何实现长图拼接?

我们在工作中,除了和文字、表格打交道之外,还会经常涉及到批量处理图片和视频的工作。比如:媒体从业者在发微博长图文时,需要把多个图片拼接成一幅长图;作为视频剪辑人员,需要从互联网下载多段视频,再进行合并。

这类工作可以用功能强大的商业软件实现,不过这些软件大都操作繁琐,而且还需要付费。为了降低学习成本和购买软件的成本,我们往往还会使用开源软件替代商业软件来实现图片和视频处理功能。但是开源软件通常都是以命令行方式运行的,所以我们不仅要记住命令,还得记住命令的常用参数。

不过,幸运的是,虽然直接使用开源软件不够友好,但如果通过 Python 来调用这些开源软件,那实现长图和视频拼接就轻而易举了,而且还能大批量地处理图片和视频。

Python 是如何调用外部命令的

为了让你了解 Python 是如何操作这些开源软件的,先来介绍一下 Python 调用外部程序的原理。

我们要想使用 Python 语言之外的功能,要依靠两大途径:导入函数库和调用外部命令。

在第一讲使用的 xlrd 库是通过 import xlrd 命令导入到 Python 语言中的,Python 语言默认是不支持 Excel 的。那么通过导入函数库,Python 就可以获得对 Excel 的操作能力。

还有一种情况是,需要操作 Python 语言之外的功能,但这个功能没有人将它开发成函数库,那如果我们想要使用这些功能,使用的途径就是调用外部命令了,而调用外部命令就需要 Python 内部函数库的 subprocess 模块来实现。

这个模块的实现机制是:它的 run() 函数的参数可以指定一个可以运行的程序的路径,而 Python 会根据这个路径来运行可执行文件,然后再根据运行结果,以及 Python 的逻辑判断去进行后续的自动化处理工作。

这个实现机制并不难,写一段简单的程序,帮你理解 Python 是怎样调用外部命令的。这里以 macOS 系统为例,我们通过 Python 获取当前目录下所有文件的功能。

from subprocess import run, Popen, PIPE

cmd1 = ["ls", "."]
returncode = run(cmd1)

print(returncode)
# CompletedProcess(args=['ls', '.'], returncode=0)
# returncode是“ls .”的退出状态码.
# 通常来说, 一个为 0 的退出码表示进程运行正常

# 使用Popen获取程序运行结果
with Popen(cmd1, shell=True, stdout=PIPE, stderr=PIPE, encoding="utf-8") as fs:
    
    # 如果程序在 timeout 秒后未执行完成,会抛出 TimeoutExpired 异常
    fs.wait(2)

    # 从标准输出中读取数据,知道文件结束
    files = fs.communicate()[0]
    
print(files)

这段代码中最核心的函数是 run() 函数和 Popen 类。subprocess 模块就是通过这两个函数实现的外部程序调用。我来为你重点剖析一下它们的功能、参数,以及何时选择 run() 函数、何时选择 Popen 类。

为了实现 Python 调用可执行文件,首先在代码的第一行,是这样编写的:

from subprocess import run, Popen, PIPE

这样一行代码,它和第一讲使用的 import 方式导入函数库的区别是,这种形式可以让你直接使用模块中的类和方法。

如果你使用 “import subprocess”方式导入 subprocess 库的话,在调用 run() 函数的时候,就需要用 “库. 函数”的形式在 Python 中使用库当中的函数,即“subprocess.run()”。在你多次调用 run() 函数时,代码会较长,那么使用“from import”方式导入,就可以在当前代码文件中直接使用 run() 函数,为代码的阅读带来更好的体验。

接下来,定义了一个变量 cmd1。这个变量的值是 macOS 命令行能够运行的“ls .”命令,这个命令的执行结果是显示当前目录下所有文件和文件夹的名称。

run() 函数的主要功能就是执行一个新的程序,它的用法非常简单,把第一个参数指定为要执行程序的路径就可以了。如果要执行的程序带有参数,那就可以使用列表数据类型存放可执行程序名称和参数,像是我在程序中定义的 cmd1 变量一样。如果你需要运行其他命令,把代码中的 ls 替换为你想要运行的其他程序就行了。

为了让 Python 自动化处理程序更强大,除了运行程序外,你还可以得到可执行程序的运行结果。在这种情况下,我们就需要使用 Popen 类替代 run() 函数实现外部程序的调用。

可以看到,我在代码的第 12 行先通过 Popen 类执行了“ls .”命令,接着通过参数 stdout=PIPE 将命令的执行结果放入到 PIPE 对象中, 最后再通过 communicate() 函数将 PIPE 中的内容读取出来,存放到 files 变量中,这样就实现了读取命令执行结果的功能。

这个功能是无法在 run() 函数实现的,因此在你需要通过 Python 读取程序执行结果的时候,就可以选择 Popen 类。不过如果只需要运行可执行程序,那使用 run() 函数就能满足你的要求了。如果你想更深入地了解它们,建议你阅读subprocess 库的官方文档

以上就是用 subprocess 库实现 Python 调用可执行程序的方法。Python 之所以被我们称作最佳的“胶水语言”,就是因为它能轻易“粘合”可执行程序。利用 Python 灵活的逻辑判断、循环语法可以实现程序的批量执行和流程管理。

接下来,我们就使用 subpr接下来,我们就使用 subprocess 来实现长图拼接和视频拼接的功能。

长图拼接

当我进行微博文案推广的时候,需要将多个图片拼接成一个长图。拼接图片的功能 Python 本身是不具备的,因此就需要引入外部命令来实现图片拼接功能。

在 macOS 平台上找到了一个非常强大的图像处理软件叫做 ImageMagick,它能对图片进行编辑、合并、切割、旋转等 90 多种操作。 ImageMagick 软件实现图片拼接的命令格式是这样的:

composite 图片1.jpg 图片2.jpg ... 图片n.jpg 最终合成结果.jpg

在这段命令格式中,composite 命令的参数包含了多个图片文件,每个图片需要对照着文件将图片的路径和文件名写在参数中。如果手工输入图片名称,不仅效率低,而且容易遗漏。另外,如果需要大量重复使用 composite,还需要精细调整合并结果,给 composite 程序增加很多参数。

因此,就可以通过 Python 调用可执行程序的 subprocess 库,对 composite 拼长图的工作进行脚本化编程。它的核心实现代码如下:

p = Path(jpg_path)

# 增加命令
cmd = ["composite",]

# 增加参数
for x in p.iterdir() if  PurePath(x).match('*.jpg'):
    cmd.append(x)

# 增加结果
cmd.append(result_path)

run(cmd)

由于 composite 可以把长图合成的结果直接输出为文件,因此采用 run() 函数即可实现程序执行的功能。另外,当你需要调整 composite 参数时,可以直接修改 cmd 变量的值,并不需要改动程序其他部分。当你要对新的一组图片进行合成的时候,重新设置 jpg_path 变量就行了。

总结来说,使用 Python 调用 composite 合并的好处就是:你不用记住程序使用的繁杂的命令行参数,也不用记住运行逻辑,因为 Python 程序已经事先把逻辑编写好了。

视频的拆分与合并

在了解了如何使用 subprocess 调用 composite 实现长图拼接之后,我再给你讲一下如何使用 subprocess 库调用可执行程序,来进行视频的拆分与合并。

我们先来学习下视频拆分的原理。

你在电脑本地经常见到的视频格式是 MP4,但如果要把视频放在互联网上,为了减少首次播放的加载时间,你就必须把一个 MP4 切分成多个文件,而且切分之后还需要把格式转换为.TS 格式的视频文件。

为什么不直接使用 MP4 格式,而是要把 MP4 格式改成.TS 格式呢?这是因为.TS 格式可以保证多个文件之间的视频无缝播放,而且还会保证视频不会在播放下一个文件的时候,出现破音或画面中断等影响用户体验的情况。

当我们将一个视频切分成多个文件的时候,就要考虑文件的播放顺序问题了。为了记录顺序,我们需要在切分之后引入一个索引文件,这个索引文件不用手动编写,我们直接使 FFmpeg 命令就行了,它可以实现视频格式的转换、合并和拆分。FFmpeg 命令会在切分之后,自动产生一个以.M3U8 结尾的索引文件。

我来解释一下这个索引文件。M3U8 文件是指 UDF-8 编码格式下的 M3U 视频索引,播放器通过这个索引文件就可以找到视频下所有的分段,并依次播放视频。

看到这儿你应该就能明白了,想要使用 Python 进行视频拆分,我们首先需要 FFmpeg 命令,然后通过 Python 设置 FFmpeg 的参数,最后再指定 MP4 文件和.TS 文件的路径,这样就能实现拆分视频的功能了。因此我使用这样的代码来实现视频拆分:

from subprocess import run
input_video = "/Users/edz/Desktop/05/xxx.mp4"
segment_time = 10
m3u8_list = "/Users/edz/Desktop/05/xxx.m3u8"
output_video = "/Users/edz/Desktop/05/video-%04d.ts"

cmd1 = ["ffmpeg", "-i", input_video, "-f", "segment", "-segment_time", str(segment_time), "-segment_format",
    "mpegts", "-segment_list", m3u8_list, "-c", "copy", "-bsf:v", "h264_mp4toannexb", "-map", "0", output_video]

run(cmd1)

在代码中,我通过 FFmpeg 把 MP4 切分成了多段 TS 文件。你要想实现相同功能,首先需要在电脑中安装 FFmpeg 命令,它的下载地址为:https://ffmpeg.org/download.html

为了实现 MP4 文件格式的分割,需要使用 ffmpeg 非常多的参数。不过使用 Python 进行调用的好处,就是你不用记住复杂的参数。我们把输入文件路径、切分大小、输出的 M3U8 和 TS 文件指定为四个变量,这样只修改这四个变量,就可以实现拆分功能了。

如果你需要离线观看视频,就要将网络上的视频下载到本地,这时你会发现从互联网下载的格式是 M3U8 和 TS 文件。那又怎么把它们合并成 MP4 文件呢?

你同样可以使用 FFmpeg 命令,但是 FFmpeg 的参数不同。将 FFmpeg 的命令写在这里:

ffmpeg -allowed_extensions ALL -protocol_whitelist "file,http,crypto,tcp,https" -i index.m3u8 -c copy out.mp4

如果你不想背诵这么长的参数,完全可以仿照 Python 整合拆分视频的代码来实现合并功能。先 FFmpeg 命令和参数放入列表,再把 M3U8 文件和 MP4 文件放入变量,便于你合并新的视频的时候进行重新赋值。

所以你看,相比直接使用 FFmpeg,subprocess 调用 FFmpeg 的优势就在于两点,一是不用记住复杂参数,二是对批量转换视频非常有利。举两个例子。

如果你是视频剪辑的专业工作者,肯定要大量使用 FFmpeg 更复杂的功能,这些功能对应的参数一般都比较多,而且参数很多都使用了简写和大小写, 很难记忆。但要是使用 Python 调用的话,你可以直接更改要操作的文件路径,就不必记录大量的参数。

另外需要进行视频的批量转换时,可以通过第一讲的循环操作对视频任务批量处理,这样就避免了手动逐个修改书写文件的操作,从而提高视频转换的效率。

小结

最后,总结一下这节课的主要内容。

通过对 subprocess 库的讲解,你知道了怎样通过它实现 Python 加载外部可执行程序,并且能够对程序执行的结果进行处理。

也讲解了长图拼接和视频拆分合并的两个案例,更好地理解 Python 为什么会被称作“胶水”语言。

还想强调一下,通过 Python 调用可执行程序的用法非常常见,特别是在多媒体处理、自然科学、AI 等领域里。在这些专业领域,为了加快计算速度,通常会使用 C++ 语言实现专业程序。

这些专业程序参数多、功能单一,且使用命令行执行,当你需要多次执行这些程序,又不想背诵它们的参数的时候,就可以利用 Python 的判断循环功能,结合 C++ 语言实现的专业程序,来实现批量执行和减少参数手动输入的工作,提高你的工作效率。

最后,也把这节课的代码附上,你可以查看。本讲代码

思考题

在最后也请你思考一下,你在工作当中是否会使用命令行工具呢?它们能否用 Python 进行包装,从而避免手写复杂参数呢?



本文介绍了如何使用Python调用外部命令来实现长图和视频拼接的功能。作者首先解释了Python调用外部程序的原理,介绍了subprocess模块的run()函数和Popen类的使用方法。然后,作者详细讲解了如何使用subprocess库实现长图拼接的功能,以macOS平台上的ImageMagick软件为例,演示了如何通过Python调用composite命令来实现图片拼接。作者强调了使用Python调用外部命令的优势,包括简化命令行参数、提高效率、方便调整参数等。整体而言,本文通过实际示例生动地展示了Python调用外部命令的实现方法,为读者提供了一种简单、高效的图片和视频处理方式。

在文章的后半部分,作者还介绍了如何使用subprocess库调用可执行程序,来进行视频的拆分与合并。通过讲解视频拆分的原理和使用FFmpeg命令的方法,作者展示了如何通过Python实现视频拆分的功能,并强调了使用Python调用FFmpeg命令的优势,包括简化参数、批量处理视频等。最后,作者总结了subprocess库的用法,强调了Python调用可执行程序在多媒体处理、自然科学、AI等领域的重要性,并提供了思考题引导读者思考在工作中是否可以使用Python包装命令行工具,提高工作效率。

总的来说,本文通过生动的示例和清晰的讲解,帮助读者了解了Python调用外部命令的实现方法,以及在多媒体处理领域中的应用,为读者提供了有益的技术指导。 

  • 15
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值