mecobalamin’s diary

人間万事塞翁が馬

https://help.hatenablog.com/entry/developer-option

OpenCVを使って物体認識その2、正解データの作成

前回の続き
mecobalamin.hatenablog.com

正解データをImageJを使って作成する
猫の顔を学習させて認識できるか試す

猫画像はここからダウンロードした
写真素材なら「写真AC」無料(フリー)ダウンロードOK
実際に使った画像のリンク先は別にまとめた
mecobalamin.hatenablog.com


結局猫かい


手順は以下の通り

  1. 正解画像にROIを書く
  2. 画像をROIに合わせて切り抜く
  3. 画像ファイルのパスなどを記録したファイルを作成する

画像の処理はwindowsで行うが、opencvはwslで実行するので
画像ファイルのパスや改行コードの変更が必要になる

正解画像の猫画像はこれ
f:id:mecobalamin:20200110201207j:plain
顔の輪郭に合わせてROIを作成した

ROIの作成については以前の記事を参照する
mecobalamin.hatenablog.com

ROIの位置情報があればわざわざ切り抜く必要はないのだが
他にも使いたいので切り抜いてファイルを作成する
openCVで物体認識 by traincascade - Qiita

切り抜いてサイズ調整した
f:id:mecobalamin:20200117161457j:plain
背景を黒くした画像も用意する
f:id:mecobalamin:20200117160637j:plain


ImageJでROIを切り抜くマクロを以下に示す
マクロの記述で次のことに気をつける

  • ディレクトリを示すときバックスラッシュを2回続ける
  • コマンドの内部でファイルをつなぐとき"+"の前後にスペースを入れない

マクロについて少し説明
ROI managerのリセットのやり方がよくわかなかったので
黒い画像を作成してダミーのROIを作成
その後ROIを消してROI managerからROIをなくした

画像のサイズを256 px x 256 pxに揃えるため
足りない部分に空白を作った
黒い背景なので若干気になる
それでROIの外側を黒くした画像も作成できるようにした

コメントアウトしてあるが

CopyonBlackimage("duplicate", RoiX, RoiY);

このfunctionを使うとROIの外側を黒くできる

この関数ではROIで指定した領域と
同じサイズで背景が黒の矩形の画像を作成し
ROIの内部の画像をコピーで貼り付けている

マスクを作って切り抜きたかったけど
色情報が無くなってしまって
うまくいかず。。。

あとif文の内部でexitを使うとマクロを止められる

切り抜いた画像を並べるとこんな感じ
f:id:mecobalamin:20200117191909j:plain

マクロはこうなる

// --- Reset status ---
String.resetBuffer;
List.clear;
run("Clear Results");

// --- Set Directories ---
inputdirectory = getDirectory("Select a Directory of Input Images");
WorkingDirectory = File.getParent(inputdirectory);
OutputDirectory = WorkingDirectory+"\\_PositiveImages\\";
RoiDirectory = WorkingDirectory + "\\_Rois\\";
TextDirectory = WorkingDirectory + "\\_Text\\";
RoiList = getFileList(RoiDirectory);
ImageList = getFileList(inputdirectory);

// --- Create text window for cascade file---
TextFileTitle = "positive.txt";
WindowTitle = "["+TextFileTitle+"]";
if (isOpen(TextFileTitle)){
	print(WindowTitle, "\\Update:");
}else{
	run("Text Window...", "name="+WindowTitle+" width=80 height=20 menu");
}

selectWindow(TextFileTitle);
saveAs("text", TextDirectory + "\\" + TextFileTitle);
//saveAs("text", WorkingDirectory + "\\" + TextFileTitle);

// ---Create Output Directory---
File.makeDirectory(OutputDirectory);
File.makeDirectory(TextDirectory);

// --- Set Batch mode ---
setBatchMode(true);
print("\\Clear");

// --- add dummy ROI to clear ROI manager ---
newImage("dummyROI", "32-bit", 512, 512, 1);
makeRectangle(0, 0, 100, 100);
roiManager("add");
close("dummyROI");

