randomAffine3d is not properly random
    3 views (last 30 days)
  
       Show older comments
    
the transformations are highly biased, and don't cover the unit sphere with any angle coverage. Image shows 4 compass points randomly rotated +-45. Code replicates the image, as well as a 'correctly' random version.
is this a bug, or was it a bug now fixed (i am using 2019b)

ori = [0,0,1;0,0,-1;1,0,0;-1,0,0];
%% randomaffine3d is NOT usefully random (highly biased, missing regions)
o = [];
for i=1:10000
tform = randomAffine3d('Rotation',[-45 45]);
mat = tform.T(1:3,1:3); r = ori*mat; % alternative to prove TPF isn't broken
r = transformPointsForward(tform,ori);
o(end+1:end+size(ori,1),:) = r;
end
plot3(o(:,1),o(:,2),o(:,3),'.'); axis equal
%% manually random rotation matrix - correctly random
b = [];
for i=1:10000
    ax = randn(1,3); ax = ax./norm(ax); %true random unit vectors
    mat = rotmat(ax,rand*pi/4); % random rotation matrix (but not isotropic)
    r = ori*mat;
    b(end+1:end+size(ori,1),:) = r;
end
figure(); plot3(b(:,1),b(:,2),b(:,3),'.'); axis equal
%% internal funct
function t = rotmat(ax,rad)
ax = ax/norm(ax);
x = ax(1); y = ax(2); z = ax(3);
c = cos(rad); s = sin(rad);
t1 = c + x^2*(1-c);
t2 = x*y*(1-c) - z*s;
t3 = x*z*(1-c) + y*s;
t4 = y*x*(1-c) + z*s;
t5 = c + y^2*(1-c);
t6 = y*z*(1-c)-x*s;
t7 = z*x*(1-c)-y*s;
t8 = z*y*(1-c)+x*s;
t9 = c+z^2*(1-c);
t = [t1 t2 t3
    t4 t5 t6
    t7 t8 t9];
end
2 Comments
  Cris LaPierre
    
      
 on 7 Nov 2024
				I ran your code so you can see the results in R2024b.
Note that you are asking the community. If you want to report a concern directly to MathWorks, please contact support: https://www.mathworks.com/support/contact_us.html
  Bruno Luong
      
      
 on 7 Nov 2024
				Alternative correct code
ori = [0,0,1;
    0,0,-1;
    1,0,0;
    -1,0,0];
%% manually random rotation matrix - correctly random
b = [];
for i=1:10000
    mat = rotmat2(randn(1,3),rand*pi/4); % random rotation matrix (but not isotropic)
    r = ori*mat;
    b(end+1:end+size(ori,1),:) = r;
end
figure(); plot3(b(:,1),b(:,2),b(:,3),'.'); axis equal
%% internal funct
function t = rotmat2(ax,rad)
M = makehgtform("axisrotate",ax,rad);
t = M(1:3,1:3);
end
Answers (3)
  Matt J
      
      
 on 7 Nov 2024
        If you want an isotropic distribution across the entire sphere, this should do it.
ori = [eye(3);-eye(3)];
N=1000;
o=cell(N,1);
for i=1:N
    [Q,R]=qr(rand(3));
    Q=Q*det(Q);
    r = ori*Q;
   o{i} = r;
end
o=vertcat(o{:});
plot3(o(:,1),o(:,2),o(:,3),'.'); axis equal
5 Comments
  Bruno Luong
      
      
 on 8 Nov 2024
				
      Edited: Bruno Luong
      
      
 on 8 Nov 2024
  
			When you slide the sphere with delta el the area is not uniform as with az.
The delta area is cos(el) so what you see is histogram(el) ~ cos(el) when the points are uniformly distributed on the sphere.
N = 1e6;
o = randn(6*N,3);
o = o ./ vecnorm(o,2,2);
figure
[az,el] = cart2sph(o(:,1), o(:,2), o(:,3));
h = histogram(el,'Normalization','percentage');
mx = max(h.Values);
hold on
EZ = linspace(-pi/2,pi/2);
plot(EZ, mx*cos(EZ),'r','LineWidth',2)
As criteria you could also check uniformity of
figure
histogram(atan2(o(:,3),o(:,1))), 
or any projection of o on two arbitrary orthogonal unit vectors.
  Bruno Luong
      
      
 on 8 Nov 2024
				
      Edited: Bruno Luong
      
      
 on 8 Nov 2024
  
			Generate unfiform points on S^2 using spherical coordinates
