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

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

【ImageJマクロ応用編】#4 PluginとMacroの処理時間の違い

 

 




 

 

皆さまこんにちは。

最近NewJeansにハマってます、yu3xx(ゆーさんちょめちょめ)です。

 

 

 

ImageJ上で自動処理を行うための拡張機能には「Macro」のほかに「Plugin」というものがあります。

 

PluginはJava言語で記述することになるのですが、レクチャー記事がMacroに比べ極端に少ないため、勉強するのが面倒でとっつきづらいのが現状です。

 

とはいえ、PluginがMacroよりも「よっぽど」優れているのであれば、まあ...仕方ない、ちょっとは勉強してやるか、と思うところもなくはないのですが...。

 

ということで、本記事では漫画自炊用Pluginを実際に作って、その処理時間を計測し、Macroの場合と比較してみました。

 

 


もくじ

 

 

Plugin

 

画像コントラストを変更するためのPluginです。試行錯誤しながらなんとか作りました...

 

Pluginで画像を扱う際には、まずImagePlusというクラス(属性)で画像を取得し、さらに画像処理を行う場合にはImageProcessorというクラスに画素値を入れ込むようです。

 

ふぇぇ。なんのこっちゃ。

 


import ij.*;
import ij.process.*;
import ij.gui.*;
import java.awt.*;
import ij.plugin.*;
import ij.plugin.frame.*;
import java.io.File;


public class contrast_Adjust implements PlugIn {

	double min;
	double max;

	public void run(String arg) {
		//Select dir
		IJ.showMessage("Select Open Folder");
		String openDir = IJ.getDirectory("Choose a Directory");
		if (openDir == null) return;
		
		IJ.showMessage("Select Save Folder");
		String saveDir = IJ.getDirectory("Choose a Directory");
		if (saveDir == null) return;
		
		//Get min max
		min = IJ.getNumber("Min (42 de OK?):", 42);
		max = IJ.getNumber("Max (243 de OK?):", 243);

		//Open dir
		File dir = new File(openDir);
		File[] list = dir.listFiles();

		//Operation
		for (File file : list) {
			if (file.isFile()) {
				processImage(file.getAbsolutePath(), saveDir);
			}
		}

		
	}

	private void processImage(String filePath, String saveDir) {
		//Open image
		ImagePlus imp = IJ.openImage(filePath);

		//Get Px data
		ImageProcessor ip = imp.getProcessor();

		//Apply min max
		ip.setMinAndMax(min, max);
		IJ.run(imp, "Apply LUT", "");

		//Display image
		//imp.show();
	
		//Save image
		String fileName = new File(filePath).getName();
		String savePath = saveDir + fileName;
		IJ.saveAs(imp, "JPEG", savePath);
	}


}

 

Macro

 

マクロは、以前公開した「アレンジ用プログラム」をベースにして、上記プラグインと同じような処理が出来るようにしました。

 


//ListFilesRecursively_free

//JIBUNN DE IROIRO TSUKUTTE NE
//"??????" NO TOKORONI IROIRO KAITE NE


//Do something for selected folder
showMessage("Select Processing Folder");
openDir = getDirectory("Choose a Directory");
print("Processing :",openDir);
selectWindow("Log");
showMessage("Select Save Folder");
saveDir = getDirectory("Choose a Directory");
print("Save to :",saveDir);
selectWindow("Log");
list = getFileList(openDir);
wait(1000);


//Enter parameter
Dialog.create("Processing Setting");
items = newArray("JPEG", "PNG");
Dialog.addRadioButtonGroup("Output format", items, 1, 2, "JPEG");
Dialog.show;
output = Dialog.getRadioButton();

//Extension
if(output == "JPEG") ext = ".jpg";
if(output == "PNG") ext = ".png";

//JPEG,PNG quality setting(jpeg=90,gif=-1)
quality = 90;
run("Input/Output...", "jpeg=quality gif=-1 file=.csv copy_column copy_row save_column save_row");



//make parentDirectory
parentDir = saveDir+"postProc_"+getTimeStamp()+"/";
File.makeDirectory(parentDir);


//operation
startTime = whatTimeNow();
setBatchMode(true);
var totalFiles = 0;
var procCount = 0;

countFiles(openDir);
listFiles(openDir);


//fin
finishTime = whatTimeNow();
print("");

print("Start Time .... ",startTime);
print("FinishTime ... ",finishTime);
print("oshimai");
beep();



//-----------------------------------------------------------------------------
//Define countFiles

function countFiles(dir){
	list = getFileList(dir);
	for(i=0; i<list.length; i++){
		if(endsWith(list[i],"/")){
			countFiles(dir+list[i]);
		}else{
			totalFiles++;
		}
	}
}
//-----------------------------------------------------------------------------

//-----------------------------------------------------------------------------
//Define listFiles

function listFiles(dir){
	
	list = getFileList(dir);
	for(i=0; i<list.length; i++){
		if(endsWith(list[i],"/")){
			listFiles(dir+list[i]);
		}else{
			open(dir+list[i]);
			operation();
		}
	}
	
}
//-----------------------------------------------------------------------------

