Detection of Cars in Videos using HOG + SVM

This Notebook contains the code used during lab session of Case Study presented on Sept 2nd at SSSIHL. Follwing are the resources used

In [51]:
import os
import matplotlib.pyplot as plt
import glob
import cv2
import numpy as np
import matplotlib.image as mpimg
%matplotlib inline

car_images = glob.glob('vehicles/*/*.png')
noncar_images = glob.glob('non-vehicles/*/*.png')
In [11]:
print(len(car_images), len(noncar_images))
8792 8968
In [15]:
fig, axs = plt.subplots(8,8, figsize=(16, 16))
fig.subplots_adjust(hspace = .2, wspace=.001)
axs = axs.ravel()

#Randomly Pick 32 Images from Cars as Examples
for i in np.arange(32):
    img = cv2.imread(car_images[np.random.randint(0,len(car_images))])
    img = cv2.cvtColor(img,cv2.COLOR_BGR2RGB)
    axs[i].axis('off')
    axs[i].set_title('car', fontsize=10)
    axs[i].imshow(img)
for i in np.arange(32,64):
    img = cv2.imread(noncar_images[np.random.randint(0,len(noncar_images))])
    img = cv2.cvtColor(img,cv2.COLOR_BGR2RGB)
    axs[i].axis('off')
    axs[i].set_title('non-car|', fontsize=10)
    axs[i].imshow(img)
In [16]:
def get_hog_features(img, orient, pix_per_cell, cell_per_block, 
                        vis=False, feature_vec=True):
    # Call with two outputs if vis==True
    if vis == True:
        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:      
        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

print('...')
...
In [130]:
from skimage.feature import hog
car_img = cv2.imread(car_images[5])
_, car_dst = get_hog_features(car_img[:,:,2], orient = 9, pix_per_cell =8, cell_per_block = 8, vis=True, feature_vec=True)
noncar_img = cv2.imread(noncar_images[5])
_, noncar_dst = get_hog_features(noncar_img[:,:,2], 9, 8, 8, vis=True, feature_vec=True)

# Visualize 
f, ((ax1, ax2), (ax3, ax4)) = plt.subplots(2, 2, figsize=(7,7))
f.subplots_adjust(hspace = .4, wspace=.2)
ax1.imshow(car_img)
ax1.set_title('Car Image', fontsize=16)
ax2.imshow(car_dst, cmap='gray')
ax2.set_title('Car HOG', fontsize=16)
ax3.imshow(noncar_img)
ax3.set_title('Non-Car Image', fontsize=16)
ax4.imshow(noncar_dst, cmap='gray')
ax4.set_title('Non-Car HOG', fontsize=16)
print('...')
...
In [54]:
def extract_features(imgs, orient=9, 
                        pix_per_cell=8, cell_per_block=2, hog_channel=0):
    # Create a list to append feature vectors to
    features = []
    # Iterate through the list of images
    for file in imgs:
        # Read in each one by one
        image = mpimg.imread(file)
        feature_image = cv2.cvtColor(image, cv2.COLOR_RGB2YUV)
        
        hog_features = []
        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)        
        
        # Append the new feature vector to the features list
        features.append(hog_features)
    # Return list of feature vectors
    return features

print('...')
...
In [55]:
orient = 11
pix_per_cell = 16
cell_per_block = 2
car_features = extract_features(car_images, orient=orient, 
                        pix_per_cell=pix_per_cell, cell_per_block=cell_per_block) 
                        
In [56]:
notcar_features = extract_features(noncar_images,orient=orient, 
                        pix_per_cell=pix_per_cell, cell_per_block=cell_per_block)
                        
In [143]:
X = np.vstack((car_features, notcar_features)).astype(np.float64) 
print(X.shape)
(17760, 1188)
In [148]:
y = np.hstack((np.ones(len(car_features)), np.zeros(len(notcar_features))))
print(y.shape)
t1=np.ones(3)
t2=np.zeros(2)
t3=np.hstack((t1,t2))
print(t3)

print(t3.shape)
k = np.ones((3,5))
print(k.shape)
(17760,)
[ 1.  1.  1.  0.  0.]
(5,)
(3, 5)
In [59]:
from sklearn.model_selection import train_test_split
rand_state = np.random.randint(0, 100)
X_train, X_test, y_train, y_test = train_test_split(
    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]))
