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?
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
table
represent x, y, z respectivelyNaN
(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:
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
.Remarks:
What you can do is
meshgrid
, kd-tree
search, i.e. label your data associating to each cloud point a grid nodeaccumarray
).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);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With