[Automator & AppleScript] Automatorとsips、ImageOptimをつかって画像を変換する方法

以前、IDEA*IDEA さんで「sips」コマンドを知って使ってたんですが、さらにファイル容量の節減をImageOptimでできると便利かなと思い、自動化してみました。

【ひとりStartMac】 ターミナルから画像を一括リサイズする方法
sipsはコマンド、ImageOptimはアプリなのでAutomatorで作ってみようと作ったんですが、あとからImageOptimはコマンドからも使えることを知り複雑な気分に…。
ImageOptim

$ open -a ImageOptim.app *.jpg
  

いちおうAutomatorで作ると右クリックメニューから使えるようになります。

Automatorの仕組みを確認する

Automatorの仕組みは、おおまかにこのようになっています。
ConvertImagesTest.workflow 2013-04-26 15-43-44
* 右側のステージに並んだアイテム(?)が上から下へ順に実行されていく。
* アイテムには「アクション」と「変数」の2種類がある。
* 左側のライブラリからアクションを選び、右側のステージにドロップする。
* アクションは前のアクションからの入力を受け取って出力をする。
* 前のアクションからの入力をオプションで無視することができる。
* ステージ上で条件分岐はできない。

必要な機能と分からないところ

今回はこんなAutomatorを作ります。
* ファイルまたはフォルダー内の画像を一括処理する。
* 対象画像は別フォルダにコピーし、オリジナルはそのまま残す。
* 画像の最も長い辺をアスペクト比を保ったまま縮小する。
* 縮小サイズは、ダイアログで入力させる(px)。
* 入力数値より小さい画像は縮小しない。
* sips -Z コマンドで対象ファイルを縮小する。
* すべての画像をImageOptimに渡し、容量を節減する。
モヤモヤしているところはこんなところになります。
* Finderからの入力をうける?
* 変数を受け渡す?
* 条件分岐をシェルスクリプトで行う?
* シェルスクリプトでコマンドを実行する?

引っかかったポイント

分からないことだらけだったんですが、岩風呂さんの下記エントリーで勉強させてもらいました。

Automator&Shellscript入門その3

どのアクションも基本的には入力と結果が設定されています。入力されたデータは内部の処理に使われ、結果のデータからは消えているのが普通です。

しかし、例外があります。 ユーティリティ>【変数の値を取得】 ユーティリティ>【変数の値を設定】 のアクションです。

この変数アクションを使うことで、食べられてしまう運命の”フォルダパス”データを、シェルスクリプトに伝えるデータの中に割り込ませることが可能になります。
この変数アクションの扱いに癖があるようなのでこれを念頭に進めます。

作成手順:ファイルリスト、サイズ、新規ディレクトリ作成まで

ここから作成手順です。
まずAutomatorを起動し、サービスを選択して作成を始めます。
「”サービス”は、次の選択項目を受け取ります:」のタブで、ファイルまたはフォルダを選択します。
また、「検索対象」のタブをFinder.appにします。
次に左側の「ライブラリ」-「ユーティリティ」から「変数の値を設定」アクションをドロップします。
ConvertImagesTest.workflow 2013-04-26 16-35-24
するとアクションがつながっているのが分かります。
これが前のアクションから入力を受け取っている状態であることを表しています。
変数をクリックして「新規変数」として「対象ディレクトリ」を設定します。
この変数にオリジナルファイルの情報が収められます。
この要領で進めましょう。
ConvertImagesTest.workflow 2013-04-26 16-41-52
「テキストの入力を求める」「変数の値を設定」「新規フォルダ」を順番にドロップします。
「テキストの入力を求める」では最大サイズを半角数字で入力してもらいますので、説明文を入力し、「回答を必須」にします。また初期値を300とします。
このアクションでは「変数:対象ディレクトリ」を使いたくないので、オプションから「このアクションの入力を無視」をチェックします。
入力された数値を「変数の値を設定」に収めます。
変数は「ピクセル数」としました。
「新規フォルダ」を作成します。「Desktop」に「ConvertImages」という名前で作成する事にします。
「このアクションの入力を無視」をチェックします。
ここまでで以下の処理ができました。
* 「変数:対象ディレクトリ」変換したいファイルの情報
* 「変数:ピクセル数」最大サイズのピクセル数
* 「ConvertImages」変換後のファイル保存場所作成

