Code covered by the BSD License  

Highlights from
MorphTool

image thumbnail

MorphTool

by

 

14 Apr 2009 (Updated )

An interactive environment for morphologically operating on images.

Editor's Notes:

This file was selected as MATLAB Central Pick of the Week

MorphTool(img,parent)
function morphToolParentHandle = MorphTool(img,parent)
%Interactive GUI for morphological operations.
%
% SYNTAX:
% MorphTool(img)   Creates a new instance of MorphTool, using im
%
% See also: StrelTool, SegmentTool
%
% Copyright The MathWorks, 2012.

% Brett Shoelson, PhD
% Datestamp: 1/04/2011
%    MAJOR OVERHAUL; eliminates reliance on timer objects, object
%    listeners. (Listeners were problematic with non-OO code.) Result is a
%    much more stable, responsive application. (Fixes latency issues.) Also
%    adds support for a number of different morphological operations. Adds
%    auto-expand/contract button to grow/shrink the window. (This is
%    particularly useful when MorphTool is created as a child of a uipanel
%    in a SegmentTool-like environment.)
%
%    Minor: parent issues, issue with uppercase 'Cameraman.tif'; copyright
%    issue.
% Datestamp: 2/16/2012
%    Fixed some issues with writing of output string to
%    Command Line. Also fixed issues with deprecated
%    validation functions. Also hid expand/contract button
%    if MorphTool is a child of the root, rather than of a
%    uipanel.
% Datestamp: 3/21/2012
%    Fixed a bug in persistent auto-adjust after pressing
%    the "Adjust Intensity" button. (Persistent
%    clim=manual.)
% Datestamp: 8/8/2012
%    Replaced deprecated "iptchecknargin" with narginchk,
%    and provided image-loading capability when MorphTool is
%    a child of a figure. (Opens with default
%    'cameraman.tif' image if no input is provided.)
% Datestamp: 9/10/2012
%    Suppressed expand/contract button--it creates more problems than it
%    solves. Default now shows no morphological operation. (This is
%    preferable for large images.) Also, fixed a problem with normalized
%    position of axes, and repositioned a few items. Included Scott
%    Hirsch's UIGETVARIABLES to manage loading of workspace variables.


narginchk(0,2);
singleton = false;
if singleton && ~isempty(findall(0,'name','MorphTool'))
    delete(findall(0,'name','MorphTool')); %Singleton
end

if nargin == 0
    imgName = 'cameraman.tif';
    img = imread(imgName);
end

morphToolParent = [];
parentName = 'MorphTool';
if nargin > 1 % PARENT specified
    if ~ishandle(parent)
        error('MORPHTOOL: Second argument must be a single handle for the parent of the segtool.');
    elseif ~ismember(get(parent,'type'),{'figure','uipanel'})
        error('MORPHTOOL: Invalid parent specified. (Must be existing figure or uipanel.)');
    end
    morphToolParent = parent;
    parentName = get(ancestor(morphToolParent,'figure','toplevel'),'name');
end

% INITIALIZATIONS:
validateattributes(img, {'numeric','char','logical'}, {}, mfilename, 'img', 1);

highlightColor = [0.85 0.9 0.9];

if ischar(img)
    imgName = img;
    img = imread(img);
else
    imgName = 'Original Image';
end
try
    bgcolor = get(morphToolParent,'backgroundcolor');
catch
    bgcolor = [0.553 0.659 0.678];
end
if isempty(bgcolor)
    bgcolor = [0.553 0.659 0.678];
end
if isempty(morphToolParent)
    morphToolParent = figure('numbertitle','off',...
        'Units','normalized',...
        'Position',[0.05 0.087 0.9 0.829 ],...
        'Tag','morphToolParent',...
        'Name','MorphTool',...
        'NumberTitle','off',...
        'Visible','on',...
        'Color', bgcolor,...
        'MenuBar','none',...
        'ToolBar','auto',...
        'Resize','on',...
        'WindowStyle','normal');
end
morphToolParentHandle = morphToolParent;
%
% bgc = [0.55 0.65 0.65];
% tbc = 240/255; %toolbar color, approximately
if  strcmp(get(morphToolParent,'type'),'figure')
    ht                         = uitoolbar(morphToolParent);
    tmp                        = im2double(imread('file_open.png'));
    tmp(tmp==0)                = NaN;
    loadImageTool              = uitoggletool(ht,...
        'CData',               tmp,...
        'oncallback',          @GetNewFile,...
        'offcallback',         '',...
        'Tooltipstring',       'Load new image',...
        'Tag',                 'loadImageTool');
end


% (For STRELTOOL)
uipanel1 = uipanel('parent',morphToolParent,...
    'Units','normalized',...
    'Position',[0.020 0.422 0.43 0.553 ],...
    'Tag','uipanel1',...
    'TitlePosition','lefttop',...
    'BorderType','etchedin',...
    'BorderWidth',1.000,...
    'BackgroundColor',bgcolor,...
    'ForegroundColor',[0.000 0.000 0.000 ],...
    'FontName','MS Sans Serif',...
    'FontSize',8.000,...
    'HighlightColor',[1.000 1.000 1.000 ],...
    'ShadowColor',[0.500 0.500 0.500 ]);
% if ~strcmp(get(morphToolParent,'type'),'figure')
%     uicontrol('parent',morphToolParent,...
%         'Units','normalized',...
%         'Position',[0 0.3875 0.025 0.035 ],...
%         'style','pushbutton',...
%         'fontweight','bold',...
%         'fontsize',14,...
%         'string','<',...
%         'tag','expandContractButton',...
%         'tooltipstr','Expand/Contract window',...
%         'callback',@expandContract);
% end
%
% (For Morphological Operators)
uipanel2 = uibuttongroup('parent',morphToolParent,...
    'Units','normalized',...
    'Position',[0.020 0.16 0.43 0.239 ],...
    'Tag','uipanel2',...
    'Title','REQUESTED MORPHOLOGICAL OPERATION',...
    'TitlePosition','lefttop',...
    'BorderType','etchedin',...
    'BorderWidth',1.000,...
    'BackgroundColor',bgcolor,...
    'ForegroundColor',[0.000 0.000 0.000 ],...
    'FontName','MS Sans Serif',...
    'FontSize',8.000,...
    'HighlightColor',[1.000 1.000 1.000 ],...
    'ShadowColor',[0.500 0.500 0.500 ]);
