OpenCV-C++ 圖像濾波(二)-中值濾波-雙邊濾波


這一節主要講一下,中值濾波, 高斯雙邊濾波;

中值濾波

中值濾波(Median Filter)是一種典型的非線性濾波技術,基本思想是用像素點領域灰度值的中值來代替像素點的灰度值,該方法在去除脈沖噪聲,椒鹽噪聲的同時又能保留圖像邊緣信息;

中值濾波是基於排序統計理論的一種能夠有效地抑制噪聲的非線性信號處理技術,其基本原理是把數字圖像或數字序列中一點的值用該點的一個領域內各點值的中值代替,讓周圍的像素值接近真實值,從而消除孤立的噪聲點,對於斑點噪聲(Speckle noise)和椒鹽噪聲(salt-pepper noise)來說尤其有用,因為它不依賴於領域內那些與典型值差別很大的值;

中值濾波在處理連續圖像窗函數時與線性濾波器的工作方式類似,但濾波過程卻不再是加權運算;

中值濾波在一定的條件下,可以克服常見線性濾波器如最小均方濾波器, 方框濾波器, 均值濾波器帶來的圖像細節模糊,而且對濾除脈沖干擾及圖像掃描噪聲非常有效,也常用於保護邊緣信息,保存邊緣的特性使他在不希望出現出現邊緣模糊的場合也很有用,是非常經典的平滑噪聲處理方法;

優缺點:

  • 均值濾波的輸出會受到到噪聲點的影響,而中值濾波不會受其影響;
  • 中值濾波耗時在均值濾波的5倍以上;

計算過程:

  1. 獲取目標像素值周圍一定領域范圍的像素值;
  2. 按灰度值進行從小到大的排序
  3. 選擇排序后的中間值作為,替代掉目標像素值;

OpenCV中medianBlur API的介紹如下:

void medianBlur( InputArray src, OutputArray dst, int ksize );
  • src需要濾波的原圖像
  • dst中值濾波后輸出圖像
  • ksize表示領域范圍大小,必須是一個奇數;

示例使用如下:

// 增加椒鹽噪聲
Mat srcSaltPepper = addSaltNoise(src, 100);

// 中值濾波
Mat dstMedian;
medianBlur(srcSaltPepper, dstMedian, 3);

// 高斯濾波用於對比
Mat dstGaussian;
GaussianBlur(srcSaltPepper, dstGaussian, Size(3, 3), 3, 3);

namedWindow("src", WINDOW_AUTOSIZE);
imshow("src", src);
namedWindow("srcSaltPepper", WINDOW_AUTOSIZE);
imshow("srcSaltPepper", srcSaltPepper);
namedWindow("medianBlur", WINDOW_AUTOSIZE);
imshow("medianBlur", dstMedian);
namedWindow("GaussianBlur", WINDOW_AUTOSIZE);
imshow("GaussianBlur", dstGaussian);
  • 為了驗證中值濾波對去除椒鹽噪聲的效果,對原始圖像增加了椒鹽噪聲,加噪的代碼最下面的完整代碼中有;
  • 對比了中值濾波與高斯濾波對椒鹽噪聲的處理效果,如下圖所示,很明顯中值濾波效果要好;

下圖,從左到右分別為: 噪聲圖像, 中值濾波效果,高斯濾波效果;

高斯雙邊濾波

雙邊濾波也是一種非線性的濾波方法,是結合圖像的空間鄰近度和像素值相似度的一種折中處理,同時考慮空域信息和灰度相似性,達到保邊去噪的目的;

雙邊濾波的好處在於可以作邊緣保存, 一般過去用的維納濾波或者高斯濾波去降噪,都會較明顯的模糊邊緣,對於高頻細節的保護效果並不明顯;

雙邊濾波顧名思義比高斯濾波多了一個高斯方差sigma-d,它是基於空間分布的高斯濾波函數,所以在邊緣附近,離的較遠的像素不會太多影響到邊緣上的像素值,這樣就保證了邊緣附近像素值的保存;

但是由於保存了過多的高頻信息,對於彩色圖像里的高頻噪聲,雙邊濾波不能干凈的濾掉,只能夠對於低頻信息進行較好的濾波;

輸出的像素值計算方式如下:

\[g(i, j) = \dfrac{\sum){k, l}f(k, l)w(i, j, k, l)}{\sum_{k, l}w(i, j, k, l)} \]

  • g(i, j)表示輸出像素值;
  • f(k, l)表示卷積核;
  • w(i, j, k, l)表示加權系數,取決於定義域核和值域核的乘積;

有:

\[w(i,j, k, l) = d(i, j,k, l) \cdot r(i, j, k, l) \]

