经典的变分法图像去噪的C++实现

由于这学期的图像处理课程的大作业需要写一个图像处理程序,不能使用古典的线性滤波,或者基于频域(小波)或者基于统计之类的方法。只能用老师讲过的一些方法,诸如变分,PDE,微分几何等。。感觉上简单的变分法稍微要好实现一些,就打算基于最早的TV图像去噪模型,做一个VC的实现。但是找遍了网上也没有TV去噪的C++源码,与之只好自己动手写了。

关于变分法和泛函分析的一些基础原理今天就先不多说了,TV图像去噪经典论文:《Nonlinear Total Variation based noise removal algorithms》Google上可以搜得到。

关于Matlab的程序实现,有一个经典的主页: http://visl.technion.ac.il/~gilboa/PDE-filt/tv_denoising.html

下面是一个Matlab代码实现:复制到记事本用matlab打开就可以运行,要注意图像的名称和路径要对应。如果只是想学学算法思路或者看看处理效果的话,只需要Matlab的代码就行了。
function J=tv(I,iter,dt,ep,lam,I0,C)

%%Private function: tv (by Guy Gilboa).

%%Total Variation denoising.

%%Example: J=tv(I,iter,dt,ep,lam,I0)

%%Input: I-image (doublearray gray level1-256),

%%iter-num of iterations,

%%dt-time step [0.2],

%%ep-epsilon (of gradient regularization) [1],

%%lam-fidelity term lambda [0],

%%I0-input (noisy) image [I0=I]

%%(defaultvalues arein[])

%%Output: evolved image



clc

clear

I
=imread('grids.bmp');%load image

I
=double(I);



if~exist('ep')

ep
=1;

end

if~exist('dt')

dt
=ep/5;%dt below the CFL bound

end

if~exist('lam')

lam
=0;

end

if~exist('I0')

I0
=I;

end

if~exist('C')

C
=0;

end

[ny,nx]
=size(I); ep2=ep^2;



%params

iter
=80;



fori=1:iter,%%doiterations

%estimate derivatives

I_x
=(I(:,[2:nx nx])-I(:,[11:nx-1]))/2;

I_y
=(I([2:ny ny],:)-I([11:ny-1],:))/2;

I_xx
=I(:,[2:nx nx])+I(:,[11:nx-1])-2I;

I_yy
=I([2:ny ny],:)+I([11:ny-1],:)-2
I;

Dp
=I([2:ny ny],[2:nx nx])+I([11:ny-1],[11:nx-1]);

Dm
=I([11:ny-1],[2:nx nx])+I([2:ny ny],[11:nx-1]);

I_xy
=(Dp-Dm)/4;

%compute flow

Num
=I_xx.(ep2+I_y.^2)-2I_x.I_y.I_xy+I_yy.(ep2+I_x.^2);

Den
=(ep2+I_x.^2+I_y.^2).^(3/2);

I_t
=Num./Den+lam.
(I0-I+C);

I
=I+dtI_t;%%evolve image by dt

end
%fori



%%returnimage

%J=I
Imean/mean(mean(I));%normalize to original mean

J
=I;



figure(
1); imshow(uint8(I0)); title('Noisy image');

%denoise image byusingtvforsome iterations

figure(
2); imshow(uint8(J)); title('Denoised image');


另外我在我的图像处理框架程序里实现了这个最经典版本的TV去噪算法,核心代码如下:


//TV去噪函数

boolMyCxImage::TVDenoising(intiter/= 80/)

