How to "smear" a logical mask without looping

3 views (last 30 days)
I would like to "smear" a logical mask - fast. There may be a proper term and even standard operation for this but I haven't been able to find them. The following code and image help describe the requirement:
r=20;
c=25;
a=false(r,c);
a(10,3)=true;
a(4,15)=true;
a(18,18)=true;
a2=a;
n=5; %length to "smear"
for j=1:c
i=1;
while i<=r
if a2(i,j);
a2(i:(i+n),j)=true;
i=i+n;
end
i=i+1;
end
end
a2=a2(1:r,:);
figure(1)
colormap(flipud(gray))
subplot(1,2,1)
imagesc(a)
title('Input')
subplot(1,2,2)
imagesc(a2)
title('Desired Output')
This is easy in a loop but very slow for large arrays. I've managed a few approaches without loops, some are faster but still messy and I'm sure there is a better way! Hence posting it here for the Gurus :)

Accepted Answer

Greg Dionne
Greg Dionne on 22 Mar 2017
Edited: Greg Dionne on 22 Mar 2017
If you have a recent copy (R2016a) try:
a2 = movmax(a, [n 0]);
  3 Comments
Sonomatic Australia
Sonomatic Australia on 22 Mar 2017
Wow how good is that!? Time to update my Matlab then!
Greg Dionne
Greg Dionne on 22 Mar 2017
Thanks Guillaume,
I've updated the answer accordingly.
-G

Sign in to comment.

More Answers (3)

Stephen23
Stephen23 on 21 Mar 2017
Edited: Stephen23 on 21 Mar 2017
I have no idea how fast this is, but it is relatively compact:
>> idx = cumsum(cumsum(a,1),1);
>> out = 0<idx & idx<=n
out =
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0
Note that this method will not work if there are more than one non-zero value in a column. It could be adapted for that situation though.
  1 Comment
Sonomatic Australia
Sonomatic Australia on 21 Mar 2017
Thank you Stephen, that's pretty neat! I do need to handle columns with more than one non-zero value. Will keep trying!

Sign in to comment.


Guillaume
Guillaume on 21 Mar 2017
This would work regardless of the numbers of non-zero values in each column. The loop is only over the length of the smear, so should be fairly fast:
a2 = a;
smearlength = 5;
for s = 1 : smearlength
a2 = a2 | [zeros(s, size(a, 2)); a(1:end-s, :)];
end
  1 Comment
Sonomatic Australia
Sonomatic Australia on 22 Mar 2017
Nice one Guillaume, thank you for that! I'll have to run some comparisons with larger arrays and post results.

Sign in to comment.


Guillaume
Guillaume on 22 Mar 2017
Edited: Guillaume on 22 Mar 2017
And here is a one liner that also works regardless of the numbers of ones in each column:
%a: logical matrix
%n: number of 1s to add to each smear
a2 = any(a(permute(toeplitz(1:size(a, 1), ones(1, n+1)), [1 3 2]) + (0:size(a, 1):numel(a)-1)), 3);
Requires R2016b or later (otherwise use bsxfun for the +) and is probably not faster than my loop answer.
edit: actually, it is faster than the loop on my machine. But not as fast as Stephen's answer.
  1 Comment
Sonomatic Australia
Sonomatic Australia on 22 Mar 2017
Thank you Guillaume, I need some time to understand that one! For interest, I setup the following and did a quick test:
r=50000;
c=10000;
a=false(r,c);
ntrue=round(r*c*0.3);
b=round(rand(20,1)*numel(a));
a(b)=true;
n=30; %length to "smear"
Your method which loops for "n" took 183.267894 s. The method in the original question took 6.552720 s which surprised me.

Sign in to comment.

Categories

Find more on Creating and Concatenating Matrices in Help Center and File Exchange

Community Treasure Hunt

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

Start Hunting!