最近看到一篇文章讲IMAGE DECOMPOSITION,里面提到了将图像分为Texture layer和Structure layer,测试了很多方法,对于那些具有非常强烈纹理的图像,总觉得用TV去燥的方法分离的结果都比其他的方法都要好(比如导向、双边),比如下图:

  再比如:

 

  可见TV可以把纹理很好的提取出来。

  现在应该能找到很多的TV代码,比如IPOL上就有,详见http://www.ipol.im/pub/art/2013/61/

  我在其他地方也见过一些,比如这里: http://yu-li.github.io/paper/li_eccv14_jpeg.zip,他是借助于FFT实现的,当然少不了多次迭代,速度也是比较慢的。

  我还收藏了很久前一位朋友写的M代码,但是现在我不知道把他QQ或者微信弄到哪里去了,也不知道他会不会介意我把他的代码分享出来。

function dualROF()
clc f0=imread('rr.bmp');
f0=f0(:,:,);
[m,n]=size(f0);
f0=double(f0); lamda=; % smoothness paramter, the larger the smoother
tao=.; % fixed do not change it. p1=zeros(m,n);
p2=zeros(m,n); tic
for step=:
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
div_p=div(p1,p2);
cx=Fx(div_p-f0/lamda);
cy=Fy(div_p-f0/lamda);

    abs_c=sqrt(cx.^2+cy.^2);
    p1=(p1+tao*cx)./(1+tao*abs_c);
    p2=(p2+tao*cy)./(1+tao*abs_c);

end

u=f0-lamda*div_p;
toc
figure; imagesc(f0); colormap(gray); axis off; axis equal;
figure; imagesc(u); colormap(gray); axis off; axis equal; % Compute divergence using backward derivative
function f = div(a,b)
f = Bx(a)+By(b); % Forward derivative operator on x with boundary condition u(:,:,)=u(:,:,)
function Fxu = Fx(u)
[m,n] = size(u);
Fxu = circshift(u,[ -])-u;
Fxu(:,n) = zeros(m,); % Forward derivative operator on y with boundary condition u(,:,:)=u(m,:,:)
function Fyu = Fy(u)
[m,n] = size(u);
Fyu = circshift(u,[- ])-u;
Fyu(m,:) = zeros(,n); % Backward derivative operator on x with boundary condition Bxu(:,)=u(:,)
function Bxu = Bx(u)
[~,n] = size(u);
Bxu = u - circshift(u,[ ]);
Bxu(:,) = u(:,);
Bxu(:,n) = -u(:,n-); % Backward derivative operator on y with boundary condition Bxu(,:)=u(,:)
function Byu = By(u)
[m,~] = size(u);
Byu = u - circshift(u,[ ]);
Byu(,:) = u(,:);
Byu(m,:) = -u(m-,:);

  M的代码,代码量不大,那是因为Matlab的向量化确实很厉害,但是这个代码还是很慢的,256*256的灰度图迭代100次都要700ms了。

  这里抛开一些优化不说,用这个circshift会造成很大的性能损失,我们稍微分析下就能看到用这个地方其实就是简单的水平或者垂直方向的差分,完全没有必要这样写。

  直接按照代码的意思用C语言把他们展开并不做其他的优化可得到大概下面这种不怎么好的代码:

