Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Edge following with camera

I want to follow the rightmost edge in the following picture with a line following robot.

source9

I tried simple "thresholding", but unfortunately, it includes the blurry white halo:

posterized9

The reason I threshold is to obtain a clean line from the Sobel edge detector:

edge9

Is there a good algorithm which I can use to isolate this edge/move along this edge? The one I am using currently seems error prone, but it's the best one I've been able to figure out so far.

Note: The edge may curve or be aligned in any direction, but a point on the edge will always lie very close to the center of the image. Here's a video of what I'm trying to do. It doesn't follow the edge after (1:35) properly due to the halo screwing up the thresholding.


Here's another sample:

source6

posterized6

edge6

Here, I floodfill the centermost edge to separate it from the little bump in the bottom right corner:

final6

like image 282
Mateen Ulhaq Avatar asked Apr 21 '16 22:04

Mateen Ulhaq


1 Answers

Simplest method (vertical line)

If you know that your image will have black on the right side of the line, here's a simple method:

1) apply the Sobel operator to find the first derivative in the x direction. The result will be an image that is most negative where your gradient is strongest. (Use a large kernel size to average out the halo effect. You can even apply a Gaussian blur to the image first, to get even more averaging if the 7x7 kernel isn't enough.)

2) For each row of the image, find the index of the minimum (i.e. most negative) value. That's your estimate of line position in that row.

3) Do whatever you want with that. (Maybe take the median of those line positions, on the top half and the bottom half of the image, to get an estimate of 2 points that describe the line.)

Slightly more advanced (arbitrary line)

Use this if you don't know the direction of the line, but you do know that it's straight enough that you can approximate it with a straight line.

1)

dx = cv2.Sobel(grayscaleImg,cv2.cv.CV_32F,1,0,ksize=7)
dy = cv2.Sobel(grayscaleImg,cv2.cv.CV_32F,0,1,ksize=7)
angle = np.atan2(dy,dx)
magnitudeSquared = np.square(dx)+np.square(dy)

You now have the angle (in radians) and magnitude of the gradient at each point in your image.

2) From here you can use basic numpy operations to find the line: Filter the points to only keep points where magnitudeSquared > some threshold. Then grab the most common angle (np.bincount() is useful for that). Now you know your line's angle.

3) Further filter the points to only keep points that are close to that angle. You now have all the points on your line. Fit a line through the coordinates of those points.

Most advanced and brittle (arbitrary curve)

If you really need to handle a curve, here's one way:

1) Use your method above to threshold the image. Manually tune the threshold until the white/black division happens roughly where you want it. (Probably 127 is not the right threshold. But if your lighting conditions are consistent, you might be able to find a threshold that works. Confirm it works across multiple images.)

2) Use OpenCV's findcontours() to fit a curve to the white/black boundary. If it's too choppy, use approxPolyDP() to simplify it.

like image 91
Luke Avatar answered Oct 02 '22 06:10

Luke