Using: 11 orientations 16 pixels per cell and 2 cells per block
Feature vector length: 1188
In [60]:
from sklearn.svm import LinearSVC
svc = LinearSVC()
svc.fit(X_train, y_train)
print('Test Accuracy of SVC = ', round(svc.score(X_test, y_test), 4))
n_predict = 10
print('SVC predicts: ', svc.predict(X_test[0:n_predict]))
print('For these',n_predict, 'labels: ', y_test[0:n_predict])
Test Accuracy of SVC =  0.9794
SVC predicts:  [ 1.  1.  1.  0.  0.  0.  0.  0.  1.  0.]
For these 10 labels:  [ 1.  1.  1.  0.  0.  0.  0.  0.  1.  0.]
In [123]:
# Define a single function that can extract features using hog sub-sampling and make predictions
def find_cars(img, ystart, ystop, scale, svc,orient, 
              pix_per_cell, cell_per_block,show_all_rectangles=False):
    
    # array of rectangles where cars were detected
    rectangles = []
    
    img = img.astype(np.float32)/255
    
    img_tosearch = img[ystart:ystop,:,:]

    # apply color conversion if other than 'RGB'
    ctrans_tosearch = cv2.cvtColor(img_tosearch, cv2.COLOR_RGB2YUV)
    
    # rescale image if other than 1.0 scale
    imshape = ctrans_tosearch.shape
    ctrans_tosearch = cv2.resize(ctrans_tosearch, (np.int(imshape[1]/scale), np.int(imshape[0]/scale)))
    
    ch1 = ctrans_tosearch[:,:,0]
    ch2 = ctrans_tosearch[:,:,1]
    ch3 = ctrans_tosearch[:,:,2]
    
    # Define blocks and steps as above
    nxblocks = (ch1.shape[1] // pix_per_cell)+1  #-1
    nyblocks = (ch1.shape[0] // pix_per_cell)+1  #-1 
    
    nfeat_per_block = orient*(cell_per_block**2)
    
    # 64 was the orginal sampling rate, with 8 cells and 8 pix per cell
    window = 64
    nblocks_per_window = (window // pix_per_cell)-1 
    cells_per_step = 2  # Instead of overlap, define how many cells to step
    nxsteps = (nxblocks - nblocks_per_window) // cells_per_step
    nysteps = (nyblocks - nblocks_per_window) // cells_per_step
    
    # Compute individual channel HOG features for the entire image
    hog1 = get_hog_features(ch1, orient, pix_per_cell, cell_per_block, feature_vec=False)   
    hog2 = get_hog_features(ch2, orient, pix_per_cell, cell_per_block, feature_vec=False)
    hog3 = get_hog_features(ch3, orient, pix_per_cell, cell_per_block, feature_vec=False)
    
    for xb in range(nxsteps):
        for yb in range(nysteps):
            ypos = yb*cells_per_step
            xpos = xb*cells_per_step
            
            # Extract HOG for this patch
            hog_feat1 = hog1[ypos:ypos+nblocks_per_window, xpos:xpos+nblocks_per_window].ravel()
            hog_feat2 = hog2[ypos:ypos+nblocks_per_window, xpos:xpos+nblocks_per_window].ravel() 
            hog_feat3 = hog3[ypos:ypos+nblocks_per_window, xpos:xpos+nblocks_per_window].ravel() 
            hog_features = np.hstack((hog_feat1, hog_feat2, hog_feat3))
            xleft = xpos*pix_per_cell
            ytop = ypos*pix_per_cell
                                  
            test_prediction = svc.predict(hog_features.reshape(1,-1))
            
            if test_prediction == 1 or show_all_rectangles:
                xbox_left = np.int(xleft*scale)
                ytop_draw = np.int(ytop*scale)
                win_draw = np.int(window*scale)
                rectangles.append(((xbox_left, ytop_draw+ystart),(xbox_left+win_draw,ytop_draw+win_draw+ystart)))
                
    return rectangles
    
print('...')
...
In [124]:
test_img = mpimg.imread('./test_images/test1.jpg')

ystart = 400
ystop = 656
scale = 1
orient = 11
pix_per_cell = 16
cell_per_block = 2

rectangles = find_cars(test_img, ystart, ystop, scale, svc,orient, pix_per_cell, cell_per_block)

print(len(rectangles), 'rectangles found in image')
5 rectangles found in image
In [137]:
# Here is your draw_boxes function from the previous exercise
def draw_boxes(img, bboxes, color=(0, 0, 255), thick=6):
    # Make a copy of the image
    imcopy = np.copy(img)
    random_color = False
    # Iterate through the bounding boxes
    for bbox in bboxes:
        
        if color == 'random' or random_color:
            color = (np.random.randint(0,255), np.random.randint(0,255), np.random.randint(0,255))
            random_color = True
        # Draw a rectangle given bbox coordinates
        cv2.rectangle(imcopy, bbox[0], bbox[1], color, thick)
    # Return the image copy with boxes drawn
    return imcopy

print('...')
...
In [118]:
test_img_rects = draw_boxes(test_img, rectangles)
plt.figure(figsize=(10,10))
plt.imshow(test_img_rects)
print('...')
((832, 432), (896, 496))
((864, 400), (928, 464))
((864, 432), (928, 496))
((1088, 400), (1152, 464))
((1184, 432), (1248, 496))
...
In [65]:
rectangles = find_cars(test_img, ystart, ystop, scale, svc,orient, pix_per_cell, cell_per_block,True)
print(len(rectangles), 'rectangles found in image')
100 rectangles found in image
In [66]:
test_img_rects = draw_boxes(test_img, rectangles)
plt.figure(figsize=(10,10))
plt.imshow(test_img_rects)
print('...')
...
In [106]:
def process_frame(img):

    rectangles = []

    orient = 11
    pix_per_cell = 16
    cell_per_block = 2
    
    ystart = 400
    ystop = 464
    scale = 1.0
    rectangles.append(find_cars(img, ystart, ystop, scale, svc,orient, pix_per_cell, cell_per_block))
    
    ystart = 416
    ystop = 480
    scale = 1.0
    rectangles.append(find_cars(img, ystart, ystop, scale, svc,orient, pix_per_cell, cell_per_block))
    
    ystart = 400
    ystop = 496
    scale = 1.5
    rectangles.append(find_cars(img, ystart, ystop, scale, svc,orient, pix_per_cell, cell_per_block))
    
    ystart = 432
    ystop = 528
    scale = 1.5
    rectangles.append(find_cars(img, ystart, ystop, scale, svc,orient, pix_per_cell, cell_per_block))
    
    ystart = 400
    ystop = 528
    scale = 2.0
    rectangles.append(find_cars(img, ystart, ystop, scale, svc,orient, pix_per_cell, cell_per_block))
    
    ystart = 432
    ystop = 560
    scale = 2.0
    #rectangles.append(find_cars(test_img, ystart, ystop, scale, svc,orient, pix_per_cell, cell_per_block))
    
    ystart = 400
    ystop = 596
    scale = 3.5
    #rectangles.append(find_cars(test_img, ystart, ystop, scale, svc,orient, pix_per_cell, cell_per_block))
    
    ystart = 464
    ystop = 660
    scale = 3.5
    #rectangles.append(find_cars(test_img, ystart, ystop, scale, svc,orient, pix_per_cell, cell_per_block))
    
    rectangles = [item for sublist in rectangles for item in sublist] 
    draw_img = draw_boxes(np.copy(img), rectangles)
    return draw_img

print('...')
...
In [104]:
test_images = glob.glob('./test_images/test*.jpg')

fig, axs = plt.subplots(3, 2, figsize=(16,14))
fig.subplots_adjust(hspace = .004, wspace=.002)
axs = axs.ravel()

for i, im in enumerate(test_images):
    axs[i].imshow(process_frame(mpimg.imread(im)))
    axs[i].axis('off')
In [149]:
cap = cv2.VideoCapture('project_video.mp4')
framenum=0
while True:
    grabbed,frame = cap.read()
    if not grabbed:
        print('Not Grabbed')
        break
    if(framenum%5==0):
        RGB_img = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
        pf = process_frame(RGB_img)
        BGR_img = cv2.cvtColor(pf, cv2.COLOR_RGB2BGR)
        cv2.imshow('Video',BGR_img)
    
    framenum=framenum+1
    if cv2.waitKey(20) & 0xFF == ord('q'):
        break
        
cap.release()
cv2.destroyAllWindows()
Not Grabbed
In [ ]: