その漫画自炊オタクはImageJマクロに恋をする

プログラミングを用いた、自炊漫画の画像処理

【ImageJマクロ超入門】#7 ROIを使った自動解析

 

f:id:yu3xx:20210512062602j:plain

 

 

皆さまこんにちは。

マイベストアルバム(邦楽)はmegの『room girl』です、yu3xx(ゆーさんちょめちょめ)です。

room girl

room girl

  • アーティスト:meg
  • Dream Machine
Amazon

 

 

 前回のおさらい

imagej-jisui.hatenablog.com

 


前回は自作関数の作り方と使い方を勉強しました。


今回は、「ROIを使った解析の自動化」についてです。

 

研究でROIを使った解析をする場合によく使います。

漫画自炊でも「表紙カバーの切り抜き(Crop)」や「ノンブル領域のカット」で使う処理です。


もくじ

 

要求される動作


ROI(Region of Interest)を使った自動解析では、解析内容によって要求される動作が違ってきます。

 

今回は以下のような条件で、マクロを組みました。
 ① フォルダ内全画像に対する処理

 ② ROIは自動設置ではなく、マニュアルで設置

 ③ 全画像同じ位置にROIを置くのではなく、画像を見て微調整しながら置いていく


いわば半自動化マクロで、これによって短縮できる作業は、「画像を開く」「計測ボタン(Measure)を押す」の2つです。

 

解析枚数が多い場合だと画像をいちいち開くだけでも意外としんどいので、まあまあ作業は快適になります。

 

ROI自動解析コード

//example 7-1

//処理したいフォルダを準備
showMessage("Select Open Folder");
openDir=getDirectory("Choose a Directory");
list=getFileList(openDir);

//ROI Managerの初期化
roiCount = roiManager("count");
if(roiCount > 0){
	roiManager("Deselect");
	roiManager("Delete");
}

//フォルダ内全画像に処理
for (i=0; i<list.length; i++){
	
	open(openDir+list[i]);

	//2枚目以降の処理。1枚目で登録したROIを設置
	if(i != 0){
		roiManager("Select", 0);
	}

	//OKを押すまで待ち状態。ここでROI設置と調整
	waitForUser("Set ROI, then click \"OK\".");

	//1枚目の処理。設置したROIを登録
	if(i == 0){
		roiManager("Add");
	}

	//ROI内の情報を解析して各変数に格納
	getStatistics(area, mean, min, max, std);

	//結果表示。ここではmeanだけ
	print(mean);

	close();

}

 
マクロを実行後、以下の流れで進んでいきます。

 ① 処理したいフォルダを選択

 ② 1枚目の画像が自動で開かれて、指示待ちウィンドウが現れる

 ③ ROIを手動設置したあと、指示待ちウィンドウの[OK]をクリック

 ④ ROI内の情報(平均値mean)がLogウィンドウに表示される

 ⑤ 2枚目の画像が自動で開かれて、指示待ちウィンドウが現れる

 ⑥ 1枚目で使ったROIが自動で置かれるので、これを微調整して[OK]をクリック

 ⑦ Logに結果表示。

 ⑧ 残りの画像でも⑤~⑦を繰り返す

 


コード解説

書かれているコードを上から順番に、ではなく、実際に動作する順番で説明を進めます。

 

過去に掲載した、フォルダ内自動処理の説明も参考にしてみてください。



① ROI初期化

roiCount = roiManager("count");
if(roiCount > 0){
	roiManager("Deselect");
	roiManager("Delete");
}

・解析前にROI Managerを初期化して、マクロ実行前に登録されているROIを消去。

・まずroiManager("count")で登録されたROIの数をカウント。

・もしカウントが0よりも大きかったら、ROI Manager上でROIを選択しない状態にしてDelete。「選択しない状態でDelete」は「全ROI消去」の命令。


② 画像開く


open(openDir+list[i]);

 ・iをforループで変えながら、順次画像を開く。


③ 指示待ち命令


waitForUser("Set ROI, then click \"OK\".");
 

・" "内の文章を表示させるポップアップウィンドウを表示し、[OK]をクリックするまでマクロの動作をストップさせる命令。

