Test if points are within rectangles - matlab

I have some data points and can easily plot them in a figure in MATLAB.
I need to find out which data points are located inside some rectangular areas, as can be seen in the attached picture. In this picture, black dots represent my data points and red rectangles represent mentioned areas.
How can I search through my data points and see whether or not they belong to any of the rectangles? I need to have a list of all members (data points) for each rectangle.
Sample data points and rectangles:

As Ozcan said in the comments, inpolygon is the way to go. Here's a quick demo, see comments for details:
% Create 4 random rectangles, defined by their x and y coords in rectX and rectY.
% Each column defines a different rectangle.
sizes = randi([5,10], 2, 4)./10;
rectX = randi([1,5], 1, 4); rectX = [rectX; rectX; rectX + sizes(1,:); rectX + sizes(1,:)];
rectY = randi([1,5], 1, 4); rectY = [rectY; rectY + sizes(2,:); rectY + sizes(2,:); rectY];
% Create a random set of 1000 points for testing
points = [rand(1000, 1)*range(rectX(:))+min(rectX(:)), rand(1000, 1)*range(rectY(:))+min(rectY(:))];
% Set up logical matrix of test results
inrect = logical(zeros(size(points,1), size(rectX,2)));
% Plot the rectangles using patch
figure;
patch(rectX,rectY,'red')
% Hold on and plot all of the points as black dots
hold on;
plot(points(:,1),points(:,2),'.k');
% Loop through each rectangle, testing the points
for r = 1:size(rectX, 2)
% Test points using inpolygon, store results to inrect matrix
inrect(:,r) = inpolygon(points(:,1), points(:,2), rectX(:,r), rectY(:,r));
end
% Plot all points which are in any rectangle as blue circles
plot(points(any(inrect,2), 1), points(any(inrect,2), 2), 'bo');
Result:
Note you now have the logical matrix inrect, which is true (in one column per rectangle, one row per point) when the point is within a rectangle. The code above uses the any operator for plotting when the point is in any of the rectangles.

Related

What’s the most efficient way to find the coordinates of all internal edges of a 3D grid?

I need to find the coordinates of all the internal edges of an image. I have tried to achieve this with the following procedure:
Generate grid using pixel size of 0.5
Obtain coordinates of all vertices.
Remove all unwanted rows:
Rows with all integer values
Rows with 2 non-integers
Rows with all non-integers
Re-sample your grid using mesh size of 1 pixel.
I feel that resampling the grids with a pixel spacing of 1 will leave me with the coordinates obtained in step 3 as the coordinates of the edges.
The code is as follows:
% input image
Ydim = 3;
Xdim = 3;
Zdim = 3;
% generate 3D mesh using a pixel spacing of 0.5
[xgv, ygv, zgv] = meshgrid(1:0.5:Xdim - 1, 1:0.5:Ydim - 1, 1:0.5:Zdim - 1);
% obtain coordinates of all internal vertices and edges
allCoords = [ygv(:), xgv(:), zgv(:)];
% Obtain only the coordinates of edges by keeping only two integers and one
% non-integer in each row
edgeCoords = allCoords;
edgeCoords((sum(mod(allCoords,1)~=0, 2))~=1, :) = [];
% re-sample 3D grid using a pixel spacing of 1
[columnsInImage, rowsInImage, pagesInImage] = meshgrid(1:Xdim - 1, 1:Ydim - 1, 1:Zdim - 1);
% plot points
plot3(edgeCoords(:,2), edgeCoords(:,1), edgeCoords(:,3), '.b', 'markersize', 20)
hold on;
grid on;
scatter3(edgeCoords(:,1), edgeCoords(:,2), edgeCoords(:,3));
I am not exactly sure of the assumption in 5) above, as well as the overall procedure. Hence, I need someone to help me look at this. Is my assumption correct? Is there a more efficient way to do this?
Lastly, I would love to visualize this better than I already have in the code. Thanks for your help guys!

How to create a polygon out of the outer cells of a contiguous patch of cells

