In [2]:
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import cv2
import glob
import matplotlib.image as mpimg
import matplotlib.pyplot as plt
import time
from sklearn.svm import LinearSVC
from sklearn.preprocessing import StandardScaler
from skimage.feature import hog
from sklearn.cross_validation import train_test_split
import pickle
from scipy.ndimage.measurements import label
from moviepy.editor import VideoFileClip


C:\Users\Brendan_Work\Anaconda3\envs\carnd-term1\lib\site-packages\sklearn\cross_validation.py:44: DeprecationWarning: This module was deprecated in version 0.18 in favor of the model_selection module into which all the refactored classes and functions are moved. Also note that the interface of the new CV iterators are different from that of this module. This module will be removed in 0.20.
  "This module will be removed in 0.20.", DeprecationWarning)

Importing Images


In [3]:
# block to loop through vehicle and non-vehicle datasets, append to lists

vehicles_files = glob.glob('dataset/vehicles/**/*.png',recursive=True)
non_vehicles_files = glob.glob('dataset/non-vehicles/**/*.png',recursive=True)
vehicles = []
non_vehicles = []
for i in range(len(vehicles_files)):
    vehicles.append(plt.imread(vehicles_files[i]))
for i in range(len(non_vehicles_files)):
    non_vehicles.append(plt.imread(non_vehicles_files[i]))

Processing Functions


In [4]:
def get_hog_features(img, orient, pix_per_cell, cell_per_block, 
                        vis=False, feature_vec=True):
    '''
    Purpose: To extract hog features from image
    
    Inputs:
        img - image to process
        orient - orientations to process hog features for
        pix_per_cell - number of pixels to use per cell
        cell_per_block - number of cells per block
        vis - whether to produce a visualization or not
        feature_vec - whether to return a ravelled feature vector or not
    Outputs:
        [vis] (if vis is True) - a numpy array containing the returned image
        feature_vec - the returned hog features
    '''
    # Call with two outputs if vis==True
    if vis == True:
        # note that transform_sqrt is set to False
        features, hog_image = hog(img, orientations=orient, 
                                  pixels_per_cell=(pix_per_cell, pix_per_cell),
                                  cells_per_block=(cell_per_block, cell_per_block), 
                                  transform_sqrt=False, 
                                  visualise=vis, feature_vector=feature_vec)
        return features, hog_image
    # Otherwise call with one output
    else:      
        # note that transform_sqrt is set to False
        features = hog(img, orientations=orient, 
                       pixels_per_cell=(pix_per_cell, pix_per_cell),
                       cells_per_block=(cell_per_block, cell_per_block), 
                       transform_sqrt=False, 
                       visualise=vis, feature_vector=feature_vec)
        return features

 
def bin_spatial(img, size=(32, 32)):
    '''
    Purpose: Compute binned colour features
    Inputs: 
        img - image to have spatial features extracted
        size - size to resize input image to
    Outputs:
        features - ravelled and resized input features
    
    '''
    # Use cv2.resize().ravel() to create the feature vector
    features = cv2.resize(img, size).ravel() 
    # Return the feature vector
    return features


# Note: NEED TO CHANGE bins_range if reading .png files with mpimg!
def color_hist(img, nbins=32, bins_range=(0, 256)):
    '''
    Purpose: Compute colour histogram features
    Inputs:
        img - image to have colour features extracted from (3 channels)
        nbins - number of bins to use for histogram
        bins_range - colour range for channels
    Outputs:
        hist_features - colour histogram features of image
    '''
    # Compute the histogram of the color channels separately
    channel1_hist = np.histogram(img[:,:,0], bins=nbins, range=bins_range)
    channel2_hist = np.histogram(img[:,:,1], bins=nbins, range=bins_range)
    channel3_hist = np.histogram(img[:,:,2], bins=nbins, range=bins_range)
    # Concatenate the histograms into a single feature vector
    hist_features = np.concatenate((channel1_hist[0], channel2_hist[0], channel3_hist[0]))
    # Return the individual histograms, bin_centers and feature vector
    return hist_features

