Find Value at a given Orientation in Matrix - matlab

In Matlab I've matrix where, in a previous stage of my code, an specific element was chosen. From this point of the matrix I would like to find a maximum, not just the maximum value between all its surounding neighbours for a given radius, but the maximum value at a given angle of orientation. Let me explain this with an example:
This is matrix A:
A =
0 1 1 1 0 0 9 1 0
0 2 2 4 3 2 8 1 0
0 2 2 3 3 2 2 1 0
0 1 1 3 2 2 2 1 0
0 8 2 3 3 2 7 2 1
0 1 1 2 3 2 3 2 1
The element chosen in the first stage is the 4 in A(2,4), and the next element should be the maximum value with, for example, a 315 degrees angle of orientation, that is the 7 in A(5,7).
What I've done is, depending on the angle, subdivide matrix A in different quadrants and make a new matrix (an A's submatrix) with only the values of that quadrant.
So, for this example, the submatrix will be A's 4th quadrant:
q_A =
4 3 2 8 1 0
3 3 2 2 1 0
3 2 2 2 1 0
3 3 2 7 2 1
2 3 2 3 2 1
And now, here is my question, how can I extract the 7?
The only thing I've been able to do (and it works) is to find all the values over a threshold value and then calculate how those points are orientated. Then, saving all the values that have a similar orientation to the given one (315 degrees in this example) and finally finding the maximum among them. It works but I guess there could be a much faster and "cleaner" solution.

