一、前言
上一篇我們講解了OpenCV的圖像亮度和對比度調(diào)整。本篇主要向大家介紹下圖像模糊。按慣例,先來一張效果圖。

二、模糊
所謂模糊,可以先簡單理解為每一個像素都取周邊像素的平均值。

上圖中,2是中間點像素值,周邊像素都是1。

中間點取周圍點的平均值,就會變成1。在數(shù)值上叫平滑。在圖形上,就產(chǎn)生了模糊效果,也就是中間點失去了細(xì)節(jié)。

三、圖像模糊
圖像模糊是opencv常見的操作,使用模糊操作的原因是為了給圖像預(yù)處理時降低噪聲影響。
Smooth和Blur是opencv圖像模糊的API,其背后的原理其實是數(shù)學(xué)的卷積操作

其中權(quán)重核h(k,l) 為“濾波系數(shù)”。上面的式子可以簡記為:

通常這些卷積算子計算都是線性操作,所以又叫線性濾波。
四、線性濾波
1、均值濾波
均值濾波是典型的線性濾波算法,它是指在圖像上對目標(biāo)像素給一個模板,該模板包括了其周圍的臨近像素,再用模板中的全體像素的平均值來代替原來像素值。
還記得第二篇里所講掩膜操作吧,均值濾波的過程跟掩膜操作極其相似。

濾波操作:在9x9上面有3x3的窗口,從左到右,從上到下移動,白色的每個像素點值之和取平均值賦給中心紅色像素作為它處理之后的像素值。其中,模板就是3x3的窗口,紅色格子為目標(biāo)像素,白色格子為周圍的臨近像素。
此類操作被稱為卷積計算,而模板和kernel就是卷積計算中的卷積算子。
opencv提供了均值濾波(模糊)的API
/**
@param src 輸入圖像
@param dst 輸出圖像
@param ksize 模糊kernel大小
@param anchor 錨點; 默認(rèn)值為Point(-1,-1) ,表示在錨點在kernel的中心
@param borderType 查看#BorderTypes
*/
blur( InputArray src, OutputArray dst,
Size ksize, Point anchor = Point(-1,-1),
int borderType = BORDER_DEFAULT );
鎧玩模糊

+ (UIImage *)blur:(UIImage *)image sizeX:(int)sizeX sizeY:(int)sizeY {
Mat src;
UIImageToMat(image, src);
Mat dst;
blur(src, dst, cv::Size(sizeX, sizeY));
UIImage* result = MatToUIImage(dst);
return result;
}
class BlurViewController: UIViewController {
@IBOutlet weak var imageView: UIImageView!
@IBOutlet weak var resultImageView: UIImageView!
private var sizeX: Int32 = 3
private var sizeY: Int32 = 3
override func viewDidLoad() {
super.viewDidLoad()
}
@IBAction func onSliderValueChanged(_ sender: UISlider) {
sizeX = Int32(sender.value)
transform()
}
@IBAction func onSlider2ValueChanged(_ sender: UISlider) {
sizeY = Int32(sender.value)
transform()
}
private func transform() {
let image = OpenCV.blur(imageView.image, sizeX: sizeX, sizeY: sizeY)
resultImageView.image = image
}
}
一些思考
濾波操作為何能將圖像模糊?
濾波操作其實是用周圍的像素平均值作為目標(biāo)像素的值,經(jīng)過這樣的處理后其實就是整個圖像像素值差距縮小。差距縮小了自然就比較沒有辨識度了,也就是模糊了。
為何在鎧玩模糊中,只改變Size的x大小會讓鎧在x軸方向模糊?
這題就當(dāng)討論題吧,有興趣的朋友可以在文章的評論處討論。
提示:改變x的值其實就是改變kernel的形態(tài),單獨將x增加,就相當(dāng)于kernel的形態(tài)變成寬度大于高度的長方形。
均值模糊有沒有什么問題?
我們知道圖像都是連續(xù)的,越靠近的點關(guān)系越密切,越遠(yuǎn)離的點關(guān)系越疏遠(yuǎn),均值模糊只是簡單的取平均,沒有分配權(quán)重,肯定存在不合理之處。相比之下,加權(quán)平均更合理,距離越近的點權(quán)重越大,距離越遠(yuǎn)的點權(quán)重越小。
下面我們就來看下帶權(quán)重的高斯濾波。
2、高斯濾波
正態(tài)分布

如上圖,正態(tài)分布是一種鐘擺形曲線,越接近中心,取值越大,越遠(yuǎn)離中心,取值越小。
計算平均值的時候,我們只需要將中心點作為原點,其他點按照其在正態(tài)曲線上的位置,分配權(quán)重,就可以得到一個加權(quán)平均值。
高斯函數(shù)
一維函數(shù)

σ:標(biāo)準(zhǔn)差,在這里又叫做高斯半徑。
σ2:方差。
f(x):概率
μ:均值,即期望。
在計算平均值的時候,中心點就是原點,所以μ等于0??傻煤喕蟮暮瘮?shù):

根據(jù)一維函數(shù),可以推導(dǎo)得到二維函數(shù):

圖像是二維的,所以通常處理圖像時,我們使用二維高斯函數(shù)。
計算例子
假定中心點的坐標(biāo)是(0,0),那么距離它最近的8個點的坐標(biāo)如下:

假設(shè)σ=1.5,則權(quán)重矩陣如下:

這9個點的權(quán)重總和等于0.4787147,歸一化后得到最終的權(quán)重矩陣:

假設(shè)現(xiàn)有圖像矩陣如下:

與權(quán)重相乘后得到的矩陣如下:

將這9個值相加就是中心點的最終值:13.9401236。而通過均值濾波得到的結(jié)果是13.5。
均值與高斯哪家強
@param sigmaX Gaussian kernel standard deviation in X direction.
@param sigmaY Gaussian kernel standard deviation in Y direction; if sigmaY is zero, it is set to be
equal to sigmaX, if both sigmas are zeros, they are computed from ksize.width and ksize.height,
respectively (see #getGaussianKernel for details); to fully control the result regardless of
possible future modifications of all this semantics, it is recommended to specify all of ksize,
sigmaX, and sigmaY.
@param borderType pixel extrapolation method, see #BorderTypes
@sa sepFilter2D, filter2D, blur, boxFilter, bilateralFilter, medianBlur
*/
CV_EXPORTS_W void GaussianBlur( InputArray src, OutputArray dst, Size ksize,
double sigmaX, double sigmaY = 0,
int borderType = BORDER_DEFAULT );
+ (UIImage *)gaussianblur:(UIImage *)image sizeX:(int)sizeX sizeY:(int)sizeY {
Mat src;
UIImageToMat(image, src);
Mat dst;
GaussianBlur(src, dst, cv::Size(sizeX, sizeY), 11);
UIImage* result = MatToUIImage(dst);
return result;
}


仔細(xì)觀看可以看到,高斯模糊圖像的輪廓較均值的清晰些,沒有那么“模糊”。
五、小結(jié)
本篇主要介紹了圖像模糊的概念,并通過例子講解了均值模糊和高斯模糊。模糊經(jīng)常在圖像預(yù)處理降時使用到,需要好好掌握其原理,以便于應(yīng)對不同情況。今天就到這了,有疑問的朋友可以給我留言,咱們下篇見!