【图像处理知识复习】11高斯滤波(滤波,卷积)Matlab,C++实现

版权声明:原创文章,欢迎转载。转载请注明:转载自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,这里会求和

猜你喜欢

转载自blog.csdn.net/jizhidexiaoming/article/details/80311345
今日推荐