Main Content

Create an App for Comparing Image Filters

R2026b
Since R2026b

This example shows how to build an app to interactively filter images using App Designer. Using the ImageFilterComparisonApp app, users can interactively filter the same image using two different image filtering methods and compare the results side by side. The app can compare the results of these image filtering functions: imfilter, medfilt2, imgaussfilt, bwareafilt, ordfilt2, stdfilt, imbilatfilt, imboxfilt, and entropyfilt.

In this example, you first build a custom UI component you can use to filter an image, and then build an app that uses the custom UI component to compare image filtering results. The custom UI component inherits from the matlab.ui.componentcontainer.ComponentContainer class. You can reuse the custom UI component in different apps to improve code reusability, logic isolation, and maintainability.

Open App Designer

App Designer is an interactive development environment for designing custom UI components and apps and programming their behavior.

To build the app from scratch, open App Designer using this command. Alternatively, you can open App Designer by selecting the Design App option on the Apps tab of the MATLAB® toolstrip.

appdesigner

In this example, you build the ImageFilter custom UI component and the ImageFilterComparisonApp app. The ImageFilter component and the ImageFilterComparisonApp app are also attached to this example as supporting files. For information on running the app, see the Compare Image Filters Using the App section.

You can also open this example from the App Designer home page by clicking Show examples in the Apps section of the home page and selecting Compare Image Filtering Results from the list of examples.

You can customize the code of the app in the attached supporting files. For information on customizing the app, see the Customize the App section.

App Layout Design

The ImageFilterComparisonApp app has two main regions: a toolstrip and a working area.

The toolstrip contains these sections:

  • Import — Consists of UI components used to import image data from a file or the workspace.

  • Export — Consists of UI components used to export the filtered images.

The working area of the app consists of a single section containing two instances of the custom UI component ImageFilter, which filter the imported image using two different methods.

Each ImageFilter component contains these sections:

  • Image view — Consists of a Viewer UI component that displays the imported image as filtered by the selected filter. The component displays the image using the imageshow function. The handle of the displayed image is an Image object, for which the Viewer object is the parent. For more information about the Viewer object, see Viewer Properties. For more information about the Image object, see Image Properties.

  • Image filter — Consists of UI components used to interactively select an image filter and its parameters.

App layout.

The app uses this grid layout structure to create the defined layout.

  • Main app figure

    • Main app grid layout

      • Toolstrip grid layout

        • Toolstrip elements like import, export

      • Working area grid layout

        • ImageFilter custom UI component

For more information on using a grid layout with App Designer, see Use Grid Layout Managers in App Designer.

Create the ImageFilter Custom UI Component

Each ImageFilter custom component includes these properties:

  • OriginalImage — Original image loaded by the user of the app.

  • Image — Displayed image. Initially, Image is the same as OriginalImage. When you apply a filtering method to the image, the app updates Image to the filtered image being displayed.

  • UpdateRequestedFlag — Flag to trigger the update method of the component. This is a private flag, not accessible to users of the app.

Custom UI components inherit from the matlab.ui.componentcontainer.ComponentContainer class and have two primary methods: setup and update.

The setup method and other custom functions of the ImageFilter custom component create and set up its layout, including these UI components.

  • A grid layout for the UI components.

  • The Viewer objects used to display the image.

  • UI elements used to interactively select an image filtering method and its parameters.

The ImageFilter component stores the image to be filtered in the Image property. When the user sets the Image property of the component, the component resets to the default view. The component determines the applicable filtering methods based on the size and data type of the input image. The set.Image method defines this behavior.

