Create an App for Live Image Acquisition
This example shows how to build an app in App Designer that discovers all available cameras and shows a custom live preview from the selected camera. Additionally, the example shows how to use the app to configure camera properties, set up and configure a preview, and save images and video to disk.
What you need to run the app:
Image Acquisition Toolbox™
One of the following adaptors: winvideo, macvideo, linuxvideo (Image Acquisition Toolbox Support Package for OS Generic Video Interface), gentl (Image Acquisition Toolbox Support Package for GenICam™ Interface), gige (Image Acquisition Toolbox Support Package for GigE Vision Hardware). Other adaptors might work with minor modifications.
A camera supported by one of the installed adaptors
Design the App Layout
Lay out the app in App Designer using Design View to provide controls for users based on the app goals. In this case, lay out the app so that users can:
Create and maintain the list of available cameras.
Connect and disconnect from cameras.
Set camera properties.
Get a live preview and configure how it is displayed.
To organize these functions, the app layout contains different panels, such as Camera Connection, Camera Properties, Preview Properties, Logging, and a live preview display.
Create and Maintain Camera List
Create an app helper function, refreshCameraList
, to get the list of connected cameras and populate the camera selection drop-down items.
function refreshCameraList(app) updateDeviceTable(app); deviceList = strcat(app.DeviceTable(:,:).Name, '-', app.DeviceTable(:,:).Adaptor); app.CameraDropDown.Items = deviceList; app.FormatDropDown.Items = imaqhwinfo(app.DeviceTable(1,:).Adaptor{1}).DeviceInfo.SupportedFormats; end
To maintain the camera list, the updateDeviceTable
helper function uses the imaqhwinfo
function and steps through the installed adaptors to get all associated devices. The function saves camera information in a table containing the adaptor name, the device index within that adaptor, and the name of the device.
function updateDeviceTable(app) % Create the device table with all recognized devices app.DeviceTable = table; % Get a list of all the installed adaptors adaptors = imaqhwinfo().InstalledAdaptors; % Iterate over every element in an array for adaptor = adaptors % Step through each adaptor devices = imaqhwinfo(cell2mat(adaptor)).DeviceInfo; % get all devices for adaptor for ii = 1:size(devices,2) % Add the device to the device table app.DeviceTable = [app.DeviceTable; {string(adaptor), ii, string(devices(ii).DeviceName)}]; end end % Set the table column names for easy access in the future app.DeviceTable.Properties.VariableNames = ["Adaptor", "Number", "Name"]; end
When the app starts, the startupFcn
executes refreshCameraList
to update the camera list in the app.
function startupFcn(app) ... refreshCameraList(app) ... end
Connect to Camera
When the app user clicks the Connect button create a videoinput object based on the selected camera. To allow other app functions to access the videoinput
object, store it in an app property, app.VideoObject
.
function ConnectButtonPushed(app, event) ... app.VideoObject = videoinput(string(selectedCamera.Adaptor), selectedCamera.Number, app.FormatDropDown.Value); ... end
To disconnect from the camera, execute the videoinput
delete
function. Access the videoinput
object using the app property.
function DisconnectButtonPushed(app, event) stoppreview(app.VideoObject); delete(app.VideoObject); ... end
Configure Camera Properties
Cameras can have a large number of configurable properties. However, the app user might want to adjust only a few properties. The setupPropUIComponents
helper functions programmatically generates UI components for the user specified camera properties. Specifically, it creates a slider and numeric edit field for each numeric camera property and a dropdown for each camera property that is an enumeration.
When an app user changes a UI component property value, update the camera property value in the component callback function. If the specified value is not supported by the camera, then the camera adjusts the property to a supported value instead. Refresh the UI components to keep them in sync with the updated camera property value.
The UI component Tag object identifier and the findobj
function find the UI component that is associated with the camera property being updated.
function sliderChangedFcn(app, src, event) app.CameraSource.(src.Tag) = event.Value; actualValue = app.CameraSource.(src.Tag); src.Value = double(actualValue); editField = findobj(app.PropertiesLayout,'Tag',src.Tag + "Edit"); editField.Value = double(actualValue); end
Preview
Use a custom preview callback to process the captured images before they are displayed live. Also, for cameras with high resolution, use the MaxRenderedResolution image property to improve preview performance by using a lower onscreen preview resolution.
function refreshPreview(app) ... app.ImagePreview = image(app.imageAxes,zeros(resolution(2), resolution(1), nBands, imaqinfo.NativeDataType)); ... app.ImagePreview.MaxRenderedResolution = 1920; ... setappdata(app.ImagePreview,'UpdatePreviewWindowFcn',@(obj,event,hImage) previewCallback(app,obj,event,hImage)) ... preview(app.VideoObject,app.ImagePreview); end
This example uses the fliplr
function to mirror the image
function previewCallback(app,obj,event,hImage) cdata = event.Data; if app.MirrorSwitch.Value == "On" cdata = fliplr(cdata); end hImage.CData = cdata; end
Non-optical cameras can return data in monochrome format (intensity images). For example, thermal infrared cameras return temperature image data. For this type of application, to effectively visualize the data, apply a non-grayscale colormap and set color limits. Additionally, when using a format with a bitdepth of greater than 8 bits, the PreviewFullBitDepth property needs to be set so that the entire data range can be visualized appropriately.
function adjustPreviewBands(app) nBands = app.VideoObject.NumberOfBands; % Check for grayscale/scientific formats if(nBands == 1) app.VideoObject.PreviewFullBitDepth = "on"; ... app.ImagePreview.CDataMapping = 'scaled'; app.ImagePreview.Parent.CLimMode = 'auto'; ... end end
function ColormapDropDownValueChanged(app, event) value = app.ColormapDropDown.Value; colormap(app.ImagePreview.Parent,value); end
Save Images and Video to Disk
To save an image to disk, call videoinput
getsnapshot
to get an image, and use the imwrite
function.
function SnapshotButtonPushed(app, event) img = getsnapshot(app.VideoObject); ... imwrite(img,fileName); ... end
To save video to disk use the builtin videoinput
DiskLogger functionality.
function RecordButtonPushed(app, event) ... logfile = VideoWriter(fileName, format); app.VideoObject.DiskLogger = logfile; ... start(app.VideoObject) end
Handling Errors
You can display errors as alerts in the app instead of in the Command Window. Use Use try/catch to Handle Errors blocks and the uialert
function.
function ConnectButtonPushed(app, event) ... try app.VideoObject = videoinput(string(selectedCamera.Adaptor), selectedCamera.Number, app.FormatDropDown.Value); catch E uialert(app.UIFigure,E.message, "Camera Object Creation Error"); return end ... end
Customize and Run the App
To run this app with your camera, customize the camera properties that you want to configure when using the app.
You can specify properties in the CameraProperties
structure, which is defined as an app property. The available properties depend on the videoinput
adaptor and your camera. This example app supports both numeric and enumerated properties.
properties (Access = private) ... CameraProperties = struct( ... winvideo = ["Exposure", "ExposureMode", "FrameRate"], ... gentl = ["AcquisitionFrameRate", "AcquisitionFrameRateEnable", "ExposureTime", "ExposureAuto"], ... gige = ["AcquisitionFrameRate", "AcquisitionFrameRateEnable", "ExposureTime", "ExposureAuto"], ... demo = string.empty, ... macvideo = string.empty, ... linuxvideo = string.empty) ... end
Some camera properties are interdependent, for example, Exposure
and ExposureMode
. You can define the dependent properties in the CameraPropertiesDict
app property, which is a structure with dictionary
fields for each videoinput
adaptor. The dictionary key is the controlling property and the value is the dependent property.
properties (Access = private) ... CameraPropertiesDict = struct( ... winvideo=dictionary(ExposureMode="Exposure"), ... gentl=dictionary( ... AcquisitionFrameRateEnable="AcquisitionFrameRate", ... ExposureAuto="ExposureTime"), ... gige=dictionary( ... AcquisitionFrameRateEnable="AcquisitionFrameRate", ... ExposureAuto="ExposureTime"), ... demo = dictionary(), ... macvideo = dictionary(), ... linuxvideo = dictionary()) ... end
The app property DisablePropStrings
defines which value controls the state (enabled/disabled) of the UI components associated with the paired dependent property. For example, Exposure
and ExposureMode
are paired. To enable the Exposure slider and numeric edit field, ExposureMode
must be set to manual.
properties (Access = private) ... DisablePropStrings = struct( ... winvideo="manual", ... gentl=["True", "Off"], ... gige=["True", "Off"], ... demo = [], ... macvideo = [], ... linuxvideo = []) ... end