优化IPOL网站中基于DCT(离散余弦变换)的图像去噪算法(附源代码)。

news/2024/5/13 9:54:45/文章来源:https://blog.csdn.net/chuifuhuo6864/article/details/100886688

  

     在您阅读本文前,先需要告诉你的是:即使是本文优化过的算法,DCT去噪的计算量依旧很大,请不要向这个算法提出实时运行的苛刻要求。  

  言归正传,在IPOL网站中有一篇基于DCT的图像去噪文章,具体的链接地址是:http://www.ipol.im/pub/art/2011/ys-dct/,IPOL网站的最大特点就是他的文章全部提供源代码,而且可以基于网页运行相关算法,得到结果。不过其里面的代码本身是重实现论文的过程,基本不考虑速度的优化,因此,都相当的慢。

      这篇文章的原理也是非常简单的,整个过程就是进行dct变换,然后在dct域进行硬阈值操作,再反变换回来。但是DCT变换不是针对整幅图进行处理,而是基于每个像素点的领域(这里使用的8领域或者16领域),每次则移动一个像素。IPOL上提供的代码函数也很少,但是一个关键的问题就是内存占用特别夸张,我们贴他的部分代码:

 

// Transfer an image im of size width x height x channel to sliding patches of // size width_p x height_p xchannel. // The patches are stored in patches, where each ROW is a patch after being // reshaped to a vector.
void Image2Patches(vector<float>& im, vector< vector< vector< vector< float > > > >& patches, int width, int height, int channel, int width_p, int height_p) { int size1 = width * height; int counter_patch = 0; // Loop over the patch positionsfor (int j = 0; j < height - height_p + 1; j ++) for (int i = 0; i < width - width_p + 1; i ++) { int counter_pixel = 0; // loop over the pixels in the patchfor (int kp = 0; kp < channel; kp++) for (int jp = 0; jp < height_p; jp ++) for (int ip = 0; ip < width_p; ip ++) { patches[counter_patch][kp][jp][ip] = im[kp*size1 + (j+jp)*width + i + ip]; counter_pixel ++; } counter_patch ++; } }

  看到这里的patches了,他的作用是保存每个点周边的8*8领域的DCT变换的结果的,即使使用float类型的变量,也需要约Width * Height * 8 * 8 * sizeof(float)个字节的数组,假定宽度和高度都为1024的灰度图,这个数据量为256MB,其实256MB的内存在现在机器来说毫无压力,可这里要求是连续分布内存,则很有可能分配失败,在外部表现的错误则是内存溢出。我们首先来解决这个问题。

  我们来看看原作者的代码中patches的主要作用,见下面这部分代码:

    // Loop over the patch positionsfor (int j = 0; j < height - height_p + 1; j ++) for (int i = 0; i < width - width_p + 1; i ++) { int counter_pixel = 0; // loop over the pixels in the patchfor (int kp = 0; kp < channel; kp++) for (int jp = 0; jp < height_p; jp ++) for (int ip = 0; ip < width_p; ip ++) { im[kp*size1 + (j+jp)*width + i + ip] += patches[counter_patch][kp][jp][ip]; im_weight[kp*size1 + (j+jp)*width + i + ip] ++; counter_pixel ++; } counter_patch ++; } // Normalize by the weightfor (int i = 0; i < size; i ++) im[i] = im[i] / im_weight[i];

  可见patches主要是为了保存每个点领域的DCT变换的数据,然后累加,上述四重循环外围两个是图像的宽度和高度方向,内部两重则是DCT的变换数据的行列方向,如果我们把DCT的行列方向的循环提到最外层,而把图像的宽度和高度方向的循环放到内存,其一就是整个过程只需要一个8*8*sizeof(float)大小的重复利用的内存,第二就是正好符号了内部放大循环,外部放小循环的优化准则,在速度和内存占用上都有重大提高。

      我们来继续优化,在获取每个点的领域时,传统的方式需要8*8个循环,那整个图像就需要Width * Height * 8 * 8次了, 这个数据量太可观了,在图像处理中任意核卷积(matlab中conv2函数)的快速实现 一文中共享的代码里提到了一种方式,大约只需要Width * Height * 8次读取,并且其cache命中率还要高很多,具体的方式可参考本文附带的代码。

      继续可以优化的地方就是8*8点的浮点DCT变换了。原文提供一维DCT变换的代码如下:

for (int j = 0; j < 8; j ++) { out[j] = 0; for (int i = 0; i < 8; i ++) { out[j] += in[i] * DCTbasis[j][i]; } }

     就是两个循环,共64次乘法和64次加法,上面的DCTbasis为固定的DCT系数矩阵。

  而一维的IDCT的变换代码为:

for (int j = 0; j < PATCHSIZE; j ++) { out[j] = 0; for (int i = 0; i < PATCHSIZE; i ++) { out[j] += in[i] * DCTbasis[i][j]; } }

      和DCT唯一的区别仅仅是DCTbasis的行列坐标相反。

      这种代码一看就想到了有SSE进行优化,PATCHSIZE为8 正好是两个SSE浮点数m128的大小,乘法和加法都有对应的SSE函数,一次性可进行4个浮点加法和浮点乘法,效率当然会高很多,优化后的代码如下所示:

/// <summary>
/// 8*8的一维DCT变换及其逆变换。 /// </summary>
/// <param name="In">输入的数据。</param>
/// <param name="Out">输出的数据。</param>
/// <param name="Invert">是否进行逆变换。</param>
/// <remarks> 1、输入和输出不能相同,即不支持in-place操作。</remarks>
/// <remarks> 2、算法只直接翻译IPOL上的,利用了SSE加速。</remarks>
/// <remarks> 3、在JPEG压缩等程序中的8*8DCT变换里优化的算法乘法比较少,但不好利用SSE优化,我用那里的代码测试还比下面的慢。</remarks>
/// <remarks> 4、有关8*8 DCT优化的代码见:http://insurgent.blog.hexun.com/1797358_d.html</remarks>
void DCT8X81D(float *In, float *Out, bool Invert) { __m128 Sum, A, B; const float *Data = (const float *)&Sum; A = _mm_load_ps(In); B = _mm_load_ps(In + 4); if (Invert == FALSE) { /*for (int Y = 0; Y < PATCHSIZE; Y ++) { Out[Y] = 0; for (int X = 0; X < PATCHSIZE; X ++) { Out[Y] += In[X] * DCTbasis[Y * PATCHSIZE + X]; } }*/ Sum = _mm_mul_ps(A, _mm_load_ps(DCTbasis)); Sum = _mm_add_ps(Sum, _mm_mul_ps(B, _mm_load_ps(DCTbasis + 4))); Out[0] = Data[0] + Data[1] + Data[2] + Data[3];                            // 这里是否还有更好的方式实现呢?
 Sum = _mm_mul_ps(A, _mm_load_ps(DCTbasis + 8)); Sum = _mm_add_ps(Sum, _mm_mul_ps(B, _mm_load_ps(DCTbasis + 12)));        // 不用一个Sum变量,用多个是不是还能提高指令级别的并行啊Out[1] = Data[0] + Data[1] + Data[2] + Data[3]; Sum = _mm_mul_ps(A, _mm_load_ps(DCTbasis + 16)); Sum = _mm_add_ps(Sum, _mm_mul_ps(B, _mm_load_ps(DCTbasis + 20))); Out[2] = Data[0] + Data[1] + Data[2] + Data[3]; Sum = _mm_mul_ps(A, _mm_load_ps(DCTbasis + 24)); Sum = _mm_add_ps(Sum, _mm_mul_ps(B, _mm_load_ps(DCTbasis + 28))); Out[3] = Data[0] + Data[1] + Data[2] + Data[3]; Sum = _mm_mul_ps(A, _mm_load_ps(DCTbasis + 32)); Sum = _mm_add_ps(Sum, _mm_mul_ps(B, _mm_load_ps(DCTbasis + 36))); Out[4] = Data[0] + Data[1] + Data[2] + Data[3]; Sum = _mm_mul_ps(A, _mm_load_ps(DCTbasis + 40)); Sum = _mm_add_ps(Sum, _mm_mul_ps(B, _mm_load_ps(DCTbasis + 44))); Out[5] = Data[0] + Data[1] + Data[2] + Data[3]; Sum = _mm_mul_ps(A, _mm_load_ps(DCTbasis + 48)); Sum = _mm_add_ps(Sum, _mm_mul_ps(B, _mm_load_ps(DCTbasis + 52))); Out[6] = Data[0] + Data[1] + Data[2] + Data[3]; Sum = _mm_mul_ps(A, _mm_load_ps(DCTbasis + 56)); Sum = _mm_add_ps(Sum, _mm_mul_ps(B, _mm_load_ps(DCTbasis + 60))); Out[7] = Data[0] + Data[1] + Data[2] + Data[3]; } else { /*for (int Y = 0; Y < PATCHSIZE; Y ++) { Out[Y] = 0; for (int X = 0; X < PATCHSIZE; X ++) { Out[Y] += In[X] * IDCTbasis[Y * PATCHSIZE + X]; } }*/ Sum = _mm_mul_ps(A, _mm_load_ps(IDCTbasis)); Sum = _mm_add_ps(Sum, _mm_mul_ps(B, _mm_load_ps(IDCTbasis + 4))); Out[0] = Data[0] + Data[1] + Data[2] + Data[3]; Sum = _mm_mul_ps(A, _mm_load_ps(IDCTbasis + 8)); Sum = _mm_add_ps(Sum, _mm_mul_ps(B, _mm_load_ps(IDCTbasis + 12))); Out[1] = Data[0] + Data[1] + Data[2] + Data[3]; Sum = _mm_mul_ps(A, _mm_load_ps(IDCTbasis + 16)); Sum = _mm_add_ps(Sum, _mm_mul_ps(B, _mm_load_ps(IDCTbasis + 20))); Out[2] = Data[0] + Data[1] + Data[2] + Data[3]; Sum = _mm_mul_ps(A, _mm_load_ps(IDCTbasis + 24)); Sum = _mm_add_ps(Sum, _mm_mul_ps(B, _mm_load_ps(IDCTbasis + 28))); Out[3] = Data[0] + Data[1] + Data[2] + Data[3]; Sum = _mm_mul_ps(A, _mm_load_ps(IDCTbasis + 32)); Sum = _mm_add_ps(Sum, _mm_mul_ps(B, _mm_load_ps(IDCTbasis + 36))); Out[4] = Data[0] + Data[1] + Data[2] + Data[3]; Sum = _mm_mul_ps(A, _mm_load_ps(IDCTbasis + 40)); Sum = _mm_add_ps(Sum, _mm_mul_ps(B, _mm_load_ps(IDCTbasis + 44))); Out[5] = Data[0] + Data[1] + Data[2] + Data[3]; Sum = _mm_mul_ps(A, _mm_load_ps(IDCTbasis + 48)); Sum = _mm_add_ps(Sum, _mm_mul_ps(B, _mm_load_ps(IDCTbasis + 52))); Out[6] = Data[0] + Data[1] + Data[2] + Data[3]; Sum = _mm_mul_ps(A, _mm_load_ps(IDCTbasis + 56)); Sum = _mm_add_ps(Sum, _mm_mul_ps(B, _mm_load_ps(IDCTbasis + 60))); Out[7] = Data[0] + Data[1] + Data[2] + Data[3]; } }

   当然,简单的循环并不是效率最高的算法,在标准的JPG压缩中就有着8*8的DCT变换,哪里的计算量有着更少的乘法和加法,在 http://insurgent.blog.hexun.com/1797358_d.html 中提出代码里,只有32次乘法和更少的加法,但是由于这个代码的向量性很差,是很难用SSE进行优化的,我实测的结果时他的代码比我用SSE优化后的速度慢。

     当进行2维的DCT的时候,其步骤为对每行先进行行方向的一维DCT,然后对结果转置,在对转置后的数据进行行方向一维DCT,得到的结果再次转置则为2维DCT。8*8的转置虽然直接实现基本不存在cache miss的问题,不过还是用有关的SSE来实现未尝不是个好主意,在intrinsic中提供了一个4*4浮点转置的宏_MM_TRANSPOSE4_PS,我们对这个宏稍作修改,修改后的代码如下:

//    http://stackoverflow.com/questions/5200338/a-cache-efficient-matrix-transpose-program
//    http://stackoverflow.com/questions/16737298/what-is-the-fastest-way-to-transpose-a-matrix-In-c
//    https://msdn.microsoft.com/en-us/library/4d3eabky(v=vs.90).aspx
//    高效的矩阵转置算法,速度约为直接编写的4倍, 整理时间 2015.10.12
inline void TransposeSSE4x4(float *Src, float *Dest) 
{__m128 Row0 = _mm_load_ps(Src);__m128 Row1 = _mm_load_ps(Src + 8);__m128 Row2 = _mm_load_ps(Src + 16);__m128 Row3 = _mm_load_ps(Src + 24);//        _MM_TRANSPOSE4_PS(Row0, Row1, Row2, Row3);                            //    或者使用这个SSE的宏
__m128 Temp0    = _mm_shuffle_ps(Row0, Row1, _MM_SHUFFLE(1, 0, 1, 0));      //    Row0[0] Row0[1] Row1[0] Row1[1]    __m128 Temp2    = _mm_shuffle_ps(Row0, Row1, _MM_SHUFFLE(3, 2, 3, 2));      //    Row0[2] Row0[3] Row1[2] Row1[3]        __m128 Temp1    = _mm_shuffle_ps(Row2, Row3, _MM_SHUFFLE(1, 0, 1, 0));      //    Row2[0] Row2[1] Row3[0] Row3[1]        __m128 Temp3    = _mm_shuffle_ps(Row2, Row3, _MM_SHUFFLE(3, 2, 3, 2));        //    Row2[2] Row2[3] Row3[2] Row3[3]          
Row0 = _mm_shuffle_ps(Temp0, Temp1, _MM_SHUFFLE(2, 0, 2, 0));                //    Row0[0] Row1[0] Row2[0] Row3[0]             Row1 = _mm_shuffle_ps(Temp0, Temp1, _MM_SHUFFLE(3, 1, 3, 1));                //    Row0[1] Row1[1] Row2[1] Row3[1]                     Row2 = _mm_shuffle_ps(Temp2, Temp3, _MM_SHUFFLE(2, 0, 2, 0));                //    Row0[2] Row1[2] Row2[2] Row3[2]                Row3 = _mm_shuffle_ps(Temp2, Temp3, _MM_SHUFFLE(3, 1, 3, 1));                //    Row0[3] Row1[3] Row2[3] Row3[3]             
_mm_store_ps(Dest, Row0);_mm_store_ps(Dest + 8, Row1);_mm_store_ps(Dest + 16, Row2);_mm_store_ps(Dest + 24, Row3);
}

     本质上说,这些优化都是小打小闹,提高不了多少速度,当然还有一些可以优化的地方,比如权重和累加和的更新,最后的累加和和权重的相除得到结果等等都有有关的SSE函数可以使用。

     还有一个可以优化的地方就是,在高度方向上前后两个像素8*8领域 在进行2D的DCT变换时,其第一次行方向上的DCT变换有7行的结果是可以重复利用的,如果利用这一点,则可以获得约15%的速度提示。

   综合以上各种优化手段,在I5的机器上一幅512*512 的灰度图像大约处理用时为100ms左右 ,比起IPOL的demo运行速度快了很多倍了。

     DCT滤波的效果上很多情况下也是相当不错的,想比NLM也毫不逊色,我们贴一些图片看下效果:

                         

    

                        噪音图像                                                                                            去噪后效果(Sigma = 10)

     为显示方便,上面的图像都是设置了一定程度的缩放。

     共享我改写的这个代码供大家共同学习提高,如果哪位能进一步提高算法的速度 ,也希望不吝赐教。

  代码下载链接:http://files.cnblogs.com/files/Imageshop/DCT_Denosing.rar

 

  后记:  继续优化了下8*8点的DCT里SSE代码的处理方式,改变了累加的方向,速度提高30%;然后把64次阈值处理的代码也改成SSE优化,速度提高10%;在如果考虑进行隔行隔列取样然后在DCT进行累加,经过测试基本上看不出有什么效果上的区别,但是速度大约能提高3.5倍左右;如果考虑多线程的方式,比如开个双线程,速度约能提高0.8倍,如果CPU支撑AVX,则大概又能提高0.9倍,算来算去,我感觉可以实时了。

 

 ****************************作者: laviewpbt   时间: 2015.11.14    联系QQ:  33184777 转载请保留本行信息**********************

 

 

 

转载于:https://my.oschina.net/abcijkxyz/blog/792229

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.luyixian.cn/news_show_788965.aspx

如若内容造成侵权/违法违规/事实不符,请联系dt猫网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

共享收集的图像处理方面的一些资源和网站。

首先&#xff0c;共享在软件编写过程访问和收集到的一些与图像或优化有关的网站和博客。 http://blog.csdn.net/housisong/category/325273.aspx 图像处理的相关技术博客 http://www.cnblogs.com/xiaotie/category/145078.html 图像处理的相关技术…

全球银行网站成黑客主攻目标 阿里云提供安全防御应急方案

2019独角兽企业重金招聘Python工程师标准>>> 近日&#xff0c;阿里云监控发现&#xff0c;匿名者&#xff08;Anonymous&#xff09;组织成员正在发起针对全球中央银行网站的攻击行动&#xff0c;截止目前&#xff0c;国内有超过2家以上的重要网站被攻击&#xff0c…

《Django开发教程》1.3 我们的第一个网站

1、启动项目可以访问 上节课&#xff0c;我们创建了一个HelloWorld项目&#xff0c;目录结果如下&#xff1a; $ cd HelloWorld/ $ tree . |-- HelloWorld | |-- __init__.py | |-- asgi.py | |-- settings.py | |-- urls.py | -- wsgi.py -- manage.py启动项目&am…

如何实现网站白名单控制,只允许访问指定站点?

对于一些安全性要求比较高的局域网来说&#xff0c;有时候只允许客户机访问指定的网站&#xff0c;其他网络行为一律禁止。这时候我们就需要用到“网站白名单”功能&#xff08;只允许访问下列网站&#xff09;。具体的配置如下图&#xff1a;1. 在网页过滤中开启“只允许访问下…

基于django的个人博客网站建立(四)

基于django的个人博客网站建立&#xff08;四&#xff09; 前言 网站效果可点击这里访问 今天主要添加了留言与评论在后台的管理和主页文章的分页显示&#xff0c;文章类别的具体展示以及之前预留链接的补充 主要内容 其实今天的内容和前几天的基本相似&#xff0c;就是个体力活…

ffmpeg技术 - 一个不错的网站_拔剑-浆糊的传说_新浪博客

http://www.ffmpeg.com.cn 首页 [编辑]ffmpeg技术 http://www.ffmpeg.com.cn ffmpeg快速入门 ffmpeg简介 ffmpeg入门基础知识 ffmpeg快速安装 ffmpeg快速命令使用 ffmpeg快速应用开发 ffmpeg编译详解 ffmpeg编译FAQ集 ffmpeg命令使用 ffmpeg使用事例 ffplay使用事例 ffserver使…

web应用程序和web网站_使用推荐引擎个性化您的Web应用程序

web应用程序和web网站为了在快速发展的全球行业中保持相关性&#xff0c;技术专业人员必须跟踪IT的重大趋势&#xff0c;并找到方法将重要的趋势纳入其公司的技术产品组合中。 这样的趋势之一就是使用推荐引擎来驱动用户探索您的网站或企业的其他产品。 这些引擎根据各种模式向…

互动3D网站已触手可及

XML3D仅仅需要适当的3D模型、互联网连接和一个浏览器&#xff0c;就可以让顾客看到网上商店里的互动3D物品。当顾客访问一家在线商店时&#xff0c;他们往往希望能看到商品的整个全貌。比如&#xff0c;想放大了看&#xff0c;或者通过调整角度让物品形象化。直到现在&#xff…

web应用程序和web网站_改善Web应用程序的性能

web应用程序和web网站总览 富Internet应用程序&#xff08;RIA&#xff09;在Web 2.0域中非常流行。 为了提供新颖新颖的用户体验&#xff0c;许多网站都使用JavaScript或Flash将其复杂的内容从后端服务器移至客户端。 如果数据大小相当小&#xff0c;这将提供方便&#xff0c…

自学html5的网站有什么区别,在微信上HTML5 网页和普通的网页开发有何不同

原标题&#xff1a;在微信上HTML5 网页和普通的网页开发有何不同html5网页开发自问世以来受到的关注应该超过了开发者们的预期&#xff0c;在微信开发上html5网页技术的特性同样被高度运用。本文华清创客学院讲师和大家分享一下在微信上HTML5 网页和普通的网页开发有何不同?在…

爬取网站视频命令行工具you-get的安装及使用方法

爬取网站视频命令行工具you-get安装及使用方法软件简介下载方法Step.01Step.02使用方法报错提示软件简介 you-get 是一个跨平台命令行视频、音频与图像下载工具&#xff0c;支持国内外常用的各种多媒体网站。 下载方法 Step.01 下载Python&#xff0c;可以在python官网下载…

html5和css3_使用HTML5和CSS3创建现代网站

html5和css3在你开始前 本教程假定您具有HTML&#xff0c;CSS和JavaScript的一些基本经验。 它假定您了解HTML元素或标记是什么&#xff0c;属性的含义&#xff0c;HTML标记的基本语法&#xff0c;网页的一般结构&#xff0c;等等。 就CSS而言&#xff0c;您应该熟悉基于元素&…

【已解决】Nginx部署网站后外网访问不了

解决方案&#xff1a; 加入nginx.exe的路径

Github/Gitlab/Gitee徽章生成网站shields.io的使用方法

网站地址&#xff1a;https://shields.io/ 【静态徽标】 label&#xff1a;左边标签 message&#xff1a;右边具体信息 color&#xff1a;颜色&#xff0c;常用颜色如下 网址1&#xff1a;https://img.shields.io/static/v1?label<LABEL>&message<MESSAGE>…

IBM Security Access Manager:通过基于上下文的访问保护网站

IBM Security Access Manager for Mobile&#xff08;ISAM for Mobile&#xff09;允许安全设计师通过IBM Security Access Manager for Web&#xff08;ISAM for Web&#xff09;对Web访问执行基于上下文的授权&#xff08;CBA&#xff09;决策&#xff08;也称为基于风险的访…

php模板框架教程,PHP制作静态网站的模板框架教程

模板能够改善网站的结构。本文阐述如何通过PHP 4的一个新功能和模板类&#xff0c;在由大量静态HTML页面构成的网站中巧妙地运用模板控制页面布局。提纲&#xff1a;分离功能和布局避免页面元素重复静态网站的模板框架分离功能和布局首先我们来看看应用模板的两个主要目的&…

官网用什么php系统搭建开源,用云服务器搭建Typecho网站(开源PHP建站系统),...

用云服务器搭建Typecho网站(开源PHP建站系统)&#xff0c;用云服务器搭建Typecho&#xff0c;Typecho来自于开发团队的头脑风暴&#xff0c;基于PHP5开发&#xff0c;支持多种数据库&#xff0c;是一款内核强健﹑扩展方便﹑体验友好﹑运行流畅的轻量级开源博客程序。第一步、搭…

html表单收集信息,如何在网站上嵌入表单来获取访客信息

原标题&#xff1a;如何在网站上嵌入表单来获取访客信息有时我们在浏览某个网站的时候&#xff0c;会看到网站上有在线提交信息的表格&#xff0c;比如“问题反馈表”、“申请参会表”等&#xff0c;这就是网站表单。对于企业来说&#xff0c;在自己的官网上做这样一个在线表单…

很实用的网站收集

很实用的网站收集 ● gif 动画在线生成 loading GIF图片生成 loading GIF图片 在线loading图片制作工具 intoGIF ● CSS 标签兼容性 Can I use... Support tables for HTML5, CSS3, etc ● 网页兼容性测试 LambdaTest ● SVN 托管 免费有大小限制&#xff0c;可以购买空间 …

百度百科网站源码,国外多语言wikipedia百科网站搭建 第一篇

前端时间我这边研究一下类似百度百科的网站&#xff0c;然后按照百度百科的一些功能&#xff0c;还有结合了海外做得比较大的wikipedia百科功能&#xff0c;做了整合开发&#xff0c;现在把那个项目的开发过程等功能&#xff0c;提供给大家参考&#xff0c;希望能够帮到大家的学…