Creating a matrix containing a filled ellipse based on a non-contiguous outline - matlab

I'm trying to create a matrix of 0 values, with 1 values filling a ellipse shape. My ellipse was generated using minVolEllipse.m (Link 1) which returns a matrix of the ellipse equation in the 'center form' and the center of the ellipse. I then use a snippet of code from Ellipse_plot.m (from the aforementioned link) to parameterize the vector into major/minor axes, generate a parametric equation, and generate a matrix of transformed coordinates. You can see their code to see how this is done. The result is a matrix that has index locations for points along the ellipse. It does not encompass every value along the outline of the ellipse unless I set the number of grid points, N, to a ridiculously high value.
When I use the MATLAB plot or patch commands I see exactly the result I'm looking for. However, I want this represented as a matrix of 0 values with 1s where patch 'fills in' the blanks. It is apparent that MATLAB has this functionality, but I have yet to find the code to execute it. What I am looking for is similar to how bwfill of the image processing toolbox works (Link 2). bwfill does not work for me because my ellipse is not contiguous, so the function returns a matrix filled completely with 1 values.
Hopefully I have outlined the problem well enough, if not please comment and I can edit the post to clarify.
EDIT:
I have devised a strategy using the 2-D X vector from Ellipse_plot.m as an input to EllipseDirectFit.m (Link 3). This function returns the coefficients for the ellipse function ax^2+bxy+cy^2+dx+dy+f=0. Using these coefficients I calculate the angle between the x-axis and the major axis of the ellipse. This angle, along with the center and major/minor axes are passed into ellipseMatrix.m (Link 4), which returns a filled matrix. Unfortunately, the matrix appears to be out of rotation from what I want. Here is the portion of my code:
N = 20; %Number of grid points in ellipse
ellipsepoints = clusterpoints(clusterpoints(:,1)==i,2:3)';
[A,C] = minVolEllipse(ellipsepoints,0.001,N);
%%%%%%%%%%%%%%
%
%Adapted from:
% Ellipse_plot.m
% Nima Moshtagh
% nima#seas.upenn.edu
% University of Pennsylvania
% Feb 1, 2007
% Updated: Feb 3, 2007
%%%%%%%%%%%%%%
%
%
% "singular value decomposition" to extract the orientation and the
% axes of the ellipsoid
%------------------------------------
[U D V] = svd(A);
%
% get the major and minor axes
%------------------------------------
a = 1/sqrt(D(1,1))
b = 1/sqrt(D(2,2))
%theta values
theta = [0:1/N:2*pi+1/N];
%
% Parametric equation of the ellipse
%----------------------------------------
state(1,:) = a*cos(theta);
state(2,:) = b*sin(theta);
%
% Coordinate transform
%----------------------------------------
X = V * state;
X(1,:) = X(1,:) + C(1);
X(2,:) = X(2,:) + C(2);
% Output: Elip_Eq = [a b c d e f]' is the vector of algebraic
% parameters of the fitting ellipse:
Elip_Eq = EllipseDirectFit(X')
% http://mathworld.wolfram.com/Ellipse.html gives the equation for finding the angle theta (teta).
% The coefficients from EllipseDirectFit are rescaled to match what is expected in the wolfram link.
Elip_Eq(2) = Elip_Eq(2)/2;
Elip_Eq(4) = Elip_Eq(4)/2;
Elip_Eq(5) = Elip_Eq(5)/2;
if Elip_Eq(2)==0
if Elip_Eq(1) < Elip_Eq(3)
teta = 0;
else
teta = (1/2)*pi;
endif
else
tetap = (1/2)*acot((Elip_Eq(1)-Elip_Eq(3))/(Elip_Eq(2)));
if Elip_Eq(1) < Elip_Eq(3)
teta = tetap;
else
teta = (pi/2)+tetap;
endif
endif
blank_mask = zeros([height width]);
if teta < 0
teta = pi+teta;
endif
%I may need to switch a and b, depending on which is larger (so that the fist is the major axis)
filled_mask1 = ellipseMatrix(C(2),C(1),b,a,teta,blank_mask,1);
EDIT 2:
As a response to the suggestion from #BenVoigt, I have written a for-loop solution to the problem, here:
N = 20; %Number of grid points in ellipse
ellipsepoints = clusterpoints(clusterpoints(:,1)==i,2:3)';
[A,C] = minVolEllipse(ellipsepoints,0.001,N);
filled_mask = zeros([height width]);
for y=0:1:height
for x=0:1:width
point = ([x;y]-C)'*A*([x;y]-C);
if point < 1
filled_mask(y,x) = 1;
endif
endfor
endfor
Although this is technically a solution to the problem, I am interested in a non-iterative solution. I'm running this script over many large images, and need it to be very fast and parallel.
EDIT 3:
Thanks #mathematical.coffee for this solution:
[X,Y] = meshgrid(0:width,0:height);
fill_mask=arrayfun(#(x,y) ([x;y]-C)'*A*([x;y]-C),X,Y) < 1;
However, I believe there is yet a better way to do this. Here is a for-loop implementation that I did that runs faster than both above attempts:
ellip_mask = zeros([height width]);
[U D V] = svd(A);
a = 1/sqrt(D(1,1));
b = 1/sqrt(D(2,2));
maxab = ceil(max(a,b));
xstart = round(max(C(1)-maxab,1));
xend = round(min(C(1)+maxab,width));
ystart = round(max(C(2)-maxab,1));
yend = round(min(C(2)+maxab,height));
for y = ystart:1:yend
for x = xstart:1:xend
point = ([x;y]-C)'*A*([x;y]-C);
if point < 1
ellip_mask(y,x) = 1;
endif
endfor
endfor
Is there a way to accomplish this goal (the total image size is still [width height]) without this for-loop? The reason this is faster is because I don't have to iterate over the entire image to determine if my point is within the ellipse. Instead, I can simply iterate over a square region that is the length of the center +/- the largest principle axis.

Expanding the matrix multiply, which is an elliptic norm, gives a fairly simply vectorized expression:
[X,Y] = meshgrid(0:width,0:height);
X = X - C(1);
Y = Y - C(2);
fill_mask = X.^2 * A(1,1) + X.*Y * (A(1,2) + A(2,1)) + Y.^2 * A(2,2) < 1;
This is what I intended by my original comment.

Related

Finding the values of a scalar function on a 3D grid which correspond to the direction of slowest increase

I have some scalar function F(x,y,z) defined on a grid in 3D space, and there is a minimum of F somewhere in the array. Example code to generate such a function, and locate the coordinates of the minimum, is given below:
x = linspace(-10,80,100);
y = linspace(-20,5,100);
z = linspace(-10,10,100);
[X,Y,Z] = meshgrid(x,y,z);
F = some_scalar_function(X, Y, Z);
% Find the minimum of the function on the grid
[minval,ind] = min(F(:));
[ii,jj,kk] = ind2sub(size(F),ind);
xmin = x(jj);
ymin = y(ii);
zmin = z(kk);
figure;isosurface(X,Y,Z,F,minval+100)
% Some sample scalar function (assume it is given on the grid, and the analytic form not known)
function F = some_scalar_function(X, Y, Z)
F = (X-6).^2 + 10*(Y+2).^2 + 10*Z.^2 + 5*X.*Y;
end
I would like to obtain a vector of F values from the grid along some new direction (let's call it r) which corresponds to the direction of slowest increase of the function F, i.e starting from the minimum and "walking" outwards. I would also like to obtain the corresponding values of the coordinate r as well. I have tried to explain what I mean in the figure below:
Taking a path along any direction other than r should lead to a steeper increase in F, and is therefore not the correct route. Can anyone show how this can be done in Matlab? Thanks!
EDIT
After the comments from rahnema1 and Ander Biguri, I have run the command
[Gmag,Gazimuth,Gelevation] = imgradient3(F);
Taking a look at a plane through z=0, the function F(x,y,z=0) itself looks like the following:
and the outputs from imgradient3() look like this (again, only a single plane from the resulting full 3D arrays):
How can I obtain the line cut corresponding to path of slowest increase as a function of r from these? (still bearing in mind they are 3D arrays, and the direction is not necessarily constrained to the z=0 plane).
Consider a sphere that its center is the position of the minimum point of F.
For each point on the surface of the sphere compute the shortest path to the center point. use imerode to find the surface of the sphere and use shortestpathtree to compute the tree of shortest paths.
Gradient magnitude is set as the difficulty of path traversal. Use imgradient3 to calculate the gradient magnitude.
The path between the surface point with the minimum distance to the center , path1, is considered the direction of slowest increase of the function F.
But path1 is the half of the path. Other half ,path2, is computed by eliminating the sub-tree that contains the first surface point and again computing the shortest path tree using the resulting graph. The function im2col3 is defined for converting the 3D array to graph data structure.
Here is the non tested code:
% im2col for 3D image
function col = im2col3 (im)
col = zeros(prod(size(im)-2), 26);
n = 1;
for kk = 1:3
for jj = 1:3
for ii = 1:3
if ~(ii == 2 && jj == 2 && kk == 2)
col(:, n) = reshape(im(ii:end-(3-ii), jj:end-(3-jj), kk:end-(3-kk)),[],1);
n = n + 1;
end
end
end
end
end
% gradient magnitude
[Gmag,~,~] = imgradient3(F);
% convert array to graph
idx = reshape(1:numel(F), size(F));
t = im2col3(idx);
w = Gmag(t);
s = repmat(reshape(idx(2:end-1,2:end-1,2:end-1),[],1), size(t, 2));
G = graph(s, t, w);
% form the sphere
[~, sphere_center] = min(F(:));
[r, c, z] = ind2sub(size(F), sphere_center);
sphere_radius_2 = min([r c z]) ^ 2;
sphere_logical = ((1:size(F, 1)).'- r) .^ 2 ...
+ ((1:size(F, 2))- c) .^ 2 ...
+ (reshape(1:size(F, 3), 1, 1, [])- z) .^ 2 ...
< sphere_radius_2;
se = strel('cube',3);
sphere_surface = xor(imerode(sphere_logical, se), sphere_logical);
sphere_nodes = idx(sphere_surface);
% compute the half of the path
[T, D] = shortestpathtree(G, sphere_center, sphere_nodes,'OutputForm','cell');
[mn1, im1] = min(D);
path1 = T{im1};
% eliminate the sub-tree
subtree_root = path1(2);
subtree_nodes = bfsearch(T, subtree_root);
G1 = rmnodes (G, subtree_nodes);
sphere_nodes = setdiff (sphere_nodes, subtree_nodes);
% computing other half
[T1, D1] = shortestpathtree(G1, sphere_center, sphere_nodes,'OutputForm','cell');
[mn2, im2] = min(D1);
path2 = T1{im2};

Matlab - plot speed vector of a satellite in Keplerian orbit

I have to plot the speed vector of an object orbiting around a central body. This is a Keplerian context. The trajectory of object is deduced from the classical formula ( r = p/(1+e*cos(theta)) with e=eccentricity.
I manage into plotting the elliptical orbit but now, I would like to plot for each point of this orbit the velocity speed of object.
To compute the velocity vector, I start from classical formulas (into polar coordinates), below the 2 components :
v_r = dr/dt and v_theta = r d(theta)/dt
To take a time step dt, I extract the mean anomaly which is proportional to time.
And Finally, I compute the normalization of this speed vector.
clear % clear variables
e = 0.8; % eccentricity
a = 5; % semi-major axis
b = a*sqrt(1-e^2); % semi-minor axis
P = 10 % Orbital period
N = 200; % number of points defining orbit
nTerms = 10; % number of terms to keep in infinite series defining
% eccentric anomaly
M = linspace(0,2*pi,N); % mean anomaly parameterizes time
% M varies from 0 to 2*pi over one orbit
alpha = zeros(1,N); % preallocate space for eccentric anomaly array
%%%%%%%%%%
%%%%%%%%%% Calculations & Plotting
%%%%%%%%%%
% Calculate eccentric anomaly at each point in orbit
for j = 1:N
% initialize eccentric anomaly to mean anomaly
alpha(j) = M(j);
% include first nTerms in infinite series
for n = 1:nTerms
alpha(j) = alpha(j) + 2 / n * besselj(n,n*e) .* sin(n*M(j));
end
end
% calcualte polar coordiantes (theta, r) from eccentric anomaly
theta = 2 * atan(sqrt((1+e)/(1-e)) * tan(alpha/2));
r = a * (1-e^2) ./ (1 + e*cos(theta));
% Compute cartesian coordinates with x shifted since focus
x = a*e + r.*cos(theta);
y = r.*sin(theta);
figure(1);
plot(x,y,'b-','LineWidth',2)
xlim([-1.2*a,1.2*a]);
ylim([-1.2*a,1.2*a]);
hold on;
% Plot 2 focus = foci
plot(a*e,0,'ro','MarkerSize',10,'MarkerFaceColor','r');
hold on;
plot(-a*e,0,'ro','MarkerSize',10,'MarkerFaceColor','r');
% compute velocity vectors
for i = 1:N-1
vr(i) = (r(i+1)-r(i))/(P*(M(i+1)-M(i))/(2*pi));
vtheta(i) = r(i)*(theta(i+1)-theta(i))/(P*(M(i+1)-M(i))/(2*pi));
vrNorm(i) = vr(i)/norm([vr(i),vtheta(i)],1);
vthetaNorm(i) = vtheta(i)/norm([vr(i),vtheta(i)],1);
end
% Plot velocity vector
quiver(x(30),y(30),vrNorm(30),vthetaNorm(30),'LineWidth',2,'MaxHeadSize',1);
% Label plot with eccentricity
title(['Elliptical Orbit with e = ' sprintf('%.2f',e)]);
Unfortunately, once plot performed, it seems that I get a bad vector for speed. Here for example the 30th element of vrNorm and vthetaNorm arrays :
As you can see, the vector has the wrong direction (If I assume to take 0 for theta from the right axis and positive variation like into trigonometrics).
If someone could see where is my error, this would nice.
UPDATE 1: Has this vector representing the speed on elliptical orbit to be tangent permanently to the elliptical curve ?
I would like to represent it by taking the right focus as origin.
UPDATE 2:
With the solution of #MadPhysicist, I have modified :
% compute velocity vectors
vr(1:N-1) = (2*pi).*diff(r)./(P.*diff(M));
vtheta(1:N-1) = (2*pi).*r(1:N-1).*diff(theta)./(P.*diff(M));
% Plot velocity vector
for l = 1:9 quiver(x(20*l),y(20*l),vr(20*l)*cos(vtheta(20*l)),vr(20*l)*sin(vtheta(20*l)),'LineWidth',2,'MaxHeadSize',1);
end
% Label plot with eccentricity
title(['Elliptical Orbit with e = ' sprintf('%.2f',e)]);
I get the following result :
On some parts of the orbit, I get wrong directions and I don't understand why ...
There are two issues with your code:
The normalization is done incorrectly. norm computes the generalized p-norm for a vector, which defaults to the Euclidean norm. It expects Cartesian inputs. Setting p to 1 means that it will just return the largest element of your vector. In your case, the normalization is meaningless. Just set vrNorm as
vrNorm = vr ./ max(vr)
It appears that you are passing in the polar coordinates vrNorm and vthetaNorm to quiver, which expects Cartesian coordinates. It's easy to make the conversion in a vectorized manner:
vxNorm = vrNorm * cos(vtheta);
vyNorm = vrNorm * sin(vtheta);
This assumes that I understand where your angle is coming from correctly and that vtheta is in radians.
Note
The entire loop
for i = 1:N-1
vr(i) = (r(i+1)-r(i))/(P*(M(i+1)-M(i))/(2*pi));
vtheta(i) = r(i)*(theta(i+1)-theta(i))/(P*(M(i+1)-M(i))/(2*pi));
vrNorm(i) = vr(i)/norm([vr(i),vtheta(i)],1);
vthetaNorm(i) = vtheta(i)/norm([vr(i),vtheta(i)],1);
end
can be rewritten in a fully vectorized manner:
vr = (2 * pi) .* diff(r) ./ (P .* diff(M))
vtheta = (2 * pi) .* r .* diff(theta) ./ (P .* diff(M))
vrNorm = vr ./ max(vr)
vxNorm = vrNorm * cos(vtheta);
vyNorm = vrNorm * sin(vtheta);
Note 2
You can call quiver in a vectorized manner, on the entire dataset, or on a subset:
quiver(x(20:199:20), y(20:199:20), vxNorm(20:199:20), vyNorm(20:199:20), ...)

Plot quiver polar coordinates

I want to plot the field distribution inside a circular structure with radius a.
What I expect to see are circular arrows that from the centre 0 go toward a in the radial direction like this
but I'm obtaining something far from this result. I wrote this
x_np = besselzero(n, p, 1); %toolbox from mathworks.com for the roots
R = 0.1:1:a; PHI = 0:pi/180:2*pi;
for r = 1:size(R,2)
for phi = 1:size(PHI,2)
u_R(r,phi) = -1/2*((besselj(n-1,x_np*R(1,r)/a)-besselj(n+1,x_np*R(1,r)/a))/a)*cos(n*PHI(1,phi));
u_PHI(r,phi) = n*(besselj(n,x_np*R(1,r)/a)/(x_np*R(1,r)))*sin(PHI(1,phi));
end
end
[X,Y] = meshgrid(R);
quiver(X,Y,u_R,u_PHI)
where u_R is supposed to be the radial component and u_PHI the angular component. Supposing the formulas that I'm writing are correct, do you think there is a problem with for cycles? Plus, since R and PHI are not with the same dimension (in this case R is 1x20 and PHI 1X361) I also get the error
The size of X must match the size of U or the number of columns of U.
that I hope to solve it if I figure out which is the problem with the cycles.
This is the plot that I get
The problem has to do with a difference in co-ordinate systems.
quiver expects inputs in a Cartesian co-ordinate system.
The rest of your code seems to be expressed in a polar co-ordinate system.
Here's a snippet that should do what you want. The initial parameters section is filled in with random values because I don't have besselzero or the other details of your problem.
% Define initial parameters
x_np = 3;
a = 1;
n = 1;
% Set up domain (Cartesian)
x = -a:0.1:a;
y = -a:0.1:a;
[X, Y] = meshgrid(x, y);
% Allocate output
U = zeros(size(X));
V = zeros(size(X));
% Loop over each point in domain
for ii = 1:length(x)
for jj = 1:length(y)
% Compute polar representation
r = norm([X(ii,jj), Y(ii,jj)]);
phi = atan2(Y(ii,jj), X(ii,jj));
% Compute polar unit vectors
rhat = [cos(phi); sin(phi)];
phihat = [-sin(phi); cos(phi)];
% Compute output (in polar co-ordinates)
u_R = -1/2*((besselj(n-1, x_np*r/a)-besselj(n+1, x_np*r/a))/a)*cos(n*phi);
u_PHI = n*(besselj(n, x_np*r/a)/(x_np*r))*sin(phi);
% Transform output to Cartesian co-ordinates
U(ii,jj) = u_R*rhat(1) + u_PHI*phihat(1);
V(ii,jj) = u_R*rhat(2) + u_PHI*phihat(2);
end
end
% Generate quiver plot
quiver(X, Y, U, V);

eigenvalue decomposition of structure tensor in matlab

I have a synthetic image. I want to do eigenvalue decomposition of local structure tensor (LST) of it for some edge detection purposes. I used the eigenvaluesl1 , l2 and eigenvectors e1 ,e2 of LST to generate an adaptive ellipse for each pixel of image. Unfortunately I get unequal eigenvalues l1 , l2 and so unequal semi-axes length of ellipse for homogeneous regions of my figure:
However I get good response for a simple test image:
I don't know what is wrong in my code:
function [H,e1,e2,l1,l2] = LST_eig(I,sigma1,rw)
% LST_eig - compute the structure tensor and its eigen
% value decomposition
%
% H = LST_eig(I,sigma1,rw);
%
% sigma1 is pre smoothing width (in pixels).
% rw is filter bandwidth radius for tensor smoothing (in pixels).
%
n = size(I,1);
m = size(I,2);
if nargin<2
sigma1 = 0.5;
end
if nargin<3
rw = 0.001;
end
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% pre smoothing
J = imgaussfilt(I,sigma1);
% compute gradient using Sobel operator
Sch = [-3 0 3;-10 0 10;-3 0 3];
%h = fspecial('sobel');
gx = imfilter(J,Sch,'replicate');
gy = imfilter(J,Sch','replicate');
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% compute tensors
gx2 = gx.^2;
gy2 = gy.^2;
gxy = gx.*gy;
% smooth
gx2_sm = imgaussfilt(gx2,rw); %rw/sqrt(2*log(2))
gy2_sm = imgaussfilt(gy2,rw);
gxy_sm = imgaussfilt(gxy,rw);
H = zeros(n,m,2,2);
H(:,:,1,1) = gx2_sm;
H(:,:,2,2) = gy2_sm;
H(:,:,1,2) = gxy_sm;
H(:,:,2,1) = gxy_sm;
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% eigen decomposition
l1 = zeros(n,m);
l2 = zeros(n,m);
e1 = zeros(n,m,2);
e2 = zeros(n,m,2);
for i = 1:n
for j = 1:m
Hmat = zeros(2);
Hmat(:,:) = H(i,j,:,:);
[V,D] = eigs(Hmat);
D = abs(D);
l1(i,j) = D(1,1); % eigen values
l2(i,j) = D(2,2);
e1(i,j,:) = V(:,1); % eigen vectors
e2(i,j,:) = V(:,2);
end
end
Any help is appreciated.
This is my ellipse drawing code:
% determining ellipse parameteres from eigen value decomposition of LST
M = input('Enter the maximum allowed semi-major axes length: ');
I = input('Enter the input data: ');
row = size(I,1);
col = size(I,2);
a = zeros(row,col);
b = zeros(row,col);
cos_phi = zeros(row,col);
sin_phi = zeros(row,col);
for m = 1:row
for n = 1:col
a(m,n) = (l2(m,n)+eps)/(l1(m,n)+l2(m,n)+2*eps)*M;
b(m,n) = (l1(m,n)+eps)/(l1(m,n)+l2(m,n)+2*eps)*M;
cos_phi1 = e1(m,n,1);
sin_phi1 = e1(m,n,2);
len = hypot(cos_phi1,sin_phi1);
cos_phi(m,n) = cos_phi1/len;
sin_phi(m,n) = sin_phi1/len;
end
end
%% plot elliptic structuring elements using parametric equation and superimpose on the image
figure; imagesc(I); colorbar; hold on
t = linspace(0,2*pi,50);
for i = 10:10:row-10
for j = 10:10:col-10
x0 = j;
y0 = i;
x = a(i,j)/2*cos(t)*cos_phi(i,j)-b(i,j)/2*sin(t)*sin_phi(i,j)+x0;
y = a(i,j)/2*cos(t)*sin_phi(i,j)+b(i,j)/2*sin(t)*cos_phi(i,j)+y0;
plot(x,y,'r','linewidth',1);
hold on
end
end
This my new result with the Gaussian derivative kernel:
This is the new plot with axis equal:
I created a test image similar to yours (probably less complicated) as follows:
pos = yy([400,500]) + 100 * sin(xx(400)/400*2*pi);
img = gaussianlineclip(pos+50,7) + gaussianlineclip(pos-50,7);
I = double(stretch(img));
(This requires DIPimage to run)
Then ran your LST_eig on it (sigma1=1 and rw=3) and your code to draw ellipses (no change to either, except adding axis equal), and got this result:
I suspect some non-uniformity in some of the blue areas of your image, which cause very small gradients to appear. The problem with the definition of the ellipses as you use them is that, for sufficiently oriented patterns, you'll get a line even if that pattern is imperceptible. You can get around this by defining your ellipse axes lengths as follows:
a = repmat(M,size(l2)); % longest axis is always the same
b = M ./ (l2+1); % shortest axis is shorter the more important the largest eigenvalue is
The smallest eigenvalue l1 is high in regions with strong gradients but no clear direction. The above does not take this into account. One option could be to make a depend on both energy and anisotropy measures, and b depend only on energy:
T = 1000; % some threshold
r = M ./ max(l1+l2-T,1); % circle radius, smaller for higher energy
d = (l2-l1) ./ (l1+l2+eps); % anisotropy measure in range [0,1]
a = M*d + r.*(1-d); % use `M` length for high anisotropy, use `r` length for high isotropy (circle)
b = r; % use `r` width always
This way, the whole ellipse shrinks if there are strong gradients but no clear direction, whereas it stays large and circular when there are only weak or no gradients. The threshold T depends on image intensities, adjust as needed.
You should probably also consider taking the square root of the eigenvalues, as they correspond to the variance.
Some suggestions:
You can write
a = (l2+eps)./(l1+l2+2*eps) * M;
b = (l1+eps)./(l1+l2+2*eps) * M;
cos_phi = e1(:,:,1);
sin_phi = e1(:,:,2);
without a loop. Note that e1 is normalized by definition, there is no need to normalize it again.
Use Gaussian gradients instead of Gaussian smoothing followed by Sobel or Schaar filters. See here for some MATLAB implementation details.
Use eig, not eigs, when you need all eigenvalues. Especially for such a small matrix, there is no advantage to using eigs. eig seems to produce more consistent results. There is no need to take the absolute value of the eigenvalues (D = abs(D)), as they are non-negative by definition.
Your default value of rw = 0.001 is way too small, a sigma of that size has no effect on the image. The goal of this smoothing is to average gradients in a local neighborhood. I used rw=3 with good results.
Use DIPimage. There is a structuretensor function, Gaussian gradients, and a lot more useful stuff. The 3.0 version (still in development) is a major rewrite that improves significantly on dealing with vector- and matrix-valued images. I can write all of your LST_eig as follows:
I = dip_image(I);
g = gradient(I, sigma1);
H = gaussf(g*g.', rw);
[e,l] = eig(H);
% Equivalences with your outputs:
l1 = l{2};
l2 = l{1};
e1 = e{2,:};
e2 = e{1,:};

Fit plane to N dimensional points in MATLAB

I have a set of N points in k dimensions as a matrix of size N X k.
How can I find the best fitting line through these points? The line will be a plane (hyerpplane) in k dimensions. It will have k coefficients and one bias term.
Existing functions like fit seem to be usable only for points in 2 or 3 dimension.
You can fit a hyperplane (or any lower dimensional affine space) to a set of D dimensional data using Principal Component Analysis. Here's an example of fitting a plane to a set of 3D data. This is explained in more detail in the MATLAB documentation but I tried to construct the simplest example I could.
% generate some random correlated data
D = 3;
mu = zeros(1,D);
sqrt_sig = randn(D);
sigma = sqrt_sig'*sqrt_sig;
% generate 50 points in a D x 50 matrix
X = mvnrnd(mu, sigma, 50)';
% perform PCA
coeff = pca(X');
% The last principal component is normal to the best fit plane and plane goes through mean of X
a = coeff(:,D);
b = -mean(X,2)'*a;
% plane defined by a'*x + b = 0
dist = abs(a'*X+b) / norm(a);
mse = mean(dist.^2)
Edit: Added example plot of results for D = 3. I take advantage of the orthogonality of the other principal components here. Ignore the code if you want it's just to demonstrate that the plane does in fact fit the data pretty well.
% plot in 3D
X0 = bsxfun(#minus,X,mean(X,2));
b1 = coeff(:,1); b2 = coeff(:,2);
y1 = b1'*X0; y2 = b2'*X0;
y1_min = min(y1); y1_max = max(y1);
y1_span = y1_max - y1_min;
y2_min = min(y2); y2_max = max(y2);
y2_span = y2_max - y2_min;
pad = 0.2;
y1_min = y1_min - pad*y1_span;
y1_max = y1_max + pad*y1_span;
y2_min = y2_min - pad*y2_span;
y2_max = y2_max + pad*y2_span;
[y1_m,y2_m] = meshgrid(linspace(y1_min,y1_max,5), linspace(y2_min,y2_max,5));
grid = bsxfun(#plus, bsxfun(#times,y1_m(:)',b1) + bsxfun(#times,y2_m(:)',b2), mean(X,2));
x = reshape(grid(1,:),size(y1_m));
y = reshape(grid(2,:),size(y1_m));
z = reshape(grid(3,:),size(y1_m));
figure(1); clf(1);
surf(x,y,z,'FaceColor','black','FaceAlpha',0.3,'EdgeAlpha',0.6);
hold on;
plot3(X(1,:),X(2,:),X(3,:),' .');
axis equal;
axis vis3d;
Edit2: When I say "principal component" I'm being a bit sloppy (or just plain wrong) with the wording. I'm actually referring to the orthogonal basis vectors that the principal components are expressed in.
Here's a simpler solution, that just uses MATLAB's \ operator. We start with defining a plane in k dimensions:
% 0 = a + x(1) * b(1) + x(2) * b(2) + ... + x(k) * 1
k = 8;
a = randn(1);
b = randn(k-1,1);
(note that we assume b(k)=1, you can always multiply the plane parameters by any value without changing the plane).
Next we generate N random points within this plane:
N = 1000;
x = rand(N,k-1);
x(:,k) = -(a + x * b);
...sorry, it's not the best way to generate random points on the plane, but it's good enough for the demonstration here. Add noise to the points:
x = x + 0.05*randn(size(x));
To find the parameters of the plane, we solve the system of equations
% a + x(1:k-1) * b == -x(k)
in the least-squares sense. a and b are the unknowns there. We can rewrite the left-hand side as [1,x(1:k-1)] * [a;b]. If we have a matrix equation M*p=v we can solve for p by writing p=M\v:
p = [ones(N,1),x(:,1:k-1)]\(-x(:,k));
disp(['ground truth: [a,b,1] = ',mat2str([a,b',1],3)]);
disp(['estimated : [a,b,1] = ',mat2str([p',1],3)]);
This gives as output:
ground truth: [a,b,1] = [-1.35 -1.44 -1.48 1.17 0.226 -0.214 0.234 -1.59 1]
estimated : [a,b,1] = [-1.41 -1.38 -1.43 1.14 0.219 -0.195 0.221 -1.54 1]
The less noise or the more points in the dataset, the smaller the error will be of course!