def extract_features(imgs, color_space='RGB', spatial_size=(32, 32),
                        hist_bins=32, orient=9, 
                        pix_per_cell=8, cell_per_block=2, hog_channel=0,
                        spatial_feat=True, hist_feat=True, hog_feat=True):
    '''
    Purpose: Extract features from a list of images
    Inputs:
        imgs - list of images
        color_space - colour space to extract features from
        spatial_size - image shape to use for spatial parameter extraction
        hist_bins - number of histogram bins for colour feature extraction
        orient - number of HOG orientations to use
        pix_per_cell - number of pixels to use per cell
        cell_per_block - number of cells per block
        hog_channel - hog channels to use for HOG feature extraction
        spatial_feat - whether to include spatial features or not
        hist_feat - whether to include colour histogram features or not
        hog_feat - whether to use HOG features or not
    Outputs:
        features - a feature vector with the desired features for the images
    '''
    # Create a list to append feature vectors to
    features = []
    # Iterate through the list of images
    for image in imgs:
        file_features = []
        # Read in each one by one
        
        # apply color conversion if other than 'RGB'
        if color_space != 'RGB':
            if color_space == 'HSV':
                feature_image = cv2.cvtColor(image, cv2.COLOR_RGB2HSV)
            elif color_space == 'LUV':
                feature_image = cv2.cvtColor(image, cv2.COLOR_RGB2LUV)
            elif color_space == 'HLS':
                feature_image = cv2.cvtColor(image, cv2.COLOR_RGB2HLS)
            elif color_space == 'YUV':
                feature_image = cv2.cvtColor(image, cv2.COLOR_RGB2YUV)
            elif color_space == 'YCrCb':
                feature_image = cv2.cvtColor(image, cv2.COLOR_RGB2YCrCb)
        else: feature_image = np.copy(image)      
        
        # boolean conditions to determine which features are extracted
        if spatial_feat == True:
            spatial_features = bin_spatial(feature_image, size=spatial_size)
            file_features.append(spatial_features)
        if hist_feat == True:
            # Apply color_hist()
            hist_features = color_hist(feature_image, nbins=hist_bins)
            file_features.append(hist_features)
        if hog_feat == True:
        # Call get_hog_features() with vis=False, feature_vec=True
            if hog_channel == 'ALL':
                hog_features = []
                # loop through each channel of image for feature extraction if 'ALL' was used
                for channel in range(feature_image.shape[2]):
                    hog_features.append(get_hog_features(feature_image[:,:,channel], 
                                        orient, pix_per_cell, cell_per_block, 
                                        vis=False, feature_vec=True))
                hog_features = np.ravel(hog_features)        
            else:
                # otherwise, use specified channel
                hog_features = get_hog_features(feature_image[:,:,hog_channel], orient, 
                            pix_per_cell, cell_per_block, vis=False, feature_vec=True)
                
            # Append the new feature vector to the features list
            file_features.append(hog_features)
        features.append(np.concatenate(file_features))
    # Return list of feature vectors
    return features
    

def slide_window(img, x_start_stop=[None, None], y_start_stop=[None, None], 
                    xy_window=(64, 64), xy_overlap=(0.5, 0.5)):
    '''
    Purpose: determine sliding windows to be searched based on input parameters
    Inputs:
        img - image to be searched
        x_start_stop -  start and stop values in x range
        y_start_stop -  start and stop values in y range
        xy_window - window size to be used for window sliding
        xy_overlap - x and y overlap fractions to be used for window sliding
    Outputs:
        window_list - a list of containing windows
    '''
    # If x and/or y start/stop positions not defined, set to image size
    if x_start_stop[0] == None:
        x_start_stop[0] = 0
    if x_start_stop[1] == None:
        x_start_stop[1] = img.shape[1]
    if y_start_stop[0] == None:
        y_start_stop[0] = 0
    if y_start_stop[1] == None:
        y_start_stop[1] = img.shape[0]
        
    # Compute the span of the region to be searched    
    xspan = x_start_stop[1] - x_start_stop[0]
    yspan = y_start_stop[1] - y_start_stop[0]
    
    # Compute the number of pixels per step in x/y
    nx_pix_per_step = np.int(xy_window[0]*(1 - xy_overlap[0]))
    ny_pix_per_step = np.int(xy_window[1]*(1 - xy_overlap[1]))
    
    # Compute the number of windows in x/y
    nx_windows = np.int(xspan/nx_pix_per_step) - 1
    ny_windows = np.int(yspan/ny_pix_per_step) - 1
    
    # Initialize a list to append window positions to
    window_list = []
    
    # Loop through finding x and y window positions
    for ys in range(ny_windows):
        for xs in range(nx_windows):
            # Calculate window position
            startx = xs*nx_pix_per_step + x_start_stop[0]
            endx = startx + xy_window[0]
            starty = ys*ny_pix_per_step + y_start_stop[0]
            endy = starty + xy_window[1]
            
            # Append window position to list
            window_list.append(((startx, starty), (endx, endy)))
    # Return the list of windows
    return window_list

