基于记忆性的中值滤波O(r)与O(1)复杂度的算法实现
本文参考博客:https://www.cnblogs.com/Imageshop/archive/2013/04/26/3045672.html
原生的中值滤波是基于排序算法的,这样的算法复杂度基本在O(r2)左右,当滤波半径较大时,排序算法就显得很慢。对此有多种改进算法,这里介绍经典
的Huang算法与O(1)算法,两者都是基于记忆性的算法,只是后者记性更强。
排序算法明显的一个不足之处就是无记忆性。当核向右移动一列后,只是核的最左和最右列数据发生了变化,中间不变的数据应当被存储起来,而排序算法
并没有做到这点。Huang算法的思想是建立一个核直方图,来统计核内的各灰度的像素数。当核向右移动时,就将新的一列所有数据加入到直方图中,同时将最
左列的旧数据从直方图中删除,如下图所示。这样做使得大部分数据能够被记忆,减少重复操作。当直方图更新完毕后,就可以通过从左到右累计像素来找到中值。
下面是算法具体实现步骤与代码:
1.每行开始都将直方图、像素计数、中值变量清零,将核覆盖的所有像素加到直方图中。
2.计算中值,sumcnt为小于中值灰度的像素数和。如果当前sumcnt大于等于阈值,则表明,实际中值比当前median小,则直方图向左减去像素数,同时median
也减小,直到sumcnt小于阈值,则此时median为中值(sumcnt加上中值像素数将大于或等于阈值);如果sumcnt加上median像素数仍小于阈值,表明,实
际中值比当前median大,则直方图向右累加像素数,同时median增加,直到sumcnt加上median像素数大于等于阈值,则此时median为中值(sumcnt小于阈值)。
3.核每向右移动一列,更新核直方图,比较每一个新加入的灰度值与median,小于则median加1,比较每一个减去的灰度值与median,小于则median减1。执行
一次步骤2后继续移动核,直到整幅图都被滤波完毕。
%中值滤波黄法
%输入:8位深度图像、滤波半径(*3的半径为1,中值百分比)
function grayimg=medianfilterhuang(grayimg,radius,percentage)
[sizex,sizey]=size(grayimg);
thres=int16(((*radius+)^*percentage));
%扩展边界
extendimg=int16(horzcat(repmat(grayimg(:,),,radius),grayimg,repmat(grayimg(:,sizey),,radius)));
extendimg=int16(vertcat(repmat(extendimg(,:),radius,),extendimg,repmat(extendimg(sizex,:),radius,)));
for i=radius+:sizex+radius
%初始化直方图,每行第一个将所有像素加入直方图
histogram=int16(zeros(,));
sumcnt=int16();
median=int16();
for j=radius+:sizey+radius
%遍历卷积覆盖行
for k=i-radius:i+radius
if (j>radius+)
%删去左列,加入新右列
histogram(extendimg(k,j-radius-)+)=histogram(extendimg(k,j-radius-)+)-;
histogram(extendimg(k,j+radius)+)=histogram(extendimg(k,j+radius)+)+;
%只关心中值灰度以左的像素数量
if (extendimg(k,j-radius-)<median)
sumcnt=sumcnt-;
end
if (extendimg(k,j+radius)<median)
sumcnt=sumcnt+;
end
else
%行首
for h=j-radius:j+radius
histogram(extendimg(k,h)+)=histogram(extendimg(k,h)+)+;
end
end
end
%sumcnt不将中值像素数累加进去
%旧中值偏大
while(sumcnt>=thres)
median=median-;
sumcnt=sumcnt-histogram(median+);
end
%旧中值偏小
while(sumcnt+histogram(median+)<thres)
sumcnt=sumcnt+histogram(median+);
median=median+;
end
grayimg(i-radius,j-radius)=median;
end
end
end
Huang算法
性能分析发现,Huang算法的大部分时间花在了更新核直方图上。并且由于它是O(r)的复杂度,因此当滤波半径变大后,更新直方图部分花费的时间也变多,而计算
中值部分所花时间一直都是固定的,因为它的复杂度是O(1),与滤波半径无关。那么为了使直方图更新的复杂度能降到O(1),就要增强算法的记忆性。可以看到以相邻行
相同列为中心的核之间也存在重合,这跟之前以相邻列相同行为中心的核存在重合类似。于是,我们可以为每列都维护一个直方图,而核直方图可以由这些列直方图相加
得到,更新核的时候,最右边的一列直方图向下移动一行,就更新了列直方图,再减去最左边一列直方图,核就得到更新。这样每次更新直方图都只要移动一次列直方图,
而其余的都是直方图的加减操作,值得注意的是,这样的实现很完美得利用了可并行的矩阵加减来代替顺序执行的for迭代。因此,虽然更新过程看上去有许多加减操作,
实际上都是可快速并行执行的。
还有一点,由于并行化的矩阵加减,就不能用sumcnt来跟踪阈值变化了。如果直接迭代累加直方图,最坏的情况是要执行255加法。为了使中值计算更快,可以采用
两层直方图,高层用于保存高4位(直方图大小为16*1),低层保存全位(直方图大小为256*1)。每次先累加高层进行范围缩小,再从相应范围累加低层找到中值。只是
这样就牺牲了内存空间。
下面是算法具体实现步骤与代码:
1.扩展图像边界,上边界扩展r+1行,其他边界扩展r行;为每列生成两层直方图,并为其初始化,添加各列的前2*r+1个像素;生成一个低层链接高层的索引矩阵。
2.每行开始清零核直方图,更新核覆盖的各列直方图(向下移动一行),将各列加到核直方图中。
3.寻找中值,初始化sumcnt=0,这里它表示小于等于中值灰度的像素和,从左到右迭代高层直方图,直到sumcnt大于等于阈值;再从高层确定的范围内从右到左迭代
低层直方图,直到sumcnt(减去了迭代灰度的像素数)小于阈值,则当前迭代值为中值。
4.核每向右移动,更新列直方图与核直方图,执行一次步骤3,然后继续移动核,直到完成整个图的滤波。
function grayimg=medianfilterdp(grayimg,radius,percentage)
[sizex,sizey]=size(grayimg);
thres=int16(((*radius+)^*percentage));
%扩展边界
extendimg=int16(horzcat(repmat(grayimg(:,),,radius),grayimg,repmat(grayimg(:,sizey),,radius)));
extendimg=int16(vertcat(repmat(extendimg(,:),radius+,),extendimg,repmat(extendimg(sizex,:),radius,)));
%为每一列维护一个全位直方图和一个高位直方图
histogram_cols_low=int16(zeros(sizey+*radius,));
histogram_cols_high=int16(zeros(sizey+*radius,));
%中值计算所用的一个全位直方图和一个高位直方图
histogram_low=int16(zeros(,));
histogram_high=int16(zeros(,));
%全位转高位的加速索引
grayrange=int16(zeros(,));
for i=:
grayrange(:,(i-)*+:i*)=i;
end
%初始化各列直方图
for row=:*radius+
for col=:sizey+*radius
histogram_cols_low(col,extendimg(row,col)+)=histogram_cols_low(col,extendimg(row,col)+)+;
histogram_cols_high(col,grayrange(extendimg(row,col)+))=histogram_cols_high(col,grayrange(extendimg(row,col)+))+;
end
end for row=int16(radius+:sizex+radius+)
for col=int16(radius+:sizey+radius)
if(col>radius+)
%更新右边新列,删除左边旧列
histogram_cols_low(col+radius,extendimg(row+radius,col+radius)+)=histogram_cols_low(col+radius,extendimg(row+radius,col+radius)+)+;
histogram_cols_low(col+radius,extendimg(row-radius-,col+radius)+)=histogram_cols_low(col+radius,extendimg(row-radius-,col+radius)+)-;
histogram_cols_high(col+radius,grayrange(extendimg(row+radius,col+radius)+))=histogram_cols_high(col+radius,grayrange(extendimg(row+radius,col+radius)+))+;
histogram_cols_high(col+radius,grayrange(extendimg(row-radius-,col+radius)+))=histogram_cols_high(col+radius,grayrange(extendimg(row-radius-,col+radius)+))-;
histogram_low=histogram_low+histogram_cols_low(col+radius,:)-histogram_cols_low(col-radius-,:);
histogram_high=histogram_high+histogram_cols_high(col+radius,:)-histogram_cols_high(col-radius-,:);
else
histogram_low(:,:)=;
histogram_high(:,:)=;
%行首手动初始化各列
for index=:*radius+
histogram_cols_low(index,extendimg(row+radius,index)+)=histogram_cols_low(index,extendimg(row+radius,index)+)+;
histogram_cols_low(index,extendimg(row-radius-,index)+)=histogram_cols_low(index,extendimg(row-radius-,index)+)-;
histogram_cols_high(index,grayrange(extendimg(row+radius,index)+))=histogram_cols_high(index,grayrange(extendimg(row+radius,index)+))+;
histogram_cols_high(index,grayrange(extendimg(row-radius-,index)+))=histogram_cols_high(index,grayrange(extendimg(row-radius-,index)+))-;
histogram_low=histogram_low+histogram_cols_low(index,:);
histogram_high=histogram_high+histogram_cols_high(index,:);
end
end
%中值及之前的所有像素数
sumcnt=int16();
for gray_high=:
sumcnt=sumcnt+histogram_high(gray_high);
if (sumcnt>=thres)
break;
end
end for gray_low=gray_high*:-:(gray_high-)*+
sumcnt=sumcnt-histogram_low(gray_low);
if (sumcnt<thres)
break;
end
end grayimg(row-radius-,col-radius)=gray_low-;
end
end end
O(1)中值滤波
下面是两种算法的性能对比,可以发现,在滤波半径较小的时候,用Huang算法更好;当滤波半径>=8时,用后一种算法更快。实际中我们一般用不到大半径,但这两种
算法的思想很不错,尤其是第二种的空间换时间,我们可能可以发现横向存在重复性,但很容易忽略了纵向上的重复性。
基于记忆性的中值滤波O(r)与O(1)复杂度的算法实现的更多相关文章
- 基于Opencv的自适应中值滤波函数selfAdaptiveMedianBlur()
7.3.3 自适应滤波器 自适应中值滤波器 对于7.3.2节所讨论的中值滤波器,只要脉冲噪声的空间密度不大,性能还是可以的(根据经验需Pa和Pb小于0.2).本节将证明,自适应中值滤波器可以处理更大概 ...
- verilog实现中值滤波
前言 项目需要,想要实现算法中的其中一步即中值滤波,同时,因为图像处理部分中值滤波相对来说还是比较简单的,将中值滤波的硬件实现作为进入FPGA领域的第一次尝试.虽然说网上有较多关于中值滤波的文档,可是 ...
- opencv-11-中值滤波及自适应中值滤波
开始之前 在上一篇我们实现了读取噪声图像, 然后 进行三种形式的均值滤波得到结果, 由于我们自己写的均值滤波未作边缘处理, 所以效果有一定的下降, 但是总体来说, 我们得到的结果能够说明我们的算法执行 ...
- 基于FPGA的中值滤波算法实现
在这一篇开篇之前,我需要解决一个问题,上一篇我们实现了基于FPGA的均值滤波算法的实现,最后的显示效果图上发现有一些黑白色的斑点,我以为是椒盐噪声,然后在做基于FPGA的中值滤波算法的实验时,我发现黑 ...
- 基于MATLAB的中值滤波均值滤波以及高斯滤波的实现
基于MATLAB的中值滤波均值滤波以及高斯滤波的实现 作者:lee神 1. 背景知识 中值滤波法是一种非线性平滑技术,它将每一像素点的灰度值设置为该点某邻域窗口内的所有像素点灰度值的中值. 中值滤 ...
- 基于MATLAB的中值滤波算法实现
在实时图像采集中,不可避免的会引入噪声,尤其是干扰噪声和椒盐噪声,噪声的存在严重影响边缘检测的效果,中值滤波是一种基于排序统计理论的非线性平滑计数,能有效平滑噪声,且能有效保护图像的边缘信息,所以被广 ...
- OpenCV计算机视觉学习(4)——图像平滑处理(均值滤波,高斯滤波,中值滤波,双边滤波)
如果需要处理的原图及代码,请移步小编的GitHub地址 传送门:请点击我 如果点击有误:https://github.com/LeBron-Jian/ComputerVisionPractice &q ...
- 学习 opencv---(8)非线性滤波:中值滤波,双边滤波
正如我们上一篇文章中讲到的,线性滤波可以实现很多种不同的图像变换.然而非线性滤波,如中值滤波器和双边滤波器,有时可以达到更好的实现效果. 邻域算子的其他一些例子还有对 二值图像进行操作的形态学算子,用 ...
- verilog 实现中值滤波
图像信号在形成.传输和记录的过程中,由于成像系统.传输介质.工作环境和记录设备等的固有缺陷,不可避免地产生各种类型的噪声,降低了图像的质量,进而影响后续处理(如边缘检测.图像分割.特征提取.模式识别等 ...
随机推荐
- [日常] 解决github速度特别慢
执行下面这俩命令,找到对应的IP,增加host就能解决 nslookup github.global.ssl.fastly.Netnslookup github.com root@tao-PC:/va ...
- fiddler---Fiddler修改数据信息
在测试的过程中,可能我们会遇到需要修改一些数据查看请求返回内容是如何的,刚好Fiddler也可以满足我们的要求,Fiddler不仅可以抓包还可以修改包的内容 Fiddler修改数据原理 Fiddler ...
- 30.Java基础_接口_抽象类_具体类实现案例
public interface SpeakEnglish { public abstract void speak(); } public abstract class Person { priva ...
- ubuntu pdfium
dept_tool export PATH=`pwd`/depot_tools:"$PATH" gn工具在内
- selenium中driver.close()和driver.quit()的不同点
driver.quit()与driver.close()的不同:driver.quit(): Quit this driver, closing every associated windows;dr ...
- CentOS7 通过 devstack 安装 OpenStack
安装前的准备 修改源 (可跳过) 将下载源变更到国内可以时下载速度大大提升 打开下面的文件 vim /etc/yum.repos.d/CentOS-Base.repo 将原来的注释掉改成: [base ...
- C#中List<T>转DataTable
通过查询出来的类的集合的属性集合生成数据行,并通过属性获取每一个实体的指定属性的指定值
- mq代替db
系统有个很严重的性能问题,法国人浪费了半年多都没有解决,他们试图从sql的角度分析哪里能有改善,大方向错了,再努力也没用. 我接手以后,也走了点弯路,一上手觉得肯定能用cache解决问题,结果cach ...
- 快速傅立叶变换(FFT)
多项式 系数表示法 设\(f(x)\)为一个\(n-1\)次多项式,则 \(f(x)=\sum\limits_{i=0}^{n-1}a_i*x_i\) 其中\(a_i\)为\(f(x)\)的系数,用这 ...
- Win10安装 oracle11g 出现INS-13001环境不满足最低要求解决方法
Win10安装 oracle11g 出现INS-13001环境不满足最低要求 首先,打开你的解压后的database文件夹,找到stage,然后cvu,找到cvu_prereq.xml文件,用note ...