这个课题在很久以前就已经有所接触,不过一直没有用代码去实现过。最近买了一本《机器视觉算法与应用第二版》书,书中再次提到该方法:使用傅里叶变换进行滤波处理的真正好处是可以通过使用定制的滤波器来消除图像中某些特定频率,例如这些特定频率可能代表着图像中重复出现的纹理。

  在网络上很多的PS教程中,也有提到使用FFT来进行去网纹的操作,其中最为广泛的是使用PS小插件FOURIER TRANSFORM,使用过程为:打开图像--进行FFT RGB操作,然后定位到红色通道,选取通道中除了最中心处的之外的白点区域,然后填充黑色,在返回综合通道,点击IFFT RGB,就OK了,

      

               原图                            FFR RGB  频谱图

     

   用于消除与纹理对应的频率的滤波器                      IFFT RGB处理的结果图  

  针对这一幅,我曾尝试在PS中用其他的方法来去背景纹理,可是一般去网的同时也把相片模糊了,只有FFT去网纹插件能完美去掉相片的网纹而且不损伤画质。

  这个插件有个特性,他要求输入必须是3通道或者4通道的图,但是用他处理完成后的图虽然表面上看还是3通道还是4通道的,但是他已经失去了彩色信息了,我们注意到他在进行FFT RGB操作后,RGB三个通道中,R通道保存了频谱图,G通道了保存了相位图,B通道为固定值128,频谱和相位组合在一起,只能回复一个通道的信息,因此处理后的图也只能是一个颜色了,这是这个插件的缺陷或者说作为插件的必然性。

  按照这个思路,如果用户提供了用于消除与纹理对应的频率的滤波器,则该过程的一个大概算法流程如下所示:

int IM_TextureRemoval(unsigned char *Src, unsigned char *Mask, unsigned char *Dest, int Width, int Height, int Stride)
{
int Channel = Stride / Width;
if ((Src == NULL) || (Dest == NULL)) return IM_STATUS_NULLREFRENCE;
if ((Width <= ) || (Height <= )) return IM_STATUS_INVALIDPARAMETER;
if ((Channel != ) && (Channel != )) return IM_STATUS_INVALIDPARAMETER; if (Channel == )
{
Complex *Data = (Complex*)malloc(Width * Height * sizeof(Complex)); if (Data == NULL) return IM_STATUS_OUTOFMEMORY; for (int Y = ; Y < Height; Y++)
{
unsigned char *LinePS = Src + Y * Stride; // 填充FFT变换的复数数据
Complex *LinePD = Data + Y * Width;
for (int X = ; X < Width; X++)
{
LinePD[X].Real = LinePS[X];
LinePD[X].Imag = ;
}
}
IM_FFT2D(Data, Data, Width, Height, false, , ); // FFT变换
IM_FFTShift(Data, Data, Width, Height); // 平移中心到图像的中心
for (int Y = ; Y < Height; Y++) // FFT变换的结果乘以用于消除与纹理对应的频率的滤波器
{
unsigned char *LinePS = Mask + Y * Stride;
Complex *LinePD = Data + Y * Width;
for (int X = ; X < Width; X++)
{
LinePD[X].Real *= LinePS[X] * IM_INV255;
LinePD[X].Imag *= LinePS[X] * IM_INV255;
}
}
IM_IFFTShift(Data, Data, Width, Height); // 在反中心化
IM_FFT2D(Data, Data, Width, Height, true, , ); // FFT逆变换 for (int Y = ; Y < Height; Y++) // 转换成图像
{
Complex *LinePS = Data + Y * Width;
unsigned char *LinePD = Dest + Y * Stride;
for (int X = ; X < Width; X++)
{
LinePD[X] = IM_ClampToByte(LinePS[X].Real);
}
}
free(Data);
}
else
{ }
return IM_STATUS_OK; }

  这个过程也是非常简单的。

  对于彩色的图像,可以把他们先劈成3个独立的通道,然后调用上述单通道的处理方法,然后在合成。

  不过这个方法还是有限制的,他能处理的对象是有非常严重网纹的图像,我们测试过对于普通的身份证照片、摩尔纹等是起不到去除作用的,从频谱上来说,就是要在频谱上能看到分布在四周处有一些很明显的独立的亮点。这些亮点就对应着纹理的频率。

  上面的过程需要人工的参与,我们这里进行一下扩展,尝试下对这类图像进行自动的纹理去除。这里的核心是找到纹理的频率,也就是那些白色独立的亮点。

  我们看上面的FFT频谱图,这种显示基本上都是对直接进行FFT变换后的浮点数据进行对数变换后,在线性映射到0到255范围内的,有进行了log操作,数据压缩了很多,导致频谱图的对比度不是很强,也不利于我们分隔出那些亮点,如果我们不记性这种操作,而是直接绝对值Clamp显示,大概能得到下面的效果:

      

  这种效果的FFT图很明显更有利于纹理特征的提取。

  下面的步骤就是:OSTU二值化 -- 》膨胀  --》 腐蚀 -- 》 反色  ---》中心核保留  -- 》中值  得到纹理频率的滤波器。整个效果如下图:

  

          二值化                     膨胀(半径2)                腐蚀(半径2)

  

      反色                    保留中心区域                中值(半径1)

  稍微分析下原理吧(也不一定科学)。

  首先二值化,没啥好说的。 二值后,我们看到白色部分有很多零碎的部分,特别是图像的中心区域的零碎化对最后的效果有非常不好的影响(我们必须保持中心部分没啥变化),所以后续使用了开操作来改善效果,先膨胀后腐蚀。 接着我们反色一下,因为后续的滤波器是非中心区域的白色部分是要变为黑色的,第五步,也是比较核心的步骤,我们需要把中心部分的黑色部分变为白色,因为这部分保留着图像的大部分信息, 这里我们可以采用基于4领域的区域生长法,因为在频谱中的中心点,这一点二值后肯定是白色的,在反色后就是白色,就以这一点为种子点,向四周进行区域生长,这样就可以把中心处的黑色反色过来,而其他地方的黑色保持不变。

  第五步的中值,或者可以用其他模糊来代替,也是有点必要的,对于有些图像,经过前面的处理后,有些核心的线(垂直或者水平方向)也被标记为黑色的了,正在处理完成的图像中会带来原本没有的新条纹。

  

                原图                                     频谱图

            去除中值滤波后的滤波器                                  对应的结果(有瑕疵)

 

          增加中值后的滤波器                                    对应的结果

  上述过程先关的函数如下所示:

//    根据频谱图预估纹理的频谱蒙版区域,支持InPlace操作
int IM_GetTextureMask(unsigned char *Src, unsigned char *Dest, int Width, int Height, int Stride)
{
int Channel = Stride / Width;
if ((Src == NULL) || (Dest == NULL)) return IM_STATUS_NULLREFRENCE;
if ((Width <= ) || (Height <= )) return IM_STATUS_INVALIDPARAMETER;
if (Channel != ) return IM_STATUS_INVALIDPARAMETER;
int Status = IM_STATUS_OK;
unsigned char *Temp = (unsigned char *)malloc(Height * Stride * sizeof(unsigned char));
if (Temp == NULL){ Status = IM_STATUS_OUTOFMEMORY; goto FreeMemory; } int Threshold = ;
Status = IM_GetOSTUThreshold(Src, Width, Height, Stride, Threshold); // 使用OSTU方法二值化
if (Status != IM_STATUS_OK) goto FreeMemory;
Status = IM_Threshold(Src, Temp, Width, Height, Stride, Threshold); // 二值化
if (Status != IM_STATUS_OK) goto FreeMemory;
Status = IM_Dilate(Temp, Dest, Width, Height, Stride, , false); // 先膨胀下(最大值),注意膨胀和腐蚀函数不支持InPlace操作
if (Status != IM_STATUS_OK) goto FreeMemory;
Status = IM_Erode(Dest, Temp, Width, Height, Stride, , false); // 然后在腐蚀(最小值),恢复原来的差不多大小,但是这样中心区域不相邻的点就少了很多
if (Status != IM_STATUS_OK) goto FreeMemory;
Status = IM_Invert(Temp, Dest, Width, Height, Stride); // 这个时候的图,纹理的频谱和其他核心能量区域都还是白色,为后续的处理需要先反色
if (Status != IM_STATUS_OK) goto FreeMemory;
Status = IM_InvertCenter(Dest, Temp, Width, Height, Stride); // 把中心的能量区域保留(白色),其他的纹理的频谱删除(黑色)
if (Status != IM_STATUS_OK) goto FreeMemory;
Status = IM_MedianBlur(Temp, Dest, Width, Height, Stride, , ); // 执行半径为1的中值,这样可能可以减少部分垂直或者水平的核心能力被删除
if (Status != IM_STATUS_OK) goto FreeMemory; FreeMemory:
if (Temp != NULL) free(Temp);
return Status;
}

  我们注意到,上面的操作对纹理处频率处对应的滤波器系数都为0了,也就是这一块的信息全部被消除了,当然实际操作时也可以稍微羽化一下,对最后的结果影响不大。

《任何未通知的转载或转发,都是猪狗不如的作为》。

  根据上述的步骤,有选择性的处理了几幅图,结果如下所示:

   

   

   

  可以看出,虽然能再一定程度上去除网纹,但是也就有一些去除的不完全,这主要还是因为自动提取的滤波器还是不够准确,要想获取更为理想的结果,必须手动的予以修缮。

  对于常规的图片,或者说纹理信息不明显的图,及时执行了上面的去纹理,图片也基本上没有什么变化,因为按照上述方法得到的滤波器基本都为白色。

  本文算法的测试例程见 : http://files.cnblogs.com/files/Imageshop/SSE_Optimization_Demo.rar,位于菜单FFT-->TextureRemoval下。

