此部分實驗包含:
1、算術均值濾波去噪算法
2、幾何均值濾波去噪算法
3、諧波均值濾波去噪算法
4、反諧波均值濾波去噪算法
5、中值濾波去噪算法
6、自適應中值濾波去噪算法
7、自適應局部降低噪聲濾波器去噪算法
當一副圖片中唯一存在的退化是噪聲時,就有
和
這兩個公式,其中
是噪聲項,且是未知的。所以從
中減去噪聲項是不可能的。這里我們只討論存在加性噪聲,我們可以使用空間濾波的方法來去噪。
1、算術均值濾波去噪算法
這是一種簡單的均值濾波器,令
是中心點在
處,大小為
的矩陣子圖像的一組坐標。算術均值就是計算
定義區域中被污染圖像
的均值,即是

這個操作可以使用
的空間濾波器實現,濾波器的所有的系數都為其值的
,這種方式可以模糊結果,但同時也降低噪聲。
1.1、 算術均值濾波編碼實現
void arithmeticMeanBlur(Mat& src, Mat& dst, int m, int n)
{
double weight = 1.0 / ((double)m * (double)n);
//根據m*n 大小的核填充處理圖像
//填充邊緣大小
int row = src.rows;
int col = src.cols;
int rowPadding = getBordValue(row, 1, n);
int colPadding = getBordValue(col, 1, m);
Mat t_src;
copyMakeBorder(src, t_src, rowPadding, rowPadding, colPadding, colPadding, BORDER_DEFAULT);
//進行算術均值濾波
int channels = src.channels();
int cols = t_src.cols - rowPadding;
int rows = t_src.rows - colPadding;
for (int i = rowPadding; i < rows; i++) {
for (int j = colPadding; j < cols; j++) {
double sum[3] = { 0 };
for (int k = -rowPadding; k <= rowPadding; k++) {
for (int m = -colPadding; m <= colPadding; m++) {
if (channels == 1) {
sum[0] += weight * t_src.at<uchar>(i + k, j + m);
}
else if (channels == 3) {
Vec3b rgb = t_src.at<Vec3b>(i + k, j + m);
sum[0] += weight* rgb[0];
sum[1] += weight * rgb[1];
sum[2] += weight * rgb[2];
}
}
}
//限定像素值在0-255之間
for (int i = 0; i < channels; i++) {
if (sum[i] < 0)
sum[i] = 0;
else if (sum[i] > 255)
sum[i] = 255;
}
//
if (channels == 1) {
dst.at<uchar>(i - rowPadding, j - colPadding) = static_cast<uchar>(sum[0]);
}
else if (channels == 3) {
Vec3b rgb;
rgb[0] = static_cast<uchar>(sum[0]);
rgb[1] = static_cast<uchar>(sum[1]);
rgb[2] = static_cast<uchar>(sum[2]);
dst.at<Vec3b>(i - rowPadding, j - colPadding) = rgb;
}
}
}
}
void comparedMeanBlur(Mat& sapSrc, Mat& sSrc, Mat& pSrc, Mat& gSrc)
{
//5*5 的算術均值濾波處理椒鹽噪聲
Mat dst1 = sapSrc.clone();
arithmeticMeanBlur(sapSrc, dst1, 5, 5);
imshow("5*5算術均值處理椒鹽噪聲結果", dst1);
//5*5 的算術均值濾波處理椒噪聲
Mat dst2 = pSrc.clone();
arithmeticMeanBlur(pSrc, dst2, 5, 5);
imshow("5*5算術均值處理椒噪聲結果", dst2);
//5*5 的算術均值濾波處理鹽噪聲
Mat dst3 = sSrc.clone();
arithmeticMeanBlur(sSrc, dst3, 5, 5);
imshow("5*5算術均值處理鹽噪聲結果", dst3);
//5*5 的算術均值濾波處理高斯噪聲
Mat dst4 = gSrc.clone();
arithmeticMeanBlur(gSrc, dst4, 5, 5);
imshow("5*5算術均值處理高斯噪聲結果", dst4);
}
3.1.2 算術均值濾波實驗效果
原圖

