OpenCV C++(五)----圖像平滑

每一幅圖像都包含某種程度的噪聲,噪聲可以理解為由一種或者多種原因造成的灰 度值的隨機變化,如由光子通量的隨機性造成的噪聲等,在大多數(shù)情況下,通過平滑技術(shù)(也常稱為濾波技術(shù))進行抑制或者去除, 其中具備保持邊緣(Edge Preserving)作用的平滑技術(shù)得到了更多的關(guān)注。常用的平滑處理算法包括基于二維離散卷積的高斯平滑、均值平滑,基于統(tǒng)計學方法的中值平滑,具備保持邊緣作用的平滑算法的雙邊濾波、導向濾波等。

5.1、二維離散卷積

I與K的二維離散卷積的計算步驟如下。

  • 第一步: 將K 逆時針翻轉(zhuǎn)180°。

  • 第二步: Kflip沿著I 按照先行后列的順序移動, 每移動到一個固定位置, 對應位置就相乘, 然后求和。

1、full卷積

顯然,高為H1、寬為W1的矩陣I與高為H2、寬為W2的卷積核K 的full卷積結(jié)果是一 個高為H1+H2-1、寬為W1+W2-1的矩陣,一般H2 ≤H1,W2 ≤W1。

2、valid卷積

從full卷積的計算過程可知, 如果Kflip靠近I 的邊界, 那么就會有部分延伸到I之外而導致訪問到未定義的值, 忽略邊界,只是考慮I能完全覆蓋Kflip內(nèi)的值的情況, 該過程稱為valid卷積。
當然, 只有當H2≤H1且W2≤W1時才會存在 valid卷積 。

image.png

3、same卷積

為了使得到的卷積結(jié)果和原圖像的高、寬相等,所以通常在計算過程中給Kflip指定 一個“錨點”, 然后將“錨點”循環(huán)移至圖像矩陣的(r, c) 處, 其中0≤r< H1, 0≤c<W1,接下來對應位置的元素逐個相乘,最后對所有的積進行求和作為輸出圖像矩陣在 (r, c) 處的輸出值。這個卷積過程稱為same卷積,

image.png

大部分時候,為了更方便地指定卷積核的錨點,通常卷積核的寬、高為奇數(shù),那么可以簡單地令中心點為錨點的位置。same卷積是full卷積的一部分,而如果valid卷積存在,那么valid卷積是same卷積的一部分。

4、邊界擴充

對于full卷積和same卷積,矩陣I 邊界處的值由于缺乏完整的鄰接值,因此卷積運算 在這些區(qū)域需要特殊處理,方法是進行邊界擴充,有如下幾種常用方式。

  • (1) 在矩陣I 邊界外填充常數(shù), 通常進行的是0擴充。
  • (2) 通過重復I 邊界處的行和列, 對輸入矩陣進行擴充, 使卷積在邊界處可計算。
  • (3) 卷繞輸入矩陣, 即矩陣的平鋪。
  • (4) 以矩陣邊界為中心, 令矩陣外某位置上未定義的灰度值等于圖像內(nèi)其鏡像位置 的灰度值, 這種處理方式會令結(jié)果產(chǎn)生最小程度的干擾。(最常用

利用上述不同的邊界擴充方式得到的same卷積只是在距離矩陣上、下、左、右四個邊界小于卷積核半徑的區(qū)域內(nèi)值會不同,所以只要在用卷積運算進行圖像處理時,圖像的重要信息不要落在距離邊界小于卷積核半徑的區(qū)域內(nèi)就行。

copyMakeBorder(inputArray src,OutputArray dst,int top,int bottom,int left,int right,int borderType,
const Scalar& calue=Scalar())

5、卷積運算

void conv2D(InputArray src, InputArray kernel, OutputArray dst, int ddepth, Point anchor , int borderType)
{
    //step1:卷積核逆時針翻轉(zhuǎn)180°
    Mat kernelFlip;
    flip(kernel, kernelFlip, -1);
    //step:卷積運算
    filter2D(src, dst, ddepth, kernelFlip, anchor, 0.0, borderType);
}
可分離卷積核(速度快

如果一個卷積核至少由兩個尺寸比它小的卷積核full卷積而成,并且在計算過程中在所有邊界處均進行擴充零的操作,且滿足

image.png

其中kerneli的尺寸均比Kernel小,1≤i≤n,則稱該卷積核是可分離的。

在圖像處理中經(jīng)常使用這樣的卷積核,它可以分離為一維水平方向和一維垂直方向上的卷積核。

(1)full卷積性質(zhì)

如果卷積核Kernel是可分離的, 且Kernel=kernel1★kernel2, 則有:

image.png

(2)same卷積性質(zhì)

image.png
void sepConv2D_Y_X(InputArray src, OutputArray src_kerY_kerX, int ddepth, InputArray kernelY, InputArray kernelX, Point anchor , int borderType )
{
    //輸入矩陣與垂直方向上的卷積核的卷積
    Mat src_kerY;
    conv2D(src, kernelY, src_kerY, ddepth, anchor, borderType);
    //上面得到的卷積結(jié)果,接著和水平方向上的卷積核卷積
    conv2D(src_kerY, kernelX, src_kerY_kerX, ddepth, anchor, borderType);
}

5.2、高斯平滑

  • 第一步: 計算高斯矩陣。
image.png

其中

image.png
  • 第二步: 計算高斯矩陣的和。
image.png
  • 第三步: 高斯矩陣除以其本身的和, 即歸一化, 得到的便是高斯卷積算子。
image.png

其中,根據(jù)可分離卷積的性質(zhì),有

image.png
Mat gaussBlur(const Mat &image, Size winSize, float sigma, int ddepth , Point anchor , int borderType )
{
    //卷積核的寬、高均為奇數(shù)
    //CV_ASSERT(winSize.width % 2 == 1 && winSize.height % 2 == 1);
    //構(gòu)建垂直方向上的高斯卷積算子
    Mat gK_y = getGaussianKernel(sigma, winSize.width, CV_64F);
    //構(gòu)建水平方向上的高斯卷積算子
    Mat gK_x= getGaussianKernel(sigma, winSize.height, CV_64F);
    gK_x = gK_x.t();//轉(zhuǎn)置
    //分離的高斯卷積
    Mat blurImage;
    sepConv2D_Y_X(image, blurImage, ddepth, gK_y, gK_x, Point(-1, -1), borderType);
    return blurImage;
}

理解了上述高斯平滑的過程, 就可以明白OpenCV實現(xiàn)的高斯平滑函數(shù):

void GaussianBlur( InputArray src, OutputArray dst, Size ksize,
                                double sigmaX, double sigmaY = 0,
                                int borderType = BORDER_DEFAULT );

從參數(shù)的設(shè)置可以看出, GaussianBlur也是通過分離的高斯卷積核實現(xiàn)的,也可以令水平方向和垂直方向上的標準差不相同,但是一般會取相同的標準差。 當平滑窗口比較小時, 對標準差的變化不是很敏感, 得到的高斯平滑效果差別不大; 相反,當平滑窗口 較大時,對標準差的變化很敏感, 得到的高斯平滑效果差別較大 。

5.3、均值平滑

5.3.1、均值卷積核

image.png

利用卷積核 的分離性和卷積的結(jié)合律,雖然減少了運算量,但是隨著卷積核窗口的增加,計算量仍會繼續(xù)增大,可以利用圖像的積分,實現(xiàn)時間復雜度為O(1)的快速均值平滑。

5.3.2、快速均值平滑

image.png

即任意一個位置的積分等于該位置左上角所有值的和。 利用矩陣的積分,可以計算出矩陣中任意矩形區(qū)域的和。

image.png
    void integral(InputArray src,OutputArray sum,int sdepth=-1)
    void boxFilter(InputArray src,OutputArray dst,int ddepth,Size ksize,Point anchor=Point(-1,-1),
    bool normalize=true,int bordreType=BORDER_DEFAULT)
    void blur(InputArray src,OutputArray dst,Size ksize,Point anchor=Point(-1,-1),int borderType=BORDER_DEFAULT)

5.4、中值平滑

中值濾波最重要的能力是去除椒鹽噪聲。椒鹽噪聲是指在圖像傳輸系統(tǒng)中由于解碼誤差等原因,導致圖像中出現(xiàn)孤立的白點或者黑點。

Mat medianSmooth(const Mat& Input, Size size, int borderType )
{
    //輸入圖像應該為八位的灰度圖
    int height = size.height;
    int width = size.width;
    //窗口的高、寬均為奇數(shù),一般設(shè)置兩者都相同
    //對原圖矩陣進行邊界擴充
    int h = (height - 1) / 2;
    int w = (width - 1) / 2;
    Mat Ip;
    copyMakeBorder(Input, Ip, h, h, w, w, borderType);
    //輸入圖像的高、寬
    int rows = Input.rows;
    int cols = Input.cols;
    Mat medianI(Input.size(), CV_8UC1);
    int i = 0, j = 0;
    //中數(shù)的位置
    int index = (height*width - 1) / 2;
    for (int r = h; r < h + rows; r++)
    {
        for (int c = w; c < w + cols; c++)
        {
            //取以當前位置為中心、大小為size的鄰域
            Mat region = Input(Rect(c - w, r - h, width, height)).clone();
            //將該鄰域轉(zhuǎn)換成行矩陣
            cv::sort(region, region, CV_SORT_EVERY_ROW);//注:不能直接使用sort,分不清是cv的
            //取中數(shù)
            uchar mValue = region.at<uchar>(0, index);
            medianI.at<uchar>(i, j) = mValue;
            j++;
        }
        i++;
        j = 0;
    }
    return medianI;
}

一般來說,如果圖像中出現(xiàn)較亮或者較暗的物體,若其大小小于中值平滑的窗口半徑,那么它們基本上會被濾掉,而較大的目標則幾乎會原封不動地保存下來。

中值平滑需要對鄰域中的所有像素點按灰度值排序, 一般比卷積運算要慢。

在OpenCV中同樣通過定義函數(shù):

medianBlur(InputArray src,OutputArray dst,int ksize)

此外, 中值平滑只是排序統(tǒng)計平滑中的一種, 如果將取鄰域的中值變?yōu)槿∴徲蛑械?最小值或者最大值, 顯然會使圖像變暗或者變亮。 這類方法就是后面要介紹的形態(tài)學 處理的基礎(chǔ)。

高斯平滑、均值平滑在去除圖像噪聲時,會使圖像的邊緣信息變得模糊,接下來就 介紹在圖像平滑處理過程中可以保持邊緣的平滑算法: 雙邊濾波和導向濾波。

5.5、雙邊濾波

雙邊濾波是根據(jù)每個位置的鄰域, 對該位置構(gòu)建不同的權(quán)重模板。 詳細過程如下:

  • 第一步, 構(gòu)建winH×winW的空間距離權(quán)重模板, 與構(gòu)建高斯卷積核的過程類似, winH 和winW均為奇數(shù)。
image.png

其中0≤h<winH, 0≤w<winW, 且每個位置的空間距離權(quán)重模板是相同的。

  • 第二步, 構(gòu)建winH×winW的相似性權(quán)重模板, 是通過(r, c) 處的值與其鄰域值的差值的指數(shù)衡量的。
image.png

其中0≤h<winH, 0≤w<winW, 顯然每個位置的相似性權(quán)重模板是不一樣的。

  • 第三步, 將closenessWeightsimilarityWeight的對應位置相乘(即點乘),然后進行歸一化,便可得到該位置的權(quán)重模板。將所得到的權(quán)重模板和該位置鄰域的對應位置相乘,然后求和就得到該位置的輸出值, 和卷積運算的第二步操作類似。
    void bilateralFilter( InputArray src, OutputArray dst, int d,
                                       double sigmaColor, double sigmaSpace,
                                       int borderType = BORDER_DEFAULT );     

5.6、聯(lián)合雙邊濾波

  • 第一步,對每個位置的鄰域構(gòu)建空間距離權(quán)重模板。與雙邊濾波構(gòu)建空間距離權(quán)重模板一樣。
  • 第二步,構(gòu)建相似性權(quán)重模板。這是與雙邊濾波唯一的不同之處,雙邊濾波是根據(jù)原圖,對于每一個位置, 通過該位置和其鄰域的灰度值的差的指數(shù)來估計相似性;而聯(lián)合雙邊濾波是首先對原圖進行高斯平滑,根據(jù)平滑的結(jié)果,用當前位置及其鄰域的值的差來估計相似性權(quán)重模板。
  • 第三步,空間距離權(quán)重模板和相似性權(quán)重模板點乘,然后歸一化,作為最后的權(quán)重模板。
  • 第四步將權(quán)重模板與原圖(注意不是高斯平滑的結(jié)果)在該位置的鄰域?qū)恢梅e 的和作為輸出值。

整個過程只在第二步計算相似性權(quán)重模板時和雙邊濾波不同, 但是對圖像平滑的效果, 特別是對紋理圖像來說, 卻有很大的不同。

擴展
循環(huán)引導濾波 是一種迭代的方法, 本質(zhì)上是一種多次迭代的聯(lián)合雙邊濾波, 只是每次計算相似性權(quán)重 模板的依據(jù)不一樣——利用本次計算的聯(lián)合雙邊濾波結(jié)果作為下一次聯(lián)合雙邊濾波計算 相似性權(quán)重模板的依據(jù)。

5.7、導向濾波

導向濾波在平滑圖像的基礎(chǔ)上,有良好的保邊作用, 而且在細節(jié)增強等方面都有良好的表現(xiàn),在執(zhí)行時間上也比雙邊濾波快很多。

image.png
image.png
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容