3. 2次元FFTによる画像のフィルタ処理

 1次元の信号波 形に対する1次元の離散フーリエ変換について既に学習した。2次元画像に対しては,2次元の離散フーリエ変換が適用できる。ここでは,離散フーリエ変換の 高速計算法である2次元FFTを用いて,画像 のフィルタ処理の実習を行なう。

1)2次元画像 データの準備

実習に先立って2次元データを準備する。
ImageJ で,前回と同様,brain.raw を読み込み,正中の断面 (62フレーム目) を表示する。
  図3-1-1

Image → Duplicate で,現在表示中のフレーム(62)の2次元データを複製する。
 図3-1-2

Titleに適当な名前を入れ
OKを押す。
 図3-1-3
 図 3-1-4

Image→Adjust→Brightness/Contrast を見ると濃度の最大値が96であることがわかる。
 図3-1- 5

実習の画像処理では,画素の濃度を8bits(=1byte)で表現することにする。
濃度の範囲(0〜255)を有効に使用するために,現在の濃度を2.6倍する。
 図3-1-6
 図 3-1-7

Image→Adjust→Brightness/Contrast で 見ると濃度の最大値が255近くになっていることが分かる。
 図3-1 -8

Auto ボタンを押して図を見やすくする。
  図3-1-9
結果の図を middsagittal.raw という名前をつけ,Raw format で Saveする。

 図3-1-10
  図3-1-11


2) Octaveによる2次元
画像データの読み 込み

ここで ImageJ を終了し,Octave を起動する。midsagittal.raw は image_proc
フォルダに保存されているので,以下の作業は image_proc フォルダ(directory) で行なう。
ターミナル窓内で,つぎの unix コマンドをタイプする。
ターミナルは,2.時系列信号処理の基礎1.unixの立ち上げで既出。)

cd
cd image_proc
octave
ホーム・ディレクトリに戻る
image_proc に移動
octave の実行




注意:octaveの最近の版でplot関係のコマンドが大幅に改訂さ れたために,まだバグが残っています。imagescで画像表示した時に不要なgnuplot画面が上に重なって表示されることがありますが,それは消し てください。また,imagesc コマンドの直前に plot コマンドを置くと画像が表示されない場合があります。上のプログラム・リストのような順にコマンドを並べると回避できます。



図3-2-1の白線の部分(x=90)の濃度変化を図3-2-2に表示。


  図3-2-1

 図3-2 -2

 
写真画像の画像ファイル化

  
2. 時系列信号処理の基礎:付録で述べた方法で画像をファイル化する。


3)2次元FFT と空間周波数

 信号処理の項で扱った1次元データは,時間とともに変化するデータである。それを周波数の異なる正弦波の重ね合わせとして表現した離散フーリエ変換の結 果は,周波数次元の表現である。
 2次元の画像データは時間の関数ではないので,その離散フーリエ変換の結果は,1次元データと同 じ周波数次元の表現ではない。
しかし, 上に述べたように,2次元画像データの各画素はそれぞれ濃度値をもつので,例えば図3-2-2のように,2次元画像からx軸の値を一定にして切り出したy 軸上の濃度値の変化は,1次元の信号波形と同様の波形になる。
 そこで,x軸,y軸それぞれについて,濃度値の変化波形を周波数の異なる正弦波の重ね合わせとして表現し,単位長さあたりの正弦波の波数を周波数とみな すことにする。2次元の画像データにおけ る周波数のことを
空間周波数と 呼ぶことが多い。低い空間周波数成分は画像の大まかな部分,高い空間周波数成分は画像の微細な部分を表している。(下のフィルタの実習で確認すること。)
 空間周波数は,視覚の検査や研究などにも
用いられる概念である。(視覚では一般に横方向の視角1°を単位長さとしている。)
 2次元離散フーリエ変換は,医学の分野では例えばMRIデータの再構成(reconstruction)(k空間から画像への変換)などにも用いられて いる。

X軸方向だけが正弦波状に変化する2次元画像

プログラムを実行すると,図が多数表示されて見難くなるので,各図左上のをクリックして不必要な図 を消すこと。(または,図の上で [CTRL] キーを押しながらキーをたたく。)

下のプログラム・リストはテキスト・エディタへのタイプ入力時間を節約するため,image_procフォルダ内に image_x_fft.m.zip という圧縮形式で予め保存してある。アイコンをダブルクリックすると解凍されて image_x_fft.m というファイルができる。それを octave で実行すること。(以下のプログラムも同様に解凍する。)

image_x_fft.m

配列のx, yの指定は,表示される図にあわせてあるので,逆になっている。


