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

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