function segtoolHandle = SegmentTool(varargin)
% SEGMENTTOOL: Interactive GUI for exploration of segmentation options
%
% Segmentation masks and recreation steps (i.e., "code") are exportable to
% base workspace and Command Window.
%
% SYNTAX
% 1) SEGMENTTOOL
% If SegmentTool GUI exists, brings it to the front; otherwise,
% creates a SegmentTool GUI with a default image. New images can be
% imported, from file or from workspace variables.
%
% 2) SEGMENTTOOL(IMG)
% Creates a SegmentTool GUI, pre-loaded with the image in IMG. (See
% NOTE on "singleton" below.)
%
% 3) SEGMENTTOOL(IMG,MAP)
% Also loads colormap MAP.
%
% 4) SEGMENTTOOL(IMG,MAP,PARENT)
% Creates SegmentTool as a child of PARENT. (DEFAULT behavior is a
% new figure.) Note that MAP may be empty.
%
% NOTES: Note that SegmentTool is "singleton" by design; you can't have
% multiple instances open simultaneously. You can change that
% behavior by changing "singleton" to false at line 60.
%
% All images are converted to type 'double' for processing.
%
% In many cases, tooltipstrings guide the user. In others,
% functionality is thought to be self-explanatory and
% self-discoverable. When in doubt about the usage of some function
% parameters, simply try them out or read the documentation for the
% function.
%
% In acknowledgement of the fact that tooltipstrings and
% notification sounds can get annoying, I have implemented menu
% items (under OPTIONS) to toggle them on/off. (They are both "on"
% by default.)
%
% CURRENT TAB PANELS:
% EDGE, THRESHOLDING, HOUGH LINE/CIRCLE, and
% REGIONAL/EXTENDED MIN/MAX.
%
% COMING SOON:
% MANUAL segmentation tabs.
%
% Thanks to Simone Haemmerle (my colleague in the German MathWorks
% office) for her suggestions.
%
% Comments, suggestions welcome!
%
% See also: StrelTool, MorphTool
% Written by Brett Shoelson, PhD
% brett.shoelson@mathworks.com
%
% UPDATES:
% 11/01/12: Included color segmentation tab/functionality.
%
% Copyright 2010-2012 The MathWorks, Inc.
narginchk(0,3);
tmp = findall(0,'name','SegmentTool');
singleton = true;
if ~nargin
if ~isempty(tmp)
figure(tmp)
return
end
else
if singleton
delete(findall(0,'name','SegmentTool')); %Singleton
end
end
% INITIALIZATIONS/DEFAULTS:
segtool = [];
requestedPanels = {{'Edge','Thresholding','Hough Line/Circle','Regional/Extended Min/Max'}...
{'Color-Based'}};
original.fname = 'Original';
if nargin > 0
% nargin > 0
validateattributes(varargin{1},{'numeric','char','logical'},{'nonempty'})
original.img = varargin{1};
% nargin > 1
if nargin > 1 % COLORMAP specified
original.cmap = varargin{2};
end
if ischar(original.img)
try
original.fname = original.img;
[original.img,original.cmap] = imread(original.img);
catch %#ok
error('SEGMENTTOOL: Unable to read specified image.');
end
end
% nargin > 2
if nargin > 2 % PARENT specified
iptcheckinput(varargin{3}, {'double'}, {''},...
mfilename, 'segtool', 3);
segtool = varargin{3};
if ~ishandle(segtool)
error('Third argument must be a single handle for the parent of the segtool.');
end
end
else
original.img = imread('peppers.png');
original.cmap = [];
original.fname = 'Original';
end
%Share in main workspace
[threshSldr,threshLine,threshval,hSldr,nConn,nConnOpts,...
extType,extDir,nConnValue,sensSldr,sensEdt,sigmaEdt,...
sigmaSldr,autoSens,autoSigma,edgeType,edgeDir,...
thetaRes,thetaMin,thetaMax,rhoRes,numPeaks,houghAx,houghPeakOpts,...
houghLinesOpts,notification,lastCommand,allTooltips,histax,...
nColorsSldr,nColorsEdt,colorMaskPanel,colorMaskAxHandles,nColors,...
titleHandles,X,xdivs,ydivs,divs,resizeval,manualMaskIm,manualMaskAx,...
sectionMarker,dx,dy,subImLimits,subimage,workingMask,...
overlayHndls,retrieveFromSegmentToolButton,exportToSegmentToolButton,...
pctBelowRedSldr,pctAboveRedSldr,pctBelowGreenSldr,pctAboveGreenSldr,...
pctBelowBlueSldr,pctAboveBlueSldr,manuallySampledColor,workingAxTitle] = deal([]);
[wav,freq] = audioread('notify.wav');
notification = audioplayer(wav,freq);
colors = bone(20);
colors = colors(8:end,:);
bgc = colors(4,:);
if isempty(segtool)
segtool = figure(...
'numbertitle', 'off',...
'WindowStyle','normal',...
'name', 'SegmentTool',...
'units', 'pixels',...
'color', bgc,...
'position', ceil(get(0,'screensize') .* [1 1 0.975 0.875]),...%ceil(get(0,'screensize')*0.82),...
'menubar', 'none','toolbar','none','visible','off');
end
segtoolHandle = segtool;
set(segtool,'visible','off');
if isrgb(original.img)
%DEFAULT grayversion
original.grayversion = rgb2gray(original.img);
else
original.grayversion = original.img;
end
% WORK WITH ALL IMAGES AS TYPE DOUBLE
if ~islogical(original.img)
original.img = im2double(original.img);
original.segmented = [];
else
original.segmented = original.img;
end
original.grayversion = im2double(original.grayversion);
% UIMENUS
parentFigure = ancestor(segtool,'figure');
f = uimenu(parentFigure,'Label','FILE');
uimenu(f,'Label','Import','callback',@getFile);
uimenu(f,'Label','Commit current working image','callback',{@convertImage,'CurrentWorkingImage'});
uimenu(f,'Label','Export Segmented','callback',@exportBW);
f = uimenu(parentFigure,'Label','CONVERSIONS');
tmp = uimenu(f,'Label','...to Grayscale');
uimenu(tmp,'Label','RGB2Gray','callback',{@convertImage,'RGB2gray'});
uimenu(tmp,'Label','Select Gray Image','callback',{@convertImage,'grayscale'});
uimenu(f,'Label','...to HSV','callback',{@convertImage,'HSV'});
uimenu(f,'Label','...to L*A*B*','callback',{@convertImage,'LAB'});
uimenu(f,'Label','...Decorrelation Stretch','callback',{@convertImage,'Decorrstretch'});
uimenu(f,'Label','Complement Image','callback',{@convertImage,'Complement'});
f = uimenu(parentFigure,'Label','MANAGE MASK');
uimenu(f,'Label','Reverse Mask','callback',@reverseMask);
uimenu(f,'Label','Export Mask','callback',@exportBW);
f = uimenu(parentFigure,'Label','OPTIONS');
uimenu(f,'Label','Verify Destructive Commands','checked','on',...
'tag','Verify','callback',@toggleMenuItem);
uimenu(f,'Label','Disable Tooltips','checked','off',...
'tag','DisableTooltips','callback',@toggleMenuItem);
uimenu(f,'Label','Turn sounds off','checked','off',...
'tag','TurnOffSounds','callback',@toggleMenuItem);
% CREATE MAIN FIGURE
if strcmp(get(segtool,'type'),'figure')
centerfig(segtool);
end
% Default units
tmp = get(0,'screensize');
if tmp(3) > 1200
defaultFontsize = 8;
else
defaultFontsize = 7;
end
set(segtool,'DefaultUicontrolUnits','normalized',...
'DefaultUicontrolFontSize',defaultFontsize);
annotation('textbox',[0.015 0.97 0.565 0.0225],...
'string','CLICK ON ANY IMAGE TO VIEW IT IN A LARGER WINDOW.',...
'textcolor', [0.043137 0.51765 0.78039]*0.8,...
'horizontalalignment','c','fontweight','b','fontsize',8,...
'backgroundcolor',bgc*1.3);
% CREATE MAIN IMAGE PANEL
% CREATE IMAGE AXES
origax = axes('parent',segtool,'pos', [0.1 0.1 0.1 0.1],'visible','off');%TEMPORARY...for setup
cla
imshow(original.img);
% overlayax = axes('parent',segtool,'pos',[0.24 0.02 0.2 0.25],'visible','off');
workingax = axes('parent',segtool,'pos', [0.1 0.1 0.1 0.1],'visible','off');%TEMPORARY...for setup
% OVERLAY PANEL
overlayColor = [1 0 0];
overlayPanel = uipanel('parent',segtool,'position',[0.26 0.02 0.32 0.25],...
'bordertype','etchedin','title','Segmentation-Visualization Tools',...
'backgroundcolor',colors(4,:));
overlayax = axes('parent',overlayPanel,'pos',[0.1 0.1 0.1 0.1],'visible','off');%TEMPORARY...for setup
overlayColorButton = uicontrol(overlayPanel,'style','pushbutton',...
'pos',[0.635 0.8 0.075 0.15],...
'cdata',reshape(kron(overlayColor,ones(15,15)),15,15,3),...
'callback',@changeOverlayColor,...
'tooltipstr','Change the color of the segmentation-mask overlay.');
setappdata(overlayColorButton,'overlayColor',overlayColor);
uicontrol(overlayPanel,'style','text','string',{'Overlay';'Color'},...
'position',[0.75 0.69 0.2 0.25],'backgroundcolor',bgc,...
'horizontalalignment','l',...
'tooltipstr','Change the color of the segmentation-mask overlay.');
uicontrol(overlayPanel,'style','pushbutton','pos',[0.635 0.675 0.35 0.1],...
'string','Change Order','callback',@flipImages,...
'tooltipstr','Reverse the stacking order of the image and the current overlay (for visualization of segmentation-mask alignment).');
[imgOpacitySldr,~,~] = ...
sliderPanel(overlayPanel,...
{'backgroundcolor',bgc,'title','Image Opacity','pos',[0.635 0.05 0.35 0.275],...
'units','normalized','fontsize',defaultFontsize},...
{'backgroundcolor',colors(5,:),'min',0,'max',1,'value',1,...
'callback',{@modifyOpacity,2},'sliderstep',[0.01 0.1],...
'tooltipstring',sprintf('Modify the transparency of the image.\nRight-Click bar to reset to default.')},...
{'backgroundcolor',colors(5,:),'fontsize',defaultFontsize},...
{'backgroundcolor',bgc,'fontsize',defaultFontsize},...
'%0.2f');
[overlayOpacitySldr,~,~] = ...
sliderPanel(overlayPanel,...
{'backgroundcolor',bgc,'title','Overlay Opacity','pos',[0.635 0.375 0.35 0.275],...
'units','normalized','fontsize',defaultFontsize},...
{'backgroundcolor',colors(5,:),'min',0,'max',1,'value',0.4,...
'callback',{@modifyOpacity,1},'sliderstep',[0.01 0.1],...
'tooltipstring',sprintf('Modify the transparency of the overlay.\nRight-Click bar to reset to default.')},...
{'backgroundcolor',colors(5,:),'fontsize',defaultFontsize},...
{'backgroundcolor',bgc,'fontsize',defaultFontsize},...
'%0.2f');
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% MAIN PANELS: SEGMENTATION
% Create Working TabPanels
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
del = 0.1;
[~,mainTabCardHandles,mainTabHandles] = ...
tabPanel(segtool,requestedPanels,...
'panelpos',[0.60 0.12+del-0.05 0.38 0.86-del+0.05],...
'tabpos','t',...
'colors',colors,...
'tabHeight',35,...
'highlightColor','w',...'c'
'tabCardPVs',{'bordertype','etchedin','fontsize',defaultFontsize},...
'tabLabelPVs',{'fontsize',defaultFontsize});
set(mainTabHandles{1}(1),'tooltipstring','Find edges in a grayscale image using any of the 6 algorithms in the Image Processing Toolbox.');
set(mainTabHandles{1}(2),'tooltipstring','Interactively apply global or blockwise threshold values.');
set(mainTabHandles{1}(3),'tooltipstring','Detect lines or circles in the working image.');
set(mainTabHandles{1}(4),'tooltipstring','Detect extended and regional minima and maxima.');
set(mainTabHandles{2}(1),'tooltipstring','Use color information to segment image.');
setappdata(segtool,'mainTabCardHandles',mainTabCardHandles);
setappdata(segtool,'mainTabHandles',mainTabHandles);
tmp = get(mainTabCardHandles{1}(1),'units');
set(mainTabCardHandles{1}(1),'units','characters');
stringWidth = get(mainTabCardHandles{1}(1),'pos');
stringWidth = floor(stringWidth(3)*0.7);
set(mainTabCardHandles{1}(1),'units',tmp);
[objpos,objdim] = distributeObjects(2,0.6,0.98,0.01);
%resetButton = ...
uicontrol(segtool,'style','pushbutton',...
'pos',[objpos(1) 0.125 objdim 0.0375],...
'string','Reset to Original','callback',@reset,...
'tooltipstring','Reset working image to original image (or image "committed" via File Menu.');
%exportButton = ...
uicontrol(segtool,'style','pushbutton',...
'pos',[objpos(2) 0.125 objdim 0.0375],...
'string','Export Image and Generate Code','callback',@exportBW,...
'tooltipstring',sprintf('Write image to base workspace (naming is automatic)\nand show reproduction steps (code) in Command Window.'));
commentPanel = uipanel(segtool,'bordertype','etchedin','title','COMMENTS',...
'backgroundcolor',bgc,...
'position',[0.6 0.02 0.38 0.0975]);
commentBox = uicontrol(commentPanel,'style','listbox',...
'position',[0.05 0.025 0.9 0.95],'backgroundcolor',bgc,...
'foregroundcolor','k','fontsize',defaultFontsize+1,'max',10,'min',1,...
'horizontalalignment','l','string',[]);
throwComment(sprintf('Original Image is %0.0f x %0.0f x %0.0f, class %s',size(original.img,1),size(original.img,2),size(original.img,3), class(original.img)));
if ~isempty(original.img)
updateWorkingImg(original.img,[],original.fname,1)
end
[objpos,objdim] = distributeObjects(3,0.05,0.95,0.01);
if iscell(requestedPanels{1})
for tier = 1:size(requestedPanels,2)
for rank = 1:numel(requestedPanels{tier})
setupPanel(requestedPanels{tier}(rank),tier,rank);
end
end
else
tier = 1;
for rank = 1:numel(requestedPanels)
setupPanel(requestedPanels(rank),tier,rank)
end
end
% SET ALL CALLBACKS AND BUSYACTIONS
hndls = [autoSens,autoSigma,edgeType,edgeDir,extType,...
extDir,nConnValue,houghPeakOpts,houghLinesOpts];
%DEFAULT CALLS TO PROCESSREQUEST (no inputs)
for jj = 1:numel(hndls)
iptaddcallback(hndls(jj),'callback',@processRequest);
end
set(hndls,'BusyAction','cancel');
set(segtool,'visible','on');
if nargout < 1
clear segtoolHandle
end
% BEGIN NESTED SUBFUNCTIONS
function overlayColor = changeOverlayColor(varargin)
overlayColor = getappdata(overlayColorButton,'overlayColor');
overlayColor = uisetcolor(overlayColor);
set(overlayColorButton,'cdata',reshape(kron(overlayColor,ones(15,15)),15,15,3));
setappdata(overlayColorButton,'overlayColor',overlayColor);
set(parentFigure,'CurrentAxes',overlayax);
img = get(imhandles(workingax),'cdata');
if islogical(img)
updateOverlay(img);
end
end
function modifyOpacity(varargin)
set(parentFigure,'CurrentAxes',overlayax);
switch varargin{3}
case 1
if ~isempty(findall(gcf,'tag','opaqueOverlay'))
showMaskAsOverlay(get(overlayOpacitySldr,'value'));
end
case 2
set(original.overlayImgHndl,...
'alphadata',get(imgOpacitySldr,'value'));
end
end
function updateOverlay(varargin)
% Updates OVERLAY display
% Takes one input: binary mask to OVERLAY
% If OVERLAY is empty, mask is cleared; otherwise, it is written
overlay = varargin{1};
if isempty(overlay)
delete(findall(overlayax,'tag','opaqueOverlay'))
else
set(parentFigure,'CurrentAxes',overlayax);
overlayColor = getappdata(overlayColorButton,'overlayColor');
opacity = get(overlayOpacitySldr,'value');
showMaskAsOverlay(opacity,varargin{1},overlayColor);
expandAxes(overlayax);
end
end
function toggleMenuItem(varargin)
item = varargin{1};
checked = '';
switch get(item,'type')
case 'uimenu'
checked = get(item,'checked');
if strcmp(checked,'on')
set(item,'checked','off');
else
set(item,'checked','on');
end
end
if strcmp(get(item,'Tag'),'DisableTooltips')
tmp = sort(findall(segtool));
if strcmp(checked,'off') %Now turned on
allTooltips = cell(numel(tmp),1);
for tmpind = 1:numel(tmp)
try
allTooltips{tmpind} = get(tmp(tmpind),'tooltipstring');
set(tmp(tmpind),'tooltipstring','');
end
end
else
for tmpind = 1:numel(tmp)
try
set(tmp(tmpind),'tooltipstring',allTooltips{tmpind});
end
end
end
end
end
function img = getFile(varargin)
[img,cmap,original.fname,~,userCanceled] = getNewImage(true);
if userCanceled
return
end
lastCommand = '';
if isrgb(img)
original.grayversion = rgb2gray(img);
else
original.grayversion = img;
end
original.img = img;
if ~islogical(original.img)
original.img = im2double(original.img);
original.segmented = [];
else
original.segmented = original.img;
end
original.grayversion = im2double(original.grayversion);
isImageTypeIndexed = ~isempty(original.cmap);
if isImageTypeIndexed
if isempty(map_var_name)
% user is importing an indexed image, but did not select a
% colormap. USE DEFAULT.
else
original.cmap = evalin('base',map_var_name);
img = ind2rgb(img,original.cmap);
end
end
updateWorkingImg(img,cmap,original.fname,1);
end
function updateWorkingImg(img,cmap,fname,updateOriginal)
% UPDATES Main working axis, and image therein
% INPUTS:
% IMG................Image DISPLAYED in working ax
% CMAP...............If nonempty, modifies display of IMG
% FNAME..............Modifies display of filename
% UPDATEORIGINAL.....Overwrites data stored in original.img
% DEFAULTS
if nargin < 3 || isempty(fname)
fname = 'Original';
end
if nargin < 4 || isempty(updateOriginal)
updateOriginal = 0;
end
if strcmp(fname,'FromMorphTool')
img = imhandles(workingax);
img = get(img,'cdata');
updateOriginal = true;
end
if updateOriginal
% Update data stored in ORIGINAL data structure
img = im2double(img);
original.img = img;
if isrgb(img)
original.grayversion = rgb2gray(img);
else
original.grayversion = img;
end
original.cmap = cmap;
% Update DISPLAY of ORIGAX
delete(origax);
origax = axes('parent',segtool,'pos', [0.02 0.03 0.1968 0.2]);
set(parentFigure,'CurrentAxes',origax);
cla;
tmp = imshow(img,[],'parent',origax);
expandAxes(origax);
[~,fn,ext] = fileparts(fname);
title([fn ext],'fontweight','bold');
% Update display of OVERLAYAX
delete(overlayax);
%overlayPanel position = [0.26 0.02 0.32 0.25]
%origax position = [0.02 0.03 0.1968 0.2]
overlayax = axes('parent',overlayPanel,'pos',[0.02 0.05 0.1968/0.32 0.2/0.25]);
set(parentFigure,'CurrentAxes',overlayax);
cla;
original.overlayImgHndl = imshow(img,[],'parent',overlayax);
expandAxes(overlayax);
title('Overlay','fontweight','bold');
%Update IMHIST on threshold panel:
if ishandle(histax)
parent = get(histax,'parent');
delete(histax);
histax = axes('parent',parent,'units','normalized',...
'pos',[0.1 0.55 0.85 0.35]);
imhist(original.grayversion);
hold on;
threshval = graythresh(original.grayversion);
title(sprintf('(Graythresh = %0.2f)',threshval));
threshLine = line([threshval, threshval],ylim,'color','r','linewidth',2);
end
end
% Update WORKINGAX
delete(workingax);
workingax = axes('parent',segtool,'pos', [0.02 0.3 0.56 0.625]);
set(parentFigure,'CurrentAxes',workingax);
cla;
tmp = imshow(img,[],'parent',workingax);
expandAxes(workingax);
workingAxTitle = title('Working Axes','fontweight','bold');
end
function reset(varargin)
% WRAPPER for verification of destructive command
verify = get(findobj(segtool,'tag','Verify'),'checked');
if strcmp(verify,'on')
tmp = questdlg(sprintf('This operates on the original image; any interim results will be lost.\n(You may want to save/export your results first.)\n\n(Turn off ''Verify Destructive Commands'' in the Options Menu to suppress this warning.)'),'Continue?','CONTINUE','Cancel','CONTINUE');
else
tmp = 'CONTINUE';
end
if strcmp(tmp,'CONTINUE')
updateWorkingImg(original.img,original.cmap,original.fname,1);
updateOverlay([]);
end
throwComment('Ready');
end
function convertImage(varargin)
successMsg = 'Update Successful.';
verify = get(findobj(segtool,'tag','Verify'),'checked');
tmp = 'CONTINUE';
switch varargin{3}
case 'CurrentWorkingImage'
% COMMIT
if strcmp(verify,'on')
tmp = questdlg(sprintf('This modifies the original image; are you sure you want to continue?\n\n(Turn off ''Verify Destructive Commands'' in the Options Menu to suppress this warning.)'),'Continue?','CONTINUE','Cancel','CONTINUE');
else
tmp = 'CONTINUE';
end
if ~strcmp(tmp,'CONTINUE')
return
end
updateWorkingImg(get(imhandles(workingax),'cdata'),[],'Original',1);
throwComment(successMsg,0,1);
case 'double'
updateWorkingImg(original.img,[],original.fname,1);
throwComment(successMsg,0,1);
case 'RGB2gray'
if ~isempty(original.cmap)
updateWorkingImg(ind2gray(original.img,cmap),[],original.fname,0);
throwComment(successMsg,0,1);
throwComment('To continue working with this modified image, you may "Commit" it using FILE->COMMIT, if you so desire.',1,1);
elseif isrgb(original.img)
updateWorkingImg(rgb2gray(original.img),[],original.fname,0);
throwComment(successMsg,0,1);
throwComment('To continue working with this modified image, you may "Commit" it using FILE->COMMIT, if you so desire.',1,1);
else
throwComment('Invalid conversion for original image.',1,1);
end
case 'grayscale'
if ~isrgb(original.img)
throwComment('Conversion valid only for RGB original',1,1);
return
else
figure('numbertitle','off','name','Temporary','tag','tmpfig','windowstyle','normal');
ax(1) = subplot(2,2,1);
imshow(rgb2gray(original.img));
title('IM2GRAY');
ax(2) = subplot(2,2,2);
imshow(original.img(:,:,1));
title('RED Plane')
ax(3) = subplot(2,2,3);
imshow(original.img(:,:,2));
title('GREEN Plane')
ax(4) = subplot(2,2,4);
imshow(original.img(:,:,3));
title('BLUE Plane')
set(ax,'units','normalized')
a = get(ax,'position');
for ii = 1:4
tmp = a{ii};
uicontrol('style','radio','units','normalized','pos',[tmp(1) tmp(2)*0.9 tmp(3) 0.05],...
'string','Use this image','value',0,'callback',{@useThisImage,ii});
end
return
end
case {'RedPlane','GreenPlane','BluePlane'}
if size(original.img,3) < 3
throwComment('Invalid conversion for original image',1,1);
else
updateWorkingImg(original.img(:,:,strcmp(varargin{3},{'RedPlane','GreenPlane','BluePlane'})),[],original.fname,0);
throwComment(successMsg,0,1);
throwComment('To continue working with this modified image, you may "Commit" it using FILE->COMMIT, if you so desire.',1,1);
end
case 'HSV'
if isrgb(original.img)
updateWorkingImg(rgb2hsv(original.img),[],original.fname,0);
throwComment('To continue working with this modified image, you may "Commit" it using FILE->COMMIT, if you so desire.',1,1);
else
throwComment('Conversion valid only for RGB original',1,1);
end
case 'LAB'
if isrgb(original.img)
cform = makecform('srgb2lab');
updateWorkingImg(applycform(original.img,cform),[],original.fname,0);
throwComment(successMsg,0,1);
throwComment('To continue working with this modified image, you may "Commit" it using FILE->COMMIT, if you so desire.',1,1);
else
throwComment('Conversion valid only for RGB original',1);
end
case 'Decorrstretch'
if isrgb(original.img)
updateWorkingImg(decorrstretch(original.img),[],original.fname,0);
throwComment(successMsg,0,1);
throwComment('To continue working with this modified image, you may "Commit" it using FILE->COMMIT, if you so desire.',1,1);
else
throwComment('Conversion valid only for RGB original',1,1);
end
case 'Complement'
updateWorkingImg(imcomplement(original.img),[],original.fname,0);
throwComment(successMsg,0,1);
throwComment('To continue working with this modified image, you may "Commit" it using FILE->COMMIT, if you so desire.',1,1);
end
end
function pass = isrgb(test)
s = size(test);
pass = length(s) == 3 && s(3) == 3 && ~islogical(test);
end
function throwComment(commentString,beepOn,append)
soundsOff = get(findobj(segtool,'tag','TurnOffSounds'),'checked');
if nargin < 2
beepOn = 0;
end
if nargin < 3
append = 1;
end
if append
currString = get(commentBox,'string');
currString = char(cellstr({currString;commentString}));
if all(double(currString(1,:)== 32))
currString = currString(2:end,:);
end
set(commentBox,'string',currString);
else
set(commentBox,'string',commentString);
end
tmp = size(get(commentBox,'string'),1);
set(commentBox,'listboxtop',tmp,'value',tmp);
if beepOn && ~strcmp(soundsOff,'on')
play(notification) %This seems to be the workaround for the issues with SOUND
end
drawnow;
end
function img = segment(segType,varargin)
% THE WORKHORSE
% USE ORIGINAL;
usingGray = false;
% Some processes return non-binary images
if ismember(segType,{'Hough Line/Circle'})%,'Thresholding'
if ~isempty(original.segmented)
img = original.segmented;
else
img = get(imhandles(workingax),'cdata');
end
else
img = original.img;
end
uwi = 1; % Flag: update working image
switch segType
case 'Edge'
if isrgb(img)
usingGray = true;
img = original.grayversion;
end
ind = find(cell2mat(get(edgeType,'value')));
requestedEdge = get(edgeType(ind),'string');%#ok
useAutoSens = get(autoSens,'value');
if useAutoSens
thresh = [];
else
thresh = get(sensSldr,'value');
end
switch requestedEdge
case {'Sobel','Prewitt'}
ind = find(cell2mat(get(edgeDir,'value')));
requestedDir = get(edgeDir(ind),'string');%#ok
[img,threshOut] = edge(img,requestedEdge,thresh,requestedDir);
lastCommand = sprintf('[img,threshOut] = edge(img,''%s'',[%0.2f],''%s'');',requestedEdge,thresh,requestedDir);
throwComment(sprintf('Searching for edges in [the] %s direction[s]',requestedDir),0,1);
case 'Roberts'
[img,threshOut] = edge(img,requestedEdge,thresh);
lastCommand = sprintf('[img,threshOut] = edge(img,''%s'',%0.2f);',requestedEdge,thresh);
case {'LOG','Canny'}
useAutoSigma = get(autoSigma,'value');
if useAutoSigma
if strcmp(requestedEdge,'LOG')
sigma = 2; %Default
else
sigma = 1; %Default
end
set(sigmaEdt,'string',num2str(sigma));
set(sigmaSldr,'value',sigma);
else
sigma = max(0.001,get(sigmaSldr,'value'));
end
[img,threshOut] = edge(img,requestedEdge,thresh,sigma);
if isempty(thresh)
thresh = '[]';
else
thresh = num2str(thresh);
end
lastCommand = sprintf('[img,threshOut] = edge(img,''%s'',%s,%0.2f);',requestedEdge,thresh,sigma);
case 'ZeroCross'
[img,threshOut] = edge(img,requestedEdge,thresh);
lastCommand = sprintf('[img,threshOut] = edge(img,''%s'',%0.2f);',requestedEdge,thresh);
end
if useAutoSens
if numel(threshOut) > 1
threshOut = threshOut(2);
% See note on Canny
end
set(sensEdt,'string',sprintf('%0.3f',threshOut));
set(sensSldr,'value',threshOut);
end
throwComment([requestedEdge ' Edge Detection']);
case 'Thresholding'
if isrgb(img)
usingGray = true;
img = original.grayversion;
end
throwComment('THRESHOLDING',0,1);
threshval = get(threshSldr,'value');
img = img >= threshval;
lastCommand = sprintf('img = img >= %0.2f;\nOR\nimg = im2bw(img,%0.2f);',threshval,threshval);
case 'Hough Line/Circle'
if ~islogical(img)
throwComment('Hough functions operate on binary image; consider using edge detection routine before continuing.',1);
togglePointer('arrow');
return
else
thetaResVal = min(90-1e-1,max(1e-1,get(thetaRes,'value')));
rhoResVal = get(rhoRes,'value');
if get(rhoRes,'value') > norm(size(img))
throwComment('rhoRes auto-limited to norm(size(img))',1,1);
rhoResVal = min(norm(size(img))-1e-1,max(1e-1,get(rhoRes,'value')));
end
minTheta = get(thetaMin,'value');
maxTheta = get(thetaMax,'value');
if minTheta >= maxTheta
throwComment('Waiting for valid theta range (minTheta < maxTheta)',0,1);
togglePointer('arrow');
return
end
numPeaksVal = round(get(numPeaks,'value'));
houghPeakThresh = str2double(get(houghPeakOpts(1),'string'));
houghNHoodSize1 = str2double(get(houghPeakOpts(2),'string'));
houghNHoodSize2 = str2double(get(houghPeakOpts(3),'string'));
houghLinesFillGap = str2double(get(houghLinesOpts(1),'string'));
houghLinesMinLength = str2double(get(houghLinesOpts(2),'string'));
if maxTheta >=90
maxTheta = 90-thetaResVal;
end
[H,T,R] = hough(img,'Theta',minTheta:thetaResVal:maxTheta,...
'RhoResolution',rhoResVal);
%toc
if any([houghNHoodSize1 >= size(H,1),houghNHoodSize2 >= size(H,2),~isodd(houghNHoodSize1),~isodd(houghNHoodSize2)])
houghNHoodSize1 = min(houghNHoodSize1,size(H,1)-1);
if ~isodd(houghNHoodSize1)
houghNHoodSize1 = max(2*ceil(houghNHoodSize1/2) - 1, 1); % Make sure the nhood size is odd
end
houghNHoodSize2 = max(1,min(houghNHoodSize2,size(H,2)-1));
if ~isodd(houghNHoodSize2)
houghNHoodSize2 = max(2*ceil(houghNHoodSize2/2) - 1, 1); % Make sure the nhood size is odd
end
set(houghPeakOpts(2),'string',houghNHoodSize1);
set(houghPeakOpts(3),'string',houghNHoodSize2);
throwComment('Using nearest valid value of neighborhood size. (See Help for HOUGHPEAKS.)',0,1);
end
P = houghpeaks(H,numPeaksVal,...
'threshold',houghPeakThresh,...
'NHoodSize',[houghNHoodSize1 houghNHoodSize2]);
% display the hough matrix
axes(houghAx);
cla;
imshow(imadjust(mat2gray(H)),'XData',T,'YData',R,'InitialMagnification','fit');
xlabel('\theta'), ylabel('\rho');
axis on, axis normal, hold on;
xlim([minTheta,maxTheta])
lines = [];
if ~isempty(P)
plot(T(P(:,2)),R(P(:,1)),'s','color','r');
lines = houghlines(img, T, R, P,'FillGap',houghLinesFillGap,'MinLength',houghLinesMinLength);
end
set(parentFigure,'CurrentAxes',workingax);
delete(findall(parentFigure,'tag','tmphough'));
if isfield(lines,'point1') %Successfully captured at least one line
hold on
for k = 1:length(lines)
xy = [lines(k).point1; lines(k).point2];
plot(xy(:,1),xy(:,2),'LineWidth',2,'Color','green','tag','tmphough');
% Plot beginnings and ends of lines
plot(xy(1,1),xy(1,2),'x','LineWidth',2,'Color','yellow','tag','tmphough');
plot(xy(2,1),xy(2,2),'x','LineWidth',2,'Color','red','tag','tmphough');
end
end
end
lastCommand = ...
{sprintf('[H,T,R] = hough(img,''Theta'',%0.2f:%0.2f:%0.2f,''RhoResolution'',%0.2f);',minTheta,thetaResVal,maxTheta,rhoResVal);
sprintf('P = houghpeaks(H,%d,''threshold'',%0.2f,''NHoodSize'',[%d %d]);',numPeaksVal,houghPeakThresh,houghNHoodSize1,houghNHoodSize2);
sprintf('lines = houghlines(img,T,R,P,''FillGap'',%d,''MinLength'',%d);\n',houghLinesFillGap,houghLinesMinLength);
sprintf('%%%%VISUALIZATION:\nfigure;\nimshow(img);\nhold on;');
sprintf('for ii = 1:length(lines)');
sprintf('\txy = [lines(ii).point1; lines(ii).point2];');
sprintf('\tplot(xy(:,1),xy(:,2),''LineWidth'',2,''Color'',''green'');\nend')};
lastCommand = char(lastCommand);
uwi = 0;
case 'Regional/Extended Min/Max'
img = original.img;
ind = find(cell2mat(get(extType,'value')));
requestedType = get(extType(ind),'string');%#ok
ind = find(cell2mat(get(extDir,'value')));
requestedDir = get(extDir(ind),'string');%#ok
nConn = nConnOpts{get(nConnValue,'value')};
h = get(hSldr,'value');
switch requestedType
case 'Extended'
if strcmp(requestedDir,'Minimum')
img = imextendedmin(img,h,nConn);
lastCommand = sprintf('img = imextendedmin(img,%0.2f,%i);\n',h,nConn);
else
img = imextendedmax(img,h,nConn);
lastCommand = sprintf('img = imextendedmax(img,%0.2f,%i);\n',h,nConn);
end
case 'Regional'
%h = nConnOpts{get(nConnValue,'value')};
if strcmp(requestedDir,'Minimum')
img = imregionalmin(img,nConn);
lastCommand = sprintf('img = imregionalmin(img,%d);',nConn);
else
img = imregionalmax(img,nConn);
lastCommand = sprintf('img = imregionalmax(img,%d);',nConn);
end
end
throwComment([requestedType '/' requestedDir ' Segmentation'],0,1);
if size(img,3) ~= 1
img = im2double(img);
throwComment('Visualizing multidimensional logical mask AS DOUBLE',0,1);
end
case 'Chroma'
throwComment('CHROMA',0,1);
case 'thresholdLocally'
localThreshold = varargin{1};
autoblocksize = get(localThreshold(9),'value');
if autoblocksize
[M,N] = bestblk([size(img,1),size(img,2)]);
else
M = str2double(get(localThreshold(1),'string'));
N = str2double(get(localThreshold(2),'string'));
end
BS(1) = str2double(get(localThreshold(3),'string'));
BS(2) = str2double(get(localThreshold(4),'string'));
tmp = {'replicate','symmetric'};
PM = tmp{get(localThreshold(6),'value')};
FF = str2double(get(localThreshold(8),'string'));
img = thresholdLocally(img,[M N],...
'BorderSize',[BS(1),BS(2)],...
'PadPartialBlocks',get(localThreshold(5),'value') == 1,...
'PadMethod',PM,...
'TrimBorder',get(localThreshold(7),'value') == 1,...
'FudgeFactor',FF);
lastCommand = sprintf('imgOut = thresholdLocally(imgIn,[%d, %d],...\n\t''BorderSize'',[%d,%d],...\n\t''PadPartialBlocks'',%d,...\n\t''PadMethod'',''%s'',...\n\t''TrimBorder'',%d,...\n\t''FudgeFactor'',%0.2f);\n\n%% (If you haven''t already done so, please download ''thresholdLocally'' from the File Exchange!)',M,N,BS(1),BS(2),get(localThreshold(5),'value') == 1,PM,get(localThreshold(7),'value') == 1,FF);
case 'calcColorMasksButton'
nColors = str2double(get(nColorsEdt,'string'));
throwComment(sprintf('Calculating %0.0f masks.....',nColors),0,1);
X = rgb2ind(img,nColors);
% (X ranges from 0 to nColors-1)
nRows = ceil(sqrt(nColors));
nCols = ceil(nColors/nRows);
[hobjpos,hobjdim] = distributeObjects(nRows,0.025,0.975,0.01);
[vobjpos,vobjdim] = distributeObjects(nCols,0.9,0.025,0.1);
colorMaskAxHandles = zeros(nColors,1);
imHandles = colorMaskAxHandles;
titleHandles = colorMaskAxHandles;
delete(findall(gcf,'tag','colorMaskAx'));
ind = 1;
for jjsub = 1:nCols
for iisub = 1:nRows
colorMaskAxHandles(ind) = axes('parent',colorMaskPanel,...
'units','normalized',...
'pos',[hobjpos(iisub) vobjpos(jjsub) hobjdim vobjdim]); %#ok
ind = ind + 1;
if ind > nColors
break
end
end
end
for iisub = 0:nColors-1
tmp = ismember(X,iisub);
[m,n,~] = size(tmp);
while m*n > 5e5
tmp = imresize(tmp,0.7);
[m,n,~] = size(tmp);
end
imHandles(iisub+1) = imshow(tmp,'parent',colorMaskAxHandles(iisub+1));
titleHandles(iisub+1) = title(sprintf('%02d',iisub),'parent',colorMaskAxHandles(iisub+1),'color','w','fontsize',14,...
'fontweight','b','backgroundcolor','r','buttondownfcn',@imSelected,'userdata',iisub,...
'interpreter','none');
end
set(titleHandles,'hittest','on');
set(colorMaskAxHandles,'box','on','linewidth',1,...
'xcolor','y','ycolor','y','visible','on',...
'xtick',[],'ytick',[],'tag','colorMaskAx')
expandAxes(colorMaskAxHandles)
case 'selectColorAndCreateMask'
updateWorkingImg(img);
updateOverlay([]);
if isempty(manuallySampledColor) || strcmp(get(gcbo,'style'),'pushbutton')
throwComment('Select sample pixel(s) of desired color',1,1);
axes(workingax)
oldwat = get(workingAxTitle);
set(workingAxTitle,'string','Click to Select Color. (Double-click when done.)','color','r','fontsize',18);
tmp = impixel;
set(workingAxTitle,'string',oldwat.String,'color',oldwat.Color,'fontsize',oldwat.FontSize);
manuallySampledColor = mean(tmp,1);
end
img =...
img(:,:,1) >= manuallySampledColor(1) - get(pctBelowRedSldr,'value') /100 & ...
img(:,:,1) <= manuallySampledColor(1) + get(pctAboveRedSldr,'value') /100 & ...
img(:,:,2) >= manuallySampledColor(2) - get(pctBelowGreenSldr,'value')/100 & ...
img(:,:,2) <= manuallySampledColor(2) + get(pctAboveGreenSldr,'value')/100 & ...
img(:,:,3) >= manuallySampledColor(3) - get(pctBelowBlueSldr,'value') /100 & ...
img(:,:,3) <= manuallySampledColor(3) + get(pctAboveBlueSldr,'value') /100;
updateOverlay(img);
updateWorkingImg(img);
throwComment('Ready',0,1);
lastCommand = {
sprintf('\nimg = im2double(img);');
sprintf('mask = ...\nimg(:,:,1)>=%0.2f & ...',...
manuallySampledColor(1)-get(pctBelowRedSldr,'value')/100);
sprintf('img(:,:,1)<=%0.2f & ...',...
manuallySampledColor(1)+get(pctAboveRedSldr,'value')/100);
sprintf('img(:,:,2)>=%0.2f & ...',...
manuallySampledColor(2)-get(pctBelowGreenSldr,'value')/100);
sprintf('img(:,:,2)<=%0.2f & ...',...
manuallySampledColor(2)+get(pctAboveGreenSldr,'value')/100);
sprintf('img(:,:,3)>=%0.2f & ...',...
manuallySampledColor(3)-get(pctBelowBlueSldr,'value')/100);
sprintf('img(:,:,3)<=%0.2f;\n',...
manuallySampledColor(3)+get(pctAboveBlueSldr,'value')/100)};
lastCommand = char(lastCommand);
end
if uwi %Update Working Imgage?
updateWorkingImg(img);
end
if usingGray
throwComment('Operating on GRAYSCALE version',0,1);
end
throwComment('Ready',0,1);
togglePointer('arrow');
end
function processRequest(varargin)
% Main Switchyard to manage callback requests.
% Called with one or three inputs.
% If one argument, the input is the handle to the Callback Object,
% and the type of the requested segmentation is automatically
% determined by the name of the active panel. If multiple inputs
% are passed, varargin{3} should be the name of the requested
% segmentation type. (e.g., {@processRequest,'Edge'})).
%
% The multiple-argument form of this function allows multiple
% segmentation types to be captured in a single panel.
togglePointer('watch');
%throwComment('Working...')
requestingObject = varargin{1};
reqObjTag = get(requestingObject,'tag');
allHandles = cell2mat(mainTabCardHandles');
usePanel = find(strcmp(get(allHandles,'visible'),'on'));
if nargin < 3
segType = get(allHandles(usePanel),'Title');%#ok
else
segType = varargin{3};
end
optionalInputs = [];
% Specific actions prior to segmentation
switch reqObjTag
case 'SensSldr'
set(autoSens,'value',0);
case 'SigmaSldr'
set(autoSigma,'value',0);
case 'ThreshSldr'
threshval = get(threshSldr,'value');
set(threshLine,'xdata',[threshval threshval])
case 'HoughPeakThresh'
newVal = str2double(get(requestingObject,'string'));
if isempty(newVal) || isnan(newVal) || newVal < 0
newVal = 0.5*max(original.img(:)); %Default
end
set(requestingObject,'string',sprintf('%0.3f',newVal));
case {'HoughNHoodSize1','HoughNHoodSize2'}
%dim = find(strcmp(reqObjTag,{'HoughNHoodSize1','HoughNHoodSize2'}));
%maxsize = size(original.img,dim);
newVal = floor(str2double(get(requestingObject,'string')));
if isempty(newVal) || isnan(newVal) || newVal < 0 %|| newVal >= maxsize
newVal = size(original.img)/50;
newVal = max(2*ceil(newVal/2) + 1, 1); % Make sure the nhood size is odd; % Default
newVal = newVal(strcmp(reqObjTag,{'HoughNHoodSize1','HoughNHoodSize2'}));
end
set(requestingObject,'string',sprintf('%0.0f',newVal));
case 'HoughLinesFillGap'
newVal = str2double(get(requestingObject,'string'));
if isempty(newVal) || isnan(newVal) || newVal < 0 || isinf(newVal)
newVal = 20;
end
set(requestingObject,'string',newVal);%sprintf('%0.0f',newVal));
case 'HoughLinesMinLength'
newVal = str2double(get(requestingObject,'string'));
if isempty(newVal) || isnan(newVal) || newVal < 0 || isinf(newVal)
newVal = 40;
end
set(requestingObject,'string',newVal);%sprintf('%0.0f',newVal));
case 'LocalOtsuThresholding'
optionalInputs = varargin{4};
case 'calcColorMasksButton'
segType = 'calcColorMasksButton';
img = original.img;
if ~isrgb(img)
throwComment('This option is available only for RGB images!',1,1);
set(gcf,'pointer','arrow');
return
end
updateWorkingImg(img);
updateOverlay([]);
case 'selectColorAndCreateMask'
segType = 'selectColorAndCreateMask';
img = original.img;
if ~isrgb(original.img)
throwComment('This option is only valid for RGB images.',1,1);
set(gcf,'pointer','arrow');
return
end
end
% Create segmentation mask
segmented = segment(segType,optionalInputs);
if islogical(segmented)
updateOverlay(segmented);
original.segmented = segmented;
end
end
function useThisImage(varargin)
close(findobj('tag','tmpfig'));
switch varargin{3}
case 1
updateWorkingImg(rgb2gray(original.img));
case 2
updateWorkingImg(original.img(:,:,1));
case 3
updateWorkingImg(original.img(:,:,2));
case 4
updateWorkingImg(original.img(:,:,3));
end
throwComment('To continue working with this modified image, you may "Commit" it using FILE->COMMIT, if you so desire.',1,1);
end
function togglePointer(pType)
if nargin == 0 || isempty(pType)
if strcmp(get(gcf,'pointer'),'arrow')
set(gcf,'pointer','watch');
else
set(gcf,'pointer','arrow');
end
else
set(gcf,'pointer',pType);
end
drawnow;
end
function exportBW(varargin)
img = get(imhandles(workingax),'cdata');
if ~islogical(img)
throwComment('It appears that you haven''t segmented the image yet!',1,1);
return
end
n = 0; tmp = 1;
while tmp
n = n + 1;
tmp = evalin('base',['exist(''segtoolimage', num2str(n), ''')']);
end
throwComment(sprintf('Current image written to segtoolimage%d, and reproduction steps written to Command Window.\n',n),1,1);
fprintf('\nCurrent image written to segtoolimage%d\n',n)
assignin('base',['segtoolimage' num2str(n)],img);
fprintf('\nREPRODUCTION STEPS:\n******************\n')
disp(char(lastCommand))
fprintf('******************\n')
fprintf('(NOTE that non-double images are\nconverted to type ''double'' in SegmentTool;\nthese commands reflect operations on images\nthat may have been converted with ''IM2DOUBLE''.)\n\n');
end
function reverseMask(varargin)
img = get(imhandles(workingax),'cdata');
if ~islogical(img)
throwComment('There does not appear to be a valid binary image in the working axis!',1,1);
return
else
img = ~img;
throwComment('Mask reversed.',0,1);
updateWorkingImg(img);
updateOverlay(img);
end
end
function flipImages(varargin)
set(overlayax,'children',flipud(get(overlayax,'children')));
end
function setupPanel(requestedPanel,tier,rank)
parent = mainTabCardHandles{tier}(rank);
bgc = get(parent,'backgroundcolor');
tmp = rgb2gray(bgc);
if tmp(1) > 0.4
txtc = [0 0 0];
else
txtc = [1 1 1];
end
switch requestedPanel{1}
case 'Edge'
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Edge Detection Segmentation
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
edgeButtons = uibuttongroup(parent,...
'Position',[0.05 0.825 0.9 0.125],...
'backgroundcolor',bgc,'title','Algorithm');
% Create radio buttons in the button group.
allEdgeTypes = {'Sobel','Prewitt','Roberts','LOG','ZeroCross','Canny'};
tmp = {sprintf('The Sobel method finds edges using the Sobel approximation to the derivative.\nIt returns edges at those points where the gradient of I is maximum.');
sprintf('The Prewitt method finds edges using the Prewitt approximation to the derivative.\nIt returns edges at those points where the gradient of I is maximum.');
sprintf('The Roberts method finds edges using the Roberts approximation to the derivative. \nIt returns edges at those points where the gradient of I is maximum.');
'The Laplacian of Gaussian method finds edges by looking for zero crossings after filtering I with a Laplacian of Gaussian filter.';
'The zero-cross method finds edges by looking for zero crossings after filtering I with a filter you specify.';
sprintf('The Canny method finds edges by looking for local maxima of the gradient of I.\nThe gradient is calculated using the derivative of a Gaussian filter.\nThe method uses two thresholds, to detect strong and weak edges, and includes the weak edges in the output only if they are connected to strong edges.\nThis method is therefore less likely than the others to be fooled by noise, and more likely to detect true weak edges.')};
edgeType = zeros(numel(allEdgeTypes,1));
for ii = 0:numel(allEdgeTypes)-1
edgeType(ii+1) = uicontrol('parent',edgeButtons,'Style','Radio','String',allEdgeTypes{ii+1},...
'pos',[objpos(rem(ii,3)+1) 0.15+(0.5*(ii<3)) objdim 0.3],'HandleVisibility','off',...
'backgroundcolor',bgc,'fontsize',defaultFontsize,'value',ii==0,...
'tooltipstring',tmp{ii+1});
end
string = ' All Algorithms ';
uicontrol('parent',parent,'style','text',...
'pos',[0.05 0.75 0.9 0.05],...
'backgroundcolor',bgc,...
'string',padString(string,stringWidth),...
'fontsize',defaultFontsize+1,'foregroundcolor',txtc,...
'horizontalalignment','c');
[sensSldr,~,sensEdt] = ...
sliderPanel(parent,...
{'backgroundcolor',bgc,...
'title','Sensitivity Threshold',...
'pos',[0.05 0.625 0.6 0.125],...
'units','normalized'},...
{'backgroundcolor',colors(5,:),...
'min',0,'max',1,'value',0.5,...
'callback',@processRequest,'sliderstep',[0.001 0.05],...
'tag','SensSldr',...
'tooltipstring',sprintf('Specifies the sensitivity threshold for the selected edge detection method.\nedge ignores all edges that are not stronger than thresh.\nIf you do not specify thresh, or if thresh is empty ([]), edge chooses the value automatically.\n\nRight-Click bar to reset to default.')},...
{'backgroundcolor',colors(5,:),'fontsize',defaultFontsize},...
{'backgroundcolor',bgc,'fontsize',defaultFontsize},...
'%0.3f');
autoSens = uicontrol('parent',parent,'style','checkbox',...
'value',1,'pos',[0.675 0.625 0.3 0.1],...
'backgroundcolor',bgc,'string','Auto-set','fontsize',defaultFontsize,...
'tooltipstring','Use default value');
%
string = ' Sobel, Prewitt ';
uicontrol('parent',parent,'style','text',...
'pos',[0.05 0.55 0.9 0.05],...
'backgroundcolor',bgc,...
'string',padString(string,stringWidth),...
'fontsize',defaultFontsize+1,'foregroundcolor',txtc,...
'horizontalalignment','c');
edgeDirs = uibuttongroup(parent,...
'Position',[0.05 0.475 0.9 0.08],...
'backgroundcolor',bgc,'title','Direction');
edgeDir(1) = uicontrol('parent',edgeDirs,'Style','Radio',...
'String','Horizontal',...
'pos',[objpos(1) 0.2 objdim 0.6],'HandleVisibility','off',...
'backgroundcolor',bgc,'fontsize',defaultFontsize,'value',0);
edgeDir(2) = uicontrol('parent',edgeDirs,'Style','Radio',...
'String','Vertical',...
'pos',[objpos(2) 0.2 objdim 0.6],'HandleVisibility','off',...
'backgroundcolor',bgc,'fontsize',defaultFontsize,'value',0);
edgeDir(3) = uicontrol('parent',edgeDirs,'Style','Radio',...
'String','Both',...
'pos',[objpos(3) 0.2 objdim 0.6],'HandleVisibility','off',...
'backgroundcolor',bgc,'fontsize',defaultFontsize,'value',1);
%
string = ' LOG, Canny ';
uicontrol('parent',parent,'style','text',...
'pos',[0.05 0.4 0.9 0.05],...
'backgroundcolor',bgc,...
'string',padString(string,stringWidth),...
'fontsize',defaultFontsize+1,'foregroundcolor',txtc,...
'horizontalalignment','c');
[sigmaSldr,~,sigmaEdt] = ...
sliderPanel(parent,...
{'backgroundcolor',bgc,...
'title','Sigma',...
'pos',[0.05 0.275 0.6 0.125],...
'units','normalized'},...
{'backgroundcolor',colors(5,:),'min',0,'max',7,'value',2,'callback',@processRequest,...
'sliderstep',[0.01 0.1],'tag','SigmaSldr',...
'tooltipstr',sprintf('Sigma specifies the standard deviation of the Gaussian filter.\nIt is relevant to Canny and LOG edge-detection.\nSee the documentation for EDGE for details.\n\nRight-Click bar to reset to default.')},...
{'backgroundcolor',colors(5,:),'fontsize',defaultFontsize},...
{'backgroundcolor',bgc,'fontsize',defaultFontsize},...
'%0.2f');
autoSigma = uicontrol('parent',parent,'style','checkbox',...
'value',1,'pos',[0.675 0.275 0.3 0.1],...
'backgroundcolor',bgc,'string','Auto-set','fontsize',defaultFontsize,...
'tooltipstring','Use default value');
%
case 'Thresholding'
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Segmentation by Thresholding
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
[objpos,objdim] = distributeObjects(3,0.05,0.95,0.01);
string = ' Global Thresholding ';
uicontrol('parent',parent,'style','text',...
'pos',[0.05 0.955 0.9 0.025],...
'backgroundcolor',bgc,...
'string',padString(string,stringWidth),...
'fontsize',defaultFontsize+1,'foregroundcolor',txtc,...
'horizontalalignment','c');
histax = axes('parent',parent,'units','normalized',...
'pos',[0.1 0.55 0.85 0.35]);
imhist(original.grayversion);
hold on;
threshval = graythresh(original.grayversion);
title(sprintf('(Graythresh = %0.2f)',threshval));
threshLine = line([threshval, threshval],ylim,'color','r','linewidth',2);
[threshSldr,~,~] = ...
sliderPanel(parent,...
{'backgroundcolor',bgc,...
'title','',...
'pos',[0.05 0.36 0.95 0.125],...
'units','normalized','bordertype','none'},...
{'backgroundcolor',bgc/2,'min',0,'max',1,...
'value',roundto(threshval,2),'callback',@processRequest,...
'sliderstep',[0.01 0.1],'tag','ThreshSldr',...
'tooltipstring',sprintf('Slide to change segmentation threshold value.\nRight-Click bar to reset to default.')},...
{'fontsize',defaultFontsize},...
{'backgroundcolor',bgc,'fontsize',defaultFontsize},...
'%0.2f');
string = ' Local (BLOCKPROC-BASED) Otsu Thresholding ';
rshift = 0.45;
uicontrol('parent',parent,'style','text',...
'pos',[0.05 0.285 0.9 0.05],...
'backgroundcolor',bgc,...
'string',padString(string,stringWidth),...
'fontsize',defaultFontsize+1,'foregroundcolor',txtc,...
'horizontalalignment','c');
uicontrol('parent',parent,'style','text',...
'string','BlockSize >>>',...
'pos',[0.05+rshift 0.225 0.4 0.05],...
'backgroundcolor',bgc,'foregroundcolor',txtc,...
'horizontalalignment','l',...
'tooltipstring','Specifies size of block [M, N] with which to process the image.');
localThresh(1) = uicontrol('parent',parent,'style','edit',...
'pos',[0.28+rshift 0.25 0.075 0.035],...
'string',32,'foregroundcolor',txtc);
localThresh(2) = uicontrol('parent',parent,'style','edit',...
'pos',[0.415+rshift 0.25 0.075 0.035],...
'string',32,'foregroundcolor',txtc);
downshift = 0.045;
uicontrol('parent',parent,'style','text',...
'string','BorderSize >>>',...
'pos',[0.05+rshift 0.225-downshift 0.4 0.05],...
'backgroundcolor',bgc,'foregroundcolor',txtc,...
'horizontalalignment','l',...
'tooltipstring',sprintf('A two-element vector, [V H], specifying the amount of border pixels to add to each block. \nThe function adds V rows above and below each block and H columns left and right of each block.\nThe size of each resulting block will be: [M + 2*V, N + 2*H].\nBy default, the function automatically removes the border from the result of fun.\nSee the ''TrimBorder'' parameter for more information.\nThe function pads blocks with borders extending beyond the image edges with zeros.\nDefault: [0 0] (no border)'));
localThresh(3) = uicontrol('parent',parent,'style','edit',...
'pos',[0.28+rshift 0.25-downshift 0.075 0.035],...
'string',6,'foregroundcolor',txtc);
localThresh(4) = uicontrol('parent',parent,'style','edit',...
'pos',[0.415+rshift 0.25-downshift 0.075 0.035],...
'string',6,'foregroundcolor',txtc);
uicontrol('parent',parent,'style','text',...
'string','Pad Partial Blocks:',...
'pos',[0.05 0.225-2*downshift 0.4 0.05],...
'backgroundcolor',bgc,'foregroundcolor',txtc,...
'horizontalalignment','l',...
'tooltipstring',sprintf('A logical scalar. When set to true, blockproc pads partial blocks to make them full-sized (M-by-N) blocks.\nPartial blocks arise when the image size is not exactly divisible by the block size.\nIf they exist, partial blocks lie along the right and bottom edge of the image.\nThe default is false, meaning that the function does not pad the partial blocks, but processes them as-is.\nblockproc uses zeros to pad partial blocks when necessary.\nDefault: false.'));
localThresh(5) = uicontrol('parent',parent,'style','popupmenu',...
'pos',[0.325 0.25-2*downshift 0.225 0.035],...
'string',{'true','false'},'value',1,'foregroundcolor',txtc);
uicontrol('parent',parent,'style','text',...
'string','Pad Method:',...
'pos',[0.05 0.225-3*downshift 0.4 0.05],...
'backgroundcolor',bgc,'foregroundcolor',txtc,...
'horizontalalignment','l',...
'tooltipstring',sprintf('The ''PadMethod'' determines how blockproc will pad the image boundary. Options are:\nX: Pads the image with a scalar (X) pad value. (By default X == 0.)\n''replicate'': Repeats border elements of image A.\n''symmetric'': Pads image A with mirror reflections of itself.'));
localThresh(6) = uicontrol('parent',parent,'style','popupmenu',...
'pos',[0.325 0.25-3*downshift 0.225 0.035],...
'string',{'replicate','symmetric'},'value',1,'foregroundcolor',txtc);
uicontrol('parent',parent,'style','text',...
'string','Trim Border:',...
'pos',[0.05 0.225-4*downshift 0.4 0.05],...
'backgroundcolor',bgc,'foregroundcolor',txtc,...
'horizontalalignment','l',...
'tooltipstring',sprintf('A logical scalar. When set to true, the blockproc function trims off border pixels from the output of the user function, fun.\nThe function removes V rows from the top and bottom of the output of fun, and H columns from the left and right edges.\nThe ''BorderSize'' parameter defines V and H.\nThe default is true, meaning that the blockproc function automatically removes borders from the fun output.'));
localThresh(7) = uicontrol('parent',parent,'style','popupmenu',...
'pos',[0.325 0.25-4*downshift 0.225 0.035],...
'string',{'true','false'},'value',1,'foregroundcolor',txtc);
uicontrol('parent',parent,'style','text',...
'string','Fudge Factor:',...
'pos',[0.05 0.225-5*downshift 0.4 0.05],...
'backgroundcolor',bgc,'foregroundcolor',txtc,...
'horizontalalignment','l',...
'tooltipstring','Multiplier for graythresh value');
localThresh(8) = uicontrol('parent',parent,'style','edit',...
'pos',[0.325 0.25-5*downshift 0.225 0.035],...
'string',1,'foregroundcolor',txtc);
localThresh(9) = uicontrol('parent',parent,'style','checkbox',...
'string','Auto-select Blocksize',...
'pos',[0.05 0.225 0.35 0.05],...
'backgroundcolor',bgc,'foregroundcolor',txtc,...
'value',1,'tag','autoSelectBlocksize','tooltipstring','Use default value');
set([localThresh(1),localThresh(2)],'callback',@clearAutoBlocksize);
uicontrol('parent',parent,'style','pushbutton',...
'pos',[0.65 0.075 0.3 0.1],...
'backgroundcolor',bgc,...
'string','Apply Local Thresholding','tag','LocalOtsuThresholding',...
'fontsize',defaultFontsize+1,'foregroundcolor',txtc,...
'fontweight','bold',...
'horizontalalignment','c','callback',...
{@processRequest,'thresholdLocally',localThresh},...
'tooltipstring','Use BLOCKPROC to calculate automatically block-wise (i.e., "local") threshold values');
case 'Hough Line/Circle'
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Hough, HoughPeaks, HoughLines all merged herein
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
[objpos,objdim] = distributeObjects(3,0.965,0.325,0.11);
string = ' Hough Function ';
uicontrol('parent',parent,'style','text',...
'pos',[0.05 0.92 0.9 0.05],...
'backgroundcolor',bgc,...
'string',padString(string,stringWidth),...
'fontsize',defaultFontsize+1,'foregroundcolor',txtc,...
'horizontalalignment','c','fontname','arial');
HoughFcnPanel = uipanel('parent',parent,...
'pos',[0.05,0.6625,0.9,0.265],...
'backgroundcolor',bgc,'foregroundcolor',txtc,'bordertype','none');
parent = HoughFcnPanel;
[Lobjpos,Lobjdim] = distributeObjects(2,1,0,0.05);
thetaRes = ...
sliderPanel(parent,...
{'backgroundcolor',bgc,...
'title','Theta Resolution',...
'pos',[0 Lobjpos(1) 0.475 Lobjdim],...
'units','normalized'},...
{'backgroundcolor',bgc/2,'min',0,'max',90,...
'value',1,'callback',@processRequest,...
'sliderstep',[1/90 5/90],'tag','ThetaResSldr',...
'tooltipstring',sprintf('''Theta'' specifies a vector of Hough transform theta (angle) values, specified on the interval [-90, 90) degrees.\nCalculated here as [ThetaMin:ThetaRes:ThetaMax]\n\nRight-Click bar to reset to default.')},...
{'fontsize',defaultFontsize+1},...
{'backgroundcolor',bgc,'fontsize',defaultFontsize+1},...
'%0.1f');
[thetaMin,~,~] = ...
sliderPanel(parent,...
{'backgroundcolor',bgc,...
'title','Theta Minimum',...
'pos',[0 Lobjpos(2) 0.475 Lobjdim],...
'units','normalized'},...
{'backgroundcolor',bgc/2,'min',-90,'max',90,...
'value',-90,'callback',@processRequest,...
'sliderstep',[1/180 5/180],'tag','ThetaMinSldr',...
'tooltipstring',sprintf('''Theta'' specifies a vector of Hough transform theta (angle) values, specified on the interval [-90, 90) degrees.\nCalculated here as [ThetaMin:ThetaRes:ThetaMax].\n\nRight-Click bar to reset to default.')},...
{'fontsize',defaultFontsize+1},...
{'backgroundcolor',bgc,'fontsize',defaultFontsize+1},...
'%0.1f');
[thetaMax,~,~] = ...
sliderPanel(parent,...
{'backgroundcolor',bgc,...
'title','Theta Maximum',...
'pos',[0.525 Lobjpos(2) 0.475 Lobjdim],...
'units','normalized'},...
{'backgroundcolor',bgc/2,'min',-90,'max',90,...
'value',90,'callback',@processRequest,...
'sliderstep',[1/180 5/180],'tag','ThetaMaxSldr',...
'tooltipstring',sprintf('''Theta'' specifies a vector of Hough transform theta (angle) values, specified on the interval [-90, 90) degrees.\nCalculated here as [ThetaMin:ThetaRes:ThetaMax].\n\nRight-Click bar to reset to default.')},...
{'fontsize',defaultFontsize+1},...
{'backgroundcolor',bgc,'fontsize',defaultFontsize},...
'%0.1f');
rhoRes = ...
sliderPanel(parent,...
{'backgroundcolor',bgc,...
'title','Rho Resolution',...
'pos',[0.525 Lobjpos(1) 0.475 Lobjdim],...
'units','normalized'},...
{'backgroundcolor',bgc/2,'min',0.1,'max',200,...
'value',1,'callback',@processRequest,...
'sliderstep',[1/90 5/90],'tag','ThetaResSldr',...
'tooltipstring',sprintf('Real scalar on the interval (0, norm(size(BW)) ), specifying the spacing of the Hough transform bins along the rho axis.\nDefault: 1.\n\nRight-Click bar to reset to default.')},...
{'fontsize',defaultFontsize},...
{'backgroundcolor',bgc,'fontsize',defaultFontsize},...
'%0.1f');
% Reset Parent
parent = mainTabCardHandles{tier}(rank);
string = ' HoughPeaks Function ';
uicontrol('parent',parent,'style','text',...
'pos',[0.05 objpos(2)+objdim-0.13 0.9 0.05],...
'backgroundcolor',bgc,...
'string',padString(string,stringWidth),...
'fontsize',defaultFontsize+1,'foregroundcolor',txtc,...
'horizontalalignment','c');
HoughPeaksPanel = uipanel('parent',parent,...
'pos',[0.05,0.52-0.065,0.9,0.14],...
'backgroundcolor',bgc,'foregroundcolor',txtc,'bordertype','none');
parent = HoughPeaksPanel;
editShift = 0.05;
numPeaks = ...
sliderPanel(parent,...
{'backgroundcolor',bgc,...
'title','Number of Peaks',...
'pos',[0 0 0.475 1],...
'units','normalized'},...
{'backgroundcolor',bgc/2,'min',1,'max',200,...
'value',1,'callback',@processRequest,...
'sliderstep',[1/199 5/199],'tag','NumPeaksSldr',...
'tooltipstring',sprintf('Numpeaks specifies the maximum number of peaks to identify.\nDefault = 1.\n\nRight-Click bar to reset to default.')},...
{'fontsize',defaultFontsize},...
{'backgroundcolor',bgc,'fontsize',defaultFontsize},...
'%0.0f');
uicontrol('parent',parent,'style','text','string','Peak Threshold:',...
'pos',[0.4875 0.5 0.31 0.3],...
'backgroundcolor',bgc,'foregroundcolor',txtc,'horizontalalignment','r',...
'tooltipstring',sprintf('Nonnegative scalar.\nValues of H below ''Threshold'' will not be considered to be peaks.\nThreshold can vary from 0 to Inf.\nDefault: 0.5*max(H(:))'));
houghPeakOpts(1) = uicontrol('parent',parent,'style','edit',...
'string',sprintf('%0.3f',0.5*max(original.img(:))),...
'pos',[0.9 0.475+editShift 0.1 0.3],'tag','HoughPeakThresh');
uicontrol('parent',parent,'style','text','string','Neighborhood Size:',...
'pos',[0.4875 0.15 0.31 0.3],...
'backgroundcolor',bgc,'foregroundcolor',txtc,'horizontalalignment','r',...
'tooltipstring',sprintf('Two-element vector of positive odd integers: [M N], specifying the size of the suppression neighborhood.\nThis is the neighborhood around each peak that is set to zero after the peak is identified.\nDefault: smallest odd values >= size(H)/50'));
nhood = size(original.img)/50;
nhood = max(2*ceil(nhood/2) + 1, 1); % Make sure the nhood size is odd;
houghPeakOpts(2) = uicontrol('parent',parent,'style','edit',...
'string',sprintf('%0.0f',nhood(1)),...
'pos',[0.81 0.15+editShift 0.075 0.3],'tag','HoughNHoodSize1');
houghPeakOpts(3) = uicontrol('parent',parent,'style','edit','string',sprintf('%0.0f',nhood(2)),...
'pos',[0.925 0.15+editShift 0.075 0.3],'tag','HoughNHoodSize2');
% Reset Parent
parent = mainTabCardHandles{tier}(rank);
string = ' HoughLines Function ';
uicontrol('parent',parent,'style','text',...
'pos',[0.05 objpos(3)+objdim-0.085 0.9 0.05],...
'backgroundcolor',bgc,...
'string',padString(string,stringWidth),...
'fontsize',defaultFontsize+1,'foregroundcolor',txtc,...
'horizontalalignment','c');
HoughLinesPanel = uipanel('parent',parent,...
'pos',[0.05,0.43-0.085,0.9,0.045],...
'backgroundcolor',bgc,'foregroundcolor',txtc,'bordertype','none');
parent = HoughLinesPanel;
editShift = 0.025;
uicontrol('parent',parent,'style','text','string','Fill Gap:',...
'pos',[0 0 0.2 1],...
'backgroundcolor',bgc,'foregroundcolor',txtc,...
'horizontalalignment','l',...
'tooltipstring',sprintf('When houghlines finds two line segments associated with the same Hough transform bin\nthat are separated by less than ''FillGap'' distance,\nhoughlines merges them into a single line segment.\nDefault: 20'));
houghLinesOpts(1) = uicontrol('parent',parent,'style','edit',...
'string',20,...
'pos',[0.25 editShift 0.1 1],'tag','HoughLinesFillGap');
uicontrol('parent',parent,'style','text','string','Minimum Length:',...
'pos',[0.575 0 0.3 1],...
'backgroundcolor',bgc,'foregroundcolor',txtc,'horizontalalignment','l',...
'tooltipstring',sprintf('Merged line segments shorter than ''MinLength'' are discarded.\nDefault: 40'));
houghLinesOpts(2) = uicontrol('parent',parent,'style','edit','string',40,...
'pos',[0.9 editShift 0.1 1],'tag','HoughLinesMinLength');
% Reset Parent
parent = mainTabCardHandles{tier}(rank);
% Circular Hough
string = ' Circular Hough ';
uicontrol('parent',parent,'style','text',...
'pos',[0.015 0.065 0.9 0.05],...
'backgroundcolor',bgc,...
'string',padString(string,stringWidth),...
'fontsize',defaultFontsize+1,'foregroundcolor',txtc,...
'horizontalalignment','c');
uicontrol(parent,'style','pushbutton',...
'pos',[0.05 0.015 0.9 0.05],...
'foregroundcolor','r',...
'fontweight','bold',...
'fontsize',defaultFontsize+1,...
'string','Launch FindCirclesGUI','callback',@LaunchFindCirclesGUI,...
'tooltipstring',sprintf('Detect circles in image CURRENTLY DISPLAYED in main (working) axes.\n \n NOTE: Hough circle detection (IMFINDCIRCLES) was added to the Image Processing Toolbox in R2012a.\nThis button launches a standalone GUI for circle detection which must be downloaded and installed from MATLAB Central.\nIf you haven''t already, you should also upgrade to a post-R2011b version to use this functionality.'));
% Reset Parent
parent = mainTabCardHandles{tier}(rank);
houghAx = axes('parent',parent,'units','normalized','pos',[0.085 0.19 0.85 0.125],...
'fontsize',defaultFontsize+1,'visible','off');
case 'Regional/Extended Min/Max'
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Regional Min and Max Segmentation
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
[objpos,objdim] = distributeObjects(3,0.95,0.05,0.4);
string = ' IMEXTENDEDMIN / IMEXTENDEDMAX ';
uicontrol('parent',parent,'style','text',...
'pos',[0.05 0.9 0.9 0.075],...
'backgroundcolor',bgc,...
'string',padString(string,stringWidth),...
'fontsize',defaultFontsize+1,'foregroundcolor',txtc,...
'horizontalalignment','c');
[hSldr,~,~] = ...
sliderPanel(parent,...
{'backgroundcolor',bgc,...
'title','Height Threshold (Extended transforms)',...
'pos',[0.05 0.755 0.6 0.15],...
'units','normalized'},...
{'backgroundcolor',colors(5,:),...
'min',0,'max',1,'value',0.3,...
'callback',@processRequest,...
'sliderstep',[0.01 0.1],'tag','hSldr',...
'tooltipstring',sprintf('See help for Hough.\nDefault = 0.3.\n\nRight-Click bar to reset to default.')},...
{'backgroundcolor',colors(5,:),'fontsize',defaultFontsize},...
{'backgroundcolor',bgc,'fontsize',defaultFontsize},...
'%0.2f');
string = ' EXTENDED and REGIONAL Min/Max Operations ';
uicontrol('parent',parent,'style','text',...
'pos',[0.05 0.645 0.9 0.075],...
'backgroundcolor',bgc,...
'string',padString(string,stringWidth),...
'fontsize',defaultFontsize+1,'foregroundcolor',txtc,...
'horizontalalignment','c');
minmaxButtons = uibuttongroup(parent,...
'Position',[0.05 0.55 0.9 0.105],...
'backgroundcolor',bgc,'title','Direction');
extDir(1) = uicontrol('parent',minmaxButtons,'Style','Radio',...
'String','Minimum',...
'pos',[0.05 0.2 0.3 0.6],'HandleVisibility','off',...
'backgroundcolor',bgc,'fontsize',defaultFontsize,'value',0);
extDir(2) = uicontrol('parent',minmaxButtons,'Style','Radio',...
'String','Maximum',...
'pos',[0.5 0.2 0.3 0.6],'HandleVisibility','off',...
'backgroundcolor',bgc,'fontsize',defaultFontsize,'value',0);
regExtButtons = uibuttongroup(parent,...
'Position',[0.05 0.425 0.9 0.105],...
'backgroundcolor',bgc,'title','Type');
extType(1) = uicontrol('parent',regExtButtons,'Style','Radio',...
'String','Extended',...
'pos',[0.05 0.2 0.3 0.6],'HandleVisibility','off',...
'backgroundcolor',bgc,'fontsize',defaultFontsize,'value',0);
extType(2) = uicontrol('parent',regExtButtons,'Style','Radio',...
'String','Regional',...
'pos',[0.5 0.2 0.2 0.6],'HandleVisibility','off',...
'backgroundcolor',bgc,'fontsize',defaultFontsize,'value',0);
string = ' IMREGIONALMIN / IMREGIONALMAX ';
uicontrol('parent',parent,'style','text',...
'pos',[0.05 0.33 0.9 0.075],...
'backgroundcolor',bgc,...
'string',padString(string,stringWidth),...
'fontsize',defaultFontsize+1,'foregroundcolor',txtc,...
'horizontalalignment','c');
nConnOpts = {4;8;6;18;26};
nConnValue = uicontrol('parent',parent,'style','listbox',...
'pos',[0.05 0.2 0.1 0.15],...
'string',nConnOpts,...
'backgroundcolor',bgc);
if ~isrgb(original.img)
set(nConnValue,'value',2);
else
set(nConnValue,'value',5);
end
uicontrol('parent',parent,'style','edit',...
'pos',[0.2 0.2 0.75 0.15],'min',1,'max',3,'horizontalalignment','l',...
'string',{'CONNECTIVITY:', 'IMREGIONALMIN and IMREGIONALMAX support any nonsparse, numeric class and any dimension.',...
'By default, imregionalmin uses 8-connected neighborhoods for 2-D images and 26-connected neighborhoods for 3-D images. For higher dimensions, imregionalmin uses conndef(ndims(I),''maximal'')'});
case 'Color-Based'
[~,colorTabCardHandles,colorTabHandles] = ...
tabPanel(mainTabCardHandles{tier}(rank),...
{'RGB2IND','Manual Selection'},...
'panelpos',[0 0 1 1],...
'tabpos','t',...
'colors',colors(5:6,:),...
'highlightColor','w',...'c'
'tabHeight',40,...
'tabCardPVs',{'bordertype','line','title',''},...
'tabLabelPVs',{'fontsize',7,'foregroundcolor',[0.2 0.2 0.2]});
bgc = colors(5,:);
set(colorTabHandles{1}(1),'tooltipstring','Specify number of colors, let MATLAB create combinable masks of each.')
set(colorTabHandles{1}(2),'tooltipstring','Select a color and tolerance, and MATLAB will create a mask from the information.')
parent = colorTabCardHandles{1}(1);
[nColorsSldr,~,nColorsEdt] = ...
sliderPanel(parent,...
{'backgroundcolor',bgc,...
'title','Number of Colors',...
'pos',[0.025 0.88 0.4 0.1],...
'units','normalized'},...
{'backgroundcolor',bgc,...
'min',2,'max',36,'value',12,...
'callback',@processRequest,...
'sliderstep',[1/34 3/34],'tag','nColorsSldr',...
'tooltipstring',sprintf('Slide to select number of colors in which to quantize image.\nDefault = 12.\n\nRight-Click bar to reset to default.')},...
{'backgroundcolor',bgc,'fontsize',7},...
{'backgroundcolor',bgc,'fontsize',7},...
'%0.0f');
calcColorMasksButton = uicontrol('parent',parent,...
'string','Calculate Colormasks',...
'position',[0.45 0.88 0.525 0.1],...
'foregroundcolor',txtc,...
'fontsize',18,'fontweight','bold',...
'tag','calcColorMasksButton',...
'tooltipstring','Create masks based on slider selection.',...
'callback',@processRequest);
uicontrol(parent,'style','text','units','normalized',...
'position',[0.025 0.8 0.95 0.05],...
'string','Click on index number (title) to include (''green'') or omit (''red'') color masks; click image to expand axes.',...
'fontweight','bold','foregroundcolor',txtc,...
'horizontalalignment','l',...
'backgroundcolor',bgc,'fontsize',10);
colorMaskPanel = uipanel('parent',parent,...
'pos',[0.025,0.025,0.95,0.775],...
'backgroundcolor',bgc,'foregroundcolor',txtc,...
'borderType','etchedin');%'none'
clearMasksButton = uicontrol('style','pushbutton',...
'parent',colorMaskPanel,'position',[0.975 0.95 0.025 0.05],...
'foregroundcolor',txtc,'string','X',...
'callback',@clearColorMasks);
bgc = colors(6,:);
[objpos, objdim] = distributeObjects(3,0.025,0.975,0.025,1);
parent = colorTabCardHandles{1}(2);
[pctBelowRedSldr,~,pctBelowRed] = ...
sliderPanel(parent,...
{'backgroundcolor',bgc,...
'foregroundcolor','k',...
'fontweight','bold',...
'title','Percent of Range Below Red',...
'pos',[objpos(1) 0.89 objdim 0.1],...
'units','normalized'},...
{'backgroundcolor',bgc,...
'min',0,'max',100,'value',5,...
'callback',@processRequest,...
'sliderstep',[1/50 5/50],'tag','selectColorAndCreateMask',...
'tooltipstring',sprintf('Percentage threshold below red.\nDefault = 5.\n\nRight-Click bar to reset to default.')},...
{'backgroundcolor',[0.8 0 0],'fontsize',8,...
'foregroundcolor','w','fontweight','bold'},...
{'backgroundcolor',bgc,'fontsize',7},...
'%0.0f');
[pctAboveRedSldr,~,pctAboveRed] = ...
sliderPanel(parent,...
{'backgroundcolor',bgc,...
'foregroundcolor','k',...
'fontweight','bold',...
'title','Percent of Range Above Red',...
'pos',[objpos(1) 0.78 objdim 0.1],...
'units','normalized'},...
{'backgroundcolor',bgc,...
'min',0,'max',100,'value',5,...
'callback',@processRequest,...
'sliderstep',[1/50 5/50],'tag','selectColorAndCreateMask',...
'tooltipstring',sprintf('Percentage threshold above red.\nDefault = 5.\n\nRight-Click bar to reset to default.')},...
{'backgroundcolor',[0.8 0 0],'fontsize',8,...
'foregroundcolor','w','fontweight','bold'},...
{'backgroundcolor',bgc,'fontsize',7},...
'%0.0f');
[pctBelowGreenSldr,~,pctBelowGreen] = ...
sliderPanel(parent,...
{'backgroundcolor',bgc,...
'foregroundcolor','k',...
'fontweight','bold',...
'title','Percent of Range Below Green',...
'pos',[objpos(2) 0.89 objdim 0.1],...
'units','normalized'},...
{'backgroundcolor',bgc,...
'min',0,'max',100,'value',5,...
'callback',@processRequest,...
'sliderstep',[1/50 5/50],'tag','selectColorAndCreateMask',...
'tooltipstring',sprintf('Percentage threshold below green.\nDefault = 5.\n\nRight-Click bar to reset to default.')},...
{'backgroundcolor',[0 0.8 0],'fontsize',8,...
'foregroundcolor','w','fontweight','bold'},...
{'backgroundcolor',bgc,'fontsize',7},...
'%0.0f');
[pctAboveGreenSldr,~,pctAboveGreen] = ...
sliderPanel(parent,...
{'backgroundcolor',bgc,...
'foregroundcolor','k',...
'fontweight','bold',...
'title','Percent of Range Abpve Greem',...
'pos',[objpos(2) 0.78 objdim 0.1],...
'units','normalized'},...
{'backgroundcolor',bgc,...
'min',0,'max',100,'value',5,...
'callback',@processRequest,...
'sliderstep',[1/50 5/50],'tag','selectColorAndCreateMask',...
'tooltipstring',sprintf('Percentage threshold above green.\nDefault = 5.\n\nRight-Click bar to reset to default.')},...
{'backgroundcolor',[0 0.8 0],'fontsize',8,...
'foregroundcolor','w','fontweight','bold'},...
{'backgroundcolor',bgc,'fontsize',7},...
'%0.0f');
[pctBelowBlueSldr,~,pctBelowBlue] = ...
sliderPanel(parent,...
{'backgroundcolor',bgc,...
'foregroundcolor','k',...
'fontweight','bold',...
'title','Percent of Range Below Blue',...
'pos',[objpos(3) 0.89 objdim 0.1],...
'units','normalized'},...
{'backgroundcolor',bgc,...
'min',0,'max',100,'value',5,...
'callback',@processRequest,...
'sliderstep',[1/50 5/50],'tag','selectColorAndCreateMask',...
'tooltipstring',sprintf('Percentage threshold below blue.\nDefault = 5.\n\nRight-Click bar to reset to default.')},...
{'backgroundcolor',[0 0 0.8],'fontsize',8,...
'foregroundcolor','w','fontweight','bold'},...
{'backgroundcolor',bgc,'fontsize',7},...
'%0.0f');
[pctAboveBlueSldr,~,pctAboveBlue] = ...
sliderPanel(parent,...
{'backgroundcolor',bgc,...
'foregroundcolor','k',...
'fontweight','bold',...
'title','Percent of Range Above Blue',...
'pos',[objpos(3) 0.78 objdim 0.1],...
'units','normalized'},...
{'backgroundcolor',bgc,...
'min',0,'max',100,'value',5,...
'callback',@processRequest,...
'sliderstep',[1/50 5/50],'tag','selectColorAndCreateMask',...
'tooltipstring',sprintf('Percentage threshold above blue.\nDefault = 5.\n\nRight-Click bar to reset to default.')},...
{'backgroundcolor',[0 0 0.8],'fontsize',8,...
'foregroundcolor','w','fontweight','bold'},...
{'backgroundcolor',bgc,'fontsize',7},...
'%0.0f');
selectColorAndCreateMask = uicontrol('parent',parent,...
'string','Select Color and Create Mask',...
'position',[0.025 0.7 0.95 0.05],...
'foregroundcolor',txtc,...
'fontsize',18,'fontweight','bold',...
'tag','selectColorAndCreateMask',...
'callback',@processRequest);
otherwise
error('Unrecognized panel requested.')
end
end
function clearAutoBlocksize(varargin)
set(findobj('tag','autoSelectBlocksize'),'value',0);
end
function LaunchFindCirclesGUI(varargin)
if exist('FindCirclesGUI','file')
FindCirclesGUI(get(imhandles(workingax),'cdata'))
else
throwComment('Please ensure that you are using MATLAB R2012a or later, and ...',1,1);
throwComment(' ... download and install FindCirclesGUI from the MATLAB Central File Exchange!',0,1);
end
end
function img = imSelected(varargin)
currColor = get(gcbo,'backgroundcolor');
if all(currColor == [1 0 0])
set(gcbo,'backgroundcolor','g');
else
set(gcbo,'backgroundcolor','r');
end
inclusions = [];
for kk = 1:nColors
tmp = get(titleHandles(kk),'backgroundcolor');
if all(tmp == [0 1 0])
inclusions = [inclusions,get(titleHandles(kk),'userdata')]; %#ok
end
end
img = ismember(X,inclusions);
updateWorkingImg(img);
updateOverlay(img);
throwComment('Ready',0,1);
lastCommand = {sprintf('X = rgb2ind(img,%0.0f);',nColors);
sprintf('mask = ismember(X,[%s]);',num2str(inclusions))};
togglePointer('arrow');
end
end %NESTED SUBFUNCTIONS
% SUBFUNCTIONS (NOT NESTED)
function string = padString(string,varargin)
string = [' ' upper(string) ' '];
end
function k=isodd(x)
if nargin ~=1||~isa(x,'double')||floor(x)~=x
error('Function isodd requires a double argument, with all matrix elements integers.');
end
%k = x/2~=floor(x/2);
k = rem(x,2) ~= 0;
end