説明(1)
1-cos(xfreq・2πi/n) の計算
濃度は正の値をとるので 1-cos() の形にする。最大濃度が254になるように127倍する。

imagesc は,2次元画像を表示する関数。
fft2 は,2次元fftを計算する関数。
mesh は,濃度をz軸にとり3次元表示する関数。表示に時間がかかるので,ここでは間引きしたデータを表示している。
abs   は,絶対値をとる関数。
fftshift は,原点を軸の中心に移す関数。ifft2(2次元逆FFT)を計算する前に実行する必要がある。
max(max()) により,2次元配列のすべての要素の最大値が求まる。

図3-3-1
X軸が4Hzで正弦波状に変化する。
図3-3-2 FFT 結果の絶対値をとって3次元表示した図。
画像はX軸方向しか変化しないので,図の手前の両側にしかスペクトル成分がない。

図3-3-3 fftshift により原点を中心に移動した図。
この操作は,後で逆FFTするために必要。
図3-3-4
fftshift した結果のlogをとった後,256レベル内にスケーリングして2次元表示した図。中央の点は,基線(=周波数0)成分,左右の点は4 Hz成分を示す。(拡大表示してある。)

図3-3-5
逆 FFT により再現した図。
図3-3-1と同じ図になる。

Y 軸方向だけが正弦波状に変 化する2次元画像

image_y_fft.m を octave で実行すること。

図3-3-6
Y軸が8Hzで正弦波状に変化する。
図3-3-7 FFT結果の絶対値をとって3次元表示した図。
画像はY軸方向しか変化しないので,図の左奥の両側にしかスペクトル成分がない。

図3-3-8 fftshift により原点を中心に移動した図。中央の成分は直流(DC)成分(1-cosにしてゲタをはかせた分)。
図3-3-9
中央の点は,基線(=周波数0)(直流)成分,上下の点は8Hz成分を示す。点の並ぶ方向が図3-3-4と違うことに注意。(拡大表示してある。)

図3-3-10
逆FFTにより再現した図。
図3-3-6と同じ図になる。

X, Y軸方向とも正弦波状 に変化する2次元画像
%=== image_xy_fft.m ===

image_xy_fft.m をoctave で実行すること。



説明(2)
1-cos(xfreq・2πi/n) の計算
濃度は正の値をとるので 1-cos() の形にする。x,yとも最大値が2となるので,最大濃度が252になるように63倍している。

図3-3-11
X軸が4Hz,Y軸が8Hzで正弦波状に変化する。
図3-3-12
FFT結果の絶対値をとって3次元表示した図。
画像はX軸,Y軸方向とも変化するので,四隅にスペクトル成分がある。

図3-3-13
fftshift により原点を中心に移動した図。
図3-3-14
中央の点は,基線(=周波数0)成分,左右の点は4Hz成分,上下の点は8Hz成分を示す。点の並ぶ方向を図3-3-4,図3-3-9と比べて見ること。 (拡大表示してある。)

図3-3-15
逆FFTにより再現した図。
図3-3-11と同じ図になる。


2次元FFTを用いた画像フィルタ(X軸方向だけが変化する6つの正弦波の 加算波形を用いて)
%=== image_sine6add_filter.m ===

close;
clear;
image_viewer("display %s")
graymap=gray(256);
colormap(graymap);
n=256;
nh=n/2;
xsize=n;
ysize=n;
k=pi*2/n;       % 2π/n
%------------ 原画像の作成----------------
for j=1:6
H(j)=4/(pi*(2*j-1));
end
XFREQ=1:2:11;    % 周波数6ステップ
X=zeros(xsize,ysize);
SUM=zeros(xsize,ysize);

for j=1:6
for i=1:xsize
X(i,:)=H(j)*sin(XFREQ(j)*k*(0:n-1));
end
SUM = SUM + X;
end
S=SUM*100+128;   %ゲタをはかせて >0 にする

