1. 程式人生 > >opencv學習筆記(十七)——線性濾波

opencv學習筆記(十七)——線性濾波

影象線性濾波:

  • 影象濾波:指儘量在儲存影象細節特徵的條件下對目標影象的噪聲進行抑制。
  • 影象濾波的目的:一是抽出物件的特徵作為影象識別的特徵模式;二是消除影象中混入的噪聲。
  • 影象濾波的要求:一是不能損壞影象的輪廓及邊緣等重要資訊;二是使影象清晰視覺效果好。
  • 濾波和模糊的區別:拿高斯濾波來舉例:濾波一般可以分為高通濾波和低通濾波,對於高斯低通濾波就會產生模糊效果,如果對於高斯高通濾波就會產生銳化的效果。所以通常是:高斯濾波就是指使用高斯函式進行濾波;高斯模糊就是指低通濾波。
  • 線性過濾器:即兩個訊號之和的響應和它們各自響應之和相等,換句話說,每個畫素的輸出值是一些輸入畫素的加權和,使用乘積和的計算,例如:R = w1z1 + w2z2 + … + wnzn
    高通:邊緣增強、邊緣提取
    低通:鈍化影象、去除噪音
    帶通:刪除特定頻率、增強中很少用

方框濾波(boxfilter)

方框濾波的原理:

方框濾波的核表示如下:
這裡寫圖片描述
這裡寫圖片描述
h代表該點的畫素值
hsize代表濾波核
這裡的normalize預設為ture,normalize為ture時,方框濾波其實就是均值濾波了
假設濾波核大小為3*3的的,錨點座標為(x,y),則在目標影象上對應(x,y)這點的畫素值為:
h = (f(x-1,y-1) + f(x,y-1)+ f(x+1,y-1) + f(x-1,y) + f(x,y) + f(x+1,y) + f(x-1,y+1) + f(x,y+1) + f(x+1,y+1))/(3*3)

Boxfilter函式API介紹:

C++: void boxFilter(InputArray src,OutputArray dst, int ddepth, Size ksize, Point anchor=Point(-1,-1), boolnormalize=true, int borderType=BORDER_DEFAULT )
  • 第一個引數,InputArray型別的src,輸入影象,即源影象,填Mat類的物件即可。該函式對通道是獨立處理的,且可以處理任意通道數的圖片,但需要注意,待處理的圖片深度應該為CV_8U, CV_16U, CV_16S, CV_32F 以及 CV_64F之一。
  • 第二個引數,OutputArray型別的dst,即目標影象,需要和源圖片有一樣的尺寸和型別。
  • 第三個引數,int型別的ddepth,輸出影象的深度,-1代表使用原圖深度,即src.depth()。
  • 第五個引數,Point型別的anchor,表示錨點(即被平滑的那個點),注意他有預設值Point(-1,-1)。如果這個點座標是負值的話,就表示取核的中心為錨點,所以預設值Point(-1,-1)表示這個錨點在核的中心。
  • 第七個引數,int型別的borderType,用於推斷影象外部畫素的某種邊界模式。有預設值BORDER_DEFAULT。

使用例項:

#include<opencv2/opencv.hpp>

using namespace cv;

int main()
{
    cv::Mat srcimage = cv::imread("test.png");

    cv::namedWindow("源影象");
    cv::namedWindow("濾波後");
    std::cout << std::endl;
    std::cout << std::endl;
    std::cout << std::endl;
    //show the source image
    cv::imshow("源影象", srcimage);

    cv::Mat dstimage;
    cv::boxFilter(srcimage, dstimage,-1, cv::Size(3,3));
    cv::imshow("濾波後", dstimage);
    cv::waitKey(0);
    return 0;

}

可以使用這種方式檢視影象對應的畫素值:

    for (int y = 0; y < srcimage.rows; y++)
    {
        for (int x = 0; x < srcimage.cols; x++)
        {
            std::cout << (int)(srcimage.at<Vec3b>(y, x)[0]) << " " << (int)(srcimage.at<Vec3b>(y, x)[1]) << " " << (int)(srcimage.at<Vec3b>(y, x)[2]) << ",";
        }
        std::cout << std::endl;
    }

但是這裡涉及一個邊界填充的問題,邊上進行濾波的時候,會有邊界填充的,如果使用的是BORDER_REFLECT_101,就需要使用對稱法,也就是以最邊緣畫素為軸對稱,不能簡單地由源影象按公式推輸出影象,

均值濾波(Blur)

介紹:

缺陷:不能很好得儲存影象的細節,去噪的同時會破壞影象的細節從而使影象模糊,不能很好地去噪。

均值濾波的原理介紹:

這裡寫圖片描述
它的原理就是在求均值,對濾波核所對應的所有元素的畫素值進行求和再求出均值,再將這個均值賦值給錨點所對應畫素作為該畫素的值。

