Non-Photorealistic Rendering (Domain transform for edge-aware image and video processing)

opencv 3.0 的photo模块有Non-Photorealistic Rendering 可以将进行保边的平滑滤波, 铅笔画,细节增强等等。其对应的论文是:Eduardo S. L. Gastal, Manuel M. Oliveira, “Domain transform for edge-aware image and video processing”, ACM Trans. Graph. 30(4): 69, 2011.  作者网页可以下载matlab代码


Main idea: domain transform from image in R5 (x,y,r,g,b) to lower dimension where distances are preserved. Then, perform filtering on the transformed image

在保存距离的情况下,将高维空间图像转换到低维,再对转换后图像进行滤波


2D RGB图像I: 


可以定义在R5上定义二维空间Mi, 令:


分别用空间坐标(spatial coordinates) 和值坐标(Range coordinates)表示:

如果R5空间中的保留边滤波器为Fq, 那么滤波图像为:


需找一个转换t,使得


在Rl 上寻找一个滤波核H,其作用等同于在五维空间的滤波核F,如:


令ct(x) 表示将(x, I(x))的映射,其中(x是空间坐标,I(x)是x位置对应的r,g,b值)

转换的空间需要保存原来的距离(这里是L1),所以必须满足:


其中h是采样距离。 如下图所示,C是新的空间。线上两点的距离在距离足够小的情况下:Distance = h + d。 下面先以一维信号为例。


用积分的角度可以表示为:



在多通道的情况下,可以令导数为各通道倒数之和。


其中c是通道。

上面考虑的都是一维信号,在二维的情况下,可以对此做些扩展:1. 每一行处理;  2. 每一列处理; 3. 迭代n次

可以类似双边滤波器,将spatial和range参数都加入考虑。分别用sigma_s, sigma_r表示。

现在对图像滤波转化为:


先求解t,在选择H。H的选择至少应该比R5滤波核F更快。原文中H的选择有:Normalized Convolution, Interpolated Convolution, Recursive Filtering

文中Normalized Convolution的方法是:


r是距离,在这个距离之内的数据进行考虑,进行插值。

文末推导了Recursive Filtering方法的参数由来,该方法实现更加简单。


opencv以该滤波方法实现了几个应用:edgePreservingFilter, detailEnhance, pencilSketch。 其中detailEnhance转化为lab,只对l分量做处理。pencilSketch转化为YCBCR进行处理


总结:

1. 作者文中提到R5到Rl, 看起来有点玄乎。后面才知道实际上是处理一维信号,对于二维先行,再列,再搞几遍。。

2. 考虑每一行,曲线上每个点的值可以看作rgb值通过某种运算等到(看代码),行上两点的距离是曲线上的距离。 现在想对某一点插值,那么就将左右距离d(曲线)内的所有点的值相加,再除以左右两点的水平距离。这样就完成了滤波。

3. 在值变化较大的区域(边缘),曲线更加陡峭,使得在相同的d内,实际上水平距离很小。这样的好处就是,在这小范围呢,像素值变化不大,更容易保留信息(边)。反之,曲线平缓,实际水平距离较大,滤波的结果会更平滑。



最后附上作者matlab程序(中文注释是我写的--):

%  NC  Domain transform normalized convolution edge-preserving filter.
% 
%  F = NC(img, sigma_s, sigma_r, num_iterations, joint_image)
%
%  Parameters:
%    img             Input image to be filtered.
%    sigma_s         Filter spatial standard deviation.
%    sigma_r         Filter range standard deviation.
%    num_iterations  Number of iterations to perform (default: 3).
%    joint_image     Optional image for joint filtering.
%
%
%
%  This is the reference implementation of the domain transform NC filter
%  described in the paper:
% 
%    Domain Transform for Edge-Aware Image and Video Processing
%    Eduardo S. L. Gastal  and  Manuel M. Oliveira
%    ACM Transactions on Graphics. Volume 30 (2011), Number 4.
%    Proceedings of SIGGRAPH 2011, Article 69.
%
%  Please refer to the publication above if you use this software. For an
%  up-to-date version go to:
%  
%             http://inf.ufrgs.br/~eslgastal/DomainTransform/
%
%
%  THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY EXPRESSED OR IMPLIED WARRANTIES
%  OF ANY KIND, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
%  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
%  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
%  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
%  OUT OF OR IN CONNECTION WITH THIS SOFTWARE OR THE USE OR OTHER DEALINGS IN
%  THIS SOFTWARE.
%
%  Version 1.0 - August 2011.

