作者:翟天保Steven
版权声明:著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处
实现原理
基于累计直方图的中值滤波算法是一种图像处理技术,用于去除图像中的噪声。它利用了像素值的频数分布,通过计算累计直方图来快速确定中值,以实现滤波操作。
下面是该算法的基本步骤:
-
计算累计直方图:对输入图像进行直方图统计,并计算每个像素值及其之前所有像素值的累计频数。
-
确定中值位置:根据累计直方图,找到频数超过总像素数一半的像素值,该值即为中值。
-
应用中值滤波:对图像中的每个像素,使用中值替换其原始值,以完成滤波操作。
这种方法相对于传统的中值滤波有一定的优势,因为它利用了累计直方图的性质,使得中值的计算速度更快,尤其适用于处理灰度图像中像素值范围在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这种,就无法进行准确滤波,需要进一步改进才可。
彩色图和灰度图本质上一样,无非是对三个通道进行滤波,本文就不展开了。
如果函数有什么可以改进完善的地方,非常欢迎大家指出,一同进步何乐而不为呢~
如果文章帮助到你了,可以点个赞让我知道,我会很快乐~加油!