I have below a contiguous patch of cells plotted in Matlab.
The outer cells of the red patch have to be determined and then a polygon joining the centers of these cells will give me a polygon. How do i compute the outer cells of the contiguous patch?
I have an array of integers whose elements denote the cell in the red patch, for example,
a=[1;64;23;456;345];
Each element , say 64 corresponds to a cell in the image, and it is the cell belonging to the red patch.
The motivation to solve the problem is to deal with a polygon with minimal number of edges rather than so many cells. it slows down computation. Convex hull is not good enough. I don't want the resulting polygon to overlap with the brown area at all.
What i am suggesting is the case on the left in image below but it seems ugly. So a better way would be as in right to just skip the cells only sharing a single point with the outer brown area. I would like my outer cells to then be only those that share more than just a single point with the outer brown area.
But we want to avoid large number of edges in the resultant polygon!
I first processed the sample image from your question to create a logical mask (you already have an example of how to do that here).
Once you have this mask, there is a really easy way to generate the polygon you want using the bwtraceboundary function from the Image Processing Toolbox. This will give you a set of pixel indices in order around the perimeter of your masked region:
[r, c] = find(mask, 1);
coords = bwtraceboundary(mask, [r c], 'N');
And we can visualize it like so:
imagesc(mask);
colormap([0.9 0.9 0.9; 0.6 0.6 0.6]);
axis equal
set(gca, 'XLim', [0.5 0.5+size(mask, 2)], 'YLim', [0.5 0.5+size(mask, 1)]);
hold on;
plot(coords(:, 2), coords(:, 1), 'r', 'LineWidth', 2);
plot(coords(1, 2), coords(1, 1), 'go', 'LineWidth', 2);
The coordinates for the red line are ordered starting from the green circle and moving clockwise around the perimeter pixels of the masked region.
If you would prefer to generate a boundary outline that follows along the perimeter pixel edges of the region instead of the perimeter pixel centers, you can use the solution from my answer to a related question. This will yield the following:
Although the answer by #rahnema1 is really cool, I think the OP is asking more how to extract the set of edges according to the described rules.
Here is my approach identifying all the 10 patterns of 2x2 pixels that contain edges. Assuming the matrix A has the image with 1s and 0s (A = zeros(ny, nx); A(a) = 1):
% we identify patterns with edges over 2x2 patches, describing with
% the first 4 binary values what pixels are set, and with the next 2
% the edge with 2 indices over the 2x2 patch
patterns = [
0,1,0,1, 3,4 % vertical edge at rhe right
1,0,1,0, 1,2 % vertical edge at the left
0,0,1,1, 2,4 % horizontal edge at the bottom
1,1,0,0, 1,3 % horizontal edge at the top
1,0,0,1, 1,4 % diagonal edge
0,1,1,0, 2,3 % diagonal edge
1,0,1,1, 1,4 % diagonal edge, extra pixel set
1,1,0,1, 1,4 % diagonal edge, extra pixel set
1,1,1,0, 2,3 % diagonal edge, extra pixel set
0,1,1,1, 2,3 % diagonal edge, extra pixel set
];
% 2x2 patches (matrix form)
P00 = A(1:end-1,1:end-1);
P10 = A(2:end,1:end-1);
P01 = A(1:end-1,2:end);
P11 = A(2:end,2:end);
% edge unique identifier using powers of 2
id = #(p00,p01,p10,p11) 1*p00 + 2*p10 + 4*p01 + 8*p11;
P = id(P00,P01,P10,P11); % vectorized pattern identification
% edges
e0 = []; % from (i,j)
e1 = []; % to (i,j)
for i = 1:size(patterns, 1) % small loop over the 10 patterns
p = patterns(i, :);
E = (P == id(p(1),p(2),p(3),p(4))); % pattern search, vectorized
[c,r] = ind2sub(size(E), find(E));
[c0,r0] = ind2sub([2,2], p(5));
[c1,r1] = ind2sub([2,2], p(6));
e0 = [e0; c+c0, r+r0];
e1 = [e1; c+c1, r+r1];
end
And here the result applying it to your image (I used GIMP for capture, resize and filter, so maybe the image is not exactly the same):
X = [e0(:,2) e1(:,2)];
Y = size(A,1) - [e0(:,1) e1(:,1)];
plot(X', Y', '.-')
I am assuming that obtaining an ordered sequence of edges describing the polygon (or polygons) is not the main problem here once you have the aforementioned set.
Using Image Processing toolbox You can apply dilation on the image and than apply and operator between result of dilation and the original image.
A = imread('bnhfm.png');
B = A & imdilate(~A, true(3));
imshow(B);
imwrite(B, 'result.png');

Generating a curve around the point on a scatter plot

