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
|