Code covered by the BSD License  

Highlights from
SegmentTool: An Interactive GUI for Segmenting Images

image thumbnail

SegmentTool: An Interactive GUI for Segmenting Images

by

 

09 Nov 2012 (Updated )

Interactively find the best approach to segmenting (masking) your image

sliderPanel(parent,PanelPVs,SliderPVs,EditPVs,LabelPVs,numFormat,varargin)
function [sliderHandle,panelHandle,editHandle] = sliderPanel(parent,PanelPVs,SliderPVs,EditPVs,LabelPVs,numFormat,varargin)
% [sliderHandle,panelHandle,editHandle] = sliderPanel(parent,PanelPVs,SliderPVs,EditPVs,LabelPVs,numFormat)
%
% Creates a slider in a separate uipanel, with an associated
% interactive EditBox, and left and right labels showing the
% minimum and maximum values of the slider, respectively.
% Moving the slider automatically updates the textbox, and vice
% versa. Both slider movement and text edits will trigger
% (non-recursively) the callback of the slider.
%
% The EditBox automatically disallows the entry of
% non-numeric values, or of values outside of [min,max].
% Attempts to enter disallowed values will be ignored.
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% SYNTAXES:
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% 1) The main syntax for sliderPanel allows full control of
% all elements of the uitool. (See INPUT ARGUMENTS below for
% details).
%
% [sliderHandle,panelHandle,editHandle] =
%     sliderPanel(parent,PanelPVs,SliderPVs,EditPVs,LabelPVs,numFormat);
%
% 2)The following syntax captures a small subset of the
% sliderPanel functionality;
%
% hFig = gcf;
% sliderPanel(...
% 		'Parent'  , hFig, ...
% 		'Title'   , 'Slider Panel', ...
% 		'Position', [0.3 0.5 0.4 0.2], ...
%       'Backgroundcolor', 'r',...
% 		'Min'     , 0, ...
% 		'Max'     , 100, ...
% 		'Value'   , 50, ...
% 		'FontName', 'Verdana', ...
% 		'Callback', @myCallback);
%
% Note: This simplified syntax supports a small subset of
% the available PV pairs that can be controlled via the
% primary syntax. Supported parameters, and the object to
% which they assigned, are shown below:
%
% Parameter       Object(s) affected
%____________________________________
% Parent            uipanel
% Title             uipanel
% Position          uipanel
% Backgroundcolor   uipanel
% Bordertype        uipanel
% Tag               uipanel
% Fontname          uipanel, edit box, labels
% Fontweight        uipanel, edit box, labels
% Fontsize          uipanel, edit box, labels
% Min               slider
% Max               slider
% Value             slider
% Sliderstep        slider
% Callback          slider (and, by extension, edit box)
% Units             uipanel, slider, edit box, labels
% Visible           uipanel (as a parent)
% Numberformat      edit box
%
% (The code is easily modifiable to add new PV support.)
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% INPUT ARGUMENTS (ALL OPTIONAL):
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%    PARENT:    the handle of the parent object for the
%               uipanel. Default is the current figure.
%
%    PANELPVS:  a cell array of any parameter-value pairs
%               valid for the uipanel object, or a structure
%               of same. (Defaults are those used by
%               UIPANEL.)
%
%    SLIDERPVS: a cell array of any parameter-value pairs
%               valid for the slider object, or a structure
%               of same. (Defaults are those used by
%               UICONTROL('STYLE','SLIDER').)
%
%    EDITPVS:   a cell array of any parameter-value pairs
%               valid for the UICONTROL EDIT object, or a
%               structure of same. (Defaults are those used
%               by UICONTROL('STYLE','EDIT').)
%
%    LABELPVS:  a cell array of any parameter-value pairs
%               valid for the UICONTROL TEXT objects used to
%               label the slider, or a structure of same.
%               (Defaults are those used by
%               UICONTROL('STYLE','TEXT').)
%               NOTE: The following are valid syntaxes for LABELPVS:
%               1) Applied to both (l/r) labels:
%                  {param1, val1, param2, val2,...}
%               2) First array applied to left label, second
%                  array applied to right label:
%                  {{P-V array},{P-V array}}
%               3) First array applied to left label, second
%                  array applied to right label, third array
%                  applied to both (l/r) labels:
%                  {{P-V array},{P-V array},{P-V array}}
%
%    NUMFORMAT: a format string accepted by SPRINTF, which
%               controls the formatting of the EditBox when
%               the slider is dragged. (Typing directly in
%               the EditBox does not trigger the formatting
%               constraint.)
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% OUTPUT ARGUMENTS:
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%    SLDRHNDL:  handles of slider object.
%
%    PNLHNDL:   handle of uipanel object.
%
%    EDITHNDL:  handle of edit box.
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% EXAMPLES:
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% 1) Create a single-slider sliderPanel using cell-array inputs.
%
% [sldr,pnl,edt] = ...
%                   sliderPanel(gcf,...
%                   {'title','Threshold','pos',[0.1 0.2 0.8 0.15],'fontweight','b','units','pixels'},...
%                   {'max',80,'value',60,'callback','disp(''Slid'')'},...
%                   {},...
%                   {{'string','Low','foregroundcolor','b'},...
%                      {'string','High','foregroundcolor','r'},...
%                      {'fontweight','b'}},...
%                   '%0.1f');
%
% 2) Create a single-slider sliderPanel as a child of a
% uipanel, using struct and cell-array inputs. Moving the
% slider or updating the edit box will immediately refresh
% the value of variable sliderVal in the base workspace.
%
% hFig = figure;
% hPanel = uipanel(hFig,'title','MASTER','pos',[0.3 0.1 0.4 0.6]);
% PnlOpt.title = 'Threshold';
% PnlOpt.position = [0.05 0.2 0.9 0.4];
% PnlOpt.fontsize = 10;
% SldrOpt.min = 10;
% SldrOpt.max = 100;
% SldrOpt.value = 50;
% SldrOpt.callback = 'assignin(''base'',''sliderVal'',get(gcbo,''value''));';
% sliderPanel(hPanel,PnlOpt,SldrOpt,{'fontsize',12},{},'%0.0f')
%
% 3) Create multiple sliderPanels as children of a UIPANEL.
%
% figure;
% h = uipanel(gcf,'title','MAIN','units','normalized','pos',[0.2 0.1 0.6 0.8]);
% PnlOpt.title = 'Parameter Tool';
% PnlOpt.bordertype = 'none';
% PnlOpt.titleposition = 'centertop';
% PnlOpt.fontweight = 'bold';
% SldrOpt.min = 0;
% SldrOpt.max = 255;
% SldrOpt.value = 50;
% EditOpts = {'fontsize',10};
% LabelOpts = {'fontsize',9,'fontweight','b'};
% numFormat = '%0.0f';
% titleStrings = {'Slider 1','Slider 2', 'Slider 3', 'Slider 4'};
% startPos = {[0.1 0.05 0.8 0.21];
% 	        [0.1 0.28 0.8 0.21];
% 	        [0.1 0.51 0.8 0.21];
% 	        [0.1 0.74 0.8 0.21]};
% 		sldrCallbacks = {'disp(''Slider 1 moved'')';
% 			'disp(''Slider 2 moved'')';
% 			'disp(''Slider 3 moved'')';
% 			'disp(''Slider 4 moved'')'};
% for ii = 1:4
% 	PnlOpt.position = startPos{ii};
% 	PnlOpt.title = titleStrings{ii};
% 	SldrOpt.callback = sldrCallbacks{ii};
% 	sliderPanel(h,PnlOpt,SldrOpt,EditOpts,LabelOpts,numFormat);
% end
%
% 4) Demonstrate the use of sliderPanel in a function that
% interactively thresholds an image. (To try this example,
% paste the following code (both TEST and THRESH) into a new
% mfile, save it, and run it.)
%
% function test
% figure;
% ax1 = axes('units','normalized','pos',[0.1 0.25 0.8 0.7]);
% myimg = im2double(imread('cameraman.tif'));
% imobj = imshow(myimg);
% sliderPanel(gcf,{'pos',[0.1 0.05 0.8 0.15]},{'callback',{@thresh,myimg,imobj}},{},{},'%0.1f')
%
% % SUBFUNCTION:
% function thresh(varargin)
% sldr = varargin{1};
% newval = get(sldr,'value');
% myimg = varargin{3};
% imobj = varargin{4};
% set(imobj,'cdata',myimg > newval);
%
% 5) SIMPLIFIED SYNTAX
%
% hFig = gcf;
% [a,b]=sliderPanel(...
% 'Title'   , 'Slider Panel', ...
%   'Position', [0.3, 0.5, 0.4, 0.2], ...
%   'Min'     , 0, ...
%   'Max'     , 100, ...
%   'Value'   , 50, ...
%   'String'  , 'Slider 1', ...
%   'FontName', 'Verdana', ...
%   'units','pixels',...
%   'numformat','%0.0f',...
%   'Callback', 'disp(''slid'')')