function set.Image(comp,img)
    % Save the input image to OriginalImage and display it. Based 
    % on the type of image passed, update the available filters.
    comp.OriginalImage = img;
    comp.ImageshowHandle.Data = img;
    if (ismatrix(img) || (ndims(img)==3 && size(img,3)==1))
        if isa(img,"logical")
            % 2D BW image
            comp.SelectFilterDropDown.Items = [comp.DefaultDropDownValue,"imfilter","medfilt2","bwareafilt","ordfilt2","stdfilt","entropyfilt"];
        else
            % 2D Grayscale image
            comp.SelectFilterDropDown.Items = [comp.DefaultDropDownValue,"imfilter","medfilt2","imgaussfilt","ordfilt2","stdfilt","imbilatfilt","imboxfilt","entropyfilt"];
        end
    else
        if isa(img,"logical")
            comp.SelectFilterDropDown.Items = [comp.DefaultDropDownValue,"imfilter","stdfilt","entropyfilt"];
        else
            % 2D Color image
            comp.SelectFilterDropDown.Items = [comp.DefaultDropDownValue,"imfilter","imgaussfilt","stdfilt","imbilatfilt","imboxfilt","entropyfilt"];
        end
    end
    % Reset filter selection on new data load
    comp.SelectFilterDropDown.Value = comp.DefaultDropDownValue;
    comp.SelectFilterDropDownLabel.Enable = "on";
    comp.SelectFilterDropDown.Enable = "on";
    delete(comp.FilterPropertiesGridLayout.Children);
end

When the user changes the filtering method or the filter parameters, the ImageFilter component triggers the update method. The update method computes the filtered image based on the original image and current input values and displays the results.

