Interactive Mask demo

Interactively create a polygon mask.

This tutorial demonstrates how to make mask image (black and white). The program takes as input a source image and outputs its corresponding mask image.

Sources:

function varargout = create_mask_demo_gui(im)
    % load an image
    if nargin < 1
        src = imread(fullfile(mexopencv.root(),'test','fruits.jpg'));
    elseif isempty(im)
        [im,cancel] = imgetfile();
        assert(~cancel, 'No file selected');
        src = imread(im);
    elseif ischar(im)
        src = cv.imread(im, 'Color',true);
    else
        src = im;
    end
    assert(size(src,3) == 3, 'Expecting a color source image');

    % shared data
    sz = size(src);
    out = [];        % output image
    pts = [];        % 2D points

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


    function h = buildGUI()
        %BUILDGUI  Creates the UI

        h = struct();
        h.fig = figure('Name','Interactive Mask', 'Menubar','none', ...
            'Position',[200 200 sz(2) sz(1)]);
        if ~mexopencv.isOctave()
            %HACK: not implemented in Octave
            movegui(h.fig, 'center');
        end
        h.ax = axes('Parent',h.fig, 'Units','normalized', 'Position',[0 0 1 1]);
        if ~mexopencv.isOctave()
            h.img = imshow(src, 'Parent',h.ax);
        else
            %HACK: https://savannah.gnu.org/bugs/index.php?45473
            axes(h.ax);
            h.img = imshow(src);
        end
    end

    function onHelp(~,~)
        %ONHELP  Display usage help dialog

        hd = helpdlg({
            'Left-click the mouse to define the vertices of the polygon.'
            'Right-click when finished to close the polygon, connecting'
            'the last vertex with the first.'
            'Hot keys:'
            '  h: usage dialog'
            '  r: reset'
            '  s: save current output image as PNG image'
            '  p: save current points matrix as MAT-file'
            '  q: quit the program'
        }, 'Interactive mask demo');
        set(hd, 'WindowStyle','modal');
    end

    function onReset(~,~)
        %ONRESET  Restart from scratch

        % reset data
        pts = zeros(0,2);
        out = src;

        % register mouse button handlers and change cursor
        set(h.fig, 'Pointer','cross', 'WindowKeyPressFcn',@onType, ...
            'WindowButtonDownFcn',@onMouseDown, ...
            'WindowButtonUpFcn',@onMouseUp);

        % update plot
        set(h.img, 'CData',out);
        drawnow;
    end

    function onType(~,e)
        %ONTYPE  Event handler for key press on figure

        % handle keys
        switch e.Key
            case 'r'
                onReset([],[]);
            case 'h'
                onHelp([],[]);
            case 's'
                fname = [tempname() '.png'];
                imwrite(out, fname);
                fprintf('Output saved as "%s"\n', fname);
            case 'p'
                uisave({'pts'}, 'points.mat');
            case {'q', 'escape'}
                close(h.fig);
        end
    end

    function onMouseDown(~,~)
        %ONMOUSEDOWN  Event handler for mouse down on figure

        if strcmp(get(h.fig,'SelectionType'), 'normal')
            % get current location of mouse pointer
            pt = get(h.ax, 'CurrentPoint');
            pt = round(pt(1,1:2));

            % draw point
            out = cv.circle(out, pt, 2, ...
                'Color',[255 0 0], 'Thickness','Filled');

            % store point
            pts(end+1,:) = pt;

            % connect to previous point by a straight line
            if size(pts,1) >= 2
                out = cv.line(out, pts(end-1,:), pts(end,:), ...
                    'Color',[255 0 0], 'Thickness',2);
            end

            % update plot
            set(h.img, 'CData',out);
            drawnow;
        else
            % create a polygon from all vertices
            out = src;
            if ~isempty(pts)
                out = cv.polylines(out, pts, ...
                    'Closed',true, 'Color',[0 0 0], 'Thickness',2);
            end

            % update plot
            set(h.img, 'CData',out);
            drawnow;
        end
    end

    function onMouseUp(~,~)
        %ONMOUSEUP  Event handler for mouse up on figure

        % only respond to right mouse button up
        if strcmp(get(h.fig,'SelectionType'), 'normal'), return; end

        % unregister all mouse button handlers
        set(h.fig, 'Pointer','arrow', ...
            'WindowButtonDownFcn','', 'WindowButtonUpFcn','');

        % create binary mask from polygon
        mask = zeros(sz(1:2), 'uint8');
        if ~isempty(pts)
            mask = cv.fillPoly(mask, pts, 'Color',[255 255 255]);
        end
        cv.imwrite(fullfile(tempdir(), 'mask.png'), mask);
        disp(['Mask saved: ' fullfile(tempdir(), 'mask.png')]);

        % apply mask on image
        out = bsxfun(@times, uint8(mask~=0), src);

        % update plot
        set(h.img, 'CData',out);
        drawnow;
    end
end