%
[objpos,objdim] = distributeObjects(3,0.02, 0.45, 0.01);
uicontrol('parent',morphToolParent,...
    'style','pushbutton',...
    'Units','normalized',...
    'Tag','ActivateAlteredImage',...
    'string','Activate Altered Image',...
    'Position',[objpos(1) 0.11 objdim 0.035 ],...
    'callback',@updateImage);

uicontrol('parent',morphToolParent,...
    'style','pushbutton',...
    'Units','normalized',...
    'string','Adjust Intensity',...
    'tooltipstring','Adjust dynamic range of image: IMSHOW(*,[]); for visualization only.',...
    'Position',[objpos(2) 0.11 objdim 0.035 ],...
    'callback',@imadjustIt);

autoadjust = uicontrol('parent',morphToolParent,...
    'style','checkbox',...
    'Units','normalized',...
    'string','Auto-Adjust Intensity',...
    'tooltipstring','Automatically adjust dynamic range of image: IMSHOW(*,[]); for visualization only.',...
    'BackgroundColor',bgcolor,...
    'Position',[objpos(1) 0 objdim 0.035 ],...
    'value',0);

if ~strcmp(parentName,'MorphTool')
    tmp = ['EXPORT to ', parentName];
else
    tmp = 'EXPORT';
end
uicontrol('parent',morphToolParent,...
    'style','pushbutton',...
    'Units','normalized',...
    'string',tmp,...
    'tag','exportImageButton',...
    'Position',[objpos(3) 0.11 objdim 0.035 ],...
    'foregroundcolor','r',...
    'callback',{@exportImage,morphToolParent});

instructBox = uicontrol('parent',morphToolParent,...
    'style','listbox',...
    'units','normalized',...
    'position',[0.02 0.03 0.43 0.075],...
    'BackgroundColor',highlightColor,...
    'ForegroundColor',[0.000 0.000 0.000 ],...
    'FontName','MS Sans Serif',...
    'FontSize',8.000,...
    'Max',3,...
    'Min',1,...
    'horizontalalignment','left',...
    'tag','instructBox',...
    'string','');

tabLabels = {'StrelTool','StrelTool2','Non-Strel Parameters'};
[mainTabHandle,tabCardHandles,tabHandles] = ...
    tabPanel(uipanel1,tabLabels,...
    'tabpos','t','tabheight',60,'colors',[min(1,bgcolor*1.2);bgcolor*0.9;bgcolor*0.6],...
    'TabCardPVs',{'bordertype','etchedin','title',''},...
    'TabLabelPVs',{'fontsize',11,'fontweight','n'}); %#ok<NASGU,ASGLU>

% Populate STRELTOOL1 TAB
strel1Parent = tabCardHandles{1}(1);
[~,strelButtons,strelSliders] = StrelTool(strel1Parent);
tmp = fieldnames(getappdata(strel1Parent));
SE = getappdata(strel1Parent,tmp{1});
%setappdata(morphToolParent,'oldSE',SE);

% Populate STRELTOOL2 TAB
strel2Parent = tabCardHandles{1}(2);
[~,strelButtons2,strelSliders2] = StrelTool(strel2Parent,'SE2');
SE2 = getappdata(strel2Parent,'SE2');

% Populate MISCELLANEOUS CONTROLS TAB
miscUicontrolParent = tabCardHandles{1}(3);
bgc = get(miscUicontrolParent,'backgroundcolor');
highlightColor = [0.8 0.8 0.8];
sliderPanel(miscUicontrolParent, ...
    {'title','Minimum Blob Size','pos',[0.05 0.8 0.4 0.15],...
    'backgroundcolor', highlightColor,...
    'fontsize',8,'foregroundcolor','w'},...
    {'backgroundcolor', bgc,...
    'min',0,'max',1000,'value',0,'tag','minBlobSize','callback',@morphOp},...
    {'backgroundcolor',bgc,'fontsize',8},...
    {'backgroundcolor', highlightColor,'fontsize',8,'foregroundcolor','w'},...
    '%0.0f');
uicontrol('parent',miscUicontrolParent,...
    'style','text',...
    'units','normalized',...
    'position',[0.05 0.7 0.4 0.05],...
    'backgroundcolor', bgc,...
    'foregroundcolor','w',...
    'horizontalalignment','l',...
    'string','Connectivity');
uicontrol('parent',miscUicontrolParent,...
    'style','listbox',...
    'units','normalized',...
    'backgroundcolor', bgc,...
    'foregroundcolor','w',...
    'string',{4,8,6,18,26},...
    'value',1,...
    'position',[0.05 0.5225 0.175 0.175],...
    'tag','connectivityValue',...
    'callback',@morphOp);
uicontrol('parent',miscUicontrolParent,...
    'style','text',...
    'units','normalized',...
    'position',[0.275 0.7 0.4 0.05],...
    'backgroundcolor', bgc,...
    'foregroundcolor','w',...
    'horizontalalignment','l',...
    'string','Distance Metric');
uicontrol('parent',miscUicontrolParent,...
    'style','listbox',...
    'units','normalized',...
    'backgroundcolor', bgc,...
    'foregroundcolor','w',...
    'string',{'euclidean', 'cityblock', 'chessboard', 'quasi-euclidean'},...
    'value',1,...
    'position',[0.275 0.5225 0.175 0.175],...
    'tag','distanceMetric',...
    'callback',@morphOp);
uicontrol('parent',miscUicontrolParent,...
    'style','text',...
    'units','normalized',...
    'position',[0.55 0.9 0.4 0.05],...
    'backgroundcolor', bgc,...
    'foregroundcolor','w',...
    'horizontalalignment','l',...
    'string','BWMorph Operation');