function update(comp)
    % Based on the current filter selection, compute the
    % filtered image and display it
    value = comp.SelectFilterDropDown.Value;
    switch value
        case "imfilter"
            if strcmp(comp.ImportFilterFromWorkspaceDropdown.Value,comp.DefaultDropDownValue)
                % If multidimensional filter has not yet been
                % selected, then just display the base image
                comp.ImageshowHandle.Data = comp.OriginalImage;
                return;
            end
            comp.ImageshowHandle.Data = imfilter(comp.OriginalImage, ...
                double(evalin("base",comp.ImportFilterFromWorkspaceDropdown.Value)), ...
                comp.PaddingDropDown.Value, ...
                comp.OutputSizeDropDown.Value, ...
                comp.CorrOrConvDropDown.Value);
        case "medfilt2"
            comp.ImageshowHandle.Data = medfilt2(comp.OriginalImage, ...
                [comp.NeighbourhoodSizeRowsEditField.Value comp.NeighbourhoodSizeColumnsEditField.Value], ...
                comp.PaddingDropDown.Value);
        case "imgaussfilt"
            if ~rem(comp.FilterSizeRowsEditField.Value,2) || ~rem(comp.FilterSizeColumnsEditField.Value,2)
                % If filter size is not an odd number, then return an error
                comp.displayError("Filter size must be odd");
                return;
            end
            comp.ImageshowHandle.Data = imgaussfilt(comp.OriginalImage, ...
                [comp.SigmaRowsEditField.Value comp.SigmaColumnsEditField.Value], ...
                FilterSize=[comp.FilterSizeRowsEditField.Value comp.FilterSizeColumnsEditField.Value], ...
                Padding=comp.PaddingDropDown.Value, ...
                FilterDomain=comp.FilterDomainDropDown.Value);
        case "bwareafilt"
            if strcmp(comp.ModeRadioButtonGroup.SelectedObject.Text,"Range")
                % 'Range' mode is selected
                comp.ImageshowHandle.Data = bwareafilt(comp.OriginalImage, ...
                    [comp.RangeMinEditField.Value comp.RangeMaxEditField.Value], ...
                    str2double(comp.PixelConnectivityDropDown.Value));
            else
                % 'Keep' mode is selected
                comp.ImageshowHandle.Data = bwareafilt(comp.OriginalImage, ...
                    comp.NumObjectsToInclude.Value, ...
                    comp.SizeOfObjectToKeepDropDown.Value, ...
                    str2double(comp.PixelConnectivityDropDown.Value));
            end
        case "ordfilt2"
            if ~strcmp(comp.ImportDomainFromWorkspaceDropdown.Value,comp.DefaultDropDownValue)
                domain = evalin("base",comp.ImportDomainFromWorkspaceDropdown.Value);
                if any(domain~=0 & domain~=1,"all")
                    % If domain matrix does not contain only 0s and 1s, return error
                    comp.displayError("Domain must contain only values of 0 or 1");
                    return;
                end
                if strcmp(comp.ImportAdditiveOffsetFromWorkspaceDropdown.Value,comp.DefaultDropDownValue)
                    comp.ImageshowHandle.Data = ordfilt2(comp.OriginalImage, ...
                        comp.OrderNumericEditField.Value, ...
                        domain,comp.PaddingDropDown.Value);
                else
                    % Additive offset is also selected
                    offset = evalin("base",comp.ImportAdditiveOffsetFromWorkspaceDropdown.Value);
                    if ~isequal(size(domain),size(offset))
                        % Additive offset and domain matrix are not
                        % of the same size.
                        comp.displayError("Domain and additive offset must be of same size");
                        return;
                    end
                    comp.ImageshowHandle.Data = ordfilt2(comp.OriginalImage, ...
                        comp.OrderNumericEditField.Value, ...
                        domain,offset, ...
                        comp.PaddingDropDown.Value);
                end
            else
                % If domain matrix is not yet selected, then show
                % the base image
                comp.ImageshowHandle.Data = comp.OriginalImage;
            end
        case "stdfilt"
            if strcmp(comp.ImportNeighborhoodFromWorkspaceDropdown.Value,comp.DefaultDropDownValue)
                comp.ImageshowHandle.Data = stdfilt(comp.OriginalImage);
            else
                % Neighborhood matrix is also passed
                nhood = evalin("base",comp.ImportNeighborhoodFromWorkspaceDropdown.Value);
                if any(nhood~=0 & nhood~=1,"all")
                    % If neighborhood matrix does not contain only 0s and 1s, return error
                    comp.displayError("Neighborhood must contain only values of 0 or 1");
                    return;
                end
                comp.ImageshowHandle.Data = stdfilt(comp.OriginalImage,nhood);
            end
        case "imbilatfilt"
            if ~rem(comp.NeighborhoodSizeEditField.Value,2)
                % If neighborhood size is not odd, return error
                comp.displayError("Neighborhood size must be odd");
                return;
            end
            comp.ImageshowHandle.Data = imbilatfilt(comp.OriginalImage, ...
                comp.SmoothingEditField.Value, ...
                comp.SigmaEditField.Value, ...
                NeighborhoodSize=comp.NeighborhoodSizeEditField.Value, ...
                Padding=comp.PaddingDropDown.Value);
        case "imboxfilt"
            if ~rem(comp.FilterSizeRowsEditField.Value,2)||~rem(comp.FilterSizeColumnsEditField.Value,2)
                % If the size of box filter is not odd in both dimensions, return error
                comp.displayError("Filter size must be odd in both dimensions");
                return;
            end
            comp.ImageshowHandle.Data = imboxfilt(comp.OriginalImage, ...
                [comp.FilterSizeRowsEditField.Value comp.FilterSizeColumnsEditField.Value], ...
                Padding=comp.PaddingDropDown.Value, ...
                NormalizationFactor=comp.NormalizationFactorEditField.Value);
        case "entropyfilt"
            if strcmp(comp.ImportNeighborhoodFromWorkspaceDropdown.Value,comp.DefaultDropDownValue)
                comp.ImageshowHandle.Data = entropyfilt(comp.OriginalImage);
            else
                % Neighborhood matrix is also passed
                nhood = evalin("base",comp.ImportNeighborhoodFromWorkspaceDropdown.Value);
                if any(nhood~=0 & nhood~=1,"all")
                    % If neighborhood matrix does not contain only 0s and 1s, return error
                    comp.displayError("Neighborhood must contain only values of 0 or 1");
                    return;
                end
                comp.ImageshowHandle.Data = entropyfilt(comp.OriginalImage,nhood);
            end
    end
end

Define Callbacks for ImageFilter Custom UI Component

The ImageFilter component uses callback functions to handle user interactions. The callback functions ensure that the ImageFilter component triggers the update method when you change the image, filtering method, or filter parameters.

