IT pass HikiWiki - [Exp2021]NetCDF データの可視化 図の連続生成と動画の作成 Diff

  • Added parts are displayed like this.
  • Deleted parts are displayed like this.

((<"スケジュール表・各回資料 (08/13)"|[Exp2021]スケジュール表・各回資料#08-2F13>))

{{toc}}

ここまでの説明では, それぞれのスクリプトは一つの画像を表示するだけでした.
しかし, 例えば時間変化する場の確認には複数の時間にわたって一連の画像を表示することが有益です.

ここでは複数の一連の画像を一つのスクリプトで描画する方法を説明します.

#----------------------------------------------------------------------------
= 画面への描画

下のスクリプトは,
((<詳しい指定|[Exp2021]NetCDF データの可視化 詳しい指定>))で説明した ((<tone_xy_coast.rb|URL:/exp/fy2021/210813/practice_gphys/tone_xy_coast.rb>)) を基に平均操作を行ったものです.

((<tone_xy_coast_anim.rb|URL:/exp/fy2021/210813/practice_gphys/tone_xy_coast_anim.rb>))

    01  #!/usr/bin/ruby
    02  # 使用するライブラリの読み込み. (以下 2 行は「決まり文句」.)
    03  require "numru/ggraph"
    04  include NumRu
    05
    06  # NetCDF ファイル "air.2019.nc" から変数 "air" を読み, GPhys オブジェクト gp に
    07  # 格納
    08  gp = GPhys::IO.open( "air.2019.nc", "air" )
    09
    10  # 画面を開く (open)
    11  #   引数の 1 は画面への描画を表す
    12  #          2 はファイルへの出力を表す
    13  #            (デフォルトでは出力は pdf 形式でファイル名は dcl.pdf)
    14  DCL.gropn(1)
    15
    16  # 描画に関わる各種の指定
    17  DCL.sgpset('lfull',true) # 描画範囲を最大に設定 (ウィンドウのギリギリま で使用)
    18  DCL.uzfact(0.6)          # フォントサイズ (元の値に対する比率)
    19  DCL.sgpset('lclip',true) # 軸範囲を超えた領域を描画しない
    20  DCL.glpset('lmiss',true) # 欠損値処理
    21  GGraph.set_axes('xlabelint'=>30) # x 軸にラベルを書く間隔
    22  GGraph.set_axes('ylabelint'=>30) # y 軸にラベルを書く間隔
    23
    24
    25  # 描画画面を準備
    26  #   itr の 1 は横軸, 縦軸ともに線形を表す
    27  #          2 は横軸が線形軸, 縦軸が対数軸を表す
    28  #          3 は横軸が対数軸, 縦軸が線形軸を表す
    29  #          4 は横軸, 縦軸ともに対数軸を表す
    30  #          10 は正距円筒図法 (いわゆる経度-緯度座標) を表す
    31  #   viewport は描画可能範囲の中での図の描画範囲を表す
    32  #            (四隅 [x1,x2,y1,y2] の全体に対する比率)
    33  #   window は描画する軸の範囲を表す (四隅 [x1,x2,y1,y2] の値)
    34  GGraph.set_fig( 'itr'=> 10, 'viewport'=>[0.1,0.9,0.2,0.55], 'window'=>[0,360,-90,90] )
    35
    36  # 地図情報の指定. ここでは海岸線を描くように設定.
    37  GGraph.set_map( 'coast_world'=>true, 'grid'=>true )
    38
    39  # for を使うことで, 2019 年 1 月 1 日, 2 月 1 日, 3 月 1 日, ... の一か 月ごとの分布を描画
    40  for i in 1..12
    41
    42    # 2019 年 1 月 1 日から 12 月 1 日までの一か月おきの日付の文字列を生成
    43    #   ruby では文字列を + で結合することができる
    44    #   i は元々整数型変数.
    45    #   + を使って結合するためには文字列でなければならないため,
    46    #   to_s メソッド (s は string の略) を使って文字型変数に変換している.
    47    time_label = "2019-" + i.to_s + "-01 00:00:0.0"
    48
    49    # 繰り返し回数, 日付を画面に表示
    50    print i, " : ", time_label, "\n"
    51
    52    # トーン (色付け) で描画
    53    #   第一引数は描画するデータの GPhys オブジェクト
    54    #   第二引数の true は新しい図であることの指定
    55    #   以下の指定の順番は問わない
    56    #   annotate は図の右上に書かれる情報の表示の有無の指定
    57    #   map_axes は正距円筒図法において軸のラベルを書くかどうかの指定
    58    #   nlev は使用するトーン (色分け) の数の指定
    59    #   min は描画するトーンの最小値の指定
    60    #   max は描画するトーンの最大値の指定
    61    #
    62    #   GPhys オブジェクト gp から time_label を用いて切り出していることに注意
    63    GGraph.tone( gp.cut('time'=>DateTime.parse(time_label)), true, 'annotate'=>true, 'map_axes'=>true, 'nlev'=>30, 'min'=>210, 'max'=>320 )
    64    # カラーバーを描画
    65    GGraph.color_bar
    66
    67  end
    68
    69  # 画面を閉じる (close)
    70  DCL.grcls

上のスクリプトを実行すると, 下のような図が表示されるでしょう.
このスクリプトでは 1 ページごとにスクリプトが停止します.
1 枚描画されるごとに描画ウィンドウをクリックすることで次のページに進みます.

((<URL:/exp/fy2021/210813/practice_gphys/tone_xy_coast_anim_0001_small.png>))
((<URL:/exp/fy2021/210813/practice_gphys/tone_xy_coast_anim_0002_small.png>))
...

((<tone_xy_coast_anim.rb|URL:/exp/fy2021/210813/practice_gphys/tone_xy_coast.rb>)) との違いは以下です.
* 描画部分に繰り返しを使うことで 1 月 1 日から 12 月 1 日までの一カ月おきの分布を連続して描画しています.
  その際, 時刻切り出しのために, 47 行目で日時の文字列を生成し, 63 行目で GPhys オブジェクトから各月のデータを切り出しています.
  * ruby には様々な文字列操作の方法が用意されていて, ここでは to_s メソッド ((数値などの)変数を文字型に変換する) や + 演算子 (文字列を結合する) を使って日時の文字列を生成しています.
* トーンを描画する 63 行目では, min, max によってトーンを描画する値の範囲を指定しています.
  これを指定しない場合, トーンを描画する値の範囲が月ごとに自動的に決定されますので, 月によって色のつけ方がバラバラになってしまいます. min, max を指定することで, すべての動画で同じ色のつけ方をすることができます.
  * 分布を比較する際にはこのような配慮が不可欠です.

== 凝ったトーンの指定

さらに凝った色指定には下のような方法があります.

  levels = [210,220,230,240,250,260,270,280,290,300,310,320]
  patterns =  [10999,20999,30999,40999,50999,60999,70999,75999,80999,90999,95999]
  GGraph.tone( gp.cut('time'=>DateTime.parse(time_label)), true, 'annotate'=>true, 'map_axes'=>true, 'lev'=>levels, 'pat'=>patterns )

この例では, 配列 levels と patterns にそれぞれ等値線の値と色を指定しています.  この例では等間隔ですが, 不等間隔に色を塗ることもできます.

色は 5 桁の数字で指定されており, 上位 2 桁が色および塗り方を表し, 下位 3 桁が塗り方 (パターン番号) を表します.  
* 色 (上位 2 桁) は 10 から 99 までの数値で指定します.
  * 標準設定では, 10 から 99 までの数値が, 紫から青, 緑, 黄色, 赤を経て白までの色に割り当てられています.
    * 色の割り当ては((<こちら|URL:http://www.gfd-dennou.org/library/dcl/dcl-7.4.0/src/env1/colormap/colormap_gallery.html>))を参照してください.
    * 色の割り当て方はいくつか用意されています. 変更するには, 事前に
        DCL.sgscmn(N)
      を実行してカラーマップを変更します. N はカラーマップ番号です.
      例えば 5 を与えると, 白黒 (グレースケール) になります.
* 塗り方 (下位 3 桁) の 999 はべた塗りを意味します.
  * パターン番号の詳細は((<こちら|URL:http://ruby.gfd-dennou.org/products/ruby-dcl/ruby-dcl-doc/grph1/node21.html>))で確認できます.

このようにすることで等値線の間隔も配色も好きなように設定することができます.

また, 上の例では levels に与える等値線の値の数と patterns に与える数値の数は同じです.
しかし, patterns に与える数は, levels に与える数より二つ多くの数字を指定することができます.
このとき, levels に与えた最小値以下と最大値以上の領域にも色を塗ることができます.


#----------------------------------------------------------------------------
= 動画ファイルの作成

上では画面上に一連の画像を表示しましたが, それらをファイルに保存してみましょう.

== 一つの pdf ファイルへの保存

図を pdf 形式でファイルに保存する方法は既に説明した通りで, gropn の引数の数字を変更します.
上のスクリプトの

    DCL.gropn(1)



    DCL.gropn(2)

と変更することで, pdf ファイルとして保存することができます.

この時生成される pdf ファイル (dcl.pdf) は 12 ページからなり, 各ページに一か月ごとの図になります.

== 複数の png ファイルへの保存

図を png 形式でファイルに保存する方法は既に説明した通りで, gropn の引数の数字を変更し, また画像形式を指定します.
上のスクリプトの

    DCL.gropn(1)



    DCL.swpset("ifl",1)
    DCL.gropn(2)

と変更することで, png ファイルとして保存することができます.

この時生成される png ファイルは dcl_0001.png, dcl_0002.png, ..., dcl_0012.png の 12 個のファイルとなり, 各ファイルに一か月ごとの図が保存されています.

== 動画ファイルの生成

上の方法で各時刻の png ファイルが生成できたら, それらを結合することで動画ファイルを作成することができます.
下では二つの形式での動画ファイルの作成方法を説明します.

* gif アニメ
* avi (mpeg4)

gif アニメの方が手軽です.
しかし, 再生中に停止することができません.
avi ファイルは, プレイヤーによっては停止することもできます.


=== gif アニメ

複数の図をまとめて動画ファイルを作成する方法は複数ありますが, 方法の一つは convert コマンドを使って gif アニメファイルを作ることです.

上に説明したように, dcl_0001.png, dcl_0002.png, ..., dcl_0012.png というファイルが作成したとき, 下のようにすることで gif アニメーションファイルを作ることができます.

  $ convert -delay 100 -loop 0 dcl_*.png movie.gif

このコマンドを実行することで movie.gif ファイルが作成されます.

上で用いた convert のオプションは以下です.
* -delay : コマの時間間隔 (1/100 秒単位)
* -loop  : 繰り返し回数. 0 は無限に繰り返すことを意味する.

gif アニメーションファイルは eog コマンドで観ることができるでしょう.

  $ eog movie.gif

下のような動画が表示されるでしょう.

((<URL:/exp/fy2021/210813/practice_gphys/tone_xy_coast_anim.gif>))

なお, gif アニメのファイルは, 自分の PC にダウンロードした際にはブラウザで開けば動画が再生されるでしょう.

==== 注意

convert コマンドは, 大量の画像を一度に処理すると, 計算機リソースを使い切って異常停止することがあります.
その際には,
* convert のメモリ使用制限を緩和する
* ちょっと迂回して gif アニメファイルを作成する
* 下で説明している方法で avi (mpeg4) 形式の動画を作成する
のどれかをお勧めします.

* convert のメモリ使用制限を緩和する

  convert のメモリ使用上限は /etc/ImageMagick-6/policy.xml で規定されています.
  したがって, このファイルを編集することで異常停止を防ぐことができるかもしれません.

  例えば /etc/ImageMagick-6/policy.xml を emacs で編集するには

    $ sudo emacs -nw /etc/ImageMagick-6/policy.xml

  として emacs を起動します.

  そしてファイル内の

    <policy domain="resource" name="memory" value="256MiB"/>

  を

    <policy domain="resource" name="memory" value="1GiB"/>

  などに変えてみましょう. "2GiB", "4GiB" でもよいかもしれません.

  これで convert のメモリ使用量の上限が緩和されるでしょう.


* ちょっと迂回して gif アニメファイルを作成する方法:

  ここでは gifsicle コマンドを用いて gif アニメファイルを作成する方法を説明します.
  この方法にもリソース消費の限界があるかもしれませんが, 経験的には, convert
  コマンドで作成するよりはマシのようです.

  そのため gifsicle パッケージをインストールします.

    $ sudo apt install gifsicle

  gifsicle は複数の gif ファイルを結合して gif アニメファイルを作成するコマンドです.

  次に, gifsicle コマンドの入力になる gif ファイルを作成しなければいけません.
  このため, 上に説明した方法で作成された複数の png ファイルを, mogrify コマンドを使って gif ファイルに変換します.

    $ mogrify -format gif dcl_*.png

  これで, dcl_*.png ファイルからそれぞれ dcl_*.gif ファイルが作成されます.
  その上で, gifsicle コマンドを用いて動画ファイルを作成することができます.

    $ gifsicle -d100 -l0 -o movie.gif dcl_*.gif

  * -d : コマの時間間隔 (1/100 秒単位)
  * -l : 繰り返し回数. 0 は無限に繰り返すことを意味する. ("-l" と "0" の間に空白を入れない.)

  sigsicle のオプションは -h で確認できます.


=== avi (mpeg4)

複数の図をまとめて動画ファイルを作成するもう一つの方法は ffmpeg コマンドを使って avi ファイル (中身は mpeg4) を作ることです.

まず ffmpeg コマンドをインストールします.

  $ sudo apt install ffmpeg

上に説明したように, dcl_0001.png, dcl_0002.png, ..., dcl_0012.png というファイルを作成したとき, 下のようにすることで avi ファイルを作ることができます.

  $ ffmpeg -r 1 -i dcl_%04d.png -q:v 0 movie.avi

このコマンドを実行することで movie.avi ファイルが作成されます.

上で用いた ffmpeg のオプションは以下です.
* -r   : 1 秒あたりのコマ数
* -i   : 入力ファイル. %04 は 4 桁の数字を意味する.
* -q:v : 出力画質指定 (実はこの指定は不要かもしれない/効いてないかもしれない)

avi ファイルは linux では mplayer コマンドで観ることができるでしょう.

mplayer のインストール

  $ sudo apt install mplayer

mplayer での再生

  $ mplayer movie.avi

あるいは, 自分の PC にダウンロードして再生しても良いでしょう.
PC では例えば ((<VLC player|URL:https://www.videolan.org/index.ja.html>)) で再生することができます.
(経験としては, Windows10 標準のアプリでは上手く再生されないことがあります. そんなときには VLC player を試してみましょう.)

できる動画は ((<URL:/exp/fy2021/210813/practice_gphys/tone_xy_coast_anim.avi>)) のようになります.