matchStrings = {'bothat', 'branchpoints', 'bridge', 'clean', 'close',...
    'diag', 'dilate', 'endpoints', 'erode', 'fatten', 'fill', 'hbreak',...
    'majority', 'perim4', 'perim8', 'open', 'remove', 'shrink', 'skeleton',...
    'spur', 'thicken', 'thin', 'tophat'};
uicontrol('parent',miscUicontrolParent,...
    'style','listbox',...
    'units','normalized',...
    'backgroundcolor', bgc,...
    'foregroundcolor','w',...
    'string',matchStrings,...
    'value',1,...
    'position',[0.55 0.5225 0.175 0.375],...
    'tag','requestedBWMorphOperation',...
    'callback',@morphOp);
sliderPanel(miscUicontrolParent, ...
    {'title','BWMorph Times','pos',[0.775 0.7775 0.175 0.15],...
    'backgroundcolor', bgc,...
    'fontsize',8,'foregroundcolor','w'},...
    {'backgroundcolor', highlightColor,...
    'min',0,'max',100,'value',1,'tag','bwmorphTimes','callback',@morphOp},...
    {'backgroundcolor',highlightColor,'fontsize',8},...
    {'backgroundcolor', bgc,'fontsize',8,'foregroundcolor','w'},...
    '%0.0f');
uicontrol('parent',miscUicontrolParent,...
    'style','checkbox',...
    'units','normalized',...
    'backgroundcolor', bgc,...
    'foregroundcolor','w',...
    'string','Infinite',...
    'value',0,...
    'position',[0.775 0.695 0.175 0.075],...
    'tag','bwmorphInf',...
    'callback',@morphOp);
%
[objpos,objdim] = distributeObjects(2,0.05, 0.95, 0.075);

axes2 = axes('parent',morphToolParent,...
    'Units','normalized',...
    'Position',[0.475 objpos(1) 0.505 objdim ],...
    'Tag','MorphToolAxes2',...
    'LineWidth',0.500,...
    'FontName','Helvetica',...
    'FontSize',10.000,...
    'FontUnits','points',...
    'FontWeight','normal',...
    'FontAngle','normal',...
    'Color',[1.000 1.000 1.000 ],...
    'DataAspectRatio',[1.000 1.000 1.000]);
%
axes1 = axes('parent',morphToolParent,...
    'Units','normalized',...
    'Position',[0.475 objpos(2) 0.505 objdim ],...
    'Tag','MorphToolAxes1',...
    'LineWidth',0.500,...
    'FontName','Helvetica',...
    'FontSize',10.000,...
    'FontUnits','points',...
    'FontWeight','normal',...
    'FontAngle','normal',...
    'Color',[1.000 1.000 1.000 ],...
    'DataAspectRatio',[1.000 1.000 1.000]);
%
[objpos,objdim] = distributeObjects(7,0.95, 0.05, 0.025);
[hobjpos,hobjdim] = distributeObjects(3,0.025, 0.975, 0.01);


% SUPPORTED MORPHOLOGICAL OPERATIONS:
%   1     IMDILATE
%   2     IMERODE
%   3     IMOPEN
%   4     IMCLOSE
%   5     IMTOPHAT
%   6     IMBOTHAT
%   7     GRADIENT (Dilation - Erosion)
%         Ref: DIPUM 2e, p.524
%   8     ENTROPY
%   9     CONTRAST Enhancement (Image + Tophat - Bothat)
%         Ref: DIPUM 2e, p. 529
%   10    BWHITMISS
%   11    BWAREAOPEN
%   12    BWDIST
%   13    BWULTERODE
%   14    IMRECONSTRUCT

SelectMorphType(1) = uicontrol('parent',uipanel2,...
    'Style','radiobutton',...
    'Units','Normalized',...
    'Position',[hobjpos(1)    objpos(1)    hobjdim    objdim],...
    'String','IMDILATE',...
    'Tag','radiobutton1',...
    'Value',1.000,...
    'Min',0.000,...
    'Max',1.000,...
    'BackgroundColor',bgcolor,...
    'ForegroundColor',[0.000 0.000 0.000 ],...
    'ListboxTop',1.000,...
    'TooltipString','IM2 = imdilate(IM,SE)',...
    'SliderStep',[0.010 0.100 ],...
    'FontName','MS Sans Serif',...
    'HorizontalAlignment','center',...
    'FontSize',7,...
    'Enable','on');
%
SelectMorphType(2) = uicontrol('parent',uipanel2,...
    'Style','radiobutton',...
    'Units','Normalized',...
    'Position',[hobjpos(1)    objpos(2)    hobjdim    objdim],...
    'String','IMERODE',...
    'Tag','radiobutton2',...
    'Value',0.000,...
    'Min',0.000,...
    'Max',1.000,...
    'BackgroundColor',bgcolor,...
    'ForegroundColor',[0.000 0.000 0.000 ],...
    'ListboxTop',1.000,...
    'TooltipString','IM2 = imerode(IM,SE)',...
    'SliderStep',[0.010 0.100 ],...
    'FontName','MS Sans Serif',...
    'HorizontalAlignment','center',...
    'FontSize',7,...
    'Enable','on');
%
SelectMorphType(3) = uicontrol('parent',uipanel2,...
    'Style','radiobutton',...
    'Units','Normalized',...
    'Position',[hobjpos(1)    objpos(3)    hobjdim    objdim],...
    'String','IMOPEN',...
    'Tag','radiobutton3',...
    'Value',0.000,...
    'Min',0.000,...
    'Max',1.000,...
    'BackgroundColor',bgcolor,...
    'ForegroundColor',[0.000 0.000 0.000 ],...
    'ListboxTop',1.000,...
    'TooltipString','IM2 = imopen(IM,SE)',...
    'SliderStep',[0.010 0.100 ],...
    'FontName','MS Sans Serif',...
    'HorizontalAlignment','center',...
    'FontSize',7,...
    'Enable','on');
