シリーズ6.マクロ言語を使った画像処理の応用編~ノイズ軽減② 空間フィルタ処理~

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

目次

前回の記事で空間フィルタ処理、カーネルの設計について学習しました。
今回は前回説明しなかった画像の端を含めてフィルタをかけるための手法について学びます。
前回も説明したように3x3のカーネルを作成してフィルタをかける場合は画像の外側の上下左右に1ピクセル増やす必要があります。ImageJでは1ピクセル増やすと増えたピクセルの輝度値が0になっていますがこれでは元の画像よりも輝度値が低くなってしまうため不正確であると考えられます。

そこで、この0になっている輝度値を図のように画像の最も外側のピクセルの輝度値に置き換えることを考えます。

【100x100の画像を例にマクロでみましょう】

Step1.

100x100ピクセルの画像を用意します。
(ここではサンプル画像を100ピクセル×100ピクセルの画像にします。)

100x100の画像

Step2. 

この画像の輝度値を左上から右下に1ピクセルずつ読み取り配列に保管します。
Intensity=newArray(height*width);//配列を定義

for(y=0;y<height;y++)
{
        for(x=0;x<width;x++)
        {        
                Intensity[i]=getPixel(x,y); 
                i++;
        }        
}
輝度値を読み取るコード

Step3. 

もとの画像の大きさを上下左右1ピクセル分増やします。
ImageJのメニューのImage→Adjust→Convas Sizeをクリックします。

Step4. 

新しい画像ができたら周囲の1ピクセル増やした部分に輝度値を代入していくことを考えます。
このとき橙色で示した四隅は除外し、青で示した①から④の部分に分割してマクロを考えます。
(四隅についてはStep5で説明します)

【領域①】
領域①はx座標は0のままでy座標を1ピクセルずつ増やして座標を読み取り、配列に代入したもとの画像の輝度値を代入していけばよいことになります。

配列をIntensity[i]とすると、画像の輝度値は次のように考えられます。

i=0; 
for(y=1;y<101;y++)
{
        setPixel(0,y,Intensity[100*i]);
        i++;
}
領域①

【領域②】

領域②はy座標は0のままでx座標を1ピクセルずつ増やして座標を読み取り、配列に代入したもとの画像の輝度値を代入していけばよいことになります。

配列をIntensity[i]とすると、輝度値は次のように考えられます。

i=0;
for(x=1;x<101;x++)
{
        setPixel(x,0,Intensity[i]);
        i++;
}
領域②

【領域③】

領域③はx座標は101のままでy座標を1ピクセルずつ増やして座標を読み取り、配列に代入したもとの画像の輝度値を代入していけばよいことになります。
配列をIntensity[i]とすると、画像の輝度値は次のように考えられます。

i=0;
for(y=1;y<101;y++)
{
        setPixel(101, y, Intensity[(100*i)+99]);
        i++;
}
領域③

【領域④】

領域④はy座標は101のままでx座標を1ピクセルずつ増やして座標を読み取り、配列に代入したもとの画像の輝度値を代入していけばよいことになります。

配列をIntensity[i]とすると、画像の輝度値は次のように考えられます。

i=0;
for(x=0;x<101;x++)
{
        setPixel(x, 101, Intensity[(9900+i]);
        i++;
}
領域④

【四隅について】

上記では除外した四隅
(0,0), (0, 101), (101, 0), (101, 101)については次のようになります。
(0,0)の輝度値←Intensity[0]の輝度値
(0, 101)の輝度値←Intensity[9900]の輝度値
(101, 0)の輝度値←Intensity[99]の輝度値
(100,101)の輝度値←Intensity[9999]の輝度値

◆スクリプト

height=getHeight(); //縦のピクセル数を抽出 画像の幅と高さ
width=getWidth(); //横のピクセル数を抽出
Intensity=newArray(height*width);//height, widthともに100
i=0;

run("32-bit");
//まずもとの画像から輝度値を抽出し配列Intensityに代入する。
for(y=0;y<height;y++)
{
        for(x=0;x<width;x++)
        {        
                Intensity[i]=getPixel(x,y);
                i++;
        }        
}


run("Canvas Size...", "width=101 height=100 position=Top-Right zero");
run("Canvas Size...", "width=102 height=100 position=Top-Left zero");
run("Canvas Size...", "width=102 height=101 position=Bottom-Center zero");
run("Canvas Size...", "width=102 height=102 position=Bottom-Right zero");
//102×102の画像を作成

i=0; //領域①
for(y=1;y<101;y++)
{
        setPixel(0,y,Intensity[100*i]);
        i++;
}

i=0;//領域②
for(x=1;x<101;x++)
{
        setPixel(x,0,Intensity[i]);
        i++;
}


i=0;//領域③
for(y=1;y<101;y++)
{
        setPixel(101, y, Intensity[(100*i)+99]);
        i++;
}

i=0;//領域④
for(x=0;x<101;x++)
{
        setPixel(x, 101, Intensity[(9900+i]);
        i++;
}

//四隅の輝度値
setPixel(0,0,Intensity[0]);
setPixel(0,101,Intensity[9900]);
setPixel(101,0,Intensity[99]);
setPixel(101,101,Intensity[9999]);


N=3;//カーネルの大きさ
intensity=0;

run("32-bit");
for(y=1;y<101;y++){
        for(x=1; x<101;x++){ 
                        
intensity=(getPixel(x-1,y-1)
+getPixel(x,y-1)
+getPixel(x+1,y)
+getPixel(x-1,y)
+getPixel(x,y)
+getPixel(x+1,y)
+getPixel(x+1,y+1)
+getPixel(x, y+1)
+getPixel(x+1, y+1))
/(N*N);                
                        setPixel (x,y, intensity); 
                        
                }
        }
ノイズ軽減のスクリプト

フィルタ処理前

 フィルタ処理後

フィルタ処理後の画像の端が暗くなる問題も解決できました。100×100の大きさの画像のみではなく多くの画像に対応できるように101や99、9900などの部分をheightとwidthを用いて記述することもできます。

空間フィルタ処理の原理と実際の処理の方法が理解できましたか?ノイズ軽減に必要なフィルタ処理は画像解析に必須になるのでぜひ覚えておくことをお勧めします。