perimeter of periodic binary matrix (in matlab) - matlab

I have a periodic binary matrix which includes a block of ones. I need to find the boundary between zeros and ones in the matrix. Here is an example:
A=[0,1,1,1,0,0,0,1,1;
0,0,0,0,1,1,1,1,0;
0,0,0,0,1,1,1,1,0;
0,0,0,0,1,1,1,1,0;
0,1,1,1,1,1,1,0,0;
0,1,1,1,1,1,1,0,0;
0,1,1,1,0,0,0,0,0;
0,1,1,1,0,0,0,0,0;
0,1,1,1,0,0,0,0,0]
If the matrix was not periodic, I could use
perim=bwperim(A);
to find the perimeter. But this does not work perfectly for periodic matrices. I appreciate it if someone can help me with this problem.
EDIT:
a periodic matrix is a matrix that its boundary elements are connected to the boundary elements on the other side of the matrix.

You can pad the image on each side with the column/row that it's connected to. For example, the left edge will be padded with the rightmost column. Then use bwperim, and trim one row/column from each side of the result.
A = [0 1 1 1 0 0 0 1 1;
0 0 0 0 1 1 1 1 0;
0 0 0 0 1 1 1 1 0;
0 0 0 0 1 1 1 1 0;
0 1 1 1 1 1 1 0 0;
0 1 1 1 1 1 1 0 0;
0 1 1 1 0 0 0 0 0;
0 1 1 1 0 0 0 0 0;
0 1 1 1 0 0 0 0 0];
padded_A = [0 A(end,:) 0;
A(:,end) A A(:,1);
0 A(1,:) 0];
padded_perim = bwperim(padded_A);
perim = padded_perim(2:end-1,2:end-1);
Result:
perim =
0 1 1 1 0 0 0 1 1
0 0 0 0 1 1 1 1 0
0 0 0 0 1 0 0 1 0
0 0 0 0 1 0 0 1 0
0 1 1 1 0 0 1 0 0
0 1 0 0 1 1 1 0 0
0 1 0 1 0 0 0 0 0
0 1 0 1 0 0 0 0 0
0 1 0 1 0 0 0 0 0

You can use conv2 and padarray to find the perimeter:
4-connected:
perim = A & conv2(padarray(A, [1 1], 'circular'), [0 1 0;1 0 1;0 1 0], 'valid') < 4;
8-connected:
perim = A & conv2(padarray(A, [1 1], 'circular'), [1 1 1;1 0 1;1 1 1], 'valid') < 8;
Using the definition of the perimeter:
A pixel is part of the perimeter if it is nonzero and it is connected to at least one zero-valued pixel.
Use A & ... to ensure that the pixel is nonzero. The boundary pixels are replicated using padarray with option 'circular'. The padded array is convolved with the connectivity mask. use conv2 with option 'valid' to trim the replicated boundary pixels. If it is less than 4 or 8 it means that it is connected to at least one zero-valued pixel.

Related

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:

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;

Distance between connected components

I wanted to know if there is some inbuilt function to get distance between different connected components in MATLAB. I am using bwlabel to get the various connected components.Is there some way to get the distance between these connected components?
I guess you could use regionprops to locate the centroid of each connected component and then apply pdist to find the pairwise distance between each of them.
Simple example:
clear
clc
close all
%// Create logical array
BW = logical ([1 1 1 0 0 0 0 0
1 1 1 0 1 1 0 0
1 1 1 0 1 1 0 0
1 1 1 0 0 0 1 0
1 1 1 0 0 0 1 0
1 1 1 0 0 0 1 0
1 1 1 0 0 1 1 0
1 1 1 0 0 0 0 0])
%/ Call regionprops and concatenate centroid coordinates
S = regionprops(bwlabel(BW,4),'Centroid')
Centroids = vertcat(S.Centroid)
%// Measure pairwise distance
D = pdist(Centroids,'euclidean')
Outputs in the Command Window:
BW =
1 1 1 0 0 0 0 0
1 1 1 0 1 1 0 0
1 1 1 0 1 1 0 0
1 1 1 0 0 0 1 0
1 1 1 0 0 0 1 0
1 1 1 0 0 0 1 0
1 1 1 0 0 1 1 0
1 1 1 0 0 0 0 0
S =
3x1 struct array with fields:
Centroid
Centroids =
2.0000 4.5000
5.5000 2.5000
6.8000 5.8000
D =
4.0311 4.9729 3.5468

