在Opencv环境下用C++实现均值滤波

  • 前言

到今天为止,已经接触图像处理行业四年左右,但是大部分时间都是在调用别人已经封装好的函数,即传说中的掉包侠。虽然清楚算法原理,但是自己从来没有比较系统的实现过一个算法。今天就以均值滤波算法为例,用C++自行实现。均值滤波算法的原理比较简单,这里就不再赘述。

  • 最简单的均值滤波算法实现

效果图

                        

                                                                               运行时间对比

代码:


//////////////////////////////////
//opencv4.1.0
//////////////////////////////////

#include <opencv2/opencv.hpp>

using namespace std;
using namespace cv;

void MeanImage(Mat& src, Mat& dst); //均值滤波

int main() {
	TickMeter tm;
	Mat src, srcOpencv, srcMyself;
	src = imread("1.png",0);

	//调用opencv的函数实现均值滤波
	tm.start();
	blur(src, srcOpencv, Size(3, 3));
	tm.stop();
	cout << "opencv函数运行时间:" << tm.getTimeMilli() << "ms" << endl;

	//使用c++自行实现均值滤波
	tm.start();
	MeanImage(src, srcMyself);
	tm.stop();
	cout << "自己实现的函数运行时间:" << tm.getTimeMilli() << "ms" << endl;

	//显示
	imshow("原图", src);
	imshow("opencv函数均值滤波", srcOpencv);
	imshow("自己实现的函数均值滤波", srcMyself);
	waitKey(0);
	return 0;
}

void MeanImage(Mat& src,Mat& dst) {
	dst = Mat::zeros(src.size(), src.type());
	for (size_t i = 1; i < src.rows-1; i++){
		for (size_t j = 1; j < src.cols-1; j++){
			dst.at<uchar>(i, j) = (src.at<uchar>(i - 1, j - 1) + src.at<uchar>(i - 1, j) + src.at<uchar>(i - 1, j + 1) +
				                   src.at<uchar>(i, j - 1) + src.at<uchar>(i, j) + src.at<uchar>(i, j + 1) +
				                   src.at<uchar>(i + 1, j - 1) + src.at<uchar>(i + 1, j) + src.at<uchar>(i + 1, j + 1)) / 9;
		}
	}
}

上面的代码虽然实现了均值滤波的功能,但是我们需要解决代码中存在的如下诸多缺陷:

           1.掩模尺寸不能改变,只能实现掩模尺寸为3x3的滤波

                      2.没有对图像边界的像素进行处理

                      3.算法没有经过优化,因此运行时间太长

  • 添加掩模尺寸可变的功能

效果图同上,这里就不再给出。但值得注意的一点是,到目前为止的代码都没有考虑边界像素的处理问题,因此结果图中宽度为(Height-1)/2的边界像素值都为0 ,其中Height为掩模的高度(掩模的宽高相等)。

                                                                                边界像素值为0 

代码:


//////////////////////////////////
//opencv4.1.0
//////////////////////////////////

#include <opencv2/opencv.hpp>

using namespace std;
using namespace cv;

void MeanImage(Mat& src, Mat& dst, Size ksize); //均值滤波

int main() {
	TickMeter tm;
	Mat src, srcOpencv, srcMyself;
	src = imread("1.png",0);

	//调用opencv的函数实现均值滤波
	tm.start();
	blur(src, srcOpencv, Size(3, 3));
	tm.stop();
	cout << "opencv函数运行时间:" << tm.getTimeMilli() << "ms" << endl;

	//使用c++自行实现均值滤波
	tm.start();
	MeanImage(src, srcMyself,Size(3,3));
	tm.stop();
	cout << "自己实现的函数运行时间:" << tm.getTimeMilli() << "ms" << endl;

	//显示
	imshow("原图", src);
	imshow("opencv函数均值滤波", srcOpencv);
	imshow("自己实现的函数均值滤波", srcMyself);
	waitKey(0);
	return 0;
}