%--- 正弦波加算波形ファイルの書き出し ---
[fid, msg]=fopen('sine6add.raw',"wb");
count=fwrite(fid,S');
fclose(fid);

figure(1);
plot(SUM(1,:));     % 濃度変化を1行だけ表示

input('Hit Return1');
close;
colormap(graymap);
imagesc(S);       % 画像表示

%------------- 2次元 FFT -----------------
XF=fft2(S);       % 2次元 FFT
XFS=fftshift(XF);     % 原点を中心に移動
Z=abs(XFS);

input('Hit Return2');
close;
figure(2);
mesh(Z(1:4:n, 1:4:n));     %間引いて表示

% 濃度のlogをとり,最大値を255に正規化
LZ=log(Z+1);
MAXLZ=max(max(LZ));
MINLZ=min(min(LZ));
SCALEZ=(LZ-MINLZ)/(MAXLZ-MINLZ);
input('Hit Return3');
close;
colormap(graymap);
imagesc(SCALEZ*255);

%---------------- 逆FFT -----------------
W=ifft2(XFS);  % 原点を移動したデータを用いる
input('Hit Return4');
%close;
%colormap(graymap);
imagesc(abs(W));

%---------- Lowpass Filterの作成 ---------
fsize=4;
FL=zeros(xsize,ysize);
FL(:, nh-fsize+1:nh+fsize)=1;
GL=FL .* (SCALEZ*192+63);
input('Hit Return5');
%close;
%colormap(graymap);
imagesc(GL);

% フィルタをかけて逆 FFT
W=XFS .* FL;
w=ifft2(W);
wa=abs(w);
maxwa=max(max(wa))
minwa=min(min(wa))
%input('Hit Return6');
%close;
figure(3);
plot(wa(1,:));     % 濃度変化を1行だけ表示
input('Hit Return7');
close;
%colormap(graymap);
imagesc(wa);

%---------- Highpass Filterの作成 -----------
fsize=nh-4;
FH=zeros(xsize,ysize);
FH(:, 1:fsize)=1;
FH(:, n-fsize+1:n)=1;
GH=FH .* (SCALEZ*192+63);
input('Hit Return8');
%close;
%colormap(graymap);
imagesc(GH);

% フィルタをかけて逆 FFT
W=XFS .* FH;
w=ifft2(W);
wa=abs(w);

input('Hit Return9');
%close;
figure(4);
plot(wa(1,:));     % 濃度変化を1行だけ表示
input('Hit Return10');
close;
maxwa=max(max(wa))
minwa=min(min(wa))
wan=wa*256/(maxwa-minwa);   %waを正規化
%colormap(graymap);
imagesc(wan);
 この課題では,輝度が正弦波状に変化する縞模様を順に加算した画像を つくり,それらのスペクトルを求める。
image_sine6add_filter.m を octave で実行すること。
画像上の正弦波が,スペクトル平面でどう表現されるかを確かめる。
 ついで,スペクトルの一部をローパス・フィルタ およびハイパス・フィルタで取り出し,逆FFTすることにより,それらの空間周波数成分が画像の構成にどう寄与しているかを確かめる。

for j のループでは,j の値が大きくなるにつれて,周波数が順に増加する正弦波を,x軸に重ね合わせて,縞模様の画像をつくる。

ファイルへの書き出しは,次のようにする。
[fid, msg]=fopen('出力ファイル名',"wb");
  % fidはfile index, msgにはメッセージが返る。
  % wb はwrite/binary。
count=fwrite(fid, 出力する配列); % fopenのfidを使う。
fclose(fid);

fft2 は,2次元FFTの組込み関数。

fftshift は,FFTした直後に両端にある周波数軸の原点0を,中央に移動させる。

mesh(Z(1:4:n,1:4:n)); は,表示時間がかかるため間引きして表示している。

log 演算は,2次元FFT像を256階調で表示するために行なっている。

ifft2 は,2次元逆FFTの組込み関数。

ローパス・フィルタ(LPF)をかけて,フィルタの形状とフィルタされた結果を表示する。低い周波数成分は中心に近い所にあるので,フィルタにより中心部 分を取り出し,逆FFTを実行する。フィルタの形状の図中央の 縦に白い部分がフィルタにより取り出される部分である。

ハイパス・フィルタ(HPF)をかけて,フィルタの形状とフィルタされた結果を表示する。高い周波数成分は周辺に近い所にあるので,フィルタにより中心以 外の部分を取り出し,逆FFTを実行する。フィルタの形状の図の白い部 分がフィルタにより取り出される部分である。

GH=FH .* (SCALEZ*192+63)の+63は,逆FFTするスペクトル成分と,ローパス・フィルタまたはハイパス・フィルタのスペクトル平面上の領域との関 係がわかるように,フィルタする領域を灰色表示するため だけのものである。
図3-3-16
X軸方向6つの正弦波の加算画像

図3-3-17
濃度変化を1行だけ表示
図3-3-18
スペクトル平面
x軸の正弦波画像は,スペクトル平面上では,原点とその左右の点で表現される。原点には元画像の直流成分(輝度の平均値)が表われる。

図3-3-19
fftshift して,原点を中央に移動した図。(表示に時間がかかる。)
図3-3-20
ローパス・フィルタ
スペクトル平面より中央部だけ取り出す。
図3-3-21
ローパス・フィルタをかけた結果の画像

図3-3-22
濃度変化を1行だけ表示。

画像の大まかな部分が示されている。
図3-3-23
ハイパス・フィルタ
スペクトル平面より周辺部だけ取り出す。
図3-3-24
ハイパス・フィルタをかけた結果の画像

図3-3-25
濃度変化を1行だけ表示
画像の微細な部分が示されている。
縦軸の振幅が
図3-3-22の約1/4になっていることに注意。
左の画像は詳細が分かるように正規化して表示してある。

2 次元FFTを用いた画像フィルタ ( ImageJ による処理)

 Octave で行なったのと同様の処理は,ImageJ でもできる。

Sine6add Original


上で作成したファイル:sine6add.raw を raw形式で開く。
(256 x 256 x 1,
Image Type: 8-bit)
FFTを計算する
(Process->FFT->FFT)
Image->Adjust->Brightness/Contrast...
図3-3-19と同様の図になる。

ローパス・フィルタ (LPF)

左端のRectangularボタンを押す。
黄色い線で領域を囲む。
Edit->Clear
(FFT窓内で Edit->Clearして白抜きになった部分だけを通し,残りの部分をマスクする。)


x軸上で中央に近い部分の情報だけを取り出している。
Sine6add LPF IFFT

Process->FFT->Inverse FFT
で,ローパス・フィルタ処理をした結果を表示。
Straight line selections ボタンを押す。
左端から右端まで直線を引く。

左図:もとの図に直線
右図:LPF処理結果に直線。
直線を引いた部分の値の変化を表示

ImageJで,Analyze->
Plot Profile

もとの図の直線部分のProfile。
LPF処理結果の直線部分のProfile。

高次の正弦波成分が取り除かれているのが分かる。

ハイパス・フィルタ(HPF)

Process->FFT->Redisplay Power Spectrum
で再表示してから 領域を四角で囲み Edit->Fill。
(FFT窓内で Fill して黒くなった部分だけをマスクし,残りの部分を通す。)

x軸上で中央から離れた部分の情報だけを取り出している。
Sine6add HPF IFFT

Process->FFT->Inverse FFT
で,ハイパス・フィルタ処理をした結果を表示。

フィルタした結果の別の表示法

 以下3つの左側の図は,それぞれ,上の Sine6add Original, Sine6add LPF FFT, Sine6add HPF FFTの画像を表示した状態で,File->Save As->Text Image... で,各画像の濃度値の行列をテキスト保存する。
それを Excel で開き,1行だけ指定して折線グラフを描いたものである。
 右側の図は,それぞれ,上の Sine6add Original, Sine6add LPF FFT, Sine6add HPF FFTの画像を表示した状態で,Analyze->Surface Plot を押し,

で,Surface Plot したものである。Octave の課題と同様,フィルタのかかっていることが分かる。

演習
 ハイパス・フィルタをかけた結果を表示すること。



)MRI 画像の処理

 上では,x軸上での1次元フィルタについて述べたが,ここでは,MRI画像を使って2次元のフィルタ処理を行なう。
