以前紹介したお役立ち多目的マクロに、「コントラスト自動調整マクロoptimizeMinAndMax」をくっつけました。
何かと出番の多い汎用型マクロの最新版です!
//Almighty_Processing.txt
//ContrastAdjust (As necessary, for 8bit, FullColor)
//Optimize minAndMax (As necessary)
//SetResolution (As necessary, for any Type)
//NombreCut type1 (As necessary, UseOneROI)
//NombreCut type2 (As necessary, CaseDivided by ODD/EVEN, [number==0 -> error])
//Renumbering from1 (As necessary, Files must be in subDirectory)
//Renumbering (As necessary, Automater)
//MakeDirAuto (FileName : trueTitle_number.ext)
print("");
print("Almighty Processing");
print("makeDirAuto");
print("listFilesRecursively");
//Define variable
flagFullColor = 0;
//Select Processing
Dialog.create("Processing setting");
Dialog.addMessage("Select Processing");
Dialog.addMessage("");
Dialog.addMessage("- Main -");
Dialog.addCheckbox("Contrast Adjustment", false);
Dialog.addCheckbox("Change the Resolution",false);
Dialog.addCheckbox("Nombre Cut",false);
Dialog.addMessage("");
Dialog.addMessage("- Additional -");
Dialog.addMessage("");
Dialog.addCheckbox("Optimize minAndMax",false);
Dialog.addCheckbox("Renumbering [from 1]",false);
Dialog.addCheckbox("Renumbering [Automator]",false);
Dialog.show;
flagContr = Dialog.getCheckbox();
flagReso = Dialog.getCheckbox();
flagNombre = Dialog.getCheckbox();
flagOptMinMax = Dialog.getCheckbox();
flagRenumFrom1 = Dialog.getCheckbox();
flagRenumATMT = Dialog.getCheckbox();
//print
print("");
if(flagContr == 1) print("flagContra = ON");
if(flagReso == 1) print("flagReso = ON");
if(flagNombre == 1) print("flagNombre = ON");
if(flagOptMinMax == 1) print("flagOptMinMax = ON");
if(flagRenumFrom1 == 1) print("flagRenumFron1 = ON");
if(flagRenumATMT == 1) print("flagRenumATMT = ON");
print("");
if(flagRenumFrom1 == 1 && flagRenumATMT == 1) exit("Two \"Renumbering Processing\" were selected.");
if (flagContr == 0 && flagReso == 0 && flagNombre == 0) exit("You must select at least one processing!!");
//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);
//optimizeMinAndMax
var optimizedMin = 0;
var optimizedMax = 255;
if(flagOptMinMax == 1) optMinMax();
//Select Nombre Type
if(flagNombre == 1){
Dialog.create("Nombre Cut setting");
items = newArray("Use One ROI", "Case Divided by ODD/EVEN");
Dialog.addRadioButtonGroup("Select NombreCut Type", items, 2, 1, "Use One ROI");
Dialog.show;
nombreRadio = Dialog.getRadioButton();
if(nombreRadio == "Use One ROI") NombreType = 1;
if(nombreRadio== "Case Divided by ODD/EVEN") NombreType = 2;
}
//Clear ROI Manager and set ROI
if(flagNombre == 1){
roiCount = roiManager("count");
if(roiCount > 0){
roiManager("Deselect");
roiManager("Delete");
}
if(NombreType == 1){
//ROI setting
waitForUser("ROI setting","Open an image and set [ROI] using rectangle tool, then click [OK].");
roiManager("Add");
close();
}
if(NombreType == 2){
//ROI setting ODD(1,3,5,7,...)
waitForUser("ODD(1,3,5,7,...)","Open an [ODD number] file and set [ROI] using rectangle tool, then click [OK].");
roiManager("Add");
close();
//ROI setting EVEN(2,4,6,8,...)
waitForUser("EVEN(2,4,6,8,...)","Open an [EVEN number] file and set [ROI] using rectangle tool, then click [OK].");
roiManager("Add");
close();
}
}
//Enter parameter
Dialog.create("Processing Setting");
if(flagContr == 1) Dialog.addNumber("min (0-255)",optimizedMin);
if(flagContr == 1) Dialog.addNumber("Max (0-255)",optimizedMax);
if(flagContr == 1) Dialog.addCheckbox("Full Color", false);
if(flagContr == 1) Dialog.addMessage("");
if(flagReso == 1) Dialog.addNumber("pre dpi",600);
if(flagReso == 1) Dialog.addNumber("post dpi(Target)",400);
if(flagRenumATMT == 1 || flagRenumFrom1 == 1) Dialog.addNumber("Digit of Page Number",4);
items = newArray("JPEG", "PNG");
Dialog.addRadioButtonGroup("Output format", items, 1, 2, "JPEG");
Dialog.show;
if(flagContr == 1) min = Dialog.getNumber();
if(flagContr == 1) max = Dialog.getNumber();
if(flagContr == 1) flagFullColor = Dialog.getCheckbox();
if(flagReso == 1) preDpi = Dialog.getNumber();
if(flagReso == 1) postDpi = Dialog.getNumber();
if(flagRenumATMT == 1 || flagRenumFrom1 == 1) digit = Dialog.getNumber();
output = Dialog.getRadioButton();
//print
print("");
if(flagFullColor == 1) print("flagFullColor = ON");
if (flagContr == 1) print("min =",min,", Max =",max);
if (flagReso == 1) print("pre dpi =",preDpi,", Target dpi =",postDpi);
//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 save_column save_row");
if (output == "JPEG") print("JPEG quality =",quality);
print("");
wait(1000);
//SetResolution Setting
if(flagReso == 1) compressionRate = postDpi/preDpi;
//reference setting
var referLength;
if(flagRenumATMT == 1){
referenceCheck(openDir);
}
//make parentDirectory
parentDir = saveDir+"postProc_"+getTimeStamp()+"/";
File.makeDirectory(parentDir);
//operation
startTime = whatTimeNow();
setBatchMode(true);
var totalFiles = 0;
var procCount = 0;
if (flagRenumFrom1 == 1){
Table.create("Correspondence Table");
}
countFiles(openDir);
listFiles(openDir);
//clear ROI Manager
if(flagNombre == 1){
roiManager("Deselect");
roiManager("Delete");
}
//fin
if (flagRenumFrom1 == 1){
Table.update;
}
finishTime = whatTimeNow();
print("");
if (flagContr == 1) print("min =",min,", Max =",max);
if (flagReso == 1) print("Compression Rate =",compressionRate);
if (output == "JPEG") print("JPEG quality =",quality);
print("Start Time .... ",startTime);
print("FinishTime ... ",finishTime);
print("oshimai");
beep();
//-----------------------------------------------------------------------------
//Define referenceCheck
function referenceCheck(dir){
list = getFileList(dir);
if(endsWith(list[0],"/")){
referenceCheck(dir+list[0]);
}else{
name = list[0];
dotIndex = lastIndexOf(name,".");
title = substring(name,0,dotIndex);
space = lastIndexOf(title," ");
trueTitle = substring(title,0,space);
number = substring(title,space+1);
reference = trueTitle;
referLength = lengthOf(reference);
print("Check trueTitle. (e.g. NARUTO_60)");
print("trueTitle=[",trueTitle,"]");
showMessageWithCancel("Check trueTitle (except : number.ext), and click [OK].");
}
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//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++;
//Rename Section
if(flagRenumATMT == 1){
name = getTitle();
dotIndex = lastIndexOf(name,".");
title = substring(name,0,dotIndex);
//ReNumbering
titleLength = lengthOf(title);
if(referLength < titleLength){
space = lastIndexOf(title," ");
trueTitle = substring(title,0,space);
number = substring(title,space+1);
newname = trueTitle+"_"+zeroPad(number,digit)+ext;
}else if(referLength == titleLength){
newname = title+"_"+zeroPad(1,digit)+ext;
dotIndex = lastIndexOf(newname,".");
title = substring(newname,0,dotIndex);
underBar = lastIndexOf(title,"_");
trueTitle = substring(title,0,underBar);
number = substring(title,underBar+1);
}
}else if(flagRenumATMT == 0){
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;
if (flagRenumFrom1 == 1) newname = trueTitle+"_"+zeroPad(i+1,digit)+ext;
}
rename(newname);
//Output Correspondence table
if (flagRenumFrom1 == 1){
Table.set("index",procCount-1,procCount);
Table.set("Original",procCount-1,name);
Table.set("Newname",procCount-1,newname);
}
//Nombre Cut
if(flagNombre == 1){
if(NombreType == 1){
roiManager("Select", 0);
run("Crop");
}
if(NombreType == 2){
number = parseInt(number);
if(number == 0) exit("Error!! Number must not be null.");
//ODD(1,3,5,7,...)
if(number%2 == 1){
roiManager("Select", 0);
run("Crop");
}
//EVEN(2,4,6,8,...)
if(number%2 == 0){
roiManager("Select", 1);
run("Crop");
}
}
}
//ContrastAdjust 8 bit
depth = bitDepth();
if(flagFullColor == 0) {
if (depth == 8) {
//ContrastAdjust (As necessary)
if (flagContr == 1) setMinAndMax(min, max);
if (flagContr == 1) run("Apply LUT");
}
}
if(flagFullColor == 1) {
//ContrastAdjust (As necessary)
if (flagContr == 1) setMinAndMax(min, max);
if (flagContr == 1) run("Apply LUT");
}
//SetResolution
if (flagReso == 1){
width = getWidth;
height = getHeight;
targetW = floor(width*compressionRate);
run("Size...", "width=targetW constrain average interpolation=Bicubic");
}
//Progress.the second decimal place by 10000/100
print(procCount,"/",totalFiles,"...Progress=",floor(procCount/totalFiles*10000)/100,"%");
//Save to SubDirectory
subDir = parentDir+trueTitle+"/";
if(!File.exists(subDir)){
File.makeDirectory(subDir);
}
saveAs(output, subDir+newname);
print("Save to...",subDir+newname);
close(newname);
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//Define optMinMax
function optMinMax(){
print("");
print("OptimizeMinAndMax");
if(nImages == 0) waitForUser("OptMinAndMax. Open an image, then click [OK]");
//Enter parameter
Dialog.create("OptMinAndMax Setting");
Dialog.addNumber("targetMeanWhite",254.97);
Dialog.addNumber("targetMeanBlack",27); //default->18
Dialog.show;
targetMeanWhite = Dialog.getNumber();
targetMeanBlack = Dialog.getNumber();
name = getTitle();
showMessage("Select Desktop (OptimizeMinAndMax) ");
saveDir = getDirectory("OptimizeMinAndMax. Choose Desktop");
print("");
print("Your Desktop is...");
print(saveDir);
print("");
saveAs("Jpeg", saveDir+"CheckMaxAndMin.jpg");
//optimize Max
setTool("rectangle");
run("Specify...", "width=100 height=100 x=10 y=10");
waitForUser("ROI selection","Set ROI on the \"WHITE BACK GROUND\", then click [OK].");
getStatistics(area,mean);
setBatchMode(true);
if(mean < 150){
exit("Error!! This area is not WHITE BACK GROUND.");
}
run("Crop");
saveAs("Jpeg", saveDir+"CheckMax.jpg");
close();
Max = 254;
min = 0;
print("optimized Max Macro, TargetMean =",targetMeanWhite);
for (i=0; i<254; i++){
open(saveDir+"CheckMax.jpg");
setMinAndMax(min, Max);
run("Apply LUT");
getStatistics(area,mean);
print("Max=",Max," mean=",mean);
if(mean >= targetMeanWhite){
optimizedMax = Max;
i = 255; //break
}
Max = Max-1;
close();
}
print("OptimizedMax =",optimizedMax);
setBatchMode(false);
//optimize min
open(saveDir+"CheckMaxAndMin.jpg");
setTool("rectangle");
run("Specify...", "width=100 height=100 x=10 y=10");
waitForUser("ROI selection","Set ROI on the \"BLACK ZONE\", then click [OK].");
getStatistics(area,mean);
setBatchMode(true);
if(mean > 100){
exit("Error!! This area is not BLACK ZONE.");
}
run("Crop");
saveAs("Jpeg", saveDir+"CheckMin.jpg");
close();
max = optimizedMax;
min = 1;
print("");
print("OptimizedMin Macro, TargetMean=",targetMeanBlack);
for (i=0; i<254; i++){
open(saveDir+"CheckMin.jpg");
setMinAndMax(min, max);
run("Apply LUT");
getStatistics(area,mean);
print("min=",min," mean=",mean);
if(mean <= targetMeanBlack){
optimizedMin = min;
i=255; //break
}
min = min+1;
close();
}
print("");
print("Title =",name);
print("OptimizedMin =",optimizedMin);
print("OptimizedMax =",optimizedMax);
print("oshimai");
setBatchMode(false);
} //function end
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//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;
}
//-----------------------------------------------------------------------------
新機能
① 適切なコントラストと濃度を自動で調べてくれるマクロ、「Optimize Min And Max」を搭載
「Optimize Min And Max」で調べた値をそのまま「Contrast Adjustment」に引き継いでくれるので、少し作業がラクになります。min値Max値入力ダイアログの初期値として引き継ぐので、微調整も可能です。
そのままコードコピペだけで使えるように、「function optMinMax」内のsaveDirのデスクトップ指定を「ディレクトリをそのつど選択」の形式にしています。いちいち面倒だと思うので、コードをいじれる方は「saveDir = getDirectoryなにがし」のところを、「saveDir = /Users/yu3xx/Desktop/」とかに書き換えると幸せになれると思います。
ちなみにここで保存するcheckMaxなどの3つのファイルは、解析に使う目的で一時的に保存しているだけなので、あとで捨ててOKなファイルです。
② フルカラー画像に対するコントラスト・濃度調整を実装
地味に新機能です。いままでの「Contrast Adjustment」は白黒画像に対してでしたが、フルカラー画像に対しても、min値Max値の調整で簡易的にコントラスト・濃度を調整できるようにしました。使いたい場合はパラメータ設定画面で[Full Color]にチェック。
※ 残念なことに、Optimize Min And Maxはフルカラーには対応していないので、あらかじめ適切なmin値Max値を調べておいてください。手作業です。
使い方のポイント
① 起動すると処理選択ウインドウが表示されますので、必要なものだけチェックしてください。
② 選択したProcessing Folderの中に入っているサブフォルダ内のデータまで全て処理します(ListFiles)。
③ フォルダ自動生成を命令しているので、各ファイルの名前は「trueTitle_number.拡張子」じゃないと動きません。(numberの前のアンダーバーが大事!)
例)僕のヒーローアカデミア_07_0149.png
trueTitle = 僕のヒーローアカデミア_07
number = 0149
※Renumbering [Automator] は例外です。numberの前にspaceが来るはずです。
④ 各処理の細かな解説は、以下のページを参考にしてみてください。
・Contrast Adjustment
コントラスト・濃度を調整するMacro - その漫画自炊オタクはImageJマクロに恋をする
・Optimize Min And Max
コントラスト調整に必要なMax値とMin値を自動で求めるためのMacro - その漫画自炊オタクはImageJマクロに恋をする
・Change the Resolution
解像度を変更するMacro - その漫画自炊オタクはImageJマクロに恋をする
・Nombre Cut
実用版!ノンブル領域を削除し, コントラスト調整, 解像度変更も一括で行えるMacro - その漫画自炊オタクはImageJマクロに恋をする
・Renumbering [from 1]
【多目的】コントラスト調整やリサイズ処理などなどを、一気にやってしまうためのMacro - その漫画自炊オタクはImageJマクロに恋をする
・Renumbering [Automator]
【Mac】実用版!PDFからJPEGに変換した画像のコントラスト調整+フォルダ収納のためのMacro - その漫画自炊オタクはImageJマクロに恋をする
マクロの起動方法
①ImageJ上部タブの[Plugins]→[New]→[Macro]で起動したエディタに、記事の一番上のコードをコピペしてtxtファイル(Almighty_Processing.txt)を作成・保存する。
②保存したファイルをImageJフォルダ内の[plugins]フォルダにしまう。
このとき、[plugins]フォルダの中に新たに適当な名前のフォルダを作って、その中にしまってもOKです。ここでは仮に「自炊」というフォルダにtxtファイルを突っ込んだとします。
③一度ImageJを再起動すると、マクロがインストールされ、起動準備OK。
④上部タブ[Plugins]→[自炊]→[Almighty Processing]でマクロが実行されます。
ライセンスなんかは一切無いので、ぜひぜひ自由に使ってみてください!