1.圖像濾波
圖像濾波是指去除圖像中不重要的內(nèi)容,而使關(guān)心的內(nèi)容表現(xiàn)得更加清晰的方法,例如去除圖像中的噪聲、提取某些信息等。
根據(jù)圖像濾波的目的不同,可以將圖像濾波分為消除圖像噪聲的濾波和提取圖像中部分特征信息的濾波。
去除圖像中的噪聲稱作圖像的平滑或者圖像去噪。由于噪聲信號在圖像中主要集中在高頻段,因此圖像去噪可以看作去除圖像中高頻段信號的同時保留圖像的低頻段和中頻段信號,此時使用的濾波器就是低通或者高阻濾波器。圖像中紋理變化越明顯的區(qū)域信號頻率也就越高,因此使用高通濾波器對圖像信號處理可以起到對圖像邊緣信息提取、增強和圖像銳化的作用。
在部分圖像處理書籍中,常用圖像模糊來替代圖像的低通濾波,因為圖像的低通濾波在去除圖像噪聲的同時會將圖像的邊緣信息弱化,使得整幅圖像看起來變得模糊。在低通濾波中,模糊可以與濾波等價,例如圖像高斯模糊和圖像高斯低通濾波是一個概念。
總之有:
噪聲在高頻 紋理信息在高頻
圖像平滑:去噪 -> 低通高阻濾波器 -> 卷積
圖像銳化:提取紋理 -> 低阻高通濾波器 -> 微分
本文講解使用低通濾波對圖像進(jìn)行去噪。
圖像的濾波分為線性濾波和非線性濾波,常見的線性濾波包括均值濾波、方框濾波、高斯濾波,常見的非線性濾波主要包括中值濾波、雙邊濾波。
2.線性濾波
圖像的線性濾波與圖像的卷積類似,卷積操作中的卷積模版(卷積核)在圖像濾波中稱為濾波模版、濾波器或者領(lǐng)域算子。濾波器表示中心像素與濾波范圍內(nèi)其他像素之間的線性關(guān)系,通過濾波范圍內(nèi)所有像素值之間的線性組合,得到中心位置像素濾波后的像素值,因此這種方式稱為線性濾波。
2.1均值濾波
均值濾波將濾波器內(nèi)所有像素值都看作中心像素的測量,將濾波器內(nèi)所有的像素值的平均值作為濾波器中心處圖像像素值。
均值濾波的優(yōu)點是,在像素值變換趨勢一致的情況下,可以將受噪聲影響而突然變化的像素值修正為周圍鄰近像素值的平均值,去除噪聲影響。但是這種方式會縮小像素值之間的差距,使得細(xì)節(jié)信息變得更加模糊,濾波器范圍越大,變模糊越明顯。
均值濾波對于去除高斯噪聲等均勻分布的噪聲效果較好,但在處理圖像中的細(xì)節(jié)和邊緣時可能會導(dǎo)致模糊。此外,均值濾波不能有效消除椒鹽噪聲等離散噪聲,因為離群值會對平均值產(chǎn)生較大影響。均值濾波是一種簡單但計算量較大的濾波方法,特別是對于較大的滑動窗口。在實際應(yīng)用中,可能會使用更高級的濾波方法來處理圖像噪聲。
OpenCV中提供了blur()
函數(shù)用于實現(xiàn)圖像的均值濾波:
void blur(
InputArray src, // 待均值濾波的圖像
OutputArray dst, // 均值濾波后的圖像
Size ksize, // 濾波器尺寸
Point anchor = Point(-1,-1), // 內(nèi)核基準(zhǔn)點,默認(rèn)為濾波器中心點
int borderType = BORDER_DEFAULT // 像素邊界外推標(biāo)志 在
);
輸入濾波器的尺寸(3x3或5x5)后,函數(shù)會自動確定濾波器:
K
=
1
k
s
i
z
e
.
w
i
d
t
h
×
k
s
i
z
e
.
h
e
i
g
h
t
[
1
1
?
1
1
1
1
?
1
1
?
?
?
?
?
1
1
?
1
1
1
1
?
1
1
]
K = \frac{1}{ksize.width \times ksize.height} \begin{bmatrix} 1 & 1 & \cdots & 1 & 1 \\ 1 & 1 & \cdots & 1 & 1 \\ \vdots & \vdots & \ddots & \vdots & \vdots\\ 1 &1 & \cdots & 1 & 1\\ 1 &1 & \cdots & 1 & 1\\ \end{bmatrix}
K=ksize.width×ksize.height1?
?11?11?11?11???????11?11?11?11?
?
下面例子給出了利用不同尺寸的濾波器分別處理不含有噪聲的圖像、含有椒鹽噪聲的圖像和含有高斯噪聲的圖像。通過結(jié)果可以發(fā)現(xiàn),濾波器的尺寸越大,濾波后圖像變得越模糊:
#include <opencv2/core/utils/logger.hpp>
#include <opencv2/opencv.hpp>
using namespace cv;
using namespace std;
int main()
{
cout << "OpenCV Version: " << CV_VERSION << endl;
utils::logging::setLogLevel(utils::logging::LOG_LEVEL_SILENT);
Mat equalLena = imread("equalLena.png", IMREAD_ANYDEPTH);
Mat equalLena_gauss = imread("equalLena_gauss.png", IMREAD_ANYDEPTH);
Mat equalLena_salt = imread("equalLena_salt.png", IMREAD_ANYDEPTH);
if (equalLena.empty() || equalLena_gauss.empty() || equalLena_salt.empty())
{
cout << "請確認(rèn)圖像文件名稱是否正確" << endl;
return -1;
}
Mat result_3, result_9; //存放不含噪聲濾波結(jié)果,后面數(shù)字代表濾波器尺寸
Mat result_3gauss, result_9gauss; //存放含有高斯噪聲濾波結(jié)果,后面數(shù)字代表濾波器尺寸
Mat result_3salt, result_9salt; //存放含有椒鹽噪聲濾波結(jié)果,后面數(shù)字代表濾波器尺寸
//調(diào)用均值濾波函數(shù)blur()進(jìn)行濾波
blur(equalLena, result_3, Size(3, 3));
blur(equalLena, result_9, Size(9, 9));
blur(equalLena_gauss, result_3gauss, Size(3, 3));
blur(equalLena_gauss, result_9gauss, Size(9, 9));
blur(equalLena_salt, result_3salt, Size(3, 3));
blur(equalLena_salt, result_9salt, Size(9, 9));
//顯示不含噪聲圖像
imshow("equalLena ", equalLena);
imshow("result_3", result_3);
imshow("result_9", result_9);
//顯示含有高斯噪聲圖像
imshow("equalLena_gauss", equalLena_gauss);
imshow("result_3gauss", result_3gauss);
imshow("result_9gauss", result_9gauss);
//顯示含有椒鹽噪聲圖像
imshow("equalLena_salt", equalLena_salt);
imshow("result_3salt", result_3salt);
imshow("result_9salt", result_9salt);
int k = waitKey(0); // Wait for a keystroke in the window
return 0;
}
2.2方框濾波
方框濾波是均值濾波的一般形式。在均值濾波中,將濾波器中所有的像素值求和后的平均值作為濾波后結(jié)果。方框濾波也是求濾波器內(nèi)所有像素值的和,但是方框濾波可以選擇不進(jìn)行歸一化,是將所有像素值的和作為濾波結(jié)果,而不是平均值。
OpenCV中提供了 boxFilter()
函數(shù)實現(xiàn)方框濾波:
void boxFilter(
InputArray src, // 待濾波圖像
OutputArray dst, // 輸出圖像
int ddepth, // 輸出圖像數(shù)據(jù)類型,-1表示自動選擇
Size ksize, // 卷積核尺寸
Point anchor = Point(-1,-1), // 內(nèi)核的基準(zhǔn)點
bool normalize = true, // 是否歸一化標(biāo)志,默認(rèn)進(jìn)行歸一化
int borderType = BORDER_DEFAULT // 像素邊界外推標(biāo)志
);
該函數(shù)使用和 blur()
類似,不過可以選擇輸出圖像的數(shù)據(jù)類型。在不考慮數(shù)據(jù)類型的情況下,方框濾波函數(shù)和均值濾波函數(shù)具有相同的濾波效果。
除對濾波器內(nèi)每個像素值直接求和之外,OpenCV還提供了 sqrtBoxFilter()
函數(shù)實現(xiàn)對濾波器內(nèi)每個像素值的平方求和,之后根據(jù)輸入?yún)?shù)選擇是否進(jìn)行歸一化:
void sqrBoxFilter(
InputArray src, // 參數(shù)同上
OutputArray dst,
int ddepth,
Size ksize,
Point anchor = Point(-1, -1),
bool normalize = true,
int borderType = BORDER_DEFAULT );
CV_8U數(shù)據(jù)類型的圖像像素值為 0-255,計算平方后數(shù)據(jù)會變得更大,即使歸一化操作也不能保證像素值不會超過最大值。CV_32F數(shù)據(jù)類型的圖像像素值是 0-1的小數(shù),平方之后會變得更小,但始終保持在0-1.因此該函數(shù)處理圖像濾波的任務(wù)主要針對CV_32F數(shù)據(jù)類型的圖像,在歸一化后,圖像在變模糊的同時亮度也會變暗。
下面例子給出了利用方框濾波分別處理矩陣數(shù)據(jù)和圖相關(guān)的實例:
#include <opencv2/core/utils/logger.hpp>
#include <opencv2/opencv.hpp>
using namespace cv;
using namespace std;
int main()
{
cout << "OpenCV Version: " << CV_VERSION << endl;
utils::logging::setLogLevel(utils::logging::LOG_LEVEL_SILENT);
Mat equalLena = imread("equalLena.png", IMREAD_ANYDEPTH); //用于方框濾波的圖像
if (equalLena.empty())
{
cout << "請確認(rèn)圖像文件名稱是否正確" << endl;
return -1;
}
//驗證方框濾波算法的數(shù)據(jù)矩陣
float points[25] = {
1,2,3,4,5,
6,7,8,9,10,
11,12,13,14,15,
16,17,18,19,20,
21,22,23,24,25 };
Mat data(5, 5, CV_32FC1, points);
//將CV_8U類型轉(zhuǎn)換成CV_32F類型
Mat equalLena_32F;
equalLena.convertTo(equalLena_32F, CV_32F, 1.0 / 255);
Mat resultNorm, result, dataSqrNorm, dataSqr, equalLena_32FSqr;
//方框濾波boxFilter()和sqrBoxFilter()
boxFilter(equalLena, resultNorm, -1, Size(3, 3), Point(-1, -1), true); //進(jìn)行歸一化
boxFilter(equalLena, result, -1, Size(3, 3), Point(-1, -1), false); //不進(jìn)行歸一化
sqrBoxFilter(data, dataSqrNorm, -1, Size(3, 3), Point(-1, -1),
true, BORDER_CONSTANT); //進(jìn)行歸一化
sqrBoxFilter(data, dataSqr, -1, Size(3, 3), Point(-1, -1),
false, BORDER_CONSTANT); //不進(jìn)行歸一化
sqrBoxFilter(equalLena_32F, equalLena_32FSqr, -1, Size(3, 3), Point(-1, -1),
true, BORDER_CONSTANT);
cout << "data: " << endl << data << endl;
cout << "dataSqrNorm: " << endl << dataSqrNorm << endl;
cout << "dataSqr: " << endl << dataSqr << endl;
//顯示處理結(jié)果
imshow("resultNorm", resultNorm);
imshow("result", result);
imshow("equalLena_32FSqr", equalLena_32FSqr);
int k = waitKey(0); // Wait for a keystroke in the window
return 0;
}
/*
data:
[1, 2, 3, 4, 5;
6, 7, 8, 9, 10;
11, 12, 13, 14, 15;
16, 17, 18, 19, 20;
21, 22, 23, 24, 25]
dataSqrNorm:
[10, 18.11111111111111, 24.77777777777778, 32.77777777777778, 24.66666666666666;
39.44444444444444, 66.33333333333333, 81.33333333333333, 98.33333333333333, 71.44444444444444;
99.44444444444444, 161.3333333333333, 186.3333333333333, 213.3333333333333, 151.4444444444444;
192.7777777777778, 306.3333333333333, 341.3333333333333, 378.3333333333333, 264.7777777777778;
163.3333333333333, 258.1111111111111, 284.7777777777778, 312.7777777777778, 218]
dataSqr:
[90, 163, 223, 295, 222;
355, 597, 732, 885, 643;
895, 1452, 1677, 1920, 1363;
1735, 2757, 3072, 3405, 2383;
1470, 2323, 2563, 2815, 1962]
*/
上面的dataSqr中:計算的597
[1, 2, 3
6, 7, 8
11, 12, 13]
平方和為597
上面的dataSqrNorm中:計算的66.33333333333333
597/9=66.33333333333333
2.3高斯濾波
高斯濾波器考慮了像素離濾波器中心距離的影響,以濾波器中心位置為高斯分布的均值,根據(jù)高斯分布公式和每個像素離中心位置的距離計算出濾波器內(nèi)每個位置的數(shù)值。
OpenCV中提供了 GaussianBlur()
函數(shù)對圖像進(jìn)行高斯濾波:
void GaussianBlur(
InputArray src, // 待高斯濾波圖像
OutputArray dst, // 輸出圖像
Size ksize, // 高斯濾波器的尺寸,可以不為正方形,但必須為正奇數(shù),如果為0那么由標(biāo)準(zhǔn)偏差計算尺寸
double sigmaX, // X方向的高斯濾波器標(biāo)準(zhǔn)偏差
double sigmaY = 0, // Y方向的高斯濾波器標(biāo)準(zhǔn)偏差。如果為0,將其設(shè)置為sigmaX,如果兩個標(biāo)準(zhǔn)偏差都為0,那么根據(jù)輸入的ksize計算標(biāo)準(zhǔn)偏差
int borderType = BORDER_DEFAULT
);
該函數(shù)能夠根據(jù)輸入的參數(shù)自動生成高斯濾波器,實現(xiàn)對圖像的高斯濾波。
高斯濾波器的尺寸和標(biāo)準(zhǔn)偏差存在著一定的互相轉(zhuǎn)換關(guān)系,OpenCV中提供了輸入濾波器單一方向尺寸和標(biāo)準(zhǔn)偏差生成單一方向高斯濾波器的 getGaussianKernel()
函數(shù)。
Mat getGaussianKernel(
int ksize, // 高斯濾波器尺寸
double sigma, // 高斯濾波器的標(biāo)準(zhǔn)差
int ktype = CV_64F
);
該函數(shù)給出了濾波器尺寸和標(biāo)準(zhǔn)偏差存在的關(guān)系,這個關(guān)系不是數(shù)學(xué)中存在的關(guān)系,而是OpenCV為了方便自己設(shè)定的關(guān)系。該函數(shù)用于生成指定尺寸的高斯濾波器,生成的是 ksize x 1
的Mat類矩陣。
標(biāo)準(zhǔn)差如果為負(fù)數(shù),則使用如下公式計算標(biāo)準(zhǔn)差:
s
i
g
m
a
=
0.3
(
(
k
s
i
z
e
?
1
)
0.5
?
1
+
0.8
)
sigma = 0.3((ksize -1)0.5 -1 + 0.8)
sigma=0.3((ksize?1)0.5?1+0.8)
生成一個二維的高斯濾波需要調(diào)用兩次 getGaussianKernel()
函數(shù),將 X 方向的一維高斯濾波器和 Y 方向的以為高斯濾波器相乘得到最終的二維高斯濾波器。
#include <opencv2/core/utils/logger.hpp>
#include <opencv2/opencv.hpp>
using namespace cv;
using namespace std;
int main()
{
cout << "OpenCV Version: " << CV_VERSION << endl;
utils::logging::setLogLevel(utils::logging::LOG_LEVEL_SILENT);
cv::Mat x = cv::getGaussianKernel(3, -1, CV_32FC1);
cv::Mat y = cv::getGaussianKernel(3, -1, CV_32FC1);
x = x.reshape(1).t();
Mat matrix = x.t() * y;
std::cout << "x * y: " << std::endl << matrix << std::endl;
int k = waitKey(0); // Wait for a keystroke in the window
return 0;
}
不知道為什么輸出的不是一個矩陣?
下面的例子中中利用高斯濾波分別處理不含有噪聲的圖像、含有椒鹽噪聲的圖像和含有高斯噪聲的圖像。通過處理結(jié)果發(fā)現(xiàn),高斯濾波對高斯噪聲去除效果較好,但是同樣會對圖像造成模糊,并且濾波器的尺寸越大,濾波后圖像變得越模糊。
#include <opencv2/core/utils/logger.hpp>
#include <opencv2/opencv.hpp>
using namespace cv;
using namespace std;
int main()
{
cout << "OpenCV Version: " << CV_VERSION << endl;
utils::logging::setLogLevel(utils::logging::LOG_LEVEL_SILENT);
Mat equalLena = imread("equalLena.png", IMREAD_ANYDEPTH);
Mat equalLena_gauss = imread("equalLena_gauss.png", IMREAD_ANYDEPTH);
Mat equalLena_salt = imread("equalLena_salt.png", IMREAD_ANYDEPTH);
if (equalLena.empty() || equalLena_gauss.empty() || equalLena_salt.empty())
{
cout << "請確認(rèn)圖像文件名稱是否正確" << endl;
return -1;
}
Mat result_5, result_9; //存放不含噪聲濾波結(jié)果,后面數(shù)字代表濾波器尺寸
Mat result_5gauss, result_9gauss; //存放含有高斯噪聲濾波結(jié)果,后面數(shù)字代表濾波器尺寸
Mat result_5salt, result_9salt; 存放含有椒鹽噪聲濾波結(jié)果,后面數(shù)字代表濾波器尺寸
//調(diào)用均值濾波函數(shù)blur()進(jìn)行濾波
GaussianBlur(equalLena, result_5, Size(5, 5), 10, 20);
GaussianBlur(equalLena, result_9, Size(9, 9), 10, 20);
GaussianBlur(equalLena_gauss, result_5gauss, Size(5, 5), 10, 20);
GaussianBlur(equalLena_gauss, result_9gauss, Size(9, 9), 10, 20);
GaussianBlur(equalLena_salt, result_5salt, Size(5, 5), 10, 20);
GaussianBlur(equalLena_salt, result_9salt, Size(9, 9), 10, 20);
//顯示不含噪聲圖像
imshow("equalLena ", equalLena);
imshow("result_5", result_5);
imshow("result_9", result_9);
//顯示含有高斯噪聲圖像
imshow("equalLena_gauss", equalLena_gauss);
imshow("result_5gauss", result_5gauss);
imshow("result_9gauss", result_9gauss);
//顯示含有椒鹽噪聲圖像
imshow("equalLena_salt", equalLena_salt);
imshow("result_5salt", result_5salt);
imshow("result_9salt", result_9salt);
int k = waitKey(0); // Wait for a keystroke in the window
return 0;
}
2.4可分離濾波
前面介紹的濾波函數(shù)使用的濾波器都是固定形式的濾波器,有時需要根據(jù)實際需求調(diào)整濾波模版。例如,在濾波計算過程中,濾波器中心位置的像素值不參與計算、濾波器中參與計算的像素值不是一個矩形區(qū)域等。OpenCV無法根據(jù)每種需要求單獨編寫濾波函數(shù),因此提供了根據(jù)自定義濾波器實現(xiàn)圖像濾波的函數(shù),即 圖像卷積 一文中介紹的卷積函數(shù) filter2D()
,其實應(yīng)該叫濾波函數(shù)更為準(zhǔn)確。
無論是圖像卷積還是濾波,在原始圖像上移動濾波器的過程中每一次的計算結(jié)果都不會影響到后面過程的計算結(jié)果,因此圖像濾波是一個并行算法,在可以提供并行計算的處理器中可以極大地加快圖像濾波的處理速度。
此外,圖像濾波還具有可分離性,可分離性指的是先對 X(Y) 方向濾波,再對Y(X)方向濾波的結(jié)果與將兩個方向的濾波器聯(lián)合后整體濾波的結(jié)果相同。兩個方向的的濾波器的聯(lián)合就是將兩個方向的濾波器相乘得到一個矩形的濾波器,如 X 方向濾波器為 x = [ x 1 , x 2 , x 3 ] x=[x_1,x_2,x_3] x=[x1?,x2?,x3?] ,Y方向的濾波器為 y = [ y 1 , y 2 , y 3 ] T y = [y_1,y_2,y_3]^T y=[y1?,y2?,y3?]T ,則兩個方向的聯(lián)合濾波器為:
x y = [ y 1 y 2 y 3 ] [ x 1 x 2 x 3 ] = [ x 1 y 1 x 2 y 1 x 3 y 1 x 1 y 2 x 2 y 2 x 3 y 2 x 1 y 3 x 2 y 3 x 3 y 3 ] xy = \begin{bmatrix} y_1 \\y_2 \\y_3 \\ \end{bmatrix} \begin{bmatrix}x_1 & x_2 & x_3 \\\end{bmatrix} =\begin{bmatrix} x_1y_1 & x_2y_1 & x_3y_1 \\ x_1y_2 & x_2y_2 & x_3y_2 \\ x_1y_3 & x_2y_3 & x_3y_3 \\ \end{bmatrix} xy= ?y1?y2?y3?? ?[x1??x2??x3??]= ?x1?y1?x1?y2?x1?y3??x2?y1?x2?y2?x2?y3??x3?y1?x3?y2?x3?y3?? ?
因此,在高斯濾波中,可以利用 getGaussianKernel
函數(shù)分別得到 X 方向和 Y 方向濾波器,之后通過生成聯(lián)合濾波器或者分別用兩個方向的濾波器進(jìn)行濾波,計算結(jié)果相同。
兩個方向的濾波器可以使用 filter2D
計算濾波結(jié)果。OpenCV提供了可以輸入兩個方向的濾波器實現(xiàn)濾波:
void sepFilter2D(
InputArray src, // 帶濾波圖像
OutputArray dst, // 輸出圖像
int ddepth, // 輸出圖像數(shù)據(jù)類型
InputArray kernelX, // X方向濾波器
InputArray kernelY, // Y方向濾波器
Point anchor = Point(-1,-1), // 內(nèi)核基準(zhǔn)點
double delta = 0, // 偏值
int borderType = BORDER_DEFAULT // 像素邊界外推標(biāo)志
);
該函數(shù)將可分離的線性濾波器分離成 X 方向和 Y 方向進(jìn)行處理,與 filter2D
函數(shù)不同之處在于,filter2D
函數(shù)需要通過濾波器的尺寸區(qū)分濾波操作是作用在 X 方向還是 Y 方向,例如濾波器尺寸為
K
×
1
K \times 1
K×1 時是Y方向濾波,
1
×
K
1 \times K
1×K 尺寸的濾波器是X方向濾波。而 sepFilter2D
函數(shù)通過不同參數(shù)區(qū)分是作用在 X 方向還是作用在 Y 方向。
下面例子中給出了利用 filter2D
函數(shù)依次進(jìn)行 Y 方向和 X 方向濾波,將結(jié)果與兩個方向聯(lián)合濾波器濾波結(jié)果相比較,驗證兩種方式計算結(jié)果的一致性。同時,將兩個方向的濾波器輸入 sepFilter2D
函數(shù)匯總,驗證該函數(shù)計算結(jié)果是否與前面的計算結(jié)果一致。
#include <opencv2/core/utils/logger.hpp>
#include <opencv2/opencv.hpp>
using namespace cv;
using namespace std;
int main()
{
cout << "OpenCV Version: " << CV_VERSION << endl;
utils::logging::setLogLevel(utils::logging::LOG_LEVEL_SILENT);
float points[25] = {
1,2,3,4,5,
6,7,8,9,10,
11,12,13,14,15,
16,17,18,19,20,
21,22,23,24,25 };
Mat data(5, 5, CV_32FC1, points);
//X方向、Y方向和聯(lián)合濾波器的構(gòu)建
Mat a = (Mat_<float>(3, 1) << -1, 3, -1);
Mat b = a.reshape(1, 1);
Mat ab = a * b;
//驗證高斯濾波的可分離性
Mat gaussX = getGaussianKernel(3, 1);
Mat gaussData, gaussDataXY;
GaussianBlur(data, gaussData, Size(3, 3), 1, 1, BORDER_CONSTANT);
sepFilter2D(data, gaussDataXY, -1, gaussX, gaussX, Point(-1, -1), 0, BORDER_CONSTANT);
//輸入兩種高斯濾波的計算結(jié)果
cout << "gaussData=" << endl
<< gaussData << endl;
cout << "gaussDataXY=" << endl
<< gaussDataXY << endl;
//線性濾波的可分離性
Mat dataYX, dataY, dataXY, dataXY_sep;
filter2D(data, dataY, -1, a, Point(-1, -1), 0, BORDER_CONSTANT);
filter2D(dataY, dataYX, -1, b, Point(-1, -1), 0, BORDER_CONSTANT);
filter2D(data, dataXY, -1, ab, Point(-1, -1), 0, BORDER_CONSTANT);
sepFilter2D(data, dataXY_sep, -1, b, b, Point(-1, -1), 0, BORDER_CONSTANT);
//輸出分離濾波和聯(lián)合濾波的計算結(jié)果
cout << "dataY=" << endl
<< dataY << endl;
cout << "dataYX=" << endl
<< dataYX << endl;
cout << "dataXY=" << endl
<< dataXY << endl;
cout << "dataXY_sep=" << endl
<< dataXY_sep << endl;
//對圖像的分離操作
Mat img = imread("lena.png");
if (img.empty())
{
cout << "請確認(rèn)圖像文件名稱是否正確" << endl;
return -1;
}
Mat imgYX, imgY, imgXY;
filter2D(img, imgY, -1, a, Point(-1, -1), 0, BORDER_CONSTANT);
filter2D(imgY, imgYX, -1, b, Point(-1, -1), 0, BORDER_CONSTANT);
filter2D(img, imgXY, -1, ab, Point(-1, -1), 0, BORDER_CONSTANT);
imshow("img", img);
imshow("imgY", imgY);
imshow("imgYX", imgYX);
imshow("imgXY", imgXY);
int k = waitKey(0); // Wait for a keystroke in the window
return 0;
}
輸出結(jié)果:
gaussData=
[1.7207065, 2.822206, 3.5481372, 4.2740688, 3.430702;
4.6296568, 7, 8, 9, 6.9852457;
8.2593136, 12, 13, 14, 10.614902;
11.888971, 17, 18, 19, 14.244559;
10.270683, 14.600147, 15.326078, 16.05201, 11.98068]
gaussDataXY=
[1.7207065, 2.822206, 3.5481372, 4.2740688, 3.430702;
4.6296568, 7, 8, 9, 6.9852457;
8.2593136, 12, 13, 14, 10.614902;
11.888971, 17, 18, 19, 14.244559;
10.270683, 14.600147, 15.326078, 16.05201, 11.98068]
dataY=
[-3, -1, 1, 3, 5;
6, 7, 8, 9, 10;
11, 12, 13, 14, 15;
16, 17, 18, 19, 20;
47, 49, 51, 53, 55]
dataYX=
[-8, -1, 1, 3, 12;
11, 7, 8, 9, 21;
21, 12, 13, 14, 31;
31, 17, 18, 19, 41;
92, 49, 51, 53, 112]
dataXY=
[-8, -1, 1, 3, 12;
11, 7, 8, 9, 21;
21, 12, 13, 14, 31;
31, 17, 18, 19, 41;
92, 49, 51, 53, 112]
dataXY_sep=
[-8, -1, 1, 3, 12;
11, 7, 8, 9, 21;
21, 12, 13, 14, 31;
31, 17, 18, 19, 41;
92, 49, 51, 53, 112]
3.非線性濾波
非線性濾波的濾波結(jié)果不是由濾波器內(nèi)的像素值通過線性組合計算得到,其計算過程可能包含排序、邏輯計算等。
由于線性濾波是通過對所有像素值的線性組合得到濾波后的結(jié)果,因此含有噪聲的像素點也會被考慮進(jìn)去,噪聲不會被消除,而是以更柔和的形式存在。
常見的非線性濾波有中值濾波和雙邊濾波。
3.1中值濾波
中值濾波就是用濾波器范圍內(nèi)所有像素值的中值來替代濾波器中心位置像素值的濾波方法,是一種基于排序統(tǒng)計理論的能夠有效抑制噪聲的非線性信號處理方法。
相比于均值濾波,中值濾波對于脈沖干擾信號和圖像掃描噪聲的處理效果更佳,同時,在一定條件下,中值濾波對圖像的邊緣信息保護(hù)效果更佳,可以避免圖像細(xì)節(jié)的模糊,但是,當(dāng)中值濾波尺寸變大之后,同樣會產(chǎn)生圖像模糊的效果。在處理時間上,中值濾波消耗的時間要遠(yuǎn)大于均值濾波消耗的時間。
OpenCV提供了 medianBlur
函數(shù)對圖像進(jìn)行中值濾波:
void medianBlur(
InputArray src, // 原圖
OutputArray dst, // 中值濾波后圖像
int ksize // 濾波器尺寸,奇數(shù)
);
下面代碼對含有椒鹽噪聲的灰度圖像和含有椒鹽噪聲的彩色圖像進(jìn)行中值濾波:
#include <opencv2/core/utils/logger.hpp>
#include <opencv2/opencv.hpp>
using namespace cv;
using namespace std;
int main()
{
cout << "OpenCV Version: " << CV_VERSION << endl;
utils::logging::setLogLevel(utils::logging::LOG_LEVEL_SILENT);
Mat gray = imread("equalLena_salt.png", IMREAD_ANYCOLOR);
Mat img = imread("lena_salt.png", IMREAD_ANYCOLOR);
if (gray.empty() || img.empty())
{
cout << "請確認(rèn)圖像文件名稱是否正確" << endl;
return -1;
}
Mat imgResult3, grayResult3, imgResult9, grayResult9;
//分別對含有椒鹽噪聲的彩色和灰度圖像進(jìn)行濾波,濾波模板為3×3
medianBlur(img, imgResult3, 3);
medianBlur(gray, grayResult3, 3);
//加大濾波模板,圖像濾波結(jié)果會變模糊
medianBlur(img, imgResult9, 9);
medianBlur(gray, grayResult9, 9);
//顯示濾波處理結(jié)果
imshow("img", img);
imshow("gray", gray);
imshow("imgResult3", imgResult3);
imshow("grayResult3", grayResult3);
imshow("imgResult9", imgResult9);
imshow("grayResult9", grayResult9);
int k = waitKey(0); // Wait for a keystroke in the window
return 0;
}
3.2雙邊濾波
前面介紹的濾波方法都會對圖像造成模糊,使得邊緣信息變?nèi)趸蛘呦?,因此需要一種能夠?qū)D像邊緣信息進(jìn)行保留的濾波算法。
雙邊濾波是一種綜合考慮濾波器內(nèi)圖像空域信息和濾波器內(nèi)圖像像素灰度值相似性的濾波算法,可以實現(xiàn)在保留區(qū)域信息的基礎(chǔ)上實現(xiàn)對噪聲的去除、對局部邊緣的平滑。雙邊濾波對高頻率的波動信號起到平滑作用,同時保留大幅值變化的信號波動,進(jìn)而實現(xiàn)對保留圖像中邊緣信息的作用。
雙邊濾波器是兩個濾波器的結(jié)合,分別考慮空域信息和值域信息,使得濾波器對邊緣附近的像素進(jìn)行濾波時,距離邊緣較遠(yuǎn)的像素值不會對邊緣上的像素值影響太多,進(jìn)而保留邊緣的清晰性。
雙邊濾波原理的數(shù)學(xué)表達(dá)式:
g
(
i
,
j
)
=
∑
k
,
l
f
(
k
,
l
)
ω
(
i
,
j
,
k
,
l
)
∑
k
,
l
ω
(
i
,
j
,
k
,
l
)
g(i,j) = \frac{\sum_{k,l}f(k,l) \omega(i,j,k,l)}{\sum_{k,l}\omega(i,j,k,l)}
g(i,j)=∑k,l?ω(i,j,k,l)∑k,l?f(k,l)ω(i,j,k,l)?
其中
ω
(
i
,
j
,
k
,
l
)
\omega(i,j,k,l)
ω(i,j,k,l) 為加權(quán)系數(shù),其值取決于空域濾波器和值域濾波器的乘積。
空域濾波器表示如下:
d
(
i
,
j
,
k
,
l
)
=
e
x
p
(
?
(
i
?
k
)
2
+
(
j
?
l
)
2
2
σ
d
2
)
d(i,j,k,l) = exp({-\frac{(i-k)^2 + (j-l)^2}{2 \sigma_d^2}})
d(i,j,k,l)=exp(?2σd2?(i?k)2+(j?l)2?)
值域濾波器表示如下:
r
(
i
,
j
,
k
,
l
)
=
e
x
p
(
?
∣
∣
f
(
i
,
j
)
?
f
(
k
,
l
)
∣
∣
2
2
σ
d
2
)
r(i,j,k,l) = exp(- \frac{||f(i,j) - f(k,l)||^2}{2 \sigma_d^2})
r(i,j,k,l)=exp(?2σd2?∣∣f(i,j)?f(k,l)∣∣2?)
兩者相乘后產(chǎn)生如下依賴于數(shù)據(jù)的雙邊濾波器:
ω
(
i
,
j
,
k
,
l
)
=
e
x
p
(
?
(
i
?
k
)
2
+
(
j
?
l
)
2
2
σ
d
2
?
∣
∣
f
(
i
,
j
)
?
f
(
k
,
l
)
∣
∣
2
2
σ
d
2
)
\omega(i,j,k,l) = exp({-\frac{(i-k)^2 + (j-l)^2}{2 \sigma_d^2} - \frac{||f(i,j) - f(k,l)||^2}{2 \sigma_d^2}})
ω(i,j,k,l)=exp(?2σd2?(i?k)2+(j?l)2??2σd2?∣∣f(i,j)?f(k,l)∣∣2?)
OpenCV提供了 bilateralFilter()
函數(shù)對圖像進(jìn)行雙邊濾波:
void bilateralFilter(
InputArray src, // 單通道或者三通道圖像
OutputArray dst, // 雙邊濾波后的輸出圖像
int d, // 濾波過程中每個像素鄰域的直徑。如果為非正數(shù),則有第五個參數(shù)計算得到
double sigmaColor, // 顏色空間濾波器的標(biāo)準(zhǔn)差值。這個參數(shù)越大,表明該像素鄰域內(nèi)有越多的顏色被混合在一起,產(chǎn)生較大的半相等顏色區(qū)域。
double sigmaSpace, // 空間坐標(biāo)中濾波器的標(biāo)準(zhǔn)差值。這個參數(shù)越大,表明越遠(yuǎn)的像素會互相影響,從而使更大領(lǐng)域內(nèi)有足夠相似的顏色獲取相同的顏色。當(dāng)d大于0時,領(lǐng)域范圍由d確定,當(dāng)d小于等于0時,領(lǐng)域范圍由正比于這個參數(shù)的數(shù)值
int borderType = BORDER_DEFAULT // 像素邊界外推標(biāo)志
);
當(dāng)濾波器直徑大于5時,函數(shù)的運行速度會變慢,實時系統(tǒng)可以設(shè)為5,離線處理可以設(shè)為9。第4、5兩個參數(shù),可以將他們設(shè)置成相同的值,當(dāng)他們小于10時,濾波器對圖像的濾波作用較弱,當(dāng)他們大于150時,濾波效果會非常強烈,使圖像看起來具有卡通效果。該函數(shù)的缺點是運行時間比其他濾波方法要長。
下面給出了使用雙邊濾波對人臉圖像進(jìn)行濾波:文章來源:http://www.zghlxwxcb.cn/news/detail-717432.html
#include <opencv2/core/utils/logger.hpp>
#include <opencv2/opencv.hpp>
using namespace cv;
using namespace std;
int main()
{
cout << "OpenCV Version: " << CV_VERSION << endl;
utils::logging::setLogLevel(utils::logging::LOG_LEVEL_SILENT);
//讀取兩張含有人臉的圖像
Mat img1 = imread("img1.png", IMREAD_ANYCOLOR);
Mat img2 = imread("img2.png", IMREAD_ANYCOLOR);
if (img1.empty() || img2.empty())
{
cout << "請確認(rèn)圖像文件名稱是否正確" << endl;
return -1;
}
Mat result1, result2, result3, result4;
//驗證不同濾波器直徑的濾波效果
bilateralFilter(img1, result1, 9, 50, 25 / 2);
bilateralFilter(img1, result2, 25, 50, 25 / 2);
//驗證不同標(biāo)準(zhǔn)差值的濾波效果
bilateralFilter(img2, result3, 9, 9, 9);
bilateralFilter(img2, result4, 9, 200, 200);
//顯示原圖
imshow("img1", img1);
imshow("img2", img2);
//不同直徑濾波結(jié)果
imshow("result1", result1);
imshow("result2", result2);
//不同標(biāo)準(zhǔn)差值濾波結(jié)果
imshow("result3 ", result3);
imshow("result4", result4);
int k = waitKey(0); // Wait for a keystroke in the window
return 0;
}
從結(jié)果可以看出,濾波直徑越大,濾波效果越明顯,當(dāng)濾波直徑相同時,標(biāo)準(zhǔn)差值越大,濾波效果越明顯。能對人臉起到美顏效果。文章來源地址http://www.zghlxwxcb.cn/news/detail-717432.html
到了這里,關(guān)于OpenCV14-圖像平滑:線性濾波和非線性濾波的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!