<jupyter_start><jupyter_text>Self-Driving Car Engineer Nanodegree Project 1: Finding Lane Lines On The RoadThis first project of Udacity's SDCND is to recognize lane-lines on a freeway in a video and overlay them with a continuous (red or any preferred color) line using computer vision techniques. The computer vision techniques were applied by using the Open Computer Vision (OpenCV) library. The project was written in Python. The project has a template that leads one to achieve this goal as a step-by-step process.- Import a sample image and pre-process it- Pre-processing includes - convert to grayscale, apply gaussian smoothing- Apply Canny edge detection technique- Identify region of interest in the edge detected image- Apply Probabilistic Hough Transform- Overlay continuous lines on the original image- Use the above pipeline to find lane-lines on a collection of freeway images- Test the same pipeline on a short video sample of the freeway---The following sections will detail the above mentioned pipeline to achieve the goal of finding lane-lines on a freeway and overlaying them with continous lines to mark a lane using computer vision techniques.  Import packages<jupyter_code># Define the necessary imports
import matplotlib.pyplot as plt
import matplotlib.image as mpimg
import numpy as np
import cv2
%matplotlib inline<jupyter_output><empty_output><jupyter_text>Read in an image<jupyter_code># input image
image = mpimg.imread('test_images/whiteCarLaneSwitch.jpg')

# print out the stats of the image and plot it
print('This image is: ', type(image), 'with dimensions: ', image.shape)
plt.imshow(image)
plt.title('Original input color image')<jupyter_output>This image is:  <class 'numpy.ndarray'> with dimensions:  (540, 960, 3)<jupyter_text>Helper Functions<jupyter_code>import math

def grayscale(img):
    # convert the input image to grayscale
    return cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)

def gaussianBlur(img, kernel_size):
    # apply gaussian smoothing to eliminate the image noise
    return cv2.GaussianBlur(img,(kernel_size, kernel_size), 0)

def cannyEdges(img, low_threshold, high_threshold):
    # apply canny transform to the gaussian image
    return cv2.Canny(img, low_threshold, high_threshold)

def regionOfInterest(img, vertices):
    # a polygon to mask the region of interest in the image
    # the ROI is retained and the rest of the image is set black
    
    # create a blank image with original image attributes
    mask = np.zeros_like(img)
    
    # define 3channel or 1channel color to fill the mask with depending
    # on the input image
    if len(img.shape) > 2:
        channel_count = img.shape[2] # 3 or 4 depending on the image
        ignore_mask_color = (255,) * channel_count
    else:
        ignore_mask_color = 255
    
    # fill pixels inside the polygon defined by "vertices" with the fill color
    cv2.fillPoly(mask, vertices, ignore_mask_color)
    
    # returning the image only where mask pixels are non-zero
    masked_image = cv2.bitwise_and(img, mask)
    return masked_image

def draw_lines(img, lines, color=[255,0,0], thickness=2):
    for line in lines:
        for x1,y1,x2,y2 in line:
            cv2.line(img,(x1,y1), (x2,y2), color, thickness)

def draw_lines_extrapolation(img, lines, color=[255,0,0], thickness=5):
    # modified 'draw_lines' helper function to include techniques to 
    # draw extrapolated lines on the lanes.
    
    # lists to store the values x1 and x2 values from eqn of a line
    x1_leftLine = []
    x2_leftLine = []
    x1_rightLine = []
    x2_rightLine = []
    
    # iterating through the 'lines' image which has the Hough transformed lines
    for line in lines:
        for x1,y1,x2,y2 in line:
            slope = (y2-y1)/(x2-x1) 
            if slope < 0: # slope is < 0 for left lane line
                polyval = np.polyfit([x1, x2], [y1, y2], 1) # to find slope and intercept
                eqnX1 = (img.shape[0] - polyval[1])/polyval[0] # rewriting line equation to find x value
                eqnX2 = (int(img.shape[0]*0.611) - polyval[1])/polyval[0] # img.shape *0.611 = 330, upper value of the ROI mask
                x1_leftLine.append(int(eqnX1)) 
                x2_leftLine.append(int(eqnX2))
            
            elif slope > 0: # slope is > 0 for right lane line
                polyval = np.polyfit([x1, x2], [y1, y2], 1)
                eqnX1 = (img.shape[0] - polyval[1])/polyval[0]
                eqnX2 = (int(img.shape[0]*0.611) - polyval[1])/polyval[0]
                x1_rightLine.append(int(eqnX1))
                x2_rightLine.append(int(eqnX2))
    
    # to compute the average of the list for left and right lines by ignoring NaNs and 
    # get x1,y1,x2,y2 to draw the extrapolated lines
    
    x1_coordR = int(np.nanmean(x1_rightLine))
    x2_coordR = int(np.nanmean(x2_rightLine))
    
    x1_coordL = int(np.nanmean(x1_leftLine))
    x2_coordL = int(np.nanmean(x2_leftLine))
    
    
    # a limiter to avoid abnormal detection of white lines and lane overlaying hapazardness
    if x2_coordR >= 480 and x2_coordR <= 545:
        if x2_coordL >= 415 and x2_coordL <= 480:
        
        # draw the extrapolated lines from the x values obtained above, y values are from ROI 
            cv2.line(img,(x1_coordR,img.shape[0]), (x2_coordR,int(img.shape[0]*0.611)), color, thickness)
            cv2.line(img,(x1_coordL,img.shape[0]), (x2_coordL,int(img.shape[0]*0.611)), color, thickness)

    
