便利で便利なImageJでプラグインを作りたいという話
日本語文献が少なすぎるので、話が進まぬ。
これは自分用メモという名の(英語文献との)戦いの記録である。
ImageJとは
こんな記事を探す人には不要な説明かもしれませんが、
オープンソースでパブリックドメインの画像処理ソフト
プラグインやマクロによる拡張性が高い
Wikiより
とのことなので、マクロやプラグインを使わないなんてもったいない!
とは言いつつも、これらを使いこなすためにはプログラミングの知識もさることながら、Imagejの開発者向け情報の理解が求められます。
マクロの方では、"Imagej macro function"などとググれば本家の関数解説と日本語によるIfやForの書き方が出るので、ある程度のプログラミング知識と英語の知識(あるいはGoogle翻訳とガッツ)があれば何とかなるかと(´ω`)
問題はプラグインである。ぜひ日本語文献の開拓が進んで欲しいものですが、(少)ないものは(少)ない、ということで、Web情報提供者の先駆けを目指す記事です。(と言いつつも、すでに日本語文献はあるわけですが。。。こちらとか。つまりもはや先駆けになることはできないのである)
まぁ、少なければ需要もあるかもという期待も込めつつ、この記事では備忘録的なものもかねて、英語文献"Writing ImageJ Plugins–A Tutorial"からプラグインの概要を解読し、重要そうなところを(極力)わかりやすいようにまとめると共に、
・ハロワのポップアップ
・グラデーションのかかった画像の作成
・開いている8Bit画像の白黒反転
・RGB画像をR、G、Bの3色に分ける
・ボタンを2つ配置しメッセージボックスと関連づける
というものの作成を目指します。
英語の参考文献は
Writing ImageJ Plugins–A Tutorial
本家
日本語では
ImageJ Plugin を作るための Java #1
ImageJプラグインの作成:序論
ちなみにボクはC++やVB畑の人間で、Javaには詳しくないので間違いがあったらごめんなさい、、、Javaを書くことを強いられているんだッ!
開発0歩目
Imagejでプラグインを開発するにあたって知っておきたい知識。
画像のタイプ
| タイプ | 定義 | 説明 |
|---|---|---|
| 8bitグレースケール | 0 ( =ImagePlus.GRAY8) | 白~灰色~黒色の画像。各画素をbyteで表現する |
| 8bitカラー | ルックアップテーブル(LUT)を用いた表現。画素値(0~255)がそれぞれ何色に相当するか、という情報を元に最大256色の画像を表現する。 | |
| 16bitグレースケール | 1 ( =ImagePlus.GRAY16) | 1つの画素値が0~65535となる。各画素の情報量が8bit画像の256倍となるとんでも画像。各画素をshortで表現する。 |
| RGBカラー | 3 ( =ImagePlus.COLOR_RGB)※ | ImagePlus. 赤緑青の3色を各8bitで表現した画像。ネットで見る画像はおそらく大体これになる。各画素はintで表現する |
| 32bitグレースケール | 2 ( =ImagePlus.GRAY32) | 1つの画素に32bitも割り振ったスーパーな画像。例えば各画素を0~1.0にしたりできる(すなわち実数) 。各画素をfloatで表現する。 |
| ※ImagePlus内ではCOLOR_256 = 3、COLOR_RGB = 4と定義されているが、NewImageで実際に作ると、3の時にRGB画像が作られ、4以上の数字では8bitとなる。 |
開発の第一歩
”Plugins”→”New”→”Plugin〇〇”を選択するとプラグインのエディタが表示されます。実行するためには保存が必要らしいので、ダイアログに従って保存しましょう。ファイル名にアンダーバーが必要らしいので、File_nameみたいに保存。説明ではImagej本体のフォルダの下にあるPluginフォルダに保存するだけでおkってなってたけど、Imagejにて表示されなかったので”ImageJ\plugins\”にMyPluginsというフォルダを作り、そこにMy_Pluginという名前で保存しました。
この時点でのトラブルシューティング
コンパイラー.jarが見つからない→JDKのインストールとImagejの最新版への更新を行ったら解決しました。どっちが効いたのかはわからぬ。。。
テンプレート
”Plugins”→”New”→”Plugin〇〇”で作られるテンプレートについて
忙しい人向け
PlugIn
必ずしも画像の入力を必要としないもの。例えば解析結果の処理を書きたい場合などはこちらになるかと。
PlugInFilter
画像の入力を求めるもの。画像のタイプによって実行しなかったりできる。
e.g. 16ビット画像以外の入力は認めぬ、RGB画像はダメ絶対 など
PluginFrame
新たなウィンドウを作りたいときはこちらを使うっぽい
PlugIn
標準で
public void run(String arg)
というメソッドが準備されています。ボクが試した時には
import ij.*;
import ij.process.*;
import ij.gui.*;
import java.awt.*;
import ij.plugin.*;
import ij.plugin.frame.*;
public class My_Plugin implements PlugIn {
public void run(String arg) {
ImagePlus imp = IJ.getImage();
IJ.run(imp, "Invert", "");
IJ.wait(1000);
IJ.run(imp, "Invert", "");
}
}
というテンプレートが書かれていました。これは表示されている画像を白黒反転し、1秒待ち、再び白黒反転する(すなわち元に戻す)という内容です。
PlugInFilter
こちらは
public int setup(String arg, ImagePlus imp)
public void run(ImageProcessor ip)
がテンプレートにあります。
import ij.*;
import ij.process.*;
import ij.gui.*;
import java.awt.*;
import ij.plugin.filter.*;
public class Filter_Plugin implements PlugInFilter {
ImagePlus imp;
public int setup(String arg, ImagePlus imp) {
this.imp = imp;
return DOES_ALL;
}
public void run(ImageProcessor ip) {
ip.invert();
}
}
Setupはプラグインがインスタンス化されたとき(おそらく準備できたとき?)に呼び出されます。で、その時に2つの引数が渡されるとか。
setupのargはPlugInのrunのargと同じ、setupのimpはImagejによってハンドルされており、現在アクティブな画像が渡されるとのこと。
返り値はフラグ。どのようなタイプの画像を扱うことができるかのフラグをセットできるみたいです。
| フラグ | 効果 |
|---|---|
| DOES_ALL | どんな画像でも受け入れる |
| DOES_RGB | RGB画像を受け入れる |
| DOES_16 | 16ビットグレースケール画像を受け入れる |
| DOES_32 | 32ビット浮動小数点グレースケール画像を受け入れる |
| DOES_8C | 8ビットカラー画像を受け入れる |
| DOES_8G | 8ビットグレースケール画像を受け入れる |
おまけ
| フラグ | 効果 |
|---|---|
| DOES_STACKS | スタックを受け入れる |
| DONE | runを実行しない |
| NO_CHANGES | ピクセルデータを書き換えない |
| NO_IMAGE_REQUIRED | 画像を必要としない |
| NO_UNDO | ”やり直し”に対応しない |
| ROI_REQUIRED | ROIを必要とする |
| SUPPORTS_MASKING | Plugin filters always work on the bounding rectangle of theROI. If this flag is set and there is a non-rectangular ROI, ImageJ will restore the pixelsthat are inside the bounding rectangle but outside the ROI |
| よくわからず。。 |
RunメソッドはImageProcessorを受け取り、実際の処理を行います。
runに渡されるipについては
引数にプロセッサーを持つ。 プロセッサを直接修正することも、新しいプロセッサと新しい画像をそのデータに基づいて元の画像を変更しないようにすることもできる。 プラグインの実行中は、元の画像はロックされている。
とのことらしいです。(英語文献をGoogle翻訳にかけて修正)
PlugInFrame
PluginインターフェースをimplementsするAWTのサブクラスなので、awtを利用したボタンやテキストボックスが配置されたウィンドウが作れるようです。
あなたのプラグインはPlugInFilterのサブクラスとして宣言できます、とのこと。
import ij.*;
import ij.process.*;
import ij.gui.*;
import java.awt.*;
import ij.plugin.frame.*;
public class Plugin_Frame extends PlugInFrame {
public Plugin_Frame() {
super("Plugin_Frame");
TextArea ta = new TextArea(15, 50);
add(ta);
pack();
GUI.center(this);
show();
}
}
ちなみにこれらの3種類間のテンプレートで、ij.pulugin.*、ij.plugin.filter.*、ij.pulugin.frame.*のどれがインポートされるか異なるとか。
Imagejのクラスたち
テンプレートが何をしているかがわかったら、今度は実際にImagejのAPIを活用して実際のプログラムを書いていきたいところ。その時にImagejにはどのようなクラスがあり、そそれで何ができるのかは大事。APIの解説ページのパッケージにあるij.guiとかij.measureはそれぞれ何やってくれるのってことですね。
ちなみにUMLクラス図とやらはこちら。
ij
ImageJ
ImageJの根幹となるもの。このクラスにはプログラムのメインエントリーポイントとImageJのメインウィンドウへの情報が含まれている。
IJ
様々な便利な関数がある。
//エラーメッセージの表示
static void error(java.lang.String message);
//エラーメッセージをlog画面に表示させることもできる
static void redirectErrorMessages();
//メッセージボックスの表示
static void showMessage(java.lang.String message);
static void showMessage(java.lang.String title, java.lang.String message);
//キャンセルボタンのあるメッセージボックスの表示
static boolean showMessageWithCancel(java.lang.String title, java.lang.String message);
//Resultウィンドウに文字を書き出す
//非推奨らしいです
static void write(java.lang.String s);
//Resultウィンドウの項目(AreaやMeanなど)を書く
//tab文字で区切ることで複数項目を設定できます。
//このメソッドを呼び出すと表示されているものはリセットされます
static void setColumnHeadings(java.lang.String headings);
//logウィンドウに表示
static void log(java.lang.String s);
//数字を文字に変換
//小数点何桁までかも選べる
static java.lang.String d2s(double n);
static java.lang.String d2s(double n,int precison);
//ステータスバー(メインのImagejのウィンドウ)に文字を書き出す
static void showStatus(java.lang.String s);
//ステータスバー(メインのImagejのウィンドウ)にのプログレスバーを操作する
//0.0~1.0までの値を与えるか、現在の値/目標値を与える
static void showProgress(double val);
static void showProgress(int currentIndex, int finalIndex);
//ユーザーに値の入力を求めることもできる
static double getNumber(java.lang.String prompt, double defaultValue)
static java.lang.String getString(java.lang.String prompt, java.lang.String defaultString)
//メニューにある他のプラグインを使う
//MacroのRecorder機能を使えば引数として何を与えればよいかの参考になるかと思います。
static void run(java.lang.String command);
static void run(java.lang.String command,java.lang.String options);
//現在アクティブな画像を取得する
static ImagePlus getImage();
ImagePlus
ImageProcessorクラスに基づくクラス。ImageJでの画像はこのクラスに基づいて表現されている。
つまり画像処理のプラグインを作るときに避けては通れないと言えるかもしれない。
ちなみに表示のためにはImageWindowクラスが必要となります。
show()メソッドで表示できるっぽい。
もしかしてImageWindowクラスを使うことって基本ないんですかね
ImagePlus ()
//例えばhttps://imagej.nih.gov/ij/images/lena.jpg を指定すると
//nihのページからレナさんの画像をダウンロードして表示する。
ImagePlus ( java.lang.String pathOrURL)
//titleに指定した文字はImageWindowクラスで表示するときのタイトルとなる。
ImagePlus ( java.lang.String title, java.awt.Image img)
ImagePlus ( java.lang.String title, ImageProcessor ip)
ImagePlus ( java.lang.String title, ImageStack stack)
//ウィンドウを作り画像を表示する
void show();
//ステータスバー(Imagejの本体部分の下にありカーソルの座標やその部分の色を表示する部分)
//に文字を表示することもできる
void show(java.lang.String statusMessage);
//showメソッドでウィンドウを表示した後に
//ImageProcessorクラスなどで画像に変更を加えた後
//drawを呼び出すことで画像を更新する
void draw();
//更新する範囲を指定することもできる
void draw(int x, int y, int width, int height);
//更新して表示する
void updateAndDraw();
//画像だけでなく画像情報(タイプやサイズなど)も更新して表示する
//内部でupdateAndDrawを呼び出している
void updateAndRepaintWindow();
//表示されていれば閉じる
void hide();
//RGBや8bitなどを示すIDを返す。
int getType ()
//画像の幅を返す。
int getWidth ()
//画像の高さを返す。
int getHeight ()
//ウィンドウに書かれているタイトルを返す。
java.lang.String getTitle ()
//様々な画像の情報を返す。
ij.io.FileInfo getFileInfo ()
//java.awt.Image型の画像を返す。
java.awt.Image getImage ()
//タイトルをセットする。
void setTitle ( java.lang.String title)
//画像をセットする。
void setImage ( java.awt.Image img)
ImagePlusにはユーザー作成のプロパティもセットできるようです。
java.util.Properties getProperties()
java.lang.Object getPropertiy ( java.lang.String key)
void setProperty ( java.lang.String key, java.lang.Object value)
ImageStack
画像の配列に関するクラス。
WindowManager
開いているウィンドウを管理するクラス。
ij.gui
ImageWindow
出番がないと言ったな、あれは嘘だ
Imagejが画像などを表示する時のウィンドウに関するクラスのようです。
内部に
ImageCanvas:画像の描写やサイズなどの情報の描写領域に関するクラス
ImagePlus:画像のデータを持つクラス
等を所有します。
そしてこのImageWindowクラス。java.awt.Frameあたりから継承されているのでボタンなどの配置が可能です。なのでボタンなどのパーツを配置したウィンドウが作りたい場合にはImagePlusのshowだけでなくImageWindowクラスに対するアクセスが必要となります。
awtの詳しい解説はC++畑の人間には荷が重いので優れた他の記事にお任せするとして、この記事の末に2つのボタンを配置し、それぞれに異なる反応をさせるサンプルを置いておきます。
ちなみに以下のようにすることで画面が表示されている間だけWhileループを回すこともできる用です。
ImagePlus ip = new ImagePlus(imageProcessor型の変数);
ImageWindow iw = new ImageWindow(ip);
iw.running = true;
double count =0;
while(iw.running){
IJ.log(IJ.d2s(count));
count++;
IJ.wait(100);
}
ImageCanvas
前述の画像やサイズなどの描写に関するクラスです。
//現在のカーソルの座標を取得します
java.awt.Point getCursorLoc();
ProgressBar
大量の画像を読み込むときに進捗を示してくれたりするあれ。
使うだけなら
for (int n = 1;n<=10 ;n++ ) {
IJ.showProgress(n / 10.0);
IJ.wait(100);
}
とかだけでいい気がする。
NewImage
新しい画像を作るクラス。
//testというタイトルで200x100、8bit、1つのスタックにnStack枚の画像を作り表示する
int nStack=3;
ij.gui.NewImage.open("test",200,100,nStack,ImagePlus.GRAY8,0);
//新しい画像作成のダイアログを表示
ij.gui.NewImage ni = new ij.gui.NewImage();
また、NewImageクラスには画像作成のための便利な静的メソッドがあります。
//8bitグレースケール画像
static ImagePlus createByteImage(java.lang.String title, int width, int height,int slices, int option);
//32bitグレースケール画像
static ImagePlus createFloatImage(java.lang.String title, int width, int height,int slices, int option);
//RGB画像
static ImagePlus createRGBImage(java.lang.String title, int width, int height,int slices, int option);
//16bit画像
static ImagePlus createShortImage(java.lang.String title, int width, int height,int slices, int option);
//(一応)任意のビット深度の画像。試しに10bitで作ってみたらエラー出ました、、、
static ImagePlus createImage(java.lang.String title, int width, int height,int nSlices, int bitDepth, int options);
| オプション名 | 効果 |
|---|---|
| FILL_BLACK | 黒塗り |
| FILL_WHITE | 白塗り |
| FILL_RAMP | 水平方向にグラデーション |
| FILL_NOISE | ノイズがかった状態 |
| FILL_RANDOM | ノイズがかった状態 |
| これらのオプションはNewImageクラスにて定義されているので、NewImage.(オプション名)とすることでアクセスできる。 |
Roi dialog window etc...
ij.io
ファイルの読み込み/保存などを行う。
ij.macro
マクロ言語と組み込みマクロのパーサーとして構文解析をしてくれる。
//Logに5を書き出す
ij.macro.MacroRunner mr= new ij.macro.MacroRunner("print(5)");
ij.measure
測定用のクラスが含まれる。
ij.plugin
Imagejの多くの機能はプラグインとして実装されていることから、pluginクラスあるいはそのサブパッケージを探せば大体見つかるって参考文献が言ってた。
ij.plugin.filter
(必要かどうかを含めて)理解できず。
ij.plugin.frame
理解できず。
ij.process
画像自体のデータを保存・処理したりするクラスの集まり
ImageProcessor
画像を処理する時の一番のベースとなるクラス。ここからImagePlusを作ったりできる。PlugInFilterからプラグインを作ったときにはrunメソッドの引数として渡される。
画素へのアクセスを含め、画像の処理を行いたいときにはこのクラスが役に立ちそうです。
画素へのアクセス
//画素データを配列として受け取る。
//配列のタイプは画像のタイプに依存するので、目的の画像のタイプに合わせて
//返り値をキャストしてやる必要があります。
//また、画像は2次元ですが、返り値は1次元の配列になっているので、
//この配列を介して画素にアクセスするためには[x + y × "画像の幅"]などとすると良いでしょう。
getPixels()
//ImageProcessorの型、サイズに合わせた配列をセットする。
void setPixels(java.lang.Object pixels)
//各画素に個別にアクセスして値を取得/設定する
int getPixel(int x, int y)
void putPixel(int x, int y, int value)
float getPixelValue(int x, int y)
//画素単位のアクセスが可能。RGB画像の時に便利になると思われます。
//下にサンプルを作ってみました。
int[] getPixel(int x, int y, int[] iArray)
// (x,y)から始まる行/列にアクセスし、値を取得/設定する
void getRow(int x, int y, int[] data, int length)
void putRow(int x, int y, int[] data, int length)
void getColumn(int x, int y, int[] data, int length)
void putColumn(int x, int y, int[] data, int length)
//(x1, y1) → (x2, y2) までの画素値を返す
10. double[] getLine(int x1, int y1, int x2, int y2)
//RGB画像を受け取る例
//RGB画像が収まっているImageProcessorの変数名をmyProcessorとする
int[] pixels = (int[]) myProcessor.getPixels();
//(100, 50)の座標の画素にアクセス
//画像の幅をimageWidthと言う変数に代入しているとする
int pixel = pixels[100 + 50*imageWidth];
int red = (int)((pixel & 0xff0000) >> 16);
int green = (int)((pixel & 0x00ff00) >> 8);
int blue = (int)((pixel & 0x0000ff) );
//このサンプルはグラデーションのかかったRGB画像を作ります。
//`getPixel`で値を取得していますが、結果としては"a0 b50 c100"と表示されます。
//このようにビット演算子を使わなくても特定画素の各チャネルにアクセスできていることが分かります。
import ij.*;
import ij.process.*;
import ij.gui.*;
import java.awt.*;
import ij.plugin.*;
import ij.plugin.frame.*;
public class My_Plugin implements PlugIn {
public void run(String arg) {
int pxs[] = new int[256*256];
for(int y = 0; y<256; y++){
for(int x = 0; x<256; x++){
pxs[x+y *256]=(int)(y + (y/2 <<8)) ;
}
}
ColorProcessor bp = new ColorProcessor(256,256);
bp.setPixels(pxs);
ImagePlus ip = new ImagePlus("test",bp);
ImageWindow iw = new ImageWindow(ip);
ImageProcessor IP = ip.getProcessor();
int Arr[] = new int[3];
IJ.showMessage("a"+ IP.getPixel(0,100,Arr)[0] +
" b"+ IP.getPixel(0,100,Arr)[1] +
" c"+ IP.getPixel(0,100,Arr)[2]);
}
}
コピー
画像を処理したいときに、元画像に変更を加えたくないことがあります。その場合にはNewImageクラスを用いる、ImagePlusクラスのcreateImagePlusメソッドを用いるなどすれば新しい画像が作られるのでした。しかしこれらのクラスで作られた画像は空(=真っ白、真っ黒など)でした。それらの画像に元画像をコピーしたい時に各画素にアクセスし、値を一つ一つコピーしていくのはスマートとは言い難いかも知れません。
その時には以下のメソッドを用いると良いでしょう。
//ipの内容をxloc,ylocの場所に上書きする
void insert(ImageProcessor ip, int xloc, int yloc);
//ipの内容をxloc,ylocの場所にmodeに従って書き込む
void copyBits(ImageProcessor ip, int xloc, int yloc, int mode);
modeにはij.processのBlitterで定義されているinterfaceを用いることができます。
dstがコピー先の各画素の値、srcがコピー元の各画素の値を示します。
自分は
dst.copyBits(src, xloc, yloc, Blitter.*mode*)
という認識で使ってます。
| 定義名 | 効果 |
|---|---|
| COPY | dst=src |
| COPY_INVERTED | dst=255-src (8-bits and RGB) |
| ADD | dst=dst+src |
| SUBTRACT | dst=dst-src |
| MULTIPLY | dst=src*src |
| DIVIDE | dst=dst/src |
| AVERAGE | dst=(dst+src)/2 |
| DIFFERENCE | dst=abs(dst-src) |
| AND | dst=dst AND src |
| OR | dst=dst OR src |
| XOR | dst=dst XOR src |
| MIN | dst=min(dst,src) |
| MAX | dst=max(dst,src) |
| COPY_TRANSPARENT | 白い画素を透明にしてコピーする |
| COPY_ZERO_TRANSPARENT | 黒い画素を透明にしてコピーする |
画像のタイプの変換
画像のタイプを変換したいこともあるかもしれません。可能です。
//変換後の方は関数名が示す通りです。
//doScalingによって0-255、0-65535の範囲にスケーリングするかを決められるようです。
ImageProcessor convertToByte(boolean doScaling);
ImageProcessor convertToRGB()
ImageProcessor convertToShort(boolean doScaling)
//float型への変換
//この関数、キャリブレーションテーブルなるものがセットされている場合は
//キャリブレーションもしてくれるようです。
ImageProcessor convertToFloat()
//キャリブレーションについて
//ルックアップテーブルをセットすることでそれに従った変換が可能です。
//byte型への変換の時にはfloat[256]を、
//Short型への変換の時にはfloat[65536]を
//ルックアップテーブルとして使用します。
//使用例
//この例だと8bitグレースケール画像に対し
//画素値が100の時だけ1.0へ、それ以外の時には0.0へと変換します。
float[] calib = new float[256];
for (int k = 0; k <256 ; k++ ) {
calib[k] = (float)0.0;
}
calib[100] = (float)1.0;
(srcImageProcessor).setCalibrationTable(calib);
ImageProcessor ip = (srcImageProcessor).convertToFloat();
ImagePlus IpF = new ImagePlus("float",ip);
IpF.show();
二値化
二値化すらここで定義されているぜ
//閾値を設定しての二値化
void threshold(int level);
//閾値を自動で設定しての二値化
void autoThreshold();
//自動で設定される閾値の取得
int getAutoThreshold();
roi(Regions of interest)の使用
1. void setRoi(int x, int y, int width, int height)
2. void setRoi(java.awt.Rectangle r)
3. viod serRoi(java.awt.Polygon r)
Roiをセットする。
Roiの基底クラスはij.guiにて定義されている模様です。
ij.guiには長方形以外のRoiのクラスもあり、Line、OverROI、PolygoneRoi、FreehandRoi、PointRoi、ShapeRoi、TextRoiなどがあるそうです。
ほかにも便利な関数が多数あります
ヒストグラムの取得、フィルタ、画像中の最大値/最小値の取得、反転、描写などはImageProcessorクラスに定義されているようです。
ImagePlusとの相互変換
| 変換方向 | 方法 | 説明 |
|---|---|---|
| ImagePlus → ImageProcesosr | (ImagePlus).getProcessor() | ImagePlusのgetProcessorメソッドを使う |
| ImageProcesosr → ImagePlus | ImagePlus(ImageProcessor ip) | ImagePlusのコンストラクタの引数として渡す |
画素値をキャストするときの注意
javaではbyteやshortはsigned、すなわち-128~127や-32768~32767の範囲で定義されていますが、我々が画像処理をする時は0~255や0~65535の範囲だとして処理をします。
それゆえ数値のキャストの際には符号に注意したキャストが必要になります。
| 変換方向 | 方法 |
|---|---|
| byte → int | int pix = pix_byte & 0xff; |
| int → byte | pix_byte = (byte) pix; |
| short → int | int pix = pix_short & 0xffff; |
| int → short | pix_short = (short) pix; |
とする必要があります。
また、このクラスは抽象クラスでインスタンスを生成することは出来ません。引数として入力されている、ImagePlusから生成した、以外の場合。。例えば0からImageProcessorを介して画像を作りたいなどの場合には以下のサブクラスを用いるようです。
インスタンス生成時には引数として(int width, int height)が必要となります。
1. ByteProcessor
8bit画像のために使われる。BinaryProcessorのサブクラス。
2. ShortProcessor
16bit画像のために使われる。
3. ColorProcessor
RGB画像のために使われる。1つの画素に32bitを割り振り、その中で各色を8bitで表現する。
4. FloatProcessor
32bit画像のために使われる。
この時点でのトラブルシューティング
仕様表を見てもよくわからない→ソース読むしかねぇ、、、
i.e.
参考文献中にある、ij.plugin内のconverterクラス。画像をRGB→8bitなどと変換できるようですが、仕様表ではメソッドはrunとconvertしか実装されていませんでした。それぞれの引数もstringで、画像はいつ選択するんだろう、と思ったら、runメソッドの中でWindowManager.getCurrentImage()を実行していることが分かりました。
なので
Converter cnv =new Converter ();
cnv.run("");//現在開いている画像を選択
cnv.convert("8-bit");//変換
とすることで利用できることが分かりました。
サンプル集
ハローワールド
ハロワをするのであればPlugInを使用するのが一番シンプルかと。
runの中にこれを書きます。
IJ.showMessage("hello","world!");
グラデーションのかかった画像
準備した各画素を示す配列に二重のforループで値をセットし、それをImageProcessorクラスにセットします。そしてそれをImagePlusクラスに変換し、ImageWindowクラスで表示します。
import ij.*;
import ij.process.*;
import ij.gui.*;
import java.awt.*;
import ij.plugin.*;
import ij.plugin.frame.*;
public class My_Plugin implements PlugIn {
public void run(String arg) {
byte pxs[] = new byte[256*256];
for(int y = 0; y < 256; y++){
for(int x = 0; x < 256; x++){
pxs[x + y * 256]=(byte) y;
}
}
ByteProcessor bp = new ByteProcessor(256,256);
bp.setPixels(pxs);
ImagePlus ip = new ImagePlus("test",bp);
ImageWindow iw = new ImageWindow(ip);
}
}
白黒反転
参考文献に書かれているサンプルそのまんまっていうね。
import ij.*;
import ij.process.*;
import ij.gui.*;
import java.awt.*;
import ij.plugin.filter.*;
public class Filter_Plugin implements PlugInFilter {
ImagePlus imp;
public int setup(String arg, ImagePlus imp) {
if (arg.equals("about"))
{
showAbout();
return DONE;
}
return DOES_8G+DOES_STACKS+SUPPORTS_MASKING;
}
public void run(ImageProcessor ip) {
byte[] pixels = (byte[])ip.getPixels();
int width = ip.getWidth();
Rectangle r = ip.getRoi();
int offset, i;
for (int y=r.y; y<(r.y+r.height); y++)
{
offset = y*width;
for (int x=r.x; x<(r.x+r.width); x++)
{
i = offset + x;
pixels[i] = (byte)(255-pixels[i]);
}
}
}
void showAbout() {
IJ.showMessage("About Inverter_...",
"This sample plugin filter inverts 8-bit images. Look\n" +
"at the 'Inverter_.java' source file to see how easy it is\n" +
"in ImageJ to process non-rectangular ROIs, to process\n" +
"all the slices in a stack, and to display an About box.");
}
}
・RGB画像をR、G、Bの3色に分ける
import ij.*;
import ij.process.*;
import ij.gui.*;
import java.awt.*;
import ij.plugin.filter.*;
public class Filter_Plugin implements PlugInFilter {
ImagePlus imp;
public int setup(String arg, ImagePlus imp) {
this.imp = imp;
return DOES_RGB;
}
public void run(ImageProcessor ip) {
ImagePlus ipMain = new ImagePlus("main",ip);
int width = ipMain.getWidth();
int height = ipMain.getHeight();
byte[] r = new byte[width * height];
byte[] g = new byte[width * height];
byte[] b = new byte[width * height];
int[] px = (int[])ip.getPixels();
for (int y = 0; y < height ; y++ ) {
for (int x = 0; x < width ; x++ ) {
r[x + y * width] = (byte)( (px[x + y * width] & 0xff0000) >> 16);
g[x + y * width] = (byte)( (px[x + y * width] & 0x00ff00) >> 8);
b[x + y * width] = (byte)( (px[x + y * width] & 0x0000ff));
}
}
ByteProcessor IPr = new ByteProcessor(width,height);
ByteProcessor IPg = new ByteProcessor(width,height);
ByteProcessor IPb = new ByteProcessor(width,height);
IPr.setPixels(r);
IPg.setPixels(g);
IPb.setPixels(b);
ImagePlus ipr = new ImagePlus("red",IPr);
ImagePlus ipg = new ImagePlus("grean",IPg);
ImagePlus ipb = new ImagePlus("blue",IPb);
ipr.show();
ipg.show();
ipb.show();
}
}
ボタンを2つ配置しメッセージボックスと関連づける
このサンプルではPlugInFilterのプラグインで開いている画像にボタンを2つ加えたウィンドウを作るサンプルです。新たに作られたウィンドウのサイズを変更するとボタンがあり、それをクリックするとそれぞれがメッセージボックスを表示します。
import ij.*;
import ij.process.*;
import ij.gui.*;
import java.awt.*;
import java.awt.event.*;
import ij.plugin.filter.*;
public class Filter_Plugin implements PlugInFilter {
ImagePlus imp;
public int setup(String arg, ImagePlus imp) {
this.imp = imp;
return DOES_ALL;
}
public void run(ImageProcessor ip) {
ImagePlus IP = new ImagePlus("test",ip);
ImageWindow iw = new ImageWindow(IP);
Button b1 = new Button("b1");
b1.addActionListener(new b1ActLis());
iw.add(b1);
Button b2 = new Button("b2");
b2.addActionListener(new b2ActLis());
iw.add(b2);
//サイズ変更のマクロを走らせることで、ウィンドウの大きさを
//ボタンを含めた最適な大きさに変えることができる模様
IJ.run("Out [-]");
IJ.run("In [+]");
}
class b1ActLis implements ActionListener{
public void actionPerformed(ActionEvent e){
IJ.showMessage("b1");
}
}
class b2ActLis implements ActionListener{
public void actionPerformed(ActionEvent e){
IJ.showMessage("b2");
}
}
}
解説文章内で見つけた重要そうな事柄
・プラグインのクラス内でpackageを使ってはいけない
・Imagejのライブラリと標準のJavaライブラリ以外のライブラリを使いたい場合は"ImageJ\jre\lib\ext"ディレクトリに置くのが一番シンプルらしい
終わりに
自分の実力不足や、需要がわからず、なんとなく中途半端な記事になってしまった印象があります。こんな記事でも誰かの参考になれば望外な幸せです。