Measuring the distance between matrices with different dimensions - matlab

I have two matrices, one matrix with values ranging from 0-1, and another having only values of 0 and 1. I'm interested in calculating the distance only to those pixels equal to 1. In this case, I thought of making a new matrix that only contains the pixels with value 1 from the second object and then taking the distance to those. But, in this case, the dimensions will differ.
Note: The two matrices are originally of the same dimension. But, there are many cels not of interest (i.e; those having value 0)
How can I perform such distance calculation with objects of different dimensions?
Thanks.

If the positions of the pixels are not important to the calculation, then you can use logical indexing like so:
A = [0 1 0 0 1 1 0 0 1 1 1];
B = rand(size(A));
nonZeroInd = A == 1;
Anz = A(nonZeroInd);
Bnz = B(nonZeroInd);
dist = Bnz - Anz;
If you need the results in a matrix in the same positions as the original then you can do
C = zeros(size(A));
C(nonZeroInd) = dist;

Related

Finding the maximum of a pixel in an RGB image

I have a MxNx3 matrix that represents an RGB image. I am trying to retrieve, for each pixel, the maximum among R, G and B. This would be made easy by using a for loop, which I do not wish to do for performance reasons.
How could I go about doing that? My idea is to use find and max in the following way and get an MxN matrix:
maxRGB = find(max(rgbImage(i, j, :)));
But I am not sure how I could eliminate i and j.
The max function allows to specify along which dimension the maximum value is determined. The standard value is the first dimension. In your case, you'll want to calculate the maximum along the third dimension of the array:
maxValue = max(rgbImage,[],3);
Which returns a matrix of size MxN containing the maximum value of each pixel.
For example, lets take a random 3x3 RGB image. Applying the max function as above yields
rgbImage = rand(3,3,3);
maxValue = max(rgbImage,[],3);
maxValue =
0.6948 0.7094 0.7655
0.6555 0.7547 0.7952
0.9502 0.3816 0.8235
These are the maximal values which were present in rgbImage at each pixel location. But, you don't know if this value was in the R, G or B pixel.
To find out, which color was maximal, you can use the second (optional) argument of max, which is the index of the found maximum:
[~,maxIndex] = max(rgbImage,[],3);
which in this small example was
maxIndex =
2 3 2
1 3 2
2 2 1
where 1 corresponds to R, 2 corresponds to G and 3 corresponds to B.
To find all pixels, in which the red component was the largest, you can use the find function (probably with 2 output arguments: row and column)
[xRed,yRed] = find(maxIndex == 1)
xRed =
2
3
yRed =
1
3
So for the pixels at (2,1) and at (3,3) the red component was the largest. This is exactly what the matrix maxIndex also shows us.

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

Fastest way to find a cross in a matrix

