Code covered by the BSD License  

Highlights from
Ghosting Demo

image thumbnail
from Ghosting Demo by David Tarkowski
GUI showing alpha blending of streamed image pairs.

demo_imaqghost(obj)
function demo_imaqghost(obj)
% DEMO_IMAQGHOST Demonstrate alpha blending using callbacks.
%
%    This file demonstrates how to configure a VIDEOINPUT device to perform
%    analysis on images as they are being acquired and display the results
%    live.  When the demo is started, it takes a snapshot of the
%    background.  Subsequent images are combined with the background image
%    using a user specified alpha value.  This make moving objects appear
%    to be semi-transparent.
%
%    GHOSTDEMO will find the first VIDEOINPUT object installed on the
%    machine and use the default format on that device for the
%    demonstration.
%
%    GHOSTDEMO(OBJ) will use the user-configured VIDEOINPUT object OBJ for
%    the demonstration.
%
%    See also VIDEOINPUT.

% 4/2004 DT
% Copyright 2004 by The MathWorks, Inc.
% $Revision: 1 $   $Date: 4/08/04 2:39p $ 

% Check the input arguments.  If an argument is supplied verify that it is
% a valid videoinput object.
if (nargin == 0)
    obj = [];
elseif ( (nargin == 1) && ~isa(obj, 'imaqdevice') && ~isvalid(obj) )
    error('imaq:ghostdemo:invalidobject', 'OBJ must be a valid VIDEOINPUT object.');
end

% Create the GUI.  This could also be done using GUIDE.
handles = localConfigureGUI;

% Configure the videoinput object.  If one was provided, it will be used.
% Otherwise a new object will be created.
handles = localConfigureObject(obj, handles);

% Everything is set up and ready to run.  Configure the callbacks.  Once
% the callbacks are configured, clicking on the run or preview button will
% start the demo.
localConfigureCallbacks(handles);

% End of ghostdemo

function handles = localConfigureObject(obj, handles)
% localConfigureObject sets up the callbacks on the videoinput object OBJ
% to work with the demo.  If an object is not passed in, then a new object
% will be created.
%
% The object's TimerFcn will be used because that allows callbacks to be
% scheduled as rapidly as possible.  If the callbacks are executing too
% rapidly (for example the processing takes too long), then some callbacks
% will be dropped.  Since the data is just being displayed to the screen,
% this is the desired behavior.  If processing every frame is necessary,
% the FramesAcquiredFcn should be used.

% Create the object if necessary.
if isempty(obj)
    obj = localCreateObj;
else
    % Set the object's tag so that it can be found later.
    set(obj, 'Tag', 'User ghost object');
    
    % Clear the UserData since it will be used by the demo
    set(obj, 'UserData', []);
end

% Set the window title now that the name of the object is known.  This is
% just cosmetic.
set(handles.ghost, 'Name', ['Ghost demo using: ' obj.Name]);

% Store the object in the handles struct for later use.
handles.imaqObj = obj;

% Configure the object for manual trigger.  This allows the object to be
% started and acquire data.  Since the object is acquiring data, calls to
% GETSNAPSHOT will return the latest frame very quickly.  However, the
% object does not log data until TRIGGER is called, so only a single frame
% is ever in memory.
triggerconfig(obj, 'manual');

% Configure the callbacks.  The TimerFcn is configured to use a function
% handle to a local function.  The handles to the axes and the slider will
% be passed to this function, as well as the videoinput object and an event
% structure that are passed whenever a function handle is used for a
% callback.
obj.TimerFcn = {@localUpdateDisplay, handles.imDispAxes, handles.alphaSlider};

% Since the TimerFcn discards callbacks if they are called to rapidly, it
% is safe to set a very quick period here for the maximum display rate.
obj.TimerPeriod = 0.01;

% End of localConfigureObject

function obj = localCreateObj
% localCreateObj searches for the first supported device with a
% default format.  Once found, it creates an object for that device and
% returns the object to the calling function.  Some devices do not have
% default formats and can not be used since the GUI doesn't provide a
% method of specifying camera files.

% Determine the available adaptors.
info = imaqhwinfo;

adaptorName = [];
adaptorID = [];
adaptorIndex = 1;

