Create 3D logical mask from points - matlab

I have a set of 3D points specifying points on a surface of an object. From these points, i need to construct a 3D logical mask. How can I solve this with matlab? Hope to get some insights.

% parameters
num_coordinates = 100;
max_coordinate = 20;
% generate random coordinate
x = sort(randi(max_coordinate, [num_coordinates, 1]));
y = sort(randi(max_coordinate, [num_coordinates, 1]));
z = sort(randi(max_coordinate, [num_coordinates, 1]));
% create the mask
mask = false(max_coordinate, max_coordinate, max_coordinate);
for k = 1 : length(x)
mask(x(k), y(k), z(k)) = true;
end
If speed is important, I suppose there is a faster solution.

If you have the "Curve Fitting Toolbox" you could fit a surface formula to the data.
And if you now the exact type (like a ball, cone, ...) you can define that as formular to fit to.
Maybe you can provide some example data.

Related

Find points within polygon with multiple self intersections with Matlab

I have a polygon which intersects itself multiple times. I try to create a mask from this polygon, i.e., to find all points/pixels location within the polygon. I use the Matlab function poly2mask for this. However, due to the multiple self-intersections this is the results I obtain:
Resulting mask from poly2mask for multi-self-intersecting polygon
So, some areas remain unmasked, because of the intersections. I think Matlab sees this as some sort of inclusions. The Matlab help for poly2mask doesn't mention anything about this. Does anyone have an idea how to also include these regions in the mask?
I obtain good results combining a small erosion/dilation step and imfill as follows:
data = load('polygon_edge.mat');
x = data.polygon_edge(:, 1);
y = data.polygon_edge(:, 2);
bw1 = poly2mask(x,y,ceil(max(y)),ceil(max(x)));
se = strel('sphere',1);
bw2 = imerode(imdilate(bw1,se), se);
bw3 = imfill(bw2, 'holes');
figure
imshow(bw3)
hold on
plot(x(:, 1),y(:, 1),'g','LineWidth',2)
The small erosion and dilation step is needed to be sure that all the regions are connected even at places where the polygon is only connected through a single point, otherwise imfill may see some non-existing holes.
you can use inpolygon:
bw1 = poly2mask(x,y,1000,1000);
subplot(131)
imshow(bw1)
hold on
plot(x([1:end 1]),y([1:end 1]),'g','LineWidth',2)
title('using poly2mask')
[xq,yq] = meshgrid(1:1000);
[IN,ON] = inpolygon(xq,yq,x,y);
bw2 = IN | ON;
subplot(132)
imshow(bw2)
hold on
plot(x([1:end 1]),y([1:end 1]),'g','LineWidth',2)
title('using inpolygon')
% boundary - seggested by another answer
k = boundary(x, y, 1); % 1 == tightest single-region boundary
bw3 = poly2mask(x(k), y(k), 1000, 1000);
subplot(133)
imshow(bw3)
hold on
plot(x([1:end 1]),y([1:end 1]),'g','LineWidth',2)
title('using boundary')
Update - I updated my answer to include boundary - it not seems to work well in my case.
You should first calculate the boundary of your polygon and use this to create your mask.
k = boundary(x, y, 0.99); % 1 == tightest single-region boundary
BW = poly2mask(x(k), y(k), m, n)
Using a shrink factor of 0.99 instead of 1 avoids undercutting, but sharp non-convex corners are still not fitted correctly.

How would I plot the for loop from my code below?