For example, when you change the selected filtering method, the ImageFilter component deletes the existing UI elements for the parameters of the previous filter and creates UI elements for the filter parameters of the newly selected filter. The SelectFilterDropDownValueChanged callback, as shown here, invokes a filter creation function that corresponds to the selected filter. For example, if you select the imfilter filtering method, the callback invokes the createImfilter function. The filter creation function creates the UI elements for the filter parameters and invokes the updateImage function when you change any filter parameter. The updateImage function in turn sets the UpdateRequestedFlag to true, which triggers the update method of the ImageFilter component.

% Value changed function: SelectFilterDropDown
function SelectFilterDropDownValueChanged(comp,event)
    % Callback function to create the UI
    % components of the selected filter
    value = comp.SelectFilterDropDown.Value;
    % Delete any existing input components
    delete(comp.FilterPropertiesGridLayout.Children);
    switch value
        case "imfilter"
            comp.createInputImfilter();
        case "medfilt2"
            comp.createInputMedfilt2();
        case "imgaussfilt"
            comp.createInputGaussFilt();
        case "bwareafilt"
            comp.createInputBwAreaFilt();
        case "ordfilt2"
            comp.createInputOrdfilt2();
        case "stdfilt"
            comp.createInputStdfilt();
        case "imbilatfilt"
            comp.createInputBilatfilt();
        case "imboxfilt"
            comp.createInputBoxfilt();
        case "entropyfilt"
            comp.createInputEntropyfilt();
    end
end

function createInputImfilter(comp)
    % Function to create UI components to get imfilter parameters
    % from user
    imfilterGrid = uigridlayout([4,2], ...
        Parent=comp.FilterPropertiesGridLayout, ...
        Scrollable="on", ...
        Padding=0, ...
        RowHeight=[20 20 20 20]);

    % Create import from workspace label and drop-down for selecting filter
    uilabel(Text="Multidimensional Filter", ...
        Tooltip="Import variable from workspace", ...
        Parent=imfilterGrid);
    importFilterFromWorkspaceDropdown = uidropdown(Items=comp.DefaultDropDownValue, ...
        Parent=imfilterGrid);
    importFilterFromWorkspaceDropdown.DropDownOpeningFcn = @(src,~)comp.getVarFromWorkspace(src);
    importFilterFromWorkspaceDropdown.ValueChangedFcn = @(~,~)comp.updateImage();
    comp.ImportFilterFromWorkspaceDropdown = importFilterFromWorkspaceDropdown;

    % Create Padding label and drop-down
    uilabel(Text="Padding",Parent=imfilterGrid);
    paddingDropDown = uidropdown(Items=["symmetric","replicate","circular"], ...
        Parent=imfilterGrid);
    paddingDropDown.ValueChangedFcn = @(~,~)comp.updateImage();
    comp.PaddingDropDown = paddingDropDown;

    % Create OutputSize label and drop-down
    uilabel(Text="Output Size",Parent=imfilterGrid);
    outputSizeDropDown = uidropdown(Items=["same","full"],Parent=imfilterGrid);
    outputSizeDropDown.ValueChangedFcn = @(~,~)comp.updateImage();
    comp.OutputSizeDropDown = outputSizeDropDown;

    % Create Correlation or Convolution label and drop-down
    uilabel(Text="Correlation or Convolution",Parent=imfilterGrid);
    corrOrConvDropDown = uidropdown(Items=["corr","conv"], ...
        Parent=imfilterGrid);
    corrOrConvDropDown.ValueChangedFcn = @(~,~)comp.updateImage();
    comp.CorrOrConvDropDown = corrOrConvDropDown;
end

function updateImage(comp)
    % Function to request update when user updates a UI input
    comp.UpdateRequestedFlag = true;
end

The ImageFilter component displays error messages when an error occurs in execution. For example, when the Filter Size parameter of the imgaussfilt function is not an odd number, the component displays an error. The component displays the error message in a dialog box using the uialert function. Because uialert requires a figure to display the message, the ImageFilter component attempts to get the ancestor figure of the component to display the message. If it fails to get an ancestor figure, it displays the error message in the command window by using the error function instead of a dialog box. The displayError function defines this behavior.

