Modify parts of a matrix based on linear equation on row and column numbers - matlab

For example:
>> tmp = ones(5,5)
tmp =
1 1 1 1 1
1 1 1 1 1
1 1 1 1 1
1 1 1 1 1
1 1 1 1 1
I want a command like:
tmp(colNum - 2*rowNum > 0) = 0
that modifies entries of tmp when the column number is more than twice the row number e.g. it should produce:
tmp =
1 1 0 0 0
1 1 1 1 0
1 1 1 1 1
1 1 1 1 1
1 1 1 1 1
As a second example, tmp(colNum - rowNum == 0) = 0 should set the diagonal elements of tmp to be zero.

A possibly more efficient solution is to use bsxfun like so
nRows = 5;
nCols = 5;
bsxfun(#(col,row)~(col - 2*row > 0), 1:nCols, (1:nRows)')
You can generalize this to just accept a function so it becomes
bsxfun(#(col,row)~f(col,row), 1:nCols, (1:nRows)')
And now just replace f with exactly the way you specify the equation in your question i.e.
f = #(colNum, rowNum)(colNum - 2*rowNum > 0)
or
f = #(colNum, rowNum)(colNum - rowNum == 0)
of course it might make more sense to specify your function to accept (row,col) instead of (col,row) as that's how MATLAB indexes

You can use meshgrid to generate a grid of 2D coordinates, then use this to impose any condition you wish. The variant you seek outputs 2 2D matrices where the first matrix gives you the column locations and the second matrix outputs the row locations.
For example, given your situation above:
>> [X,Y] = meshgrid(1:5, 1:5)
X =
1 2 3 4 5
1 2 3 4 5
1 2 3 4 5
1 2 3 4 5
1 2 3 4 5
Y =
1 1 1 1 1
2 2 2 2 2
3 3 3 3 3
4 4 4 4 4
5 5 5 5 5
You can see that each unique spatial location shared between X and Y give you the desired 2D location as if you were envisioning a 2D grid.
Therefore, you would do something like this for your first situation:
[X,Y] = meshgrid(1:5,1:5); % Generate 2D coordinates
tmp = ones(5); % Generate desired matrix
tmp(X > 2*Y) = 0; % Set desired locations to 0
We get:
>> tmp
tmp =
1 1 0 0 0
1 1 1 1 0
1 1 1 1 1
1 1 1 1 1
1 1 1 1 1
Finally for your second example:
[X,Y] = meshgrid(1:5,1:5); % Generate 2D coordinates
tmp = ones(5); % Generate desired matrix
tmp(X == Y) = 0; % Set desired locations to 0
We get:
>> tmp
tmp =
0 1 1 1 1
1 0 1 1 1
1 1 0 1 1
1 1 1 0 1
1 1 1 1 0
Simply put, generate a grid of 2D coordinates, then use those directly to index into your desired matrix using logical / Boolean conditions to set the desired locations to 0.

Related

Is there a way to do this without a for-loop?

Currently I have this function in MATLAB
function [ y ] = pyramid( x )
%PYRAMID Returns a "pyramid"-shapped matrix.
y = zeros(x); % Creates an empty matrix of x by x.
rings = ceil(x/2); % Compute number of "rings".
for n = 1:rings
% Take the first and last row of the ring and set values to n.
y([n,x-n+1],n:x-n+1) = n*ones(2,x-2*(n-1));
% Take the first and last column of the ring and set values to n.
y(n:x-n+1,[n,x-n+1]) = n*ones(x-2*(n-1),2);
end
end
Which produces the following output:
piramide(4)
ans =
1 1 1 1
1 2 2 1
1 2 2 1
1 1 1 1
piramide(5)
ans =
1 1 1 1 1
1 2 2 2 1
1 2 3 2 1
1 2 2 2 1
1 1 1 1 1
piramide(6)
ans =
1 1 1 1 1 1
1 2 2 2 2 1
1 2 3 3 2 1
1 2 3 3 2 1
1 2 2 2 2 1
1 1 1 1 1 1
Is there a way to achive the same result without using a for-loop ?
If you have the Image Processing Toolbox you can use bwdist:
function y = pyramid(x)
m([1 x], 1:x) = 1;
m(1:x, [1 x]) = 1;
y = bwdist(m,'chessboard')+1;
end
Other solution using min:
pyramid = #(x) min(min((1:x),(1:x).'), min((x:-1:1),(x:-1:1).'));

Transform a matrix to a stacked vector where all zeroes after the last non-zero value per row are removed

I have a matrix with some zero values I want to erase.
a=[ 1 2 3 0 0; 1 0 1 3 2; 0 1 2 5 0]
>>a =
1 2 3 0 0
1 0 1 3 2
0 1 2 5 0
However, I want to erase only the ones after the last non-zero value of each line.
This means that I want to retain 1 2 3 from the first line, 1 0 1 3 2 from the second and 0 1 2 5 from the third.
I want to then store the remaining values in a vector. In the case of the example this would result in the vector
b=[1 2 3 1 0 1 3 2 0 1 2 5]
The only way I figured out involves a for loop that I would like to avoid:
b=[];
for ii=1:size(a,1)
l=max(find(a(ii,:)));
b=[b a(ii,1:l)];
end
Is there a way to vectorize this code?
There are many possible ways to do this, here is my approach:
arotate = a' %//rotate the matrix a by 90 degrees
b=flipud(arotate) %//flips the matrix up and down
c= flipud(cumsum(b,1)) %//cumulative sum the matrix rows -and then flip it back.
arotate(c==0)=[]
arotate =
1 2 3 1 0 1 3 2 0 1 2 5
=========================EDIT=====================
just realized cumsum can have direction parameter so this should do:
arotate = a'
b = cumsum(arotate,1,'reverse')
arotate(b==0)=[]
This direction parameter was not available on my 2010b version, but should be there for you if you are using 2013a or above.
Here's an approach using bsxfun's masking capability -
M = size(a,2); %// Save size parameter
at = a.'; %// Transpose input array, to be used for masked extraction
%// Index IDs of last non-zero for each row when looking from right side
[~,idx] = max(fliplr(a~=0),[],2);
%// Create a mask of elements that are to be picked up in a
%// transposed version of the input array using BSXFUN's broadcasting
out = at(bsxfun(#le,(1:M)',M+1-idx'))
Sample run (to showcase mask usage) -
>> a
a =
1 2 3 0 0
1 0 1 3 2
0 1 2 5 0
>> M = size(a,2);
>> at = a.';
>> [~,idx] = max(fliplr(a~=0),[],2);
>> bsxfun(#le,(1:M)',M+1-idx') %// mask to be used on transposed version
ans =
1 1 1
1 1 1
1 1 1
0 1 1
0 1 0
>> at(bsxfun(#le,(1:M)',M+1-idx')).'
ans =
1 2 3 1 0 1 3 2 0 1 2 5

Finding a critical point in matrix

I'm attempting to find a critical point in a matrix. The value at index (i,j) should be greater than or equal to all elements in its row, and less than or equal to all elements in its column.
Here is what I have (it's off but I'm close):
function C = critical(A)
[nrow ncol] = size(A);
C = [];
for i = 1:nrow
for j = 1:ncol
if (A(i,j) >= A(i,1:end)) && (A(i,j) <= A(1:end,j))
C = [C ; A(i,j)]
end
end
end
You can use logical indexing.
minI = min(A,[],1);
maxI = max(A,[],2);
[row,col] = find(((A.'==maxI.').' & A==minI) ==1)
Details
Remember that Matlab is column major. We therefore transpose A and maxI.
A = [
3 4 1 1 2
2 4 2 1 4
4 3 2 1 2
3 3 1 1 1
2 3 0 2 1];
A.'==maxI.'
ans =
0 0 1 1 0
1 1 0 1 1
0 0 0 0 0
0 0 0 0 0
0 1 0 0 0
Then do the minimum
A==minI
ans =
0 0 0 1 0
1 0 0 1 0
0 1 0 1 0
0 1 0 1 1
1 1 1 0 1
And then multiply the two
((A.'==maxI.').' & A==minI)
ans =
0 0 0 0 0
0 0 0 0 0
0 0 0 0 0
0 1 0 0 0
0 1 0 0 0
Then find the rows and cols
[row,col] = find(((A.'==maxI.').' & A==minI) ==1)
row =
4
5
col =
2
2
Try this vectorised solution using bsxfun
function [ r,c,criP ] = critical( A )
%// finding the min and max values of each col & row resptly
minI = min(A,[],1);
maxI = max(A,[],2);
%// matching all the values of min & max for each col and row resptly
%// getting the indexes of the elements satisfying both the conditions
idx = find(bsxfun(#eq,A,maxI) & bsxfun(#eq,A,minI));
%// getting the corresponding values from the indexes
criP = A(idx);
%// Also getting corresponding row and col sub
[r,c] = ind2sub(size(A),idx);
end
Sample Run:
r,c should be a vector of equal length which represents the row and column subs of each Critical point. While val is a vector of same length giving the value of the critical point itself
>> A
A =
3 4 1 1 2
2 4 2 1 4
4 3 2 1 2
3 3 1 1 1
2 3 0 2 1
>> [r,c,val] = critical(A)
r =
4
5
c =
2
2
val =
3
3
I think there is a simpler way with intersect:
>> [~, row, col] = intersect(max(A,[],2), min(A));
row =
4
col =
2
UPDATE:
With intersect, in case you have multiple critical points, it will only give you the first one. To have all the indicies, there is also another simple way:
>> B
B =
3 4 1 4 2 5
2 5 2 4 4 4
4 4 2 4 2 4
3 4 1 4 1 4
2 5 4 4 4 5
>> row = find(ismember(max(B,[],2),min(B)))
row =
3
4
>> col = find(ismember(min(B),max(B,[],2)))
col =
2 4 6
Note that the set of critical points now should be the combination of row and col, means you have total 6 critical points in this example: (3,2),(4,2),(3,4),(4,4),(3,6),(4,6).
Here you can find how to export such combination.

Selecting that pixel that minimizes the distance

Say I have the following two matrices:
>> x = [1 4 3; 6 4 3; 6 9 3; 2 4 3; 5 4 0; 5 3 1; 6 4 7];
>> y = [0 0 1; 1 1 0; 1 1 0; 0 1 1; 0.2 0.8 0.54; 1 1 1; 0 0 0];
Where you can think of x as some image, and y as the degree of membership of each element of x to some region of interest.
Say I set those elements in x that have degree of membership = 1 to 1 (core) and the other elements to 0 as follows:
x = zeros(size(y));
x(y==1) = 1;
In which case I will have the following output:
0 0 1
1 1 0
1 1 0
0 1 1
0 0 0
1 1 1
0 0 0
Now, for the elements of 0, I substitute their values with the value of y in the corresponding location as follows:
x(x==0)=y(x==0);
Now, I select those pixels that are considered 4-neighbours of core but not in core as follows:
four_neighbourhood_pixels = imdilate(core, strel('diamond', 1)) - core;
My question is: how can we select a pixel p that belongs to four_neighbourhood_pixels that minimizes the distance between x & core?
Provided that for distance I calculate it as follows:
pdist([x,core],'minkowski');
Provided that x in the preceding command will be the matrix after substituting the zeros with the degree of membership values y i the corresponding location?
So, how can I select that pixel that belongs to four_neighbourhood_pixels that minimizes the distance between x with the zeros substituted and core?
Thanks.
If I understand correctly, core is the following matrix:
0 0 1
1 1 0
1 1 0
0 1 1
0 0 0
1 1 1
0 0 0
First find the distance between x and core.
dist=pdist([x,core],'minkowski');
dist1=squareform(dist);
[row1,row2]=find(dist1==min(dist1(:)); %interpretation: you get the minimum distance between row1 and row2 of [x core]
Veify if my understanding is correct:
You want a pixel from x which minimizes the distance dist and it should belong to four_neighbourhood_pixels. This is the matrix [x core]
1 4 3 0 0 1
6 4 3 1 1 0
6 9 3 1 1 0
2 4 3 0 1 1
5 4 0 0 0 0
5 3 1 1 1 1
6 4 7 0 0 0
Suppose you get the minimum value between 2nd row and 3rd row. Now based on this tell us what you mean by "find a pixel which minimizes..."

Finding maxima in a vector using MATLAB

i'm trying to find local maxima of a vector of numbers using MATLAB. The built-in findpeaks function will work for a vector such as:
[0 1 2 3 2 1 1 2 3 2 1 0]
where the peaks (each of the 3's) only occupy one position in the vector, but if I have a vector like:
[0 1 2 3 3 2 1 1 2 3 2 1 0]
the first 'peak' occupies two positions in the vector and the findpeaks function won't pick it up.
Is there a nice way to write a maxima-finding function which will detect these sort of peaks?
You can use the REGIONALMAX function from the Image Processing Toolbox:
>> x = [0 1 2 3 3 2 1 1 2 3 2 1 0]
x =
0 1 2 3 3 2 1 1 2 3 2 1 0
>> idx = imregionalmax(x)
idx =
0 0 0 1 1 0 0 0 0 1 0 0 0
Something much easier:
a = [1 2 4 5 5 3 2];
b = find(a == max(a(:)));
output:
b = [4,5]
a = [ 0 1 2 3 3 2 1 2 3 2 1 ];
sizeA = length(a);
result = max(a);
for i=1:sizeA,
if a(i) == result(1)
result(length(result) + 1) = i;
end
end
result contains the max, followed by all the values locations that are equal to max.