Alexander Korovin
Alexander Korovin

Reputation: 1475

How to use logical conditions only for some dimensions in multidimensional arrays in MATLAB

Let us have a 4D matrix (tensor), output:

[X,Y,Z] = ndgrid(-50:55,-55:60,-50:60);
a = 1:4;
output = zeros([size(X),length(a)]);

Next, we determine the area inside the ellipsoid:

position = [0,0,0];
radius = [10,20,10];
test_func = @(X,Y,Z) ((X-position(1))/radius(1)).^2 ...
            + ((Y-position(2))/radius(2)).^2 ...
            + ((Z-position(3))/radius(3)).^2 <= 1;

condition = test_func(X,Y,Z);

I need to fill the matrix output inside the ellipsoid for the first 3 dimensions. But for the fourth dimension I need to fill a. I need to do something like this:

 output(condition,:) = a;

But it does not work. How to do it? Any ideas please!

Upvotes: 1

Views: 100

Answers (1)

rayryeng
rayryeng

Reputation: 104484

If I understand your question correctly, you have a 4D matrix where each temporal blob of pixels in 3D for each 4D slice is filled with a number... from 1 up to 4 where each number tells you which slice you're in.

You can cleverly use bsxfun and permute to help you accomplish this task:

output = bsxfun(@times, double(condition), permute(a, [1 4 3 2]));

This takes a bit of imagination to imagine how this works but it's quite simple. condition is a 3D array of logical values where each location in this 3D space is either 0 if it doesn't or 1 if it does belong to a point inside an ellipsoid. a is a row vector from 1 through 4 or however many elements you want this to end with. Let's call this N to be more general.

permute(a, [1 4 3 2]) shuffles the dimensions such that we create a 4D vector where we have 1 row, 1 column and 1 slice but we have 4 elements going into the fourth dimension.

By using bsxfun in this regard, it performs automatic broadcasting on two inputs where each dimension in the output array will match whichever of the two inputs had the largest value. The condition is that for each dimension independently, they should both match or one of them is a singleton dimension (i.e. 1).

Therefore for your particular example, condition will be 106 x 116 x 111 while the output of the permute operation will be 1 x 1 x 1 x N. condition is also technically 106 x 116 x 111 x 1 and using bsxfun, we would thus get an output array of size 106 x 116 x 111 x N. Performing the element-wise @times operation, the permuted vector a will thus broadcast itself over all dimensions where each 3D slice i will contain the value of i. The condition matrix will then duplicate itself over the fourth dimension so we have N copies of the condition matrix, and performing the element-wise multiply thus completes what you need. This is doable as the logical mask you created contains only 0s and 1s. By multiplying element-wise with this mask, only the values that are 1 will register a change. Specifically, if you multiply the values at these locations by any non-zero value, they will change to these non-zero values. Using this logic, you'd want to make these values a. It is important to note that I had to cast condition to double as bsxfun only allows two inputs of the same class / data type to be used.

To visually see that this is correct, I'll show scatter plots of each blob where the colour of each blob would denote what label in a it belongs to:

close all;
N = 4;
clrs = 'rgbm';
figure;
for ii = 1 : N
    blob = output(:,:,:,ii);
    subplot(2,2,ii);        
    plot3(X(blob == ii), Y(blob == ii), Z(blob == ii), [clrs(ii) '.']);
end

We get:

enter image description here

Notice that the spatial extent of each ellipsoid is the same but what is different are the colours assigned to each blob. I've made it such that the values for the first blob, or those assigned to a = 1 are red, those that are assigned to a = 2 are assigned to green, a = 3 to blue and finally a = 4 to magenta.

If you absolutely want to be sure that the coordinates of each blob are equal across all blobs, you can use find in each 3D blob individually and ensure that the non-zero indices are all equal:

inds = arrayfun(@(x) find(output(:,:,:,x)), a, 'un', 0);
all_equal = isequal(inds{:});

The code finds in each blob the column major indices of all non-zero locations in 3D. To know whether or not we got it right, each 3D blob should all contain the same non-zero column major indices. The arrayfun call goes through each 3D blob and returns a cell array where each cell element is the column major indices for a particular blob. We then pipe this into isequal to ensure that all of these column major indices arrays are equal. We get:

>> all_equal

all_equal =

     1

Upvotes: 3

Related Questions