画像処理--ガウスフィルタ


一部の内容は以下から整理する.
http://blog.csdn.net/jianxiong8814/article/details/1562728
http://www.cnblogs.com/pegasus/archive/2011/05/20/2052031.html
http://blog.sina.com.cn/s/blog_640577ed0100yz8v.html
http://blog.csdn.net/pearl333/article/details/8696307
http://www.cnblogs.com/tornadomeet/tag/opencv/
フィルタリングについて
フィルタリングは、通常、ボリュームまたは相関によって記述されるが、線形フィルタリングは、一般にボリュームによって記述される.彼らはとても似ていますが、違いがあります.次に、関連とボリューム計算プロセスに基づいて、彼らの具体的な違いを理解します.
ボリュームの計算手順:
1)ボリュームコアを自分のコア要素の周りに時計回りに180度回転
2)入力画像の処理対象画素の真上にボリュームコアの中心要素を移動する
3)回転後のボリュームコアでは、入力画像の画素値を重みとして乗算する
4)第3ステップの各結果の和当該入力画素に対応する出力画素
関連する計算手順:
1)関連コアの中心要素を入力画像の処理対象画素の真上に移動させる
2)入力画像の画素値を重みとして相関コアを乗じる
3)上記各ステップで得られた結果を加算して出力する
彼らの主な違いは,ボリュームを計算する際に,ボリュームコアが先に回転することであることがわかる.計算相関過程で相関コアを回転させる必要はありません.
例えば、magic(3)=[8 1 6;3 5 7;4 9 2]は、180度回転すると[2 9 4;7 5 3;6 1 8]となる
 
ガウスフィルタ
ガウスフィルタリングは線形平滑フィルタリングであり,ガウスノイズの除去に適しており,画像処理のノイズ低減過程に広く応用されている.一般的に、Gaussフィルタリングは、画像全体を重み付け平均するプロセスであり、各画素点の値は、それ自体と隣接領域内の他の画素値が重み付け平均された後に得られる.
ガウスフィルタリングの具体的な動作は、画像中の各画素を1つのテンプレート(またはボリューム、マスク)で走査し、テンプレートによって決定された隣接領域内の画素の重み付け平均階調値をテンプレート中心画素点の値に置き換えることである.
3を使用すると×3テンプレートでは、計算式は、g(x,y)={f(x−1,y−1)+f(x−1,y+1)+f(x+1,y−1)+f(x+1,y+1)+[f(x−1,y)+f(x,y−1)+f(x,y−1)+f(x,y+1)]*2+f(x,y)*4}/16である.
ここで、f(x,y)は画像中の(x,y)点の階調値であり、g(x,y)はこの点がGaussフィルタリングされた値である.
 
 
1、ガウス分布:
1 Dガウス分布:
  图像处理--高斯滤波_第1张图片
 
2 Dガウスの分布:
  图像处理--高斯滤波_第2张图片
2、ガウス核
理論的には,Gauss分布はすべての定義ドメインに非負の値を有し,無限大のボリュームコアが必要である.実際には、平均値の周囲の3倍の標準差内の値を取り、外部部で直接取り除くだけでよい.次の図は、標準差1.0の整数値Gaussコアです.
图像处理--高斯滤波_第3张图片
3、応用:
ガウスフィルタリング後の画像の平滑化の程度は標準差に依存する.その出力は領域画素の重み付け平均であり、中心に近いほど画素の重み付けが高くなる.従って、平均フィルタリング(mean filter)に比べてスムーズな効果がより柔らかく、エッジが保持されるのもより良い.
ガウスフィルタが平滑フィルタとして用いられる本質的な原因は、ローパスフィルタ(ある周波数以下の信号成分を通過させ、その周波数以上の信号成分を大幅に抑制する)であるからである.
 