int IM_DualTVDenoising(unsigned char *Src, unsigned char *Dest, int Width, int Height, int Stride,  float Lamda =  , int Iter = )
{
int Channel = Stride / Width;
if ((Src == NULL) || (Dest == NULL)) return IM_STATUS_NULLREFRENCE;
if ((Width <= ) || (Height <= )) return IM_STATUS_INVALIDPARAMETER;
if ((Channel != ) && (Channel != ) && (Channel != )) return IM_STATUS_INVALIDPARAMETER; if (Channel == )
{
float tao = 0.125; // fixed do not change it.
float InvLamda = 1.0 / Lamda; float *p1 = (float *)malloc(Width * Height * sizeof(float));
float *p2 = (float *)malloc(Width * Height * sizeof(float));
float *div_p = (float *)malloc(Width * Height * sizeof(float));
float *cx = (float *)malloc(Width * Height * sizeof(float));
float *cy = (float *)malloc(Width * Height * sizeof(float));
float *Temp = (float *)malloc(Width * Height * sizeof(float)); int X, Y;
float q1, q2, q, abs_c;
float *LineP1, *LineP2, *LineP3, *LineP4;
unsigned char *LinePS, *LinePD;
for (int Z = ; Z < Iter; Z++)
{
//Div(p1, p2, div_p); for (Y = ; Y < Height; Y++)
{
LineP1 = p1 + Y * Width; //Fx(Temp, cx);
LineP2 = cx + Y * Width;
LineP2[] = LineP1[];
for (X = ; X < Width; X++)
{
LineP2[X] = LineP1[X] - LineP1[X - ];
}
LineP2[Width - ] = -LineP1[Width - ];
} memcpy(cy, p2, Width * sizeof(float));
for (Y = ; Y < Height; Y++)
{
LineP1 = (float *)(p2 + (Y - )* Width);
LineP2 = (float *)(p2 + Y * Width); //Fy(Temp, cy);
LineP3 = (float *)(cy + Y * Width);
for (X = ; X < Width; X++)
{
LineP3[X] = LineP2[X] - LineP1[X];
}
}
LineP1 = (float *)(p2 + (Height - ) * Width);
LineP2 = (float *)(cy + (Height - ) * Width);
for (X = ; X < Width; X++)
{
LineP2[X] = -LineP1[X];
} for (Y = ; Y < Height; Y++)
{
LineP1 = (float *)(cx + Y * Width);
LineP2 = (float *)(cy + Y * Width); //Fy(Temp, cy);
LineP3 = (float *)(div_p + Y * Width);
for (X = ; X < Width; X++)
{
LineP3[X] = LineP1[X] + LineP2[X];
}
} for (Y = ; Y < Height; Y++)
{
LineP1 = (float *)(div_p + Y * Width);
LineP2 = (float *)(Temp + Y * Width);
LinePS = Src + Y * Stride; for (X = ; X < Width; X++)
{
LineP2[X] = LineP1[X] - LinePS[X] * InvLamda;
}
} for (Y = ; Y < Height; Y++)
{
LineP1 = (float *)(Temp + Y * Width); //Fx(Temp, cx);
LineP2 = (float *)(cx + Y * Width);
for (X = ; X < Width - ; X++)
{
LineP2[X] = LineP1[X + ] - LineP1[X];
}
LineP2[Width - ] = ;
} for (Y = ; Y < Height - ; Y++)
{
LineP1 = (float *)(Temp + Y * Width);
LineP2 = (float *)(Temp + (Y + ) * Width); //Fy(Temp, cy);
LineP3 = (float *)(cy + Y * Width);
for (X = ; X < Width; X++)
{
LineP3[X] = LineP2[X] - LineP1[X];
}
}
memset(Temp + (Height - ) * Width, , Width * sizeof(float)); for (Y = ; Y < Height; Y++)
{
LineP1 = (float *)(p1 + Y * Width);
LineP2 = (float *)(p2 + Y * Width);
LineP3 = (float *)(cx + Y * Width);
LineP4 = (float *)(cy + Y * Width); for (X = ; X < Width; X++)
{
abs_c = sqrt(LineP3[X] * LineP3[X] + LineP4[X] * LineP4[X]);
abs_c = / ( + tao * abs_c);
LineP1[X] = (LineP1[X] + tao * LineP3[X]) * abs_c;
LineP2[X] = (LineP2[X] + tao * LineP4[X]) * abs_c;
}
}
}
for (Y = ; Y < Height; Y++)
{
LineP1 = (float *)(div_p + Y * Width);
LinePS = Src + Y * Stride;
LinePD = Dest + Y * Stride;
for (X = ; X < Width; X++)
{
LinePD[X] = IM_ClampToByte((int)(LinePS[X] - Lamda * LineP1[X]));
}
} free(p1);
free(p2);
free(div_p);
free(cx);
free(cy);
free(Temp);
}
else
{ } }

  算法明显占用很大的内存,而且看起来别扭,不过速度还是杠杠的,256*256的灰度图迭代100次都要30ms了。反编译看了下代码,编译器对代码做了很好的SIMD指令优化。

  上面的C语言还是可以继续优化的,这就需要大家自己的认真的去研读代码深层次的逻辑关系了,实际上可以只要上面的一半的临时内存的,而且很多计算可以集中在一个循环里完成,可以手动内嵌SIMD指令,或者直接使用编译器的优化能力,基本上这样的简单的算法逻辑编译器编译后的速度不会比我们手写的SIMD指令慢,有的时候还是会快一些,不得不佩服那些写编译器的大牛。优化后的速度大概在14ms左右。

  研究TV算法需要很好的数学功底,以前朋友曾经给我寄过一本书,里面都是微分方面的数学公式,看的我吓死了,不过TV算法似乎有很多很好的应用,也曾经流行过一段时间,可惜现在深度学习一出来,很多人都喜欢这种直接从海量数据中建造黑盒模型,而对那些有着很明显的数学逻辑的算法嗤之以鼻了,真有点可惜。

  以前在基于总变差模型的纹理图像中图像主结构的提取方法 一文中曾提到那个论文附带的Matlab代码没有什么意义,因为他很难转换成C的代码,即时转换成功了,也处理不了大图,但是本文这里的TV算法总的来说在内存占用或者速度方面都还令人满意。

  在去噪效果上,这个算法还算可以:

         

  本文Demo下载地址:  http://files.cnblogs.com/files/Imageshop/SSE_Optimization_Demo.rar, 算法位于Denoise --> TV Denoising下。