% REVISIONS:
% 06/07/2010
% Modified simple syntax to specify label colors the same as background
% colors, unless otherwise set.
% 
% 10/10/2012
% Implemented right-click resetting to default (initial) value. Right-click
% anywhere on the slider itself, and the slider and text reset
% automatically.
%
% 1/18/2013 Right-clicking to reset default now (appropriately) triggers
% slider's callback.

% Written by Brett Shoelson, PhD
% brett.shoelson@mathworks.com
% 01/21/07
% Copyright 2007 - 2012 MathWorks, Inc.


if nargin < 6, numFormat = []; end
if nargin < 5, LabelPVs = {}; end
if nargin < 4, EditPVs = {}; end
if nargin < 3, SliderPVs = {}; end
if nargin < 2, PanelPVs = {}; end
if nargin == 0, parent = gcf; end

% OPTIONAL CALLING SYNTAX:
% Subset of functionality available for this calling syntax
if nargin > 1 && ~iscell(PanelPVs) && ~isa(PanelPVs,'struct')
    allargs = {parent,PanelPVs,SliderPVs,EditPVs,LabelPVs,numFormat,varargin{:}};
    PanelPVs = {};SliderPVs = {};EditPVs = {};LabelPVs = {};numFormat = [];
    if ishandle(allargs{1}),
        parent = allargs{1};
    end
    loc = cellfind(allargs,'parent');
    if ~isempty(loc)
        parent = allargs{loc+1};
    end
    if ~ishandle(parent)
        parent = gcf;
    end
    loc = cellfind(allargs,'numformat');
    if ~isempty(loc)
        numFormat = allargs{loc+1};
    end
    loc = cellfind(allargs,'numberformat');
    if ~isempty(loc)
        numFormat = allargs{loc+1};
    end

    PanelPVs = validate(allargs,PanelPVs,...
        {'title','pos','position','fontname','fontname','fontsize',...
        'fontweight','backgroundcolor','bordertype','tag','units','visible','userdata'});
    SliderPVs = validate(allargs,SliderPVs,...
        {'min','max','value','callback','sliderstep','units'});
    EditPVs = validate(allargs,EditPVs,...
        {'fontname','fontsize','fontweight'});
    LabelPVs = validate(allargs,LabelPVs,{'fontname','fontsize',...
        'fontweight','units'});
    if isfield(PanelPVs,'backgroundcolor') && ~isfield(LabelPVs,'backgroundcolor')
        LabelPVs.backgroundcolor = PanelPVs.backgroundcolor;
    end
