:author: 朱邓达
:date: 2025-06-18

.. index:: ! movie
.. program:: movie

movie
=====

:官方文档: :doc:`gmt:movie`
:简介: 制作动画

**movie** 模块能够使用单个绘图脚本生成 GMT 动画，该脚本会重复应用于所有帧，
同时通过特定帧变量进行一些变化。该模块简化（并隐藏）了通常设置完整动画任务所需的大部分步骤。
相反，用户可以专注于构建主帧绘图，而让帧的并行执行以及图片到动画的组合在后台进行。
单个帧会从 *PostScript* 绘图转换为无损、不透明的 *PNG* 图片，
并可选择组合成动画（这最后一步需要外部工具，这些工具必须存在于用户的路径中；
请参阅下面的 `技术细节`_ ）。用户可以根据需要添加片头、淡入淡出效果、标签和进度指示器。

.. only:: html

    :download:`Source Code <https://dengda98.github.io/zh/posts/tech/gmt_movie/movie_ex01.sh>`

    .. figure:: https://dengda98.github.io/zh/posts/tech/gmt_movie/anim01.gif
        :width: 500 px
        :align: center

语法
----

**gmt movie**
*mainscript*
:option:`-C`\ *canvassize*\ [**+c|i**]
:option:`-N`\ *prefix*
:option:`-T`\ *nframes*\|\ *min*/*max*/*inc*\ [**+n**]\|\ *timefile*\ [**+p**\ *width*]\ [**+s**\ *first*]\ [**+w**\ [*str*]\|\ **W**]
[ :option:`-A`\ *audiofile*\ [**+e**] ]
[ :option:`-D`\ *displayrate* ]
[ :option:`-E`\ *titlepage*\ [**+d**\ [*duration*\ [**s**]]][**+f**\ [**i**\|\ **o**]\ [*fade*\ [**s**]]]\ [**+g**\ *fill*] ]
[ :option:`-F`\ *gif*\|\ *mp4*\|\ *webm*\|\ *png*\ [**+i**\ *options*][**+l**\ [*n*]][**+o**\ *options*][**+s**\ *stride*][**+t**][**+v**] ]
[ :option:`-G`\ [*fill*]\ [**+p**\ *pen*] ]
[ :option:`-H`\ *scale*]
[ :option:`-I`\ *includefile* ]
[ :option:`-K`\ [**+f**\ [**i**\|\ **o**]\ [*fade*\ [**s**]]]\ [**+g**\ *fill*]\ [**+p**\ [**i**\|\ **o**]] ]
[ :option:`-L`\ *labelinfo* ]
[ :option:`-M`\ [*frame*],[*format*][**+r**\ *dpu*][**+v**] ]
[ :option:`-P`\ *progress* ]
[ :option:`-Q`\ [**s**] ]
[ :option:`-Sb`\ *background* ]
[ :option:`-Sf`\ *foreground* ]
[ :option:`-V`\ [*level*] ]
[ :option:`-W`\ [*dir*] ]
[ :option:`-Z`\ [**s**] ]
[ :option:`-f`\ *flags* ]
[ :option:`-x`\ [[-]n] ]
[ :doc:`--PAR=value </conf/overview>` ]

必须选项
--------

*mainscript*
    一个独立的 GMT 现代模式主脚本，用于生成帧。
    该脚本可以访问帧变量，例如帧号等，并且可以使用
    Bourne shell（.sh）、Bourne again shell（.bash）、
    csh（.csh）或 DOS 批处理语言（.bat）编写。
    脚本语言根据文件扩展名推断，然后 GMT 使用相同脚本语言构建隐藏的 movie 脚本。
    可访问的参数将在下文讨论。
    **注：**
    如果最终的 **gmt end** 语句以 **show** 结尾，则会展示动画主帧（仅当使用了 :option:`-M` 时）。

.. option:: -C

**-C**\ *canvassize*\ [**+c|i**]
    指定组合帧时的画布大小。支持两种指定方式：

    + 使用专有名称指定预设格式（见下方表格 :ref:`Presets <tbl-presets>` 第一列的名称）。

    + 用户可指定自定义布局 *width*\ **x**\ *height*\ **x**\ *dpu* ，
      其中画布宽度 *width* 和画布高度 *height* 后可接单位 **c|i** ，
      像素密度 *dpu* （dots-per-unit）为每单位大小内的像素点数
      （在预设格式中像素密度会自动计算）。
      *width* 和 *height* 也可以指定为像素级宽度和长度，此时需在 *dpu* 后面加上 **+c** 或
      **+i** 以表明 *dpu* 的单位大小为每厘米 (cm) 或每英寸 (inches) 。

    **注：** 用户设定的 :term:`PROJ_LENGTH_UNIT` 将决定 **movie** 模块使用 SI 或 US 单位。

    .. _tbl-presets:

    *16:9 (24x13.5 cm or 9.6x5.4 inch)*

    =================================== ================== ========= ==========
    Preset format (alias)               Pixel dimensions   DPC       DPI
    =================================== ================== ========= ==========
    4320p (8k and uhd-2)                7680 x 4320        320       800
    2160p (4k and uhd)                  3840 x 2160        160       400
    1080p (fhd and hd)                  1920 x 1080        80        200
    720p                                1280 x 720         53.3333   133.3333
    540p                                960 x 540          40        100
    480p                                854 x 480          35.5833   88.9583
    360p                                640 x 360          26.6667   66.6667
    240p                                426 x 240          17.75     44.375
    =================================== ================== ========= ==========

    *4:3 (24x18 cm or 9.6x7.2 inch)*

    =================================== ================== ========= ==========
    Preset format (alias)               Pixel dimensions   DPC       DPI
    =================================== ================== ========= ==========
    uxga                                1600 x 1200        66.6667   166.6667
    sxga+                               1400 x 1050        58.3333   145.8333
    xga                                 1024 x 768         42.6667   106.6667
    svga                                800 x 600          33.3333   83.3333
    dvd                                 640 x 480          26.6667   66.6667
    =================================== ================== ========= ==========

