MATLAB, what is the best way to trace a boarder in a matrix that is changing each step? - matlab

I want to calculate the average slope or gradient at each iteration in such a matrix.
a=[ 10 9 8 7 6 5 5;
9 9 8 7 8 5 5;
8 8 7 7 5 5 5;
7 7 7 6 5 5 5;
6 6 6.6 5 5 5 5;
6 6 6.1 5 5 5 5;
6.3 5 5 5 5 5 5]
Where I am wanting to find the slope or gradient between the a(1,1) position during each step and at each point that boarders a value of 5. Each iteration the position of the 5's changes and so do the other values.
After doing so I will then average the slope. I haven't encountered a problem like this yet and I could not find a Matlab command to simplify.

First you must find out which the coast elements are. From your definition, an element is a coast element if it border (from the right) with a 5. If the sea level is 5, and is the lowest possible value i.e. no element goes beyond sea level, then you must first find all the land elements as,
land=a>5;
This returns,
ans =
1 1 1 1 1 0 0
1 1 1 1 1 0 0
1 1 1 1 0 0 0
1 1 1 1 0 0 0
1 1 1 0 0 0 0
1 1 1 0 0 0 0
1 0 0 0 0 0 0
Now, the coast elements are 1s that are followed by a 0. Take the column difference of the land matrix,
coastTmp=diff(land,1,2);
returning,
ans =
0 0 0 0 -1 0
0 0 0 0 -1 0
0 0 0 -1 0 0
0 0 0 -1 0 0
0 0 -1 0 0 0
0 0 -1 0 0 0
-1 0 0 0 0 0
and find the -1s,
coast=find(coastTmp==-1);
which are,
coast =
7
19
20
24
25
29
30
From here it is easy. The gradient is the difference of a(1,1) with all the coast elements, i.e.
slope=a(coast)-a(1,1); % negative slope here
giving,
slope =
-3.700000000000000
-3.400000000000000
-3.900000000000000
-3.000000000000000
-4.000000000000000
-4.000000000000000
-2.000000000000000
and of course the mean is,
mean(slope);

Related

using maltab to plot a matrix

I have a matrix. The entries are all integers. For example, my matrix would look like this
M = 1 1 1 2 2 2 2 3 3
1 1 1 2 2 2 2 3 0
4 4 4 5 5 5 5 0 0
4 4 4 5 5 5 0 0 0
4 4 4 5 5 0 0 0 0
4 4 4 5 0 0 0 0 0
6 6 6 0 0 0 0 0 0
6 6 0 0 0 0 0 0 0
6 0 0 0 0 0 0 0 0
I wonder if matlab has some neat function to plot the boundary of these sets generated by this matrix?
I think you are looking for a variation of contour plot. Don't forget that it will flip Y axis.
contour(M)
g = gca;
g.YDir = 'reverse';
for jj=1:size(M,1)
for ii=1:size(M,2)
text(jj,ii,num2str(M(ii,jj)));
end
end

How to normalize matrix setting 0 for minimum values and 1 for maximum values?