void MeanImage(Mat& src,Mat& dst,Size ksize) {

	//初始化输出图像
	dst = Mat::zeros(src.size(), src.type());

	//确保掩模宽高相等
	if (ksize.height != ksize.width){
		ksize.height = ksize.width;
	}
	//确保掩模宽高为奇数
	if (ksize.height % 2 ==0){
		ksize.height = ksize.height + 1;
		ksize.width = ksize.width + 1;
	}

	//掩模宽高的一半
	int HH = (ksize.height - 1) / 2;
	int HW = (ksize.width - 1) / 2;
	
	//遍历除边界像素外的所有像素
	int sum = 0;
	for (size_t r = HH; r < src.rows - HH; r++){
		for (size_t c = HW; c < src.cols - HW; c++){
			//计算掩模覆盖的所有像素值的和		
			for (size_t i = r-HH; i <= r+HH; i++){
				for (size_t j = c-HW; j <= c+HW; j++){
					sum += src.at<uchar>(i,j);
				}
			}
			//求均值并赋值给相应位置的像素
			dst.at<uchar>(r,c) = sum / ksize.area();
			sum = 0;
		}
	}
}
  • 添加对图像边界像素的处理

可直接调用opencv中的copyMakeBorder函数对图像边界像素进行扩充,此函数本文不做过多介绍,详情可参考张美丽的博客。对边界扩充后的效果图:略

代码:


//////////////////////////////////
//opencv4.1.0
//////////////////////////////////

#include <opencv2/opencv.hpp>

using namespace std;
using namespace cv;

void MeanImage(Mat& src, Mat& dst, Size ksize); //均值滤波

int main() {
	TickMeter tm;
	Mat src, srcOpencv, srcMyself;
	src = imread("1.png",0);

	//调用opencv的函数实现均值滤波
	tm.start();
	blur(src, srcOpencv, Size(3, 3));
	tm.stop();
	cout << "opencv函数运行时间:" << tm.getTimeMilli() << "ms" << endl;

	//使用c++自行实现均值滤波
	tm.start();
	MeanImage(src, srcMyself,Size(3,3));
	tm.stop();
	cout << "自己实现的函数运行时间:" << tm.getTimeMilli() << "ms" << endl;

	//显示
	imshow("原图", src);
	imshow("opencv函数均值滤波", srcOpencv);
	imshow("自己实现的函数均值滤波", srcMyself);
	waitKey(0);
	return 0;
}

void MeanImage(Mat& src,Mat& dst,Size ksize) {

	//初始化输出图像
	dst = Mat::zeros(src.size(), src.type());

	//确保掩模宽高相等
	if (ksize.height != ksize.width){
		ksize.height = ksize.width;
	}
	//确保掩模宽高为奇数
	if (ksize.height % 2 ==0){
		ksize.height = ksize.height + 1;
		ksize.width = ksize.width + 1;
	}

	//掩模宽高的一半
	int HH = (ksize.height - 1) / 2;
	int HW = (ksize.width - 1) / 2;

	//扩充边缘像素
	Mat MakeBorder;
	copyMakeBorder(src, MakeBorder, HH, HH, HW, HW, BORDER_REFLECT_101);

	//遍历除边界像素外的所有像素
	int sum = 0;
	for (size_t r = HH; r < src.rows + HH; r++){
		for (size_t c = HW; c < src.cols + HW; c++){
			//计算掩模覆盖的所有像素值的和		
			for (size_t i = r-HH; i <= r+HH; i++){
				for (size_t j = c-HW; j <= c+HW; j++){
					sum += MakeBorder.at<uchar>(i,j);
				}
			}
			//求均值并赋值给相应位置的像素
			dst.at<uchar>(r-HH,c-HW) = sum / ksize.area();
			sum = 0;
		}
	}
}
  • 算法优化的内容较多,由于篇幅的问题,这里就不在详述。详情请查看本人的另一篇博客优化均值滤波算法

猜你喜欢

转载自blog.csdn.net/jgj123321/article/details/95078503
今日推荐