sorting 2d points into a matrix - algorithm

I have the following problem:
An image is given and I am doing some blob detection. As a limit, lets say I have a max of 16 blobs and from each blob I calculate the centroid (x,y position).
If no distorion happends, these centroids are arranged in an equidistant 4x4 grid but they could be really much distorted.
The assumption is that they keep more or less the grid form but they could be really much warped.
I need to sort the blobs such that I know which one is the nearest left, right, up and down. So the best would be to write these blobs into a matrix.
If this is not enough, it could happen that I detect less then 16 and then I also need to sort them into a matrix.
Does anyone know how this could be efficiently solved in Matlab?
Thanks.
[update 1:]
I uploaded an image and the red numbers are the numbers which my blob detection algorithm assign each blob.
The resulting matrix should look like this with these numbers:
1 2 4 3
6 5 7 8
9 10 11 12
13 16 14 15
e.g. I start with blob 11 and the nearest right number is 12 and so on
[update 2:]
The posted solution looks quite nice. In reality it could happen, that one of the outer spots is missing or maybe two ... I know that this makes everything much more complicated and I just want to get a feeling if this is worth spending time.
These problems arise if you analyze a wavefront with a shack-hartmann wavefront sensor and you want to increase the dynamic range :-)
The spots could be really warped such that the dividing lines are not orthogonal any more.
Maybe someone knows a good literature for classification algorithms.
Best solution would be one, which could be implemented on a FPGA without to much effort but this is at this stage not so much important.

