シリーズ4.ImageJマクロ言語を用いた画像解析~②二値化処理-3~

【記事の目標】 画像を触ったことがない人を対象として、適切な画像解析を施すまでのImageJのマクロ言語を用いた学習過程を示す。

目次

概要

ImageJのThresholdの中にもPercentileという項目があり、マクロで書かなくても簡単にPercentile法で二値化処理を施すことは可能です。しかし、ImageJでは輝度値ヒストグラムの上位50%を閾値とする(=50パーセンタイル)という設定しか施すことができません。画像によっては上位50%が適切な閾値であるとは限らないので、この設定を任意の数値に変えることが可能になると、さまざまな画像で領域分割が可能になります。
今回は上位X%を閾値とする二値化処理を施すスクリプトをマクロで書くことを学びます。
サンプル画像(下記リンク先を参照)Rat_Hippocampal_Neuron2.zip を例に見てみましょう。
今回は神経細胞の突起に見られる粒子状の構造の画像を使います。

ImageJ(サンプル画像)

最終的にこの粒子状の構造をカウントすることを目的に画像処理を施すことを考えます。まず、Percentile法で二値化処理を施してみます。
Image→Adjust→ThresholdをクリックしてPercentileを選択すると、ヒストグラムの画像の左下に50.15%と表示されています。これが閾値です。しかし50%を閾値にすると下図のように細胞の粒子状の構造のみならず繊維の部分も含まれてしまいうまくカウントできないように思われます。
そこで試しに上段のバーをマウスで動かして閾値を変えてみます。5%付近にするとカウントしたい粒子状の構造が選択されることがわかります。

◆50%パーセンタイルの場合

◆5%パーセンタイルの場合

これより、上記の画像に「上位5%を閾値とする二値化処理を施し粒子の数をカウント」する画像処理を施すことにします。この処理をマクロで記述するために必要な知識をまず説明します。

【配列】

array[]
arrayという名前の配列の定義。イメージとしてはarrayという名前の箱に[]内に書いた数だけ文字または数字を格納する場所があると考えるとわかりやすいと思います。 ※配列の名前は任意に変更可。


【getHistogramの使い方】

getHistogram()
8bit画像の場合、0~255までの輝度値に対応するピクセル数が順番に格納されます。

※getHistogram(引数1, 引数2, 256) と記述した場合
引数1に輝度値(0-255)、引数2に各輝度値に対応する度数(ピクセル数)をかえす。

配列

【if文の中に条件を2つ書きたいとき】

これまでの記事ではif文の中に記述する条件は一つのみで他に条件を記述するときはelseやif文を2つ用いてきました。今回はif文に条件を2つ書くことを考えます。
今回の例では&&を使用して2つ条件を記述することが可能です。
A && B; AかつBと意味で、AとBの両方が成り立つという条件を示します。


【上位5% (5パーセンタイル) 】

画像の輝度値の分布で輝度値0から順にピクセル数を足していったときに全体の95%を占めるピクセル数limに到達したらその輝度値 を閾値Thrとします。


上記をふまえマクロで記述すると、以下のようになります。

◆スクリプト

open("C:\\Users\\Hiro\\Downloads\\Rat_Hippocampal_Neuron2.zip");
run("Split Channels");
selectWindow("C2-Rat_Hippocampal_Neuron2.tif");
run("Grays");
run("8-bit");

width=getWidth();//画像の横幅を取得
height=getHeight();//画像の高さを取得

percentile=5; //任意の数値に変更可能。
lim=width*height*(1-percentile/100);//全体のピクセル数のうち下位95%を占めるピクセル数

getHistogram(AryI, AryF, 256);//AryFはピクセル数、AryIは対応する輝度値、8bit画像なのでピクセル数と輝度値の対応が0-255までの256個取得されることを示します。したがって、配列AryFにピクセル数が、配列AryIに輝度値がそれぞれ256個ずつ格納されます。

total=0;//変数totalの宣言, 輝度値0から順にピクセル数を足した合計のピクセル数
Thr=0;//変数Thrの宣言,  閾値
Counter=0;//変数Counterの宣言

for (i=0;i<256;i++){
        total+=AryF[i];//配列AryFのi番目に格納されているピクセル数を変数totalに足してtotalに代入する。
        if (total > lim && Counter ==0)//変数totalがlimより大きくかつ変数Counterが0のとき
        {
                Counter++;//Counterの数値に1を足す
                Thr=i;//閾値Thrにiを代入する
        }//変数totalがはじめてlimより大きくなったときの閾値は輝度値iであり、このfor文は1回のみ実行されればよい。したがって、変数total>limという条件かつ変数Counterが0であるという条件を定義する。for文が1回実行されるとそれ以降はCounter=1, 2…となりif文にCounter==0という条件がついていることでfor文が2回以上実行されることはない。
}
for(y=0;y<height;y++){
        for(x=0; x<width;x++){ 
                Intensity=getPixel(x,y);
                if(Intensity>Thr){
                        setPixel(x,y,0); 
                }else{
                        setPixel (x,y, 255); 
                }
        }
}//あとの二値化処理は以前の記事(シリーズ3.ImageJマクロ言語を用いた画像解析~②二値化処理-1~)で学んだ時のスクリプトと同じです。
Percentile-1.macro
以下のような二値化ができましたか?

*補足ですが、if (x > lim && Counter ==0){ }のところをbreakを使って記述することもできます。
open("C:\\Users\\Hiro\\Downloads\\Rat_Hippocampal_Neuron2.zip");
run("Split Channels");
selectWindow("C2-Rat_Hippocampal_Neuron2.tif");
run("Grays");
run("8-bit");

width=getWidth();//画像の横幅を取得
height=getHeight();//画像の高さを取得


percentile=5; //任意の数値に変更可能。
lim=width*height*(1-percentile/100);


getHistogram(AryI, AryF, 256);
total=0;
Thr=0;

for (i=0;i<256;i++){
        total+=AryF[i];
        
        if (total>lim)
        {
                Thr=i;
                break;
        }
}



for(y=0;y<height;y++){
        for(x=0; x<width;x++){ 
                Intensity=getPixel(x,y);
                if(Intensity>Thr){
                        setPixel(x,y,0); 
                }else{
                        setPixel (x,y, 255); 
                }
        }
}
Percentile-2.macro

【まとめ】

二値化処理の手法のひとつであるPercentile法の実装の仕方が理解できたでしょうか?
今回のマクロのスクリプトには配列array[]やgetHistogram()、if文の中に条件を2つ書くなども含まれていて、より複雑な画像処理を実行するために非常に参考になると思います。
次回はここまでの小括としてマクロ言語の文法のまとめを紹介します。