Learning to build band matrices in MATLAB - matlab

I am trying to build square band matrices using blkdiag or spdiags, but can't figure out how. I find the documentation for spdiags a bit confusing, and am not sure I can build these matrices in a simple call to blkdiag.
I would like to build a square band matrix from two parameters:
The width of the band
Matrix size
For example:
band_width = 2;
matrix size = 9;
Result:
[1 1 1 0 0 0 0 0 0]
[1 1 1 1 0 0 0 0 0]
[1 1 1 1 1 0 0 0 0]
[0 1 1 1 1 1 0 0 0]
[0 0 1 1 1 1 1 0 0]
[0 0 0 1 1 1 1 1 0]
[0 0 0 0 1 1 1 1 1]
[0 0 0 0 0 1 1 1 1]
[0 0 0 0 0 1 1 1 1]
[0 0 0 0 0 0 1 1 1]

A tricky one-line way to create a matrix like this is with convolution:
M = sign(conv2(eye(matrix_size),ones(band_width+1),'same'));
An identity matrix is created of the given size, then convolved in 2-D with a square matrix of ones, then converted to zeroes and ones by taking the sign.
The above is fine for making relatively small non-sparse matrices. For larger matrices the convolution may get expensive and you would probably want to represent the result as a sparse matrix instead. Here is how you can do this in a general way using SPDIAGS:
M = spdiags(ones(matrix_size,2*band_width+1),...
-band_width:band_width,matrix_size,matrix_size);

Related

How to select bits of a main matrix using a submatrix and store the selected its in an array? [duplicate]