添加噪聲的圖片,這里椒鹽噪聲、胡椒噪聲、鹽粒噪聲比例為0.1, 高斯噪聲 均值為15 方差為25。

5*5 算術均值濾波的結果

2 幾何均值濾波器
使用幾何均值濾波器復原的一副圖片的表達式:
其中,每個復原的像素由窗口中像素的乘積的
次冪給出。
2.1 幾何均值濾波編碼實現
void geometricMean(Mat& src, Mat& dst, int m, int n)
{
double weight = 1.0 / ((double)m * (double)n);
//根據m*n 大小的核填充處理圖像
//填充邊緣大小
int row = src.rows;
int col = src.cols;
int rowPadding = getBordValue(row, 1, n);
int colPadding = getBordValue(col, 1, m);
//這里不要直接填充src,因為這里是引用,可能后面的實驗會重復填充
Mat t_src;
copyMakeBorder(src, t_src, rowPadding, rowPadding, colPadding, colPadding, BORDER_DEFAULT);
int channels = src.channels();
int cols = t_src.cols - rowPadding;
int rows = t_src.rows - colPadding;
for (int i = rowPadding; i < rows; i++) {
for (int j = colPadding; j < cols; j++) {
double sum[3] = { 1,1,1 };
for (int k = -rowPadding; k <= rowPadding; k++) {
for (int m = -colPadding; m <= colPadding; m++) {
if (channels == 1) {
sum[0] *= (double)t_src.at<uchar>(i + k, j + m);
}
else if (channels == 3) {
Vec3b rgb = t_src.at<Vec3b>(i + k, j + m);
sum[0] *= rgb[0];
sum[1] *= rgb[1];
sum[2] *= rgb[2];
}
}
}
//乘以1/mn次冪
for (int i = 0; i < channels; i++) {
sum[i] = pow(sum[i], weight);
}
//限定像素值在0-255之間
for (int i = 0; i < channels; i++) {
if (sum[i] < 0)
sum[i] = 0;
else if (sum[i] > 255)
sum[i] = 255;
}
//
if (channels == 1) {
dst.at<uchar>(i - rowPadding, j - colPadding) = static_cast<uchar>(sum[0]);
}
else if (channels == 3) {
Vec3b rgb;
rgb[0] = static_cast<uchar>(sum[0]);
rgb[1] = static_cast<uchar>(sum[1]);
rgb[2] = static_cast<uchar>(sum[2]);
dst.at<Vec3b>(i - rowPadding, j - colPadding) = rgb;
}
}
}
}
void comparedGeometricMeanBlur(Mat& sapSrc, Mat& sSrc, Mat& pSrc, Mat& gSrc)
{
//5*5 的幾何均值濾波處理椒鹽噪聲
Mat dst1 = sapSrc.clone();
geometricMean(sapSrc, dst1, 5, 5);
imshow("5*5幾何均值處理椒鹽噪聲結果", dst1);
//5*5 的幾何均值濾波處理椒噪聲
Mat dst2 = pSrc.clone();
geometricMean(pSrc, dst2, 5, 5);
imshow("5*5幾何均值處理椒噪聲結果", dst2);
//5*5 的幾何均值濾波處理鹽噪聲
Mat dst3 = sSrc.clone();
geometricMean(sSrc, dst3, 5, 5);
imshow("5*5幾何均值處理鹽噪聲結果", dst3);
//5*5 的幾何均值濾波處理高斯噪聲
Mat dst4 = gSrc.clone();
geometricMean(gSrc, dst4, 5, 5);
imshow("5*5幾何均值處理高斯噪聲結果", dst4);
}
2.2 幾何均值處理結果
原圖

添加噪聲的圖片,這里椒鹽噪聲、胡椒噪聲、鹽粒噪聲比例為0.1, 高斯噪聲 均值為15 方差為25。

5*5 幾何均值處理圖片結果

