Skip to content

jmsktm/CarND-Advanced-Lane-Lines

 
 

Repository files navigation

Advanced Lane Finding Project

The goals / steps of this project are the following:

  • Compute the camera calibration matrix and distortion coefficients given a set of chessboard images.
  • Apply a distortion correction to raw images.
  • Use color transforms, gradients, etc., to create a thresholded binary image.
  • Apply a perspective transform to rectify binary image ("birds-eye view").
  • Detect lane pixels and fit to find the lane boundary.
  • Determine the curvature of the lane and vehicle position with respect to center.
  • Warp the detected lane boundaries back onto the original image.
  • Output visual display of the lane boundaries and numerical estimation of lane curvature and vehicle position.

Setup

Link to Jupyter Notebook

1. Compute the camera calibration matrix and distortion coefficients given a set of chessboard images.

1.1 Computation of camera calibration matrix and distortion coefficients.

The code comprises of two methods:

  • get_distortion_vars(): This function steps through the list of calibration images, and finds their object points and image points to perform camera calibration.
  • undistort(image): This function takes an image, and undistorts it using the calibration attributes returned by the above function.

1.2. Test for image distortion correction on chessboard image

In this section, I have used the functions in code section 1.1 to calibrate and undistort one of the calibration image.

Calibration Image:

alt text

Undistorted Chessboard Image:

alt text

2. Apply distortion correction to raw images.

In this section, I have used the functions from Code section 1 to undistort an image of a road image.

Original Image

I have used the image /test_images/test2.jpg for the purpose of testing the functionalities below.
alt text

Undistorted Image

alt text

Overlapped Image

The difference between the original and undistorted images isn't quite evident when seen separately. So I have created an overlapped image.
alt text

3. Use color transforms, gradients, etc., to create a thresholded binary image.

3.1 HLS and Color Thresholds

I have splitted the image into H, L and S channels to check which one depicts the lane more prominently.

H-Channel

alt text

L-Channel

alt text

S-Channel

alt text

We see that the lanes are more prominent on the S-channel. In the sections below, we will perform further operations on the S-channel image for lane detection.

3.2 Threshold codes taken from Course resources

Sobel operator applied along the x-axis

alt text

Sobel operator applied along the y-axis

alt text

Magnitude of the gradient

alt text

Direction of the gradient

alt text

Threshold image of the S-channel

alt text

Combined threshold

alt text

In the quizzes, the combined threshold was generated as:
combined[((gradx == 1) & (grady == 1)) | ((mag_binary == 1) & (dir_binary == 1))] = 1

However, I observed that the s-channel provided a pretty good and strong signal about the lanes. So, I have factored that in during the generation of combined threshold. combined[((gradx == 1) & (grady == 1)) | ((mag_binary == 1) & (dir_binary == 1)) | (s_channel_binary == 1)] = 1

4. Apply a perspective transform to rectify binary image ("birds-eye view").

4.1 Finding street endpoints from a straight road for perspective transform

I calibrated for straight road using the provide image straight_lines_2.jpg and traced lines over the lanes to obtain the endpoints for use in subsequent steps.

Corner Source coordinate
Top Left (595, 450)
Top Right (690, 450)
Bottom Right (1115, 720)
Bottom Left (216, 720)
Calibration image with lane markers

alt text

Perspective transformation of the image over the four endpoints (obtained from above)

alt text

4.2 Using the four coordinates from above to perform perspective image on other road image

Firstly, I overlapped the corners obtained from straight lane calibration over the test image.
alt text

Then I warped the image along the four coordinates. The red lines here (and below) serve as a reference to the straight lanes obtained from straight lane calibration above.

alt text

I then performed a series of threshold operations on the warped image.

Threshold image obtained by applying Sobel operator along x-axis on the road image

alt text

Threshold image obtained by applying Sobel operator along y-axis on the road image

alt text

Threshold image from magnitude of the gradient on the road image

alt text

Threshold image from direction of the gradient on the road image

alt text

Threshold image from s-channel of the road image

alt text

Combined threshold of above threshold images

alt text

We'll be using the combined threshold image for lane detection in the subsequent steps.

5. Detect lane pixels and fit to find the lane boundary.

5.1 Find the lane pixels, and fit polynomial

We are using sliding window approach for lane detection. Here is the result:

Fitted lines with sliding windows

alt text

I have then used the coordinates obtained from sliding windows to polyfill the lane on the warped image.
alt text

The layer is then unwarped back to the real world perspective.
alt text

The unwarped layer is then overlaid on top of the real image to get the final image. alt text

6. Determine the curvature of the lane and vehicle position with respect to center.

6.1 Determine lane curvature

Here, I am simply using the formula from the lesson to calculate lane radius based from the position (y-coordinate on the image), and the coefficients of the second-degree curves representing the lanes.

