Matlab/Octave: how to write n-dimensional zero padding algorithm without eval

225 views Asked by At

I would like to write a "syntactical sugar" Octave or Matlab zero-padding function, to which the user sends an n-dimensional object and a vector of <= n entries. The vector contains new, equal or larger dimensions for the object, and the object is zero-padded to match these dimensions. Any dimensions not specified are left alone. One expected use is, given for example a 5d block X of 3d medical image volumes, I can call

y = simplepad(X, [128 128 128]);

and thus pad the first three dimensions to a power of two for wavelet analysis (in fact I use a separate function nextpwr2 to find these dimensions) while leaving the others.

I have racked my brains on how to write this method avoiding the dreaded eval, but cannot thus far find a way. Can anyone suggest a solution? Here is more or less what I have:

function y = simplepad(x, pad)
szx = size(x);
n_pad = numel(pad);
szy = [pad szx(n_pad+1:end)];
y = zeros(szy);
indices_string = '(';
for n = 1:numel(szx)
    indices_string = [indices_string, '1:', num2str(szx(n))];
    if n < numel(szx)
        indices_string = [indices_string, ','];
    else
        indices_string = [indices_string, ')'];
    end
end
command = ['y',indices_string,'=x;'];
eval(command);
end
2

There are 2 answers

5
Andrei Davydov On BEST ANSWER

As I understand, you want just pass the some dynamic arguments to function. You can do this by converting these arguments to cell and call your function with passing cell content. So, your function will look like:

function y = simplepad(x, pad)
    szx = size(x);
    n_pad = numel(pad);
    szy = [pad szx(n_pad+1:end)];
    y = x;
    szyc = num2cell(szy);
    y(szyc{:}) = 0; % warning: assume x array only grows
end
0
gnovice On

Here's a solution that should handle all the little corner cases:

function A = simplepad(A, pad)

  % Add singleton dimensions (i.e. ones) to the ends of the old size of A 
  %   or pad as needed so they can be compared directly to one another:

  oldSize = size(A);
  dimChange = numel(pad)-numel(oldSize);
  oldSize = [oldSize ones(1, dimChange)];
  pad = [pad ones(1, -dimChange)];

  % If all of the sizes in pad are less than or equal to the sizes in
  %   oldSize, there is no padding done:

  if all(pad <= oldSize)
    return
  end

  % Use implicit zero expansion to pad:

  pad = num2cell(pad);
  A(pad{:}) = 0;

end

And a few test cases:

>> M = magic(3)
M =
     8     1     6
     3     5     7
     4     9     2
>> simplepad(M, [1 1])    % No change, since the all values are smaller
ans =
     8     1     6
     3     5     7
     4     9     2
>> simplepad(M, [1 4])    % Ignore the 1, pad the rows
ans =
     8     1     6     0
     3     5     7     0
     4     9     2     0
>> simplepad(M, [4 4])    % Pad rows and columns
ans =
     8     1     6     0
     3     5     7     0
     4     9     2     0
     0     0     0     0
>> simplepad(M, [4 4 2])  % Pad rows and columns and add a third dimension
ans(:,:,1) =
     8     1     6     0
     3     5     7     0
     4     9     2     0
     0     0     0     0
ans(:,:,2) =
     0     0     0     0
     0     0     0     0
     0     0     0     0
     0     0     0     0