I have a reference picture and i want to draw a circle around a picture that exists in the reference picture.
Now it draw a rectangle over the picture that exists in the reference picture but i don't know how to make it circle.
boxImage = imread('RefImg.jpg');
sceneImage = imread('full_image.jpg');
boxPoints = detectSURFFeatures(rgb2gray(boxImage));
scenePoints = detectSURFFeatures(rgb2gray(sceneImage));
[boxFeatures, boxPoints] = extractFeatures(rgb2gray(boxImage), boxPoints);
[sceneFeatures, scenePoints] = extractFeatures(rgb2gray(sceneImage), scenePoints);
boxPairs = matchFeatures(boxFeatures, sceneFeatures);
matchedBoxPoints = boxPoints(boxPairs(:, 1), :);
matchedScenePoints = scenePoints(boxPairs(:, 2), :);
figure;
showMatchedFeatures(rgb2gray(boxImage),rgb2gray(sceneImage), matchedBoxPoints, ...
matchedScenePoints, 'montage');
title('Putatively Matched Points (Including Outliers)');
[tform, inlierBoxPoints, inlierScenePoints] = ...
estimateGeometricTransform(matchedBoxPoints, matchedScenePoints, 'affine');
figure;
showMatchedFeatures(rgb2gray(boxImage), rgb2gray(sceneImage), inlierBoxPoints, ...
inlierScenePoints, 'montage');
title('Matched Points (Inliers Only)');
boxPolygon = [1, 1;... % top-left
size(boxImage, 2), 1;... % top-right
size(boxImage, 2), size(boxImage, 1);... % bottom-right
1, size(boxImage, 1);... % bottom-left
1, 1]; % top-left again to close the polygon
newBoxPolygon = transformPointsForward(tform, boxPolygon);
figure;
imshow(sceneImage);
hold on;
line(newBoxPolygon(:, 1), newBoxPolygon(:, 2), 'Color', 'y');
title('Detected Box');
Thanks,
You can use rectangle to actually draw an ellipse around your object of interest by using the Curvature parameter.
%// Transform your points
boxCorners = [1, 1; size(boxImage, 2), size(boxImage, 1)];
box = transformPointsForward(tform, boxCorners);
%// Position as [x, y, width, height]
position = [boxCorners(1,:), diff(boxCorners)];
%// Display the image
imshow(sceneImage);
hold on
%// Plot an ellipse at this location
rectangle('Position', position, 'Curvature', [1 1])
If you want to enforce an actual circle, you will want the diameter to be the diagonal distance across the rectangle and the center to be the midpoint of the diagonal of the rectangle.
boxCorners = [1, 1; size(boxImage, 2), size(boxImage, 1)];
box = transformPointsForward(tform, boxCorners);
%// Now compute the diagonal distance (diameter)
diameter = sqrt(sum(diff(box).^2));
%// Now determine the middle of the circle
center = mean(box);
%// Display the image
imshow(sceneImage);
hold on
%// Now plot the circle
t = linspace(0, 2*pi, 100);
plot(center(1) + cos(t) * diameter/2, ...
center(2) + sin(t) * diameter/2);
Related
I am trying to draw a cone, connected to the sphere in Matlab. I have the point [x1,y1,z1] outside of the sphere [x2,y2,z2] with R radius and I want it to be the top of the cone, created out of tangents.
On this pictures you can see what I have in mind:
Below you can see what I have already done. I am using it in order to mark the part of the Earth's surface, visible from the satellite position in orbit. Unfortunately, the cone in this picture is approximate, I need to create accurate one, connected with surface. For now, it is not only inaccurate, but also goes under it.
I am creating the sphere with this simple code (I am skipping the part of putting the map on it, it's just an image):
r = 6371.0087714;
[X,Y,Z] = sphere(50);
X2 = X * r;
Y2 = Y * r;
Z2 = Z * r;
surf(X2,Y2,Z2)
props.FaceColor= 'texture';
props.EdgeColor = 'none';
props.FaceLighting = 'phong';
figure();
globe = surface(X2,Y2,Z2,props);
Let's assume that I have the single point in 3D:
plot3(0,0,7000,'o');
How can I create such a cone?
There are two different questions here:
How to calculate cone dimensions?
How to plot lateral faces of a 3D cone?
Calculating Cone Dimensions
Assuming that center of sphere is located on [0 0 0]:
d = sqrt(Ax^2+Ay^2+Az^2);
l = sqrt(d^2-rs^2);
t = asin(rs/d);
h = l * cos(t);
rc = l * sin(t);
Plotting the Cone
The following function returns coordinates of lateral faces of cone with give apex point, axis direction, base radius and height, and the number of lateral faces.
function [X, Y, Z] = cone3(A, V, r, h, n)
% A: apex, [x y z]
% V: axis direction, [x y z]
% r: radius, scalar
% h: height, scalar
% n: number of lateral surfaces, integer
% X, Y, Z: coordinates of lateral points of the cone, all (n+1) by 2. You draw the sphere with surf(X,Y,Z) or mesh(X,Y,Z)
v1 = V./norm(V);
B = h*v1+A;
v23 = null(v1);
th = linspace(0, 2*pi, n+1);
P = r*(v23(:,1)*cos(th)+v23(:,2)*sin(th));
P = bsxfun(#plus, P', B);
zr = zeros(n+1, 1);
X = [A(1)+zr P(:, 1)];
Y = [A(2)+zr P(:, 2)];
Z = [A(3)+zr P(:, 3)];
end
The Results
rs = 6371.0087714; % globe radius
A = rs * 2 * [1 1 1]; % sattelite location
V = -A; % vector from sat to the globe center
% calculating cone dimensions
d = norm(A); % distance from cone apex to sphere center
l = (d^2-rs^2)^.5; % length of generating line of cone
sint = rs/d; % sine of half of apperture
cost = l/d; % cosine of half of apperture
h = l * cost; % cone height
rc = l * sint; % cone radius
% globe surface points
[XS,YS,ZS] = sphere(32);
% cone surface points
[XC, YC, ZC] = cone3(A, V, rc, h, 32);
% plotting results
hold on
surf(XS*rs,YS*rs,ZS*rs, 'facecolor', 'b', 'facealpha', 0.5, 'edgealpha', 0.5)
surf(XC, YC, ZC, 'facecolor', 'r', 'facealpha', 0.5, 'edgealpha', 0.5);
axis equal
grid on
Animating the satellite
The simplest way to animate objects is to clear the whole figure by clf and plot objects again in new positions. But a way more efficient method is to plot all objects once and in each frame, only update positioning data of moving objects:
clc; close all; clc
rs = 6371.0087714; % globe radius
r = rs * 1.2;
n = 121;
t = linspace(0, 2*pi, n)';
% point on orbit
Ai = [r.*cos(t) r.*sin(t) zeros(n, 1)];
[XS,YS,ZS] = sphere(32);
surf(XS*rs,YS*rs,ZS*rs, 'facecolor', 'b', 'facealpha', 0.5, 'edgealpha', 0.5)
hold on
[XC, YC, ZC] = cone3(Ai(1, :), Ai(1, :), 1, 1, 32);
% plot a cone and store handel of surf object
hS = surf(XC, YC, ZC, 'facecolor', 'r', 'facealpha', 0.5, 'edgealpha', 0.5);
for i=1:n
% calculating new point coordinates of cone
A = Ai(i, :);
V = -A;
d = norm(A);
l = (d^2-rs^2)^.5;
sint = rs/d;
cost = l/d;
h = l * cost;
rc = l * sint;
[XC, YC, ZC] = cone3(A, V, rc, h, 32);
% updating surf object
set(hS, 'xdata', XC, 'ydata', YC, 'zdata', ZC);
pause(0.01); % wait 0.01 seconds
drawnow(); % repaint figure
end
Another sample with 3 orbiting satellites:
I modified a code based on the shown in https://www.mathworks.com/matlabcentral/answers/377838-please-how-can-i-find-the-center-and-the-radius-of-the-inscribed-circle-of-a-set-of-points in order to find the inscribe circle but I do not understand why the image is rotated. Why and how can I solve it?
Code:
url='https://i.pcmag.com/imagery/reviews/00uaCVfzQ4Gsuhmh85WvT3x-4.fit_scale.size_1028x578.v_1569481686.jpg';
Image = rgb2gray(imread(url));
Image = imcomplement(Image);
fontSize = 10;
% determine contours
BW = imbinarize(Image);
BW = imfill(BW,'holes');
[B,L] = bwboundaries(BW,'noholes');
k = 1;
b = B{k};
y = b(:,2);
x = b(:,1);
subplot(2, 2, 1);
plot(x, y, 'b.-', 'MarkerSize', 3);
grid on;
title('Original Points', 'FontSize', fontSize);
% Enlarge figure to full screen.
set(gcf, 'Units', 'Normalized', 'OuterPosition', [0, 0.04, 1, 0.96]);
% Make data into a 1000x1000 image.
xMin = min(x)
xMax = max(x)
yMin = min(y)
yMax = max(y)
scalingFactor = 1000 / min([xMax-xMin, yMax-yMin])
x2 = (x - xMin) * scalingFactor + 1;
y2 = (y - yMin) * scalingFactor + 1;
mask = poly2mask(x2, y2, ceil(max(y2)), ceil(max(x2)));
% Display the image.
p2 = subplot(2, 2, 2);
imshow(mask);
axis(p2, 'on', 'xy');
title('Mask Image', 'FontSize', fontSize);
% Compute the Euclidean Distance Transform
edtImage = bwdist(~mask);
% Display the image.
p3 = subplot(2, 2, 3);
imshow(edtImage, []);
axis(p3, 'on', 'xy');
% Find the max
radius = max(edtImage(:))
% Find the center
[yCenter, xCenter] = find(edtImage == radius)
% Display circles over edt image.
viscircles(p3, [xCenter, yCenter], radius,'Color','g');
% Display polygon over image also.
hold on;
plot(x2, y2, 'r.-', 'MarkerSize', 3, 'LineWidth', 2);
title('Euclidean Distance Transform with Circle on it', 'FontSize', fontSize);
% Display the plot again.
subplot(2, 2, 4);
plot(x, y, 'b.-', 'MarkerSize', 3);
grid on;
% Show the circle on it.
hold on;
% Scale and shift the center back to the original coordinates.
xCenter = (xCenter - 1)/ scalingFactor + xMin
yCenter = (yCenter - 1)/ scalingFactor + yMin
radius = radius / scalingFactor
rectangle('Position',[xCenter-radius, yCenter-radius, 2*radius, 2*radius],'Curvature',[1,1]);
title('Original Points with Inscribed Circle', 'FontSize', fontSize);
Original image:
Output image
[B,L] = bwboundaries(BW,...) returns in B the row and column values (documentation). That is, the first column of B{k} is y, the second one is x.
After changing this bit of code as follows:
y = b(:,1);
x = b(:,2);
you will notice that the image is upside down! That is because in an image the y-axis increases down (y is the row number in the matrix), whereas in the plot the y-axis increases up (mathematical coordinate system).
The axes where you use imshow in are automatically set to the right coordinate system, but then you do axis(p3, 'on', 'xy');, turning it upside down again. Instead, use
axis(p1, 'on', 'image');
on the axes where you don't use imshow (i.e. the top-left and bottom-right ones).
I want to obtain a 2D slice from the 3D volume in the example (slightly modified) How do I resolve this issue with 3D image visualization? as follows:
% create input image
imageSizeX = 10;
imageSizeY = 10;
imageSizeZ = 10
% generate 3D grid using voxel size = 0.5
[Xq, Yq, Zq] = ndgrid(1:0.5:imageSizeX-1, 1:0.5:imageSizeY-1, 1:0.5:imageSizeZ-1);
% obtain coordinates of all internal vertices, faces, and edges
allCoords = [Xq(:), Yq(:), Zq(:)]; % i need this bit for something very important but not shown in the question.
% Re-generate 3D grid using voxel size = 1
[columnsInImage, rowsInImage, pagesInImage] = ndgrid(1: imageSizeX-1, 1: imageSizeY-1, 1: imageSizeZ-1);
% create the sphere in the image.
centerY = imageSizeY/2;
centerX = imageSizeX/2;
centerZ = imageSizeZ/2;
diameter = 4;
radius = diameter/2;
sphereVoxels = flipud((rowsInImage - centerY).^2 ...
+ (columnsInImage - centerX).^2 + (pagesInImage - centerZ).^2 <= radius.^2);
% change image from logical to numeric labels.
Img = double(sphereVoxels);
for ii = 1:numel(Img)
if Img(ii) == 0
Img(ii) = 2; % intermediate phase voxels
end
end
% specify the desired angle
angle = 30;
% specify desired pixel height and width of solid
width = imageSizeX;
height = imageSizeY;
page = imageSizeZ;
% Find the row point at which theta will be created
y = centerY - ( radius*cos(angle * pi/180) )
% determine top of the solid bar
y0 = max(1, y-height);
% label everything from y0 to y to be = 3 (solid)
Img(1:width, y0:y, 1:page)=3;
%%%%%% Plot the surfaces
[X, Y, Z, C] = build_voxels(Img > 0);
hSurface = patch(X, Y, Z, Img(C),...
'AmbientStrength', 0.5, ...
'BackFaceLighting', 'unlit', ...
'EdgeColor', 'none', ...
'FaceLighting', 'flat');
colormap([1 0 0; 1 1 0]);
axis equal;
axis tight;
view(45, 45);
grid on;
xlabel('x-axis (voxels)');
ylabel('y-axis (voxels)');
zlabel('z-axis (voxels)');
light('Position', get(gca, 'CameraPosition'), 'Style', 'local');
zoom on;
hold on;
Vq = griddata(columnsInImage, rowsInImage, pagesInImage, Img, Xq, Yq, Zq);
figure
h1 = slice(permute(Xq, [2 1 3]),permute(Yq, [2 1 3]),permute(Zq, [2 1 3]), Vq, 5,2,5);
When i run the code, i get an Error message:
"The number of data point locations should equal the number of data point values.
Error in griddata>useScatteredInterp (line 188)
F = scatteredInterpolant(inargs{1}(:),inargs{2}(:),inargs{3}(:), ..."
I want to believe this is so because the size of columnsInImage and size of pagesInImage are not equal to size(P,1) and size(P,3), respectively.
Nonetheless, I also tried to use a vector as follows:
figure
h1 = slice(Img(:,1), Img(:,2), Img(:,3), Img, 5,2,5);
I however still end up with the error message:
"Error using griddedInterpolant
The grid was created from grid vectors that were not strictly monotonic increasing.
Error in interp3 (line 142)
F = griddedInterpolant(X, Y, Z, V, method,extrap);"
Please, guys i need suggestions/ideas on how i could remedy these. Many thanks in advance!..
How could I create a 3D binary matrix/image from a surface mesh in Matlab?
For instance, when I create ellipsoid using:
[x, y, z] = ellipsoid(0,0,0,5.9,3.25,3.25,30);
X, Y and X are all 2D matrix with size 31 x 31.
Edited based on suggestion of #Magla:
function Create_Mask_Basedon_Ellapsoid3()
close all
SurroundingVol = [50, 50, 20];
%DATA
[MatX,MatY,MatZ] = meshgrid(-24:1:25, -24:1:25, -9:1:10);
[mask1, x, y, z] = DrawEllipsoid([0, -10, 0], [6, 3, 3], MatX,MatY,MatZ);
[mask2, x2, y2, z2] = DrawEllipsoid([15, 14, 6], [6, 3, 3], MatX,MatY,MatZ);
mask = mask1 + mask2;
%Surface PLOT
figure('Color', 'w');
subplot(1,2,1);
%help: Ideally I would like to generate surf plot directly from combined mask= mask1 + mask2;
s = surf(x,y,z); hold on;
s2 = surf(x2,y2,z2); hold off;
title('SURFACE', 'FontSize', 16);
view(-78,22)
subplot(1,2,2);
xslice = median(MatX(:));
yslice = median(MatY(:));
zslice = median(MatZ(:));
%help: Also how do I decide correct "slice" and angles to 3D visualization.
h = slice(MatX, MatY, MatZ, double(mask), xslice, yslice, zslice)
title('BINARY MASK - SLICE VOLUME', 'FontSize', 16);
set(h, 'EdgeColor','none');
view(-78, 22)
%az = 0; el = 90;
%view(az, el);
end
function [mask, Ellipsoid_x, Ellipsoid_y, Ellipsoid_z] = DrawEllipsoid(CenterEllipsoid, SizeEllipsoid, MatX, MatY, MatZ)
[Ellipsoid_x, Ellipsoid_y, Ellipsoid_z] = ellipsoid(CenterEllipsoid(1), CenterEllipsoid(2), CenterEllipsoid(3), SizeEllipsoid(1)/2 , SizeEllipsoid(2)/2 , SizeEllipsoid(3)/2 ,30);
v = [Ellipsoid_x(:), Ellipsoid_y(:), Ellipsoid_z(:)]; %3D points
%v = [x(:), y(:), z(:)]; %3D points
tri = DelaunayTri(v); %triangulation
SI = pointLocation(tri,MatX(:),MatY(:),MatZ(:)); %index of simplex (returns NaN for all points outside the convex hull)
mask = ~isnan(SI); %binary
mask = reshape(mask,size(MatX)); %reshape the mask
end
There you go:
%// Points you want to test. Define as you need. This example uses a grid of 1e6
%// points on a cube of sides [-10,10]:
[x y z] = meshgrid(linspace(-10,10,100));
x = x(:);
y = y(:);
z = z(:); %// linearize
%// Ellipsoid data
center = [0 0 0]; %// center
semiaxes = [5 4 3]; %// semiaxes
%// Actual computation:
inner = (x-center(1)).^2/semiaxes(1).^2 ...
+ (y-center(2)).^2/semiaxes(2).^2 ...
+ (z-center(3)).^2/semiaxes(3).^2 <= 1;
For the n-th point of the grid, whose coordinates are x(n), y(n), z(n), inner(n) is 1 if the point lies in the interior of the ellipsoid and 0 otherwise.
For example: draw the interior points:
plot3(x(inner), y(inner), z(inner), '.' , 'markersize', .5)
Here is a method for creating a binary mask from an ellipsoid. It creates a corresponding volume and sets to NaN the points outside the ellipsoid (ones inside).
It doesn't take into consideration the formula of the ellipsoid, but uses a convex hull. Actually, it works for any volume that can be correctly described by a 3D convex hull. Here, the convexhulln step is bypassed since the ellipsoid is already a convex hull.
All credits go to Converting convex hull to binary mask
The following plot
is produced by
%DATA
[x, y, z] = ellipsoid(0,0,0,5.9,3.25,3.25,30);
%METHOD
v = [x(:), y(:), z(:)]; %3D points
[X,Y,Z] = meshgrid(min(v(:)):0.1:max(v(:))); %volume mesh
tri = DelaunayTri(v); %triangulation
SI = pointLocation(tri,X(:),Y(:),Z(:)); %index of simplex (returns NaN for all points outside the convex hull)
mask = ~isnan(SI); %binary
mask = reshape(mask,size(X)); %reshape the mask
%PLOT
figure('Color', 'w');
subplot(1,2,1);
s = surf(x,y,z);
title('SURFACE', 'FontSize', 16);
view(-78,22)
subplot(1,2,2);
xslice = median(X(:));
yslice = median(Y(:));
zslice = median(Z(:));
h = slice(X, Y, Z, double(mask), xslice, yslice, zslice)
title('BINARY MASK - SLICE VOLUME', 'FontSize', 16);
set(h, 'EdgeColor','none');
view(-78,22)
Several ellipsoids
If you have more than one ellipsoid, one may use this masking method for each of them, and then combine the resulting masks with &.
Choice of slices and angle
"Correct" is a matter of personal choice. You can either
create the unrotated mask and rotate it after (Rotate a 3D array in matlab).
create a mask on already rotated ellipsoid.
create a mask on a slightly rotated ellipsoid (that gives you the choice of a "correct" slicing), and rotate it further to its final position.
I am able to create a 3D cone in MATLAB, but: does anyone know how to paint the cone so that it recreates the HSV color space? I know there is the command:
colormap hsv;
but how do I use it?
Thanks in advance.
I'm guessing you want to create a plot similar to the cone in the following Wikipedia image:
One way to do this is to plot your cone and texture map the surface with an image of the HSV color space. Here's how you could do this:
% First, create a 100-by-100 image to texture the cone with:
H = repmat(linspace(0, 1, 100), 100, 1); % 100-by-100 hues
S = repmat([linspace(0, 1, 50) ... % 100-by-100 saturations
linspace(1, 0, 50)].', 1, 100); %'
V = repmat([ones(1, 50) ... % 100-by-100 values
linspace(1, 0, 50)].', 1, 100); %'
hsvImage = cat(3, H, S, V); % Create an HSV image
C = hsv2rgb(hsvImage); % Convert it to an RGB image
% Next, create the conical surface coordinates:
theta = linspace(0, 2*pi, 100); % Angular points
X = [zeros(1, 100); ... % X coordinates
cos(theta); ...
zeros(1, 100)];
Y = [zeros(1, 100); ... % Y coordinates
sin(theta); ...
zeros(1, 100)];
Z = [2.*ones(2, 100); ... % Z coordinates
zeros(1, 100)];
% Finally, plot the texture-mapped surface:
surf(X, Y, Z, C, 'FaceColor', 'texturemap', 'EdgeColor', 'none');
axis equal
And you should get the following figure: