Main Content

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 Vision (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 contains different sections. There is a section for camera configuration, camera properties, preview properties, logging, and displaying the live preview.

Create and Maintain Camera List

Create an app helper function 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

In this example, updateDeviceTable steps through the installed adaptors and gets all associated devices. The function saves device information in a table containing the adaptor, the device index within that adaptor, and the name of the device. The Connect callback function will use the information in this table to populate the camera-selection drop-down list and create the videoinput object.

If new hardware is connected to the machine, MATLAB will typically not see it immediately as the list of devices are cached. To look for new devices, the imaqreset command can be used to clear out the existing device cache. After resetting the device cache, the app uses the same updateDeviceTable function as before to create a new device table, which will include any newly connected hardware.

Connect and Disconnect Camera

Write a callback function for the Connect button to connect to a camera. To allow other app functions to access the videoinput object, store a handle to it in an app property.

When someone using the app clicks Connect, the app creates a videoinput object using the selected camera and format specified in the Format dropdown.

function ConnectButtonPushed(app, event)
    ...
    app.VideoObject = videoinput(string(selectedCamera.Adaptor), selectedCamera.Number, app.FormatDropDown.Value);
    ...
end

To disconnect from the camera and reset the app to its initial state, in the Disconnect button callback, write code to stop the preview and delete all objects related to image acquisition, preview, and properties.

function DisconnectButtonPushed(app, event)
    stoppreview(app.vid);
    delete(app.vid);
    delete(app.hImage);
    delete(app.propertiesLayout);
    app.setAppViewState("DeviceConfiguration")
end

Set Camera Properties

Create a callback for all camera property UI components. In this callback, write code to update the camera property to the new value specified by the user.

This example creates a slider and numeric edit field for setting numeric camera properties. The values of these two controls stay in sync. Refer to the SliderChangedFcn app function for more details. Because in this example the camera property components are created programmatically, the Tag property is used to store the camera property name for identifying which camera property components are paired:

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

Conversely, when an app user changes the UI component values, update the corresponding videoinput property values. If the specified value is not supported by the camera, then the camera adjusts the specified value to use a supported value instead. Keep the UI components in sync by setting them equal to the value from the camera.

Preview

To customize your live preview functionality, use a custom preview callback. Using a custom preview allows the preview to be customized live, allowing image processing or other operations to be done to the image before displaying it. In this example, it is used to mirror the image.

For cameras with high resolution, use the maxRenderedResolution image property to use a lower onscreen resolution for the preview and improve preview performance.

To keep the aspect ratio of the preview 1:1, use the axis function with "image" as an input.

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, for effective visualization of the data, apply a non-grayscale colormap and set color limits. Additionally, when using a format with a bitdepth of greater than 8bits, the PreviewFullBitDepth property also needs to be set so that the entire data range can be visualized appropriately.

function refreshPreview(app)
    ...
    app.hImage.MaxRenderedResolution = 1920;
    ...
    axis(app.imageAxes, "image");
    preview(app.vid,app.hImage);
end

Save Images and Video to Disk

Create a Snapshot button callback function. Write code in the callback that calls the videoinput getsnapshot to get an image, and use the imwrite function to save it to a file.

function SnapshotButtonPushed(app, event)
    img = getsnapshot(app.vid);
    ...
    imwrite(img,fileName);
    ...
end

To save video to disk, create a callback function for the capture button. Write code to start the acquisition, and use the builtin videoinput DiskLogger functionality to log a video to a file.

function RecordButtonPushed(app, event)
    ...
    logfile = VideoWriter(fileName, format);
    app.vid.DiskLogger = logfile;
    ... 
    start(app.vid)
end

Handling Errors

You can display errors as alerts in the app instead of displaying errors in the Command Window. Use Try/Catch blocks and the uialert function. For example:

try
    app.vid = videoinput(selectedCamera.Adaptor, selectedCamera.Number, app.FormatDropDown.Value);
catch E
    uialert(app.UIFigure, E.message, "Camera Error:")
end

Customize and Run the App

To run this app with your camera, you may want to customize the camera properties of interest that are shown within the app.

To make app scalable with any properties of interest, the device specific properties are determined programmatically based on the CameraProperties property defined in the app.

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

Add properties you want to display to this definition. What properties you can add depends on the adaptor your camera uses and the properties supported by your camera. This example supports both bounded and enumerated properties.

Some camera properties are interdependent, for example, Exposure and ExposureMode. Their dependency is defined in the app property CameraPropertiesDict, with the key being the controlling property, and the value being 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

This variable is used within the app code to check if properties depend on each other so that they can be handled appropriately. The app property disablePropStrings defines which value should be used when disabling properties. The app code checks if any of the defined values are applicable to the controlling property, and if so, sets the property to that value so that the dependent one can be controlled appropriately.

properties (Access = private)
    ...
    DisablePropStrings = struct( ...
        winvideo="manual", ...
        gentl=["True", "Off"], ...
        gige=["True", "Off"], ...
        demo = [], ...
        macvideo = [], ...
        linuxvideo = [])
    ...
end

For example, Exposure and ExposureMode are paired For the Exposure value to take effect, ExposureMode must be set to manual, which is defined in DisablePropStrings.

See Also