Skip to main content
GameDev.net gamedev.net
🔒 Locked

Robust contour detection?

Started by Zipster Sep 6, 2008 at 4:43 PM 7 replies 3.9k views
Original Post
Zipster
Zipster
I'm running into a few hurdles trying to generate inner and outer outlines for a series of 2D shapes, and I was wondering if anyone had some good approaches. The image below is a visual representation of what I have (the black boundary), and what I want (the other color outlines). You'll probably have to zoom in to see the individual pixels. Basically, I have a list of points with integer coordinates on a 2D grid that define the boundary of a polygon. Between each pair of adjacent points I rasterize a line onto this grid. Essentially, I turn the boundary into a 2D collection of cells that enclose the polygon's area. The shapes can be convex or concave, however we can assume that they aren't self-intersecting. What I need to be able to do is find the inner and outer outlines of these boundary cells as shown in the image. I've tried a few approaches thus far, with varying results: 1) Incremental flood-fill: Flood-fill the shape. Any cells in the flood-fill that touch the original boundary (black) become part of the inner outline (red). Flood-fill again, this time within the new inner outline (red). Any new flood-fill cells that touch this outline become part of a second outline (orange). Recurse to create as many contour "rings" as needed. The problems I had with this approach were that a) detecting where the flood-fill should begin wasn't very robust and often took a few tries to find, b) it wouldn't work when trying to create the outer outline (green), lest I flood-fill everything outside the shape(!), c) the flood-fills were very slow, especially for large areas, and d) in the case of the shape in the lower-right, if I chose the flood-fill to start in the thin part of the shape in an orange cell, it would only flood-fill a single cell (since it doesn't flood-fill in the diagonal directions). There really wasn't a way to detect this as an "invalid" fill since it technically worked (i.e. didn't leak into the world). 2) Marching squares: Start at a border cell and march to generate the inner and outer outlines. This is the approach I'm currently using, and it works very well for most shapes. The problems occur once we start generating multiple outlines. Take for instance the shape in the lower-left. The red outline is generated just fine. But when we go to generate the orange outline, we get this long "tail" that is a single cell in width. This is a valid outline, however if I were to try and generate yet another outline, this tail would completely confuse the marching algorithm since we essentially have a self-intersecting polygon. How would it know where the inner outline should be (in blue)? In the worst case it would detect the tail and do another outer outline. Would I have to try and scan for the inside first? Hopefully I've explained my problem clearly enough for someone to understand :) My biggest problem in general is that generating multiple contours eventually produces shapes that the algorithms I've tried can't handle. Ideally I'd like to stick with an adapted marching squares algorithm since it's very fast and makes a lot more sense since I only care about contours. Perhaps one march for finding contours and another march for finding interiors? EDIT: Just thinking outside the box, perhaps since this is an image-based problem, a GPU solution would be appropriate? Blurring perhaps? I'd like to stick with the CPU if I can however since I'll be performing these tasks in an area of code that doesn't have... easy access to the GPU :) But above all else I need a robust solution that works 100% of the time.
Emergent
Emergent
-- Original post ----

Hmmm... What happens if you think about it this way...

Compute the distance transform w.r.t. the black polygon. Each "ring" is then an isosorface of the distance transform (which you can walk around).

See the Wikipedia article, and when it refers to "obstacle pixels" take them to be your black pixels.

-- Some more thoughts -----

Basically, what I'm suggesting is a flood-fill algorithm that uses ALL of the black pixels as the "starting points" for your flood fill (if you're using an 'OPEN' list in your implementation, these all begin in your 'OPEN' list.)
Zipster
Zipster
Quote:
Original post by Emergent
Hmmm... What happens if you think about it this way...

Compute the distance transform w.r.t. the black polygon. Each "ring" is then an isosorface of the distance transform (which you can walk around).

See the Wikipedia article, and when it refers to "obstacle pixels" take them to be your black pixels.

That sounds like it would work, but the performance would also be worse than the flood-fill since I'd have to iterate each non-obstacles cell and determine the closest obstacle cell, which is yet another search in itself :( Plus how would I distinguish between inside and outside the shape?

Quote:
Basically, what I'm suggesting is a flood-fill algorithm that uses ALL of the black pixels as the "starting points" for your flood fill (if you're using an 'OPEN' list in your implementation, these all begin in your 'OPEN' list.)

