[GMT] 動畫製作的簡單說明與準備

昨天課堂上,我和同學們一起準備期中報告,有些同學富有挑戰精神,決定製作動畫。但是沒有真正跑過,電腦也沒有準備好相關的環境,因此製作就碰到了問題。下面記述一些基本觀念與如何在 Mac 環境下製作 GMT 的範例動畫。

基本概念

用GMT製作動畫大概可以分成以下幾個階段:

  1. 準備資料(切圖、計算等等)。
  2. 準備固定、不隨偵數變化的底圖,假設另存為 Base.ps。這邊檔案不能終結。
  3. 撰寫迴圈,準備繪製「動」畫的部分。
    1. 複製 Base.ps 檔案到一個暫存檔 tmp.ps。
    2. 以繪圖指令將變動的部分加入 tmp.ps。
    3. 將 tmp.ps 轉換為圖片檔(jpg, tiff etc.)
  4. 組合所有圖片變成 gif 檔,範例中使用 graphicsmagick 這個軟體完成。

安裝 GraphicsMagick

在 Mac 上可以用 Homebrew 來確定套件資訊:

$brew info graphicsmagick

graphicsmagick: stable 1.3.25 (bottled), HEAD
Image processing tools collection
http://www.graphicsmagick.org/
/usr/local/Cellar/graphicsmagick/1.3.25 (485 files, 12.9MB) *
  Built from source on 2017-04-19 at 11:40:13 with: --with-ghostscript --with-x11
From: https://github.com/Homebrew/homebrew-core/blob/master/Formula/graphicsmagick.rb
==> Dependencies
Build: pkg-config ✔
Required: libtool ✔
Recommended: jpeg ✔, libpng ✔, libtiff ✔, freetype ✔
Optional: little-cms2 ✔, jasper ✔, libwmf ✘, ghostscript ✔, webp ✔
==> Requirements
Optional: x11 ✔
==> Options
--with-ghostscript
	Build with ghostscript support
--with-jasper
	Build with jasper support
--with-libwmf
	Build with libwmf support
--with-little-cms2
	Build with little-cms2 support
--with-perl
	Build PerlMagick; provides the Graphics::Magick module
--with-quantum-depth-16
	Compile with a quantum depth of 16 bit (default)
--with-quantum-depth-32
	Compile with a quantum depth of 32 bit
--with-quantum-depth-8
	Compile with a quantum depth of 8 bit
--with-webp
	Build with webp support
--with-x11
	Build with x11 support
--without-freetype
	Build without freetype support
--without-jpeg
	Build without jpeg support
--without-libpng
	Build without libpng support
--without-libtiff
	Build without libtiff support
--without-magick-plus-plus
	disable build/install of Magick++
--without-svg
	Compile without svg support
--HEAD
	Install HEAD version

因為要遠端使用 X11 ,因此我安裝時就加了下面兩個參數「–with-ghostscript –with-x11」(不知道對不對啦 😛 )

安裝完成後,以「gm」測試是否安裝成功:

$ gm
GraphicsMagick 1.3.25 2016-09-05 Q16 http://www.GraphicsMagick.org/
Copyright (C) 2002-2016 GraphicsMagick Group.
Additional copyrights and licenses apply to this software.
See http://www.GraphicsMagick.org/www/Copyright.html for details.
Usage: gm command [options ...]

Where commands include: 
    animate - animate a sequence of images
      batch - issue multiple commands in interactive or batch mode
  benchmark - benchmark one of the other commands
    compare - compare two images
  composite - composite images together
    conjure - execute a Magick Scripting Language (MSL) XML script
    convert - convert an image or sequence of images
    display - display an image on a workstation running X
       help - obtain usage message for named command
   identify - describe an image or image sequence
     import - capture an application or X server screen
    mogrify - transform an image or sequence of images
    montage - create a composite image (in a grid) from separate images
       time - time one of the other commands
    version - obtain release version

gmt_shell_functions.sh

GMT 開發者寫了一些簡單的 shell 工具給大家使用,詳細的說明可以到>這邊<查看。

以範例一來說,我們主要會用到的是:

  • gmt_cleanup .gmt :刪除所有含有PID數字($$)的暫存檔,有「參數」時同時刪除「參數*」的檔案。
  • gmt_abort “${0}: First frame plotted to ${name}.ps”:終止程式執行,並將第一個參數輸出至標準錯誤輸出。
  • gmt_set_framename ${name} ${frame}:設定檔案的名字,參數一是前綴(prefix),參數二是流水號,程式會組合成「參數一_XXXXXX」。
  • gmt_set_framenext ${frame}:將流水號加一。
