中值滤波能够有效去除图像中的异常点,具有去除图像噪声的作用。传统中值滤波的算法一般都是在图像中建立窗口,然后对窗口内的所有像素值进行排序,选择排序后的中间值作为窗口中心像素滤波后的值。由于这个做法在每个像素点处都要建立窗口并排序,非常耗时,尤其是有大量的冗余计算。如下图:

黄色区域+中间粉色区域是第一个像素为中心建立的滤波窗口,粉色区域+右边蓝色区域为同一行第二个像素为中心建立的滤波窗口。传统做法对每一个窗口内所有像素排序,那么中间粉色区域的像素就会被重复的做排序运算,存在大量冗余计算。如果窗口移动一个像素的时候只用考虑去除左边一列内(黄色区域)的像素,并且加上右边一列(蓝色区域)的像素,运算会大大简化。这样的操作可以使用直方图来实现。

一、直方图实现快速中值滤波算法流程:
1.读取图像I,并且设定滤波窗口大小(winX*winY),一般winX=winY,奇数。

2.设定中值滤波直方图中的阈值,Thresh=(winX*winY)/2 +1;

3.如果要考虑边界情况,可以先对原图像进行扩展,左、右边界分别扩展winX/2个像素,上下边界分别扩展winY/2个像素。

4.逐行遍历图像像素,以第一行为例:先取第一行第一个要处理的像素(窗口中心像素),建立滤波窗口,提取窗口内所有像素值(N=winX*winY个),获取N个像素的直方图Hist。从左到右累加直方图中每个灰度层级下像素点个数,记为sumCnt,直到sumCnt>=Thresh,这时的灰度值就是当前窗口内所有像素值的中值MediaValue。将MediaValue值赋值给窗口中心像素,表明第一个像素中值滤波完成。

5.此时窗口需要向右移动一个像素,开始滤波第二个像素,并且更新直方图。以第二个像素为窗口中心建立滤波窗口,从前一个窗口的灰度直方图Hist中减去窗口中最左侧的一列像素值的灰度个数,然后加上窗口最右侧一列像素值的灰度个数。完成直方图的更新。

6.直方图更新后,sumCnt值有三种变化可能:(1)减小(2)维持不变(3)增大。这三种情况与减去与加入的像素值灰度有关。此时为了求得新的中值,需要不断调整sumCnt与Thresh之间的关系。

(1)如果sumCnt值小于Thresh:说明中值在直方图当前灰度层级的右边,sumCnt就依次向右加上一个灰度层级中灰度值个数,直到满足sumCnt>=Thresh为止。记录此时的灰度层级代表的灰度值,更新MediaValue,作为第二个像素的滤波后的值。

(2)维持不变:说明MediaValue值不变,直接作为第二个像素滤波后的值。

(3)如果sumCnt值大于Thresh:说明中值在直方图当前灰度层级的左边,sumCnt就依次向左减去一个灰度层级中灰度值个数,直到满足sumCnt<=Thresh为止。记录此时的灰度层级代表的灰度值,更新MediaValue值,作为第二个像素的滤波后的值。

7.窗口逐行依次滑动,求得整幅图像的中值滤波结果。

具体代码如下:

#include <opencv2\opencv.hpp>
#include <iostream>
#include <string>using namespace cv;
using namespace std;//计算亮度中值和灰度<=中值的像素点个数
int calMediaValue(const int histogram[], int thresh)
{int sum = 0;for (int i = 0; i < 256; ++i){sum += histogram[i];if (sum>= thresh){return i;}}return 255;
}void fastMedianBlur(const Mat &srcImg, Mat &dstImg, int diameter)
{int radius = (diameter - 1) / 2;int imgW = srcImg.cols;int imgH = srcImg.rows;int channels = srcImg.channels();dstImg = Mat::zeros(imgH, imgW, CV_8UC1);int windowSize = diameter*diameter;//直方图int Hist[256]={0};int histogramSize = 256;//灰度等级int thresholdValue = windowSize / 2 + 1;uchar *pSrcData=srcImg.data;uchar *pDstData=dstImg.data;int right=imgW-radius;int bot=imgH-radius;for (int i=radius; i<right; i++){for (int j=radius; j<bot; j++){//每一行第一个待滤波像素建立直方图if(j==radius){memset(Hist, 0, histogramSize*sizeof(int));for (int y=i-radius; y<=i+radius; y++){for (int x=j-radius; x<=j+radius; x++){uchar value=pSrcData[ y*imgW+x];Hist[value]++;}}}else//更新直方图{int left=j-radius-1;int right=j+radius;for (int y=i-radius; y<=i+radius; y++){//减去左边一列int leftIdx=y*imgW+left;uchar leftValue=pSrcData[leftIdx];Hist[leftValue]--;//加上右边一列int rightIdx=y*imgW+right;uchar rightValue=pSrcData[rightIdx];Hist[rightValue]++;}}//直方图求中值uchar filterValue=calMediaValue(Hist, thresholdValue);pDstData[i*imgW+j]=filterValue;}}//边界直接赋原始值,不做滤波处理pSrcData = srcImg.data;pDstData = dstImg.data;//上下边界for (int j = 0; j < imgW; j++){for (int i = 0; i < radius; i++){int idxTop = i*imgW + j;pDstData[idxTop] = pSrcData[idxTop];int idxBot = (imgH - i - 1)*imgW + j;pDstData[idxBot] = pSrcData[idxBot];}}//左右边界for (int i = radius; i < imgH - radius - 1; i++){for (int j = 0; j < radius; j++){int idxLeft = i*imgW + j;pDstData[idxLeft] = pSrcData[idxLeft];int idxRight = i*imgW + imgW - j-1;pDstData[idxRight] = pSrcData[idxRight];}}
}void main()
{string imgPath = "data/";Mat srcImg = imread(imgPath + "moon.jpg", 0);Mat dstImg;double t0 = cv::getTickCount();fastMedianBlur(srcImg, dstImg, 5);double t1 = cv::getTickCount();cout << "time=" << (t1 - t0) / cv::getTickFrequency() << endl;imwrite("data/test/srcImg.jpg", srcImg);imwrite("data/test/myFilter.jpg", dstImg);
}
#include <opencv2\opencv.hpp>
#include <iostream>
#include <string>using namespace cv;
using namespace std;//计算亮度中值和灰度<=中值的像素点个数
void CalculateImage_MedianGray_PixelCount(const Mat &histogram, int pixelCount, int &medianValue, int &pixleCountLowerMedian)
{float *data = (float *)histogram.data;//直方图int sum = 0;for (int i = 0; i <= 255; ++i){//sum += data[i];if (2 * sum>pixelCount){medianValue = i;pixleCountLowerMedian = sum;break;}}
}void fastMedianBlur(const Mat &srcImg, Mat &dstImg, int diameter)
{int radius = (diameter - 1) / 2;int imgW = srcImg.cols;int imgH = srcImg.rows;int channels = srcImg.channels();dstImg = Mat::zeros(imgH, imgW, CV_8UC1);int windowSize = diameter*diameter;Mat windowImg = Mat::zeros(diameter, diameter, CV_8UC1);//直方图Mat histogram;int histogramSize = 256;//灰度等级int thresholdValue = windowSize / 2 + 1;//step1.设置阈值(步骤参考:图像的高效编程要点之四)//待处理图像像素uchar *pDstData = dstImg.data + imgW*radius + radius;//整个图像中窗口位置指针uchar *pSrcData = srcImg.data;//逐行遍历for (int i = radius; i <= imgH - 1 - radius; i++){//列指针uchar *pColDstData = pDstData;uchar *pColSrcData = pSrcData;//单个窗口指针uchar *pWindowData = windowImg.data;uchar *pRowSrcData = pColSrcData;//从源图中提取窗口图像for (int winy = 0; winy <= diameter - 1; winy++){for (int winx = 0; winx <= diameter - 1; winx++){pWindowData[winx] = pRowSrcData[winx];}pRowSrcData += imgW;pWindowData += diameter;}//求直方图,确定中值,并记录亮度<=中值的像素点个数calcHist(&windowImg,1,//Mat的个数0,//用来计算直方图的通道索引,通道索引依次排开Mat(),//Mat()返回一个空值,表示不用mask,histogram, //直方图1, //直方图的维数,如果计算2个直方图,就为2&histogramSize, //直方图的等级数(如灰度等级),也就是每列的行数0//分量的变化范围);//求亮度中值和<=中值的像素点个数int medianValue, pixelCountLowerMedian;CalculateImage_MedianGray_PixelCount(histogram, windowSize, medianValue, pixelCountLowerMedian);滑动窗口操作结束/////滤波pColDstData[0] = (uchar)medianValue;//处理同一行下一个像素pColDstData++;pColSrcData++;for (int j = radius + 1; j <= imgW - radius - 1; j++){//维护滑动窗口直方图//删除左侧uchar *pWinLeftData = pColSrcData - 1;float *pHistData = (float*)histogram.data;for (int winy = 0; winy < diameter; winy++){uchar grayValue = pWinLeftData[0];pHistData[grayValue] -= 1.0;if (grayValue <= medianValue){pixelCountLowerMedian--;}pWinLeftData += imgW;}//增加右侧uchar *pWinRightData = pColSrcData + diameter - 1;for (int winy = 0; winy < diameter; winy++){uchar grayValue = pWinRightData[0];pHistData[grayValue] += 1.0;if (grayValue <= medianValue){pixelCountLowerMedian++;}pWinRightData += imgW;}//计算新的中值if (pixelCountLowerMedian > thresholdValue){while (1){pixelCountLowerMedian -= pHistData[medianValue];medianValue--;if (pixelCountLowerMedian <= thresholdValue){break;}}}else{while (pixelCountLowerMedian < thresholdValue){medianValue++;pixelCountLowerMedian += pHistData[medianValue];}}pColDstData[0] = medianValue;//下一个像素pColDstData++;pColSrcData++;}//移动至下一行pDstData += imgW;pSrcData += imgW;}//边界直接赋原始值,不做滤波处理pSrcData = srcImg.data;pDstData = dstImg.data;//上下边界for (int j = 0; j < imgW; j++){for (int i = 0; i < radius; i++){int idxTop = i*imgW + j;pDstData[idxTop] = pSrcData[idxTop];int idxBot = (imgH - i - 1)*imgW + j;pDstData[idxBot] = pSrcData[idxBot];}}//左右边界for (int i = radius; i < imgH - radius - 1; i++){for (int j = 0; j < radius; j++){int idxLeft = i*imgW + j;pDstData[idxLeft] = pSrcData[idxLeft];int idxRight = i*imgW + imgW - j-1;pDstData[idxRight] = pSrcData[idxRight];}}
}void main()
{string imgPath = "data/";Mat srcImg = imread(imgPath + "moon.jpg", 0);Mat dstImg;double t0 = cv::getTickCount();fastMedianBlur(srcImg, dstImg, 5);//cv::medianBlur(srcImg, dstImg, 5); //OpenCVdouble t1 = cv::getTickCount();cout << "time=" << (t1 - t0) / cv::getTickFrequency() << endl;imwrite("data/test/srcImg.bmp", srcImg);imwrite("data/test/myFilter.bmp", dstImg);
}