end

% CREATE PANEL (DEFAULT)
% Uipanels can be children of figures, uipanels, or
% uibuttongroups; the latter two are of 'type' 'uipanel'

% CREATE UIPANEL AS PARENT
panelHandle = uipanel('parent',parent);

% CREATE SLIDER (DEFAULT)
sliderHandle = uicontrol(panelHandle,'style','slider','units','normalized',...
    'pos',[0.05 0.5 0.9 0.40]);
% CREATE EDIT BOX (DEFAULT)
editHandle = uicontrol(panelHandle,'style','edit','units','normalized',...
    'pos',[0.35 0.05 0.3 0.35]);
% CREATE LABELS (DEFAULT)
labelHandle(1) = uicontrol(panelHandle,'style','text','units','normalized',...
    'pos',[0.05 0.05 0.25 0.25],'horizontalalignment','l',...
    'Backgroundcolor',get(panelHandle,'Backgroundcolor'));
labelHandle(2) = uicontrol(panelHandle,'style','text','units','normalized',...
    'pos',[0.7 0.05 0.25 0.25],'horizontalalignment','r',...
    'Backgroundcolor',get(panelHandle,'Backgroundcolor'));

% CUSTOMIZE PER USER-DEFINED PV PAIRS
applyPVs(panelHandle,PanelPVs);
applyPVs(sliderHandle,SliderPVs);

% EXTRACT SLIDER PARAMETERS
sldr.minval = get(sliderHandle,'min');
sldr.maxval = get(sliderHandle,'max');
sldr.value = get(sliderHandle,'value');
%Ensure that slider's value is in  acceptable range of
%[min,max]
if sldr.value < sldr.minval || sldr.value > sldr.maxval
    disp('Out-of-range value ignored for slider object.');
    sldr.value = sldr.minval;
    set(sliderHandle,'value',sldr.value);
end
sldr.callback = get(sliderHandle,'callback');

set(labelHandle(1),'string',sldr.minval);
set(labelHandle(2),'string',sldr.maxval);
set(editHandle,'string',sldr.value);

applyPVs(editHandle,EditPVs);
% If LabelPVs is a 1x2 array of cells, then cell 1 is
% applied to label 1, and cell 2 is applied to label 2.
% Otherwise,
if numel(LabelPVs)>1 && iscell(LabelPVs{1}) && iscell(LabelPVs{2})
    applyPVs(labelHandle(1),LabelPVs{1});
    applyPVs(labelHandle(2),LabelPVs{2});
else
    applyPVs(labelHandle,LabelPVs);
end
% If a third array of PVs is provided, use it for both
% labels.
if numel(LabelPVs)>2 && iscell(LabelPVs{3})
    applyPVs(labelHandle,LabelPVs{3});