%
SelectMorphType(4) = uicontrol('parent',uipanel2,...
    'Style','radiobutton',...
    'Units','Normalized',...
    'Position',[hobjpos(1)    objpos(4)    hobjdim    objdim],...
    'String','IMCLOSE',...
    'Tag','radiobutton4',...
    'Value',0.000,...
    'Min',0.000,...
    'Max',1.000,...
    'BackgroundColor',bgcolor,...
    'ForegroundColor',[0.000 0.000 0.000 ],...
    'ListboxTop',1.000,...
    'TooltipString','IM2 = imclose(IM,SE)',...
    'SliderStep',[0.010 0.100 ],...
    'FontName','MS Sans Serif',...
    'HorizontalAlignment','center',...
    'FontSize',7,...
    'Enable','on');
%
SelectMorphType(5) = uicontrol('parent',uipanel2,...
    'Style','radiobutton',...
    'Units','Normalized',...
    'Position',[hobjpos(1)    objpos(5)    hobjdim    objdim],...
    'String','IMTOPHAT',...
    'Tag','radiobutton5',...
    'Value',0.000,...
    'Min',0.000,...
    'Max',1.000,...
    'BackgroundColor',bgcolor,...
    'ForegroundColor',[0.000 0.000 0.000 ],...
    'ListboxTop',1.000,...
    'TooltipString','IM2 = imtophat(IM,SE)',...
    'SliderStep',[0.010 0.100 ],...
    'FontName','MS Sans Serif',...
    'HorizontalAlignment','center',...
    'FontSize',7,...
    'Enable','on');
%
SelectMorphType(6) = uicontrol('parent',uipanel2,...
    'Style','radiobutton',...
    'Units','Normalized',...
    'Position',[hobjpos(1)    objpos(6)    hobjdim    objdim ],...%23.400 7.044 17.400 1.769
    'String','IMBOTHAT',...
    'Tag','radiobutton6',...
    'Value',0.000,...
    'Min',0.000,...
    'Max',1.000,...
    'BackgroundColor',bgcolor,...
    'ForegroundColor',[0.000 0.000 0.000 ],...
    'ListboxTop',1.000,...
    'TooltipString','IM2 = imbothat(IM,SE)',...
    'SliderStep',[0.010 0.100 ],...
    'FontName','MS Sans Serif',...
    'HorizontalAlignment','center',...
    'FontSize',7,...
    'Enable','on');
%
SelectMorphType(7) = uicontrol('parent',uipanel2,...
    'Style','radiobutton',...
    'Units','Normalized',...
    'Position',[hobjpos(1)    objpos(7)    hobjdim    objdim],...
    'String','GRADIENT Image',...
    'Tag','radiobutton7',...
    'Value',0.000,...
    'Min',0.000,...
    'Max',1.000,...
    'BackgroundColor',bgcolor,...
    'ForegroundColor',[0.000 0.000 0.000 ],...
    'ListboxTop',1.000,...
    'TooltipString','Imdilate - Imerode.',...
    'SliderStep',[0.010 0.100 ],...
    'FontName','MS Sans Serif',...
    'HorizontalAlignment','center',...
    'FontSize',7,...
    'Enable','on');
%
SelectMorphType(8) = uicontrol('parent',uipanel2,...
    'Style','radiobutton',...
    'Units','Normalized',...
    'Position',[hobjpos(2)    objpos(1)    hobjdim    objdim],...%23.400 4.352 17.400 1.769
    'String','ENTROPY FILTER',...
    'Tag','radiobutton8',...
    'Value',0.000,...
    'Min',0.000,...
    'Max',1.000,...
    'BackgroundColor',bgcolor,...
    'ForegroundColor',[0.000 0.000 0.000 ],...
    'ListboxTop',1.000,...
    'TooltipString','J = entropyfilt(I,NHOOD); [NOTE: NHOOD is defined automatically by GETNHOOD(SE).]',...
    'SliderStep',[0.010 0.100 ],...
    'FontName','MS Sans Serif',...
    'HorizontalAlignment','center',...
    'FontSize',7,...
    'Enable','on');
%
SelectMorphType(9) = uicontrol('parent',uipanel2,...
    'Style','radiobutton',...
    'Units','Normalized',...
    'Position',[hobjpos(2)    objpos(2)    hobjdim    objdim],...
    'String','CONTRAST Enhancement',...
    'Tag','radiobutton9',...
    'Value',0.000,...
    'Min',0.000,...
    'Max',1.000,...
    'BackgroundColor',bgcolor,...
    'ForegroundColor',[0.000 0.000 0.000 ],...
    'ListboxTop',1.000,...
    'TooltipString','Image + Tophat - Bothat',...
    'SliderStep',[0.010 0.100 ],...
    'FontName','MS Sans Serif',...
    'HorizontalAlignment','center',...
    'FontSize',7,...
    'Enable','on');
%
SelectMorphType(10) = uicontrol('parent',uipanel2,...
    'Style','radiobutton',...
    'Units','Normalized',...
    'Position',[hobjpos(2)    objpos(3)    hobjdim    objdim],...
    'String','BWHITMISS',...
    'Tag','radiobutton10',...
    'Value',0.000,...
    'Min',0.000,...
    'Max',1.000,...
    'BackgroundColor',bgcolor,...
    'ForegroundColor',[0.000 0.000 0.000 ],...
    'ListboxTop',1.000,...
    'TooltipString','BW2 = bwhitmiss(BW1,SE1,SE2); (Preserves: Hit SE1 AND Miss SE2)',...
    'SliderStep',[0.010 0.100 ],...
    'FontName','MS Sans Serif',...
    'HorizontalAlignment','center',...
    'FontSize',7,...
    'Enable','on');
%
SelectMorphType(11) = uicontrol('parent',uipanel2,...
    'Style','radiobutton',...
    'Units','Normalized',...
    'Position',[hobjpos(2)    objpos(4)    hobjdim    objdim],...
    'String','BWAREAOPEN',...
    'Tag','radiobutton11',...
    'Value',0.000,...
    'Min',0.000,...
    'Max',1.000,...
    'BackgroundColor',bgcolor,...
    'ForegroundColor',[0.000 0.000 0.000 ],...
    'ListboxTop',1.000,...
    'TooltipString','BW2 = bwareaopen(BW, minBlobSize, Connectivity)',...
    'SliderStep',[0.010 0.100 ],...
    'FontName','MS Sans Serif',...
    'HorizontalAlignment','center',...
    'FontSize',7,...
    'Enable','on');
