OpenCV图像处理专栏九 | 基于直方图的快速中值滤波算法
转载自: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图像处理专栏九 | 基于直方图的快速中值滤波算法相关推荐
- 直方图实现快速中值滤波opencv
中值滤波能够有效去除图像中的异常点,具有去除图像噪声的作用.传统中值滤波的算法一般都是在图像中建立窗口,然后对窗口内的所有像素值进行排序,选择排序后的中间值作为窗口中心像素滤波后的值.由于这个做法在每 ...
- 高效快速中值滤波算法c语言,快速中值滤波及c语言实现.docx
. .. 快速中值滤波及c语言实现 学生姓名: 刘 勇 学 号: 6100410218 专业班级: 数媒101 [摘要]本文讨论了用c语言在微机上实现中值滤波及快速算法,在程序设计的过程中充分考虑到程 ...
- (MATLAB/C/Python)快速中值滤波
(MATLAB/C/Python)快速中值滤波 一.中值滤波 二.快速中值滤波 介绍 原理 优化 三.代码 MATLAB C Python 四.测试 其他 by HPC_ZY 最近一个项目中需要用到中 ...
- matlab中基于十字形窗口的滤波算法,#215;字形滤波窗口在Matlab自适应中值滤波算法中的应用 - 21ic中国电子网...
由于种种原因,图像在生成.传输.变换等过程中往往会受到各种噪声的污染,从而导致图像质量退化.噪声信号的滤波是图像处理的基本任务之一,主要有线性滤波和非线性滤波两种方法.线性滤波方法一般具有低通特性,而 ...
- matlab实现 中值滤波去除基线漂移,快速中值滤波在滤除心电信号基线漂移中的应用...
[摘要]文中给出了一种非线性的滤除心电信号基线漂移的滤波方法,把基于排序统计理论的快速中值滤波方法应用于处理心电信号,通过多次对心电信号中选择的窗口数据进行排序,然后取中值的方法来达到滤波的效果.试验 ...
- 图像处理之积分图应用二(快速边缘保留滤波算法)
图像处理之积分图应用二(快速边缘保留滤波算法) 一:基本原理 传统的图像边缘保留滤波算法-如高斯双边模糊.Mean-Shift模糊等计算复杂.效率比较低,虽然有各种手段优化或者快速计算方法,当时算法相 ...
- 快速中值滤波——Python实现
原理 中值滤波是空域中常用的一种滤波方式,是一种非线性的滤波.它的原理就是将窗口像素排序,取中值,然后移动窗口,不断重复取中值的过程. 但是,可以发现,每次移动窗口,都需要对像素点进行排序,从而选取中 ...
- matlab中值滤波法算法,基于MATLAB中值滤波算法的优化与实现
总第238期2014年第4期 舰 船 电 子 工 程 Ship Electronic Engineering Vol.34No.437 基于 MATLAB中值滤波算法的优化与实现* 赵建春 刘力源 ( ...
- 快速中值滤波在心电图ECG中的应用
1.算法介绍和实现 首先来搞明白,什么是快速中值滤波? 快速中值滤波非常简单,就是用过去连续N个数据,再对这N个数据进行排序,取排序后的中间那个数据,做为当前的输出,N即为窗口的长度. 算法实现: 1 ...
最新文章
- 固定定位及布局知识总结
- HDU 1874 畅通工程续 2008浙大研究生复试热身赛(2)
- git stash操作
- SQL Server日期函数集合
- 【BZOJ3218】a+b problem (最小割 + 主席树)
- spring框架(六)之拦截器
- eclipse导入后将普通项目变为java项目build path,no action avilia
- Spring Cloud入门教程-Ribbon实现客户端负载均衡
- Quick BI 支持多种数据源进行多维分析
- Android应用保活方案的另类出路,让你应用长生不老,实战案例
- c语言程序设计高校饭卡系统,C语言课程设计-学生饭卡管理系统(45页)-原创力文档...
- 2021-07-29 vue事件冒泡和事件捕获,阻止冒泡和阻止浏览器默认行为
- 这黑科技从 B站 火到 GitHub
- 提高你修养的100句话
- c语言源程序自动评判系统,C语言源程序的自动评判系统.pdf
- 重庆APP开发价格费用如何计算?
- 废土艺术风格 2丨游戏主题概念设计
- html显示下滑线,html超链接添加下划线
- FPGA的UART信息回显
- thymeleaf配置
热门文章
- 数据库的隔离级别介绍
- 常用的四种sql增删改查
- 使用OpenStreetMap,Hot export tool,OSMNX下载地图资源
- java开发ria是指什么_Java RIA Demo
- 双目立体视觉中的坐标系与转换关系 [留意~摄影测量学与计算机视觉学科中的差异]
- 分享Node.js + Koa2 + MySQL + Vue.js 实战开发一套完整个人博客项目网站
- 【APIcould】连接手机设备运行调试
- 用小程序商城模板搭建商城【商城小程序】
- 【根据模板导出多sheet表格数据】
- 重启电脑后发现EPLAN又出现未激活情况