end

%USERDATA IS A CHAR
set(editHandle,'userdata',get(editHandle,'string'));

%GIVE ADDHNDLEVENT/EVALHNDLEVENT A TRY
addHndlEvent(sliderHandle,'callback',@updateText);
addHndlEvent(editHandle,'callback',@updateSlider);

    function applyPVs(obj,pvarray)
        if isstruct(pvarray)
            set(obj,pvarray);
        else %Cell
            if ~isempty(pvarray)
                for ii = 1:2:numel(pvarray)
                    set(obj,pvarray{ii},pvarray{ii+1});
                end
                %set(obj,pvarray{:});
            end
        end
        % NEW: 10/10/2012: implement right-click resetting of default
        %      initial value
        if strcmp(get(obj,'type'),'uicontrol')
            if strcmp(get(obj,'style'),'slider')
            val = get(obj,'value');
            set(obj,'buttondownfcn',{@resetDefault,gcbo,val});
            end
        end
    end

    function resetDefault(varargin)
        set(varargin{1},'value',varargin{4});
        updateText;
        if ~isempty(sldr.callback)
            feval(sldr.callback,varargin{1});
        end
    end

    function updateText(varargin)
        %Triggered by slider move
        newVal = get(sliderHandle,'value');
        if ~isempty(numFormat)
            newVal = sprintf(numFormat,newVal);
        end
        set(editHandle,'string',newVal,'userdata',newVal);
        drawnow;
    end

    function updateSlider(varargin)
        %Triggered by text change
        newVal = get(editHandle,'string');
        if isnan(str2double(newVal)) || str2double(newVal) < get(sliderHandle,'min') || str2double(newVal) > get(sliderHandle,'max')
            set(editHandle,'string',get(editHandle,'userdata'));
            return
        end
        set(sliderHandle,'value',str2double(newVal));
        set(editHandle,'userdata',newVal);
        set(editHandle,'value',str2double(newVal));%BUG FIX, 7/1/10
        drawnow;
        currFun = sldr.callback;
        if isempty(currFun)
            %Do nothing
        elseif isa(currFun,'function_handle')
            %REPLACE OBJECT HANDLE WITH THAT OF SLIDER
            varargin{1} = sliderHandle;
            currFun(varargin{:});
            %feval(sldr.callback,varargin{:});
        elseif isa(currFun,'char')
            %Just in case the callback specifies GCBO (which
            %will initially point to the edit box, rather
            %than the slider:
            currFun = strrep(currFun,'gcbo','sliderHandle');
            eval(currFun);
        elseif isa(currFun,'cell');
            %REPLACE OBJECT HANDLE WITH THAT OF SLIDER
            varargin{1} = sliderHandle;
            currFun{1}(varargin{:},currFun{2:end});
        else
            %Shouldn't ever get here...but if you do, I
            %would like to know about it.
            error('Unrecognized event registered in eventid %d.',ii);
        end
    end

    function addHndlEvent(hndl,eventtype,newevent)
        hndlevent = getappdata(hndl,[eventtype 'hndlevent']);
        if isempty(hndlevent)
            %Initialize to original event comand
            hndlevent.cmdset = get(hndl,eventtype);
        end
        if isempty(hndlevent.cmdset)
            numevents = 0;
        else
            numevents = numel(hndlevent);
        end
        hndlevent(numevents+1).cmdset = newevent;

        set(hndl,eventtype,@evalHndlEvent);
        setappdata(hndl,[eventtype 'hndlevent'],hndlevent);

        function evalHndlEvent(varargin)
            hndlevent = getappdata(hndl,[eventtype 'hndlevent']);
            if nargin < 4
                eventList = 1:numel(hndlevent);
            end
            for ii = eventList
                currFun = hndlevent(ii).cmdset;
                if isempty(currFun)
                    continue
                elseif ischar(currFun)
                    eval(currFun);
                elseif iscell(currFun)
                    currFun{1}(varargin{:},currFun{2:end});
                else
                    currFun(varargin{:});
                end
            end
        end
    end

    function PVarray = validate(allargs, PVarray, parameterStrings)
        for ii = 1:numel(parameterStrings)
            parameter = parameterStrings{ii};
            loc = cellfind(allargs, parameter);
            if ~isempty(loc)
                eval(['PVarray.' parameter ' = allargs{loc(1)+1};']);
            end
        end
    end

    function posns = cellfind(cellarray, searchval)
        posns = [];
        if ischar(searchval)
            searchval = lower(searchval);
            for ii = 1:numel(cellarray)
                tmp = cellarray{ii};
                if ischar(tmp)
                    tmp = lower(tmp);
                end
                if isequal(searchval,tmp)
                    posns = [posns;ii];
                end
            end
        end
    end

end

Contact us