Laplacian Pyramid Construction and Merging

References:

Sources:

function varargout = lap_pyr_demo_gui(varargin)
    % create the UI
    nlevel = 6;  % number of pyramid levels
    h = buildGUI(nlevel, varargin{:});
    if nargout > 0, varargout{1} = h; end

    % main loop
    while ishghandle(h.fig)
        % get new frame
        frame = h.cap.read();
        if isempty(frame), break; end

        % levels control values
        vals = get(h.slid, 'Value');

        % Laplacian pyramid filtering
        pyr = build_lap_pyr(frame, nlevel);
        pyr = cellfun(@(p,v) p*round(v/5), pyr, vals, 'UniformOutput',false);
        res = merge_lap_pyr(pyr);

        % display
        set(h.img, 'CData',res);
        drawnow;
    end
end

function levels = build_lap_pyr(img, nlevel, dtype)
    %BUILD_LAP_PYR  Build Laplacian pyramid
    %
    %     levels = build_lap_pyr(img, nlevel)
    %     levels = build_lap_pyr(img, nlevel, dtype)
    %
    % ## Input
    % * __img__ input image
    % * __nlevel__ number of pyramid levels
    % * __dtype__ precision for image arithmetics, default 'int16'
    %
    % ## Output
    % * __levels__ Laplacian pyramid, cell array of length `nlevel`
    %
    % See also: cv.buildPyramid, cv.Blender.createLaplacePyr
    %

    if nargin < 3, dtype = 'int16'; end
    img = cast(img, dtype);
    levels = cell(nlevel,1);
    for i=1:nlevel-1
        next_img = cv.pyrDown(img);
        img1 = cv.pyrUp(next_img, 'DstSize',[size(img,2) size(img,1)]);
        levels{i} = img - img1;
        img = next_img;
    end
    levels{nlevel} = img;
end

function img = merge_lap_pyr(levels)
    %MERGE_LAP_PYR  Reconstruct image from Laplacian pyramid
    %
    %     img = merge_lap_pyr(levels)
    %
    % ## Input
    % * __levels__ Laplacian pyramid, cell array
    %
    % ## Output
    % * __img__ output image
    %
    % See also: cv.Blender.restoreImageFromLaplacePyr
    %

    img = levels{end};
    for i=numel(levels)-1:-1:1
        lev_img = levels{i};
        img = cv.pyrUp(img, 'DstSize',[size(lev_img,2) size(lev_img,1)]);
        img = img + lev_img;
    end
    img = uint8(min(max(img, 0), 255));
end

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

    % update UI
    for i=1:numel(h.slid)
        v = round(get(h.slid(i), 'Value'));
        set(h.txt(i), 'String',sprintf('Level %d: %d', i, v));
    end
    drawnow;
end

function h = buildGUI(nlevel, varargin)
    %BUILDGUI  Creates the UI

    % setup video capture
    if nargin > 1
        cap = cv.VideoCapture(varargin{:});
    else
        cap = createVideoCapture([], 'lena');
    end
    assert(cap.isOpened(), 'Could not initialize capturing');

    % video settings
    frame = cap.read();
    assert(~isempty(frame), 'Could not read frame');
    sz = size(frame);

    % build the user interface (no resizing to keep it simple)
    h = struct();
    h.cap = cap;
    h.fig = figure('Name','Laplacian Pyramid Filter', ...
        'NumberTitle','off', 'Menubar','none', 'Resize','off', ...
        'Position',[200 200 sz(2) sz(1)+5+nlevel*25-1]);
    if ~mexopencv.isOctave()
        %HACK: not implemented in Octave
        movegui(h.fig, 'center');
    end
    h.ax = axes('Parent',h.fig, 'Units','pixels', ...
        'Position',[1 5+nlevel*25 sz(2) sz(1)]);
    if ~mexopencv.isOctave()
        h.img = imshow(frame, 'Parent',h.ax);
    else
        %HACK: https://savannah.gnu.org/bugs/index.php?45473
        axes(h.ax);
        h.img = imshow(frame);
    end
    for i=1:nlevel
        h.txt(i) = uicontrol('Parent',h.fig, 'Style','text', ...
            'Position',[5 5+(i-1)*25 130 20], 'FontSize',11, ...
            'String',sprintf('Level %d: 5',i));
        h.slid(i) = uicontrol('Parent',h.fig, 'Style','slider', ...
            'Position',[135 5+(i-1)*25 sz(2)-135-5 20], 'Value',5, ...
            'Min',0, 'Max',50, 'SliderStep',[1 5]./(50-0));
    end

    % hook event handlers
    set(h.slid, 'Callback',{@onChange,h}, ...
        'Interruptible','off', 'BusyAction','cancel');
end