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

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

【縦線まとめ】縦線ノイズ除去系マクロの整理!

 

f:id:yu3xx:20200624020109j:plain

 

縦線ノイズ、横線ノイズの自動検出・除去マクロの最新修正版のまとめです。

 

 いちいち色んなページにいくのも大変だし、中身が古いバージョンのモノもあるので、ここで最新修正版をまとめて整理しちゃいます。

 

もくじ

 

 

1. フォルダ内全画像タテ線ノイズ自動除去

フォルダ内全画像に対して、縦線ノイズ自動検出+除去を行います。検出・除去された画像のみ保存フォルダに保存されます。

//Vertical_Noise_Correction_HiSp.txt

version = "3.2.1";
print("ver",version);

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);

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

//Output format
Dialog.create("Select Output Format");
items = newArray("JPEG", "PNG");
Dialog.addRadioButtonGroup("Output format", items, 1, 2, "JPEG");
Dialog.show;
output = Dialog.getRadioButton();


//--------Set Parameter--------------

//Segmentation setting
xSeg = 20;
ySeg = 20;

//BlackZone threshold(L_mean)
TH_kuroBeta = 50;

//WhiteLine threshold(s_mean)
TH_whiteLine = 20;

//HighIntensity threshold
TH_HighIntensity = 20;

//HighCount threshold
TH_countRatio = 90;

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


//To prevent a memory shortage error
var x,y,sum,xStep,yStep,depth,segCount,xi,yi,pixelValue,detectedCount,rejectCounter,correctCount;
var saveFlag,width,height,L_mean,a,b,s_mean,count,ii,jj,pixel_intensity,iii,beta,correctedValue;
var xSeg,ySeg,TH_kuroBeta,TH_whiteLine,TH_HighIntensity,TH_countRatio,seed;


//operation

startTime = whatTimeNow();
x = 0;
y = 0;

detectedCount = 0;
rejectCounter = 0;

for (i=0; i<list.length;i++){
	
	open(openDir+list[i]);

	//progress
	print(i+1,"/",list.length,"...Progress=",floor((i+1)/list.length*10000)/100,"%");
	
	memory = IJ.currentMemory();
	memory = parseInt(memory)/1000000;
	print("Current Memmory=",d2s(memory,2),"MB");

	saveFlag = 0;
	correctCount = 0;
	sum = 0;

	width = getWidth();
	height = getHeight();
	
	//value of movement
	xStep = floor(xSeg/2);
	yStep = floor(ySeg/2);

	depth = bitDepth();
	if (depth == 8) {
		
		//Segmentation section
		segCount = 1;
		for(y=0; y<height-ySeg; y=y+yStep){
			for(x=0; x<width-xSeg; x=x+xStep){
				for(xi=0; xi<xSeg; xi++){
					for(yi=0; yi<ySeg; yi++){
						pixelValue = getPixel(x+xi,y+yi);
						sum = sum+pixelValue;
					}
				}
				L_mean = sum/xSeg/ySeg;
				sum = 0;
				
				//print(i+1,"/",list.length,"...Progress=",floor((i+1)/list.length*10000)/100,"%",", ","Segment No=",segCount);
				segCount++;

				//recognize kuroBeta
				if(L_mean < TH_kuroBeta){
					a = x+5; //exclude both ends of image (5+5 pixels)
					b = y;

					//small rectangle ROI scan
					for(ii=0; ii<xSeg-10; ii++){
						b = y; //reset b (++ by jj_loop)
						for(yi=0; yi<ySeg; yi++){
							pixelValue = getPixel(a,b+yi);
							sum = sum+pixelValue;
						}
						s_mean = sum/ySeg;
						sum = 0;

						//vertical direction scan on detected whiteLine
						if(s_mean > TH_whiteLine){
							count = 0;
							for(jj=0; jj<ySeg; jj++){
								pixel_intensity = getPixel(a,b);
								
								//count HighIntensity
								if(pixel_intensity > TH_HighIntensity){
									count++;
									b++;
								}
							}
							//Noise Correction
							if(count > ySeg*TH_countRatio/100){
								correctCount++;
								//print("(",a,",",b,")","Noise Correction!!",correctCount);
								saveFlag = 1;
								b = y;		
								for(iii=0; iii<ySeg; iii++){
									seed = random(); //between 0 and 1
									beta = L_mean-seed*10;
									if(beta < 0) beta = 0;
									correctedValue = floor(beta);
									setPixel(a,b,correctedValue);
									b = b+1;
								}
							}
						}
						a = a+1; //in ii_loop
					}
				}
			}
		}
	}else{
		rejectCounter++;
		print("...Color Image!");
	}	
	if(saveFlag == 1){
		name = getTitle();
		dotIndex = lastIndexOf(name,".");
		title = substring(name,0,dotIndex);

		if (output == "JPEG") newname = title+".jpg";
		else if (output == "PNG") newname = title+".png";

		rename(newname);

		if (output == "JPEG") saveAs("Jpeg", saveDir+newname);
		else if (output == "PNG") saveAs("PNG", saveDir+newname);

		detectedCount++;
	}
	close();
}

