text: import matplotlib.image as mpimg
  from skimage.feature import hog
  from scipy.ndimage.measurements import label
  from sklearn.svm import LinearSVC
  from sklearn.preprocessing import StandardScaler
  from sklearn.model_selection import train_test_split
  from os import walk
  from os import path
  import time
  import pickle
  from copy import copy
  from visualizations import *
  from timeit import default_timer as timer
  
  #color_space can be selected from YCrCb RGB, HSV, LUV, HLS, YUV, YCrCb  
  color_space = 'RGB'
  orient = 9  # HOG orientations
  pix_per_cell = 8 # HOG pixels per cell
  cell_per_block = 2 # HOG cells per block, which can handel e.g. shadows
  hog_channel = "ALL" # Can be 0, 1, 2, or "ALL"
  # hog_channel = 2 # Can be 0, 1, 2, or "ALL"
  spatial_size = (16, 16) # Spatial binning dimensions
  hist_bins = 32    # Number of histogram bins
  spatial_feat = True # Spatial features 
  hist_feat = True # Histogram features 
  hog_feat = True # HOG features
  buffer_weights=[0.1,0.2,0.3,0.4]
  clf_path = 'clf_pickle_all_v1.p'  # CAMERA CALIBRATION FILE
  Heatmap_buffer = []
  N_buffer = 10
  y_start_stop = [400, 656] # Min and MAX in y to search in slide_window()
  ystart_0 = y_start_stop[0]
  ystop_0 = ystart_0 + 64*3
  ystart_1 = ystart_0
  ystop_1 = y_start_stop[1]
  ystart_2 = ystart_0
  ystop_2 = y_start_stop[1]
  ystarts = [ystart_1, ystart_2]
  ystops = [ystop_1-100, ystop_2]
  search_window_scales = [1.5, 2]  # (64x64), (96x96), (128x128)
  
  
  # Define a function to return HOG features and visualization
  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=True,
                                    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=True,
                         visualise=vis, feature_vector=feature_vec)
          return features
  
  
  def bin_spatial(img, size=(64, 64)):
      color1 = cv2.resize(img[:,:,0], size).ravel()
      color2 = cv2.resize(img[:,:,1], size).ravel()
      color3 = cv2.resize(img[:,:,2], size).ravel()
      return np.hstack((color1, color2, color3))
  
  
  # COLOUR HISTOGRAM FUNCTION [COMPUTING SEPERATELY FOR EVERY CHANNEL ]
  def color_hist(img, nbins=32):
      channel1_hist = np.histogram(img[:,:,0], bins=nbins)
      channel2_hist = np.histogram(img[:,:,1], bins=nbins)
      channel3_hist = np.histogram(img[:,:,2], bins=nbins)
      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
  
  
  # Feature extraction function[call bin_spatial() and color_hist()]
  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):
      # Feature vectors List
      features = []
      # list of images
      for file in imgs:
          file_features = []
  
          image = mpimg.imread(file)
  
          # Color Space conversion
          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)
  
          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:
              if hog_channel == 'ALL':
                  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)
              else:
                  hog_features = get_hog_features(feature_image[:,:,hog_channel], orient,
                              pix_per_cell, cell_per_block, vis=False, feature_vec=True)
              # Adding 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=(16, 16), xy_overlap=(0.5, 0.5)):
      # 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 region
      xspan = x_start_stop[1] - x_start_stop[0]
      yspan = y_start_stop[1] - y_start_stop[0]
      # 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]))
      # The number of windows in x/y
      nx_buffer = np.int(xy_window[0]*(xy_overlap[0]))
      ny_buffer = np.int(xy_window[1]*(xy_overlap[1]))
      nx_windows = np.int((xspan-nx_buffer)/nx_pix_per_step)
      ny_windows = np.int((yspan-ny_buffer)/ny_pix_per_step)
      # List Of Windows 
      window_list = []
      for ys in range(ny_windows):
          for xs in range(nx_windows):
              # 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]
  
              # Add window position to list
              window_list.append(((startx, starty), (endx, endy)))
      # Return the list of windows
      return window_list
  
  
  # Function for bounding box
  def draw_boxes(img, bboxes, color=(0, 0, 255), thick=6):
      imcopy = np.copy(img)
      for bbox in bboxes:
          cv2.rectangle(imcopy, bbox[0], bbox[1], color, thick)
      # Return the image copy with boxes drawn
      return imcopy
  
  
  def convert_color(img, conv='RGB2YCrCb'):
      if conv == 'RGB2YCrCb':
          return cv2.cvtColor(img, cv2.COLOR_RGB2YCrCb)
      if conv == 'BGR2YCrCb':
          return cv2.cvtColor(img, cv2.COLOR_BGR2YCrCb)
      if conv == 'RGB2LUV':
          return cv2.cvtColor(img, cv2.COLOR_RGB2LUV)
      if conv == 'RGB2HSV':
          return cv2.cvtColor(img, cv2.COLOR_RGB2HSV)
  
  
  # Extracting features from a single image window
  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):
      #List for Features
      img_features = []
      #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)
      #Computing spatial features if flag is set
      if spatial_feat == True:
          spatial_features = bin_spatial(feature_image, size=spatial_size)
          #Add features to list
          img_features.append(spatial_features)
      #Computing histogram features if flag is set
      if hist_feat == True:
          hist_features = color_hist(feature_image, nbins=hist_bins)
          #Add features to list
          img_features.append(hist_features)
      #Computing 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)
          #Add features to Image feature list
          img_features.append(hog_features)
  
      #Return array of features
      return np.concatenate(img_features)
  
  
  # Search window
  def search_windows(img, windows, clf, scaler, 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):
  
      #list for detection windows
      on_windows = []
      for window in windows:
          test_img = cv2.resize(img[window[0][1]:window[1][1], window[0][0]:window[1][0]], (64, 64))  # training image is (64,64)
  
          #Extracting 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)
          #Scaling extracted features to be fed to classifier
          X = np.array(features).reshape(1, -1)
          test_features = scaler.transform(X)
          #Prediction
          prediction = clf.predict(test_features)
          #If positive (prediction == 1) then save the window
          if prediction == 1:
              on_windows.append(window)
      #Return windows for positive detections
      return on_windows
  
  
  def find_cars(img, ystart, ystop, scale, svc, X_scaler, orient, pix_per_cell, cell_per_block, spatial_size, hist_bins,
                hog_channel, color_space, spatial_feat, hist_feat, hog_feat):
  
      on_windows = []
      img = img.astype(np.float32)/255
  
      img_tosearch = img[ystart:ystop,:,:]
      if  color_space == 'YCrCb':
          ctrans_tosearch = convert_color(img_tosearch, conv='RGB2YCrCb')
      else:
          ctrans_tosearch = convert_color(img_tosearch, conv='RGB2HSV')
  
  
      if scale != 1:
          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, hold the number of hog cells
      nxblocks = (ch1.shape[1] // pix_per_cell)-1
      nyblocks = (ch1.shape[0] // pix_per_cell)-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: there are 8 cells, and move 2 cells per step, 75% overlap
      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
  
              if hog_feat:
                  # Extract HOG for this patch
                  if hog_channel == 0:
                      hog_features = hog1[ypos:ypos+nblocks_per_window, xpos:xpos+nblocks_per_window].ravel()
                  elif hog_channel == 1:
                      hog_features = hog2[ypos:ypos+nblocks_per_window, xpos:xpos+nblocks_per_window].ravel()
                  elif hog_channel == 2:
                      hog_features = hog3[ypos:ypos+nblocks_per_window, xpos:xpos+nblocks_per_window].ravel()
                  else:
                      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
  
              # Extract the image patch
              subimg = cv2.resize(ctrans_tosearch[ytop:ytop+window, xleft:xleft+window], (64,64))
  
              # Get color features
              if spatial_feat:
                  spatial_features = bin_spatial(subimg, size=spatial_size)
  
              if hist_feat:
                  hist_features = color_hist(subimg, nbins=hist_bins)
  
              X = np.hstack((spatial_features, hist_features, hog_features)).reshape(1, -1)
  
              test_features = X_scaler.transform(X)
              test_prediction = svc.predict(test_features)
  
              if test_prediction == 1:
                  xbox_left = np.int(xleft*scale)
                  ytop_draw = np.int(ytop*scale)
                  win_draw = np.int(window*scale)
                  # cv2.rectangle(draw_img,(xbox_left, ytop_draw+ystart),(xbox_left+win_draw,ytop_draw+win_draw+ystart),(0,0,255),6)
                  on_windows.append(((xbox_left, ytop_draw+ystart),(xbox_left+win_draw,ytop_draw+win_draw+ystart)))
  
      return on_windows
  
  
  def add_heat(heatmap, bbox_list):
      # 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# Iterate through list of bboxes
  
  
  def apply_threshold(heatmap, threshold):
      # Zero out pixels below the threshold
      heatmap[heatmap <= threshold] = 0
      # Return thresholded map
      return heatmap
  
  
  def draw_bboxes(img, heatmap_buffer, heatmap_pre, N_buffer):
  
      heatmap_buffer.append(heatmap_pre)
  
      if len(heatmap_buffer) > N_buffer: # remove the first component if it is more than N_buffer elements
          heatmap_buffer.pop(0)
  
      # weight the heatmap based on current frame and previous N frames
      idxs = range(N_buffer)
      for b, w, idx in zip(heatmap_buffer, buffer_weights, idxs):
          heatmap_buffer[idx] = b * w
  
      heatmap = np.sum(np.array(heatmap_buffer), axis=0)
      heatmap = apply_threshold( heatmap, threshold= sum(buffer_weights[0:N_buffer])*2)
  
      # Find final boxes from heatmap using label function
      labels = label(heatmap)
  
      bboxes = []
      # locate the bounding box
      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_tmp = ((np.min(nonzerox), np.min(nonzeroy)), (np.max(nonzerox), np.max(nonzeroy)))
          bboxes.append(bbox_tmp)
  
  
      for bbox in bboxes:
          # Draw the box on the image
          cv2.rectangle(img, bbox[0], bbox[1], (0,0,255), 4)
  
      # Return the image
  
      return img, heatmap, bboxes
  
  
  def generate_heatmap(image, windows_list):
  
      heat = np.zeros_like(image[:,:,0]).astype(np.float)
  
      # Adding heat to each box in box list
      heat = add_heat(heat, windows_list)
  
      # Applying threshold to help remove false positives
      heat = apply_threshold(heat, 1)
  
      # Visualize the heatmap when displaying
      heatmap = np.clip(heat, 0, 255)
  
      # if np.amax(heatmap) == 0:
      #     flag = False
      # else:
      #     flag = True
  
      return heatmap
  
  
  def get_fileNames(rootdir):
      data=[]
      for root, dirs, files in walk(rootdir, topdown=True):
          for name in files:
              _, ending = path.splitext(name)
              if ending != ".jpg" and ending != ".jepg" and ending != ".png":
                  continue
              else:
                  data.append(path.join(root, name))
      return data
  
  
  
  # if svm classifer exist, load it; otherwise, compute the svm classifier
  if path.isfile(clf_path):
  
      print('loading existing classifier...')
      with open(clf_path, 'rb') as file:
          clf_pickle = pickle.load(file)
          svc = clf_pickle["svc"]
          X_scaler = clf_pickle["scaler"]
          orient = clf_pickle["orient"]
          pix_per_cell = clf_pickle["pix_per_cell"]
          cell_per_block = clf_pickle["cell_per_block"]
          spatial_size = clf_pickle["spatial_size"]
          hist_bins = clf_pickle["hist_bins"]
          color_space = clf_pickle["color_space"]
  """
  else:
      # Read in cars and notcars
      #images = glob.glob('data/small/all/*.jpeg')
      #car_path = '/data/vehicles'
      #notcars_path = '/data/non-vehicles'
      cars = get_fileNames(car_path)
      notcars = get_fileNames(notcars_path)
  
      # set the sample size
      sample_size = min(len(cars), len(notcars))
      cars = cars[0:sample_size]
      notcars = notcars[0:sample_size]
  
      print('filenames are saved!')
  
      print('extracting 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)
      print('car features extracted!')
      print('extracting noncar features...')
      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)
      print('noncar features extracted!')
  
      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()
  
      # save classifier
      clf_pickle = {}
      clf_pickle["svc"] = svc
      clf_pickle["scaler"] = X_scaler
      clf_pickle["orient"] = orient
      clf_pickle["pix_per_cell"] = pix_per_cell
      clf_pickle["cell_per_block"] = cell_per_block
      clf_pickle["spatial_size"] = spatial_size
      clf_pickle["hist_bins"] = hist_bins
      clf_pickle["color_space"] = color_space
  
      destnation = clf_path
      pickle.dump( clf_pickle, open( destnation, "wb" ) )
      print("Classifier is written into: {}".format(destnation))
  
  """
  def vehicle_detection_svm(image, img_lane_augmented, lane_info):
  
      start = timer()
  
      windows_list = []
      for search_window_scale, ystart, ystop in zip(search_window_scales, ystarts, ystops):
          windows_list_tmp = find_cars(np.copy(image), ystart, ystop, search_window_scale, svc, X_scaler, orient, pix_per_cell, cell_per_block,
                              spatial_size, hist_bins, hog_channel, color_space, spatial_feat, hist_feat, hog_feat)
          windows_list.extend(windows_list_tmp)
  
      heatmap_pre = generate_heatmap(image, windows_list)
  
      draw_img, heatmap_post, bboxes = draw_bboxes(np.copy(img_lane_augmented), copy(Heatmap_buffer), heatmap_pre, min(len(Heatmap_buffer)+1,N_buffer) )
  
      if len(Heatmap_buffer) >= N_buffer:
          Heatmap_buffer.pop(0)
  
      fps = 1.0 / (timer() - start)
  
      # drawing background highlight
      draw_img = draw_background_highlight(image, draw_img, image.shape[1])
      # drawing vehicle thumbnails
      draw_thumbnails(draw_img, image, bboxes)
  
      # drawing speed
      draw_speed(draw_img, fps, image.shape[1])
  
      # drawing lane status
      draw_lane_status(draw_img, lane_info)
  
      return draw_img
  