1. 程式人生 > >影象處理算法系列 第三章 影象模糊處理 (平滑處理)

影象處理算法系列 第三章 影象模糊處理 (平滑處理)

本章主要講影象處理中的模糊處理部分

英文叫做blur, 也叫做smootiing,  中文中叫做模糊或者平滑。

用過photoshop的人都應該知道,濾鏡裡面就有模糊這個選項,我們現在看看它是怎麼實現的。

一含義

   模糊(平滑)是一種常用的圖片處理方式,它的作用可以用來降低噪聲,還有其他用途

   看一下opencv 裡面的公式

               g(i,j) = \sum_{k,l} f(i+k, j+l) h(k,l)

     g(i,j)是目標座標的畫素值, f(i+k,j+l)是k,l這些地方的畫素值, h(k,l)是 kernel,  我不知道怎麼去準確翻譯它的意義,它是過濾器的係數。 

    簡單的按照我的思路去理解,就是一個權值,模糊的含義是將所有的畫素按照一定的權值進行運算,得到一個比較均衡的結果。

二 型別

型別有很多種: 均值模糊(box blur) 高斯模糊(gaussian blur)  中值模糊(media blur) 二值模糊(bilateral blur) 本文只講均值模糊和高斯模糊

三 演算法

1 均值模糊    均值模糊很簡單就是周邊所有的影響都是1,求平均值即可 K = \dfrac{1}{K_{width} \cdot K_{height}} \begin{bmatrix}    1 & 1 & 1 & ... & 1 \\    1 & 1 & 1 & ... & 1 \\    . & . & . & ... & 1 \\    . & . & . & ... & 1 \\    1 & 1 & 1 & ... & 1   \end{bmatrix} 2 高斯模糊 關於高斯模糊的演算法,推薦這個文章 根據這個公式計算出係數即可。 上篇文章寫得很詳細,我就不班門弄斧了。

四均值模糊的程式碼和效果

     先放上均值模糊的程式碼
void boxblur(Mat input ,Mat &out, int x, int y)
{
	// accept only char type matrices
	CV_Assert(input.depth() != sizeof(uchar));

	out.create(input.size(),input.type());

	int nChannels = input.channels();
	int nRows = input.rows;
	int nCols = input.cols;

	int size = x * y;
	float kernel = 1.0/size;

	int i,j;
	uchar* p;
	uchar* q;
	uchar R,G,B;

	for( i = x; i < nRows - x; ++i)
	{
		q = out.ptr<uchar>(i);
		for ( j = y; j < nCols - y; ++j)
		{
			float sumR = 0;
			float sumG = 0;
			float sumB = 0;
			for (int k =0; k<x;k++)
			{
				p = input.ptr<uchar>(i-x+k);
				for(int l = 0; l < y;l++)
				{
					sumB += input.at<uchar>(i - x + k,(j + l - y)*nChannels) * kernel;//p[(l + j -y)*nChannels ] * kernel;
					sumG += input.at<uchar>(i - x + k,(j + l - y)*nChannels + 1) * kernel;//p[(l + j -y)*nChannels + 1] * kernel;
					sumR += input.at<uchar>(i - x + k,(j + l - y)*nChannels + 2) * kernel;//p[(l + j -y)*nChannels + 2] * kernel;
} } q[j*nChannels] = sumB; q[j*nChannels+1] = sumG; q[j*nChannels+2] = sumR; } } }

紅色部分是我想直接用at,而不用指標,但是效率低的厲害。
下圖是用指標的相差了20倍。。。可見指標雖然萬惡,但是確實是個好東西。
由於size(4,4)圖太小看不清, 實際用的是8
原始 opencv 本文

五高斯模糊的程式碼和效果

程式碼如下:
void gaussblur(Mat input ,Mat &out, int x, int y)
{
	float sigma = 1.5;
	Mat kernel;
	float pi = 3.1415926;

	kernel.create(x ,y ,CV_32F);

	float mx = x/2.0;
	float my = y/2.0;

       //這裡有問題,後面做修正。
	for (int i =0; i< x;i++)
	{
		for (int j =0; j<y;j++)
		{
			kernel.at<float>(i,j) = exp(-1 * ((i - mx) * (i - mx) +(j - my) * (j-my) )/( 2 * sigma * sigma))/(2 * pi * sigma *sigma) ;
		}
	}

    int nChannels = input.channels();
	int nRows = input.rows;
	int nCols = input.cols;

	out.create(input.size(),input.type());
    uchar* p;
	uchar* q;
	float* s;

	for(int  i = x; i < nRows - x; ++i)
	{
		q = out.ptr<uchar>(i);
		for (int j = y; j < nCols - y; ++j)
		{
			float sumR = 0;
			float sumG = 0;
			float sumB = 0;
			for (int k =0; k<x;k++)
			{
				p = input.ptr<uchar>(i-x+k);
				s = kernel.ptr<float>(k); 
				for(int l = 0; l < y;l++)
				{
					sumB += p[(l + j -y)*nChannels ] * s[l];//input.at<uchar>(i - x + k,(j + l - y)*nChannels) * kernel;//
					sumG += p[(l + j -y)*nChannels + 1] *s[l];//input.at<uchar>(i - x + k,(j + l - y)*nChannels + 1) * kernel;//
					sumR += p[(l + j -y)*nChannels + 2] * s[l];//input.at<uchar>(i - x + k,(j + l - y)*nChannels + 2) * kernel;
				}
			}
			q[j*nChannels] = sumB;
			q[j*nChannels+1] = sumG;
			q[j*nChannels+2] = sumR;
		}
	}

	
}

效率如下:
效果圖如下: 本文沒有考慮邊界的情況,所以都是灰色的,可以考慮一下如何處理邊界。
原始 opencv 本文

上面程式碼有兩處問題: 第一是在size比較小的時候,這些點的概率之和不等於1,會導致圖片出問題。修正如下:
	float sum = 0;
	for (int i =0; i< x;i++)
	{
		for (int j =0; j<y;j++)
		{
			sum+= kernel.at<float>(i,j) = exp(-1 * ((i - mx) * (i - mx) +(j - my) * (j-my) )/( 2 * sigma * sigma))/(2 * pi * sigma *sigma) ;
		}
	}
	for (int i =0; i< x;i++)
	{
		for (int j =0; j<y;j++)
		{
			kernel.at<float>(i,j) = kernel.at<float>(i,j)/ sum ;
		}
	}


第二個問題是本文中sigma 是個固定值,實際上它是個可變值,具體怎麼計算,我沒有搞清楚,可以檢視opencv的原始碼,下面文章有參考價值 更新一下參考opencv裡面的可以這樣計算 sigma = 0.3*((ksize-1)*0.5 - 1) + 0.8 .
修改程式之後發現和原始的高斯函式基本一致,希望廣大朋友們多多評論,本人水平有限,很多地方有紕漏,希望能夠共同提高。