1 ## Copyright (C) 2004 Josep Mones i Teixidor
3 ## This program is free software; you can redistribute it and/or modify
4 ## it under the terms of the GNU General Public License as published by
5 ## the Free Software Foundation; either version 2 of the License, or
6 ## (at your option) any later version.
8 ## This program is distributed in the hope that it will be useful,
9 ## but WITHOUT ANY WARRANTY; without even the implied warranty of
10 ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 ## GNU General Public License for more details.
13 ## You should have received a copy of the GNU General Public License
14 ## along with this program; If not, see <http://www.gnu.org/licenses/>.
17 ## @deftypefn {Function File} {@var{B} = } padarray (@var{A},@var{padsize})
18 ## @deftypefnx {Function File} {@var{B} = } padarray (@var{A},@var{padsize},@var{padval})
19 ## @deftypefnx {Function File} {@var{B} = } padarray (@var{A},@var{padsize},@var{padval},@var{direction})
20 ## Pads an array in a configurable way.
22 ## B = padarray(A,padsize) pads an array @var{A} with zeros, where
23 ## @var{padsize} defines the amount of padding to add in each dimension
24 ## (it must be a vector of positive integers).
26 ## Each component of @var{padsize} defines the number of elements of
27 ## padding that will be added in the corresponding dimension. For
28 ## instance, [4,5] adds 4 elements of padding in first dimension (vertical)
29 ## and 5 in second dimension (horizontal).
31 ## B = padarray(A,padsize,padval) pads @var{A} using the value specified
32 ## by @var{padval}. @var{padval} can be a scalar or a string. Possible
37 ## Pads with 0 as described above. This is the default behaviour.
39 ## Pads using @var{padval} as a padding value.
41 ## Pads with a circular repetition of elements in @var{A} (similar to
44 ## Pads replicating values of @var{A} which are at the border of the
47 ## Pads with a mirror reflection of @var{A}.
49 ## Same as "symmetric", but the borders are not used in the padding.
52 ## B = padarray(A,padsize,padval,direction) pads @var{A} defining the
53 ## direction of the pad. Possible values are:
57 ## For each dimension it pads before the first element the number
58 ## of elements defined by @var{padsize} and the same number again after
59 ## the last element. This is the default value.
61 ## For each dimension it pads before the first element the number of
62 ## elements defined by @var{padsize}.
64 ## For each dimension it pads after the last element the number of
65 ## elements defined by @var{padsize}.
69 ## Author: Josep Mones i Teixidor <jmones@puntbarra.com>
71 function B = padarray(A, padsize, padval = 0, direction = "both")
73 if (nargin < 2 || nargin > 4)
77 if (!isvector(padsize) || !isnumeric(padsize) || any(padsize < 0) || any(padsize != round(padsize)))
78 error("padarray: padsize must be a vector of positive integers.");
80 if (!isscalar(padval) && !ischar(padval))
81 error("padarray: third input argument must be a string or a scalar");
83 if (!ischar(direction) || strcmpi(direction, {"pre", "post", "both"}))
84 error("padarray: fourth input argument must be 'pre', 'post', or 'both'");
87 ## Assure padsize is a row vector
88 padsize = padsize(:).';
91 pre = any(strcmpi(direction, {"pre", "both"}));
92 post = any(strcmpi(direction, {"post", "both"}));
98 # padding in this dimension was requested
100 ds = [ds, ones(1,dim-length(ds))]; # data size
102 ps(dim) = s; # padding size
105 # Init a "index all" cell array. All cases need it.
106 idx = cell(1, length(ds));
115 if (ps(dim) > ds(dim))
116 complete = floor(ps(dim)/ds(dim));
117 ps(dim) = rem(ps(dim), ds(dim));
124 idxt{dim} = ds(dim)-ps(dim)+1:ds(dim);
125 B = cat(dim, D(idxt{:}), B);
132 idxt{dim} = 1:ps(dim);
133 B = cat(dim, B, D(idxt{:}));
142 # can we do this without the loop?
144 B = cat(dim, pad, B);
149 idxt{dim} = size(B, dim);
152 B = cat(dim, B, pad);
158 if (ps(dim) > ds(dim))
159 error("padarray: padding is longer than data using symmetric padding");
163 idxt{dim} = ps(dim):-1:1;
164 B = cat(dim, B(idxt{:}), B);
169 idxt{dim} = sbd:-1:sbd-ps(dim)+1;
170 B = cat(dim, B, B(idxt{:}));
175 if (ps(dim) > ds(dim)-1)
176 error("padarray: padding is longer than data using 'reflect' padding");
180 idxt{dim} = (ps(dim):-1:1) + 1;
181 B = cat(dim, B(idxt{:}), B);
185 sbd = size(B, dim)-1;
186 idxt{dim} = sbd:-1:sbd-ps(dim)+1;
187 B = cat(dim,B,B(idxt{:}));
192 error("padarray: invalid string in padval parameter.");
195 # end cases where padval is a string
197 elseif (isscalar(padval))
198 # Handle fixed value padding
200 pad = zeros(ps, class(A)); ## class(pad) = class(A)
202 pad = padval*ones(ps, class(A)); ## class(pad) = class(A)
205 # check if this is not quicker than just 2 calls (one for each)
206 B = cat(dim, pad, B, pad);
208 B = cat(dim, pad, B);
210 B = cat(dim, B, pad);
219 %! padarray([1,2,3;4,5,6],[2,1])
220 %! % pads [1,2,3;4,5,6] with a whole border of 2 rows and 1 columns of 0
223 %! padarray([1,2,3;4,5,6],[2,1],5)
224 %! % pads [1,2,3;4,5,6] with a whole border of 2 rows and 1 columns of 5
227 %! padarray([1,2,3;4,5,6],[2,1],0,'pre')
228 %! % pads [1,2,3;4,5,6] with a left and top border of 2 rows and 1 columns of 0
231 %! padarray([1,2,3;4,5,6],[2,1],'circular')
232 %! % pads [1,2,3;4,5,6] with a whole 'circular' border of 2 rows and 1 columns
233 %! % border 'repeats' data as if we tiled blocks of data
236 %! padarray([1,2,3;4,5,6],[2,1],'replicate')
237 %! % pads [1,2,3;4,5,6] with a whole border of 2 rows and 1 columns which
238 %! % 'replicates' edge data
241 %! padarray([1,2,3;4,5,6],[2,1],'symmetric')
242 %! % pads [1,2,3;4,5,6] with a whole border of 2 rows and 1 columns which
243 %! % is symmetric to the data on the edge
245 % Test default padval and direction
246 %!assert(padarray([1;2],[1]), [0;1;2;0]);
247 %!assert(padarray([3,4],[0,2]), [0,0,3,4,0,0]);
248 %!assert(padarray([1,2,3;4,5,6],[1,2]), \
249 %! [zeros(1,7);0,0,1,2,3,0,0;0,0,4,5,6,0,0;zeros(1,7)]);
251 % Test padding on 3D array
253 %! int8(0); % fail for octave <= 2.1.57 without crashing
254 %! assert(padarray([1,2,3;4,5,6],[3,2,1]), cat(3, \
256 %! [zeros(3,7); [zeros(2,2), [1,2,3;4,5,6], zeros(2,2)]; zeros(3,7)], \
259 % Test if default param are ok
260 %!assert(padarray([1,2],[4,5])==padarray([1,2],[4,5],0));
261 %!assert(padarray([1,2],[4,5])==padarray([1,2],[4,5],0,'both'));
263 % Test literal padval
264 %!assert(padarray([1;2],[1],i), [i;1;2;i]);
266 % Test directions (horizontal)
267 %!assert(padarray([1;2],[1],i,'pre'), [i;1;2]);
268 %!assert(padarray([1;2],[1],i,'post'), [1;2;i]);
269 %!assert(padarray([1;2],[1],i,'both'), [i;1;2;i]);
271 % Test directions (vertical)
272 %!assert(padarray([1,2],[0,1],i,'pre'), [i,1,2]);
273 %!assert(padarray([1,2],[0,1],i,'post'), [1,2,i]);
274 %!assert(padarray([1,2],[0,1],i,'both'), [i,1,2,i]);
276 % Test vertical padsize
277 %!assert(padarray([1,2],[0;1],i,'both'), [i,1,2,i]);
279 % Test circular padding
283 %! assert(padarray(A,[1,2],'circular','pre'), B(2:4,2:6));
284 %! assert(padarray(A,[1,2],'circular','post'), B(3:5,4:8));
285 %! assert(padarray(A,[1,2],'circular','both'), B(2:5,2:8));
286 %! % This tests when padding is bigger than data
287 %! assert(padarray(A,[5,10],'circular','both'), B(2:13,3:25));
289 % Test replicate padding
292 %! B=kron(A,ones(10,5));
293 %! assert(padarray(A,[9,4],'replicate','pre'), B(1:11,1:6));
294 %! assert(padarray(A,[9,4],'replicate','post'), B(10:20,5:10));
295 %! assert(padarray(A,[9,4],'replicate','both'), B);
297 % Test symmetric padding
300 %! HA=[3:-1:1;6:-1:4];
302 %! VHA=[6:-1:4;3:-1:1];
303 %! B=[VHA,VA,VHA; HA,A,HA; VHA,VA,VHA];
304 %! assert(padarray(A,[1,2],'symmetric','pre'), B(2:4,2:6));
305 %! assert(padarray(A,[1,2],'symmetric','post'), B(3:5,4:8));
306 %! assert(padarray(A,[1,2],'symmetric','both'), B(2:5,2:8));
308 % Repeat some tests with int* uint* class types
309 %!assert(padarray(int8([1;2]),[1]), int8([0;1;2;0]));
310 %!assert(padarray(uint8([3,4]),[0,2]), uint8([0,0,3,4,0,0]));
311 %!assert(padarray(int16([1;2]),[1],4), int16([4;1;2;4]));
312 %!assert(padarray(uint16([1;2]),[1],0), uint16([0;1;2;0]));
313 %!assert(padarray(int32([1;2]),[1],int32(4),'pre'), int32([4;1;2]));
314 %!assert(padarray(uint32([1;2]),[1],6,'post'), uint32([1;2;6]));
316 % Test circular padding with int* uint* class types
318 %! A=int8([1,2,3;4,5,6]);
320 %! assert(padarray(A,[1,2],'circular','pre'), B(2:4,2:6));
321 %! assert(padarray(A,[1,2],'circular','post'), B(3:5,4:8));
322 %! assert(padarray(A,[1,2],'circular','both'), B(2:5,2:8));
323 %! % This tests when padding is bigger than data
324 %! assert(padarray(A,[5,10],'circular','both'), B(2:13,3:25));
326 % Test replicate padding with int* uint* class types
328 %! A=uint8([1,2;3,4]);
329 %! B=[ones(10,5,"uint8")*1,ones(10,5,"uint8")*2; \
330 %! ones(10,5,"uint8")*3,ones(10,5,"uint8")*4];
331 %! assert(padarray(A,[9,4],'replicate','pre'), B(1:11,1:6));
332 %! assert(padarray(A,[9,4],'replicate','post'), B(10:20,5:10));
333 %! assert(padarray(A,[9,4],'replicate','both'), B);
335 % Test symmetric padding with int* uint* class types
337 %! A=int16([1:3;4:6]);
338 %! HA=int16([3:-1:1;6:-1:4]);
339 %! VA=int16([4:6;1:3]);
340 %! VHA=int16([6:-1:4;3:-1:1]);
341 %! B=[VHA,VA,VHA; HA,A,HA; VHA,VA,VHA];
342 %! assert(padarray(A,[1,2],'symmetric','pre'), B(2:4,2:6));
343 %! assert(padarray(A,[1,2],'symmetric','post'), B(3:5,4:8));
344 %! assert(padarray(A,[1,2],'symmetric','both'), B(2:5,2:8));
350 % Revision 1.2 2007/03/23 16:14:37 adb014
351 % Update the FSF address
353 % Revision 1.1 2006/08/20 12:59:35 hauberg
354 % Changed the structure to match the package system
356 % Revision 1.6 2005/09/08 02:00:17 pkienzle
357 % [for Bill Denney] isstr -> ischar
359 % Revision 1.5 2004/09/03 18:33:11 pkienzle
360 % skip tests which use cat(3,X,Y) for octave <= 2.1.57
362 % Revision 1.4 2004/09/03 13:37:10 jmones
363 % Corrected behaviour for int* and uint* types
365 % Revision 1.3 2004/08/15 19:21:50 jmones
366 % support column vector padsize
368 % Revision 1.2 2004/08/11 15:04:59 pkienzle
369 % Convert dos line endings to unix line endings
371 % Revision 1.1 2004/08/08 21:20:25 jmones
372 % uintlut and padarray functions added