%
SelectMorphType(12) = uicontrol('parent',uipanel2,...
    'Style','radiobutton',...
    'Units','Normalized',...
    'Position',[hobjpos(2)    objpos(5)    hobjdim    objdim],...
    'String','BWDIST',...
    'Tag','radiobutton12',...
    'Value',0.000,...
    'Min',0.000,...
    'Max',1.000,...
    'BackgroundColor',bgcolor,...
    'ForegroundColor',[0.000 0.000 0.000 ],...
    'ListboxTop',1.000,...
    'TooltipString','BW2 = bwdist(BW, distanceMetric)',...
    'SliderStep',[0.010 0.100 ],...
    'FontName','MS Sans Serif',...
    'HorizontalAlignment','center',...
    'FontSize',7,...
    'Enable','on');
%
SelectMorphType(13) = uicontrol('parent',uipanel2,...
    'Style','radiobutton',...
    'Units','Normalized',...
    'Position',[hobjpos(2)    objpos(6)    hobjdim    objdim],...
    'String','BWULTERODE',...
    'Tag','radiobutton13',...
    'Value',0.000,...
    'Min',0.000,...
    'Max',1.000,...
    'BackgroundColor',bgcolor,...
    'ForegroundColor',[0.000 0.000 0.000 ],...
    'ListboxTop',1.000,...
    'TooltipString','BW2 = bwulterode(BW, distanceMetric, Connectivity)',...
    'SliderStep',[0.010 0.100 ],...
    'FontName','MS Sans Serif',...
    'HorizontalAlignment','center',...
    'FontSize',7,...
    'Enable','on');
%
SelectMorphType(14) = uicontrol('parent',uipanel2,...
    'Style','radiobutton',...
    'Units','Normalized',...
    'Position',[hobjpos(2)    objpos(7)    hobjdim    objdim],...
    'String','BWMORPH',...
    'Tag','radiobutton14',...
    'Value',0.000,...
    'Min',0.000,...
    'Max',1.000,...
    'BackgroundColor',bgcolor,...
    'ForegroundColor',[0.000 0.000 0.000 ],...
    'ListboxTop',1.000,...
    'TooltipString','BW2 = bwmorph(BW,OPERATION,N)',...
    'SliderStep',[0.010 0.100 ],...
    'FontName','MS Sans Serif',...
    'HorizontalAlignment','center',...
    'FontSize',7,...
    'Enable','on');
%
SelectMorphType(15) = uicontrol('parent',uipanel2,...
    'Style','radiobutton',...
    'Units','Normalized',...
    'Position',[hobjpos(3)    objpos(1)    hobjdim    objdim],...
    'String','IMRECONSTRUCT',...
    'Tag','radiobutton14',...
    'Value',0.000,...
    'Min',0.000,...
    'Max',1.000,...
    'BackgroundColor',bgcolor,...
    'ForegroundColor',[0.000 0.000 0.000 ],...
    'ListboxTop',1.000,...
    'TooltipString','IM = imreconstruct(marker,mask,conn)',...
    'SliderStep',[0.010 0.100 ],...
    'FontName','MS Sans Serif',...
    'HorizontalAlignment','center',...
    'FontSize',7,...
    'Enable','on');


% Initial Display
%altered = imdilate(img,(strel('disk',6)));
altered = img;
%throwComment(get(SelectMorphType(1),'tooltipstring'),0,0);
originalImg = imshow(img,'parent',axes1);
set(originalImg,'tag','MorphToolImg');
title(imgName);
if exist('expandAxes','file')==2
    expandAxes(axes1);
end
morphAlteredImg = imshow(altered,'parent',axes2);
set(morphAlteredImg,'tag','MorphToolImg');
%set(morphAlteredImg,'cdata',altered);
if exist('expandAxes','file')==2
    expandAxes(axes2);
end

set(axes1,'visible','on','xtick',[],'ytick',[])
set(axes2,'visible','on','xtick',[],'ytick',[])
title(axes1,imgName)
title(axes2,'Morphologically Altered Image');
setappdata(morphToolParent,'currSelection',1);

% LISTENERS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% ADD LISTENERS FOR STREL_SHAPES
for ii = 1:length(strelButtons)
    %addlistener(strelButtons(ii),'Value','PostSet',@morphOp);
    iptaddcallback(strelButtons(ii),'Callback',@morphOp);
    iptaddcallback(strelButtons2(ii),'Callback',@morphOp);
end
% ADD LISTENERS FOR STREL_VALUES
for ii = 1:length(strelSliders)
    %addlistener(strelSliders(ii),'Value','PostSet',@morphOp);
    iptaddcallback(strelSliders(ii),'Callback',@morphOp);
    iptaddcallback(strelSliders2(ii),'Callback',@morphOp);
end
% LISTENERS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% CALLBACKS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
set(SelectMorphType,'callback',@morphOp,'Units','normalized')
% CALLBACKS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

set(morphToolParent,'visible','on');
if nargout < 1
    clear morphToolParentHandle
end