//fin
finishTime = whatTimeNow();
print("DetectedCounts=",detectedCount);
print("Start Time .... ",startTime);
print("FinishTime ... ",finishTime);
print("oshimai");


//-----------------------------------------------------------------------------
//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;
}

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

 2020.04.08. 追記

書き出しフォーマット(JPEG, PNG)をダイアログから選択出来るように修正しました。

 

 2021.11.07. 追記

解析過程のLogを表示させることが使用メモリ過多の原因となっていたので、改善しました。

 

コード内容紹介ページ

 

2. フォルダ内全画像ヨコ線ノイズ自動除去

フォルダ内全画像に対して、横向きスキャン時の横線ノイズ自動検出+除去を行います。検出・除去された画像のみ保存フォルダに保存されます。

//Horizontal_Noise_Correction_HiSp.txt

version = "3.4.1";
print("ver",version);

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);

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

//Output format
Dialog.create("Select Output Format");
items = newArray("JPEG", "PNG");
Dialog.addRadioButtonGroup("Output format", items, 1, 2, "JPEG");
Dialog.show;
output = Dialog.getRadioButton();


//--------Set Parameter--------------

//Segmentation setting
xSeg = 20;
ySeg = 20;

//BlackZone threshold(L_mean)
TH_kuroBeta = 50;

//WhiteLine threshold(s_mean)
TH_whiteLine = 20;

//HighIntensity threshold
TH_HighIntensity = 20;

//HighCount threshold
TH_countRatio = 90;

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


//To prevent a memory shortage error
var x,y,sum,xStep,yStep,depth,segCount,xi,yi,pixelValue,detectedCount,rejectCounter,correctCount;
var saveFlag,width,height,L_mean,a,b,s_mean,count,ii,jj,pixel_intensity,iii,beta,correctedValue;
var xSeg,ySeg,TH_kuroBeta,TH_whiteLine,TH_HighIntensity,TH_countRatio,seed;


//operation

startTime = whatTimeNow();
x = 0;
y = 0;

detectedCount = 0;
rejectCounter = 0;