Definition:
A(i, j) = 1 is a midpoint of a cross if the elements
A(i-1, j) = 1
A(i+1, j) = 1
A(i, j+1) = 1
A(i, j-1) = 1.
Together the elements and the midpoint form a cross in a matrix A, where A is at least a 3-by-3 matrix and i, j ∈ ℕ\{0}.
Suppose the image above is the 8-by-8 matrix A with natural numbers 1, 2, 3 ... as elements. From this definition the matrix has a total of 3 crosses. The crosses have their midpoints on A(2,2), A(5, 4) and A(5, 5).
What I want to do is write a function that finds the number of crosses in the matrix A. I have an idea but I'm not sure it's the most optimal one. Here's the pseudocode for it:
ITERATE FROM row 2 TO row 7
ITERATE FROM column 1 TO column 8
IF current element contains 1
INCREMENT xcount by 1
IF xcount >= 3
CHECK IF counted 1:s is part of a cross
ELSE IF xcount IS NOT 0
SET xcount to 0
The idea is to iterate through every column from row 2 to row 7. If I find 3 consecutive 1:s on the same row I immediately check if the 1:s belongs to a cross. This should work, but imagine having a very large matrix A - how efficient would this code be in that situation? Couldn't this problem be solved using vector notation?
Any answer is very much appreciated. Thanks in advance!
Not near matlab at the moment, but this is what I'd do. Assuming A is binary (has only 0'a and 1's):
crs=[0 1 0 ; 1 1 1 ; 0 1 0]; % a minimal "cross" filter
C=conv2(A,crs./sum(crs(:)),'same'); % convolve A with it
[x y]=find(C>0.9); % find x,y positions of the crosses by looking
% for peak values of C
so you basically convolve with a "minimal" (normalized) cross (crs) and look for peaks using max. x and y are the coordinates of your cross positions. No need to use for loops, just the built in (and pretty fast) 2d convolution, and the max function.
The threshold condition C>0.9, is just to illustrate that there's need to be a threshold that is weighted by intensity of crs. In this case I have normalized crs in the colvolution line (crs/sum(crs(:))) so if A is a binary matrix as in the example, you find that the convolution of the minimal normalized cross will leave the value of the pixel where the cross is at 1, whereas other pixels will be less than 1 (that's why I arbitrarily chose 0.9) . So you can replace the threshold to C==1, if it's always a binary.
Another way to visulize the position of the cross is just to look at C.*(C==1). This will generate a matrix the size of A with 1s only where the crosses were...
EDIT:
For maximal speed, you may consider writing it as a one liner, for example:
[x y]=find(conv2(A,[0 1 0 ; 1 1 1 ; 0 1 0]./5,'same')==1);
Using bit masks:
ux = [false(size(A,1),1) (A(:,3:end) & A(:,2:end-1) & A(:,1:end-2)) false(size(A,1),1)]
uy = [false(1,size(A,2)); (A(3:end,:) & A(2:end-1,:) & A(1:end-2,:)); false(1, size(A,2))]
u = ux & uy
[x y] = find(u)

Median filter binary image with 3x3 mask in Matlab

