1

I am programming a GUI for the selection of regions of interest (ROIs) in an image. Several kinds of regions may be chosen using built-in MATLAB functions such as impoly/imellipse.

Below, I provide a minimal working example of a GUI where my problem may be addressed.

The question is: supposing one user misclicks one of the ROI selection buttons (i.e. chooses to select an Ellipse when the goal was to select a Polygon) how can I cancel the interactive tool for the ROI selection AVOIDING errors in the workspace?

I know that pressing the "Esc" key cancels the interactive tool but I would like to avoid errors.

One idea is to have another push button (Abort) to perform the interruption but I've been unable to come up with code to perform this operation.

function roiData = testGUI(sourceImage)

% Initialize main figure
hdl.mainfig = figure();

% Initialize roiData and roiCounter
roiData    = [];
roiCounter = 0;

% Select Elliptical ROI button
hdl.selectEllipseButton = uicontrol(hdl.mainfig, 'Units', 'normalized', 'Position',[0.05 0.7 0.2 0.1], 'String', 'Select Ellipse', 'Callback', @selectEllipse);
% Select Polygonal ROI button
hdl.selectPolygonButton = uicontrol(hdl.mainfig, 'Units', 'normalized', 'Position',[0.05 0.6 0.2 0.1], 'String', 'Select Polygon', 'Callback', @selectPolygon);
% Abort Button
hdl.abort = uicontrol(hdl.mainfig, 'Units', 'normalized', 'Position',[0.05 0.2 0.2 0.2], 'String', 'Abort', 'Callback', @abort);
% Axes
hdl.axes = axes('Parent',hdl.mainfig, 'Units', 'Normalized', 'Position', [0.35 0.2 0.6 0.6]);


    function selectEllipse(~, ~, ~)
        imshow(sourceImage, [], 'Parent', hdl.axes);
        roiCounter = roiCounter + 1;
        h = imellipse(hdl.axes);
        mask = uint16(createMask(h));
        wait(h);            
        roiData(roiCounter).mask = mask;
    end

    function selectPolygon(~, ~, ~)
        imshow(sourceImage, [], 'Parent', hdl.axes);
        roiCounter = roiCounter + 1;
        h = impoly(hdl.axes);
        mask = uint16(createMask(h));
        wait(h);
        roiData(roiCounter).mask = mask;
    end

    function abort(~, ~, ~)
        cla(hdl.axes)
        % I need something else here... (see above)
    end

% Pause until figure is closed
waitfor(hdl.mainfig);

end
tshepang
  • 12,111
  • 21
  • 91
  • 136
fnery
  • 758
  • 1
  • 11
  • 22
  • This is a great question and one I wasn't able to figure out at the time when I wanted to do something similar. I'll look into this. My work around at the time was to basically force the user to draw whatever they selected but then after they were done they could delete the imroi by overwriting the `'UIContextMenu'`. – JustinBlaber Mar 17 '13 at 23:33

1 Answers1

1

Try this out:

function roiData = testGUI(sourceImage)
% Initialize main figure
hdl.mainfig = figure();

% Initialize roiData and roiCounter
roiData    = [];
roiCounter = 0;

% Select Elliptical ROI button
hdl.selectEllipseButton = uicontrol(hdl.mainfig, 'Units', 'normalized', 'Position',[0.05 0.7 0.2 0.1], 'String', 'Select Ellipse', 'Callback', @selectEllipse);
% Select Polygonal ROI button
hdl.selectPolygonButton = uicontrol(hdl.mainfig, 'Units', 'normalized', 'Position',[0.05 0.6 0.2 0.1], 'String', 'Select Polygon', 'Callback', @selectPolygon);
% Abort Button
hdl.abort = uicontrol(hdl.mainfig, 'Units', 'normalized', 'Position',[0.05 0.2 0.2 0.2], 'String', 'Abort', 'Callback', @abort);
% Axes
hdl.axes = axes('Parent',hdl.mainfig, 'Units', 'Normalized', 'Position', [0.35 0.2 0.6 0.6]);

% Callbackrunning parameter
callbackrunning = false;

    function selectEllipse(hObject,eventdata)   
        if(callbackrunning)
            return;
        end

        callbackrunning = true;

        imshow(sourceImage, [], 'Parent', hdl.axes);
        roiCounter = roiCounter + 1;
        h = imellipse(hdl.axes);
        if(~callbackrunning)
            return;
        end
        mask = uint16(createMask(h));
        roiData(roiCounter).mask = mask;

        callbackrunning = false;
    end

    function selectPolygon(hObject,eventdata)   
        if(callbackrunning)
            return;
        end

        callbackrunning = true;

        imshow(sourceImage, [], 'Parent', hdl.axes);
        roiCounter = roiCounter + 1;
        h = impoly(hdl.axes);
        if(~callbackrunning)
            return;
        end
        mask = uint16(createMask(h));
        roiData(roiCounter).mask = mask;

        callbackrunning = false;
    end

    function abort(hObject,eventdata)   
        if(callbackrunning)
            disp('Fire escape key to cancel previous imrect');
            % Create robot that fires an escape key
            robot = java.awt.Robot;
            robot.keyPress    (java.awt.event.KeyEvent.VK_ESCAPE);
            robot.keyRelease  (java.awt.event.KeyEvent.VK_ESCAPE);   
            callbackrunning = false;
        end
    end

% Pause until figure is closed
waitfor(hdl.mainfig);
end

The robot object fires the escape key which cancels imrect.

JustinBlaber
  • 4,629
  • 2
  • 36
  • 57
  • Thanks man, it works :). Thanks a lot for introducing me to the java robot class! – fnery Mar 18 '13 at 13:52
  • @fnery Dude I just found out about it and it looks really really cool. Here's more about it here: http://undocumentedmatlab.com/blog/gui-automation-robot/. Also, you might need to make sure you aren't having a memory leak with the java robot. Its possible you need to use `clear java` at the end of the script. – JustinBlaber Mar 18 '13 at 18:56