OpenCV-基于累计直方图的中值滤波算法

发布于:2024-05-10 ⋅ 阅读:(34) ⋅ 点赞:(0)

作者:翟天保Steven
版权声明:著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处

实现原理

       基于累计直方图的中值滤波算法是一种图像处理技术,用于去除图像中的噪声。它利用了像素值的频数分布,通过计算累计直方图来快速确定中值,以实现滤波操作。

       下面是该算法的基本步骤:

  1. 计算累计直方图:对输入图像进行直方图统计,并计算每个像素值及其之前所有像素值的累计频数。

  2. 确定中值位置:根据累计直方图,找到频数超过总像素数一半的像素值,该值即为中值。

  3. 应用中值滤波:对图像中的每个像素,使用中值替换其原始值,以完成滤波操作。

       这种方法相对于传统的中值滤波有一定的优势,因为它利用了累计直方图的性质,使得中值的计算速度更快,尤其适用于处理灰度图像中像素值范围在0到255之间的情况。

功能函数代码

// 基于累计直方图的中值滤波算法
cv::Mat filterMedian_CPU(cv::Mat input, int filterWindowSize)
{
	int row = input.rows;
	int col = input.cols;
	int r = filterWindowSize / 2;
	cv::Mat output(row, col, CV_8UC1);
#pragma omp parallel for
	for (int i = 0; i < row; ++i)
	{
		for (int j = 0; j < col; ++j)
		{
			int ms = std::max(i - r, 0);
			int me = std::min(i + r, row - 1);
			int ns = std::max(j - r, 0);
			int ne = std::min(j + r, col - 1);
			// 初始化
			int count = 0;
			int histogram[256] = { 0 };
			// 直方图累计求中值
			for (int m = ms; m <= me; ++m)
			{
				for (int n = ns; n <= ne; ++n)
				{
					histogram[input.at<uchar>(m, n)]++;
					count++;
				}
			}
			int count_ = 0;
			int thresh = count / 2 + 1;
			int mid = 0;
			for (int k = 0; k < 256; ++k)
			{
				count_ += histogram[k];
				if (count_ >= thresh)
				{
					mid = k;
					break;
				}
			}
			output.at<uchar>(i, j) = mid;
		}
	}
	return output;
}

C++测试代码

       为了对比算法效果,我写了一个基于排序的中值滤波算法,两者均采用OpenMP并行技术提速,进行对比测试。

#include <time.h>
#include <omp.h>
#include <vector>
#include <iostream>
#include <algorithm>
#include <opencv2/opencv.hpp>

using namespace std;

// 中值滤波-CPU-排序法
cv::Mat filterMedian_CPU_Sort(cv::Mat input, int filterWindowSize)
{
	int row = input.rows;
	int col = input.cols;
	cv::Mat output(row, col, CV_8UC1);
	int r = filterWindowSize / 2;
#pragma omp parallel for
	for (int i = 0; i < row; ++i)
	{
		vector<float> datas;
		for (int j = 0; j < col; ++j)
		{
			int ms = std::max(i - r, 0);
			int me = std::min(i + r, row - 1);
			int ns = std::max(j - r, 0);
			int ne = std::min(j + r, col - 1);
			// 求窗口内有效数据的中值
			datas.clear();
			for (int m = ms; m <= me; ++m)
			{
				for (int n = ns; n <= ne; ++n)
				{
					datas.push_back(input.at<uchar>(m, n));
				}
			}
			sort(datas.begin(), datas.end());
			output.at<uchar>(i, j) = datas[datas.size() / 2];
		}
	}
	return output;
}

// 基于累计直方图的中值滤波算法
cv::Mat filterMedian_CPU(cv::Mat input, int filterWindowSize)
{
	int row = input.rows;
	int col = input.cols;
	int r = filterWindowSize / 2;
	cv::Mat output(row, col, CV_8UC1);
#pragma omp parallel for
	for (int i = 0; i < row; ++i)
	{
		for (int j = 0; j < col; ++j)
		{
			int ms = std::max(i - r, 0);
			int me = std::min(i + r, row - 1);
			int ns = std::max(j - r, 0);
			int ne = std::min(j + r, col - 1);
			// 初始化
			int count = 0;
			int histogram[256] = { 0 };
			// 直方图累计求中值
			for (int m = ms; m <= me; ++m)
			{
				for (int n = ns; n <= ne; ++n)
				{
					histogram[input.at<uchar>(m, n)]++;
					count++;
				}
			}
			int count_ = 0;
			int thresh = count / 2 + 1;
			int mid = 0;
			for (int k = 0; k < 256; ++k)
			{
				count_ += histogram[k];
				if (count_ >= thresh)
				{
					mid = k;
					break;
				}
			}
			output.at<uchar>(i, j) = mid;
		}
	}
	return output;
}
int main(void)
{
	// 测试
	cv::Mat input = cv::imread("test.jpg", 0);
	cv::Mat output0, output1;
	int filterWindowSize = 15;
	cout << "filterWindowSize:" << filterWindowSize << endl;
	// 在CPU上执行中值滤波-排序法
	clock_t ss, es;
	ss = clock();
	output0 = filterMedian_CPU_Sort(input, filterWindowSize);
	es = clock();
	cout << "Sort CPU time:" << double(es - ss) / 1000 << endl;
	// 在CPU上执行中值滤波-累计直方图法
	clock_t sc, ec;
	sc = clock();
	output1 = filterMedian_CPU(input, filterWindowSize);
	ec = clock();
	cout << "His CPU time:" << double(ec - sc) / 1000 << endl;
	return 0;
}

测试效果 

       如上图所示,分别是原图、排序法结果和直方图法结果,在速度方面,对1920*1080的图像,在窗口尺寸为15*15时,我的电脑运行速度分别是4s和0.16s。直方图算法速度快,但也有其局限性,若进行滤波的数据是非uchar型,而是更大范围甚至小数点数据,如int、double、float这种,就无法进行准确滤波,需要进一步改进才可。

       彩色图和灰度图本质上一样,无非是对三个通道进行滤波,本文就不展开了。

       如果函数有什么可以改进完善的地方,非常欢迎大家指出,一同进步何乐而不为呢~

       如果文章帮助到你了,可以点个赞让我知道,我会很快乐~加油!


网站公告

今日签到

点亮在社区的每一天
去签到