Slow Find Loop no vectorisation

3 views (last 30 days)
Hi,
I have a 3d image where each colour is a unique labelled cell. I'm trying to find the centre of mass of each of these individual colours.
for colour = 1:2^16
disp(100*colour/(2^16));
coords = find(image == colour);
[y_pos,x_pos,z_pos] = ind2sub(size(image),coords);
data(colour,:) = mean([x_pos,y_pos,z_pos]);
end
This loop does the job but it is incredible slow across all 2^16 colours. Is there a way of vectorising this problem?
Thanks,
Craig

Accepted Answer

Andrei Bobrov
Andrei Bobrov on 20 Apr 2016
Please try it:
co = (1:2^16)';
[l0,i0] = ismember(image1(:),co);
s = size(image1);
[ii jj k] = ndgrid(1:s(1),1:s(2),1:s(3));
coor = [ii(:),jj(:),k(:)];
i1 = (1:numel(image1))';
v = accumarray(i0(l0),i1(l0),[numel(co) 1],@(x){mean(coor(x,:),1)});
out = [co(~cellfun(@isempty,v)), cell2mat(v)];
  1 Comment
Craig Russell
Craig Russell on 20 Apr 2016
This works! I don't understand how, but it works.

Sign in to comment.

More Answers (2)

Teja Muppirala
Teja Muppirala on 20 Apr 2016
FOR loops are not necessarily slow. This is in R2016a.
%%Make some data...
rng(0);
image = randi(2^16,[30 40 50]);
%% 1. Original method takes 7 seconds. (By the way, it should be MEAN([...],1) not just MEAN([...])
tic
numColors = 2^16;
data = zeros(numColors,3);
for colour = 1:numColors
%disp(100*colour/(2^16));
coords = find(image == colour);
[y_pos,x_pos,z_pos] = ind2sub(size(image),coords);
data(colour,:) = mean([x_pos,y_pos,z_pos],1);
end
toc
% Elapsed time is 7.447380 seconds
%% Using a for loop more efficiently takes less than 0.1 seconds.
tic
data2 = zeros(numColors,4);
for n1 = 1:size(image,1)
for n2 = 1:size(image,2)
for n3 = 1:size(image,3)
data2(image(n1,n2,n3),:) = data2(image(n1,n2,n3),:) + [1 n2 n1 n3];
end
end
end
data2 = bsxfun(@rdivide,data2(:,2:4),data2(:,1));
toc
% Elapsed time is 0.091517 seconds
%% Show they are the same.
isequaln(data,data2)
% ans =
%
% 1

Guillaume
Guillaume on 20 Apr 2016
Edited: Guillaume on 20 Apr 2016
I don't think you can get rid of the colour loop, but you can speed up the loop code with:
[y_pos, x_pos, z_pos] = ndgrid(1:size(image, 2), 1:size(image, 1), 1:size(image, 3));
for colour = 1 : pow2(16)
iscolour = image == colour; %use logical instead of find
data(colour, :) = mean([x_pos(iscolour), y_pos(iscolour), z_pos(iscolour)]);
end
edit: I've just thought that you can replace the loop with accumarray and I see that Andrei answered with that in the meantime.
edit-edit: Here is a (more readable?) alternative to andrei's answer:
[y_pos, x_pos, z_pos] = ndgrid(1:size(image, 2), 1:size(image, 1), 1:size(image, 3));
datarows = [pow2(16), 1];
data = [accumarray(image(:), x_pos(:), datarows, @mean), ...
accumarray(image(:), y_pos(:), datarows, @mean), ...
accumarray(image(:), z_pos(:), datarows, @mean)];

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!