OpenCV 4学習ノート(16)——画像シャープ化(USMシャープ化アルゴリズム)


今回整理した内容は画像のシャープ化についてです.画像シャープ化は,本質的に画像ハイパスフィルタリング後の結果と原図を画素重畳した出力である.画像のハイパスフィルタリングとは、1枚の画像がフィルタを通過した後、その細部、エッジ部分を残し、他の本体内容をフィルタリングすることである.したがって、画像をハイパスフィルタリングした出力結果が画像の細部部分であり、これらの細部部分を一定の重みで原画像に重畳すると、原画像の細部部分がより際立って、視覚的により立体的になる.次に、画像のシャープ化のコード実装を示します.
	Mat sharpen_image, blur_image, sub_image;

	//GaussianBlur(image, blur_image, Size(), 5, 5);
	fastNlMeansDenoisingColored(image, blur_image, 10, 10, 7, 21);			
	
	subtract(image, blur_image, sub_image, Mat(), CV_16S);
	
	add(sub_image, image, sharpen_image, Mat(), CV_16S);
	convertScaleAbs(sharpen_image, sharpen_image);
	imshow("  ", sharpen_image);

まず、入力画像をフィルタリングアルゴリズムによりノイズを除去し、そのエッジと詳細部分をぼかしながら、非局所平均フィルタリングアルゴリズムを用いてノイズ除去を行うことができる.非局所平均フィルタリングアルゴリズムはGaussノイズの抑制効果が比較的よいからである.そして、元の画像からぼかした画像を減算することにより、本来ぼかしたエッジと細部部分が得られ、2つのCV_に注意する8 UCタイプは減算し、負の値が出る可能性があるので、減算後の出力画像はCV_16 S以上のタイプ定義.最後に減算されたエッジと詳細部分を原図に合成し、convertScaleAbs(sharpen_image, sharpen_image)というAPIを介して他のタイプの画像をCV_に変換する.8 UCタイプで、画像を出力します.このようにして画像をシャープ化する効果図を以下に示す.
以上のシャープ化の実現形態は、抽出したディテールエッジ情報を完全に原画像に合成することで、このようにして形成されたシャープ化画像も効果的であるが、ディテールを際立たせすぎたためか、ざらざらして不自然に見える.特に上の効果図では、このような鋭化された画像を見ると硬く、毛が刺さっているように見え、原図と比べると少し気分が悪い.この問題はUSM(UnSharpen Mask)シャープ化アルゴリズムを用いて解決できる.USMシャープ化アルゴリズムの構想は,まず原画像をGaussブラーしてノイズ低減,ディテールエッジ消去の効果を達成し,処理後の画像を得てから,Gaussブラー後の画像と原画像を重みで加算することである.出力画像=(1+weight)x原画像+(-weight)xガウスボケ画像という処理後、重ね合わせた高周波情報の出力画像における割合を重みでバランスさせることで、画像をシャープ化した後の表示遷移を比較的スムーズかつ滑らかにすることができ、高周波情報が画像の表示効果に影響を及ぼすことを避けることができると考えられる.以下はUSMアルゴリズムがコード実装である.
	//USM(UnSharpen Mask)    
	Mat gaussian_image, USM_image;
	GaussianBlur(image, gaussian_image, Size(), 10, 10);
	addWeighted(image, 1.5, gaussian_image, -0.5, 0, USM_image, CV_16S);
	convertScaleAbs(USM_image, USM_image);
	imshow("USM_image", USM_image);