【算法随记六】一段Matlab版本的Total Variation(TV)去噪算法的C语言翻译。的更多相关文章

  1. Kmeans算法的应用实例(Matlab版本)

    K-means是一种经典的聚类算法,是十大经典数据挖掘算法之一.K-means算法的基本思想是:以空间中k个点为中心进行聚类,对最靠近他们的对象归类.通过迭代的方法,逐次更新各聚类中心的值,直至得到最 ...

  2. 纯MATLAB版本 SIFT代码

    先贴几个链接: http://blog.csdn.net/abcjennifer/article/details/7639681  Rachel-Zhang的 http://blog.csdn.net ...

  3. 吴恩达《机器学习》课程笔记——第六章:Matlab/Octave教程

    上一篇  ※※※※※※※※  [回到目录]  ※※※※※※※※  下一篇 这一章的内容比较简单,主要是MATLAB的一些基础教程,如果之前没有学过matlab建议直接找一本相关书籍,边做边学,matl ...

  4. 记一次安装多版本php的四个雷区,你踩着了吗

    记一次安装多版本php的四个雷区,你踩着了吗   技术小疯子关注3人评论740人阅读2018-06-29 15:00:30   记一次安装多版本的php的四个雷区,你踩着了吗 需求:公司需要在同一台服 ...

  5. MATLAB版本(2012b 64bit),在尝试调用svmtrain函数时报错

    问题:MATLAB版本(2012b 64bit),在尝试调用svmtrain函数时报错: 解决方案:参照https://blog.csdn.net/TIME_LEAF/article/details/ ...

  6. 【算法随记三】小半径中值模糊的急速实现(16MB图7.5ms实现) + Photoshop中蒙尘和划痕算法解读。

    在本人的博客里,分享了有关中值模糊的O(1)算法,详见:任意半径中值滤波(扩展至百分比滤波器)O(1)时间复杂度算法的原理.实现及效果 ,这里的算法的执行时间和参数是无关的.整体来说,虽然速度也很快, ...

  7. 搜索引擎算法研究专题六:HITS算法

    搜索引擎算法研究专题六:HITS算法 2017年12月19日 ⁄ 搜索技术 ⁄ 共 1240字 ⁄ 字号 小 中 大 ⁄ 评论关闭   HITS(Hyperlink-Induced Topic Sea ...

  8. ecos matlab版本安装

    官网链接 github地址 1.注意不仅要下载matlab版本,同时还要下载c版本,因为matlab版本缺少第三方软件,将两个版本解压缩后将c版本下的文件夹external,ecos_bb,inclu ...

  9. Atitit.java图片图像处理attilax总结  BufferedImage extends java.awt.Image获取图像像素点image.getRGB(i, lineIndex); 图片剪辑/AtiPlatf_cms/src/com/attilax/img/imgx.javacutImage图片处理titit 判断判断一张图片是否包含另一张小图片 atitit 图片去噪算法的原理与

    Atitit.java图片图像处理attilax总结 BufferedImage extends java.awt.Image 获取图像像素点 image.getRGB(i, lineIndex); ...

随机推荐

  1. [剑指offer]第1题,二维数组中的查找

    ①题目 在一个二维数组中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序.请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数. ...

  2. java编程思想第四版第八章习题

    第一题 package net.mindview.polymorphism; //基类-自行车 class Cycle{ } //子类-单轮车 class Unicycle extends Cycle ...

  3. 微擎签名出错 invalid signature

    微擎签名出错 错误信息: config:fail,Error: 系统错误,错误码:63002,invalid signature 修改方法: PHP 端 $account_api = WeAccoun ...

  4. jeson navo

    QT TEMPLATE = app CONFIG += console c++11 CONFIG -= app_bundle CONFIG -= qt INCLUDEPATH += /usr/loca ...

  5. windows下大数据开发环境搭建(1)——Hadoop环境搭建

    所需环境 jdk 8 Hadoop下载 http://hadoop.apache.org/releases.html 配置环境变量 HADOOP_HOME: C:\hadoop-2.7.7 Path: ...

  6. RALM: 实时 Look-alike 算法在微信看一看中的应用

    嘉宾:刘雨丹 腾讯 高级研究员 整理:Jane Zhang 来源:DataFunTalk 出品:DataFun 注:欢迎关注DataFunTalk同名公众号,收看第一手原创技术文章. 导读:本次分享是 ...

  7. 记一次Pod中java进程内存“异常”消耗

    背景 环境:openshift3.11 开发反映部署在容器中的java应用内存持续增长,只升不降,具体为: java应用部署在容器中,配置的jvm参数为-Xms1024m -Xmx1024m,容器me ...

  8. Flex容器拖动(Bordercontainer为例)

    Bordercontainer的拖放到任意位置. mxml: 为Bordercontainer添加鼠标按下和弹起事件 <s:BorderContainer id="bdShow&quo ...

  9. 2019-9-10:渗透测试,基础学习,nmap扫描命令,php基本语法学习,笔记

    nmap参数-sT,使用tcp全连接的方式 ,扫描过程需要三次握手,建立链接,则说明端口开放,扫描速度慢-sS,使用syn的数据包去检测,接收到ACK说明端口开放-sN,null扫描,发出去的数据包不 ...

  10. php+redis实现注册、删除、编辑、分页、登录、关注等功能

    本文实例讲述了php+redis实现注册.删除.编辑.分页.登录.关注等功能.分享给大家供大家参考,具体如下: 主要界面 ​ 连接redis redis.php <?php //实例化 $red ...