其中,定義域核\(d(i, j, k, l)\)表示為:

\[d(i, j, k, l) = exp(-\dfrac{(i-k)^2+(j-l)^2}{2\sigma_d^2}) \]

值域核\(r(i, j, k, l)\)表示為:

\[r(i, j, k, l) = exp(-\dfrac{||f(i,j) - f(k, l)||^2}{2\sigma_r^2}) \]

因此, 有:

\[w(i, j, k, l) = exp(-\dfrac{(i-k)^2+(j-l)^2}{2\sigma_d^2} -\dfrac{||f(i,j) - f(k, l)||^2}{2\sigma_r^2}) \]

OpenCV中bilateralFilterAPI的介紹如下:

void bilateralFilter( InputArray src, OutputArray dst, int d,
                     double sigmaColor, double sigmaSpace,
                     int borderType = BORDER_DEFAULT );
  • src需要去噪的圖像
  • dst輸出的圖像
  • d表示每個像素領域的直徑
  • sigmaColor,顏色空間濾波器的sigma值;這個參數值越大,就表明該像素領域內有更寬廣的顏色會被混合到一起,產生較大的半相等的顏色區域;
  • sigmaSpace坐標空間中的濾波器的sigma值,坐標空間的標准方差;數值越大,意味着越遠的像素會互相影響,從而使得更大的區域足夠相似的顏色獲取相同的顏色;當\(d>0\),\(d\)指定了領域大小且與sigmaSpace,否則,d正比於sigmaSpace;

示例, 關於bilateralFilterAPI的使用:

// 雙邊濾// 雙邊濾波
Mat dstBilateralFilter;
bilateralFilter(srcSaltPepper, dstBilateralFilter, 25, 25*2, 25/2);

結果如下圖所示,從左到右分別為:噪聲圖像, 雙邊濾波,高斯濾波;

可以發現,的確對圖像邊緣細節進行了保留;

最后,完整的代碼如下:

#include <iostream>
#include <opencv2/opencv.hpp>

using namespace std;
using namespace cv;

Mat addSaltNoise(const Mat src, int n);  // 添加椒鹽噪聲

int main(){

    // 讀取圖像
    Mat src = imread("/home/chen/dataset/lena.jpg");
    if (src.empty()){
        cout << "cloud not load image." << endl;
        return -1;
    }
    // 增加椒鹽噪聲
    Mat srcSaltPepper = addSaltNoise(src, 100);

    // 中值濾波
    Mat dstMedian;
    medianBlur(srcSaltPepper, dstMedian, 3);

    Mat dstGaussian;
    GaussianBlur(srcSaltPepper, dstGaussian, Size(3, 3), 3, 3);

    // 雙邊濾波
    Mat dstBilateralFilter;
    bilateralFilter(srcSaltPepper, dstBilateralFilter, 25, 25*2, 25/2);

    namedWindow("src", WINDOW_AUTOSIZE);
    imshow("src", src);
    namedWindow("srcSaltPepper", WINDOW_AUTOSIZE);
    imshow("srcSaltPepper", srcSaltPepper);
    namedWindow("medianBlur", WINDOW_AUTOSIZE);
    imshow("medianBlur", dstMedian);
    namedWindow("GaussianBlur", WINDOW_AUTOSIZE);
    imshow("GaussianBlur", dstGaussian);
    namedWindow("bilateralFilter", WINDOW_AUTOSIZE);
    imshow("bilateralFilter", dstBilateralFilter);

    waitKey(0);
    return 0;
}

// 添加椒鹽噪聲
Mat addSaltNoise(const Mat src, int n){

    Mat dst = src.clone();
    for (int k = 0; k < n; k++){
        // 隨機選擇行列
        int i = rand() % dst.rows;
        int j = rand() % dst.cols;

        if (dst.channels() == 1){
            dst.at<uchar>(i, j) = 255;  // 鹽噪聲
        } else{
            dst.at<Vec3b>(i, j)[0] = 255;
            dst.at<Vec3b>(i, j)[1] = 255;
            dst.at<Vec3b>(i, j)[2] = 255;
        }
    }
    for (int k = 0; k < n; k++)
	{
		//隨機取值行列
		int i = rand() % dst.rows;
		int j = rand() % dst.cols;
		//圖像通道判定
		if (dst.channels() == 1)
		{
			dst.at<uchar>(i, j) = 0;  // 椒噪聲
		} else
		{
			dst.at<Vec3b>(i, j)[0] = 0;
			dst.at<Vec3b>(i, j)[1] = 0;
			dst.at<Vec3b>(i, j)[2] = 0;
		}
	}
    return dst;
}

Reference:


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM