]> Creatis software - CreaPhase.git/blob - octave_packages/image-1.0.15/padarray.m
Add a useful package (from Source forge) for octave
[CreaPhase.git] / octave_packages / image-1.0.15 / padarray.m
1 ## Copyright (C) 2004 Josep Mones i Teixidor
2 ##
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.
7 ##
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.
12 ##
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/>.
15
16 ## -*- texinfo -*-
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.
21 ##
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). 
25 ##
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).
30 ##
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
33 ## values are:
34 ##
35 ## @table @asis
36 ## @item 0
37 ## Pads with 0 as described above. This is the default behaviour.
38 ## @item Scalar
39 ## Pads using @var{padval} as a padding value.
40 ## @item "Circular"
41 ## Pads with a circular repetition of elements in @var{A} (similar to
42 ## tiling @var{A}).
43 ## @item "Replicate"
44 ## Pads replicating values of @var{A} which are at the border of the
45 ## array.
46 ## @item "Symmetric"
47 ## Pads with a mirror reflection of @var{A}.
48 ## @item "Reflect"
49 ## Same as "symmetric", but the borders are not used in the padding.
50 ## @end table
51 ##
52 ## B = padarray(A,padsize,padval,direction) pads @var{A} defining the
53 ## direction of the pad. Possible values are:
54 ##
55 ## @table @asis
56 ## @item "Both"
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.
60 ## @item "Pre"
61 ## For each dimension it pads before the first element the number of
62 ## elements defined by @var{padsize}.
63 ## @item "Post"
64 ## For each dimension it pads after the last element the number of
65 ## elements defined by @var{padsize}.
66 ## @end table
67 ## @end deftypefn
68
69 ## Author:  Josep Mones i Teixidor <jmones@puntbarra.com>
70
71 function B = padarray(A, padsize, padval = 0, direction = "both")
72   # Check parameters
73   if (nargin < 2 || nargin > 4)
74     print_usage();
75   endif
76
77   if (!isvector(padsize) || !isnumeric(padsize) || any(padsize < 0) || any(padsize != round(padsize)))
78     error("padarray: padsize must be a vector of positive integers.");
79   endif
80   if (!isscalar(padval) && !ischar(padval))
81     error("padarray: third input argument must be a string or a scalar");
82   endif
83   if (!ischar(direction) || strcmpi(direction, {"pre", "post", "both"}))
84     error("padarray: fourth input argument must be 'pre', 'post', or 'both'");
85   endif
86
87   ## Assure padsize is a row vector
88   padsize = padsize(:).';
89
90   # Check direction
91   pre  = any(strcmpi(direction, {"pre", "both"}));
92   post = any(strcmpi(direction, {"post", "both"}));
93   
94   B = A;
95   dim = 1;
96   for s = padsize
97     if (s > 0)
98       # padding in this dimension was requested
99       ds = size(B);
100       ds = [ds, ones(1,dim-length(ds))]; # data size
101       ps = ds;
102       ps(dim) = s;                     # padding size
103
104       if (ischar(padval))
105         # Init a "index all" cell array. All cases need it.
106         idx = cell(1, length(ds));
107         for i = 1:length(ds)
108           idx{i} = 1:ds(i);
109         endfor
110
111         switch (padval)
112           case ("circular")
113             complete = 0;
114             D = B;
115             if (ps(dim) > ds(dim))
116               complete = floor(ps(dim)/ds(dim));
117               ps(dim) = rem(ps(dim), ds(dim));
118             endif
119             if (pre)
120               for i = 1:complete
121                 B = cat(dim, D, B);
122               endfor
123               idxt = idx;
124               idxt{dim} = ds(dim)-ps(dim)+1:ds(dim);
125               B = cat(dim, D(idxt{:}), B);
126             endif
127             if (post)
128               for i = 1:complete
129                 B = cat(dim, B, D);
130               endfor
131               idxt = idx;
132               idxt{dim} = 1:ps(dim);
133               B = cat(dim, B, D(idxt{:}));
134             endif
135             # end circular case
136
137           case ("replicate")
138             if (pre)
139               idxt = idx;
140               idxt{dim} = 1;
141               pad = B(idxt{:});
142               # can we do this without the loop?        
143               for i = 1:s
144                 B = cat(dim, pad, B);
145               endfor
146             endif
147             if (post)
148               idxt = idx;
149               idxt{dim} = size(B, dim);
150               pad = B(idxt{:});
151               for i = 1:s
152                 B = cat(dim, B, pad);
153               endfor
154             endif
155             # end replicate case
156         
157           case ("symmetric")
158             if (ps(dim) > ds(dim))
159               error("padarray: padding is longer than data using symmetric padding");
160             endif
161             if (pre)
162               idxt = idx;
163               idxt{dim} = ps(dim):-1:1;
164               B = cat(dim, B(idxt{:}), B);
165             endif
166             if (post)
167               idxt = idx;
168               sbd = size(B, dim);
169               idxt{dim} = sbd:-1:sbd-ps(dim)+1;
170               B = cat(dim, B, B(idxt{:}));
171             endif
172             # end symmetric case
173
174           case ("reflect")
175             if (ps(dim) > ds(dim)-1)
176               error("padarray: padding is longer than data using 'reflect' padding");
177             endif
178             if (pre)
179               idxt = idx;
180               idxt{dim} = (ps(dim):-1:1) + 1;
181               B = cat(dim, B(idxt{:}), B);
182             endif
183             if (post)
184               idxt = idx;
185               sbd = size(B, dim)-1;
186               idxt{dim} = sbd:-1:sbd-ps(dim)+1;
187               B = cat(dim,B,B(idxt{:}));
188             endif
189             # end reflect case
190
191           otherwise
192             error("padarray: invalid string in padval parameter.");
193
194         endswitch
195         # end cases where padval is a string
196
197       elseif (isscalar(padval))
198         # Handle fixed value padding
199         if (padval == 0)
200           pad = zeros(ps, class(A));       ## class(pad) = class(A)
201         else
202           pad = padval*ones(ps, class(A)); ## class(pad) = class(A)
203         endif
204         if (pre && post)
205           # check if this is not quicker than just 2 calls (one for each)
206           B = cat(dim, pad, B, pad);
207         elseif (pre)
208           B = cat(dim, pad, B);
209         elseif (post)
210           B = cat(dim, B, pad);
211         endif
212       endif
213     endif
214     dim+=1;
215   endfor
216 endfunction
217
218 %!demo
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
221
222 %!demo
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
225
226 %!demo
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
229
230 %!demo
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
234
235 %!demo
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
239
240 %!demo
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 
244
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)]);
250
251 % Test padding on 3D array
252 %!test
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,                       \
255 %!      zeros(8,7),                                                     \
256 %!      [zeros(3,7); [zeros(2,2), [1,2,3;4,5,6], zeros(2,2)]; zeros(3,7)], \
257 %!      zeros(8,7))); 
258
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'));
262
263 % Test literal padval
264 %!assert(padarray([1;2],[1],i), [i;1;2;i]);
265
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]);
270
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]);
275
276 % Test vertical padsize
277 %!assert(padarray([1,2],[0;1],i,'both'), [i,1,2,i]);
278
279 % Test circular padding
280 %!test
281 %! A=[1,2,3;4,5,6];
282 %! B=repmat(A,7,9);
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));
288
289 % Test replicate padding
290 %!test
291 %! A=[1,2;3,4];
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);
296
297 % Test symmetric padding
298 %!test
299 %! A=[1:3;4:6];
300 %! HA=[3:-1:1;6:-1:4];
301 %! VA=[4:6;1:3];
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));
307
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]));
315
316 % Test circular padding with int* uint* class types
317 %!test
318 %! A=int8([1,2,3;4,5,6]);
319 %! B=repmat(A,7,9);
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));
325
326 % Test replicate padding with int* uint* class types
327 %!test
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);
334
335 % Test symmetric padding with int* uint* class types
336 %!test
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));
345
346
347
348 %
349 % $Log$
350 % Revision 1.2  2007/03/23 16:14:37  adb014
351 % Update the FSF address
352 %
353 % Revision 1.1  2006/08/20 12:59:35  hauberg
354 % Changed the structure to match the package system
355 %
356 % Revision 1.6  2005/09/08 02:00:17  pkienzle
357 % [for Bill Denney] isstr -> ischar
358 %
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
361 %
362 % Revision 1.4  2004/09/03 13:37:10  jmones
363 % Corrected behaviour for int* and uint* types
364 %
365 % Revision 1.3  2004/08/15 19:21:50  jmones
366 % support column vector padsize
367 %
368 % Revision 1.2  2004/08/11 15:04:59  pkienzle
369 % Convert dos line endings to unix line endings
370 %
371 % Revision 1.1  2004/08/08 21:20:25  jmones
372 % uintlut and padarray functions added
373 %
374 %