高斯滤波及其实现
- 高斯滤波的解释及其具体操作
- 创建高斯滤波核
1.高斯滤波的解释及其具体操作
- 高斯滤波是一种线性平滑滤波,适用于消除高斯噪声,应用于图像处理的减噪过程。高斯滤波就是对整幅图像进行加权平均的过程,每一个像素点的值,都由其本身和邻域内的其他像素值经过加权平均后得到。
- 高斯滤波的具体操作是:用一个模板(或称卷积、掩模)扫描图像中的每一个像素,用模板确定的邻域内像素的加权平均灰度值去替代模板中心像素点的值。
2.创建高斯滤波
高斯滤波模板是通过二维高斯函数计算出来的:
G(x,y)=exp^((-x^2-y^2)/(2σ^2))/(2π*σ^2)
- 高斯模板需要知道模板大小及σ值,再通过上述高斯函数来确定模板系数。以模板中心为坐标原点,其高斯函数中的x^2+y^2就是指模板在坐标系中离原点的距离。
- 利用高斯分布函数求得后,先归一化(左上角的元素为1),取整,再乘以模板取整后的系数和。
- 代码如下:
Mat customGaussianBlur(Mat src, double sigma)
{
//生成高斯核
Mat gauss(5, 5, CV_64FC1);
//构建5x5的高斯卷积核
for (int i = -2; i <= 2; i++)
{
for (int j = -2; j <= 2; j++)
{
//计算二维高斯分布
gauss.at<double>(i + 2, j + 2) = exp(-(i * i + j * j) / (2 * sigma * sigma))
/ (2 * PI * sigma * sigma);
}
}
//满足加权平均条件
double gaussSum = sum(gauss).val[0];
//权值归一化
for (int i = -2; i <= 2; i++)
{
for (int j = -2; j <= 2; j++)
{
gauss.at<double>(i + 2, j + 2) /= gaussSum;
}
}
卷积操作
- 卷积运算是加权求和的过程,使用到的图像区域中的每个像素分别与卷积和(权矩阵)的每个元素对应相乘,所有乘积之和作为区域中心像素的新值。
- 对一幅图像进行卷积运算, 首先将核的参考点定位于图像的第一个像素点,核的其余元素覆盖图像总其对应的局部像素点。对于每一个核点,我们可以得到这个点的值以及图像中对应图像点的值。将这些值相乘并求和,并将这个结果放在与输入图像参考点所对应的位置。通过在整个图像上扫描卷积核,对图像的每个点重复此操作。最终可以得到图像的卷积图像。
- 代码如下:
//卷积操作
//首先对边缘进行补零操作,这里核大小为5x5,所以width+4, height+4
copyMakeBorder(src, src, 2, 2, 2, 2, BORDER_CONSTANT, Scalar(0));
//创建输出图像,同样是填充过后的
Mat dst = src.clone();
for (int i = 0; i < src.rows - 4; i++)
{
for (int j = 0; j < src.cols - 4; j++)
{
double sum = 0; //初始化卷积中心值
for (int k = 0; k < gauss.rows; k++)
{
for (int s = 0; s < gauss.cols; s++)
{
//求卷积
sum += (double)(src.at<uchar>(i + k, j + s)) * gauss.at<double>(k, s);
}
}
//因为输出图像也进行了边缘填充,所以真正第一个卷积中心是(i+2,j+2)
//可以看到,输出图像有一圈黑边,这就是没有处理到的图像边缘
dst.at<uchar>(i + 2, j + 2) = (uchar)sum;
}
}
图像的基本操作
- 加载、修改图像
- 读写图片像素
1.加载、修改图像
- 加载图像cv::mired
利用Mat对象先创建一个对象,如命名为src,利用imread(“图像路径”,图像类型)加载图像;图像类型默认为1。
加载完图像后利用namedWindow函数创建一个窗口 namedWindow(“窗口名”,窗口类型) 。
设置好窗口格式后,在显示图片,利用imshow(“窗口名”,src)。 - 修改图像cv::cvtColor
cvtColor把图像从一个色彩空间转换成另一个色彩空间。cvtColor(src,dst,变换方案),src为源图像,dst为Mat新建的一个目标对象,变换方案就是对源图像做一些颜色变换。
2. 读写图片像素
图像有单通道和三通道之分,单通道就是灰度图像,黑白灰的那种,三通道就是rgb图像,彩色的那种。
- 单通道:image.at(x,y),单通道是uchar类型的。
- 三通道:image.at(x,y)[index],多通道中需要除了填写类型和坐标外,还需要填写index值,即三个通道0、1、2。
- 代码如下:
#include <opencv2/opencv.hpp>
#define PI 3.1415926
using namespace std;
using namespace cv;
Mat customGaussianBlur(Mat src, double sigma)
{
//生成高斯核
Mat gauss(5, 5, CV_64FC1);
//构建5x5的高斯卷积核
for (int i = -2; i <= 2; i++)
{
for (int j = -2; j <= 2; j++)
{
//计算二维高斯分布
gauss.at<double>(i + 2, j + 2) = exp(-(i * i + j * j) / (2 * sigma * sigma))
/ (2 * PI * sigma * sigma);
}
}
//满足加权平均条件
double gaussSum = sum(gauss).val[0];
//权值归一化
for (int i = -2; i <= 2; i++)
{
for (int j = -2; j <= 2; j++)
{
gauss.at<double>(i + 2, j + 2) /= gaussSum;
}
}
//卷积操作
//首先对边缘进行补零操作,这里核大小为5x5,所以width+4, height+4
copyMakeBorder(src, src, 2, 2, 2, 2, BORDER_CONSTANT, Scalar(0));
//创建输出图像,同样是填充过后的
Mat dst = src.clone();
for (int i = 0; i < src.rows - 4; i++)
{
for (int j = 0; j < src.cols - 4; j++)
{
double sum = 0; //初始化卷积中心值
for (int k = 0; k < gauss.rows; k++)
{
for (int s = 0; s < gauss.cols; s++)
{
//求卷积
sum += (double)(src.at<uchar>(i + k, j + s)) * gauss.at<double>(k, s);
}
}
//因为输出图像也进行了边缘填充,所以真正第一个卷积中心是(i+2,j+2)
//可以看到,输出图像有一圈黑边,这就是没有处理到的图像边缘
dst.at<uchar>(i + 2, j + 2) = (uchar)sum;
}
}
return dst;
}
int main(int argc, const char * argv[])
{
Mat src = imread("E:\\photo\\_DSC3065.JPG");
if (src.empty()) {
cout << "could not load the image" << endl;
return -1;
}
cvtColor(src, src, CV_BGR2GRAY);
namedWindow("src", WINDOW_AUTOSIZE);
imshow("src", src);
Mat dst1, dst2, dst3, dst4;
//自定义高斯模糊
dst1 = customGaussianBlur(src, 50);
imshow("dst1", dst1);
//线性平滑滤波
blur(src, dst2, Size(5, 5), Point(-1, -1), 4);
imshow("dst2", dst2);
//高斯模糊
GaussianBlur(src, dst3, Size(5, 5), 15, 0, 4);
imshow("dst3", dst3);
GaussianBlur(src, dst4, Size(0, 0), 15, 0, 4);
imshow("dst4", dst4);
waitKey(0);
return 0;
}