#!/bin/bash
#	$Id: gmt_shell_functions.sh 17326 2016-11-08 20:28:56Z pwessel $
#
# These functions can be used from any sh/bash script by specifying
# . gmt_shell_functions.sh
# in your script. Placing it in .bashrc makes the functions available
# on the command line as well.  See documentation for usage.
#
#----GMT SHELL FUNCTIONS--------------------
#	Creates a unique temp directory and points GMT_TMPDIR to it
gmt_init_tmpdir () {
	export GMT_TMPDIR=`mktemp -d ${TMPDIR:-/tmp}/gmt.XXXXXX`
}

#	Remove the temp directory created by gmt_init_tmpdir
gmt_remove_tmpdir () {
	rm -rf $GMT_TMPDIR
	unset GMT_TMPDIR
}

#	Remove all files and directories in which the current process number is part of the file name
gmt_cleanup() {
	rm -rf *$$*
	if [ $# -eq 1 ]; then
		rm -rf ${1}*
	fi
}

#	Send a message to stderr
gmt_message() {
	echo "$*" >&2
}

#	Print a message to stderr and exit
gmt_abort() {
	echo "$*" >&2
	exit
}

#	Return integer total number of lines in the file(s)
gmt_get_nrecords() {
	cat $* | wc -l | awk '{print $1}'
}
#	Same with backwards compatible name...
gmt_nrecords() {
	cat $* | wc -l | awk '{print $1}'
}

#	Return integer total number of data records in the file(s)
gmt_get_ndatarecords() {
	cat $* | egrep -v '^>|^#' | wc -l | awk '{print $1}'
}

#	Returns the number of fields or arguments
gmt_get_nfields() {
	echo $* | awk '{print NF}'
}
#	Same with backwards compatible name...
gmt_nfields() {
	echo $* | awk '{print NF}'
}

#	Returns the given field (arg 1) in current record (arg 2)
#	Must pass arg 2 inside double quotes to preserve it as one item
gmt_get_field() {
	echo $2 | cut -f$1 -d ' '
}

#	Return w/e/s/n from given table file(s)
#	May also add -Idx/dy to round off answer
gmt_get_region() {
	printf "%s/%s/%s/%s\n" `gmt info -C $* | cut -d'	' -f1-4`
}

#	Return the w/e/s/n from the header in grd file
gmt_get_gridregion() {
	printf "%s/%s/%s/%s\n" `gmt grdinfo -C $* | cut -d'	' -f2-5`
}

# 4 obsolete but backwards compatible functions in use before -W was introduced in mapproject 5.2:
#	Return the current map width (expects -R and -J settings)
gmt_get_map_width() {
	gmt mapproject $* -Ww
}
#	Same with backwards compatible name...
gmt_map_width() {
	gmt mapproject $* -Ww
}

#	Return the current map height (expects -R and -J settings)
gmt_get_map_height() {
	gmt mapproject $* -Wh
}
#	ame with backwards compatible name...
gmt_map_height() {
	gmt mapproject $* -Wh
}

# Make output PostScript file name based on script base name
gmt_set_psfile() {
	echo `basename $1 '.sh'`.ps
}

# Make output PDF file name based on script base name
gmt_set_pdffile() {
	echo `basename $1 '.sh'`.pdf
}

# For animations: Create a lexically increasing file namestem (no extension) based on prefix and frame number
# i.e., prefix_######
gmt_set_framename() {
	echo $1 $2 | awk '{printf "%s_%06d\n", $1, $2}'
}

# For animations: Increment frame counter by one

gmt_set_framenext() {
	echo $(($1 + 1))
}

 

GMT 的範例動畫

GMT 的範例有附了五個動畫,其中很妙的是,要真正畫出動畫來,在呼叫 anim_xx.sh 的時候,一定要傳入至少一個參數,否則只會輸出第一張圖片給你看,至於參數是什麼就不是哪麼重要了。

其他要知道的一些 bash 語法與變數是:

  • $$ :代表執行時的 PID (Process ID),這邊用來指定暫存檔檔名,讓他不會重複。
  • $# :代表有多少變數傳入(類似 C++ main中「argc」)
  • $n (n為整數):代表呼叫Script時傳入的第幾個參數(類似 C++ main中「argv」)
  • while [判斷式] ; do

    done
  • if [判斷式] ; then

    fi
  • printf “文字” 變數:格式化輸出文字
  • <<< 字串:將後面的文字以「字串」的形式輸入標準輸入

設定變數以及一些參數:

首先先設定一些關於動畫的變數,包括影像大小、禎數、檔名等等。

第一行「. gmt_shell_functions.sh」是引入「gmt_shell_functions.sh」這個檔案,就跟C/C++開頭的 #include 相似吧!

這邊也先畫好了一張基底圖

# 1a) Assign movie parameters
. gmt_shell_functions.sh
width=4i
height=2i
dpi=125
n_frames=5
name=anim_01
ps=${name}.ps
# 1b) Do frame-independent calculations and setup
angle_step=`gmt math -Q 360 ${n_frames} DIV =`
angle_inc=`gmt math -Q ${angle_step} 10 DIV =`
gmt psbasemap -R0/360/-1.2/1.6 -JX3.5i/1.65i -P -K -X0.35i \
  -Y0.25i -BWSne+glightgreen -Bxa90g90f30+u\\312 \
  -Bya0.5f0.1g1 --PS_MEDIA=${width}x${height} --FONT_ANNOT_PRIMARY=9p \
  > $$.map.ps

 

繪製變動的部分

這邊開始畫每張變動的部分。首先創造一個暫存資料夾,接著複製底圖,然後針對變動部分繪製內容,最後轉ps檔為圖片並存到暫存資料夾中。

這邊有許多判斷式表示法:[n1 OP n2],比較運算子(OP): -eq:等於-ne:不等於、-lt:小於-le:不大於、-gt:大於-ge:不小於(equal to, not equal to, less than, less than or equal to, greater than, or greater than or equal to)
可以參考這邊

# 2. Main frame loop
mkdir -p $$
frame=0
while [ ${frame} -le ${n_frames} ]; do
	# Create file name using a name_##.tif format
	file=`gmt_set_framename ${name} ${frame}`
	cp -f $$.map.ps $$.ps
	angle=`gmt math -Q ${frame} ${angle_step} MUL =`
	if [ ${frame} -gt 0 ]; then	# First plot has no curves
#		Plot smooth blue curve and dark red dots at all angle steps so far
		gmt math -T0/${angle}/${angle_inc} T SIND = $$.sin.d
		gmt psxy -R -J -O -K -W1p,blue $$.sin.d >> $$.ps
		gmt math -T0/${angle}/${angle_step} T SIND = $$.sin.d
		gmt psxy -R -J -O -K -Sc0.1i -Gdarkred $$.sin.d >> $$.ps
	fi
	#	Plot red dot at current angle and annotate
	sin=`gmt math -Q ${angle} SIND =`
	gmt psxy -R -J -O -K -Sc0.1i -Gred >> $$.ps <<< "${angle} ${sin}"
	printf "0 1.6 a = %03d" ${angle} | gmt pstext -R -J -F+f14p,Helvetica-Bold+jTL -O -K \
		-N -Dj0.1i/0.05i >> $$.ps
	gmt psxy -R -J -O -T >> $$.ps
	[[ ${frame} -eq 0 ]] && cp $$.ps ${ps}
	if [ $# -eq 0 ]; then
		gmt_cleanup .gmt
		gmt_abort "${0}: First frame plotted to ${name}.ps"
	fi
#	RIP to TIFF at specified dpi
	gmt psconvert -E${dpi} -Tt $$.ps
	mv -f $$.tif $$/${file}.tif
	echo "Frame ${file} completed"
	frame=`gmt_set_framenext ${frame}`
done

組合並轉檔

這邊用 GraphicsMagick 進行轉檔,因此 「${GRAPHICSMAGICK-gm}」可以取代成「gm」或 「GraphicsMagick安裝路徑/bin/gm 」。
convert 啟動轉檔模組, -delay 表示延遲時間(1/100 s)、-loop 表示迭代剛剛轉存好的 tiff 檔案、最後一個變數是輸出的位置。

最後寫一個網頁來呈現結果, cat 會把後面到「END」前的文字(html程式碼)存進「${name}.html」中。

# 3. Create animated GIF file and HTML for web page
${GRAPHICSMAGICK-gm} convert -delay 20 -loop 0 $$/${name}_*.tif ${name}.gif
cat << END > ${name}.html
<HTML>
<TITLE>GMT Trigonometry: The sine movie</TITLE>
<BODY bgcolor="#ffffff">
<CENTER>
<H1>GMT Trigonometry: The sine movie</H1>
<IMG src="${name}.gif">
</CENTER>
<HR>
We demonstrate how the sine function <I>y = sin(a)</I> varies with <I>a</I> over
the full 360-degree interval.  We plot a bright red circle at each
new angle, letting previous circles turn dark red.  The underlying
sine curve is sampled at 10 times the frame sampling rate in order to reproduce
a smooth curve.  Our animation uses GraphicsMagick's convert tool to make an animated GIF file
with a 0.2 second pause between frames, set to repeat forever.
<HR>
<I>${name}.sh: Created by ${USER} on `date`</I>
</BODY>
</HTML>
END

清除暫存檔案

# 4. Clean up temporary files
gmt_cleanup .gmt

 

參考資料:

  • GMT 官網與 code :http://gmt.soest.hawaii.edu/doc/5.3.3/gallery/anim01.html#anim-01
  • Bash 說明:http://www.tldp.org/LDP/Bash-Beginners-Guide/html/Bash-Beginners-Guide.html
  • gm convert 官方說明:http://www.graphicsmagick.org/convert.html

請多多指教!

這個網站採用 Akismet 服務減少垃圾留言。進一步瞭解 Akismet 如何處理網站訪客的留言資料