% Search through all of the installed adaptors until an available adaptor
% with hardware that has a default format is found.
while (isempty(adaptorName) && isempty(adaptorID))
    
    if (adaptorIndex > length(info.InstalledAdaptors))
        error('imaq:ghostdemo:nohardware', 'No hardware compatible with GHOSTDEMO was found.');
    end
    
    % Get information on the current adaptor.
    curAdaptor = info.InstalledAdaptors{adaptorIndex};
    adaptorInfo = imaqhwinfo(curAdaptor);
    
    % For the current adaptor, search through each installed device looking
    % for a device that has a default format.
    idIndex = 1;
    while (isempty(adaptorID) && (idIndex <= length(adaptorInfo.DeviceIDs)))
        curID = adaptorInfo.DeviceIDs{idIndex};
        if ~isempty(adaptorInfo.DeviceInfo(idIndex).DefaultFormat)
            adaptorName = curAdaptor;
            adaptorID = curID;
        end
        
        % If the current device doesn't have a default format check the
        % next one.
        idIndex = idIndex + 1;
    end
    
    % Check the next adaptor.
    adaptorIndex = adaptorIndex + 1;
end

% Create the object.
obj = videoinput(adaptorName, adaptorID);

% Configure the object's tag so that it can be found later by using
% IMAQFIND.
set(obj, 'Tag', 'Ghost Demo Object');

% End of localCreateObj

function localUpdateDisplay(obj, event, axesHandle, sliderHandle);
% localUpdateDisplay does most of the work of the demo.  This function
% acquired data, does the processing, and displays the result.
% 
% OBJ is the videoinput object.
% EVENT is an event structure, it is not used by this function.
% AXESHANDLE is a handle to the axes that the image should be display in.
% SLIDERHANDLE is the handle of the slider that specifies the alpha value.

% The background image is stored in the videoinput object's UserData field.
background = get(obj, 'Userdata');

% If this is the first time that this function is called, there will not be
% a background.  In that case get an image and store it as the background.
if isempty(background),
    background = getsnapshot(obj);
    set(obj, 'Userdata', background);
end

% Get the current frame.
current = getsnapshot(obj);

% Get the alpha value.
val = get(sliderHandle, 'Value');

% Compute the image to display.
if exist('imlincomb')
    % Use INLINCOMB if available since it is highly optimized.  INLINCOMB
    % is provided by the Image Processing Toolbox.
    result = imlincomb(val, current, 1-val, background);
else
    % If the Image Processing Toolbox is not available, use standard MATLAB
    % math.
    result = val .* current + (1-val) .* background;
end

% Make sure that the demo's axes is selected and display the result.
imagesc(result, 'Parent', axesHandle);

% Don't show the tick marks.
set(axesHandle, 'Visible', 'off');

% Make sure that the figure window is allowed to refresh itself.  Since the
% processing can take a large amount of CPU time, the figure window needs
% to be explicitly told that it is okay to update itself with the new
% image.
drawnow

% End of localUpdateDisplay

function previewButtonCallback(hObject, event, handles)
% Handles chages to the state of the preview button.

% Get the current value (the value after the mouse click) of the preview
% button.
previewState = get(hObject, 'Value');

% Set the state of the preview window to match the preview button.
localSetPreviewState(previewState, handles.previewButton, handles.imaqObj);

% End of previewButtonCallback

function localSetPreviewState(newState, bHandle, obj);
% Sets the state of the object's preview window.  If newState is true then
% the preview window will be opened.  If it is false, the preview window
% will be closed.

if newState
    % Open the preview window.
    preview(obj)
    
    % Update the preview button string to indicate what will happen when it
    % is clicked on.
    set(bHandle, 'String', 'Stop Preview');
else
    % Close the preview window.
    closepreview(obj)

    % Update the preview button string to indicate what will happen when it
    % is clicked on.
    set(bHandle, 'String', 'Preview');
end

% End of localSetPreviewState

function runButtonCallback(hObject, event, handles)
% Handles the run button.  When the user clicks the run button, the preview
% window will be closed and the videoinput object is started.  Starting the
% videoinput object will cause the TimerFcn callback to start updating
% which will run the demo.
%
% If the run button is clicked again, the object will stop and the demo
% will cease to run.

% Get the value of the run button.
runState = get(hObject, 'Value');

if runState
    % The run button is "on" so the demo should be started.
    
    % Close the preview window and update the preview button.
    localSetPreviewState(false, handles.previewButton, handles.imaqObj);
    set(handles.previewButton, 'Value', false);
    
    % Update the string displayed in the instructions panel.
    crlf = sprintf('\n');
    set(handles.instructions, 'String', ...
        ['While the demo is running, you should see a continuously ' ...
         'updating stream of images ' ...
         'in the display area.  Any objects that were not in the scene '...
         'initially should be partially transparent.' crlf crlf ...
         'To see how the processing is accomplished, view the ' ...
         'localUpdateDisplay function in the demo file.']);
    
    % Update the string of the run button.
    set(hObject, 'String', 'Stop Running');
    
    % Start the object.
    start(handles.imaqObj);
