4

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
Dev-iL
  • 23,742
  • 7
  • 57
  • 99
H.L.
  • 41
  • 2
  • You can't do it directly, as (as you write), `nlfilter` accepts a scalar function handle. You can write a wrapper that calls `nlfilter` once for each field of the `out` struct. – Itamar Katz Feb 29 '16 at 08:13
  • what's wrapper? I can't understand it well. Can you edit an example code for me? Now, I have an idea,I can save the out structure array as a mat file.But if I do this,there will be too many mat files.If I want to use the output result,it will take long time to load these mat files.I just want to save the output to one mat file .Do you have any good idea?Thank you! @Itamar Katz – H.L. Feb 29 '16 at 12:18
  • It is not clear what is your desired output. `nlfilter` returns a matrix, if your struct has `n` fields, and you have an array of such structs of length `m`, then you get `m*n` matrices. How do you want to store them? – Itamar Katz Feb 29 '16 at 18:53
  • Do you mean that I can split the function's out put.Just like this – H.L. Mar 01 '16 at 02:17
  • I can write n functions,each function just return one number.So I can get the result. – H.L. Mar 01 '16 at 02:19
  • Thank you! The problem has been solved.How can I adopt you answer and vote for you – H.L. Mar 01 '16 at 06:43
  • Thanks but it wasn't really an answer, seems you figured it out by yourself. If you think your solution may help others, you can answer your own question and accept it later. – Itamar Katz Mar 01 '16 at 08:24

1 Answers1

4

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)).

Graham
  • 7,431
  • 18
  • 59
  • 84
Dev-iL
  • 23,742
  • 7
  • 57
  • 99
  • 2
    Using `nlfilter` to output instances of a class rather than a single scalar output is totally thinking outside the box! – rayryeng Nov 19 '16 at 14:27