for (i=0; i<list.length;i++){
	
	open(openDir+list[i]);
	run("Rotate 90 Degrees Right");

	//progress
	print(i+1,"/",list.length,"...Progress=",floor((i+1)/list.length*10000)/100,"%");
	
	memory = IJ.currentMemory();
	memory = parseInt(memory)/1000000;
	print("Current Memmory=",d2s(memory,2),"MB");

	saveFlag = 0;
	correctCount = 0;
	sum = 0;

	width = getWidth();
	height = getHeight();
	
	//value of movement
	xStep = floor(xSeg/2);
	yStep = floor(ySeg/2);

	depth = bitDepth();
	if (depth == 8) {
		
		//Segmentation section
		segCount = 1;
		for(y=0; y<height-ySeg; y=y+yStep){
			for(x=0; x<width-xSeg; x=x+xStep){
				for(xi=0; xi<xSeg; xi++){
					for(yi=0; yi<ySeg; yi++){
						pixelValue = getPixel(x+xi,y+yi);
						sum = sum+pixelValue;
					}
				}
				L_mean = sum/xSeg/ySeg;
				sum = 0;
				
				//print(i+1,"/",list.length,"...Progress=",floor((i+1)/list.length*10000)/100,"%",", ","Segment No=",segCount);
				segCount++;

				//recognize kuroBeta
				if(L_mean < TH_kuroBeta){
					a = x+5; //exclude both ends of image (5+5 pixels)
					b = y;

					//small rectangle ROI scan
					for(ii=0; ii<xSeg-10; ii++){
						b = y; //reset b (++ by jj_loop)
						for(yi=0; yi<ySeg; yi++){
							pixelValue = getPixel(a,b+yi);
							sum = sum+pixelValue;
						}
						s_mean = sum/ySeg;
						sum = 0;

						//horizontal direction scan on detected whiteLine
						if(s_mean > TH_whiteLine){
							count = 0;
							for(jj=0; jj<ySeg; jj++){
								pixel_intensity = getPixel(a,b);
								
								//count HighIntensity
								if(pixel_intensity > TH_HighIntensity){
									count++;
									b++;
								}
							}
							//Noise Correction
							if(count > ySeg*TH_countRatio/100){
								correctCount++;
								//print("(",a,",",b,")","Noise Correction!!",correctCount);
								saveFlag = 1;
								b = y;		
								for(iii=0; iii<ySeg; iii++){
									seed = random(); //between 0 and 1
									beta = L_mean-seed*10;
									if(beta < 0) beta = 0;
									correctedValue = floor(beta);
									setPixel(a,b,correctedValue);
									b = b+1;
								}
							}
						}
						a = a+1; //in ii_loop
					}
				}
			}
		}
	}else{
		rejectCounter++;
		print("...Color Image!");
	}	
	if(saveFlag == 1){
		run("Rotate 90 Degrees Left");
		name = getTitle();
		dotIndex = lastIndexOf(name,".");
		title = substring(name,0,dotIndex);

		if (output == "JPEG") newname = title+".jpg";
		else if (output == "PNG") newname = title+".png";

		rename(newname);
		
		if (output == "JPEG") saveAs("Jpeg", saveDir+newname);
		else if (output == "PNG") saveAs("PNG", saveDir+newname);

		detectedCount++;
	}
	close();
}

//fin
finishTime = whatTimeNow();
print("DetectedCounts=",detectedCount);
print("Start Time .... ",startTime);
print("FinishTime ... ",finishTime);
print("oshimai");


//-----------------------------------------------------------------------------
//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;
}

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

 

コード内容紹介ページ

 

3. 局所版タテ線ノイズ除去(選択範囲内のみ)

縦線のある画像に対して、ROI(四角の枠線 : ImageJのRectangle Tool)を用いて範囲を選択し、範囲内に限定した自動検出+除去を行います。保存は手動で行ってください。

//Local_Vertical_Noise_Correction_HiSp.txt


//Segmentation setting
xSeg = 20;
ySeg = 20;

//BlackZone threshold(L_mean)
TH_kuroBeta = 50;

//WhiteLine threshold(s_mean)
TH_whiteLine = 15;

//HighIntensity threshold
TH_HighIntensity = 15;

//HighCount threshold
TH_countRatio = 80;


//operation
var correctCount = 0;
getSelectionBounds(x0,y0,width,height);
VNC();

