summation of level values from contour plot - matlab

I'm trying to compute the sum of the level (z axis) values inside a contour:
I've managed to obtain the lines (or edges) of the contour, so I have the limits of each line:
What I want is to sum all the levels in the z axis of the contour that are inside the outer blue line in the second plot, to compare it with the sum of the values of outside the blue line. Is there any way to do this? The code I have so far is:
C = contourc(f, t, abs(tfr));
%extracts info from contour
sz = size(C,2); % Size of the contour matrix c
ii = 1; % Index to keep track of current location
jj = 1; % Counter to keep track of # of contour lines
while ii < sz % While we haven't exhausted the array
n = C(2,ii); % How many points in this contour?
s(jj).v = C(1,ii); % Value of the contour
s(jj).x = C(1,ii+1:ii+n); % X coordinates
s(jj).y = C(2,ii+1:ii+n); % Y coordinates
ii = ii + n + 1; % Skip ahead to next contour line
jj = jj + 1; % Increment number of contours
end

So after you run the code in the question you have the coordinates of each contour in the array S. Assuming you have variables f and t of the form f = 94:0.1:101 and t = 0:1000 or similar, and the that value you want to sum are abs(tfr) you should be able to use
[fg, tg] = meshgrid(f,t)
is_inside = inpolygon(fg,tg, S(1).x, S(1).y)
integral = sum(abs(tfr(is_inside))
And similarly for other entries in S. See help for inpolygon for more examples. You can use ~is_inside for the points outside the curve

Related

how to do 3D number-density scatter plot using given data?

I have a cubic box with the size of, lets say 300 in each direction. I have some data (contains X,Y,Z coordinates) which represent the distribution of nearly 1 million data-points within this box. I want to specify a color to their Number density (its an intensive quantity used to describe the degree of concentration of countable objects in this case data-points). In another word, Using color to illustrate which part is more condensed in terms of data-points rather than the other parts. The index for the color-bar in the final image should represent the percentage of data-points specified with that color.
I have tried to do it by dividing the whole space in cubic box to 1 million smaller cube (each cube has a length of 3 in all direction). By counting the number of particles within those cube, I will know how they distributed in the box and the number of existed data-points within. Then I can specify a color to them which I wasn’t successful in counting and specifying. Any suggestion is appreciated.
%reading the files
[FileName,PathName,FilterIndex] = uigetfile('H:\*.txt','MultiSelect','on');
numfiles = size(FileName,2);%('C:\final1.txt');
j=1;
X=linspace(0,300,100);
for ii = 1:numfiles
FileName{ii}
entirefile = fullfile(PathName,FileName{ii});
a = importdata(entirefile);
x = a(:,2);
y = a(:,3);
z = a(:,4);
%% I DON'T KNOW HOW TO CREAT THIS LOOP TO COUNT FOR THE NUMBER OF PARTICLES WITHIN EACH DEFINED CUBE %%
for jj = 2:size(X,2)
%for kk=1:m
if x(:)<X(jj) & y(:)<X(jj) & z(:)<X(jj)
x;
end
%end
end
h=figure(j);
scatter3(x, y, z, 'filled', 'MarkerSize', 20);
cb = colorbar();
cb.Label.String = 'Probability density estimate';
end
I need to get a similar result like the following image. but I need the percentage of data-point specified by each color. Thanks in advance.
Here is a link to a sampled data.
Here is a way to count the 3D density of a point cloud. Although I am affraid the sample data you provided do not yield the same 3D distribution than on your example image.
To count the density, the approach is broken down in several steps:
Calculate a 2D density in the [X,Y] plane: This counts the number of points laying in each (x,y) bin. However, at this stage this number of point incorporates all the Z column for a given bin.
For each non-empty (x,y) bin, calculate the distribution along the Z column. We now have the number of point falling in each (x,y,z) bin. Counting the density/percentage is simply done by dividing each count by the total number of points.
Now for each non-empty (x,y,z) bin, we identify the linear indices of the points belonging to this bin. We then assign the bin value (color, percentage, or any value associated to this bin) to all the identified points.
display the results.
In code, it goes like this:
%% Import sample data
entirefile = '1565015520323.txt' ;
a = importdata(entirefile);
x = a(:,1);
y = a(:,2);
z = a(:,3);
npt = numel(x) ; % Total Number of Points
%% Define domain and grid parameters
nbins = 100 ;
maxDim = 300 ;
binEdges = linspace(0,maxDim,nbins+1) ;
%% Count density
% we start counting density along in the [X,Y] plane (Z axis aglomerated)
[Nz,binEdges,~,binX,binY] = histcounts2(y,x,binEdges,binEdges) ;
% preallocate 3D containers
N3d = zeros(nbins,nbins,nbins) ; % 3D matrix containing the counts
Npc = zeros(nbins,nbins,nbins) ; % 3D matrix containing the percentages
colorpc = zeros(npt,1) ; % 1D vector containing the percentages
% we do not want to loop on every block of the domain because:
% - depending on the grid size there can be many
% - a large number of them can be empty
% So we first find the [X,Y] blocks which are not empty, we'll only loop on
% these blocks.
validbins = find(Nz) ; % find the indices of non-empty blocks
[xbins,ybins] = ind2sub([nbins,nbins],validbins) ; % convert linear indices to 2d indices
nv = numel(xbins) ; % number of block to process
% Now for each [X,Y] block, we get the distribution over a [Z] column and
% assign the results to the full 3D matrices
for k=1:nv
% this block coordinates
xbin = xbins(k) ;
ybin = ybins(k) ;
% find linear indices of the `x` and `y` values which are located into this block
idx = find( binX==xbin & binY==ybin ) ;
% make a subset with the corresponding 'z' value
subZ = z(idx) ;
% find the distribution and assign to 3D matrices
[Nz,~,zbins] = histcounts( subZ , binEdges ) ;
N3d(xbin,ybin,:) = Nz ; % total counts for this block
Npc(xbin,ybin,:) = Nz ./ npt ; % density % for this block
% Now we have to assign this value (color or percentage) to all the points
% which were found in the blocks
vzbins = find(Nz) ;
for kz=1:numel(vzbins)
thisColorpc = Nz(vzbins(kz)) ./ npt * 100 ;
idz = find( zbins==vzbins(kz) ) ;
idx3d = idx(idz) ;
colorpc(idx3d) = thisColorpc ;
end
end
assert( sum(sum(sum(N3d))) == npt ) % double check we counted everything
%% Display final result
h=figure;
hs=scatter3(x, y, z, 3 , colorpc ,'filled' );
xlabel('X'),ylabel('Y'),zlabel('Z')
cb = colorbar ;
cb.Label.String = 'Probability density estimate';
As I said at the beginning, the result is slightly different than your example image. This sample set yields the following distribution:
If you want a way to "double check" that the results are not garbage, you can look at the 2D density results on each axis, and check that it matches the apparent distribution of your points:
%% Verify on 3 axis:
Nz = histcounts2(y,x,binEdges,binEdges) ./ npt *100 ;
Nx = histcounts2(z,y,binEdges,binEdges) ./ npt *100 ;
Ny = histcounts2(x,z,binEdges,binEdges) ./ npt *100 ;
figure
ax1=subplot(1,3,1) ; bz = plotDensity(Nz,ax1) ; xlabel('X'),ylabel('Y') ;
ax2=subplot(1,3,2) ; bx = plotDensity(Nx,ax2) ; xlabel('Y'),ylabel('Z') ;
ax3=subplot(1,3,3) ; by = plotDensity(Ny,ax3) ; xlabel('Z'),ylabel('X') ;
Click on the image to see it larger:
The code for plotDensity.m:
function hp = plotDensity(Ndist,hax)
if nargin<2 ; hax = axes ; end
hp = bar3(Ndist,'Parent',hax) ;
for k = 1:length(hp)
zdata = hp(k).ZData;
hp(k).CData = zdata;
hp(k).FaceColor = 'interp';
end
shading interp

Plotting cantilever and beam plots using Matlab

Problem
I have to plot a beam/cantilever using Matlab. Where my inputs are:
Length of the beam
Position of the loads (input is a vector)
Forces of the load (input is a vector)
Whether is it a cantilever or not. Because I have different equations for calculating the displacement.
My Solution
I have come to an idea on how I can actually plot the cantilever, but I can not formulate it into a code in MATLAB. I have spent hours trying to write something on Matlab but I have gotten nowhere. (I am a novice to Matlab)
My solution is as follow: I have the formula for the displacement from starting position.
I can define a vector using loop for x coordinates until the given beam length. Hence,
x=[0 ... L]
Then I want to define another vector where the difference is calculated (this is where I can't figure out Matlab)
y = [h, h - y(x1), h - y(x2), .... h - y(L)]
where h is the starting height, which I have thought to be defined as (y(x1) - y(L)) + 1, so that the graph then doesn't go into the negative axes. y(x) is the function which will calculate the displacement or fall of the beam.
Once that is done, then I can simply plot(x,y) and that would give me a graph of a shape of deflected beam for the given range from 0 to beam length. I have tested my theory on excel and it works as per the graph is concerned but I can not figure out implementation on Matlab.
My incomplete code
%Firstly we need the inputs
%Length of the beam
l = str2double(input('Insert the length of your beam: ', 's'));
%Now we need a vector for the positions of the load
a = [];
while 1
a(end+1) = input('Input the coordinate for the position of your load: ');
if length(a)>1; break; end
end
%Now we need a vector for the forces of the load
W = [];
while 1
W(end+1) = input('Input the forces of your load: ');
if length(W)>1; break; end
end
%
%
%
%Define the formula
y = ((W * (l - a) * x)/(6*E*I*l)) * (l^2 - x^2 - (l - a)^2);
%Where
E = 200*10^9;
I = 0.001;
%
%
%
%Now we try to plot
%Define a vector with the x values
vectx = [];
for i = 1:l
vectx = [vectx i];
end
%Now I want to calculate displacement for each x value from vectx
vecty = [];
for i=1:l
vecty=[10 - y(x(i)) i];
end
%Now I can plot all the information
plot(vectx, vecty)
hold on
%Now I plot the coordinate of the positions of the load
plot(load)
end
Really need some help/guidance. Would be truly grateful if someone can help me out or give me a hint :)
I have edited the question with further details
There are several things that do not work in your example.
For instance, parameters should be defined BEFORE you use them, so E and I should be defined before the deflection equation. And you should define x.
I do not understand why you put your inputs whithin a while loop, if you stop it at length(a)>1;. You can remove the loop.
You do not need a loop to calculate displacements, you can just use a substraction between vectors, like displacement = 10 - y. However, I do not understand what is H in your example; since your beam is initially at position 0, your displacement is just -y.
Finally, your equation to calculate the deformed shape is wrong; it only accounts for the first part of the beam.
Here, try if this code works:
%Length of the beam
l = input('Insert the length of your beam: ');
%Now we need a vector for the positions of the load
a = input('Input the coordinate for the position of your load: ');
%Now we need a vector for the forces of the load
W = input('Input the forces of your load: ');
%Define the formula
E = 200*10^9;
I = 0.001;
% x Position along the beam
x = linspace(0,l,100);
b = l - a;
% Deflection before the load position
pos = x <= a;
y(pos) = ((W * b .* x(pos))/(6*E*I*l)) .* (l^2 - x(pos).^2 - b^2);
% Cantilever option
% y(pos) = W*x(pos).^2/(6*E*I).*(3*a-x(pos));
% Deflection after the load position
pos = x > a;
y(pos) = ((W * b )/(6*E*I*l)) .* (l/b*(x(pos)-a).^3 + (l^2 - b^2)*x(pos) - x(pos).^3);
% Cantilever option
% y(pos) = W*a^2/(6*E*I).*(3*x(pos)-a);
displacement = 10 - y; % ???
% Plot beam
figure
plot(x , x .* 0 , 'k-')
hold on;
% Plot deflection
plot(x , y , '--')
% Plot load position
% Normalize arrow size as 1/10 of the beam length
quiver(a , 0 , 0 , sign(W) .* max(abs(y))/2)

Matlab: repmat code explanation

Can anyone explain the two lines of code highlighted below which use repmat? This is taken directly from the MathWorks documentation for learning data analysis:
bin_counts = hist(c3); % Histogram bin counts
N = max(bin_counts); % Maximum bin count
mu3 = mean(c3); % Data mean
sigma3 = std(c3); % Data standard deviation
hist(c3) % Plot histogram
hold on
plot([mu3 mu3],[0 N],'r','LineWidth',2) % Mean
% --------------------------------------------------------------
X = repmat(mu3+(1:2)*sigma3,2,1); % WHAT IS THIS?
Y = repmat([0;N],1,2); % WHY IS THIS NECESSARY?
% --------------------------------------------------------------
plot(X,Y,'g','LineWidth',2) % Standard deviations
legend('Data','Mean','Stds')
hold off
Could anyone explain the X = repmat(...) line to me? I know it will be plotted for the 1 and 2 standard deviation lines.
Also, I tried commenting out the Y = ... line, and the plot looks the exact same, so what is the purpose of this line?
Thanks
Lets break the expression into multiple statements
X = repmat(mu3+(1:2)*sigma3,2,1);
is equivalent to
% First create a row vector containing one and two standard deviations from the mean.
% This is equivalent to xvals = [mu3+1*sigma3, mu3+2*sigma3];
xval = mu3 + (1:2)*sigma3;
% Repeat the matrix twice in the vertical dimension. We want to plot two vertical
% lines so the first and second point should be equal so we just use repmat to repeat them.
% This is equivalent to
% X = [xvals;
% xvals];
X = repmat(xval,2,1);
% To help understand how repmat works, if we had X = repmat(xval,3,2) we would get
% X = [xval, xval;
% xval, xval;
% xval, xval];
The logic is similar for the Y matrix except it repeats in the column direction. Together you end up with
X = [mu3+1*sigma3, mu3+2*sigma3;
mu3+1*sigma3, mu3+2*sigma3];
Y = [0, 0;
N, N];
When plot is called it plots one line per column of the X and Y matrices.
plot(X,Y,'g','LineWidth',2);
is equivalent to
plot([mu3+1*sigma3; mu3+1*sigma3], [0, N], 'g','LineWidth',2);
hold on;
plot([mu3+2*sigma3; mu3+2*sigma3], [0, N], 'g','LineWidth',2);
which plots two vertical lines, one and two standard deviations from the mean.
If you comment out Y then Y isn't defined. The reason the code still worked is probably that the previous value of Y was still stored in the workspace. If you run the command clear before running the script again you will find that the plot command will fail.

Hough transform to detect and delete lines

I want to use the Hough transform to detect lines in my image.But instead of plotting the lines I want to delete each line detected in my original image.
image=imread('image.jpg');
image = im2bw(image);
BW=edge(image,'canny');
imshow(BW);
figure,imshow(BW);
[H,T,R] = hough(BW);
P = houghpeaks(H,100,'threshold',ceil(0.3*max(H(:))));
lines = houghlines(BW,T,R,P,'FillGap',5,'MinLength',7);
Now after this I have got all the lines. But I want to delete all these lines from my original image, keeping rest of the image as before. Is there some way I can do this?
Edit I am uploading an image.I want to delete all the lines and keep the circular part.This is just an example image.Basically my objective is to delete the line segments and keep rest of the image
The issue you have is that your lines are thicker than one pixel.
The lines from the hough transform seem to be one pixel thick and
that doesn't help.
I propose that you delete the lines that you get from the Hough transform first.
This will sort of divide the hockey rink of whatever it is into segments
that will be easier to process.
Then you label each segment with bwlabel. For each object, find the
endpoints and fit a line between the endpoints. If the line and the object
have more pixels in common than a certain threshold, then we say that the object
is a line and we delete it from the image.
You may have to play around with the Hough transform's threshold value.
This technique has some flaws though. It will delete a filled square,
rectangle or circle but you haven't got any of those so you should be ok.
Results
Explanation
This is your code that I modified a bit. I removed the gradient because it
it easier to work with solid objects. The gradient gave very thin lines.
I also work on the complement image because the bw functions work with 1
as forgound rather than 0 as in your original image.
org_image_bw=im2bw(double(imread('http://i.stack.imgur.com/hcphc.png')));
image = imcomplement(org_image_bw);
[H,T,R] = hough(image);
P = houghpeaks(H,100,'threshold',ceil(0.27*max(H(:))));
lines = houghlines(image,T,R,P,'FillGap',5,'MinLength',7);
Loop through the lines you have got and delete them
processed_image = image;
for k = 1:length(lines)
xy = [lines(k).point1; lines(k).point2];
% // Use the question of a line y = kx + m to calulate x,y
% // Calculate the maximum number of elements in a line
numOfElems = max(max(xy(:,1))-min(xy(:,1)),max(xy(:,2))-min(xy(:,2)) ) ;
% // Cater for the special case where the equation of a line is
% // undefined, i.e. there is only one x value.
% // We use linspace rather than the colon operator because we want
% // x and y to have the same length and be evenly spaced.
if (diff(xy(:,1)) == 0)
y = round(linspace(min(xy(:,2)),max(xy(:,2)),numOfElems));
x = round(linspace(min(xy(:,1)),max(xy(:,1)),numOfElems));
else
k = diff(xy(:,2)) ./ diff(xy(:,1)); % // the slope
m = xy(1,2) - k.*xy(1,1); % // The crossing of the y-axis
x = round(linspace(min(xy(:,1)), max(xy(:,1)), numOfElems));
y = round(k.*x + m); % // the equation of a line
end
processed_image(y,x) = 0; % // delete the line
end
This is what the image looks after we have deleted the detected lines. Please note that the original hockey rink and been divided into multiple objects.
Label the remaining objects
L = bwlabel(processed_image);
Run through each object and find the end points.
Then fit a line to it. If, let's say 80% the fitted line covers
the object, then it is a line.
A fitted line could look like this. The diagonal blue line represents the fitted line and covers most of
the object (the white area). We therefore say that the object is a line.
% // Set the threshold
th = 0.8;
% // Loop through the objects
for objNr=1:max(L(:))
[objy, objx] = find(L==objNr);
% Find the end points
endpoints = [min(objx) min(objy) ...
;max(objx) max(objy)];
% Fit a line to it. y = kx + m
numOfElems = max(max(endpoints(:,1))-min(endpoints(:,1)),max(endpoints(:,2))-min(endpoints(:,2)) ) ;
% // Cater for the special case where the equation of a line is
% // undefined, i.e. there is only one x value
if (diff(endpoints(:,1)) == 0)
y = round(linspace(min(endpoints(:,2)),max(endpoints(:,2)),numOfElems));
x = round(linspace(min(endpoints(:,1)),max(endpoints(:,1)),numOfElems));
else
k = diff(endpoints(:,2)) ./ diff(endpoints(:,1)); % the slope
m = endpoints(1,2) - k.*endpoints(1,1); % The crossing of the y-axis
x = round(linspace(min(endpoints(:,1)), max(endpoints(:,1)), numOfElems));
y = round(k.*x + m);
% // Set any out of boundary items to the boundary
y(y>size(L,1)) = size(L,1);
end
% // Convert x and y to an index for easy comparison with the image
% // We sort them so that we are comparing the same pixels
fittedInd = sort(sub2ind(size(L),y,x)).';
objInd = sort(sub2ind(size(L),objy,objx));
% // Calculate the similarity. Intersect returns unique entities so we
% // use unique on fittedInd
fitrate = numel(intersect(fittedInd,objInd)) ./ numel(unique(fittedInd));
if (fitrate >= th)
L(objInd) = 0;
processed_image(objInd) = 0;
% // figure(1),imshow(processed_image)
end
end
Display the result
figure,imshow(image);title('Original');
figure,imshow(processed_image);title('Processed image');
Complete example
org_image_bw=im2bw(double(imread('http://i.stack.imgur.com/hcphc.png')));
image = imcomplement(org_image_bw);
[H,T,R] = hough(image);
P = houghpeaks(H,100,'threshold',ceil(0.27*max(H(:))));
lines = houghlines(image,T,R,P,'FillGap',5,'MinLength',7);
processed_image = image;
for k = 1:length(lines)
xy = [lines(k).point1; lines(k).point2];
% // Use the question of a line y = kx + m to calulate x,y
%Calculate the maximum number of elements in a line
numOfElems = max(max(xy(:,1))-min(xy(:,1)),max(xy(:,2))-min(xy(:,2)) ) ;
% // Cater for the special case where the equation of a line is
% // undefined, i.e. there is only one x value.
% // We use linspace rather than the colon operator because we want
% // x and y to have the same length and be evenly spaced.
if (diff(xy(:,1)) == 0)
y = round(linspace(min(xy(:,2)),max(xy(:,2)),numOfElems));
x = round(linspace(min(xy(:,1)),max(xy(:,1)),numOfElems));
else
k = diff(xy(:,2)) ./ diff(xy(:,1)); % the slope
m = xy(1,2) - k.*xy(1,1); % The crossing of the y-axis
x = round(linspace(min(xy(:,1)), max(xy(:,1)), numOfElems));
y = round(k.*x + m); % // the equation of a line
end
processed_image(y,x) = 0; % // delete the line
end
% // Label the remaining objects
L = bwlabel(processed_image);
% // Run through each object and find the end points.
% // Then fit a line to it. If, let's say 80% the fitted line covers
% // the object, then it is a line.
% // Set the threshold
th = 0.8;
% // Loop through the objects
for objNr=1:max(L(:))
[objy, objx] = find(L==objNr);
% Find the end points
endpoints = [min(objx) min(objy) ...
;max(objx) max(objy)];
% Fit a line to it. y = kx + m
numOfElems = max(max(endpoints(:,1))-min(endpoints(:,1)),max(endpoints(:,2))-min(endpoints(:,2)) ) ;
% Cater for the special case where the equation of a line is
% undefined, i.e. there is only one x value
if (diff(endpoints(:,1)) == 0)
y = round(linspace(min(endpoints(:,2)),max(endpoints(:,2)),numOfElems));
x = round(linspace(min(endpoints(:,1)),max(endpoints(:,1)),numOfElems));
else
k = diff(endpoints(:,2)) ./ diff(endpoints(:,1)); % the slope
m = endpoints(1,2) - k.*endpoints(1,1); % The crossing of the y-axis
x = round(linspace(min(endpoints(:,1)), max(endpoints(:,1)), numOfElems));
y = round(k.*x + m);
% // Set any out of boundary items to the boundary
y(y>size(L,1)) = size(L,1);
end
% // Convert x and y to an index for easy comparison with the image
% // We sort them so that we are comparing the same pixels
fittedInd = sort(sub2ind(size(L),y,x)).';
objInd = sort(sub2ind(size(L),objy,objx));
% Calculate the similarity. Intersect returns unique entities so we
% use unique on fittedInd
fitrate = numel(intersect(fittedInd,objInd)) ./ numel(unique(fittedInd));
if (fitrate >= th)
L(objInd) = 0;
processed_image(objInd) = 0;
% // figure(1),imshow(processed_image)
end
end
% // Display the result
figure,imshow(image);title('Original');
figure,imshow(processed_image);title('Processed image');
You could use J. E. Bresenham's algorightm. It is implemented by A. Wetzler in the following matlab function, which I tested myself.
The algorithm will give you the pixel coordinates of where the line would be, given that you will provide the start and end point of the line, which is already given in lines in your code above.
Here is the code I used, which uses the matlab function referenced above:
%This is your code above ========
image=imread('hcphc.png');
image = im2bw(image);
BW=edge(image,'canny');
imshow(BW);
figure,imshow(BW);
[H,T,R] = hough(BW);
P = houghpeaks(H,100,'threshold',ceil(0.3*max(H(:))));
lines = houghlines(BW,T,R,P,'FillGap',5,'MinLength',7);
% =========
% Proposed solution:
% This will work for as many lines as you detected
for k=1:length(lines)
% Call Bresenham's algorithm
[x, y] = bresenham(lines(k).point1(1), lines(k).point1(2), ...
lines(k).point2(1), lines(k).point2(2));
% This is where you replace the line, here I use 0, but you can use
% whatever you want. However, note that if you use BW, you should only
% replace with 0 or 1, because is a logical image. If you want to use
% the original image, well, you know what to do.
BW(y, x) = 0;
% And now watch the lines disapear! (you can remove this line)
imagesc(BW), drawnow; pause(1);
end
Remember, download the matlab function first.

Sorting two column vectors into 3D matrix based on position

Using the imfindcircles function in MATLAB to track circles in two images. I start with approximately a grid of circles which deforms. I am trying to sort the two column vector from imfindcircles into matrices so that neighbouring circles are neighbouring elements in the matrices. The first image the circles conform to a grid and the following code works:
[centXsort,IX] = sortrows(centres1,1); %sort by x
centYsort =zeros(289,2); %preallocate
for i = 1:17:289
[sortedY,IY] = sortrows(centXsort(i:i+16,:),2); %sort by y within individual column
centYsort(i:i+16,:) = sortedY;
end
cent1mat = reshape(centYsort,17,17,2); %reshape into centre matrices
This doesn't work for the second image as some of the circles overlap in the x or y direction, but neighbouring circles never overlap. This means that in the second set of matrices the neighbouring circles aren't neighbouring elements after sorting.
Is there a way to approximate a scatter of points into a matrix?
This answer doesn't work in every single case, but it seems good enough for situations where the points don't vary too wildly.
My idea is to start at the grid corners and work our way along the outside diagonals of the matrix, trying to "grab" the nearest points that seem like they fit into the grid-points based any surrounding points we've already captured.
You will need to provide:
The number of rows (rows) and columns (cols) in the grid.
Your data points P arranged in a N x 2 array, rescaled to the unit square on [0,1] x [0,1]. (I assume the you can do this through visual inspection of the corner points of your original data.)
A weight parameter edge_weight to tell the algorithm how much the border points should be attracted to the grid border. Some tests show that 3-5 or so are good values.
The code, with a test case included:
%// input parameters
rows = 11;
cols = 11;
edge_weight = 4;
%// function for getting squared errors between the points list P and a specific point pt
getErr =#(P,pt) sqrt( sum( bsxfun(#minus,P,pt(:)').^2, 2 ) ); %'
output_grid = zeros(rows,cols,2); %// output grid matrix
check_grid = zeros(rows,cols); %// matrix flagging the gridpoints we have covered
[ROW,COL] = meshgrid(... %// coordinate points of an "ideal grid"
linspace(0,1,rows),...
linspace(0,1,cols));
%// create a test case
G = [ROW(:),COL(:)]; %// the actual grid-points
noise_factor = 0.35; %// noise radius allowed
rn = noise_factor/rows;
cn = noise_factor/cols;
row_noise = -rn + 2*rn*rand(numel(ROW),1);
col_noise = -cn + 2*cn*rand(numel(ROW),1);
P = G + [row_noise,col_noise]; %// add noise to get points
%// MAIN LOOP
d = 0;
while ~isempty(P) %// while points remain...
d = d+1; %// increase diagonal depth (d=1 are the outer corners)
for ii = max(d-rows+1,1):min(d,rows)%// for every row number i...
i = ii;
j = d-i+1; %// on the dth diagonal, we have d=i+j-1
for c = 1:4 %// repeat for all 4 corners
if i<rows & j<cols & ~check_grid(i,j) %// check for out-of-bounds/repetitions
check_grid(i,j) = true; %// flag gridpoint
current_gridpoint = [ROW(i,j),COL(i,j)];
%// get error between all remaining points and the next gridpoint's neighbours
if i>1
errI = getErr(P,output_grid(i-1,j,:));
else
errI = edge_weight*getErr(P,current_gridpoint);
end
if check_grid(i+1,j)
errI = errI + edge_weight*getErr(P,current_gridpoint);
end
if j>1
errJ = getErr(P,output_grid(i,j-1,:));
else
errJ = edge_weight*getErr(P,current_gridpoint);
end
if check_grid(i,j+1)
errJ = errJ + edge_weight*getErr(P,current_gridpoint);
end
err = errI.^2 + errJ.^2;
%// find the point with minimal error, add it to the grid,
%// and delete it from the points list
[~,idx] = min(err);
output_grid(i,j,:) = permute( P(idx,:), [1 3 2] );
P(idx,:) = [];
end
%// rotate the grid 90 degrees and repeat for next corner
output_grid = cat(3, rot90(output_grid(:,:,1)), rot90(output_grid(:,:,2)));
check_grid = rot90(check_grid);
ROW = rot90(ROW);
COL = rot90(COL);
end
end
end
Code for plotting the resulting points with edges:
%// plotting code
figure(1); clf; hold on;
axis([-0.1 1.1 -0.1 1.1])
for i = 1:size(output_grid,1)
for j = 1:size(output_grid,2)
scatter(output_grid(i,j,1),output_grid(i,j,2),'b')
if i < size(output_grid,1)
plot( [output_grid(i,j,1),output_grid(i+1,j,1)],...
[output_grid(i,j,2),output_grid(i+1,j,2)],...
'r');
end
if j < size(output_grid,2)
plot( [output_grid(i,j,1),output_grid(i,j+1,1)],...
[output_grid(i,j,2),output_grid(i,j+1,2)],...
'r');
end
end
end
I've developed a solution, which works for my case but might not be as robust as required for some. It requires a known number of dots in a 'square' grid and a rough idea of the spacing between the dots. I find the 'AlphaShape' of the dots and all the points that lie along the edge. The edge vector is shifted to start at the minimum and then wrapped around a matrix with the corresponding points are discarded from the list of vertices. Probably not the best idea for large point clouds but good enough for me.
R = 50; % search radius
xy = centres2;
x = centres2(:,1);
y = centres2(:,2);
for i = 1:8
T = delaunay(xy); % delaunay
[~,r] = circumcenter(triangulation(T,x,y)); % circumcenters
T = T(r < R,:); % points within radius
B = freeBoundary(triangulation(T,x,y)); % find edge vertices
A = B(:,1);
EdgeList = [x(A) y(A) x(A)+y(A)]; % find point closest to origin and rotate vector
[~,I] = min(EdgeList);
EdgeList = circshift(EdgeList,-I(3)+1);
n = sqrt(length(xy)); % define zeros matrix
matX = zeros(n); % wrap x vector around zeros matrix
matX(1,1:n) = EdgeList(1:n,1);
matX(2:n-1,n) = EdgeList(n+1:(2*n)-2,1);
matX(n,n:-1:1) = EdgeList((2*n)-1:(3*n)-2,1);
matX(n-1:-1:2,1) = EdgeList((3*n)-1:(4*n)-4,1);
matY = zeros(n); % wrap y vector around zeros matrix
matY(1,1:n) = EdgeList(1:n,2);
matY(2:n-1,n) = EdgeList(n+1:(2*n)-2,2);
matY(n,n:-1:1) = EdgeList((2*n)-1:(3*n)-2,2);
matY(n-1:-1:2,1) = EdgeList((3*n)-1:(4*n)-4,2);
centreMatX(i:n+i-1,i:n+i-1) = matX; % paste into main matrix
centreMatY(i:n+i-1,i:n+i-1) = matY;
xy(B(:,1),:) = 0; % discard values
xy = xy(all(xy,2),:);
x = xy(:,1);
y = xy(:,2);
end
centreMatX(centreMatX == 0) = x;
centreMatY(centreMatY == 0) = y;