4、特徴:   
       1):一つのガウス関数ともう一つのガウス関数の畳み込みは依然として一つのガウス関数であり、A*B=C   Cの標準差の二乗はAとBの標準差の二乗和、つまり畳み込み後のガウス関数の方が広く、ぼかしの効果がより顕著(直観的にはガウスぼかし演算を連続して行うと画像がますますぼかしてしまう) .
  2次元Gauss関数の畳み込みは2段階に分けて行うことができ、まず画像と1次元Gauss関数を畳み込み、その後、畳み込み結果と方向垂直の同じ1次元Gauss関数を畳み込む.そのため、2次元Gaussフィルタリングの計算量はフィルタテンプレート幅とともに2乗ではなく線形に増加する.
      2):ガウス関数のフーリエ変換は依然としてガウス関数であり、元のガウス関数が広いほど(標準差が大きいほど)、変換後のガウス関数が狭くなり(標準差が小さい)、すなわち広いガウス関数ほどローパス(高抵抗)フィルタリングの効果が顕著になり、処理後の画像の詳細が不明になる(よりぼやけている).           デジタル画像をガウスブラーにするには、ガウス関数の分布に合致するボリュームでデジタル画像をボリューム演算します.      標準差のあるサイズ、ボリュームコアのサイズ、最後のスケール係数のサイズを決定します.   
σ大きいほどガウスフィルタの周波数帯域が広くなり、平滑度が高くなります       標準差1.4のガウス5 x 5のボリュームコア:           2   4   5  4   2      4   9   12  9   4      5   12   15  12   5      4   9   12  9   4      2   4   5  4   2                 最後に比例係数を掛ける   1/115   
 
Opencvでのガウスフィルタリングの使用
 
複合形式関数のプロトタイプは次のとおりです.
void cvSmooth(const CvArr* src, CvArr* dst,intsmoothtype=CV_GAUSSIAN,int param1=3, int param2=0,double param3=0, doubleparam4=0 );
この関数の最初の3つのパラメータは理解しやすく,後の4つのパラメータ以下については解析を行う.
      1) param 1とparam 2を指定すると、コア関数の行列、すなわちフィルタウィンドウの幅と高さを表します.
      2) Param 3:ガウス畳み込みSigma値
      3) ユーザが非対称Gaussコアを採用することを望む場合、param 4が導入され、最後の2つのパラメータはそれぞれ水平コアおよび垂直コア次元数を表す.
      4) param 3が与えられていない場合、前の2つのパラメータparam 1とparam 2がSigmaを計算する.ここでの根拠はガウス分布の特徴である(図に示すように数値分布は(μ—3σ,μ+3σ)の確率は0.9974)であり、コアマトリクスがより大きい場合は対応するSigmaもより大きくなり、逆にSigmaがより大きい場合はコアマトリクスのカバー範囲もより大きくなる.具体的にはOpenCvでは、以下の式で計算する(ソースコード表示による).
   图像处理--高斯滤波_第4张图片
 5)同様にこの式から分かるように、param 1とparam 2が0(または与えられていない)である場合、フィルタウィンドウのサイズは、後の2つのパラメータが表すSigmaによって決定される.
图像处理--高斯滤波_第5张图片
 個別のガウスフィルタ関数は
void GaussianBlur(InputArray src, OutputArray dst, Size ksize, double sigmaX, double sigmaY=0, int borderType=BORDER_DEFAULT ) ;
      機能:入力した画像srcをガウスフィルタリングしてdstで出力します.
      パラメータ:srcとdstはもちろん入力画像と出力画像です.
                  Ksizeはガウスフィルタテンプレートサイズ
                  SigmaXとsigmaYはそれぞれGaussフィルタリングの横線と縦方向のフィルタ係数である.
                  borderTypeはエッジ拡張点補間タイプです.
       次の作業はGaussianBlur関数の内部に入って、その関数コードを追跡して、分析を経て、この関数の内部で多くの他の関数を呼び出して、その呼び出しの関数の階層は下図のようです:
图像处理--高斯滤波_第6张图片
      ここでは、ソースコードを最下層に深く分析する必要はありません.関数createSeparableLinearFilterとgetGaussianKernelの層を分析するだけです.
 
      関数呼び出し階層図から、関数GaussianBlurを解析するには、呼び出した内部関数を解析する必要があることがわかります.
      