左図
ImageJのFile->Import->Raw... 1) 2次元画像データの準備の項 で作ったmidsagittal.raw という画像ファイルを開く。
( Image Type: 8-bit, Width: 256, Height: 256, Number of Images: 1)

右図 FFTした画像
Process->FFT->FFT
左から2つ目の*Elliptocal*ボタンを押し,適当な位置でマウス・ボタンを押しながらマウスを動 かすと,楕円が描かれるので,ImageJ窓内のStatus Barの表示がw=64, h=64になるところでマウス・ボタンを離して円を描く。円の中心にカーソルを持っていき,マウス・ボタンを押すとカーソルが矢印に変わる。そのままマウ スを動かし,円を画像の中心に移動させる。
LPF

ImageJのEdit->Clearで,マスクを作る。(白い部分のデータだけを使用する。データがClearされるわけではない。)

逆FFT処理を実行
Process->FFT->Inverse FFT

ローパス・フィルタされた画像が表示される。
HPF

ImageJのEdit->Fillで,マスクを作る。(黒い部分のデータは使用されない。データがFillされるわけではない。)

逆FFT処理を実行
Process->FFT->Inverse FFT
ハイパス・フィルタされた画像が表示される。
BPF

直径100の円でEdit->Clearし,次いで直径25の円でEdit->Fillすると,左図の白い部分だけを取り出すことができる。 逆FFTを実行すると,バンドパス・フィルタされた結果が表示される。

演習
 直径40と80の円を使ってそれぞれLPFとHPF処理を行ない,結果を比べること。