Check the surrounding neighbors of a matrix element in Matlab - matlab

I have a 480-by-640 matrix A. For each pixel, I want to check its neighbors. The neighbors of the pixel are determined by a value N. For example, this is a part of matrix A where all the zeros are the neighbours of pixel X when N=3:
0 0 0 0 0 0 0
0 0 0 0 0 0 0
0 0 0 0 0 0 0
0 0 0 X 0 0 0
0 0 0 0 0 0 0
0 0 0 0 0 0 0
0 0 0 0 0 0 0
As shown, because N=3, all these zeros are pixel X's neighbors. The problem is if X is located before the index N=3. Here the neighbors will be pixels with one values:
X 1 1 1 0 0 0
1 1 1 1 0 0 0
1 1 1 1 0 0 0
1 1 1 1 0 0 0
0 0 0 0 0 0 0
0 0 0 0 0 0 0
0 0 0 0 0 0 0
Could anyone advise on how to handle this?

The simplest way to proceed is just to pad your array with with values that do not return true for whatever you are checking (say, if you're looking for nonzeros, pad with zeros, or if you're looking for finite values, pad with NaN.) The padarray function can do this for you, but requires the Image Processing Toolbox*. Otherwise, you can pad arrays yourself. For example, an unoptimized way to proceed might be
A = rand(m,n);
Apadded = [zeros(N,2*N+n); [zeros(m,N), A, zeros(m,N)]; zeros(N,2*N+n)];
for i = N+1:N+m+1
for j = N+1:N+n+1
% Process neighborhood of A(i,j)
end
end
*Also note that these sorts of "sliding neighborhood" operations, being common in image processing, are implemented for you in the Image Processing Toolbox.

Related

How to randomly select x number of indices from a matrix in Matlab

I'm trying to generate a randomly scattered but limited number of 1's in a matrix of zeros efficiently.
Say I have a 10x10 matrix of zeros (zeros(10)) and I want to randomly place ten 1's so it looks like:
0 0 0 0 0 0 0 0 0 1
0 0 0 0 0 1 0 0 0 0
0 0 1 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 1 0
1 0 0 0 0 1 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 1 0 0 0 0 0 0
0 1 0 0 0 0 0 1 0 0
0 0 0 0 1 0 0 0 0 0
0 0 1 0 0 0 0 0 0 0
How can I do this WITHOUT a for-loop and without manually plugging in each position (this example is a much smaller version of my real problem)?
My code so far:
% Generate zeros
M = zeros(10)
% Generate random indices
Rands = [randsample(10, 10) randsample(10, 10)]
Where the first column is intended to be the row indices and the second column the column indices.
Now I obviously can't just drop these indices into the row and column indices of M like this:
M(Rands(:,1), Rands(:,2)) = 1
How can I vecorise the changes to these random indices?
You can use randperm to randomly generate the linear indices to be filled with 1:
sz = [10 10]; % desired size
n = 10; % desired number of ones
M = zeros(sz);
M(randperm(prod(sz), n)) = 1;
Alternatively, you can use randperm and reshape in one line:
M = reshape(randperm(prod(sz))<=n, sz);
You can use sub2ind to convert subscripts to linear index:
M(sub2ind(size(M),Rands(:,1),Rands(:,2)))=1

What is the result of dilation of this image?

I have this following matrix:
0 0 0 0 0 0 0
0 0 0 0 0 0 0
0 0 0 0 0 0 0
0 0 0 0 0 0 0
1 1 0 0 0 0 0
1 0 0 0 0 1 1
1 0 0 0 0 1 1
And I want to dilate it with the following structuring element:
1 0 0
1 1 1
1 1 1
I already did dilation on Matlab but the result does not match with the one I did by hand. So I guess I missing something here. As far as I know if any '1' of the structuring element touches any of the '1' in the matrix, then it means it is a hit and the center of the current window should be set as 1. If I make dilation in such fashion I would get following (without considering edges):
0 0 0 0 0 0 0
0 0 0 0 0 0 0
0 0 0 0 0 0 0
0 1 1 0 0 0 0
1 1 1 0 1 1 0
1 1 1 0 1 1 1
1 0 0 0 0 1 1
But Matlab gives the following result:
0 0 0 0 0 0 0
0 0 0 0 0 0 0
0 0 0 0 0 0 0
1 **0** **0** 0 0 0 0
1 1 1 0 1 1 0
1 1 1 0 1 1 1
1 1 0 0 1 1 1
Without considering edges it almost looks like my result but pixels styled with bold are '1' in my result but '0' in Matlab's result. What am I doing wrong? Pixel just below those '0's are '1's in the original image and the structuring element has '1' at that space when the center of the window is on those '0's so it is a hit and the center must be set '1' but Matlab doesn't do this. Can anyone explain me why? Am I missing something essential here?
Suppose matrix A is your image and matrix B is structuring element. You should pad the matrix A with zeroes on all sides. So let's say you have matrix C. Then you should perform Logical AND operation of C and B. The result matrix called D will be a dilated matrix. For further information, please see here.
Here is the code of Matlab which dilate image without 'imdilate' function:
clc;clearvars;close all;
%Image Dilation without using 'imdilate' function
% Matrix A is our image
A=[0 0 0 0 0 0 0;
0 0 0 0 0 0 0;
0 0 0 0 0 0 0 ;
0 0 0 0 0 0 0;
1 1 0 0 0 0 0;
1 0 0 0 0 1 1 ;
1 0 0 0 0 1 1 ];
%Structuring element
B=[1 0 0; 1 1 1; 1 1 1];
%Pad zeros on all the sides
C=padarray(A,[1 1]);
%Intialize a matrix of matrix size A with zeros
D=false(size(A));
for i=1:size(C,1)-2
for j=1:size(C,2)-2
%Perform logical AND operation
D(i,j)=sum(sum(B&C(i:i+2,j:j+2)));
end
end
display(D);
And this is my output, dilated matrix D:

Removing single pixels Matlab

I have a binary image. I have several single pixels in images. Single pixels are white (1) and all of their neighborhoods are black (0). for example image below shows a single pixel (at center) and two pixels (at left-bottom):
0 0 0 0 0
0 0 0 0 0
0 0 1 0 0
0 0 0 0 0
1 1 0 0 0
How can I remove single pixels with morphological operations in Matlab?
I give you another option without loop, using a 2D convolution with conv2:
M = [0 0 0 0 0
0 0 1 0 0
0 0 0 0 0
0 0 0 0 0
1 1 0 0 0]
C = [0 1 0
1 1 1
0 1 0]; % The matrice that is going to check if a `1` is alone or not.
%if you also want to consider the neibhbors on the diagonal choose:
%C = ones(3);
R = M.*conv2(M,C,'same')>1 %Check for the neighbors.
RESULT
R =
0 0 0 0 0
0 0 0 0 0
0 0 0 0 0
0 0 0 0 0
1 1 0 0 0
Upon request by the OP, I'm converting my short comment into a reply:
Since you explicitly asked for morphological operations: bwmorph has a 'clean' option which is described as "Removes isolated pixels (individual 1s that are surrounded by 0s)" with an example close to yours. Have a look at the bwmorph documentation page.
As in your previous question, you can use bwboundaries:
if P is the binary image, than:
B = bwboundaries(P,8);
for k = 1:numel(B)
if size(B{k})<=2
P(B{k}(1,1),B{k}(1,2)) = 0;
end
end
So for the example above P becomes:
P =
0 0 0 0 0
0 0 0 0 0
0 0 0 0 0
0 0 0 0 0
1 1 0 0 0

Changing the diagonals beside center diagonal of matrix

Is there a quick way to change the diagonals beside the center diagonal (referring to the 1s below):
m =
2 1 0 0 0 0 0 0 0
1 2 1 0 0 0 0 0 0
0 1 2 1 0 0 0 0 0
0 0 1 2 1 0 0 0 0
0 0 0 1 2 1 0 0 0
0 0 0 0 1 2 1 0 0
0 0 0 0 0 1 2 1 0
0 0 0 0 0 0 1 2 1
0 0 0 0 0 0 0 1 2
A quick way to change the center diagonal is m(logical(eye(size(m)))) = 2. How about assigning the diagonals beside it to values of 1?
The diag function takes a second parameter, k, which specifies which diagonal to target:
diag([-1,-1,-1,-1],-1) % or diag(-1*ones(4,1),1)
ans =
0 0 0 0 0
-1 0 0 0 0
0 -1 0 0 0
0 0 -1 0 0
0 0 0 -1 0
diag([1,1,1,1],1)
ans =
0 1 0 0 0
0 0 1 0 0
0 0 0 1 0
0 0 0 0 1
0 0 0 0 0
diag([2,2,2],2)
ans =
0 0 2 0 0
0 0 0 2 0
0 0 0 0 2
0 0 0 0 0
0 0 0 0 0
If you already have an existing matrix and you want to change one of the diagonals you could do this:
M = magic(5) % example matrix
v = [1,2,3,4] % example vector that must replace the first diagonal of M, i.e. the diagonal one element above the main diagonal
M - diag(diag(M,1),1) + diag(v,1)
The idea is to first use diag to extract the numbers of the diagonal you want to change, diag(M,1). Then to use diag again to change the vector that the first call to diag created into a matrix, diag(diag(M,1),1). You'll notice that this creates a matrix with the same dimensions as M, the same numbers as M on the 1st diagonal and 0s everywhere else. Thus M - diag(diag(M,1),1) just sets that first diagonal to 0. Now diag(v,1) creates a matrix with the same dimensions as M that is 0 everywhere but with the numbers of v on the first diagonal and so adding diag(v,1) only affects that first diagonal which is all 0s thanks to -diag(diag(M,1),1)
An alternative if you are just applying a constant to a diagonal (for example setting all the values on the first diagonal below the main diagonal to 6):
n = 5;
k = -1;
a = 6;
M = magic(n);
ind = diag(true(n-abs(k),1),k);
M(ind) = a;