This question already exists:
How to select bits of a matrix in a particular pattern? [closed]
Closed 2 years ago.
Let A = 6x6 matrix,
which contains binary bits (nXn matrix).`
A=[ 1 0 1 0 1 0 ; 0 1 1 0 1 1;1 1 0 1 1 0; 0 0 1 0 0 1;0 1 1 0 1 1;0 1 0 1 1 1];
Let B be a 3x3sub matrix of A
B = [1 0 1;0 1 1;1 1 0];
Now, I want to select the bits 1 0 1 1 0, i.e, need to select bits present in positions (1,1) (1,2) (2,2) (3,2) (3,3).
Next I need to traverse this pattern in main Matrix A.
So, with the above pattern total bits selected will be 20 and remaining positions bits will be 16.
I want store the pattern wise selected values in one array and the values of remaining position values in another array.
Pattern wise selected values,
`p1=[1 0 1 1 0 0 1 1 1 0 0 0 1 1 0 0 0 1 1 1];`
values of remaining position,
p2=[1 0 1 1 0 0 1 1 1 0 1 0 1 0 1 1];
It would be more helpful if the code is generalized for nXn main matrix.
I have attached the pattern and the procedure as an image.
Can some one help me out?
[Procedure] 1
[Pattern] 2
I think this is what you want. It gives the same answers that you have at least. It should work for any size matrix A. It assumes that the pattern is always 3x3, but that could easily be generalised.
A = [1 0 1 0 1 0;0 1 1 0 1 1;1 1 0 1 1 0;0 0 1 0 0 1;0 1 1 0 1 1;0 1 0 1 1 1];
pattern = logical([1 1 0;0 1 0;0 1 1]);
% Split A into 3x3 sub matrices
subs = mat2cell(A, [3,3], [3,3])';
s = size(subs);
p1=[];
p2=[];
for i = 1 : s(1)*s(2)
sub = subs{i};
p1 = [p1 sub'(pattern')'];
p2 = [p2 sub'(~pattern')'];
end
p1
p2

Generating in Matlab a "modified" diagonal matrix

I want to construct a matrix A in Matlab of dimension w x (m*w) where
each row is full of zeros except m consecutive ones that shift towards the right hand side as we move down to the rows.
Few examples can clarify
w=3,m=4
A=[1 1 1 1 0 0 0 0 0 0 0 0;
0 0 0 0 1 1 1 1 0 0 0 0;
0 0 0 0 0 0 0 0 1 1 1 1]
or
w=3, m=3
A=[1 1 1 0 0 0 0 0 0;
0 0 0 1 1 1 0 0 0;
0 0 0 0 0 0 1 1 1]
or
w=2, m=3
A=[1 1 1 0 0 0;
0 0 0 1 1 1]
I can't see how to proceed and any hint would be extremely helpful.
Step 1. Simplify the problem
If you write the "modified diagonal matrix" you are asking about as a row vector it will always look like the following
% 1 ... 1 0 ... ... 0 ... ... ... ... ... ... ... ... 1 ... 1
% m ones m*w zeros w-1 times the same as before m ones
Step 2. Think how to solve the simplified problem
The fundamental unit you need is a vector of m ones followed by m*w zeros;
Once you have built such vector, you need it to be repeated w times, MATLAB already knows how to do that;
The only thing you miss are the trailing ones: append them;
Now that the vector you were looking for is completed, you need to turn it into a matrix. MATLAB already knows how to do this too.
Final code
Once you understood the above steps, the final behaviour can be achieved even with a one-liner
>> m = 4; w = 3;
>> vec2mat([repmat([ones(1, m) zeros(1, m*w)], 1, w-1) ones(1, m)], w*m)
ans =
1 1 1 1 0 0 0 0 0 0 0 0
0 0 0 0 1 1 1 1 0 0 0 0
0 0 0 0 0 0 0 0 1 1 1 1
About speed
It's true, for loops aren't so slow anymore. I timed my one-liner solution, the trivial for loop and Luis Mendo's solution with eye() and repelem().
Click on images to zoom
Tested on the same machine, with MATLAB R2018a.
As you can see, as long as m and w are quite small, even if you could point out some differences in speed, them won't be noticeable to humans.
Anyway if you are going to work with bigger matrices, it becomes quite obvious which solution is the best.
Here are some approaches:
Using eye and repelem:
A = repelem(eye(w), 1, m);
Using eye and indexing:
A = eye(w);
A = A(1:w, ceil(1/m:1/m:w));
Using eye and kron:
A = kron(eye(w), ones(1,m));
Using singleton expansion:
A = bsxfun(#eq, (1:m).', ceil(1/m:1/m:w)); % Or A = (1:m).'==ceil(1/m:1/m:w);

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.

Matlab: How to insert zeros in specific places of vector

Can someone help me with the following problem in Matlab? I have a first vector containing the elements values. For example,
[2 8 4 9 3].
And a second one with the desired places in a second vector. For example,
[0 0 1 0 0 0 0 1 1 0 0 1 0 0 1].
Now I want to put the values from the first vector on the positions of the second one to end up with
[0 0 2 0 0 0 0 8 4 0 0 9 0 0 3].
What is the most efficient way of doing this when the size of the vector can be very large. (then thousands of elements)?
You can consider the y values as logical indicators, then use logical indexing to set those values to the values in x.
x = [2 8 4 9 3];
y = [0 0 1 0 0 0 0 1 1 0 0 1 0 0 1];
y(logical(y)) = x;
Alternatively, you could use
y(y==1) = x;
Use self-indexing:
% Your values:
V = [2 8 4 9 3];
% The desired locations of these values:
inds = [0 0 1 0 0 0 0 1 1 0 0 1 0 0 1];
% index the indices and assign
inds(inds>0) = V

From 4-connectivity to 8-connectivity in MATLAB

Say I have an boundary image in a logical matrix where true means boundary and false means region interior. The image encodes a tessellation of a 2D domain.
I was wondering if there is a compact way in MATLAB to "fix" those pixel neighborhoods where the separation between adjacent regions is only 4-connected and transform them into 8-connected in a manner that preserves the topology of the tessellation.
I believe this can be done with LUTs, but I'm not sure how to proceed. Do I have to, and if so, how do I exactly evaluate all the 3x3 pixel regions where the connectivity is only 4-wise to fill-in the corresponding pixels?
My proposed solution: use BWHITMISS to find the pixels whose neighborhood is at least 4-connected, dilate the result with a rectangular-shaped structuring element to convert those neighborhoods to 8-connected, finally we combine with the original image using logical-OR.
Example:
bw = [
0 0 0 1 0 1 0
0 0 1 1 1 1 1
0 1 1 1 0 1 0
0 0 1 0 1 0 0
0 1 1 0 0 0 0
0 0 1 0 1 1 1
0 0 1 0 0 1 0
];
hm = bwhitmiss(bw, [0 1 0; 1 1 1; 0 1 0]); %# [-1 1 -1; 1 1 1; -1 1 -1]
bw2 = imdilate(hm,ones(3)) | bw;
We can visualize the result:
[r c] = find(hm);
subplot(121), imshow(bw), hold on, plot(c(:),r(:),'o')
subplot(122), imshow(bw2)