// ---- MAIN ----
print("Crop Cat Faces by ROIs.");

// Compare list sizes.
// the Macro will stop if list sizes are different.
if(RoiList.length != ImageList.length){
	setBatchMode("exit and display");
	print("File numbers are not same.\nPlease confirm number of files in each directories.");
	print("Macro finished.");
	selectWindow(TextFileTitle);
	close(TextFileTitle);
	exit("File numbers are not same.\nPlease cofirm number of files");
}

// Repeat trimming and saving images for number of images in imput directory.
for (i = 0; i < ImageList.length; i++){
	roiManager("Deselect");
	roiManager("Delete");

	roiManager("Open", RoiDirectory + RoiList[i]);
	numRoi = roiManager("count");
	open(inputdirectory + ImageList[i]);

	for (j = 0; j < numRoi; j++){

		// trimming and saving image
		RoiInfo = CropROIs(j, ImageList[i]);
		cropfname = RoiInfo[0];
		selectWindow(cropfname);
		saveAs("jpeg", OutputDirectory + cropfname);
		close(cropfname);

		// show statistics for saving
		print(WindowTitle, OutputDirectory + cropfname + " 1 " + RoiInfo[1] + " " + RoiInfo[2] + " " + RoiInfo[3] + " " + RoiInfo[4] + "\n");
		run("Clear Results");
	}
	close(ImageList[i]);
}

roiManager("Deselect");
roiManager("Delete");

// --- save text file ---
selectWindow(TextFileTitle);
saveAs("text", TextDirectory + "\\" + TextFileTitle);
close(TextFileTitle);

// --- Exit Batch mode ---
setBatchMode("exit and display");
print("operation completed.");

// --- end of main --- //


// --- --------- --- //
// --- functions --- //
// --- --------- --- //

// --- Crop image by Rois --- //
function CropROIs(n, fname){
	selectWindow(fname);
	DuplicateName = "duplicate";

	roiManager("Select", n);
	roiname = Roi.getName;

	// get statistics inside ROI
	getStatistics(area, mean, min, max, std);

	// duplicate image with ROI
	run("Duplicate...", "title="+DuplicateName+" duplicate");
	RoiX = getWidth();
	RoiY = getHeight();

	// convert outside ROI to black
	selectWindow("duplicate");
	// CopyonBlackimage("duplicate", RoiX, RoiY);

	// transform image size
	if(RoiX >= RoiY){
		RoiWidth = 256;
		RoiHeight = round(256 / RoiX * RoiY);
	} else {
		RoiWidth = round(256 / RoiY * RoiX);
		RoiHeight = 256;
	}

	selectWindow(DuplicateName);
	run("Size...", "width=RoiWidth height=RoiHeight constrain average interpolation=None");
	run("Canvas Size...", "width=256 height=256 position=Center");

	// add ROI name in file name
	cropfname = replace(fname, ".jpg", "_" + roiname + ".jpg");
	rename(cropfname);

	// get position of ROI on image.
	PositionX = round(128 - RoiWidth / 2);
	PositionY = round(128 - RoiHeight / 2);
	RoiInfo = newArray(cropfname, PositionX, PositionY, RoiWidth, RoiHeight, mean);

	// return image data by Array.
	return RoiInfo;
}

// --- copy cropped image on black background --- //
function CopyonBlackimage(fname, RoiX, RoiY){
	newImage("tmpImage", "RGB black", RoiX, RoiY, 1);
	selectWindow("duplicate");
	run("Copy");
	selectWindow("tmpImage");
	run("Paste");
	selectWindow("duplicate");
	close(fname);
	selectWindow("tmpImage");
	rename(fname);
}

続きます


ちょっと言い訳
英語は苦手です

それでもコードのコメントを英語にしてあるのは
コード内でできるだけ2バイト文字を使いたくないので・・・
すべて英語で書けたらいいけどねぇ