Skip to content
This repository has been archived by the owner. It is now read-only.
Permalink
master
Switch branches/tags

Name already in use

A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
Go to file
 
 
Cannot retrieve contributors at this time
classdef WidgetVideoAcquisition < handle
%WIDGETVIDEOACQUISITION
% initialize video stream
properties
stream
adaptor
device
resolution
fps
bitdepth
cdata
snapshot
autolim
flim
clim
thread
end
%% --- user interface properties --- %%
properties (Access = private)
ui_parent
ui_grid
ui_panel
ui_text_device
ui_text_status
ui_checkbox_auto
ui_pushbutton_grab
ui_pushbutton_preview
end
%% --- preview window properties --- %%
properties (Access = private)
pw_parent
pw_axes
pw_image
pw_patch
pw_mask
pw_state
end
%% --- constan properties --- %%
properties (Access = private, Constant = true, Hidden = true)
UIWINDOW_SIZE = [1, 1, 256, 99];
GRID_VGAP = [12, 2, 5];
GRID_HGAP = [5, 2, 5];
BACKGROUND_COLOR = [1, 1, 1];
FOREGROUND_COLOR = [0.5, 0.5, 0.5];
PATCH_COLOR = [255, 0, 0];
SATURATION_THRESHOLD = 0.995;
PUSHBUTTON_SIZE = [1, 1, 90, 26];
CHECKBOX_SIZE = [1, 1, 45, 20];
PREVIEW_SCREEN_RATIO = 0.4
DEVICE_ID = 1;
BIT_LABEL = {'8 bit', '12 bit', '16 bit'};
BIT_MAX = [2^8, 2^12, 2^16] - 1;
FRAMES_PER_TRIGGER = 10;
STATE_STREAM = 0;
STATE_SNAPSHOT = 1;
end
%% --- constructor / destructor methods --- %%
methods
function obj = WidgetVideoAcquisition(varargin)
%WIDGETVIDEOACQUISITION class constructor
% use input parser
parserObj = inputParser;
addParameter(parserObj, 'Parent', [], @isgraphics);
addParameter(parserObj, 'Adaptor', [], @ischar);
parse(parserObj, varargin{:});
% set parent graphics object
if isempty(parserObj.Results.Parent)
obj.ui_parent = figure(...
'Visible', 'on',...
'Tag', 'hVideoAcquisition',...
'Name', 'Video Acquisition',...
'MenuBar', 'none',...
'ToolBar', 'none',...
'NumberTitle', 'off',...
'Color', obj.BACKGROUND_COLOR,...
'Resize', 'off',...
'Units', 'pixels',...
'Position', obj.UIWINDOW_SIZE,...
'CloseRequestFcn', @obj.fcnCallback_closeUserInterface);
movegui(obj.ui_parent, 'northwest');
else
obj.ui_parent = parserObj.Results.Parent;
end
% set device adaptor
obj.adaptor = parserObj.Results.Adaptor;
% recognize device adaptor
obj.setVideoAdaptor();
% recognize video properties
obj.setVideoProperties();
% initialize video stream
obj.setStreamObject();
% render user interface
obj.renderUserInterface();
% render preview window
obj.renderPreviewWindow();
% start video stream
start(obj.stream);
end
function obj = dispose(obj)
%DISPOSE class destructor
if isa(obj.thread, 'timer')
stop(obj.thread);
delete(obj.thread);
end
if isa(obj.stream, 'videoinput')
stop(obj.stream);
end
if isgraphics(obj.pw_parent, 'figure')
delete(obj.pw_parent);
end
if isa(obj.ui_grid, 'uiGridLayout')
delete(obj.ui_grid);
end
if isgraphics(obj.ui_parent, 'figure')
delete(obj.ui_parent);
end
delete(obj);
end
function obj = renderUserInterface(obj)
%RENDERUSERINTERFACE
%%% --- create widget panel --- %%%
obj.ui_panel = uipanel(...
'Parent', obj.ui_parent,...
'Title', 'Video Acquisition',...
'TitlePosition', 'lefttop',...
'BorderType', 'line',...
'HighlightColor', obj.FOREGROUND_COLOR,...
'ForegroundColor', obj.FOREGROUND_COLOR,...
'BackgroundColor', obj.BACKGROUND_COLOR,...
'Units', 'normalized',...
'Position', [0, 0, 1, 1],...
'Units', 'pixels');
%%% --- create grid object --- %%%
obj.ui_grid = uiGridLayout(...
'Parent', obj.ui_panel,...
'VGrid', 3,...
'HGrid', 4,...
'VGap', obj.GRID_VGAP,...
'HGap', obj.GRID_HGAP);
obj.ui_text_device = uicontrol(...
'Parent', obj.ui_panel,...
'Style', 'text',...
'String', sprintf('%s, %s, (%d x %d)',obj.device, obj.adaptor, obj.resolution),...
'BackgroundColor', obj.BACKGROUND_COLOR,...
'Units', 'pixels',...
'Position', obj.ui_grid.getGrid('VIndex', 1, 'HIndex', 1:2));
obj.ui_grid.align(obj.ui_text_device,...
'VIndex', 1,...
'HIndex', 1:4,...
'Anchor', 'center');
obj.ui_text_status = uicontrol(...
'Parent', obj.ui_panel,...
'Style', 'text',...
'String', sprintf('%s, FPS %.2f, VL [%d %d]',...
obj.BIT_LABEL{obj.bitdepth},...
obj.fps,...
obj.clim),...
'BackgroundColor', obj.BACKGROUND_COLOR,...
'Units', 'pixels',...
'Position', obj.ui_grid.getGrid('VIndex', 2, 'HIndex', 1:2));
obj.ui_grid.align(obj.ui_text_status,...
'VIndex', 2,...
'HIndex', 1:3,...
'Anchor', 'center');
obj.ui_checkbox_auto = uicontrol(...
'Parent', obj.ui_panel,...
'Style', 'checkbox',...
'String', 'auto',...
'Value', 1,...
'BackgroundColor', obj.BACKGROUND_COLOR,...
'Callback', @obj.fcnCallback_checkAutoLimits,...
'Units', 'pixels',...
'Position', obj.CHECKBOX_SIZE);
obj.ui_grid.align(obj.ui_checkbox_auto,...
'VIndex', 2,...
'HIndex', 4,...
'Anchor', 'center');
obj.ui_pushbutton_preview = uicontrol(...
'Parent', obj.ui_panel,...
'Style', 'pushbutton',...
'String', 'Preview',...
'Callback',@obj.fcnCallback_preview,...
'Units', 'pixels',...
'Position', obj.PUSHBUTTON_SIZE);
obj.ui_grid.align(obj.ui_pushbutton_preview,...
'VIndex', 3,...
'HIndex', 1:2,...
'Anchor', 'center');
obj.ui_pushbutton_grab = uicontrol(...
'Parent', obj.ui_panel,...
'Style', 'pushbutton',...
'String', 'Grab',...
'Callback',@obj.fcnCallback_grab,...
'Units', 'pixels',...
'Position', obj.PUSHBUTTON_SIZE);
obj.ui_grid.align(obj.ui_pushbutton_grab,...
'VIndex', 3,...
'HIndex', 3:4,...
'Anchor', 'center');
end
function obj = renderPreviewWindow(obj)
%RENDERPREVIEWWINDOW
% get screen resolution
screenResolution = get(0, 'ScreenSize');
screenResolution = floor(obj.PREVIEW_SCREEN_RATIO * screenResolution(3:4));
aspectRatioCam = obj.resolution(1) ./ obj.resolution(2);
previewWidth = screenResolution(1);
previewHeight = floor(aspectRatioCam * previewWidth);
% render image window handlers
obj.pw_parent = figure(...
'Visible','on',...
'MenuBar','none',...
'ToolBar','none',...
'Name','video stream',...
'NumberTitle','off',...
'Resize', 'off',...
'Units','pixels',...
'Position', [1, 1, previewWidth, previewHeight],...
'CloseRequestFcn', @obj.fcnCallback_closeViewerWindow);
movegui(obj.pw_parent, 'north');
obj.pw_axes = axes(...
'Parent', obj.pw_parent,...
'Visible','off',...
'Units', 'pixels',...
'Position', [1, 1, previewWidth, previewHeight]);
obj.pw_image = image(...
obj.cdata,...
'Parent', obj.pw_axes,...
'CDataMapping', 'scaled');
set(obj.pw_axes,...
'XTick', [],...
'YTick', [],...
'CLim', obj.clim);
colormap(obj.pw_axes, 'gray');
obj.pw_mask = false(size(obj.cdata));
hold(obj.pw_axes, 'on');
obj.pw_patch = image(...
repmat(shiftdim(obj.PATCH_COLOR, -1), obj.resolution(1), obj.resolution(2)),...
'Parent', obj.pw_axes,...
'CDataMapping', 'scaled',...
'AlphaData', obj.pw_mask);
hold(obj.pw_axes,'off');
% activate preview state
obj.pw_state = obj.STATE_STREAM;
end
end
%% --- user interface callbacks --- %%
methods
function obj = fcnCallback_closeUserInterface(obj, ~, ~)
%FCNCALLBACK_CLOSE close class destructor
obj.dispose();
end
function obj = fcnCallback_closeViewerWindow(obj, ~, ~)
%FCNCALLBACK_CLOSEVIEWERWINDOW toggle visibility property
set(obj.pw_parent, 'Visible', 'off');
end
function obj = fcnCallback_checkAutoLimits(obj, ~, ~)
%FCNCALLBACK_CHECKAUTOLIMITS
% update autolimit
obj.autolim = get(obj.ui_checkbox_auto, 'Value');
% update snapshot limits
if (obj.pw_state == obj.STATE_SNAPSHOT)
if obj.autolim == 1
set(obj.pw_axes, 'CLim', obj.clim);
else
set(obj.pw_axes, 'CLim', [0, obj.BIT_MAX(obj.bitdepth)]);
end
end
end
function obj = fcnCallback_preview(obj, ~, ~)
%FCNCALLBACK_preview activates preview stream on camera
obj.preview();
end
function obj = fcnCallback_grab(obj, ~, ~)
%FCNCALLBACK_grab gets current frame
obj.grab();
end
end
%% --- functional methods --- %%
methods
function obj = setVideoAdaptor(obj)
%SETVIDEOADAPTOR determine video adapter
hwinfo = imaqhwinfo();
adaptors = hwinfo.InstalledAdaptors;
if ~any(strcmp(obj.adaptor, adaptors))
switch computer
case 'PCWIN64'
obj.adaptor = 'winvideo';
case 'GLNXA64'
obj.adaptor = 'linuxvideo';
case 'MACI64'
obj.adaptor = 'macvideo';
end
end
end
function obj = setVideoProperties(obj)
%SETVIDEOPROPERTIES calculates FPS and resolution
% shoot 50 frames to test camera
deviceInfo = imaqhwinfo(obj.adaptor, obj.DEVICE_ID);
obj.device = deviceInfo.DeviceName;
vidobj = videoinput(obj.adaptor, obj.DEVICE_ID);
set(vidobj,...
'ReturnedColorSpace', 'grayscale',...
'LoggingMode', 'memory',...
'FramesPerTrigger', 50,...
'TriggerFrameDelay', 5);
start(vidobj);
wait(vidobj, Inf);
stop(vidobj);
[frames, timeStamp] = getdata(vidobj);
frames = squeeze(frames);
% set fps
obj.fps = 1 / mean(diff(timeStamp));
% set stream resolution
obj.resolution = vidobj.VideoResolution;
obj.resolution = fliplr(obj.resolution);
% set frame limits
obj.autolim = 1;
obj.flim = [min(frames(:)), max(frames(:))];
% remove stream obj
delete(vidobj);
% set preview limits
obj.setPreviewLimits();
% initialize cdata
obj.cdata = frames(:,:,end);
end
function obj = setStreamObject(obj)
%SETSTREAMOBJECT initialize video stream object
% initialize stream object
obj.stream = videoinput(obj.adaptor, obj.DEVICE_ID);
set(obj.stream,...
'ReturnedColorSpace', 'grayscale',...
'LoggingMode', 'memory',...
'FramesAcquiredFcn', @obj.fcnCamera_stream,...
'FramesAcquiredFcnCount', obj.FRAMES_PER_TRIGGER,...
'FramesPerTrigger', obj.FRAMES_PER_TRIGGER,...
'TriggerRepeat', Inf);
triggerconfig(obj.stream, 'immediate');
% initialize callback thread
obj.thread = timer(...
'ExecutionMode', 'singleShot',...
'Name', 'ThreadIntrinsicAnalysis',...
'TimerFcn', @obj.fcnThread_setPreviewProperties,...
'TasksToExecute', 1);
end
function obj = setPreviewLimits(obj)
%SETPREVIEWLIMITS
if obj.flim(2) <= obj.BIT_MAX(1)
obj.bitdepth = 1;
elseif (obj.flim(2) > obj.BIT_MAX(1)) && (obj.flim(2) <= obj.BIT_MAX(2))
obj.bitdepth = 2;
else
obj.bitdepth = 3;
end
% default color limit is determined by bitdepth
obj.clim = [0, obj.BIT_MAX(obj.bitdepth)];
if obj.autolim == 1
obj.clim = obj.flim;
end
end
function obj = grab(obj)
%GRAB exaecute a snapshot from camera and stop preview
set(obj.pw_parent, 'Visible', 'on');
set(obj.pw_parent, 'Name', 'Grabbed Frame');
obj.pw_state = obj.STATE_SNAPSHOT;
obj.snapshot = get(obj.pw_image, 'CData');
end
function obj = preview(obj)
%PREVIEW
if strcmp('off', get(obj.stream, 'Running'))
% start video stream
start(obj.stream);
end
set(obj.pw_parent, 'Visible', 'on');
set(obj.pw_parent, 'Name', 'Video Stream');
obj.pw_state = obj.STATE_STREAM;
end
function obj = close(obj)
set(obj.pw_parent, 'Visible', 'off');
end
end
%% --- camera callbacks --- %%
methods
function obj = fcnCamera_stream(obj, hSrc, ~)
% read available frames
framesCount = get(hSrc, 'FramesAvailable');
[frames, timeStamp] = getdata(hSrc, framesCount);
frames = squeeze(frames);
% calculate current FPS
obj.fps = 1 / mean(diff(timeStamp));
% calculate frame intensity limit
obj.flim = [min(frames(:)), max(frames(:))];
% assign current cdata
obj.cdata = frames(:,:,end);
% update properties on separate thread
start(obj.thread);
end
function obj = fcnThread_setPreviewProperties(obj, ~, ~)
%FCNTHREAD_SETPREVIEWPROPERTIES
% check if current preview is stream or snapshot
if obj.pw_state == obj.STATE_STREAM
% set preview intensity limit
obj.setPreviewLimits();
% saturation mask
obj.pw_mask = (obj.cdata >= obj.SATURATION_THRESHOLD * obj.BIT_MAX(obj.bitdepth));
% update cdata
set(obj.pw_image, 'CData', obj.cdata);
set(obj.pw_axes, 'CLim', obj.clim);
elseif obj.pw_state == obj.STATE_SNAPSHOT
% clear saturation mask
obj.pw_mask = false(obj.resolution);
% update cdata
set(obj.pw_image, 'CData', obj.snapshot);
end
% update saturation mask
set(obj.pw_patch, 'AlphaData', obj.pw_mask);
% update properties status
set(obj.ui_text_status,...
'String', sprintf('%s, FPS %.2f, VL [%d %d]',...
obj.BIT_LABEL{obj.bitdepth},...
obj.fps,...
obj.flim));
obj.ui_grid.align(obj.ui_text_status,...
'VIndex', 2,...
'HIndex', 1:3,...
'Anchor', 'center');
end
end
%% --- PUBLIC METHODS --- %%
methods
function obj = enable(obj, varstate)
%ENABLE
if strcmp('on', varstate) || strcmp('off', varstate)
set(obj.ui_pushbutton_preview, 'Enable', varstate);
set(obj.ui_pushbutton_grab, 'Enable', varstate);
set(obj.ui_checkbox_auto, 'Enable', varstate);
end
end
end
end