ここではaddWeighted(image, 1.5, gaussian_image, -0.5, 0, USM_image, CV_16S)というAPIが用いられ、入力された2枚の画像をそれぞれの重みで加算し、CV_16 Sのような[0255]よりも広い範囲のタイプで返すことに注意する.以下はUSMアルゴリズムの効果図です:私の主観的な感じから見ると、USMアルゴリズムは普通の鋭化アルゴリズムより明らかに優れており、細部、エッジ、テクスチャ、画像内容の間の移行などの面で比較的良い表現があります.しかし,通常のシャープ化アルゴリズムでもUSMアルゴリズムでも,輪郭エッジと物体内部のテクスチャの違いを考慮していないという問題がある.上記の2つの画像シャープ化アルゴリズムでは、画像中のすべての高周波情報が同様に強化されており、効果図の猫と他の物体を例にとると、猫が主体と他の物体との間のエッジとして強化される必要があり、画像がより立体化し、視覚効果が向上するように見えるしかし、他の物体の表面のテクスチャは、同等の強度で強化する必要はありません.そうしないと、画像本体の表示効果が弱まり、他のテクスチャにも満たされます.したがって、画像シャープ化処理では、本体エッジを比較的大きく補強することが好ましく、他のテクスチャを比較的小さく補強することで、より良い視覚効果を達成することができる.動的USMシャープ化アルゴリズムはこのような機能を実現し、以下に私がカスタマイズしたUSM動的アルゴリズムを提供します.
/************************************  USM    ***********************************/
	Mat gaussian_image, USM_image;
	//cvtColor(image, USM_image, COLOR_BGR2GRAY);
	//cvtColor(image, image, COLOR_BGR2GRAY);
	GaussianBlur(image, gaussian_image, Size(), 20, 20);
	int height = image.rows;
	int width = image.cols;
	int channels = image.channels();
	//        , [0,255]      
	int threshold_L = 60;
	int threshold_R = 120;
	//              
	float weight_L = 0.4;	
	float weight_M = 0.6;
	float weight_R = 0.8;	
	//       
	USM_image = image.clone();
	if (channels != 1)			//      
	{
     
		//    、      、USM           
		vector<Mat> image_bgr, gaus_image_bgr, USM_bgr;
		split(image, image_bgr);
		split(gaussian_image, gaus_image_bgr);
		split(USM_image, USM_bgr);
		//              
		for (int ch = 0; ch < channels; ch++)
		{
     
			for (int row = 0; row < height; row++)
			{
     
				for (int col = 0; col < width; col++)
				{
     
					//                     
					uchar image_value = image_bgr[ch].at<uchar>(row, col);
					uchar gaus_image_value = gaus_image_bgr[ch].at<uchar>(row, col);
					int dif_value = image_value - gaus_image_value;
					if (dif_value < threshold_L)			//         [0, 60]
					{
     
						//                        
						uchar new_value = saturate_cast<uchar>(((1 + weight_L) * image_value - weight_L * gaus_image_value));
						//                     
						USM_bgr[ch].at<uchar>(row, col) = new_value;
					}
					else if(dif_value >= threshold_L && dif_value <= threshold_R)			//         [60, 120]
					{
     
						uchar new_value = saturate_cast<uchar>(((1+weight_M) * image_value - weight_M * gaus_image_value));
						USM_bgr[ch].at<uchar>(row, col) = new_value;
					}
					else			//         [60, 255]
					{
     
						char new_value = saturate_cast<uchar>(((1 + weight_R) * image_value - weight_R * gaus_image_value));
						USM_bgr[ch].at<uchar>(row, col) = new_value;
					}
				}
			}
		}
		merge(USM_bgr, USM_image);					//                
	}
	else			//     
	{
     
		for (int row = 0; row < height; row++)
		{
     
			for (int col = 0; col < width; col++)
			{
     
				uchar image_value = image.at<uchar>(row, col);
				uchar gaus_image_value = gaussian_image.at<uchar>(row, col);
				int dif_value = image_value - gaus_image_value;
				if (dif_value < threshold_L)
				{
     
					uchar new_value = saturate_cast<uchar>(((1 + weight_L) * image_value - weight_L * gaus_image_value));
					USM_image.at<uchar>(row, col) = new_value;
				}
				else if (dif_value >= threshold_L && dif_value <= threshold_R)
				{
     
					uchar new_value = saturate_cast<uchar>(((1 + weight_M) * image_value - weight_M * gaus_image_value));
					USM_image.at<uchar>(row, col) = new_value;
				}
				else
				{
     
					uchar new_value = saturate_cast<uchar>(((1 + weight_R) * image_value - weight_R * gaus_image_value));
					USM_image.at<uchar>(row, col) = new_value;
				}
			}
		}
	}
	imshow("USM_image", USM_image);
	//imwrite("D:\\opencv_c++\\opencv_tutorial\\data\\images\\USM_flower2.jpg", USM_image);

上記アルゴリズムでは、入力画像を遍歴し、原画像とガウスボケ後の画像を比較し、画素値の差が閾値より大きい場合にシャープ化処理を行う.また、差異値を3つの区間に分けて処理し、差異の大きい区間には大きな重みを使用し、そのエッジの詳細をより際立たせ、差異の小さい区間には小さな重みを使用し、過度な増強を避ける.3チャネル画像が入力されている場合は、チャネル分離後に異なるチャネルをシャープ化し、最後に3チャネルをマージします.以下はダイナミックUSMアルゴリズムの効果図です.上のアルゴリズムを処理して画像のシャープ化を実現するほか、カスタムボリュームで画像のシャープ化を実現することもできます.
	//               ,           
	Mat sharpen = (Mat_<int>(3, 3) << 0, -1, 0, -1, 5, -1, 0, -1, 0);	
	//Mat sharpen = (Mat_(3, 3) << -1, -1, -1, -1, 9, -1, -1, -1, -1);	
	filter2D(image, sharpen_image, CV_32F, sharpen);
	convertScaleAbs(sharpen_image, sharpen_image);
	imshow("  ", sharpen_image);


上に2つの一般的なシャープ化演算子を示し,勾配をボリューム計算することによって画像のシャープ化を実現したが,効果は実際には理想的ではなく,ここでは示さない.ここで、filter2D(image, sharpen_image, CV_32F, sharpen)は、1枚の画像と1つのボリュームコアとを実現するためのAPIであり、ボリュームコアは事前に定義され、出力のタイプは浮動小数点型(例えばCV_32 F)であり、ボリュームが完了した後に出力画像を[0,255]区間に正規化し、OpenCVは画素値が[0,255]区間の画像のみが正常に表示される.
さて、今回は画像のシャープ化に関する内容をここに記録し、前回のブログで実験画像を変えたいという願いにも応えました...また次回に続き~~~
PS:私のコメントは雑で、自分の心得もあれば、ネット上で资料を调べる时に抜粋した知识の内容もあります.だから、同じように、私が先辈に勉强した敬意です.もし先辈が私のノートの内容があなたの知的财产権を侵害していると思ったら、私に连络してください.私は関连する博文の内容を削除します.ありがとうございます.