Matlab adding rows and columns elegantly - matlab

Say that we have the following random matrix:
1 2 3 4
5 6 7 8
9 8 7 6
5 4 3 2
I'd like to transform it into the following:
1 0 2 0 3 0 4 0
0 0 0 0 0 0 0 0
5 0 6 0 7 0 8 0
0 0 0 0 0 0 0 0
9 0 8 0 7 0 6 0
0 0 0 0 0 0 0 0
5 0 4 0 3 0 2 0
0 0 0 0 0 0 0 0
For some reason I cannot use mathjax format so it looks a bit awful, sorry for this. Point, is, that I want to add row and columns of zeros in between of my current rows and columns so that I increase its size 2x.
I came up with the following code, but it only works for very small matrixes if i use it on a a big image it cannot finish due to memory limitation problems.
clear all
I=imread('image.png');
I=rgb2gray(I);
B=zeros(2*size(I));
[x, y]=find(-inf<I<inf);
xy=[x,y];
nxy=xy;
%coord change
nxy=2*xy-1;
B(nxy(:,1),nxy(:,2))=I(xy(:,1),xy(:,2));
I expected to be fast because it is fully vectorised with maltlab functions but it fails miserably. Is there some other elegant way to do this?

If you take a look at your indexing vectors, this is something like I([1 1 2 2] ,[1 2 1 2] ); for a 2x2 matrix which means you index each row and column twice. The right solution is B(1:2:end,1:2:end)=I; which indexes every second row and every second column.

This can be also done via the one liner, say your original matrix is called A, then
kron(A,[1,0;0,0])

Related

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.

MATLAB Add 1's to matrix elements around a specific element

Using MATLAB, I have a matrix such as:
1 1 0
1 0 1
1 1 1
The aim is to represent the zero's as a mine in a minesweeper program and the values around the 0's should reflect how many mines are adjacent to it.
Therefore creating a vector like this:
1 2 0
1 0 2
1 1 1
I have thought to take elements around the zero as a sub matrix and then add 1, but then it will turn 0's into 1's.
How would I program such a task?
I think this can be achieved by simple convolution plus some post-processing on the resultant matrix as follows:
% Defining a 6x6 matrix of zeros and ones
mineMat=randi(2,6,6)-1;
numberOfMines=conv2(double(~mineMat),ones(3,3),'same').*mineMat;
% Result:
mineMat=
1 0 1 1 0 0
0 0 0 1 0 0
1 1 1 1 1 0
1 1 1 1 0 1
0 1 0 0 0 0
0 1 1 0 0 0
numberOfMines=
3 0 3 3 0 0
0 0 0 3 0 0
2 3 2 3 4 0
1 2 2 4 0 4
0 3 0 0 0 0
0 3 3 0 0 0
Parag's answer would be my first option. Another approach is to use blockproc (Image Processing Toolbox):
blockproc(~M, [1 1], #(x)sum(x.data(:)), 'Bordersize', [1 1], 'TrimBorder', 0).*M
Sounds like you are looking to apply a (two dimensional) filter:
M = [1 1 0; 1 0 1; 1 1 1]==0;
F = filter2(ones(3),M);
F(M)=0
The middle line basically does the work (applying the filter) to create the count. The last line ensures that the mines stay at value 0.

Copying part of a matrix to another empty matrix with the same indices

I'm trying to copy part of a matrix (matrix 1) in matlab to another empty matrix of zeros (matrix 2) so that the section I copy from matrix 1 has the same indices in matrix 2, e.g.
Matrix 1 (mat1):
0 3 0 0 2 4 1 2 6
1 3 4 2 0 0 0 2 0
0 2 6 1 3 6 6 1 1
0 0 0 2 1 3 3 1 0
1 4 5 2 3 3 0 0 1
Matrix 2 (mat2) desired output:
0 0 0 0 0 0 0 0 0
0 0 4 2 0 0 0 0 0
0 0 6 1 3 6 6 0 0
0 0 0 2 1 3 3 0 0
0 0 0 0 0 0 0 0 0
I've tried something like
mat2([2:4],[3:7]) = mat1([2:4],[3:7])
but of course it doesn't work... any ideas of an efficient way to do this? I couldn't find another thread to help with this problem.
Thanks!
It does work. You just need to create mat2 first:
mat2 = zeros(size(mat1));
mat2(2:4, 3:7) = mat1(2:4, 3:7);
Note that you don't need the square brackets on those ranges.
Do this:
mat2 = zeros(size(mat1));
Before copying over.

Create a lower triangular matrix from a given vector

My problem is the following: I have a vector as
[3,4,5,6,7]
I want to create a matrix as
3 0 0 0 0
3 4 0 0 0
3 4 5 0 0
3 4 5 6 0
3 4 5 6 7
However, I don't want to use for loops because of the problem of size that I will eventually get.
I was thinking about using flipud, fliprl, hankel and toeplitz functions but cannot find a solution.
Try this:
x = [3,4,5,6,7]
tril(ones(length(x),1)*x)
ans =
3 0 0 0 0
3 4 0 0 0
3 4 5 0 0
3 4 5 6 0
3 4 5 6 7
If A is your vector, you can do
M=repmat(A, length(A), 1) .* tril(ones(length(A),length(A)),0)

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

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);