I'm trying to find vanishing points of vanishing lines to estimate a depth map for a 2D image.
First, I detected the vanishing lines of the 2D image using hough transform. Here is my code in Matlab:
Img =imread('landscape.bmp'); %read the 2D image
%Convert the image to Grayscale
I=rgb2gray(Img);
%Edge Detection
Ie=edge(I,'sobel');
%Hough Transform
[H,theta,rho] = hough(Ie);
% Finding the Hough peaks (number of peaks is set to 5)
P = houghpeaks(H,5,'threshold',ceil(0.2*max(H(:))));
x = theta(P(:,2));
y = rho(P(:,1));
%Vanishing lines
lines = houghlines(I,theta,rho,P,'FillGap',170,'MinLength',350);
[rows, columns] = size(Ie);
figure, imshow(~Ie)
hold on
xy_1 = zeros([2,2]);
for k = 1:length(lines)
xy = [lines(k).point1; lines(k).point2];
% Get the equation of the line
x1 = xy(1,1);
y1 = xy(1,2);
x2 = xy(2,1);
y2 = xy(2,2);
slope = (y2-y1)/(x2-x1);
xLeft = 1; % x is on the left edge
yLeft = slope * (xLeft - x1) + y1;
xRight = columns; % x is on the reight edge.
yRight = slope * (xRight - x1) + y1;
plot([xLeft, xRight], [yLeft, yRight], 'LineWidth',1,'Color','blue');
%intersection of two lines (the current line and the previous one)
slopee = #(line) (line(2,2) - line(1,2))/(line(2,1) - line(1,1));
m1 = slopee(xy_1);
m2 = slopee(xy);
intercept = #(line,m) line(1,2) - m*line(1,1);
b1 = intercept(xy_1,m1);
b2 = intercept(xy,m2);
xintersect = (b2-b1)/(m1-m2);
yintersect = m1*xintersect + b1;
plot(xintersect,yintersect,'m*','markersize',8, 'Color', 'red')
xy_1 = xy;
% Plot original points on the lines .
plot(xy(1,1),xy(1,2),'x','markersize',8,'Color','yellow');
plot(xy(2,1),xy(2,2),'x','markersize',8,'Color','green');
end
Now I need to find the vanishing point to be able to estimate the depth map.
The vanishing point is chosen as the intersection point with the greatest number of intersections around it.
My question, in other words, is how can I find the intersection of a number of lines (vanishing lines) in Matlab? I guess one way to do it is to find the point whose sum of squared distances from all lines is minimal, but not sure how to do that in Matlab?
Any help would be appreciated.
Edit: I tried to find the intersection of the lines, but I could only find the intersection of each a line and the line after it. I don't know how to find the intersection of all the lines?
Here is an example of a picture I am using:
https://www.dropbox.com/s/mbdt6v60ug1nymb/landscape.bmp?dl=0
I am posting a link because I don't have enough reputations to post an image.
A simplistic approach:
You should be able to create an array with all the intersection points between the lines.
Pseudo code:
for i = 1:length(lines)-1
for j = i+1:length(lines)
//add intersection of lines i and j
If you have all the intersections, you could simply take the average.
OR, take the approach written up here:
https://math.stackexchange.com/questions/61719/finding-the-intersection-point-of-many-lines-in-3d-point-closest-to-all-lines
3d can be simplified to 2d :)
Related
How to distribute the points to be like Fig.A
This matlab code for Fig. B :
N = 30; % number of points
r = 0.5; % r = radius
d = 50; % dimension
C_point = 0; % center point
figure, clf
C = ones(1, d) * C_point;
C_rep = repmat( C,N,1);
X = randn(N,d);
s2 = sum(X.^2,2) ;
radius = r * (rand(N,1).^(1/d));
X = X.*repmat(radius./sqrt(s2),1,d) + C_rep;
%% Plot 2D
t = linspace(0, 2*pi, 100);
x = r*cos(t) + C(1);
y = r*sin(t) + C(2);
plot(x,y,'b')
hold on
plot(C(1),C(2),'b.', 'MarkerSize', 10) % center point
hold on
plot(X(:,1), X(:,2),'r.','markersize',10);
axis equal;rotate3d off; rotate3d on;drawnow;shg;
hold on
ax = axis;
Source of the code
What I should change to be like fig. A
The OP's code computes points uniformly distributed within a d-dimensional box, projects those onto a d-dimensional sphere, then samples the radius to move them inside the d-dimensional ball. This is perfect except that the points inside the box, when projected onto the sphere, do not form a uniform distribution on that sphere. If instead you find random points distributed in a Gaussian distribution, you are guaranteed uniform angle distribution.
First compute points with a Gaussian distribution in d dimensions (I do all here with minimal changes to the OP's code):
N = 1000; % number of points
r = 0.5; % r = radius
d = 3; % dimension
C_point = 0; % center point
C = ones(1,d) * C_point;
C_rep = repmat(C,N,1);
X = randn(N,d);
Note that I use randn, not rand. randn creates a Gaussian distribution.
Next we normalize the vectors so the points move to the sphere:
nX = sqrt(sum(X.^2,2));
X = X./repmat(nX,1,d);
These points are uniformly distributed, which you can verify by scatter3(X(:,1),X(:,2),X(:,3)); axis equal and turning the display around (a 2D rendering doesn't do it justice). This is the reason I set d=3 above, and N=1000. I wanted to be able to plot the points and see lots of them.
Next we compute, as you already did, a random distance to the origin, and correct it for the dimensionality:
radius = r * (rand(N,1).^(1/d));
X = X.*repmat(radius,1,d) + C_rep;
X now is distributed uniformly in the ball. Again, scatter3(X(:,1),X(:,2),X(:,3)); axis equal shows this.
However, if you set d=50 and then plot only two dimensions of your data, you will not see the data filling the circle. And you will not see a uniform distribution either. This is because you are projecting a 50-D ball onto 2 dimensions, this simply does not work. You either have to trust the math, or you have to slice the data:
figure, hold on
t = linspace(0, 2*pi, 100);
x = r*cos(t) + C(1);
y = r*sin(t) + C(2);
plot(x,y,'b')
plot(C(1),C(2),'b.', 'MarkerSize', 10) % center point
axis equal
I = all(abs(X(:,3:d))<0.1,2);
plot(X(I,1), X(I,2),'r.','markersize',10);
The I there indexes points that are close to the origin in dimensions perpendicular to the first two shown. Again, with d=50 you will have very few points there, so you will need to set N very large! To see the same density of points as in the case above, for every dimension you add, you need to multiply N by 10. So for d=5 you'd have N=1000*10*10=1e5, and for d=50 you'd need N=1e50. That is totally impossible to compute, of course.
I want to generate equidistant points on a sphere (surface of the sphere). I have come up with this code.
n = 30; % number of points
r = 10; % radius of the sphere
thetha = 0:pi/(n/2):2*pi;
phi = -pi:2*pi/n:pi;
xp = r.*sin(phi).*cos(thetha);
yp = r.*sin(thetha).*sin(phi);
zp = r.*cos(phi);
figure;plot3(xp,yp,zp,'*')
But this is what I get
Can anyone tell where what mistake I am making in my code?
You're only generating one path: a figure eight combination of a single closed circle in the x-y plane with single cosine along the z.
To get a full sphere shape, permutations of the two paths must be taken. This can be accomplished with meshgrid:
[t,p] = meshgrid(thetha,phi);
xp = r.*sin(p).*cos(t);
yp = r.*sin(t).*sin(p);
zp = r.*cos(p);
plot3(xp,yp,zp,'-*');
grid('on');
box('on');
axis('square');
There is a noisy image as fig a. by reducing image noise finally a smoothspline could fit to the dots as shown in fig b. now it's desire to find two axillary lines both side of the original line like offset (in this case it's better to say outline). how can these lines(yellow and green) position be found?
if there is a simple straight line it would be easy but here is spline.
any idea would be appreciated.
I think this is what you wanted:
% generate random curve
xy = randi(5,[2,3]);
t = 1:3;
tq = linspace(1,3,100);
xyq = interp1(t',xy',tq','spline');
xx = xyq(:,1);
yy = xyq(:,2);
% get curve's approx. angle in each point
theta = atan2(xyq(2:end,2) - xyq(1:end-1,2),xyq(2:end,1) - xyq(1:end-1,1));
theta(end+1) = theta(end);
% add or subtract 90 degrees to get downward or upward normal angle
tp1 = theta + pi/2;
tp2 = theta - pi/2;
% distance from original curve
d = 0.1;
% compute x-y additions
[xa1,ya1] = pol2cart(tp1,d);
[xa2,ya2] = pol2cart(tp2,d);
% plot curve and its axillary lines
plot(xx,yy,'g')
hold on
plot(xyq(:,1) + xa1,xyq(:,2) + ya1,'b')
plot(xyq(:,1) + xa2,xyq(:,2) + ya2,'r')
legend('orig.','axil._1','axil._2');
and you get this:
Using the imfindcircles function in MATLAB to track circles in two images. I start with approximately a grid of circles which deforms. I am trying to sort the two column vector from imfindcircles into matrices so that neighbouring circles are neighbouring elements in the matrices. The first image the circles conform to a grid and the following code works:
[centXsort,IX] = sortrows(centres1,1); %sort by x
centYsort =zeros(289,2); %preallocate
for i = 1:17:289
[sortedY,IY] = sortrows(centXsort(i:i+16,:),2); %sort by y within individual column
centYsort(i:i+16,:) = sortedY;
end
cent1mat = reshape(centYsort,17,17,2); %reshape into centre matrices
This doesn't work for the second image as some of the circles overlap in the x or y direction, but neighbouring circles never overlap. This means that in the second set of matrices the neighbouring circles aren't neighbouring elements after sorting.
Is there a way to approximate a scatter of points into a matrix?
This answer doesn't work in every single case, but it seems good enough for situations where the points don't vary too wildly.
My idea is to start at the grid corners and work our way along the outside diagonals of the matrix, trying to "grab" the nearest points that seem like they fit into the grid-points based any surrounding points we've already captured.
You will need to provide:
The number of rows (rows) and columns (cols) in the grid.
Your data points P arranged in a N x 2 array, rescaled to the unit square on [0,1] x [0,1]. (I assume the you can do this through visual inspection of the corner points of your original data.)
A weight parameter edge_weight to tell the algorithm how much the border points should be attracted to the grid border. Some tests show that 3-5 or so are good values.
The code, with a test case included:
%// input parameters
rows = 11;
cols = 11;
edge_weight = 4;
%// function for getting squared errors between the points list P and a specific point pt
getErr =#(P,pt) sqrt( sum( bsxfun(#minus,P,pt(:)').^2, 2 ) ); %'
output_grid = zeros(rows,cols,2); %// output grid matrix
check_grid = zeros(rows,cols); %// matrix flagging the gridpoints we have covered
[ROW,COL] = meshgrid(... %// coordinate points of an "ideal grid"
linspace(0,1,rows),...
linspace(0,1,cols));
%// create a test case
G = [ROW(:),COL(:)]; %// the actual grid-points
noise_factor = 0.35; %// noise radius allowed
rn = noise_factor/rows;
cn = noise_factor/cols;
row_noise = -rn + 2*rn*rand(numel(ROW),1);
col_noise = -cn + 2*cn*rand(numel(ROW),1);
P = G + [row_noise,col_noise]; %// add noise to get points
%// MAIN LOOP
d = 0;
while ~isempty(P) %// while points remain...
d = d+1; %// increase diagonal depth (d=1 are the outer corners)
for ii = max(d-rows+1,1):min(d,rows)%// for every row number i...
i = ii;
j = d-i+1; %// on the dth diagonal, we have d=i+j-1
for c = 1:4 %// repeat for all 4 corners
if i<rows & j<cols & ~check_grid(i,j) %// check for out-of-bounds/repetitions
check_grid(i,j) = true; %// flag gridpoint
current_gridpoint = [ROW(i,j),COL(i,j)];
%// get error between all remaining points and the next gridpoint's neighbours
if i>1
errI = getErr(P,output_grid(i-1,j,:));
else
errI = edge_weight*getErr(P,current_gridpoint);
end
if check_grid(i+1,j)
errI = errI + edge_weight*getErr(P,current_gridpoint);
end
if j>1
errJ = getErr(P,output_grid(i,j-1,:));
else
errJ = edge_weight*getErr(P,current_gridpoint);
end
if check_grid(i,j+1)
errJ = errJ + edge_weight*getErr(P,current_gridpoint);
end
err = errI.^2 + errJ.^2;
%// find the point with minimal error, add it to the grid,
%// and delete it from the points list
[~,idx] = min(err);
output_grid(i,j,:) = permute( P(idx,:), [1 3 2] );
P(idx,:) = [];
end
%// rotate the grid 90 degrees and repeat for next corner
output_grid = cat(3, rot90(output_grid(:,:,1)), rot90(output_grid(:,:,2)));
check_grid = rot90(check_grid);
ROW = rot90(ROW);
COL = rot90(COL);
end
end
end
Code for plotting the resulting points with edges:
%// plotting code
figure(1); clf; hold on;
axis([-0.1 1.1 -0.1 1.1])
for i = 1:size(output_grid,1)
for j = 1:size(output_grid,2)
scatter(output_grid(i,j,1),output_grid(i,j,2),'b')
if i < size(output_grid,1)
plot( [output_grid(i,j,1),output_grid(i+1,j,1)],...
[output_grid(i,j,2),output_grid(i+1,j,2)],...
'r');
end
if j < size(output_grid,2)
plot( [output_grid(i,j,1),output_grid(i,j+1,1)],...
[output_grid(i,j,2),output_grid(i,j+1,2)],...
'r');
end
end
end
I've developed a solution, which works for my case but might not be as robust as required for some. It requires a known number of dots in a 'square' grid and a rough idea of the spacing between the dots. I find the 'AlphaShape' of the dots and all the points that lie along the edge. The edge vector is shifted to start at the minimum and then wrapped around a matrix with the corresponding points are discarded from the list of vertices. Probably not the best idea for large point clouds but good enough for me.
R = 50; % search radius
xy = centres2;
x = centres2(:,1);
y = centres2(:,2);
for i = 1:8
T = delaunay(xy); % delaunay
[~,r] = circumcenter(triangulation(T,x,y)); % circumcenters
T = T(r < R,:); % points within radius
B = freeBoundary(triangulation(T,x,y)); % find edge vertices
A = B(:,1);
EdgeList = [x(A) y(A) x(A)+y(A)]; % find point closest to origin and rotate vector
[~,I] = min(EdgeList);
EdgeList = circshift(EdgeList,-I(3)+1);
n = sqrt(length(xy)); % define zeros matrix
matX = zeros(n); % wrap x vector around zeros matrix
matX(1,1:n) = EdgeList(1:n,1);
matX(2:n-1,n) = EdgeList(n+1:(2*n)-2,1);
matX(n,n:-1:1) = EdgeList((2*n)-1:(3*n)-2,1);
matX(n-1:-1:2,1) = EdgeList((3*n)-1:(4*n)-4,1);
matY = zeros(n); % wrap y vector around zeros matrix
matY(1,1:n) = EdgeList(1:n,2);
matY(2:n-1,n) = EdgeList(n+1:(2*n)-2,2);
matY(n,n:-1:1) = EdgeList((2*n)-1:(3*n)-2,2);
matY(n-1:-1:2,1) = EdgeList((3*n)-1:(4*n)-4,2);
centreMatX(i:n+i-1,i:n+i-1) = matX; % paste into main matrix
centreMatY(i:n+i-1,i:n+i-1) = matY;
xy(B(:,1),:) = 0; % discard values
xy = xy(all(xy,2),:);
x = xy(:,1);
y = xy(:,2);
end
centreMatX(centreMatX == 0) = x;
centreMatY(centreMatY == 0) = y;
Does anyone know how to use the Hough transform to detect the strongest lines in the binary image:
A = zeros(7,7);
A([6 10 18 24 36 38 41]) = 1;
Using the (rho; theta) format with theta in steps of 45° from -45° to 90°. And how do I show the accumulator array in MATLAB as well.
Any help or hints please?
Thank you!
If you have access to the Image Processing Toolbox, you can use the functions HOUGH, HOUGHPEAKS, and HOUGHLINES:
%# your binary image
BW = false(7,7);
BW([6 10 18 24 36 38 41]) = true;
%# hough transform, detect peaks, then get lines segments
[H T R] = hough(BW);
P = houghpeaks(H, 4);
lines = houghlines(BW, T, R, P, 'MinLength',2);
%# show accumulator matrix and peaks
imshow(H./max(H(:)), [], 'XData',T, 'YData',R), hold on
plot(T(P(:,2)), R(P(:,1)), 'gs', 'LineWidth',2);
xlabel('\theta'), ylabel('\rho')
axis on, axis normal
colormap(hot), colorbar
%# overlay detected lines over image
figure, imshow(BW), hold on
for k = 1:length(lines)
xy = [lines(k).point1; lines(k).point2];
plot(xy(:,1), xy(:,2), 'g.-', 'LineWidth',2);
end
hold off
Each pixel (x,y) maps to a set of lines (rho,theta) that run through it.
Build an accumulator matrix indexed by (rho theta).
For each point (x,y) that is on, generate all the quantized (rho, theta) values that correspond to (x,y) and increment the corresponding point in the accumulator.
Finding the strongest lines corresponds to finding peaks in the accumulator.
In practice, the descritization of the polar parameters is important to get right. Too fine and not enough points will overlap. Too coarse and each bin could correspond to multiple lines.
in pseudo code with liberties:
accum = zeros(360,100);
[y,x] = find(binaryImage);
y = y - size(binaryImage,1)/2; % use locations offset from the center of the image
x = x - size(binaryImage,2)/2;
npts = length(x);
for i = 1:npts
for theta = 1:360 % all possible orientations
rho = %% use trigonometry to find minimum distance between origin and theta oriented line passing through x,y here
q_rho = %% quantize rho so that it fits neatly into the accumulator %%
accum(theta,rho) = accum(theta,rho) + 1;
end
end