Converting 3D to 2D cloud of points

I am trying to convert a set of data which is a cloud of points from 3D to 2D, I am using this code:
% Function from 3D to 2D
function [D,R,T]=dimred(X)
T = repmat(mean(X),[size(X,1),1]);
XX = X-T;
[R,N]=eig(XX'*XX);
D=XX*R;
D=D(:,2:end);
Q=[zeros(size(D,2),1) eye(size(D,2))];
R=Q*R';
return
% 3D to 2D
[nodes_2D,R,T]=dimred(newnodes1) % R = rot matrix, T = Translation matrix
The data correctly converts to 2D, in this case, looking at the image the 3D points are rotated to the left, but for a different cloud of points it rotates it to the right. My question is the next, is there any way of forcing to apply the rotation always in the same direction? (without manually reversing the 2D red plot).
I have attached the figure and the .mat containing the cloud of points in 3D.
Thank you for any help.

10 Comments

I notice you use the eigenvectors as returned. Any scalar multiple of an eigenvector acts the same way, including negative multiples. As I do not see any code in there to explicitly normalize, I wonder if the difficulty you are facing is that some of your eigenvectors just happen to be coming out with the negative of the sign you are expecting for the purposes of rotation ?
Alfonso
Alfonso on 30 Jun 2018
Edited: Alfonso on 30 Jun 2018
Hello Walter, you are right, the eigenvectors are in SOME cases coming up with the opposite sign that I want them to, and I don't know why this happens, it seems that depending on the initial 3D data, it gets flattened to the left or to the right. My objective is to always get the same "2D view" of my 3D cloud of points (always flattened to the right for example).
These are the 2 only possibilities of 2D result from the 3D blue data. Currently for this dataset, I obtain the red 2D plot, but I want the magenta one which I have been able to plot reversing the R (rotation matrix). The problem as I said, is that the result obtained (red or magenta) changes for each 3D dataset, so I don't know how to detect before seeing the plots if the rotation has been the one I want (magenta) or not.
I hope my explanation does not look too messy,
Thank you
Can you divide each eigenvector by its first non-zero element, so that the leading entries are all 0 or positive ?
For this 3D dataset using the code I posted I get the following:
% R = Eigenvalues
R =
0.0600 0.3950 -0.9167
0.4589 0.8047 0.3768
% N = Right Eigenvectors
N =
1.0e+05 *
0.0000 0 0
0 2.4094 0
0 0 3.1734
So right eigenvectors result in 0/+ when dividing by the 1st element, did you mean this or am I getting confused?
I also uploaded the .mat file with the 3D data in the initial post.
I meant like
for K = 1 : size(R,2)
firstR = find(R(:,K),1,'first');
R(:,K) = R(:,K) ./ firstR;
end
... Of course there are ways to vectorize this, but it is simply not worth the trouble.
In order for the division to be +/0 all values of each column in R should have the same sign, this never or rarely happens as I have seen for other 3D datasets.
Perhaps it is only one particular one of the dimensions that needs to be positive and you can divide by the sign of that particular dimension?
Note: your comments are reversed above, R is the eigenvectors and diag(N) are the eigenvalues.
When I try to transform from 3D to 2D several datasets, in some of them I get the correct 2D view but the ones I don't get correctly are always reversed in Y. So if for example I do this:
R(:,2) = R(:,2) * -1;
And then transform to 2D, I obtain the correct 2D view (in the case the first result was the wrong view).
Maybe the sign of one of the values of the eigenvector corresponding to Y axis determines if it is flattened to the right or left, I have to take a look, but could this be?
for K = 1 : size(R,2)
s = sign(R(2,K));
R(:,K) = R(:,K) .* s;
end
You need to transform the entire eigenvector.
Hello Walter, it does not seem to work. For a 3D dataset 1 it flattens it to the left side, whereas for a 3D dataset 2 of the same plane as dataset 1 it flattens it to the right (same behaviour I had).
3D dataset1:
3D dataset2:

Sign in to comment.

 Accepted Answer

Matt J
Matt J on 30 Jun 2018
Edited: Matt J on 30 Jun 2018
This uses AxelRot (Download),
normal=null(XX);
normal=normal(:,end)*sign(normal(1,end));
rotaxis=-cross([0,0,1].',normal);
theta=asind(norm(rotaxis));
[D,R,~] = AxelRot(XX.', theta, rotaxis, []);

11 Comments

Hello Matt, as you are using XX I assume the code you wrote must go inside the function I posted after: XX = X-T?
I have tried this:
X = newnodes1;
T = repmat(mean(X),[size(X,1),1]);
XX = X-T;
normal=null(XX);
normal=normal(:,end)*sign(normal(1,end));
rotaxis=-cross([0,0,1].',normal);
theta=asind(norm(rotaxis));
[2D_data,R,~] = AxelRot(XX.', theta, rotaxis, []);
but the transformation from 3D to 2D does nor perform correctly, surely I am misunderstanding something.
"2D_data" is not a valid Matlab variable name. That will surely throw an error.
Try this,
X = newnodes1;
T = repmat(mean(X),[size(X,1),1]);
XX = X-T;
normal=null(XX);
normal=normal(:,end);
rotaxis=cross(normal,[0,0,1].');
s=-sign(mean(sign(rotaxis(1:2)))); %decide which way is "left"
rotaxis=s*rotaxis;
theta=acosd(s*normal(3));
[D,R,~] = AxelRot(XX.', theta, rotaxis, []);
D=D(1:2,:).';
Alfonso
Alfonso on 1 Jul 2018
Edited: Alfonso on 1 Jul 2018
Hello Matt, I am testing it with all my 3D datasets right now. If I have understood correctly, if I mantain the sign of 's' as, say +, it will always force the rotation to go the same way, so the 3D data will always flatten in this direction (what is what I'm looking for),
I'll let you know when I finish testing the datasets,
Thank you.
Works perfectly as I want to with the 3D dataset I initially posted and also work right for 3D datasets that belong to the same plane as the initial dataset, but I have tried with a different 3D cloud of points that belong to another plane and all values in the resulting 2D data are NaN, do you know what might be the problem?
Seems to me maybe the problem is in here (I am not sure)
s=-sign(mean(sign(rotaxis(1:2))));
rotaxis(1:2)=
0.0000
-0.4472
% So the sign will be the mean between 1 and -1, which is 0
rotaxis=s*rotaxis; % will give [0;0;0]
also warning messages appear:
Warning: Matrix is singular, close to singular or badly scaled. Results may be inaccurate. RCOND = NaN.
> In AxelRot (line 94)
> In AxelRot (line 63)
I have attached the 3D dataset,
Thank you.
You need to give us a more precise and general definition of "rotate to the left". Which way is "left", in general, if the 3D orientation of the cloud can be arbitrary? Perhaps this?
normal=null(XX);
normal=normal(:,end);
rotaxis=cross(normal,[0,0,1].');
[~,idx]=max(abs(rotaxis));
s=-sign(rotaxis(idx)); %decide which way is "left"
I will try this code. In order to understand what I mean, I have attached two figures, one represents que 3D data converted to 2D rotated to the "left" and the other figure is rotated to the "right". Manually rotate the figures and that way I think you will know what I mean by left/right.
Thank you.
Matt J
Matt J on 1 Jul 2018
Edited: Matt J on 1 Jul 2018
OK, well see if you like my last implementation.
However, your attached .fig files don't clear up the ambiguity. Your notions of "right" and "left" appear to depend arbitrarily on the plot camera angle. For example, if we were to rotate the plot perspective 180 degrees about the z-axis, so that we were viewing the original blue points from the opposite side, then left would become right and right would become left.
To put it another way, suppose your blue points lay entirely in the xz-plane. I know you want the points rotated clockwise all the time about a consistent axis, and I know that axis is either the positive x-axis or the negative x-axis, but which one is it? Similarly, what about if the data is in the yz-plane. Do we rotate clockwise about y or about -y?
Alfonso
Alfonso on 1 Jul 2018
Edited: Alfonso on 1 Jul 2018
Yes, it is quite ambiguous, but it's hard to explain. Look at this view, I want the the 2D data to be like the 3D data in this view. The result is the magenta plot, which is like if you pushed the top part of the blue plot with your hand about 90º. This is the 2D "view" I am looking for for all my 3D datasets.
I have tried your new code and seems to work right. I have 3 type of 3D data planes, for one I get the 2D view I want with - sign for the second one with + sign, and the third one is a horizontal plane so I just put the angle of rotation to 0 and I get the correct view.
I will test it with more 3D datasets of this 3 planes to see if for all datasets of the same plane it satisfies the same sign (-/+), but at the moment it looks like it works just as I wanted.
for example this is what I should get (a general sign for all datasets of the same plane):
All 3D datasets of plane1: correct view with sign -
All 3D datasets of plane2: correct view with sign +
Matt J
Matt J on 2 Jul 2018
Edited: Matt J on 2 Jul 2018
which is like if you pushed the top part of the blue plot with your hand about 90º
That is not consistent with your original post. In your original post we were pulling, not pushing. And the point to be clarified is still how you decide whether to push or pull.
Hello Matt, with a few modifications I think I finally got it to work right. Thank you for your help.
No problem. Glad you got what you needed.

Sign in to comment.

More Answers (0)

Asked:

on 30 Jun 2018

Commented:

on 4 Jul 2018

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!