How can I compute a contour bounded by the combination of multiple contour lines?
Ältere Kommentare anzeigen
I wrote a Matlab script that computes about a hundred of contour lines which all have the same general shape but are very slightly different in dimensions (and they can sometimes intersect). You can imagine concentric circles, except that the contours don't have a regular geometric shape and can often overlap each other. I would like to extract the contour of the 'blank space' that remains inside of this chaos:

The contour lines come from Matlab's "ContourMatrix" with this code:
[~,hcontour] = contour(x,y,z,'LevelList',k);
c = hcontour.ContourMatrix;
I then wrote a loop that selects the relevant contour lines and stores them in a matrix where the first three lines contain the X,Y and Z coordinates for the first contour line, then the next three lines contain the coordinates for the second line etc...
When I plot the outcome, the problem is that no single line is entirely contained within all the others, so I can't just compute the area of all the lines and see which is the smallest. I need to compute the boundary that only contains blank space and doesn't cross any of the contour lines.
Thanks in advance!
6 Kommentare
Wick
am 2 Mai 2018
Antoine,
I've spent some time thinking about this problem because it irked me. The trouble is, you're going to have to define some threshold for what constitutes "inside" the profile.
When defining the exterior hull of a field of points it's easy to define "outside" because you can declare that no segment between any two points in the cloud crosses your outside hull.
On the inside, you can always cut across any interior shape and end up eliminating part of the area. In your figure above, if you put a vertical line across the region where it necks down you could cut off the tail to the left. That seems obviously wrong, but when you zoom in at the spacing-between-points level, how do you decide when you should make a high aspect ratio triangle that goes further out or skip across to the next point? I don't think there's a solution that doesn't involve some sort of threshold or smoothing to accomplish what you want.
If you're willing to smooth the set of points, I'd recommend using a 2D kernel estimator to approximate a distribution function that generates your points. You can then take a contour of that function (or of its gradient or del2) at whatever level you decide is a good threshold.
I have had good luck using Botev's kde2d code for such purposes. If you upload a set of example contour values I can help you run it through this code if you'd like.
Antoine Bridet
am 2 Mai 2018
Wick
am 2 Mai 2018
Actually, I think a kernel estimation may work well to define a loci of points to use as origins in the inverse radius idea below. The kernel should bleed into the shape you want to keep. Then you can cycle through the process below and find local boundaries for each starting point not worrying about whether part of the shape is cut off. That will be reached from other starting points.
If you send some data I'm sure I can whip that together in no time flat.
Antoine Bridet
am 3 Mai 2018
Yes, if you do that one contour at a time it should work. Is computational time a concern? I doubt my solution is going to be quick.
Edit: Just took a look at some of those contours. The data for X < 50 is quite noisy and doesn't lend itself to the analysis you suggest. And the data from 200 < x < 300 is too sparse to use kde2d effectively.
Working on something else. It won't be fast either.
Antoine Bridet
am 3 Mai 2018
Bearbeitet: Antoine Bridet
am 3 Mai 2018
Akzeptierte Antwort
Weitere Antworten (2)
The solution is to find a convex hull that encompasses all the points from all the contours. An example using the 'peaks' function is shown.
figure(1)
clf
%#ok<*AGROW>
clearvars
W = peaks(25);
[xx,yy] = meshgrid(linspace(-1,1,25));
subplot(121);
% this draws the contours we're going to encompass
[~,hcontour] = contour(xx,yy,W,-3:2:5);
C3 = hcontour.ContourMatrix;
hold on
% this while loop allows me to build a matrix of all the X,Y pairs form all
% the contours. There is not (to my knowledge) a built-in way to do this.
jj = 1;
X = [];
Y = [];
while jj < size(C3,2)
Z = C3(1,jj);
L = C3(2,jj);
X = [X C3(1,jj+1:jj+L)];
Y = [Y C3(2,jj+1:jj+L)];
jj = jj + 1 + L;
end
plot(X,Y,'.k','MarkerSize',8)
hold off
dt = delaunayTriangulation(X',Y');
[Outside_Index, Av] = convexHull(dt);
Outside_X = dt.Points(Outside_Index,1);
Outside_Y = dt.Points(Outside_Index,2);
subplot(122)
plot(X,Y,'.k',Outside_X,Outside_Y,'-r')
Edited to remove the 'column' function calls. They're not necessary in this demo.
3 Kommentare
Antoine Bridet
am 2 Mai 2018
Bearbeitet: Antoine Bridet
am 2 Mai 2018
sorry, my column function is my own utility that I didn't realize was in there. It's just:
function out = column(x)
out = reshape(x,numel(x),1);
return
I misread the original request. I thought you were looking for the outer envelope, not the inner one.
I don't know of a built-in way to do that. However, I have a quick hack that would work better for your results than my demo.
Pick a point that is "inside" the final result and convert the (x,y) pairs into polar coordinates about that point as the origin. invert the radius for each point (r = 1./r) and calculate new Cartesian coordinates. The exterior convex hull of that set of points should be the same as the interior of the original set once you convert them back to the original coordinate system.
Edit: The idea of using inverse radius doesn't work. The interior envelope curvature relative to the origin may change such that you'd need a concave section of the voronoi diagram which convexHull can't find.
Adam Danz
am 1 Mai 2020
FYI, there are several function on the file exchange the extract the controur line coordinates.
Antoine Bridet
am 3 Mai 2018
Kategorien
Mehr zu 3-D Visualization finden Sie in Hilfe-Center und File Exchange
Produkte
Community Treasure Hunt
Find the treasures in MATLAB Central and discover how the community can help you!
Start Hunting!
