在 3D 图像序列中按组平均

Average by groups in a sequence of 3D images

以下情况。我在 4D 矩阵中有一个时间序列的 3 维图像。 3D图像的分辨率为177 * 209 * 156。 然后我有一个 3D-Mask 图像,它带有 3D-Image 中体素所属的 500 个组中的一个的信息,因此它与一个 3D 图像具有相同的维度,但值为 [2;501] (502实际上,但最后一组是 "outsiders",这些将被忽略)。 现在对于那些 3D 图像,我想要一个 500 * X 矩阵,它保存每个图像的每个组的平均值,其中 X 是图像计数(在本例中为 1500)。我编写的用于生成此向量(针对每个图像)的代码如下:

...

final_vec = zeros(500, TRs);
mask_hist = get_histogram(mask_mat);
mask_hist(1) = [];
mask_hist(end) = [];

for i = 1:TRs
    TR = new_nifti_mat(:,:,:,i);

    for k = 1:numel(TR)
        if mask_mat(k) < 502 && mask_mat(k) > 1
            final_vec(mask_mat(k) - 1, i) = final_vec(mask_mat(k) - 1, i) + TR(k);
        end
    end

    final_vec(:, i) = final_vec(:, i)./mask_hist
end

...

此代码的问题在于,它会永远运行。对于一组数据它 运行 超过 12h。我知道 matlab 不适合 for 循环,并且可能有一种非常优雅的 1 或 2 行代码方法可以在我的代码所需时间的一小部分内完成此操作

最好的问候 乌作

让我们先创建示例数据:

x = rand(4,4,2,8); %// random data
mask(:,:,1) = [ 3 2 2 3
                2 4 4 2
                2 4 4 2
                3 2 2 3 ];
mask(:,:,2) = mask(:,:,1); %// example 4x4x2 mask

方法 1:一个循环和 accumarray

遍历每个 3D 图像,并对每个图像使用 accumarray 按组进行平均:

mask = mask - min(mask(:)) + 1; %// so that lowest value is 1
result = NaN(max(mask(:)),size(x,4)); %// preallocate result
for n = 1:size(x,4);
    result(:,n) = accumarray(mask(:), reshape(x(:,:,:,n),[],1), [], @mean, NaN);
end

方法二:bsxfun和矩阵乘法;没有循环

线性化前三个维度并构建一个零一掩码,以便每个组内所有值的累加成为矩阵乘法:

mask2 = bsxfun(@eq, mask(:), min(mask(:)):max(mask(:))).'; %'
result = mask2*reshape(x,[],size(x,4)); %// sum for each group
result = bsxfun(@rdivide, result, sum(mask2,2)); %// transform sum into average