//-----------------------------------------------------------------------------
//Define operation

function operation(){
	
	procCount++;

	//Name setting
	name = getTitle();
	dotIndex = indexOf(name, ".");
	title = substring(name,0,dotIndex);
	underBar = lastIndexOf(title,"_");
	trueTitle = substring(title,0,underBar);
	number = substring(title,underBar+1);
	newname = title+ext;	
	rename(newname);

	

	

	//--------- KOKO NI IROIRO IRETE NE ----------

	//????????????????????????????????????????????



	setMinAndMax(42,243);

	run("Apply LUT");


	//????????????????????????????????????????????

	//Save to SubDirectory
	subDir = parentDir+trueTitle+"/";
	if(!File.exists(subDir)){
		File.makeDirectory(subDir);
	}
	
	saveAs(output, subDir+newname);

	close(newname);
}
//-----------------------------------------------------------------------------

//-----------------------------------------------------------------------------
//Define function getTimeStamp

function getTimeStamp(){
	getDateAndTime(year,month,dayOfWeek,dayOfMonth,hour,minute,second,msec);
	timeStamp = "string";
	strYear = ""+year;
	month = month+1;
	if(month < 10){
		strMonth = "0"+month;
	}else{
		strMonth = ""+month;
	}
	if(dayOfMonth < 10){
		strDayOfMonth = "0"+dayOfMonth;
	}else{
		strDayOfMonth = ""+dayOfMonth;
	}
	if(hour < 10){
		strHour = "0"+hour;
	}else{
		strHour = ""+hour;
	}
	if(minute < 10){
		strMinute = "0"+minute;
	}else{
		strMinute = ""+minute;
	}
	if(second < 10){
		strSecond = "0"+second;
	}else{
		strSecond = ""+second;
	}
	timeStamp = strYear+strMonth+strDayOfMonth+"_"+strHour+"h"+strMinute+"m"+strSecond+"s";
	return timeStamp;
}
//-----------------------------------------------------------------------------

//-----------------------------------------------------------------------------
//Define function whatTimeNow

function whatTimeNow(){
	getDateAndTime(year,month,dayOfWeek,dayOfMonth,hour,minute,second,msec);
	stringTime = "string";
	strYear = ""+year;
	month = month+1;
	if(month < 10){
		strMonth = "0"+month;
	}else{
		strMonth = ""+month;
	}
	if(dayOfMonth < 10){
		strDayOfMonth = "0"+dayOfMonth;
	}else{
		strDayOfMonth = ""+dayOfMonth;
	}
	if(hour < 10){
		strHour = "0"+hour;
	}else{
		strHour = ""+hour;
	}
	if(minute < 10){
		strMinute = "0"+minute;
	}else{
		strMinute = ""+minute;
	}
	if(second < 10){
		strSecond = "0"+second;
	}else{
		strSecond = ""+second;
	}
	stringTime = strYear+"/"+strMonth+"/"+strDayOfMonth+"_"+strHour+":"+strMinute+":"+strSecond;
	return stringTime;
}

//-----------------------------------------------------------------------------

//-----------------------------------------------------------------------------
//Define function zeroPadding

function zeroPad(int,digitZeroPad){
	if(int < 0){
		exit("ZeroPadding Error!!  int<0");
	}
	stringInt = ""+int;
	digitStringInt = lengthOf(stringInt);
	digitSubtra = digitZeroPad-digitStringInt;
	if(digitSubtra < 0){
		exit("ZeroPadding Error!!  digitSubtra<0");
	}
	if(digitZeroPad > 0){
		for(i=0; i<digitSubtra; i++){
			stringInt = "0"+stringInt;
		}
	}
	return stringInt;
}	

//-----------------------------------------------------------------------------

 

比較方法

 

・Plugin、Macroともに、min 42、max 243でコントラスト調整しJPEG保存

・処理対象はOne Piece集英社)第107巻、全235ファイル

・処理開始から235ファイル目が保存されるまでの時間を3回計測し平均(ディレクトリ選択やmin maxの手動設定を除いた、実際の処理時間)

 

結果

 

・Plugin... 64.2 sec、62.8 sec、63.3 sec。平均 63.4 sec

・Macro... 60.2 sec、59.8 sec、61.4 sec。平均 60.4 sec

 

感想

 

なんと

Macroの方がちょびっとだけ速い

という結果でした。

 

もっと難しい処理であればPlugin側が有利になるかもしれませんが、簡単な自炊画像処理においてはMacroで十分ということでしょうか。

 

 

 

Pluginの処理コードを実際に書いてみてわかったこと

・ネット上に情報が少ないので拾いづらい

・ChatGPT大先生が頼りになるけどウソも多い

・Macroでは使えたRecord機能が無いのがつらい

・でも試行錯誤してやっと動いたときにはとても気持ちいい

 

 

 

 

まとめ

 

 ・漫画自炊処理においては、PluginとMacroの処理時間はほぼ同等

 ・Pluginに乗り換えるほどの価値は感じないが、なんだかんだでPlugin作りも楽しい

 

 

 

 


おしまい

 

 

  

 

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

imagej-jisui.hatenablog.com