# Define a function to draw bounding boxes
def draw_boxes(img, bboxes, color=(0, 0, 255), thick=6):
    '''
    Purpose: draw bounding boxes on image
    Inputs:
        img - image to draw boxes onto
        bboxes - list of bounding boxes
        color - colour to use for drawing boxes
        thick - thickness of bounding boxes to be drawn
    Outputs:
        imcopy - image with bounding boxes drawn
    '''
    
    # Make a copy of the image
    imcopy = np.copy(img)
    # Iterate through the bounding boxes
    for bbox in bboxes:
        # Draw a rectangle given bbox coordinates
        cv2.rectangle(imcopy, bbox[0], bbox[1], color, thick)
    # Return the image copy with boxes drawn
    return imcopy

# Define a function to extract features from a single image window
# This function is very similar to extract_features()
# just for a single image rather than list of images
def single_img_features(img, color_space='RGB', spatial_size=(32, 32),
                        hist_bins=32, orient=9, 
                        pix_per_cell=8, cell_per_block=2, hog_channel=0,
                        spatial_feat=True, hist_feat=True, hog_feat=True):    
    '''
    Purpose: Extract features from a single image
    Inputs:
        img - image for features to be extracted from
        color_space - colour space to extract features from
        spatial_size - image shape to use for spatial parameter extraction
        hist_bins - number of histogram bins for colour feature extraction
        orient - number of HOG orientations to use
        pix_per_cell - number of pixels to use per cell
        cell_per_block - number of cells per block
        hog_channel - hog channels to use for HOG feature extraction
        spatial_feat - whether to include spatial features or not
        hist_feat - whether to include colour histogram features or not
        hog_feat - whether to use HOG features or not
    Outputs:
        features - a feature vector with the desired features for the image
    '''
    #1) Define an empty list to receive features
    img_features = []
    #2) Apply color conversion if other than 'RGB'
    if color_space != 'RGB':
        if color_space == 'HSV':
            feature_image = cv2.cvtColor(img, cv2.COLOR_RGB2HSV)
        elif color_space == 'LUV':
            feature_image = cv2.cvtColor(img, cv2.COLOR_RGB2LUV)
        elif color_space == 'HLS':
            feature_image = cv2.cvtColor(img, cv2.COLOR_RGB2HLS)
        elif color_space == 'YUV':
            feature_image = cv2.cvtColor(img, cv2.COLOR_RGB2YUV)
        elif color_space == 'YCrCb':
            feature_image = cv2.cvtColor(img, cv2.COLOR_RGB2YCrCb)
    else: feature_image = np.copy(img)      
    #3) Compute spatial features if flag is set
    if spatial_feat == True:
        spatial_features = bin_spatial(feature_image, size=spatial_size)
        #4) Append features to list
        img_features.append(spatial_features)
    #5) Compute histogram features if flag is set
    if hist_feat == True:
        hist_features = color_hist(feature_image, nbins=hist_bins)
        #6) Append features to list
        img_features.append(hist_features)
    #7) Compute HOG features if flag is set
    if hog_feat == True:
        if hog_channel == 'ALL':
            hog_features = []
            for channel in range(feature_image.shape[2]):
                hog_features.extend(get_hog_features(feature_image[:,:,channel], 
                                    orient, pix_per_cell, cell_per_block, 
                                    vis=False, feature_vec=True))      
        else:
            hog_features = get_hog_features(feature_image[:,:,hog_channel], orient, 
                        pix_per_cell, cell_per_block, vis=False, feature_vec=True)
        #8) Append features to list
        img_features.append(hog_features)

    #9) Return concatenated array of features
    features = np.concatenate(img_features)
    return features