I am generating a scatter plot containing data from multiple sources, as displayed below.
I would like to be able to generate a curve surrounding an arbitrary query point and passing through points on scatter plot. Final goal is to calculate the area between the lines on the plot.
I have implemented solution using finding points with knnsearch in a circular fashion and then applying hampel filter to eliminate noise. In the example below, I have selected a point right about in the middle of the blue-shaded area. As you can see, the result is far from perfect, and I need more precision.
I am looking for something similar to boundary function, but to work from the inside of the point cloud, not from the outside.
Final goal is to calculate the area between the lines on the plot.
I would do it differently. Just take any two lines of the plot, calculate the area under the curves with some kind of numerical approximation (for example trapezoidal numerical integration), then subtract the areas and obtain the area between the lines.
Thank to idea in Trilarion's answer, I was able to come up with the better solution.
Note that I use notation for YZ plane instead of XY (to keep consistent with robot coordinate system).
Solution
Generate curves for each set of scatter data
% Scatter data is in iy and iz vectors.
curve = fit(iy, iz, 'smoothingspline', 'SmoothingParam', 0.5);
% Remove outliers.
fdata = feval(curve, iy);
I = abs(fdata - iz) > 0.5 * std(iz);
outliers = excludedata(iy, iz, 'indices', I);
% Final curve without outliers.
curve = fit(iy, iz, 'smoothingspline', 'Exclude', outliers, 'SmoothingParam', 0.5);
Plot curves and scatter data
% Color maps generated by MATLAB's colormap function.
h_curve = plot(curve);
set(h_curve, 'Color', color_map_light(i,:));
scatter(iy, iz, '.', 'MarkerFaceColor', color_map(i,:))
Let user provide an input by selecting points
User selects one point as a query point and two points for limits along Y axis. This is because some curves come close, but never intersect.
[cs_position.y, cs_position.z] = ginput(1);
[cs_area_limits, ~] = ginput(2);
if cs_area_limits(1) > cs_area_limits(2)
cs_area_limits = flipud(cs_area_limits);
end
plot_cross_section(cs_position);
Finally calculate and plot surface area
This section uses fantastic answer by Doresoom.
function [ ] = plot_cross_section(query_point)
%PLOT_CROSS_SECTION Calculates and plots cross-section area.
% query_point Query point.
% Find values on query point's Y on each of the curves.
z_values = cellfun(#(x, y) feval(x, y),...
curves, num2cell(ones(size(curves)) * query_point.y))
% Find which curves are right above and below the query point.
id_top = find(z_values >= query_point.z, 1, 'first')
id_bottom = find(z_values < query_point.z, 1, 'last')
if isempty(id_top) || isempty(id_bottom)
return
end
% Generate points along curves on the range over Y.
y_range = cs_area_limits(1):0.1:cs_area_limits(2);
z_top = feval(curves{id_top}, y_range).';
z_bottom = feval(curves{id_bottom}, y_range).';
% Plot area.
Y = [ y_range, fliplr(y_range) ];
Z = [ z_top, fliplr(z_bottom) ];
fill(Y, Z, 'b', 'LineStyle', 'none')
alpha 0.5
hold on
% Calculate area and show to user.
cs_area = polyarea(Y, Z);
area_string = sprintf('%.2f mm^2', cs_area);
text(0, -3, area_string, 'HorizontalAlignment', 'center')
end
Result

remapping arbitary curves

