How to "smear" a logical mask without looping

3 visualizaciones (últimos 30 días)
Sonomatic Australia
Sonomatic Australia el 21 de Mzo. de 2017
Comentada: Greg Dionne el 22 de Mzo. de 2017
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 :)

Respuesta aceptada

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

Iniciar sesión para comentar.

Más respuestas (3)

Stephen23
Stephen23 el 21 de Mzo. de 2017
Editada: Stephen23 el 21 de Mzo. de 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 comentario
Sonomatic Australia
Sonomatic Australia el 21 de Mzo. de 2017
Thank you Stephen, that's pretty neat! I do need to handle columns with more than one non-zero value. Will keep trying!

Iniciar sesión para comentar.


Guillaume
Guillaume el 21 de Mzo. de 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 comentario
Sonomatic Australia
Sonomatic Australia el 22 de Mzo. de 2017
Nice one Guillaume, thank you for that! I'll have to run some comparisons with larger arrays and post results.

Iniciar sesión para comentar.


Guillaume
Guillaume el 22 de Mzo. de 2017
Editada: Guillaume el 22 de Mzo. de 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 comentario
Sonomatic Australia
Sonomatic Australia el 22 de Mzo. de 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.

Iniciar sesión para comentar.

Categorías

Más información sobre Creating and Concatenating Matrices en Help Center y File Exchange.

Community Treasure Hunt

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

Start Hunting!

Translated by