N = 1e6;
az = 2*pi*rand(1,N);
el = asin(2*rand(1,N)-1);
r = ones(1,N);
[x,y,z] = sph2cart(az,el,r);
figure
plot3(x,y,z,'.')
axis equal
figure
histogram(atan2(z,x))
  Matt J
      
      
 on 7 Nov 2024
        
      Edited: Matt J
      
      
 on 7 Nov 2024
  
      EDITED: If you don't like what the default randomizer is doing, randomAffine3d() also let's you define your own, e.g.,
ori = [eye(3);-eye(3)];
N=1e4;
o=cell(N,1);
for i=1:N
    tform = randomAffine3d('Rotation',@selectRotation);
    mat = tform.T(1:3,1:3);
    r = transformPointsForward(tform,ori);
   o{i} = r;
end
o=vertcat(o{:});
[az,el] = cart2sph(o(:,1), o(:,2), o(:,3));
subplot(1,2,1) 
histogram(az)  ; axis square; xlabel AZ
subplot(1,2,2)
histogram(el) ; axis square; xlabel EL
function [rotationAxis,theta] = selectRotation()
  [Q,~]=qr(randn(3));
  Q=Q*det(Q);
  [V,d]=eig(Q,'vector');
   [~,i]=max(real(d));
   rotationAxis=real(V(:,i)');
   B=[null(rotationAxis),rotationAxis'];
   B(:,1)=B(:,1)*det(B);
   c=B'*Q*B(:,1);
   theta=atan2d(c(2),c(1));
end
4 Comments
  Bruno Luong
      
      
 on 8 Nov 2024
				
      Edited: Bruno Luong
      
      
 on 8 Nov 2024
  
			One must convert angle to degree as for interface with randomAffine3d
To convert rotation matrix (Q) to rotation angle/ vector better method is using quaternion or Caykey method:
But in this thread one can generate directly rotation vector uniform on S^2 and rotation angle, uniform as user prescribed as with randomAffine3d('Rotation',[angledeg_lo angle_hi])
ori = [0,0,1;
    0,0,-1;
    1,0,0;
    -1,0,0];
N=1e4;
o=[];
for i=1:N
    tform = randomAffine3d('Rotation',@selectRotation2);
    mat = tform.T(1:3,1:3);
    r = transformPointsForward(tform,ori);
   o(end+1:end+size(ori,1),:) = r;
end
figure(); plot3(o(:,1),o(:,2),o(:,3),'.'); axis equal
%% internal funct
function [rotationAxis,theta] = selectRotation2()
rotationAxis = randn(1,3);
rotationAxis = rotationAxis / norm(rotationAxis);
theta = 45 * (2*rand()-1);
end
  Bruno Luong
      
      
 on 8 Nov 2024
				There is a not well specification the the doc of randomAffine3d
In the "Rotation" section one can read
"A function handle of the form 
[rotationAxis,theta] = selectRotation
The function selectRotation must accept no input arguments. The function must return two output arguments: rotationAxis, a 3-element vector defining the axis of rotation, and theta, a rotation angle in degrees."
Actually it accepts only a 3-element row vector, a column vector will crash the function.
  Bruno Luong
      
      
 on 8 Nov 2024
        
      Edited: Bruno Luong
      
      
 on 8 Nov 2024
  
      This is not an answer  but I puspose introduce a bug that generates the rotation axis in the positivve part oof S^2 and  it reproduces the same figure as reported in  the question:
ori = [0,0,1;
    0,0,-1;
    1,0,0;
    -1,0,0];
N=1e4;
o=[];
for i=1:N
    tform = randomAffine3d('Rotation',@selectRotation2);
    mat = tform.T(1:3,1:3);
    r = transformPointsForward(tform,ori);
   o(end+1:end+size(ori,1),:) = r;
end
figure(); plot3(o(:,1),o(:,2),o(:,3),'.'); axis equal
%% internal funct
function [rotationAxis,theta] = selectRotation2()
az = 2*pi*rand();
el = asin(2*rand()-1);
r = 1;
[x,y,z] = sph2cart(az,el,r);
rotationAxis = [x,y,z]; 
rotationAxis = abs(rotationAxis); % Bug introduced with ABS
theta = 45 * (2*rand()-1);
end
For those who has the toolbox, Is randomAffine3d code is inn an lfiile and visible?
0 Comments
See Also
Categories
				Find more on Special Functions in Help Center and File Exchange
			
	Community Treasure Hunt
Find the treasures in MATLAB Central and discover how the community can help you!
Start Hunting!

















