Learning OpenCV with iOS: 圖像模糊--非線性濾波

一、前言

上一篇我們講解了OpenCV圖像模糊中的線性濾波。本篇主要向大家介紹下非線性濾波。按慣例,先來看下效果圖。

給鎧祛痘
阿珂美顏

二、線性與非線性

上一篇中使用“卷積算子計(jì)算都是線性操作,所以又叫線性濾波”簡(jiǎn)單描述了線性濾波概念。下面我們?cè)敿?xì)了解下線性濾波非線性濾波

數(shù)學(xué)角度

數(shù)學(xué)里,一般說的線性,是說的線性映射:
線性 = 齊次性 + 可加性
齊次性: f(ax)=af(x)
可加性: f(x+y)=f(x)+f(y)
非線性就是這兩條至少之一不成立.

圖像角度

線性濾波:兩個(gè)信號(hào)之和的響應(yīng)和他們各自響應(yīng)之和相等(可加性)。換句話說,每個(gè)像素的輸出值是一些輸入像素的加權(quán)和
非線性濾波:原始數(shù)據(jù)與濾波結(jié)果是一種邏輯關(guān)系,即通過比較一定鄰域內(nèi)的灰度值大小來實(shí)現(xiàn)的。

線性濾波器易于構(gòu)造,并且易于從頻率響應(yīng)角度來進(jìn)行分析。但是,線性濾波在處理散粒噪聲(即圖像偶爾會(huì)出現(xiàn)很大的值)的時(shí)候,無法將噪聲像素去除,只能轉(zhuǎn)換為更為柔和但仍然可見的散粒。

這時(shí)非線性濾波就該登場(chǎng)了。

三、非線性濾波

1、中值濾波(Median filter)

中值濾波是一種典型的非線性濾波技術(shù),原理是用鄰域像素灰度值的中值來代替該像素點(diǎn)的灰度值。

濾波過程
中值濾波

濾波操作:在9 x 9上面有3 x 3的窗口,從左到右,從上到下移動(dòng)。將3 x 3窗口內(nèi)的灰度值按順序排列,然后取中值代替中心的灰度值。

中值濾波在一定的條件下可以克服常見線性濾波器,如均值濾波帶來的圖像細(xì)節(jié)模糊。而且對(duì)去除椒鹽噪聲非常有效,也常用于保護(hù)邊緣信息, 保存邊緣的特性使它在不希望出現(xiàn)邊緣模糊的場(chǎng)合也很有用,是非常經(jīng)典的平滑噪聲處理方法。

中值濾波與均值濾波比較

中值濾波優(yōu)勢(shì)

在均值濾波中,將噪聲像素與非噪聲像素一并放入平均計(jì)算中,影響了輸出。在中值濾波中,噪聲像素很難被選成中值,所以幾乎不會(huì)影響到輸出。因此,中值濾波消除噪聲和邊緣保護(hù)方便都更勝一籌。

中值濾波劣勢(shì)

因?yàn)橹兄禐V波要進(jìn)行排序操作,所以處理的時(shí)間長(zhǎng),是均值濾波的5倍以上。

給鎧祛痘

給鎧祛痘

OpenCV提供了中值濾波的API

/** 
@param ksize aperture linear size; it must be odd and greater than 1, for example: 3, 5, 7 ...
 */
void medianBlur( InputArray src, OutputArray dst, int ksize );

注意:ksize必須是奇數(shù)

+ (UIImage *)medianBlur:(UIImage *)image size:(int)size {
    Mat src;
    UIImageToMat(image, src);
    
    int finalSize = size;
    if (size%2 == 0) {
        finalSize = size + 1;
    }
    Mat dst;
    medianBlur(src, dst, finalSize);
    
    UIImage* result = MatToUIImage(dst);
    
    return result;
}

class NolinearBlurViewController: UIViewController {

    @IBOutlet weak var resultImageView: UIImageView!
    let image = #imageLiteral(resourceName: "ddkai")
    
    @IBAction func onSliderValueChanged(_ sender: UISlider) {
        resultImageView.image = OpenCV.medianBlur(self.image, size: Int32(Int(sender.value)))
    }
}

一些思考

為何ksize必須是奇數(shù)?

