そもそもの発端は、普段の作業でよくPhotoshop → tiff書き出し → OpenCVなどプログラム上で画像処理 → 再度Photoshop読み込み…の繰り返しで作業することが多くて効率が悪く、願わくばPhotoshopにOpenCVなどの画像処理ライブラリをプラグインとして取り込んで一括処理できれば便利と考えたこと。
いつもなにかとお世話になっているPhotoshopだが、なぜかあまり情報のないプラグインの作り方についてのメモ。
バージョンが上がるにつれて使い切れないほどの機能が増えていくのではあるが、それらはあくまでもマーケティングの結果ある程度のニーズがあると判断されたもののみ。
これらを寄せ集めてもなお図星の機能がない場合にプラグインというバックドアを作っておいてくれている。
コマーシャルなプラグインも有名・無名取り混ぜて数多くあるが、C++が扱えれば自作することもできる。
そのためのSDKも公開されていて、ユーザ登録すれば誰でもDLできる。
https://www.adobe.com/devnet/photoshop/sdk.html
しかしながら、超有名なソフトにもかかわらず、そのプラグインの開発を始めるにあたって初心者向けの分かりやすい資料というものがほとんどない。
SDKに幾つかのドキュメントが付属してくるが、概念が分かっている人向けなので不親切。
Web上にもわざと隠蔽しているのではないかと思うほど情報が乏しい。
そんな中で、比較的分かりやすくて十分な情報が得られる記事が非常に古いが以下。
http://preserve.mactech.com/articles/mactech/Vol.15/15.04/PhotoshopPlug-InsPart1/index.html
http://preserve.mactech.com/articles/mactech/Vol.15/15.05/PhotoshopPlug-InsPart2/index.html
前世紀のPPC版MAC向けの記事なので、現在では通用しない部分もあるが大筋の重要な部分は変わっていない。特にPart1が必読。
以下はこれらを踏まえて現状に即して解説した自分用メモ。
今回使用したWindows用SDK(adobe_photoshop_sdk_cc_2017_win.zip)を解凍してみると直下には以下の三つのフォルダがある。
connectionsdk
images
pluginsdk
プラグインを作成する限りではpluginsdk以下のみが必要で、その中身のフォルダはこんな感じ。
documentation
photoshopapi
samplecode
説明する必要もないほど分かりやすいフォルダ名。
photoshopapi以下がプラグイン用のAPI本体で、自分が作ったプラグインのソースと共にこの中から必要なものをコンパイル・リンクして8bfファイル(実質DLL)を生成する。8bfファイルがプラグインの本体となる。
最も一般的な画像処理フィルタのプラグインの勉強をするにはサンプルの中でもdissolveを解析することがお勧めされている。
samplecode/filter/dissolve
加えて、ほとんどの付属サンプルはユーティリティとして以下の共通ライブラリを使用。
samplecode/common
dissolveやcommonの中のソースと上のサイトの情報を時間をかけて見比べてみると大体のプラグインの作り方が見えてくるはず。
サンプルのdissolveそのものはフィルタ起動した際に表示されるDialogで指定した頻度と色でランダムに点を打つという簡単なものだが、プラグイン作成に普通に使いそうな機能を一通り含めてくれている。
dissolveのコードを理解できれば最低限のプラグインを作ることはできるようになるはず。
しかしながら、dissolveの内容は結構盛りだくさんなので、解読を始めた当初はかなり苦行を強いられるはず。
なぜかというと以下のような少々手間のかかる機能を含んでいるから。
・GUI表示
・GUI表示時Preview
・現状パラメータのレジストリ保持・復元
・いろいろなpixel深度(色空間も?)に対応
・などなど
ということで、自身の勉強のためにDissolveから更に機能を削除して、本当に最低限の機能、つまり画像処理のみを行うプラグインを作ってみた。
https://github.com/delphinus1024/minimum_ps_plugin
まだよく分かっていない部分も多くて、正しい使い方かどうかはいささか自信がないが、さしあたり動作しているのでよしとする。
機能は以下の通り。
・呼び出すとGUIもなにも表示せず、画像のRGB各チャンネルのレベルを独立して操作する
・パラメータはなし(パラメータが介入するとGUIやレジストリ操作などの雑多なコードが入るので)
・RGB8bitのみ対応
・VisualStudioのIDEを使わず、コンソール上でnmakeビルド
・とにかく余計なコードとライブラリは極力排除してシンプル化
・Photoshop Win64版 only
・CC2017で動作チェック済
ビルド方法はリンク先参照。
Dissolveと比べると遥かに簡単になっているが、以下解説が必要そうな箇所を列挙。
・PiPL
PlugIn Property Listの略。
簡単に言うと、Photoshopがプラグインをロードする際にその素性を調べるためのプロパティ群で、Windows版の場合はリソースとしてプラグイン本体に格納される。
今回のプログラムではcommon/minimum.rがそのソースファイルとなる。
(大昔のMAC環境の名残らしいのだが)rファイル->rrファイル->piplファイルという順で変換(コンパイル?)していく。(その際SDKに付属してくるsamplecode\resources\cnvtpipl.exeを使用する。)
出来上がったpiplファイルはwin/minimum.rcでincludeされてリソースとしてプラグインに取り込まれる。
rファイルの内容はまだ完全に理解できていないのだが、エントリポイントの関数名、プラグイン名、ベンダー名、サポートする色空間・色深度、使用する最大メモリ領域等を適宜記述しておく。
その中の重要な項目はrファイルでincludeしているminimumScripting.h内でdefineしている。
・minimum.cpp
これがプラグインのプログラム本体となる。
上記のrファイルで指定したPluginMainがエントリポイントとなり、以下のようにselectorの値を変えて何度かコールバックされる。
filterSelectorParameters → filterSelectorPrepare → filterSelectorStart → filterSelectorContinue(0~複数回) → filterSelectorFinish
PluginMainの引数selectorの値は状態で状態遷移のたびにコールバックされると考えるとわかりやすい。
それぞれの役割は以下の通り。
filterSelectorAbout: 処理とは関係ない。Aboutがクリックされたときに呼ばれる。
filterSelectorParameters: フィルタ呼び出し時に呼ばれる。必要であればダイアログボックスなどを表示してパラメータ入力を促す。バッチ処理時やフィルタの再実行の時はこのコールは省略されるので、必要であればレジストリなどに最後のパラメータを保存しておく。
filterSelectorPrepare: 前処理を行う。メモリ確保など。
filterSelectorStart: 処理開始。実際の画像処理を行う。
filterSelectorContinue: 必要であれば、何度かに分けて画像処理を行う。おそらく処理時間が長い時のハングアップ防止やメモリ搭載の少ないPCでの分割処理用。 gFR->outRect及びgFR->inRectに0以外をセットすると何度でも呼ばれる。これらに0をセットすると処理終了の意味。
filterSelectorFinish: 後処理を行う。
今回は簡単なのでfilterSelectorStartでのみで処理を行っている。
PluginMainの引数filterRecord gFRにはいろいろなパラメータが格納されていて、これらを理解するのがプラグイン作成のカギとなる…とはいえまだ完全には理解できていないが。
画像処理の手順はStartProc関数内に凝縮してあるが、少々解説すると、
gFR->inLoPlane = 0;
gFR->inHiPlane = planes - 1;
gFR->outLoPlane = 0;
gFR->outHiPlane = planes - 1;
gFR->outRect = gFR->filterRect;
gFR->inRect = gFR->filterRect;
あたりで処理を行うプレーンや領域を指定してからgFR->advanceState()で処理データを取得、これに対して画像処理を行う。
おそらく昔のPCはメモリが乏しく、一括処理が難しかったのでこういう仕組みになっているのだと思われる。
今回はすべてのプレーンと領域を指定して一括処理している。RGBそれぞれのレベルを適当に弄っているだけだが。
gFR->inData、gFR->outDataがそれぞれ入力画像と結果格納画像となり、フォーマットによって格納の仕方が異なるので、ポインタの型やその進め方に注意。今回はRGB 8bitのみ対応。
まだよくわからないのが、マスクが設定されているときの処理で、本来これをプラグイン側で判定して画像処理に反映させるはずなのだが、なぜか最近のPhotoshopだとマスクが常にOFFになっている。Photoshop側でケアするように仕様が変わったのかもしれないが要調査。
そういうこともあって今回はマスク関係はコメントアウトしているが、Photoshop側できちんとマスク処理は行われているみたい。
・出来上がったプラグイン8bfファイルはデフォルトだと以下の場所に適当にサブディレクトリを作ってコピーする。管理者権限が必要。
C:/Program Files/Adobe/Adobe Photoshop CC 2017/Plug-ins
ちなみにこのプログラムの画像処理部分にOpenCVの簡単なフィルタ処理を入れてみたところ、期待動作することをを確認。
とりあえず最低限の目的は達成できたようです。