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 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%