转载自:https://zhuanlan.zhihu.com/p/98092747  侵删

前言

这是OpenCV图像处理专栏的第9篇文章,主要介绍一个基于直方图的快速中值滤波算法,希望对大家有帮助。

算法原理

传统的中值滤波是通过滑动窗口不断在图像上移动,求出窗口内的中值作为中心像素点的像素。在这个过程中显然存在大量的重复计算,所以效率很低。因此有人提出了一个利用直方图来做中值滤波的算法,如下图所示:

可以把整个图片看成滑动窗口,当我们从左边移动到右边时,中间的粉色部分是共享的,只有黄色部分变为了蓝色部分,所以就想了利用直方图来更新的方法。

算法步骤

  • 读取图像I,并且设定滤波窗口大小(winX,winY),一般winX=winY,奇数。
  • 设定中值滤波直方图中的阈值,Thresh=(winX*winY)/2 +1;
  • 如果要考虑边界情况,可以先对原图像进行扩展,左、右边界分别扩展winX/2个像素,上下边界分别扩展winY/2个像素。
  • 逐行遍历图像像素,以第一行为例:先取第一行第一个要处理的像素(窗口中心像素),建立滤波窗口,提取窗口内所有像素值(N=winX*winY个),获取N个像素的直方图Hist。从左到右累加直方图中每个灰度层级下像素点个数,记为sumCnt,直到sumCnt>=Thresh,这时的灰度值就是当前窗口内所有像素值的中值MediaValue。将MediaValue值赋值给窗口中心像素,表明第一个像素中值滤波完成。
  • 此时窗口需要向右移动一个像素,开始滤波第二个像素,并且更新直方图。以第二个像素为窗口中心建立滤波窗口,从前一个窗口的灰度直方图Hist中减去窗口中最左侧的一列像素值的灰度个数,然后加上窗口最右侧一列像素值的灰度个数。完成直方图的更新。
  • 直方图更新后,sumCnt值有三种变化可能:(1)减小(2)维持不变(3)增大。这三种情况与减去与加入的像素值灰度有关。此时为了求得新的中值,需要不断调整sumCnt与Thresh之间的关系。 (1)如果sumCnt值小于Thresh:说明中值在直方图当前灰度层级的右边,sumCnt就依次向右加上一个灰度层级中灰度值个数,直到满足sumCnt>=Thresh为止。记录此时的灰度层级代表的灰度值,更新MediaValue,作为第二个像素的滤波后的值。 (2)维持不变:说明MediaValue值不变,直接作为第二个像素滤波后的值。 (3)如果sumCnt值大于Thresh:说明中值在直方图当前灰度层级的左边,sumCnt就依次向左减去一个灰度层级中灰度值个数,直到满足sumCnt<=Thresh为止。记录此时的灰度层级代表的灰度值,更新MediaValue值,作为第二个像素的滤波后的值。
  • 窗口逐行依次滑动,求得整幅图像的中值滤波结果。

代码实现

