A pipeline used to identify the lane boundaries in a video from a front-facing camera on a car.
The steps of this project are follows:
A set of chessboard images photographed at different angles were used for this purpose.
There are two types of distortion in this set of calibration images.
Tangential | Radial |
---|---|
![]() |
![]() |
![]() |
![]() |
So for each image path:
cv2.cvtColor()
cv2.findChessboardCorners()
Once the chessboard corners are found, cv2.calibrateCamera()
is called to obtain the calibration value that will be used to undistort our video images.
Using cv2.undistort
, the chessboard images can be undistorted.
Original | Corrected |
---|---|
![]() |
![]() |
Filters used:
These thresholds are combined using logical OR
.
Original | Thresholded |
---|---|
![]() |
![]() |
From an image frame, coordinates are selected in the shape of a trapezoid. The destination coordinates, in the shape of a rectangle, are selected. Using cv2.getPerspectiveTransform
, perspective transform M and inverse perspective transform Minv are calculated, which then will be used for image transformation.
def transform_image(img,size,src,dst):
M = cv2.getPerspectiveTransform(src,dst)
Minv = cv2.getPerspectiveTransform(dst,src)
warped_image = cv2.warpPerspective(img,M,size,flags=cv2.INTER_LINEAR)
return warped_image, M, Minv
Original | Transformed |
---|---|
![]() |
![]() |
Using scipy.signal’s find_peaks_cwt()
lane line pixels are calculated. Then using sliding window search, found peaks are then connected to form lines.
Sliding Window Search | Peaks Connected |
---|---|
![]() |
![]() |
The curvature of 2nd order polnomial can be calculated with:
def curvature_radius (leftx, rightx, img_shape, xm_per_pix=3.7/800, ym_per_pix = 25/720):
ploty = np.linspace(0, img_shape[0] - 1, img_shape[0])
leftx = leftx[::-1] # Reverse to match top-to-bottom in y
rightx = rightx[::-1] # Reverse to match top-to-bottom in y
# Fit a second order polynomial to pixel positions in each lane line
left_fit = np.polyfit(ploty, leftx, 2)
left_fitx = left_fit[0]*ploty**2 + left_fit[1]*ploty + left_fit[2]
right_fit = np.polyfit(ploty, rightx, 2)
right_fitx = right_fit[0]*ploty**2 + right_fit[1]*ploty + right_fit[2]
# Define conversions in x and y from pixels space to meters
ym_per_pix = 25/720 # meters per pixel in y dimension
xm_per_pix = 3.7/800 # meters per pixel in x dimension
# Fit new polynomials to x,y in real space
y_eval = np.max(ploty)
left_fit_cr = np.polyfit(ploty*ym_per_pix, leftx*xm_per_pix, 2)
right_fit_cr = np.polyfit(ploty*ym_per_pix, rightx*xm_per_pix, 2)
# Calculate the new radii of curvature
left_curverad = ((1 + (2*left_fit_cr[0]*y_eval*ym_per_pix + left_fit_cr[1])**2)**1.5) / np.absolute(2*left_fit_cr[0])
right_curverad = ((1 + (2*right_fit_cr[0]*y_eval*ym_per_pix + right_fit_cr[1])**2)**1.5) / np.absolute(2*right_fit_cr[0])
# Curvature and radius in meters
return (left_curverad, right_curverad)
With the average of the two polynomials (bottom portion), mid point is calculated to determine the vehicle’s position since the camera is assumed to be attached in the middle of the vehicle.
To draw the boundaries onto the original image:
cv2.fillPoly()
.Original | Lane Area Drawn |
---|---|
![]() |
![]() |