・このタイミングで自由にROIを設置。

f:id:yu3xx:20210512214211p:plain

Source: https://www.pakutaso.com


④ ROI登録 


if(i == 0){
	roiManager("Add");
}

・iが0のとき、つまり1枚目画像の場合の処理。

・waitFouUser中に設置したROIを登録。

・このあと何枚もROI解析することになるが、ROI登録は1枚目の画像のみ。


⑤ ROI内解析


getStatistics(area, mean, min, max, std);

・設置したROIの情報を解析し、( )内の変数に格納。

・変数の名前は自由。自分がわかりやすければなんでもOK。

・順番に「ROI面積(pixel数)、平均値、最小値、最大値、標準偏差」が各変数に格納される。例えば間違って、getStatistics(mean, area, min, max, std);とすると、変数meanに面積情報が格納される。


⑥ 結果表示


print(mean);

 ・Logウィンドウに結果を表示。

・たくさんの情報を表示するとどれがどれだかわからなくなるので、今回は「mean」だけ表示。


⑦ ROI設置(マニュアル微調整もする)


if(i != 0){
	roiManager("Select", 0);
}

・1周目ループでは無視されていた部分。

・「!=」は「イコールの否定」。「iが0じゃない時には」の意味。

・roiManager("Select",ROIの番号)で登録されたROIを呼び出して設置。

・ROI番号は0から数えるので、ROI番号0はROI Manager上で一番上にあるROI。

・このROIをwaitForUser中に再度微調整して場所を合わせる。

f:id:yu3xx:20210512215756p:plain

結果の例。meanが解析の順番に表示される


このコードの説明は以上です。

 

自動化したマクロの中に、waitForUserでマニュアル処理を埋め込むというというのがミソです。


このマクロの問題点は、結果が1種類(ここではmean)しか表示されないということです。

紹介しておいてなんですが、残念ながらこれだと使いづらいですよね。
ということで、このコードを改良して、結果をまるっとTableで表示するバージョンを紹介します。

 

ROI自動解析コード改

example 7-2

//処理したいフォルダを準備
showMessage("Select Open Folder");
openDir=getDirectory("Choose a Directory");
list=getFileList(openDir);

//ROI Managerの初期化
roiCount = roiManager("count");
if(roiCount > 0){
	roiManager("Deselect");
	roiManager("Delete");
}

//Table作成の準備
Table.create("Results");

//フォルダ内全画像に処理
for (i=0; i<list.length; i++){
	
	open(openDir+list[i]);

	//2枚目以降の処理。1枚目で登録したROIを設置
	if(i != 0) roiManager("Select", 0);

	//OKを押すまで待ち状態。ここでROI設置と調整
	waitForUser("Set ROI, then click \"OK\".");
	
	//1枚目の処理。設置したROIを登録
	if(i == 0) roiManager("Add");

	//ROI内の情報を解析して各変数に格納
	getStatistics(area, mean, min, max, std);

	//Tableを埋める
	Table.set("Img.No", i, i+1);
	Table.set("Area", i, area);
	Table.set("Mean", i, mean);
	Table.set("StdDiv", i, std);
	Table.set("Max", i, max);
	Table.set("min", i, min);
	
	close();

}

//情報を反映させてTableを可視化
Table.update;

 

追加したのは「Table.なにがし」という処理です。

このあたりはちょっと使い方にクセがあります。

 

Table表示までの手順としては以下の3つ

 ① Tableの準備。タイトルを決めて、空っぽのTableを表示させる

 ② Tableに値を埋めていく。まだここでは「コンピュータ上の情報」として持っているだけ。我々にはまだ見ることができない

 ③ 全部埋め終わったら、情報をアップデートして表を見えるカタチにする。終了

 

コード解説


① Tableの準備


Table.create("Results");

・" "内の文章がタイトル。空っぽのTableを準備。


② Tableを埋める

基本形はこんな感じ


Table.set("列のラベル名", 行番号, 埋めたい値);

・列の指定が「列のラベル名」、行の指定が「行番号」。

・「列のラベル名」は「命名」と同時に「列指定」を行う。

