通过平均将点云转换为体素

时间:2019-05-10 10:17:13

标签: matlab histogram

我有以下数据:

N = 10^3;
x = randn(N,1);
y = randn(N,1);
z = randn(N,1);
f = x.^2+y.^2+z.^2;

现在,我想将此连续的3D空间拆分为nB个容器。

nB = 20;
[~,~,x_bins] = histcounts(x,nB);
[~,~,y_bins] = histcounts(y,nB);
[~,~,z_bins] = histcounts(z,nB);

然后将每个多维数据集的平均值fnan放入多维数据集中,如果没有观测值:

F = nan(50,50,50);

for iX = 1:20
    for iY = 1:20
        for iZ = 1:20
            idx = (x_bins==iX)&(y_bins==iY)&(z_bins==iZ);
            F(iX,iY,iZ) = mean(f(idx));
        end
    end
end
isosurface(F,0.5)

这段代码可以满足我的要求。我的问题是速度。当N > 10^5nB = 100时,此代码非常慢。

如何加快此代码的速度?


我还尝试了accumarray()函数:

subs=([x_bins,y_bins,z_bins]);
F2 = accumarray(subs,f,[],@mean);
all(F(:) == F2(:)) % false

但是,此代码会产生不同的结果。

1 个答案:

答案 0 :(得分:3)

OP中代码的问题在于,它针对输出数组中的每个元素测试数据的所有元素。输出数组包含nB^3个元素,数据包含N个元素,因此算法为O(N*nB^3)。取而代之的是,可以在输入的N元素上循环,并在输出数组中设置相应的元素,这是一个操作O({N)(下面的第二个代码块)。

OP中的accumarray解决方案需要使用fillvals参数,并将其设置为NaN(下面的第三个代码块)。

要比较结果,需要显式测试两个数组在相同位置具有NaN,并且在其他位置具有相同的非NaN值:

all( ( isnan(F(:)) & isnan(F2(:)) ) | ( F(:) == F2(:) ) )
%    \-------same NaN values------/   \--same values--/

这是代码。所有这三个版本产生相同的结果。 Octave 4.4.1中的时序(无JIT),在MATLAB中,循环代码应该更快。 (使用来自OP的输入数据,N=10^3nB=20)。

%% OP's code, O(N*nB^3)
tic
F = nan(nB,nB,nB);
for iX = 1:nB
    for iY = 1:nB
        for iZ = 1:nB
            idx = (x_bins==iX)&(y_bins==iY)&(z_bins==iZ);
            F(iX,iY,iZ) = mean(f(idx));
        end
    end
end
toc
% Elapsed time is 1.61736 seconds.

%% Looping over input, O(N)
tic
s = zeros(nB,nB,nB);
c = zeros(nB,nB,nB);
ind = sub2ind([nB,nB,nB],x_bins,y_bins,z_bins);
for ii=1:N
   s(ind(ii)) = s(ind(ii)) + f(ii);
   c(ind(ii)) = c(ind(ii)) + 1;
end
F2 = s ./ c;
toc
% Elapsed time is 0.0606539 seconds.

%% Other alternative, using accumarray
tic
ind = sub2ind([nB,nB,nB],x_bins,y_bins,z_bins);
F3 = accumarray(ind,f,[nB,nB,nB],@mean,NaN);
toc
% Elapsed time is 0.14113 seconds.