This is my theory, but I don't have the image processing toolbox to test it. Maybe someone who does can comment?
%make (r,c) the center by padding with zeros
if r > size(A,1)/2
At = padarray(A, [size(A,1) - r], 0, 'pre');
else
At = padarray(A, [r-1], 0 'post');
if c > size(A,2)/2
At = padarray(At, [size(A,2) - c], 0, 'pre');
else
At = padarray(At, [c-1], 0 'post');
%rotate by your angle (maybe this should be -angle or else 360-angle or 2*pi-angle, I'm not sure
Ar = imrotate(At,angle, 'nearest', 'loose'); %though I think nearest and loose are defaults
%find the max
max(Ar(size(Ar,1)/2, size(Ar,2)/2:end); %Obviously you must adjust this to handle the case of odd dimension sizes.
Also depending on your array requirements, padding with -inf might be better than 0

The following is a relatively inexpensive solution to the problem, although I found wrapping my head around the matrix coordinate system a real pain, and there is probably room to tidy it up somewhat. It simply traces all matrix entries along a line around the starting point at the supplied angle (all coordinates and angles are of course based on matrix index units):
A = [ 0 1 1 1 0 0 9 1 0
0 2 2 4 3 2 8 1 0
0 2 2 3 3 2 2 1 0
0 1 1 3 2 2 2 1 0
0 8 2 3 3 2 7 2 1
0 1 1 2 3 2 3 2 1 ];
alph = 315;
r = 2;
c = 4;
% generate a line through point (r,c) with angle alph
[nr nc] = size(A);
x = [1:0.1:nc]; % overkill
m = tan(alph);
b = r-m*c;
y = m*x + b;
crd = unique(round([y(:) x(:)]),'rows');
iok = find((crd(:,1)>0) & (crd(:,1)<=nr) & (crd(:,2)>0) & (crd(:,2)<=nc));
crd = crd(iok,:);
indx=sub2ind([nr,nc],crd(:,1),crd(:,2));
% find max and position of max
[val iv]=max(A(indx)); % <-- val is the value of the max
crd(iv,:) % <-- matrix coordinates (row, column) of max value
Result:
val =
7
iv =
8
ans =
5 7

Related

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.

Transform a matrix to a stacked vector where all zeroes after the last non-zero value per row are removed

I have a matrix with some zero values I want to erase.
a=[ 1 2 3 0 0; 1 0 1 3 2; 0 1 2 5 0]
>>a =
1 2 3 0 0
1 0 1 3 2
0 1 2 5 0
However, I want to erase only the ones after the last non-zero value of each line.
This means that I want to retain 1 2 3 from the first line, 1 0 1 3 2 from the second and 0 1 2 5 from the third.
I want to then store the remaining values in a vector. In the case of the example this would result in the vector
b=[1 2 3 1 0 1 3 2 0 1 2 5]
The only way I figured out involves a for loop that I would like to avoid:
b=[];
for ii=1:size(a,1)
l=max(find(a(ii,:)));
b=[b a(ii,1:l)];
end
Is there a way to vectorize this code?
There are many possible ways to do this, here is my approach:
arotate = a' %//rotate the matrix a by 90 degrees
b=flipud(arotate) %//flips the matrix up and down
c= flipud(cumsum(b,1)) %//cumulative sum the matrix rows -and then flip it back.
arotate(c==0)=[]
arotate =
1 2 3 1 0 1 3 2 0 1 2 5
=========================EDIT=====================
just realized cumsum can have direction parameter so this should do:
arotate = a'
b = cumsum(arotate,1,'reverse')
arotate(b==0)=[]
This direction parameter was not available on my 2010b version, but should be there for you if you are using 2013a or above.
Here's an approach using bsxfun's masking capability -
M = size(a,2); %// Save size parameter
at = a.'; %// Transpose input array, to be used for masked extraction
%// Index IDs of last non-zero for each row when looking from right side
[~,idx] = max(fliplr(a~=0),[],2);
%// Create a mask of elements that are to be picked up in a
%// transposed version of the input array using BSXFUN's broadcasting
out = at(bsxfun(#le,(1:M)',M+1-idx'))
Sample run (to showcase mask usage) -
>> a
a =
1 2 3 0 0
1 0 1 3 2
0 1 2 5 0
>> M = size(a,2);
>> at = a.';
>> [~,idx] = max(fliplr(a~=0),[],2);
>> bsxfun(#le,(1:M)',M+1-idx') %// mask to be used on transposed version
ans =
1 1 1
1 1 1
1 1 1
0 1 1
0 1 0
>> at(bsxfun(#le,(1:M)',M+1-idx')).'
ans =
1 2 3 1 0 1 3 2 0 1 2 5

Matlab: Using a matrix as a mask to perform elementwise operations

In Matlab, I have two matrices: one with integers,x, and one with booleans, y:
x =
2 4 2
3 3 1
4 1 5
y =
0 0 1
1 1 0
1 0 1
What I now want to do is to assign some elements of x to 5, and I want to use y as a mask to determine which elements should be set to 5. So elements with a corresponding value of 0 in y should remain as they are in x, but those with a corresponding value of 1 in y should be set to 5. Therefore, the output should be:
2 4 5
5 5 1
5 1 5
I have tried the following:
x(y) = 5
Which gives me the error:
Subscript indices must either be real positive integers or logicals.
And I have also tried:
y(x) = 5
Which gives me the following:
5 5 1
5 1 0
5 0 1
Can somebody please explain what is going on here, and what I need to do to get my desired result?
The error you've got is due to the fact that, apparently, y is of type double while, in this case, it should be of type logical
You could try:
x(logical(y))=5
Hope this helps
Its not a fancy solution but will solve your problem
>> x = [ 2 4 2;3 3 1;4 1 5];
y = logical([ 0 0 1;1 1 0;1 0 1]);
f = x(:);
f(y(:)) = 5;
x = reshape(f,size(x))
x =
2 4 5
5 5 1
5 1 5
>>
x(find(y)) = 5; should work fine.

Histogram of subblock matrix

Given some matrix, I want to divide it into blocks of size 2-by-2 and show a histogram for each of the blocks. The following is the code I wrote to solve the problem, but the sum of the histograms I'm generating is not the same as the histogram of the whole matrix. Actually the the sum of the blocks' histograms is double what I expected. What am I doing wrong?
im =[1 1 1 2 0 6 4 3; 1 1 0 4 2 9 1 2; 1 0 1 7 4 3 0 9; 2 3 4 7 8 1 1 4; 9 6 4 1 5 3 1 4; 1 3 5 7 9 0 2 5; 1 1 1 1 0 0 0 0; 1 1 2 2 3 3 4 4];
display(imhist(im));
[r c]=size(im);
bs = 2; % Block Size (8x8)
nob=[r c ]./ bs; % Total number of Blocks
% Dividing the image into 8x8 Blocks
kk=0;
for k=1:nob/2
for i=1:(r/bs)
for j=1:(c/bs)
Block(:,:,kk+j)=im((bs*(i-1)+1:bs*(i-1)+bs),(bs*(j-1)+1:bs*(j-1)+bs));
count(:,:,kk+j)=sum(sum(sum(hist(Block(:,:,kk+j)))));
p=sum(count(:,:,kk+j));
end
kk=kk+(r/bs);
end
end
The reason they aren't the same is because you use imhist for im and hist for the blocks. Hist separates data into 10 different bins based on your data range, imhist separates data based on the image type. Since your arrays are doubles, the imhist bins are from 0 to 1.0 Thats why your imhist has only values at 0, and 1. The hist produces bins based on your data range, so it will actually change slightly depending on what value you pass in. So you cant simply add bins together. Even though they are the same size vector 10x1 , the values in them can be very different. in one set bin(1) can be the range 1-5 but in another set of data bin(1) could be 1-500.
To fix all these issues I used imhist, and converted your data to uint8. At the very end I subtract the two histograms from one another and get zero, this shows that they are indeed the same
im =uint8([1 1 1 2 0 6 4 3 ;
1 1 0 4 2 9 1 2 ;
1 0 1 7 4 3 0 9 ;
2 3 4 7 8 1 1 4 ;
9 6 4 1 5 3 1 4 ;
1 3 5 7 9 0 2 5 ;
1 1 1 1 0 0 0 0 ;
1 1 2 2 3 3 4 4 ]);
orig_imhist = imhist(im);
%% next thing
[r c]=size(im);
bs=2; % Block Size (8x8)
nob=[r c ]./ bs; % Total number of Blocks
%creates arrays ahead of time
block = uint8(zeros(bs,bs,nob(1)*nob(2)));
%we use 256, because a uint8 has 256 values, or 256 'bins' for the
%histogram
block_imhist = zeros(256,nob(1)*nob(2));
sum_block_hist = zeros(256,1);
% Dividing the image into 2x2 Blocks
for i = 0:nob(1)-1
for j = 0:nob(2)-1
curr_block = i*nob(1)+(j+1);
%creates the 2x2 block
block(:,:,curr_block) = im(bs*i+1:bs*i+ bs,bs*j+1:bs*j+ bs);
%creates a histogram for the block
block_imhist(:,curr_block) = imhist(block(:,:,curr_block));
%adds the current histogram to the running sum
sum_block_hist = sum_block_hist + block_imhist(:,curr_block);
end
end
%shows that the two are the same
sum(sum(orig_imhist-sum_block_hist))
if my solution solves your problem please mark it as the answer

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,:)