function VNC(){
	x = x0;
	y = y0;

	sum = 0;

	//value of movement
	xStep = floor(xSeg/2);
	yStep = floor(ySeg/2);

	depth = bitDepth();
	if (depth == 8) {
		
		//Segmentation section
		segCount = 1;
		for(y=y0; y<y0+height-ySeg; y=y+yStep){
			for(x=x0; x<x0+width-xSeg; x=x+xStep){
				for(xi=0; xi<xSeg; xi++){
					for(yi=0; yi<ySeg; yi++){
						pixelValue = getPixel(x+xi,y+yi);
						sum = sum+pixelValue;
					}
				}
				L_mean = sum/xSeg/ySeg;
				sum = 0;
				
				print("Segment No=",segCount);
				segCount++;

				//recognize kuroBeta
				if(L_mean < TH_kuroBeta){
					a = x+5;
					b = y;

					//small rectangle ROI scan
					for(ii=0; ii<xSeg-10; ii++){
						b = y;
						for(yi=0; yi<ySeg; yi++){
							pixelValue = getPixel(a,b+yi);
							sum = sum+pixelValue;
						}
						s_mean = sum/ySeg;
						sum = 0;

						//vertical direction scan on detected whiteLine
						if(s_mean > TH_whiteLine){
							count = 0;
							for(jj=0; jj<ySeg; jj++){
								pixel_intensity = getPixel(a,b);
								
								//count HighIntensity
								if(pixel_intensity > TH_HighIntensity){
									count++;
									b++;
								}
							}
							//Noise Correction
							if(count > ySeg*TH_countRatio/100){
								correctCount++;
								print("(",a,",",b,")","Noise Correction!!",correctCount);
								b = y;		
								for(iii=0; iii<ySeg; iii++){
									seed = random();
									beta = L_mean-seed*10;
									if(beta < 0) beta = 0;
									correctedValue = floor(beta);
									setPixel(a,b,correctedValue);
									b = b+1;
								}
							}
						}
						a = a+1;
					}
				}
			}
		}
	}else{
		print("...Color Image!");
	}	
}
print("correctCount=",correctCount);
print("oshimai");

 

コード内容紹介ページ


4. 局所版ヨコ線ノイズ除去(選択範囲内のみ)

横線のある画像に対して、ROI(四角の枠線 : ImageJのRectangle Tool)を用いて範囲を選択し、範囲内に限定した自動検出+除去を行います。保存は手動で行ってください。

//Local_Horizontal_Noise_Correction_HiSp.txt


//Segmentation setting
xSeg = 20;
ySeg = 20;

//BlackZone threshold(L_mean)
TH_kuroBeta = 50;

//WhiteLine threshold(s_mean)
TH_whiteLine = 15;

//HighIntensity threshold
TH_HighIntensity = 15;

//HighCount threshold
TH_countRatio = 80;

//correctedValue setting
correctedValue = 0;


//operation
var correctCount = 0;
getSelectionBounds(x0,y0,width,height);
HNC();

function HNC(){
	x = x0;
	y = y0;

	sum = 0;

	//value of movement
	xStep = floor(xSeg/2);
	yStep = floor(ySeg/2);

	depth = bitDepth();
	if (depth == 8) {
		
		//Segmentation section
		segCount = 1;
		for(y=y0; y<y0+height-ySeg; y=y+yStep){
			for(x=x0; x<x0+width-xSeg; x=x+xStep){
				for(xi=0; xi<xSeg; xi++){
					for(yi=0; yi<ySeg; yi++){
						pixelValue = getPixel(x+xi,y+yi);
						sum = sum+pixelValue;
					}
				}
				L_mean = sum/xSeg/ySeg;
				sum = 0;
				
				print("Segment No=",segCount);
				segCount++;

				//recognize kuroBeta
				if(L_mean < TH_kuroBeta){
					a = x;
					b = y+5;

					//small rectangle ROI scan
					for(ii=0; ii<ySeg-10; ii++){
						a = x;
						for(xi=0; xi<xSeg; xi++){
							pixelValue = getPixel(a+xi,b);
							sum = sum+pixelValue;
						}
						s_mean = sum/xSeg;
						sum = 0;

						//horizontal direction scan on detected whiteLine
						if(s_mean > TH_whiteLine){
							count = 0;
							for(jj=0; jj<xSeg; jj++){
								pixel_intensity = getPixel(a,b);
								
								//count HighIntensity
								if(pixel_intensity > TH_HighIntensity){
									count++;
									a++;
								}
							}
							//Noise Correction
							if(count > ySeg*TH_countRatio/100){
								correctCount++;
								print("(",a,",",b,")","Noise Correction!!",correctCount);
								a = x;		
								for(iii=0; iii<xSeg; iii++){
									seed = random();
									beta = L_mean-seed*10;
									if(beta < 0) beta = 0;
									correctedValue = floor(beta);
									setPixel(a,b,correctedValue);
									a = a+1;
								}
							}
						}
						b = b+1;
					}
				}
			}
		}
	}else{
		print("...Color Image!");
	}	
}
print("correctCount=",correctCount);
print("oshimai");

 

