Filtering Demo

This program demonstrates Domain Transform filtering using cv.DTFilter.

Sources:

function varargout = dtFilter_demo_gui(im)
    % Load an image
    if nargin < 1
        src = imread(fullfile(mexopencv.root(),'test','fruits.jpg'));
    elseif ischar(im)
        src = imread(im);
    else
        src = im;
    end

    % make sure we start with a color image
    if ndims(src) ~= 3
        src = cv.cvtColor(src, 'GRAY2RGB');
    end

    % speedup filtering
    cv.Utils.setNumThreads(cv.Utils.getNumberOfCPUs());

    % create the UI
    h = buildGUI(src);
    if nargout > 0, varargout{1} = h; end
end

function onChange(~,~,h)
    %ONCHANGE  Event handler for UI controls

    % retrieve current values from UI controls
    opts.contrastBase = round(get(h.slid(1), 'Value'));
    opts.detailsLevel = round(get(h.slid(2), 'Value'));
    opts.edgesGamma   = round(get(h.slid(3), 'Value'));
    opts.sigmaSpatial = round(get(h.slid(4), 'Value'));
    opts.sigmaColor   = round(get(h.slid(5), 'Value'));
    set(h.txt(1), 'String',sprintf('Detail contrast: %3d',opts.contrastBase));
    set(h.txt(2), 'String',sprintf('Detail level: %3d',   opts.detailsLevel));
    set(h.txt(3), 'String',sprintf('Style gamma: %3d',    opts.edgesGamma));
    set(h.txt(4), 'String',sprintf('Sigma Spatial: %3d',  opts.sigmaSpatial));
    set(h.txt(5), 'String',sprintf('Sigma Color: %3d',    opts.sigmaColor));
    drawnow

    % apply filtering operation
    filterIdx = get(h.pop, 'Value');
    filtersOp = {@filterDetailsEnhancement, @filterStylizing, @filterBlurring, @filterDoNothing};
    dst = feval(filtersOp{filterIdx}, h.src, opts);

    % show result
    set(h.img, 'CData',dst)
    drawnow
end

function h = buildGUI(img)
    %BUILDGUI  Creates the UI

    sz = size(img);
    sz(2) = max(sz(2), 200);  % minimum figure width

    % parameters
    contrastBase = 100; % for Details Enhancement mode
    detailsLevel = 100; % for Details Enhancement mode
    edgesGamma   = 100; % for Stylizing mode
    sigmaSpatial = 10;  % common for every mode
    sigmaColor   = 25;  % common for every mode

    % build the user interface (no resizing to keep it simple)
    h = struct();
    h.src = img;
    h.fig = figure('Name','DTFilter Demo', ...
        'NumberTitle','off', 'Menubar','none', 'Resize','off', ...
        'Position',[200 200 sz(2)*2 sz(1)+155-1]);
    if ~mexopencv.isOctave()
        %HACK: not implemented in Octave
        movegui(h.fig, 'center');
    end
    h.ax(1) = axes('Parent',h.fig, 'Units','pixels', 'Position',[1 155 sz(2) sz(1)]);
    h.ax(2) = axes('Parent',h.fig, 'Units','pixels', 'Position',[sz(2)+1 155 sz(2) sz(1)]);
    if ~mexopencv.isOctave()
        imshow(img, 'Parent',h.ax(1));
        h.img = imshow(img, 'Parent',h.ax(2));
    else
        %HACK: https://savannah.gnu.org/bugs/index.php?45473
        axes(h.ax(1)); imshow(img);
        axes(h.ax(2)); h.img = imshow(img);
    end
    opts = {'FontSize',11, 'HorizontalAlignment','right'};
    uicontrol('Parent',h.fig, 'Style','text', 'FontSize',11, ...
        'Position',[5 5 150 20], 'String','Mode');
    h.pop = uicontrol('Parent',h.fig, 'Style','popupmenu', ...
        'Position',[155 5 150 20], ...
        'String',{'DetailsEnhancement','Stylizing','Blurring','DoNothing'});
    h.txt(1) = uicontrol('Parent',h.fig, 'Style','text', opts{:}, ...
        'Position',[5 30 150 20], 'String',sprintf('Detail contrast: %3d',contrastBase));
    h.txt(2) = uicontrol('Parent',h.fig, 'Style','text', opts{:}, ...
        'Position',[5 55 150 20], 'String',sprintf('Detail level: %3d',   detailsLevel));
    h.txt(3) = uicontrol('Parent',h.fig, 'Style','text', opts{:}, ...
        'Position',[5 80 150 20], 'String',sprintf('Style gamma: %3d',    edgesGamma));
    h.txt(4) = uicontrol('Parent',h.fig, 'Style','text', opts{:}, ...
        'Position',[5 105 150 20], 'String',sprintf('Sigma Spatial: %3d', sigmaSpatial));
    h.txt(5) = uicontrol('Parent',h.fig, 'Style','text', opts{:}, ...
        'Position',[5 130 150 20], 'String',sprintf('Sigma Color: %3d',   sigmaColor));
    h.slid(1) = uicontrol('Parent',h.fig, 'Style','slider', ...
        'Value',contrastBase, 'Min',0, 'Max',200, 'SliderStep',[1 10]./(200-0), ...
        'Position',[155 30 sz(2)*2-155-10 20]);
    h.slid(2) = uicontrol('Parent',h.fig, 'Style','slider', ...
        'Value',detailsLevel, 'Min',0, 'Max',200, 'SliderStep',[1 10]./(200-0), ...
        'Position',[155 55 sz(2)*2-155-10 20]);
    h.slid(3) = uicontrol('Parent',h.fig, 'Style','slider', ...
        'Value',edgesGamma, 'Min',0, 'Max',300, 'SliderStep',[1 10]./(300-0), ...
        'Position',[155 80 sz(2)*2-155-10 20]);
    h.slid(4) = uicontrol('Parent',h.fig, 'Style','slider', ...
        'Value',sigmaSpatial, 'Min',0, 'Max',200, 'SliderStep',[1 10]./(200-0), ...
        'Position',[155 105 sz(2)*2-155-10 20]);
    h.slid(5) = uicontrol('Parent',h.fig, 'Style','slider', ...
        'Value',sigmaColor, 'Min',0, 'Max',200, 'SliderStep',[1 10]./(200-0), ...
        'Position',[155 130 sz(2)*2-155-10 20]);

    % hook event handlers, and trigger default start
    set([h.pop, h.slid], 'Callback',{@onChange,h}, ...
        'Interruptible','off', 'BusyAction','cancel');
    onChange([],[],h);
end

function dst = filterDoNothing(frame, ~)
    %FILTERDONOTHING  Trivial filter

    dst = frame;
end

function dst = filterBlurring(frame, opts)
    %FILTERBLURRING  Simple edge-aware blurring

    dst = cv.DTFilter.dtFilter(frame, frame, 'Mode','RF', ...
        'SigmaSpatial',opts.sigmaSpatial, 'SigmaColor',opts.sigmaColor);
end

function dst = filterStylizing(frame, opts)
    %FILTERSTYLIZING  Stylizing filter

    % blur frame
    filtered = cv.DTFilter.dtFilter(frame, frame, 'Mode','NC', ...
        'SigmaSpatial',opts.sigmaSpatial, 'SigmaColor',opts.sigmaColor);

    % compute grayscale blurred frame
    filteredGray = cv.cvtColor(filtered, 'RGB2GRAY');

    % find gradients of blurred frame
    gradX = cv.Sobel(filteredGray, 'XOrder',1, 'YOrder',0, 'KSize',3, ...
        'DDepth','single', 'Scale',1/255);
    gradY = cv.Sobel(filteredGray, 'XOrder',0, 'YOrder',1, 'KSize',3, ...
        'DDepth','single', 'Scale',1/255);

    % compute magnitude of gradient and fit it accordingly the gamma parameter
    gradMagnitude = hypot(gradX, gradY) .^ (opts.edgesGamma/100.0);

    % multiply a blurred frame to the value inversely proportional to the magnitude
    multiplier = 1.0 ./ (1.0 + gradMagnitude);
    multiplier = cv.cvtColor(multiplier, 'GRAY2RGB');
    dst = uint8(single(filtered) .* multiplier);
end

function dst = filterDetailsEnhancement(frame8u, opts)
    %FILTERDETAILSENHANCEMENT  Details enhancement filter

    frame = single(frame8u) / 255;

    % Decompose image to 3 Lab channels
    frameLab = cv.cvtColor(frame, 'RGB2Lab');

    % Generate progressively smoother versions of the lightness channel
    layer0 = frameLab(:,:,1);  % first channel is original lightness
    layer1 = cv.DTFilter.dtFilter(layer0, layer0, 'Mode','IC', ...
        'SigmaSpatial',opts.sigmaSpatial, 'SigmaColor',opts.sigmaColor);
    layer2 = cv.DTFilter.dtFilter(layer1, layer1, 'Mode','IC', ...
        'SigmaSpatial',2*opts.sigmaSpatial, 'SigmaColor',opts.sigmaColor);

    % Compute detail layers
    detailLayer1 = layer0 - layer1;
    detailLayer2 = layer1 - layer2;

    cBase = opts.contrastBase / 100.0;
    cDetails1 = opts.detailsLevel / 100.0;
    cDetails2 = 2.0 - opts.detailsLevel / 100.0;

    % Generate lightness
    meanLigtness = mean(mean(frameLab(:,:,1)));
    frameLab(:,:,1) = cBase*(layer2 - meanLigtness) + meanLigtness;  % fit contrast of base (most blurred) layer
    frameLab(:,:,1) = frameLab(:,:,1) + cDetails1*detailLayer1;  % add weighted sum of detail layers to new lightness
    frameLab(:,:,1) = frameLab(:,:,1) + cDetails2*detailLayer2;

    % Update new lightness
    dst = uint8(cv.cvtColor(frameLab, 'Lab2RGB') * 255);
end