I am trying to use the medfilt2 function on a 2D binary image with a given 3x3 mask.
unfortunately, medfilt2 does not take a mask as a parameter.
How is it otherwise possible to median filter an image with a 3x3 mask ?
for example :
binary_image = [0 0 0 0 0 0 0 0;
0 1 0 1 0 1 1 0;
0 1 1 1 1 1 1 0;
0 1 0 1 1 1 0 0;
0 0 0 1 1 0 1 0;
0 1 1 1 0 1 1 0;
0 1 0 1 1 1 1 0;
0 0 0 0 0 0 0 0];
mask = [1 0 1;
0 1 1 ;
1 1 1];
The short answer is you can use nlfilter where you can specify what is done on pixel neighbourhoods in your image. Specifically, you would call nlfilter like so:
B = nlfilter(A, [m n], fun);
A would be an image, [m n] would specify the size of the pixel neighbourhoods you're considering (m x n), and fun is a function that is applied to each neighbourhood. The input is a m x n patch, and the output should be a single value. Assuming that mask is logical, and your image is stored in im, all you have to do is:
out = nlfilter(im, size(mask), #(x) median(x(mask)));
x(mask) accesses those locations that are valid within the neighbourhood, and you would then apply median to these values to retrieve the median value for each neighbourhood over the valid locations.
However, nlfilter is known to be slow. I would recommend you look at my post here: Matlab Median Filter Code .
This computes the median filter from first principles very fast. I will leave it to you to read through the post and understand what I did. However, what you would have to do to modify this for your purposes is to remove those rows in the output of im2col that correspond to invalid values in the mask. As such, do something like this:
N = size(mask,1); %// Size of mask - Assume that # of rows = # of columns
im_pad = padarray(im, [floor(N/2) floor(N/2)]);
im_col = im2col(im_pad, [N N], 'sliding');
%// Get locations in mask that don't count towards getting median
invalid_rows = ~(mask(:));
%// Remove from column neighbourhood matrix
im_col(invalid_rows,:) = [];
%// Determine new median index
val = floor((N*N - sum(invalid_rows))/2) + 1;
%// apply algorithm as normal
sorted_cols = sort(im_col, 1, 'ascend');
med_vector = sorted_cols(val, :);
out = col2im(med_vector, [N N], size(im_pad), 'sliding');
invalid_rows unrolls the mask into a single column, much like what im2col does for each pixel neighbourhood, and then we invert the mask to determine those locations that should not count towards the final median. Also, val determines the new index of where we need to obtain the median from once we start removing pixels in neighbourhoods that are not analyzed.
If the input is a binary image, then eigenchris's answer to you should be very fast and much faster than what I have written. However, if this is applied to grayscale images, then what I have written will work. This works on both binary and grayscale images.
If you're only dealing with binary images, you can get around this problem by performing convolution with the conv2() function.
The logic is that, since you have 7 valid pixels in your mask, if a pixel in the convolution result has a value greater than or equal to 4, we know the median is 1. Otherwise the median must be 0.
In general, if you have n valid pixels in your mask, we can get the masked median by rounding the result of the convolution matrix divided by n.
n = sum(sum(mask)); % number of valid pixels in the mask
maskFlipped = fliplr(flipud(mask)); % flip mask so it faces the right way
convResult = conv2(binary_image,maskFlipped,'same');
maskedMedianFilterResult = round(convResult/n);

MATLAB: Return the number of discrete points under a graph

I need to write a function that will take a radius r and return an integer number of discrete points strictly within a circle of radius r, centered at the origin. Any tips would be appreciated.
Even though you haven't showed us any attempts to solving your code, this is a nice exercise that I wouldn't mind tackling. What you can do first is generate a square grid of co-ordinates centered at the origin that span between -r and +r. Bear in mind that the spacing between each point in your 2D grid is 1, if I'm interpreting your question right.
Once you do this, you can find those locations where the Euclidean distance is strictly less than r, then return the number of points where this condition is satisfied. To generate the square grid of points, use meshgrid. Assuming that you've defined your radius in r, you would do the following code:
[x,y] = meshgrid(-r:r, -r:r);
x = x(:);
y = y(:);
num_points = sum(x.^2 + y.^2 < r^2);
The x = x(:); and y = y(:); are important. This turns the 2D grid for each x and y into single column vectors. Specifically, it takes each column of your matrix, and stacks all of them from top to bottom to make a single vector. It makes analysis easier. The reason why is because if we tried to use sum on a 2D matrix, it can only sum in one direction. You can either sum over all of the columns individually, or sum over all of the rows individually. Since you want to sum over the entire array, you can either call sum twice, or convert your 2D grid into a stack of 1D arrays. I opted for the second approach because I think it's cleaner, but some people don't mind chaining sum calls together too... it's just a style preference.
Once you do this, we simply check to see whether the Euclidean distance is less than the radius. Note that I'm calculating the Euclidean squared distance to avoid computing the square root. It'll save in computation time. We then sum over all instances and this will define how many points that fall within the radius.
As an example, supposing our radius was r = 2. This is what our meshgrid of points would look like:
r = 2;
[x,y] = meshgrid(-r:r, -r:r)
x =
-2 -1 0 1 2
-2 -1 0 1 2
-2 -1 0 1 2
-2 -1 0 1 2
-2 -1 0 1 2
y =
-2 -2 -2 -2 -2
-1 -1 -1 -1 -1
0 0 0 0 0
1 1 1 1 1
2 2 2 2 2
For each co-ordinate in our 2D grid, we have an (x,y) pair associated at each point. The final number of points we get is:
num_points =
9
This makes sense as the points that are strictly less than 2 should only be the 3 x 3 block that is centered at the origin. If you want to be sure, let's visualize what the grid looks like before you convert the co-ordinates into 1D vectors:
[x,y] = meshgrid(-r:r, -r:r);
disp(x.^2 + y.^2 < r^2);
0 0 0 0 0
0 1 1 1 0
0 1 1 1 0
0 1 1 1 0
0 0 0 0 0
Locations that are 1 denote true, which means that this co-ordinate satisfies it being strictly less than r. Locations that are 0 denote false, which means that they're outside. The last part of the algorithm is to sum over all of this array, which gives us 9 and that's how many points are strictly within r.
Hope this helps. Good luck!
Just as an approach:
define a square matrix that would fit the discrete points
define a function/condition if the matrix point would be (strictly) within the circle
count the number of true values
Good luck, and if you have problems somewhere, feel free to modify the post.