Neighboring gray-level dependence matrix (NGLDM) in MATLAB - algorithm

I would like to calculate a couple of texture features (namely: small/ large number emphasis, number non-uniformity, second moment and entropy). Those can be computed from Neighboring gray-level dependence matrix. I'm struggling with understanding/implementation of this. There is very little info on this method (publicly available).
According to this paper:
This matrix takes the form of a two-dimensional array Q, where Q(i,j) can be considered as frequency counts of grayness variation of a processed image. It has a similar meaning as histogram of an image. This array is Ng×Nr where Ng is the number of possible gray levels and Nr is the number of possible neighbours to a pixel in an image.
If the image function f(i,j) is discrete, then it is easy to computer the Q matrix (for positive integer d, a) by counting the number of times the difference between each element in f(i,j) and its neighbours is equal or less than a at a certain distance d.
Here is the example from the same paper (d = 1, a = 0):
Input (image) matrix and output matrix Q:
I've been looking at this example for hours now and still can't figure out how they got that Q matrix. Anyone?
The method was originally created by C. Sun and W. Wee and was described in a paper called: "Neighboring gray level dependence matrix for texture classification" to which I got access, but can't download (after pressing download the page reloads and that's it).

In the example that you have provided, d=1 and a=0. When d=1, we consider pixels in an 8-pixel neighbourhood. When a=0, this means that we look for pixels that have the same value as the centre of the neighbourhood.
The basic algorithm is the following:
Initialize your NGLDM matrix to all zeroes. The total number of rows corresponds to the total number of possible intensities / values in your image. The total number of columns corresponds to how many pixels are in your neighbourhood plus 1. As such for d=1, we have an 8-pixel neighbourhood and so 8 + 1 = 9. Because there are 4 possible intensities (0,1,2,3), we thus have a 4 x 9 matrix. Let's call this matrix M.
For each pixel in your matrix, take note of this pixel. This goes in the Ng row.
Write out how many valid neighbours there are that surround this pixel.
Count how many times you see the neighbouring pixels matching that pixel in Step #1. This is your Nr column.
Once you figure out the numbers in Step #1 and Step #2, increment this location by 1.
Here's a slight gotcha: They ignore the border locations. As such, you don't do this procedure for the first row, last row, first column or last column. My guess is that they want to be sure that you have an 8-pixel neighbourhood all the time. This is also dictated by the distance d=1. You must be able to grab every valid pixel given a centre location at d=1. If d=2, then you would have to make sure that every pixel in the centre of the neighbourhood has a 25 pixel neighbourhood and so on.
Let's start from the second row, second column location of this matrix. Let's go through the steps:
Ng = 1 as the location is 1.
Valid neighbours - Starting from the top left pixel in this neighbourhood, and scanning left to right and omitting the centre, we have: 1, 1, 2, 0, 1, 0, 2, 2.
How many values are equal to 1? Three times. Therefore Nr = 3
M(Ng,Nr) += 1. Access row Ng = 1, and access row Nr = 3, and increment this spot by 1.
Want to know how I figured out they don't count the borders? Let's do the bottom left pixel. That location is 0, so Ng = 0. If you repeat the algorithm that I just said, you would expect Ng = 0, Nr = 1, and so you would expect at least one entry in that location in your matrix... but you don't! If you do similar checks around the border of the image, you'll see that entries that are supposed to be there... aren't. Take a look at the third row, fifth column. You would think that Ng = 1 and Nr = 1, but we don't see that in the matrix.
One more example. Why is M(Ng,Nr) = 4, Ng = 2, Nr = 4? Well, take a look at every pixel that has a 2 in it. The only valid locations where we can capture an 8 pixel neighbourhood successfully are the row=2, col=4, row=3, col=3, row=3, col=4, row=4, col=3, and row=4, col=4. By applying the same algorithm that we have seen, you'll see that for each of those locations, Nr = 4. As such, we see this combination of Ng = 2, Nr = 4 four times, and that's why the location is set to 4. However, in row=3, col=4, this actually is Nr = 5, as there are five 2s in that neighbourhood at that centre. That's why you see Ng = 2, Nr = 5, M(Ng,Nr) = 1.
As an example, let's do one of the locations. Let's do the 2 smack dab in the middle of the matrix (row=3, col=3):
Ng = 2
What are the valid neighbouring pixels? 1, 1, 2, 0, 2, 3, 2, 2 (omit the centre)
Count how many pixels equal to 2. There are four of them, so Nr = 4
M(Ng,Nr) += 1. Take Ng = 2, Nr = 4 and increment this spot by 1.
If you do this with the other valid locations that have 2, you'll see that Nr = 4 each time with the exception of the third row and fourth column, where Nr = 5.
So how would we implement this in MATLAB? What you can do is use im2col to transform each valid neighbourhood into columns. What I'm also going to do is extract the centre of each neighbourhood. This is actually the middle row of the matrix. We will then figure out how many pixels for each neighbourhood equal the centre, sum them up, and this will determine our Nr values. The Ng values will be the middle row values themselves. Once we do this, we can compute a histogram based on these values just like how the algorithm is doing to get our matrix. In other words, try doing this:
% // Your example
A = [1 1 2 3 1; 0 1 1 2 2; 0 0 2 2 1; 3 3 2 2 1; 0 0 2 0 1];
B = im2col(A, [3 3]); %//Convert neighbourhoods to columns - 3 x 3 means d = 1
C = bsxfun(#eq, B, B(5,:)); %//Figure out a logical matrix where each column tells
%//you how many elements equals the one in each centre
D = sum(C, 1) - 1; %// Must subtract by 1 to discount centre pixel
Ng = B(5,:).' + 1; % // We must make this into a column vector, and we also must
% // offset by 1 as MATLAB starts indexing by 1.
%// Column vector is for accumarray input
Nr = D.' + 1; %// Do the same for Nr. We could have simply left out the + 1 here and
%// took out the subtraction of -1 for D, but I want to explicitly show
%// the steps
Q = accumarray([Ng Nr], 1, [4 9]); %// 4 unique intensities, 9 possible locations (0-8)
... and here is our matrix:
Q =
0 0 1 0 0 0 0 0 0
0 0 1 1 0 0 0 0 0
0 0 0 0 4 1 0 0 0
0 1 0 0 0 0 0 0 0
If you check this, you'll see this matches with Q.
Bonus
If you want to be able to accommodate for the algorithm in general, where you specify d and a, we can simply follow the guidelines of your text. For each neighbourhood, you find the difference between the centre pixel and all of the other pixels. You count how many pixels are <= a for any positive integer d. Note that this will create a 2*d + 1 x 2*d + 1 neighbourhood we need to examine. We can also make this into a function. Without further ado:
%// Set A up yourself, then use a and d as inputs
%// Precondition - a and d are both integers. a can be 0 and d is positive!
function [Q] = calculateGrayDepMatrix(A, a, d)
neigh = 2*d + 1; % //Calculate rows/columns of neighbourhood
numTotalNeigh = neigh*neigh; % //Calculate total number of pixels in neighbourhood
middleRow = ceil(numTotalNeigh / 2); %// Figure out which index the middle row is
B = im2col(A, [neigh neigh]); %// Make into columns
Cdiff = abs(bsxfun(#minus, B, B(middleRow,:))); %// For each neighbourhood, subtract with its centre
C = Cdiff <= a; %// For each neighbourhood, figure out which differences are <= a
D = sum(C, 1) - 1; % //For each neighbourhood, add them up
Ng = B(middleRow,:).' + 1; % // Determine Ng and Nr, and find Q
Nr = D.' + 1;
Q = accumarray([Ng Nr], 1, [max(Ng) numTotalNeigh]);
end
We can recreate the scenario we showed above with the example matrix by:
A = [1 1 2 3 1; 0 1 1 2 2; 0 0 2 2 1; 3 3 2 2 1; 0 0 2 0 1];
Q = calculateGrayDepMatrix(A, 0, 1);
Q is thus:
Q =
0 0 1 0 0 0 0 0 0
0 0 1 1 0 0 0 0 0
0 0 0 0 4 1 0 0 0
0 1 0 0 0 0 0 0 0
Hope this helps!

Related

Most efficent way of finding submatrices of a matrix [matlab]

Say we have a matrix of zeros and ones
0 1 1 1 0 0 0
1 1 1 1 0 1 1
0 0 1 0 0 1 0
0 1 1 0 1 1 1
0 0 0 0 0 0 1
0 0 0 0 0 0 1
and we want to find all the submatrices (we just need the row indices and column indices of the corners) with these properties:
contain at least L ones and L zeros
contain max H elements
i.e. take the previous matrix with L=1 and H=5, the submatrix 1 2 1 4 (row indices 1 2 and column indices 1 4)
0 1 1 1
1 1 1 1
satisfies the property 1 but has 8 elements (bigger than 5) so it is not good;
the matrix 4 5 1 2
0 1
0 0
is good because satisfies both the properties.
The objective is then to find all the submatrices with min area 2*L, max area H and containg at least L ones and L zeros.
If we consider a matrix as a rectangle it is easy to find all the possibile subrectangles with max area H and min area 2*L by looking at the divisors of all the numbers from H to 2*L.
For example, with H=5 and L=1 all the possibile subrectangles/submatrices are given by the divisors of
H=5 -> divisors [1 5] -> possibile rectangles of area 5 are 1x5 and 5x1
4 -> divisors [1 2 4] -> possibile rectangles of area 4 are 1x4 4x1 and 2x2
3 -> divisors [1 3] -> possibile rectangles of area 3 are 3x1 and 1x3
2*L=2 -> divisors [1 2] -> possibile rectangles of area 2 are 2x1 and 1x2
I wrote this code, which, for each number finds its divisors and cycles over them to find the submatrices. To find the submatrices it does this: take for example a 1x5 submatrix, what the code does is to fix the first line of the matrix and move step by step (along all the columns of the matrix) the submatrix from the left edge of the matrix to the right edge of the matrix, then the code fixes the second row of the matrix and moves the submatrix along all the columns from left to right, and so on until it arrives at the last row.
It does this for all the 1x5 submatrices, then it considers the 5x1 submatrices, then the 1x4, then the 4x1, then the 2x2, etc.
The code do the job in 2 seconds (it finds all the submatrices) but for big matrices, i.e. 200x200, a lot of minutes are needed to find all the submatrices. So I wonder if there are more efficient ways to do the job, and eventually which is the most efficient.
This is my code:
clc;clear all;close all
%% INPUT
P= [0 1 1 1 0 0 0 ;
1 1 1 1 0 1 1 ;
0 0 1 0 0 1 0 ;
0 1 1 0 1 1 1 ;
0 0 0 0 0 0 1 ;
0 0 0 0 0 0 1];
L=1; % a submatrix has to containg at least L ones and L zeros
H=5; % max area of a submatrix
[R,C]=size(P); % rows and columns of P
sub=zeros(1,6); % initializing the matrix containing the indexes of each submatrix (columns 1-4), their area (5) and the counter (6)
counter=1; % no. of submatrices found
%% FIND ALL RECTANGLES OF AREA >= 2*L & <= H
%
% idea: all rectangles of a certain area can be found using the area's divisors
% e.g. divisors(6)=[1 2 3 6] -> rectangles: 1x6 6x1 2x3 and 3x2
tic
for sH = H:-1:2*L % find rectangles of area H, H-1, ..., 2*L
div_sH=divisors(sH); % find all divisors of sH
disp(['_______AREA ', num2str(sH), '_______'])
for i = 1:round(length(div_sH)/2) % cycle over all couples of divisors
div_small=div_sH(i);
div_big=div_sH(end-i+1);
if div_small <= R && div_big <= C % rectangle with long side <= C and short side <= R
for j = 1:R-div_small+1 % cycle over all possible rows
for k = 1:C-div_big+1 % cycle over all possible columns
no_of_ones=length(find(P(j:j-1+div_small,k:k-1+div_big))); % no. of ones in the current submatrix
if no_of_ones >= L && no_of_ones <= sH-L % if the submatrix contains at least L ones AND L zeros
% row indexes columns indexes area position
sub(counter,:)=[j,j-1+div_small , k,k-1+div_big , div_small*div_big , counter]; % save the submatrix
counter=counter+1;
end
end
end
disp([' [', num2str(div_small), 'x', num2str(div_big), '] submatrices: ', num2str(size(sub,1))])
end
if div_small~=div_big % if the submatrix is a square, skip this part (otherwise there will be duplicates in sub)
if div_small <= C && div_big <= R % rectangle with long side <= R and short side <= C
for j = 1:C-div_small+1 % cycle over all possible columns
for k = 1:R-div_big+1 % cycle over all possible rows
no_of_ones=length(find(P(k:k-1+div_big,j:j-1+div_small)));
if no_of_ones >= L && no_of_ones <= sH-L
sub(counter,:)=[k,k-1+div_big,j,j-1+div_small , div_big*div_small, counter];
counter=counter+1;
end
end
end
disp([' [', num2str(div_big), 'x', num2str(div_small), '] submatrices: ', num2str(size(sub,1))])
end
end
end
end
fprintf('\ntime: %2.2fs\n\n',toc)
Here is a solution centered around 2D matrix convolution. The rough idea is to convolve P for each submatrix shape with a second matrix such that each element of the resulting matrix indicates how many ones are in the submatrix having its top left corner at said element. Like this you get all solutions for a single shape in one go, without having to loop over rows/columns, greatly speeding things up (it takes less than a second for a 200x200 matrix on my 8 years old laptop)
P= [0 1 1 1 0 0 0
1 1 1 1 0 1 1
0 0 1 0 0 1 0
0 1 1 0 1 1 1
0 0 0 0 0 0 1
0 0 0 0 0 0 1];
L=1; % a submatrix has to containg at least L ones and L zeros
H=5; % max area of a submatrix
submats = [];
for sH = H:-1:2*L
div_sH=divisors(sH); % find all divisors of sH
for i = 1:length(div_sH) % cycle over all couples of divisors
%number of rows of the current submatrix
nrows=div_sH(i);
% number of columns of the current submatrix
ncols=div_sH(end-i+1);
% perpare matrix to convolve P with
m = zeros(nrows*2-1,ncols*2-1);
m(1:nrows,1:ncols) = 1;
% get the number of ones in the top left corner each submatrix
submatsums = conv2(P,m,'same');
% set values where the submatrices go outside P invalid
validsums = zeros(size(P))-1;
validsums(1:(end-nrows+1),1:(end-ncols+1)) = submatsums(1:(end-nrows+1),1:(end-ncols+1));
% get the indexes where the number of ones and zeros is >= L
topLeftIdx = find(validsums >= L & validsums<=sH-L);
% save submatrixes in following format: [index, nrows, ncols]
% You can ofc use something different, but it seemed the simplest way to me
submats = [submats ; [topLeftIdx bsxfun(#times,[nrows ncols],ones(length(topLeftIdx),1))]];
end
end
First, I suggest that you combine finding the allowable sub-matrix sizes.
for smaller = 1:sqrt(H)
for larger = 2*L:H/smaller
# add smaller X larger and larger x smaller to your shapes list
Next, start with the smallest rectangles in the shapes. Note that any solution to a small rectangle can be extended in any direction, to the area limit of H, and the added elements will not invalidate the solution you found. This will identify many solutions without bothering to check the populations within.
Keep track of the solutions you've found. As you work your way toward larger rectangles, you can avoid checking anything already in your solutions set. If you keep that in a hash table, checking membership is O(1). All you'll need to check thereafter will be larger blocks of mostly-1 adjacent to mostly-0. This should speed up the processing somewhat.
Is that enough of a nudge to help?

Choosing the Best Matrix

I am given X matrices of size Ni*Mi where 1<=N<=4 and 1<=M<=4, for all 1 <= i <= X
The game consists of choosing any rectangle (submatrix) from one of the given X matrices and removing this submatrix.
For Example: we have 1 matrix of size 4x4. Player 1 can choose a submatrix of size 4x4 (the entire matrix in this case) and remove it. Or they can choose a submatrix of 2x2 or 1x1 or 2x3 or any valid submatrix and remove it from the 4x4 matrix and we have the remaining matrix left in the game.
The player who can't make a move loses.
Which player wins?
Both Player Plays optimally.
This problem is solved with the Sprague Grundy theorem, which says that when you xor the nimbers of the individual matrices, then the player to move only loses when the result is 0 (because any move will turn the losing position in a winning position and the other player can then turn the winning position in a losing position again and so on, it's the nature of nim-like games).
The nimbers are calculated recursively, a nimber of a matrix is the mex (minimal exclusive = the smallest non-negative integer not present in the collection) of the nimbers of all reachable states.
All 0s is 0, you don't have a valid move and therefore it is a loss (0).
Exactly one 1 is 1 because the only reachable position is 0 and mex(0) = 1.
For two ones we have to decide whether they are adjacent or not, adjacent = mex(0, 1) = 2, not adjacent = mex(1) = 0... and so on.
Example:
1 0 0 0 1 1 0 1 1
0 0 1 1 0 0 0 1 1
0 0 1 0 0 0 1
0 0 0 0
= = =
2 xor 3 xor 1 = 0 => player to move loses.
A fast implementation could look like this:
Calculate the nimbers for the 16 (10 with symmetries) different cases in advance and store them in an array.
Assign result = 0
result = result xor nimbers[readRowCount()][readColCount()]
Repeat 3. until all matrix dimensions are read
if result != 0 then the first player wins else the second player
Example 2: nimber calculation
matrix:
1 1
1 1
valid moves:
0 1* 1 0* 1 1* 1 1* 0 0 1 1+ 0 0+ 1 0+ 0 1+
1 1 1 1 0 1 1 0 0 0 0 0 1 1 1 0 0 1
=
0 by definition
The other matrices can be grouped into 2 groups * and + because of symmetries.
Reachable positions from *:
0 1 0 1 0 0
0 1 1 0 0 1
= = =
1 = mex(0), because the only reachable position has a nimber of 0
0 = mex(1), because the only reachable position has a nimber of 1
2 = mex(0,1), because the reachable positions have nimbers of 0 and 1
==> the nimber for * is mex(0, 1, 2) = 3.
==> we already know now that the nimber of + is 2.
==> the nimber of the given matrix is mex(0, 2, 3) = 1.
(Before maraca posted his answer about the Sprague Grundy theorem, I was trying to identify an optimal strategy for the game; I'll describe it below, maybe it can also be used to code a solution.)
The outcome of the game is the result of whether each rectangle is removed in an odd or even number of moves. If the total number of moves is odd, player 1 wins; if it is even, player 2 wins.
Let's take a look at the possible rectangles:
For all "normal" rectangles (except 2x2 and 1x1) the player who first removes part of it can decide whether it is completely removed in an odd number of moves (e.g. by removing it completely in one go) or in an even number of moves; the cells indicated in yellow show a first move which leaves the player in control.
For "thin" rectangles which are only 1 cell wide, the odd/even decision is taken immediately (by either removing the whole rectangle, or leaving 1 cell). For the other "normal" rectangles, the decision is delayed from 1 up to 3 moves, depending on the other player's actions.
The 1x1 rectangle can only be removed in one move (i.e. an odd number of moves). The 2x2 rectangle can be removed in one move, but the player cannot force an even number of moves by removing only part of it; the other player can always decide odd or even.
As you can see from the moves indicated in yellow in the images, a move which creates a symmetrical situation (e.g. dividing the 4x4 square into two 4x1 rectangles) leaves the person who created this situation in control of the outcome for this rectangle. He can e.g. force an even number of moves for this rectangle like this:
This is also true for the whole game: if a player's move results in a symmetrical situation (e.g. two identical L-shapes and four 3x1 rectangles) he can respond to the other player's moves by mirroring them, and then when there is only one rectangles greater than 1x1 left, he can choose to remove it completely or leave one cell (depending on whether the number of single cells left over is odd or even) and win the game.
So the strategy comes down to creating a symmetrical situation, and not giving the other player the opportunity to create a symmetrical situation.
Note: a more complicated symmetry can be created by removing the center of a 3x3, 4x3 or 4x4 rectangle and creating a loop. Instead of mirroring the other player's moves, you then rotate them by 180 degrees (i.e. point-mirroring).
A few game results based on these ideas:
One rectangle: player 1 wins.
Two identical rectangles: player 2 wins.
A 1x1 and a thin rectangle: player 1 wins.
A 1x1 and a 2x2 rectangle: player 2 wins.
A 1x1 and a larger rectangle: player 1 wins.
A 2x2 and a thin rectangle: player 1 wins.
A 2x2 and a larger rectangle: player 1 wins.
Three identical rectangles: player 1 wins.
A 1x1, a 2x2 and any other rectangle: player 1 wins.
An even number of identical rectangles: player 2 wins.
An even number of identical and any other rectangle: player 1 wins.
An odd number of identical rectangles: player 1 wins.
Below is an implementation of the Sprague-Grundy theorem, as explained in maraca's answer. It uses a list of pre-calculated nimbers for rectangles up to 4x4.
function outcome(rectangles) {
var n = 0, nimbers = [[1,2,3,4],[2,1,5,8],[3,5,4,7],[4,8,7,3]];
for (var i = 0; i < rectangles.length; i++) {
n ^= nimbers[rectangles[i][0] - 1][rectangles[i][1] - 1];
}
return n > 0 ? 1 : 2;
}
document.write("Player " + outcome([[3,3],[3,4],[4,4]]) + " wins.<br>");
document.write("Player " + outcome([[1,1],[2,2],[3,3],[4,4]]) + " wins.");
The nimber of any 4x4 matrix can be calculated with the algorithm below. The 4x4 matrices are represented by 16-bit patterns, e.g. 65535 is a matrix filled with ones. A list of all rectangles (possible moves), represented as bit patterns, is pre-calculated.
function nimber(matrix) {
var rect = [ 1, 2, 3, 4, 6, 7, 8, 12, 14, 15,
16, 17, 32, 34, 48, 51, 64, 68, 96, 102,
112, 119, 128, 136, 192, 204, 224, 238, 240, 255,
256, 272, 273, 512, 544, 546, 768, 816, 819, 1024,
1088, 1092, 1536, 1632, 1638, 1792, 1904, 1911, 2048, 2176,
2184, 3072, 3264, 3276, 3584, 3808, 3822, 3840, 4080, 4095,
4096, 4352, 4368, 4369, 8192, 8704, 8736, 8738,12288,13056,
13104,13107,16384,17408,17472,17476,24576,26112,26208,26214,
28672,30464,30576,30583,32768,34816,34944,34952,49152,52224,
52416,52428,57344,60928,61152,61166,61440,65280,65520,65535];
var memo = [0]; // nimber of empty matrix is 0
return nim(matrix);
function nim(current) {
if (memo.hasOwnProperty(current)) return memo[current]; // use memoized value
var exclude = []; // set of nimbers of reachable states
for (var i = 0; i < rect.length; i++) {
if ((current & rect[i]) == rect[i]) { // this rectangle is a valid move
var next = current & ~rect[i]; // remove rectangle
exclude[nim(next)] = true; // recurse and add nimber to set
}
}
return (memo[current] = mex(exclude)); // memoize this nimber
}
function mex(n) { // minimum excludant of nimber set
var m = 0;
while (n[m]) ++m;
return m;
}
}
document.write(nimber(65535)); // 0b1111111111111111 represents a filled 4x4 matrix
The list of 16-bit patterns representing the rectangles of all sizes, positions and orientations in a 4x4 matrix can be generated like this:
function rectangles(width, height) {
var rect = [], line = (1 << width) - 1;
for (var y = 0; y < height; y++) {
for (var x = 0; x < width; x++) {
for (var h = 1; h <= height - y; h++) {
for (var w = 1; w <= width - x; w++) {
var bits = ((line >> (width - w)) << (width - x - w)) << (y * width)
for (var row = 1; row < h; row++) {
bits |= (bits << width);
}
rect.push(bits);
}
}
}
}
return rect;
}
document.write(rectangles(4,4));

Make an n x n-1 matrix from 1 x n vector where the i-th row is the vector without the i-th element, without a for loop

I need this for Lagrange polynomials. I'm curious how one would do this without a for loop. The code currently looks like this:
tj = 1:n;
ti = zeros(n,n-1);
for i = 1:n
ti(i,:) = tj([1:i-1, i+1:end]);
end
My tj is not really just a 1:n vector but that's not important. While this for loop gets the job done, I'd rather use some matrix operation. I tried looking for some appropriate matrices to multiply it with, but no luck so far.
Here's a way:
v = [10 20 30 40]; %// example vector
n = numel(v);
M = repmat(v(:), 1, n);
M = M(~eye(n));
M = reshape(M,n-1,n).';
gives
M =
20 30 40
10 30 40
10 20 40
10 20 30
This should generalize to any n
ti = flipud(reshape(repmat(1:n, [n-1 1]), [n n-1]));
Taking a closer look at what's going on. If you look at the resulting matrix closely, you'll see that it's n-1 1's, n-1 2's, etc. from the bottom up.
For the case where n is 3.
ti =
2 3
1 3
1 2
So we can flip this vertically and get
f = flipud(ti);
1 2
1 3
2 3
Really this is [1, 2, 3; 1, 2, 3] reshaped to be 3 x 2 rather than 2 x 3.
In that line of thinking
a = repmat(1:3, [2 1])
1 2 3
1 2 3
b = reshape(a, [3 2]);
1 2
1 3
2 3
c = flipud(b);
2 3
1 3
1 2
We are now back to where you started when we bring it all together and replace 3's with n and 2's with n-1.
Here's another way. First create a matrix where each row is the vector tj but are stacked on top of each other. Next, extract the lower and upper triangular parts of the matrix without the diagonal, then add the results together ensuring that you remove the last column of the lower triangular matrix and the first column of the upper triangular matrix.
n = numel(tj);
V = repmat(tj, n, 1);
L = tril(V,-1);
U = triu(V,1);
ti = L(:,1:end-1) + U(:,2:end);
numel finds the total number of values in tj which we store in n. repmat facilitates the stacking of the vector tj to create a matrix that is n x n large. After, we use tril and triu so that we extract the lower and upper triangular parts of the matrices without the diagonal. In addition, the rest of the matrix is all zero except for the relevant triangular parts. The -1 and 1 flags for tril and triu respectively extract this out successfully while ensuring that the diagonal is all zero. This creates a column of extra zeroes appearing at the last column when calling tril and the first column when calling triu. The last part is to simply add these two matrices together ignoring the last column of the tril result and the first column of the triu result.
Given that tj = [10 20 30 40]; (borrowed from Luis Mendo's example), we get:
ti =
20 30 40
10 30 40
10 20 40
10 20 30

Convert non-zero image pixels to row-column coordinates and save the output to workspace

I'm having difficulties converting image pixels to coordinates and making them appear in my MATLAB workspace. For example, I have the image with pixel values as below (it's a binary image of size 4x4):
0 0 0 0
0 1 1 0
0 1 1 0
0 0 0 0
After getting the pixels, I want to read each value and if they're not equal to zero (which means 1), I want to read the coordinates of that value and save them in to my MATLAB workspace. For example, this is the idea that I thought of:
[x,y] = size(image)
for i=1:x
for j=1:y
if (image(i,j)~=0)
....
However, I am stuck. Can anyone give any suggestion on how to read the coordinates of the non-zero values and save them to my workspace?
Specifically, my expected result in the workspace:
2 2
2 3
3 2
3 3
Doing it with loops is probably not the most efficient way to do what you ask. Instead, use find. find determines the locations in a vector or matrix that are non-zero. In your case, all you have to do is:
[row,col] = find(image);
row and col would contain the row and column locations of the non-zero elements in your binary image. Therefore, with your example:
b = [0 0 0 0;
0 1 1 0;
0 1 1 0;
0 0 0 0];
We get:
>> disp([row, col]);
2 2
3 2
2 3
3 3
However, you'll see that the locations are not in the order you expect. This is because the locations are displayed in column-major order, meaning that the columns are traversed first. In your example, you are displaying them in row-major order. If you'd like to maintain this order, you would sort the results by the row coordinate:
>> sortrows([row, col])
ans =
2 2
2 3
3 2
3 3
However, if you really really really really... I mean really... want to use for loops, what you would do is keep two separate arrays that are initially empty, then loop through each pixel and determine whether it's non-zero. If it is, then you would add the x and y locations to these two separate arrays.
As such, you would do this:
row = []; col = [];
[x,y] = size(image);
for i=1:x
for j=1:y
if (image(i,j)~=0)
row = [row; i]; %// Concatenate row and column location if non-zero
col = [col; j];
end
end
end
This should give you the same results as find.
you can use meshgrid() to collect those coordinates. The function generates two outputs, first being x coordinates, second being y coordinates. you'd go like this:
[xcoord ycoord] = meshgrid( 1:x_size, 1:y_size);
zeros_coordsx = xcoord( image == 0);
zeros_coordsy = ycoord( image == 0);
this is way faster that nested looping and keeps you within matlab's natural vector operation space... these two outputs are in sync,meaning that
image( zeros_coordsy(1), zeros_coordsx(1))
is one of the zeros on the image

How to find all connected components in a binary image in Matlab?

I have been trying to find all connected components using 8 neighbors in a binary image, without using the function "bwlabel".
For example, my input matrix is:
a =
1 1 0 0 0 0 0
1 1 0 0 1 1 0
1 1 0 0 0 1 0
1 1 0 0 0 0 0
0 0 0 0 0 1 0
0 0 0 0 0 0 0
I would to have something like this:
a =
1 1 0 0 0 0 0
1 1 0 0 2 2 0
1 1 0 0 0 2 0
1 1 0 0 0 0 0
0 0 0 0 0 3 0
0 0 0 0 0 0 0
There are 3 connected objects in this image.
This is a common problem in image processing. There are many variations, such as flood filling a region in an image, or finding what pixels belong to the same region. One common approach is to use depth first search. The idea is that you traverse your image from left to right and top to bottom and for any pixels encountered that are equal to 1, you add them to a stack. For each pixel in your stack, you pop off the stack, then look at the neighbouring pixels that are surrounding this pixel. Any pixels that are 1 you add to the stack. You need to keep an additional variable where any pixels you have already visited, you don't add these to the stack. When the stack is empty, we have found those pixels that are an entire region, so you mark these with a unique ID. You then repeat this procedure until you run out of regions in your image.
As such, given that your matrix is stored in A, this is the basic algorithm:
Initialize an array that's the same size as A that is logical. This will record which pixels we have examined or visited. Also initialize an output array B to all zeroes that gives you all of the connected components that you are seeking. Any locations that are zero in the end don't belong to any connected components. Also initialize an ID counter that keeps track of what connected component label each of these will have.
For each location that's in our matrix:
a. If the location is 0, mark this location as visited and continue.
b. If we have already visited this location, then continue.
c. If we have not visited this location... go to Step #3.
Add this unvisited location to a stack.
a. While this stack is not empty...
b. Pop this location off of the stack
c. If we have visited this location, then continue.
d. Else, mark this location as visited and mark this location with the connected components ID.
e. Given this location, look at the 8 neighbouring pixels.
f. Remove those pixels in this list that have been visited, not equal to 1 or out of bounds of the matrix
g. Whatever locations are remaining, add these to the stack.
Once the stack is empty, increment the counter, then go back to Step #2.
Keep going until we have visited all of the locations in our array.
Without further ado, here's the code.
%// Step #1
visited = false(size(A));
[rows,cols] = size(A);
B = zeros(rows,cols);
ID_counter = 1;
%// Step 2
%// For each location in your matrix...
for row = 1 : rows
for col = 1 : cols
%// Step 2a
%// If this location is not 1, mark as visited and continue
if A(row,col) == 0
visited(row,col) = true;
%// Step 2b
%// If we have visited, then continue
elseif visited(row,col)
continue;
%// Step 2c
%// Else...
else
%// Step 3
%// Initialize your stack with this location
stack = [row col];
%// Step 3a
%// While your stack isn't empty...
while ~isempty(stack)
%// Step 3b
%// Pop off the stack
loc = stack(1,:);
stack(1,:) = [];
%// Step 3c
%// If we have visited this location, continue
if visited(loc(1),loc(2))
continue;
end
%// Step 3d
%// Mark location as true and mark this location to be
%// its unique ID
visited(loc(1),loc(2)) = true;
B(loc(1),loc(2)) = ID_counter;
%// Step 3e
%// Look at the 8 neighbouring locations
[locs_y, locs_x] = meshgrid(loc(2)-1:loc(2)+1, loc(1)-1:loc(1)+1);
locs_y = locs_y(:);
locs_x = locs_x(:);
%%%% USE BELOW IF YOU WANT 4-CONNECTEDNESS
% See bottom of answer for explanation
%// Look at the 4 neighbouring locations
% locs_y = [loc(2)-1; loc(2)+1; loc(2); loc(2)];
% locs_x = [loc(1); loc(1); loc(1)-1; loc(1)+1];
%// Get rid of those locations out of bounds
out_of_bounds = locs_x < 1 | locs_x > rows | locs_y < 1 | locs_y > cols;
locs_y(out_of_bounds) = [];
locs_x(out_of_bounds) = [];
%// Step 3f
%// Get rid of those locations already visited
is_visited = visited(sub2ind([rows cols], locs_x, locs_y));
locs_y(is_visited) = [];
locs_x(is_visited) = [];
%// Get rid of those locations that are zero.
is_1 = A(sub2ind([rows cols], locs_x, locs_y));
locs_y(~is_1) = [];
locs_x(~is_1) = [];
%// Step 3g
%// Add remaining locations to the stack
stack = [stack; [locs_x locs_y]];
end
%// Step 4
%// Increment counter once complete region has been examined
ID_counter = ID_counter + 1;
end
end %// Step 5
end
With your example matrix, this is what I get for B:
B =
1 1 0 0 0 0 0
1 1 0 0 2 2 0
1 1 0 0 0 2 0
1 1 0 0 0 0 0
0 0 0 0 0 3 0
0 0 0 0 0 0 0
To search in a 4-connected neighbourhood
To modify the code to search in a 4-connected region, that is only North, East, West and South, the section where you see %// Look at the 8 neighbouring locations, that is:
%// Look at the 8 neighbouring locations
[locs_y, locs_x] = meshgrid(loc(2)-1:loc(2)+1, loc(1)-1:loc(1)+1);
locs_y = locs_y(:);
locs_x = locs_x(:);
To search in a 4-connected fashion, you simply have to modify this code to only give those cardinal directions:
%// Look at the 4 neighbouring locations
locs_y = [loc(2)-1; loc(2)+1; loc(2); loc(2)];
locs_x = [loc(1); loc(1); loc(1)-1; loc(1)+1];
The rest of the code remains untouched.
To match MATLAB's bwlabel function
If you want to match the output of MATLAB's bwlabel function, bwlabel searches for connected components in column major or FORTRAN order. The above code searches in row major or C order. Therefore you simply have to search along columns first rather than rows as what the above code is doing and you do this by swapping the order of the two for loops.
Specifically, instead of doing:
for row = 1 : rows
for col = 1 : cols
....
....
You would do:
for col = 1 : cols
for row = 1 : rows
....
....
This should now replicate the output of bwlabel.

Resources