# Define a function you will pass an image 
# and the list of windows to be searched (output of slide_windows())
def search_windows(img, windows, clf, scaler, color_space='RGB', 
                    spatial_size=(32, 32), hist_bins=32, 
                    hist_range=(0, 256), orient=9, 
                    pix_per_cell=8, cell_per_block=2, 
                    hog_channel=0, spatial_feat=True, 
                    hist_feat=True, hog_feat=True):
    '''
    Purpose: search windows of image to predict if they contain a car or not
    Inputs:
        img - image to search
        windows - windows to search within image
        clf - classifier to use for predictions
        scaler - scaler to use for scaling features
        color_space - colour space to extract features from
        spatial_size - image shape to use for spatial parameter extraction
        hist_bins - number of histogram bins for colour feature extraction
        orient - number of HOG orientations to use
        pix_per_cell - number of pixels to use per cell
        cell_per_block - number of cells per block
        hog_channel - hog channels to use for HOG feature extraction
        spatial_feat - whether to include spatial features or not
        hist_feat - whether to include colour histogram features or not
        hog_feat - whether to use HOG features or not
    Outputs:
        windows containing true predictions
    '''

    #1) Create an empty list to receive positive detection windows
    on_windows = []
    #2) Iterate over all windows in the list
    for window in windows:
        #3) Extract the test window from original image
        test_img = cv2.resize(img[window[0][1]:window[1][1], window[0][0]:window[1][0]], (64, 64))      
        #4) Extract features for that window using single_img_features()
        features = single_img_features(test_img, color_space=color_space, 
                            spatial_size=spatial_size, hist_bins=hist_bins, 
                            orient=orient, pix_per_cell=pix_per_cell, 
                            cell_per_block=cell_per_block, 
                            hog_channel=hog_channel, spatial_feat=spatial_feat, 
                            hist_feat=hist_feat, hog_feat=hog_feat)
        #5) Scale extracted features to be fed to classifier
        test_features = scaler.transform(np.array(features).reshape(1, -1))
        #6) Predict using your classifier
        prediction = clf.predict(test_features)
        #7) If positive (prediction == 1) then save the window
        if prediction == 1:
            on_windows.append(window)
    #8) Return windows for positive detections
    return on_windows

def add_heat(heatmap, bbox_list):
    '''
    Purpose: return incremented heatmap based on bounding boxes
    Inputs:
        heatmap - heatmap to be incremented
        bbox_list - list of bounding boxes
    Outputs:
        heatmap - incremented heatmap
    '''
    # Iterate through list of bboxes
    for box in bbox_list:
        # Add += 1 for all pixels inside each bbox
        # Assuming each "box" takes the form ((x1, y1), (x2, y2))
        heatmap[box[0][1]:box[1][1], box[0][0]:box[1][0]] += 1

    # Return updated heatmap
    return heatmap
    
def apply_threshold(heatmap, threshold):
    '''
    Purpose: threshold a heatmap based on a certain value to remove false positives
    Inputs;
        heatmap - input heatmap to be thresholded
        threshold - heat value to use for thresholding (integer)
    Outputs:
        thresholded heat map
    '''
    # Zero out pixels below the threshold
    heatmap[heatmap <= threshold] = 0
    # Return thresholded map
    return heatmap

def draw_labeled_bboxes(img, labels):
    '''
    Purpose: draw labeled bounding boxes based on given labels
    Inputs:
        image to have bounding boxes drawn onto
        labels: bounding box labels for each car
    Outputs:
        image with bounding boxes appended to
    '''
    # Iterate through all detected cars
    copy_img = np.copy(img)
    bboxes = []
    for car_number in range(1, labels[1]+1):
        # Find pixels with each car_number label value
        nonzero = (labels[0] == car_number).nonzero()
        # Identify x and y values of those pixels
        nonzeroy = np.array(nonzero[0])
        nonzerox = np.array(nonzero[1])
        # Define a bounding box based on min/max x and y
        bbox = ((np.min(nonzerox), np.min(nonzeroy)), (np.max(nonzerox), np.max(nonzeroy)))
        
        bboxes.append(bbox)
        # Draw the box on the image, value of 255 squared since image is divided by 255 
        # to be scaled between 0 and 1
        cv2.rectangle(copy_img, bbox[0], bbox[1], (0,0,255**2), 6)
        
        
    # Return the image
    return copy_img

def draw_labeled_bboxes_video(img, labels):
    '''
    Purpose: 
        Draw labeled bounding boxes based on given labels. This is the same as draw_labeled_bboxes
        except that it also returns the bounding boxes
    Inputs:
        image to have bounding boxes drawn onto
        labels: bounding box labels for each car
    Outputs:
        copy_img - image with bounding boxes appended to
        bboxes - bounding boxes
    '''
    # Iterate through all detected cars
    copy_img = np.copy(img)
    bboxes = []
    for car_number in range(1, labels[1]+1):
        # Find pixels with each car_number label value
        nonzero = (labels[0] == car_number).nonzero()
        # Identify x and y values of those pixels
        nonzeroy = np.array(nonzero[0])
        nonzerox = np.array(nonzero[1])
        # Define a bounding box based on min/max x and y
        bbox = ((np.min(nonzerox), np.min(nonzeroy)), (np.max(nonzerox), np.max(nonzeroy)))
        
        
        bboxes.append(bbox)  
        # Draw the box on the image
        cv2.rectangle(copy_img, bbox[0], bbox[1], (0,0,255**2), 6)
        
        
    # Return the image
    return copy_img, bboxes