{

if(my_image==NULL)returnfalse;

if(!my_image->IsValid())returnfalse;

//算法目前不支持彩色图像,所以对于彩图,先要转换成灰度图。

if(!my_image->IsGrayScale())

{

my_image
->GrayScale();

//return false;

}



//基本参数,这里由于设置矩阵C为0矩阵,不参与运算,所以就忽略之

intep=1, nx=width, ny=height;

doubledt=(double)ep/5.0f, lam=0.0;

intep2=epep;



double
image=newDoubleMatrix(nx, ny);

double
image0=newDoubleMatrix(nx, ny);

//注意一点是CxImage里面图像存储的坐标原点是左下角,Matlab里面图像时左上角原点

for(inti=0; i<ny; i++)

{

for(intj=0; j<nx; j++)

{

image0[i][j]
=image[i][j]=my_image->GetPixelIndex(j, ny-i-1);

}

}



double
image_x=newDoubleMatrix(nx, ny);//I_x = ( I(:,[2:nx nx]) - I(:,[1 1:nx-1]))/2;

double
image_xx=newDoubleMatrix(nx, ny);//I_xx = I(:,[2:nx nx])+I(:,[1 1:nx-1])-2I;

doubleimage_y=newDoubleMatrix(nx, ny);//I_y = (I([2:ny ny],:)-I([1 1:ny-1],:))/2;

double
image_yy=newDoubleMatrix(nx, ny);//I_yy = I([2:ny ny],:)+I([1 1:ny-1],:)-2I;

double
image_tmp1=newDoubleMatrix(nx, ny);

double
image_tmp2=newDoubleMatrix(nx, ny);



double
image_dp=newDoubleMatrix(nx, ny);//Dp = I([2:ny ny],[2:nx nx])+I([1 1:ny-1],[1 1:nx-1



double
image_dm=newDoubleMatrix(nx, ny);//Dm = I([1 1:ny-1],[2:nx nx])+I([2:ny ny],[1 1:nx-1]);



double
image_xy=newDoubleMatrix(nx, ny);//I_xy = (Dp-Dm)/4;



double
image_num=newDoubleMatrix(nx, ny);//Num = I_xx.(ep2+I_y.^2)-2I_x.I_y.I_xy+I_yy.(ep2+I_x.^2);

doubleimage_den=newDoubleMatrix(nx, ny);//Den = (ep2+I_x.^2+I_y.^2).^(3/2);



//////////////////////////////////////////////////////////////////////////

//对image进行迭代iter次

iter=80;

for(intt=1; t<=iter; t++)

{

//进度条

my_image->SetProgress((long)100t/iter);

if(my_image->GetEscape())

break;

//////////////////////////////////////////////////////////////////////////

//计算I(:,[2:nx nx])和I(:,[1 1:nx-1])

//公共部分2到nx-1列

for(inti=0; i<ny; i++)

{

for(intj=0; j<nx-1; j++)

{

image_tmp1[i][j]
=image[i][j+1];

image_tmp2[i][j
+1]=image[i][j];

}

}

for(inti=0; i<ny; i++)

{

image_tmp1[i][nx
-1]=image[i][nx-1];

image_tmp2[i][
0]=image[i][0];

}



//计算I_x, I_xx

//I_x = ( I(:,[2:nx nx]) - I(:,[1 1:nx-1]))/2

//I_xx = I(:,[2:nx nx])+I(:,[1 1:nx-1])-2
I;

for(inti=0; i<ny; i++)

{

for(intj=0; j<nx; j++)

{

image_x[i][j]
=(image_tmp1[i][j]-image_tmp2[i][j])/2;

image_xx[i][j]
=(image_tmp1[i][j]+image_tmp2[i][j])-2image[i][j];

}

}



//////////////////////////////////////////////////////////////////////////

//计算I([2:ny ny],:)和I([1 1:ny-1],:)

//公共部分2到ny-1行

for(inti=0; i<ny-1; i++)

{

for(intj=0; j<nx; j++)

{

image_tmp1[i][j]
=image[i+1][j];

image_tmp2[i
+1][j]=image[i][j];

}

}

for(intj=0; j<nx; j++)

{

image_tmp1[ny
-1][j]=image[ny-1][j];

image_tmp2[
0][j]=image[0][j];

}

//计算I_xx, I_yy

//I_y = I([2:ny ny],:)-I([1 1:ny-1],:)

//I_yy = I([2:ny ny],:)+I([1 1:ny-1],:)-2
I;

for(inti=0; i<ny; i++)

{

for(intj=0; j<nx; j++)

{

image_y[i][j]
=(image_tmp1[i][j]-image_tmp2[i][j])/2;

image_yy[i][j]
=(image_tmp1[i][j]+image_tmp2[i][j])-2image[i][j];

}

}



//////////////////////////////////////////////////////////////////////////

//计算I([2:ny ny],[2:nx nx])和I([1 1:ny-1],[1 1:nx-1])

//公共部分分别是矩阵右下角,左上角的ny-1行和nx-1列

for(inti=0; i<ny-1; i++)

{

for(intj=0; j<nx-1; j++)

{

image_tmp1[i][j]
=image[i+1][j+1];

image_tmp2[i
+1][j+1]=image[i][j];

}

}

for(inti=0; i<ny-1; i++)

{

image_tmp1[i][nx
-1]=image[i+1][nx-1];

image_tmp2[i
+1][0]=image[i][0];

}

for(intj=0; j<nx-1; j++)

{

image_tmp1[ny
-1][j]=image[ny-1][j+1];

image_tmp2[
0][j+1]=image[0][j];

}

image_tmp1[ny
-1][nx-1]=image[ny-1][nx-1];

image_tmp2[
0][0]=image[0][0];

//计算Dp = I([2:ny ny],[2:nx nx])+I([1 1:ny-1],[1 1:nx-1]);

for(inti=0; i<ny; i++)

{

for(intj=0; j<nx; j++)

{

image_dp[i][j]
=image_tmp1[i][j]+image_tmp2[i][j];

}

}



//////////////////////////////////////////////////////////////////////////

//计算I([1 1:ny-1],[2:nx nx])和I([2:ny ny],[1 1:nx-1])

//公共部分分别是矩阵左下角,右上角的ny-1行和nx-1列

for(inti=0; i<ny-1; i++)

{

for(intj=0; j<nx-1; j++)

{

image_tmp1[i
+1][j]=image[i][j+1];

image_tmp2[i][j
+1]=image[i+1][j];

}

}

for(inti=0; i<ny-1; i++)

{

image_tmp1[i
+1][nx-1]=image[i][nx-1];

image_tmp2[i][
0]=image[i+1][0];

}

for(intj=0; j<nx-1; j++)

{

image_tmp1[
0][j]=image[0][j+1];

image_tmp2[ny
-1][j+1]=image[ny-1][j];

}

image_tmp1[
0][nx-1]=image[0][nx-1];

image_tmp2[ny
-1][0]=image[ny-1][0];



//计算Dm = I([1 1:ny-1],[2:nx nx])+I([2:ny ny],[1 1:nx-1]);

for(inti=0; i<ny; i++)

{

for(intj=0; j<nx; j++)

{

image_dm[i][j]
=image_tmp1[i][j]+image_tmp2[i][j];

}

}



//////////////////////////////////////////////////////////////////////////

//计算I_xy = (Dp-Dm)/4;

for(inti=0; i<ny; i++)

{

for(intj=0; j<nx; j++)

{

image_xy[i][j]
=(image_dp[i][j]-image_dm[i][j])/4;

}

}



//////////////////////////////////////////////////////////////////////////

//计算过程:



//计算Num = I_xx.
(ep2+I_y.^2)-2I_x.I_y.I_xy+I_yy.(ep2+I_x.^2) 和 Den = (ep2+I_x.^2+I_y.^2).^(3/2);

for(inti=0; i<ny; i++)

{

for(intj=0; j<nx; j++)

{

image_num[i][j]
=image_xx[i][j](image_y[i][j]image_y[i][j]+ep2)

-2image_x[i][j]image_y[i][j]image_xy[i][j]+image_yy[i][j](image_x[i][j]image_x[i][j]+ep2);



image_den[i][j]
=pow((image_x[i][j]
image_x[i][j]+image_y[i][j]image_y[i][j]+ep2),1.5);

}

}



//计算I: I_t = Num./Den + lam.
(I0-I+C); I=I+dtI_t; %% evolve image by dt

for(inti=0; i<ny; i++)

{

for(intj=0; j<nx; j++)

{

image[i][j]
+=dt
(image_num[i][j]/image_den[i][j]+lam*(image0[i][j]-image[i][j]));

}

}

}

//迭代结束



//////////////////////////////////////////////////////////////////////////

//赋值图像

BYTE tmp;

for(inti=0; i<ny; i++)

{

for(intj=0; j<nx; j++)

{

tmp
=(BYTE)image[i][j];

tmp
=max(0, min(tmp,255));

my_image
->SetPixelIndex(j, ny-i-1, tmp);

}

}



//////////////////////////////////////////////////////////////////////////

//删除内存

deleteDoubleMatrix(image_x, nx, ny);

deleteDoubleMatrix(image_y, nx, ny);

deleteDoubleMatrix(image_xx, nx, ny);

deleteDoubleMatrix(image_yy, nx, ny);

deleteDoubleMatrix(image_tmp1, nx, ny);

deleteDoubleMatrix(image_tmp2, nx, ny);

deleteDoubleMatrix(image_dp, nx, ny);

deleteDoubleMatrix(image_dm, nx, ny);

deleteDoubleMatrix(image_xy, nx, ny);

deleteDoubleMatrix(image_num, nx, ny);

deleteDoubleMatrix(image_den, nx, ny);

deleteDoubleMatrix(image0, nx, ny);

deleteDoubleMatrix(image, nx, ny);



returntrue;

}