コード内容紹介ページ

 

5. ほどよいパラメータ見つけるマクロ(タテ線ノイズ用)

初出しです。ほどよいパラメータを見つけるために、パラメータを変えながら何度も処理を試したい時に使ってください。ここで見つけたパラメータを上記の「フォルダ内全画像自動処理版」や「局所版」に打ち込みましょう。

//Local_VNC_AdjustParameters.txt


//Segmentation setting
xSeg = 20;
ySeg = 20;

//BlackZone threshold(L_mean)
TH_kuroBeta = 50;

//WhiteLine threshold(s_mean)
TH_whiteLine = 15;

//HighIntensity threshold
TH_HighIntensity = 15;

//HighCount threshold
TH_countRatio = 80;

//To prevent a memory shortage error
var x,y,sum,xStep,yStep,depth,segCount,xi,yi,pixelValue,correctCount;
var L_mean,a,b,s_mean,count,ii,jj,pixel_intensity,iii,beta,correctedValue;
var xSeg,ySeg,TH_kuroBeta,TH_whiteLine,TH_HighIntensity,TH_countRatio,seed,endFlag;


//operation
endFlag = 1;
correctCount = 0;
getSelectionBounds(x0,y0,width,height);

if(endFlag == 1)VNCtest();

function VNCtest(){

	Dialog.create("Parameters")
	Dialog.addNumber("xSeg",xSeg);
	Dialog.addNumber("ySeg",ySeg);
	Dialog.addNumber("TH_kuroBeta",TH_kuroBeta);
	Dialog.addNumber("TH_whiteLine",TH_whiteLine);
	Dialog.addNumber("TH_HighIntensity",TH_HighIntensity);
	Dialog.addNumber("TH_countRatio",TH_countRatio);
	Dialog.show;
	xSeg = Dialog.getNumber();
	ySeg = Dialog.getNumber();
	TH_kuroBeta = Dialog.getNumber();
	TH_whiteLine = Dialog.getNumber();
	TH_HighIntensity = Dialog.getNumber();
	TH_countRatio = Dialog.getNumber();

	x = x0;
	y = y0;

	sum = 0;

	//value of movement
	xStep = floor(xSeg/2);
	yStep = floor(ySeg/2);

	depth = bitDepth();
	if (depth == 8) {
		
		//Segmentation section
		segCount = 1;
		for(y=y0; y<y0+height-ySeg; y=y+yStep){
			for(x=x0; x<x0+width-xSeg; x=x+xStep){
				for(xi=0; xi<xSeg; xi++){
					for(yi=0; yi<ySeg; yi++){
						pixelValue = getPixel(x+xi,y+yi);
						sum = sum+pixelValue;
					}
				}
				L_mean = sum/xSeg/ySeg;
				sum = 0;
				
				print("Segment No=",segCount);
				segCount++;

				//recognize kuroBeta
				if(L_mean < TH_kuroBeta){
					a = x+5;
					b = y;

					//small rectangle ROI scan
					for(ii=0; ii<xSeg-10; ii++){
						b = y;
						for(yi=0; yi<ySeg; yi++){
							pixelValue = getPixel(a,b+yi);
							sum = sum+pixelValue;
						}
						s_mean = sum/ySeg;
						sum = 0;

						//vertical direction scan on detected whiteLine
						if(s_mean > TH_whiteLine){
							count = 0;
							for(jj=0; jj<ySeg; jj++){
								pixel_intensity = getPixel(a,b);
								
								//count HighIntensity
								if(pixel_intensity > TH_HighIntensity){
									count++;
									b++;
								}
							}
							//Noise Correction
							if(count>ySeg*TH_countRatio/100){
								correctCount++;
								print("(",a,",",b,")","Noise Correction!!",correctCount);
								b = y;		
								for(iii=0; iii<ySeg; iii++){
									seed = random();
									beta = L_mean-seed*10;
									if(beta < 0) beta = 0;
									correctedValue = floor(beta);
									setPixel(a,b,correctedValue);
									b = b+1;
								}
							}
						}
						a = a+1;
					}
				}
			}
		}

	}else{
		print("...Color Image!");
	}

	print("correctCount=",correctCount);
	waitForUser("Click image and check effect of noise correction");
	endFlag = getBoolean(" Again?\n   [Yes]...Change parameters and retry.\n    [No]...Confirm results.");
	if(endFlag == 1){
		run("Revert");
		VNCtest();
	}
	else if(endFlag == 0){
		print("------Parameters------");
		print("xSeg =",xSeg);
		print("ySeg =",ySeg);
		print("TH_kuroBeta =",TH_kuroBeta);
		print("TH_whiteLine =",TH_whiteLine);
		print("TH_HighIntensity =",TH_HighIntensity);
		print("TH_countRatio =",TH_countRatio);
		print("oshimai");
	}
}

 

