Code covered by the BSD License  

Highlights from
MAGIC - MATLAB Generic Imaging Component

image thumbnail

MAGIC - MATLAB Generic Imaging Component

by

 

21 May 2009 (Updated )

Tutorial GUI to demonstrate basic functionality of various controls on the GUI

magic_gui(varargin)
function varargout = magic_gui(varargin)
% Tutorial m-file to demonstrate the operation of various controls 
% such as listbox, radio button, checkbox, push button, static text, and axes.
% Requires the Image Processing Toolbox.
%
% Written by Mark Hayworth, Ph.D.
% Advanced Imaging Section
% email: hayworth dot ms at pg dot com
% The Procter & Gamble Company, Cincinnati, Ohio, USA.
% December 2008 - May 2009.  Updated Feb. 2013.
%
% magic_gui M-file for magic_gui.fig
%      magic_gui by itself, creates a new magic_gui or raises the existing
%      singleton*.
%
%      H = magic_gui returns the handle to a new magic_gui or the handle to
%      the existing singleton*.
%
%      magic_gui('CALLBACK',hObject,eventData,handles,...) calls the local
%      function named CALLBACK in magic_gui.M with the given input arguments.
%
%      magic_gui('Property','Value',...) creates a new magic_gui or raises the
%      existing singleton*.  Starting from the left, property value pairs are
%      applied to the GUI before magic_gui_OpeningFcn gets called.  An
%      unrecognized property name or invalid value makes property application
%      stop.  All inputs are passed to magic_gui_OpeningFcn via varargin.
%
%      *See GUI Options on GUIDE's Tools menu.  Choose "GUI allows only one
%      instance to run (singleton)".
%
% See also: GUIDE, GUIDATA, GUIHANDLES

% Edit the above text to modify the response to help magic_gui

% Last Modified by GUIDE v2.5 20-Mar-2013 15:26:39

% Begin initialization code - DO NOT EDIT
gui_Singleton = 1;
gui_State = struct('gui_Name',       mfilename, ...
                   'gui_Singleton',  gui_Singleton, ...
                   'gui_OpeningFcn', @magic_gui_OpeningFcn, ...
                   'gui_OutputFcn',  @magic_gui_OutputFcn, ...
                   'gui_LayoutFcn',  [], ...
                   'gui_Callback',   []);
if nargin && ischar(varargin{1})
    gui_State.gui_Callback = str2func(varargin{1});
end

if nargout
    [varargout{1:nargout}] = gui_mainfcn(gui_State, varargin{:});
else
    gui_mainfcn(gui_State, varargin{:});
end
% End initialization code - DO NOT EDIT

% --- Executes just before magic_gui is made visible.
function magic_gui_OpeningFcn(hObject, eventdata, handles, varargin)
% This function has no output args, see OutputFcn.
% hObject    handle to figure
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)
% varargin   command line arguments to magic_gui (see VARARGIN)

% Choose default command line output for magic_gui
handles.output = hObject;

%=====================================================================
% --- My Startup Code --------------------------------------------------
    % Clear old stuff from console.
	clc;
	fprintf(1, 'Just entered magic_gui_OpeningFcn...\n');
	
	% Change the current folder to the folder of this m-file.
	% (The line of code below is from Brett Shoelson of The Mathworks.)
	cd(fileparts(which(mfilename)));

	% MATLAB QUIRK: Need to clear out any global variables you use anywhere
	% otherwise it will remember their prior values from a prior running of the macro.
    clear global;

	handles.macroFolder = cd;
    handles.maskFolder = [handles.macroFolder '/Masks'];
	set(handles.figMainWindow, 'Visible', 'off');

	% Load up the initial values from the mat file.
	strIniFile = fullfile(handles.macroFolder, 'Magic_GUI.mat');
	if exist(strIniFile, 'file')
		% Pull out values and stuff them in structure initialValues.
		initialValues = load(strIniFile);
		% Assign the image folder from the lastUsedImageFolder field of the
		% structure.
	    handles.ImageFolder = initialValues.lastUsedImageFolder;
	end
	% If the image folder does not exist, but the imdemos folder exists, then point them to that.
	if exist(handles.ImageFolder, 'dir') == 0
		% Folder stored in the mat file does not exist.  Try the imdemos folder instead.
		imdemosFolder = fullfile(matlabroot, '\toolbox\images\imdemos');
		if exist(imdemosFolder, 'dir') == 0
			% imdemos folder exists.  Use it.
			handles.ImageFolder = imdemosFolder;
		else
			% imdemos folder does not exist.  Use current folder.
			handles.ImageFolder = cd;
		end
	end
	% handles.ImageFolder will be a valid, existing folder by the time you get here.
	if exist(strIniFile, 'file')
		% If the mat file is not there, save current folder out in our mat file.
		% Save the image folder in our ini file.
		lastUsedImageFolder = handles.ImageFolder;
		save(strIniFile, 'lastUsedImageFolder');
	end
    set(handles.txtFolder, 'string', handles.ImageFolder);
	
    %uiwait(msgbox(handles.ImageFolder));
    % Load list of images in the image folder.
    handles = LoadImageList(handles);
	% Select none of the items in the listbox.
	set(handles.lstImageList, 'value', []);
	% Update the number of images in the Analyze button caption.
	UpdateAnalyzeButtonCaption(hObject, eventdata, handles)
    
    % Load list of mask images in the app/masks folder.
    handles=LoadMaskList(handles);
	
	% Load a splash image.
    %axes(handles.axesImage);
	fullSplashImageName = fullfile(handles.macroFolder, '/Splash Images/Magic Hat.png');
	
	if exist(fullSplashImageName, 'file')
		% Display splash image.
		imgOriginal = imread(fullSplashImageName);
	else
		% Display something
		imgOriginal = peaks(300);
	end
    % Display image array in a window on the user interface.
	% Display in axes, storing handle of image for later quirk workaround.
	hold off;	% IMPORTANT NOTE: hold needs to be off in order for the "fit" feature to work correctly.
    axesChildHandle = imshow(imgOriginal, 'InitialMagnification', 'fit', 'parent', handles.axesImage);
	imageFolder = handles.ImageFolder;
	h = axesChildHandle;
	setappdata(h, 'imageFolder', imageFolder);

	txtInfo = sprintf('MAGIC - MATLAB Analysis with Generic Imaging Code.\n\nBy Mark Hayworth, Ph.D.\nThe Procter & Gamble Company\n\nTutorial GUI to demonstrate basic functionality\nof various controls on the GUI.');
	set(handles.txtInfo, 'string', txtInfo);
	
	% !!!! QUIRK workaround.!!!!
	% Make it so that if they click in the image axes, it will execute a
	% button down callback called "axesImage_ButtonDownFcn".
	% Now it won't  -- unless you do this quirk workaround.
	% Double click on the dialog box's (main figure's) background to bring up the property inspector.
	% Change both the WindowButtonDownFcn and ButtonDownFcn properties so that they are blank.
	% Also set the main figure's HitTest to off.  Then, make sure you already have the handle to the
	% image living in the axes by getting it from an imshow.  Then do this:
	set(axesChildHandle, 'ButtonDownFcn', @axesImage_ButtonDownFcn);
	% Put everything you want to do when they click the image into the function axesImage_ButtonDownFcn()
	% !!!! End QUIRK workaround.!!!!

	fprintf(1, 'Done with magic_gui_OpeningFcn.\n');

	% Update handles structure
    guidata(hObject, handles);

% --- End of My Startup Code --------------------------------------------------
%=====================================================================


%=====================================================================
% --- Outputs from this function are returned to the command line.
function varargout = magic_gui_OutputFcn(hObject, eventdata, handles) 
% varargout  cell array for returning output args (see VARARGOUT);
% hObject    handle to figure
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)
	fprintf(1, 'Just entered magic_gui_OutputFcn...\n');
	% Get default command line output from handles structure
	varargout{1} = handles.output;
	% Call special function that The Mathworks provides on their website to maximize the window and leave it in focus.
	% 	minfig(F,1) % minimizes the figure window for the figure with handle F
	% 	minfig(F,0) % restores figure F if F is minimized
	% 	maxfig(F,1) % maximizes the figure window for the figure with handle F
	% 	maxfig(F,0) % restores figure F if F is maximized s = figstate(F) % returns the state of figure { Maximized | Minimized | Normal }
	% Note: This works only if the window is already displayed.  If not, it will throw a java exception.
	% The best place to put this is in the yourApp_OutputFcn(), 
	% which actually gets called as the last thing before your app is made available to the user.
	checkForMaxfig = whos('maxfig');
	if isempty(checkForMaxfig)
		% maxfig is not installed on this computer.
		% Enlarge figure to full screen.
		set(gcf, 'units','normalized','outerposition',[0 0 1 1]);
	else
		maxfig(handles.figMainWindow, 1) % Maximizes the figure window for the figure with handle in the first argument.
	end
	fprintf(1, 'Done with magic_gui_OutputFcn.\n');

