Steve on Image Processing with MATLAB

Image processing concepts, algorithms, and MATLAB

Revisiting dctdemo – part 2

This is the second part of my plan to rewrite an Image Processing Toolbox example from 20 years ago using more modern MATLAB language features.

Recall the old app I'm trying to reinvent:

The idea is to experiment with the basic principles of DCT-based image compression. I've decided that I don't want to copy this original layout and functionality exactly. For one thing, computers (and MATLAB!) are a lot faster than they were 20 years ago, and I think we really don't need a separate "Apply" button.

Here's my first attempt at sketching what I'd like to make this time around.

The truth, is, I'm making the code and these posts up as I go along. So far I've only had a chance to get some of the initial pieces up and working. But it's enough to start exploring some of the coding ideas.

Here's what happens when I call the app class, which I've called (for now) dctCompressionExample_v2.

app = dctCompressionExample_v2
app = 

  dctCompressionExample_v2 handle

  Properties:
         OriginalImage: [240x240 double]
    ReconstructedImage: []
            ErrorImage: []
    DCTCoefficientMask: []
    NumDCTCoefficients: 10


You can see I got some output in the Command Window, and I also got a new figure on the screen.

Here's the code so far.

classdef dctCompressionExample_v2 < handle
    properties (SetAccess = private)
        OriginalImage
        ReconstructedImage
        ErrorImage
        DCTCoefficientMask
    end
    properties
        NumDCTCoefficients = 10
    end
    properties (Access = private)
        Slider
        OriginalImageAxes
        ReconstructedImageAxes
        ErrorImageAxes
        MaskAxes
    end
    methods
        function this_app = dctCompressionExample_v2
            this_app.OriginalImage = initialImage;
            layoutApp(this_app);
            update(this_app);
        end
        function layoutApp(app)
            f = figure;
            app.OriginalImageAxes = axes('Parent',f, ...
                'Position',[0.12 0.56 0.28 0.28]);
            app.ReconstructedImageAxes = axes('Parent',f, ...
                'Position',[0.60 0.56 0.28 0.28]);
            app.MaskAxes = axes('Parent',f, ...
                'Position',[0.12 0.16 0.28 0.28]);
            app.ErrorImageAxes = axes('Parent',f, ...
                'Position',[0.60 0.16 0.28 0.28]);
            app.Slider = uicontrol('Style','slider', ...
                'Value',app.NumDCTCoefficients/64, ...
                'Units','normalized', ...
                'Position',[0.12 0.08 0.28 0.04], ...
                'Callback',@(~,~,~) app.reactToSliderChange);
        end
        function reactToSliderChange(app)
            v = get(app.Slider,'Value');
            app.NumDCTCoefficients = round(64*v);
            update(app);
        end
        function update(app)
            imshow(app.OriginalImage,'Parent',app.OriginalImageAxes);
            title(app.OriginalImageAxes,'Original Image');
            imshow(app.OriginalImage,'Parent',app.ReconstructedImageAxes);
            title(app.ReconstructedImageAxes,'Reconstructed Image');
            imshow(zeros(size(app.OriginalImage)),'Parent',app.ErrorImageAxes);
            title(app.ErrorImageAxes,'Error Image');
            imshow(zeros(size(app.OriginalImage)),'Parent',app.MaskAxes);
            title(app.MaskAxes,'DCT Coefficient Mask');
            drawnow;
        end
    end
end
function I = initialImage
pout = imread('pout.tif');
pout2 = pout(1:240,:);
I = im2double(adapthisteq(pout2));
end

At this point I'll remind you of Dave Garrison's article that I mentioned last time. This article teaches about how this kind of code works. I don't intend to repeat all of that article here. I'll just point out a few things.

Here's the function that runs when you launch the app.

        function this_app = dctCompressionExample_v2
            this_app.OriginalImage = initialImage;
            layoutApp(this_app);
            update(this_app);
        end

initialImage is a little function I stuck at the bottom of the file. It's purpose is to create our sample image, which for the purpose of this simple DCT demonstration needs to be a multiple of 8-by-8 in size. (Plus, I threw in a call to adapthisteq because the original image has low contrast.)

function I = initialImage
pout = imread('pout.tif');
pout2 = pout(1:240,:);
I = im2double(adapthisteq(pout2));
end

Then there's the properties block.

    properties (SetAccess = private)
        OriginalImage
        ReconstructedImage
        ErrorImage
        DCTCoefficientMask
    end
    properties
        NumDCTCoefficients = 10
    end
    properties (Access = private)
        Slider
        OriginalImageAxes
        ReconstructedImageAxes
        ErrorImageAxes
        MaskAxes
    end

Some of the properties, such as OriginalImage, I want to make accessible but read-only. Some of them, such as OriginalImageAxes, are private because they are only needed by the guts of the app. Notice that one of them, NumDCTCoefficients, is both readable and settable. I wanted to explore the idea of giving this app a little programmable interface so that you can write code to make it change.

I would like to make it so that the NumDCTCoefficients property changes when you drag the slider. Here's how to accomplish that:

            app.Slider = uicontrol('Style','slider', ...
                'Value',app.NumDCTCoefficients/64, ...
                'Units','normalized', ...
                'Position',[0.12 0.08 0.28 0.04], ...
                'Callback',@(~,~,~) app.reactToSliderChange);

Setting the 'Callback' property of the slider like this causes the app's function reactToSliderChange to get called whenever the slider is dragged. Here's what reactToSliderChange does:

        function reactToSliderChange(app)
            v = get(app.Slider,'Value');
            app.NumDCTCoefficients = round(64*v);
            update(app);
        end

Now we're starting to get a little interactivity going in our app.

That's about as far as I got this week. Next time we'll continue wiring things up and maybe start putting in some algorithmic DCT block processing code.




Published with MATLAB® R2012b

|
  • print

Comments

To leave a comment, please click here to sign in to your MathWorks Account or create a new one.