.. option:: -N

**-N**\ *prefix*
    指定最终动画文件的名称以及包含帧图片的子目录（例外情况见 :option:`-W` ）。
    **注：** 如果该子目录已存在，则 GMT 将立即退出。
    因此，用户需要先删除以该名称命名的任何旧目录。这样做是为了防止意外丢失重要数据。
    用户可以通过使用 :option:`-Z` 在成功运行后删除该目录来避免此问题。

.. option:: -T

**-T**\ *nframes*\|\ *min*/*max*/*inc*\ [**+n**]\|\ *timefile*\ [**+p**\ *width*]\ [**+s**\ *first*]\ [**+w**\ [*str*]\|\ **W**]
    创建一个包含从 *min* 到 *max* 每隔 *inc* 变化的一列数据集以指定要生成多少帧，
    或提供一个数据集文件 *timefile* ，一个记录（即一行）对应一帧。
    列中的值将作为命名变量 **MOVIE_COL0** 、 **MOVIE_COL1** 等的形式提供给主脚本
    *mainscript* ，而每行任何尾部文本都可以通过变量 **MOVIE_TEXT** 进行访问。
    文件行数即为帧数。注意 *timefile* 文件可以在 *background* 脚本中创建（见 :option:`-Sb` ），
    因此在 *background* 脚本执行前后 GMT 都会查找 *timefile* 是否存在。
    如果仅指定帧数 *nframes* ，则只有变量 **MOVIE_FRAME** 可用，因为没有数据文件。
    对于数组生成的一些细节参数，详见 :doc:`/array` 。还支持以下子选项：

    + **+n** - *inc* 变为 *min* 到 *max* 之间的帧数。
    + **+p** - 设置每帧图片命名的帧号格式的标签宽度 *width* ，
      例如 *name_000010.png* 的帧号标签宽度为 6 。默认自动设置 *width* ，
      但如果用户将大型任务分布在多台计算机上的话（通过 **+s** ），
      用户必须确保所有帧名称的标签宽度一致。
    + **+s** - 输出帧起始编号从 0 改为 *first* 。
      注意这仍然包括所有帧，该子选项仅影响输出编号。
    + **+w** - 将尾部的文本字符串拆分成单个单词，可通过变量 **MOVIE_WORD0**, **MOVIE_WORD1**
      访问。默认将空格符 (spaces) 或制表符 (tabs) 作为分隔符，可自定义 *str* 作为有效分隔符。
      使用 **+W** 则将 TAB 作为唯一分隔符。

可选选项
--------

.. option:: -A

**-A**\ *audiofile*\ [**+e**]
    添加音轨（例如旁白解说），大概与动画长度相同，但可以更短或更长。
    GMT 将音轨与第一帧对齐。使用 **+e** 可将拉伸音轨以精确适配动画长度
    （前提是缩放比例不小于0.5或大于2.0）。

.. option:: -D

**-D**\ *displayrate*
    设置最终动画帧率（每秒） [默认 24]。

.. option:: -E

**-E**\ *titlepage*\ [**+d**\ [*duration*\ [**s**]]][**+f**\ [**i**\|\ **o**]\ [*fade*\ [**s**]]]\ [**+g**\ *fill*]
    指定脚本 *titlepage* 以创建静态页面作为动画片头 [默认无片头]。
    或者，*titlepage* 可以是尺寸与 :option:`-C` 中设置的画布大小完全匹配的 *PostScript*
    或 *EPS* 图（文件扩展名为.ps）。支持以下子选项控制片头：

    + **+d** - 设置片头持续长度，后跟 *duration* 表示持续帧数，有单位 **s** 表示持续秒数 [默认 4 秒]。
    + **+f** - 为片头添加以帧号或秒数（带单位 **s** ）为单位的淡入淡出长度 *fade* [默认 1s] 。
      使用 **+fi** 或 **fo** 可分别指定单边的淡入或淡出长度，
      或者都使用以指定长度不等的淡入淡出 [默认淡入淡出长度一致]。
      淡入淡出效果会影响片头持续时间 *duration* 。
    + **+g**\ *fill* - 设置淡入前和淡出后的颜色 [默认 black]。

    关于淡入淡出效果，详见 `片头和淡入淡出`_ ， :option:`-K` 也有相关用法。

.. option:: -F

**-F**\ *gif*\|\ *mp4*\|\ *webm*\|\ *png*\ [**+i**\ *options*][**+l**\ [*n*]][**+o**\ *options*][**+s**\ *stride*][**+t**][**+v**]
    选择动画输出类型，可多次使用 :option:`-F` 以生成多种类型。可选类型包括 *gif* , *mp4* , *webm* ,
    或只是 *png* 图片。其中 *mp4* 和 *webm* 为电影格式，需使用 FFmpeg
    工具将多个图片组合成电影。如果只选择 *png* 则不会组合。
    不使用 :option:`-F` 则不会输出任何与动画相关的文件（但要求使用 :option:`-M` ）。支持以下子选项：

    + **+i** - 为输入文件添加额外的 FFmpeg 编码设置（多个词用引号括住）。
    + **+l** - 循环播放 *gif* 动画，或者可指定循环次数 *n* [默认一直循环] 。
    + **+o** - 为输出文件添加额外的 FFmpeg 编码设置（ *mp4* 或 *webm* ，多个词用引号括住）。
    + **+s** - 当输出类型为 *mp4* 或 *webm* 时，指定步长 *stride* 可创建帧率低的 gif 图，
      其中 *stride* 可取 2, 5, 10, 20, 50, 100, 200, 和 500 。
    + **+t** - 选择生成透明的 *PNG* 图片 [默认不透明]。
      详见 `透明图片`_ 。
    + **+v** - 使用默认预览工具打开 *mp4* 或 *webm* 。