作成手順:ファイルを他のディレクトリにコピーする

ConvertImagesTest.workflow 2013-04-26 17-13-00
「変数の値を取得」で「変数:対象ディレクトリ」を呼びます。
前のアクションとは切り離したいので「このアクションの入力を無視」をチェックします。
「フォルダの内容を取得」変数を使ってファイルの内容を取りに行きます。
「Finder 項目をコピー」ファイルを「ConvertImages」へコピーします。値の設定のためには、一度ディレクトリを手動で作っておく必要があります。
「フォルダの内容を取得」で「ConvertImages」ディレクトリ内のファイルを取りに行きます。
「変数の値を設定」で「変数:対象一覧」にファイルリストを収めます。
アクションが上から下へ流れてますね。
ここまでで以下のことができました。
* 変換したいファイルを「ConvertImages」へコピー。
* 「変数:対象一覧」にファイルリストを収めました。
この処理は冒頭で終わらせた方がすっきりしますが、説明のために…。

作成手順:sips コマンドで対象ファイルを縮小する

いよいよ sips コマンドですが、結論としてアクションはこうしました。
「変数の値を取得」で「変数:ピクセル数」を呼びます。「このアクションの入力を無視」をチェックします。
「変数の値を取得」で「変数:対象一覧」を呼びます。
「シェルスクリプトを実行」で2つの変数を使い、sipsコマンドを実行します。
ConvertImagesTest.workflow 2013-04-26 17-50-58
2つの変数をシェルスクリプトに渡したかったのですが、

Automator&Shellscript入門その4

変数アクションは受け取ったデータをそのまま次のアクションに引き渡しています。
ということで、ひとつの引数にまとまって渡されるようです。
どのような形で渡されているかですが、
Automator&Shellscript入門その3

perlでは、引数としてデータを受け取ると、@ARGVという特殊配列変数にデータが格納されます。また、データを次のアクションに渡すには標準出力のprint(またはecho)を使います。

1個1個データを引き出す場合$var1 = shift @ARGV;
なので、@ARGV配列のひとつめから「変数:ピクセル数」の値をshiftで取り出し、以降を「変数:対象一覧」とみなしforeachしました。

$maxpx = shift @ARGV;
if($maxpx !~ /^d+$/){
    $maxpx = 300;
}
foreach $fname(@ARGV) {
    $sips = `sips -g pixelWidth -g pixelHeight "$fname"`;
    $sips =~ /pixelWidth: (d+)s+pixelHeight: (d+)/im;
    $width = $1;
    $height = $2;
    if($width <= $maxpx && $height <= $maxpx) {
        next;
    }
    `sips -Z $maxpx "$fname"`;
}

ピクセル数は、半角数字に限定し、それ以外の入力があった場合は初期値にしてみました。
なお、perlスクリプトは、でこいさんを参考にさせて頂きました。

【Mac】Automatorで画像をリサイズする方法

作成手順:ImageOptimでファイルを処理する

あとはImageOptimでファイルを処理します。
「指定された Finder 項目を取得」で「ConvertImages」内の情報を取ります。 前からの入力を使っていないので「このアクションの入力を無視」をチェックしておきます。
「フォルダの内容を取得」でファイルリストを取ります。
「Finder 項目を開く」で「ImageOptim.app」を選択し、処理させます。
ConvertImagesTest.workflow 2013-04-26 18-11-50

使い方

保存したAutomatorファイルは、~/Library/Services に置かれます。
あとは変換したいファイルやフォルダを右クリックし、Automatorファイルを選択するだけです。
ConvertImagesTest.workflow 2013-04-26 18-22-50
ひとまず完成しました。
間違いなどありましたら教えて下さい。
しかし思ったより長文になってつかれた…。