Watershed Demo
An example using the watershed algorithm.
This program demonstrates the famous watershed segmentation algorithm in OpenCV.
Sources:
function varargout = watershed_demo_gui(im) % load an image if nargin < 1 src = imread(fullfile(mexopencv.root(),'test','fruits.jpg')); elseif isempty(im) fmts = imformats(); filtspec = strjoin(strcat('*.', [fmts.ext]), ';'); [fn,fp] = uigetfile(filtspec, 'Select an image'); if fp==0, error('No file selected'); end src = imread(fullfile(fp,fn)); elseif ischar(im) src = imread(im); else src = im; end % we expect an 8-bit RGB image validateattributes(src, {'uint8'}, ... {'ndims',3, 'size',[nan nan 3], 'nonempty'}); % initialize app state, and create the UI app = initApp(src); h = buildGUI(app); % hook event handlers opts = {'Interruptible','off', 'BusyAction','cancel'}; set(h.btn(1), 'Callback',@onHelp); set(h.btn(2), 'Callback',@onReset); set(h.btn(3), 'Callback',@onSegment, opts{:}); set(h.fig, 'WindowKeyPressFcn',@onType, ... 'WindowButtonDownFcn',@onMouseDown, opts{:}); % return graphics handles if nargout > 0, varargout{1} = h; end % ========== Event Handlers ========== function onHelp(~,~) %ONHELP Display usage help dialog helpdlg({ 'Hot keys:' 'ESC - quit the program' 'r - restore the original image' 'SPACE - run watershed segmentation algorithm' '(before running it, roughly mark the areas to segment on' 'the image)' '(before that, roughly outline several markers on the image)' }); end function onReset(~,~) %ONRESET Event handler for reset button app.markerMask(:) = 0; app.pts = zeros(0,2); set(h.img(1), 'CData',app.img0); set(h.img(2), 'CData',app.markerMask); set(h.line, 'XData',NaN, 'YData',NaN); drawnow; end function onSegment(~,~) %ONSEGMENT Event handler for segment button % check markers were specified if isempty(app.pts) disp('markers are not set'); return; end % find connected components in markers mask [contours, hierarchy] = cv.findContours(app.markerMask, ... 'Mode','CComp', 'Method','Simple'); if isempty(contours), return; end % iterate through all the top-level contours, and % draw each connected component with its own label markers = zeros(size(app.markerMask), 'int32'); compCount = 0; idx = 0; while idx >= 0 markers = cv.drawContours(markers, contours, ... 'Hierarchy',hierarchy, 'ContourIdx',idx, ... 'Color',repmat(compCount+1,1,3), 'Thickness','Filled'); idx = hierarchy{idx+1}(1); compCount = compCount + 1; end if compCount == 0, return; end % segment using the markers tic markers = cv.watershed(app.img0, markers); toc % paint the watershed image % (-1: boudaries, 0: unlabeled, 1~N: labeled regions) colorTab = [1 1 1; 0 0 0; rand([compCount 3])]; wshed = uint8(ind2rgb(double(markers+2), colorTab) * 255); % show result if mexopencv.require('images') wshed = imlincomb(0.5, wshed, 0.5, app.imgGray, 'uint8'); else wshed = cv.addWeighted(wshed, 0.5, app.imgGray, 0.5, 0); end set(h.img(2), 'CData',wshed); drawnow; end function onType(~,e) %ONTYPE Event handler for key press on figure % handle keys switch e.Key case {'q', 'escape'} close(h.fig); return; case 'h' onHelp([],[]); case 'r' onReset([],[]); case {'w', 'space'} onSegment([],[]); end end function onMouseDown(~,~) %ONMOUSEDOWN Event handler for mouse down on figure % ignore anything but left mouse clicks if ~strcmp(get(h.fig,'SelectionType'), 'normal') return; end % attach event handlers, and change mouse pointer set(h.fig, 'Pointer','circle', ... 'WindowButtonMotionFcn',@onMouseMove, ... 'WindowButtonUpFcn',@onMouseUp); % start a new polyline onMouseMove([],[]); end function onMouseMove(~,~) %ONMOUSEMOVE Event handler for mouse move on figure % get current point and append it app.pts(end+1,:) = getCurrentPoint(); % update graphic line set(h.line, 'XData',app.pts(:,1), 'YData',app.pts(:,2)); drawnow; end function onMouseUp(~,~) %ONMOUSEUP Event handler for mouse up on figure % detach event handlers, and restore mouse pointer set(h.fig, 'Pointer','arrow', ... 'WindowButtonMotionFcn','', ... 'WindowButtonUpFcn',''); % mark end of polyline app.pts(end+1,:) = NaN; %TODO: improve by only drawing the new polyline points, % no need to re-draw old ones % divide sequences of points separated by NaNs idx = all(isnan(app.pts), 2); % rows with NaN lens = diff([0; find(idx)]) - 1; % length of each run of points pts = mat2cell(app.pts(~idx,:), lens, 2); % cell array of points % fill mask by drawing polylines onto it app.markerMask(:) = 0; app.markerMask = cv.polylines(app.markerMask, pts, ... 'Closed',false, 'Color',255, 'Thickness',5); end % ========== Helper Functions ========== function p = getCurrentPoint() % retrieve current mouse location p = get(h.ax(1), 'CurrentPoint'); p = p(1,1:2); % clamp to within image coordinates p = max(p, [1 1]); p = min(p, [app.sz(2) app.sz(1)]); end end % ========== Initializer functions ========== function app = initApp(img) %INITAPP Initialize app state app = struct(); app.img0 = img; app.imgGray = repmat(rgb2gray(img), [1 1 3]); app.sz = size(img); app.markerMask = zeros(size(img,1), size(img,2), 'uint8'); app.pts = zeros(0,2); end function h = buildGUI(app) %BUILDGUI Creates the UI % parameters sz = app.sz; sz(2) = max(sz(2), 200); % minimum figure width % build the user interface (no resizing to keep it simple) h = struct(); h.fig = figure('Name','Watershed Demo', ... 'NumberTitle','off', 'Menubar','none', 'Resize','off', ... 'Position',[200 200 sz(2)*2 sz(1)+29]); if ~mexopencv.isOctave() %HACK: not implemented in Octave movegui(h.fig, 'center'); end h.ax(1) = axes('Parent',h.fig, ... 'Units','pixels', 'Position',[1 30 sz(2) sz(1)]); h.ax(2) = axes('Parent',h.fig, ... 'Units','pixels', 'Position',[sz(2)+1 30 sz(2) sz(1)]); if ~mexopencv.isOctave() h.img(1) = imshow(app.img0, 'Parent',h.ax(1)); h.img(2) = imshow(app.markerMask, 'Parent',h.ax(2)); else %HACK: https://savannah.gnu.org/bugs/index.php?45473 axes(h.ax(1)); h.img(1) = imshow(app.img0); axes(h.ax(2)); h.img(2) = imshow(app.markerMask); end h.btn(1) = uicontrol('Parent',h.fig, 'Style','pushbutton', ... 'Position',[5 5 60 20], 'String','Help'); h.btn(2) = uicontrol('Parent',h.fig, 'Style','pushbutton', ... 'Position',[70 5 60 20], 'String','Reset'); h.btn(3) = uicontrol('Parent',h.fig, 'Style','pushbutton', ... 'Position',[135 5 60 20], 'String','Segment'); % initialize line h.line = line(NaN, NaN, 'Color','w', 'Parent',h.ax(1), 'LineWidth',5); end