Matlab draw line in image using line equation - matlab

Given two coordinate points A and B and its gradient 'slopes'. Now, I want to draw a line (with linspace - function) on an image which worked for lines with a gradient ~1. The line stopped at the image boundary (due to xlims and ylims).
imshow(I)
xlims = xlim(gca);
ylims = ylim(gca);
Now the problem: I try to repeat for another line which is nearly vertical and it did not stop at the image boundary (see picture):
Any solutions how I can draw a line using a y=mx+c equation that stops at the image boundary independent from the gradient?
EDIT:
img = uint8(zeros(382, 382));
pointA = [181.4594, 129.7092];
pointB = [185.3411, 251.6005];
imshow(img)
hold on
plot(pointA(1), pointA(2), 'ro')
plot(pointB(1), pointB(2), 'bo')
hold off
% From y = mx + x
slope = 31.4015;
intercept = -5.5684e+03;
xlims = xlim(gca);
ylims = ylim(gca);
y = xlims*slope + intercept;
Xline = linspace(xlims(1), xlims(2), 382);
Xline_pole1 = linspace(xlims(1), pointA(1), 382/2);
Xline_pole2 = linspace(xlims(2), pointB(1), 382/2);
Yline = Xline*slope + intercept;
Yline_pole1 = Xline_pole1*slope + intercept;
Yline_pole2 = Xline_pole2*slope + intercept;
hold on
plot( Xline_pole1, Yline_pole1, 'Color', 'b', 'LineWidth', 3 );
hold off
% Check how many coordinates are within image frame
for zz=1:length(Xline_pole1)
hold on
plot(Xline_pole1(zz),Yline_pole1(zz), 'm+');
end

Easy workaround:
Just reset your limits after you plotted your lines
imshow(I)
xlims = xlim(gca);
ylims = ylim(gca);
% plot your lines
xlim(xlims)
ylim(ylims)

Related

Calculate Y coordinates of an image from graph point of origin

