ImageMagick 使用例 -- Windows での利用
[ はじめに](#intro)
- 自分の Windows PC で IM スクリプトを使う意義は?
- IM コマンドラインツールの実行環境
- スクリプトを効果的に実行する
- Convert の問題
- 文字エンコーディング
- Windows への ImageMagick のインストール
- 特殊事情と落とし穴
- ヘルプの入手
- バッチファイルでのファイル名の取り扱い
- 複数ファイルのバッチ処理
- ImageMagick からの情報取得
- 計算を実行する
- 編集・デバッグ・実行時エラーのテスト
- 実行時間の最適化
- バッチプログラミングの指針
- 複数ファイルを扱う
- テキストファイルを扱う
- パイプ処理
-
さらなる情報 IM Examples 内のコマンドのほとんどは、もともと LINUX や UNIX のシェルスクリプトを念頭に置いて書かれています。これらのシステムはバッチ処理やネットワークサーバを想定して設計されているからです。しかし、Windows 環境から ImageMagick を使いたいというユーザーがますます増えています。本節では、その環境で IM を使う方法、そしてより重要なこととして、UNIX シェルコマンド(IM Examples の他の部分で使われているもの)をどうやって Windows DOS の同等物へと magick するかについて、詳細と例を示します。原文のノートを全面的に書き直し、Windows ユーザーに特有の関心事を扱う幅広いトピックへと拡張してくださった Wolfgang Hugemann
氏に、特別な感謝を表したいと思います。ここに見られるのは彼の仕事であり、IM ユーザーは彼の時間と忍耐に対して恩義を負っています。
はじめに
自分の Windows PC で IM スクリプトを使う意義は?
以下の例では、基本的にあなたが IM を Windows デスクトップコンピュータ(おそらくネットワークに接続されている)で実行することを前提とします。さて、Adobe Photoshop、Corel の Paint Shop Pro、IrfanView (http://www.irfanview.com/)、さらには GIMP (http://www.gimp.org/) など、すぐに使える画像処理プログラムは数多くあります。では、なぜわざわざ IM のコマンドラインプログラムやスクリプトで画像処理を行うべきなのでしょうか。マウス操作のインターフェースの代わりに ImageMagick を使う本当の利点は、定型的な操作を、単一ファイルに対してもファイルの大量処理に対しても、完全に自動化できる点にあります。たとえば次のようなタスクです:
- 一括フォーマット変換
- これは IrfanView をはじめ、かなり多くの Windows プログラムが提供しています。しかし、画像フォーマットに関する IM の汎用性は群を抜いています。たとえば、PDF の全ページを一連の JPEG へと magick することができます(コンピュータに GhostScript がインストールされていれば)。
- デジタル写真の縮小と前処理
- デジタル写真をワープロ文書に埋め込む際には、文書をより速く印刷できるよう、通常は解像度を下げるべきです。同じことは、FreePDF のような PDF プリンタドライバ経由で文書を PDF へ magick する場合にも当てはまります。前処理には、色補正やレンズ補正のルーチンを含めることもできます。
大量のデジタル写真へロゴを配置する
- 一連の操作を大量のデジタル写真へ適用する
- マウス操作のプログラムを使って一連の作業ステップを練り上げた後、これらのステップを将来の一括処理のために自動化したいと思うかもしれません。しかし、スクリプト言語(Adobe の Action Script など)は Windows の画像処理プログラムではあまり一般的ではありません。
複数の画像を 1 つのカタログ画像にまとめる
これらのタスクのいくつか(特に一括縮小)は他のフリーウェアプログラムでも提供されています(特に IrfanView のバッチ処理)が、どの処理ステップを適用するかについて自由に選べることは決してありません。プログラムが提供するもので我慢するしかないのです。たとえば IrfanView のバッチ処理では、大量の写真へテキスト文字列を配置することはできますが、ロゴはできません。またガンマ値を変更することはできますが、写真のヒストグラムを両端で切り詰めることはできません(IM の "[-contrast-stretch](https://imagemagick.org/command-line-options/#contrast-stretch)" が行うように。Normalize と Contrast Stretch を参照)。IM スクリプトは、会社のネットワークでの実用に特に適しています。既製のスクリプトは誰でも適用でき、エンドユーザーが背後で何が起きているかを必ずしも知る必要がないからです。こうして、画像に対する標準的なワークフローのステップを完全に自動化(そして本当に標準化)できます。以下で紹介するスクリプトのいくつかは、私たちの小さな会社(事故再現の分野で活動)での実用のために作られたものです。私は卓越した Windows スクリプトプログラマでもなければ、IM のコマンドラインツールに最も精通しているわけでもありません。以下で扱う問題のいくつかには、おそらくもっとエレガントなアプローチがあるでしょう。私が言いたい点は次のとおりです:
- IM のコマンドラインツールを使った Windows スクリプトプログラミングの基本的なテクニックをいくつか示すこと。
- IM ベースのスクリプトの利用が、芸術のための芸術でも学究的な暇つぶしでもないことを証明すること。
- IM のコマンドラインツールが(Web サーバ上だけでなく)ローカルネットワークで実際の仕事をこなせることを示すこと。
IM の他の Example ページと同様、ここでも IM のコマンドラインツールのみを使い、さまざまなプログラミングインターフェースは脇に置きます。スクリプトは、各ネットワークドライブにドライブ文字が割り当てられたローカルネットワーク内で実行されることを想定しています。スクリプトのほとんどはそのネットワークのクライアントコンピュータで実行されることを意図しており、ネットワーク(ファイル)サーバを対象とするものはわずかです。
IM コマンドラインツールの実行環境
Windows では、単純な IM コマンドは通常 Windows コマンドシェル(cmd.exe を起動して動かす「DOS シェル」)で実行します。長いコマンドラインや一連のコマンドラインで実行する複雑な操作には、スクリプトを書いたほうがよいでしょう。単純なコマンドの連なりであれば、それはおそらく Windows コマンドシェルで実行される DOS バッチファイルになります。しかしこの方法には欠点があります。バッチファイルのコマンドセットは、一般的な UNIX コマンドシェルのものと比べてかなり限定的だからです。Windows で IM を実行する場合、基本的に次の選択肢があります:
- Windows コマンドシェル(「DOS ウィンドウ」)
- Windows NT 4.0、Windows XP 以降では
cmd.exe(32 ビットモード)で実行され、どの Windows コンピュータにも存在します。DOS シェルとバッチファイルを使う を、また Convert の問題 についての特別な注記を参照してください。 - Cygwin
- bash ライクなコマンドシェル (http://www.cygwin.com/)。このシェルを使えば、本使用例セクションの他の部分で示す IM の例を、書かれたとおりそのまま実行できます。UNIX 風のコマンドラインシェルが利用できるからです。下記の Cygwin を使う を参照してください。
- Windows Script Host
- Windows Script Host は .COM 技術に基づいています。現代の Windows コンピュータにはどれにも存在し、WSH スクリプトは単純な DOS バッチファイルよりもずっと強力です。Windows Script Host は複数のプログラミングインターフェースを提供しており、VBScript (Visual Basic Script) と JScript (Java Script) が最も一般的です。IM のコマンドラインツールは、Shell オブジェクトの DOS シェルコマンド
RunやExecを使って呼び出せます。下記の Visual Basic Script (VBS) を参照してください。 - Windows PowerShell
- 古い DOS シェルのはるかに強力な後継で、.NET 2.0 技術に基づいています。PowerShell は Windows 7 に同梱され、
powershell.exeで実行します。Windows XP と Vista 向けには Microsoft の Web サイトでダウンロードできます。
スクリプトを効果的に実行する
完璧な Windows スクリプト(DOS バッチファイル、VBScript、その他何でも)があり、それが入力ファイルの名前をコマンドラインパラメータとして受け取り、何らかの操作を行って、結果を 1 枚の画像または一連の画像として吐き出すとしましょう。毎回 DOS コマンドシェル(や代替手段)を起動して、ファイル名を打ち込んでスクリプトに渡すのは、きっと嫌でしょう。このような面倒な手順を避けるには、基本的に ドラッグ&ドロップ または SendTo を使えます。ドラッグ&ドロップ を使う場合、DOS バッチファイルや VBScript(その他)を、デスクトップや処理対象の画像ファイルを置いているディレクトリなど、すぐにアクセスできる場所に置きます。そして Windows エクスプローラで処理対象のファイルを選択し、それらをスクリプトファイルの上にドロップするだけです。ファイル名はコマンドラインパラメータとしてスクリプトに渡され、スクリプト内で参照できます。代替として、スクリプト(またはそのリンク)を SendTo フォルダに置くこともできます。このフォルダ内のプログラムは、エクスプローラのファイルペインで右クリックしたときに Windows エクスプローラのコンテキストメニューに現れます。この場合も、選択されたファイルの名前がコマンドラインパラメータとしてスクリプトに渡されます。SendTo フォルダは SendTo という名前です。その場所は Windows のバージョンが変わるたびに移動するようです。確実に見つける方法は、ファイル名を指定して実行するボックスに shell:sendto と打ち込むことです。Windows XP 以降では、1 つのコマンドラインは 8192 (= 2¹³) 文字まで可能です。したがって IM ツールをコマンドラインから直接、あるいは必要なすべてのファイルを直接名指しするバッチファイル経由で呼び出すなら、この制限にぶつかることはまずありません。しかしドラッグ&ドロップは ExecShell ルーチンを使い、これは「わずか」2048 (= 2¹¹) 文字の文字列に制限されています。すべてのファイルは完全修飾ファイル名(すなわちドライブ + パス + 名前)で渡されるため、パス名が長くなりすぎると、WSH 経由で実行されるバッチファイルや VBScript にとって問題になり得ます。このエラーは発生してしまうとスクリプト側で適切に処理できません。なぜならエラーは実際にスクリプトが実行される 前 に起きるからです。Windows XP での解決策は通常、パス名がより短い場所にファイルを置くことです。
Convert の問題
IM の Windows インストールルーチンは、IM のプログラムディレクトリを検索パスに追加するため、パス名を指定せずにコマンドプロンプトから IM のコマンドラインツールを直接呼び出せます。既定では magick をコマンドラインツールとして使うことになります。しかし旧来のプログラム名を選んだ場合、IM のコマンドラインツールの名前はかなり一般的なもの(例: convert、identify、compare ...)であり、他のプログラムとの名前衝突を引き起こします。特に convert は Windows のシステムツールで、Windows システムディレクトリ (c:\Windows\system32\convert.exe) にあり、FAT32 ファイルシステムを今では一般的な NTFS へ変換します。しかし "convert.exe" という名前の他のプログラムも存在します。たとえば Delphi のレポート変換ユーティリティです。FAT → NTFS の magick ツールは Windows XP で初めて同梱され、IM のコマンドラインツール "convert" との名前衝突を生みました。IM のプログラムディレクトリが DOS 検索パス(すなわち PATH 環境変数)に 末尾追加 されたため、システムツールのほうが先に見つかり、古いスクリプト内の単純な "magick" 呼び出しが正しく解決されませんでした。しかし現行バージョンの IM Windows セットアッププログラムは、IM のプログラムディレクトリを検索パスの先頭に置き、名前が衝突する場合でも IM のコマンドラインツールが通常先に見つかるようにしています。とはいえ、同名の他のユーティリティ(例: Delphi のレポート変換ツール)も同じ問題にぶつかり、同じ解決策、すなわち自分のプログラムパスを path 変数の先頭に置く方法を採りました。つまりこの解決策は万全ではありません。Delphi が IM の後にインストールされると、Convert の単純な呼び出しは IM の Convert ではなくレポート変換ツールを起動してしまいます。Windows XP での Convert ツールの導入は、多くの旧来のスクリプトをクラッシュさせました。一般的な解決策は、IM の Convert ツールを "IMconvert" などの別の名前にリネームすることでした(システムコマンドはリネームできないことに注意。次の Windows サービスパックがおそらく単に元に戻し、リネーム版を無視するためです)。この解決策は、今では不要ですが、インターネット中で今なお見つかります。将来あり得る名前衝突を避ける最良の解決策は、どのスクリプトでも IM のコマンドラインツールをフルパス名で呼び出すことです。すなわち、その場所を変数や定数に格納します。したがって、すべてのバッチファイルは次のような行で始めるべきです
SETLOCAL EnableDelayedExpansion
SET IMCONV="%PROGRAMFILES%\ImageMagick\Convert"
...
%IMCONV% -size 128x128 xc:white test.gif
このコードは、IM がプログラムフォルダ下の "ImageMagick" という名前のフォルダにインストールされたことを前提としていますが、これはインストールフォルダの標準的な名前付けでは ありません。(詳細は Windows への ImageMagick のインストール を参照。)%PROGRAMFILES% は環境変数で、プログラムディレクトリの名前、すなわち英語版 Windows では "Program Files"、ドイツ語版 Windows では "Programme" に展開されます。SETLOCAL は、新しい環境変数(IMCONV など)の定義をバッチファイルのスコープに限定します。EnableDelayedExpansion はここでは実際には必要ありませんが、SETLOCAL を使うたびにこのオプションを使うのは良い習慣です。詳細は バッチプログラミングの指針 を参照してください。IM のインストールフォルダを突き止めるための、より洗練された万全な方法があり、それは 編集・デバッグ・実行時エラーのテスト で扱います。同等の VBScript コードは次のようなものになります
Set wsh = WScript.CreateObject("WScript.Shell")
IMconv = wsh.ExpandEnvironmentStrings("%PROGRAMFILES%") & "\ImageMagick\Convert"
簡潔さのため、以下では毎回このコードを使うことはしませんが、良い習慣として頭に留めておいてください。この問題に対する良い要約と解決策については Ron Savage: MS Windows and convert.exe を参照してください。
文字エンコーディング
ImageMagick は文字列を Unicode、より正確には UTF-8 でエンコードします。これに対して、DOS は文字をコードページ(多くは 8 ビット)でエンコードします。これは、'label' や '-title' を扱うときなど、画像に文字列を書き込む際に問題を生みます。非 ASCII 文字を使うと、単純なやり方ではうまくいきません。たとえばドイツ語のウムラウト 'ä'、'ö'、'ü' のラベルを作ろうとする場合、Linux では次のように簡単に使えます…
magick label:äöü test.png
しかし Windows ではこれは望みどおりの文字列を作りません。ただし、テキストファイルから UTF-8 エンコードの文字列を読み込むことはできます:
magick label:umlauts.txt test.png
さらに、事前にコードページを UTF-8 に切り替えておけば、"echo" を使ってこのテキストファイルを作ることもできます:
CHCP 65001
ECHO äöü > umlauts.txt
magick label:umlauts.txt test.png
しかし DOS コマンドの出力を処理したい場合、たとえばあるディレクトリに含まれる JPEG のインデックスプリントにそのディレクトリ名でタイトルを付けようとすると、困ったことになります。コードページ 65001 への切り替えは、特にディレクトリツリーをループする場合、ほとんどの DOS コマンドではうまくいきません。そして、たとえば 1252(西ヨーロッパラテン)と 65001 との間でコードページを行き来して切り替えても、通常はうまくいかないか、少なくともかなり厄介になります。最も安全なアプローチは、文字列が必要になったときに、外部のコマンドラインプログラム、たとえば "Iconv.exe"(Windows でも利用できる UNIX ツール)を使って magick することです。セットアップファイルを SourgeForge からダウンロードし、標準ディレクトリ C:\Program Files\GnuWin32 にファイルをインストールしてください。そして、スクリプト内で DOS コマンドの出力をテキストファイルにダンプし、そのファイルを次のように UTF-8 へ magick します:
CHCP 1252
DIR /B äöü.jpg > temp.txt
"C:\Program Files\Gnuwin32\bin\iconv.exe" -f ISO-8859-1 -t UTF-8 temp.txt > title.txt
magick label:@title.txt äöü.jpg -append äöü_labelled.jpg
パラメータは Iconv に、ISO-8859-1(コードページ 1252)から UTF-8 へ変換するよう指示しています。Iconv は出力を stdout に書き込むので、'label' で使うにはそれをファイルへリダイレクトする必要があります。なお、いずれにせよ出力をテキストファイルにダンプするのは推奨されます。なぜなら、それは ImageMagick にファイルの内容を文字どおりに解釈するよう伝え、Windows のバックスラッシュ ("\") をエスケープ文字と取らないようにするからです。もちろん上記の例のコードは、ここでは実演目的で示しているので、それ自体としてはあまり意味がありません。実用的な DOS バッチファイルでは、ファイル名はおそらく FOR ループの中で生成されるでしょう。実用例は後述します((サブ)ディレクトリツリーのバッチ処理 を参照)。UTF の扱いについては、他の IM の例や Unicode または UTF8 形式のテキスト処理 の情報を参照してください。
Windows への ImageMagick のインストール
ImageMagick は絶えず開発が進められており、新しいバージョンがおよそ月に一度の頻度でリリースされます。特に IM が期待どおりに仕事をこなさないように見えるときは、最新版の IM を使うことが強く推奨されます。多くの場合、現行バージョンをインストールすれば問題は解決します。現行のバイナリリリースのセットアッププログラムは https://imagemagick.org/script/binary-releases.php#windows で入手できます。HDRI(浮動小数点品質)および FFT(高速フーリエ変換)対応版の ImageMagick は Astronomy and Astrophotography Blog からもダウンロードできます。既定では、IM は C:\Program Files\ImageMagick x.x.x-x というプログラムディレクトリに自分自身をインストールします。ここで "x.x.x-x" はバージョン番号を表します。既定では、IM を初めてインストールするとき、セットアッププログラムは PATH 環境変数を拡張するよう提案します(すなわち「アプリケーションパスをシステムパスに追加する」がチェックされています)。古いバージョンのアンインストールを忘れると、対応する PATH 拡張を伴うさまざまな ImageMagick バージョンの楽しいコレクションがあっという間にできあがります。そこで私たちのオフィスでは、IM を C:\Program Files\ImageMagick にインストールし、新しいバージョンを古いものの上にそのまま入れ、PATH 環境変数には手を付けない習慣を採りました。何年もの使用において、このやり方に何ら問題は見つかりませんでした。IM のバージョン番号を本当に知りたければ、いつでも "convert -version" を呼び出せますし、特定の最小バージョン番号に依存するスクリプトであれば、間違いの起こりにくいスクリプトがバージョン番号を評価できます。詳細は「編集・デバッグ・実行時エラーのテスト」の節を参照してください。ImageMagick はいくつかのレジストリキーを HKEY Local Machine\Software\ImageMagick に書き込みます。複数のインストール済み IM バージョンを扱う場合、最も重要なキーは HKEY Local Machine\Software\ImageMagick\Current で、ここには BinPath という IM のバイナリへのパスも見つかります。任意のスクリプトの先頭でこのレジストリエントリを照会すれば、PATH 環境変数に頼らずにプログラムパスを判定できます。詳細は「編集・デバッグ・実行時エラーのテスト」の節を参照してください。IM のセットアッププログラムは、Install ImageMagickObject OLE Control for VBscript, Visual Basic, and WSH というオプションで、ImageMagick 用の COM+ オブジェクトをインストールするオプションを提供します。このオプションは既定ではチェックされておらず、COM+ オブジェクトのインストールは、以下で証明するように、VBScript で IM を使うための 前提条件ではありません。いずれにせよ、自分自身のもの以外のスクリプト対象マシンに COM+ オブジェクトがインストールされていることを当てにすべきではありません。PostScript ファイルを扱う場合、ImageMagick は PostScript や PDF ファイルを自身が使える画像フォーマットへ読み込み・変換するために、別のプログラム "Ghostscript" に依存します。すなわち、そうした文書を読むには、コンピュータに Ghostscript がインストールされている必要があります。その最新版は SourceForge でダウンロードできます。GhostScript と ImageMagick をインストールする順序は問題になりません。ImageMagick より前に GhostScript をインストールする必要はなく、ImageMagick はそれがインストールされていなくても問題なく動作します。それが必要になるのは、PostScript や PDF ファイルを扱いたい場合だけです。IM は実行時に Ghostscript の場所を判定し、レジストリから照会します。
特殊事情と落とし穴
Windows のプログラムとしてはかなり例外的に、IM は画像を stdout へ書き出し、stdin から読み込むことを許し、こうしてパイプを使って画像処理タスクを連鎖させることができます。次の操作
magick -size 128x128 xc:white gif:- | magick - test.gif
は次と同等です
magick -size 128x128 xc:white test.gif
パイプの最初のコマンドでは、マイナス演算子が IM に画像を stdout へ書き出すよう伝え、パイプの 2 番目のコマンドのマイナス演算子は IM に画像を stdin から読み込むよう伝えます。この進め方により、一時ファイルを明示的に使うのを避けられます。あるコマンドが特定の操作、たとえばトリミングを提供しない場合に特に便利です:
montage -tile 2x2 -geometry 400x300+60+60 1.png 2.png 3.png 4.png miff:- | magick - -trim montage.png
この機能の結果として、テキスト出力は通常 stdout ではなく stderr に書き込まれます。たとえば、Compare のテキスト出力をテキストファイルにリダイレクトしたい場合は、次のように書く必要があります
magick compare -metric PSNR 1.png 2.png dif_1_2.png 2>result.txt
つまり、stdout("1>" または単に ">")ではなく stderr("2>")をテキストファイルへリダイレクトしなければなりません。
ヘルプの入手
コマンドラインのオプションは IM の Web サイト に詳しく文書化されていますが、それらを実際に動かす方法を本当に示しているのは Web サイトの Usage セクション です。Web サイトのこのセクションは非常によく構造化されており、行いたいタスクに対して問題指向のアプローチが取れます。しかし手っ取り早いやり方は、念頭に置いているコマンドラインオプションについて、Web サイトのそのセクションで Google 検索することです。複数の画像のモンタージュを行いたくて -tile オプションについて知りたいなら、次のいずれかで Google 検索できます
そうすれば要点をすぐに見つけられます。ただし、これは UNIX / LINUX 環境での適用を意図したコードを明らかにするものであり、Windows で適用するには少し調整が必要なことを覚えておいてください。もう 1 つの非常に役立つ情報源は IM のディスカッションボード、すなわち Discourse Server User Forum で、ブラウザのブックマーク/お気に入りに入れておくべきです。メンバーになれば質問を投稿でき、その多くは博識なユーザーによってすぐに答えられます。
補助プログラムと代替手段
はい、本当です。ImageMagick よりもうまくこなせる仕事が他のプログラムには 確かに あります。典型的には、ImageMagick が提供する汎用的な画像操作ではなく、特定のフォーマットを念頭に設計されたものです。
- IrfanView はおそらく Windows で最も一般的な画像ビューアで、いくつかの基本的な画像操作も可能です。
- Adobe Photoshop や Gimp のような GUI プログラムは、複雑な画像操作ステップの直接編集やテストにより適しています。
- デジタル写真の EXIF ヘッダの操作には、Jhead や ExifTool のほうが ImageMagick より多機能です。
- PDF からの JPEG ストリームの抽出は xPDF で行うべきです。
- 動画処理は VirtualDub で行うのがよく、AviSynth とそのエディタ AvsP との組み合わせが最良です。
これは、こうした画像作業で ImageMagick を無視すべきだという意味ではありません。しかし、それらを組み合わせればもっと多くのことができます。
Cygwin を使う
上で述べたように、ImageMagick は UNIX や Linux を念頭に設計されているので、おそらく最も簡単なアプローチは、Windows システムに Bash シェルをインストールし、そのシステム向けにすでに書かれた多様な IM スクリプト、たとえば Fred Weinhaus 氏のスクリプト を実行することです。Cygwin は、開発者の言葉を借りれば「Windows 向けの Linux ライクな環境」を提供します。通常 Linux シェルで利用できるすべてのツールから構成されています。私は Fred Weinhaus 氏の bash スクリプトのいくつかを Cygwin の Bash シェルでテストし、完全に機能することを確認しました。Cygwin Web サイトのルートページの下部に、Install or update now! というラベルのリンクがあり、これは Setup.exe という名前のインストール用スタブをダウンロードします。このプログラムを起動すると、サイトミラーの一覧が提示されます。そのうちの 1 つを選ぶと、ルーチンがインストールしようとするツールのツリービューを提示します。標準の選択は私には妥当に思えるので、そのまま進めてよいでしょう。インストールルーチンはその後、必要なパッケージをダウンロードし、Cygwin をコンピュータにインストールします。 | インストール時には、既定では有効になっていない "bc" パッケージを必ず含めてください。Cygwin のインストールパネルからオプションとしてインストールできます。これは浮動小数点演算ユーティリティで、Fred Weinhaus 氏が使うような IM スクリプトでは不可欠になり得ます。
Cygwin を起動すると、その Bash シェルは DOS ボックス、つまり黒い背景のテキストウィンドウとよく似て見えます。DOS ウィンドウと同様、フォントはタイトルバー左にあるウィンドウのシステムメニューをクリックして選べます。基本的なコマンドは次のとおりです:
- カレントディレクトリは
CDコマンドで変更し、おおむね DOS と同様です。ただし、バックスラッシュ ("\") はスラッシュ ("/") に置き換える必要があります。ドライブも DOS と同様にCDで変更します。たとえばCD w:はドライブ w: へ切り替えます。通常の Unix 環境とは異なり、パス名は大文字・小文字を区別しません。ウムラウトなどの特殊文字を使えます。一部のコマンドでは、コロンを避ける特別な構文でドライブ名を渡す必要があります。たとえば/cygdrive/w/testです。 - Cygwin は Windows の PATH 環境変数を読み、それに応じて自身の PATH を設定します。Bash シェルに
echo $PATHと打ち込んで確認できます。注意: Windows とは異なり、環境変数の名前は大文字・小文字を区別するので、PATH を参照するときは大文字を使う必要があります。 - Windows とは異なり、既定ではカレントディレクトリは検索パスに 含まれません! たとえばシェルスクリプト "autolevel1" が
W:\scriptsにある場合、シェルスクリプトを呼び出すときにそのディレクトリへ切り替えるだけではいけません。スクリプトの先頭に少なくとも最小限のディレクトリ位置を付ける必要があります。たとえば./autolevel1(カレントディレクトリにあるスクリプトの場合)のように。あるいはスクリプトの完全なパス、たとえば/cygdrive/w/scriptsのように。 - あるいは、
PATH=$PATH:/cygdrive/w/scriptsを使ってそのスクリプトディレクトリを明示的に検索パスに追加できます。コロンはパスの区切りとして使われるので、w:/scriptsではなく/cygdrive/w/scriptsのような、この特別な記法を使う必要があります。
Cygwin シェルについてのこの説明は、このページの将来のバージョンで拡張される予定です。今のところ、上記の情報で始めるには十分なはずです。
DOS シェルとバッチファイルを使う
スクリプトの変換: UNIX シェルから Windows DOS へ
IM コマンドを DOS コマンドシェル(cmd.exe を使用)から直接呼び出す場合、IM の Example サイトに示されたスクリプト(本節由来でないもの)を変更する必要があります。(他のセクションで)提供される例のほとんどは、一般に UNIX や LINUX のコマンドシェルで実行することを意図しているからです。DOS コマンドシェルから実行するには、次の変更を行う必要があります:
- たいてい、コマンド引数が正しく保たれるよう、単一引用符 '
'' の代わりに二重引用符 '"' を使う必要があります。[-draw](https://imagemagick.org/command-line-options/#draw)演算子のような引用符の中の引用符に注意してください。DOS の二重引用符で囲まれた引数の中では単一引用符を使えます。これらはスクリプトで処理されず、処理のため IM に渡されるからです。 - 示した例の行末に現れるバックスラッシュ '
\' は「行継続」を表し、次の行を同じコマンドラインのシーケンスに付け加えます。DOS バッチファイルで行継続を表すには、これらを '^' 文字に置き換えてください。DOS では次の行はスペースで始める必要もありますが、それはごく標準的な慣行なので、大した問題にはならないはずです。すべての行を 1 行につなげてバックスラッシュを取り除くこともできますが、これはコマンドの編集を、そして後で読むことをずっと難しくします。そのため、この慣行は推奨されません。 - 二重引用符の中にない予約済みシェル文字はすべて、字義どおりの意味で使う場合(すなわち通常の目的を果たさない場合)、'
^'(キャレットまたはサーカムフレックス)でエスケープしなければなりません。これらの予約済みシェル文字は: '&'、'|'、'('、')'、'<'、'>'、'^' です。これは特に次のことを意味します:- 特殊文字 '
>'(リサイズのジオメトリで使われる)は '^' を使ってエスケープする必要があります。たとえば "-resize 100x100^>"。 - 同様に「内側に収めるリサイズ」フラグ '
^' は二重にして '^^' にする必要があります。
- 特殊文字 '
- UNIX シェルのエスケープ用バックスラッシュ '
\' は、括弧 '()' や感嘆符 '!' をエスケープするのに必要ありません。 - それ以外の場合、UNIX シェルのエスケープ用バックスラッシュ '
\' は、'<' や '>' のような文字をエスケープするときに、サーカムフレックス '^' 文字に置き換える必要があります。
たとえば: "-resize 100x100\>" は "-resize 100x100^>" になります。 - DOS バッチファイルでは、パーセント '
%' 文字も、コマンドラインパラメータを参照するため特別な意味を持ちます。たとえば%1, %2, ...(UNIX シェルでは同じ一般的な意味で '$' 記号が使われます)。DOS バッチファイルでは、("FORコマンド" に現れるような)単一のパーセント記号を二重にして '%%' にする必要があります。ImageMagick 自体は通常、パーセント記号があるかどうかを見るだけで、複数与えられていても気にしません。したがって、テキストのラベルやコメント文字列の一部でない限り、すべてのパーセント記号を二重にしても一般に害はありません。 - Windows のファイル名にはスペース文字を含められることを覚えておいてください。スペースは UNIX でも使えますが、あまり一般的ではありません。スペースを含み得るそのようなファイル名は、二重引用符で囲む必要があります
"file name.jpg"または"file name".jpg。ドラッグ&ドロップや SendTo を通じてコマンドラインパラメータとしてスクリプトやバッチファイルに渡されるファイル名は特別な注意が必要です。スペース文字を含まなければ引用符なしで、含む場合は引用符付きで渡されるからです。 - UNIX シェルスクリプトのコメントは、行のどこにあっても引用符で囲まれていない '
#' で始まり、行末まで続きます。色の設定(赤色を表す "#FF0000" など)は、この特別な意味を取り除くためにしばしば引用符で囲まれます。この引用は DOS では不要ですが、その周りに二重引用符 '"' を付けても問題なく、そのまま残しておくべきです。DOS では、コメントは 'REM'、'@REM'、'::' の接頭辞を使って行頭にのみ現れます。これらも行末まで続きます。どのコメント方法を使うかはあなた次第です。しかし、どのバッチファイルでも良いコメントは常に推奨されます。数か月後や数年後にスクリプトを見返したとき、各ステップでコマンドが何をしようとしているかが分かるからです。他人が理解するのもずっと容易になります。すべてのスクリプトは、そのスクリプトが何をするか、どう使うべきかを説明する、より大きなコメントで始めるべきです。これはまさに良いプログラミングの慣行です。 - DOS バッチファイルを実行する場合、既定では個々のコマンドがエコーされます。すなわち、コマンドが出力 DOS ボックスに表示されます。UNIX ではむしろ、このように実行中のコマンドを表示するには特別なコマンドやオプションを加える必要があります。スクリプトを "
@ECHO OFF" で始めることで、この「エコー」出力をオフにできます。UNIX シェルスクリプトの特別な先頭コメント "#!/path/to/shell" は DOS バッチファイルでは不要です。したがって、この行は DOS バッチファイルでは "@ECHO OFF" コマンドに置き換えられます。
たとえば、上記の規則に従うと、この UNIX シェルスクリプト…
#!/bin/sh
# Create a negated rose image and overlay a comment
magick -background none -fill red -gravity center \
-font Candice -size 140x92 caption:'A Rose by any Name' \
\( rose: -negate -resize 200% \) +swap -composite \
output.gif
は、Windows DOS バッチファイルとして次のようなものになります…
@ECHO OFF
:: Create a negated rose image and overlay a comment
magick -background none -fill red -gravity center ^
-font "C:\path\to the\font\candice.ttf" ^
-size 140x92 caption:"A Rose by any Name" ^
( rose: -negate -resize 200%% ) +swap -composite ^
C:\where\to\save\output.gif
私は SED(Streaming EDitor)を使って、基本的な Linux シェルから DOS バッチファイルへのコンバータを書きました。SED は一般的な UNIX / Linux のテキストファイル操作プログラムで、http://sed.sourceforge.net/ で Windows 向けにも入手できます。IM がコマンド駆動の画像操作プログラムであるのと同様、SED はコマンド駆動のエディタです。必要な操作を行う SED スクリプト cim.txt は、(コメントを取り除くと)次のようになります:
s/'/\"/g
s/%/%%/g
s/\\\([()!]\)/\1/g
s/\([&|<>]\)/^\1/g
s/^[ ]*#/::/
s/\(^.*\)\( #.*$\)/\2\n\1/
s/\(.:.*\.[a-z,A-Z]*\)[ ]/\"\1\" /g
s/\\[ ]*$/^/
s/^[ ][ ]//
完全にコメントを付けたバージョンのファイル sed_script.zip をダウンロードできます。変換対象の Linux シェルスクリプトと同じフォルダに SED スクリプト cim.txt を置けば、次のように変換を実行できます:
%programfiles%\GnuWin32\bin\SED -f cim.txt linux.scr > windows.bat
次のバッチファイルを使って、SendTo やドラッグ&ドロップ経由で変換を実行することもできます:
SET SP=%programfiles%\GnuWin32\bin
%SP%\SED -f %SP%\cim.txt "%~1"> "%~dpn1.bat"
このバッチファイルは、SED スクリプト cim.txt を SED のプログラムフォルダ内に置いたことを前提とします。Linux シェルスクリプトのファイル名を唯一のコマンドラインパラメータとして受け取り、同じフォルダに同じ名前で拡張子 '.bat' のバッチファイルを生成します。(謎めいたファイル名操作 "%~dpn1.bat" は次の節で説明します。)注意: 上記の SED スクリプトは、上で述べた初歩的な置換のみを行います。洗練された Linux シェルスクリプト(Fred Weinhaus 氏の Web サイト に示されたものなど)を同等のバッチファイルへ変換することは しません!
バッチファイルでのファイル名の取り扱い
上で述べたように、IM は標準的な一連の処理ステップを画像ファイルに適用する場合に特に役立ちます。そのような場合、ファイル名はドラッグ&ドロップまたは SendTo を通じてコマンドラインパラメータとしてスクリプトに渡されます。これらのテクニックを使うと、DOS バッチファイルに渡されるファイル名は完全修飾ファイル名、すなわちドライブ名とディレクトリパスを含むものになります。次のバッチファイルにファイルをドロップしてこれを試せます:
ECHO Filename: %1
PAUSE
PAUSE 文があるため、DOS ボックスはユーザーがキーを押すまで開いたままになり、結果を確認できます。スペースを含むファイル名で上記を試すと、ファイル名が二重引用符で囲まれることに気付くでしょう。ここおよび以下のすべての例では、ネットワーク上のファイルにはドライブ文字が割り当てられていることを前提とします。実際問題として、ローカルの商用ネットワークで違う状況を見たことはありません。バッチファイルを扱うときは、UNC 名を扱おうとすべきではありません。バッチを動かせるかもしれませんが、不必要な厄介ごとをたくさん引き起こします。このファイル名を Convert のコマンドラインで使う場合、この挙動は問題を引き起こす可能性があります。他の任意のフォーマットから JPEG への単純な変換を行ってみましょう。最も基本的なコードは次のようになります:
magick %1 %1.jpg
PAUSE
これは同じディレクトリに、追加の ".jpg" 拡張子が付いた JPEG ファイル(標準の品質と解像度)を生成します。上記のコードは、スペースを含むか含まないかにかかわらず、どんなファイル名でも動作します。元の拡張子を取り除きたい場合、少々厄介になります:
magick %1 "%~dpn1.jpg"
PAUSE
上記のバッチファイルは ~ 演算子を使ってファイル名を操作します: %~1 |
%1 を展開し、周囲の引用符 (") を取り除く |
|---|---|
| %~f1 | %1 を完全修飾パス名へ展開する |
| %~d1 | %1 をドライブ文字のみへ展開する |
| %~p1 | %1 をパスのみへ展開する |
| %~n1 | %1 をファイル名のみへ展開する |
| %~x1 | %1 をファイル拡張子のみへ展開する |
これらの修飾子は組み合わせられるので、"%~dpn1" は「ドライブ + パス + 拡張子と囲み引用符を除いた名前」を意味します。したがって、スペースを含むファイル名でもコードが動作するよう、名前を二重引用符で囲む必要があります。(スペースを含まなくても、引用符は害になりません。)PAUSE 文はテスト目的のみで、最終的なバッチファイルでは省けます。IM を実際に呼び出さずにコードをテストしたいだけなら、次のように書くべきです: |
ECHO magick %1 "%~dpn1.jpg"
PAUSE
複数ファイルのバッチ処理
FOR ループ
DOS の FOR コマンドは、UNIX とよく似たやり方で一連のファイルを処理するのに使えます。カレントディレクトリのすべての JPEG ファイルを 50% に縮小するには、DOS ボックスに次の行を打ち込めます:
FOR %a in (*.jpg) DO magick %a -resize 50% small_%a
パーセント記号は二重に なっていない ことに注意してください。しかしこのコマンドをバッチファイルに置く場合は、次のものに置き換える必要があります
FOR %%a in (*.jpg) DO magick %%a -resize 50%% small_%%a
ここでも、このバルク操作をドラッグ&ドロップや SendTo で実行し、完全修飾ファイル名(やフォルダ名)を、おそらく別のディレクトリ(shell:sendto など)にある DOS バッチファイルに渡すのが便利です。この場合、最初のステップでファイルのディレクトリをカレントディレクトリにすべきです:
%~d1
CD "%~p1"
MD small
FOR %%a in (*.jpg) DO magick %%a -resize 50%% small\%%a
PAUSE
このバッチファイルでは次のことを行っています
- ドライブ名(d: など)を与えてドライブを変更する
- ファイルのフォルダをカレントディレクトリにする
- "small" という名前のサブディレクトリを作る
- すべての JPEG ファイルを 50% に縮小し、これらの縮小版を新しいサブディレクトリに置く
注意: チルダ (~) は、コマンドラインパラメータとして渡されたファイル名から、囲み得る引用符を取り除くので、通常そのような操作の結果を再び引用符で囲む必要があります。そのため上の例では CD "%~p1" と書きました。CD コマンドの場合、囲み引用符を省くこともできます。このコマンドはパラメータを 1 つしか受け取らないので、囲み引用符なしでも空白を扱えるからです。最初のステップでファイルのディレクトリをカレントディレクトリにすると、ファイル名への参照が少し容易で短くなるので、後続のステップが少し楽になります。しかし、次のように書くこともできました:
MD "%~dp1small"
FOR %%a in ("%~dp1*.jpg") DO magick "%%a" -resize 50%% "%%~dpasmall\%%~nxa"
PAUSE
これは少し短くなるかもしれませんが、ずっと厄介でもあります。ファイル名修飾子は For ループ変数 %%a に対しても機能することに注意してください。注意その 2: 末尾のバックスラッシュはパス名の一部です。したがって "%~dp1\small" ではなく "%~dp1small" でなければならず、これはコードを、特に "%%~dpasmall\%%~nxa" の場合に、より読みやすくするわけではありません。FOR 文にはいくつかの欠点と注意点があります。その 1 つは、DO の後で基本的に 1 つのコマンドしか実行できないことです。しかし一連の DOS コマンドを括弧 "(", ")" でグループ化し、それによって単純なコマンドのシーケンスを実行できます:
@ECHO OFF
:: Lighten darker areas of all images in a directory
%~d1
CD "%~p1"
FOR %%a in (*.jpg) DO (
ECHO Processing file: "%%~nxa"
magick %%a -blur 30 -negate %%a.miff
magick composite %%a.miff %%a -compose overlay "%%~dpn1_light"%%~xa
DEL %%a.miff
)
PAUSE
このバッチファイルは、コマンドライン引数として渡されたディレクトリにあるすべての画像を処理します。まず元の画像をぼかして反転し、中間結果を追加の ".miff" 拡張子を持つファイルに保存します。次に、元の画像をこの加工版の上に重ね合わせ、それによって元の画像の暗い部分を明るくします。最後に中間画像を削除します。上記では、単純なコマンドのシーケンス に重きが置かれていることに注意してください: ブロック内で GOTO ジャンプを使うことはできません。そのような挙動が必要なら、FOR ループから別のバッチファイルを呼び出す必要があります:
%~d1
CD %~p1
MD small
FOR %%a in (*.jpg) DO CALL "%~dp0process" "%%~fa"
ここで "process.bat" は実際の作業を行うバッチファイルで、呼び出し側のバッチファイルと同じディレクトリにあります。コマンドラインパラメータ 0 ("%0") はバッチファイル自身の名前なので、"%~dp0process" は同じディレクトリのバッチファイル process.bat を呼び出します。FOR 文はファイル名だけを提供し、それが "%~fa" によって完全修飾ファイル名に変えられます。今回の場合、バッチファイル process.bat 内のコードは、上の例で括弧の間に置かれたものと同じになります:
magick %%1 -blur 30 -negate %%1.miff
magick composite %%1.miff %%1 -compose overlay "%%~dpn1_light"%%~x1
DEL %%1.miff
しかし、別個のバッチファイルを使うと、DOS バッチファイルの(限られた)すべての可能性が得られます。この例では違いはありませんが、後でこのアプローチの利点を示します。2 つのバッチファイルを扱うのが面倒なら、スクリプトに 2 つ目の process.bat スクリプトを(ECHO を使って)作らせ、メインループから呼び出し、作業が終わったら削除させられます:
ECHO magick %%1 -blur 30 -negate %%1.miff >%~dp0process.bat
ECHO composite %%1.miff %%1 -compose overlay "%%~dpn1_light"%%~x1 >>%~dp0process.bat
ECHO DEL %%1.miff >>%~dp0process.bat
%~d1
CD %~p1
MD small
FOR %%a in (*.jpg) DO CALL "%~dp0process" "%%~fa"
DEL %~dp0process.bat
ECHO コマンドを使う場合、DOS の特殊文字、特にパーセント記号をもう一度エスケープする必要があります。そして確かに、IM は上記すべてを 1 つの処理コマンドで行え、".miff" の中間画像も不要にできたでしょうが、それはこの例の趣旨ではありません。
(サブ)ディレクトリツリーのバッチ処理
(サブ)ディレクトリツリー内のすべてのファイルを処理するテクニックはいくつかあります。最も単純なアプローチは、FOR 文で "/R" フラグを使い、カレントディレクトリ下のすべてのサブディレクトリ内のすべてのファイルをループさせることです。サブディレクトリツリー内のすべての TIFF ファイルを JPEG へ magick するには、次のように単純に打ち込むだけです:
FOR /R %%a IN (*.tif) DO imconv "%%~a" "%%~dpna.jpg"
"/R" フラグを使う場合、ファイルのソートやフィルタリングのオプションなしに、常にサブディレクトリツリー全体をループします。次の例では、すべてのサブディレクトリについて写真のインデックスプリントを生成し、それらをルートディレクトリに置きます。これは、Windows エクスプローラのプレビューに似ているものの、検索のたびにディレクトリツリー全体を再スキャンする(時間のかかる)必要なしに、ある写真を視覚的に探す簡単な方法を提供します。手始めに、ループを行うものと実際の作業を行うものの 2 つのバッチファイルの助けを借りて問題に取り組みます。インデックスプリントは IDX_0001.jpg, IDX_0002.jpg, IDX_0003.jpg などと名付けられた低品質の JPEG ファイルになります。まずループを確立します:
DEL IDX_????.JPG
SET COUNT=0
FOR /F "delims=" %%a in ('DIR /S /B /AD ^|FIND /I "Porsche" ^|SORT') DO CALL c.bat "%%a"
DEL title.txt
最初の行は以前の検索結果を片付けます。2 行目では、IDX_nnnn.JPG というファイル名を生成するために使う環境変数 COUNT を定義します。3 行目では、DIR /S /B /AD ですべてのサブディレクトリの一覧を確立し、"Porsche" という語を含むディレクトリを抽出し(オプション /I の使用により大文字・小文字を区別しない)、このフィルタした一覧をソートします。ソートにより、IDX ファイルの数値順序がディレクトリパス名のアルファベット順序と一致するようになります。オプション "delims=" は、最初の空白で行を切り詰める標準の挙動を抑制します。バッチファイル C.BAT を呼び出すとき、空白が正しく扱われるようパス名を引用符で囲みます。最後の行では、バッチファイル C.BAT が作る一時ファイルを削除します。さて、実際の作業に移ります:
CHCP 1252
DIR %1\*.jpg>nul || GOTO :EOF
:: Generate IDX filename
SET /A COUNT+=1
SET TFILE=000%COUNT%
SET TFILE=IDX_%TFILE:~-4%.jpg
:: Generate title without bracketing quotes
ECHO %~1>temp.txt
"C:\Program Files\Gnuwin32\bin\iconv.exe" -f ISO-8859-1 -t UTF-8 temp.txt>title.txt
montage -geometry 210x140+0+5 -tile 6x -title @title.txt %1\*.jpg -quality 30%% %TFILE%
jhead -cl %1 %TFILE%
最初の行でコードページを西ヨーロッパラテン (ISO-8859-1) に切り替えます。2 行目で、ディレクトリに実際に写真が含まれているかを確認し、なければバッチの残りをスキップします。(代わりに残りを実行しても実際には害はありません。magick montage は単に出力を生成しないだけだからです。しかし IDX ファイルの数が連続しなくなります。)Windows XP 以降には特別な GoTo ターゲットラベル :EOF があり、適切なラベルを定義せずに実行を終了できます。次に "COUNT" をインクリメントし、先頭にいくつかゼロを付け、%TFILE:~-4% で末尾 4 文字を抽出し、それを連結して "IDX_nnnn.jpg" の形式のファイル名を作ることで、インデックスファイルのファイル名 "TFILE" を生成します。計算を行うための SET /A 文の使用は後の SET を使った計算 で説明します。続く行では、パス名 "PNAME" を引用符から解放し、結果を中間ファイル 'temp.txt' に格納します。これは "Icon.exe" の助けで Unicode (UTF-8) へ変換されます("文字エンコーディング" を参照)。'title.txt' に格納された Unicode 文字列は、その後 IM の Montage で読み込まれます。これにより文字列が文字どおりに扱われ、Windows のパス名のバックスラッシュをエスケープする必要がなくなります。Montage はその後、写真を 6 つずつの行に並べ(-tile 6x)、パス名でタイトルを付けます。結果のインデックスプリントは幅 1260 ピクセルで、保存容量の需要を減らすために JPG 品質 30% で格納されます。最後の行では、プログラム JHEAD を使ってパス名を JPEG コメントに書き込みます。これは、ファイル名内の特定の部分文字列を求めてファイルをテキスト検索することで、Windows エクスプローラ内でインデックスプリントをフィルタする可能性を提供します。「作業バッチ」のコードを FOR ループに置いて、2 つのバッチファイルを 1 つにまとめられます:
SETLOCAL EnableDelayedExpansion
SET ICONV="%PROGRAMFILES%\Gnuwin32\bin\iconv.exe" -f ISO-8859-1 -t UTF-8
CHCP 1252
DEL IDX_????.JPG
SET COUNT=0
FOR /F "delims=" %%a in ('DIR /S /B /AD ^|FIND /I "Porsche" ^|SORT') DO (
DIR "%%a\*.jpg">nul
IF !ERRORLEVEL!==0 (
SET /A COUNT+=1
SET TFILE=000!COUNT!
SET TFILE=IDX_!TFILE:~-4!.jpg
ECHO %%a >temp.txt
%ICONV% temp.txt>title.txt
montage -geometry 210x140+0+5 -tile 6x -title @title.txt "%%a\*.jpg" -quality 30%% !TFILE!
jhead -cl %%a !TFILE!
)
)
DEL temp.txt
DEL title.txt
基本的に、初期のコードには 2 つの変更を加える必要があります:
- 遅延展開を有効にし、
FORループ内で使う環境変数を、パーセント記号ではなく感嘆符で囲んで参照しなければなりません。 - コマンドプロセッサをリセットしてしまう
GOTO文を避けなければなりません。
既定では、FOR ループ内の環境変数は実行時には評価され ません。代わりに、括弧内に与えられた一覧を使ってコードが前処理されます。したがって FOR ループ内の %COUNT% への参照は、常に同じ値を返します。環境変数の実行時評価を有効にするには、遅延展開をオンにする必要があります。これは、cmd /V:on でコマンドプロセッサを呼び出すか、あるいは次の REG ファイルを使ってレジストリで全体的にオンにすることで行えます:
Windows Registry Editor Version 5.00
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Command Processor]
"DelayedExpansion"="1"
[HKEY_CURRENT_USER\Software\Microsoft\Command Processor]
"DelayedExpansion"="1"
しかし、間違いの起こりにくいバージョンは、SETLOCAL EnableDelayedExpansion を使ってこのオプションをバッチ内で設定することです。これは環境変数への変更を現在のバージョンのコマンドプロセッサに限定もするので、おそらく望ましいでしょう。ループ内の環境変数の実行時の値への参照は、パーセント記号の代わりに感嘆符を使う必要があります。FOR ループ内での GOTO 文の使用は非常に微妙なエラーの源になり得るので、避けるべきです。しかし私たちのバッチでは、montage のコードを含むコードブロックを IF 文で囲むことで、ジャンプを容易に置き換えられます。
任意個数のファイルのバッチ処理
DOS バッチファイルでは、%1 から %9 で直接扱えるコマンドラインパラメータは 9 個だけです。以前の Windows バージョンでは、この制限を回避するには SHIFT コマンドしかなく、これはコマンドラインパラメータの循環シフトを引き起こしました。新しいバージョンでは、コマンドラインパラメータを For ループで扱えます:
FOR %%i in (%*) DO ...
これにより、ドラッグ&ドロップで渡された任意個数の画像を Montage できます:
@ECHO OFF
SETLOCAL EnableDelayedExpansion
%~d1
CD "%~p1"
DEL Files.txt 2>nul:
DEL FSorted.txt 2>nul:
FOR %%I in (%*) DO ECHO %%~nxI>>files.txt
FOR /F "delims=" %%A in ('TYPE files.txt ^| Sort') Do (
ECHO %%A>>fsorted.txt
)
SET MONTAGE=montage -tile 3x
FOR /F "delims=" %%A in (FSorted.txt) Do (
SET MONTAGE=!MONTAGE! %%A
)
SET MONTAGE=%MONTAGE% result%~x1
%MONTAGE%
DEL Files.txt
DEL FSorted.txt
上記のコードは、コマンドラインパラメータとして渡されたファイルが同じディレクトリにあり、ファイル名の英数字順に従ってモンタージュされるべきことを前提とします。これはたとえば、撮影時刻に従って並べられるデジタル写真のインデックスにとって理にかなうでしょう。まずファイルのディレクトリをカレントディレクトリにし、これにより以降のコードがずっと単純になります。次にファイル名を files.txt にダンプします。ファイルがアルファベット順に選ばれたとしても、ファイル名がスクリプトに渡されるときにその順序が保たれるとは限りません。そこで、2 番目の作業ステップでファイルを並べ替え、fsorted.txt にダンプします。そのファイルに基づき、別の For ループで Montage のコマンドラインを構築します。Montage の出力ファイルは最初の入力ファイルと同じ拡張子 (%~x1) を使います。(すべてのファイルが同じ拡張子を共有していると仮定して。)最後の Montage コマンドは、環境変数を評価するだけ、すなわち %MONTAGE% で呼び出されることに注意してください。 で述べたように、長い(完全)ファイル名の大量のファイルを渡すと Windows XP では問題になり得ます。ShellExecute のパラメータリストが 2048 文字に制限されているからです。このエラーはバッチファイルでは処理できません。制御がバッチファイルに渡される 前 に起きるからです。スクリプトベースで考え得る解決策は、1 つのファイルだけがバッチに渡された場合に、ディレクトリ内のすべての画像ファイルを処理することです:
%~d1 & IF EXIST %1\* (CD %1) ELSE CD %~p1
IF "%2"=="" (
SET PATTERN=*.jpg
) ELSE (
SET PATTERN=%*
)
FOR %%v IN (%PATTERN%) DO (...)
最初の行で、2 番目のコマンドラインパラメータが存在するかを確認します。存在すれば、すべてのコマンドラインパラメータ (%*) が処理されます。1 つのファイルだけがスクリプトに渡されれば、パターンはすべての JPEG ファイルに設定されます。この単純なパターンマッチでは、カレントドライブ(%~d1 経由)とカレントフォルダに変更しておく必要があります。すなわち
- フォルダ名がスクリプトに渡された場合は CD %1
- ファイル名がスクリプトに渡された場合は CD %p1
ImageMagick からの情報取得
IM コマンドの出力を再利用する
最近の Windows バージョンでは、FOR 文ははるかに強力になっています。DOS "For" Command Help を参照してください。"/F" オプションを使うと、置換変数の入力をファイル、文字列、あるいは別の DOS コマンドや別のプログラムの出力から読み込めます。後者は IM で特に便利です。IM のオーバーレイ方式が実際どういうものか大まかに把握するには、次のバッチファイルを使えます:
@ECHO OFF
:: compose two gradients using all compose methods available
::
magick -size 80x80 -flip gradient: compose_src.png
magick compose_src.png -rotate 90 compose_dst.png
FOR /F %%A in ('magick -list compose') DO ^
magick composite compose_src.png compose_dst.png -compose %%A compose_%%A.png
![[IM Output]](../static/img/images/compose_src.png)
Src | ![[IM Output]](../static/img/images/compose_dst.png)
Dst | | ![[IM Output]](../static/img/images/compose_multiply.png)
Multiply | ![[IM Output]](../static/img/images/compose_screen.png)
Screen | ![[IM Output]](../static/img/images/compose_overlay.png)
Overlay
---|---|---|---|---|---
このスクリプトは、利用可能なあらゆる アルファ合成方式 を使って 2 つのグラデーション画像を合成するので、演算子が画像の色にどう影響するかを見られます。生成される画像のうちいくつかだけを上に示しました。これは Composition Tables のために生成された画像と似ており、演算子が画像の色にどう影響するかを見られます。上記の行はバッチファイルと仮定されているので、パーセント記号を二重にする必要があります。上記のスクリプトはまず、グレースケールの全範囲をカバーするグラデーションを持つ 2 つの直交(直角に揃った)グレースケール画像を作ります。IM コマンド Convert -list compose は、考え得るオプションの一覧を提供し、それぞれが 1 つの出力行に置かれます。括弧内でコマンドを参照する場合は単一引用符を使う必要があることに注意してください。"/F" オプションを使うと、FOR コマンドはこれらの各出力行を処理し、DO で実行されるコマンドに渡します。その結果、2 つのグラデーション画像が、IM が知るすべてのオーバーレイ方式を適用して重ね合わされます。出力ファイルはオーバーレイ方式に対応して名付けられます。 次の例では、IM が提供する色空間を図示します。上記と同じグラデーションのテクニックを使い、色空間の 3 つの座標が張る立方体の面を生成します:
magick -size 256x256 gradient: gy.miff
magick gy.miff -rotate 90 gx.miff
magick -size 256x256 xc:black black.miff
::R + G top left
magick gy.miff gx.miff -flop black.miff -set colorspace %1 -combine ^
-resize 260x300! -background none -shear 0x-30 ^
-virtual-pixel Transparent RG.miff
:: R + B top right
magick gy.miff black.miff gx.miff -set colorspace %1 -combine ^
-resize 260x300! -background none -shear 0x30 RB.miff
:: G + B bottom
magick black.miff gx.miff gy.miff -set colorspace %1 -combine ^
-resize 260x300! -background none -shear 0x30 -rotate 120 ^
-crop 520x300+0+75 GB.miff
magick -set colorspace %1 RG.miff RB.miff +append top.miff
magick -set colorspace %1 -size 520x150 xc:Transparent w.miff
magick top.miff w.miff -append topx.miff
composite -geometry +0+299 GB.miff topx.miff colorspace_%1.png
DEL *.miff
![[IM Output]](../static/img/images/colorspace_RGB.png)
colorspace RGB | ![[IM Output]](../static/img/images/colorspace_sRGB.png)
colorspace sRGB
---|---
上記と似た例で UNIX シェルスクリプトを使ったものは せん断を使った等角立方体 にあります。
このバッチファイルは色空間をコマンドラインパラメータ "%1" として受け取ります。それから立方体の 3 つの面を生成し、それらをせん断して組み立て、点 (0,0,0) が六角形の中心に来る等角投影が得られるようにします。最終的な画像は色空間(すなわち "%1")にちなんで名付けられ、PNG として格納されます。今度は、色空間の名前を提供する別のバッチファイルから、この("cspace.bat" として保存した)バッチファイルを呼び出したいと思います:
FOR /F %%A in ('magick -list colorspace') DO CALL cspace %%A
DOS でパイプして -list オプションの出力をフィルタすることもできます:
magick -list colorspace | FIND "RGB" >>clist.txt
FOR /F %%A in (clist.txt) DO CALL cspace %%A
DEL clist.txt
この例では、出力から "RGB" を含む行をフィルタしてファイル clist.txt に書き込みます。このファイルはその後、FOR /F コマンドの入力として使われます。一時ファイルを避けて、これを 1 回で行うこともできます:
FOR /F %%A in ('magick -list colorspace ^| FIND "RGB"') DO CALL cspace %%A
この場合、パイプ記号 "|" はエスケープする必要があります。それは二重引用符で囲まれておらず(FOR 文に必要な単一引用符でのみ囲まれている)、(少なくとも上のコマンドラインでは)通常の意味で使われていないからです。
1 行出力の処理
このテクニックは 1 行出力にも役立てられます。たとえば、Fred Weinhaus 氏の Web サイト で説明されているテクニックにより、画像の平均輝度をおおよそ量子範囲の中央(すなわち 8 ビットの色深度では 127)に設定する自動ガンマ補正を適用できます:
FOR /F %%a in ('identify -format "%%[fx:log(mean)/log(0.5)]" %1') DO ^
magick %1 -gamma %%a "%~dpn1"_c.%~x1
このバッチファイルには、コマンドラインパラメータ "%1" として完全修飾ファイル名が、おそらくドラッグ&ドロップや SendTo で渡されます。IM の Identify コマンドの出力がガンマ値を提供し、それが画像の平均輝度をそのダイナミックレンジの中央に設定します。この値は FX 形式の式 を使って計算されます。Identify コマンドの 1 行出力は "%%a" 変数に保存され、ガンマ演算子 の引数として Convert コマンドに渡されます。 | FOR コマンドは行継続に関してかなり敏感なようです: 行継続を使う場合は、次の行をスペースで始めないようにしてください。
| この自動ガンマ補正の方法は今では "[-auto-gamma](https://imagemagick.org/command-line-options/#auto-gamma)" として IM に組み込まれており、IM v6.5.5-1 で追加されました。しかしこれは、後のコマンド引数で使うためにコマンド出力を再利用するテクニックを示しています。
同じ FOR テクニックで、写真に埋め込まれた EXIF 情報から読み込み、それを画像の左上隅に書き込めます:
FOR /F "tokens=1,2" %%i IN ('identify -format "%%[EXIF:DateTime]" %1') DO ^
magick %1 -pointsize 18 -fill white -gravity northwest ^
-annotate +0+0 "%%i %%j" "%~dpn1"_dated%~x1
| 写真は通常 JPEG フォーマットで保存されます。JPEG 画像を読み込んで再保存すると、JPEG の不可逆圧縮 のため画像がわずかに劣化するので、JPEG への保存し直しは推奨されません。
上記のバッチファイルでは、写真のファイル名が唯一のコマンドラインパラメータとして与えられ、"%1" として参照されます。Identify コマンドは、JPEG ファイル内の EXIF 情報から写真が撮影された日付と時刻を読み込みます。FOR コマンドはその後この出力を Convert に渡し、Convert は写真の左上隅に対応する注釈を付けます。EXIF の日付・時刻情報は "yyyy:mm:dd hh:mm:ss" 形式、たとえば "2006:12:26 00:22:38" となっています。したがって日付と時刻はスペース文字で区切られています。既定では、FOR 文はタブとスペース文字を標準の区切りとして、各行の最初のトークン(「単語」)だけを見つけます。したがって上の例では、標準の処理は日付だけを返し、時刻は返しません。オプション "tokens=1,2" を使って両方のトークンに関心があることを宣言し、それらは "%x, %y" のように連続して名付けられます。しかし、次のコードで日付のかなり型破りな書式を変えられます:
FOR /F "tokens=1,2,3,* delims=: " %%i IN ('identify -format "%%[EXIF:DateTime]" %1') DO ^
magick %1 -pointsize 18 -fill white -gravity northwest ^
-annotate +0+0 "%%j/%%k/%%i %%l" "%~dpn1"_dated%~x1
ここではコロン (':') を追加の区切りとして定義したので、日付が 3 つのトークン "%i"、"%j"、"%k" に分割されます。次に見つかる区切りは、日付と時刻を分けるスペース文字です。アスタリスク ("*") で、行の残りを 4 番目のトークン "%l" に格納するよう求めています。これで日付を好きなように書式化できます。上の例のように、英米式の表記 "mm/dd/yyyy" を選びました。
1 つのコマンドから複数の値を設定する
1 つの ImageMagick コマンドから複数の値を設定するテクニックは、コマンドにデータを書式化させ、複数の変数を設定できるようにすることです。
FOR /F %%L IN ('identify -format "Width=%%w\nHeight=%%h" %1') DO set %%L
これにより、単一の ImageMagick コマンド呼び出しから DOS 変数 'Width' と 'Height' の両方が設定されます。
計算を実行する
DOS コマンドインタプリタは計算に関しては貧弱です。単純な整数演算には使えます。しかし、より複雑な浮動小数点演算を行うには、IM の FX 形式の式、またはサードパーティの DOS 計算プログラムを利用できます。
IM の FX 式を使う
IM の FX 形式の式 は浮動小数点演算に使え、その計算をより大きな書式化された文字列に加えられます。これは上記の 1 行出力の処理 の節の最初の例で示したとおりです。SET コマンドを使えば、結果を環境変数に格納し、後でバッチファイル内で使えます。単純な例として、上の例の日付・時刻文字列のフォントサイズを写真の寸法に対応して調整したいとしましょう:
FOR /F %%i IN ('identify -format "%%[fx:min(w,h)*0.05]" %1') DO SET psize=%%i
FOR /F "tokens=1,2,3,* delims=: " %%i IN ('identify -format "%%[EXIF:DateTime]" %1') DO ^
magick %1 -pointsize %psize% -fill white -gravity northwest ^
-annotate +0+0 "%%j/%%k/%%i %%l" "%~dpn1"_dated%~x1
最初の行で写真の小さいほうの寸法を "%[fx:min(w,h)]" で評価し、この値の 5% を取って環境変数 PSIZE に格納します。この値は次の文 (%psize%) で参照され、日付・時刻情報のフォントサイズを設定します。そしてここでは、回転したサムネイル画像を作るために、-15° から +15° の間の乱数の角度を整数として計算します。
FOR /F %%x IN ('magick null: -format "%%[fx:int(rand()*31)-15]" info:') DO SET angle=%%x
magick %1 -thumbnail x90 -alpha set ^
-background none -rotate %angle% "%~dpn1"_rotated.png
FX 式 は数値を生成できるだけでなく、より大きな文字列に埋め込まれた複数の数値も生成できます。たとえば 角を丸めた縁取り では、画像の幅と高さの情報に基づいて複雑な draw 文字列を直接生成するのに使われました。この追加機能は、他の外部プログラムへのさらなる依存を避けることと相まって、バッチスクリプトで計算を行う上でこの方法を望ましいものにしています。
SET コマンドを使う
SET コマンドは、"/A" オプションを呼び出すと、いくつかの単純な整数演算といくつかの基本的な文字列操作を行えます。次の例では、SET コマンドを使って日付・時刻文字列の幅をおおよそ計算します:
:: Determine the font height
FOR /F %%i IN ('identify -format "%%[fx:min(w,h)*0.05]" %1') DO SET psize=%%i
:: The width of the date-time string is roughly 9 times its height
SET /A pwidth=%psize% * 9
:: Calculate the average brightness in this section
:: and choose the text color correspondingly
FOR /F %%i IN ('identify -format "%%[fx:mean]" -crop %pwidth%x%psize%+0+0 %1') DO SET mean=%%i
IF %mean% LEQ 0.5 (SET fcolor=white) ELSE SET fcolor=black
:: Annotate the photograph
FOR /F "tokens=1,2,3,* delims=: " %%i IN ('identify -format "%%[EXIF:DateTime]" %1') DO ^
magick %1 -pointsize %psize% -fill %fcolor% -gravity northwest ^
-annotate +0+0 "%%j/%%k/%%i %%l" "%~dpn1"_dated%~x1
このサンプルバッチファイルは、日付・時刻文字列が置かれる領域の平均輝度 (%mean%) に対応して、その文字列の色を選びます。平均色強度が 50% 未満なら文字列は白に、そうでなければ黒になります。この例では IF 文も使っています。ELSE の部分は同じ行に置かなければならず、最初のコマンドは括弧で囲まなければならないことに注意してください。
その他の外部計算プログラムを使う
代替として、浮動小数点演算を提供する DOS プログラム、たとえば EVAL を使えます。このファイルを IM のプログラムディレクトリや Windows システムディレクトリに置けば、任意の DOS シェルウィンドウで浮動小数点計算を行えます。EVAL プログラム、FOR コマンド、環境変数を使えば、上記の色立方体の例をいくらか柔軟にし、その各種計算をより分かりやすくできます:
magick -size 256x256 gradient: gy.gif
magick gy.gif -rotate 90 gx.gif
magick -size 256x256 xc:black black.gif
:: Set the dimension of the color cube / hexagon and calculate the various lengths
SET l1=512
FOR /F %%i IN ('EVAL "round(cos(degree*30)*%l1%)"') DO SET l2=%%i
FOR /F %%i IN ('EVAL "2*%l2%"') DO SET l3=%%i
FOR /F %%i IN ('EVAL "round((1+sin(degree*30))*%l1%/2)"') DO SET l4=%%i
FOR /F %%i IN ('EVAL "round(%l1%/4)"') DO SET l5=%%i
magick gy.gif gx.gif -flop black.gif -set colorspace %1 -combine ^
-resize %l2%x%l1%! -background none -shear 0x-30 ^
-virtual-pixel Transparent RG.miff
magick gy.gif black.gif gx.gif -set colorspace %1 -combine ^
-resize %l2%x%l1%! -background none -shear 0x30 RB.miff
magick black.gif gx.gif gy.gif -set colorspace %1 -combine ^
-resize %l2%x%l1%! -background none -shear 0x30 -rotate 120 ^
-crop %l3%x%l1%+0+%l5% GB.miff
magick -set colorspace %1 RG.miff RB.miff +append top.miff
magick -set colorspace %1 -size %l3%x%l4% xc:Transparent w.miff
magick top.miff w.miff -append topx.miff
magick composite -geometry +0+%l1% GB.miff topx.miff colorspace_%1.png
DEL *.miff
DEL *.gif
編集・デバッグ・実行時エラーのテスト
原理的には、DOS バッチファイルはどんなエディタでも、Windows のメモ帳でさえ書けます。しかし、DOS バッチファイル向けの構文ハイライトを備えたエディタを使うべきです。私個人としては Notepad++ が最適なツールだと思いますが、エディタの話は人を不機嫌にさせがちです。なので: はい、他のどんなエディタでもかまいません。私の知る限り、市場には無料のバッチファイル IDE(統合開発環境)はありません。これは OS に付属していてしかるべきものだと思われますが、これまで一度もそうではありませんでした。これまで私はすべてのバッチファイルを Notepad++ で書いてきましたが、日常的にバッチファイルを書く人には、Running Steps バッチ IDE が役立つかもしれません。これはシェアウェアで、約 80 ドルです。DOS コマンドの包括的な説明は http://www.computerhope.com/msdos.htm にあります。DOS バッチ言語そのものと同様、バッチファイルのデバッグはかなり奇妙な仕事です。まず手始めに、どんなバッチファイルも DOS ボックスでテストするでしょう。ドラッグ&ドロップや SendTo をテストするときは、バッチジョブが終わった後も DOS ボックスが開いたままになるよう、バッチファイルを PAUSE 文で終わらせるのが推奨されます。実行時エラーメッセージについては、一般的なアプローチは DOS の ERRORLEVEL を確認し、ECHO コマンドで生成した対応するエラーメッセージへジャンプすることです。最もあり得るエラーの源の 1 つは、スクリプトを実行するマシンで Convert プログラムが適切に見つからないことだと分かりました。したがって、自分のバッチスクリプトを他人と共有するつもりなら、まず Convert にアクセスできるかを確認すべきです:
@ECHO OFF
magick -version 1>nul: 2>nul:
IF NOT %errorlevel%==0 GOTO NoMagick:
magick ...
GOTO :EOF
...
:NoMagick
ECHO ImageMagick (convert.exe) not found.
PAUSE
最初の行で ImageMagick のバージョンを問い合わせ、標準出力を 1>nul:(または単に >nul:)で、エラーメッセージを 2>nul: で抑制します。つまり _stdout_ と _stderr_ を nul: へリダイレクトします。IM の Convert への呼び出しが失敗すると、代わりにシステムプログラムの Convert が呼ばれ、これは -version オプションを扱えず、ERRORLEVEL 変数を設定します。なぜ Convert が見つからないかを突き止め、問題を修正しようと試みることもできます: IM のプログラムパスが環境変数 PATH の一部かどうかを判定できます:
@ECHO OFF
PATH | FIND /I "ImageMagick"
IF NOT %errorlevel%==0 GOTO NoPath:
...
:NoPath
...
Find(大文字・小文字を区別しないオプション /I で呼び出した)が文字列を見つけられないと、ERRORLEVEL を設定します。より洗練されたアプローチでは、PATH に頼らずに、代わりにレジストリエントリを確認できます:
@ECHO OFF
FOR /F "tokens=1,2,*" %%A in ^
('reg query "HKCM\Software\ImageMagick\Current" ^| FIND "BinPath"') DO ^
SET MPATH=%%C
IF [%MPATH%]==[] GOTO NoMagick:
"MPATH\convert.exe" ...
...
:NoMagick
...
このコードで、IM のレジストリキー Current を照会し、エントリ BinPath を探します。出力の決定的な行は次のとおりです:LibPath REG_SZ C:\Program Files\ImageMagickこのテキスト行の「単語」はタブ(Windows XP)または複数の空白(Windows Vista)で区切られています。これらは For /F が使う標準の区切りです。私たちが探している 3 番目の「単語」(%%C) を環境変数 MPATH に格納し、後でスクリプト内で magick を呼び出すときにこれを参照できます。スクリプトが、ある特定の最小バージョン番号の ImageMagick のインストールを要求することがあるかもしれません。たとえば 遠近法ひずみの手法 はバージョン 6.3.5-9(2007 年 9 月)で初めて実装されました。したがって、スクリプトが遠近法による補正を扱うなら、インストールされている IM のバージョンがそれより新しいかをテストすべきです:
@ECHO OFF
SETLOCAL EnableDelayedExpansion
SET MINVERSION=7.5.3-0
FOR /F "tokens=1,2,3" %%a in ('magick -version ^|FIND "Version"') DO SET VERSION=%%c
IF %VERSION% LSS %MINVERSION% GOTO GetNewVersion:
Goto :EOF
:GetNewVersion
ECHO This Script requires et least ImageMagick version %MINVERSION%.
ECHO Yours is %VERSION%.
PAUSE
Goto :EOF
SETLOCAL は環境変数への変更を現在のスクリプトに制限するので、副作用を恐れる必要がありません。オプション EnableDelayedExpansion はここでは実際には必要ありませんが、SETLOCAL を使うときはいつでもそのオプションを使うのが良い習慣です。次に必要な最小バージョンを環境変数 MINVERSION に格納します。3 行目で Convert を '-version' オプションで呼び出し、出力から最初の行を ^|FIND "Version" で抽出し、その行の 3 番目の単語を取得して環境変数 VERSION に格納します。4 行目でこのバージョンを必要な最小バージョンと比較します。
実行時間の最適化
実行時間を測定するには、%TEMP% 環境変数の内容を表示できます。次のスクリプトは、(大きな)画像ファイル、たとえばデジタル写真の平均輝度を計算するさまざまな方法をテストします:
IF "%1"=="" GOTO :EOF
ECHO %TIME%
Identify -verbose %1 | FIND "mean" & ECHO %TIME%
copy %1 %TEMP%\*.* & ECHO %TIME%
Identify -verbose %TEMP%\%1 | FIND "mean" & ECHO %TIME%
Convert %TEMP%\%1 -format %%[fx:mean] info: & ECHO %TIME%
Convert %TEMP%\%1 -resize 1x1! -format %%[fx:mean] info: & ECHO %TIME%
DEL %TEMP%\%1
画像は唯一のコマンドラインパラメータとしてスクリプトに渡され、スクリプトの最初の行でテストされます。以下では、文の前後で環境変数 %TIME% をエコーすることで、さまざまなアプローチの実行時間を測定します。アンパサンド & は複数の DOS コマンドを 1 行に置くことを可能にし、ここではコードに必要なスペースを短縮するために使われているだけです。画像がネットワークドライブ上に置かれている場合、クライアントコンピュータのメモリへの転送が実行時間のかなりの部分を消費することがあります。そこでファイルはローカルの一時フォルダ(その名前は環境変数 %TEMP% に格納されている)にコピーされます。結果として、単純な Convert コマンドは Identify の結果をフィルタするのとちょうど同じだけの実行時間がかかりますが、-resize 1x1! で画像を 1 ピクセルにリサイズすると、結果をあまり変えずに操作が大幅に高速化されることが分かります。なお、ここでは TIME /T の代わりに ECHO %TIME% を使っています。後者は時と分しか表示しないのに対し、前者は 100 分の 1 秒まで提供するからです。UNIX コマンドシェルとは対照的に、相対時間を測定する、すなわち基準点を設定し、文を実行し、それから必要な時間を評価する直接的な方法はありません。バッチファイル内での相対時間の計算は、バッチファイルが整数演算しか許さないという事実によって難しくなります。FOR コマンドで秒を抽出し、それから IM の fx 演算子を使って減算を行い、60 秒のオーバーフローを許容できます
FOR /F "tokens=1,2,* delims=:" %%a in ("%TIME%") DO SET START=%%c
... some command(s)...
FOR /F "tokens=1,2,* delims=:" %%a in ("%TIME%") DO SET STOP=%%c
Convert null: -format "%%[fx:(%STOP%-%START%<0.0)?%STOP%-%START%+60.0:%STOP%-%START%]" info:
ただしこれは、小数の区切りが小数点に設定されている Windows バージョンでのみ機能します。時間はロケールに従って提供され、fx は計算を米国式に従って行うからです。VBScript とは対照的に、バッチファイル内でロケールを変更する方法はありません(レジストリ経由でマシン全体について変更する以外には)。代替として、時間全体を 100 分の 1 秒へ magick でき、これは整数演算で行え、後で差分を計算するのも整数演算で行えます。基本的なコードは次のとおりです
for /f "tokens=1-4 delims=:., " %%a in ("%TIME%") do ^
set /a Start100S=1%%a * 360000 + 1%%b * 6000 + 1%%c * 100 + 1%%d - 36610100
分割された時間コードの各部分の前に挿入された "1" は、先頭にゼロが付いた数が 8 進数と(誤)解釈されるのを防ぎます。この進め方で生じる「計算誤差」は、後で最後に 36610100 を引くことで補償されます。完全な(そしてよりエレガントな)サンプルコードは ここ にあります。もう 1 つのアプローチは、Windows 2003 Resource Kit が提供する TimeIt ユーティリティで、ここ でダウンロードできます。ただし公式には Windows XP しかサポートしていません。
バッチプログラミングの指針
要するに、バッチファイルをプログラミングする際に従うべき規則は次のとおりです:
SETLOCAL EnableDelayedExpansionで始める- IM の Convert ツールを参照する変数を定義する:
SET IMCONV="%PROGRAMFILES%\ImageMagick" - 複数のファイルを扱うときは、ファイルを含むフォルダをカレントフォルダにすると、しばしばコードが単純になる。すなわち
%~d1でドライブを変更するCD %~p1でフォルダを変更する
- GnuTools の iconv.exe を使ってテキストを UTF-8 へ magick し、ファイルからテキストを与える。
- Convert と Montage はテキスト出力を stdout ではなく stderr に書くことを覚えておく。
- 多くのエラーは空白を含むファイル名から生じるので、各バッチがそのようなファイル名を正しく扱うかどうかを確認する。
- すべてのパーセント記号を二重にするのを忘れない。たとえば JPEG 品質を設定するとき。
- 文字列内の特殊文字をエスケープするのを忘れない。たとえば "^|"。
SET /Aで行う整数計算は、ゼロで始まる数をすべて 8 進数として扱うので、そのような各数の前に "1" を付ける。
まとめ
上記の例は、ImageMagick が提供する可能性と組み合わせれば、単純な DOS バッチファイルが驚くほど多用途であることを証明しています。実際、ほとんど何でもバッチファイルで(多少粗削りな)何らかの方法で行えます。DOS バッチファイル言語の開発で取られた奇妙なやり方で考えることに慣れてしまえば、スクリプトはかなり短いものにすらなり得ます。とはいえ、バッチファイル言語に本当に精通していない限り、このわずか数行のコードにも、おそらく何時間もの退屈な実験が費やされたことでしょう。基本的な画像処理タスク以上を目指すなら、おそらくより洗練されたスクリプト言語を使うのが推奨されます。コードの開発がより単純で構造化されたものになるからです。
Visual Basic Script (VBS)
Microsoft Windows Script Host (WSH) のスクリプト機能は、単純なバッチファイル言語のものよりも洗練されています。WSH は、異なる Active Scripting 言語エンジンを利用できるという意味で言語に依存しません。既定では、プレーンテキストの JScript ファイル (Java Script) と VBScript ファイル (VisualBasic Script) を解釈・実行します。Windows Script Host は Windows 98 以降では既定で配布・インストールされています(ただしセキュリティ上の懸念から、対象マシンでオフにされている可能性はあります)。WSH はオブジェクトモデルを実装しており、システムオブジェクト、特にファイルシステムを扱える COM インターフェースのセットを公開します。ここでは Windows Script Host を詳しく論じません。それは他で(おそらくもっとうまく)なされているからです。むしろ、典型的な問題に対処する実践的な例をいくつか示します。例は VisualBasic Script で示しますが、JScript のコードもよく似たものになるので、これがお好みの言語なら、例を JScript へ書き換えるのは容易なはずです。バッチファイルと同様、VBScript はどんなエディタでも書け、ここでもエディタとして Notepad++ をお勧めします。バッチファイルと同様、Microsoft は VBScript の開発をサポートするように仕立てられた IDE を提供していません。Microsoft Office 2000 から 2003 に同梱された Microsoft Script Editor がありましたが、私は一度も試したことがありません。Microsoft は(非常に初歩的な)Microsoft Script Debugger も提供していますが、これもまた、私には個人的な経験があまりありません。VbsEdit のように、手頃な価格でシェアウェアとして提供される商用 VBS IDE がいくつかあります。Windows への ImageMagick のインストール の節で述べたように、以下では一般に ImageMagick の COM+ インターフェースは使いません。Convert、Montage、Identify といった IM のツールは代わりに、必要なすべてのオプションとファイル名を付けて、シェルの run コマンドを呼び出すことで直接実行します。これはバッチファイルでの行い方とほぼ同じです。コマンド文字列を組み立てる際には、しかし本物のプログラミング言語が提供する機能を活用します。
基本例: レンズ補正
WSH の使用はいくらかのオーバーヘッドを生むので、私たちの出発点の例は、単純なバッチファイルと比べた VBScript の利点を示すために、あまり初歩的ではありません。以下では、IM の 樽型ひずみ を使って Nikon 995 デジタルカメラのレンズひずみを補正します。補正パラメータは焦点距離に依存し、それはまず magick identify で調べられます。Nikon 995 レンズの補正には、パラメータ b(すなわち a, c = 0)だけが必要で、これは焦点距離 f から次のように計算できます: b = 0.000005142 f ³ - 0.000380839 f ² + 0.009606325 f - 0.075316854 この依存関係は、このレンズの樽型ひずみパラメータを記載している lensfun データベース を使って見つけられました。というわけで、これが私たちの VBScript です:
SetLocale(1033) ' US, i.e. decimal point
const strConv = "IMCONV" ' name of the IM Convert program
const strAdd = "_pt" ' string attached to the filename
'
Dim wsh
Set wsh = CreateObject("Wscript.Shell")
'
' names of the in- and output files
strFileIn = WScript.Arguments(0)
Pos = InStrRev(strFileIn,".")
strFileOut = Left(strFileIn,Pos - 1) & strAdd & Mid(strFileIn, Pos)
'
' evaluation of the focal length and calculation of parameter b
command = "identify -format ""%[EXIF:FocalLength]"" """ & strFileIn & """"
Set objExec = wsh.Exec(command)
strf = objExec.StdOut.Readline
f = eval(strf)
b = 0.000005142 * f * f * f -0.000380839 * f * f + 0.009606325 * f -0.075316854
'
Command = strConv & " """ & strFileIn _
& """ -virtual-pixel black -filter point -distort Barrel ""0.0 " _
& b & " 0.0 "" """ & strFileOut & """"
wsh.run command,7,true
最初の行でロケールを米国英語 (1033) に設定します。これは(他のことに加えて)小数の区切りを小数点に設定します。そうしないと、小数値はロケールに従って、すなわちおそらく小数点ではなく小数のカンマで提示され、そのような値が IM に渡されるときに問題を引き起こします。文字列定数の定義の後の 2 行は標準的なオーバーヘッドです。IM のプログラムを Exec や Run メソッドで起動するには、常に Shell オブジェクトが必要だからです。唯一のスクリプト引数はファイル名で、通常はドラッグ&ドロップや SendTo で提供します。出力ファイルを名付けるとき、PTlens がそうするように、元のファイル名に "pt" を付け加えます。たとえば _Photo.JPG → Photo_pt.JPG。ファイル名は strFileIn に格納され、そこから出力ファイル strFileOut の名前を導きます。次に IM の Identify プログラムを実行します。結果(すなわち焦点距離を表す EXIF の有理数)は strf に格納されます。EXIF の有理数は分子/分母として提供され、たとえば 82 / 10 = 8.2 mm です。したがって有理数は、パラメータ b を計算する数式で使う前に評価しなければなりません。最後の 2 行で Convert のコマンドラインを構築し、Shell オブジェクトの Run メソッドで文を実行します。パラメータ 7 はウィンドウを最小化し、TRUE はスクリプトに結果を待つよう伝えます。上記のスクリプトは、IM のコマンドラインツールを(COM+ オブジェクトではなく)VBScript で使うときの一般的な戦略を概略しています。これらは次のいずれかで呼び出されます
- テキスト出力が期待されない場合は Shell オブジェクトの Run コマンドで
- テキスト出力を評価する必要がある場合(Identify では典型的にそうです)は Shell オブジェクトの Exec コマンドで。
この最初の VBScript は、次のようなバッチファイルでもできなかったことは何もしていません
SETLOCAL EnableDelayedExpansion
FOR /F %%i in ('identify -format "%%[EXIF:FocalLength]" %1') DO SET FL=%%i
FOR /F %%i IN ('Convert null: -format "%%[fx:0.000005142*(%FL%)^3 - 0.000380839 * (%FL%)^2 + 0.009606325 * %FL% - 0.075316854]" info:') DO SET B=%%i
Convert %1 -distort barrel "0.0 !B! 0.0" "%~dpn1_pt%~x1"
しかし VBS コードのほうが分かりやすく、修正が容易で、エラーチェックなどに関して容易に拡張できます。2 番目の FOR 文では改行が不可能なので、それをそのまま長いものとして残さなければならないことに注意してください。
複数ファイルを扱う
DOS バッチファイルと比べた VBScript の本物の利点の 1 つは、任意個数のコマンドライン引数を容易にループできることです。たとえば、Windows エクスプローラで複数のファイルを選び、選択した画像を IM の Montage でインデックスプリントにまとめられます。基本的なコードは次のようになります:
Dim wsh
Set wsh = CreateObject("Wscript.Shell")
'
strInputFiles = ""
For EACH Arg IN WScript.Arguments
strInputFiles = strInputFiles & " """ & Arg & """"
next
'
IndexFile= Left(WScript.Arguments(1),InStrRev(WScript.Arguments(1),"\")) & "index.jpg"
Command = "montage -geometry 210x140+0+5 -tile 6x " & strInputfiles & " -quality 80% """ & IndexFile & """"
wsh.run command, 7, true
スクリプトはまず、ドラッグ&ドロップでスクリプトに渡されたファイル名を連結します。次に、いくつかの単純な文字列操作で同じフォルダの JPEG 出力ファイルの名前を導きます。最後に、適切なパラメータで Montage が呼び出されます。より大きなスクリプトでは、ファイル名を配列に格納すると便利です:
Dim FName()
Dim wsh
Set wsh = CreateObject("Wscript.Shell")
'
NArgs = WScript.Arguments.Count
Redim FName(NArgs-1)
FOR i = 0 TO NArgs - 1
FName(i) = """" & WScript.Arguments(i) & """"
NEXT
まず動的配列を Dim FName() で定義し、それから Redim FName(NArgs-1) で再次元化します。Montage のコマンドラインは非常に長くなる可能性があります。入力ファイルの一覧で、各ファイルが完全修飾ファイル名で名指しされるからです。最大長は通常の 8192 文字 ではなく、ShellExecute 関数の呼び出しに許される最大長で決まり、それは Windows XP では 2048 文字しかありません。ディレクトリ名が非常に長いと、これが問題を引き起こし得ます。生じるエラーはスクリプトでは処理 できません。エラーはスクリプトが実行される 前 に起きるからです。考え得る解決策は、より短い名前のローカルフォルダにファイルを置くことです。スクリプトベースの(部分的な)解決策はバッチファイルと同じです: 1 つのファイル名だけが与えられた場合、親ディレクトリ内のすべての画像を処理します。フォルダ内のすべての GIF を処理するには、次のような感じのことができます:
Dim fs, folder
Set fs = CreateObject("Scripting.FileSystemObject")
If WScript.Arguments.Count <> 1 Then WScript.Quit(1)
set Folder = fs.getFolder(fs.GetParentFoldername(WScript.Arguments(0)))
FN=""
FOR EACH file in folder.files
If instr(file,"gif") <> 0 THEN FN = FN & File & vbLF
NEXT
MsgBox FN
ここでは FileSystem オブジェクトを使って親フォルダの名前を判定します。しかしファイル拡張子の照合は通常のやり方で確認しています。Files オブジェクトは代わりに Type プロパティしか提供しないからです。ドラッグ&ドロップや SendTo はファイル名をランダムな順序でスクリプトに渡すので、しばしばファイル名をアルファベット順にソートする必要があります。これはバブルソートで実現できます:
for i = 0 to NArgs - 1
for j = i + 1 to NArgs - 1
if FName(i) > FName(j) then
Temp = FName(i)
FName(i) = FName(j)
FName(j) = Temp
end if
next
next
上で概略した概念のより洗練された応用を右に示します: 一連の動画フレームが、VBScript と ImageMagick によって 2 本の平行な「フィルムストリップ」に組み立てられています。パーフォレーション(穴)は、フレームの進行が、通常の西洋の読み方である左から右、それから上から下とは対照的に、上から下、すなわち列ごとであることを視覚的に示しています。この仕事を行うスクリプト全体はアーカイブ strip.zip からダウンロードできます。余談: 各フレームの右上の赤いタイムコードは AVIsynth スクリプトで生成されました。フレームは VirtualDub からエクスポートしてダンプしました。埋め込まれたタイムコードがあるので、フレームは必ずしも時間的に等間隔である必要はなく、すなわち Windows エクスプローラで必要に応じて選び、SendTo フォルダに置かれたスクリプトに送れます。
テキストファイルを扱う
クライアントコンピュータでスクリプトを扱うとき、入力情報は一般にドラッグ&ドロップや SendTo で提供されます。すなわち基本的にはファイル名から成り、スクリプトが事前定義した方法で処理されます。追加の情報は、実行時のユーザー操作で提供されるか、テキストファイルの形で提供される必要があります。基本的に、次の選択肢があります:
- スクリプトは画像ファイルを入力として受け取り、追加情報を提供する(必須でない場合もある)テキストファイルを伴う。
- スクリプトは単一のテキストファイルを入力として受け取り、それが処理対象の画像と必要な追加情報を列挙する。
前者の場合、必須でないテキストファイルを画像と同じディレクトリに置き、標準的な名前を割り当てるのが適しています。スクリプトはその後、入力画像から親ディレクトリ名を導き、存在すれば同じディレクトリのテキストファイルを開けます。このアプローチの例は、上で言及した「フィルムストリップ」です: スクリプトの最初で、スクリプトに渡された画像の数に応じて、フレームの何らかの標準的な順序を定義するでしょう。しかし、フレームの標準的な順序から逸脱したいシナリオもあるかもしれません。そこで、フレームのディレクトリに ordering.txt という名前のテキストファイルを置けば、それが存在する場合、フレームの順序を制御します:
strTxtFile="ordering.txt"
PDir = fs.getParentFolderName(FName(0)) & "\"
Wsh.CurrentDirectory = PDir
If fs.FileExists(strTxtFile) then
Set objFile = fs.OpenTextFile(strTxtFile, 1)
bCtrlFile = True
NCols = objFile.ReadLine
objFile.close
else
bCtrlFile = False
end if
2 番目の概念の有用な応用は、Fred Weinhaus 氏の Web サイトで 実演されている ように、画像を対象平面へ遠近法でマッピングすることに見られます。この概念を使って、遠近法でひずんだ画像を遠近法で(おおむね)正しい版に「スキン」できます。右に実演したとおりです: 上部では、左の画像は軽微な事故の現場で撮られた写真を示しています。右の写真は、後日現場を再訪した際に、やや高い位置から撮られたものです。下部では、事故の写真(すなわち平面である路面)が、遠近法変換によって対象の写真にマッピングされています。(これの狙いは、黒い車の右前タイヤが残したかすかなスリップ痕の向きの角度を視覚化することです。)この仕事を行うには、ユーザーが元画像の 4 点と、遠近法で正しい画像でのそれらの対象点を選ぶ必要があります。これを手作業で行い、元画像と対象画像での座標を、画像ビューア(IrfanView など)で点を選んで判定し、それらの座標を書き留めて Convert のコマンドラインに与えることもできます。しかしこの退屈な作業は、フリーウェアプログラム WinMorph で単純化できます。これはまさにこれを行う便利なインターフェースを提供します: 2 枚の絵から、いくつかの元の点と対応する対象点を選ぶのです。2 枚の写真の黄色の折れ線は、各絵で選んだ 4 点を結んでいます。しかしモーフィングアルゴリズム自体は、遠近法補正を行うのには適していません。(このアルゴリズムの基本的な働きは、IM の Web サイトの Usage セクションの Distorts の部分で説明されています。モーフィングへの有用性の実演は、Fred Weinhaus 氏の ShapeMorph スクリプトにあります。)WinMorph は、その情報を構造化されたテキストファイルに格納します。これには(他の情報に加えて)元と対象の両方のファイル名、ならびに元と対象の点の座標が含まれます。したがって、IM の遠近法ひずみに必要なすべての情報を WinMorph ファイルから導けます。関係するすべてのファイルと画像のコピーは wmpr.zip でダウンロードできます。
パイプ処理
これまで、IM のコマンドラインツール(Convert、Identify、Montage など)を Shell オブジェクトの Run や Exec 関数で直接呼び出してきました。しかし、IM のパイプ機能、すなわち先行するコマンドの出力で次のコマンドに与える機能を使いたい場合は、コマンドライン環境を通じてコマンドラインツールを呼び出す必要があります。たとえば、上記のスクリプトが生成したインデックスプリントの白い縁をトリミングしたい場合、コードは次のようになります:
Dim wsh
Set wsh = CreateObject("Wscript.Shell")
'
strInputFiles = ""
For EACH Arg IN WScript.Arguments
strInputFiles = strInputFiles & " """ & Arg & """"
next
'
IndexFile= Left(WScript.Arguments(1),InStrRev(WScript.Arguments(1),"\")) & "index.jpg"
Command = "cmd /c montage -geometry 210x140+0+5 -tile 6x " & strInputfiles & " miff:- | magick - -trim """ & IndexFile & """"
wsh.run command, 7, true
ここでは、終了時にコマンドボックスを自動的に閉じるよう、コマンドプロセッサ cmd をオプション /c で呼び出します。デバッグ目的では、/k オプションで呼び出すこともでき、これはコマンドボックスを開いたままにして、起こり得るエラーメッセージを読めるようにします。
VBScript のテストとデバッグ
基本的に、私たちは VBScript を使って IM のコマンドラインツールの引数リストを構築し、それらをそのまま、あるいは DOS ボックス内で実行します。これはつまり、まず第一に次のことを確認すべきだということです
- コマンドライン自体が期待どおりのことを行う
- コマンドラインがスクリプトによって正しく構築される。
なので手始めに、DOS ボックス内でコマンドライン自体をテストすべきです。スクリプトを最初にテストするときは、IM コマンドを実行するのではなく、むしろ MsgBox(strCommand) でメッセージボックスにテキスト文字列を表示すべきです。コマンドライン自体が間違っていれば、どんなデバッグツールにもほとんど打つ手がないからです。単純なメッセージボックスはスクリプトのデバッグにも役立ち、私は洗練されたデバッガの必要性を本当に感じたことはありません。実行時テストについては、次のことを確認すべきです
- IM のコマンドラインツールに正しくアクセスできる
- ユーザーが、あなたが選ぶよう期待したもの、すなわち複数のファイル(ある特定の種類かもしれない)、ディレクトリ、テキストファイルなどを選んだ。
エラーメッセージは MsgBox(...) を使って容易に表示できます。
さらなる情報
残念ながら、DOS バッチファイルで ImageMagick コマンドを使うことを特に扱った(これ以外の)チュートリアルは知られていません。Web サイト DOS "For" Command Help のページには、"FOR" コマンドの使用についてより良い説明があります。Bonzo 氏 の Batch Script ページも見るとよいかもしれません。