C++实现,基于opencv的均值滤波器
目的:用于图像去除图像的颗粒噪声,使图像平滑.
原理: 即以当前像素点为中心,求相邻的8领域和自身的值的和,以其平均值作为中心像素新的值。
数学公式
伪代码:
输入:原图像,输出图像,核的大小
(1).判断原图像是否为空,为空就直接返回
(2).判断卷积核是否为奇数
(3).准备一个新的对象来存放边界填充后的图像
(4).边界填充,并清空目标图像
(5).均值滤波处理
①、三通道的处理
定义RGB三个通道的核的和以及平均数,并置0
求核的总和
求平均数
写入目标图像
核和平均数置0
②、单通道的处理
定义核的和以及平均数,并置0
求核的总和
求平均数
写入目标图像
核和平均数置0
版本:
opencv4.5.1+vs2017
源代码:
只处理单通道和三通道
// ConsoleApplication3.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#include <iostream>
#include <opencv2/opencv.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/core.hpp>
#include <opencv2/imgproc.hpp>
using namespace cv;
using namespace std;
// 均值滤波对话框值滤波
void MeanFilter(Mat& src, Mat& dst, Size wsize) {
// 判断原图像是否为空
if (src.empty()) {
return;
}
//判断矩阵的行列数为奇数
if (wsize.height % 2 == 0 || wsize.width % 2 == 0) {
fprintf(stderr, "Please enter odd size!");
exit(-1);
}
int hh = (wsize.height - 1) / 2;
int hw = (wsize.width - 1) / 2;
Mat Newsrc;
copyMakeBorder(src, Newsrc, hh, hh, hw, hw, BORDER_REFLECT_101);//以边缘为轴,对称
dst = Mat::zeros(src.size(), src.type());
//第二种方法:指针方法
// 三通道彩色图片处理
if (Newsrc.channels() == 3) {
//int q = 0;//test
// 定义每个通道的核的总数和平均值,并置0
int sumR = 0, sumG = 0, sumB = 0;
int averageR = 0, averageG = 0, averageB = 0;
for (int i = hh; i < src.rows + hh; i++)
for (int j = hw; j < src.cols + hw; j++) {
for (int r = i - hh; r <= i + hh; r++) {
Vec3b* data_New = Newsrc.ptr<Vec3b>(r);
// 求核的总数
for (int k = j - hh; k <= j + hh; k++) {
sumR += data_New[k][0];
sumG += data_New[k][1];
sumB += data_New[k][2];
}
}
// 求平均数
averageR = sumR / (wsize.area());
averageG = sumG / (wsize.area());
averageB = sumB / (wsize.area());
// 写入目标图像
Vec3b* dataDst = dst.ptr<Vec3b>(i - hh);
dataDst[j - hw][0] = averageR;
dataDst[j - hw][1] = averageG;
dataDst[j - hw][2] = averageB;
// 总和和平均数置0
sumR = 0; sumG = 0; sumB = 0;
averageR = 0; averageG = 0; averageB = 0;
}
}
// 单通道图片处理
else if (Newsrc.channels() == 1) {
int sum = 0;
int average = 0;
for (int i = hh; i < src.rows + hh; i++) {
for (int j = hw; j < src.cols + hw; j++) {
for (int r = i - hh; r <= i + hh; r++) {
uchar* data = Newsrc.ptr<uchar>(r);
for (int k = j - hh; k <= j + hh; k++) {
sum += data[k];
}
}
average = sum / (wsize.area());
uchar* data_dst = dst.ptr<uchar>(i - hh);
data_dst[j - hw] = average;
sum = 0;
average = 0;
}
}
}
else
{
return;
}
}
int main()
{
Mat src = imread("C:/Users/vmyf16/Desktop/1.jpg");
if (src.empty()) {
return -1;
}
imshow("原图", src);
Mat dst2 = Mat::zeros(src.size(), src.type());
MeanFilter(src, dst2, Size(5, 5));
imshow("自己写的", dst2);
Mat dst1 = Mat::zeros(src.size(), src.type());
blur(src, dst1, Size(5, 5));
imshow("opencv自带的", dst1);
imshow("两者差值", dst1 - dst2);
waitKey(0);
return 0;
}
// 运行程序: Ctrl + F5 或调试 >“开始执行(不调试)”菜单
// 调试程序: F5 或调试 >“开始调试”菜单
// 入门使用技巧:
// 1. 使用解决方案资源管理器窗口添加/管理文件
// 2. 使用团队资源管理器窗口连接到源代码管理
// 3. 使用输出窗口查看生成输出和其他消息
// 4. 使用错误列表窗口查看错误
// 5. 转到“项目”>“添加新项”以创建新的代码文件,或转到“项目”>“添加现有项”以将现有代码文件添加到项目
// 6. 将来,若要再次打开此项目,请转到“文件”>“打开”>“项目”并选择 .sln 文件
结果图像:
原图像:
结果图: