Back in October I introduced the concept of filter separability. A two-dimensional filter s is said to be separable if it can be written as the convolution of two one-dimensional filters v and h:
I said then that "next time" I would explain how to determine whether a given filter is separable. Well, I guess I got side-tracked, but I'm back on topic now.
This question gave me one of earliest opportunities at The MathWorks to wander down to company co-founder Cleve's office and ask for advice. I asked, "How can I determine if a matrix is an outer product of two vectors?" Cleve was very helpful, as he always is, although I was a little embarrassed afterward that I hadn't figured it out myself. "Go look at the rank function," Cleve told me.
Of course. If a matrix is an outer product of two vectors, its rank is 1. Here are the key lines of code in rank:
dbtype 15:19 rank
15 s = svd(A); 16 if nargin==1 17 tol = max(size(A)') * eps(max(s)); 18 end 19 r = sum(s > tol);
So the test is this: The rank of A is the number of nonzero singular values of A, with some numerical tolerance based on eps and the size of A.
Let's try it with a few common filters.
An averaging filter should be obvious:
averaging = ones(5,5) / 25; rank(averaging)
ans = 1
The Sobel kernel:
sobel = [-1 0 1; -2 0 2; -1 0 1]; rank(sobel)
ans = 1
The two-dimensional Gaussian is the only radially symmetric function that is also separable:
gaussian = fspecial('gaussian'); rank(gaussian)
ans = 1
A disk is not separable:
disk = fspecial('disk'); rank(disk)
ans = 5
So how can we determine the outer product vectors? The answer is to go back to the svd function. Here's a snippet from the doc: [U,S,V] = svd(X) produces a diagonal matrix S of the same dimension as X, with nonnegative diagonal elements in decreasing order, and unitary matrices U and V so that X = U*S*V'.
A rank 1 matrix has only one nonzero singular value, so U*S*V' becomes U(:,1) * S(1,1) * V(:,1)'. This is basically the outer product we were seeking. Therefore, we want the first columns of U and V. (We have to remember also to use the nonzero singular value as a scale factor.)
Let's try this with the Gaussian filter.
[U,S,V] = svd(gaussian)
U = -0.1329 0.9581 -0.2537 -0.9822 -0.1617 -0.0959 -0.1329 0.2364 0.9625 S = 0.6420 0 0 0 0.0000 0 0 0 0.0000 V = -0.1329 -0.6945 -0.7071 -0.9822 0.1880 0.0000 -0.1329 -0.6945 0.7071
Now get the horizontal and vertical vectors from the first columns of U and V.
v = U(:,1) * sqrt(S(1,1))
v = -0.1065 -0.7870 -0.1065
h = V(:,1)' * sqrt(S(1,1))
h = -0.1065 -0.7870 -0.1065
I have chosen, somewhat arbitrarily to split the scale factor, S(1,1), "equally" between v and h.
Now check to make sure this works:
gaussian - v*h
ans = 1.0e-015 * -0.0243 -0.1527 -0.0243 -0.0139 -0.1110 -0.0139 -0.0035 0 -0.0035
Except for normal floating-point roundoff differences, gaussian and v*h are equal.
Get the MATLAB code
Published with MATLAB® 7.3