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

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

【高速版】縦線ノイズを自動検出・補正するMacro

 

f:id:yu3xx:20200624015303j:plain

 

指定したフォルダ内の、全ての画像の縦線ノイズを検出し、補正するためのマクロの『高速改良版』です。

 

 

 

//Vertical_Noise_Correction_HiSp.txt

//Folder setting
showMessage("Select Open Folder");
openDir = getDirectory("Choose a Directory");
showMessage("Select Save Folder");
saveDir = getDirectory("Choose a Directory");
list = getFileList(openDir);

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

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

//BlackZone threshold(L_mean)
TH_kuroBeta=40;

//WhiteLine threshold(s_mean)
TH_whiteLine=15;

//HighIntensity threshold
TH_HighIntensity=15;

//HighCount threshold
TH_countRatio=80;

//correctedValue setting
correctedValue=0;

//operation

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,"%");
	
	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;
					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);
								saveFlag=1;
								b=y;		
								for(iii=1;iii<=ySeg;iii++){
									setPixel(a,b,correctedValue);
									b=b+1;
								}
							}
						}
						a=a+1;
					}
				}
			}
		}
	}else{
		rejectCounter++;
		print("...Color Image!");
	}	
	if(saveFlag==1){
		name=getTitle();
		dotIndex=lastIndexOf(name,".");
		title=substring(name,0,dotIndex);
		newname = title+".jpg";
		rename(newname);
		saveAs("Jpeg", saveDir+newname);

		detectedCount++;
	}
close();
}

print("DetectedCounts=",detectedCount);
print("oshimai");

 

従来のコードで、ものすご〜く時間のかかっていたROI設定・移動の部分を、ROIを実際には置かずに全て計算で行うよう変更したので、所要時間は体感で1/10以下になっています。

 

 

アルゴリズムの概要

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

②画像を順番に開き、小さなセグメントを移動させ、平均値を調べることで黒ベタ塗りの部分を探す。

③黒ベタがみつかれば、セグメントの中を縦細長のROI走査で平均値チェックして、線状の高信号(白線)を探す。

④白線を見つけたら、その白線の信号値を上から1ピクセルずつ調べる。

⑤その白線上で、閾値以上の高信号ピクセルが占める割合が80%(デフォルト値)以上だったら、縦線ノイズと認識。

⑥縦線ノイズに該当する縦細長ROI部分をcorrectedValue(デフォルト値=0)で埋める。

⑦すべてのセグメント・画像でこれを繰り返す。

⑧検出・修正したページのみ、Save Folderに保存。

 

 

問題点

① 黒ベタじゃない部分の縦線と、縦線のはしっこ等の補正漏れ

② ノイズではない縦の白線部分も勝手に消しちゃう

③ コード内のパラメータ(セグメントの大きさ、閾値)あたりのバランスが微妙すぎて、ベストがよくわからん

 

 

 

 パラメータについて補足

このプログラムは300dpiで「コントラスト・濃度を調整するMacro」を使用して黒ベタ部を0近辺まで下げたデータに対する処理を前提とした設定になっています。

 

もしもこれに沿わない場合は、縦線検出感度が強すぎたり弱すぎたりする可能性があるので、以下のパラメータを調整する必要があります。

 

・xSeg, ySeg...黒ベタ認識の区域分けの大きさ。大きすぎると長さの短い縦線ノイズを見逃すが、小さすぎると正常な縦線をノイズと誤認しやすくなる。dpiによっても変えるべき。奇数だと補正が飛び飛びになるので、偶数に。

 

・TH_kuroBeta...この値より平均信号が低い (黒い) セグメントを黒ベタと認識する。高くすると認識感度は高くなり、低くすると感度は落ちる。補正したいデータの黒ベタ信号値によっても変えるべき。

 

・TH_whiteLine...黒ベタ認識後の長方形ROI走査で次のステップ(長方形ROI内の全信号値check)に進むための閾値。高くすると淡い縦線を検出出来ない。低くすると淡い縦線を検出しやすいが誤認増える。

 

・TH_HighIntensity...長方形ROI内全信号値checkの際に、「高信号である」と捉えるための閾値。TH_whiteLineと同じで良い気がする。

 

・TH_countRatio...長方形ROIの中に「高信号」が何%以上あるとノイズと認識するか。

 

・correctedValue...検出されたノイズ部をどんな値で埋めるか。黒ベタ部の信号で変えるべき。あらかじめコントラスト・濃度調整で黒ベタを0近辺まで落とし、ほぼ黒潰れさせた状態にしておいた上でcorrectedValueを0にするのが一番仕上がりが綺麗。

 

 

このパラメータ設定がキモなのですが、少しずつ値を変えながら自分なりのベストを見つけるのが大事です。(妥協はもっと大事)

 

 

応用編: 誤検出による誤修正が気になる時には...

このプログラムは「多少の誤検出は恐れず、ただし縦線ノイズはなるべく多く修正する」ことを暫定目標として作っています。

 

ですのでもしも、誤検出により「ノイズではない正常な白い縦線を塗りつぶしてしまう」ことが気になる場合には、このMacroをいじって「検出のみ」に機能を削り、「補正」は手作業で、という使い方がオススメです。

 

その場合には、感度は少し落とした方が良いので、xSeg, ySegは少し大きめにし、ノイズを1つでも検出したタイミングで、setPixel(a,b,correctedValue)の命令を削り、代わりにループを抜けるような指示を出し次の画像へ進む、というように書き換えるのが良いのかなと思います。

 

ImageJ上での縦線ノイズマニュアル修正は、「ノイズ近傍のデータをコピペして覆う」か、[back spaceキー] or [deleteキー]で「選択範囲を黒く塗りつぶす」だけなので、地道ですがわりと簡単に作業出来ます。

 

 

マクロの起動方法

①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

 

 

 

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

 

 

初代縦線ノイズ除去マクロも参考にどうぞ。

 

2020.03.08追記

横向きスキャン対応版も公開しました!

 

 2020/03/08 追記その2

そもそも縦線の発生しづらいデータを作るためのコツです。実は縦線対策にはこのマクロが一番オススメです!

 

 

 

 2020/06/24 追記

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

imagej-jisui.hatenablog.com