function displayError(comp,errorMsg)
    % Function to display error messages. To display the error
    % message as a uialert, this function tries to get an ancestor
    % figure to which the custom UI component is parented. If
    % it fails to find an ancestor figure, the error passes to the command window.
    fig = ancestor(comp,"figure");
    if isempty(fig)
        error(errorMsg);
    else
        uialert(fig,errorMsg,"Error");
    end
end

Define App Methods

The app uses methods to import and visualize data, process user input and update the display, export the filtered images, and control the app state. The app also uses some helper functions to improve code readability and code reusability. These are some of the important app methods.

Create Two ImageFilter Components

To enable users to view the results of two different image filtering operations side by side, the working area of the app is split into two components, defined as the LeftComponent and RightComponent properties of the app class. Each component of the working area contains an instance of the ImageFilter custom UI component, and the Viewer objects from the ImageFilter components are linked using the linkviewers function. Linking the viewers enables the user to pan and zoom both images synchronously. The startupFcn method defines the creation and placement of the custom UI components.

% Code that executes after component creation
function startupFcn(app)
    % Initialization function to create the component containers
    % and link their viewers
    app.LeftComponent = ImageFilter(app.WorkingAreaGridLayout);
    app.RightComponent = ImageFilter(app.WorkingAreaGridLayout);
    linkviewers([app.LeftComponent.Viewer app.RightComponent.Viewer],"on");

    % Set a name for each component to make it easy to identity them
    app.LeftComponent.ImageName = "Left Image";
    app.RightComponent.ImageName = "Right Image";
end

Reset App on New Image Load

When the user loads a new image into the app, both the ImageFilter components clear the existing data and display the loaded image with no filter applied. The app also enables the Export section of the app toolstrip. The resetAppOnNewDataLoad method defines this behavior.

function resetAppOnNewDataLoad(app,img)
    % Function to set input image and reset app on new data load
    app.LeftComponent.Image = img;
    app.RightComponent.Image = img;

    app.ToFileLabel.Enable = "on";
    app.ToFileDropDown.Enable = "on";
    app.ToWorkspaceLabel.Enable = "on";
    app.ToWorkspaceDropDown.Enable = "on";

end

Import Images in App

Users can load an image into the app from a file or from the workspace using the Import section of the app toolstrip. If the user loads the image from a file by selecting the Browse option, the app opens a dialog box enabling the user to browse for files that have image file formats. When the user selects a new image file, the app resets. The BrowseButtonPushed method defines the file browsing behavior.

function BrowseButtonPushed(app,event)
    % Function to import an image file by browsing file system
    filterSpec = app.getSupportedFileFilter();

    [file,location] = uigetfile(filterSpec,"Select an image file");
    if ~isequal(file,0)
        try
            imageData = imread(fullfile(location,file));
        catch
            uialert(fig,"Unable to read image file.","Invalid File");
            return;
        end
        app.resetAppOnNewDataLoad(imageData);
    end
end

If the user loads the image from the workspace by selecting the Select option, the app filters workspace variables for potential images and displays the variable names in the drop-down. When the user selects a new image from the workspace, the apps resets. The FromWorkspaceDropDownOpening and FromWorkspaceDropDownValueChanged methods define these behaviors.

% Drop-down opening function: FromWorkspaceDropDown
function FromWorkspaceDropDownOpening(app,event)
    % Callback function to filter possible images in workspace and
    % display them in FromWorkspaceDropDown for input selection
    vars = evalin("base","whos");
    supportedClasses = ["int8","uint8","int16","uint16","int32","uint32","single","double","logical"];
    ValidInputVariables = app.DefaultImportFromWorkspaceDropDownValue;
    for idx = 1:numel(vars)
        var = vars(idx);
        TF = ismember(var.class,supportedClasses) && ((length(var.size) == 2) || (length(var.size) == 3 && var.size(3) == 3));
        if TF
            ValidInputVariables(end+1) = convertCharsToStrings(vars(idx).name);%#ok<AGROW>
        end
    end
    app.FromWorkspaceDropDown.Items = ValidInputVariables;
end

