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

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

【Mac】実用版!PDFからJPEGに変換した画像のコントラスト調整+フォルダ収納のためのMacro

 

f:id:yu3xx:20200624014908j:plain

 

PDFから変換したJPG画像に対して、濃度・コントラスト調整や解像度変更などの画像処理を行い、さらに自動作成したフォルダに格納するまでを一括で行うことの出来るマクロです。

 

 

//ContrastAdjust_Renumbering_mkDirAuto.txt
//ContrastAdjust 8bit
//ReNumbering (number:digit=5)
//MakeDirectoryAuto
//SetResolution (As necessary)
//preProcess FileFormat...[trueTitle number] (Automator:PDFtoJPG)

setBatchMode(true);

//Do something for selected folder
showMessage("Select Open 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);

//reference setting
i = 0;
name = list[i];
name = replace(name," ","_");//hankaku space
name = replace(name," ","_");//ZENKAKU space
dotIndex = lastIndexOf(name,".");
title = substring(name,0,dotIndex);
underBar = lastIndexOf(title,"_");
trueTitle = substring(title,0,underBar);
number = substring(title,underBar+1);

reference = trueTitle;
referLength = lengthOf(reference);
print("trueTitle=[",trueTitle,"]");
print("EX) NARUTO_60");
showMessageWithCancel("Check trueTitle (except : number.extension), and click [OK].");

//Whether or not to set resolution
Dialog.create("SetResolution Setting");
items = newArray("No", "Yes");
Dialog.addRadioButtonGroup("Do you change the resolution?", items, 1, 2, "No");
Dialog.show;
ansReso=Dialog.getRadioButton();
if(ansReso == "Yes") flagReso = 1;
else flagReso = 0;

//Enter parameter
Dialog.create("Processing Setting");
Dialog.addNumber("min (0-255)",0);
Dialog.addNumber("Max (0-255)",255);
if (flagReso == 1) Dialog.addNumber("axis x matrixSize (400dpi... 1600-1800)",0);
Dialog.show;
min = Dialog.getNumber();
max = Dialog.getNumber();
if (flagReso == 1) reso = Dialog.getNumber();

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

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


//operation
startTime = whatTimeNow();
rejectCounter = 0;

for (i=0; i<list.length; i++){
	
	open(openDir+list[i]);
	
	//Progress.the second decimal place by 10000/100
	print(i+1,"/",list.length,"...Progress=",floor((i+1)/list.length*10000)/100,"%");
	
	//Processing for GrayScale Images
	depth = bitDepth();
	if (depth == 8) {
		setMinAndMax(min, max);
		run("Apply LUT");

		//SetResolution (As necessary)
		if (flagReso == 1) run("Size...", "width=reso constrain average interpolation=Bicubic");
	
	}else{
		rejectCounter++;
	}
	
	name = getTitle();
	name2 = replace(name," ","_");//hankaku space
	name2 = replace(name2," ","_");//ZENKAKU space
	dotIndex = lastIndexOf(name2,".");
	title = substring(name2,0,dotIndex);

	//ReNumbering Section
	titleLength = lengthOf(title);

	if(referLength < titleLength){
		underBar = lastIndexOf(title,"_");
		trueTitle = substring(title,0,underBar);
		number = substring(title,underBar+1);

		newname = trueTitle+"_"+zeroPad(number,5)+".jpg";
		
	}else if(referLength == titleLength){
		newname = title+"_00001.jpg";
		dotIndex = lastIndexOf(newname,".");
		title = substring(newname,0,dotIndex);
		underBar = lastIndexOf(title,"_");
		trueTitle = substring(title,0,underBar);
		number = substring(title,underBar+1);
	}	

	//Save to SubDirectory
	subDir = parentDir+"/"+trueTitle+"/";
	if(!File.exists(subDir)){
		File.makeDirectory(subDir);
	}
		
	rename(newname);
	if (depth == 8) {
		saveAs("Jpeg", subDir+newname);
		print("Save to...",subDir+newname);
	}else{
		File.copy(openDir+name,subDir+newname);
		print("Move to...",subDir+newname);
	}
	close(newname);
}

//fin
finishTime = whatTimeNow();
print("");
print("min=",min,", Max=",max);
print("JPEG quality=",quality);
print("Rejected Color Image=",rejectCounter);
print("Start Time .... ",startTime);
print("FinishTime ... ",finishTime);

print("oshimai");
beep();


//-----------------------------------------------------------------------------
//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+strMinute+strSecond;
	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;
}	

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

 2020.04.09.追記

解像度変更の有無をダイアログから選択出来るように修正しました。

 

事前準備

PDFをJPGに変換するためにMac初期装備のAutomator(オートメーター)を使用します。

f:id:yu3xx:20200203230514p:plain

Automatorの使い方は下記サイト様がくわしく解説してくれています。

bamka.info

 

私の使用しているAutomator設定はこんな感じです。

f:id:yu3xx:20200203230702p:plain

RGBにしておくと、カラーページはRGB colorで、グレースケールページは8bitで勝手に吐き出してくれるので便利です。

画質設定はお好みですが、上記の設定でもわりと十分だと感じてます。

 

Automatorのアプリケーションを一度作っておけば、あとは作成したアイコン上にPDFをドラッグするだけで自動的にJPGが生成されます。

 

このあと、ImageJを用いて上記マクロで画像処理・フォルダ格納していくのですが、ここで少し問題があります。

 

たとえばNARUTO_03.pdfを変換すると、生成されるjpgは

NARUTO_03.jpeg

NARUTO_03 2.jpeg

NARUTO_03 3.jpeg

...

NARUTO_03 100.jpeg

...

となります。

何が問題かというと

 

①1ページ目に番号(number)がふられていない。

②巻数(volume)とページ数(number)の間が「半角スペース」(なんとなく気持ち悪い)

③ページ数のケタが不揃いのため、使用アプリによってはソートが狂う可能性がある。

 

この3つです。 

この問題を解決して、NARUTO_03_00001.jpgとするために、上記コードで「Renumbering」の命令を組んでいます。

 

 

コードの内容

①作業対象となるOpen Folderと保存先のSave Folderを設定。

②Renumberingの準備としてreference(ページ順を抜かしたファイル名、例えば NARUTO_60)を認識させ、確認する。

濃度・コントラスト調整

④場合によっては、解像度変更

Renumberingでファイル名を変更(桁揃えはZeroPadding)

⑥ファイル名からサブフォルダを自動生成する

⑦それぞれの画像を正しいサブフォルダの中に保存する

 

 

 

 

マクロの起動方法

①ImageJ上部タブの[Plugins]→[New]→[Macro]で起動したエディタに、記事の一番上のコードをコピペしてtxtファイル(ContrastAdjust_Renumbering_mkDirAuto.txt)を作成・保存する。

 

②保存したファイルをImageJフォルダ内の[plugins]フォルダにしまう。

 

このとき、[plugins]フォルダの中に新たに適当な名前のフォルダを作って、その中にしまってもOKです。ここでは仮に「自炊」というフォルダにtxtファイルを突っ込んだとします。

 

③一度ImageJを再起動すると、マクロがインストールされ、起動準備OK。

 

④上部タブ[Plugins]→[自炊]→[ContrastAdjust Renumbering mkDirAuto]でマクロが実行されます。

 

 

imagej-jisui.hatenablog.com

 

 

 

 

ライセンスなんかは一切無いので、ぜひぜひ自由に使ってみてください!

 

imagej-jisui.hatenablog.com