参考:https://www.cnblogs.com/riddick/p/7989871.html

直方图实现快速中值滤波opencv相关推荐

  1. OpenCV图像处理专栏九 | 基于直方图的快速中值滤波算法

    转载自:https://zhuanlan.zhihu.com/p/98092747  侵删 前言 这是OpenCV图像处理专栏的第9篇文章,主要介绍一个基于直方图的快速中值滤波算法,希望对大家有帮助. ...

  2. (MATLAB/C/Python)快速中值滤波

    (MATLAB/C/Python)快速中值滤波 一.中值滤波 二.快速中值滤波 介绍 原理 优化 三.代码 MATLAB C Python 四.测试 其他 by HPC_ZY 最近一个项目中需要用到中 ...

  3. 快速中值滤波——Python实现

    原理 中值滤波是空域中常用的一种滤波方式,是一种非线性的滤波.它的原理就是将窗口像素排序,取中值,然后移动窗口,不断重复取中值的过程. 但是,可以发现,每次移动窗口,都需要对像素点进行排序,从而选取中 ...

  4. 快速中值滤波在心电图ECG中的应用

    1.算法介绍和实现 首先来搞明白,什么是快速中值滤波? 快速中值滤波非常简单,就是用过去连续N个数据,再对这N个数据进行排序,取排序后的中间那个数据,做为当前的输出,N即为窗口的长度. 算法实现: 1 ...

  5. matlab实现 中值滤波去除基线漂移,快速中值滤波在滤除心电信号基线漂移中的应用...

    [摘要]文中给出了一种非线性的滤除心电信号基线漂移的滤波方法,把基于排序统计理论的快速中值滤波方法应用于处理心电信号,通过多次对心电信号中选择的窗口数据进行排序,然后取中值的方法来达到滤波的效果.试验 ...

  6. 高效快速中值滤波算法c语言,快速中值滤波及c语言实现.docx

    . .. 快速中值滤波及c语言实现 学生姓名: 刘 勇 学 号: 6100410218 专业班级: 数媒101 [摘要]本文讨论了用c语言在微机上实现中值滤波及快速算法,在程序设计的过程中充分考虑到程 ...

  7. 实时高速实现改进型中值滤波算法_爱学术_免费下载

    [摘要]在图像采集和处理过程中会引入噪声,必须先对图像进行预处理.本文介绍一种快速中值滤波算法,该算法在硬件平台上实现实时处理功能.综合考虑,选择现场可编程门阵列(FPGA)作为硬件平台,采用硬件描述 ...

  8. 噪点检测matlab,基于噪点检测的中值滤波图像去噪方法

    摘  要: 图像去噪是图像处理中一个非常重要的环节.针对传统中值滤波方法存在的不足,提出一种新的基于噪点检测的自适应中值滤波图像去噪方法.该方法通过自适应地改变滤波窗口的大小,局部检测并判断极值点是否 ...

  9. FPGA verilog HDL实现中值滤波

    FPGA verilog HDL实现中值滤波 今天给大侠简单带来FPGA verilog HDL实现中值滤波,话不多说,上货. 一.实现步骤: 1.查看了中值滤波实现相关的网站和paper: 2.按照 ...

