annotate toolboxes/MIRtoolbox1.3.2/MIRToolbox/convolve2.m @ 0:e9a9cd732c1e tip

first hg version after svn
author wolffd
date Tue, 10 Feb 2015 15:05:51 +0000
parents
children
rev   line source
wolffd@0 1 function y = convolve2(x, m, shape, tol)
wolffd@0 2 %CONVOLVE2 Two dimensional convolution.
wolffd@0 3 % Y = CONVOLVE2(X, M) performs the 2-D convolution of matrices X and
wolffd@0 4 % M. If [mx,nx] = size(X) and [mm,nm] = size(M), then size(Y) =
wolffd@0 5 % [mx+mm-1,nx+nm-1]. Values near the boundaries of the output array are
wolffd@0 6 % calculated as if X was surrounded by a border of zero values.
wolffd@0 7 %
wolffd@0 8 % Y = CONVOLVE2(X, M, SHAPE) where SHAPE is a string returns a
wolffd@0 9 % subsection of the 2-D convolution with size specified by SHAPE:
wolffd@0 10 %
wolffd@0 11 % 'full' - (default) returns the full 2-D convolution,
wolffd@0 12 % 'same' - returns the central part of the convolution
wolffd@0 13 % that is the same size as A (using zero padding),
wolffd@0 14 % 'valid' - returns only those parts of the convolution
wolffd@0 15 % that are computed without the zero-padded
wolffd@0 16 % edges, size(Y) = [mx-mm+1,nx-nm+1] when
wolffd@0 17 % size(X) > size(M),
wolffd@0 18 % 'wrap' - as for 'same' except that instead of using
wolffd@0 19 % zero-padding the input A is taken to wrap round as
wolffd@0 20 % on a toroid.
wolffd@0 21 % 'reflect' - as for 'same' except that instead of using
wolffd@0 22 % zero-padding the input A is taken to be reflected
wolffd@0 23 % at its boundaries.
wolffd@0 24 %
wolffd@0 25 % CONVOLVE2 is fastest when mx > mm and nx > nm - i.e. the first
wolffd@0 26 % argument is the input and the second is the mask.
wolffd@0 27 %
wolffd@0 28 % If the rank of the mask M is low, CONVOLVE2 will decompose it into a
wolffd@0 29 % sum of outer product masks, each of which is applied efficiently as
wolffd@0 30 % convolution with a row vector and a column vector, by calling CONV2.
wolffd@0 31 % The function will often be faster than CONV2 or FILTER2 (in some
wolffd@0 32 % cases much faster) and will produce the same results as CONV2 to
wolffd@0 33 % within a small tolerance.
wolffd@0 34 %
wolffd@0 35 % Y = CONVOLVE2(... , TOL) where TOL is a number in the range 0.0 to
wolffd@0 36 % 1.0 computes the convolution using a reduced-rank approximation to
wolffd@0 37 % M, provided this will speed up the computation. TOL limits the
wolffd@0 38 % relative sum-squared error in the effective mask; that is, if the
wolffd@0 39 % effective mask is E, the error is controlled such that
wolffd@0 40 %
wolffd@0 41 % sum(sum( (M-E) .* (M-E) ))
wolffd@0 42 % -------------------------- <= TOL
wolffd@0 43 % sum(sum( M .* M ))
wolffd@0 44 %
wolffd@0 45 % See also CONV2, FILTER2.
wolffd@0 46
wolffd@0 47 % David Young, Department of Informatics, University of Sussex, February 2002,
wolffd@0 48 % revised January 2005.
wolffd@0 49
wolffd@0 50 % Deal with optional arguments
wolffd@0 51 error(nargchk(2,4,nargin));
wolffd@0 52 if nargin < 3
wolffd@0 53 shape = 'full'; % shape default as for CONV2
wolffd@0 54 tol = 0;
wolffd@0 55 elseif nargin < 4
wolffd@0 56 if isnumeric(shape)
wolffd@0 57 tol = shape;
wolffd@0 58 shape = 'full';
wolffd@0 59 else
wolffd@0 60 tol = 0;
wolffd@0 61 end
wolffd@0 62 end;
wolffd@0 63
wolffd@0 64 % Set up to do the wrap & reflect operations, not handled by conv2
wolffd@0 65 if strcmp(shape, 'wrap')
wolffd@0 66 x = wraparound(x, m);
wolffd@0 67 shape = 'valid';
wolffd@0 68 elseif strcmp(shape, 'reflect')
wolffd@0 69 x = reflectborders(x, m);
wolffd@0 70 shape = 'valid';
wolffd@0 71 end
wolffd@0 72
wolffd@0 73 % do the convolution itself
wolffd@0 74 y = doconv(x, m, shape, tol);
wolffd@0 75
wolffd@0 76 %-----------------------------------------------------------------------
wolffd@0 77
wolffd@0 78 function y = doconv(x, m, shape, tol);
wolffd@0 79 % Carry out convolution
wolffd@0 80 [mx, nx] = size(x);
wolffd@0 81 [mm, nm] = size(m);
wolffd@0 82
wolffd@0 83 % If the mask is bigger than the input, or it is 1-D already,
wolffd@0 84 % just let CONV2 handle it.
wolffd@0 85 if mm > mx | nm > nx | mm == 1 | nm == 1
wolffd@0 86 y = conv2(x, m, shape);
wolffd@0 87 else
wolffd@0 88 % Get svd of mask
wolffd@0 89 if mm < nm; m = m'; end % svd(..,0) wants m > n
wolffd@0 90 [u,s,v] = svd(m, 0);
wolffd@0 91 s = diag(s);
wolffd@0 92 rank = trank(m, s, tol);
wolffd@0 93 if rank*(mm+nm) < mm*nm % take advantage of low rank
wolffd@0 94 if mm < nm; t = u; u = v; v = t; end % reverse earlier transpose
wolffd@0 95 vp = v';
wolffd@0 96 % For some reason, CONV2(H,C,X) is very slow, so use the normal call
wolffd@0 97 y = conv2(conv2(x, u(:,1)*s(1), shape), vp(1,:), shape);
wolffd@0 98 for r = 2:rank
wolffd@0 99 y = y + conv2(conv2(x, u(:,r)*s(r), shape), vp(r,:), shape);
wolffd@0 100 end
wolffd@0 101 else
wolffd@0 102 if mm < nm; m = m'; end % reverse earlier transpose
wolffd@0 103 y = conv2(x, m, shape);
wolffd@0 104 end
wolffd@0 105 end
wolffd@0 106
wolffd@0 107 %-----------------------------------------------------------------------
wolffd@0 108
wolffd@0 109 function r = trank(m, s, tol)
wolffd@0 110 % Approximate rank function - returns rank of matrix that fits given
wolffd@0 111 % matrix to within given relative rms error. Expects original matrix
wolffd@0 112 % and vector of singular values.
wolffd@0 113 if tol < 0 | tol > 1
wolffd@0 114 error('Tolerance must be in range 0 to 1');
wolffd@0 115 end
wolffd@0 116 if tol == 0 % return estimate of actual rank
wolffd@0 117 tol = length(m) * max(s) * eps;
wolffd@0 118 r = sum(s > tol);
wolffd@0 119 else
wolffd@0 120 ss = s .* s;
wolffd@0 121 t = (1 - tol) * sum(ss);
wolffd@0 122 r = 0;
wolffd@0 123 sm = 0;
wolffd@0 124 while sm < t
wolffd@0 125 r = r + 1;
wolffd@0 126 sm = sm + ss(r);
wolffd@0 127 end
wolffd@0 128 end
wolffd@0 129
wolffd@0 130 %-----------------------------------------------------------------------
wolffd@0 131
wolffd@0 132 function y = wraparound(x, m)
wolffd@0 133 % Extend x so as to wrap around on both axes, sufficient to allow a
wolffd@0 134 % "valid" convolution with m to return the cyclical convolution.
wolffd@0 135 % We assume mask origin near centre of mask for compatibility with
wolffd@0 136 % "same" option.
wolffd@0 137 [mx, nx] = size(x);
wolffd@0 138 [mm, nm] = size(m);
wolffd@0 139 if mm > mx | nm > nx
wolffd@0 140 error('Mask does not fit inside array')
wolffd@0 141 end
wolffd@0 142
wolffd@0 143 mo = floor((1+mm)/2); no = floor((1+nm)/2); % reflected mask origin
wolffd@0 144 ml = mo-1; nl = no-1; % mask left/above origin
wolffd@0 145 mr = mm-mo; nr = nm-no; % mask right/below origin
wolffd@0 146 me = mx-ml+1; ne = nx-nl+1; % reflected margin in input
wolffd@0 147 mt = mx+ml; nt = nx+nl; % top of image in output
wolffd@0 148 my = mx+mm-1; ny = nx+nm-1; % output size
wolffd@0 149
wolffd@0 150 y = zeros(my, ny);
wolffd@0 151 y(mo:mt, no:nt) = x; % central region
wolffd@0 152 if ml > 0
wolffd@0 153 y(1:ml, no:nt) = x(me:mx, :); % top side
wolffd@0 154 if nl > 0
wolffd@0 155 y(1:ml, 1:nl) = x(me:mx, ne:nx); % top left corner
wolffd@0 156 end
wolffd@0 157 if nr > 0
wolffd@0 158 y(1:ml, nt+1:ny) = x(me:mx, 1:nr); % top right corner
wolffd@0 159 end
wolffd@0 160 end
wolffd@0 161 if mr > 0
wolffd@0 162 y(mt+1:my, no:nt) = x(1:mr, :); % bottom side
wolffd@0 163 if nl > 0
wolffd@0 164 y(mt+1:my, 1:nl) = x(1:mr, ne:nx); % bottom left corner
wolffd@0 165 end
wolffd@0 166 if nr > 0
wolffd@0 167 y(mt+1:my, nt+1:ny) = x(1:mr, 1:nr); % bottom right corner
wolffd@0 168 end
wolffd@0 169 end
wolffd@0 170 if nl > 0
wolffd@0 171 y(mo:mt, 1:nl) = x(:, ne:nx); % left side
wolffd@0 172 end
wolffd@0 173 if nr > 0
wolffd@0 174 y(mo:mt, nt+1:ny) = x(:, 1:nr); % right side
wolffd@0 175 end
wolffd@0 176
wolffd@0 177 %-----------------------------------------------------------------------
wolffd@0 178
wolffd@0 179 function y = reflectborders(x, m)
wolffd@0 180 % Extend x so as to reflect at each boundary, sufficient to allow a
wolffd@0 181 % "valid" convolution with m to return a matrix the same size as
wolffd@0 182 % the orginal.
wolffd@0 183 % We assume mask origin near centre of mask for compatibility with
wolffd@0 184 % "same" option.
wolffd@0 185 [mx, nx] = size(x);
wolffd@0 186 [mm, nm] = size(m);
wolffd@0 187 if mm > mx | nm > nx
wolffd@0 188 error('Mask does not fit inside array')
wolffd@0 189 end
wolffd@0 190
wolffd@0 191 mo = floor((1+mm)/2); no = floor((1+nm)/2); % reflected mask origin
wolffd@0 192 ml = mo-1; nl = no-1; % mask left/above origin
wolffd@0 193 mr = mm-mo; nr = nm-no; % mask right/below origin
wolffd@0 194 me = mx-mr+1; ne = nx-nr+1; % translated margin in input
wolffd@0 195 mt = mx+ml; nt = nx+nl; % top/right of image in output
wolffd@0 196 my = mx+mm-1; ny = nx+nm-1; % output size
wolffd@0 197
wolffd@0 198 y = zeros(my, ny);
wolffd@0 199 y(mo:mt, no:nt) = x; % central region
wolffd@0 200 if ml > 0
wolffd@0 201 y(1:ml, no:nt) = x(ml:-1:1, :); % top side
wolffd@0 202 if nl > 0
wolffd@0 203 y(1:ml, 1:nl) = x(ml:-1:1, nl:-1:1); % top left corner
wolffd@0 204 end
wolffd@0 205 if nr > 0
wolffd@0 206 y(1:ml, nt+1:ny) = x(ml:-1:1, nx:-1:ne); % top right corner
wolffd@0 207 end
wolffd@0 208 end
wolffd@0 209 if mr > 0
wolffd@0 210 y(mt+1:my, no:nt) = x(mx:-1:me, :); % bottom side
wolffd@0 211 if nl > 0
wolffd@0 212 y(mt+1:my, 1:nl) = x(mx:-1:me, nl:-1:1); % bottom left corner
wolffd@0 213 end
wolffd@0 214 if nr > 0
wolffd@0 215 y(mt+1:my, nt+1:ny) = x(mx:-1:me, nx:-1:ne); % bottom right corner
wolffd@0 216 end
wolffd@0 217 end
wolffd@0 218 if nl > 0
wolffd@0 219 y(mo:mt, 1:nl) = x(:, nl:-1:1); % left side
wolffd@0 220 end
wolffd@0 221 if nr > 0
wolffd@0 222 y(mo:mt, nt+1:ny) = x(:, nx:-1:ne); % right side
wolffd@0 223 end