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

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

【局所版】選択した範囲内の「縦線ノイズ / 横線ノイズ」を自動補正・除去するためのMacro

 

f:id:yu3xx:20200624015950j:plain

 

縦線ノイズもしくは横線ノイズのある部位をROIで囲み、その範囲内のみを自動解析してノイズを除去するためのマクロです。

  

縦線対策を考慮した自炊ルーチン

① 通常の縦線抑制法(スキャン読取面の清掃)をしつつ『裏向きスキャン統合法』でスキャン。

② 読みたくなったタイミングで、縦線やスキャンミス等も視野に入れつつ普通に読む。

③ 縦線がもし入っていればページをメモしておく。

④ あとからまとめて縦線補正(「黒ベタコピペの手作業」 or 「選択範囲内縦線ノイズ除去マクロ」

 

こんな感じでやっています。

「黒ベタコピペ法」を使うか、選択範囲内縦線ノイズ除去マクロ」を使うかの判断は気まぐれです。おおざっぱな部分はマクロで、細かな部分はコピペ法って気持ちもあります。

 

◆ちなみに...

「フォルダ内全自動式縦線ノイズ除去マクロ」も公開していますが、「ノイズではない正常な縦線」もおかまいなしで消してしまうことが弱点です。

この選択範囲内縦線ノイズ除去マクロはこの問題をクリアすることをコンセプトとして作成しました。

「ノイズではない正常な縦線」を極力範囲内に含まないようにしてROIを設定すると、仕上がりは綺麗だし無駄な解析時間も省ける、ということが強みです。

 

 

タテ線ノイズ補正のコード

//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=1;ii<xSeg-9;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=1;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=1;iii<=ySeg;iii++){
									beta=random();
									beta=L_mean-beta*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");

 

ヨコ線ノイズ補正のコード

//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=1;ii<ySeg-9;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=1;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=1;iii<=xSeg;iii++){
									beta=random();
									beta=L_mean-beta*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");

 

 

補正の例

 

f:id:yu3xx:20200528000245j:plain

 画像引用:高田裕三, 3×3EYES, 単行本第40巻, 197ページ, 講談社, 2002

 

使い方

① 縦線の入っている画像をImageJで開く。

② ノイズ部分を長方形ROIで囲む(少し余裕を持って)。

② マクロ「Local_Vertical (Horizontal)_Noise_Correction_HiSp.txt」を起動させる。

(ショートカットに登録しておくとラク。マクロをインストール(再起動)したあと、上部タブ[Plugins]→[Shortcuts]→[Add shortcut]。私の場合はF1にVertical、F2にHorizontalを入れています。)

③ 短時間の解析後、縦線が補正される。

④ 良ければ上書き保存や[File]→[Save As]等で保存する。

 

 

マクロの起動方法

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

 

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

 

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

 

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

 

④上部タブ[Plugins]→[自炊]→[Local Vertcal(Horizontal) Noise Correction HiSp]でマクロが実行されます。

(ショートカットから起動がオススメです!)

 

 

imagej-jisui.hatenablog.com

 

 

 

 

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

 

 

パラメータ変更はこちらの縦線ノイズ除去マクロ(高速版)を参考に。

 

裏向きスキャン統合法もオススメです。

imagej-jisui.hatenablog.com

  

作成したマクロのまとめです。

imagej-jisui.hatenablog.com