均值濾波函式(Blur函式)的API介紹:

C++: void blur(InputArray src, OutputArraydst, Size ksize, Point anchor=Point(-1,-1), int borderType=BORDER_DEFAULT )
  • 第一個引數,InputArray型別的src,輸入影象,即源影象,填Mat類的物件即可。該函式對通道是獨立處理的,且可以處理任意通道數的圖片,但需要注意,待處理的圖片深度應該為CV_8U,
    CV_16U, CV_16S, CV_32F 以及 CV_64F之一。
  • 第二個引數,OutputArray型別的dst,即目標影象,需要和源圖片有一樣的尺寸和型別。比如可以用Mat::Clone,以源圖片為模板,來初始化得到如假包換的目標圖。
  • 第三個引數,Size型別(對Size型別稍後有講解)的ksize,核心的大小。一般這樣寫Size( w,h )來表示核心的大小( 其中,w
    為畫素寬度, h為畫素高度)。Size(3,3)就表示3x3的核大小,Size(5,5)就表示5x5的核大小
  • 第四個引數,Point型別的anchor,表示錨點(即被平滑的那個點),注意他有預設值Point(-1,-1)。如果這個點座標是負值的話,就表示取核的中心為錨點,所以預設值Point(-1,-1)表示這個錨點在核的中心。
  • 第五個引數,int型別的borderType,用於推斷影象外部畫素的某種邊界模式。有預設值BORDER_DEFAULT,我們一般不去管它。

使用例項:

#include<opencv2/opencv.hpp>

void main3()
{
    cv::Mat srcimage = cv::imread("腐蝕_膨脹.png");

    cv::namedWindow("源影象");
    cv::imshow("源影象", srcimage);
    cv::namedWindow("均值濾波");

    cv::Mat dstimage;

    cv::blur(srcimage, dstimage, cv::Size(15, 15));
    cv::imshow("均值濾波", dstimage);
    cv::waitKey(0);
}

高斯濾波(GaussianBlur)

介紹:

高斯濾波是一種線性平滑濾波,可以消除高斯噪聲,廣泛應用於影象處理減噪過程。

高斯濾波的原理:

高斯濾波就是每個畫素點的值都是由其本身和鄰域內的其他畫素值經過加權平均後得到的。
高斯函式具有五個重要的性質,這些性質使得它在早期影象處理中特別有用.這些性質表明,高斯平滑濾波器無論在空間域還是在頻率域都是十分有效的低通濾波器,且在實際影象處理中得到了工程人員的有效使用.高斯函式具有五個十分重要的性質,它們是:
(1)二維高斯函式具有旋轉對稱性,即濾波器在各個方向上的平滑程度是相同的.一般來說,一幅影象的邊緣方向是事先不知道的,因此,在濾波前是無法確定一個方向上比另一方向上需要更多的平滑.旋轉對稱性意味著高斯平滑濾波器在後續邊緣檢測中不會偏向任一方向.
(2)高斯函式是單值函式.這表明,高斯濾波器用畫素鄰域的加權均值來代替該點的畫素值,而每一鄰域畫素點權值是隨該點與中心點的距離單調增減的.這一性質是很重要的,因為邊緣是一種影象區域性特徵,如果平滑運算對離運算元中心很遠的畫素點仍然有很大作用,則平滑運算會使影象失真.
(3)高斯函式的付立葉變換頻譜是單瓣的.正如下面所示,這一性質是高斯函式付立葉變換等於高斯函式本身這一事實的直接推論.影象常被不希望的高頻訊號所汙染(噪聲和細紋理).而所希望的影象特徵(如邊緣),既含有低頻分量,又含有高頻分量.高斯函式付立葉變換的單瓣意味著平滑影象不會被不需要的高頻訊號所汙染,同時保留了大部分所需訊號.
(4)高斯濾波器寬度(決定著平滑程度)是由引數σ表徵的,而且σ和平滑程度的關係是非常簡單的.σ越大,高斯濾波器的頻帶就越寬,平滑程度就越好.通過調節平滑程度引數σ,可在影象特徵過分模糊(過平滑)與平滑影象中由於噪聲和細紋理所引起的過多的不希望突變數(欠平滑)之間取得折衷.
(5)由於高斯函式的可分離性,大高斯濾波器可以得以有效地實現.二維高斯函式卷積可以分兩步來進行,首先將影象與一維高斯函式進行卷積,然後將卷積結果與方向垂直的相同一維高斯函式卷積.因此,二維高斯濾波的計算量隨濾波模板寬度成線性增長而不是成平方增長.