因?yàn)槿绻鹝size是偶數(shù),那么將像素灰度值從小到大排列后,必然就沒有唯一的中值。即使得出中值,那么又將那個(gè)作為中心像素呢?因此,中值濾波要求ksize必須是奇數(shù)。

ksize

中值濾波有什么不適合的場(chǎng)景?

對(duì)一些細(xì)節(jié)多,特別是線、尖頂等細(xì)節(jié)多的圖像不宜采用中值濾波。因?yàn)橹兄禐V波會(huì)將這些細(xì)節(jié)也模糊掉。

2、雙邊濾波(Bilateral filter)

空間域&像素值域

對(duì)于圖像濾波來說,圖像在空間中變化緩慢,因此相鄰的像素點(diǎn)會(huì)更相近。但是這個(gè)假設(shè)在圖像的邊緣處變得不成立。如果在邊緣處也用這種思路來進(jìn)行濾波的話,即認(rèn)為相鄰相近,則得到的結(jié)果必然會(huì)模糊掉邊緣。因?yàn)檫吘墐蓚?cè)的點(diǎn)的像素值差別很大,所以權(quán)重還需考慮像素值。

因此,濾波不但要考慮空間域(以下簡(jiǎn)稱空域),還需要考慮像素值域(以下簡(jiǎn)稱值域)。

濾波分析

  • 均值濾波無法克服邊緣像素信息丟失。原因是均值濾波是基于平均權(quán)重,沒有考慮空域值域
  • 高斯模糊部分克服了該缺陷(考慮了空域),但是無法完全避免,因?yàn)?strong>沒有考慮像素值的不同,即沒有考慮值域。

雙邊濾波

雙邊濾波是一種非線性的濾波方法,具有保邊去噪的效果。

雙邊濾波

雙邊濾波的基本思路是同時(shí)考慮像素點(diǎn)的空域值域。
雙邊濾波在考慮值域時(shí),利用像素點(diǎn)的值的大小進(jìn)行補(bǔ)充,因?yàn)檫吘墐蓚?cè)的點(diǎn)的像素值差別很大,因此會(huì)使得其加權(quán)的時(shí)候權(quán)重具有很大的差別,從而使得只考慮自己所屬的一邊的鄰域。可以理解成先根據(jù)像素值對(duì)要用來進(jìn)行濾波的鄰域做一個(gè)分割或分類,再給該點(diǎn)所屬的類別相對(duì)較高的權(quán)重,然后進(jìn)行鄰域加權(quán)求和,得到最終結(jié)果。

在雙邊濾波器中,輸出像素的值依賴于鄰域像素值的加權(quán)值組合:

雙邊濾波
  • w(i,j,k,l): 加權(quán)系數(shù), 取決于空域核和值域核的乘積。
  • (i,j),(k,l): 指兩個(gè)像素點(diǎn)的坐標(biāo)。

空域核:

空域核

值域核:

值域核

雙邊濾波權(quán)重函數(shù):

雙邊濾波權(quán)重函數(shù)

空域核(d)函數(shù)是根據(jù)像素距離選擇權(quán)重,距離越近權(quán)重越大。
值域核(r)函數(shù)則是根據(jù)像素的差異來分配權(quán)值。如果兩個(gè)像素值越接近,即使相距較遠(yuǎn),也比差異大而距離近的像素點(diǎn)權(quán)重大。這點(diǎn)使得邊緣(即相距近但差異大的像素點(diǎn))的特性得以保留。

阿珂美顏

阿珂美顏
阿珂美顏

OpenCV提供了雙邊濾波的API