This will work as long as the blobs form a square and are relatively ordered:
Image:
Code:
bw = imread('blob.jpg');
bw = im2bw(bw);
rp = regionprops(bw,'Centroid');
% Must be a square
side = sqrt(length(rp));
centroids = vertcat(rp.Centroid);
centroid_labels = cellstr(num2str([1:length(rp)]'));
figure(1);
imshow(bw);
hold on;
text(centroids(:,1),centroids(:,2),centroid_labels,'Color','r','FontSize',60);
hold off;
% Find topleft element - minimum distance from origin
[~,topleft_idx] = min(sqrt(centroids(:,1).^2+centroids(:,2).^2));
% Find bottomright element - maximum distance from origin
[~,bottomright_idx] = max(sqrt(centroids(:,1).^2+centroids(:,2).^2));
% Find bottom left element - maximum normal distance from line formed by
% topleft and bottom right blob
A = centroids(bottomright_idx,2)-centroids(topleft_idx,2);
B = centroids(topleft_idx,1)-centroids(bottomright_idx,1);
C = -B*centroids(topleft_idx,2)-A*centroids(topleft_idx,1);
[~,bottomleft_idx] = max(abs(A*centroids(:,1)+B*centroids(:,2)+C)/sqrt(A^2+B^2));
% Sort blobs based on distance from line formed by topleft and bottomleft
% blob
A = centroids(bottomleft_idx,2)-centroids(topleft_idx,2);
B = centroids(topleft_idx,1)-centroids(bottomleft_idx,1);
C = -B*centroids(topleft_idx,2)-A*centroids(topleft_idx,1);
[~,leftsort_idx] = sort(abs(A*centroids(:,1)+B*centroids(:,2)+C)/sqrt(A^2+B^2));
% Reorder centroids and redetermine bottomright_idx and bottomleft_idx
centroids = centroids(leftsort_idx,:);
bottomright_idx = find(leftsort_idx == bottomright_idx);
bottomleft_idx = find(leftsort_idx == bottomleft_idx);
% Sort blobs based on distance from line formed by bottomleft and
% bottomright blob
A = centroids(bottomright_idx,2)-centroids(bottomleft_idx,2);
B = centroids(bottomleft_idx,1)-centroids(bottomright_idx,1);
C = -B*centroids(bottomleft_idx,2)-A*centroids(bottomleft_idx,1);
[~,bottomsort_idx] = sort(abs(A*reshape(centroids(:,1),side,side)+B*reshape(centroids(:,2),side,side)+C)/sqrt(A^2+B^2),'descend');
disp(leftsort_idx(bsxfun(#plus,bottomsort_idx,0:side:side^2-1)));
Output:
2 12 13 20 25 31
4 11 15 19 26 32
1 7 14 21 27 33
3 8 16 22 28 34
6 9 17 24 29 35
5 10 18 23 30 36
Just curious, are you using this to automate camera calibration through a checkerboard or something?
UPDATE:
For skewed image
tform = maketform('affine',[1 0 0; .5 1 0; 0 0 1]);
bw = imtransform(bw,tform);
Output:
1 4 8 16 21 25
2 5 10 18 23 26
3 6 13 19 27 29
7 9 17 24 30 32
11 14 20 28 33 35
12 15 22 31 34 36
For rotated image:
bw = imrotate(bw,20);
Output:
1 4 10 17 22 25
2 5 12 18 24 28
3 6 14 21 26 31
7 9 16 23 30 32
8 13 19 27 33 35
11 15 20 29 34 36

Related

Find the coordinates (indexes) of an element matrix knowing its linear value

The title isn't very accurate because I didn't know the specific terms to describe it.
I have a matrix of known dimensions width and height. This matrix contains every
integer from 0 to width*height-1 so for example:
width = 7
height = 4
0 1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
n = 18
x = ?
y = ?
Now if I'm given the value n = 18 I want the program to set x = 4 and y = 2 .
What is the mathematical operation behind it?
It feels like it should be pretty easy but I can wrap my head around it.
I thought the answer was x = n % width and y = n / height but it's not working, unless I'm just implementing it the wrong way
SOLVED
I'll leave the post here in case someone needs it.
The answer was actually x = n % width and y = n / width and I feel kinda dumb. You can also use height instead of width but keep in mind that you'll need to swap the operations.

tile image with MATLAB imrotate

When I use the imrotate function of MATLAB, output image has missing parts which are filled with zeros.
Can I somehow make it so the triangles in the corners (i.e. the missing parts) are filled with the opposite edge of the image? So it will be like I have tiled the image with 8 neighbors around itself, and then rotated and cropped the bigger image?
Thanks for any help,
I think that the best (and most memory efficient) way of doing this, would be to use interp2 to sample the original image at the new pixel centers (the original pixel centers "rotated" by the opposite of the desired angle). And then to use mod on these sample points to ensure that they fall within the dimension of the original image. And the added benefit of mod is that the new x,y coordinates that are out of range, simply "wrap around" to the other side of the image.
% Load some sample image
load mri
im = double(D(:,:,12));
[rows, cols] = size(im);
% The original pixel centers
[xx,yy] = meshgrid(1:cols, 1:rows);
% Setup the rotation matrix
theta = pi/4;
R = [cos(-theta), -sin(-theta);
sin(-theta), cos(-theta)];
% Center of Rotation (center of the image)
center = [cols/2, rows/2];
% Determine the new pixel centers (rotated)
xy = bsxfun(#minus, [xx(:), yy(:)], center) * R;
xy = bsxfun(#plus, xy, center);
% Mod these using the dimensions of the image to "wrap" around
x = mod(xy(:,1) - 1, cols - 1) + 1;
y = mod(xy(:,2) - 1, rows - 1) + 1;
% Sample the original image at the new pixel centers
im2 = interp2(xx, yy, im, x, y);
im2 = reshape(im2, [rows, cols])
Rotation = 45 degrees
This will work with any arbitrary aspect ratio (below is an image demonstrating the issue brought up by #BlackAdder where a repmat [3,3] wouldn't work due to the image being tall and narrow).
Rotation = 90 degrees
This also has the added benefit that it doesn't rely on the Image Processing Toolbox.
The most direct way, using the Image Processing Toolbox, is to use padarray. Given an image:
>> img = reshape(1:25, 5, 5)
img =
1 6 11 16 21
2 7 12 17 22
3 8 13 18 23
4 9 14 19 24
5 10 15 20 25
Then you can replicate the image on all sides:
>> padarray(img, size(img), 'circular')
ans =
1 6 11 16 21 | 1 6 11 16 21 | 1 6 11 16 21
2 7 12 17 22 | 2 7 12 17 22 | 2 7 12 17 22
3 8 13 18 23 | 3 8 13 18 23 | 3 8 13 18 23
4 9 14 19 24 | 4 9 14 19 24 | 4 9 14 19 24
5 10 15 20 25 | 5 10 15 20 25 | 5 10 15 20 25
--------------------------------------------------------------------------
1 6 11 16 21 | 1 6 11 16 21 | 1 6 11 16 21
2 7 12 17 22 | 2 7 12 17 22 | 2 7 12 17 22
3 8 13 18 23 | 3 8 13 18 23 | 3 8 13 18 23
4 9 14 19 24 | 4 9 14 19 24 | 4 9 14 19 24
5 10 15 20 25 | 5 10 15 20 25 | 5 10 15 20 25
--------------------------------------------------------------------------
1 6 11 16 21 | 1 6 11 16 21 | 1 6 11 16 21
2 7 12 17 22 | 2 7 12 17 22 | 2 7 12 17 22
3 8 13 18 23 | 3 8 13 18 23 | 3 8 13 18 23
4 9 14 19 24 | 4 9 14 19 24 | 4 9 14 19 24
5 10 15 20 25 | 5 10 15 20 25 | 5 10 15 20 25
(Lines added to show the original matrix in the center and the padded copies.) Once you're done rotating, you can crop the middle of the matrix for your final image.
Note that this method also works on 3-channel images.
As #Suever and #BlackAdder note in the comments, this padding can be insufficient for images with large aspect ratio (greater than 25.456:9), particularly for rotations near odd multiples of 45°. You can make the padding more accurate by calculating the maximum you might need.
s = size(img);
s = s(1:2); % account for multi-channel images
maxext = sqrt(s * s.'); % calculate length of image diagonal
padsize = ceil((maxext - s)/2); % find amount of padding needed for each side
padarray(img, padsize, 'circular');

Where is my mistake in this answer to Project Euler #58?

I am solving project Euler question 58. Here a square is created by starting with 1 and spiralling anticlockwise in the following way (here is side length equal to 7:
37 36 35 34 33 32 31
38 17 16 15 14 13 30
39 18 5 4 3 12 29
40 19 6 1 2 11 28
41 20 7 8 9 10 27
42 21 22 23 24 25 26
43 44 45 46 47 48 49
The question is to find out when we keep spiralling around the square, when the ratio of primes in the diagonals and the amount of numbers in the diagonal is smaller than 0.10.
I am convinced I have the solution with the code below (see code comments for clarification), but the site states that the answer is wrong when I am entering it.
require 'prime'
# We use a mathematical derivation of the corner values, keep increasing the value till we find a ratio smaller
# than 0.10 and increase the grid_size and amount of numbers on diagonals each iteration
side_length = 3 # start with grid size of 3x3 so that we do not get into trouble with 1x1 grid
prime_count = 3 # 3, 5, 7 are prime and on a diagonal in a 3x3 grid
diagonal_size = 5
prime_ratio = 1 # dummy value bigger than 0.10 so we can start the loop
while prime_ratio >= 0.10
# Add one to prime count for each corner if it is prime
# Corners are given by n2 (top left), n2-n+1, n2-2n+2, and n2-3n+3
prime_count += 1 if (side_length**2).prime?
prime_count += 1 if (side_length**2-side_length+1).prime?
prime_count += 1 if (side_length**2-2*side_length+2).prime?
prime_count += 1 if (side_length**2-3*side_length+3).prime?
# Divide amount of primes counted by the diagonal length to get prime ratio
prime_ratio = prime_count/diagonal_size.to_f
# Increase the side length by two (full spiral) and diagonal size by four
side_length += 2 and diagonal_size += 4
end
puts side_length-2 #-2 to account for last addition in while-loop
# => 26612
It probably is wrong and site is right. I am stuck on this problem for quite some time now. Can anyone point me the mistake?
side_length += 2 and diagonal_size += 4 should be at the beginning of the loop.
Couldn't check, I do not have ruby installed, but I can reproduce the same problem on my python solution.

Sum Pyramid with backtracking

I'm trying to solve this problem and I'm new to backtracking algorithms,
The problem is about making a pyramid like this so that a number sitting on two numbers is the sum of them. Every number in the pyramid has to be different and less than 100. Like this:
88
39 49
15 24 25
4 11 13 12
1 3 8 5 7
Any pointers on how to do this using backtracking?
Not necessarily backtracking but the property you are asking for is interestingly very similar to the Pascal Triangle property.
The Pascal Triangle (http://en.wikipedia.org/wiki/Pascal's_triangle), which is used for efficient computation of binomial coefficient among other things, is a pyramid where a number is equal to the sum of the two numbers above it with the top being 1.
As you can see you are asking the opposite property where a number is the sum of the numbers below it.
1
1 1
1 2 1
1 3 3 1
1 4 6 4 1
1 5 10 10 5 1
1 6 15 20 15 6 1
1 7 21 35 35 21 7 1
1 8 28 56 70 56 28 8 1
For instance in the Pascal Triangle above, if you wanted the top of your pyramid to be 56, your pyramid will be a reconstruction bottom up of the Pascal Triangle starting from 56 and that will give something like:
56
21 35
6 15 20
1 5 10 10
Again that's not a backtracking solution and this might not give you a good enough solution for every single N though I thought this was an interesting approximation that was worth noting.

Summation of difference between matrix elements

I am in the process of building a function in MATLAB. As a part of it I have to calculate differences between elements in two matrices and sum them up.
Let me explain considering two matrices,
1 2 3 4 5 6
13 14 15 16 17 18
and
7 8 9 10 11 12
19 20 21 22 23 24
The calculations in the first row - only four elements in both matrices are considered at once (zero indicates padding):
(1-8)+(2-9)+(3-10)+(4-11): This replaces 1 in initial matrix.
(2-9)+(3-10)+(4-11)+(5-12): This replaces 2 in initial matrix.
(3-10)+(4-11)+(5-12)+(6-0): This replaces 3 in initial matrix.
(4-11)+(5-12)+(6-0)+(0-0): This replaces 4 in initial matrix. And so on
I am unable to decide how to code this in MATLAB. How do I do it?
I use the following equation.
Here i ranges from 1 to n(h), n(h), the number of distant pairs. It depends on the lag distance chosen. So if I choose a lag distance of 1, n(h) will be the number of elements - 1.
When I use a 7 X 7 window, considering the central value, n(h) = 4 - 1 = 3 which is the case here.
You may want to look at the circshfit() function:
a = [1 2 3 4; 9 10 11 12];
b = [5 6 7 8; 12 14 15 16];
for k = 1:3
b = circshift(b, [0 -1]);
b(:, end) = 0;
diff = sum(a - b, 2)
end

Resources