TV去噪的拙劣理解以及python实现

TV去噪的原理描述为:

受噪声污染的图像总变分比无噪图像总变分大

啥是总变分?根据以下公式:

J ( u ) = ∬ x , y ∈ i m a g e ∣ ▽ u ∣ d x d y = ∬ x , y ∈ i m a g e u x 2 + u y 2 d x d y J(u)=\iint_{x,y\in image}^{}|\triangledown u|dxdy=\iint_{x,y\in image}^{}\sqrt{u_{x}^{2}+u_{y}^{2}}dxdy J(u)=x,yimageudxdy=x,yimageux2+uy2 dxdy

其中 u x u_{x} ux, u y u_{y} uy是像素在 x x x, y y y方向的梯度

可知总变分是梯度幅值的积分,为了消除噪声,需要使总变分( J J J)最小化。

公式推导看着晕,这到底什么意思?

根据公式的字面理解,是要让全图梯度值之和最小?那纯色图的梯度值之和是最小的,那确实也没有噪声了。

难道只是说噪声图像的梯度变化更大,那不是废话。。。

实际上为了使去噪结果更接近原图,避免失真,推导时会加入保真项,最后的推导结果是:

λ ( u − u 0 ) − ▽ ( ▽ u ∣ ▽ u ∣ ) = 0 \lambda (u-u_{0})-\triangledown (\frac{\triangledown u}{|\triangledown u|})=0 λ(uu0)(uu)=0

这就是 J J J取极值的解,以下的思考与其它地方查到的不同,不知是否更准确:

  • 当在平滑区域,梯度的梯度同样是0,即 ▽ ( ▽ u ∣ ▽ u ∣ ) = 0 \triangledown (\frac{\triangledown u}{|\triangledown u|})=0 (uu)=0,此时 u = u 0 u=u_{0} u=u0 u 0 u_{0} u0就是原图。
  • 当在图像边缘时,梯度 ▽ u \triangledown u u变大,梯度的梯度具有一定的值, ▽ ( ▽ u ∣ ▽ u ∣ ) \triangledown (\frac{\triangledown u}{|\triangledown u|}) (uu)值较小,此时 u ≈ u 0 u\approx u_{0} uu0
  • 当在噪声区域中时,突变的噪点会使梯度的梯度增加,此时 ▽ ( ▽ u ∣ ▽ u ∣ ) \triangledown (\frac{\triangledown u}{|\triangledown u|}) (uu)较大,就起到了去噪的效果。