def draw_old_bboxes(img, bboxes):
    '''
    Purpose: draw a given list of bounding boxes on an image
    Inputs:
        img - image to have bounding boxes drawn onto
        bboxes - list of bounding boxes
    Outputs:
        copy_img - image with bounding boxes appended
    '''
    # copy input image
    copy_img = np.copy(img)
    
    # loop through all bounding boxes and append rectangle to image
    for i in range(len(bboxes)):
        cv2.rectangle(copy_img, bboxes[i][0],bboxes[i][1], (0,0,255**2), 6)
    return copy_img

Classifier Function


In [5]:
# function to train classifier
def train_classifier(cars,notcars,color_space,orient, pix_per_cell, cell_per_block, hog_channel, spatial_size,
                    hist_bins, spatial_feat, hist_feat, hog_feat):
    '''
    Purpose: This function is used to train a linear SVM classifier on the car and not car dataset
    Inputs:
        cars - list of car data
        notcars - list of not-car data
        color_space - colour space to extract features from
        spatial_size - image shape to use for spatial parameter extraction
        hist_bins - number of histogram bins for colour feature extraction
        orient - number of HOG orientations to use
        pix_per_cell - number of pixels to use per cell
        cell_per_block - number of cells per block
        hog_channel - hog channels to use for HOG feature extraction
        spatial_feat - whether to include spatial features or not
        hist_feat - whether to include colour histogram features or not
        hog_feat - whether to use HOG features or not
    Outputs:
        svc - the fitted support vector machine classifier
        X_scaler - the input feature scaler
    '''
    
    # extract car and non-car features
    car_features = extract_features(cars, color_space=color_space, 
                            spatial_size=spatial_size, hist_bins=hist_bins, 
                            orient=orient, pix_per_cell=pix_per_cell, 
                            cell_per_block=cell_per_block, 
                            hog_channel=hog_channel, spatial_feat=spatial_feat, 
                            hist_feat=hist_feat, hog_feat=hog_feat)
    notcar_features = extract_features(notcars, color_space=color_space, 
                            spatial_size=spatial_size, hist_bins=hist_bins, 
                            orient=orient, pix_per_cell=pix_per_cell, 
                            cell_per_block=cell_per_block, 
                            hog_channel=hog_channel, spatial_feat=spatial_feat, 
                            hist_feat=hist_feat, hog_feat=hog_feat)
    
    # combine features
    X = np.vstack((car_features, notcar_features)).astype(np.float64)                        
    # Fit a per-column scaler
    X_scaler = StandardScaler().fit(X)
    # Apply the scaler to X
    scaled_X = X_scaler.transform(X)

    # Define the labels vector
    y = np.hstack((np.ones(len(car_features)), np.zeros(len(notcar_features))))


    # Split up data into randomized training and test sets
    rand_state = np.random.randint(0, 100)
    X_train, X_test, y_train, y_test = train_test_split(
        scaled_X, y, test_size=0.2, random_state=rand_state)

    print('Using:',orient,'orientations',pix_per_cell,
        'pixels per cell and', cell_per_block,'cells per block')
    print('Feature vector length:', len(X_train[0]))
    # Use a linear SVC 
    svc = LinearSVC()
    # Check the training time for the SVC
    t=time.time()
    svc.fit(X_train, y_train)
    t2 = time.time()
    print(round(t2-t, 2), 'Seconds to train SVC...')
    # Check the score of the SVC
    print('Test Accuracy of SVC = ', round(svc.score(X_test, y_test), 4))
    # Check the prediction time for a single sample
    t=time.time()
    return svc, X_scaler

Running Classifier


In [6]:
# Block to extract hog features and train an SVM classifier

# Read in cars and notcars
cars = vehicles
notcars = non_vehicles

