ImageJでPlug-inを作る時の参考になれば幸いです。
画像処理Plug-inは、Interface ij.plugin.filter.PlugInFilterの 実装として作ります。ここではまず、Test_というPlug-inを 作ってみます。この場合、少なくとも実装する必要があるのは、 void run(ImageProcessor ip)と int setup(java.lang.String arg, ImagePlus imp)です。 setup()は、Plug-in Filterが処理できる画像の種類を 返す初期関数、run()は、実際の処理関数です。 詳しくは、API referenceを参照して下さい。 Plug-inの名前には_が含まれている必要があるようです。
import ij.*;
import ij.process.*;
import ij.gui.*;
import ij.plugin.filter.*;
public class Test_ implements PlugInFilter
{
public int setup(String arg, ImagePlus img)
{
if (IJ.versionLessThan("1.28e"))
return DONE;
else
// どの種類の画像も扱えるし、マルチページtiff
// なども扱えることを言う。
return DOES_ALL+DOES_STACKS;
}
public void run(ImageProcessor ip)
{
// 幅を取得
int width = ip.getWidth();
// 高さを取得
int height = ip.getHeight();
int[] p1 = new int[3];
// load
for (int y=1; y<height; y++)
{
for (int x=0; x<width; x++)
{
// (x,y)のPixelデータを取得。
// int[] は、RGBである。
// !!!! 注意 !!!!
// 以降の例題ではほとんどこのgetPixelを使っていますが、
// これはプログラムを見やすくするためです。
// 画像の画素数だけ呼び出すので、
// 実用的ではありません。実際に使う場合は、
// getPixelsなどを使って高速化してください。
p1 = ip.getPixel(x, y, p1);
}
}
}
}
この内容をTest_.javaという名前で、 ImageJのディレクトリの中のpluginsというディレクトリに 保存してください。
コンパイルする時は、以下のようにして、ImageJのライブラリの 位置をjavacに教える必要があります。
コンパイルが終ってTest_.classができれば、java -jar ij.jarなりして ImageJを実行します。一番右のPluginsというメニューの中に Testという項目ができているでしょう。そうしたら、あとは画像を開いて その項目を選ぶだけです。 上の例では、画像の縦横を取得し、1つ1つのPixelを 読むという動作をします。画像は変更されず、見かけ上はなにも起きません。
次は、それぞれの画素に対してFilterをかけてみましょう。ここでは、 それぞれの画素に対し、そこが明るさで200より小さければ 黒く、それ以外はそのままというものを作ってみます。今度は、Test2_.java という名前にして下さい。
import ij.*;
import ij.process.*;
import ij.gui.*;
import ij.plugin.filter.*;
public class Test2_ implements PlugInFilter
{
// 明るさ Y = 0.299R+0.587G+0.114B
// 色差 Cr = 0.500R-0.419G-0.081B
// 色差 Cb = -0.169R-0.332G+0.500B
// 画素から明るさを計算
private double getY(int[] a)
{
if (a.length != 3)
return 0;
return 0.299*((double)a[0])+0.587*((double)a[1])+0.114*((double)a[2]);
}
public int setup(String arg, ImagePlus img)
{
if (IJ.versionLessThan("1.28e"))
return DONE;
else
// どの種類の画像も扱えるし、マルチページtiff
// なども扱えることを言う。
return DOES_ALL+DOES_STACKS;
}
public void run(ImageProcessor ip)
{
// 幅を取得
int width = ip.getWidth();
// 高さを取得
int height = ip.getHeight();
int[] p1 = new int[3];
int[] black = {0, 0, 0};
// load
for (int y=1; y<height; y++)
{
for (int x=0; x<width; x++)
{
// (x,y)のPixelデータを取得。
// int[] は、RGBである。
p1 = ip.getPixel(x, y, p1);
// 明るさが100より小さければ黒にしてしまう。
if (getY(p1) < 100)
ip.putPixel(x, y, black);
}
}
}
}
ここでは、putPixelという関数で出力しています。
今度は、画像に対し、計測を行なってその結果を表で出力 するものを考えてみましょう。
import ij.*;
import ij.process.*;
import ij.gui.*;
import ij.plugin.filter.*;
public class Test3_ implements PlugInFilter
{
// 明るさ Y = 0.299R+0.587G+0.114B
// 色差 Cr = 0.500R-0.419G-0.081B
// 色差 Cb = -0.169R-0.332G+0.500B
// 画素から明るさを計算
private double getY(int[] a)
{
if (a.length != 3)
return 0;
return 0.299*((double)a[0])+0.587*((double)a[1])+0.114*((double)a[2]);
}
public int setup(String arg, ImagePlus img)
{
if (IJ.versionLessThan("1.28e"))
return DONE;
else
// どの種類の画像も扱えるし、マルチページtiff
// なども扱えることを言う。
return DOES_ALL+DOES_STACKS;
}
public void run(ImageProcessor ip)
{
// 幅を取得
int width = ip.getWidth();
// 高さを取得
int height = ip.getHeight();
int[] p1 = new int[3];
// 表のsetup
// 例えば、一行に3つの事項を出力するなら、
// 1 \t 2 \t 3と、3つのタイトルを\tで区切って書く。
IJ.setColumnHeadings("x\t y\t bright");
// load
for (int y=1; y<height; y++)
{
for (int x=0; x<width; x++)
{
// (x,y)のPixelデータを取得。
// int[] は、RGBである。
p1 = ip.getPixel(x, y, p1);
// 明るさが500より小さければ、
// 場所と明るさを表に出力
// 出力方法はさっきと同じように\tで区切る
if (getY(p1) < 500)
IJ.write(x + "\t" + y + "\t" + getY(p1));
}
}
}
}
先ほどの例でいえば、明るさがどの値より大きいのかを 実行毎に指定できるようにしてみましょう。これには GenericDialogを使用します。
import ij.*;
import ij.process.*;
import ij.gui.*;
import ij.plugin.filter.*;
public class Test4_ implements PlugInFilter
{
// 明るさ Y = 0.299R+0.587G+0.114B
// 色差 Cr = 0.500R-0.419G-0.081B
// 色差 Cb = -0.169R-0.332G+0.500B
// 画素から明るさを計算
private double getY(int[] a)
{
if (a.length != 3)
return 0;
return 0.299*((double)a[0])+0.587*((double)a[1])+0.114*((double)a[2]);
}
public int setup(String arg, ImagePlus img)
{
if (IJ.versionLessThan("1.28e"))
return DONE;
else
// どの種類の画像も扱えるし、マルチページtiff
// なども扱えることを言う。
return DOES_ALL+DOES_STACKS;
}
public void run(ImageProcessor ip)
{
// dialogの表示。キャンセルでそのまま戻る。
if(getParam())
return ;
// 幅を取得
int width = ip.getWidth();
// 高さを取得
int height = ip.getHeight();
int[] p1 = new int[3];
int[] black = new int[3];
black[0] = 0; black[1] = 0; black[2] = 0;
// 表のsetup
// 例えば、一行に3つの事項を出力するなら、
// 1 \t 2 \t 3と、3つのタイトルを\tで区切って書く。
IJ.setColumnHeadings("x\t y\t bright");
// load
for (int y=1; y<height; y++)
{
for (int x=0; x<width; x++)
{
// (x,y)のPixelデータを取得。
// int[] は、RGBである。
p1 = ip.getPixel(x, y, p1);
// 明るさが50より小さければ、
// 場所と明るさを表に出力
// 出力方法はさっきと同じように\tで区切る
if (getY(p1) < brightLimit)
IJ.write(x + "\t" + y + "\t" + getY(p1));
// もし画像変更が許可されていれば
// 条件に合わないところを黒で塗りつぶす
else if (ifChange)
ip.putPixel(x, y, black);
}
}
}
// staticにしておくと、次にPlug-inを実行した時も
// 以前の入力結果が保存されている。
static int brightLimit = 50;
// static でなければ実行毎に初期化されます。
private boolean ifChange = false;
// ここでユーザからParameterを取得。
// 実行前にdialogが出ます。
private boolean getParam()
{
GenericDialog gd = new GenericDialog("Enter Parameters.",
IJ.getInstance());
gd.addNumericField("", brightLimit, 0);
gd.addCheckbox("change image", ifChange);
// 同じようにして他のParameterも取得できます。
// dialog表示
gd.showDialog();
// dialogでキャンセルボタンが押された時
// (ちょっと汚い処理の仕方です。Javaらしくするなら例外を使うのかな。)
if (gd.wasCanceled())
{
return true;
}
// ここで取得結果を代入。詳しくはAPI docを参照して下さい。
brightLimit = (int) gd.getNextNumber();
ifChange = (boolean) gd.getNextBoolean();
// 他のParameterもここで取得可能です。
return false;
}
}
おそらく実行すれば上のようなdialogが現れるでしょう。 Change imageにチェックをつけて実行すれば画像が変更され、 そうでなければ単に計測が行なわれます。
他にも色々な関数が存在するので、APIを見て試してみて下さい。
Stray XSLT code causes more deaths than road accidents.
-- Shlomi Fish
-- XSLT Facts by Shlomi Fish and Friends ( http://www.shlomifish.org/humour/bits/facts/XSLT/ )
Chandler: She's amazing! She makes the women that I dream about look like
short, fat, bald men!
Monica: Well, go over to her! She's not with anyone.
Chandler: Oh yeah, and what would my opening line be? 'Excuse me.
Blarrglarrghh.'
Rachel: Oh, c'mon. She's a person, you can do it!
Chandler: Oh please, could she be more out of my league? Ross, back me up
here.
Ross: He could never get a woman like that in a million years.
Chandler: Thank you, buddy.
Phoebe: Oh, oh, but y'know, you always see those really beautiful women with
those really nothing guys. You could be one of those guys!
-- David Crane & Marta Kauffman
-- "Friends" (T.V. Show) ( http://en.wikipedia.org/wiki/Friends )