generating combinations in Matlab

I have a column vector x made up of 4 elements, how can i generate all the possible combinations of the values that x can take such that x*x' is less than or equal to a certain value?
note that the values of x are positive and integers.
To be more clear:
the input is the number of elements of the column vector x and the threshold, the output are the different possible combinations of the values of x respecting the fact that x*x' <=threshold
Example: threshold is 4 and x is a 4*1 column vector.....the output is x=[0 0 0 0].[0 0 0 1],[1 1 1 1]......
See if this works for you -
threshold = 4;
A = 0:threshold
A1 = allcomb(A,A,A,A)
%// Or use: A1 = combvec(A,A,A,A).' from Neural Network Toolbox
combs = A1(sum(A1.^2,2)<=threshold,:)
Please note that the code listed above uses allcomb from MATLAB File-exchange.
Output -
combs =
0 0 0 0
0 0 0 1
0 0 0 2
0 0 1 0
0 0 1 1
0 0 2 0
0 1 0 0
0 1 0 1
0 1 1 0
0 1 1 1
0 2 0 0
1 0 0 0
1 0 0 1
1 0 1 0
1 0 1 1
1 1 0 0
1 1 0 1
1 1 1 0
1 1 1 1
2 0 0 0

matlab's imfill function seems bugged?

I need to work with imfill in Matlab (Version 2010b, 7.11.0). I now think there is a bug in the program.
The most simple example that i found here is following: (Fills the Image background (0) beginning at the position [4 3])
BW = [ 0 0 0 0 0 0 0 0;
0 1 1 1 1 1 0 0;
0 1 0 0 0 1 0 0;
0 1 0 0 0 1 0 0;
0 1 0 0 0 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];
imfill(BW,[4 3])
According to the specifications this should work IMHO, but I always get following message. Can anyone tell me what I am doing wrong?
??? Error using ==> iptcheckconn at 56
Function IMFILL expected its second input argument, CONN,
to be a valid connectivity specifier.
A nonscalar connectivity specifier must be 3-by-3-by- ...
-by-3.
Error in ==> imfill>parse_inputs at 259
iptcheckconn(conn, mfilename, 'CONN', conn_position);
Error in ==> imfill at 124
[I,locations,conn,do_fillholes] = parse_inputs(varargin{:});
Error in ==> test at 9
imfill(BW,[4 3])
That does not explain the problem but converting BW to a logical array does work. I'm not sure as to why it's like this though:
clear
close all
clc
BW = [ 0 0 0 0 0 0 0 0
0 1 1 1 1 1 0 0
0 1 0 0 0 1 0 0
0 1 0 0 0 1 0 0
0 1 0 0 0 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];
BW2 = imfill(logical(BW),[4 3])
BW2 =
0 0 0 0 0 0 0 0
0 1 1 1 1 1 0 0
0 1 1 1 1 1 0 0
0 1 1 1 1 1 0 0
0 1 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
As you have already seen in the other solution by #Benoit_11, that most probably that input wasn't of logical class, which was throwing an error at you. So, you are set there!
Now, I would like to put forth a tiny bit of bonus suggestion here.
Let's suppose you have a set of seed points with their row and column IDs and you would like to fill an image with those seed points in one go. For that case,
you need to use those IDs as column vectors. Thus, if you have the row and column IDs as -
row_id = [4 3];
col_id = [3 7];
You can fill image with this -
BW = imfill(BW,[row_id(:) col_id(:)])
But, the following code would throw error at you -
BW = imfill(BW,[row_id col_id])