最新文章

  1. 2022-2028年中国喷涂速凝橡胶行业市场调研分析及未来前景分析报告
  2. Python--day43--增删改查补充和limit以及order by
  3. IT容灾系统周期管理图
  4. Resource Path Location Type Target runtime Apache Tomcat v6.0 is not defined已解决
  5. Java获取并Kill系统进程
  6. 自动注入、加载 properties 文件、scope 属性、单例设计模式
  7. 1核1g java_wordpress博客1核1G1M够用吗
  8. P2922 [USACO08DEC]秘密消息Secret Message
  9. linux监控进程挂死,linux系统下实时监控进程以及定位杀死挂起的进程
  10. 流量分析技术丨分享科来网络通讯协议图2022版本(附下载链接)
  11. matlab画一只猫,【MATLAB系列04】当一只猫遇见了Matlab
  12. 常用的即时通讯软件有哪些
  13. One Last Kiss风格封面生成器;程序内存分析工具;Python入门课程资料;神经文本语音合成教程;前沿论文 | ShowMeAI资讯日报
  14. 物理学四大神兽,除了“薛定谔的猫”, 你还知道哪几个?
  15. mysql查询平均工资最低的部门_查询出部门名称、部门的员工数、部门的平均工资、部门的最低收入雇员姓名和最高收入雇员的姓名...
  16. Arduino制作俄罗斯方块小游戏(一)整体设计思路
  17. 拼多多怎么调整后台数据|聚创卓跃
  18. linux 安装toolchain工具
  19. QQ聊天对话框(Js实现,支持表情插入文本中间)
  20. 手机的Ram和Rom是什么意思

热门文章

  1. Java月薪24k_一位月薪1.2w的北漂程序员真实生活!
  2. sasl java_java SASL_SSL 帐号密码 方式访问 kafka
  3. fatal: unable to access ‘https://github.com/apc.git/‘: Failed to connect to github.comport 443:
  4. linux 查看 man 路径配置文件 man.config,linux man 1,2,3....
  5. Docker 容器的--restart参数
  6. 区块链游戏- Solcery(Summoner 召唤者)
  7. 自学无果 报班学习的每日知识点总结与回顾 0基础学前端的小伙伴可以进来看看 一起学习一起进步(三)
  8. 现代教育技术没有计算机基础知识教程,浅析现代教育技术在专科计算机基础课教学中的应用...
  9. 使用DataGrip连接神通数据库
  10. 快捷键,photoshop常用快捷键大全