I need to transform a neural network output matrix with size 2 X N in zeros and ones, where 0 will represent the minimum value of the column and 1 contrariwise. This will be necessary in order to calculate the confusion matrix.
For example, consider this matrix 2 X 8:
2 33 4 5 6 7 8 9
1 44 5 4 7 5 2 1
I need to get this result:
1 0 0 1 0 1 1 1
0 1 1 0 1 0 0 0
How can I do this in MATLAB without for loops? Thanks in advance.
>> d = [ 2 33 4 5 6 7 8 9;
1 44 5 4 7 5 2 1];
>> bsxfun(#rdivide, bsxfun(#minus, d, min(d)), max(d) - min(d))
ans =
1 0 0 1 0 1 1 1
0 1 1 0 1 0 0 0
The bsxfun function is necessary to broadcast the minus and division operations to matrices of different dimensions (min and max have only 1 row each).
Other solution is the following (works only for 2 rows):
>> [d(1,:) > d(2,:); d(1,:) < d(2,:)]
ans =
1 0 0 1 0 1 1 1
0 1 1 0 1 0 0 0
If it's just 2xN, then this will work:
floor(A./[max(A); max(A)])
In general:
floor(A./repmat(max(A),size(A,1),1))

MATLAB - Inserting zero rows and columns into matrix

I have written some code that compresses a matrix to remove zero columns and rows, but I can't work out how to reconstruct the original matrix.
Say I have a matrix:
A = [ 0 3 0 2 1 0 6
3 0 0 4 8 0 5
0 0 0 0 0 0 0
2 4 0 0 2 0 1
1 8 0 2 0 0 7
0 0 0 0 0 0 0
6 5 0 1 7 0 0 ]
Here rows/columns 3 and 6 are empty, so my compression function will give the output:
A_dash = [ 0 3 2 1 6
3 0 4 8 5
2 4 0 2 1
1 8 2 0 7
6 5 1 7 0 ]
A_map = [ 1 2 4 5 7]
Where A_map is a vector mapping the indicies of the rows/columns of A_dash to A. This means that if A_map(3) = 4, then row/column 4 of A is the same as row/column 3 of A_dash - ie. a row/column of zeroes must be inserted between columns/rows 2 and 3 in A_dash
What is the easiest way people can suggest for me to recreate matrix A from A_dash, using the information in A_map?
Here is what I have got so far:
% orig_size is original number of columns/rows
c_count = size(A_dash,1);
A = zeros(c_count, orig_size); % c_count rows to avoid dimension mismatch
for ii = 1:c_count
A(:,A_map(ii)) == A_dash(:,ii);
end
This gives me the right result column-wise:
A = [ 0 3 0 2 1 0 6
3 0 0 4 8 0 5
2 4 0 0 2 0 1
1 8 0 2 0 0 7
6 5 0 1 7 0 0 ]
However, I'm not sure how i should go about inserting the rows, i suppose i could copy the first 1:i rows into one matrix, i:end rows to a second matrix and concatenate those with a zero row in between, but that feels like a bit of a
clunky solution, and probably not very efficient for large sized matrices..
Otherwise, is there a better way that people can suggest I store the map information? I was thinking instead of storing the mapping between column/row indices, that I just store the indices of the zero columns/rows and then insert columns/rows of zeros where appropriate. Would this be a better way?
You've got the indices of the valid rows/columns. Now all you've got to do is put them in a new matrix of zeros the same size as A:
B=zeros(size(A));
B(A_map,A_map)=A_dash
B =
0 3 0 2 1 0 6
3 0 0 4 8 0 5
0 0 0 0 0 0 0
2 4 0 0 2 0 1
1 8 0 2 0 0 7
0 0 0 0 0 0 0
6 5 0 1 7 0 0
Just to check...
>> A==B
ans =
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 1 1 1
1 1 1 1 1 1 1
1 1 1 1 1 1 1
1 1 1 1 1 1 1
A and B are equal everywhere, so we've reconstructed A.

Calculating a partial cumulative sum for a square matrix

Let's say I have a square matrix M:
M = [0 0 0 0 0 1 9; 0 0 0 0 0 4 4; 0 0 1 1 6 1 1; 0 1 2 9 2 1 0; 2 1 8 3 2 0 0; 0 8 1 1 0 0 0; 14 2 0 1 0 0 0]
0 0 0 0 0 1 9
0 0 0 0 0 4 4
0 0 1 1 6 1 1
M = 0 1 2 9 2 1 0
2 1 8 3 2 0 0
0 8 1 1 0 0 0
14 2 0 1 0 0 0
Now I'd like to calculate two different cumulative sums: One that goes from the top of each column to the element of the column, that is a diagonal element of the matrix, and one that goes from the bottom of the column to the same diagonal element.
The resulting matrix M'should therefore be the following:
0 0 0 0 0 1 9
0 0 0 0 0 4 5
0 0 1 1 6 2 1
M' = 0 1 3 9 4 1 0
2 2 8 5 2 0 0
2 8 1 2 0 0 0
14 2 0 1 0 0 0
I hope the explanation of what I'm trying to achieve is comprehensible enough. Since my matrices are much larger than the one in this example, the calculation should be efficient as well...but so far I couldn't even figure out how to calculate it "inefficiently".
In one line using some flipping and the upper triangular function triu:
Mp = fliplr(triu(fliplr(cumsum(M)),1)) ...
+flipud(triu(cumsum(flipud(M)),1)) ...
+flipud(diag(diag(flipud(M))));
The following will do the job:
Mnew = fliplr(triu(cumsum(triu(fliplr(M)),1))) + flipud(triu(cumsum(triu(flipud(M)),1)));
Mnew = Mnew - fliplr(diag(diag(fliplr(Mnew)))) + fliplr(diag(diag(fliplr(M))));
But is it the fastest method?
I think logical indexing might get you there faster

Matrix in matlab?

I am working on Connected Components labeling, and my matrix is:
1 1 0 2 2 2 0 3
1 1 0 2 0 2 0 3
1 1 1 1 0 0 0 3
0 0 0 0 0 0 0 3
4 4 4 4 0 5 0 3
0 0 0 4 0 5 0 3
6 6 0 4 0 0 0 3
6 6 0 4 0 7 7 7
and now, i want to do the second scan over it, for this i have made following code:
for i=1:1:r
for j=1:1:c
if (bw(i,j)>=1)
if (i-1>0 & i+1<=r)
% if ( bw(i,j)~= bw(i-1,j) | bw(i,j)~= bw(i+1,j))
if ( (bw(i,j)~= bw(i-1,j) & bw(i-1,j)>0))
bw(i,j)= min (bw(i-1,j),bw(i,j))
elseif ((bw(i,j)~= bw(i+1,j) & bw(i+1,j)>0))
bw(i,j) = min(bw(i+1,j),bw(i,j));
end
end
if (j-1>0 & j+1<=c)
if ( (bw(i,j)~= bw(i,j-1) & bw(i,j-1)>0))
bw(i,j) = min (bw(i,j-1),bw(i,j));
elseif((bw(i,j)~= bw(i,j+1) & bw(i,j+1)>0))
bw(i,j) = min (bw(i,j+1),bw(i,j)) ;
end
end
end
end
end
disp(bw);
but the problem is, when i run this code, i get the following output
1 1 0 2 2 2 0 3
1 1 0 1 0 2 0 3
1 1 1 1 0 0 0 3
0 0 0 0 0 0 0 3
4 4 4 4 0 5 0 3
0 0 0 4 0 5 0 3
6 6 0 4 0 0 0 3
6 6 0 4 0 7 7 7
only one value changes (2nd row, 4 col) in my result, whereas, I want:
1 1 0 1 1 1 0 3
1 1 0 1 0 1 0 3
1 1 1 1 0 0 0 3
0 0 0 0 0 0 0 3
4 4 4 4 0 5 0 3
0 0 0 4 0 5 0 3
6 6 0 4 0 0 0 3
6 6 0 4 0 3 3 3
could somebody please help? where am i making a mistake?
Nice solution by Jonas. As I was a bit into coding when I saw it added, I thought I'd show you my solution as well. Hopefully it is a bit more similar to your original code.
One of the mistakes you made was assuming this connection could be done in just one pass. Generaly it can not, as detections of some of the "snake elements" are dependent on the way you iterate throught the matrix. Becuase of this, I added an outer while loop.
bw =[ 1 1 0 2 2 2 0 3;
1 1 0 2 0 2 0 3;
1 1 1 1 0 0 0 3;
0 0 0 0 0 0 0 3;
4 4 4 4 0 5 0 3;
0 0 0 4 0 5 0 3;
6 6 0 4 0 0 0 3;
6 6 0 4 0 7 7 7 ];
%Set up matrix
[r,c] = size(bw);
%Zero pad border
bwZ = [zeros(1,c+2);[zeros(r,1) bw zeros(r,1)];zeros(1,c+2)];
%Iterate over all elements within zero padded border
done=0;%Done flag
cc=1; %Infinite loop protection
while not(done) && cc<r*c
done=1;cc=cc+1;
for i=2:r+1
for j=2:c+1
%Point should be evaluated
p = bwZ(i,j);
if p >= 1
%Pick out elements around active elements
if bwZ(i-1,j)==0;ue=inf;else ue = bwZ(i-1,j); end;%Up element
if bwZ(i+1,j)==0;de=inf;else de = bwZ(i+1,j); end;%Down element
if bwZ(i,j-1)==0;le=inf;else le = bwZ(i,j-1); end;%Left element
if bwZ(i,j+1)==0;re=inf;else re = bwZ(i,j+1); end;%Right element
bwZ(i,j) = min([ue de le re]);
%Set flag, if something has changed
if bwZ(i,j) ~= p
done = 0;
end
end
end
end
end
%Remove zero padding
bw = bwZ(2:end-1,2:end-1)
Output:
bw =
1 1 0 1 1 1 0 3
1 1 0 1 0 1 0 3
1 1 1 1 0 0 0 3
0 0 0 0 0 0 0 3
4 4 4 4 0 5 0 3
0 0 0 4 0 5 0 3
6 6 0 4 0 0 0 3
6 6 0 4 0 3 3 3
If the numbers don't need to be preserved, you can simply call bwlabel on your original image:
newImage = bwlabel(originalImage>0);
EDIT
Here's another version. It checks each connected component to see whether there's another connected component touching it. If yes, that connected component is relabelled.
%# nCC: number of connected components
nCC = max(originalImage(:));
for cc = 1:nCC
%# check whether the component exists
myCC = originalImage==cc;
if any(any(myCC))
%# create a mask to check for neighbors
%# by creating a border of 1 pixel
%# around the original label
msk = imdilate(myCC,true(3)) & ~myCC;
%# read all the pixel values under the mask
neighbours = originalImage(msk);
%# we're not interested in zeros, remove them
neighbours = neighbours(neighbours > 0);
if ~isempty(neighbours)
%# set the label of all neighbours to cc
originalImage( ismember(originalImage,neighbours) ) = cc;
end
end
end