In Matlab the image axes are shown as rows and columns (matrix style) which flip/cause the Y axis to start from the upper left corner. In the script below, I divide an outline to equally distance points using interparc (File Exchange link).
I wish to convert/adjust the calculated Y coordinates of the selected points so they will start from the “graph point of origin” (0,0; lower left corner) but without flipping the image. Any idea how to do this coordinates conversion?
Code:
clc;
clear;
close all;
readNumPoints = 8
numPoints = readNumPoints+1
url='https://icons.iconarchive.com/icons/thesquid.ink/free-flat-sample/512/owl-icon.png';
I = imread(url);
I = rgb2gray(I);
imshow(I);
BW = imbinarize(I);
BW = imfill(BW,'holes');
hold on;
[B,L] = bwboundaries(BW,'noholes');
k=1;
stat = regionprops(I,'Centroid');
b = B{k};
c = stat(k).Centroid;
y = b(:,2);
x = b(:,1);
plot(y, x, 'r', 'linewidth', 2);
pt = interparc(numPoints,x,y,'spline');
py = pt(:,2);
px = pt(:,1);
sz = 150;
scatter(py,px,sz,'d')
str =1:(numPoints-1);
plot(py, px, 'g', 'linewidth', 2);
text(py(1:(numPoints-1))+10,px(1:(numPoints-1))+10,string(str), 'Color', 'b');
pointList = table(py,px)
pointList(end,:) = []
You will need to flip the display direction of y-axis (as #If_You_Say_So suggested in the comment).
set(gca,'YDir','normal')
Y-axis is now pointing upward, but the image is displayed upside down. So we flip the y-data of the image as well.
I=flipud(I);
The image is flipped twice and is thus upright.
The data should be flipped before you plot it or do any calculation based on it. The direction of y-axis can be flipped later when you show the image or plot the outline.
url='https://icons.iconarchive.com/icons/thesquid.ink/free-flat-sample/512/owl-icon.png';
I = imread(url);
I = rgb2gray(I);
% Flip the data before `imshow`
I=flipud(I);
imshow(I);
% Flip the y-axis display
set(gca,'YDir','normal')
pointList =
py px
______ ______
1 109
149.02 17.356
362.37 20.77
512 113.26
413.99 270.84
368.89 505.99
141.7 508
98.986 266.62

Matlab: patch area between two curves which depend on the curves values

I'm trying to fill an area between two curves with respect to a function which depends on the values of the curves.
Here is the code of what I've managed to do so far
i=50;
cc = #(xx,x,y) 1./(1+(exp(-xx)/(exp(-x)-exp(-y))));
n_vec = 2:0.1:10;
x_vec = linspace(2,10,length(n_vec));
y_vec = abs(sin(n_vec));
N=[n_vec,fliplr(n_vec)];
X=[x_vec,fliplr(y_vec)];
figure(1)
subplot(2,1,1)
hold on
plot(n_vec,x_vec,n_vec,y_vec)
hp = patch(N,X,'b')
plot([n_vec(i) n_vec(i)],[x_vec(i),y_vec(i)],'linewidth',5)
xlabel('n'); ylabel('x')
subplot(2,1,2)
xx = linspace(y_vec(i),x_vec(i),100);
plot(xx,cc(xx,y_vec(i),x_vec(i)))
xlabel('x'); ylabel('c(x)')
This code produces the following graph
The color code which I've added represent the color coding that each line (along the y axis at a point on the x axis) from the area between the two curves should be.
Overall, the entire area should be filled with a gradient color which depends on the values of the curves.
I've assisted the following previous questions but could not resolve a solution
MATLAB fill area between lines
Patch circle by a color gradient
Filling between two curves, according to a colormap given by a function MATLAB
NOTE: there is no importance to the functional form of the curves, I would prefer an answer which refers to two general arrays which consist the curves.
The surf plot method
The same as the scatter plot method, i.e. generate a point grid.
y = [x_vec(:); y_vec(:)];
resolution = [500,500];
px = linspace(min(n_vec), max(n_vec), resolution(1));
py = linspace(min(y), max(y), resolution(2));
[px, py] = meshgrid(px, py);
Generate a logical array indicating whether the points are inside the polygon, but no need to extract the points:
in = inpolygon(px, py, N, X);
Generate Z. The value of Z indicates the color to use for the surface plot. Hence, it is generated using the your function cc.
pz = 1./(1+(exp(-py_)/(exp(-y_vec(i))-exp(-x_vec(i)))));
pz = repmat(pz',1,resolution(2));
Set Z values for points outside the area of interest to NaN so MATLAB won't plot them.
pz(~in) = nan;
Generate a bounded colourmap (delete if you want to use full colour range)
% generate colormap
c = jet(100);
[s,l] = bounds(pz,'all');
s = round(s*100);
l = round(l*100);
if s ~= 0
c(1:s,:) = [];
end
if l ~= 100
c(l:100,:) = [];
end
Finally, plot.
figure;
colormap(jet)
surf(px,py,pz,'edgecolor','none');
view(2) % x-y view
Feel free to turn the image arround to see how it looks like in the Z-dimention - beautiful :)
Full code to test:
i=50;
cc = #(xx,x,y) 1./(1+(exp(-xx)/(exp(-x)-exp(-y))));
n_vec = 2:0.1:10;
x_vec = linspace(2,10,length(n_vec));
y_vec = abs(sin(n_vec));
% generate grid
y = [x_vec(:); y_vec(:)];
resolution = [500,500];
px_ = linspace(min(n_vec), max(n_vec), resolution(1));
py_ = linspace(min(y), max(y), resolution(2));
[px, py] = meshgrid(px_, py_);
% extract points
in = inpolygon(px, py, N, X);
% generate z
pz = 1./(1+(exp(-py_)/(exp(-y_vec(i))-exp(-x_vec(i)))));
pz = repmat(pz',1,resolution(2));
pz(~in) = nan;
% generate colormap
c = jet(100);
[s,l] = bounds(pz,'all');
s = round(s*100);
l = round(l*100);
if s ~= 0
c(1:s,:) = [];
end
if l ~= 100
c(l:100,:) = [];
end
% plot
figure;
colormap(c)
surf(px,py,pz,'edgecolor','none');
view(2)
You can use imagesc and meshgrids. See comments in the code to understand what's going on.
Downsample your data
% your initial upper and lower boundaries
n_vec_long = linspace(2,10,1000000);
f_ub_vec_long = linspace(2, 10, length(n_vec_long));
f_lb_vec_long = abs(sin(n_vec_long));
% downsample
n_vec = linspace(n_vec_long(1), n_vec_long(end), 1000); % for example, only 1000 points
% get upper and lower boundary values for n_vec
f_ub_vec = interp1(n_vec_long, f_ub_vec_long, n_vec);
f_lb_vec = interp1(n_vec_long, f_lb_vec_long, n_vec);
% x_vec for the color function
x_vec = 0:0.01:10;
Plot the data
% create a 2D matrix with N and X position
[N, X] = meshgrid(n_vec, x_vec);
% evaluate the upper and lower boundary functions at n_vec
% can be any function at n you want (not tested for crossing boundaries though...)
f_ub_vec = linspace(2, 10, length(n_vec));
f_lb_vec = abs(sin(n_vec));
% make these row vectors into matrices, to create a boolean mask
F_UB = repmat(f_ub_vec, [size(N, 1) 1]);
F_LB = repmat(f_lb_vec, [size(N, 1) 1]);
% create a mask based on the upper and lower boundary functions
mask = true(size(N));
mask(X > F_UB | X < F_LB) = false;
% create data matrix
Z = NaN(size(N));
% create function that evaluates the color profile for each defined value
% in the vectors with the lower and upper bounds
zc = #(X, ub, lb) 1 ./ (1 + (exp(-X) ./ (exp(-ub) - exp(-lb))));
CData = zc(X, f_lb_vec, f_ub_vec); % create the c(x) at all X
% put the CData in Z, but only between the lower and upper bound.
Z(mask) = CData(mask);
% normalize Z along 1st dim
Z = normalize(Z, 1, 'range'); % get all values between 0 and 1 for colorbar
% draw a figure!
figure(1); clf;
ax = axes; % create some axes
sc = imagesc(ax, n_vec, x_vec, Z); % plot the data
ax.YDir = 'normal' % set the YDir to normal again, imagesc reverses it by default;
xlabel('n')
ylabel('x')
This already looks kinda like what you want, but let's get rid of the blue area outside the boundaries. This can be done by creating an 'alpha mask', i.e. set the alpha value for all pixels outside the previously defined mask to 0:
figure(2); clf;
ax = axes; % create some axes
hold on;
sc = imagesc(ax, n_vec, x_vec, Z); % plot the data
ax.YDir = 'normal' % set the YDir to normal again, imagesc reverses it by default;
% set a colormap
colormap(flip(hsv(100)))
% set alpha for points outside mask
Calpha = ones(size(N));
Calpha(~mask) = 0;
sc.AlphaData = Calpha;
% plot the other lines
plot(n_vec, f_ub_vec, 'k', n_vec, f_lb_vec, 'k' ,'linewidth', 1)
% set axis limits
xlim([min(n_vec), max(n_vec)])
ylim([min(x_vec), max(x_vec)])
there is no importance to the functional form of the curves, I would prefer an answer which refers to two general arrays which consist the curves.
It is difficult to achieve this using patch.
However, you may use scatter plots to "fill" the area with coloured dots. Alternatively, and probably better, use surf plot and generate z coordinates using your cc function (See my seperate solution).
The scatter plot method
First, make a grid of points (resolution 500*500) inside the rectangular space bounding the two curves.
y = [x_vec(:); y_vec(:)];
resolution = [500,500];
px = linspace(min(n_vec), max(n_vec), resolution(1));
py = linspace(min(y), max(y), resolution(2));
[px, py] = meshgrid(px, py);
figure;
scatter(px(:), py(:), 1, 'r');
The not-interesting figure of the point grid:
Next, extract the points inside the polygon defined by the two curves.
in = inpolygon(px, py, N, X);
px = px(in);
py = py(in);
hold on;
scatter(px, py, 1, 'k');
Black points are inside the area:
Finally, create color and plot the nice looking gradient colour figure.
% create color for the points
cid = 1./(1+(exp(-py)/(exp(-y_vec(i))-exp(-x_vec(i)))));
c = jet(101);
c = c(round(cid*100)+1,:); % +1 to avoid zero indexing
% plot
figure;
scatter(px,py,16,c,'filled','s'); % use size 16, filled square markers.
Note that you may need a fairly dense grid of points to make sure the white background won't show up. You may also change the point size to a bigger value (won't impact performance).
Of cause, you may use patch to replace scatter but you will need to work out the vertices and face ids, then you may patch each faces separately with patch('Faces',F,'Vertices',V). Using patch this way may impact performance.
Complete code to test:
i=50;
cc = #(xx,x,y) 1./(1+(exp(-xx)/(exp(-x)-exp(-y))));
n_vec = 2:0.1:10;
x_vec = linspace(2,10,length(n_vec));
y_vec = abs(sin(n_vec));
% generate point grid
y = [x_vec(:); y_vec(:)];
resolution = [500,500];
px_ = linspace(min(n_vec), max(n_vec), resolution(1));
py_ = linspace(min(y), max(y), resolution(2));
[px, py] = meshgrid(px_, py_);
% extract points
in = inpolygon(px, py, N, X);
px = px(in);
py = py(in);
% generate color
cid = 1./(1+(exp(-py)/(exp(-y_vec(i))-exp(-x_vec(i)))));
c = jet(101);
c = c(round(cid*100)+1,:); % +1 to avoid zero indexing
% plot
figure;
scatter(px,py,16,c,'filled','s');

putting an image in the XY plane of a matlab 3D scatter graph

I am trying to put an image on the XY plane of a 3D scattergraph. The current version that I have is giving some results but the image's colours is not showing properly.
Currently, I read the image as a grayscale in, but ideally I would like to have my points above a colour image.
x = [1,2,3,4,5,6,7,8,9,10,11,1,2,3,4,5,6,7,8,9,10,11];
y = [0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1];
z = [0,0,0,1,2,3,2,1,0,0,0,0,0,1,2,3,2,1,0,0,0,0];
min_x = min(min(x));
min_y = min(min(y));
max_x = max(max(x));
max_y = max(max(y));
planeimg = rgb2gray(imread('1.jpg'));
figure; hold on;
scatter3(x,y,z);
colormap(gray);
minplaneimg = min(min(planeimg)); % find the minimum
scaledimg = (floor(((planeimg - minplaneimg) ./ ...
(max(max(planeimg)) - minplaneimg)) * 255)); % perform scaling
imgzposition = -10;
colorimg = ind2rgb(scaledimg,jet(256));
surf([min_x max_x],[min_y max_y],repmat(imgzposition, [2 2]),...
colorimg,'facecolor','texture')
view(45,30);
xlabel('x');
ylabel('y');
zlabel('z');

Plot bivariate map in Matlab

I have two maps (matrices), and want to use them to generate the corresponding bivariate map.
The below code illustrates the idea using 'scatter', but because my original matrices are quite large, I need a different solution than to draw each point individually.
red_blue_colormap = brewermap(20,'RdYlBu'); %using ColorBrewer colormaps 'brewermap'
white2red = newmap(10:-1:1,:); %color map going from white to red
white2blue = newmap(11:20,:); %color map going from white to blue
X = rand(13,8); Y = rand(13,8); %sample matrices
figure;
for i=1:size(X,1)
for j=1:size(X,2)
portion_red = white2red(ceil(X(i,j)*10),:); %value between 'white' and 'red' corresponding to value of X(i,j)
portion_blue = white2blue(ceil(Y(i,j)*10),:);
subplot(2,2,1); hold on; title('X');
scatter(i,j,100,portion_red ,'s','filled');
subplot(2,2,2); hold on; title('Y');
scatter(i,j,100,portion_blue ,'s','filled');
subplot(2,2,3); hold on; title('X vs. Y');
w1 = Y(i,j)/(X(i,j) + Y(i,j)); %relative weight of 'Y'
color1 = portion_red - ((portion_red - portion_blue ) * w1);
scatter(i,j,100,color1,'s','filled');
subplot(2,2,4); hold on; title('Color matrix');
portion_red = white2red(ceil(i/size(X,1)*10),:);
portion_blue = white2blue(ceil(j/size(X,2)*10),:);
w2 = (j/size(X,2))/(i/size(X,1) + j/size(X,2));
color2 = portion_red - ((portion_red - portion_blue ) * w2);
scatter(i,j,100,color2,'s','filled');
end
end
This generates the following figure: https://image.ibb.co/kotP7m/bivariate_map.png
I want to plot the bottom left figure in a more efficient way. Any ideas?

Placing picture on axis of MATLAB figure

I want to make animation of ball (given by the picture here) which starts from origin and goes through a track given by x-vector, y-vector, z-vector (each of nX1).I know I need to use the getframe command but I don't know how to move the picture on the axis. I know that I can put a picture in one of the corner by defining new axis, e.g (exmaple taken from MATLAB offical forum):
numberOfDataPoints = 200;
sampleData = 100*rand(1,numberOfDataPoints);
plot(sampleData);
xlim([1, numberOfDataPoints]);
hold on;
plot(sampleData);
xlim([1, numberOfDataPoints]);
axes1Position = get(gca, 'Position');
logoSizeX = 0.1;
logoSizeY = 0.1;
% Position the logo in the upper right.
x1 = axes1Position(1) + axes1Position(3) - logoSizeX;
y1 = axes1Position(2) + axes1Position(4) - logoSizeY;
hAxis2 = axes('Position', [x1 y1 logoSizeX logoSizeY]);
axis off;
imshow(ball.jpeg);
but since I don't want to create seperate axis, this does not help. How can I define movement of my ball on a given axis?
You can move the object by storing the handle returned by the image drawing function and setting its 'XData', 'YData', and 'ZData' properties. Here is a little example; this example uses warp to draw the image on a spherical surface (generated using sphere), and then moves it around a random path.
close all;
% Load image
[img, imgMap] = imread('peppers.png');
sphereImgSize = min(size(img, 1), size(img, 2));
sphereImg = img(1:sphereImgSize, 1:sphereImgSize, :);
% Generate sphere vertices
[X, Y, Z] = sphere(sphereImgSize);
lims = [-10 10];
figure;
axes;
hImg = warp(X, Y, Z, sphereImg); % NOTE: Store handle returned
xlim(lims);
ylim(lims);
zlim(lims);
axis square;
% Set up movement path
nFrames = 100;
randPathFun = #()rand(nFrames, 1) * diff(lims) + lims(1);
pathX = randPathFun();
pathY = randPathFun();
pathZ = randPathFun();
% Move the image by setting its 'XData' and 'YData' properties
for ii = 1:nFrames
xData = X + pathX(ii);
yData = Y + pathY(ii);
zData = Z + pathZ(ii);
set(hImg, 'XData', xData, 'YData', yData, 'ZData', zData);
pause(0.1);
end