To print the n nodes of maximum degree with Matlab

I need a matlab script that is going to return the n nodes of maximum degree in a graph.
For exemple:
N = maxnodes(Graph,n)
Graph is a matrix
n the number of nodes that we need
N is a vector that conatains the n nodes.
Here is my source code (script). But it doesn't work well.
M = [0 1 0 0 0 1 1 0 0 0 0 0 0 0 0 0;
1 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0;
0 1 0 1 1 0 0 0 0 0 0 0 0 0 0 0;
0 0 1 0 1 0 0 1 0 0 0 0 0 0 0 0;
0 0 1 1 0 1 0 0 0 0 0 1 0 0 0 0;
1 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0;
1 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0;
0 0 0 1 0 0 0 0 1 1 0 0 0 0 0 0;
0 0 0 0 0 0 0 1 0 0 0 0 0 1 0 1;
0 0 0 0 0 0 0 0 0 0 0 1 0 0 1 0;
0 0 0 0 0 0 1 0 0 0 0 1 0 0 0 0;
0 0 0 0 0 0 0 0 0 1 1 0 1 0 0 0;
0 0 0 0 0 0 0 0 0 0 0 1 0 0 1 0;
0 0 0 0 0 0 0 0 1 0 0 0 0 0 1 0;
0 0 0 0 0 0 0 0 0 1 0 0 1 1 0 1;
0 0 0 0 0 0 0 0 1 0 0 0 0 0 1 0;];
n = 5; % The number of nodes that we want
G=[]; % I'll store here the n nodes of maximum degree
for i=1:size(M)
G1(1,i)=sum(M(i,:)); % I'm storing each node with its degree in G1
G1(2,i)=i;
C(1,i)=G1(1,i); %I store only degree of nodes
end
C1 = sort(C,'descend'); % I sort "descendly" the degrees of nodes
for i=1:n %We want to take only the n nodes that we need and save it in C2
C2(1,i) = C1(1,i);
end
C2; % This vector stores the n descend maximum degrees that I need.
%My actual problem is here. How could I find the node that correspond to each degree?
%I tried to do it with the following loop:
for j=1:n
for i=1:size(M)
if C2(1,j) == G1(1,i)
G2(1,j)=G1(2,i);
end
end
end %But this loop doesn't store well the nodes in G2 because it repeats nodes.
G2
You have absolutely shown no effort so you actually shouldn't be getting any help from anyone.... but I love graph problems, so I'll throw you a bone.
I'm going to assume that Graph is an adjacency matrix, where each element (i,j) in the matrix corresponds to an edge connected between the two nodes i and j. I also am assuming that you have an undirected graph as input. If you examine the nature of the adjacency matrix (that Wikipedia article has a great example), it's not hard to see that the degree of a node i is simply the sum over all of the columns of row i in the adjacency matrix. Recall that the degree is defined as the total number of edges connected to a particular node. As such, all you have to do is sum over all of the columns for each row, and determine the rows that have the largest degree in your graph. Once we do this, we simply return the nodes that have this largest degree, which is up to n.
However, we will put in a safeguard where if we specify n to be larger than number of nodes having this maximum degree, we will cap it so that we only show up to this many nodes rather than n.
Therefore:
function [N] = maxnodes(Graph, n)
%// Find degrees of each node
degs = sum(Graph, 2);
%// Find those nodes that have the largest degree
locs = find(degs == max(degs));
%// If n is larger than the total number of nodes
%// having this maximum degree, then cap it
if n > numel(locs)
n = numel(locs);
end
%// Return those nodes that have this maximum degree
N = locs(1:n);
Here is a script that works very well and solves my problem. Otherwise, I would have liked well that my source code above would be debug.
function N = maxnodes(M,n)
nb1_rows= sum(M,2);
[nbs,is] = sort(nb1_rows,'descend');
N = transpose(is(1:n));