//////////////////////////////////////////////////////////////////////////

//开辟二维数组函数

double
MyCxImage::newDoubleMatrix(intnx,intny)

{

doublematrix=newdouble*[ny];



for(inti=0; i<ny; i++)

{

matrix[i]
=newdouble[nx];

}

if(!matrix)

returnNULL;

return

matrix;

}

//清除二维数组内存函数

boolMyCxImage::deleteDoubleMatrix(double
matrix,intnx,intny)

{

if(!matrix)

{

returntrue;

}

for(inti=0; i<ny; i++)

{

if(matrix[i])

{

delete[] matrix[i];

}

}

delete[] matrix;



returntrue;

}

//////////////////////////////////////////////////////////////////////////


这个代码单独显然是无法运行的,因为还要涉及底层的图像处理的类库,图像的读取显示我用了CxIamge类,而程序界面我是用的MFC的框架。不过代码基本一直都是在做矩阵运算,如果要是能有一个比较好的矩阵运算类库的话,代码会简介许多,效率也会高一些。总体上C++代码还是要比Matlab效率高许多的。

关于变分法的算法原理和基本思想,我这两天再读一些论文在做总结。。


Email:lichao@icst.pku.edu.cn


原文链接: https://www.cnblogs.com/CCBB/archive/2010/12/29/1920884.html

欢迎关注

微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍

原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/19384

非原创文章文中已经注明原地址,如有侵权,联系删除

关注公众号【高性能架构探索】,第一时间获取最新文章

转载文章受原作者版权保护。转载请注明原作者出处!

(0)
上一篇 2023年2月7日 下午8:27
下一篇 2023年2月7日 下午8:28

相关推荐