【算法随记五】使用FFT变换自动去除图像中严重的网纹。的更多相关文章

  1. Matlab实现Hough变换检測图像中的直线

    Hough变换的原理: 将图像从图像空间变换至參数空间.变换公式例如以下: 变换以后,图像空间与參数空间存在下面关系: 图像空间中的一点在參数空间是一条曲线,而图像空间共线的各点相应于參数空间交于一点 ...

  2. 查找图像中椭圆轮廓的快速随机hough变换

    查找图像中椭圆轮廓的快速随机hough变换 图像中椭圆轮廓的查找在视频监控等领域有着广泛的应用,经典hough变换给我们提供了一种查找各种图形轮廓的方法,特别是在直线查找方面具有非常高的精确度.但是由 ...

  3. SSE图像算法优化系列十一:使用FFT变换实现图像卷积。

    本文重点主要不在于FFT的SSE优化,而在于使用FFT实现快速卷积的相关技巧和过程. 关于FFT变换,有很多参考的代码,特别是对于长度为2的整数次幂的序列,实现起来也是非常简易的,而对于非2次幂的序列 ...

  4. SENet(Squeeze-and-Excitation Networks)算法笔记---通过学习的方式来自动获取到每个特征通道的重要程度,然后依照这个重要程度去提升有用的特征并抑制对当前任务用处不大的特征

    Momenta详解ImageNet 2017夺冠架构SENet 转自机器之心专栏 作者:胡杰 本届 CVPR 2017大会上出现了很多值得关注的精彩论文,国内自动驾驶创业公司 Momenta 联合机器 ...

  5. java实现FFT变换(转)

    源:java实现FFT变换 /************************************************************************* * Compilati ...

  6. 安装fftw到window(vs2010)及使用fftw库函数实现4096点fft变换计算

    Windows下FFTW库的安装: 1. 从网站http://www.fftw.org/install/windows.html上下载最新的预编译文件:    32-bit version: fftw ...

  7. SpringBoot启动流程分析(五):SpringBoot自动装配原理实现

    SpringBoot系列文章简介 SpringBoot源码阅读辅助篇: Spring IoC容器与应用上下文的设计与实现 SpringBoot启动流程源码分析: SpringBoot启动流程分析(一) ...

  8. 搜索引擎算法研究专题五:TF-IDF详解

    搜索引擎算法研究专题五:TF-IDF详解 2017年12月19日 ⁄ 搜索技术 ⁄ 共 1396字 ⁄ 字号 小 中 大 ⁄ 评论关闭   TF-IDF(term frequency–inverse ...

  9. Java实现 蓝桥杯 算法训练 第五次作业:字符串排序

    试题 算法训练 第五次作业:字符串排序 问题描述 输入一个小写字符串,按从小到大的顺序输出. 输入格式 bcaed 输出格式 abcde 顶格输出,中间没有空格 样例输入 一个满足题目要求的输入范例. ...

随机推荐

  1. 2018 Petrozavodsk Winter Camp, Yandex Cup

    A. Ability Draft solved by RDC 60min start, 148 min AC, 1Y 题意:两只 Dota 队伍,每队 \(n\) 个英雄,英雄一开始无技能,他们需要按 ...

  2. CSU 1804: 有向无环图 拓扑排序 图论

    1804: 有向无环图 Submit Page   Summary   Time Limit: 5 Sec     Memory Limit: 128 Mb     Submitted: 716    ...

  3. poj 2406 Power Strings(kmp next的应用)

    题目链接:http://poj.org/problem?id=2406 题意:就是求一个字符串最多有几个相同的小字符串组成. 题解:直接求一下next然后找到长度,长度就是len-1-next[len ...

  4. HTML图片死活不显示

    图片不显示: 1.路径 2.名称 3.少写了" ... " 正确的例子:“../images/dd.png” 4.多写了一个“/” ,或者少写了一个“ . ” ,没错.不是三个点, ...

  5. rpm简单使用

    rpm描述:利用源码包编译成rpm时,会去指定安装好这个包的位置本质:解压,然后拷贝到相关的目录,然后执行脚本 vstpd-3.0.2-9.el7.x86_64.rpm 包名 版本 release 架 ...

  6. Java多线程(十四):Timer

    Timer schedule(TimerTask task, Date time) 该方法在指定日期执行任务,如果是过去的时间,这个任务会立即被执行. 执行时间早于当前时间 示例代码,当前时间是201 ...

  7. 新建servlet工程

    1.选择新建Dynamic  Web Project 2.选择服务器和版本(2.5) 3.src目录下新建一个包 4.包里面新建一个类 5.实现Servlet接口(通过http协议访问) 6.serv ...

  8. Sublime Text 3 中实现编译C语言程序

    这个是真坑,感觉用devc++写c程序特别的不爽,所以就用了sublime,但是,编译的时候又有不少问题, 下面就把我踩的坑记录下来 tools>Build System>New Buil ...

  9. 50 (OC)* URL Scheme 网页地址协议

    在Xcode 9 下,新建的工程,在plist文件中注册URL Schemes,从safari无法打开问题 1:URL Scheme是什么 2:URL Scheme有什么作用 3:URL Scheme ...

  10. centos C++ ccache llvm编译环境配置

    下载ccache rpm包wget https://centos.pkgs.org/6/epel-x86_64/ccache-3.1.6-2.el6.x86_64.rpm.htmlyum -y ins ...