Faster way of putting Matrix elements into vectors other than a for loop - matlab

Hopefully this will come across correctly. I have 4 clouds/groups of points in a grid array (imagine a 2D space with 4 separate clusters of for example 3x3 grid of points) with each point having an X and Y coordinate. I'd like to write a vector of four points in the form of (X1, Y1, X2, Y2, X3, Y3, X4, Y4) where the number represents each cloud/group. Now I would actually like to write a matrix of all the combinations of the above vector covering all the points, so upper left points in all four groups in the first line, same for the second line, but the top middle point for group 4, etc.
One way to do it is to for-loop over all the variable, which would mean 8 nested for loops (4 for each X coordinate of 4 groups, 4 for each Y coordinate of 4 groups).
Is there a faster way maybe? 4 3x3 groups means 6561 combinations. Going to a larger array in each group, 11x11 for example, would mean 214 million combinations.
I'm trying to parallelize some calculations using these point coordinates, but writing the results in a parfor loop presents it's own set of issues if I was to do it on the points themselves. With a matrix of combinations I could just write the results in another matrix with the same number of rows and write the result of the nth row of point coordinates to the nth row of results.

As I understand, you have 4 groups of 3x3=9 coordinate pairs. You need to draw one pair from each group as one result, and produce all possible such results.
Thus, reducing each of the groups of 9 coordinate pairs to a lookup-table that is indexed with a number from 1 to 9, your problem can be reduced to drawing, with replacement, 4 values from the set 1:9.
It is fairly easy to produce all such combinations. permn is one function that does this (from the File Exchange). But you can actually get these even easier using ndgrid:
ind = 1:9;
[a1,a2,a3,a4] = ndgrid(ind,ind,ind,ind);
ind = [a1(:),a2(:),a3(:),a4(:)];
Each row in ind is the indices into one of your 3x3 grids.
For example, if grid 1 is:
x1 = [0.5,0.7,0.8];
y1 = [4.2,5.7,7.1];
then you can generate the coordinate pairs as follows:
[x1,y1] = meshgrid(x1,y1); % meshgrid is nearly the same as ndgrid...
xy1 = [x1(:),y1(:)];
Now your combination k is:
k = 563;
[xy1(ind(k,1),:), xy2(ind(k,2),:), xy3(ind(k,3),:), xy4(ind(k,4),:)]
You probably want to implement the above using multidimensional arrays rather than x1, x2, x3, etc. Adding indices to variables makes for confusing code that is difficult to extend. For example, for n groups you could write:
n = 4;
ind = 1:9;
ind = repmat({ind},n,1);
[ind{:}] = ndgrid(ind{:});
ind = cellfun(#(m)reshape(m,[],1), ind, 'UniformOutput',false);
ind = [ind{:}]; % ind is now the same as in the block of code above
etc.

Related

How do I track when multiple objects touch in MATLAB?

I have x,y pixel coordinates of multiple objects that have been tracked from an image (3744x5616). The coordinates are stored in a structure called objects, e.g.
objects(1).centre = [1868 1236]
The objects are each uniquely identified by a numerical code, e.g.
objects(i).code = 33
I want to be able to record each time any two objects come within a radius of 300 pixels each other. What would be the best way to check through if any objects are touching and then record the identity of both objects involved in the interaction, like object 33 interacts with object 34.
Thanks!
Best thing I can think of right now is a brute force approach. Simply check the distances from one object's centre with the rest of the other objects and manually check if the distances are < 300 pixels.
If you want this fast, we should probably do this without any toolboxes. You can intelligently do this with vanilla MATLAB using bsxfun. First, create separate arrays for the X and Y coordinates of each object:
points = reshape([objects.centre], 2, []);
X = points(1,:);
Y = points(2,:);
[objects.centre] accesses the individual coordinates of each centre field in your structure and unpacks them into a comma-separated list. I reshape this array so that it is 2 rows where the first row is the X coordinate and the second row is the Y coordinate. I extract out the rows and place them into separate arrays.
Next, create two difference matrices for each X and Y where the rows denote one unique coordinate and the columns denote another unique coordinate. The values inside this matrix are the differences between the point i at row i and point j at column j:
Xdiff = bsxfun(#minus, X.', X);
Ydiff = bsxfun(#minus, Y.', Y);
bsxfun stands for Binary Singleton EXpansion FUNction. If you're familiar with the repmat function, it essentially replicates matrices and vectors under the hood so that both inputs you're operating on have the same size. In this case, what I'm doing is specifying X or Y as both of the inputs. One is the transposed version of the other. By doing this bsxfun automatically broadcasts each input so that the inputs match in dimension. Specifically, the first input is a column vector of X and so this gets repeated and stacked horizontally for as many times as there are values in X.
Similarly this is done for the Y value. After you do this, you perform an element-wise subtraction for both outputs and you get the component wise subtraction between one point and another point for X and Y where the row gives you the first point, and the column gives you the second point. As a toy example, imagine we had X = [1 2 3]. Doing a bsxfun call using the above code gives:
>> Xdiff = bsxfun(#minus, [1 2 3].', [1 2 3])
Xdiff =
## | 1 2 3
----------------------
1 | 0 -1 -2
2 | 1 0 -1
3 | 2 1 0
There are some additional characters I placed in the output, but these are used solely for illustration and to give you a point of reference. By taking a row value from the ## column and subtracting from a column value from the ## row gives you the desired subtract. For example, the first row second column illustrates 1 - 2 = -1. The second row, third column illustrates 2 - 3 = -1. If you do this for both the X and Y points, you get the component-wise distances for one point against all of the other points in a symmetric matrix.
You'll notice that this is an anti-symmetric matrix where the diagonal is all 0 ... makes sense since the distance of one dimension of one point with respect to itself should be 0. The bottom left triangular portion of the matrix is the opposite sign of the right... because of the order of subtraction. If you subtracted point 1 with point 2, doing the opposite subtraction gives you the opposite sign. However, let's assume that the rows denote the first object and the columns denote the second object, so you'd want to concentrate on the lower half.
Now, compute the distance, and make sure you set either the upper or lower triangular half to NaN because when computing the distance, the sign gets ignored. If you don't ignore this, we'd find duplicate objects that interact, so object 3 and object 1 would be a different interaction than object 1 and object 3. You obviously don't care about the order, so set either the upper or lower triangular half to NaN for the next step. Assuming Euclidean distance:
dists = sqrt(Xdiff.^2 + Ydiff.^2);
dists(tril(ones(numel(objects))==1)) = NaN;
The first line computes the Euclidean distance of all pairs of points and we use tril to extract the lower triangular portion of a matrix that consists of all logical 1. Extracting this matrix, we use this to set the lower half of the matrix to NaN. This allows us to skip entries we're not interested in. Note that I also set the diagonal to 0, because we're not interested in distances of one object to itself.
Now that you're finally here, search for those objects that are < 300 pixels:
[I,J] = find(dists < 300);
I and J are row/column pairs that determine which rows and columns in the matrix have values < 300, so in our case, each pair of I and J in the array gives you the object locations that are close to each other.
To finally figure out the right object codes, you can do:
codes = [[objects(I).code].' [objects(J).code].'];
This uses I and J to access the corresponding codes of those objects that were similar in a comma-separated list and places them side by side into a N x 2 matrix. As such, each row of codes gives you unique pairs of objects that satisfied the distance requirements.
For copying and pasting:
points = reshape([objects.centre], 2, []);
X = points(1,:);
Y = points(2,:);
Xdiff = bsxfun(#minus, X.', X);
Ydiff = bsxfun(#minus, Y.', Y);
dists = sqrt(Xdiff.^2 + Ydiff.^2);
dists(tril(ones(numel(objects))==1)) = NaN;
[I,J] = find(dists < 300);
codes = [[objects(I).code].' [objects(J).code].'];
Toy Example
Here's an example that we can use to verify if what we have is correct:
objects(1).centre = [1868 1236];
objects(2).centre = [2000 1000];
objects(3).centre = [1900 1300];
objects(4).centre = [3000 2000];
objects(1).code = 33;
objects(2).code = 34;
objects(3).code = 35;
objects(4).code = 99;
I initialized 4 objects with different centroids and different codes. Let's see what the dists array gives us after we compute it:
>> format long g
>> dists
dists =
NaN 270.407100498489 71.5541752799933 1365.69396278961
NaN NaN 316.227766016838 1414.2135623731
NaN NaN NaN 1303.84048104053
NaN NaN NaN NaN
I intentionally made the last point farther than any of the other three points to ensure that we can show cases where there are points not near other ones.
As you can see, points (1,2) and (1,3) are all near each other, which is what we get when we complete the rest of the code. This corresponds to objects 33, 34 and 35 with pairings of (33,34) and (33,35). Points with codes 34 and 35 I made slightly smaller, but they are still greater than the 300 pixel threshold, so they don't count either:
>> codes
codes =
33 34
33 35
Now, if you want to display this in a prettified format, perhaps use a for loop:
for vec = codes.'
fprintf('Object with code %d interacted with object with code %d\n', vec(1), vec(2));
end
This for loop is a bit tricky. It's a little known fact that for loops can also accept matrices and the index variable gives you one column of each matrix at a time from left to right. Therefore, I transposed the codes array so that each pair of unique codes becomes a column. I just access the first and second element of each column and print it out.
We get:
Object with code 33 interacted with object with code 34
Object with code 33 interacted with object with code 35

How to create matrix of nearest neighbours from dataset using matrix of indices - matlab

I have an Nx2 matrix of data points where each row is a data point. I also have an NxK matrix of indices of the K nearest neighbours from the knnsearch function. I am trying to create a matrix that contains in each row the data point followed by the K neighbouring data points, i.e. for K = 2 we would have something like [data1, neighbour1, neighbour2] for each row.
I have been messing round with loops and attempting to index with matrices but to no avail, the fact that each datapoint is 1x2 is confusing me.
My ultimate aim is to calculate gradients to train an RBF network in a similar manner to:
D = (x_dist - y_dist)./(y_dist+(y_dist==0));
temp = y';
neg_gradient = -2.*sum(kron(D, ones(1,2)) .* ...
(repmat(y, 1, ndata) - repmat((temp(:))', ndata, 1)), 1);
neg_gradient = (reshape(neg_gradient, net.nout, ndata))';
You could use something along those lines:
K = 2;
nearest = knnsearch(data, data, 'K', K+1);%// Gets point itself and K nearest ones
mat = reshape(data(nearest.',:).',[],N).'; %// Extracts the coordinates
We generate data(nearest.',:) to get a 3*N-by-2 matrix, where every 3 consecutive rows are the points that correspond to each other. We transpose this to get the xy-coordinates into the same column. (MATLAB is column major, i.e. values in a column are stored consecutively). Then we reshape the data, so every column contains the xy-coordinates of the rows of nearest. So we only need to transpose once more in the end.

How to form a vector from RGB matrices

I have a problem in constructing a vector from an image. I had used a 512 x 512 colour image and separated the rgb planes. Now i want to convert these three planes into three 1D vectors which should be as given in the following example.
Consider a 4x4x3 matrix. Converting it into RGB planes is easy. Now I need to convert these three planes into 1D vectors as given below
V=[r1g1b1....r6]
W=[g6b6r7...g11]
X=[b11r12...B16]
The program ive written is as follows. I used the reshape function to convert RGB planes into 1D vectors. Now I have trouble in regrouping them into different vectors.
A=imread('C:\Users\Desktop\lena.jpg');
% Extract the individual red, green, and blue color channels.
R = A(:, :, 1);
G = A(:, :, 2);
B = A(:, :, 3);
R1 = reshape(R.',1,[]);
G1 = reshape(G.',1,[]);
B1 = reshape(B.',1,[]);
I had converted the 2D matrices R G and B into 1D vectors R1, G1 and B1. Now I just need to create new vectores with all three values.I have no idea how to proceed...Please do help...Thanks in advance.
OK, given your example, what you want to do is given a RGB image, you want to separate the image into 3 vectors such that the RGB components are interleaved. This can easily be achieved by a permutation of the dimensions first. What you can do specifically is:
B = permute(A, [3 1 2]);
What permute does is that it rearranges the dimensions so that it produces another matrix. Specifically, what we're going to do is we are going to take each value in the third dimension and make them appear in the first dimension. Next we will take the rows of A and make them unroll into the columns, and finally the columns of A and make them go over each plane.
The result is that each column will be a unique RGB pixel that describes your image. How the unrolling will work though is that it will go in column-major order. We can then use linear indexing to split them up into arrays like so:
N = numel(A)/3;
V = B(1 : N);
W = B(N + 1 : 2*N);
X = B(2*N + 1 : end);
The job of linear indexing is that you access elements using a single index, rather than indexing each dimension separately. How linear indexing would work here is that if we had an image that was X x Y x 3, after permutation, the image would be reshaped such that it became a 3 x X x Y matrix. N in our case would be the total number of elements in a single plane. Because you are trying to split up the image into 3 vectors, the above operation where it's calculating N should be able to evenly divide by 3 as we have three colour planes to deal with.
By doing B(1 : N), we would access all of the elements from the first slice, second slice, in column-major format up until we retrieve N elements. These get placed into V. We then continue from this point and grab N more elements and place them into W, and finally the rest go into X.
If you want to access the pixels in row-major order, you simply need to change the way permute is accessing the dimensions like so:
B = permute(A, [3 2 1]);
You would then just access the elements with the above code normally. If you don't want to use linear indexing, you could use reshape to reshape the matrix such that it becomes a three-column matrix, where each column would be the desired vector:
C = reshape(B, [], 3);
V = C(:,1);
W = C(:,2);
X = C(:,3);
From your 4x4x3 example it's clear that you want to index first with the color index. I assume you then want row and then column. In that case, if A is your image (3D array), your desired three vectors are the columns of
B = reshape(permute(A,[3 1 2]),[],3);
So, if you need those vectors as explicit variables,
vector1 = B(:,1);
vector2 = B(:,2);
vector3 = B(:,3);
If the desired index order is color, then column, then row, use
B = reshape(permute(A,[3 2 1]),[],3);

How to generate random cartesian coordinates given distance constraint in Matlab

I need to generate N random coordinates for a 2D plane. The distance between any two points are given (number of distance is N(N - 1) / 2). For example, say I need to generate 3 points i.e. A, B, C. I have the distance between pair of them i.e. distAB, distAC and distBC.
Is there any built-in function in MATLAB that can do this? Basically, I'm looking for something that is the reverse of pdist() function.
My initial idea was to choose a point (say A is the origin). Then, I can randomly find B and C being on two different circles with radii distAB and distAC. But then the distance between B and C might not satisfy distBC and I'm not sure how to proceed if this happens. And I think this approach will get very complicated if N is a large number.
Elaborating on Ansaris answer I produced the following. It assumes a valid distance matrix provided, calculates positions in 2D based on cmdscale, does a random rotation (random translation could be added also), and visualizes the results:
%Distance matrix
D = [0 2 3; ...
2 0 4; ...
3 4 0];
%Generate point coordinates based on distance matrix
Y = cmdscale(D);
[nPoints dim] = size(Y);
%Add random rotation
randTheta = 2*pi*rand(1);
Rot = [cos(randTheta) -sin(randTheta); sin(randTheta) cos(randTheta) ];
Y = Y*Rot;
%Visualization
figure(1);clf;
plot(Y(:,1),Y(:,2),'.','markersize',20)
hold on;t=0:.01:2*pi;
for r = 1 : nPoints - 1
for c = r+1 : nPoints
plot(Y(r,1)+D(r,c)*sin(t),Y(r,2)+D(r,c)*cos(t));
plot(Y(c,1)+D(r,c)*sin(t),Y(c,2)+D(r,c)*cos(t));
end
end
You want to use a technique called classical multidimensional scaling. It will work fine and losslessly if the distances you have correspond to distances between valid points in 2-D. Luckily there is a function in MATLAB that does exactly this: cmdscale. Once you run this function on your distance matrix, you can treat the first two columns in the first output argument as the points you need.

Matlab - relational matricies?

I have a vector of coordinates called x. I want to get the element(s) with the min y coordinate:
a = find(x(:,2)==min(x(:,2))); % Contains indices
This returns the indexes of the elements with the smallest y coordinates. I say element*s* because sometimes this would return more than 1 value (e.g. (10,2) and (24,2) both have 2 as y coordinate and if 2 is the min y coordinate...).
Anyway, my next step is to sort (ascending) the elements with the min y coordinates according to their x coordinates. First I do:
b = sort(x(a,1));
The above operation might rearrange the elements with min y coordinates so I want to apply this rearrangement to a as well. So I do:
[v i] = ismember(b, x(:, 1));
Unfortunately, if there are elements with the same x value but different y values and one of these elements turns out to be a member of a (i.e. b) then the above matrix chooses it. For example if (10,2) and (24,2) are the elements with smallest y coordinates and there is a 3rd element (24, 13) then it will mess up the above operation. Is there a better way? I wrote my script using loops and everything was fine but in line with Matlab's methodology I rewrote it and I fear my unfamiliarity with matlab is causing this error.
Sorry, I might have misunderstood your question but lemme rephrase what I think you want here:
You have a set of 2D coordinates:
x = [24,2; 10,2; 24,13];
You want the pairs of coordinates to stay together (24,2) (10,2) and (24,13). And you want to find the pairs of coordinates that has the min y-coordinate and if there are multiples, then you want to sort them by x-coordinate. And you want the row indices of what those coordinate pairs were in the original matrix x. So in other words, you want a final answer of:
v = [10,2; 24,2];
i = [2,1];
If I understood correctly, then this is how you can do it:
(Note: I changed x to have one more pair (40,13) to illustrate the difference between idx(i) and i)
>> x = [40,13; 24,2; 10,2; 24,13];
>> idx = find(x(:,2)==min(x(:,2))) %Same as what you've done before.
idx =
2
3
>> [v,i] = sortrows(x(idx,:)) %Use sortrows to sort by x-coord while preserving pairings
v =
10 2
24 2
i = % The indices in x(idx,:)
2
1
>> idx(i) %The row indices in the original matrix x
ans =
3
2
And finally, if this is not what you wanted, can you indicate what you think your answer [v,i] should be in the example you gave?