问题
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
回答1:
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:
classis'struct'.valueis0.sizeis thesize()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)).
来源:https://stackoverflow.com/questions/35693068/how-to-output-a-struct-array-when-i-use-the-nlfilter-function