.. option:: -G

**-G**\ [*fill*]\ [**+p**\ *pen*]
    在绘制开始前，设置画布填充色 [默认无填充]，使用 **+p** 指定画布轮廓画笔属性 [默认无轮廓]。

.. option:: -H

**-H**\ *scale*
    由于在将 *PostScript* 图形栅格化为 *PNG* 图像时所使用的每单位像素点是有限的，
    因此将特征量化为离散像素会导致舍入误差。
    部分这种误差可以通过抗锯齿设置来减轻。然而，不同帧之间的变化不受单个帧的栅格化操作控制，
    特别是移动的文本在最终动画中可能会显得抖动。
    用户可以通过选择一个整数缩放比例 *scale* 来减轻这种影响，
    该比例实际上会暂时将每单位有效点数增大 *scale* 倍，然后对帧进行栅格化处理，
    最后以相同的比例对图像进行降采样。 *scale* 越大，过渡就越平滑。
    由于处理时间会随着缩放比例的增加而增加，GMT 建议用户尝试 2 到 5 之间的值。
    请注意，当原始数据的分辨率远高于最终帧的像素尺寸时，图片也可能因量化而受到影响。
    此时也可以使用 :option:`-H` 选项来平滑结果以避免锯齿现象 [默认不进行降采样]。
    这种影响被称为亚像素渲染 (subpixel rendering)。

.. option:: -I

**-I**\ *includefile*
    将 *includefile* 的内容插入所有 movie 脚本都会访问的 movie_init.sh 脚本中。
    这种机制用于添加信息（通常是常量赋值），这些信息是主脚本 *mainscript*
    和 :option:`-S` 指定的脚本所依赖的。

.. option:: -K

**-K**\ [**+f**\ [**i**\|\ **o**]\ *fade*\ [**s**]]\ [**+g**\ *fill*]\ [**+p**\ [**i**\|\ **o**]]
    为动画添加淡入淡出效果 [默认不添加]。支持以下子选项：

    + **+f** - 设置淡入淡出长度 *fade* [默认 1s] 。
      使用 **+fi** 或 **fo** 可分别指定单边的淡入或淡出长度，
      或者都使用以指定长度不等的淡入淡出 [默认淡入淡出长度一致]。
    + **+g**\ *fill* - 设置淡入前和淡出后的颜色 [默认 black]。
    + **+p** - 保留所有帧。通常情况下，淡入淡出将被覆盖在动画的前几帧和后几帧上。
      使用 **+p** 进行复制来保留这些帧。添加 **i** 或 **o**
      分别只保留淡入或淡出的帧。

    关于淡入淡出效果，详见 `片头和淡入淡出`_ ， :option:`-E` 也有相关用法。

.. option:: -L