6.2. Determining conversions in x and y from pixels space to meters

Here, we are using the unwarped lane image for the straight road as a reference to map dimensions from pixels to meters.

alt text

Here are some calculations:

  • Dashed line width (in px): 81
  • Standard length of dashed line (in meters): 3.0
  • meters per pixel in y dimension: 0.037037037037037035
  • Lane width in px: 720.0
  • Standard width of lanes (in meters): 3.7
  • meters per pixel in x dimension: 0.005138888888888889

Hence,

  • meters/pixel along x-axis: 0.0051
  • meters/pixel along y-axis: 0.0370

6.3 Determining 1. lane curvature in pixels and meters 2. vehicle offset

Here, I have written a few helper functions for calculation of lane curvature and vehicle offset in pixels, and to convert it to meters scale.

For calculation of offset, I have compared the offset at the bottom of the images between:

  1. The center of the lane obtained from the image used to find end coordinates of a straight road.
  2. The mid-point of the detected lane.

I then used the multiplication factor (meters/pixel along x-axis: 0.0051) to find the offset in meters.

7. Warp the detected lane boundaries back onto the original image.

I have done it in step #6

6. Output visual display of the lane boundaries and numerical estimation of lane curvature and vehicle position.

Here's how it finally looks like after overlaying the lane and displaying the lane curvature and vehicle offset from the center of the road:

alt text

PIPELINE

Here's the code for the pipeline, with some comments added to explain each step:

CODE

def process_image(img):
    # Undistorting the image frame
    undistorted = undistort(img)

    # Warping the image based on the four corners obtained beforehand.
    pts = endpoints_for_perspective_transform()
    warped, M, dst = warp(undistorted, pts)

    # Thresholding on the warped image. The function get_thresholds() returns multiple
    # thresholds:
    # 1. Gradient along x-axis
    # 2. Gradient along y-axis
    # 3. Magnitude of the gradients
    # 4. Direction of the gradients
    # 5. Threshold from S-channel of HLS
    # 6. Combined threshold from the above.
    gradx, grady, mag_binary, dir_binary, s_channel_binary, combined = get_thresholds(warped)
    binary_warped = combined
    
    # Curve fitting on the binary, warped image frame.
    # out_img = image with the output of curve fitting
    # left_fit = coefficients of second degree polynomial obtained from curve fitting for the left lane
    # right_fit = coefficients of second degree polynomial obtained from curve fitting for the right lane
    # left_fitx = points on the second degree curve obtained from curve fitting for the left lane
    # right_fitx = points on the second degree curve obtained from curve fitting for the right lane
    out_img, left_fit, right_fit, left_fitx, right_fitx, ploty = fit_polynomial(binary_warped)
    
    # Create a blank layer with the lane polyfilled w.r.t. the warped image.
    layer = np.zeros_like(undistorted)
    coordinates_left = np.vstack((left_fitx.astype(int), ploty.astype(int))).T
    coordinates_right = np.flipud(np.vstack((right_fitx.astype(int), ploty.astype(int))).T)
    coordinates = np.concatenate((coordinates_left, coordinates_right), axis=0)
    cv2.fillPoly(layer, np.array([ coordinates ]), color=(0, 255, 0))
    
    # Unwarping the above image (polyfilled lane) back to real-world perspective.
    unwarped, M, dst = unwarp(layer, pts)

    # Overlay the polyfilled layer on the original image.
    overlay = weighted_img(unwarped, undistorted, 0.95, 0.6, 0)
    
    # Overlay text on the image frame
    text = get_overlay_text(binary_warped, left_fit, right_fit)
    overlay_text(overlay, text)
    
    car_offset_text = find_car_offset_text(img, left_fit, right_fit)
    overlay_text(overlay, car_offset_text, pos=(50, 100))

    # Return the composite image (with the polyfilled image overlaid on original image)
    return overlay

Running pipeline on project video

Here is the link to the Youtube video generated using the above pipeline. Click on the image to play video.

Watch the video

Discussion

The pipeline performed pretty decently for the project video, other than for 1 second at 40s. But it does't perform well with the Challenge video. I couldn't take care of it because of time constraints.

  • I would make changes to ignore sudden changes over short durations. If the change is persistent over longer than some specified duration (say, a couple of seconds), I would display some warning message.
  • I would make changes to support lane detection on highly curved (low radius) roads
  • I would make changes to save the result as a serialized data (instead of image/image) which can be overlaid on the the image as needed. That would make it more portable.
  • Not directly related to lane detection, but the process of lane-detection happens sequentially frame-by-frame and is a time consuming process. I would use python's multiprocessing to run it in parallel for each frame.

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

  • Jupyter Notebook 100.0%