diff 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
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/toolboxes/MIRtoolbox1.3.2/MIRToolbox/convolve2.m	Tue Feb 10 15:05:51 2015 +0000
@@ -0,0 +1,223 @@
+function y = convolve2(x, m, shape, tol)
+%CONVOLVE2 Two dimensional convolution.
+%   Y = CONVOLVE2(X, M) performs the 2-D convolution of matrices X and
+%   M. If [mx,nx] = size(X) and [mm,nm] = size(M), then size(Y) =
+%   [mx+mm-1,nx+nm-1]. Values near the boundaries of the output array are
+%   calculated as if X was surrounded by a border of zero values.
+%
+%   Y = CONVOLVE2(X, M, SHAPE) where SHAPE is a string returns a
+%   subsection of the 2-D convolution with size specified by SHAPE:
+%
+%       'full'    - (default) returns the full 2-D convolution,
+%       'same'    - returns the central part of the convolution
+%                   that is the same size as A (using zero padding),
+%       'valid'   - returns only those parts of the convolution
+%                   that are computed without the zero-padded
+%                   edges, size(Y) = [mx-mm+1,nx-nm+1] when
+%                   size(X) > size(M),
+%       'wrap'    - as for 'same' except that instead of using
+%                   zero-padding the input A is taken to wrap round as
+%                   on a toroid.
+%       'reflect' - as for 'same' except that instead of using
+%                   zero-padding the input A is taken to be reflected
+%                   at its boundaries.
+%
+%   CONVOLVE2 is fastest when mx > mm and nx > nm - i.e. the first
+%   argument is the input and the second is the mask.
+%
+%   If the rank of the mask M is low, CONVOLVE2 will decompose it into a
+%   sum of outer product masks, each of which is applied efficiently as
+%   convolution with a row vector and a column vector, by calling CONV2.
+%   The function will often be faster than CONV2 or FILTER2 (in some
+%   cases much faster) and will produce the same results as CONV2 to
+%   within a small tolerance.
+%
+%   Y = CONVOLVE2(... , TOL) where TOL is a number in the range 0.0 to
+%   1.0 computes the convolution using a reduced-rank approximation to
+%   M, provided this will speed up the computation. TOL limits the
+%   relative sum-squared error in the effective mask; that is, if the
+%   effective mask is E, the error is controlled such that
+%
+%       sum(sum( (M-E) .* (M-E) ))
+%       --------------------------    <=  TOL
+%            sum(sum( M .* M ))
+%
+%   See also CONV2, FILTER2.
+
+% David Young, Department of Informatics, University of Sussex, February 2002,
+%   revised January 2005.
+
+% Deal with optional arguments
+error(nargchk(2,4,nargin));
+if nargin < 3
+    shape = 'full';    % shape default as for CONV2
+    tol = 0;
+elseif nargin < 4
+    if isnumeric(shape)
+        tol = shape;
+        shape = 'full';
+    else
+        tol = 0;
+    end
+end;
+
+% Set up to do the wrap & reflect operations, not handled by conv2
+if strcmp(shape, 'wrap')
+    x = wraparound(x, m);
+    shape = 'valid';
+elseif strcmp(shape, 'reflect')
+    x = reflectborders(x, m);
+    shape = 'valid';
+end
+
+% do the convolution itself
+y = doconv(x, m, shape, tol);
+
+%-----------------------------------------------------------------------
+
+function y = doconv(x, m, shape, tol);
+% Carry out convolution
+[mx, nx] = size(x);
+[mm, nm] = size(m);
+
+% If the mask is bigger than the input, or it is 1-D already,
+% just let CONV2 handle it.
+if mm > mx | nm > nx | mm == 1 | nm == 1
+    y = conv2(x, m, shape);
+else
+    % Get svd of mask
+    if mm < nm; m = m'; end        % svd(..,0) wants m > n
+    [u,s,v] = svd(m, 0);
+    s = diag(s);
+    rank = trank(m, s, tol);
+    if rank*(mm+nm) < mm*nm         % take advantage of low rank
+        if mm < nm;  t = u; u = v; v = t; end  % reverse earlier transpose
+        vp = v';
+        % For some reason, CONV2(H,C,X) is very slow, so use the normal call
+        y = conv2(conv2(x, u(:,1)*s(1), shape), vp(1,:), shape);
+        for r = 2:rank
+            y = y + conv2(conv2(x, u(:,r)*s(r), shape), vp(r,:), shape);
+        end
+    else
+        if mm < nm; m = m'; end     % reverse earlier transpose
+        y = conv2(x, m, shape);
+    end
+end
+
+%-----------------------------------------------------------------------
+
+function r = trank(m, s, tol)
+% Approximate rank function - returns rank of matrix that fits given
+% matrix to within given relative rms error. Expects original matrix
+% and vector of singular values.
+if tol < 0 | tol > 1
+    error('Tolerance must be in range 0 to 1');
+end
+if tol == 0             % return estimate of actual rank
+    tol = length(m) * max(s) * eps;
+    r = sum(s > tol);
+else
+    ss = s .* s;
+    t = (1 - tol) * sum(ss);
+    r = 0;
+    sm = 0;
+    while sm < t
+        r = r + 1;
+        sm = sm + ss(r);
+    end
+end
+
+%-----------------------------------------------------------------------
+
+function y = wraparound(x, m)
+% Extend x so as to wrap around on both axes, sufficient to allow a
+% "valid" convolution with m to return the cyclical convolution.
+% We assume mask origin near centre of mask for compatibility with
+% "same" option.
+[mx, nx] = size(x);
+[mm, nm] = size(m);
+if mm > mx | nm > nx
+    error('Mask does not fit inside array')
+end
+
+mo = floor((1+mm)/2); no = floor((1+nm)/2);  % reflected mask origin
+ml = mo-1;            nl = no-1;             % mask left/above origin
+mr = mm-mo;           nr = nm-no;            % mask right/below origin
+me = mx-ml+1;         ne = nx-nl+1;          % reflected margin in input
+mt = mx+ml;           nt = nx+nl;            % top of image in output
+my = mx+mm-1;         ny = nx+nm-1;          % output size
+
+y = zeros(my, ny);
+y(mo:mt, no:nt) = x;      % central region
+if ml > 0
+    y(1:ml, no:nt) = x(me:mx, :);                   % top side
+    if nl > 0
+        y(1:ml, 1:nl) = x(me:mx, ne:nx);            % top left corner
+    end
+    if nr > 0
+        y(1:ml, nt+1:ny) = x(me:mx, 1:nr);          % top right corner
+    end
+end
+if mr > 0
+    y(mt+1:my, no:nt) = x(1:mr, :);                 % bottom side
+    if nl > 0
+        y(mt+1:my, 1:nl) = x(1:mr, ne:nx);          % bottom left corner
+    end
+    if nr > 0
+        y(mt+1:my, nt+1:ny) = x(1:mr, 1:nr);        % bottom right corner
+    end
+end
+if nl > 0
+    y(mo:mt, 1:nl) = x(:, ne:nx);                   % left side
+end
+if nr > 0
+    y(mo:mt, nt+1:ny) = x(:, 1:nr);                 % right side
+end
+
+%-----------------------------------------------------------------------
+
+function y = reflectborders(x, m)
+% Extend x so as to reflect at each boundary, sufficient to allow a
+% "valid" convolution with m to return a matrix the same size as
+% the orginal.
+% We assume mask origin near centre of mask for compatibility with
+% "same" option.
+[mx, nx] = size(x);
+[mm, nm] = size(m);
+if mm > mx | nm > nx
+    error('Mask does not fit inside array')
+end
+
+mo = floor((1+mm)/2); no = floor((1+nm)/2);  % reflected mask origin
+ml = mo-1;            nl = no-1;             % mask left/above origin
+mr = mm-mo;           nr = nm-no;            % mask right/below origin
+me = mx-mr+1;         ne = nx-nr+1;          % translated margin in input
+mt = mx+ml;           nt = nx+nl;            % top/right of image in output
+my = mx+mm-1;         ny = nx+nm-1;          % output size
+
+y = zeros(my, ny);
+y(mo:mt, no:nt) = x;      % central region
+if ml > 0
+    y(1:ml, no:nt) = x(ml:-1:1, :);                   % top side
+    if nl > 0
+        y(1:ml, 1:nl) = x(ml:-1:1, nl:-1:1);          % top left corner
+    end
+    if nr > 0
+        y(1:ml, nt+1:ny) = x(ml:-1:1, nx:-1:ne);      % top right corner
+    end
+end
+if mr > 0
+    y(mt+1:my, no:nt) = x(mx:-1:me, :);               % bottom side
+    if nl > 0
+        y(mt+1:my, 1:nl) = x(mx:-1:me, nl:-1:1);      % bottom left corner
+    end
+    if nr > 0
+        y(mt+1:my, nt+1:ny) = x(mx:-1:me, nx:-1:ne);  % bottom right corner
+    end
+end
+if nl > 0
+    y(mo:mt, 1:nl) = x(:, nl:-1:1);                   % left side
+end
+if nr > 0
+    y(mo:mt, nt+1:ny) = x(:, nx:-1:ne);               % right side
+end