Matlab&Python-WLS加权最小二乘滤波

最近看了论文Non-local Image Dehazing基于雾线的去雾算法

其中算法的滤波用的是weighted least square(WLS)算法,论文全称《Edge-Preserving Decompositions for Multi-Scale Tone and Detail Manipulation》,作者Z. Farbman等,发表在ACM SIGGRAPH 2007。

保边滤波器可以是两个矛盾的目标的结合体。对于一副输入图像g(N*M),我们目标图像u一方面我们希望其尽可能近似,与此同时除了在一些边缘梯度变化比较大的地方外应该越平滑越好。数学模型为:

下标代表像素点空间位置,其中p表示像素的位置,axay控制着不同位置上的平滑程度。

目标函数第一项代表输入图像和输出图像越相似越好,第二项是正则项,通过最小化的偏导,使得输出图像越平滑越好。平滑项的权重分别是和,依赖于输入图像,当输入图像的边缘梯度变化比较大的时候,我们希望其约束小一些,以保留图像的结构信息;当图像的边缘梯度变化很小的时候,这些细节信息我们认为其不重要,约束自然可以大一些,lambda是正则项参数,平衡两者比重,越大图像也就越平滑。 

上式写成矩阵就是:

其中,Ax,Ay为以ax,ay为对角元素的对角矩阵,Dx,Dy为前向差分矩阵,和是后向差分算子,要使得(2)式去的最小值,u需满足如下:

Lg为五点空间异构Laplacian矩阵。

论文中的去的平滑权重系数为:

输入图像亮度通道的log值(其实直接用原始图像也是可行的),可以看出当的梯度比较大时,会随着变小,否则反之。权重的这样设计是很合理的,可以保留较大的边缘,平滑不必要的细节。 其中 表示log, ε一般取0.0001。


原作者在项目主页中公布了源码:

稀疏矩阵A就是公式(3)中的I+λLg。这段程序和前面的推导不同之处在于平滑权重先乘以了−λ,但是最中的结果是一致的:对角线上用1去减,其实就是加;对于其他四个对角线,我们推导的结果是带有负号的平滑权重,然后乘以λ,其实就等价于直接乘以−λ。


wlsFilter.m

function OUT = wlsFilter(IN, lambda, alpha, L)
%WLSFILTER Edge-preserving smoothing based on the weighted least squares(WLS) 
%   optimization framework, as described in Farbman, Fattal, Lischinski, and
%   Szeliski, "Edge-Preserving Decompositions for Multi-Scale Tone and Detail
%   Manipulation", ACM Transactions on Graphics, 27(3), August 2008.
%
%   Given an input image IN, we seek a new image OUT, which, on the one hand,
%   is as close as possible to IN, and, at the same time, is as smooth as
%   possible everywhere, except across significant gradients in L.
%
%
%   Input arguments:
%   ----------------
%     IN              Input image (2-D, double, N-by-M matrix). 
%       
%     lambda          Balances between the data term and the smoothness
%                     term. Increasing lambda will produce smoother images.
%                     Default value is 1.0
%       
%     alpha           Gives a degree of control over the affinities by non-
%                     lineary scaling the gradients. Increasing alpha will
%                     result in sharper preserved edges. Default value: 1.2
%       
%     L               Source image for the affinity matrix. Same dimensions
%                     as the input image IN. Default: log(IN)

%
%   Example 
%   -------
%     RGB = imread('peppers.png'); 
%     I = double(rgb2gray(RGB));
%     I = I./max(I(:));
%     res = wlsFilter(I, 0.5);
%     figure, imshow(I), figure, imshow(res)
%     res = wlsFilter(I, 2, 2);
%     figure, imshow(res)

if(~exist('L', 'var')) %参数不存在,选取的默认值
    L = log(IN+eps);
end

if(~exist('alpha', 'var'))
    alpha = 1.2;
end

if(~exist('lambda', 'var'))
    lambda = 1;
end

smallNum = 0.0001;

[r,c] = size(IN);
k = r*c;

% Compute affinities between adjacent pixels based on gradients of L

dy = diff(L, 1, 1); %对L矩阵的第一维度上做差分,也就是下面的行减去上面的行,得到(N-1)xM维的矩阵
dy = -lambda./(abs(dy).^alpha + smallNum);
dy = padarray(dy, [1 0], 'post');%在最后一行的后面补上一行0
dy = dy(:);%按列生成向量,就是Ay对角线上的元素构成的矩阵

dx = diff(L, 1, 2); %对L矩阵的第二维度做差分,也就是右边的列减去左边的列,得到Nx(M-1)的矩阵
dx = -lambda./(abs(dx).^alpha + smallNum);
dx = padarray(dx, [0 1], 'post');%在最后一列的后面添加一列0
dx = dx(:);%按列生成向量,对应上面Ax的对角线元素


% Construct a five-point spatially inhomogeneous Laplacian matrix
B(:,1) = dx;
B(:,2) = dy;
d = [-r,-1];
A = spdiags(B,d,k,k);%把dx放在-r对应的对角线上,把dy放在-1对应的对角线上