高斯濾波函式(GaussianBlur函式):

  • 第一個引數,InputArray型別的src,輸入影象,即源影象,填Mat類的物件即可。它可以是單獨的任意通道數的圖片,但需要注意,圖片深度應該為CV_8U,CV_16U,
    CV_16S, CV_32F 以及 CV_64F之一。
  • 第二個引數,OutputArray型別的dst,即目標影象,需要和源圖片有一樣的尺寸和型別。比如可以用Mat::Clone,以源圖片為模板,來初始化得到如假包換的目標圖。
  • 第三個引數,Size型別的ksize高斯核心的大小。其中ksize.width和ksize.height可以不同,但他們都必須為正數和奇數。或者,它們可以是零的,它們都是由sigma計算而來。
  • 第四個引數,double型別的sigmaX,表示高斯核函式在X方向的的標準偏差。
  • 第五個引數,double型別的sigmaY,表示高斯核函式在Y方向的的標準偏差。若sigmaY為零,就將它設為sigmaX,如果sigmaX和sigmaY都是0,那麼就由ksize.width和ksize.height計算出來。
    為了結果的正確性著想,最好是把第三個引數Size,第四個引數sigmaX和第五個引數sigmaY全部指定到。
  • 第六個引數,
    int型別的borderType,用於推斷影象外部畫素的某種邊界模式。有預設值BORDER_DEFAULT,我們一般不去管它。

使用例項

#include<opencv2/opencv.hpp>

void mian()
{
    cv::Mat srcimage = cv::imread("腐蝕_膨脹.png");

    cv::namedWindow("srcimage");
    cv::namedWindow("dstimage");      

    cv::imshow("srcimage", srcimage);

    cv::Mat dstimage;

    cv::GaussianBlur(srcimage, dstimage, cv::Size(5, 5), 0, 0);

    cv::imshow("dstimage", dstimage);

    cv::waitKey(0);
}

綜合例項

#include<opencv2/opencv.hpp>

cv::Mat g_srcImage, g_dstImage1, g_dstImage2, g_dstImage3;
int g_nBoxFilterValue = 3;  //方框濾波的引數值
int g_nMeanBlurValue = 3;   //均值濾波引數值
int g_nGaussianBlurValue = 3;   //高斯濾波引數值

//軌跡調回調函式

static void on_BoxFilter(int, void *);
static void on_MeanBlur(int, void *);
static void on_GaussianBlur(int, void *);

void main4()
{
    g_srcImage = cv::imread("腐蝕_膨脹.png");
    if (!g_srcImage.data)
        std::cout << "圖片載入失敗!" << std::endl;
    g_dstImage1 = g_srcImage.clone();
    g_dstImage2 = g_srcImage.clone();
    g_dstImage3 = g_srcImage.clone();

    cv::namedWindow("源影象");
    cv::imshow("源影象", g_srcImage);

    /******************** Boxfilter ***********************/
    cv::namedWindow("BoxFilter");
    //建立滑塊條
    cv::createTrackbar("核心值:", "BoxFilter", &g_nBoxFilterValue, 40, on_BoxFilter);
    on_BoxFilter(g_nBoxFilterValue,0);
    cv::imshow("BoxFilter", g_dstImage1);

    /***********************MeanBlur************************/
    cv::namedWindow("MeanBlur");
    cv::createTrackbar("核心值:", "MeanBlur", &g_nMeanBlurValue, 40, on_MeanBlur);
    on_BoxFilter(g_nMeanBlurValue, 0);
    cv::imshow("MeanBlur", g_dstImage2);

    /*********************GaussianBlur***********************/
    cv::namedWindow("GaussianBlur");
    cv::createTrackbar("核心值:", "GaussianBlur", &g_nGaussianBlurValue, 40, on_GaussianBlur);
    on_GaussianBlur(g_nGaussianBlurValue, 0);
    cv::imshow("GaussianBlur", g_dstImage3);
    while (char(cv::waitKey(1)) != 'q') {};
}

static void on_BoxFilter(int, void *)
{
    cv::boxFilter(g_srcImage, g_dstImage1, -1, cv::Size(g_nBoxFilterValue + 1, g_nBoxFilterValue + 1));
    cv::imshow("BoxFilter", g_dstImage1);
}

static void on_MeanBlur(int, void*)
{
    cv::blur(g_srcImage, g_dstImage2, cv::Size(g_nMeanBlurValue + 1, g_nMeanBlurValue + 1),cv::Point(-1,-1));
    cv::imshow("MeanBlur", g_dstImage2);
}

static void on_GaussianBlur(int, void *)
{
    cv::GaussianBlur(g_srcImage, g_dstImage3, cv::Size(g_nGaussianBlurValue*2 + 1, g_nGaussianBlurValue*2 + 1), 0, 0);
    cv::imshow("GaussianBlur", g_dstImage3);
}