Analysis of a 3D point cloud by projection in a 2D

2019-03-20 07:00发布

I have a 3D point cloud (XYZ) where the Z can be position or energy. I want to project them on a 2D surface in a n-by-m grid (in my problem n = m) in a manner that each grid cell has a value of the maximum difference of Z, in case of Z being position, or a value of summation over Z, in case of Z being energy.

For example, in a range of 0 <= (x,y) <= 20, there are 500 points. Let's say the xy-plane has n-by-m partitions, e.g. 4-by-4; by which I mean in both x and y directions we have 4 partitions with an interval of 5 (to make it 20 at maximum. Now, each of these cells should have a value of the summation, or maximum difference, of the Z value of those points which are in the corresponding column in the defined xy-plane.

I made a simple array of XYZ just for a test as follows, where in this case, Z denotes the energy of the each point.

n=1;
for i=1:2*round(random('Uniform',1,5))
    for j=1:2*round(random('Uniform',1,5))
        table(n,:)=[i,j,random('normal',1,1)];
        n=n+1;
    end
end

How can this be done without loops?

2条回答
可以哭但决不认输i
2楼-- · 2019-03-20 07:32

The accumarray function is quite suited for this kind of task. First I define example data:

table = [ 20*rand(1000,1) 30*rand(1000,1) 40*rand(1000,1)]; % random data
x_partition = 0:2:20; % partition of x axis
y_partition = 0:5:30; % partition of y axis

I'm assuming that

  • The three columns of table represent x, y, z respectively
  • No point has x lower than that of first edge of your grid or greater than last edge, and the same for y. That is, the grid covers all points.
  • If a bin contains no values the result should be NaN (if you want some other fill value, just change last argument of accumarray).

Then:

L = size(table,1);
M = length(x_partition);
N = length(y_partition);
[~, ii] = max(repmat(table(:,1),1,M) <= repmat(x_partition,L,1),[],2);
[~, jj] = max(repmat(table(:,2),1,N) <= repmat(y_partition,L,1),[],2);
ii = ii-1; % by assumption, all values in ii will be at least 2, so we subtract 1
jj = jj-1; % same for jj
result_maxdif = accumarray([ii jj], table(:,3), [M-1 N-1], @(v) max(v)-min(v), NaN);
result_sum = accumarray([ii jj], table(:,3), [M-1 N-1], @sum, NaN);

Notes to the code:

  • The key is obtaining ii and jj, which give the indices of the x and y bins in which each point lies. I use repmat to do that. It would have been better to usebsxfun, but it doesn't support the multiple-output version of @max.
  • The result has size (M-1) x (N-1) (numbers of bins in each dimension)
查看更多
男人必须洒脱
3楼-- · 2019-03-20 07:39

Remarks:

  1. all this can be almost one-liner via python pandas and cutting methods.
  2. I've rewritten your random cloud initialization

What you can do is

  1. layout an xy grid via meshgrid,
  2. project the cloud on xy (simple marginalization)
  3. find the nearest grid point via a kd-tree search, i.e. label your data associating to each cloud point a grid node
  4. group data by label and evaluate your local statistic (via accumarray).

Here's a working example:

 samples = 500;
 %data extrema
 xl = 0; xr = 1; yl = 0; yr = 1;

 % # grid points
 sz = 20;
 % # new random cloud    
 table = [random('Uniform',xl,xr,[samples,1]) , random('Uniform',yr,yl,[samples,1]), random('normal',1,1,[samples,1])];

 figure; scatter3(table(:,1),table(:,2),table(:,3));

 % # grid construction
 xx = linspace(xl,xr,sz); yy = linspace(yl,yr,sz);
 [X,Y] = meshgrid(xx,yy);
 grid_centers = [X(:),Y(:)];

 x = table(:,1); y = table(:,2); 

 % # kd-tree
 kdtreeobj = KDTreeSearcher(grid_centers);
 clss = kdtreeobj.knnsearch([x,y]); % # classification

 % # defintion of local statistic
 local_stat = @(x)sum(x) % # for total energy
 % local_stat = @(x)max(x)-min(x) % # for position off-set

 % # data_grouping
 class_stat = accumarray(clss,table(:,3),[],local_stat );       
 class_stat_M  = reshape(class_stat , size(X)); % # 2D reshaping

 figure; contourf(xx,yy,class_stat_M,20); 

enter image description here enter image description here

查看更多
登录 后发表回答