function F = NC(img, sigma_s, sigma_r, num_iterations, joint_image)

    I = double(img);

    if ~exist('num_iterations', 'var')
        num_iterations = 3;
    end
    
    if exist('joint_image', 'var') && ~isempty(joint_image)
        J = double(joint_image);
    
        if (size(I,1) ~= size(J,1)) || (size(I,2) ~= size(J,2))
            error('Input and joint images must have equal width and height.');
        end
    else
        J = I;
    end
    
    [h w num_joint_channels] = size(J);

    %% Compute the domain transform (Equation 11 of our paper). 计算公式11
    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

    % Estimate horizontal and vertical partial derivatives using finite
    % differences.
    dIcdx = diff(J, 1, 2); %列差
    dIcdy = diff(J, 1, 1); %行差
    
    dIdx = zeros(h,w);
    dIdy = zeros(h,w);
    
    % Compute the l1-norm distance of neighbor pixels. dIdx 是 dIcdx的三通道绝对值之和
    for c = 1:num_joint_channels
        dIdx(:,2:end) = dIdx(:,2:end) + abs( dIcdx(:,:,c) );
        dIdy(2:end,:) = dIdy(2:end,:) + abs( dIcdy(:,:,c) );
    end
    
    % Compute the derivatives of the horizontal and vertical domain transforms.
    dHdx = (1 + sigma_s/sigma_r * dIdx);
    dVdy = (1 + sigma_s/sigma_r * dIdy);
    
    % Integrate the domain transforms.
    ct_H = cumsum(dHdx, 2);  %后面每列是前面所有列之和
    ct_V = cumsum(dVdy, 1);  %后面每行是前面所有行之和
    
    % The vertical pass is performed using a transposed image.
    ct_V = ct_V';
    
    %% Perform the filtering.
    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    N = num_iterations;
    F = I;

    sigma_H = sigma_s;
    
    for i = 0:num_iterations - 1
        
        % Compute the sigma value for this iteration (Equation 14 of our paper).
        sigma_H_i = sigma_H * sqrt(3) * 2^(N - (i + 1)) / sqrt(4^N - 1);
        
        % Compute the radius of the box filter with the desired variance.
        box_radius = sqrt(3) * sigma_H_i;
        
        F = TransformedDomainBoxFilter_Horizontal(F, ct_H, box_radius);
        F = image_transpose(F);

        F = TransformedDomainBoxFilter_Horizontal(F, ct_V, box_radius);
        F = image_transpose(F);
        
    end
    
    F = cast(F, class(img));

end

%% Box filter normalized convolution in the transformed domain.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function F = TransformedDomainBoxFilter_Horizontal(I, xform_domain_position, box_radius)

    [h w num_channels] = size(I);

    % Compute the lower and upper limits of the box kernel in the transformed domain.
    l_pos = xform_domain_position - box_radius;
    u_pos = xform_domain_position + box_radius;

    % Find the indices of the pixels associated with the lower and upper limits
    % of the box kernel.
    %
    % This loop is much faster in a compiled language.  If you are using a
    % MATLAB version which supports the 'parallel for' construct, you can
    % improve performance by replacing the following 'for' by a 'parfor'.

    l_idx = zeros(size(xform_domain_position));
    u_idx = zeros(size(xform_domain_position));

  
    for row = 1:h
        %找到该行中每个元素的最左和最右位置
        xform_domain_pos_row = [xform_domain_position(row,:) inf];

        l_pos_row = l_pos(row,:);
        u_pos_row = u_pos(row,:);

        local_l_idx = zeros(1, w);
        local_u_idx = zeros(1, w);

        local_l_idx(1) = find(xform_domain_pos_row > l_pos_row(1), 1, 'first');
        local_u_idx(1) = find(xform_domain_pos_row > u_pos_row(1), 1, 'first');

        for col = 2:w
            local_l_idx(col) = local_l_idx(col-1) + ...
                find(xform_domain_pos_row(local_l_idx(col-1):end) > l_pos_row(col), 1, 'first') - 1;

            local_u_idx(col) = local_u_idx(col-1) + ...
                find(xform_domain_pos_row(local_u_idx(col-1):end) > u_pos_row(col), 1, 'first') - 1;
        end
        

        l_idx(row,:) = local_l_idx;
        u_idx(row,:) = local_u_idx;
    end

    % Compute the box filter using a summed area table.
    SAT            = zeros([h w+1 num_channels]);
    SAT(:,2:end,:) = cumsum(I, 2); %后面每列是前面所有列之和
    row_indices    = repmat((1:h)', 1, w);
    F              = zeros(size(I));

    for c = 1:num_channels
        %sub2ind的参数分别为size, 行, 列, channel 的矩阵
        a = sub2ind(size(SAT), row_indices, l_idx, repmat(c, h, w)); %求出每个像素空间位置对应的下标。使得最后SAT(b)就可以直接得到所有点的SAT
        b = sub2ind(size(SAT), row_indices, u_idx, repmat(c, h, w));
        F(:,:,c) = (SAT(b) - SAT(a)) ./ (u_idx - l_idx);  %SAT(b)-SAT(a)表示在radius内,像素值的积分。(u_idx-l_idx)表示实际经过的像素
    end
    
end

%% 多通道转置
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function T = image_transpose(I)

    [h w num_channels] = size(I);
    
    T = zeros([w h num_channels], class(I));
    
    for c = 1:num_channels
        T(:,:,c) = I(:,:,c)';
    end
    
end






评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值