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

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

  在网络上很多的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 <= 0) || (Height <= 0))                            return IM_STATUS_INVALIDPARAMETER;
    if ((Channel != 1) && (Channel != 3))                        return IM_STATUS_INVALIDPARAMETER;

    if (Channel == 1)
    {
        Complex    *Data = (Complex*)malloc(Width * Height * sizeof(Complex));

        if (Data == NULL)     return IM_STATUS_OUTOFMEMORY;

        for (int Y = 0; Y < Height; Y++)
        {
            unsigned char *LinePS = Src + Y * Stride;                //    填充FFT变换的复数数据
            Complex *LinePD = Data + Y * Width;
            for (int X = 0; X < Width; X++)
            {
                LinePD[X].Real = LinePS[X];
                LinePD[X].Imag = 0;
            }
        }
        IM_FFT2D(Data, Data, Width, Height, false, 0, 0);            //    FFT变换
        IM_FFTShift(Data, Data, Width, Height);                      //    平移中心到图像的中心
        for (int Y = 0; Y < Height; Y++)                             //    FFT变换的结果乘以用于消除与纹理对应的频率的滤波器
        {
            unsigned char *LinePS = Mask + Y * Stride;
            Complex *LinePD = Data + Y * Width;
            for (int X = 0; 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, 0, 0);        //    FFT逆变换

        for (int Y = 0; Y < Height; Y++)                        //    转换成图像
        {
            Complex *LinePS = Data + Y * Width;
            unsigned char *LinePD = Dest + Y * Stride;
            for (int X = 0; 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 <= 0) || (Height <= 0))                    return IM_STATUS_INVALIDPARAMETER;
    if (Channel != 1)                                    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 = 0;
    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, 2, false);        //    先膨胀下(最大值),注意膨胀和腐蚀函数不支持InPlace操作
    if (Status != IM_STATUS_OK)        goto FreeMemory;
    Status = IM_Erode(Dest, Temp, Width, Height, Stride, 2, 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, 50);        //    执行半径为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下。

原文地址:https://www.cnblogs.com/Imageshop/p/11625895.html

时间: 2024-10-13 14:41:05

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

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

Windows下FFTW库的安装: 1. 从网站http://www.fftw.org/install/windows.html上下载最新的预编译文件:    32-bit version: fftw-3.2.2.pl1-dll32.zip (1.8MB)    64-bit version: fftw-3.2.2-dll64.zip (2.2MB) (这里建议下载32位机文件/64位向下兼容) 2.使用Lib.exe生成相应的Lib文件: lib命令是vs2010自带的工具,在vs2010安装

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

本文重点主要不在于FFT的SSE优化,而在于使用FFT实现快速卷积的相关技巧和过程. 关于FFT变换,有很多参考的代码,特别是对于长度为2的整数次幂的序列,实现起来也是非常简易的,而对于非2次幂的序列,就稍微有点麻烦了,matlab中是可以实现任意长度FFT的,FFTW也是可以的,而Opencv则有选择性的实现了某些长度序列的变换,查看Opencv的代码,可以发现其只有对是4的整数次幂的数据部分采用了SSE优化,比如4.16.64.256.1024这样的序列部分,因此基4的FFT是最快的,而剩余

记五 一 qbxt

记五 一 t qbxt 学习 --RMY 2018/5/9 9前言: 期盼已久的五一学习已经结束了,刚到后励志好好听课,然而听完一天后就抛之脑后了,开始了对于文字语言的探索(看小说),不得不说中华语言不愧是博大精深啊,两次考试还算是比较凑活吧,第二次搞得我一头雾水(评测机的输出和我的输出不一样?) ,这我就忍了,忍不了的是那天下午和我同学吃的那顿烧烤大餐,一顿饭吃了我一天的伙食费,兴致勃勃的走进烧烤店,嗯,摆设还不错,烤串种类也不少啊,我进去比较晚,刚进门同学问我,来40串羊肉的(看样子他好像已

每日算法之十五:threesumClosset

Given an array S of n integers, find three integers in S such that the sum is closest to a given number, target. Return the sum of the three integers. You may assume that each input would have exactly one solution. For example, given array S = {-1 2

每日算法之三十五:Wildcard Matching

模式匹配的实现,'?'代表单一字符,'*'代表任意多的字符,写代码实现两个字符串是否匹配. Implement wildcard pattern matching with support for '?' and '*'.. '?' Matches any single character. '*' Matches any sequence of characters (including the empty sequence). The matching should cover the en

java实现FFT变换(转)

源:java实现FFT变换 /************************************************************************* * Compilation: javac FFT.java * Execution: java FFT N * Dependencies: Complex.java * * Compute the FFT and inverse FFT of a length N complex sequence. * Bare bon

浅谈算法和数据结构: 五 优先级队列与堆排序

转载自:http://www.cnblogs.com/yangecnu/p/Introduce-Priority-Queue-And-Heap-Sort.html 浅谈算法和数据结构: 五 优先级队列与堆排序 在很多应用中,我们通常需要按照优先级情况对待处理对象进行处理,比如首先处理优先级最高的对象,然后处理次高的对象.最简单的一个例子就是,在手机上玩游戏的时候,如果有来电,那么系统应该优先处理打进来的电话. 在这种情况下,我们的数据结构应该提供两个最基本的操作,一个是返回最高优先级对象,一个是

传感器仿真平台——平台与算法接口(五)

传感器仿真平台希望做的是简化传感器仿真时,对于前台窗口.参数管理.UI绘制和数据收集等方面的操作,使得使用者有更多的时间去关心算法的实现而非其他繁杂琐事.所以说平台与算法的接口是重点.写的好,表示框架的通用性就好,不然等于是废了. 定义如上图,设置一个Method接口,他需要使用算法必须的配置参数表和回调函数作为参数初始化. process表示仿真实验的运行,这里设定输入参数为实验所需的传感器的参数. 这里或许会有疑问,数据运行结果怎么给出呢?请看回调函数,Callback其实是一个函数指针,实

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

搜索引擎算法研究专题五:TF-IDF详解 2017年12月19日 ? 搜索技术 ? 共 1396字 ? 字号 小 中 大 ? 评论关闭 TF-IDF(term frequency–inverse document frequency)是一种用于资讯检索与资讯探勘的常用加权技术.TF-IDF是一种统计方法,用以评估一字词对于一个文件集或一个语料库中的其中一份文件的重要程度.字词的重要性随著它在文件中出现的次数成正比增加,但同时会随著它在语料库中出现的频率成反比下降.TF-IDF加权的各种形式常被搜