% START NESTED FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    function altered = morphOp(varargin)
        % This construct allows the user to be able to
        % change the name of the variable in STRELTOOL
        % without causing problems here
        try %#ok This is in a try structure bc when the parent is not a figure, it will otherwise error
            set(morphToolParent,'pointer','watch');
        end
        tmp = fieldnames(getappdata(strel1Parent));
        SE = getappdata(strel1Parent,tmp{1});
        ind = find(cell2mat(get(SelectMorphType,'value')));
        if isempty(ind)
            ind = getappdata(morphToolParent,'currSelection');
            set(SelectMorphType(ind),'value',1);
        else
            setappdata(morphToolParent,'currSelection',ind);
        end
        request = get(SelectMorphType(ind),'string');
        tt = get(SelectMorphType(ind),'Tooltipstring');
        % OTHER MORPHOLOGICAL OPERATORS of note...(there are more!):
        % imclearborder      Suppress light structures connected to image border
        % imextendedmax/min  Extended-maxima and -minima transforms
        % imimposemin        Impose minima
        % watershed          Find image watershed
        
        
        % Which tab panel is currently active?
        activePanel = find(strcmp(get(cell2mat(tabCardHandles(:)),'visible'),'on'));
        % Manage changing of tab panels first
        switch request
            case {'BWAREAOPEN','BWULTERODE','BWDIST','BWMORPH'} %Use panel 3
                tabPanel(tabCardHandles,tabHandles{1}(3));
                strings = get(findall(tabCardHandles{1}(3),'tag','distanceMetric'),'string');
                distMethod = get(findall(tabCardHandles{1}(3),'tag','distanceMetric'),'value');
                distMethod = strings{distMethod};
                strings = get(findall(tabCardHandles{1}(3),'tag','connectivityValue'),'string');
                connectivityValue = get(findall(tabCardHandles{1}(3),'tag','connectivityValue'),'value');
                connectivityValue = str2double(strings{connectivityValue});
            case {'BWHITMISS'} %Allow use of panel 2
                if activePanel == 3
                    tabPanel(tabCardHandles,tabHandles{1}(1))
                end
            otherwise
                tabPanel(tabCardHandles,tabHandles{1}(1))
        end
        
        %set(instructBox,'string','');
        throwComment([tt ' '],0,0)%get(gcbo,'tooltipstring')
        switch request
            case 'IMDILATE'
                altered = imdilate(img,SE);
            case 'IMERODE'
                altered = imerode(img,SE);
            case 'IMOPEN'
                altered = imopen(img,SE);
                throwComment('Erosion, followed by dilation.',0,1);
            case 'IMCLOSE'
                altered = imclose(img,SE);
                throwComment('Dilation, followed by erosion.',0,1);
            case 'IMTOPHAT'
                altered = imtophat(img,SE);
                throwComment('Original - Imopen.',0,1);
            case 'IMBOTHAT'
                altered = imbothat(img,SE);
                throwComment('Imclose - Original.',0,1);
            case 'GRADIENT Image'
                altered = imsubtract(imdilate(img,SE),imerode(img,SE));
                throwComment('Dilation, followed by erosion.',0,1);
            case 'ENTROPY FILTER'
                % Requires odd x odd neighborhood
                H = getnhood(SE);
                sizeH = size(H);
                if any(floor(sizeH/2) == (sizeH/2))
                    msg1 = 'This function expects NHOOD to have a size that ';
                    msg2 = 'is odd in each dimension.';
                    msg = sprintf('%s\n%s\n\n',msg1,msg2);
                    throwComment(msg,1,1);
                    set(morphToolParent,'pointer','arrow');
                    return
                end