I have 3D flow data of the velocity of a fluid through a tube. I know the diameter of the tube and have looked at the velocity field and found the centre of the field for an xy plane at both ends of the tube. So I essentially have a line through the centre axis of the tube. I want to NaN all data points that are outside of the diameter. For this I am using an equation that gives the distance to a point from a line in 3D which I found here mathworld.wolfram.com/Point-LineDistance3-Dimensional.html. I then created an if statement which states points smaller than diameter will be NaN.
I am new to matlab so I don't know how I would now plot this.
%%
diff_axis = end_axis-start_axis;
diff_axis_mag = (diff_axis(1)^2 + diff_axis(2)^2 + diff_axis(3)^2)^0.5;
[rw col pl] = size(X);
for j = 1:col
for i = 1:rw
for k = 1:pl
x_curr = X(i,j,k);
y_curr = Y(i,j,k);
z_curr= Z(i,j,k);
x0 = [x_curr y_curr z_curr]
t = - dot((start_axis-x0),(diff_axis))./(diff_axis_mag)^2;
d = sqrt(((start_axis(1) - x0(1)) + (end_axis(1) - start_end(1))*t)^2 + ((start_axis(2)-x0(2))+(end_axis(2)-start_end(2))*t)^2+((start_axis(3)-x0(3))+(end_axis(3)-start_end(3))*t)^2);
if (d > D)
x_curr=NaN
y_curr=NaN
z_curr=NaN
end
end
end
end
It were nice to have explanatory names for your X, Y, and Z. I am guessing they are flow components, and diff_axis are axis coordinates? It is a very cumbersome notation.
what you do in your loops is you take point values (X,Y,Z), copy them to temporary constants and then set them to NaN if they fall out. But the problem is that usually you do not plot point-by-point in MATLAB. So these temorary guys like x_curr will be lost.
Also, the most optimal way to do things in MATLAB is to avoid loops whenever possible.
What you can do is to create first a mask
%// remember to put a dot like in `.^` for entrywise array operations
diff_axis_mag = sqrt(diff_axis(1).^2 + diff_axis(2).^2 + diff_axis(3).^2);
%// are you sure you need to include the third axis?
%// then it is a ball, not a tube
%// create a binary mask
mask = diff_axis_mag < tube_radius
X(~mask) = NaN;
Y(~mask) = NaN;
Z(~mask) = NaN;
Then you can plot your data with quiver3 or
stream3

MATLAB: 3d reconstruction using eight point algorithm

I am trying to achieve 3d reconstruction from 2 images. Steps I followed are,
1. Found corresponding points between 2 images using SURF.
2. Implemented eight point algo to find "Fundamental matrix"
3. Then, I implemented triangulation.
I have got Fundamental matrix and results of triangulation till now. How do i proceed further to get 3d reconstruction? I'm confused reading all the material available on internet.
Also, This is code. Let me know if this is correct or not.
Ia=imread('1.jpg');
Ib=imread('2.jpg');
Ia=rgb2gray(Ia);
Ib=rgb2gray(Ib);
% My surf addition
% collect Interest Points from Each Image
blobs1 = detectSURFFeatures(Ia);
blobs2 = detectSURFFeatures(Ib);
figure;
imshow(Ia);
hold on;
plot(selectStrongest(blobs1, 36));
figure;
imshow(Ib);
hold on;
plot(selectStrongest(blobs2, 36));
title('Thirty strongest SURF features in I2');
[features1, validBlobs1] = extractFeatures(Ia, blobs1);
[features2, validBlobs2] = extractFeatures(Ib, blobs2);
indexPairs = matchFeatures(features1, features2);
matchedPoints1 = validBlobs1(indexPairs(:,1),:);
matchedPoints2 = validBlobs2(indexPairs(:,2),:);
figure;
showMatchedFeatures(Ia, Ib, matchedPoints1, matchedPoints2);
legend('Putatively matched points in I1', 'Putatively matched points in I2');
for i=1:matchedPoints1.Count
xa(i,:)=matchedPoints1.Location(i);
ya(i,:)=matchedPoints1.Location(i,2);
xb(i,:)=matchedPoints2.Location(i);
yb(i,:)=matchedPoints2.Location(i,2);
end
matchedPoints1.Count
figure(1) ; clf ;
imshow(cat(2, Ia, Ib)) ;
axis image off ;
hold on ;
xbb=xb+size(Ia,2);
set=[1:matchedPoints1.Count];
h = line([xa(set)' ; xbb(set)'], [ya(set)' ; yb(set)']) ;
pts1=[xa,ya];
pts2=[xb,yb];
pts11=pts1;pts11(:,3)=1;
pts11=pts11';
pts22=pts2;pts22(:,3)=1;pts22=pts22';
width=size(Ia,2);
height=size(Ib,1);
F=eightpoint(pts1,pts2,width,height);
[P1new,P2new]=compute2Pmatrix(F);
XP = triangulate(pts11, pts22,P2new);
eightpoint()
function [ F ] = eightpoint( pts1, pts2,width,height)
X = 1:width;
Y = 1:height;
[X, Y] = meshgrid(X, Y);
x0 = [mean(X(:)); mean(Y(:))];
X = X - x0(1);
Y = Y - x0(2);
denom = sqrt(mean(mean(X.^2+Y.^2)));
N = size(pts1, 1);
%Normalized data
T = sqrt(2)/denom*[1 0 -x0(1); 0 1 -x0(2); 0 0 denom/sqrt(2)];
norm_x = T*[pts1(:,1)'; pts1(:,2)'; ones(1, N)];
norm_x_ = T*[pts2(:,1)';pts2(:,2)'; ones(1, N)];
x1 = norm_x(1, :)';
y1= norm_x(2, :)';
x2 = norm_x_(1, :)';
y2 = norm_x_(2, :)';
A = [x1.*x2, y1.*x2, x2, ...
x1.*y2, y1.*y2, y2, ...
x1, y1, ones(N,1)];
% compute the SVD
[~, ~, V] = svd(A);
F = reshape(V(:,9), 3, 3)';
[FU, FS, FV] = svd(F);
FS(3,3) = 0; %rank 2 constrains
F = FU*FS*FV';
% rescale fundamental matrix
F = T' * F * T;
end
triangulate()
function [ XP ] = triangulate( pts1,pts2,P2 )
n=size(pts1,2);
X=zeros(4,n);
for i=1:n
A=[-1,0,pts1(1,i),0;
0,-1,pts1(2,i),0;
pts2(1,i)*P2(3,:)-P2(1,:);
pts2(2,i)*P2(3,:)-P2(2,:)];
[~,~,va] = svd(A);
X(:,i) = va(:,4);
end
XP(:,:,1) = [X(1,:)./X(4,:);X(2,:)./X(4,:);X(3,:)./X(4,:); X(4,:)./X(4,:)];
end
function [ P1,P2 ] = compute2Pmatrix( F )
P1=[1,0,0,0;0,1,0,0;0,0,1,0];
[~, ~, V] = svd(F');
ep = V(:,3)/V(3,3);
P2 = [skew(ep)*F,ep];
end
From a quick look, it looks correct. Some notes are as follows:
You normalized code in eightpoint() is no ideal.
It is best done on the points involved. Each set of points will have its scaling matrix. That is:
[pts1_n, T1] = normalize_pts(pts1);
[pts2_n, T2] = normalize-pts(pts2);
% ... code
% solution
F = T2' * F * T
As a side note (for efficiency) you should do
[~,~,V] = svd(A, 0);
You also want to enforce the constraint that the fundamental matrix has rank-2. After you compute F, you can do:
[U,D,v] = svd(F);
F = U * diag([D(1,1),D(2,2), 0]) * V';
In either case, normalization is not the only key to make the algorithm work. You'll want to wrap the estimation of the fundamental matrix in a robust estimation scheme like RANSAC.
Estimation problems like this are very sensitive to non Gaussian noise and outliers. If you have a small number of wrong correspondence, or points with high error, the algorithm will break.
Finally, In 'triangulate' you want to make sure that the points are not at infinity prior to the homogeneous division.
I'd recommend testing the code with 'synthetic' data. That is, generate your own camera matrices and correspondences. Feed them to the estimate routine with varying levels of noise. With zero noise, you should get an exact solution up to floating point accuracy. As you increase the noise, your estimation error increases.
In its current form, running this on real data will probably not do well unless you 'robustify' the algorithm with RANSAC, or some other robust estimator.
Good luck.
Good luck.
Which version of MATLAB do you have?
There is a function called estimateFundamentalMatrix in the Computer Vision System Toolbox, which will give you the fundamental matrix. It may give you better results than your code, because it is using RANSAC under the hood, which makes it robust to spurious matches. There is also a triangulate function, as of version R2014b.
What you are getting is sparse 3D reconstruction. You can plot the resulting 3D points, and you can map the color of the corresponding pixel to each one. However, for what you want, you would have to fit a surface or a triangular mesh to the points. Unfortunately, I can't help you there.
If what you're asking is how to I proceed from fundamental Matrix + corresponding points to a dense model then you still have a lot of work ahead of you.
relative camera locations (R,T) can be calculated from a fundamental matrix assuming you know the internal camera params (up to scale, rotation, translation). To get a full dense matrix there are a few ways to go. you can try using an existing library (PMVS for example). I'd look into OpenMVG but I'm not sure about matlab interface.
Another way to go, you can compute a dense optical flow (many available for matlab). Look for a epipolar OF (It takes a fundamental matrix and restricts the solution to lie on the epipolar lines). Then you can triangulate every pixel to get a depthmap.
Finally you will have to play with format conversions to get from a depthmap to VRML (You can look at meshlab)
Sorry my answer isn't more Matlab oriented.

Matlab - Trying to use vectors with grid coordinates and value at each point for a color plot

I'm trying to make a color plot in matlab using output data from another program. What I have are 3 vectors indicating the x-position, y-yposition (both in milliarcseconds, since this represents an image of the surroundings of a black hole), and value (which will be assigned a color) of every point in the desired image. I apparently can't use pcolor, because the values which indicate the color of each "pixel" are not in a matrix, and I don't know a way other than meshgrid to create a matrix out of the vectors, which didn't work due to the size of the vectors.
Thanks in advance for any help, I may not be able to reply immediately.
If we make no assumptions about the arrangement of the x,y coordinates (i.e. non-monotonic) and the sparsity of the data samples, the best way to get a nice image out of your vectors is to use TriScatteredInterp. Here is an example:
% samplesToGrid.m
function [vi,xi,yi] = samplesToGrid(x,y,v)
F = TriScatteredInterp(x,y,v);
[yi,xi] = ndgrid(min(y(:)):max(y(:)), min(x(:)):max(x(:)));
vi = F(xi,yi);
Here's an example of taking 500 "pixel" samples on a 100x100 grid and building a full image:
% exampleSparsePeakSamples.m
x = randi(100,[500 1]); y = randi(100,[500 1]);
v = exp(-(x-50).^2/50) .* exp(-(y-50).^2/50) + 1e-2*randn(size(x));
vi = samplesToGrid(x,y,v);
imagesc(vi); axis image
Gordon's answer will work if the coordinates are integer-valued, but the image will be spare.
You can assign your values to a matrix based on the x and y coordinates and then use imagesc (or a similar function).
% Assuming the X and Y coords start at 1
max_x = max(Xcoords);
max_y = max(Ycoords);
data = nan(max_y, max_x); % Note the order of y and x
indexes = sub2ind(size(data), max_y, max_x);
data(indexes) = Values;
imagesc(data); % note that NaN values will be colored with the minimum colormap value

Point Cloud Generation

I have a 3-D geometrical shape which I have to convert into a point cloud.
The resultant point cloud can be considered equivalent to a point cloud output from a Laser Scan of the object.
No mesh generation is neeeded
The points generated may be evenly spaced, or maybe just randomly spaced - doesn't matter
The 3-D shape can be provided in the form of a 3-D mathematical formula
This has to be done using MATLAB
It's difficult to answer without an example but it sounds like you just want to perform a montecarlo simulation?
Lets say your shape is defined by the function f and that you have X, Y limits stored in two element vector e.g. xlim = [-10 10] i.e. all possible x values of this shape lie between x = -10 and x = 10 then I would suggest that you make f return some sort of error code if there is no value for a specific x-y pair. I'm going to assume that will be NaN. So f(x,y) is a function you are writing that either returns a z if it can or NaN if it can't
n= 10000;
counter = 1;
shape = nan(n, 3)
while counter < n
x = rand*diff(xlim) + mean(xlmin);
y = rand*diff(ylim) + mean(ylim);
z = f(x,y)
if ~isnan(z)
shape(counter, :) = [x, y, z];
counter = counter + 1
end
end
So the above code will generate 10000 (non unique, but that's easily adapted for) points randomly sample across your shape.
Now after typing this I realise that perhaps your shape is actually not all that big and maybe you can uniformly sample it rather than randomly:
for x = xlim(1):xstep:xlim(2)
for y = ylim(1):ystep:ylim(2)
shape(counter, :) = [x, y, f(x,y)];
end
end
or if you write f to be vectorized (preferable)
shape = [(xlim(1):xstep:xlim(2))', (ylim(1):ystep:ylim(2))', f(xlim(1):xstep:xlim(2), ylim(1):ystep:ylim(2));
and then either way
shape(isnan(shape(:, 3), :) = []; %remove the points that fell outside the shape
Here is the code to create a Cloud image with a Depth image from a PrimeSense Camera.
The input/Ouput of this function :
-inputs
depth -depth map
topleft -topleft coordinates of the segmented image in the whole image
-outputs
pclouds -3d point clouds
MatLab code :
depth = double(depth);
% Size of camera image
center = [320 240];
[imh, imw] = size(depth);
constant = 570.3;
% convert depth image to 3d point clouds
pclouds = zeros(imh,imw,3);
xgrid = ones(imh,1)*(1:imw) + (topleft(1)-1) - center(1);
ygrid = (1:imh)'*ones(1,imw) + (topleft(2)-1) - center(2);
pclouds(:,:,1) = xgrid.*depth/constant;
pclouds(:,:,2) = ygrid.*depth/constant;
pclouds(:,:,3) = depth;
distance = sqrt(sum(pclouds.^2,3));
Edit : This source is from this current article http://www.cs.washington.edu/rgbd-dataset/software.html
You can find some other Cloud function in MatLab and C++ that can be interest you.