I have the following function which calculates statistic parameters. I would like to pass this function to nlfilter
to do the calculation for a whole image. But the output of nlfilter
must be a scalar.
How can I convert this to a function handle suitable for use with nlfilter
so I can save the output of the function getStatistics2
?
The getStatistics2
function's output is an struct
array.
function [out] = getStatistics2(D)
D = double(D);
% out.MAX = max(D);%maximum
% out.MIN = min(D);%minimum
out.MEA = mean(D);%mean
out.MAD = mad(D);% mean absolute deviation y=mean(abs(X-mean(x)))
out.MED = median(D);%median
out.RAN = max(D) - min(D);%range
out.RMS = rms(D);%root mean square
out.STD = std(D);%stardard deviation
out.VAR= var(D);%variance
This is an interesting question. What's interesting is that your approach is almost perfect. The only reason it fails is because struct
cannot be constructed using a numeric scalar input (i.e. struct(3)
). The reason I mention this is because somewhere during the execution of nlfilter
(specifically in mkconstarray.m
), it calls the the following code:
repmat(feval(class, value), size);
Where:
class
is 'struct'
.
value
is 0
.
size
is the size()
of the input image, e.g. [100,100]
.
... and this fails because feval('struct', 0)
, which is equivalent to struct(0)
- and this we already know to be invalid.
So what do we do? Create a custom class that can be constructed this way!
Here's an example of one such class:
classdef MyStatsClass % Value class
properties (GetAccess = public, SetAccess = private)
MAX@double scalar = NaN; % Maximum
MIN@double scalar = NaN; % Minimum
MEA@double scalar = NaN; % Mean
MAD@double scalar = NaN; % Mean absolute deviation y = mean(abs(X-mean(x)))
MED@double scalar = NaN; % Median
RMS@double scalar = NaN; % Root mean square
STD@double scalar = NaN; % Stardard deviation
VAR@double scalar = NaN; % Variance
RAN@double scalar = NaN; % Range
end % properties
methods (Access = public)
%% Constructor:
function obj = MyStatsClass(vec)
%% Special case:
if (nargin == 0) || (numel(vec) == 1) && (vec == 0)
% This happens during nlfilter allocation
return
end
%% Regular case:
obj.MAX = max(vec(:));
obj.MIN = min(vec(:));
obj.MEA = mean(vec(:));
obj.MAD = mad(vec(:));
obj.MED = median(vec(:));
obj.RMS = rms(vec(:));
obj.STD = std(vec(:));
obj.VAR = var(vec(:));
obj.RAN = obj.MAX - obj.MIN;
end % default constructor
end % public methods
end % classdef
And here's how you can use it:
function imF = q35693068(outputAsStruct)
if nargin == 0 || ~islogical(outputAsStruct) || ~isscalar(outputAsStruct)
outputAsStruct = false;
end
rng(35693068); % Set the random seed, for repeatability
WINDOW_SZ = 3;
im = randn(100);
imF = nlfilter(im, [WINDOW_SZ WINDOW_SZ], @MyStatsClass);
% If output is strictly needed as a struct:
if outputAsStruct
warning off MATLAB:structOnObject
imF = arrayfun(@struct,imF);
warning on MATLAB:structOnObject
end
Notice that I have added an optional input (outputAsStruct
) that can force the output to be a struct
array (and not an array of the type of our custom class, which is functionally identical to a read-only struct
).
Notice also that by default nlfilter
pads your array with zeros, which means that the (1,1)
output will operate on an array that looks like this (assuming WINDOW_SZ=3
):
[0 0 0
0 1.8096 0.5189
0 -0.3434 0.6586]
and not on im(1:WINDOW_SZ,1:WINDOW_SZ)
which is:
[ 1.8096 0.5189 0.2811
-0.3434 0.6586 0.8919
-0.1525 0.7549 0.4497]
the "expected result" for im(1:WINDOW_SZ,1:WINDOW_SZ)
will be found further "inside" the output array (in the case of WINDOW_SZ=3
at index (2,2)
).