Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 4 years ago.
Improve this question
Question, I have a line data. However I wanted to be plotted with a 3D effect, which you can also choose as an option, for instance, within Office Excel/Powerpoint.
Currently I have the code below which kind of mimmics the effect (simply putting a lot of plots after each other):
Excel:
Matlab:
Matlab code:
x = 1:10;
y = rand(1, 10);
z = rand(1, 10);
figure;
hold on;
for i = 1:20
hFill = fill3(i*0.01*ones(1, 12), x([1 1:end end]), [0 y 0], 'b', 'FaceAlpha', 0.5);
hFill = fill3((i*0.01*(ones(1, 12)))+2, x([1 1:end end]), [0 z 0], 'g', 'FaceAlpha', 0.5);
end
grid on;
xlim([0 10]);
view(3);
You could probably construct a closed surface in one single graphic object to represent your 3D curve, however I don't have time to work that out completely so I'll go the lazy way:
Instead of stacking multiple fill objects to give a 3d feeling, I build each curve with 3 objects:
A top surface, with the desired thickness
2 patch objects to close the sides
Here goes:
%% Sample data
rng(12)
x = 1:10;
y = rand(1, 10) + .5 ;
z = rand(1, 10) + .5 ;
%% Parameters
alphaTop = .5 ; % alpha value of the top of the surface
alphaSide = .5 ; % alpha value of the sides
thickness = .5 ; % thickness of each curve
separation = 1 ; % separation of each curve
colors = {'b';'r'} ; % color for each curve
%% prepare patch coordinates
xp = x([1 1:end end 1]) ;
yp1 = [0 y 0 0] ;
yp2 = [0 z 0 0] ;
zp1 = zeros(size(yp1)) ;
zp2 = zeros(size(yp2)) + 1 ;
%% Prepare surface coordinates
xs = [ xp; xp] ;
ys1 = [yp1;yp1] ;
ys2 = [yp2;yp2] ;
zs1 = zeros(size(xs)) ;
zs1(2,:) = zs1(2,:) + thickness ;
zs2 = zs1 + separation ;
%% Display
figure
hold on
% plot the sides (one patch on each side of each curve)
hp11 = patch(zp1 ,xp,yp1, colors{1} , 'FaceAlpha', alphaSide) ;
hp12 = patch(zp1+thickness,xp,yp1, colors{1} , 'FaceAlpha', alphaSide) ;
hp21 = patch(zp2 ,xp,yp2, colors{2} , 'FaceAlpha', alphaSide) ;
hp22 = patch(zp2+thickness,xp,yp2, colors{2} , 'FaceAlpha', alphaSide) ;
% plot the top surfaces
hs1 = surf(zs1,xs,ys1, 'FaceColor',colors{1},'FaceAlpha',alphaTop) ;
hs2 = surf(zs2,xs,ys2, 'FaceColor',colors{2},'FaceAlpha',alphaTop) ;
% refine plot
xlim([0 10]);ylim([0 10]); view(3);
xlabel('X') ; ylabel('Y') ; zlabel('Z') ;
Which yields:
Once this is built, you can regroup the graphic handles to group common properties assignments. For example:
%% Optional (modify common properties in group)
% regroup graphic handles for easy common property assignment
hg1 = [hp11;hp12;hs1] ;
hg2 = [hp21;hp22;hs2] ;
% set properties in group
set(hg1,'EdgeColor',colors{1},'FaceAlpha',0.2) ;
set(hg2,'EdgeColor',colors{2},'FaceAlpha',0.2) ;
To give your curves a nice transparent mesh style:
Ultimately, if you plan to apply this method to many curves, you should either package it in a function, or at least build your curves with a loop. It should be easy to convert as long as the parameters for each curve are in an array you can index into (like I did for the colors).
Related
I created the following 3d plot in MATLAB using the function plot3:
Now, I want to get a hatched area below the "2d sub-graphs" (i.e. below the blue and red curves). Unfortunately, I don't have any idea how to realize that.
I would appreciate it very much if somebody had an idea.
You can do this using the function fill3 and referencing this answer for the 2D case to see how you have to add points on the ends of your data vectors to "close" your filled polygons. Although creating a pattern (i.e. hatching) is difficult if not impossible, an alternative is to simply adjust the alpha transparency of the filled patch. Here's a simple example for just one patch:
x = 1:10;
y = rand(1, 10);
hFill = fill3(zeros(1, 12), x([1 1:end end]), [0 y 0], 'b', 'FaceAlpha', 0.5);
grid on
And here's the plot this makes:
You can also create multiple patches in one call to fill3. Here's an example with 4 sets of data:
nPoints = 10; % Number of data points
nPlots = 4; % Number of curves
data = rand(nPoints, nPlots); % Sample data, one curve per column
% Create data matrices:
[X, Y] = meshgrid(0:(nPlots-1), [1 1:nPoints nPoints]);
Z = [zeros(1, nPlots); data; zeros(1, nPlots)];
patchColor = [0 0.4470 0.7410]; % RGB color for patch edge and face
% Plot patches:
hFill = fill3(X, Y, Z, patchColor, 'LineWidth', 1, 'EdgeColor', patchColor, ...
'FaceAlpha', 0.5);
set(gca, 'YDir', 'reverse', 'YLim', [1 nPoints]);
grid on
And here's the plot this makes:
There are many questions already on this site for something similar:
MATLAB, Filling in the area between two sets of data, lines in one figure
MATLAB fill area between lines
However, all of the existing questions relate to two curves only. How do you fill a region bounded by several curves that overlap each other?
A crude example would be:
% Create sample data as column vectors.
x = [1 : 100]';
curve1 = x/10;
curve2 = log(x/2) + rand(length(x), 1) - 0.5;
curve3 = log(x) + rand(length(x), 1) + 0.5;
% Plot it.
plot(x, curve1, 'r', 'LineWidth', 2);
hold on;
plot(x, curve2, 'b', 'LineWidth', 2);
plot(x, curve3, 'k', 'LineWidth', 2);
For the shading:
The upper limit would be the black curve followed by the red line.
The lower limit would be the blue curve (briefly), then the red line, followed by the blue curve.
In my actual dataset I have 10 curves which require a similar thing.
If I understand you correctly, you can do this by creating a min and max vectors of the area you want to shade, and use flipud to shade the region with fill
min_data=min([curve1,curve2,curve3],[],2);
max_data=max([curve1,curve2,curve3],[],2);
fill([x;flipud(x)],[min_data;flipud(max_data)],'g')
If I understood you correctly:
basevalue = min([curve1(:) ; curve2(:) ; curve3(:)]);
h = area([curve2 , curve1-curve2 , curve3-curve1],basevalue)
h(1).FaceColor = [1 1 1];
h(2).FaceColor = [0 0.5 0.5];
h(3).FaceColor = [1 1 1];
hold on
plot(x, curve1, 'r', 'LineWidth', 2);
plot(x, curve2, 'b', 'LineWidth', 2);
plot(x, curve3, 'k', 'LineWidth', 2);
ylim([ min([curve1(:) ; curve2(:) ; curve3(:)]); max([curve1(:) ; curve2(:) ; curve3(:)])])
So you need to play with area in a way that is consistent with what you want...
I have the following figure, where I plotted two surfaces and I wanted to indicate the intersection of both of them. To do that, I did the following:
zdiff = z1-z2;
C = contours(x,y,zdiff,[0 0]);
xL = C(1, 2:end);
yL = C(2, 2:end);
zL = interp2(x, y, z1, xL, yL);
line(xL, yL, zL, 'Color', 'k', 'LineWidth', 2,'Linestyle','--'); hold on;
line(xL, yL, zeros(size(zL)), 'Color', 'k', 'LineWidth', 2); hold off;
Now, I want to plot the vertical surface between the actual intersection (dash line) and its projection over XY (solid line), but I cannot figure out how to do that. Any ideas?
Another really simple option:
dist = (diff(xL).^2+diff(yL).^2).^0.5; %distance between x,y
cdist = [0, cumsum(dist)]; %cumsum of the distance
area = trapz(cdist,zL); %The area
Why not calculating it manually?
Something like (untested):
Area = 0
for i=1:numel(xL)-1
base = sqrt( (xL(i)-xL(i+1))^2 + (yL(i)-yL(i+1))^2);
Area =Area + base * (zL(i) + zL(i+1))/2;
end;
maybe not pretty but its a oneliner it might do the trick. maybe you have to adjust the format as this code is for (1,N) vectors
xL=(1:100); %size 1 100
yL=(1:100) ;%size 1 100
zL=rand(1,100);%size 1 100
line(xL,yL,zL)
line(xL,yL,zeros(size(zL)))
hold on
surf(repmat(xL,100,1),repmat(yL,100,1),cell2mat(arrayfun(#(x,y) linspace(x,y,100)',zL,zeros(size(zL)),'UniformOutput',false)))
xL=sin((1:30)/10); % Data generation for test only. Use your data
yL=cos((1:30)/10); % Data generation for test only. Use your data
zL=2+xL.*yL; % Data generation for test only. Use your data
surf([xL;xL],[yL;yL],[zeros(size(zL));zL]); % plot the surface
I'm trying to detect lines in a grayscale image. For that purpose, I'm using Radon transform in MATLAB. An example of my m-file is like below. I can detect multiple lines using this code. I also draw lines using shift and rotation properties for lines. However, I didn't understand how to get the start and end points of the detecting lines after getting rho and theta values.
It is easy for Hough transform since there is a function called houghlines() that returns the list of the lines for the given peaks. Is there any function that i can use for Radon transform similar to this function?
% Radon transform line detection algorithm
clear all; close all;
% Determine the path of the input image
str_inputimg = '3_lines.png' ;
% Read input image
I = imread(str_inputimg) ;
% If the input image is RGB or indexed color, convert it to grayscale
img_colortype = getfield(imfinfo(str_inputimg), 'ColorType') ;
switch img_colortype
case 'truecolor'
I = rgb2gray(I) ;
case 'indexedcolor'
I = ind2gray(I) ;
end
figure;
subplot(2,2,1) ;
imshow(I) ;
title('Original Image') ;
% Convert image to black white
%BW = edge(I,'Sobel');
BW=im2bw(I,0.25) ;
subplot(2,2,2) ;
imshow(BW);
title('BW Image') ;
% Radon transform
% Angle projections
theta = [0:179]' ;
[R, rho] = radon(BW, theta) ;
subplot(2,2,3) ;
imshow(R, [], 'XData', theta, 'YData', rho, 'InitialMagnification', 'fit');
xlabel('\theta'), ylabel('\rho');
axis on, axis normal, hold on;
% Detect the peaks of transform output
% Threshold value for peak detection
threshold_val = ceil(0.3*max(R(:))) ;
% Maximum nof peaks to identify in the image
max_nofpeaks = 5 ;
max_indexes = find(R(:)>threshold_val) ;
max_values = R(max_indexes) ;
[sorted_max, temp_indexes] = sort(max_values, 'descend') ;
sorted_indexes = max_indexes(temp_indexes) ;
% Get the first highest peaks for the sorted array
if (length(sorted_max) <= max_nofpeaks)
peak_values = sorted_max(1:end) ;
peak_indexes = sorted_indexes(1:end) ;
else
peak_values = sorted_max(1:max_nofpeaks) ;
peak_indexes = sorted_indexes(1:max_nofpeaks) ;
end
[y, x] = ind2sub(size(R), peak_indexes ) ;
peaks = [rho(y) theta(x)] ;
plot(peaks(:,2), peaks(:,1), 's', 'color','white');
title('Radon Transform & Peaks') ;
% Detected lines on the image
subplot(2,2,4), imshow(I), title('Detected lines'), hold on
x_center = floor(size(I, 2)/2) ;
y_center = floor(size(I, 1)/2) ;
for p=1:length(peaks)
x_1 = [-x_center, x_center] ;
y_1 = [0, 0] ;
% Shift at first
x_1_shifted = x_1 ;
y_1_shifted = [y_1(1)-peaks(p,1), y_1(2)-peaks(p,1)] ;
% Rotate
peaks(p,2) = 90 - peaks(p,2) ;
t=peaks(p,2)*pi/180;
rotation_mat = [ cos(t) -sin(t) ; sin(t) cos(t) ] ;
x_y_rotated = rotation_mat*[x_1_shifted; y_1_shifted] ;
x_rotated = x_y_rotated(1,:) ;
y_rotated = x_y_rotated(2,:) ;
plot( x_rotated+x_center, y_rotated+y_center, 'b', 'linewidth', 2 );
end
hold off;
There's a suggestion at math.SE which might help. Then there's a rather complicated-looking research paper "Sharp endpoint estimates for the X-ray transform and the Radon
transform in finite fields", which appears just to show certain bounds on estimation accuracy.
From skimming other papers, it appears that it's a nontrivial problem. I suspect it may be simpler (if less accurate) to use some adaptation of a Sobel-operation to identify high gradient points along the discovered line, and claim those as endpoints.
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 7 years ago.
Improve this question
I am going to draw a figure such as below picture in the MATLAB R2014b: .
This figure consists of many circles with different (random) colors and random sizes.
How is it possible to plot such this figure in MATLAB R2014b?
Without spelling out the code:
Pick initial circle, e.g. Position [0,0] and radius 1.
Initialise list for positions and radii.
Pick random position and radius r.
If circle is not in big one (I.e. sqrt(pos(1)^2+pos(2)^2) + r > 1) continue with 3.
If overlap with other circles (distance between positions > sum of radii), continue with 3
Add circle to list, continue with 3
Update: Example
Alright, so I just wanted to try this. I'm sure this is not the best implementation, but:
% set number of circles to plot
n = 200;
radii = zeros(n, 1);
pos = zeros(n, 2);
allColours = lines(n);
% main loop
for idx = 1:n
is_good = false;
% generate random positions and radii until we have a hit
while ~is_good
pos(idx, :) = rand(1, 2)*2 - 1;
radii(idx) = rand * (1 - max(radii));
if ((sqrt(sum(pos(idx, :).^2)) + radii(idx) ) < 1) ... % ensure we're inside the big circle
&& ((idx == 1) || ... % and either it's the first circle, or
all(sqrt(sum((pos(1:(idx-1), :) - repmat(pos(idx, :), idx-1, 1)).^2, 2)) > radii(1:(idx-1))+radii(idx))) % all distances are bigger than sum of radii of existing circles
is_good = true;
end
end
end
%% plot
figure(2);
clf;
hold on
set(gca, 'visible', 'off')
daspect([1, 1, 1])
rectangle(...
'Position',[-1 -1 2 2],...
'Curvature', [1 1],...
'FaceColor', 'none',...
'EdgeColor', [ 0, 0, 0]);
for idx = 1:n
rectangle(...
'Position',[pos(idx, 1) - radii(idx), pos(idx, 2) - radii(idx), 2*radii(idx), 2*radii(idx)],...
'Curvature', [1 1],...
'EdgeColor','none',...
'FaceColor', allColours(idx,:));
end
The general idea is below. You'll need to modify it to ensure the circle centers and colors are chosen to suit your particular purpose.
% Define parameters
maxAxis = 100;
maxRadius = 10;
nCircles = 20;
% Random centres
xLoc = randi(maxAxis,nCircles);
yLoc = randi(maxAxis,nCircles);
% Random radii
radius = randi(maxRadius,nCircles);
% Random colours
allColours = rand(nCircles,3);
% Transform the data into position = [left bottom width height]
pos = [xLoc(:)-radius(:) yLoc(:)-radius(:) 2*radius(:)*[1 1]];
% Create and format the axes
ha = axes;
hold on;
axis equal;
box on;
set(ha,'XTickLabel',[],'YTickLabel',[]);
% Create the circles (must be done in loop)
for idx = 1:nCircles
rectangle(...
'Position',pos(idx,:),...
'Curvature',[1 1],...
'FaceColor',allColours(idx,:),...
'EdgeColor','none');
end
See
>> doc retangle
for more info.