function markMovesInCamera(videoSource, moveTresh, strelOpen, strelClose)
%% function markMovesInCamera(inVideo, moveTresh, strelOpen, strelClose)
% This function tracks and presents moving regions in a video filmed by a
% still USB camera
%
%% Syntax
% markMovesInCamera(inVideo);
% markMovesInCamera(inVideo, moveTresh);
% markMovesInCamera(inVideo, moveTresh, strelOpen);
% markMovesInCamera(inVideo, moveTresh, strelOpen, strelClose);
%
%% Description
% This functions presents a figure with only moving regions of the datya
% filmed by a camera. The user can control several parameters, to achive
% the presentation of regions he is interested in. This function can be
% though of as a mammal vision system simulation (some mammals, like dogs,
% see only object that move).
%
%% Input arguments (defaults exist):
% videoSource- vidoe data device (usually USB camera)
% moveTresh- used to deetct movements. Pixels with value above this
% treshold are assumed to include movements.
% strelOpen- a structuring element, and integer value used to define
% a structuring element dimentions, or a matrix of logicals used to
% define the presneted ROI dimentions. In other worlds defines the
% area around detected movements to be presented. Larger value will
% resul in larger region.
% strelClose- a structuring element, and integer value used to define
% a structuring element dimentions, or a matrix of logicals. Those
% define the speed of "aging", or hsitorical data influencue on
% current figure. It defines how the ROI of previous frame infleuemce
% ROI of current frame. Larger value will result in fatser aging- old
% ROI data will be removed fatser. Small value will result in slow
% "aging"- areas with movements in previous frames, will be preserved
% and presenetd in longer period.
%
%% Output arguments
% None. Movements are presenetd on a figure;
%
%% Issues & Comments
% Camera is assumed to be still.
% Sometimes, whole frame is detceted as moving. Seems like a camera
% issue...
% Function is actually a combintion of IMAQMOTION by David Tarkowski (many
% thanks and big respect to the author. See
% http://www.mathworks.com/matlabcentral/fileexchange/5470-imaqmotion-image-acquisition-motion-detection
% and my fuction markMovesInVideo.
%
%% Example
% videoSource=videoinput('winvideo', 1);
% moveTresh=30;
% strelOpen=2;
% strelClose=2;
% markMovesInCamera(videoSource, moveTresh, strelOpen, strelClose);
%
%
%% See also
% IMAQMOTION
% VIDEOINPUT
% markMovesInVideo
%
%% Revision history
% First version: Nikolay S. 2011-10-21.
% Last update: Nikolay S. 2011-10-24.
%
% *List of Changes:*
%
%% Default parameters
if nargin<4
strelClose=2;
if nargin<3
strelOpen=2;
if nargin<2
moveTresh=35;
if nargin==0
error('MATLAB:markMovesInCamera:error',...
'Input source definition is missing.')
end
end
end
end
if isnumeric(strelOpen)
strelOpen = strel('disk', strelOpen , 0);
elseif ~(strcmpi(class(strelOpen), 'strel') || islogical(strelOpen))
error('MATLAB:markMovesInCamera:error',...
'Bad strelOpen input.')
end
if isnumeric(strelClose)
strelClose = strel('disk', strelClose , 0);
elseif ~(strcmpi(class(strelClose), 'strel') || islogical(strelClose))
error('MATLAB:markMovesInCamera:error',...
'Bad strelClose input.')
end
appTitle = 'Camera Motion Detector';
%% Almost unchanged code adopted from IMAQMOTION.
% My thanks and bif respect to the author: David Tarkowski.
% See http://www.mathworks.com/matlabcentral/fileexchange/5470-imaqmotion-image-acquisition-motion-detection
try
% Make sure we've stopped so we can set up the acquisition.
stop(videoSource);
% Configure the video input object to continuously acquire data.
triggerconfig(videoSource, 'manual');
set(videoSource, 'Tag', appTitle, 'FramesAcquiredFcnCount', 1, ...
'TimerFcn', @localFrameCallback, 'TimerPeriod', 0.1,...
'ReturnedColorSpace','rgb');
% set colormap colormap('default'),'colorcube','hsv' or ycbcr2rgb for
% YUV
% Check to see if this object already has an associated figure.
% Otherwise create a new one.
ud = get(videoSource, 'UserData');
if ~isempty(ud) && isstruct(ud) && isfield(ud, 'figureHandles') ...
&& ishandle(ud.figureHandles.hFigure)
appdata.figureHandles = ud.figureHandles;
figure(appdata.figureHandles.hFigure)
else
appdata.figureHandles = localCreateFigure(videoSource, appTitle);
end
% Store the application data the video input object needs.
% prepare motion detectin, aging and region conenction parameters
appdata.background = [];
appdata.prevMoveMask=false(fliplr(get(videoSource,'VideoResolution')));
appdata.moveTresh=moveTresh;
appdata.strelOpen=strelOpen;
appdata.strelClose=strelClose;
videoSource.UserData = appdata;
% Start the acquisition.
start(videoSource);
% Avoid peekdata warnings in case it takes too long to return a frame.
warning off imaq:peekdata:tooManyFramesRequested
catch
% Error gracefully.
stop(videoSource);
% delete(videoSource);
error('MATLAB:imaqmotion:error', ...
sprintf('IMAQMOTION is unable to run properly.\n%s', lasterr))
end
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function localFrameCallback(vid, event)
% Executed by the videoinput object callback
% to update the image display.
% If the object has been deleted on us,
% or we're no longer running, do nothing.
if ~isvalid(vid) || ~isrunning(vid)
return;
end
% Access our application data and parameters
appdata = get(vid, 'UserData');
background = appdata.background;
moveTresh=appdata.moveTresh;
strelOpen=appdata.strelOpen;
strelClose=appdata.strelClose;
prevMoveMask=appdata.prevMoveMask;
% Peek into the video stream. Since we are only interested
% in processing the current frame, not every single image
% frame provided by the device, we can flush any frames in
% the buffer.
frame = peekdata(vid, 1);
if isempty(frame),
return;
end
flushdata(vid);
% First time through, a background image will be needed.
if isempty(background),
background = getsnapshot(vid);
end
I = imabsdiff(frame, background);
% set(figData.hImage, 'CData', I);
% Update the patch to the new level value.
graylevel = graythresh(I);
level = max(moveTresh, floor(100*graylevel));
currMoveMask=false(size(I, 1), size(I, 2)); % Init current movements mask
nClrs=size(I, 3);
% Detect movement on every color (operation on intensity/grayscale data is
% also an option)
for iClr=1:nClrs
currMoveMask=currMoveMask | (I(:, :, iClr)>level);
end
% Apply "aging" to historical data via Morpohological Erode operator
prevMoveMask= imerode(prevMoveMask, strelClose);
% Aging is impossible if whole mask is true, prevent that.
if all(prevMoveMask)
prevMoveMask=false(size(prevMoveMask));
end
currMoveMask= imdilate(currMoveMask, strelOpen); % Connect isolated
% movement elemnets to larger regions via Morpohological Dilate operator
currMoveMask=currMoveMask | prevMoveMask; % Combine previous and current masks
% Crop only relevant parts of image
image2Show=0*frame;
currMoveMask3D=repmat(currMoveMask, [1, 1, nClrs]);
image2Show(currMoveMask3D)=frame(currMoveMask3D);
% While figure isn't closed...
if ~ishandle(appdata.figureHandles.hFigure),
stop(vid);
return;
end
% Update the figure
set(appdata.figureHandles.hImage, 'CData', image2Show);
drawnow;
% Update application data for next frame
appdata.background = frame;
appdata.prevMoveMask=currMoveMask;
set(vid, 'UserData', appdata);
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function localDeleteFig(fig, event)
% Reset peekdata warnings.
warning on imaq:peekdata:tooManyFramesRequested
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function figData = localCreateFigure(vid, figTitle)
% Creates and initializes the figure.
% Create the figure and axes to plot into.
fig = figure('NumberTitle', 'off', 'MenuBar', 'none', ...
'Name', figTitle, 'DeleteFcn', @localDeleteFig);
% Create a spot for the image object display.
nbands = get(vid, 'NumberOfBands');
res = get(vid, 'ROIPosition');
himage = imagesc(rand(res(4), res(3), nbands));
% Clean up the axes.
ax = get(himage, 'Parent');
set(ax, 'XTick', [], 'XTickLabel', [], 'YTick', [], 'YTickLabel', []);
title( sprintf('Showing areas of movement. \nTo finish close figure.'),...
'FontSize', 17);
% Store the figure data.
figData.hFigure = fig;
figData.hImage = himage;