# parameters to pass to functions 
color_space = 'YUV' # Can be RGB, HSV, LUV, HLS, YUV, YCrCb
orient = 6  # HOG orientations
pix_per_cell = 8 # HOG pixels per cell
cell_per_block = 2 # HOG cells per block
hog_channel = "ALL" # Can be 0, 1, 2, or "ALL"
spatial_size = (16, 16) # Spatial binning dimensions
hist_bins = 16    # Number of histogram bins
spatial_feat = True # Spatial features on or off
hist_feat = True # Histogram features on or off
hog_feat = True # HOG features on or off

# perform classifier training
this_svc, X_scaler = train_classifier(cars,notcars,color_space,orient, pix_per_cell, cell_per_block, hog_channel, spatial_size,
                    hist_bins, spatial_feat, hist_feat, hog_feat)


Using: 6 orientations 8 pixels per cell and 2 cells per block
Feature vector length: 4344
12.38 Seconds to train SVC...
Test Accuracy of SVC =  0.989

Creating pipeline function for running on video


In [7]:
def image_pipeline(img):
    '''
    Purpose: 
        Pipeline to process an image and produce an image with detected vehicles drawn by
        bounding boxes.
    Inputs:
        img - image to be processed
    Outputs:
        draw_img - image with bounding boxes appended
        labels - labels corresponding to the drawn bounding boxes
    
    '''
    
    # Min and max in y to search in slide_window() for lower and bottom window search
    y_start_stop_bottom = [500, None] 
    y_start_stop_top = [400, 600]
    # Min and max in x to search in slide_window(
    x_start_stop = [700,None] 
    
    # scale pixel values to be between 0 and 1
    image = img.astype(np.float32)/255
    
    
    # determine sliding windows to be evaluated by classifier
    # two sizes of windows are used. Larger ones are used for near-range and 
    # smaller ones are used for longer-range
    windows = slide_window(image, x_start_stop=x_start_stop, y_start_stop=y_start_stop_bottom, 
                        xy_window=(128, 128), xy_overlap=(0.9,0))

    windows += slide_window(image, x_start_stop=x_start_stop, y_start_stop=y_start_stop_top, 
                        xy_window=(96, 96), xy_overlap=(0.9,0))

    # determine windows that test positively in the image given the passed classifier
    hot_windows = search_windows(image, windows, this_svc, X_scaler, color_space=color_space, 
                            spatial_size=spatial_size, hist_bins=hist_bins, 
                            orient=orient, pix_per_cell=pix_per_cell, 
                            cell_per_block=cell_per_block, 
                            hog_channel=hog_channel, spatial_feat=spatial_feat, 
                            hist_feat=hist_feat, hog_feat=hog_feat)                     

                    
    # heat image template of zeros
    heat = np.zeros_like(image[:,:,0]).astype(np.float)
    
    # Add heat to each box in box list
    heat = add_heat(heat,hot_windows)

    # Apply threshold to help remove false positives
    heat = apply_threshold(heat,2)

    # Visualize the heatmap when displaying    
    heatmap = np.clip(heat, 0, 255)
    
    # Find final boxes from heatmap using label function
    labels = label(heatmap)
    
    # create a copy of image to return
    draw_img = np.copy(image)
    
    # append bounding boxes to image copy
    draw_img = draw_labeled_bboxes(draw_img, labels)
    
    return draw_img,labels