- 結果分析:由於這種方式處理圖片可以保留細節信息,所以對胡椒,鹽粒噪聲更加敏感,所有處理結果中前兩個效果並不是很好。其次這種方式處理計算量大。
3諧波均值濾波
諧波均值濾波操作給出如下的表達式:
諧波均值對鹽粒噪聲,高斯噪聲處理的效果比較好,但是不善於處理胡椒噪聲。
3.1 諧波均值濾波編碼實現
/*
* 諧波均值
* 諧波均值 適合處理鹽噪聲,不適合處理胡椒噪聲,也擅長處理高斯噪聲
*/
void harmonicMeanBlur(Mat& src, Mat& dst, int m, int n)
{
double weight = ((double)m * (double)n);
//根據m*n 大小的核填充處理圖像
//填充邊緣大小
int row = src.rows;
int col = src.cols;
int rowPadding = getBordValue(row, 1, n);
int colPadding = getBordValue(col, 1, m);
//這里不要直接填充src,因為這里是引用,可能后面的實驗會重復填充
Mat t_src;
copyMakeBorder(src, t_src, rowPadding, rowPadding, colPadding, colPadding, BORDER_DEFAULT);
int channels = src.channels();
int cols = t_src.cols - rowPadding;
int rows = t_src.rows - colPadding;
for (int i = rowPadding; i < rows; i++) {
for (int j = colPadding; j < cols; j++) {
double sum[3] = { 0,0,0 };
for (int k = -rowPadding; k <= rowPadding; k++) {
for (int m = -colPadding; m <= colPadding; m++) {
if (channels == 1) {
sum[0] += 1.0/(double)t_src.at<uchar>(i + k, j + m);
}
else if (channels == 3) {
Vec3b rgb = t_src.at<Vec3b>(i + k, j + m);
sum[0] += 1.0/rgb[0];
sum[1] += 1.0/rgb[1];
sum[2] += 1.0/rgb[2];
}
}
}
//mn/sum[]
for (int i = 0; i < channels; i++) {
sum[i] = weight/sum[i];
}
//限定像素值在0-255之間
for (int i = 0; i < channels; i++) {
if (sum[i] < 0)
sum[i] = 0;
else if (sum[i] > 255)
sum[i] = 255;
}
//
if (channels == 1) {
dst.at<uchar>(i - rowPadding, j - colPadding) = static_cast<uchar>(sum[0]);
}
else if (channels == 3) {
Vec3b rgb;
rgb[0] = static_cast<uchar>(sum[0]);
rgb[1] = static_cast<uchar>(sum[1]);
rgb[2] = static_cast<uchar>(sum[2]);
dst.at<Vec3b>(i - rowPadding, j - colPadding) = rgb;
}
}
}
}
void comparedHarmonicMeanBlur(Mat& sapSrc, Mat& sSrc, Mat& pSrc, Mat& gSrc)
{
//5*5 的諧波均值濾波處理椒鹽噪聲
Mat dst1 = sapSrc.clone();
harmonicMeanBlur(sapSrc, dst1, 5, 5);
imshow("5*5諧波均值處理椒鹽噪聲結果", dst1);
//5*5 的幾何均值濾波處理椒噪聲
Mat dst2 = pSrc.clone();
harmonicMeanBlur(pSrc, dst2, 5, 5);
imshow("5*5諧波均值處理椒噪聲結果", dst2);
//5*5 的算術均值濾波處理鹽噪聲
Mat dst3 = sSrc.clone();
harmonicMeanBlur(sSrc, dst3, 5, 5);
imshow("5*5諧波均值處理鹽噪聲結果", dst3);
//5*5 的算術均值濾波處理高斯噪聲
Mat dst4 = gSrc.clone();
harmonicMeanBlur(gSrc, dst4, 5, 5);
imshow("5*5諧波均值處理高斯噪聲結果", dst4);
}
3.2 諧波均值濾波實驗效果
原圖

添加噪聲的圖片,這里椒鹽噪聲、胡椒噪聲、鹽粒噪聲比例為0.1, 高斯噪聲 均值為15 方差為25。

