Matlab: Matrix Neighbour Extraction - matlab

I have a large number of images which I've broken down into segments such that their matrices look like:
img = [ 1 1 1 1 1 2 2 2 3 3 3 3
1 1 1 1 2 2 2 2 2 3 3 3
1 1 1 4 4 4 2 2 2 3 3 3
5 5 5 5 5 5 5 2 2 3 3 3 ];
where each number represents a different region and each region is arbitrarily shaped. So in this case, region 1 has neighbours 2, 4 and 5, region 2 has neighbours 1, 3 and 4 and so on.
I've extracted all of the regions into separate cells and obtained statistics (mean, variance, etc) which I plan to use to merge regions with statistics within a certain tolerance. I'm struggling to think of an efficient way to obtain the neighbours of each region to allow that merging to occur.
I have a horrible solution which takes a very long time for even one image:
referenceImage = [ 1 1 1 1 1 2 2 2 3 3 3 3;
1 1 1 1 2 2 2 2 2 3 3 3;
1 1 1 4 4 4 2 2 2 3 3 3;
5 5 5 5 5 5 5 2 2 3 3 3];
% Wish to extract each region into a separate cell
lastSP = 5;
sps = 1:lastSP;
% Could be a way to vectorise the below loop but it escapes me
superPixels(lastSP) = struct('Indices', 0, 'Neighbours', 0);
% Split data into separate cells
parfor a = 1 : lastSP
inds = find(referenceImage == sps(a));
superPixels(a).Indices = inds;
end
szs = size(referenceImage); % Sizes of RGB Image
for a = 1 : lastSP + 1
mask = zeros(szs(1), szs(2)); % Just bin mask wanted
mask(superPixels(a).Indices) = 1; % Mark the region pixels as one
mask = xor(bwmorph(mask, 'thicken'), mask); % Obtain the outlying regions
inds = find(mask ==1); % Fetch the external region indices
neighbours = []; % Have to dynamically grow neighbours matrix
neigh = 1;
for b = 1 : length(inds)
found = false;
if ~isempty(neighbours) % Check neighbours first
for c = 1 : length(neighbours)
if any(superPixels(neighbours(c)).Indices == inds(b))
found = true;
break;
end
end
end
if ~found
for c = 1 : lastSP + 1 % Check every other region
if any(superPixels(c).Indices == inds(b))
neighbours(neigh) = c;
neigh = neigh + 1;
break;
end
end
end
end
superPixels(a).Neighbours = neighbours;
end
I'm wondering if this is actually the best way to approach this problem. I know the very last loop is the main problem but I can't think of another way to reasonably write this, unless I recurse and check the neighbours of known neighbours.
Any help or nudges in the right direction would be greatly appreciated; thanks!

A simple (but probably not maximally efficient) solution is to dilate each region mask to pick neighbors:
labels = unique(img);
nLabels = length(labels);
neighbors = cell(nLabels,1);
for iLabel = 1:nLabels
msk = img == labels(iLabel);
adjacentPixelMask = imdilate(msk,true(3)) & ~msk;
neighbors{iLabel} = unique(img(adjacentPixelMask));
end
neighbors{1}
ans =
2
4
5

Related

Find series of the same value

Given a vector A that contains a sequence of numbers.
The objective is to find all series (longer than a given number "threshold") that contain the same value. The result should be the position of both first and last values of that series.
Example: given a vector A where:
A = [1 1 1 2 1 3 3 3 1 1 1 1 1 4 3 2 2 2 2 2 2 2 3 4];
and a threshold B = 5;
The results would be:
[9 13] % a series contain only the number 1 with length equal to 5
[16 22] % a series contain only the number 2 with length equal to 7
A=[1 1 1 2 1 3 3 3 1 1 1 1 1 4 3 2 2 2 2 2 2 2 3 4];
B = 5;
[l c]= size(A); % to know the size of 'A'
K=1; % to define the length of the series
W=1; % a value used to save the positions of the wanted series.
For i=1:c-1
If A(i)==A(i+1)
K=k+1;
Else
If k>= B % to test of the actual series is equal or longer than the given threshold
S(w,1)=i;
S(w,2)= S(w,1)-k+1; % saving the first position and the last position of the series in 'S'
w=w+1;
end
k=1;
end
S % the final result which is a table contain all wanted series.
the result is as follow:
S 13 9 % 13: the last position of the wanted series and 9 is the first position
16 22
This one work soo good. But still... it is soo slow when its come to a big table.
A faster, vectorized option is to modify the approach from this solution for finding islands of zeroes:
A = [1 1 1 2 1 3 3 3 1 1 1 1 1 4 3 2 2 2 2 2 2 2 3 4]; % Sample data
B = 5; % Threshold
tsig = (diff(A) ~= 0);
dsig = diff([1 tsig 1]);
startIndex = find(dsig < 0);
endIndex = find(dsig > 0)-1;
duration = endIndex-startIndex+1;
stringIndex = (duration >= (B-1));
result = [startIndex(stringIndex); endIndex(stringIndex)+1].';
And the results:
result =
9 13
16 22

