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.
评论
要发表评论,请点击 此处 登录到您的 MathWorks 帐户或创建一个新帐户。