**-L**\ *labelinfo*\ [*modifiers*]
    为每帧自动添加标签。最多可重复添加 32 个标签。将选定的标签放置在帧边缘，支持以下几种标签类型：

    + **e** - 持续时间（秒），可加上 **+s**\ *scale* 指定每帧对应时长（秒）[默认为帧率倒数]。
    + **s**\ *string* - 固定字符串 *string* 。
    + **f** - 当前帧号 [默认标签]。
    + **p** - 当前进度百分比。
    + **c**\ *col* - 使用 *timefile* 文件中列索引为 col 的值（首列索引为 0 ）。
    + **t**\ *col* - 使用 *timefile* 文件中尾部文本列的列索引为 col 的单词（第一个单词索引为 0 ）。

    **注：** 如果用户使用 **-Lc** 指定的列元素为绝对时间，则时间戳的格式依赖
    :term:`FORMAT_DATE_MAP` 和 :term:`FORMAT_CLOCK_MAP` 这两个参数。默认情况下，
    日期 *date* 和时间 *time* 都会显示（中间空格隔开），将其中一个参数设置为 “-”
    即可跳过该项。标签的外观属性可用以下子选项控制：

    + **+c**\ *dx*\ [/*dy*] - 设置标签和其边界框的间距，仅在设置了 **+g** 或 **+p** 时使用。
      附加单位 **c|i|p** 或字体大小的百分比 [15%]。
    + **+f** - 设置字体属性 [:term:`FONT_TAG`] 。
    + **+g**\ *fill* - 设置标签边界框的填充色 [默认不填充] 。
    + **+h**\ [*dx*/*dy*/][*shade*] - 在标签边界框后添加下拉式阴影。用户可指定偏移量 *dx/dy*
      [4p/-4p] 和阴影颜色 [gray50]，要求使用 **+g** [默认无阴影] 。
    + **+j**\ *refpoint* - 设置标签位置 [TL] 。
    + **+o**\ *dx*\ [/*dy*] - 根据 **+j** 指定的位置做偏移。
      附加单位 **c|i|p** 或字体大小的百分比 [20%]。
    + **+p**\ *pen* - 指定标签边界框的画笔属性 [默认不绘制] 。
    + **+r** - 与 **+g** 或 **+p** 一起使用，设置圆角矩形标签边界框 [默认直角]。
    + **+t**\ *format* - 设置标签显示格式 [默认无特殊格式] 。

    **注：** 如果使用了 **-Lt** ，则标签显示格式必须包含类似 %s 的格式。
    其它标签类型则可能包含整数 (%d) 或浮点数 (%e, %f, %g) 。格式详见 C 语言的
    `printf <https://linux.die.net/man/3/printf>`_ 函数。

.. option:: -M

**-M**\ [*frame*\|\ **f**\|\ **m**\|\ **l**],[*format*][**+r**\ *dpu*][**+v**]
    除了制作动画序列，还可以单独绘制自定义主帧 *frame* [默认第 0 帧]。
    该帧将被保存到当前目录中，
    文件名命名为 *prefix.format* ，其中 *format* 为 GMT 允许的图形格式 [pdf]。
    除了指定主帧 *frame* ，还会使用 **f**\ irst, **m**\ iddle, 和 **l**\ ast
    代表对应帧。 **注：** 对于栅格帧格式，用户还可以通过 **+r** 指定该帧新的 *dpu*
    [默认与动画帧的 *dpu* 相同]。最后，添加 **+v** 可以默认查看器打开主帧。

.. option:: -P

**-P**\ *progress*\ [*modifiers*]
    自动添加进度指示器。最多可重复添加 32 个指示器。
    根据选定的对齐方式将选定的指示器放置在帧边缘。指示器可从 **a-f** 中选择 [**a**] 。
    **a-c** 是不同类型的圆形指示器，而 **d-f** 是线性（轴状）指示器。
    详见 `进度指示器`_ 。支持以下子选项控制外观：

    + **+a** - 对进度指示器 **b-f** 进行标注，
      后加 **e**\|\ **f**\|\ **p**\|\ **s**\|\ **c**\ *col*\ \|\ **t**\ *col*
      之一，以指示应该标注的内容（对应含义详见 :option:`-L` ）。
    + **+f** - 指定字体 [:term:`FONT_ANNOT_SECONDARY`] 。
    + **+g**\ *fill* - 设置活动项的填充色。
    + **+G**\ *fill* - 设置静止项的填充色。
    + **+j**\ *refpoint* - 设置指示器位置 [默认圆形指示器在 TR ，轴状指示器在 BC] 。
    + **+o**\ *dx*\ [/*dy*] - 根据 **+j** 指定的位置做偏移。
    + **+p**\ *pen* - 设置活动项的画笔属性。
    + **+P**\ *pen* - 设置静止项的画笔属性。
    + **+s**\ *scale* - 用帧号乘以比例 *scale* 来计算经过的时间。
    + **+t**\ *format* - 设置标注显示格式 [默认无特殊格式] 。
    + **+w**\ *width* - 设置指示器大小 [对于圆形指示器，默认大小为画布最大边长的 5% ；
      对于轴状指示器，默认长度为 **+j** 指定画布边的边长的 60%%]。

.. option:: -Q

**-Q**\ [**s**]
    用于调试：保留 GMT 创建的所有文件和目录以供检查。
    或者，添加 **s** 只构建 movie 脚本，但不做任何运行。
    一个例外是从 :option:`-Sb` 指定后台脚本总是被执行，因为它可能在构建 movie 脚本时产生所需的数据。

.. option:: -S

.. option:: -Sb

**-Sb**\ *background*
    指定一个 GMT 现代模式脚本 *background* （使用与 *mainscript* 相同的脚本语言编写），
    其可用于以下两个目的：

    #. 它可以生成一些文件（例如 *timefile* ），这些文件是 *mainscript* 制作动画时所需的；
    #. 它可以生成一个静态背景图，该图应作为所有帧的背景。
       此时脚本必须确保其使用与 *mainscript* 相同的绘图原点（即 **-X** 和 **-Y**），
       以便图层能够正确堆叠（除非用户确实希望有不同的偏移量）。

    另外，*background* 可以是与画布尺寸完全匹配的 *PostScript* 或 *EPS* 图。

.. option:: -Sf

**-Sf**\ *foreground*
    指定一个 GMT 现代模式脚本 *foreground* （使用与 *mainscript* 相同的脚本语言编写），
    用来制作静态前景图，该前景图会覆盖在所有帧上。
    确保其使用与 *mainscript* 相同的绘图原点（即 **-X** 和 **-Y**），以便图层能够正确堆叠。
    另外，*foreground* 可以是与画布尺寸完全匹配的 *PostScript* 或 *EPS* 图。

.. include:: explain_-V.rst_

.. option:: -W

**-W**\ [*dir*]
    默认情况下，所有临时文件和帧的 PNG 文件都会在通过参数 :option:`-N` 设置的子目录 *prefix* 中创建。
    用户可以通过提供另一个目录 *dir* （相对路径或完整路径）来覆盖此选择。
    如果未给出 *dir* ，则会在系统临时文件夹中创建一个名为 *prefix* 的工作目录。
    工作目录的主要优点是避免像 Dropbox 或 TimeMachine 这样的代理程序产生的无休止的同步问题，
    或者避免由于主目录空间不足而产生的相关问题。

.. option:: -Z

**-Z**\ [**s**]
    在合成动画后，将整个 *prefix* 目录删除 [默认情况下会保留包含所有图片的目录、
    临时脚本文件、参数文件和图层 *PostScript* 文件都会被删除（例外情况见 :option:`-Q` ）] 。
    如果用户希望同时删除 *mainscript* 以及通过 :option:`-E` 、 :option:`-I` 和 :option:`-S` 选项
    传入的所有输入脚本，则添加 **s** 。

.. include:: explain_-f.rst_

.. include:: explain_core.rst_
..

    **movie** 模块是例外，其并行不需要 OpenMP。
    **注：** 一个核被 **movie** 使用，所以实际上 n-1 个核被用于帧生成。

.. include:: explain_help.rst_

参数
---------

以下参数是自动分配的，可以在编写 *mainscript* 和可选的 *background* 和 *foreground*
脚本时使用。有两组参数：一组是常量，另一组随帧号变化。

**所有脚本都可以访问这些常量：**

+ **MOVIE_WIDTH** - 画布宽度
+ **MOVIE_HEIGHT** - 画布高度
+ **MOVIE_DPU** - 当前 *dpu* （每单位大小内的像素点数）
+ **MOVIE_RATE** - 当前帧率（每秒展示多少帧）
+ **MOVIE_NFRAMES** - 总帧数

此外，如果使用了 :option:`-I` ，那么其内列出的任何静态参数也将对所有脚本可用。

**mainscript 还可访问随帧号变化的参数：**

+ **MOVIE_FRAME** - 当前帧号（整数， e.g., 136）
+ **MOVIE_ITEM** - 格式化的帧号 （字符串, e.g., 000136）
+ **MOVIE_NAME** - 当前帧的名称 （即 *prefix*\ _\ **MOVIE_ITEM**）
+ 此外，如果提供了 *timefile* 文件，还可访问：

  + **MOVIE_COL0** , **MOVIE_COL1** , ...  - *timefile* 文件中每行的各列值
  + **MOVIE_TEXT** - *timefile* 文件中每行的尾部文本
  + **MOVIE_WORD0** , **MOVIE_WORD1** , ... -
    当使用 **-T+w** 对尾部文本进行分割或在 :option:`-L` 或 :option:`-P` 中隐式地做了分割后，
    可使用该参数访问每个词。

数据文件
--------

在执行 **movie** 时，movie 脚本能够找到起始目录中现有的任何文件，
以及由 *mainscript* 生成的任何新文件，或者通过 :option:`-S` 参数设置的任何附加脚本所生成的文件。
无需指定路径即可访问这些文件。
其他文件可能需要完整的路径，除非其所在的目录已在 :term:`DIR_DATA` 设置中包含。

自定义 gmt.conf 文件
--------------------

在执行 **movie** 前，如果在主脚本 *mainscript* 所在目录下有 gmt.conf 文件，
那么它将在所有创建和执行的脚本中被共享使用，除非用户的脚本在启动新的现代模式会话时使用了
**-C** 选项。更改 GMT 默认设置的首选方式是在输入脚本中使用 :doc:`set` 命令。
**注：** 每个脚本都是在独立的现代模式下运行的，
因此试图通过在运行时创建一个 gmt.conf 文件以供其他脚本使用是徒劳的。

绘制时间变化
------------

如果动画没有任何变化，那就没意思了。要让动画动起来，用户需要确保主脚本 *mainscript*
能够随着帧号变化而访问不同的数据集，或者只需绘制数据集中的一个变化的子集，
从而只在每一帧中显示应该显示的部分。用户可以采用多种策略来实现这些效果：

#. 用户传递给 :option:`-T` 的 *timefile* 可能包含特定数据文件的名称，
   用户只需在主脚本中使用相应的 **MOVIE_TEXT** 或 **MOVIE_WORD** 来访问特定帧的文件名即可。
#. 用户有一个数据表格 *timefile* ，其中包含了每条记录的绝对时间。
   用户希望随着时间的推移来展示这些记录，但不显示未来发生的记录。
   此效果可通过设置 :doc:`/option/q` 来选择输入行，
   或使用 :doc:`events` 模块来实现。
#. 用户有一个三维网格（或者是一系列二维网格的堆叠）并且想要沿着垂直轴进行插值
   （例如深度轴）。在这种情况下，用户将使用 :doc:`grdinterpolate` 模块，
   让主脚本获取对应于正确动画时刻的切片（这可能是两个不同深度之间的插值），
   并使用这个临时网格文件来制作帧图。
#. 用户可能正在使用 :doc:`math` 或 :doc:`grdmath` 动态地创建数据，并显示这些数据；
   或者可能每帧稍微不同地处理数据（使用 *timefile* 中的参数），并展示不同处理的帧之间的变化。

画布
----

.. gmtplot:: movie/GMT_movie_canvas.sh
   :width: 600 px
   :align: center
   :show-code: false

   **MOVIE_WIDTH** 和 **MOVIE_HEIGHT** 代表了画布尺寸，用户可使用 **-X** 和 **-Y** 来设置绘图原点 [72p, 72p] 并使用投影参数 **-J** 来指示选择用于绘图的区域（图中绿色区域）。

如 :option:`-C` 所述，除非用户指定了自定义布局，否则用户获得的画布尺寸要么是 24 x 13.5 厘米
（16:9），要么是 24 x 18 厘米（4:3）。如果用户的 :term:`PROJ_LENGTH_UNIT` 设置为英寸，
那么画布尺寸（9.6 x 5.4 英寸或 9.6 x 7.2 英寸）仅比相应的国际单位制尺寸略大（1.6%）；
这不会对动画尺寸产生影响，但允许用户使用与所选 *dpu* 相配合的合适尺寸。
用户应使用给定的画布尺寸来绘制图层，而 **movie** 会做适当转换将画布转换为图像像素尺寸。
用户有责任使用 **-X** 和 **-Y** 来留出合适的边距以及在画布上对各绘图元素进行任何定位。
为了减少处理时间，建议将动画的任何静态部分视为静态背景
（由 *background* 脚本一次性生成；见 :option:`-Sb`）和/或静态前景（由 *foreground* 一次性生成；见 :option:`-Sf`）；然后 **movie** 会按帧组装这些层。
此外，任何在帧循环中使用的静态数据文件的计算都可以由 *background* 脚本完成。
任何依赖帧号的数据或变量都必须通过主脚本计算或设置，或者按照上述方式通过参数提供。
**注：** 使用变量 **MOVIE_WIDTH** 或 **MOVIE_HEIGHT**
来设定绘图尺寸可能会导致图形被裁剪到画布之外，因为这些值正是画布的实际尺寸。

外部 *PostScript* 图层
-----------------------

除了将 GMT 现代脚本传递给 :option:`-S` 外，用户还可以提供 *PostScript* 图层文件的名称。
注意，这些图层必须与画布尺寸完全匹配。举个简单的例子，
如果用户正在制作一个使用 US 单位尺寸的 HD 电影，那么一个背景粉色图层将由以下方式创建::

    gmt basemap -R0/9.6/0/5.4 -Jx1i -B+gpink -X0 -Y0 --PS_MEDIA=9.6ix5.4i -ps background

其中注意通过 :term:`PS_MEDIA` 进行的画布选择、匹配的区域和投影以及绘图原点在 (0,0)。

底图边框
--------

某些地图投影默认会绘制出一个 *fancy* 地图边框；此功能受 :term:`MAP_FRAME_TYPE` 控制。
然而，实际绘制的是 *fancy* 边框还是 *plain* 框架还取决于投影中心纬度。
因此，如果用户的影片通过改变视角来改变投影中心纬度，那么用户在设置时应将边框类型设为 *plain* 。

透明图片
------------

默认情况下， **movie** 会生成不透明的 PNG 图片，这些图片随后可以组合成动画。
大多数动画很可能会以这种方式制作。
使用 :option:`-F` 选项的实验性选项 **+t** 可以生成透明的 PNG 图像。
目前，很少有视频编解码器支持透明度。据称 H.265（HECV）和 VP9（Webm）都具备这一功能；
GMT 团队只能通过在 Chrome 中观看透明的 Webm 文件来验证后者。
GIF 也可以由透明的 PNG 图像构建而成，这里每个额外的帧都会累积到最终的动画中。
专家可能会创建透明的 PNG 图像，并使用支持透明通道的专业工具来制作动画。

添加音轨
-------------

使用 :option:`-A`\[**+e**] 选项，用户可以添加音频轨道，比如为动画配音、添加音乐，或者添加任何内容。
最终的动画长度将与音频或动画中最长的部分长度相匹配，
所以用户可能需要对音频文件进行处理以适应动画。
由于动画的长度已知为 *帧数/帧率*，因此用户可以对音频轨道进行预处理以使其具有匹配的长度。
或者，如果音频轨道的长度与动画长度大致相同（动画长度的 ±50% 范围内），
则可以添加 **+e** 选项将音频轨道缩放至与动画完全相同的长度。

技术细节
------------

**movie** 模块会创建若干隐藏的脚本文件，这些文件用于图像的生成
（由于文件扩展名取决于所使用的脚本语言，此处未列出扩展名）：

+ *movie_init* （初始化与画布大小和每单位点数相关的变量，并包含可选 *includefile* 的内容）
+ *movie_preflight* （可选，因为它源自 :option:`-Sb` 并计算所需的数据文件以及可能的背景层）
+ *movie_postflight* （可选，因为它源自 :option:`-Sf` 并构建前景层）
+ *movie_frame* （接受帧计数参数并构建帧图像）
+ *movie_cleanup* （在运行结束时删除临时文件）

对于每个帧，都有一个单独的名为 *movie_params_######*
脚本提供了特定帧的变量（例如，帧号以及通过 :option:`-T` 给出的任何内容）。
*preflight* 和 *postflight* 脚本可以访问 *movie_init* 中的信息，
而 *frame* 脚本在此基础上还能访问这个帧参数文件。
使用 :option:`-Q` 选项会生成这些脚本，然后用户可以对其进行查看。
**注：** 主脚本会按帧复制，并且每个副本都会同时在所有可用内核上运行。
因此，多线程的 GMT 模块也将只能使用单个核心。

将 PNG 图帧转换为动画 GIF（使用选项 :option:`-F`\ *gif* ）需要使用
`GraphicsMagick <http://www.graphicsmagick.org/>`_ 工具。
因此，**gm** 必须能通过标准搜索路径访问到。
同样，将 PNG 图帧转换为 MP4（使用选项 :option:`-F`\ *mp4* ）或 WebM（使用选项 :option:`-F`\ *webm* ）
的视频文件也依赖于 `FFmpeg <https://www.ffmpeg.org/>`_ 。

Shell 局限性
---------------

由于无法控制 shell（例如 bash 或 csh）在两个进程之间实现管道操作的方式（这通常涉及 sub-shell），
因此 GMT 建议不要在主脚本中使用涉及将一个 GMT 模块的结果传递到另一个模块的命令
（例如，gmt blockmean ..... \| gmt surface ...）。
因为 **movie** 会同时运行主脚本的多个副本，当涉及 sub-shell 时可能会出现奇怪的情况。
根据经验，在 movie 脚本中使用管道可能会损坏 GMT 历史文件，
导致某些帧出现诸如区域未设置之类的错误消息。
在编写 movie 主脚本时，可以使用临时文件将这样的管道结构拆分为两个部分。
**注：** 从非 GMT 模块到 GMT 模块或与之相反的管道操作是没问题的
（例如，echo ..... | gmt plot ...）。

制作动画的提示
---------------

制作动画相对简单，但用户需要从变量的角度去思考问题。
仔细研究以下所描述的示例。然后，先制作一个单一的绘图脚本（即主脚本），
并确定哪些内容会随时间变化（即随着帧号的变化）。为这些值创建变量。
如果它们属于 **movie** 创建的参数，则使用这些名称。
除非只需要帧号，否则用户还需要创建一个可以传递给 :option:`-T` 的文件。
这个文件应包含用户所需的所有数值，这些数值会按帧（即行）排列，
且涵盖了用户所需的所有列中的数值。
如果用户需要分配一些不随时间变化的固定变量，则将这些赋值任务转移到单独的
*includefile* （:option:`-I`）文件中会使得主脚本看起来更简洁、更清晰。
要测试输出动画，可首先使用选项 :option:`-Q` 和 :option:`-M` 以确保主帧页面看起来正确。
这个页面会显示动图的一个帧（可以通过 :option:`-M` 参数选择要显示的帧）。
修复在使用变量和选项时出现的任何问题，直到一切正常为止。然后用户可以尝试删除 :option:`-Q`。
GMT 建议用户制作一个非常简短（:option:`-T`）且尺寸较小（:option:`-C`）的影片，这样就能很快看到结果。
一旦一切正常运行，用户就可以增加帧数和提高影片质量了。

颜色表的使用
------------

因为 **movie** 将不同帧的绘制作为单独的会话并行运行，所以用户无法使用当前的 CPT
（即由 :doc:`makecpt` 或 :doc:`grd2cpt` 直接创建的最后一个 CPT，
或者通过 :doc:`grdimage` 或 :doc:`grdview` 间接创建的）。
因此，用户必须使用明确的文件来创建 CPT，并将这些文件名传递给需要 CPT 信息的模块。
在现代模式下，这意味着用户需要在 :doc:`makecpt` 或 :doc:`grd2cpt` 中使用 **-H** 选项，
以便将它们的输出重定向到指定的文件中。

进度指示器
------------

.. figure:: https://docs.generic-mapping-tools.org/latest/_images/GMT_movie_progress.png
    :width: 500 px
    :align: center

    六种进度指示器。它们都有默认的大小、位置、颜色和笔（如图所示），但这些都可以被相应的子选项覆盖（见下文）。

使用字母 **a-f** 选择上述六个进度指示器中的一个；每个都有默认的属性，可以通过子选项来改变：

+ **a** - 需要指定静止项填充色 (**+G**) [lightgreen] 和活动项填充色 (**+g**) [lightred] ，
  没有标注选项。
+ **b** - 可指定静止项画笔 (**+P**) [lightblue] 和活动项画笔 (**+p**) [blue] ，
  如果使用了 **+a** ，则在中心添加一个标注，大小为指示器大小的 30%
  （除非使用了 **+f** 自定义字体大小）。
+ **c** - 可指定静止项画笔 (**+P**) [dashed darkred，宽度为指示器大小的 1%]
  和弧形矢量的活动项画笔 (**+p**) [red，宽度为指示器大小的 5%] ，
  其箭头大小为指示器大小的 20% 。如果使用了 **+a** ，则在中心添加一个标注，
  大小为指示器大小的 30% （除非使用了 **+f** 自定义字体大小）。
+ **d** - 可指定静止项画笔 (**+P**) [black] 和活动项画笔 (**+p**)
  [yellow，宽度为指示器大小的 0.5%] ，如果使用了 **+a** ，则标注字体大小为静态画笔宽度的两倍
  （除非使用了 **+f** 自定义字体大小）。
+ **e** - 可指定静止项画笔 (**+P**) [red] 和活动项画笔 (**+p**) [lightgreen] ，
  如果使用了 **+a** ，则标注字体大小为静止画笔宽度的两倍（除非使用了 **+f** 自定义字体大小）。
+ **f** - 可指定静止轴的画笔 (**+P**) [black] 和滑动三角的填充色 (**+g**) [red] ，
  三角大小为轴宽的两倍，字体大小为轴宽的三倍。

关于 **d-f** 指示器：如果使用百分比标注 (**+ap**)，则轴上显示标注，否则无标注。
这些指示器对所有 *justify* 代码均保持水平，除了 **ML** 和 **MR** 。
对于静止线条，其默认线宽为指示器大小的 2.5% 和 8p 的二者较小值
（指示器 **f** 为 1.5% 和 3p）。

如果不指定指示器大小 (**+w**)，对于圆形指示器，默认大小为画布最大边长的 5% ；
对于轴状指示器，默认长度为 **+j** 指定画布边的边长的 60% 。

片头和淡入淡出
--------------

.. figure:: https://docs.generic-mapping-tools.org/latest/_images/GMT_title_fade.png
    :width: 500 px
    :align: center

    整个动画的淡化水平 (0 为全黑, 100 为全部可见) ，包含一个可选的片头。

整个动画可以有一个可选的片头（:option:`-E`），其时长 *duration* 由用户设定。
在该时长的开头和/或结尾部分，可以指定使用指定的淡化颜色 [black] 进行淡入/淡出处理。
主动画部分也可以有可选的淡入/淡出部分（:option:`-K`），
在此选项中，用户可以选择在动画上进行淡入/淡出处理，或者可以将第一个和/或最后一个动画帧冻结，
仅对这些静态图像进行渐隐处理（通过子选项 **+p**）以保留整个动画序列。

其它电影格式
------------------

按照设置，对于电影格式， **movie** 模块仅提供了 MP4 和 WebM 格式。
转换工作由工具 `FFmpeg <https://www.ffmpeg.org/>`_ 完成，该工具拥有海量的编码器和处理选项。如果用户希望使用其他选项运行 FFmpeg，请使用两种视频格式之一来运行 **movie**。
最后它会打印出使用的 FFmpeg 命令。
用户可以复制、粘贴并修改此命令以选择其他编码器、比特率和参数。
用户还可以将生成的 PNG 序列作为输入传递给诸如 QuickTime Player、iMovie、MovieMaker
等商业程序或其他工具，从而以这种方式制作电影。

基于已有的 PNG 帧重新制作电影
-----------------------------

当用户在制作好电影后，决定改变帧率，或者调整电影中所有静止图像的组合方式。
如果用户保留了所有的帧图像，那么不必重新运行整个渲染过程。
假设用户想要一个 MP4 电影，并且只是想要重新运行ffmpeg命令，这里有一个例子::

    ffmpeg -loglevel warning -f image2 -framerate 24 -y -i "mydir/myimages_%04d.png" -vcodec libx264 -pix_fmt yuv420p mymovie.mp4

当 **movie** 执行时，也会打印此命令。对于其他电影格式，需要查阅 FFmpeg 文档。
**注：** 在 Windows 上，百分比符号是特殊字符（就像shell下的美元符号），
所以用户需要输入两个（%%）。

操作多个电影
----------------

如果用户正在制作一系列相似的电影，可以使用 FFmpeg 在空间上将它们拼成一部电影。
假设有四个电影片段，分别名为 movie_1.mp4、movie_2.mp4、movie_3.mp4 和 movie_4.mp4，
而用户希望将它们组合成一个 2x2 的面板，以便同时展示这四个影片。首先，需要将片段（1,2）和（3,4）水平拼接在一起，然后将这两个拼接后的片段垂直拼接起来::

    ffmpeg -i movie_1.mp4 -i movie_2.mp4 -filter_complex hstack=inputs=2 top.mp4
    ffmpeg -i movie_3.mp4 -i movie_4.mp4 -filter_complex hstack=inputs=2 bottom.mp4
    ffmpeg -i top.mp4 -i bottom.mp4 -filter_complex vstack=inputs=2 four_movies.mp4

有关此类操作的更多信息，请参阅 FFmpeg 文档。

macOS 问题
------------

在 macOS 系统中，默认情况下限制同时打开文件的数量相对较小。
当同时构建大量帧时，出现 “打开文件过多” 错误而导致影片制作失败的情况并不罕见。
GMT 建议用户参考这篇有用的
`文章 <https://superuser.com/questions/433746/is-there-a-fix-for-the-too-many-open-files-in-system-error-on-os-x-10-7-1>`_
以获取各种解决方案。

示例
-------

.. include:: explain_example.rst_

以下是通过不断改变中心经度以实现制作旋转地球的简单示例，其中自定义一个像素大小为
600x600 的方形区域，帧数为 360 帧，在左上角放置一个帧计数器，在右上角放置一个进度指示器。
该示例不需要自定义输入数据文件，可在本地运行。

首先创建主脚本 *globe.sh* ::

    gmt begin
        gmt coast -Rg -JG${MOVIE_FRAME}/20/${MOVIE_WIDTH} -Gmaroon -Sturquoise -Bg -X0 -Y0
    gmt end

再使用 **movie** 模块生成 GIF 动图::

    gmt movie globe.sh -Nglobe -T360 -Fgif -C6ix6ix100 -Lf -P

.. only:: html

    :download:`Source Code <https://dengda98.github.io/zh/posts/tech/gmt_movie/movie_ex02.sh>`

    .. figure:: https://dengda98.github.io/zh/posts/tech/gmt_movie/globe.gif
        :width: 500 px
        :align: center

在执行过程结束时，用户会发现了 GIF 文件 *globel.gif* 以及一个目录 *glob* ，
其中包含了全部 360 张 PNG 图片。请注意， *globe.sh* 脚本中没有包含任何反映图表名称、
画布大小、栅格化 *PostScript* 的尺寸等信息的内容。
这些信息对用户是隐藏的；实际执行的 movie 脚本是根据用户提供的脚本生成的，
并提供了额外的机制。 **movie** 模块会自动使用所有可用的核心来管理对所有帧的并行执行循环。

更多示例
------------

- :gmt-docs:`GMT 官方动画示例 <animations.html>`
- `GMT YouTube Channel <https://www.youtube.com/channel/UCo1drOh0OZPcB7S8TmIyf8Q>`__

弃用
----------

- 6.3.0: Consolidate -A into -F for a more unified option. `#5613 <https://github.com/GenericMappingTools/gmt/pull/5613>`_

参考文献
--------------

Wessel, P., Esteban, F., & Delaviel-Anger, G. (2024). The Generic Mapping Tools and Animations for the Masses. Geochemistry, Geophysics, Geosystems, 25(6). https://doi.org/10.1029/2024GC011545

相关模块
-----------

:doc:`math`,
:doc:`grdinterpolate`,
:doc:`grdmath`,
:doc:`events`