5*5諧波均值處理效果

結果分析:我們可以明顯看出諧波均值在處理鹽粒噪聲和高斯噪聲上有很好的效果,但是處理椒鹽噪聲,胡椒噪聲的效果很差。
4、逆諧波均值濾波器
逆諧波均值濾波器基於如下產生一副復原的圖像:
其中Q為濾波器的階數,這種濾波器適合在實際中處理椒鹽噪聲。當Q為正的時候可以處理胡椒噪聲,當Q為負的時候,可以消除鹽粒噪聲,當Q=0時,就是算術均值濾波器。注意逆諧波均值濾波器無法同時消除胡椒和鹽粒噪聲。
4.1 逆諧波均值濾波器代碼實現
void inverseHarmonicMeanBlure(Mat& src, Mat& dst, int m, int n, double Q)
{
double weight = ((double)m * (double)n);
//根據m*n 大小的核填充處理圖像
//填充邊緣大小
int row = src.rows;
int col = src.cols;
int rowPadding = getBordValue(row, 1, n);
int colPadding = getBordValue(col, 1, m);
//這里不要直接填充src,因為這里是引用,可能后面的實驗會重復填充
Mat t_src;
copyMakeBorder(src, t_src, rowPadding, rowPadding, colPadding, colPadding, BORDER_DEFAULT);
int channels = src.channels();
int cols = t_src.cols - rowPadding;
int rows = t_src.rows - colPadding;
for (int i = rowPadding; i < rows; i++) {
for (int j = colPadding; j < cols; j++) {
double sum[3] = { 0,0,0 };
double sum1[3] = { 0,0,0 };
for (int k = -rowPadding; k <= rowPadding; k++) {
for (int m = -colPadding; m <= colPadding; m++) {
if (channels == 1) {
sum[0] += pow( (double)t_src.at<uchar>(i + k, j + m),Q+1);
sum1[0] += pow((double)t_src.at<uchar>(i + k, j + m), Q);
}
else if (channels == 3) {
Vec3b rgb = t_src.at<Vec3b>(i + k, j + m);
sum[0] += pow(rgb[0],Q+1);
sum[1] += pow(rgb[1], Q + 1);
sum[2] += pow(rgb[2], Q + 1);
sum1[0] += pow(rgb[0], Q);
sum1[1] += pow(rgb[1], Q);
sum1[2] += pow(rgb[2], Q);
}
}
}
double result[3] = { 0,0,0 };
//限定像素值在0-255之間
for (int t = 0; t < channels; t++) {
result[t] = (double)sum[t] / sum1[t];
if (result[t] < 0)
result[t] = 0;
else if (result[t] > 255)
result[t] = 255;
}
//
if (channels == 1) {
dst.at<uchar>(i - rowPadding, j - colPadding) = static_cast<uchar>(result[0]);
}
else if (channels == 3) {
Vec3b rgb;
rgb[0] = static_cast<uchar>(result[0]);
rgb[1] = static_cast<uchar>(result[1]);
rgb[2] = static_cast<uchar>(result[2]);
dst.at<Vec3b>(i - rowPadding, j - colPadding) = rgb;
}
}
}
}
void comparedInverseHarmonicMeanBlur(Mat& sapSrc, Mat& sSrc, Mat& pSrc, Mat& gSrc, double Q)
{
//5*5 的反諧波均值濾波處理椒鹽噪聲
Mat dst1 = sapSrc.clone();
inverseHarmonicMeanBlure(sapSrc, dst1, 5, 5, Q);
imshow("5*5反諧波均值處理椒鹽噪聲結果", dst1);
//5*5 的反諧波均值濾波處理椒噪聲
Mat dst2 = pSrc.clone();
inverseHarmonicMeanBlure(pSrc, dst2, 5, 5, Q);
imshow("5*5反諧波均值處理椒噪聲結果", dst2);
//5*5 的反諧波均值濾波處理鹽噪聲
Mat dst3 = pSrc.clone();
inverseHarmonicMeanBlure(sSrc, dst3, 5, 5, Q);
imshow("5*5反諧波均值處理鹽粒噪聲結果", dst3);
//5*5 的反諧波濾波處理高斯噪聲
Mat dst4 = gSrc.clone();
inverseHarmonicMeanBlure(gSrc, dst3, 5, 5, Q);
imshow("5*5反諧波均值處理高斯噪聲結果", dst4);
}
4.2 逆諧波均值濾波實驗效果
原圖