How can I calculate the relative frequency of a row in a data set using Matlab?

I am new to Matlab and I have a basic question.
I have this data set:
1 2 3
4 5 7
5 2 7
1 2 3
6 5 3
I am trying to calculate the relative frequencies from the dataset above
specifically calculating the relative frequency of x=1, y=2 and z=3
my code is:
data = load('datasetReduced.txt')
X = data(:, 1)
Y = data(:, 2)
Z = data(:, 3)
f = 0;
for i=1:5
if X == 1 & Y == 2 & Z == 3
s = 1;
else
s = 0;
end
f = f + s;
end
f
r = f/5
it is giving me a 0 result.
How can the code be corrected??
thanks,
Shosho
Your issue is likely that you are comparing floating point numbers using the == operator which is likely to fail due to floating point errors.
A faster way to do this would be to use ismember with the 'rows' option which will result in a logical array that you can then sum to get the total number of rows that matched and divide by the total number of rows.
tf = ismember(data, [1 2 3], 'rows');
relFreq = sum(tf) / numel(tf);
I think you want to count frequency of each instance, So try this
data = [1 2 3
4 5 7
5 2 7
1 2 3
6 5 3];
[counts,centers] = hist(data , unique(data))
Where centers is your unique instances and counts is count of each of them. The result should be as follow:
counts =
2 0 0
0 3 0
0 0 3
1 0 0
1 2 0
1 0 0
0 0 2
centers =
1 2 3 4 5 6 7
That it means you have 7 unique instances, from 1 to 7 and there is two 1s in first column and there is not any 1s in second and third and etc.

How do I exclude pixels whose neighbours are out of the image borders?