e = dx;
w = padarray(dx, r, 'pre'); w = w(1:end-r);
s = dy;
n = padarray(dy, 1, 'pre'); n = n(1:end-1);

D = 1-(e+w+s+n);
A = A + A' + spdiags(D, 0, k, k);%A只有五个对角线上有非0元素

% Solve
OUT = A\IN(:);
OUT = reshape(OUT, r, c);

 

demo_wlsFilter.m

img = imread('C:\Users\x\Desktop\15.jpg');
IN = double(img);
OUT = IN;

%OUT = wlsFilter(IN, 0.5);
if length(size(img))>2
    for i=1:3
        IN(:,:,i) = IN(:,:,i)/max(IN(:));
        OUT(:,:,i) = wlsFilter(IN(:,:,i),0.35);
    end
end
figure('Position',[50,50, size(IN,2)*2 , size(IN,1)]);
subplot(1,3,1); imshow(IN);title('input')
subplot(1,3,2); imshow(OUT); title('output')
imwrite(OUT,['C:\Users\x\Desktop\', int2str(16),'.jpg']);


平滑结果:


Python实现

由于numpy的矩阵很容易溢出出现MemoryError,而A的大小很容易超过10万行&列,所以图片必须压缩。测试代码用的是(128x128)大小的图片。

还有一点就是python的spdiags与matlab的参数是不一样的。spdiags(B, diags, k, k)这里的B要用matlab的B参数的转置。另外python没有\除,所以矩阵a必须先求逆矩阵,由于矩阵是16384x16384大小,所以代码要跑3分钟。。。

import numpy as np
import cv2
#import math
from scipy.sparse import spdiags

def wlsFilter(img, Lambda, alpha=1.2, eps=0.0001):
    
    L = np.log(img+0.0001)
    row, cols = img.shape[:2]
    k = row * cols
    
    #对L矩阵的第一维度上做差分,也就是下面的行减去上面的行,得到(N-1)xM维的矩阵
    dy = np.diff(L, 1, 0)
    dy = -Lambda/(np.power(np.abs(dy),alpha)+eps)
    #在最后一行的后面补上一行0
    dy = np.pad(dy, ((0,1),(0,0)), 'constant')
    #按列生成向量,对应上面Ay的对角线元素
    dy = dy.T
    dy = dy.reshape(-1,1)
    
    #对L矩阵的第二维度上做差分,也就是右边的列减去左边的列,得到Nx(M-1)的矩阵
    dx = np.diff(L, 1, 1)
    dx = -Lambda/(np.power(np.abs(dx),alpha)+eps)
    #在最后一列的后面补上一行0
    dx = np.pad(dx, ((0,0),(0,1)), 'constant')
    #按列生成向量,对应上面Ay的对角线元素
    dx = dx.T
    dx = dy.reshape(-1,1)
    
    #构造五点空间非齐次拉普拉斯矩阵
    B = np.hstack((dx,dy))
    B = B.T
    diags = np.array([-row, -1])
    #把dx放在-row对应的对角线上,把dy放在-1对应的对角线上
    A = spdiags(B, diags, k, k).toarray()
    
    e = dx
    w = np.pad(dx, ((row,0),(0,0)), 'constant')
    w = w[0:-row]
    
    s = dy
    n = np.pad(dy, ((1,0),(0,0)), 'constant')
    n = n[0:-1]
    
    D = 1-(e+w+s+n)
    D = D.T
    #A只有五个对角线上有非0元素
    diags1 = np.array([0])
    A = A + np.array(A).T + spdiags(D, diags1, k, k).toarray()
    
    
    im = np.array(img)
    p,q = im.shape[:2]
    g = p*q
    im = np.reshape(im,(g,1))
    
    a = np.linalg.inv(A)
    
    out = np.dot(a,im)
    
    out = np.reshape(out,(row, cols))
    
    return out
    
img = cv2.imread(r'C:\Users\x\Desktop\k2.jpg', cv2.IMREAD_ANYCOLOR)

m = np.double(img)

b, g, r = cv2.split(m)

ib = np.array(b)
p1,q1 = ib.shape[:2]
h1 = p1*q1
ib = np.reshape(ib,(h1,1))
b = b/np.max(ib)

ig = np.array(g)
p2,q2 = ig.shape[:2]
h2 = p2*q2
ig = np.reshape(ig,(h2,1))
g = g/np.max(ig)

ir = np.array(r)
p3,q3 = ir.shape[:2]
h3 = p3*q3
ir = np.reshape(ir,(h3,1))
r = r/np.max(ir)

wls1 = wlsFilter(b,1)
wls2 = wlsFilter(g,1)
wls3 = wlsFilter(r,1)
wls = cv2.merge([wls1, wls2, wls3])



cv2.imshow('image', img)
cv2.imshow('filter', wls)
cv2.imwrite(r'C:\Users\x\Desktop\18.jpg', wls)   
cv2.waitKey(0)
cv2.destroyAllWindows()

 

  • 7
    点赞
  • 63
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值