添加噪聲的圖片,這里椒鹽噪聲、胡椒噪聲、鹽粒噪聲比例為0.1, 高斯噪聲 均值為15 方差為25。

當 Q = 1時,對胡椒噪聲處理效果明顯。

當Q = -1時,對鹽粒噪聲處理效果明顯。

當Q = 0時,這時候相當於算術均值濾波。

5 中值濾波器
中值濾波是基於統計排序的濾波器,它使用一個像素的領域的中值來代替該點的像素值,即
這種方式可以有效處理椒鹽噪聲,鹽粒噪聲,胡椒噪聲。
5.1 中值濾波器編碼實現
/*
*中值濾波
* 中值濾波對處理椒鹽噪聲 胡椒 鹽粒噪聲的效果很好
*/
void iMidBlur(Mat& src, Mat& dst, int m, int n)
{
double weight = ((double)m * (double)n);
//根據m*n 大小的核填充處理圖像
//填充邊緣大小
int row = src.rows;
int col = src.cols;
int rowPadding = getBordValue(row, 1, n);
int colPadding = getBordValue(col, 1, m);
//這里不要直接填充src,因為這里是引用,可能后面的實驗會重復填充
Mat t_src;
copyMakeBorder(src, t_src, rowPadding, rowPadding, colPadding, colPadding, BORDER_DEFAULT);
int channels = src.channels();
int cols = t_src.cols - rowPadding;
int rows = t_src.rows - colPadding;
for (int i = rowPadding; i < rows; i++) {
for (int j = colPadding; j < cols; j++) {
vector<double> mid0,mid1,mid2;
mid0.clear();
mid1.clear();
mid2.clear();
for (int k = -rowPadding; k <= rowPadding; k++) {
for (int m = -colPadding; m <= colPadding; m++) {
if (channels == 1) {
mid0.push_back((double)t_src.at<uchar>(i + k, j + m));
}
else if (channels == 3) {
Vec3b rgb = t_src.at<Vec3b>(i + k, j + m);
mid0.push_back((double)rgb[0]);
mid1.push_back((double)rgb[1]);
mid2.push_back((double)rgb[2]);
}
}
}
sort(mid0.begin(), mid0.end());
sort(mid1.begin(), mid1.end());
sort(mid2.begin(), mid2.end());
int r = m * n / 2;
if (channels == 1) {
dst.at<uchar>(i - rowPadding, j - colPadding) = static_cast<uchar>(mid0[r]);
}
else if (channels == 3) {
Vec3b rgb;
rgb[0] = static_cast<uchar>(mid0[r]);
rgb[1] = static_cast<uchar>(mid1[r]);
rgb[2] = static_cast<uchar>(mid2[r]);
dst.at<Vec3b>(i - rowPadding, j - colPadding) = rgb;
}
}
}
}
void comparedMidBlur(Mat& sapSrc, Mat& sSrc, Mat& pSrc, Mat& gSrc)
{
//5*5 的中值濾波處理椒鹽噪聲
Mat dst1 = sapSrc.clone();
iMidBlur(sapSrc, dst1, 5, 5);
imshow("5*5中值濾波處理椒鹽噪聲結果", dst1);
//5*5 的中值濾波處理椒噪聲
Mat dst2 = pSrc.clone();
iMidBlur(pSrc, dst2, 5, 5);
imshow("5*5中值濾波處理椒噪聲結果", dst2);
//5*5 的中值濾波處理鹽噪聲
Mat dst3 = pSrc.clone();
iMidBlur(sSrc, dst3, 5, 5);
imshow("5*5中值濾波處理鹽粒噪聲結果", dst3);
//5*5 的中值濾波處理高斯噪聲
Mat dst4 = gSrc.clone();
iMidBlur(gSrc, dst3, 5, 5);
imshow("5*5中值濾波處理高斯噪聲結果", dst4);
}
5.2 中值濾波實驗效果
原圖