そこでまず関数getGaussianKernelを解析する.
      機能:ksize*1の配列を返します.配列要素はガウス式を満たします.

     係数alphaとパラメータsigmaのみが未知であり、sigmaの求め方は以下の通りである.
     入力sigmaが正でない場合、計算式はsigma=0.3*((ksize-1)*0.5-1)+0.8となる.
     入力sigmaが正の場合、この入力パラメータsigmaを使用します.
     最後にalphaは正規化係数であり,計算したksize個数の和は1でなければならないので,後でksize個数だけを必要とし,その和を計算して逆算すればよい.

    ソースコードとコメントは次のとおりです.
cv::Mat cv::getGaussianKernel( int n, double sigma, int ktype )
{
	const int SMALL_GAUSSIAN_SIZE = 7;
	static const float small_gaussian_tab[][SMALL_GAUSSIAN_SIZE] =
	{
		{1.f},
		{0.25f, 0.5f, 0.25f},
		{0.0625f, 0.25f, 0.375f, 0.25f, 0.0625f},
		{0.03125f, 0.109375f, 0.21875f, 0.28125f, 0.21875f, 0.109375f, 0.03125f}
	};


	const float* fixed_kernel = n % 2 == 1 && n <= SMALL_GAUSSIAN_SIZE && sigma <= 0 ?
		small_gaussian_tab[n>>1] : 0;
	CV_Assert( ktype == CV_32F || ktype == CV_64F );//      32      64    
	Mat kernel(n, 1, ktype);//    n*1   kernel,  Mat                     
	float* cf = (float*)kernel.data;//    cf  kernel        
	double* cd = (double*)kernel.data;//    cd  kernerl        
	double sigmaX = sigma > 0 ? sigma : ((n-1)*0.5 - 1)*0.3 + 0.8;// sigma  0 ,      sigma(  n  )
	double scale2X = -0.5/(sigmaX*sigmaX);//          
	double sum = 0;
	int i;
	for( i = 0; i < n; i++ )
	{
		double x = i - (n-1)*0.5;
		//         ,     exp(scale2X*x*x)  ,          
		double t = fixed_kernel ? (double)fixed_kernel[i] : std::exp(scale2X*x*x);
		if( ktype == CV_32F )
		{
			cf[i] = (float)t;//        cf   
			sum += cf[i];//         
		}
		else
		{
			cd[i] = t;//      cd   
			sum += cd[i];
		}
	}
	sum = 1./sum;//            1
	for( i = 0; i < n; i++ )
	{
		if( ktype == CV_32F )
			cf[i] = (float)(cf[i]*sum);//           
		else
			cd[i] *= sum;//           
	}
	return kernel;//  n*1   ,             ,       
}
次の分析関数はcreateSeparableLinearFilterです.
    機能は、元の画像とターゲットの画像データフォーマットの統合とフィルタコアの合成を主に処理する画像フィルタリングエンジンクラスを作成することです.