/** 
_Sigma values_: 為了簡(jiǎn)單起見,可以將2 Sigma值設(shè)置為相同。
如果它們很小(<10)濾波器不會(huì)有太大的效果。
如果它們很大(>150),它們將具有非常強(qiáng)烈的效果,使圖像看起來“卡通化”。

_Filter size_: 大的濾波器(D> 5)非常慢,因此建議在進(jìn)行實(shí)時(shí)處理應(yīng)用程序時(shí)使用d=5。對(duì)于需要重噪聲過濾的離線應(yīng)用程序可以試下d=9。

@param src : 即源圖像,需要為8位或者浮點(diǎn)型單通道、三通道的圖像。
@param d:過濾過程中每個(gè)像素鄰域的直徑。如果這個(gè)值我們?cè)O(shè)其為非正數(shù),那么OpenCV會(huì)從第五個(gè)參數(shù)sigmaSpace來計(jì)算出它來。
@param sigmaColor :顏色空間濾波器的sigma值。這個(gè)參數(shù)的值越大,就表明該像素鄰域內(nèi)有更寬廣的顏色會(huì)被混合到一起,產(chǎn)生較大的半相等顏色區(qū)域。
@param sigmaSpace:坐標(biāo)空間中濾波器的sigma值,坐標(biāo)空間的標(biāo)注方差。
他的數(shù)值越大,意味著越遠(yuǎn)的像素會(huì)相互影響,從而使更大的區(qū)域足夠相似的顏色獲取相同的顏色。
當(dāng)d>0,d指定了鄰域大小且與sigmaSpace無關(guān)。否則,d正比于sigmaSpace。
 */ 
void bilateralFilter( InputArray src, OutputArray dst, int d,
                                   double sigmaColor, double sigmaSpace,
                                   int borderType = BORDER_DEFAULT );
+ (UIImage *)bilateralFilter:(UIImage *)image
                           d:(int)d
                  sigmaColor:(double)sigmaColor
                  sigmaSpace:(double)sigmaSpace {
    Mat src;
    UIImageToMat(image, src);
    
    if (src.channels() == 4) {
        cvtColor(src, src, CV_BGRA2BGR);
    }
    
    Mat dst;
    bilateralFilter(src, dst, d, sigmaColor, sigmaSpace);
    
    UIImage* result = MatToUIImage(dst);
    
    return result;
}


class BilateralFilterViewController: UIViewController {

    @IBOutlet weak var imageView: UIImageView!
    @IBOutlet weak var blurImageView: UIImageView!
    @IBOutlet weak var gBlurImageView: UIImageView!
    @IBOutlet weak var resultImageView: UIImageView!
    
    private var d: Int32 = 1
    private var color: Double = 1.0
    private var space: Double = 1.0
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        blurImageView.image = OpenCV.blur(imageView.image, sizeX: 3, sizeY: 3)
        gBlurImageView.image = OpenCV.gaussianblur(imageView.image, sizeX: 3, sizeY: 3)
    }

    @IBAction func onSliderValueChanged(_ sender: UISlider) {
        d = Int32(sender.value)
        transform()
    }
    
    @IBAction func onSlider2ValueChanged(_ sender: UISlider) {
        color = Double(sender.value)
        transform()
    }
    
    @IBAction func onSlider3ValueChanged(_ sender: UISlider) {
        space = Double(sender.value)
        transform()
    }
    
    private func transform() {
        resultImageView.image = OpenCV.bilateralFilter(imageView.image, d: d, sigmaColor: color, sigmaSpace: space)
    }
}

小小經(jīng)驗(yàn)

如何選取合適的參數(shù)?
  • 使用OpenCV API時(shí)可以先看下API的注釋文檔,比如在雙邊濾波的注釋文檔中對(duì)Sigma的取值做了說明。這些值一般都是經(jīng)驗(yàn)值。
_Sigma values_: 為了簡(jiǎn)單起見,可以將2 Sigma值設(shè)置為相同。
如果它們很小(<10)濾波器不會(huì)有太大的效果。
如果它們很大(>150),它們將具有非常強(qiáng)烈的效果,使圖像看起來“卡通化”。
  • 使用滑竿幫助快速調(diào)節(jié)參數(shù),觀察效果。

  • 原理公式出發(fā),假定一些參數(shù),觀察其趨勢(shì),掌握規(guī)律。

四、小結(jié)

本篇主要介紹了非線性濾波的概念,并通過例子講解了中值濾波和雙邊濾波。 非線性濾波的應(yīng)用廣泛,不但要掌握API的調(diào)用,更要明白各種濾波的原理,這樣才能創(chuàng)造個(gè)性化的濾波,也許有一天你就創(chuàng)造出自己的美顏濾鏡了。 今天就到這了,有疑問的朋友可以給我留言,咱們下篇見!

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

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

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