添加噪聲的圖片,這里椒鹽噪聲、胡椒噪聲、鹽粒噪聲比例為0.1, 高斯噪聲 均值為15 方差為25。

5*5中值濾波器

結果分析:中值濾波對脈沖型噪聲處理效果很好。
6 自適應局部降低噪聲濾波
自適應局部降低噪聲濾波可以由如下的公式定義:

其中,
分別為全局的方差和選擇窗口的局部方差,
為窗口的均值。這種方式主要用於處理加性高斯噪聲。
6.1 自適應局部降低噪聲濾波代碼實現
/*
* 自適應均值濾波器
*/
void adaptiveMeanBlur(Mat& src, Mat& dst, int n, int m)
{
//根據m*n 大小的核填充處理圖像
//填充邊緣大小
int row = src.rows;
int col = src.cols;
int rowPadding = getBordValue(row, 1, n);
int colPadding = getBordValue(col, 1, m);
//這里不要直接填充src,因為這里是引用,可能后面的實驗會重復填充
Mat t_src;
copyMakeBorder(src, t_src, rowPadding, rowPadding, colPadding, colPadding, BORDER_DEFAULT);
//計算全局的方差
vector<Mat> splitMat;
split(src,splitMat);
double stdDevValue[3] = { 0,0,0 };
int channels = src.channels();
for (int i = 0; i < channels;i++)
{
Mat mt, dt;
meanStdDev(splitMat[i], mt, dt);
stdDevValue[i] = pow(dt.at<double>(0, 0), 2);
}
int cols = t_src.cols - rowPadding;
int rows = t_src.rows - colPadding;
int roiSize = m * n;
for (int i = rowPadding; i < rows; i++) {
for (int j = colPadding; j < cols; j++) {
//先算選擇的ROI區域的均值
double totalPiex[3] = { 0,0,0 };
for (int k = -rowPadding; k <= rowPadding; k++)
{
for (int m = -colPadding; m <= colPadding; m++)
{
if (channels == 1)
{
totalPiex[0] += (double)t_src.at<uchar>(i + k, j + m);
}
else {
Vec3b rgb = t_src.at<Vec3b>(i + k, j + m);
totalPiex[0] += (double)rgb[0];
totalPiex[1] += (double)rgb[1];
totalPiex[2] += (double)rgb[2];
}
}
}
for (int t = 0; t < channels; t++) {
//計算得到均值
totalPiex[t] = (double)totalPiex[t] / roiSize;
}
//接下來要算局部方差
double roiDev[3] = { 0,0,0 };
for (int k = -rowPadding; k <= rowPadding; k++) {
for (int m = -colPadding; m <= colPadding; m++) {
if (channels == 1) {
roiDev[0] += pow((double)t_src.at<uchar>(i + k, j + m)-totalPiex[0],2);
}
else if (channels == 3) {
Vec3b rgb = t_src.at<Vec3b>(i + k, j + m);
roiDev[0] += pow((double)rgb[0] - totalPiex[0], 2);
roiDev[1] += pow((double)rgb[1] - totalPiex[1], 2);
roiDev[2] += pow((double)rgb[2] - totalPiex[0], 2);
}
}
}
for (int t = 0; t < channels; t++) {
//計算得到方差
roiDev[t] = roiDev[t] / roiSize;
}
//計算每個像素的值 自適應均值法
double result[3] = { 0,0,0 };
for (int k = -rowPadding; k <= rowPadding; k++) {
for (int m = -colPadding; m <= colPadding; m++) {
if (channels == 1) {
result[0] = t_src.at<uchar>(i + k, j + m)-(stdDevValue[0]/roiDev[0])*(t_src.at<uchar>(i + k, j + m)-totalPiex[0]);
}
else if (channels == 3) {
Vec3b rgb = t_src.at<Vec3b>(i + k, j + m);
result[0] = rgb[0] - (stdDevValue[0] / roiDev[0]) * (rgb[0] - totalPiex[0]);
result[1] = rgb[1] - (stdDevValue[1] / roiDev[1]) * (rgb[1] - totalPiex[1]);
result[2] = rgb[2] - (stdDevValue[2] / roiDev[2]) * (rgb[2] - totalPiex[2]);
}
}
}
//限定像素值在0-255之間
for (int t = 0; t < channels; t++) {
if (result[t] < 0)
result[t] = 0;
else if (result[t] > 255)
result[t] = 255;
}
//
if (channels == 1) {
dst.at<uchar>(i - rowPadding, j - colPadding) = static_cast<uchar>(result[0]);
}
else if (channels == 3) {
Vec3b rgb;
rgb[0] = static_cast<uchar>(result[0]);
rgb[1] = static_cast<uchar>(result[1]);
rgb[2] = static_cast<uchar>(result[2]);
dst.at<Vec3b>(i - rowPadding, j - colPadding) = rgb;
}
}
}
}
void comparedAdaptiveMeanBlur(Mat& sapSrc, Mat& sSrc, Mat& pSrc, Mat& gSrc)
{
//3*3 的自適應均值濾波處理椒鹽噪聲
Mat dst1 = sapSrc.clone();
adaptiveMeanBlur(sapSrc, dst1, 3, 3);
imshow("3*3自適應均值濾波處理椒鹽噪聲結果", dst1);
//5*5 的自適應均值濾波處理椒噪聲
Mat dst2 = pSrc.clone();
adaptiveMeanBlur(pSrc, dst2, 3, 3);
imshow("3*3自適應均值濾波處理椒噪聲結果", dst2);
//3*3 的自適應均值濾波處理鹽噪聲
Mat dst3 = pSrc.clone();
adaptiveMeanBlur(sSrc, dst3, 3, 3);
imshow("3*3自適應均值濾波處理鹽粒噪聲結果", dst3);
//3*3 的自適應均值濾波處理高斯噪聲
Mat dst4 = gSrc.clone();
adaptiveMeanBlur(gSrc, dst3, 3, 3);
imshow("3*3自適應均值濾波處理高斯噪聲結果", dst4);
}
3.6.2 自適應局部降低噪聲濾波實驗效果
原圖

