昨天課堂上,我和同學們一起準備期中報告,有些同學富有挑戰精神,決定製作動畫。但是沒有真正跑過,電腦也沒有準備好相關的環境,因此製作就碰到了問題。下面記述一些基本觀念與如何在 Mac 環境下製作 GMT 的範例動畫。
基本概念
用GMT製作動畫大概可以分成以下幾個階段:
- 準備資料(切圖、計算等等)。
- 準備固定、不隨偵數變化的底圖,假設另存為 Base.ps。這邊檔案不能終結。
- 撰寫迴圈,準備繪製「動」畫的部分。
- 複製 Base.ps 檔案到一個暫存檔 tmp.ps。
- 以繪圖指令將變動的部分加入 tmp.ps。
- 將 tmp.ps 轉換為圖片檔(jpg, tiff etc.)
- 組合所有圖片變成 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