Two curves with a set of known pixel coordinates are shown at the image above. Is there a way to transform the outer curve into a circle and then remap the inner curve such that the distances at all points between the two curves and the area between the two curves are preserved?
One way that I thought that I would do this is by splitting the region in between the two curves into smaller quadrilateral sections. The top and bottom of the quadrilateral will be the outer and inner curves with a predetermined length. The sides of the quadrilateral run laterally between the two curves and should be straight. After the transformation, the outer curve will be a circular arc and the inner curve will adjust according to the pre-transform distances in order to preserve distance. In order to preserve area, the lateral lines of the quadrilateral will adjust the angles at which they were orientated, but still remain straight, to preserve area.
The problem is that I can't think of a way to code this or how I would split the region into smaller sections.
If there are any other suggestions on how I can approach my problem I am open to them.
I don't think it's possible to preserve both the area and the distance. It is possible to preserve the area and the proportional distance (from the centre of the outer circle in the original drawing - i.e., the point (mean(x), mean(y)), if x and y are the list of x-coords and y-coords of the original shape), or the distance only. The following is an illustrative example:
Edit: I thought about it a little more, and in the below code you have the parameter of the outer circle's radius, which can be freely changed to affect the area without changing the line length. You should turn the code below into a function, omitting the part that scales the area, of course, and use one of the optimisation functions to find the radius of the outer circle that gets the closest area with the same line lengths.
% Area normalisation flag
norm_area = true;
% Start with two circles, perturb them randomly
N = 100;
phi = linspace(0, 2*pi, N)';
% Set radii
r = [2 4];
% Generate data
r_pert = repmat(r, N, 1);
% Filter some random data (so it's smoothish)
filtOrd = 20;
b = ones(1, filtOrd) / filtOrd;
randData = filter(b, 1, randn(size(r_pert)));
randData = bsxfun(#minus, randData, mean(randData));
r_pert = r_pert + randData;
% Initial plot
close all;
polar(phi, r_pert(:, 2));
hold on;
polar(phi, r_pert(:, 1));
% Generate circle that encloses all radii
r_pureCirc = max(r_pert(:));
% Line lengths
lens = abs(r_pert(:, 2) - r_pert(:, 1));
r_pertCirc = r_pureCirc - lens;
% Calculate area of new and old shapes
% Elemental area is a pie slice between phi(n) - dphi/2 and phi + dphi/2
dphi = phi(2) - phi(1);
dA_orig = dphi * (r_pert(:, 2) .^ 2 - r_pert(:, 1) .^ 2) / 2;
dA_new = dphi * (r_pureCirc .^ 2 - r_pertCirc .^ 2) / 2;
A_orig = sum(dA_orig);
A_new = sum(dA_new);
r_new = [r_pertCirc repmat(r_pureCirc, N, 1)];
if norm_area
% Normalise to same area
r_new = sqrt(A_orig / A_new) * r_new;
end
% Plot again
figure;
polar(phi, r_new(:, 2));
hold on;
polar(phi, r_new(:, 1));
In this code, a pair of circles disturbed by some filtered random noise is generated - similar to your original drawing (ish). Working in polar co-ordinates, a circle is generated in which the whole original shape fits. The inner points of a second circle are calculated to preserve the distances in the original. If desired, the whole thing is then scaled by the ratio of the areas of the new and original shape.
Example plots:
Original Shape
Generated Shape

matlab: how to plot multidimensional array

Let's say I have 9 MxN black and white images that are in some way related to one another (i.e. time lapse of some event). What is a way that I can display all of these images on one surface plot?
Assume the MxN matrices only contain 0's and 1's. Assume the images simply contain white lines on a black background (i.e. pixel value == 1 if that pixel is part of a line, 0 otherwise). Assume images are ordered in such a way as to suggest movement progression of line(s) in subsequent images. I want to be able to see a "side-view" (or volumetric representation) of these images which will show the surface that a particular line "carves out" in its movement across the images.
Coding is done in MATLAB. I have looked at plot (but it only does 2D plots) and surf, which does 3D plots but doesn't work for my MxNx9 matrix of images. I have also tried to experiment with contourslice, but not sure what parameters to pass it.
Thanks!
Mariya
Are these images black and white with simple features on a "blank" field, or greyscale, with more dense information?
I can see a couple of approaches.
You can use movie() to display a sequence of images as an animation.
For a static view of sparse, simple data, you could plot each image as a separate layer in a single figure, giving each layer a different color for the foreground, and using AlphaData to make the background transparent so all the steps in the sequenc show through. The gradient of colors corresponds to position in the image sequence. Here's an example.
function plotImageSequence
% Made-up test data
nLayers = 9;
x = zeros(100,100,nLayers);
for i = 1:nLayers
x(20+(3*i),:,i) = 1;
end
% Plot each image as a "layer", indicated by color
figure;
hold on;
for i = 1:nLayers
layerData = x(:,:,i);
alphaMask = layerData == 1;
layerData(logical(layerData)) = i; % So each layer gets its own color
image('CData',layerData,...
'AlphaData',alphaMask,...
'CDataMapping','scaled');
end
hold off
Directly showing the path of movement a "line" carves out is hard with raster data, because Matlab won't know which "moved" pixels in two subsequent images are associated with each other. Don't suppose you have underlying vector data for the geometric features in the images? Plot3() might allow you to show their movement, with time as the z axis. Or you could use the regular plot() and some manual fiddling to plot the paths of all the control points or vertexes in the geometric features.
EDIT: Here's a variation that uses patch() to draw each pixel as a little polygon floating in space at the Z level of its index in the image sequence. I think this will look more like the "surface" style plots you are asking for. You could fiddle with the FaceAlpha property to make dense plots more legible.
function plotImageSequencePatch
% Made-up test data
nLayers = 6;
sz = [50 50];
img = zeros(sz(1),sz(2),nLayers);
for i = 1:nLayers
img(20+(3*i),:,i) = 1;
end
% Plot each image as a "layer", indicated by color
% With each "pixel" as a separate patch
figure;
set(gca, 'XLim', [0 sz(1)]);
set(gca, 'YLim', [0 sz(2)]);
hold on;
for i = 1:nLayers
layerData = img(:,:,i);
[x,y] = find(layerData); % X,Y of all pixels
% Reshape in to patch outline
x = x';
y = y';
patch_x = [x; x+1; x+1; x];
patch_y = [y; y; y+1; y+1];
patch_z = repmat(i, size(patch_x));
patch(patch_x, patch_y, patch_z, i);
end
hold off