再举个例子,假如离散序列为【100,100,500,500,500】
梯度是【0,400,0,0,?】( x n + 1 − x n x_{n+1}-x_{n} xn+1xn
梯度的梯度是【400,-400,0,?,?】

假如有噪点,那么离散序列是:【100,100,500,100,100】
那么梯度是【0,400,-400,0,?】
梯度的梯度是【400,-800,400,?,?】

明显噪点的 ▽ ( ▽ u ∣ ▽ u ∣ ) \triangledown (\frac{\triangledown u}{|\triangledown u|}) (uu)更可观

(以上猜想概不负责)---------------------------------------------------------------------

最后根据论文【变分图像去噪】,们得到了一个迭代公式:

u x , y n + 1 = u x , y n − △ t λ ( u x , y n − u x , y 0 ) + △ t ( d i v p ) u_{x,y}^{n+1}=u_{x,y}^{n}-\triangle t\lambda (u_{x,y}^{n}-u_{x,y}^{0})+\triangle t(divp) ux,yn+1=ux,yntλ(ux,ynux,y0)+t(divp)

其中 n n n代表迭代次数, n = 0 n=0 n=0时, u 0 u^{0} u0代表输入图, △ t \triangle t t是时间步长参数,类似权重, λ \lambda λ是正则化参数, d i v p divp divp是扩散项

d i v p = ▽ ( ▽ u x , y n ∣ ▽ u x , y n ∣ ) = u y 2 u x x − 2 u x u y u x y + u x 2 u x y u x 2 + u y 2 divp=\triangledown (\frac{\triangledown u_{x,y}^{n}}{|\triangledown u_{x,y}^{n}|})= \frac{u_{y}^{2}u_{xx}-2u_{x}u_{y}u_{xy}+u_{x}^{2}u_{xy}}{u_{x}^{2}+u_{y}^{2}} divp=(ux,ynux,yn)=ux2+uy2uy2uxx2uxuyuxy+ux2uxy

n = 0 n=0 n=0时, d i v p = 0 divp=0 divp=0

每次迭代,都要用上一次的输出图像 u n u^{n} un减去 △ t ( λ ( u x , y n − u x , y 0 ) − d i v p ) \triangle t(\lambda (u_{x,y}^{n}-u_{x,y}^{0})-divp) t(λ(ux,ynux,y0)divp),理论上当它跟之前的解一样等于0时,每次迭代的结果相同,意味着噪声被去除了。

  • 如果某个点的divp较大,那么迭代输出与前一次的差距就更大,起到了去噪的作用。

  • 如果divp=0,那么公式变为: u x , y n + 1 = ( 1 − △ t λ ) u x , y n − △ t λ u x , y 0 u_{x,y}^{n+1}=(1-\triangle t\lambda) u_{x,y}^{n}-\triangle t\lambda u_{x,y}^{0} ux,yn+1=(1tλ)ux,yntλux,y0,也就是 u x , y n u_{x,y}^{n} ux,yn u x , y 0 u_{x,y}^{0} ux,y0的加权和。

然后 u u u的一系列导数计算公式为(我看到有一篇文章搞错了加减号):

( u x ) i , j n = u i + 1 , j n − u i − 1 , j n 2 (u_{x})_{i,j}^{n}=\frac{u_{i+1,j}^{n}-u_{i-1,j}^{n}}{2} (ux)i,jn=2ui+1,jnui1,jn

( u y ) i , j n = u i , j + 1 n − u i , j − 1 n 2 (u_{y})_{i,j}^{n}=\frac{u_{i,j+1}^{n}-u_{i,j-1}^{n}}{2} (uy)i,jn=2ui,j+1nui,j1n

( u x x ) i , j n = u i + 1 , j n + u i − 1 , j n − 2 u i , j n (u_{xx})_{i,j}^{n}=u_{i+1,j}^{n}+u_{i-1,j}^{n}-2u_{i,j}^{n} (uxx)i,jn=ui+1,jn+ui1,jn2ui,jn

( u y y ) i , j n = u i , j + 1 n + u i , j − 1 n − 2 u i , j n (u_{yy})_{i,j}^{n}=u_{i,j+1}^{n}+u_{i,j-1}^{n}-2u_{i,j}^{n} (uyy)i,jn=ui,j+1n+ui,j1n2ui,jn

( u x y ) i , j n = u i + 1 , j + 1 n − u i − 1 , j − 1 n − u i − 1 , j + 1 n − u i + 1 , j − 1 4 (u_{xy})_{i,j}^{n}=\frac{u_{i+1,j+1}^{n}-u_{i-1,j-1}^{n}-u_{i-1,j+1}^{n}-u_{i+1,j-1}}{4} (uxy)i,jn=4ui+1,j+1nui1,j1nui1,j+1nui+1,j1

最后贴出论文中的步骤:
TV去噪的拙劣理解以及python实现_第1张图片
其中h是空间步长,△t是时间步长

具体求解过程,论文中更详细,数学好的朋友可以去看看。变分图像去噪

python代码我会贴在文末,注意迭代次数不能太少, λ \lambda λ=0。

# -*- coding: utf-8 -*-
"""
Created on Sun Mar  7 14:24:12 2021

@author: SEELE
"""

import cv2
import numpy as np
import math

def gasuss_noise(image, mean=0, var=0.001):
  image = np.array(image/255, dtype=float)
  noise = np.random.normal(mean, var ** 0.5, image.shape)
  out = image + noise
  if out.min() < 0:
    low_clip = -1.
  else:
    low_clip = 0.
  out = np.clip(out, low_clip, 1.0)
  out = out*255
  #cv.imshow("gasuss", out)
  return out

img = cv2.imread('F:/hh/2.jpg')
img = cv2.resize(img,(500,500))
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
gray = gray.astype('float32')
size = img.shape  # h w c

#加高斯噪声
gray = gasuss_noise(gray)
cv2.imshow('addnoise',gray/255)

maxloop = 100
n = 0
#h = 1
t = 0.1
lamda = 0
ep = 1

divp = 0
imtmp = gray.copy()#曾经用等号赋值,吃了大亏
imnew = np.zeros([size[0],size[1]])
for loop in range(0,maxloop):
    print("processed %d times" % (loop))
    
    #这段也可以直接用矩阵计算来写
    for y in range(1,size[0] - 1):
        for x in range(1,size[1] - 1):
            
            
            ux = (imtmp[y,x+1] - imtmp[y,x-1]) / 2
            uy = (imtmp[y+1,x] - imtmp[y-1,x]) / 2
            uxx = imtmp[y,x+1] + imtmp[y,x-1] - imtmp[y,x] - imtmp[y,x]
            uyy = imtmp[y+1,x] + imtmp[y-1,x] - imtmp[y,x] - imtmp[y,x]
            
            uxy = (imtmp[y+1,x+1] + imtmp[y-1,x-1] - imtmp[y+1,x-1] - imtmp[y-1,x+1]) / 4
            
            divp = ((uy * uy + ep) * uxx - 2* ux * uy * uxy + (ux * ux + ep) * uyy) / ((ux * ux + uy * uy + ep))#防止除0

            imnew[y,x] = imtmp[y,x] + t *( lamda * (gray[y,x] - imtmp[y,x]) + divp)
            dv = 1* t * lamda * (gray[y,x] - imtmp[y,x])
            
            if x == 25 and y == 15:
                print('dp=%d,div=%d,im=%d' % (divp,dv,imnew[y,x]))
          
    if loop != maxloop-1:
        imtmp = imnew.copy()
    if loop % 20 == 0:
        string = 'loop='+str(loop)
        cv2.imshow(string,imnew.astype('uint8'))
    

#可以限制一下范围
#for y in range(1,size[0] - 1):
#    for x in range(1,size[1] - 1):
#        imnew[y,x] = min(max(imnew[y,x],0),255)
        
imnew = imnew.astype('uint8')
cv2.imshow('result',imnew)
            
            
            

图中分别是循环0、20、40、60、80、100次的结果,可以看出这个算法其实还是不能直接拿来用的

你可能感兴趣的:(图像算法笔记,python,图像处理)