In [8]:
def video_pipeline(img):
    '''
    Purpose: 
        Pipeline to process a video image frame and produce an image with detected vehicles drawn by
        bounding boxes. Similar to image_pipeline, but with logic based on frame count
    Inputs:
        img - image to be processed
    Outputs:
        draw_img - image with bounding boxes appended
    
    '''
    # global variables for count of the frame and the previous list of bounding boxes
    global frame_counter
    global last_bboxes
    
    
    # Min and max in y to search in slide_window() for lower and bottom window search
    y_start_stop_bottom = [500, None] 
    y_start_stop_top = [400, 600]
    # Min and max in x to search in slide_window(
    x_start_stop = [700,None] 
    
    # scale pixel values to be between 0 and 1
    image = img.astype(np.float32)/255
    
    # create a copy of image to return
    draw_img = np.copy(img)
    
    # if no previous bounding boxes or five frames have passed, find new bounding boxes
    if frame_counter == 0 or not last_bboxes:
        
         # determine sliding windows to be evaluated by classifier
        windows = slide_window(image, x_start_stop=x_start_stop, y_start_stop=y_start_stop_bottom, 
                        xy_window=(128, 128), xy_overlap=(0.9,0.2))

        windows += slide_window(image, x_start_stop=x_start_stop, y_start_stop=y_start_stop_top, 
                            xy_window=(96, 96), xy_overlap=(0.9,0.2))

        # determine windows that test positively in the image given the passed classifier
        hot_windows = search_windows(image, windows, this_svc, X_scaler, color_space=color_space, 
                                spatial_size=spatial_size, hist_bins=hist_bins, 
                                orient=orient, pix_per_cell=pix_per_cell, 
                                cell_per_block=cell_per_block, 
                                hog_channel=hog_channel, spatial_feat=spatial_feat, 
                                hist_feat=hist_feat, hog_feat=hog_feat)                       


        # heat image template of zeros
        heat = np.zeros_like(image[:,:,0]).astype(np.float)

        # Add heat to each box in box list
        heat = add_heat(heat,hot_windows)

        # Apply threshold to help remove false positives
        heat = apply_threshold(heat,2)

        # Visualize the heatmap when displaying    
        heatmap = np.clip(heat, 0, 255)

        # Find final boxes from heatmap using label function
        labels = label(heatmap)

        
        # append new bounding boxes
        draw_img,last_bboxes = draw_labeled_bboxes_video(draw_img, labels)
        
        # reset counter if five frames have passed, otherwise increment counter
        if frame_counter != 4:
            frame_counter += 1
        else:
            frame_counter = 0
    # if not drawing new bounding boxes, used previous frame's
    elif frame_counter == 4:
            frame_counter = 0
            draw_img = draw_old_bboxes(draw_img, last_bboxes)
    else:
            frame_counter += 1
            draw_img = draw_old_bboxes(draw_img, last_bboxes)
    
    
    return draw_img

Test pipeline on image


In [13]:
# block to test image pipeline
%matplotlib inline
'''

fig = plt.figure(figsize=(10,10))
plt.subplot(3,1,1)
test = plt.imread('test_images/test2.jpg')
test_out,lbs = image_pipeline(test)
plt.imshow(test_out)
plt.subplot(3,1,2)
test = plt.imread('test_images/test5.jpg')
test_out,lbs = image_pipeline(test)
plt.imshow(test_out)
plt.subplot(3,1,3)

'''
test = plt.imread('test_images/test6.jpg')
test_out,lbs = image_pipeline(test)
plt.imshow(test_out)


Out[13]:
<matplotlib.image.AxesImage at 0x1c0d07954e0>

Test pipeline on video


In [ ]:
# block to test video pipeline
frame_counter = 0
last_bboxes = []
project_output = "project_video_out.mp4"
clip1 = VideoFileClip("project_video.mp4")
out_clip = clip1.fl_image(video_pipeline) 
%time out_clip.write_videofile(project_output, audio=False)

In [28]:
# block to demonstrate vehicle and non-vehicle example images
%matplotlib inline
fig = plt.figure(figsize=(10,10))
plt.subplot(1,2,1)
img = vehicles[8]
plt.imshow(img)
plt.title('Vehicle Example Image')
plt.subplot(1,2,2)
img = non_vehicles[10]
plt.imshow(img)
plt.title('Non-Vehicle Example Image')


Out[28]:
<matplotlib.text.Text at 0x1ffbf8bf668>

In [67]:
# block to demonstrate HOG feature extraction
fig = plt.figure(figsize=(15,15))
plt.subplot(3,4,1)
img = vehicles[8]
img2 = non_vehicles[10]
plt.imshow(img)
plt.title('Car (Original)')
 
feature_image = cv2.cvtColor(img, cv2.COLOR_RGB2YUV)
_, hog_image = get_hog_features(feature_image[:,:,0], orient=9, pix_per_cell=8, cell_per_block=2, 
                        vis=True, feature_vec=False)
plt.subplot(3,4,2)
plt.title('Car CH-1 HOG')
plt.imshow(hog_image,cmap='gray')
plt.subplot(3,4,3)
plt.title('Non-Car (Original)')
plt.imshow(img2)
plt.subplot(3,4,4)
plt.title('Non-Car CH-1 HOG')
feature_image2 = cv2.cvtColor(img2, cv2.COLOR_RGB2YUV)
_, hog_image2 = get_hog_features(feature_image2[:,:,0], orient=9, pix_per_cell=8, cell_per_block=2, 
                        vis=True, feature_vec=False)