6. ほどよいパラメータ見つけるマクロ(ヨコ線ノイズ用)

上記5と同じく初出しです。ほどよいパラメータを見つけるために、パラメータを変えながら何度も処理を試したい時に使ってください。ここで見つけたパラメータを上記の「フォルダ内全画像自動処理版」や「局所版」に打ち込みましょう。

//Local_HNC_AdjustParameters.txt


//Segmentation setting
xSeg = 20;
ySeg = 20;

//BlackZone threshold(L_mean)
TH_kuroBeta = 50;

//WhiteLine threshold(s_mean)
TH_whiteLine = 15;

//HighIntensity threshold
TH_HighIntensity = 15;

//HighCount threshold
TH_countRatio = 80;

//To prevent a memory shortage error
var x,y,sum,xStep,yStep,depth,segCount,xi,yi,pixelValue,correctCount;
var L_mean,a,b,s_mean,count,ii,jj,pixel_intensity,iii,beta,correctedValue;
var xSeg,ySeg,TH_kuroBeta,TH_whiteLine,TH_HighIntensity,TH_countRatio,seed,endFlag;


//operation
endFlag = 1;
correctCount=0;
getSelectionBounds(x0,y0,width,height);

if(endFlag == 1)HNCtest();

function HNCtest(){

	Dialog.create("Parameters")
	Dialog.addNumber("xSeg",xSeg);
	Dialog.addNumber("ySeg",ySeg);
	Dialog.addNumber("TH_kuroBeta",TH_kuroBeta);
	Dialog.addNumber("TH_whiteLine",TH_whiteLine);
	Dialog.addNumber("TH_HighIntensity",TH_HighIntensity);
	Dialog.addNumber("TH_countRatio",TH_countRatio);
	Dialog.show;
	xSeg = Dialog.getNumber();
	ySeg = Dialog.getNumber();
	TH_kuroBeta = Dialog.getNumber();
	TH_whiteLine = Dialog.getNumber();
	TH_HighIntensity = Dialog.getNumber();
	TH_countRatio = Dialog.getNumber();

	x = x0;
	y = y0;

	sum = 0;

	//value of movement
	xStep = floor(xSeg/2);
	yStep = floor(ySeg/2);

	
	depth = bitDepth();
	if (depth == 8) {
		
		//Segmentation section
		segCount = 1;
		for(y=y0; y<y0+height-ySeg; y=y+yStep){
			for(x=x0; x<x0+width-xSeg; x=x+xStep){
				for(xi=0; xi<xSeg; xi++){
					for(yi=0; yi<ySeg; yi++){
						pixelValue = getPixel(x+xi,y+yi);
						sum = sum+pixelValue;
					}
				}
				L_mean = sum/xSeg/ySeg;
				sum = 0;
				
				print("Segment No=",segCount);
				segCount++;

				//recognize kuroBeta
				if(L_mean < TH_kuroBeta){
					a = x;
					b = y+5;

					//small rectangle ROI scan
					for(ii=0; ii<ySeg-10; ii++){
						a = x;
						for(xi=0; xi<xSeg; xi++){
							pixelValue = getPixel(a+xi,b);
							sum = sum+pixelValue;
						}
						s_mean = sum/xSeg;
						sum = 0;

						//horizontal direction scan on detected whiteLine
						if(s_mean > TH_whiteLine){
							count = 0;
							for(jj=0; jj<xSeg; jj++){
								pixel_intensity = getPixel(a,b);
								
								//count HighIntensity
								if(pixel_intensity > TH_HighIntensity){
									count++;
									a++;
								}
							}
							//Noise Correction
							if(count > ySeg*TH_countRatio/100){
								correctCount++;
								print("(",a,",",b,")","Noise Correction!!",correctCount);
								a = x;		
								for(iii=0; iii<xSeg; iii++){
									seed = random();
									beta = L_mean-seed*10;
									if(beta < 0) beta = 0;
									correctedValue = floor(beta);
									setPixel(a,b,correctedValue);
									a = a+1;
								}
							}
						}
						b = b+1;
					}
				}
			}
		}
	}else{
		print("...Color Image!");
	}

	print("correctCount=",correctCount);
	waitForUser("Click image and check effect of noise correction");
	endFlag = getBoolean(" Again?\n   [Yes]...Change parameters and retry.\n    [No]...Confirm results.");
	if(endFlag == 1){
		run("Revert");
		HNCtest();
	}
	else if(endFlag == 0){
		print("------Parameters------");
		print("xSeg =",xSeg);
		print("ySeg =",ySeg);
		print("TH_kuroBeta =",TH_kuroBeta);
		print("TH_whiteLine =",TH_whiteLine);
		print("TH_HighIntensity =",TH_HighIntensity);
		print("TH_countRatio =",TH_countRatio);
		print("oshimai");
	}
}

 

