[ Main Page ]

ImageJのPlug-inを作る

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に教える必要があります。

teru@server:/tmp/ImageJ/plugins$ CLASSPATH=../ij.jar javac Test_.java

コンパイルが終ってTest_.classができれば、java -jar ij.jarなりして ImageJを実行します。一番右のPluginsというメニューの中に Testという項目ができているでしょう。そうしたら、あとは画像を開いて その項目を選ぶだけです。 上の例では、画像の縦横を取得し、1つ1つのPixelを 読むという動作をします。画像は変更されず、見かけ上はなにも起きません。

簡単なFilter

次は、それぞれの画素に対して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;
    }
}

GD Screen Shot

おそらく実行すれば上のようなdialogが現れるでしょう。 Change imageにチェックをつけて実行すれば画像が変更され、 そうでなければ単に計測が行なわれます。

他にも色々な関数が存在するので、APIを見て試してみて下さい。

Knuth is not God! Typing "God" into Google and pressing "I'm Feeling Lucky"
would not lead you to his homepage.

Shlomi Fish in Hackers-IL message No. 2084 ("The Great WWW-Wisdom Shootout")

    -- Shlomi Fish
    -- Shlomi Fish's Aphorisms Collection ( http://www.shlomifish.org/humour.html )

Rule of Open-Source Programming #15:

If you like it, let the author know. If you hate it, let the author know why.

    -- Muli Ben-Yehuda
    -- "Rules of Open Source Programming"


Powered by UNIX fortune(6)
[ Main Page ]