% Value changed function: FromWorkspaceDropDown
function FromWorkspaceDropDownValueChanged(app,event)
    % Callback function to read an image variable from the workspace and
    % load it to the app
    value = app.FromWorkspaceDropDown.Value;
    if ~strcmp(value,app.DefaultImportFromWorkspaceDropDownValue)
        imageData = evalin("base",value);
        app.resetAppOnNewDataLoad(imageData);
    end
    app.FromWorkspaceDropDown.Items = app.DefaultImportFromWorkspaceDropDownValue;
end

Export Filtered Images

Users can export the filtered images to files or to the workspace using the Export section of the app toolstrip. In both the To File and To Workspace options, the user can select either of the filtered images to export to a file or to the workspace. The ToFileDropDownValueChanged and ToWorkspaceDropDownValueChanged methods define these respective behaviors.

% Value changed function: ToFileDropDown
function ToFileDropDownValueChanged(app,event)
    % Callback function to write filtered image to disk
    value = app.ToFileDropDown.Value;
    switch value
        case "Image 1"
            imageToExport = app.LeftComponent.Image;
        case "Image 2"
            imageToExport = app.RightComponent.Image;
        otherwise
            return;
    end
    filterSpec = app.getSupportedFileFilter(true);
    [file,location] = uiputfile(filterSpec,"Save Filtered Image","filteredImage.png");
    if ~(isequal(file,0) || isequal(location,0))
        try
            imwrite(imageToExport,fullfile(location,file));
            uialert(app.ImageFilterComparisonToolFigure, ...
                "Filtered image saved successfully", ...
                "Export Success", ...
                Icon="success");
        catch ME
            uialert(app.ImageFilterComparisonToolFigure,ME.message,"Export Failed");
        end
    end
end

% Value changed function: ToWorkspaceDropDown
function ToWorkspaceDropDownValueChanged(app,event)
    % Callback function to write filtered image to base workspace
    value = app.ToWorkspaceDropDown.Value;
    switch value
        case "Image 1"
            imageToExport = app.LeftComponent.Image;
            assignin("base","filteredImageLeft",imageToExport);
        case "Image 2"
            imageToExport = app.RightComponent.Image;
            assignin("base","filteredImageRight",imageToExport);
        otherwise
            return;
    end
    uialert(app.ImageFilterComparisonAppFigure, ...
        "Filtered image saved to workspace successfully", ...
        "Export Success", ...
        Icon="success");
end

Compare Image Filters Using the App

Run the ImageFilterComparisonApp app.

Import an image either from a file or from the workspace using the options in the Import section of the app toolstrip. If you choose to import the image from a file, the app opens a dialog box enabling you to browse files that have image file formats. If you choose to import the image from the workspace, the app filters workspace variables for potential images and displays the variable names in the drop-down. When you import an image, the app resets and displays the imported image as both the Left Image and Right Image.

Import image into the app.

Select filtering methods for both the Left Image and Right Image from the Left Image's filter and Right Image's filter sections, respectively. For example, to compare the result of imgaussfilt and imboxfilt on the image, select imgaussfilt from the Select Filter drop-down for the left image, and the imboxfilt from the Select Filter drop-down for the right image. Specify parameters for the left image filter. Observe that the left image updates immediately. Similarly, specify parameters for the right image filter. Observe that the right image updates immediately.

Compare image filtering methods in the app.

You can export the filtered images to files or to the workspace. For example, to export the left image to a file, select the Image 1 option in the To File drop-down. To export the right image to the workspace, select the Image 2 option in the To Workspace drop-down.

Customize the App

You can customize the code of the ImageFilter custom UI component and ImageFilterComparisonApp app in the attached supporting files. You can support more image filtering and enhancement functions by adding them to the list of filters, adding the UI elements required for their parameters, and adding the corresponding filtering function call to the update method of the ImageFilter component. You can also add support for comparing more than two filtering methods by adding more cells to the grid layout of the app and associating ImageFilter components with them.

To customize the app, you can choose one of these options:

  • Open the attached MLAPP files in App Designer and edit the code in the Code View.

  • Open the attached MLAPP files in App Designer, select Share in the Designer tab and then Export to MATLAB Class (.m), and save the M file. You can then edit the M file.

See Also

Apps

Properties

Functions

Topics