ちなみにパラメータの推奨値ですが、私の場合EPSON製DS530で、明るさ0、コントラスト15、ガンマ2.2、400dpi)では、

xSeg = 20; ySeg = 20;

TH_kuroBeta = 50;

TH_whiteLine = 15;

TH_HighIntensity = 15;

TH_countRatio = 80; 

です。ここからちまちまと確認・微調整して使っています。参考までに。 

 

7. おまけ : 画像保存に便利なマクロ

最後に、画像保存用マクロです。局所版で補正した画像を、ショートカットに登録した下記マクロで保存すると捗ります。

 

JPEG保存用

//Save_As_JPEG_to_DeskTop.txt

saveDir = "/Users/Yu3xx/Desktop/";

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

	name = getTitle;
	dotIndex = lastIndexOf(name, ".");
	title = substring(name, 0, dotIndex);

	newname = title+".jpg";
	rename(newname);
	saveAs("Jpeg", saveDir+newname);
	
	close(newname);

print("Save OK by JPEG...",newname);

 

PNG保存用

//Save_As_PNG_to_DeskTop.txt

saveDir = "/Users/Yu3xx/Desktop/";

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

	name = getTitle;
	dotIndex = lastIndexOf(name, ".");
	title = substring(name, 0, dotIndex);

	newname = title+".png";
	rename(newname);
	saveAs("PNG", saveDir+newname);
	
	close(newname);

print("Save OK by PNG...",newname);

※1 保存先パスは使用しているPCに合わせて書き換えてください。

Windowsの場合、C:¥¥Users¥¥yu3xx¥¥Desktop¥¥(円は半角!)になります。

 

※2 (ショートカット登録は、マクロをインストール(再起動)したあと、上部タブ[Plugins]→[Shortcuts]→[Add shortcut]。私の場合は0にJPEG、9にPNGを入れています。)

 

 

マクロの起動方法

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

 

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

 

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

 

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

 

④上部タブ[Plugins]→[自炊]→[Vertical Noise Correctionなどなど]でマクロが実行されます。

 

 

imagej-jisui.hatenablog.com

 

 

 

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

 

imagej-jisui.hatenablog.com