添加噪聲的圖片,這里椒鹽噪聲、胡椒噪聲、鹽粒噪聲比例為0.1, 高斯噪聲 均值為15 方差為25。

3*3自適應局部降低噪聲濾波

結果分析:這種方式的濾波,很適合處理高斯噪聲。
7 自適應中值濾波器
前面使用中值濾波器對處理空間密度不大的脈沖噪聲效果很好,但是當脈沖噪聲的空間密度大時效果不佳。
所以這里有自適應中值濾波器來處理更大空間密度的脈沖噪聲。
符號定義:
自適應中值濾波器以兩個進程進行工作分別為進程A和進程B,如下方式:
進程A:

進程B:

7.1 自適應中值濾波編碼實現
/*
* adaptive median filtering
* Anchor Point at top left corner
* S_Min =3,S_Max=5
* cyssmile
* 2020/3/19
*/
void adaptiveMedianBlur(Mat &images)
{
int S_Max = 5;
int S_Min = 3;
int row = images.rows;
int col = images.cols;
Mat images_clone = images.clone();
autoCopyMakeBorder(images, BORDER_DEFAULT, 1, S_Min);
vector<Mat> sub_images;
split(images,sub_images);
vector<Mat> sub_images_clone;
split(images_clone, sub_images_clone);
for (int i=0;i<images.channels();i++)
{
dealMainSplitImages(sub_images[i], sub_images_clone[i]);
}
Mat dst;
merge(sub_images_clone,dst);
dealDstEdges(dst, S_Max);
imshow("dst_output", dst);
}
/*
* get the min and max value in images
* cyssmile
* 2020/03/19
*/
void getMinMaxSplitChannel(Mat &images,double &min_val,double &max_val)
{
Point minloc, maxloc;
minMaxLoc(images, &min_val, &max_val, &minloc, &maxloc);
}
/*
* get the median value in images(roi)
* cyssmile
* 2020/03/19
*/
void getMedianSplitChannel(Mat &images,double &median_val)
{
vector<double> images_data;
for (int i =0;i<images.rows;i++)
{
for (int j = 0; j < images.cols; j++)
{
images_data.push_back(images.at<uchar>(i, j));
}
}
sort(images_data.begin(),images_data.end());
median_val = images_data[images.rows*images.cols/2];
}
/*
* process_B
* cyssmile
* 2020/03/19
*/
double process_B(double Z_xy,double min_val,double max_val,double median_val)
{
if (Z_xy-min_val>0 && Z_xy-max_val<0)
{
return Z_xy;
}
else
{
return median_val;
}
}
/*
* give a roi images and then return in (x,y) its (maybe) value
* cyssmile
* 20/03/19
*/
double dealSplitSubImages(Mat &split_images,int &S_now)
{
double min = split_images.at<uchar>(0, 0);
double &min_val = min;
double max = split_images.at<uchar>(0, 0);
double &max_val = max;
getMinMaxSplitChannel(split_images, min_val, max_val);
double median = split_images.at<uchar>(0, 0);
double &median_val = median;
getMedianSplitChannel(split_images, median_val);
double result_piexl= split_images.at<uchar>(0, 0);
if (median - min_val > 0 && median - max_val < 0)
{ // turn process B
result_piexl = process_B(split_images.at<uchar>(0, 0), min_val, max_val, median_val);
}else {
S_now = S_now + 2;
}
return result_piexl;
}
/*
* deal a splited channel images
* cyssmile
* 20/03/19
*/
void dealMainSplitImages(Mat &split_images,Mat &split_images_clone)
{
int S_Min = 3, S_Max = 7;
for (int i = 0; i < split_images_clone.rows; i++)
{
for (int j = 0; j < split_images_clone.cols; j++)
{
int S_now = S_Min;
double median;
double result_piexl = split_images_clone.at<uchar>(i, j);
if ((i + S_Max < split_images.rows )&& (j + S_Max < split_images.cols))
{
while (S_now <= S_Max)
{ //重復A處理過程
Rect rec;
rec.x = j;
rec.y = i;
rec.width = S_now;
rec.height = S_now;
if ((rec.x + S_now >= split_images.rows) && (rec.y + S_now >= split_images.cols))
{
break;
}
Mat sub = split_images(rec);
int S_old = S_now;
result_piexl = dealSplitSubImages(sub, S_now);
if (S_old == S_now)
{
break;
}
else
{
getMedianSplitChannel(sub, median);
result_piexl = median;
}
}
}
split_images_clone.at<uchar>(i, j) = result_piexl;
}
}
}
/*
* deal edges int dst images
* cyssmile
* 20/03/19
*/
void dealDstEdges(Mat &src,int edges)
{
Rect rec;
rec.x = src.cols - edges;
rec.y = 0 ;
rec.width = edges;
rec.height = src.rows;
Mat NeedDealMat = src(rec);
medianBlur(NeedDealMat, NeedDealMat, 3);
Rect rec_bottom;
rec_bottom.x = 0;
rec_bottom.y = src.rows - edges;
rec_bottom.width = src.cols;
rec_bottom.height = edges;
Mat NeedDealMat_bottom = src(rec_bottom);
medianBlur(NeedDealMat_bottom, NeedDealMat_bottom, 3);
}
7.2 自適應中值濾波處理效果
這里我選擇的添加椒鹽噪聲的比例為0.25,前面的幾個實驗中添加的椒鹽噪聲比例只為0.1.

附
1、由於這次實驗時間比較近,有很多細節沒有完善好,有問題可以提醒我。
2、完整代碼:https://github.com/cyssmile/openCV_learning_notes/tree/master/opencv_test/exercise9