def houghTransform(img, rho, theta, threshold, min_line_len, max_line_gap):
    # apply probabilistoic Hough Transform
    
    lines = cv2.HoughLinesP(img, rho, theta, threshold, np.array([]),\
                           minLineLength=min_line_len, maxLineGap= \
                           max_line_gap)

    line_img = np.zeros((img.shape[0], img.shape[1], 3), dtype=np.uint8)
    draw_lines_extrapolation(line_img, lines)
    #draw_lines(line_img, lines)
    
    return line_img

def weighted_img(img, initial_img, α=0.8, β=1., γ=0.):
    """
    `img` is the output of the hough_lines(), An image with lines drawn on it.
    Should be a blank image (all black) with lines drawn on it.
    
    `initial_img` should be the image before any processing.
    
    The result image is computed as follows:
    
    initial_img * α + img * β + γ
    NOTE: initial_img and img must be the same shape!
    """
    
    return cv2.addWeighted(initial_img, α, img, β, γ)<jupyter_output><empty_output><jupyter_text>Grayscale of the input image<jupyter_code>gray = grayscale(image)
plt.title('Grayscale of the input image')
plt.imshow(gray, cmap ='gray')<jupyter_output><empty_output><jupyter_text>Gaussian smoothing applied<jupyter_code>kernel_size = 3
gs_img = gaussianBlur(gray, kernel_size)
plt.title('Gaussian smoothing applied')
plt.imshow(gs_img, cmap='gray')<jupyter_output><empty_output><jupyter_text>Apply Canny Transform<jupyter_code>l_thresh = 45
h_thresh = 135
c_img = cannyEdges(gs_img, l_thresh, h_thresh)
plt.title('After Canny transform')
plt.imshow(c_img, cmap='gray')
print(c_img.shape)<jupyter_output>(540, 960)<jupyter_text>Region Of Interest Mask<jupyter_code># define the vertices of the polygon mask
top_left = [415,330]
top_right = [545,330]
bottom_left = [150,540]
bottom_right = [900,540]
polyGon = np.array([[(top_left[0], top_left[1]),\
           (top_right[0], top_right[1]),\
           (bottom_right[0], bottom_right[1]),\
           (bottom_left[0], bottom_left[1])]], dtype=np.int32)
roi_img = regionOfInterest(c_img, polyGon)
plt.title('Masked Image')
plt.imshow(roi_img, cmap='gray')<jupyter_output><empty_output><jupyter_text>Apply Hough Transform<jupyter_code># uses the modified 'draw_lines_extrapolation' helper function to draw lines on the masked image
h_img = houghTransform(roi_img, rho=2, theta=(np.pi/180), threshold=18, min_line_len=50, max_line_gap=4)
plt.title('Probabilistic Hough Transform applied')
plt.imshow(h_img)
# Final output of the original image overlaying lane-lines 
inter_img = weighted_img(h_img, image )
plt.title('Marked Lane-lines on the road ')
plt.imshow(inter_img)<jupyter_output><empty_output><jupyter_text>Build a Lane Finding Pipeline<jupyter_code>import os
img_list = [] # to store the names of all the image files
os.makedirs('test_images_output/', exist_ok=True) # create a directory