else
    % Stop the object.
    stop(handles.imaqObj);
    
    % Reset the background image.
    handles.imaqObj.UserData = [];
    
    % Reset the string of the run button.
    set(hObject, 'String', 'Run')
end

% End of runButtonCallback

function localCleanup(hObject, events)
% This function is called when the user closes the demo's figure window.
% It needs to stop any running objects.  Additionally, if the demo created
% an object, that object needs to be deleted.  Finally the figure window
% should be closed.

% Find the object that the user passed in, if any.  If the object is found
% stop it.
obj = imaqfind('Tag', 'User ghost object');
if ~isempty(obj)
    stop([obj{:}]);
end

% Find the object that the demo created, if any.  If an object is found,
% stop it and then delete it.
obj = imaqfind('Tag', 'Ghost Demo Object');
if ~isempty(obj)
    stop([obj{:}])
    delete([obj{:}])
end

% This will close the figure window.
delete(hObject);

% End of localCleanup

function handles = localConfigureGUI
% Creates the GUI and lays out all of the UI elements.  This could also be
% done in GUIDE.

figHandle = figure('Units', 'characters', 'MenuBar', 'none', ...
    'Name', 'ghost', 'NumberTitle', 'off', ...
    'Position', [104 27 141 34.5], 'tag', 'ghost', ...
    'Color',[0.831372549019608 0.815686274509804 0.784313725490196], ...
    'CloseRequestFcn', @localCleanup);
handles.ghost = figHandle;

axesHandle = axes('Parent', figHandle, 'Position', [0.35 0.18 0.6 0.77],...
    'Tag', 'imDispAxes', 'Visible', 'off');
handles.imDispAxes = axesHandle;

sliderHandle = uicontrol('Parent', figHandle, 'Units', 'Normalized',...
    'Position', [.35 .09 .6 .05], 'String', {'Slider'},...
    'Style', 'slider', 'Value', 0.5, 'Tag','alphaSlider');
handles.alphaSlider = sliderHandle;

textHandle = uicontrol('Parent', figHandle', 'Units', 'Normalized', ...
    'Position', [.5835 .03 .14 .047], 'String', 'Transparency', ...
    'Style', 'text', 'Tag', 'sliderLabel', 'fontSize', 12);

uiPanelHandel = uipanel('Parent', figHandle, 'Title','Information',...
    'Units', 'Normalized', 'Position',[0.020 0.18 0.3 0.79],...
    'Tag','uipanel1', 'fontSize', 10);

textHandle = uicontrol('Parent', uiPanelHandel, ...
    'HorizontalAlignment', 'left', 'Units', 'Normalized',...
    'Position',[0.05 0.05 .9 .85], 'String', '',...
    'Style', 'text', 'fontSize', 10, 'Tag','instructions');
handles.instructions = textHandle;

crlf = sprintf('\n');
set(textHandle, 'String', ...
    ['The VIDEOINPUT object has been configured to use the TimerFcn '...
     'callback to process data.  See the function localConfigureObject '...
     'this file for more information.' crlf crlf ...
     'Use the Preview button to open a preview window and position ' ...
     'your camera so that it is pointing at a still background.  ' ...
     'Once the camera is configured properly, press the Run button to '...
     'start the processing.']);

previewHandle = uicontrol('Parent', figHandle, 'Units', 'Normalized',...
    'Position', [0.02 .09 .12 .05], 'String', 'Preview', ...
    'Style', 'togglebutton', 'Tag', 'previewButton', 'enable', 'inactive');
handles.previewButton = previewHandle;

runningHandle = uicontrol('Parent', figHandle, 'Units', 'Normalized',...
    'Position', [.20 .09 .12 .05], 'String', 'Run', ...
    'Style','togglebutton', 'Tag','runButton', 'enable', 'inactive');
handles.runButton = runningHandle;

% End of localConfigureGUI

function localConfigureCallbacks(handles)
% Set up the callbacks of the preview and run buttons. 
set(handles.previewButton, 'ButtonDownFcn', {@previewButtonDown, handles});
set(handles.runButton, 'ButtonDownFcn', {@runButtonDown, handles});
% End of localConfigureCallbacks

function runButtonDown(obj, event, handles)
% If the user clicks on the run button, toggle the state of the button and
% then excute the button's callback.
set(obj, 'Value', ~get(obj, 'Value'));
drawnow
runButtonCallback(obj, event, handles);
% End of runButtonDown

function previewButtonDown(obj, event, handles)
% If the user clicks on the preview button, toggle the state of the button and
% then excute the button's callback.
set(obj, 'Value', ~get(obj, 'Value'));
drawnow
previewButtonCallback(obj, event, handles);
% End of previewButtonDown

Contact us at files@mathworks.com