plt.imshow(hog_image2,cmap='gray')
plt.subplot(3,4,5)
plt.title('Car CH-1')
plt.imshow(feature_image[:,:,0],cmap='gray')
plt.subplot(3,4,6)

plt.title('Non-Car CH-1')
plt.imshow(feature_image2[:,:,0],cmap='gray')


plt.subplot(3,4,7)
plt.title('Car CH-2')
plt.imshow(feature_image[:,:,1],cmap='gray')

plt.subplot(3,4,8)
plt.title('Non-Car CH-2')
plt.imshow(feature_image2[:,:,1],cmap='gray')

plt.subplot(3,4,9)
plt.title('Car CH-3')
plt.imshow(feature_image[:,:,2],cmap='gray')

plt.subplot(3,4,10)
plt.title('Non-Car CH-3')
plt.imshow(feature_image2[:,:,2],cmap='gray')


Out[67]:
<matplotlib.image.AxesImage at 0x2001ed7fba8>

In [80]:
# block to demonstrate sliding windows

y_start_stop_bottom = [500, None] # Min and max in y to search in slide_window()
y_start_stop_top = [400, 600]
x_start_stop = [700,None] # Min and max in x to search in slide_window()
image = mpimg.imread('test_images/test2.jpg')
draw_image = np.copy(image)

fig = plt.figure(figsize=(10,10))
image = image.astype(np.float32)/255

windows = slide_window(image, x_start_stop=x_start_stop, y_start_stop=y_start_stop_bottom, 
                    xy_window=(128, 128), xy_overlap=(0.9,0.2))

windows += slide_window(image, x_start_stop=x_start_stop, y_start_stop=y_start_stop_top, 
                    xy_window=(96, 96), xy_overlap=(0.9,0.2))

for i, window in enumerate(windows):
    if i % 4 == 0:
        cv2.rectangle(draw_image, window[0], window[1],(0,0,255), 6)
plt.imshow(draw_image)


Out[80]:
<matplotlib.image.AxesImage at 0x1ffeaf20cc0>

In [87]:
# demonstration of adding bounding boxes without thresholding
y_start_stop_bottom = [500, None] # Min and max in y to search in slide_window()
y_start_stop_top = [400, 600]
x_start_stop = [700,None] # Min and max in x to search in slide_window()
image = mpimg.imread('test_images/test6.jpg')
draw_image = np.copy(image)


image = image.astype(np.float32)/255

windows = slide_window(image, x_start_stop=x_start_stop, y_start_stop=y_start_stop_bottom, 
                    xy_window=(128, 128), xy_overlap=(0.9,0.2))

windows += slide_window(image, x_start_stop=x_start_stop, y_start_stop=y_start_stop_top, 
                    xy_window=(96, 96), xy_overlap=(0.9,0.2))

hot_windows = search_windows(image, windows, this_svc, X_scaler, color_space=color_space, 
                        spatial_size=spatial_size, hist_bins=hist_bins, 
                        orient=orient, pix_per_cell=pix_per_cell, 
                        cell_per_block=cell_per_block, 
                        hog_channel=hog_channel, spatial_feat=spatial_feat, 
                        hist_feat=hist_feat, hog_feat=hog_feat)                       

window_img = draw_boxes(draw_image, hot_windows, color=(0, 0, 255), thick=6)                    
fig = plt.figure(figsize=(15,15))
plt.subplot(1,2,1)
plt.title('Original Image')
plt.imshow(draw_image)
plt.subplot(1,2,2)
plt.imshow(window_img)
plt.title('Windowed Image')


Out[87]:
<matplotlib.text.Text at 0x2001f9dcba8>

In [88]:
# demonstration of heat map

# Read in a pickle file with bboxes saved
# Each item in the "all_bboxes" list will contain a 
# list of boxes for one of the images shown above
#box_list = pickle.load( open( "bbox_pickle.p", "rb" ))

# Read in image similar to one shown above 
#image = mpimg.imread('test_image.jpg')
heat = np.zeros_like(image[:,:,0]).astype(np.float)
    
# Add heat to each box in box list
heat = add_heat(heat,hot_windows)

# Apply threshold to help remove false positives
heat = apply_threshold(heat,2)

# Visualize the heatmap when displaying    
heatmap = np.clip(heat, 0, 255)


fig = plt.figure(figsize=(15,15))
plt.subplot(121)
plt.imshow(window_img)
plt.title('Car Positions')
plt.subplot(122)
plt.imshow(heatmap, cmap='hot')
plt.title('Heat Map')
fig.tight_layout()