そのソースコードと注釈は以下の通りである.
cv::Ptr<:filterengine> cv::createGaussianFilter( int type, Size ksize,
	double sigma1, double sigma2,
	int borderType )
{
	int depth = CV_MAT_DEPTH(type);//        
	if( sigma2 <= 0 )
		sigma2 = sigma1;//  3       ,            
	// automatic detection of kernel size from sigma

	if( ksize.width <= 0 && sigma1 > 0 )//          ,         

		ksize.width = cvRound(sigma1*(depth == CV_8U ? 3 : 4)*2 + 1)|1;
	if( ksize.height <= 0 && sigma2 > 0 )

		ksize.height = cvRound(sigma2*(depth == CV_8U ? 3 : 4)*2 + 1)|1;
	CV_Assert( ksize.width > 0 && ksize.width % 2 == 1 &&
		ksize.height > 0 && ksize.height % 2 == 1 );//           
	sigma1 = std::max( sigma1, 0. );//sigma   0
	sigma2 = std::max( sigma2, 0. );
	Mat kx = getGaussianKernel( ksize.width, sigma1, std::max(depth, CV_32F) );//  x       
	Mat ky;
	if( ksize.height == ksize.width && std::abs(sigma1 - sigma2) < DBL_EPSILON )
		ky = kx;//         ,   sigma        ,y        x    ,     
	else
		ky = getGaussianKernel( ksize.height, sigma2, std::max(depth, CV_32F) );//    y        
	return createSeparableLinearFilter( type, type, kx, ky, Point(-1,-1), 0, borderType );//  2       
}
最後に本当のGaussフィルタ関数GaussianBlurを見てみましょう:機能:入力画像に対する_srcをフィルタリングして出力画像を得るdst、フィルタコアサイズksize、フィルタパラメータはsigma 1とsigma 2から計算され、エッジ拡張モードはborderTypeである.そのソースコードと注釈は以下の通りである.
	void cv::GaussianBlur( InputArray _src, OutputArray _dst, Size ksize,
	double sigma1, double sigma2,
	int borderType )
{
	Mat src = _src.getMat();//      src,  _src      
	_dst.create( src.size(), src.type() );//               
	Mat dst = _dst.getMat();//        

	if( ksize.width == 1 && ksize.height == 1 )
	{
		src.copyTo(dst);//          1  ,          ,             
		return;
	}
	if( borderType != BORDER_CONSTANT )//            
	{
		if( src.rows == 1 )
			ksize.height = 1;//            ,         1
		if( src.cols == 1 )
			ksize.width = 1;//            ,         1
	}

	Ptr f = createGaussianFilter( src.type(), ksize, sigma1, sigma2, borderType );
	f->apply( src, dst );//      ,       src         dst
}
具体的な実現可能なGaussフィルタリング
ソースコードとコメントは次のとおりです.
#include "highgui.h"  
#include "cv.h"  

#pragma comment(linker, "/subsystem:windows /entry:mainCRTStartup")

void gaussianFilter(uchar* data, int width, int height)   
{     
	int i, j, index, sum;   
	int templates[9] = { 1, 2, 1,   
		2, 4, 2,   
		1, 2, 1 };//       
	sum = height * width * sizeof(uchar);//            
	uchar *tmpdata = (uchar*)malloc(sum);   
	memcpy((char*)tmpdata,(char*)data, sum);   
	for(i = 1;i < height - 1;i++)   
	{   
		for(j = 1;j < width - 1;j++)   
		{             
			index = sum = 0;   
			for(int m = i - 1;m < i + 2;m++)   
			{   
				for(int n = j - 1; n < j + 2;n++)   
				{   
					sum += tmpdata[m * width + n] * templates[index++];  //     
				}   
			}   
			data[i * width + j] = sum / 16;   

		}   
	}   
	free(tmpdata);   
}   

void imgOperate( IplImage* image )  
{  
	cvNamedWindow( "image-in", CV_WINDOW_AUTOSIZE );  
	cvNamedWindow( "image-access", CV_WINDOW_AUTOSIZE);  
	cvNamedWindow( "image-out", CV_WINDOW_AUTOSIZE);  
	cvShowImage( "image-in", image );  

	//                
	IplImage* pGrayImg=NULL; 
	IplImage* pImg=NULL;

	pGrayImg=cvCreateImage(cvGetSize(image),8,1);  
	pImg=cvCreateImage(cvGetSize(image),8,1); 
	cvCvtColor(image,pGrayImg,CV_RGB2GRAY);  
	//  cvSmooth( image, out, CV_GAUSSIAN, 5,5 );
	//      
	cvZero(pImg);
	CvRNG rng = cvRNG(-1);  //         
	cvRandArr(&rng, pImg, CV_RAND_NORMAL, cvScalarAll(0), cvScalarAll(15));
	cvAdd(pGrayImg, pImg,pImg);
	cvShowImage("image-access",pImg);
	gaussianFilter((unsigned char*)pImg->imageData,pImg->width,pImg->height);  
	cvShowImage( "image-out", pImg );  

	cvReleaseImage( &pGrayImg );  
	cvWaitKey( 0 );   
	cvDestroyWindow("image-in" );  
	cvDestroyWindow("image-out" );      
}  

int main()  
{  
	IplImage* img = cvLoadImage("d:\\demo.jpg");  
	imgOperate( img );  
	cvReleaseImage( &img );  
	return 0;  
} 
图像处理--高斯滤波_第7张图片 图像处理--高斯滤波_第8张图片
图像处理--高斯滤波_第9张图片