版权声明:原创文章,欢迎转载。转载请注明:转载自https://blog.csdn.net/jizhidexiaoming https://blog.csdn.net/jizhidexiaoming/article/details/80311345
原理:当前像素值,是由周围像素值决定。通过模板内的值与图像卷积,模板内的值可以直接给定,值有下降的过程即可,也可以通过二维高斯函数生成,这里是通过二维高斯函数生成。
效果如下:
二维高斯函数,定义一个和原图一样大小的高斯函数图像,sigmma取1,其中(x0, y0)是图像的中心:
这个截取的模板大小为5x5,以(x0, y0)为中心截取:
1. Matlab代码实现:
% 高斯平滑,高斯滤波,高斯卷积
% 高斯滤波的模板内的值,可以直接人工给定,也可以通过二维高斯函数生成。
% 这里是高斯函数生成
clear;
clc;
img = imread('D:/Code/Image/classic.jpg');
img = rgb2gray(img);
figure, imshow(img);
% 1, 生成二维高斯模板,sigmma为1
img = double(img);
sigmma = 1;
G = zeros(size(img)); % 生成和图片一样大的空模板
[row, col] = size(G);
center_row = round(row/2); % 二维高斯函数图像的中心点
center_col = round(col/2);
for i = 1:row
for j = 1:col
G(i, j) = (1 / (2*pi*sigmma^2)) * exp(- ((i-center_row)^2 + (j-center_col)^2) / (2*sigmma^2));
end
end
% 查看生成的高斯函数图像
G = mat2gray(G);
figure,surf(G);
% 2, 截取模板大小5x5
G_kernel = G(center_row-2:center_row+2, center_col-2:center_col+2);
% 3, 滤波
new_img = zeros(row, col);
for i = 3:row-2
for j = 3:col-2
new_img(i, j) = sum(sum(img(i-2:i+2, j-2:j+2) .* G_kernel));
end
end
new_img = mat2gray(new_img);
figure,imshow(new_img);
2. C++实现:
#include <opencv2/opencv.hpp>
#include <math.h> //pow
using namespace cv;
using namespace std;
#define pi 3.1415926
int main()
{
Mat img = imread("D:/Code/Image/classic.jpg", 0);
imshow("原图", img);
// 1,利用高斯函数生成模板
double sigmma = 1.0;
int size = 5;
int center_row = round(size / 2); // 0, 1, 2, 3, 4
int center_col = round(size / 2);
// 给5x5矩阵赋值
double sum_value = 0.0;
Mat kernel = Mat::zeros(Size(5, 5), CV_64F);
for (int i = 0; i < size; i++)
{
for (int j = 0; j < size; j++)
{
kernel.at<double>(i, j) = 1 / (2 * pi*pow(sigmma, 2)) *
exp(-(pow(i - center_row, 2) + pow(j - center_col, 2)) / (2 * pow(sigmma, 2)));
sum_value += kernel.at<double>(i, j);
}
}
// gaussain核内的值和为1
for (int i = 0; i < size; i++)
{
for (int j = 0; j < size; j++)
{
kernel.at<double>(i, j) = kernel.at<double>(i, j) / sum_value;
//cout << G_kernel[i][j] << endl;
}
}
//normalize(kernel, kernel, 1, 0, NORM_MINMAX);
//cout << kernel;
// 2,卷积
Mat new_img = Mat::zeros(img.size(), CV_64F);
for (int i = 2; i < img.rows-2; i++)
{
for (int j = 2; j < img.cols-2; j++)
{
//cout << 'i ' << i << 'j ' << j <<endl;
Mat roi1;
cv::Mat m_roi = img(cv::Range(i-2, i+3), cv::Range(j-2, j+3));
m_roi.convertTo(roi1, CV_64F); // 数据类型变换为double
new_img.at<double>(i, j) = roi1.dot(kernel); // 点乘需要相同数据类型,不同于matlab,这里会求和
}
}
new_img.convertTo(new_img, CV_8UC1);
imshow("效果图", new_img);
waitKey(0);
}
知识点:
1) Kernel 内的数据 要定义为double类型:
Mat kernel = Mat::zeros(Size(5, 5), CV_64F);
2)opecv的点乘操作,需要两个mat内的数据都是同类型:
new_img.at<double>(i, j) = roi1.dot(kernel); // 点乘需要相同数据类型,不用于matlab,这里会自动求和
3)Mat内保存的数据类型转化:
m_roi.convertTo(roi1, CV_64F); // 数据类型变换为double
4)图像显示,需要将生成的Mat转化成8UC1类型:
new_img.convertTo(new_img, CV_8UC1);
imshow("效果图", new_img);
5)Mat类数据归一化到0到1之间:
normalize(kernel, kernel, 1, 0, NORM_MINMAX); % 最好加上NORM_MINMAX
6)寻找矩阵Mat中最值及其位置:
double min, max; // 检测最大值是否为1
Point minPt, maxPt;
minMaxIdx(kernel, &min, &max);
minMaxLoc(kernel, &min, &max, &minPt, &maxPt);
7)生成高斯核后,也可以自带函数简单直接卷积:
filter2D(img, new_img, img.depth(), kernel);
8)二维数组定义方式:
double **G_kernel = new double *[size]; // gaus为指针的指针,即指针指向的是一个指针,右边生成的是一个指针数组,数组内存放的是数组。
for (int i = 0; i < size; i++)
{
G_kernel[i] = new double[size];
}
9)图像矩阵内,截取块:
Mat roi1;
cv::Mat m_roi = img(cv::Range(i-2, i+3), cv::Range(j-2, j+3)); // 包含开始,不包含结束(即不包含i+3)
m_roi.convertTo(roi1, CV_64F); // 数据类型变换为double
new_img.at<double>(i, j) = roi1.dot(kernel); // 点乘需要相同数据类型,不同于matlab,这里会求和