%=====================================================================
% --- Executes on clicking in lstImageList listbox.
% Display image from disk and plots histogram
function lstImageList_Callback(hObject, eventdata, handles)
% hObject    handle to lstImageList (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)
% Hints: contents = get(hObject,'String') returns lstImageList contents as cell array
%        contents{get(hObject,'Value')} returns selected item from lstImageList
    global baseImageFileName;
	clear global imgOriginal;
	global imgOriginal;	% Declare global so that other functions can see it, if they also declare it global.
	
	% Change mouse pointer (cursor) to an hourglass.  
	% QUIRK: use 'watch' and you'll actually get an hourglass not a watch.
	set(gcf,'Pointer','watch');
	drawnow;	% Cursor won't change right away unless you do this.

	% Update the number of images in the Analyze button caption.
	UpdateAnalyzeButtonCaption(hObject, eventdata, handles)

	% Get image name
    selectedListboxItem = get(handles.lstImageList, 'value');
	if isempty(selectedListboxItem)
		% Bail out if nothing was selected.
		% Change mouse pointer (cursor) to an arrow.
		set(gcf,'Pointer','arrow');
		drawnow;	% Cursor won't change right away unless you do this.
		return;
	end
    % If more than one is selected, bail out.
    if length(selectedListboxItem) > 1 
        % Disable Draw new mask - not clear if there is an image being
        % displayed.
        set(handles.btnDrawNewMask, 'enable', 'off');
        baseImageFileName = '';
		% Change mouse pointer (cursor) to an arrow.
		set(gcf,'Pointer','arrow')
		drawnow;	% Cursor won't change right away unless you do this.
        return;
    end
    % If only one is selected, display it.
	set(handles.axesPlot, 'visible', 'off');	% Hide plot of results since there are no results yet.
    set(handles.btnDrawNewMask, 'enable', 'on');    % Enable Draw Mask Button.
    ListOfImageNames = get(handles.lstImageList, 'string');
    baseImageFileName = strcat(cell2mat(ListOfImageNames(selectedListboxItem)));
    fullImageFileName = [handles.ImageFolder '/' baseImageFileName];	% Prepend folder.
	
	[folder, baseFileName, extension] = fileparts(fullImageFileName);
	switch lower(extension)
	case {'.mov', '.wmv', '.asf'}
		msgboxw('Mov and wmv format video files are not supported by MATLAB.');
		% Change mouse pointer (cursor) to an arrow.
		set(gcf,'Pointer','arrow');
		drawnow;	% Cursor won't change right away unless you do this.
		return;
	case '.avi'
		% The only video format supported natively by MATLAB is avi.
		% A more complicated video player plug in is on MATLAB File Central
		% that will support more types of video.  It has a bunch of DLL's and
		% other files that you have to install.
		
		% Read the file into a MATLAB movie structure.
		myVideo = aviread(fullImageFileName);
		myVideoParameters = aviinfo(fullImageFileName);
		numberOfFrames = myVideoParameters.NumFrames;
		
		% Extract a frame.
		frameToView = uint8(floor(numberOfFrames/2));	% Take the middle frame.
		imgFirstFrame = myVideo(frameToView).cdata;	% The index is the frame number.
		cla(handles.axesPlot, 'reset');
		imshow(imgFirstFrame, 'Parent', handles.axesPlot); % Display the first frame.
		
		% Play the movie in the axes.  It doesn't stretch to fit the axes.
		% The macro will wait until it finishes before continuing.
		axes(handles.axesImage);
		hold off;
		cla(handles.axesImage, 'reset'); % Let image resize, for example demo video rhinos.avi won't fill the image buffer if we don't do this.
		movie(handles.axesImage, myVideo);
		cla(handles.axesPlot, 'reset'); % Clear the mini-image from the plot axes.
		
	    guidata(hObject, handles);
		% Change mouse pointer (cursor) to an arrow.
		set(gcf,'Pointer','arrow');
		drawnow;	% Cursor won't change right away unless you do this.
		return;
	otherwise
		% Display the image.
		imgOriginal = DisplayImage(handles, fullImageFileName);
	end
	
	% If imgOriginal is empty (couldn't be read), just exit.
	if isempty(imgOriginal) 
		% Change mouse pointer (cursor) to an arrow.
		set(gcf,'Pointer','arrow');
		drawnow;	% Cursor won't change right away unless you do this.
		return;
	end
	
	% Read in mask file, or if they haven't chosen one, just have mask be 
	% entire shape of imgOriginal.
	GetImageMask(handles);
	% These declarations need to be after GetImageMask because GetImageMask clears them
	% out and the association with this function is then lost.
	global imgMask;
	global maskVerticesXCoordinates;
	global maskVerticesYCoordinates;

	% Mask off the original with pre-selected mask, if they chose one.
	% Then display masked image and return it in array maskedImage and
	% return mask in logical array binaryMask
	imageSize = size(imgOriginal);
	% Convert this outline into an image.
	imgMask = poly2mask(maskVerticesXCoordinates, maskVerticesYCoordinates, imageSize(1), imageSize(2));
	[maskedImage logBinaryMask] = MaskAndDisplayImage(handles, imgOriginal, false);
	
	% Analyze this image.
	if get(handles.chkAutoAnalyze,'Value') 
		AnalyzeSingleImage(handles, maskedImage, logBinaryMask);
    end

    %fprintf('lstImageList_Callback.5\n');
    
    %axes(handles.axesImage);	% Switch current figure back to image box.
    
    %fprintf('lstImageList_Callback.6\n');
    
	% Change mouse pointer (cursor) to an arrow.
	set(gcf,'Pointer','arrow');
	drawnow;	% Cursor won't change right away unless you do this.
    guidata(hObject, handles);
    return % from lstImageList_Callback()

%=====================================================================
% Reads FullImageFileName from disk into the axesImage axes.
function imageArray = DisplayImage(handles, FullImageFileName)
	% Read in image.
	try
		[imageArray colorMap] = imread(FullImageFileName);
		% colorMap will have something for an indexed image (gray scale image with a stored colormap).
		% colorMap will be empty for a true color RGB image or a monochrome gray scale image.
	catch ME
		errorMessage = sprintf('Error opening image file with imread():\n%s', FullImageFileName);
		set(handles.txtInfo, 'String', errorMessage);
		msgboxw(errorMessage);
		imageArray = [];
		return;	% Skip the rest of this function
	end
	
	try
		% Display image array in a window on the user interface.
		%axes(handles.axesImage);
		hold off;	% IMPORTANT NOTE: hold needs to be off in order for the "fit" feature to work correctly.
		
		% Here we actually display the image in the "axesImage" axes.
		axesChildHandle = imshow(imageArray, 'InitialMagnification', 'fit', 'parent', handles.axesImage);

		% Display a title above the image.
		[folder, basefilename, extension] = fileparts(FullImageFileName);
		extension = lower(extension);
		% Convert any underlines in the name into spaces because otherwise the character after the underline would be a subscript.
		caption = strrep([basefilename extension], '_', ' ');
		% Display the title.
		title(handles.axesImage, caption, 'FontSize', 14);

		% Make it so that if they click on the image, the callback function is executed.
		% See note on QUIRK in startup code section.
		set(axesChildHandle, 'ButtonDownFcn', @axesImage_ButtonDownFcn);
		imageFolder = folder;
		setappdata(axesChildHandle, 'imageFolder', imageFolder);

		[rows columns numberOfColorChannels] = size(imageArray);
		% Get the file date
		fileInfo = dir(FullImageFileName);
		txtInfo = sprintf('%s\n\n%d lines (rows) vertically\n%d columns across\n%d color channels\n', ...
			[basefilename extension], rows, columns, numberOfColorChannels);
		% Tell user the type of image it is.
		if numberOfColorChannels == 3
			colorbar 'off';  % get rid of colorbar.
			txtInfo = sprintf('%s\nThis is a true color, RGB image.', txtInfo);
		elseif numberOfColorChannels == 1 && isempty(colorMap)
			colorbar 'off';  % get rid of colorbar.
			txtInfo = sprintf('%s\nThis is a gray scale image, with no stored color map.', txtInfo);
		elseif numberOfColorChannels == 1 && ~isempty(colorMap)
			txtInfo = sprintf('%s\nThis is an indexed image.  It has one "value" channel with a stored color map that is used to pseudocolor it.', txtInfo);
			colormap(colorMap);
			whos colorMap;
			%fprintf('About to apply the colormap...\n');
			% Thanks to Jeff Mather at the Mathworks to helping to get this working for an indexed image.
			colorbar('peer', handles.axesImage);
			%fprintf('Done applying the colormap.\n');
		end
		% Show the file time and date.
		txtInfo = sprintf('%s\n\n%s', txtInfo, fileInfo.date);
		set(handles.txtInfo, 'String', txtInfo);

		% Plot the histogram if requested.
		plotHistogram = get(handles.chkPlotHistograms, 'Value');
		if plotHistogram
			fprintf('About to plot histogram...\n');
			PlotImageHistogram(handles, imageArray);
			fprintf('Done plotting histogram.\n');
		end
	catch ME
		errorMessage = sprintf('Error in function DisplayImage.\nError Message:\n%s', ME.message);
		WarnUser(errorMessage);
	end
	return; % from DisplayImage


%=====================================================================
function DisplayResults(handles, resultsArray)
	% Display the results
	% MATLAB BUG: \t doesn't work (R2006a)
	% Workaround: convert to "courier New to get fixed space font and use
	% spaces or field widths to align the numbers.
	global CDFPercentiles;
	strHeader = sprintf('Measurement\n____________________________________________________');
	strTotalArea =    sprintf('Total area    %7d', resultsArray(1)) ;
	strMinValue =      sprintf('Minimum Value  %7.2f', resultsArray(2));
	strMaxValue =      sprintf('Minimum Value  %7.2f', resultsArray(3));
	strMeanValue =     sprintf('Mean Value     %7.2f', resultsArray(4));
	strStdDev =       sprintf('Std. Dev.     %7.2f', resultsArray(5));
	strPercentiles =  sprintf('25th percentile at %5.2f\n75th percentile at %5.2f\n50th percentile (median) at %5.2f', CDFPercentiles(25), CDFPercentiles(75), CDFPercentiles(50));
	strResults =      sprintf('%s\n%s\n%s\n%s\n%s\n%s\n%s', strHeader, strTotalArea, strMinValue, strMaxValue, strMeanValue, strStdDev, strPercentiles);
	set(handles.txtInfo, 'String', strResults);

	return;	% DisplayResults
	
%=====================================================================
% Update Analyze button and tooltip string depending on how many files in the listbox were selected.
function UpdateAnalyzeButtonCaption(hObject, eventdata, handles)
    Selected = get(handles.lstImageList, 'value');
	if length(Selected) > 1 
        buttonCaption = {'Step 6:  Analyze '};   % MATLAB quirk - needs to be cell array to keep trailing spaces.
        buttonCaption = strcat(buttonCaption, num2str(length(Selected)));
        buttonCaption = strcat(buttonCaption, ' images');
        set(handles.btnAnalyze, 'string', buttonCaption);
		set(handles.btnAnalyze, 'Tooltipstring', 'Display and analyze the selected image(s)');
	elseif length(Selected) == 1 
        set(handles.btnAnalyze, 'string', 'Step 6:  Analyze 1 image');
		set(handles.btnAnalyze, 'Tooltipstring', 'Display and analyze the selected image');		
	else
        set(handles.btnAnalyze, 'string', 'Step 6:  Analyze no images');
		set(handles.btnAnalyze, 'Tooltipstring', 'Please select image(s) first');
	end
	return;
		
%=====================================================================
% --- Executes during object creation, after setting all properties.
function lstImageList_CreateFcn(hObject, eventdata, handles)
    % hObject    handle to lstImageList (see GCBO)
    % eventdata  reserved - to be defined in a future version of MATLAB
    % handles    empty - handles not created until after all CreateFcns called

    % Hint: listbox controls usually have a white background on Windows.
    %       See ISPC and COMPUTER.
    if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
        set(hObject,'BackgroundColor','white');
    end
    return

%=====================================================================
% --- Executes on clicking btnSelectFolder button.
% Asks user to select a directory and then loads up the listbox (via a call
% to LoadImageList)
function btnSelectFolder_Callback(hObject, eventdata, handles)
    % hObject    handle to btnSelectFolder (see GCBO)
    % eventdata  reserved - to be defined in a future version of MATLAB
    % handles    structure with handles and user data (see GUIDATA)
    %msgbox(handles.ImageFolder);
    returnValue = uigetdir(handles.ImageFolder,'Select folder');
	% returnValue will be 0 (a double) if they click cancel.
	% returnValue will be the path (a string) if they clicked OK.
	if returnValue ~= 0
		% Assign the value if they didn't click cancel.
		handles.ImageFolder = returnValue;
		handles = LoadImageList(handles);
		set(handles.txtFolder, 'string' ,handles.ImageFolder);
		guidata(hObject, handles);
		% Save the image folder in our ini file.
		lastUsedImageFolder = handles.ImageFolder;
		save('Magic_GUI.mat', 'lastUsedImageFolder');
	end
    return
    
%=====================================================================
% --- Load up the listbox with tif files in folder handles.handles.ImageFolder
function handles=LoadImageList(handles)        
	ListOfImageNames = {};
	folder = handles.ImageFolder;
	if ~isempty(handles.ImageFolder) 
		if exist(folder,'dir') == false
			warningMessage = sprintf('Note: the folder used when this program was last run:\n%s\ndoes not exist on this computer.\nPlease run Step 1 to select an image folder.', handles.ImageFolder);
			msgboxw(warningMessage);
			return;
		end
	else
		msgboxw('No folder specified as input for function LoadImageList.');
		return;
	end
	% If it gets to here, the folder is good.
	ImageFiles = dir([handles.ImageFolder '/*.*']);
	for Index = 1:length(ImageFiles)
		baseFileName = ImageFiles(Index).name;
		[folder, name, extension] = fileparts(baseFileName);
		extension = upper(extension);
		switch lower(extension)
		case {'.png', '.bmp', '.jpg', '.tif', '.avi'}
			% Allow only PNG, TIF, JPG, or BMP images
			ListOfImageNames = [ListOfImageNames baseFileName];
		otherwise
		end
	end
	set(handles.lstImageList,'string',ListOfImageNames);
    return

%=====================================================================
% --- Load up the listbox with tif files in folder handles.handles.ImageFolder
function handles=LoadMaskList(handles)        
    ListOfMaskFilenames = {'Do not use a mask'};
    dirListing = dir([handles.maskFolder '/*.mat']);
    for Index = 1:length(dirListing)
        baseFileName = dirListing(Index).name;
        ListOfMaskFilenames = [ListOfMaskFilenames baseFileName];
    end
    set(handles.lstMasks, 'string', ListOfMaskFilenames);
    return


%=====================================================================
% --- Executes on clicking btnAnalyze button.
% Goes down through the list, displaying then analyzing each highlighted image file.
% Main processing is done by the function AnalyzeSingleImage()
function btnAnalyze_Callback(hObject, eventdata, handles)
    % hObject    handle to btnAnalyze (see GCBO)
    % eventdata  reserved - to be defined in a future version of MATLAB
    % handles    structure with handles and user data (see GUIDATA)
	
	% Change mouse pointer (cursor) to an hourglass.  
	% QUIRK: use 'watch' and you'll actually get an hourglass not a watch.
	set(gcf,'Pointer','watch');
	drawnow;	% Cursor won't change right away unless you do this.

	% Get a list of those indexes that are selected, so we know which images to process.
    Selected = get(handles.lstImageList, 'value');
    numberOfSelectedFiles = length(Selected);

	% Then get list of all of the filenames in the list,
	% regardless of whether they are selected or not.
    ListOfImageNames = get(handles.lstImageList, 'string');
	
    % Make an array for the results.  
	% We will send these to Excel if requested.
%     resultsArray = zeros(numberOfSelectedFiles, 6);
	
	for j = 1 : numberOfSelectedFiles    % Loop though all selected indexes.
        index = Selected(j);    % Get the next selected index.
        % Get the filename for this selected index.
        baseImageFileName = strcat(cell2mat(ListOfImageNames(index)));
		imageFullFileName = fullfile(handles.ImageFolder, baseImageFileName);
		
		% Display the image.
		imgOriginal = DisplayImage(handles, imageFullFileName);
		% If imgOriginal is empty (couldn't be read), skip to next file.
		if isempty(imgOriginal) 
			continue;
		end
	
		% If you don't want to use masking, then uncomment the next two lines.
% 		maskedImage = imgOriginal;
% 		logBinaryMask = [];
        % Mask off the original with pre-selected mask, if they chose one.
		% Then display masked image and return it in array maskedImage and
		% return mask in logical array binaryMask.
		% If you don't want to use masking, then delete the next line.
		[maskedImage logBinaryMask] = MaskAndDisplayImage(handles, imgOriginal, true);
		
		% Analyze this image.
        resultsArray = AnalyzeSingleImage(handles, maskedImage, logBinaryMask);
		
		if get(handles.chkSendToExcel, 'value') 
			% Send the results to Excel.  One workbook will be created for each image.
			% Note: You could have one workbook and one worksheet for each image, (just keep the
			% same excelFullFileName but change workSheetName), but Excel 2003 limits you
			% to 32 worksheets or less per workbook, so be aware of that.
			blankString = ' ';  % Need so that both rows of columnNames cell array have the same number of elements (or else you'll get an error).
			columnNames = {baseImageFileName, blankString, blankString, blankString,...
				blankString, blankString, blankString; ...
				'Blob #', 'Area', 'Mean Intensity', 'Perimeter', ...
				'CentroidX', 'CentroidY', 'ECD'};
			[folder, baseFileNameNoExtension, extension] = fileparts(baseImageFileName);
			excelBaseFileName = sprintf('Results for %s.xls', baseFileNameNoExtension);
			excelFullFileName = fullfile(handles.ImageFolder, excelBaseFileName);
			workSheetName = 'Results';
			% Write the filename into row 1, and the column headings into row 2.
			xlswrite(excelFullFileName, columnNames, workSheetName, 'B1');
			% Write numerical results starting on row 3.
			xlswrite(excelFullFileName, resultsArray, workSheetName, 'B3');
			msgboxw({'Done with analysis.  Results are in Excel file:'; excelFullFileName});
		end
		
		% Prompt to allow user to inspect the image.
        if j < numberOfSelectedFiles && get(handles.chkPauseAfterImage, 'value') 
            userPrompt = sprintf('Check out results, then\nclick Continue to process the next image.');
			reply = questdlg(userPrompt, ...
				'Continue?', 'Continue', 'Quit loop', 'Continue');
			% reply = '' for Upper right X, otherwise it's the exact wording.
			if strcmpi(reply, 'Quit loop')
				set(handles.txtInfo, 'string', 'Batch processing terminated.');
				break;
			end
        end
	end	
		    
	set(gcf,'Pointer','arrow');
	drawnow;	% Cursor won't change right away unless you do this.

	guidata(hObject, handles);
    return

%=====================================================================
% Mask image according to maskVerticesXCoordinates and maskVerticesYCoordinates.
% Important: imgOriginal can't be empty since MATLAB won't allow you to pass
% empty arrays as arguments.
% Inputs:
%	imgOriginal is either a 2D monochrome image or a 3D color image.
%	showImage = logical true if you want to display it.  showImage = false to not display.
% Outputs:
%	logBinaryMask is a 2D logical (binary) image of just the mask.
%	maskedImage is which has been set to zero outside of the mask.
function [maskedImage logBinaryMask] = MaskAndDisplayImage(handles, imgOriginal, showImage)
	% Declare our global variables so that this function can now see and access them.
	global maskVerticesXCoordinates;
	global maskVerticesYCoordinates;
	% Mask off the original with pre-selected mask, if they chose one.
	
	% Set up default return values.
	maskedImage = [];
	logBinaryMask = [];
	lowValue = 0;
	highValue = 255;
	
	% Gate check for valid values.
	if isempty(imgOriginal)
		% No image - nothing to do!
		return;
	end
	if isempty(maskVerticesXCoordinates) || isempty(maskVerticesYCoordinates)
		% No mask coordinates - nothing to mask.  Just display if requested, then exit.
		if showImage
			% Display masked image array in a window on the user interface.
			%axes(handles.axesImage);
			hold(handles.axesImage, 'off');	% Need to do this so the image will get updated and the image buffer won't be sized according to the last image that was in there.
			imshow(maskedImage, [lowValue highValue], 'parent', handles.axesImage);
		end
		return;
	end
	
	% If we get here, we have an image array passed in, and a valid mask.
	imageSize = size(imgOriginal);
	% Convert this outline into a binary image.
	imgMask = poly2mask(maskVerticesXCoordinates, maskVerticesYCoordinates, imageSize(1), imageSize(2));
	maskSize = size(imgMask);

	% Now, mask off image
	% First mask sure mask is the same size as the image.
	% Make sure the dimensions are the same.
	% Make sure masked image is the same size as the original.
	% You have to create it first with some definite size before you
	% can multiply it by a mask image.
	maskedImage = imgOriginal;
	if maskSize(1) ~= imageSize(1) || maskSize(1) ~= imageSize(1)
		% Dimensions of image and mask don't match.
		% Mask is the entire image - in essence, no mask.
% 		errorMessage = {'Cannot show mask over the color image because', 'the mask image dimensions do not match the image dimensions.', ['    Mask image dimensions: ' num2str(maskSize(1)) ' by ' num2str(maskSize(2))], ['    Color image dimensions: ' num2str(imageSize(1)) ' by ' num2str(imageSize(2))]};
% 		msgboxw(errorMessage);
		logBinaryMask  = ones(size(imgOriginal)) == 1;	% logical image.
	else
		% Image and mask image dimension match.
		if length(imageSize) >= 3
			numberOfColors = imageSize(3);
		else
			numberOfColors = 1;
		end
		if numberOfColors >= 2
			highRValue = max(imgOriginal(:,:,1));
			highGValue = max(imgOriginal(:,:,2));
			highBValue = max(imgOriginal(:,:,3));
			highValue = max([highRValue highGValue highBValue]);	% Max over all pixels in all color bands.
			for intColorBand = 1:numberOfColors
				maskedImage(:,:,intColorBand) = double(imgOriginal(:,:,intColorBand)) .* double(imgMask(:,:));
			end
		else
			% It's monochrome.
			lowValue = min(imgOriginal(:));
			highValue = max(imgOriginal(:));
			maskedImage = double(imgOriginal) .* double(imgMask);
		end
		logBinaryMask = imgMask > 0;	% logical image.
	end

	if showImage
		% Display masked image array in a window on the user interface.
		%axes(handles.axesImage);
		hold(handles.axesImage, 'off');	% Need to do this so the image will get updated and the image buffer won't be sized according to the last image that was in there.
		imshow(maskedImage, [lowValue highValue], 'parent', handles.axesImage);
	end
	return;
		
%=====================================================================
% --- Executes on button press in btnSelectAllOrNone.
function btnSelectAllOrNone_Callback(hObject, eventdata, handles)
% hObject    handle to btnSelectAllOrNone (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)
    % Find out button caption and take appropriate action.
    ButtonCaption = get(handles.btnSelectAllOrNone, 'string');
	if strcmp(ButtonCaption, 'Step 2:  Select All') == 1
        % Select all items in the listbox.
        % Need to find out how many items are in the listbox (both selected and
        % unselected).  It's quirky and inefficient but it's the only way I
        % know how to do it. 
        % First get the whole damn listbox text into a cell array.
        caListboxString = get(handles.lstImageList, 'string');
        NumberOfItems = length(caListboxString);    % Get length of that cell array.
        AllIndices=1:NumberOfItems; % Make a vector of all indices.
        % Select all indices.
        set(handles.lstImageList, 'value', AllIndices);
        % Finally, change caption to say "Select None"
        set(handles.btnSelectAllOrNone, 'string', 'Step 2:  Select None');
        % It scrolls to the bottom of the list.  Use the following line
        % if you want the first item at the top of the list.
        set(handles.lstImageList, 'ListboxTop', 1);
    else
        % Select none of the items in the listbox.
        set(handles.lstImageList, 'value', []);
        % Change caption to say Select All
        set(handles.btnSelectAllOrNone, 'string', 'Step 2:  Select All');
	end
	% Update the number of images in the Analyze button caption.
	UpdateAnalyzeButtonCaption(hObject, eventdata, handles)
	guidata(hObject, handles);


%=====================================================================
% --- Executes on button press in chkSendToExcel.
function chkSendToExcel_Callback(hObject, eventdata, handles)
% hObject    handle to chkSendToExcel (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)
% Hint: get(hObject,'Value') returns toggle state of chkSendToExcel
	checkboxState = get(hObject,'Value');
	message = sprintf('Now the results will be saved to an Excel workbook in folder\n%s', handles.ImageFolder);
	if checkboxState
		set(handles.txtInfo, 'string', message);
	else
		set(handles.txtInfo, 'string', 'Now the results will not be saved to an Excel workbook.');
	end


%=====================================================================
% --- Executes on button press in chkPauseAfterImage.
function chkPauseAfterImage_Callback(hObject, eventdata, handles)
% hObject    handle to chkPauseAfterImage (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)
% Hint: get(hObject,'Value') returns toggle state of chkPauseAfterImage
	checkboxState = get(hObject,'Value');
	if checkboxState
		set(handles.txtInfo, 'string', 'Now you will be able to inspect the results before it processes the next image.');
	else
		set(handles.txtInfo, 'string', 'Now the image(s) will be analyzed without pausing for you will be able to inspect the results in between images.');
	end


%=====================================================================
function GetImageMask(handles)
	% Make sure it's cleared out (work around of MATLAB feature).
	% MATLAB QUIRK.  MATLAB can remember maskVerticesXCoordinates and maskVerticesYCoordinates
	% prior runs of this macro even when you haven't clicked on the mask.
	% This is because if you don't erase it from the "global" workspace before you exit this m-file
	% then it will remain in the global workspace.
	% It will even remember it in the ImageList_Callback function even
	% though imgMask has never even been mentioned prior to it getting
	% there.  Clearing it here seems to fix everything.
	clear global maskVerticesXCoordinates;
	clear global maskVerticesYCoordinates;
	% Now redeclare them.  They should be empty at this point.
	% Declare both the logical mask image and the coordinates that created it.
	global maskVerticesXCoordinates;
	global maskVerticesYCoordinates;

	% Declare imgOriginal.  It might be filled with values here.
	global imgOriginal;	% Declare global so that other functions can see it, if they also declare it global.
	
    %fprintf('GetImageMask.4\n');

    % Clear any exiting polygon overlay that may be plotted on top of the image.
	%axes(handles.axesImage);
    %drawnow
	
    %fprintf('GetImageMask.4a\n');
	ClearLinesFromAxes(handles.axesImage);
	
	% Get the index of the entry they've selected.
    SelectedMask = get(handles.lstMasks, 'value');
	if ~isempty(imgOriginal)
		% An image is available.
		imageSize = size(imgOriginal);
		% Get mask coordinates: either derive them from the original image size,
		% or read them in from a disk file.
		if SelectedMask == 1 
			% They have selected the top entry: "Do not use a mask"
			% If they've selected an image, make the mask an array of all ones
			% the same size as the image.
			maskVerticesXCoordinates(1) = 0;
			maskVerticesXCoordinates(2) = 0;
			maskVerticesXCoordinates(3) = imageSize(2);
			maskVerticesXCoordinates(4) = imageSize(2);
			maskVerticesYCoordinates(1) = 0;
			maskVerticesYCoordinates(2) = imageSize(1);
			maskVerticesYCoordinates(3) = imageSize(1);
			maskVerticesYCoordinates(4) = 0;			
		else
			% They've selected a mask file.
			% Get the complete list of all mask file names listed,
			% whether selected or not.
			ListOfImageNames = get(handles.lstMasks, 'string');
			% Get the mask file name
			maskName = strcat(cell2mat(ListOfImageNames(SelectedMask)));

			% Get the full name of the mask coordinates file.
			[folder, baseFileName, ext] = fileparts(maskName);
			coordinatesFileName = fullfile(handles.maskFolder, [baseFileName '.mat']);
			load(coordinatesFileName, 'maskVerticesXCoordinates', 'maskVerticesYCoordinates');
			
			% Clip to mask coordinates to image boundaries.  Mask can't go outside the image.
			maskVerticesXCoordinates(maskVerticesXCoordinates > imageSize(2)) = imageSize(2);
			maskVerticesYCoordinates(maskVerticesYCoordinates > imageSize(1)) = imageSize(1);

			% Plot the mask as an outline over the image.
			hold on;
			plot(maskVerticesXCoordinates, maskVerticesYCoordinates, 'linewidth', 2);
		end
		        
	else
		% They've not clicked on an image yet.  
		% Just read in the mask coordinates and return.
		if SelectedMask == 1 
			% They have selected the top entry: "Do not use a mask"
			% If they've selected an image, make the mask an array of all ones
			% the same size as the image.
			return;
		else
			% They've selected a mask file but no image file.
			% Get the complete list of all mask file names listed,
			% whether selected or not.
			ListOfImageNames = get(handles.lstMasks, 'string');
			% Get the mask file name
			maskName = strcat(cell2mat(ListOfImageNames(SelectedMask)));

			% Get the full name of the mask coordinates file.
			[folder, baseFileName, ext] = fileparts(maskName);
			coordinatesFileName = fullfile(handles.maskFolder, [baseFileName '.mat']);
			load(coordinatesFileName, 'maskVerticesXCoordinates', 'maskVerticesYCoordinates');
			% Nothing more we can do since we don't have an image,
			% so just exit.
			return;
		end
	end
	return;
	
	
%=====================================================================
% Erases all lines from the image axes.  The current axes should be set first using the axes()
% command before this function is called, as it works from the current axes, gca.
function ClearLinesFromAxes(h)

    %fprintf('ClearLinesFromAxes.1\n');
	axesHandlesToChildObjects = findobj(h, 'Type', 'line');
    %fprintf('ClearLinesFromAxes.2\n');
	if ~isempty(axesHandlesToChildObjects)
		delete(axesHandlesToChildObjects);
	end
	return; % from ClearLinesFromAxes

	
%=====================================================================
% --- Executes on selection change in lstMasks.
% Displays mask on top of the displayed image, if imgOriginal is not empty.
% Auto-analyzes if that checkbox is checked.
function lstMasks_Callback(hObject, eventdata, handles)
% hObject    handle to lstMasks (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

% Hints: contents = get(hObject,'String') returns lstMasks contents as cell array
%        contents{get(hObject,'Value')} returns selected item from lstMasks
    
	% Read the image mask file and put the result into a logical mask image called imgMask.
	GetImageMask(handles);

	% These declarations need to be after GetImageMask because GetImageMask clears them
	% out and the association with this function is then lost.
	global imgOriginal;	% Every function that declares this variable global will be able to use and modity it.

	if ~isempty(imgOriginal)
		% Mask off the original with pre-selected mask, if they chose one.
		% Then display masked image and return it in array maskedImage and
		% return mask in logical array binaryMask
		[maskedImage logBinaryMask] = MaskAndDisplayImage(handles, imgOriginal, false);

		if get(handles.chkAutoAnalyze, 'Value') 
			% Analyze this image.
			AnalyzeSingleImage(handles, maskedImage, logBinaryMask);
		else
			set(handles.txtInfo, 'String', ' ');
		end
	end

    %axes(handles.axesImage);	% Switch current figure back to image box.
    guidata(hObject, handles);
    return


%=====================================================================
% --- Executes during object creation, after setting all properties.
function lstMasks_CreateFcn(hObject, eventdata, handles)
% hObject    handle to lstMasks (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    empty - handles not created until after all CreateFcns called

% Hint: listbox controls usually have a white background on Windows.
%       See ISPC and COMPUTER.
if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor','white');
end


%=====================================================================
% --- Executes on button press in btnDrawNewMask.
% Allows user to draw a polygon over image and then save the coordinates in a binary data file.
% (We could just as well use a mat format file.)
function btnDrawNewMask_Callback(hObject, eventdata, handles)
% hObject    handle to btnDrawNewMask (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

	% Declare our global variables in case they want to save them to a mat file.
	global maskVerticesXCoordinates;
	global maskVerticesYCoordinates;
	
    % Prompt user to draw a region on the image.
    uiwait(msgbox({'Draw a polygon over the image.' 'Right click the last vertex point to finish drawing.'}));
	
	% Erase all previous lines.
	ClearLinesFromAxes(handles.axesImage);
	
	% Get binary image that is a mask representing the region they drew.
    % binaryMask is an image of Logical data type.
    [binaryMask xCoords yCoords] = roipolyold;   % drop the binaryMask= and ; to see the binary image mask
%     imshow(binaryMask);

	% Plot the mask as an outline over the image.
	hold on;
	plot(xCoords, yCoords, 'linewidth', 2);

    reply = questdlg({'Do you want to save the mask', 'to disk as a template?'},'Save mask?', 'Yes','No', 'Yes');
    % Note: reply = '' for Upper right X, 'Yes' for Yes, 'No' for No.
    if strcmp(reply, 'Yes')
		defaultFileName = fullfile(handles.maskFolder, 'Polygon.mat');
		[fileName, folder, FilterIndex] = uiputfile(defaultFileName, 'Save mask file name');
		if FilterIndex == 0
			% If the user clicks the Cancel button, closes the dialog window, 
			% or if the file does not exist, FilterIndex is set to 0. 
			% Bail out in that case.
		    guidata(hObject, handles);
			return;
		end
		
		maskVerticesXCoordinates = xCoords;
		maskVerticesYCoordinates = yCoords;

		[folder, baseFileName, ext] = fileparts(fileName);
        % Save the mask as a PNG-format image for later recall if desired.
%         maskImageFileName = fullfile(handles.maskFolder, [baseFileName '.png']);
%         imwrite(binaryMask, maskImageFileName, 'png');
		
		% Write the coordinates out to a mat-format file.		
        coordinatesFileName = fullfile(handles.maskFolder, [baseFileName '.mat']);
		save(coordinatesFileName, 'maskVerticesXCoordinates', 'maskVerticesYCoordinates');

		% Refresh the listbox of mask filenames.
        % Load list of mask images in the app/masks folder.
        handles=LoadMaskList(handles);
    		
		% Now let's make sure that the one we just saved is the selected one.
		% First get the complete list of all mask file names listed, whether selected or not.
		ListOfImageNames = get(handles.lstMasks, 'string');
		numberOfMasks = length(ListOfImageNames);
		for maskNumber = 2:numberOfMasks
			% Get the mask file name
			maskName = strcat(cell2mat(ListOfImageNames(maskNumber)));
			if strcmpi(maskName, [baseFileName '.mat'])
				set(handles.lstMasks, 'value', maskNumber);	% Select first entry
				break;	% Skip the rest of the loop since we found it.
			end
		end
	end

    guidata(hObject, handles);
	return;


%=====================================================================
% --- Executes during object creation, after setting all properties.
% EVEN THOUGH THIS FUNCTION IS EMPTY, DON'T DELETE IT OR ERRORS WILL OCCUR
function axesPlot_CreateFcn(hObject, eventdata, handles)
% hObject    handle to axesPlot (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    empty - handles not created until after all CreateFcns called

% Hint: place code in OpeningFcn to populate axesPlot


%=====================================================================
% --- Executes during object creation, after setting all properties.
% EVEN THOUGH THIS FUNCTION IS EMPTY, DON'T DELETE IT OR ERRORS WILL OCCUR
function axesImage_CreateFcn(hObject, eventdata, handles)
% hObject    handle to axesImage (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    empty - handles not created until after all CreateFcns called

% Hint: place code in OpeningFcn to populate axesImage


%=====================================================================
% --- Executes during object creation, after setting all properties.
% EVEN THOUGH THIS FUNCTION IS EMPTY, DON'T DELETE IT OR ERRORS WILL OCCUR
function figMainWindow_CreateFcn(hObject, eventdata, handles)
% hObject    handle to figMainWindow (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    empty - handles not created until after all CreateFcns called

%=====================================================================
% --- Executes on mouse press over axes background.
function axesImage_ButtonDownFcn(hObject, eventdata, handles)
% hObject    handle to axesImage (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)
% IMNPORTANT NOTE: handles is not available for some strange reason
% when we get here as a result of clicking on the image.
% That's why we use "global" and not handles or getappdata (which also requires handles).
	fprintf(1, 'Just entered axesImage_ButtonDownFcn...\n');
    global baseImageFileName;
	if length(baseImageFileName) < 4 
		return;
	end
	% Retrieve the image folder, which was stored with the image object
	% when it was displayed with DisplayImage().
	imageFolder = getappdata(hObject, 'imageFolder');
	fullFileName = [imageFolder '/' baseImageFileName];
	if exist(fullFileName, 'file')
		hEnlargedImage = figure(1);		% Bring up a new window.
		hImage = imshow(fullFileName);
		% Make the figure fill up most of the screen.
		w = 0.96;
		h = 0.85;
		pos = [(1-w)/2, (1-h)/2, w, h]; % [left, bottom, width, height]
		% Apply to the GUI.
		set(hEnlargedImage, 'Units', 'Normalized');
		set(hEnlargedImage, 'Position', pos);
% 		disp(get(hImage,'Type'));
% 		disp(get(hEnlargedImage,'Type'));
% 		maximize(hEnlargedImage);
	else
		errorMessage = sprintf('The file does not exist:\n%s', fullFileName);
		WarnUser(errorMessage);
	end
	fprintf(1, 'Leaving axesImage_ButtonDownFcn.\n');
	return; % from axesImage_ButtonDownFcn

%=====================================================================
% --- Executes on button press in btnDeleteMask.
function btnDeleteMask_Callback(hObject, eventdata, handles)
% hObject    handle to btnDeleteMask (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)
	% Get the index of the entry they've selected.
    Selected = get(handles.lstMasks, 'value');
	if Selected == 1 
		% They have selected the top entry: "Do not use a mask"
		% If so, just return.
		msgboxw('You must select a mask file first.');
		return;
	end
	
	% Get the complete list of all mask image names listed,
	% whether selected or not.
    ListOfImageNames = get(handles.lstMasks, 'string');
    % Get the mask image name
    baseFileName = strcat(cell2mat(ListOfImageNames(Selected)));
    reply = questdlg({'Do you want to send this mask to the recycle bin?', handles.maskFolder, baseFileName},'Delete mask?', 'Yes','No', 'Yes');
    % reply = '' for Upper right X, 'Yes' for Yes, 'No' for No.
	if strcmp(reply, 'Yes')
		% Erase any existing, displayed mask.
		ClearLinesFromAxes(handles.axesImage);

        maskFullFileName = fullfile(handles.maskFolder, baseFileName);
		recycle('on')
		delete(maskFullFileName)
		% Refresh the listbox of mask filenames.
        % Load list of mask images in the app/masks folder.
		% QUIRK: need to set the selected index to something less than the
		% index at the end of the list otherwise you get an error.
		set(handles.lstMasks, 'value', 1);	% Select first entry
        handles=LoadMaskList(handles);
	end
    guidata(hObject, handles);
	return;

%=====================================================================
% --- Executes on button press in btnExit.
function btnExit_Callback(hObject, eventdata, handles)
% hObject    handle to btnExit (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)
	try
		fprintf(1, 'Just entered btnExit_Callback...\n');
		% Get rid of variables from the global workspace so that they aren't
		% hanging around the next time we try to run this m-file.
		clear global;
		% Cause it to shutdown.
		delete(handles.figMainWindow);
		fprintf(1, 'Leaving btnExit_Callback.\n');
	catch ME
		errorMessage = sprintf('Error in function btnExit_Callback.\nError Message:\n%s', ME.message);
		WarnUser(errorMessage);
	end


% --- Executes on button press in chkAutoAnalyze.
function chkAutoAnalyze_Callback(hObject, eventdata, handles)
% hObject    handle to chkAutoAnalyze (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)
% Hint: get(hObject,'Value') returns toggle state of chkAutoAnalyze
	checkboxState = get(hObject,'Value');
	if checkboxState
		set(handles.txtInfo, 'string', 'Now the image will be analyzed as soon as it is selected.');
	else
		set(handles.txtInfo, 'string', 'Now the image(s) will be analyzed only when the Analyze button is clicked.');
	end

	
% --- Executes on button press in btnMoveMask.
% Demonstrates how to call a modal dialog box and pass arguments to it and receive output variables
% back from it.
function btnMoveMask_Callback(hObject, eventdata, handles)
% hObject    handle to btnMoveMask (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

    Selected = get(handles.lstMasks, 'value');
	if Selected == 1 
		% They have selected the top entry: "Do not use a mask"
		% If so, just return.
		msgboxw('You must select a mask file first.');
		return;
	end
	global imgOriginal;
	global maskVerticesXCoordinates;
	global maskVerticesYCoordinates;
	[newMaskVerticesXCoordinates newMaskVerticesYCoordinates] = MoveMask(imgOriginal, maskVerticesXCoordinates, maskVerticesYCoordinates);
	maskVerticesXCoordinates = newMaskVerticesXCoordinates;
	maskVerticesYCoordinates = newMaskVerticesYCoordinates;
	% Delete old plot.
	%axes(handles.axesImage);
	h = findobj(handles.axesImage,'Type','line');
	if ~isempty(h)
		delete (h);
	end
	% Plot the new mask over the image.
	plot(maskVerticesXCoordinates, maskVerticesYCoordinates, 'linewidth', 2);
	return;

%=====================================================================
% Process image
% You can replace this contents of this function with whatever you want.
% Just delete it all and put your own code in here.
% For this simple tutorial, I simply thresholded the image (green band if it's a color image)
% according to MATLAB'S autothresholding method and got a distribution of blob sizes and plotted
% them.
function [resultsArray] = AnalyzeSingleImage(handles, maskedImage, binaryMask)
	try
	% Get the text in the edit text box.  (We don't do anything with it though - it's just for demo.)
	editTextContents = get(handles.edtEditText, 'String');
	editTextContents = editTextContents{:}; % Convert from cell array to string.
	fprintf(1, '%s\n', editTextContents);
	set(handles.txtInfo, 'String', editTextContents);
	
	resultsArray = zeros(1, 7);	% Initialize to make sure that something will always be returned.
	if length(size(maskedImage)) == 3
		% It's a color image.  Take the green band.
		originalImage = maskedImage(:, :, 2);
	else
		% It's a monochrome image.
		originalImage = maskedImage;
	end
    % Extract out all the pixels within the masked area so we can threshold the area within the mask.
	% The result will be a column vector, but we can still use it in graythresh().
	if isempty(binaryMask)
		% They want to use masking.
		pixelsInsideMask = uint8(maskedImage(binaryMask));   % Gives a column vector.
	else
		% They commented out the masking code.
		pixelsInsideMask = uint8(maskedImage(:));   % Gives a column vector.
	end
	thresholdLevel = graythresh(pixelsInsideMask) * 255;		% Find threshold according to Otsu's method.
	binaryImage = (originalImage >= thresholdLevel);       % Threshold to binary (a logical image)
	labeledImage = bwlabel(binaryImage, 8);     % Label each blob so can do calc on it
	% Show colored blobs just for fun.
	coloredLabels = label2rgb (labeledImage, 'hsv', 'k', 'shuffle'); % pseudo random color labels
	imshow(coloredLabels);
	drawnow; % Force an immediate update of the GUI.
	% Make the measurements
	blobMeasurements = regionprops(labeledImage, 'Area', 'Perimeter', 'PixelIdxList', 'Centroid');   % Get the blob properties of 'Area' and 'Perimeter'
	numberOfBlobs = size(blobMeasurements, 1);

	% bwboundaries returns a cell array, where each cell
	% contains the row/column coordinates for an object in the image.
	% Plot the borders of all the coins on the original
	% grayscale image using the coordinates returned by bwboundaries.
	hold on;
	boundaries = bwboundaries(binaryImage);	
	for k = 1 : numberOfBlobs
		thisBoundary = boundaries{k};
		plot(thisBoundary(:,2), thisBoundary(:,1), 'g', 'LineWidth', 2);
	end
	hold off;

	if numberOfBlobs > 1
		resultsArray = zeros(numberOfBlobs, 7);	% Pre-allocate space.
	end
	for blobNumber = 1 : numberOfBlobs           % Loop through all blobs.
		% Find the mean of each blob.  (R2008a has a better way where you can pass the original image
		% directly into regionprops.  The way below works for all versions including earlier versions.)
		thisBlobsPixels = blobMeasurements(blobNumber).PixelIdxList;  % Get list of pixels in current blob.
		meanGL = mean(originalImage(thisBlobsPixels));             % Find mean intensity (in original image!)
		blobArea = blobMeasurements(blobNumber).Area;		% Get area.
		blobPerimeter = blobMeasurements(blobNumber).Perimeter;		% Get perimeter.
		blobCentroid = blobMeasurements(blobNumber).Centroid;		% Get centroid.
		% Calculate the equivalent circular diameter - the diameter the blob would have it if were perfectly circular with the same area.
		blobECD = sqrt(4.0 * blobArea / pi);	
		resultsArray(blobNumber, 1) = blobNumber;
		resultsArray(blobNumber, 2) = blobArea;
		resultsArray(blobNumber, 3) = meanGL;
		resultsArray(blobNumber, 4) = blobPerimeter;
		resultsArray(blobNumber, 5) = blobCentroid(1);	% X coordinate.
		resultsArray(blobNumber, 6) = blobCentroid(2);	% Y coordinate.
		resultsArray(blobNumber, 7) = blobECD; % Equivalent Circular Diameter
	end
	% Set up the column headers.
	set(handles.uitable1, 'ColumnName', {'Blob #', 'Blob Area', 'Mean GL', 'Perimeter', 'Centroid X', 'Centroid Y', 'ECD'});
	% Stick the data ( a 2D numerical array) into the table.
	set(handles.uitable1, 'data', resultsArray);
	drawnow; % Force an immediate update of the GUI.

	% Read the radio button and sort according to what's selected.
	if get(handles.radOption1, 'value')
		indexToSortBy = -2;
		set(handles.pnlResults, 'title', 'Results sorted by area');
	elseif get(handles.radOption2, 'value')
		indexToSortBy = -3;
		set(handles.pnlResults, 'title', 'Results sorted by intensity');
	else
		indexToSortBy = -7;
		set(handles.pnlResults, 'title', 'Results sorted by ECD');
	end
	% Sort the array from largest blobArea to smallest blobArea (index #2).
	sortedResults = sortrows(resultsArray, indexToSortBy);
	% Put back the blob number in there (it got messed up during sorting).
	sortedResults(:, 1) = 1:numberOfBlobs;
	fprintf(1,'Blob #,    Area,     Mean GL,  Perimeter,  CentroidX,  CentroidY,   ECD\n');
	txtInfo = sprintf('Blob #      Area   Mean GL    Perimeter  CentroidX  CentroidY   ECD\n');
	for blobNumber = 1 : numberOfBlobs           % Loop through all blobs.
		% Print the values out to the command window.
		fprintf(1,'#%d %5.1f %11.1f %8.1f %8.1f %8.1f %8.1f\n', blobNumber, sortedResults(blobNumber, 2), sortedResults(blobNumber, 3), sortedResults(blobNumber, 4), sortedResults(blobNumber, 5), sortedResults(blobNumber, 6), sortedResults(blobNumber, 7));
		txtInfo = sprintf('%s#%d    %5.1f %11.1f %8.1f %8.1f %8.1f %8.1f\n', txtInfo, blobNumber, sortedResults(blobNumber, 2), sortedResults(blobNumber, 3), sortedResults(blobNumber, 4), sortedResults(blobNumber, 5), sortedResults(blobNumber, 6), sortedResults(blobNumber, 7));
	end
	set(handles.txtInfo, 'string', txtInfo);
	
    % Get a histogram of the blob ECD's within the masked area, and plot it.
	listOfECDs = sortedResults(:, 7);
	[PixelCounts, GLs] = PlotHistogram(handles, listOfECDs);

	catch ME
		% Some error happened if you get here.
		strErrorMessage = sprintf('Error in function mnuToolsSaveScreenshot_Callback\n\nError message: %s', ME.message);
		WarnUser(strErrorMessage);
	end
	return;  % AnalyzeSingleImage
	
%=====================================================================
% Takes histogram of 1-D array blobSizeArray, and plots it.
function [blobCounts, areaValues] = PlotHistogram(handles, blobSizeArray)
	% Get a histogram of the blobSizeArray and display it in the histogram viewport.
	numberOfBins = min([100 length(blobSizeArray)]);
	[blobCounts, areaValues] = hist(blobSizeArray, numberOfBins);
	% Plot the number of blobs with a certain area versus that area.
	axes(handles.axesPlot);
	bar(areaValues, blobCounts);
	title('Histogram of Blob Sizes');
	return;


%=====================================================================
function CenterFigure(handles)
	% The figure Position property
	% does not include the window borders, so this example uses a width of 5 pixels
	% on the sides and bottom and 30 pixels on the top.
	borderWidth = 5;
	titleBarWidth = 30;
	% Ensure root units are pixels and get the size of the screen:
	set(0, 'Units', 'pixels');
	set(handles.figMainWindow, 'Units', 'pixels');
	% Get the screen size in pixels.
	screenSize = get(0,'ScreenSize');
	% Get the size of the window.
	initialFigurePosition = get(handles.figMainWindow, 'Position');
	% Create an array that will center it.
	centeredX = (screenSize(3) - initialFigurePosition(3)) / 2;
	centeredY = (screenSize(4) - initialFigurePosition(4)) / 2;
	centeredPosition  = [centeredX,... 
		centeredY,...
		initialFigurePosition(3),...
		initialFigurePosition(4)];
	set(handles.figMainWindow, 'Position', centeredPosition);
	return; % from CenterFigure()


% --- Executes when selected object is changed in grpRadButtonGroup.
function grpRadButtonGroup_SelectionChangeFcn(hObject, eventdata, handles)
% hObject    handle to the selected object in grpRadButtonGroup 
% eventdata  structure with the following fields (see UIBUTTONGROUP)
%	EventName: string 'SelectionChanged' (read only)
%	OldValue: handle of the previously selected object or empty if none was selected
%	NewValue: handle of the currently selected object
% handles    structure with handles and user data (see GUIDATA)
	switch get(eventdata.NewValue,'Tag') % Get Tag of selected object.
		case 'radOption1'
			% Code for when radiobutton1 is selected.
			txtInfo = sprintf('Option 1 is selected and the others are deselected.\n\nResults will be sorted in order of decreasing area.');
			set(handles.pnlResults, 'title', 'Results sorted by area');
		case 'radOption2'
			% Code for when radiobutton2 is selected.
			txtInfo = sprintf('Option 2 is selected and the others are deselected.\n\nResults will be sorted in order of decreasing intensity.');
			set(handles.pnlResults, 'title', 'Results sorted by intensity');
		case 'radOption3'
			% Code for when togglebutton1 is selected.
			txtInfo = sprintf('Option 3 is selected and the others are deselected.\n\nResults will be sorted in order of decreasing ECD.');
			set(handles.pnlResults, 'title', 'Results sorted by ECD');
% 		case 'togglebutton2'
			% Code for when togglebutton2 is selected.
		% Continue with more cases as necessary.
		otherwise
			% Code for when there is no match.
	end
	set(handles.txtInfo, 'String', txtInfo);
	return; % from grpRadButtonGroup_SelectionChangeFcn


%=============================================================================================
% --- Executes on slider movement.
function sldHorizontalScrollbar_Callback(hObject, eventdata, handles)
% hObject    handle to sldHorizontalScrollbar (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)
% Hints: get(hObject,'Value') returns position of slider
%        get(hObject,'Min') and get(hObject,'Max') to determine range of slider
	scrollbarValue = get(hObject,'Value');
	caption = sprintf('H value = %.2f', scrollbarValue);
	set(handles.txtHScrollbar, 'string', caption);


%=============================================================================================
% --- Executes during object creation, after setting all properties.
function sldHorizontalScrollbar_CreateFcn(hObject, eventdata, handles)
% hObject    handle to sldHorizontalScrollbar (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    empty - handles not created until after all CreateFcns called

% Hint: slider controls usually have a light gray background.
if isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor',[.9 .9 .9]);
end


%=============================================================================================
% --- Executes on slider movement.
function sldVerticalScrollbar_Callback(hObject, eventdata, handles)
% hObject    handle to sldVerticalScrollbar (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)
% Hints: get(hObject,'Value') returns position of slider
%        get(hObject,'Min') and get(hObject,'Max') to determine range of slider
	scrollbarValue = get(hObject,'Value');
	caption = sprintf('V value = %.2f', scrollbarValue);
	set(handles.txtVScrollbar, 'string', caption);

%=============================================================================================
% --- Executes during object creation, after setting all properties.
function sldVerticalScrollbar_CreateFcn(hObject, eventdata, handles)
% hObject    handle to sldVerticalScrollbar (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    empty - handles not created until after all CreateFcns called
% Hint: slider controls usually have a light gray background.
if isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor',[.9 .9 .9]);
end


%=============================================================================================
% --- Executes on mouse motion over figure - except title and menu.
function figMainWindow_WindowButtonMotionFcn(hObject, eventdata, handles)
% hObject    handle to figMainWindow (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)



%=============================================================================================
function edtEditText_Callback(hObject, eventdata, handles)
% hObject    handle to edtEditText (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

% Hints: get(hObject,'String') returns contents of edtEditText as text
%        str2double(get(hObject,'String')) returns contents of edtEditText as a double


%=============================================================================================
% --- Executes during object creation, after setting all properties.
function edtEditText_CreateFcn(hObject, eventdata, handles)
% hObject    handle to edtEditText (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    empty - handles not created until after all CreateFcns called

% Hint: edit controls usually have a white background on Windows.
%       See ISPC and COMPUTER.
if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor','white');
end


%=============================================================================================
function mnuToolsSaveScreenshot_Callback(hObject, eventdata, handles)
% hObject    handle to mnuToolsSaveScreenshot (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)
% IMPORTANT NOTE: YOU MUST HAVE DOWNLOADED import_fig()
% FROM THE FILE EXCHANGE FOR THIS FUNCTION TO WORK.
	try
		% Save the figure
		% Have user browse for a file, from a specified "starting folder."
		% For convenience in browsing, set a starting folder from which to browse.
		startingFolder = 'C:\Program Files\MATLAB';
		if ~exist(startingFolder, 'dir')
			% If that folder doesn't exist, just start in the current folder.
			startingFolder = pwd;
		end
		% Get the name of the color image file that the user wants to save this figure into.
		defaultFileName = fullfile(startingFolder, 'MAGIC_Screenshot.png');
		[baseFileName, folder] = uiputfile(defaultFileName, 'Specify a file for your screenshot');
		if baseFileName == 0
			% User clicked the Cancel button.
			return;
		end
		screenshotFileName = fullfile(folder, baseFileName);
		% Display a message on the UI.
		txtInfo = sprintf('Please wait...\nSaving screenshot as\n%s', screenshotFileName);
		set(handles.txtInfo, 'String', txtInfo);
		% CALL export_fig, WHICH YOU MUST HAVE DOWNLOADED.
		export_fig(screenshotFileName);
		message = sprintf('Screenshot saved as:\n%s', screenshotFileName);
		set(handles.txtInfo, 'String', message);
		msgboxw(message);
	catch ME
		% Some error happened if you get here.
		strErrorMessage = sprintf('Error in function mnuToolsSaveScreenshot_Callback\n\nError message: %s', ME.message);
		WarnUser(strErrorMessage);
		WarnUser('Are you sure you downloaded export_fig, and it is on the search path?');
	end
	return; % from mnuToolsSaveScreenshot_Callback


%=============================================================================================
function mnuToolsMontage_Callback(hObject, eventdata, handles)
% hObject    handle to mnuToolsMontage (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)
	try
		% Change mouse pointer (cursor) to an hourglass.  
		% QUIRK: use 'watch' and you'll actually get an hourglass not a watch.
		set(gcf,'Pointer','watch');
		% Get a list of all the filenames.
		ListOfImageNames = get(handles.lstImageList, 'string');
		% Get a list of what files they selected.
		selectedItems = get(handles.lstImageList, 'value');
		% If none are selected, use them all.
		numberOfSelectedImages = length(selectedItems);
		if numberOfSelectedImages <= 1
			numberOfSelectedImages = length(ListOfImageNames);
			selectedItems = 1 : numberOfSelectedImages;
		end
		caption = sprintf('Please wait...Constructing montage of %d images...', numberOfSelectedImages);
		title(caption, 'FontSize', 14);
		set(handles.txtInfo, 'string', caption);
		drawnow;	% Cursor won't change right away unless you do this.
		% Get a list of the selected files only.
		% Warning: This will not include folders so we will have to prepend the folder.
		ListOfImageNames = ListOfImageNames(selectedItems);
		for k = 1 : numberOfSelectedImages    % Loop though all selected indexes.
			% Get the filename for this selected index.
			baseImageFileName = cell2mat(ListOfImageNames(k));
			imageFullFileName = fullfile(handles.ImageFolder, baseImageFileName);
			ListOfImageNames{k} = imageFullFileName;
		end
		% Figure out how many rows and there should be.
		% There is twice as much space horizontally as vertically so solve the equation 2*rows^2 = numberOfImages.
		rows = ceil(sqrt(numberOfSelectedImages/2));
		columns = 2 * rows;
		axes(handles.axesImage);
		cla(handles.axesImage, 'reset');
		handleToImage = handles.axesImage;
		hMontage = montage(ListOfImageNames, 'size', [rows columns]);
		caption = sprintf('Displaying a montage of %d images.', numberOfSelectedImages);
		title(caption, 'FontSize', 20);
		set(handles.txtInfo, 'string', caption);
	catch ME
		errorMessage = sprintf('Error in function mnuToolsMontage_Callback.\nError Message:\n%s', ME.message);
		set(handles.txtInfo, 'string', errorMessage);
		LoadSplashImage(handles, handles.axesImage);
		WarnUser(errorMessage);
	end
	% Change mouse pointer (cursor) to an arrow.
	set(gcf,'Pointer','arrow');
	drawnow;	% Cursor won't change right away unless you do this.

	return; % from mnuToolsMontage_Callback


%=====================================================================
% Plots the histogram of grayImage in axes axesImage
% If grayImage is a double, it must be normalized between 0 and 1.
function [minGL maxGL ] = PlotImageHistogram(handles, imageArray)
	try
		numberOfDimensions = ndims(imageArray);
		if numberOfDimensions == 1 || numberOfDimensions == 2
			% Grayscale 2D or 1D image.
			PlotGrayscaleHistogram(handles, imageArray)
		elseif numberOfDimensions == 3
			% True color RGB image.
			PlotRGBHistograms(handles, imageArray);
		else
			warningMessage = sprintf('Histograms are only supported for grayscale (2D) or color (3D) images');
			WarnUser(warningMessage);
		end
	catch ME
		errorMessage = sprintf('Error in function PlotImageHistogram.\nError Message:\n%s', ME.message);
		WarnUser(errorMessage);
	end
	return; % PlotImageHistogram

%=====================================================================
% Plots the histogram of grayImage in axes axesImage
% If grayImage is a double, it must be normalized between 0 and 1.
function [minGL maxGL] = PlotGrayscaleHistogram(handles, grayImage)
	try
		% Plot the histogram in the histogram viewport.
		% Make the plot visible.
		SetImageVisibility(handles, handles.axesPlot, 1);
		axes(handles.axesPlot);  % makes existing axes handles.axesPlot the current axes.
		
		% Get the histogram
		[pixelCounts, grayLevels] = imhist(grayImage);
		
		% Plot it.
		bar(pixelCounts);
		title('Histogram of Gray Image', 'FontSize', 12);
		grid on;
		
		% Get the min and max GL for fun.
		minGL = min(grayImage(:));
		maxGL = max(grayImage(:));
		
		% Set up the x axis of the histogram to go from 0 to the max value allowed for this type of integer (uint8 or uint16).
		theClass = class(grayImage);
		% Only set xlim for integers.  For example the demo image blobs.png is logical not integer even though it's grayscale.
		if strfind(theClass, 'int')
			xlim([0 intmax(theClass)]);
		end
	catch ME
		errorMessage = sprintf('Error in function PlotGrayscaleHistogram.\nError Message:\n%s', ME.message);
		WarnUser(errorMessage);
	end
	return; % PlotGrayscaleHistogram

%=====================================================================
% Plots the histogram of imgColorImageArray in axes axesImage
% If imgArray is a double, it must be normalized between 0 and 1.
function [minGL maxGL gl1Percentile gl99Percentile] = PlotRGBHistograms(handles, imgColorImageArray)
	try
		% Get a histogram of the entire image.  But imhist only allows 2D images, not color ones.
		% First get individual channels from the original image.
		redBand = imgColorImageArray(:,:,1);
		greenBand = imgColorImageArray(:,:,2);
		blueBand = imgColorImageArray(:,:,3);

		% Use 256 bins.
		[redCounts, redGLs] = imhist(redBand, 256);    % make sure you label the axes after imhist because imhist will destroy them.
		[greenCounts, greenGLs] = imhist(greenBand, 256);    % make sure you label the axes after imhist because imhist will destroy them.
		[blueCounts, blueGLs] = imhist(blueBand, 256);    % make sure you label the axes after imhist because imhist will destroy them.

		% GLs goes from 0 (at element 1) to 255 (at element 256) but only some
		% these bins have data in them.  The upper ones may be 0.  Find the last
		% non-zero bin so we can plot just up to there to get better horizontal resolution.
		maxBinUsedR = find(redCounts, 1, 'last' );
		maxBinUsedG = find(greenCounts, 1, 'last' );
		maxBinUsedB = find(blueCounts, 1, 'last' );
		% If the entire image is zero, have the max bin be 0.
		if isempty(maxBinUsedR); maxBinUsedR = 1; end;
		if isempty(maxBinUsedG); maxBinUsedG = 1; end;
		if isempty(maxBinUsedB); maxBinUsedB = 1; end;
		% Take the largest one overall for plotting them all.
		maxBinUsed = max([maxBinUsedR maxBinUsedG maxBinUsedB]);

		% Get subportion of array that has non-zero data.
		redCounts = redCounts(1:maxBinUsed);
		greenCounts = greenCounts(1:maxBinUsed);
		blueCounts = blueCounts(1:maxBinUsed);
		GLs = redGLs(1:maxBinUsed);

		% Because of the mask, the first bin will have a ton of zeros in it and count(1) will be sky
		% high.  Zero out this bin so we aren't getting the histogram of the mask.
		redCounts(1) = 0;
		greenCounts(1) = 0;
		blueCounts(1) = 0;

		% Assign the max and min for the 3 color bands.
		minGL(1) = GLs(maxBinUsedR);
		maxGL(2) = GLs(maxBinUsedG);
		maxGL(3) = GLs(maxBinUsedB);

		% Calculate the 1% and 99% value of the CDF for the 3 color bands.
		gl1Percentile = ones(3, 1);	    % Preallocate one element for each color.
		gl99Percentile = ones(3, 1);		% Preallocate one element for each color.
		for color = 1:3
			switch color
				case 1
					counts = redCounts;
				case 2
					counts = greenCounts;
				case 3
					counts = blueCounts;
			end
			summed = sum(counts);
			cdf = 0;
			for bin = 1 : maxBinUsed
				cdf = cdf + counts(bin);
				if cdf < 0.01 * summed
					gl1Percentile(color) = GLs(bin);
				end
				if cdf > 0.99 * summed
					break;
				end
			end
			gl99Percentile(color) = GLs(bin);
		end

		% Plot the histogram in the histogram viewport.
		% Make the plot visible.
		SetImageVisibility(handles, handles.axesPlot, 1);
		axes(handles.axesPlot);  % makes existing axes handles.axesPlot the current axes.
		% Plot the histogram as a line curve.
		plot(GLs, blueCounts, 'b', 'LineWidth', 3);
		hold on;
		plot(GLs, greenCounts, 'g', 'LineWidth', 3);
		plot(GLs, redCounts, 'r', 'LineWidth', 3);
		grid on;
		title('Red, Green, and Blue Histograms');
		maxCountInHistogram = max([redCounts(:); greenCounts(:); blueCounts(:)]);
		set(handles.axesPlot,'YLim',[0 maxCountInHistogram]);
		
		% Plot the sum of all of them
% 		sumCounts = redCounts + greenCounts + blueCounts;
% 		plot(GLs, sumCounts, 'k', 'LineWidth', 1);  % Plot sum in black color.
% 		set(handles.axesPlot,'YLim',[0 max(sumCounts)]);
		
		set(handles.axesPlot,'XTick',      [0 20 40 60 80 100 120 140 160 180 200 220 240 255]);
		set(handles.axesPlot,'XTickLabel', {0 20 40 60 80 100 120 140 160 180 200 220 240 255});
		set(get(handles.axesPlot,'XLabel'),'string','Gray Level');
		ylabel('# of Pixels');
		xlim([0 255]);
		hold off;

		axes(handles.axesImage);	% Switch current figure back to image box.
	catch ME
		errorMessage = sprintf('Error in function PlotRGBHistograms.\nError Message:\n%s', ME.message);
		WarnUser(errorMessage);
	end
	return; % PlotRGBHistograms


%=============================================================================================
function mnuTools_Callback(hObject, eventdata, handles)
% hObject    handle to mnuTools (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

%=============================================================================================
% Pops up a message box and waits for the user to click OK.
function msgboxw(in_strMessage)
    uiwait(msgbox(in_strMessage));
    return;

%==========================================================================================================================
% Warn user via the command window and a popup message.
function WarnUser(warningMessage)
	fprintf(1, '%s\n', warningMessage);
	uiwait(warndlg(warningMessage));
	return; % from WarnUser()


% --- Executes on button press in chkPlotHistograms.
function chkPlotHistograms_Callback(hObject, eventdata, handles)
% hObject    handle to chkPlotHistograms (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

% Hint: get(hObject,'Value') returns toggle state of chkPlotHistograms

Contact us