I need to effectively eliminate consecutive regions in vector "a" or better in rows/columns of matrix "A" with length of separate ones regions greater than positive integer N <= length(A):
See following example:
N = 2 % separate consecutive regions with length > 2 are zeroed
a = [0 1 1 0 0 1 1 1 0 0 1 1 1 1 0 1]
a_elim = [0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 1]
or 2D case:
N = 2
A = [1 0 1 …
1 1 0 …
1 1 0 …
0 0 1 …
1 1 1]
% elimination over columns
A_elim= 0 0 1
0 1 0
0 1 0
0 0 1
1 1 1
% elimination over rows
A_elim= 1 0 1
1 1 0
1 1 0
0 0 1
0 0 0
I am looking for effective vectorized MATLAB function performing this task for size(A) ~ [100000, 1000] (over columns case).
You can use a convolution:
For the 1D case:
N = 2 %tolerance
A = [0 1 1 0 0 1 1 1 0 0 1 1 1 1 0 1]
ind = conv(A,ones(N+1,1),'same');
%ind = 1 2 2 1 1 2 3 2 1 1 2 3 3 2 2 1
%A = 0 1 1 0 0 1 1 1 0 0 1 1 1 1 0 1
ind = conv(ind>N,ones(N+1,1),'same')>0;
%ind = 0 0 0 0 0 1 1 1 0 0 1 1 1 1 0 0
%A = 0 1 1 0 0 1 1 1 0 0 1 1 1 1 0 1
A(ind) = 0
if N is odd you need an extra step:
ind = conv(A,ones(N+1,1),'same');
ind(find(ind==N+1)+1) = N+1 %the extra step
ind = conv(ind>N,ones(N+1,1),'same')>0;
Generalization for nD dimension:
N = 3 %tolerance
A = round(rand(5,5,5));
for ii = 1:ndims(A)
conv_vec = permute(ones(N+1,1),circshift([1:ndims(A)],ii-1,2))
ind = convn(A,conv_vec,'same')
if mod(N,2) == 1
ind(find(ind==N+1)+1) = N+1
end
ind = convn(ind>N,conv_vec,'same')>0
X = A;
X(ind) = 0
end
Related
I've been struggling with this for a bit now. I have a small matrix s for example and a bigger matrix B as shown below.
B =
0 0 0 0 0 0 1 1
1 1 0 0 1 0 1 1
1 1 0 1 0 0 1 1
1 1 1 0 0 0 1 0
0 0 1 1 1 0 0 1
0 0 0 1 1 1 1 1
1 1 1 0 0 0 1 0
0 1 1 0 1 1 0 0
s =
1 1
1 1
What I want to do is iterate through B with s and compare the values. If all the values in s equal the values in B (the small section of B), then the answer is 1, if not then 0.
The 1's and 0's would be placed in a matrix as well.
This is what I've done so far but unfortunately, it doesn't iterate step by step and doesn't create a matrix either.
s = ones(2,2)
B = randi([0 1],8,8)
f = zeros(size(B))
[M,N]=size(B); % the larger array
[m,n]=size(s); % and the smaller...
for i=1:M/m-(m-1)
for j=1:N/n-(n-1)
if all(s==B(i:i+m-1,j:j+n-1))
disp("1")
else
disp("0")
end
end
end
Any help would be appreciated!
The following code works on the examples you supplied, I haven't tested it on anything else, and it will not work if the dimensions of the smaller matrix are not factors of the dimensions of the larger matrix, but you didn't indicate that it needed to do that in your description.
B =[0 0 0 0 0 0 1 1
1 1 0 0 1 0 1 1
1 1 0 1 0 0 1 1
1 1 1 0 0 0 1 0
0 0 1 1 1 0 0 1
0 0 0 1 1 1 1 1
1 1 1 0 0 0 1 0
0 1 1 0 1 1 0 0];
S =[1 1
1 1];
%check if array meets size requirements
numRowB = size(B,1);
numRowS = size(S,1);
numColB = size(B,2);
numColS = size(S,2);
%get loop multiples
incRows = numRowB/numRowS;
incCols = numColB/numColS;
%create output array
result = zeros(incRows, incCols);
%create rows and colums indices
rowsPull = 1:numRowS:numRowB;
colsPull = 1:numColS:numColB;
%iterate
for i= 1:incRows
for j= 1:incCols
result(i,j) = isequal(B(rowsPull(i):rowsPull(i)+numRowS-1, colsPull(j):colsPull(j)+numColS-1),S);
end
end
%print the resulting array
disp(result)
I have a binary array and I would like to flip values based on the length which they repeat. as an example
Ar = [0 1 0 0 0 1 1 0 0 0 1 1 1 1 1 0 0 0 0 1 1 1 1 1 1];
Ideally I would like to flip the 1's which repeat only 2 or fewer times resulting in the following.
Ar = [0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 0 0 0 0 1 1 1 1 1 1];
From what I have located online, the Diff function is most commenly used to locate and remove sequences. But from what I have located, it appears to target all instances.
Simply use imopen from Image Processing toolbox with a kernel of 3 ones -
imopen(Ar,[1,1,1])
Sample run -
>> Ar = [0 1 0 0 0 1 1 0 0 0 1 1 1 1 1 0 0 0 0 1 1 1 1 1 1];
>> out = imopen(Ar,[1,1,1]);
>> [Ar(:) out(:)]
ans =
0 0
1 0
0 0
0 0
0 0
1 0
1 0
0 0
0 0
0 0
1 1
1 1
1 1
1 1
1 1
0 0
0 0
0 0
0 0
1 1
1 1
1 1
1 1
1 1
1 1
Vectorized solution without using I.P. toolbox -
function out = filter_islands_on_length(Ar, n)
out = Ar;
a = [0 Ar 0];
d = diff(a);
r = find(d);
s0 = r(1:2:end);
s1 = r(2:2:end);
id_arr = zeros(1,numel(Ar));
m = (s1-s0) <= n;
id_arr(s0(m)) = 1;
id_arr(s1(m)) = -1;
out(cumsum(id_arr)~=0) = 0;
Sample runs -
>> Ar
Ar =
0 1 0 0 0 1 1 0 0 0 1 1 1
>> filter_islands_on_length(Ar, 2)
ans =
0 0 0 0 0 0 0 0 0 0 1 1 1
>> filter_islands_on_length(Ar, 1)
ans =
0 0 0 0 0 1 1 0 0 0 1 1 1
Another solution requiring no toolbox but needs Matlab 2016a or later:
n = 3; % islands shorter than n will be removed
Ar = movmax(movsum(Ar,n),[ceil(n/2-1) floor(n/2)])==n;
I know the code below :
N = 5;
assert(N>1 && mod(N,2)==1);
A = zeros(N);
% diamond mask
N2 = fix(N/2);
[I,J] = meshgrid(-N2:N2);
mask = (abs(I) + abs(J)) == N2;
% fill with zeros
A(mask) = 1;
which transforms matrix A to this:
A=
0 0 1 0 0
0 1 0 1 0
1 0 0 0 1
0 1 0 1 0
0 0 1 0 0
But I want the diamond to be filled with 1.
What should I do?
Here's a vectorized approach using bsxfun -
Nh = (N+1)/2;
range_vec = [1:Nh Nh-1:-1:1];
out = bsxfun(#plus,range_vec(:),range_vec) > Nh
Sample runs -
1) N = 5 :
out =
0 0 1 0 0
0 1 1 1 0
1 1 1 1 1
0 1 1 1 0
0 0 1 0 0
2) N = 9 :
out =
0 0 0 0 1 0 0 0 0
0 0 0 1 1 1 0 0 0
0 0 1 1 1 1 1 0 0
0 1 1 1 1 1 1 1 0
1 1 1 1 1 1 1 1 1
0 1 1 1 1 1 1 1 0
0 0 1 1 1 1 1 0 0
0 0 0 1 1 1 0 0 0
0 0 0 0 1 0 0 0 0
You can use tril and flip functions:
mat = tril(ones(N), round((N-1)/2)) - tril(ones(N), round((-N-1)/2));
out = mat & flip(mat)
Odd values of N:
% N = 5;
out =
0 0 1 0 0
0 1 1 1 0
1 1 1 1 1
0 1 1 1 0
0 0 1 0 0
Even values of N:
% N = 4;
out =
0 1 1 0
1 1 1 1
1 1 1 1
0 1 1 0
What you need is to return a 1 or a 0 based on the Manhattan distance from each array location to the center of your diamond
N = 5;
assert(N>1 && mod(N,2)==1);
A = false(N);
[m, n] = size(A); %dimensions of A
X = floor([m, n]/2); %floored division gives integer indices of center of array
x = X(1); y = X(2);
radius = m/2; %half the height gives the radius
for a = 1 : m
for b = 1 : n
A(a,b) = abs(a-x)+abs(b-y) <= radius; %test if manhatten distance <= radius
end
end
This naturally will need editing to suit your particular case... In particular, the center of your diamond can realistically be placed anywhere by modifying x, y, and the radius can be either smaller or larger than half the width of the array if you so choose.
Just add a for loop and fill all diagonals:
N = 5;
assert(N>1 && mod(N,2)==1);
A = zeros(N);
% diamond mask
N2 = fix(N/2);
[I,J] = meshgrid(-N2:N2);
for id = 0:N2
A((abs(I) + abs(J)) == id) = 1;
end
I need to replace the repeated elements in column of a matrix as 0's and delete the rows which has all 0's. If my matrix is like this means.
Input =
1 0 0 1
0 1 0 1
0 0 1 1
1 1 1 1
My expected output should be like this
Output =
1 0 0 1
0 1 0 0
0 0 1 0
0 0 0 0 ---> this row should be get deleted in this case
This doesn't work for my problem
c = [ 1 1 0 1 0 1 1 1 0 1 1 0];
[c, ic] = unique(a, 'first');
c(~ismember(1:length(a),ic)) = 0;
You can use logical indexing and cumsum:
A = [1 0 0 1;
0 1 0 1;
0 0 1 1;
1 1 1 1];
ind = cumsum(A); %cumulative sum (by column)
A(ind>1) = 0;
A(sum(A')==0,:)=[]
I have a Matrix
1 0 1 0 1
1 0 1 0 1
The output result is
1 1 0 0 1 1 0 0 1 1
How can I reshape this matrix so that it reads the columns first. I want the output like
1 0 1 0 1 1 0 1 0 1
Thank You
I have a few suggestions:
Reshape:
b = reshape(A.',1,[])
b =
1 0 1 0 1 1 0 1 0 1
Transpose then create a vector:
c = A.';
d = c(:).'
d =
1 0 1 0 1 1 0 1 0 1
Indexing:
e = A([1:size(e,1):end, 2:size(e,1):end])
e =
1 0 1 0 1 1 0 1 0 1
You can of course use permute to transpose it, instead of .':
f = permute(A,[2 1 3]);
g = f(:).'
g =
1 0 1 0 1 1 0 1 0 1