I have wrote a program using Level Set Function for curve propagation. Curve is represented by level zero in contour plot. How can I get the coordinates of these curve as I march in time?
Contour are obtained at every time step. How to get the coordinates at each step?
A zero level contour from a set of points z(x,y) can be obtained by contourc. For instance, for the peaks-example of MATLAB, we obtain the contour lines with height 0 as follows:
c = contourc(peaks, [0,0]);
If this contour is a single line, this is all you need to do. However, the contour might consists of multiple islands (which is the case for this example), which means that you need to split the set of (x,y) coordinates in c somehow. A split based on NaN values can be obtained through:
c(:,~c(1,:)) = NaN;
and the result can be plot by
plot(c(1,:),c(2,:),'k');
If this is not sufficient, you could also construct a cell array c_split of islands:
b = find(~c(1,:))+1;
e = b+c(2,b-1)-1;
c_split = arrayfun(#(idx) c(:,b(idx):e(idx)), 1:numel(b), 'uni', 0)';
and the visualisation is very similar:
idx = 1;
plot(c_split{idx}(1,:),c_split{idx}(2,:),'k');
If the contours need to be stored for multiple time steps, you combine all results into a cell:
c_all{idx} = c;
Related
I suppose this is not something difficult but i wonder if there is any function or any optimal way.
Consider that after an image process i have a matrix-image with 0 everywhere and 1 at the contour.
Now i want to find the x y along that contour line
The important is that eg [ x(2) y(2) ] should be the next pixel to [x(1) y(1)]
I have used this:
[c h]=contour(image,1)
x=c(1,:)
y=c(2,:)
But the result is not very good and it gives some noise points which is very bad (and for some reason it appears mirrored)
If you have the image processing toolbox, I highly suggest using bwperim which returns a logical image where true is a perimeter or contour point and false otherwise.... not to mention that it's faster.
Try doing:
bw = bwperim(image == 1); % image == 1 to ensure binary
[y, x] = find(bw);
The first line of code finds an image that only contains contour points, and we can use find after that on the result to find the row and column locations. Here y represents the row and x represents the column locations.
If you desire that the contour is ordered, then use the bwtraceboundary function instead. However, this will require that you specify an initial contour point prior to running the function.
An easy way to do this would be to find any non-zero point along the contour of your object. You could use bwperim first and sample any point from here. Choosing just any point may not give you an actual contour point.
Therefore:
bw = bwperim(image == 1); % image == 1 to ensure binary
[y, x] = find(bw, 1); % Find the first contour point
ctr = bwtraceboundary(image == 1, [y, x], 'SE');
'SE' is the direction of where to look first given the initial contour point. Here I've chosen south east. This will produce a N x 2 matrix where the first column contains the rows and second column contains the columns of the ordered contour points starting at the initial position provided by y and x.
I have posted my complete solution to help other people:
Problem:
I have a grayscale image and i want to find the coordinates X Y in order along the contour .
Solution:
Set a threshold for black and white and make the image binary (optional)
`image=image>0.5 %This is optional but some may found it usefull`
Find the Start Point:
[yStart xStart]=find(image,1);
This will scan the image column by column from left to right and up to down and will return the first non zero pixel. So this will return the 'left-est up' pixel of the image. Remember, X is the column and Y is the row. Zero is at the top-left corner!
Find the contour:
contour=bwtraceboundary(image, [yStart, xStart],'NE');
or
contour = bwtraceboundary(image,[yStart xStart],'NE',8,Inf,'clockwise');
NE is the initial direction (NorthEast)
x=contour(:,2)
y=contour(:,1)
If the point [yStart xStart] is not on the contour of an image this will not work !
If you plot (x,y) that will be mirrored. This is because the zero at the coordinate system is at the top left corner of the image and not at the bottom left. To do it properly you can do this:
y=-y
y=y+abs(min(y))+1 % +1 is to avoid y=0
When plotting streamlines in MATLAB using quiver, streamslice or similar, is it possible to extract the contour line at given contour level?
Take this example (I have numerical data in my case, but I will use analytical functions in the example):
[X,Y] = meshgrid(0:.02:1);
Z = X.*exp(-X.^2 - Y.^2);
[DX,DY] = gradient(Z,.2,.2);
figure
imagesc([0 1], [0 1], Z)
hold on
streamslice(X,Y,DX,DY) %how to extract a trajectory at a given contour level C?
hold off
colorbar
If not, is it possible to obtain them otherwise? I was thinking of using contour in this way,
contour(X,Y,sqrt(DX.*DX+DY.*DY), [1 1]*0.07)
but this is clearly wrong when I compare to the streamlines above.
hs = streamslice(X,Y,DX,DY); as result you obtain a vector with handles to the traject lines. For example, you can get the coordinates of the first traject line:
N_trajects = length(hs); % the number of all trajectories
n = 1; % the chosen trajectory
X_traject = get(hs(n),'XData');
Y_traject = get(hs(n),'YData');
or using new version of MATLAB:
X_traject = hs(n).XData;
Y_traject = hs(n).YData;
To extract countour lines data:
C = contour(X,Y,sqrt(DX.*DX+DY.*DY), [1 1]*0.07);
where C consits data with the contour lines. In your case there is one contour line only (X_contour = C(1,:), Y_contour = C(2,:)). In the case of many contour levels, to extract them see here or here or using this.
Now we know the coordinates of the trajectory and contour level. Thus you can find the point(s) of intersection between the trajectory and the contour level.
I have a number of 2d probability mass functions from 2 categories. I am trying to plot the contours to visualise them (for example at their half height, but doesn't really matter).
I don't want to use contourf to plot directly because I want to control the fill colour and opacity. So I am using contourc to generate xy coordinates, and am then using fill with these xy coordinates.
The problem is that the xy coordinates from the contourc function have strange numbers in them which cause the following strange vertices to be plotted.
At first I thought it was the odd contourmatrix format, but I don't think it is this as I am only asking for one value from contourc. For example...
contourmatrix = contourc(x, y, Z, [val, val]);
h = fill(contourmatrix(1,:), contourmatrix(2,:), 'r');
Does anyone know why the contourmatrix has these odd values in them when I am only asking for one contour?
UPDATE:
My problem seems might be a failure mode of contourc when the input 2D matrix is not 'smooth'. My source data is a large set of (x,y) points. Then I create a 2D matrix with some hist2d function. But when this is noisy the problem is exaggerated...
But when I use a 2d kernel density function to result in a much smoother 2D function, the problem is lessened...
The full process is
a) I have a set of (x,y) points which form samples from a distribution
b) I convert this into a 2D pmf
c) create a contourmatrix using contourc
d) plot using fill
Your graphic glitches are because of the way you use the data from the ContourMatrix. Even if you specify only one isolevel, this can result in several distinct filled area. So the ContourMatrix may contain data for several shapes.
simple example:
isolevel = 2 ;
[X,Y,Z] = peaks ;
[C,h] = contourf(X,Y,Z,[isolevel,isolevel]);
Produces:
Note that even if you specified only one isolevel to be drawn, this will result in 2 patches (2 shapes). Each has its own definition but they are both embedded in the ContourMatrix, so you have to parse it if you want to extract each shape coordinates individually.
To prove the point, if I simply throw the full contour matrix to the patch function (the fill function will create patch objects anyway so I prefer to use the low level function when practical). I get the same glitch lines as you do:
xc = X(1,:) ;
yc = Y(:,1) ;
c = contourc(xc,yc,Z,[isolevel,isolevel]);
hold on
hp = patch(c(1,1:end),c(2,1:end),'r','LineWidth',2) ;
produces the same kind of glitches that you have:
Now if you properly extract each shape coordinates without including the definition column, you get the proper shapes. The example below is one way to extract and draw each shape for inspiration but they are many ways to do it differently. You can certainly compact the code a lot but here I detailed the operations for clarity.
The key is to read and understand how the ContourMatrix is build.
parsed = false ;
iShape = 1 ;
while ~parsed
%// get coordinates for each isolevel profile
level = c(1,1) ; %// current isolevel
nPoints = c(2,1) ; %// number of coordinate points for this shape
idx = 2:nPoints+1 ; %// prepare the column indices of this shape coordinates
xp = c(1,idx) ; %// retrieve shape x-values
yp = c(2,idx) ; %// retrieve shape y-values
hp(iShape) = patch(xp,yp,'y','FaceAlpha',0.5) ; %// generate path object and save handle for future shape control.
if size(c,2) > (nPoints+1)
%// There is another shape to draw
c(:,1:nPoints+1) = [] ; %// remove processed points from the contour matrix
iShape = iShape+1 ; %// increment shape counter
else
%// we are done => exit while loop
parsed = true ;
end
end
grid on
This will produce:
Im just trying to draw a line through the following points in matlab. Currently the line extends only to the points. I need to to extend and intercept the x axis. The code is below
A = [209.45 198.066 162.759];
B = [1.805 1.637 1.115];
plot(A,B,'*');
axis([0 210 0 2]);
hold on
line(A,B)
hold off
If you want to augment your points with a corresponding y==0 point, I suggest using interp1 to obtain the x-intercept:
A = [209.45 198.066 162.759];
B = [1.805 1.637 1.115];
x0 = interp1(B,A,0,'linear','extrap'); %extrapolate (y,x) at y==0 to get x0
[newA, inds] = sort([x0 A]); %insert x0 where it belongs
newB = [0 B];
newB = newB(inds); %keep the same order with B
plot(A,B,'b*',newA,newB,'b-');
This will use interp1 to perform a linear interpolant, with extrapolation switched on. By interpolating (B,A) pairs, we in effect invert your linear function.
Next we add the (x0,0) point to the data, but since matlab draws lines in the order of the points, we have to sort the vector according to x component. The sorting order is then used to keep the same order in the extended B vector.
Finally the line is plotted. I made use of plot with a linespec of '-' to draw the line in the same command as the points themselves. If it doesn't bother you that the (x0,0) point is also indicated, you can plot both markers and lines together using plot(newA,newB,'*-'); which ensures that the colors match up (in the above code I manually set the same blue colour on both plots).
I am having difficulty with calculating 2D area of contours produced from a Kernel Density Estimation (KDE) in Matlab. I have three variables:
X and Y = meshgrid which variable 'density' is computed over (256x256)
density = density computed from the KDE (256x256)
I run the code
contour(X,Y,density,10)
This produces the plot that is attached. For each of the 10 contour levels I would like to calculate the area. I have done this in some other platforms such as R but am having trouble figuring out the correct method / syntax in Matlab.
C = contourc(density)
I believe the above line would store all of the values of the contours allowing me to calculate the areas but I do not fully understand how these values are stored nor how to get them properly.
This little script will help you. Its general for contour. Probably working for contour3 and contourf as well, with adjustments of course.
[X,Y,Z] = peaks; %example data
% specify certain levels
clevels = [1 2 3];
C = contour(X,Y,Z,clevels);
xdata = C(1,:); %not really useful, in most cases delimters are not clear
ydata = C(2,:); %therefore further steps to determine the actual curves:
%find curves
n(1) = 1; %n: indices where the certain curves start
d(1) = ydata(1); %d: distance to the next index
ii = 1;
while true
n(ii+1) = n(ii)+d(ii)+1; %calculate index of next startpoint
if n(ii+1) > numel(xdata) %breaking condition
n(end) = []; %delete breaking point
break
end
d(ii+1) = ydata(n(ii+1)); %get next distance
ii = ii+1;
end
%which contourlevel to calculate?
value = 2; %must be member of clevels
sel = find(ismember(xdata(n),value));
idx = n(sel); %indices belonging to choice
L = ydata( n(sel) ); %length of curve array
% calculate area and plot all contours of the same level
for ii = 1:numel(idx)
x{ii} = xdata(idx(ii)+1:idx(ii)+L(ii));
y{ii} = ydata(idx(ii)+1:idx(ii)+L(ii));
figure(ii)
patch(x{ii},y{ii},'red'); %just for displaying purposes
%partial areas of all contours of the same plot
areas(ii) = polyarea(x{ii},y{ii});
end
% calculate total area of all contours of same level
totalarea = sum(areas)
Example: peaks (by Matlab)
Level value=2 are the green contours, the first loop gets all contour lines and the second loop calculates the area of all green polygons. Finally sum it up.
If you want to get all total areas of all levels I'd rather write some little functions, than using another loop. You could also consider, to plot just the level you want for each calculation. This way the contourmatrix would be much easier and you could simplify the process. If you don't have multiple shapes, I'd just specify the level with a scalar and use contour to get C for only this level, delete the first value of xdata and ydata and directly calculate the area with polyarea
Here is a similar question I posted regarding the usage of Matlab contour(...) function.
The main ideas is to properly manipulate the return variable. In your example
c = contour(X,Y,density,10)
the variable c can be returned and used for any calculation over the isolines, including area.