%                 if ndims(img) > 2
%                     throwComment('Processing layer-by-layer!',2,1);
%                     altered = zeros(size(img));
%                     for jj = 1:ndims(img)
%                         altered(:,:,jj) = entropyfilt(img(:,:,jj),H);
%                     end
%                 else
%                     altered = entropyfilt(img,H);
%                 end
                altered = entropyfilt(img,H);
                if max(altered(:)) > 1
                    altered = altered/max(altered(:));
                    throwComment('Resulting image has been normalized by its maximum intensity.',2,1);
                end
            case 'CONTRAST Enhancement'
                if islogical(img)
                    throwComment('Contrast enhancement not appropriate for binary images!',2,1);
                    set(morphToolParent,'pointer','arrow');
                    return
                end
                altered = imsubtract(imadd(img,imtophat(img,SE)), imbothat(img,SE));
                throwComment('Dilation, followed by erosion.',0,1);
            case 'BWHITMISS'
                throwComment(sprintf('Define SE1 in the STRELTOOL Panel 1 above,\nand SE2 in STRELTOOL Panel 2.'),2,1);
                throwComment('Alternatively, define variable "MorphToolInterval" IN THE BASE WORKSPACE.',0,1);
                throwComment('NOTE: As long as variable "MorphToolInterval" exists in the Base Workspace, STRELS defined in StrelTool will be ignored!',0,1);
                throwComment('HINT: Use the Variable Editor to change MorphToolInterval, then re-engage the BWHITMISS radio button to change the interval interactively.',0,1);
                MorphToolInterval = [];
                try
                    MorphToolInterval = evalin('base','MorphToolInterval');
                end
                if ~isempty(MorphToolInterval)
                    altered = bwhitmiss(img,MorphToolInterval);
                    throwComment('BW2 = bwhitmiss(BW1,INTERVAL)',0,1);
                    disp('USING INTERVAL APPROACH');
                else
                    altered = bwhitmiss(img,SE,SE2);
                    throwComment('BW2 = bwhitmiss(BW1,SE1,SE2).',0,1);
                    disp('USING SE1:SE2 APPROACH');
                end
            case 'BWAREAOPEN'
                minBlobSize = round(get(findall(tabCardHandles{1}(3),'tag','minBlobSize'),'value'));
                altered = bwareaopen(img,minBlobSize,connectivityValue);
            case 'BWDIST'
                altered = bwdist(img,distMethod);
            case 'BWULTERODE'
                altered = bwulterode(img,distMethod,connectivityValue);
            case 'BWMORPH'
                if ndims(img) > 2
                    throwComment('BWMORPH not defined for ndims(img) > 2',1,0)
                    set(morphToolParent,'pointer','arrow');
                    return
                end
                strings = get(findall(tabCardHandles{1}(3),'tag','requestedBWMorphOperation'),'string');
                requestedBWMorphOperation = get(findall(tabCardHandles{1}(3),'tag','requestedBWMorphOperation'),'value');
                requestedBWMorphOperation = strings{requestedBWMorphOperation};
                bwmorphToInf = get(findall(tabCardHandles{1}(3),'tag','bwmorphInf'),'value');
                if bwmorphToInf
                    bwmorphTimes = Inf;
                else
                    bwmorphTimes = round(get(findall(tabCardHandles{1}(3),'tag','bwmorphTimes'),'value'));
                end
                altered = bwmorph(img,requestedBWMorphOperation,bwmorphTimes);
                if isinf(bwmorphTimes)
                    bwmorphTimes = 'INF';
                else
                    bwmorphTimes = num2str(bwmorphTimes);
                end
                throwComment(sprintf('BW2 = bwmorph(BW2, %s, %s).',upper(requestedBWMorphOperation),bwmorphTimes))
            case 'IMRECONSTRUCT'
                throwComment('NOT YET IMPLEMENTED!',1,1);
                altered = img;
            otherwise
                throwComment('MORPHTOOL: Unrecognized trigger string!',1,1);
        end
        set(morphAlteredImg,'cdata',altered);
        set(axes2,'XLim',0.5+[0 size(altered,2)],'YLim',0.5+[0 size(altered,1)]);
        if get(autoadjust,'value')
            imadjustIt;
        else
            set(axes2,'CLimMode','auto')
        end
        try %#ok This is in a try structure bc when the parent is not a figure, it will otherwise error
            set(morphToolParent,'pointer','arrow');
        end
    end

    function imadjustIt(varargin)
        %         if ~islogical(altered)
        %             set(morphAlteredImg,'cdata',imadjust(altered));
        %         end
        mini = min(altered(:));
        maxi = max(altered(:));
        if mini ~= maxi
            set(axes2,'CLim',[mini maxi])
        end
    end

    function updateImage(varargin)
        %exportImage;
        %img = altered;
        img = get(imhandles(axes2),'cdata');
        %axes(axes1);
        set(originalImg,'cdata',img);
        title(axes1,'Original Image')
        %originalImg = imshow(img);
    end

    function [img,imgoutName] = exportImage(varargin)
        img = get(morphAlteredImg,'cdata');
        n = 0; tmp3 = 1;
        while tmp3
            n = n + 1;
            tmp3 = evalin('base',['exist(''morphed', num2str(n), ''')']);
        end
        imgoutName = ['morphed' num2str(n)];
        if ~strcmp(parentName,'MorphTool')
            target = ancestor(varargin{3},'figure','toplevel');
            target = imhandles(target);
            target = target(1);
            set(target,'cdata',img);
        end
        assignin('base',imgoutName,img);
        try %#ok
            assignin('caller',imgoutName,img);
        end
        setappdata(morphToolParent,'imgoutName',imgoutName);
        msg = sprintf('Altered image written to morphed%d',n);
        fprintf('%s\n',msg)
        throwComment(msg,2,true);
        writeOperation
    end

    function writeOperation(varargin)
        % operation = {'imdilate','imerode','imopen','imclose','imtophat','imbothat','gradient','entropyfilt'};
        operation = get(SelectMorphType,'string');
        ind = cell2mat(get(SelectMorphType,'value'));
        operation = lower(operation{logical(ind)});
        strelVal = getappdata(strel1Parent,'strelVal')%#ok ...this is intentional
        imgoutName = getappdata(morphToolParent,'imgoutName');
        msg = 'This operation has not yet been defined.';
        switch operation
            case {'imdilate','imerode','imopen','imclose','imtophat','imbothat'}
                msg = sprintf('%s = %s(IMG, strel(''%s'',%g));\n\n',imgoutName,operation,strelVal.type,strelVal.opt1);
            case 'gradient image'
                msg = sprintf('SE = strel(''%s'',%g);\n%s = imsubtract(imdilate(IMG, SE),imerode(IMG, SE));\n\n',strelVal.type,strelVal.opt1,imgoutName);
            case 'entropy filter'
                msg = sprintf('%s = entropyfilt(IMG,getnhood(strel(''%s'',%g)));\n\n',imgoutName,strelVal.type,strelVal.opt1);
            case 'contrast enhancement'
                msg = sprintf('SE = strel(''%s'',%g);\n%s = imsubtract(imadd(IMG,imtophat(IMG,SE)),imbothat(IMG,SE));\n\n',strelVal.type,strelVal.opt1,imgoutName);
            case 'bwhitmiss'
                MorphToolInterval = [];
                try
                    MorphToolInterval = evalin('base','MorphToolInterval');
                end
                if ~isempty(MorphToolInterval)
                    msg = sprintf('%s = bwhitmiss(IMG, INTERVAL);\n(INTERVAL is as defined in base workspace.)\n\n',imgoutName);
                else
                    strelVal2 = getappdata(strel2Parent,'strelVal')%#ok ...this is intentional
                    msg = sprintf('%s = bwhitmiss(IMG,strel(''%s'',%g),strel(''%s'',%g));\n\n',imgoutName,strelVal.type,strelVal.opt1,strelVal2.type,strelVal2.opt1);
                end
            case 'bwareaopen'
                strings = get(findall(tabCardHandles{1}(3),'tag','connectivityValue'),'string');
                connectivityValue = get(findall(tabCardHandles{1}(3),'tag','connectivityValue'),'value');
                connectivityValue = str2double(strings{connectivityValue});
                minBlobSize = round(get(findall(tabCardHandles{1}(3),'tag','minBlobSize'),'value'));
                msg = sprintf('%s = bwareaopen(IMG,%g,%g);\n\n',imgoutName,minBlobSize,connectivityValue);
            case 'bwdist'
                strings = get(findall(tabCardHandles{1}(3),'tag','distanceMetric'),'string');
                distMethod = get(findall(tabCardHandles{1}(3),'tag','distanceMetric'),'value');
                distMethod = strings{distMethod};
                msg = sprintf('%s = bwdist(IMG, ''%s'');\n\n',imgoutName,distMethod);
            case 'bwulterode'
                strings = get(findall(tabCardHandles{1}(3),'tag','distanceMetric'),'string');
                distMethod = get(findall(tabCardHandles{1}(3),'tag','distanceMetric'),'value');
                distMethod = strings{distMethod};
                strings = get(findall(tabCardHandles{1}(3),'tag','connectivityValue'),'string');
                connectivityValue = get(findall(tabCardHandles{1}(3),'tag','connectivityValue'),'value');
                connectivityValue = str2double(strings{connectivityValue});
                msg = sprintf('%s = bwulterode(IMG, ''%s'', %g);\n\n',imgoutName,distMethod,connectivityValue);
            case 'bwmorph'
                strings = get(findall(tabCardHandles{1}(3),'tag','distanceMetric'),'string');
                distMethod = get(findall(tabCardHandles{1}(3),'tag','distanceMetric'),'value');
                distMethod = strings{distMethod};
                strings = get(findall(tabCardHandles{1}(3),'tag','connectivityValue'),'string');
                connectivityValue = get(findall(tabCardHandles{1}(3),'tag','connectivityValue'),'value');
                connectivityValue = str2double(strings{connectivityValue});
                strings = get(findall(tabCardHandles{1}(3),'tag','requestedBWMorphOperation'),'string');
                requestedBWMorphOperation = get(findall(tabCardHandles{1}(3),'tag','requestedBWMorphOperation'),'value');
                requestedBWMorphOperation = strings{requestedBWMorphOperation};
                bwmorphToInf = get(findall(tabCardHandles{1}(3),'tag','bwmorphInf'),'value');
                if bwmorphToInf
                    bwmorphTimes = Inf;
                else
                    bwmorphTimes = round(get(findall(tabCardHandles{1}(3),'tag','bwmorphTimes'),'value'));
                end
                msg = sprintf('%s = bwmorph(IMG, ''%s'', %g);\n\n',imgoutName,requestedBWMorphOperation,bwmorphTimes);
        end
        fprintf('%s\n',msg)
    end

    function throwComment(commentString,beepOn,append)
        %instructBox = findobj(gcf,'tag','instructBox');
        if nargin < 2
            beepOn       = 0;
        end
        if nargin < 3
            append       = 0;
        end
        if append
            currString   = get(instructBox,'string');
            currString   = char(cellstr({currString;commentString}));
            if all(double(currString(1,:)== 32))
                currString = currString(2:end,:);
            end
            set(instructBox,'string',currString);
        else
            set(instructBox,'string',commentString);
        end
        tmp              = size(get(instructBox,'string'),1);
        set(instructBox,'listboxtop',tmp,'value',tmp);
        switch beepOn
            case 1
                beep;
            case 2
                [wav,freq] = wavread('notify.wav');
                sound(wav,freq);
            case 3
                [chimes,freq] = wavread('chimes.wav');
                sound(chimes,freq);
        end
        drawnow;
    end

    function GetNewFile(varargin)
        set(gcbo,'state','off');
        [img,cmap,fname,fpath,userCanceled] = getNewImage(false);
        if userCanceled
            return
        end
        cla(axes1);
        originalImg = imshow(img,'parent',axes1);
        expandAxes(axes1);
        title(fname,'interpreter','none');
        % Note: I can't figure out why the following line is
        % needed, but the axis is sometimes not updating
        % limits properly when I load a new image.
        % (XLIMMODE, etc. are somehow manual at this point. WHY???)
        %set(ImageAxis','XLimMode','manual','YLimMode','manual');
        set(axes1,'XLim',0.5+[0 size(img,2)],'YLim',0.5+[0 size(img,1)]);
        morphOp;
    end

end
% END NESTED FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% START SUBFUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function [objpos,objdim] = distributeObjects(nobjects,startpos,endpos,gap,warnoff)
%[objpos, objdim] = distributeObjects(nobjects, startpos, endpos, gap, warnoff)
%
%Returns the proper positions and size for uniformly spaced GUI objects.
%
%Enter the number of objects (nobjects), the starting position (startpos),
%the end position (endpos), and the gap, and this function will return a
%vector of starting points (objpos) as well as a dimension for all
%uniformly sized, equally spaced objects (buttons, boxes, etc.).
%
%This works for horizontal OR vertical distribution of items, as long as
%startpos < endpos, and for normalized or any other kind of unit .
%
% E.g., To uniformly distributeObjects 4 buttons horizontally starting at 10
% pixels, ending at 100 pixels, and with a gap of 5 pixels,
%     [objpos,objdim] = distributeObjects(4,10,100,5) returns
% objpos = [10.0000   33.7500   57.5000   81.2500],
% objdim = 18.7500
% Thus, your GUI buttons should be positioned at
% [objpos(1) y objdim height], [objpos(2) y objdim height],....
%
%Written by Brett Shoelson
%12/09/03
%shoelson@helix.nih.gov

if nargin<5
    warnoff = 0;
end

rev = 0;
if startpos > endpos
    rev = 1;
    tmp = endpos;
    endpos = startpos;
    startpos = tmp;
end

objdim = ((endpos-startpos)-(nobjects-1)*gap)/nobjects;
objpos = startpos:objdim+gap:endpos;
%Account for case of gap==0, which generates a starting point at the end of
%the object range.
objpos = objpos(1:nobjects);

if rev
    objpos = objpos(end:-1:1);
end
if ~warnoff && (any(objpos < 0) || objdim < 0)
    warndlg('The parameters you entered result in a negative starting point or dimension. You may want to rethink that.');
end

end %FUNCTION

% function expandContract(varargin)
% parent = ancestor(gcbo,'uipanel','toplevel');
% targetPos = [0 0 1 1];
% if isempty(parent)
%     parent = ancestor(gcbo,'figure','toplevel');
%     targetPos = [0.025 0.075 0.95 0.9];
% end
% uistack(parent,'top');
% expanded = getappdata(parent,'expandContract');
% if isempty(expanded)
%     expanded.value = false;
% end
% if ~expanded.value
%     expanded.originalPosition = get(parent,'position');
%     set(parent,'units','normalized','position',targetPos);
%     expanded.value = true;
%     expanded.originalUnits = get(parent,'units');
%     set(gcbo,'string','>')
% else
%     set(parent,'units',expanded.originalUnits,'position',expanded.originalPosition);
%     expanded.value = false;
%     set(gcbo,'string','<')
% end
% drawnow;
% setappdata(parent,'expandContract',expanded)
% end
% END SUBFUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%




Contact us