# Define function to call all the helper functions in order, to process
# the images in test_images/ directory

def findLaneLines(img):
    # returns color image with 3 channel and the red lines marking the lane
    
    or_img = np.copy(img) # copy of the original image
    gray_img = grayscale(img) 
    gB_img = gaussianBlur(gray_img, kernel_size=3)
    canny_img = cannyEdges(gB_img, low_threshold=45, high_threshold=135)
    
    # region of interest vertices
    top_left = [430,327]
    top_right = [550,327]
    bottom_left = [150,540]
    bottom_right = [900,540]
    
    polyGon = np.array([[(top_left[0], top_left[1]), \
                         (top_right[0], top_right[1]),\
                         (bottom_right[0], bottom_right[1]),\
                         (bottom_left[0], bottom_left[1])]], dtype=np.int32)
    
    roi_img = regionOfInterest(canny_img, polyGon)
    hough_img = houghTransform(roi_img, rho=2, theta=(np.pi/180), threshold=55, min_line_len=40, max_line_gap=100)
    final_img = weighted_img(hough_img, or_img)
    return final_img<jupyter_output><empty_output><jupyter_text>Test the pipeline on a set of imagesImages are in .jpg format and with dimensions 960x540 pixels<jupyter_code># append the names of all the images to a list
for file in os.listdir('test_images/'):
    img_list.append(file)

# directories    
input_dir = 'test_images'
output_dir = 'test_images_output'
for x in range(len(img_list)):
    new_img = mpimg.imread(os.path.join(input_dir,img_list[x]))
    
    # test the image with the image processing pipeline 'findLaneLines'
    output_img = findLaneLines(new_img)
    
    # write the image output
    cv2.imwrite(os.path.join(output_dir, img_list[x]),cv2.cvtColor(output_img,cv2.COLOR_RGB2BGR))<jupyter_output><empty_output><jupyter_text>Finding Lane Lines on a video<jupyter_code># Import everything needed to edit/save/watch video clips
from moviepy.editor import VideoFileClip
from IPython.display import HTML
os.makedirs('test_videos_output/', exist_ok=True) # create a directory

white_output = 'test_videos_output/solidWhiteRight.mp4'
## To speed up the testing process you may want to try your pipeline on a shorter subclip of the video
## To do so add .subclip(start_second,end_second) to the end of the line below
## Where start_second and end_second are integer values representing the start and end of the subclip
## You may also uncomment the following line for a subclip of the first 5 seconds
##clip1 = VideoFileClip("test_videos/solidWhiteRight.mp4").subclip(0,5)
clip1 = VideoFileClip("test_videos/solidWhiteRight.mp4")
white_clip = clip1.fl_image(findLaneLines) #NOTE: this function expects color images!!
%time white_clip.write_videofile(white_output, audio=False)
HTML("""
<video width="960" height="540" controls>
  <source src="{0}">
</video>
""".format(white_output))
yellow_output = 'test_videos_output/solidYellowLeft.mp4'
## To speed up the testing process you may want to try your pipeline on a shorter subclip of the video
## To do so add .subclip(start_second,end_second) to the end of the line below
## Where start_second and end_second are integer values representing the start and end of the subclip
## You may also uncomment the following line for a subclip of the first 5 seconds
##clip2 = VideoFileClip('test_videos/solidYellowLeft.mp4').subclip(0,5)
clip2 = VideoFileClip('test_videos/solidYellowLeft.mp4')
yellow_clip = clip2.fl_image(findLaneLines)
%time yellow_clip.write_videofile(yellow_output, audio=False)
HTML("""
<video width="960" height="540" controls>
  <source src="{0}">
</video>
""".format(yellow_output))<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.09642379679144385, 'fraction_numerical': 0.026905080213903743, 'mean_word_length': 3.9725799750727044, 'pattern_counts': {'":': 0, '<': 54, '<?xml version=': 0, '>': 55, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '11', 'dir': 'jupyter-structured-clean-dedup', 'id': '260568', 'license': 'MIT', 'n_tokens_mistral': 4144, 'n_tokens_neox': 3881, 'n_words': 1177, 'path': '.ipynb_checkpoints/aravind-checkpoint.ipynb', 'repo_name': 'aravindk2604/P1_Lane_Lines', 'size': '11968'}