I tried something like that, but still ran into the issue of distinguishing inside from outside. I'm only going to want the outlines for one side for any given shape.

But thanks for the ideas, I didn't even think about using something like a distance transform :)
serg3d
serg3d
You can also use morphological operations to get rid of artifacts like tails, branches etc.
Thaumaturge
Thaumaturge
This is hardly my field, so I may well be very much off on this; nevertheless, it may too be helpful.

It seems to me that if you know which sides of a given pixel are "inside" the shape, you can simply iterate over the current contour and fill the adjacent pixels that are "within" the polygon.

So, the main problem then becomes determining this.

The solution that comes to mind is taking each line (as a vector, as it presumably was before rasterisation), generating an orthogonal vector to it, and from it taking two points produced by placing this orthogonal vector at the centre of the line and scaling it to a very small (pixel-sized or less) positive or negative distance. Test these points to determine whether they are within the polygon or not, and work with the sign that gives a point within the polygon (this could produce problems if the neither produces a point within the polygon, but a sufficiently small distance should help here, I think).

So, you would then have an vector orthogonal to the original line, and pointing into the polygon. Using the signs of the x- and y- components of this line, determine which of up, left, down and right point into the polygon from a point not at the ends of the line, and, iterating over the rasterisation of that line, fill those pixels with the desired colour.

If the target pixel is found to already be filled by the previous line colour, then simply ignore.

For another idea, you say that you construct your initial contour from lines defined by two end-points. This suggests that you might be able to calculate vertex normals, offset each vertex by a certain amount, and test each to determine whether or not it passes within the polygon defined by the original vertices. These could then be used to form a new outline, from which a new rasterisation could be produced.

With this second possibility I see two problems:
1) In cases such as your "tails" above, points may end up being (correctly, I think) discarded, leaving holes in the outline. I'm not sure that these actually call for filling, but if they do then I think that this can be simply solved by joining the end-points of the hole, since you have the appropriate connectivity information, if I'm not much mistaken.
2) More importantly, however, I'm not sure that this would produce good rasterisations; in fact, I'm fairly confident that you would have cases in which there would be gaps between the original line and the new one. This might be solved with a second sweep over the resultant image, filling in such holes.

I think that either idea should be adaptable to producing outlines for both the inside and the outside of the polygon; in the case of the first idea, it's simply a matter of reversing the "within polygon" and "up, down, left, right" tests, I believe, and in the second case it should simply be a matter of reversing the "within polygon" test.

Finally, I'm not sure that either idea is at all efficient. As I said, this is hardly my area, and I offer these ideas in the hope that they may be of use, but aware that they may well not be.

Good luck. ^_^
MWAHAHAHAHAHAHA!!! My Twitter Account: @EbornIan
Emergent
Emergent
Some other thoughts:

1. A related problem is actually quite easy: Suppose you had not outlines, but filled rasterized polygons. Then computing the erosion or dilation of that shape would be easy. You can then get outlines from these easily, either by XORing the original shape with the the eroded/dilated shape, or by running an edge mask over the eroded/dilated shape.

2. If you have inner and outer contours and just need to tell them apart, you could either,
(a) Take any point in the contour and do a point-in-polygon test vs. the original (vector) data.
(b) Count the number of pixels in each contour (presumably this could be done cheaply as part of the algorithm that builds the contours); whichever one has more, I would expect (though I have not proven this) would be the outer one.

If you were to go with #1, you could actually do this as a convolution, and hence using the FFT, in nlogn time.
Zipster
Zipster
After mulling it over for a while, I settled on a variant of marching squares that uses a 3x3 scanning grid. I trace along the contour and generate both the inner and outer outlines. I'm able to assume that only a handful of the 512 states will actually occur, and after I'm done tracing I remove any intersection between the inner and outer outline lists. So far it works in all my original cases, plus the edge cases that weren't working before! Thanks to everyone who replied, I definitely heard some interesting ideas I didn't consider.
oliii
oliii
Another method that springs to mind. A tesselation of the object, and keeping ony edges that belong to only one triangle. OpenGL maybe able to help with its tesselation routines.
Everything is better with Metal.
Maze Master
Maze Master
If you know the vertices and edge directions, you could do something like this:


You dont have to literally draw the lines either, just work through the math to get an equation for the coordinates of the blue points in terms of the coordinates of the black points.

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.