I need to compute the neighbours of several points in an image, after which I would do some other stuff with what I get. I am however worried about the possibility of non-existence of some of the neighbours of some of these points.
I know I could pad the borders of my image by adding extra pixels, however I do not intend to employ that approach because of the nature of what I am trying to do. I am looking for a means via which I could test to see if all 5x5 connected neighbours surrounding each of these points exist, otherwise I would want to ignore that particular point if any part of its 5x5 immediate neighbour pixels are out of the borders.
To make this a bit more clearer I have included the code below:
img = [2 2 2 2 2 2 2;
2 3 2 2 2 2 2;
2 2 2 0 2 2 2;
4 4 1 2 2 2 2;
4 4 0 4 4 0 4;
4 1 1 4 4 4 4;
1 1 1 4 4 4 4];
[nrow, ncol] = find(img == 0)
P_Idx = sub2ind(size(img), nrow, ncol);
M = size(img, 1);
neighbor_offsets = [-M-1, -M, -M+1, -1, 1, M-1, M, M+1];
if any of the 5x5 immediate neighbours of Point P are outside the image borders,
Then ignore that particular point P,
else
find the neighbours as follows:
%Compute the immediate neighbors of ‘P’
P_neighbors = bsxfun(#plus, P_Idx, neighbor_offsets);
Do the following…
end

Divide list of numbers into 3 groups in matlab

I have a list of numbers, [1:9], that I need to divide three groups. Each group must contain at least one number. I need to enumerate all of the combinations (i.e. order does not matter). Ideally, the output is a x by 3 array. Any ideas of how to do this in matlab?
Is this what you want:
x = 1:9;
n = length(x);
T=3;
out = {};
%// Loop over all possible solutions
for k=1:T^n
s = dec2base(k, T, n);
out{k}{T} = [];
for p=1:n
grpIndex = str2num(s(p))+1;
out{k}{grpIndex} = [out{k}{grpIndex} x(p)];
end
end
%// Print result. size of out is the number of ways to divide the input. out{k} contains 3 arrays with the values of x
out
Maybe this is what you want. I'm assuming that the division in groups is "monotonous", that is, first come the elements of the first group, then those of the second etc.
n = 9; %// how many numbers
k = 3; %// how many groups
b = nchoosek(1:n-1,k-1).'; %'// "breaking" points
c = diff([ zeros(1,size(b,2)); b; n*ones(1,size(b,2)) ]); %// result
Each column of c gives the sizes of the k groups:
c =
Columns 1 through 23
1 1 1 1 1 1 1 2 2 2 2 2 2 3 3 3 3 3 4 4 4 4 5
1 2 3 4 5 6 7 1 2 3 4 5 6 1 2 3 4 5 1 2 3 4 1
7 6 5 4 3 2 1 6 5 4 3 2 1 5 4 3 2 1 4 3 2 1 3
Columns 24 through 28
5 5 6 6 7
2 3 1 2 1
2 1 2 1 1
This produces what I was looking for. The function nchoosekr_rec() is shown below as well.
for x=1:7
numgroups(x)=x;
end
c=nchoosekr_rec(numgroups,modules);
i=1;
d=zeros(1,modules);
for x=1:length(c(:,1))
c(x,modules+1)=sum(c(x,1:modules));
if c(x,modules+1)==length(opt_mods)
d(i,:)=c(x,1:modules);
i=i+1;
end
end
numgroups=[];
for x=1:length(opt_mods)
numgroups(x)=x;
end
count=0;
for x=1:length(d(:,1))
combos=combnk(numgroups,d(x,1));
for y=1:length(combos(:,1))
for z=1:nchoosek(9-d(x,1),d(x,2))
new_mods{count+z,1}=combos(y,:);
numgroups_temp{count+z,1}=setdiff(numgroups,new_mods{count+z,1});
end
count=count+nchoosek(9-d(x,1),d(x,2));
end
end
count=0;
for x=1:length(d(:,1))
for y=1:nchoosek(9,d(x,1))
combos=combnk(numgroups_temp{count+1},d(x,2));
for z=1:length(combos(:,1))
new_mods{count+z,2}=combos(z,:);
new_mods{count+z,3}=setdiff(numgroups_temp{count+z,1},new_mods{count+z,2});
end
count=count+length(combos(:,1));
end
end
function y = nchoosekr_rec(v, n)
if n == 1
y = v;
else
v = v(:);
y = [];
m = length(v);
if m == 1
y = zeros(1, n);
y(:) = v;
else
for i = 1 : m
y_recr = nchoosekr_rec(v(i:end), n-1);
s_repl = zeros(size(y_recr, 1), 1);
s_repl(:) = v(i);
y = [ y ; s_repl, y_recr ];
end
end
end

Matlab: How to conditionally select a subset of a matrix based on a row function?

I have a vector like this, which represents horizontal/vertical dimensions on a board
Hor Verti
1 2
2 3
4 1
2 3
2 2
1 4
..... and many more
I also have an starting vector of (1, 1) . I want to sub-select all rows of this matrix where either horizontal == 1 and vertical is +-2 units away, or vertical == 1 and horizontal = +-2.
Think of like a rook on a mini-4x4 chess board, that is constrained to moving 2 spaces at a time. I want to find all the valid spaces that it can move to out of a series of proposed spaces, it is ok that some of the proposals exists more than once, because they are proposed by different people.
I want subset of proposals where
[ (Hori== sInitial(1) && (Vert - sInitial(2) <=2) )
|| (Vert == sInitial(2) && (Hori - sInitial(1) <=2) )
]
Is it possible to do this without a for-loop?
For a 4x4 grid of possible positions:
>> [x,y] = ndgrid(1:4,1:4)
x =
1 1 1 1
2 2 2 2
3 3 3 3
4 4 4 4
y =
1 2 3 4
1 2 3 4
1 2 3 4
1 2 3 4
>> xy = [x(:) y(:)];
If the player is in position pos = [2 3] and allowed to move up to 2 spaces (either horizontal or vertical), the possible moves would be:
>> idx = (pdist2(xy, pos, 'cityblock') <= 2) & any(bsxfun(#eq, xy, pos), 2);
>> M = reshape(double(idx), [4 4]); M(pos(1),pos(2)) = nan;
M =
0 0 1 0
1 1 NaN 1
0 0 1 0
0 0 1 0
(I've marked the initial position with NaN, possible moves with 1, rest of grid with 0).
or in terms of coordinates:
>> coords = xy(idx,:)
coords =
2 1
2 2
1 3
2 3
3 3
4 3
2 4
The above pdist2 function computes the Manhattan distance.
That's easy. Given some data
data = [1 2
2 3
4 1
2 3
2 2
1 4
3 1];
simply do:
row_indices = find( (data(:,1)==1 | data(:,2)==1) & abs(data(:,1)-data(:,2))<=2 )
data(row_indices,:)