#include "stdafx.h"
#include <stdio.h>
#include <iostream>
#include <immintrin.h>
#include <opencv2/opencv.hpp>
using namespace cv;
using namespace std;//计算中值
int getMediaValue(const int hist[], int thresh) {int sum = 0;for (int i = 0; i < 256; i++) {sum += hist[i];if (sum >= thresh) {return i;}}return 255;
}
//快速中值滤波,灰度图
Mat fastMedianBlur(Mat src, int diameter) {int row = src.rows;int col = src.cols;Mat dst(row, col, CV_8UC1);int Hist[256] = { 0 };int radius = (diameter - 1) / 2;int windowSize = diameter * diameter;int threshold = windowSize / 2 + 1;uchar *srcData = src.data;uchar *dstData = dst.data;int right = col - radius;int bot = row - radius;for (int j = radius; j < bot; j++) {for (int i = radius; i < right; i++) {//每一行第一个待滤波元素建立直方图if (i == radius) {memset(Hist, 0, sizeof(Hist));for (int y = j - radius; y <= min(j + radius, row); y++) {for (int x = i - radius; x <= min(i + radius, col); x++) {uchar val = srcData[y * col + x];Hist[val]++;}}}else {int L = i - radius - 1;int R = i + radius;for (int y = j - radius; y <= min(j + radius, row); y++) {//更新左边一列Hist[srcData[y * col + L]]--;//更新右边一列Hist[srcData[y * col + R]]++;}}uchar medianVal = getMediaValue(Hist, threshold);dstData[j * col + i] = medianVal;}}//边界直接赋值for (int i = 0; i < row; i++) {for (int j = 0; j < radius; j++) {int id1 = i * col + j;int id2 = i * col + col - j - 1;dstData[id1] = srcData[id1];dstData[id2] = srcData[id2];}}for (int i = 0; i < col; i++) {for (int j = 0; j < radius; j++) {int id1 = j * col + i;int id2 = (row - j - 1) * col + i;dstData[id1] = srcData[id1];dstData[id2] = srcData[id2];}}return dst;
}void MedianFilter(int height, int width, unsigned char * __restrict src, unsigned char * __restrict dst) {for (int i = 1; i < height - 1; i++) {for (int j = 1; j < width - 1; j++) {unsigned char a[9];a[0] = src[i * width + j];a[1] = src[i * width + j + 1];a[2] = src[i * width + j - 1];a[3] = src[(i + 1) * width + j];a[4] = src[(i + 1) * width + j + 1];a[5] = src[(i + 1) * width + j - 1];a[6] = src[(i - 1) * width + j];a[7] = src[(i - 1) * width + j + 1];a[8] = src[(i - 1) * width + j - 1];for (int ii = 0; ii < 5; ii++) {for (int jj = ii + 1; jj < 9; jj++) {if (a[ii] > a[jj]) {unsigned char temp = a[ii];a[ii] = a[jj];a[jj] = temp;}}}dst[i * width + j] = a[4];}}for (int i = 0; i < width; i++) {dst[i] = src[i];dst[(height - 1) * width + i] = src[(height - 1) * width + i];}for (int i = 0; i < height; i++) {dst[i * width] = src[i * width];dst[i * width + width - 1] = src[i * width + width - 1];}
}
Mat speed_MedianFilter(Mat src) {int row = src.rows;int col = src.cols;unsigned char * data = (unsigned char *)src.data;unsigned char *dst = new unsigned char[row * col];MedianFilter(row, col, data, dst);Mat res(row, col, CV_8UC1, dst);return res;
}int main() {Mat src = cv::imread("F:\\t1.jpg", 0);Mat dst1 = fastMedianBlur(src, 3);Mat dst2 = speed_MedianFilter(src);cv::imshow("dst1", dst1);cv::imshow("dst2", dst2);int row = src.rows;int col = src.cols;for (int i = 0; i < row; i++) {for (int j = 0; j < col; j++) {if (dst1.at<uchar>(i, j) != dst2.at<uchar>(i, j)) {printf("%d %d\n", i, j);}}}system("pause");cv::waitKey(0);return 0;
}

代码已经测试过,验证无误。在分辨率比较大的图像上执行中值滤波可以考虑一下这个算法,而且这个算法使用SSE指令可以进一步加速,后续会继续分享,欢迎大家关注阅读哦。

后记

今天为大家介绍了一个基于直方图的快速中值滤波算法,希望对大家有帮助。


欢迎关注我的微信公众号GiantPandaCV,期待和你一起交流机器学习,深度学习,图像算法,优化技术,比赛及日常生活等。

OpenCV图像处理专栏九 | 基于直方图的快速中值滤波算法相关推荐

  1. 直方图实现快速中值滤波opencv

    中值滤波能够有效去除图像中的异常点,具有去除图像噪声的作用.传统中值滤波的算法一般都是在图像中建立窗口,然后对窗口内的所有像素值进行排序,选择排序后的中间值作为窗口中心像素滤波后的值.由于这个做法在每 ...

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

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

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

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

  4. matlab中基于十字形窗口的滤波算法,#215;字形滤波窗口在Matlab自适应中值滤波算法中的应用 - 21ic中国电子网...

    由于种种原因,图像在生成.传输.变换等过程中往往会受到各种噪声的污染,从而导致图像质量退化.噪声信号的滤波是图像处理的基本任务之一,主要有线性滤波和非线性滤波两种方法.线性滤波方法一般具有低通特性,而 ...

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

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

  6. 图像处理之积分图应用二(快速边缘保留滤波算法)

    图像处理之积分图应用二(快速边缘保留滤波算法) 一:基本原理 传统的图像边缘保留滤波算法-如高斯双边模糊.Mean-Shift模糊等计算复杂.效率比较低,虽然有各种手段优化或者快速计算方法,当时算法相 ...

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

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

  8. matlab中值滤波法算法,基于MATLAB中值滤波算法的优化与实现

    总第238期2014年第4期 舰 船 电 子 工 程 Ship Electronic Engineering Vol.34No.437 基于 MATLAB中值滤波算法的优化与实现* 赵建春 刘力源 ( ...

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

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

最新文章

  1. 固定定位及布局知识总结
  2. HDU 1874 畅通工程续 2008浙大研究生复试热身赛(2)
  3. git stash操作
  4. SQL Server日期函数集合
  5. 【BZOJ3218】a+b problem (最小割 + 主席树)
  6. spring框架(六)之拦截器
  7. eclipse导入后将普通项目变为java项目build path,no action avilia
  8. Spring Cloud入门教程-Ribbon实现客户端负载均衡
  9. Quick BI 支持多种数据源进行多维分析
  10. Android应用保活方案的另类出路,让你应用长生不老,实战案例
  11. c语言程序设计高校饭卡系统,C语言课程设计-学生饭卡管理系统(45页)-原创力文档...
  12. 2021-07-29 vue事件冒泡和事件捕获,阻止冒泡和阻止浏览器默认行为
  13. 这黑科技从 B站 火到 GitHub
  14. 提高你修养的100句话
  15. c语言源程序自动评判系统,C语言源程序的自动评判系统.pdf
  16. 重庆APP开发价格费用如何计算?
  17. 废土艺术风格 2丨游戏主题概念设计
  18. html显示下滑线,html超链接添加下划线
  19. FPGA的UART信息回显
  20. thymeleaf配置

热门文章

  1. 数据库的隔离级别介绍
  2. 常用的四种sql增删改查
  3. 使用OpenStreetMap,Hot export tool,OSMNX下载地图资源
  4. java开发ria是指什么_Java RIA Demo
  5. 双目立体视觉中的坐标系与转换关系 [留意~摄影测量学与计算机视觉学科中的差异]
  6. 分享Node.js + Koa2 + MySQL + Vue.js 实战开发一套完整个人博客项目网站
  7. 【APIcould】连接手机设备运行调试
  8. 用小程序商城模板搭建商城【商城小程序】
  9. 【根据模板导出多sheet表格数据】
  10. 重启电脑后发现EPLAN又出现未激活情况