・行の番号は0から数える。「ラベルを書く行の一つ下が0番目」。

 

つまりi = 0(1枚目画像)のとき、

 Table.set("Img.No", i, i+1); は、

 Table.set("Img.No", 0, 1); となり、

ラベル「Img.No」の列の、一番上の行(0番目)に、1という値を埋める。という処理になる。


③ Tableアップデート


Table.update;

・これにより初めてTableに値が表示される。

f:id:yu3xx:20210512221354p:plain

このコードを使えば、解析はかなりラクチンになると思います。


このTableはもちろんexcelにコピペ出来ます。

 

デフォルト設定だとラベルがコピペされないので、[Results]→[Options]で[Copy column headers]にチェックを入れておくと良いです。

 

Multi Measureとの違い

 

実はImageJには、上記マクロと似ている機能で「Multi Measure」という処理を持っています。

 

Multi Measureは

 ① フォルダ内全画像を1つのウィンドウにスタック表示する

 ② ROIを設置する

 ③ ROI内の解析を全画像に対して一括で処理

という機能です。

 

f:id:yu3xx:20210512222854p:plain

スタック表示の例。下に画像送り用スライドバーが出る

 

これはこれで普通に使えます。

 

この処理の問題点としては、

・各画像の縦横Pixelがバラバラだとスタック表示できない。例えば「256×256画像」と「512×512画像」の混合はスタック表示不可

・ROIは全画像に対して、完全に同じ位置


この2つがあります。

 

ここが問題なければ「Multi Measure」でOKです。

 

「Multi Measure」の実行方法はこうです。


 ⓪ 初回だけすべき設定。デフォルトだと標準偏差が結果表示されないので、[Analyze]→[Set Measurements]→[Standard Deviation]にチェック。

 ① [File]→[Import]→[Image Sequence]でスタック表示。Sequence Optionsは基本いじらなくてOK

 ② [Analyze]→[Tools]→[ROI Manager]でROI Managerを表示しておく

 ③ ROIを好きな場所に設置

 ④ ROI Manager上の[Add]で登録

 ⑤ ROI Manager上の[More]→[Multi Measure]で一括解析。Tableで出力される。終了

 

 

ちなみに上記の「自動化コード改」も、waitForUserを「i = 0」の時だけ動作するように書き換えれば、全画像同一ROIの自動解析を達成できます。

 

おまけ:ROIに関係するその他のコード

 

① 設定したROIの位置情報を取得


getBoundingRect(x,y,width,height);

・設定した長方形ROI位置情報を変数に取得する。ROIの左上スミの座標をx,yに、ROIのタテヨコ幅をwidth,heightに格納。

 

② 設置場所の直接指定(長方形ROI)


makeRectangle(x, y, width, height);

指定した位置と大きさで長方形ROIを設置。ROIの左上スミの座標が (x, y) 、ROIのヨコタテ幅がwidth,height。

 

③ 設置場所の直接指定(円形ROI)


makeOval(x, y, width, height);

指定した位置と大きさで円形ROIを設置。ROIの左上スミ(接線の交点)の座標が (x, y) 、ROIのヨコタテ直径がwidth,height。

 

④ ROI内に含まれる座標を取り出す(矩形ROI、円形ROI、多角形ROIなんでもOK)


Roi.getContainedPoints(xpoints,ypoints);

設定したROIの中に含まれているすべての座標つまり(x, y)の組み合わせを、配列xpointsとypointsに格納する。iの順番は左上スミから+y方向にスキャンする感覚。いちばんはじめの座標は(xpoints[0], ypoints[0])となる。

 

⑤ 任意の座標の信号値を取り出す。


getPixel(x,y);

座標(x, y)の信号値を返す。④の命令に続いて、iでループを回しながら配列valueにROI内すべての座標の信号値を格納したりも出来る。

 

 

今回は以上です。


いろいろいじって、自分の解析方法に応じてアレンジしてみてください!

 

 

 次回は二次元画像からピクセル値を直接取得して解析する方法です。重要ですのでこちらもぜひ覗いてみてください!

 

 


おしまい

 

 

 

  

 

・公開したマクロのまとめ

imagej-jisui.hatenablog.com