Advanced Lane Finding

Imports


In [2]:
import cv2
import glob
import matplotlib.pyplot as plt
import matplotlib.image as mpimg
from moviepy.editor import VideoFileClip
import numpy as np
from IPython.display import HTML

%matplotlib inline

print("Dependencies Loaded!")


Dependencies Loaded!

Camera Calibration

First lets find the corners of all the chessboard calibration images


In [4]:
CALIBRATION_FILES = glob.glob("./camera_cal/calibration*.jpg")
CHESS_X = 9
CHESS_Y = 6

IMAGE_POINTS = []
OBJECT_POINTS = []
CHESSBOARD = np.zeros((CHESS_X*CHESS_Y, 3), np.float32)
CHESSBOARD[:,:2] = np.mgrid[0:CHESS_X,0:CHESS_Y].T.reshape(-1,2)

for file in CALIBRATION_FILES:
    img = mpimg.imread(file)
    
    # Convert to grayscale
    gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
    
    # Find the chessboard corners
    ret, corners = cv2.findChessboardCorners(gray, (CHESS_X,CHESS_Y), None)

    if ret == True:
        IMAGE_POINTS.append(corners)
        OBJECT_POINTS.append(CHESSBOARD)

        # Draw all the chessboard corners
        img = cv2.drawChessboardCorners(img, (CHESS_X,CHESS_Y), corners, ret)
        plt.imshow(img)


Then let's calibrate the camera with these points. We should see the chessboard's distortion at the edges of the camera uncurl


In [5]:
img = mpimg.imread("./camera_cal/calibration1.jpg")
ret, MTX, DIST, rvecs, tvecs = cv2.calibrateCamera(
    OBJECT_POINTS, 
    IMAGE_POINTS, 
    img.shape[0:2], 
    None, 
    None
)

def plot_side_by_side(img1, img2, label1="Original Image", label2="New Image", should_mask=False):
    # Helper function to plot two images side by side
    
    if should_mask:
        i1 = mask(img1)
        i2 = mask(img2)
    else:
        i1 = img1
        i2 = img2
    
    f, (ax1, ax2) = plt.subplots(1, 2, figsize=(24, 9))
    f.tight_layout()
    ax1.imshow(i1)
    ax1.set_title(label1, fontsize=50)
    
    ax2.imshow(i2)
    ax2.set_title(label2, fontsize=50)
    
    plt.subplots_adjust(left=0., right=1, top=0.9, bottom=0.)
    
def undistort(img):
    # Undistor an image from the camera calibration we did earlier
    return cv2.undistort(img, MTX, DIST, None, MTX)

plot_side_by_side(img, undistort(img), "Distorted", "Undistorted")


Binary Image

We can combine various color spaces with gradient thresholding techniques in order to reduce our input images into a binary image (1s and 0s) to better find our lane lines.

Region of Interest


In [7]:
X, Y = (1280, 720)
MID_X, MID_Y = int(X/2), int(Y/2)
X_OFF, Y_OFF = 75, 93
MASK = np.array([
    [(0, Y), (X, Y), 
     (MID_X+X_OFF, MID_Y+Y_OFF), (MID_X-X_OFF, MID_Y+Y_OFF)]
])

def mask(img):
    # Apply a mask to the image for the part of the road that we care about
    mask = np.zeros_like(img)
    
    cv2.fillPoly(mask, MASK, [255, 255, 255])
    masked_image = cv2.bitwise_and(img, mask)
    return masked_image

img = mpimg.imread("./test_images/test1.jpg")
plot_side_by_side(img, mask(img), label2="Masked")


Perspective Transform


In [8]:
img = mpimg.imread("./test_images/straight_lines1.jpg")

BIRDS_EYE = np.float32([
    [320 , 720],
    [960 , 720],
    [960 , 0],
    [320 , 0]
])

def birds_eye(img):
    # Get a transform matrix from the masked region to the birds eye view
    M = cv2.getPerspectiveTransform(np.float32(MASK[0]), BIRDS_EYE)
    
    # Apply the warp matrix
    warped = cv2.warpPerspective(mask(img), M, (img.shape[1], img.shape[0]), flags=cv2.INTER_LINEAR)
    return warped

def fps(img):
    # Get a transform matrix from the birds eye view to the masked region of interest
    M = cv2.getPerspectiveTransform(BIRDS_EYE, np.float32(MASK[0]))
    
    # Apply the matrix to unwarp the birds eye view to the road view
    unwarped = cv2.warpPerspective(img, M, (img.shape[1], img.shape[0]), flags=cv2.INTER_LINEAR)
    return unwarped

plot_side_by_side(mask(img), birds_eye(img))


Gradients with Sobel

The Sobel operator can help us find the gradient, and pick out the edges in our images.


In [9]:
def abs_sobel_thresh(img, orient="x", ksize=3, thresh=(0, 255)):
    # Transform to HLS color space, and use the S portion
    S = cv2.cvtColor(img, cv2.COLOR_RGB2HLS)[:,:,2]
    
    # Apply the proper Sobel gradient
    if orient == "x":
        sobel = cv2.Sobel(S, cv2.CV_64F, 1, 0, ksize=ksize)
    else:
        sobel = cv2.Sobel(S, cv2.CV_64F, 0, 1, ksize=ksize)
    
    # Get the absolute value of the sobel and scale it
    abs_sobel = np.absolute(sobel)
    scaled = np.uint8(255 * abs_sobel / np.max(abs_sobel))

    # Apply a mask for a bianry value
    binary = np.zeros_like(scaled)
    binary[(scaled >= thresh[0]) & (scaled <= thresh[1])] = 1
    return binary


def mag_thresh(img, ksize=3, thresh=(0, 255)):
    # Transform to HLS color space, and use the S portion
    S = cv2.cvtColor(img, cv2.COLOR_RGB2HLS)[:,:,2]
    
    # Grab the sobel values for both axis
    sobel_x = cv2.Sobel(S, cv2.CV_64F, 1, 0, ksize=ksize)
    sobel_y = cv2.Sobel(S, cv2.CV_64F, 0, 1, ksize=ksize)

    # Calculate the magnitude (distance) of the sobel at x and y
    mag = np.sqrt(sobel_x ** 2 + sobel_y ** 2)
    
    # Scale magnitude
    scaled = np.uint8(255 * mag / np.max(mag))

    # Apply a mask for a binary value
    binary = np.zeros_like(scaled)
    binary[(scaled >= thresh[0]) & (scaled <= thresh[1])] = 1
    return binary


def dir_thresh(img, ksize=3, thresh=(0, np.pi / 2)):
    # Transform to HLS color space, and use the S portion
    S = cv2.cvtColor(img, cv2.COLOR_RGB2HLS)[:,:,2]
    
    # Grab the sobel values for both axis
    sobel_x = cv2.Sobel(S, cv2.CV_64F, 1, 0, ksize=ksize)
    sobel_y = cv2.Sobel(S, cv2.CV_64F, 0, 1, ksize=ksize)

    # Get absolute value of x and y sobels
    abs_x = np.absolute(sobel_x)
    abs_y = np.absolute(sobel_y)

    # Get arctangent for direction
    arc = np.arctan2(abs_y, abs_x)

    # Apply a mask for a binary value
    binary = np.zeros_like(arc)
    binary[(arc >= thresh[0]) & (arc <= thresh[1])] = 1
    return binary

def sobel_comp(img, plot=False):
    # Calculate various sobel binary images
    lanes_x = abs_sobel_thresh(img, orient="x", ksize=31, thresh=(30, 200))
    lanes_y = abs_sobel_thresh(img, orient="y", ksize=31, thresh=(50, 200))
    lanes_mag = mag_thresh(img, ksize=31, thresh=(50, 250))
    lanes_dir = dir_thresh(img, ksize=31, thresh=(0.7, 1.3))

    # Calculate a composition of the above sobels
    lanes = np.zeros_like(lanes_x)
    lanes[
        ((lanes_x == 1) & (lanes_y == 1)) & 
        ((lanes_mag == 1) & (lanes_dir == 1))
    ] = 1

    if plot:
        plot_side_by_side(img, lanes_x, label2="Sobel X", should_mask=True)
        plot_side_by_side(img, lanes_y, label2="Sobel Y", should_mask=True)
        plot_side_by_side(img, lanes_mag, label2="Sobel Magnitude", should_mask=True)
        plot_side_by_side(img, lanes_dir, label2="Sobel Direction", should_mask=True)
        plot_side_by_side(img, lanes, label2="Composite", should_mask=True)
        
    return lanes

img = mpimg.imread("./test_images/test4.jpg")
sobel_comp(img, plot=True)


Out[9]:
array([[0, 0, 0, ..., 0, 0, 0],
       [0, 0, 0, ..., 0, 0, 0],
       [0, 0, 0, ..., 0, 0, 0],
       ..., 
       [0, 0, 0, ..., 0, 0, 0],
       [0, 0, 0, ..., 0, 0, 0],
       [0, 0, 0, ..., 0, 0, 0]], dtype=uint8)

Color Spaces

We can use the HLS and HSV color spaces to help better tease out lane lines


In [10]:
def r_thresh(img, thresh=(200,255)):
    # Get R values
    R = img[:,:,0]
    bin_r = np.zeros_like(R)
    
    # Apply a mask to get a binary image of thresholded R values
    bin_r[(R > thresh[0]) & (R <= thresh[1])] = 1
    return bin_r

def h_thresh(hls, thresh=(15,100)):
    # Get H value from hls color space image
    H = hls[:,:,0]
    bin_h = np.zeros_like(H)
    
    # Apply a mask to get a binary image of thresholded H values
    bin_h[(H > thresh[0]) & (H <= thresh[1])] = 1
    return bin_h

def l_thresh(hls, thresh=(15,100)):
    # Get L value from hls color space image
    L = hls[:,:,0]
    bin_l = np.zeros_like(L)
    
    # Apply a mask to get a binary image of thresholded L values
    bin_l[(L > thresh[0]) & (L <= thresh[1])] = 1
    return bin_l

def s_thresh(hls, thresh=(90, 255)):
    # Get S value from hls color space image
    S = hls[:,:,2]
    bin_s = np.zeros_like(S)
    
    # Apply a mask to get a binary image of thresholded S values
    bin_s[(S > thresh[0]) & (S <= thresh[1])] = 1
    return bin_s

def v_thresh(hsv, thresh=(100,255)):
    # Get V value from hsv color space image
    V = hsv[:,:,2]
    bin_v = np.zeros_like(V)
    
    # Apply a mask to get a binary image of thresholded V values
    bin_v[(V > thresh[0]) & (V <= thresh[1])] = 1
    return bin_v

def colors_comp(img, plot=False):
    hls = cv2.cvtColor(img, cv2.COLOR_RGB2HLS)
    hsv = cv2.cvtColor(img, cv2.COLOR_RGB2HSV)
    
    # Grab thresholded binary images for H, L, and S from HLS color space
    bin_h = h_thresh(hls, thresh=(0, 30))
    bin_l = l_thresh(hls, thresh=(0, 50))
    bin_s = s_thresh(hls, thresh=(100, 255))
    
    # Apply a mask to compose values
    colors = np.zeros_like(img[:,:,0])
    colors[
        (((bin_h == 1)) &
        ((bin_l == 1)) &
        ((bin_s == 1)))
    ] = 1
    
    if plot:
        plot_side_by_side(hls[:,:,0], bin_h, label2="H Channel", should_mask=False)
        plot_side_by_side(hls[:,:,1], bin_l, label2="L Channel", should_mask=False)
        plot_side_by_side(hls[:,:,2], bin_s, label2="S Channel (HLS)", should_mask=False)
        plot_side_by_side(img, colors, label2="Colors Comp", should_mask=True)
        
    return colors

img = mpimg.imread("./test_images/test4.jpg")
colors_comp(img, plot=True)


Out[10]:
array([[0, 0, 0, ..., 0, 0, 1],
       [0, 0, 0, ..., 0, 0, 0],
       [0, 0, 0, ..., 0, 0, 0],
       ..., 
       [0, 0, 0, ..., 0, 0, 0],
       [0, 0, 0, ..., 0, 0, 0],
       [0, 0, 0, ..., 0, 0, 0]], dtype=uint8)

Combined Binary Thresholding


In [11]:
def binary_comp(img, plot=False):
    # Compose the binary images from sobel gradients and colors
    colors = colors_comp(img, plot=plot)
    sob = sobel_comp(img, plot=plot)
    
    comp = np.zeros_like(img[:,:,0])
    comp[
        (colors == 1) | (sob == 1)
    ] = 1
    
    if plot:
        plot_side_by_side(img, comp, label2="Total Comp", should_mask=True)
        
    return comp

def pipeline(img):
    # Apply a pipeline to get from a RGB camera view to an overhead binary image of the lanes
    
    # Undistort the image from the camera calibrations
    undist = undistort(img)
    
    # Grab a thresholded binary image based on sobel gradients and color thresholding
    binary = binary_comp(undist)
    
    # Apply a perspective transform to obtain an overhead view of the road
    birds = birds_eye(binary)
    return birds

def test_color_thresholds(filename):
    img = mpimg.imread(filename)
    binary = binary_comp(img, plot=False)
    pipe = pipeline(img)
    
    plot_side_by_side(birds_eye(img), birds_eye(binary), label2="Total Comp", should_mask=False)
    plot_side_by_side(birds_eye(img), pipe, label2="Pipeline", should_mask=False)
    
# test_color_thresholds("./test_images/straight_lines1.jpg")
# test_color_thresholds("./test_images/straight_lines2.jpg")
# test_color_thresholds("./test_images/test1.jpg")
test_color_thresholds("./test_images/test2.jpg")
# test_color_thresholds("./test_images/test3.jpg")
# test_color_thresholds("./test_images/test4.jpg")
# test_color_thresholds("./test_images/test5.jpg")
# test_color_thresholds("./test_images/test6.jpg")


Histogram


In [12]:
def histogram(img):
    # Return a histogram of the various values in an image
    # Used to find where to first search for lanes
    return np.sum(img[img.shape[0]//2:,:], axis=0)

binary = pipeline(img)
hist = histogram(binary)
plt.plot(hist)


Out[12]:
[<matplotlib.lines.Line2D at 0x108fd9e48>]

Sliding Window

Let's find the lane lines with a sliding window technique


In [15]:
img = mpimg.imread("./test_images/test4.jpg")
binary = pipeline(img)
hist = histogram(binary)

plot_side_by_side(img, binary)
plot_side_by_side(img, binary)

def nonzeros(binary):
    # Get nonzero values from an image
    nonzero = binary.nonzero()
    x = np.array(nonzero[1])
    y = np.array(nonzero[0])
    return (x, y)

def find_lanes(binary):
    stacked = np.dstack((binary, binary, binary)) * 255
    
    # Grab the midpoint
    mid = np.int(hist.shape[0] / 2)
    
    # Where to start the left lane search
    left_lane_start = np.argmax(hist[:mid])
    
    # Where to start the right lane search
    right_lane_start = np.argmax(hist[mid:]) + mid

    # How many windows to use for search
    windows = 9
    # Height of each window used
    height = np.int(hist.shape[0] / windows)
    
    nonzero_x, nonzero_y = nonzeros(binary)

    left_curr = left_lane_start
    right_curr = right_lane_start

    # Margin of each windowed search
    margin = 100
    
    # Minimum number of pixels to recenter window
    min_pix = 50

    left_lane = []
    right_lane = []

    # Search through each window
    for window in range(windows):
        # Get window bounds
        y_lo = binary.shape[0] - (window + 1) * height
        y_hi = binary.shape[0] - window * height

        l_x_lo = left_curr - margin
        l_x_hi = left_curr + margin

        r_x_lo = right_curr - margin
        r_x_hi = right_curr + margin

        # Draw windows
        cv2.rectangle(stacked, (l_x_lo, y_lo), (l_x_hi, y_hi), (0, 255, 0), 2)
        cv2.rectangle(stacked, (r_x_lo, y_lo), (r_x_hi, y_hi), (0, 255, 0), 2)

        # Get nonzero pixels within the window
        left = (
            (nonzero_y >= y_lo) & 
            (nonzero_y < y_hi) & 
            (nonzero_x >= l_x_lo) & 
            (nonzero_x < l_x_hi)
        ).nonzero()[0]

        right = (
            (nonzero_y >= y_lo) & 
            (nonzero_y < y_hi) & 
            (nonzero_x >= r_x_lo) & 
            (nonzero_x < r_x_hi)
        ).nonzero()[0]

        # Append to the lane
        left_lane.append(left)
        right_lane.append(right)

        # If enough lane pixels were found, recenter the window for the next windowed search
        if len(left) > min_pix:
            left_curr = np.int(np.mean(nonzero_x[left]))
        if len(right) > min_pix:
            right_curr = np.int(np.mean(nonzero_x[right]))

    # Concatenate the pixels
    left_lane = np.concatenate(left_lane)
    right_lane = np.concatenate(right_lane)
    return (left_lane, right_lane)

def fit_polys(binary, lanes):
    left_lane, right_lane = lanes
    nonzero_x, nonzero_y = nonzeros(binary)
    
    left_x = nonzero_x[left_lane]
    left_y = nonzero_y[left_lane]

    right_x = nonzero_x[right_lane]
    right_y = nonzero_y[right_lane]

    # Fit a 2nd order polynomial to each lane
    left_poly = np.polyfit(left_y, left_x, 2)
    right_poly = np.polyfit(right_y, right_x, 2)
    
    return left_poly, right_poly

def plot_lanes(binary, lanes, polys):
    left_lane, right_lane = lanes
    left_poly, right_poly = polys
    nonzero_x, nonzero_y = nonzeros(binary)
    
    stacked = np.dstack((binary, binary, binary)) * 255

    # Get pixel values for left lane and right lane based on fitted polynomial
    plot_y = np.linspace(0, binary.shape[0] - 1, binary.shape[0])
    left_fit_x = left_poly[0] * plot_y**2 + left_poly[1] * plot_y + left_poly[2]
    right_fit_x = right_poly[0] * plot_y**2 + right_poly[1] * plot_y + right_poly[2]

    # Show left lane in red, right lane in blue
    stacked[nonzero_y[left_lane], nonzero_x[left_lane]] = [255, 0, 0]
    stacked[nonzero_y[right_lane], nonzero_x[right_lane]] = [0, 0, 255]
    
    # Plot the left and right fitted polynomial
    plt.imshow(stacked)
    plt.plot(left_fit_x, plot_y, color="yellow")
    plt.plot(right_fit_x, plot_y, color="yellow")
    plt.xlim(0, 1280)
    plt.ylim(720, 0)
    
lanes = find_lanes(binary)
polys = fit_polys(binary, lanes)
plot_lanes(binary, lanes, polys)



In [23]:
def curvature(fit, plot_y):        
    xmpp = 3.7 / 700 # meters per pixel for x
    ympp = 30 / 720  # meters per pixel for y
    
    # Grab max y value, at bottom of image
    y = np.max(plot_y)
    
    # Fit the curve with values rescaled to real world physical measurements
    curve = np.polyfit(ympp*plot_y, fit*xmpp, 2)
    
    # Find the radius of curvature
    rad = ((1 + (2*curve[0]*y + curve[1])**2)**1.5) / np.absolute(2*curve[0])
    return rad

def center(polys):
    left, right = polys
    
    # Y value at bottom of image
    y = 720
    
    # Find value of fitted polynomial at bottom of image for left and right lane
    leftx = left[0]*y**2 + left[1]*y + left[2]
    rightx = right[0]*y**2 + right[1]*y + right[2]
    
    # Return center of two values
    return 0.5 * (leftx + rightx)

# All left and right polynomial values for each frame
PAST_LEFT = []
PAST_RIGHT = []

# Filtered version of PAST_ values based on threshold for changes
GOOD_LEFT = []
GOOD_RIGHT = []

# Past center values
PAST_CENTER = []

# Magnitude of differences found between frames
LEFT_DIFFS = []
RIGHT_DIFFS = []

# Diff between PAST_ and GOOD_
SKIP_COUNTS = {
    "left": 0,
    "right": 0
}

def rolling_avg(array, count=10):
    # Grab last <count> values from <array>
    data = array[-count:]
    
    # Average those values
    return sum(data) / float(len(data))

def final(img):
    # Get binary overhead image
    binary = pipeline(img)
    
    # Grab y values for image
    plot_y = np.linspace(0, binary.shape[0] - 1, binary.shape[0])
    try:
        # Grab empty image to draw road on
        zeros = np.zeros_like(binary).astype(np.uint8)
        color = np.dstack((zeros, zeros, zeros))

        # Find the lanes
        lanes = find_lanes(binary)
        
        # Fit to polynomials
        polys = fit_polys(binary, lanes)

        left_poly, right_poly = polys
        left_lane, right_lane = lanes

        # Get values of polynomials with respect to image values
        left_fit = left_poly[0]*plot_y**2 + left_poly[1]*plot_y + left_poly[2]
        right_fit = right_poly[0]*plot_y**2 + right_poly[1]*plot_y + right_poly[2]

        # If not the first value
        if len(PAST_LEFT) != 0:
            # Grab previous rolling 10 averages for left and right
            prev_roll_left = rolling_avg(PAST_LEFT)
            prev_roll_right = rolling_avg(PAST_RIGHT)
            
            # Get diff between current frame and previous 10 rolling averages
            left_diff = np.sum(left_fit - prev_roll_left)
            right_diff = np.sum(right_fit - prev_roll_right)
            
            # Append diffs
            LEFT_DIFFS.append(left_diff)
            RIGHT_DIFFS.append(right_diff)

            # Only use current value if diff falls between certain range
            if left_diff > -10000 and left_diff < 10000:
                GOOD_LEFT.append(left_fit)
            else:
                SKIP_COUNTS["left"] += 1
                print("Skip Left:  (count: {}, value: {})".format(SKIP_COUNTS["left"], left_diff))

            if right_diff > -30000 and right_diff < 30000:
                GOOD_RIGHT.append(right_fit)
            else:
                SKIP_COUNTS["right"] += 1
                print("Skip Right: (count: {}, value: {})".format(SKIP_COUNTS["right"], right_diff))

            # Use rolling 10 average of the good/filtered values
            roll_left = rolling_avg(GOOD_LEFT)
            roll_right = rolling_avg(GOOD_RIGHT)
        else:
            # If first time, always append current values
            GOOD_LEFT.append(left_fit)
            GOOD_RIGHT.append(right_fit)
            roll_left = left_fit
            roll_right = right_fit

        # Append to past history for next run through
        PAST_LEFT.append(left_fit)
        PAST_RIGHT.append(right_fit)
        
        # Calculate distance from center
        xmpp = 3.7 / 700 # meters per pixel for x
        cent = ((img.shape[1] / 2.0) - center(polys)) * xmpp
        PAST_CENTER.append(cent)
    except Exception as e:
        # If error because no pixels for a lane was found, use previous value
        print("EXCEPTION in final() {}".format(e))
        roll_left = rolling_avg(GOOD_LEFT)
        roll_right = rolling_avg(GOOD_RIGHT)

    # Transform left and right lane values into format for fillPoly
    pts_left = np.array([np.transpose(np.vstack([roll_left, plot_y]))])
    pts_right = np.array([np.flipud(np.transpose(np.vstack([roll_right, plot_y])))])
    pts = np.hstack((pts_left, pts_right))

    # Fill the road inbetween the lanes
    cv2.fillPoly(color, np.int_([pts]), (0,255,0))

    # Convert result back to first person view
    res = fps(color)
    
    # Add the lane drawings with some transparency onto the original image
    undist = undistort(img)
    res = cv2.addWeighted(undist, 1, res, 0.3, 0)
    
    # Calculate the curvature of the left and right lanes
    # Draw the curvature value on the image
    curve = 0.5 * (curvature(roll_left, plot_y) + curvature(roll_right, plot_y))
    cv2.putText(res, "Curvature Radius: {:.2f}km".format(curve / 1000.0), (100, 100), cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 255, 255), 2)
    
    # Draw the distance from center on the image
    roll_cent = rolling_avg(PAST_CENTER)
    cv2.putText(res, "Center: {:.2f}m".format(roll_cent), (100, 150), cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 255, 255), 2)
    
    # Finally, return the result!
    return res
    
img = mpimg.imread("./test_images/test1.jpg")
res = final(img)
plot_side_by_side(img, res, label2="Processed")



In [18]:
processed_filename = './out_all.mp4'
clip = VideoFileClip("./project_video.mp4")

# Process the video through our pipeline, `final()`
processed = clip.fl_image(final)
%time processed.write_videofile(processed_filename, audio=False)
  
HTML("""
<video width="960" height="540" controls>
  <source src="{0}">
</video>
""".format(processed_filename))


Skip Left:  (count: 1, value: -24174.846712383045)
Skip Right: (count: 1, value: -40182.88008652351)
[MoviePy] >>>> Building video ./out_all.mp4
[MoviePy] Writing video ./out_all.mp4
  0%|          | 1/1261 [00:00<09:57,  2.11it/s]
Skip Left:  (count: 2, value: -12087.423356191523)
 13%|█▎        | 164/1261 [01:17<08:33,  2.13it/s]
Skip Right: (count: 2, value: -30476.85330105362)
 16%|█▌        | 196/1261 [01:32<08:12,  2.16it/s]
Skip Right: (count: 3, value: 46465.36846939875)
 16%|█▌        | 198/1261 [01:33<08:16,  2.14it/s]
Skip Right: (count: 4, value: 31856.264094401795)
 16%|█▌        | 199/1261 [01:34<08:16,  2.14it/s]
Skip Right: (count: 5, value: 35318.87415178459)
 25%|██▍       | 313/1261 [02:27<07:25,  2.13it/s]
Skip Left:  (count: 3, value: 10708.398561761933)
 44%|████▍     | 560/1261 [04:24<05:34,  2.09it/s]
Skip Left:  (count: 4, value: -19404.26102123237)
 44%|████▍     | 561/1261 [04:24<05:34,  2.09it/s]
Skip Left:  (count: 5, value: -21107.492276391768)
 45%|████▍     | 562/1261 [04:25<05:34,  2.09it/s]
Skip Left:  (count: 6, value: -22942.506166076215)
 45%|████▍     | 566/1261 [04:27<05:31,  2.09it/s]
Skip Left:  (count: 7, value: 13900.894401798072)
 45%|████▍     | 567/1261 [04:27<05:28,  2.11it/s]
Skip Left:  (count: 8, value: 14454.80791247252)
 45%|████▌     | 568/1261 [04:28<05:25,  2.13it/s]
Skip Left:  (count: 9, value: 14598.295118751254)
 45%|████▌     | 569/1261 [04:28<05:24,  2.13it/s]
Skip Left:  (count: 10, value: 11295.029932514324)
 45%|████▌     | 570/1261 [04:29<05:24,  2.13it/s]
Skip Left:  (count: 11, value: 10833.259969546394)
 47%|████▋     | 591/1261 [04:38<05:08,  2.17it/s]
Skip Left:  (count: 12, value: -11221.471339305834)
 47%|████▋     | 592/1261 [04:39<05:07,  2.18it/s]
Skip Left:  (count: 13, value: -10362.135624766946)
 47%|████▋     | 593/1261 [04:39<05:07,  2.18it/s]
Skip Left:  (count: 14, value: -11060.714659295061)
 47%|████▋     | 594/1261 [04:40<05:07,  2.17it/s]
Skip Left:  (count: 15, value: -12746.547920819685)
 47%|████▋     | 598/1261 [04:42<05:08,  2.15it/s]
Skip Left:  (count: 16, value: -10225.811975442402)
 48%|████▊     | 605/1261 [04:45<05:06,  2.14it/s]
Skip Right: (count: 6, value: 53695.38314988857)
 48%|████▊     | 606/1261 [04:45<05:07,  2.13it/s]
Skip Left:  (count: 17, value: 12311.32344780858)
 48%|████▊     | 607/1261 [04:46<05:06,  2.14it/s]
Skip Left:  (count: 18, value: 15790.657371557172)
Skip Right: (count: 7, value: 89808.65504020004)
 48%|████▊     | 608/1261 [04:46<05:05,  2.13it/s]
Skip Right: (count: 8, value: 43347.9201111491)
 48%|████▊     | 609/1261 [04:47<05:05,  2.14it/s]
Skip Right: (count: 9, value: 53936.70763836606)
 48%|████▊     | 610/1261 [04:47<05:04,  2.14it/s]
Skip Right: (count: 10, value: 222911.88326203718)
 48%|████▊     | 611/1261 [04:48<05:03,  2.14it/s]
Skip Right: (count: 11, value: 1339155.6407684025)
 49%|████▊     | 612/1261 [04:48<05:02,  2.15it/s]
Skip Right: (count: 12, value: -22702693.57850688)
 49%|████▊     | 613/1261 [04:49<05:01,  2.15it/s]
EXCEPTION in final() expected non-empty vector for x
 49%|████▊     | 614/1261 [04:49<05:00,  2.15it/s]
EXCEPTION in final() expected non-empty vector for x
 49%|████▉     | 615/1261 [04:50<04:58,  2.16it/s]
EXCEPTION in final() expected non-empty vector for x
 49%|████▉     | 616/1261 [04:50<05:01,  2.14it/s]
Skip Left:  (count: 19, value: 25084.185829181162)
Skip Right: (count: 13, value: 1801846.9702162838)
 49%|████▉     | 617/1261 [04:51<05:00,  2.14it/s]
EXCEPTION in final() expected non-empty vector for x
 49%|████▉     | 618/1261 [04:51<04:59,  2.15it/s]
EXCEPTION in final() expected non-empty vector for x
 49%|████▉     | 619/1261 [04:51<04:57,  2.16it/s]
EXCEPTION in final() expected non-empty vector for x
 49%|████▉     | 620/1261 [04:52<04:55,  2.17it/s]
EXCEPTION in final() expected non-empty vector for x
 49%|████▉     | 621/1261 [04:52<04:55,  2.16it/s]
Skip Left:  (count: 20, value: 26379.694516093332)
Skip Right: (count: 14, value: 1869708.269666117)
 49%|████▉     | 622/1261 [04:53<04:58,  2.14it/s]
Skip Left:  (count: 21, value: 29391.15248080956)
Skip Right: (count: 15, value: 3431851.4657432865)
 49%|████▉     | 623/1261 [04:53<04:58,  2.14it/s]
Skip Left:  (count: 22, value: 24933.51090937063)
Skip Right: (count: 16, value: 3272559.524339822)
 49%|████▉     | 624/1261 [04:54<04:57,  2.14it/s]
Skip Left:  (count: 23, value: 29002.543177775005)
Skip Right: (count: 17, value: 1309057.8090614798)
 50%|████▉     | 625/1261 [04:54<04:55,  2.15it/s]
Skip Left:  (count: 24, value: 44610.19530434096)
Skip Right: (count: 18, value: 1139111.6279289408)
 50%|████▉     | 626/1261 [04:55<04:55,  2.15it/s]
Skip Left:  (count: 25, value: 42700.480091358855)
Skip Right: (count: 19, value: 1769543.2384607357)
 50%|████▉     | 627/1261 [04:55<04:58,  2.13it/s]
Skip Left:  (count: 26, value: 39167.29108462939)
Skip Right: (count: 20, value: 245701.3143741033)
 50%|████▉     | 628/1261 [04:56<04:57,  2.13it/s]
Skip Left:  (count: 27, value: 38025.648157764634)
Skip Right: (count: 21, value: -44098261.027645685)
 50%|████▉     | 629/1261 [04:56<04:56,  2.13it/s]
Skip Left:  (count: 28, value: 27855.83350451204)
Skip Right: (count: 22, value: 7015224.2718411945)
 50%|████▉     | 630/1261 [04:57<04:56,  2.13it/s]
Skip Right: (count: 23, value: 27812562.931211047)
 50%|█████     | 631/1261 [04:57<04:55,  2.13it/s]
Skip Left:  (count: 29, value: -11005.322025154232)
Skip Right: (count: 24, value: 2445811.4316334017)
 50%|█████     | 632/1261 [04:58<04:55,  2.13it/s]
Skip Left:  (count: 30, value: -19013.53586549474)
Skip Right: (count: 25, value: 2416972.6221718574)
 50%|█████     | 633/1261 [04:58<04:53,  2.14it/s]
Skip Left:  (count: 31, value: -17728.282575602025)
Skip Right: (count: 26, value: 2575077.7104177196)
 50%|█████     | 634/1261 [04:58<04:53,  2.13it/s]
Skip Left:  (count: 32, value: -13134.350408798933)
Skip Right: (count: 27, value: 2681440.0280952514)
 50%|█████     | 635/1261 [04:59<04:51,  2.15it/s]
Skip Right: (count: 28, value: 2760344.8314222633)
 50%|█████     | 636/1261 [04:59<04:51,  2.15it/s]
Skip Right: (count: 29, value: 2624036.6359469593)
 51%|█████     | 637/1261 [05:00<04:50,  2.15it/s]
Skip Left:  (count: 33, value: 20906.29154500257)
Skip Right: (count: 30, value: 2527900.655130933)
 51%|█████     | 638/1261 [05:00<04:49,  2.15it/s]
Skip Left:  (count: 34, value: 21106.21739810932)
Skip Right: (count: 31, value: 2421772.622571145)
 51%|█████     | 639/1261 [05:01<04:50,  2.14it/s]
Skip Left:  (count: 35, value: 12319.221804351037)
Skip Right: (count: 32, value: -2405276.6215949096)
 51%|█████     | 640/1261 [05:01<04:49,  2.14it/s]
Skip Right: (count: 33, value: -1095021.725622638)
 51%|█████     | 641/1261 [05:02<04:48,  2.15it/s]
Skip Right: (count: 34, value: -245389.6251274406)
 51%|█████     | 642/1261 [05:02<04:47,  2.15it/s]
Skip Right: (count: 35, value: -222772.46107703724)
 51%|█████     | 643/1261 [05:03<04:46,  2.16it/s]
Skip Right: (count: 36, value: -177768.7618167616)
 51%|█████     | 644/1261 [05:03<04:46,  2.16it/s]
Skip Right: (count: 37, value: -148805.46889745488)
 51%|█████     | 645/1261 [05:04<04:46,  2.15it/s]
Skip Right: (count: 38, value: -144076.03689375374)
 51%|█████     | 646/1261 [05:04<04:46,  2.15it/s]
Skip Right: (count: 39, value: -135758.69003185123)
 51%|█████▏    | 647/1261 [05:05<04:46,  2.14it/s]
Skip Right: (count: 40, value: -162126.6114371525)
 51%|█████▏    | 648/1261 [05:05<04:47,  2.13it/s]
Skip Right: (count: 41, value: -84521.4874236277)
 51%|█████▏    | 649/1261 [05:05<04:47,  2.13it/s]
Skip Right: (count: 42, value: -109654.20079253791)
 52%|█████▏    | 650/1261 [05:06<04:45,  2.14it/s]
Skip Right: (count: 43, value: -112768.37886273864)
 52%|█████▏    | 655/1261 [05:08<04:42,  2.15it/s]
Skip Right: (count: 44, value: -35739.35912410105)
 53%|█████▎    | 673/1261 [05:17<04:32,  2.15it/s]
Skip Right: (count: 45, value: 30956.10492515291)
 54%|█████▍    | 679/1261 [05:19<04:30,  2.15it/s]
Skip Right: (count: 46, value: 31095.809564924904)
 61%|██████    | 766/1261 [06:00<03:49,  2.15it/s]
Skip Right: (count: 47, value: 30355.682988793687)
 65%|██████▍   | 814/1261 [06:23<03:29,  2.14it/s]
Skip Left:  (count: 36, value: -10143.107409604298)
 66%|██████▋   | 837/1261 [06:33<03:20,  2.11it/s]
Skip Left:  (count: 37, value: -10670.45615787394)
 79%|███████▊  | 990/1261 [07:46<02:11,  2.07it/s]
Skip Left:  (count: 38, value: -10651.387769284836)
 79%|███████▉  | 1001/1261 [07:51<02:04,  2.08it/s]
Skip Left:  (count: 39, value: -12402.755654717952)
 79%|███████▉  | 1002/1261 [07:52<02:03,  2.10it/s]
Skip Left:  (count: 40, value: -18133.018699338492)
 80%|███████▉  | 1003/1261 [07:52<02:03,  2.09it/s]
Skip Left:  (count: 41, value: -23584.40741495999)
 80%|███████▉  | 1004/1261 [07:53<02:02,  2.10it/s]
Skip Left:  (count: 42, value: -29546.37329123605)
 80%|███████▉  | 1005/1261 [07:53<02:01,  2.10it/s]
Skip Left:  (count: 43, value: -22917.700587518204)
 80%|███████▉  | 1008/1261 [07:55<02:00,  2.09it/s]
Skip Left:  (count: 44, value: 12874.812634291364)
 80%|████████  | 1015/1261 [07:58<01:57,  2.09it/s]
Skip Left:  (count: 45, value: -12609.648100392893)
 81%|████████  | 1016/1261 [07:59<01:57,  2.09it/s]
Skip Left:  (count: 46, value: -10934.864993454841)
 81%|████████▏ | 1025/1261 [08:03<01:53,  2.08it/s]
Skip Left:  (count: 47, value: -12862.63789012708)
 82%|████████▏ | 1030/1261 [08:05<01:52,  2.06it/s]
Skip Left:  (count: 48, value: 13032.449568694483)
 82%|████████▏ | 1031/1261 [08:06<01:51,  2.06it/s]
Skip Left:  (count: 49, value: 16896.43017947533)
 82%|████████▏ | 1032/1261 [08:06<01:50,  2.08it/s]
Skip Left:  (count: 50, value: 17981.699188555573)
 82%|████████▏ | 1033/1261 [08:07<01:49,  2.09it/s]
Skip Left:  (count: 51, value: 11911.150405316033)
 82%|████████▏ | 1034/1261 [08:07<01:48,  2.10it/s]
Skip Left:  (count: 52, value: 16748.484228448087)
 82%|████████▏ | 1035/1261 [08:08<01:47,  2.10it/s]
Skip Left:  (count: 53, value: 18887.888467240417)
 82%|████████▏ | 1036/1261 [08:08<01:47,  2.09it/s]
Skip Left:  (count: 54, value: 24570.37234245544)
 82%|████████▏ | 1037/1261 [08:09<01:46,  2.10it/s]
Skip Left:  (count: 55, value: 19523.56090463219)
Skip Right: (count: 48, value: -115495.55669331027)
 82%|████████▏ | 1038/1261 [08:09<01:46,  2.10it/s]
Skip Right: (count: 49, value: 475884.946211339)
 82%|████████▏ | 1039/1261 [08:10<01:45,  2.11it/s]
Skip Right: (count: 50, value: -155885940.39078566)
 82%|████████▏ | 1040/1261 [08:10<01:44,  2.12it/s]
Skip Right: (count: 51, value: 408277694.75025785)
 83%|████████▎ | 1041/1261 [08:11<01:43,  2.13it/s]
EXCEPTION in final() expected non-empty vector for x
 83%|████████▎ | 1042/1261 [08:11<01:42,  2.14it/s]
EXCEPTION in final() expected non-empty vector for x
 83%|████████▎ | 1043/1261 [08:11<01:41,  2.16it/s]
EXCEPTION in final() expected non-empty vector for x
 83%|████████▎ | 1044/1261 [08:12<01:40,  2.16it/s]
Skip Right: (count: 52, value: -23985924.71172161)
 83%|████████▎ | 1045/1261 [08:12<01:40,  2.15it/s]
Skip Left:  (count: 56, value: -11324.257526561098)
Skip Right: (count: 53, value: 10939821.720469056)
 83%|████████▎ | 1046/1261 [08:13<01:40,  2.14it/s]
Skip Right: (count: 54, value: -9900229.754213689)
 83%|████████▎ | 1047/1261 [08:13<01:39,  2.14it/s]
Skip Right: (count: 55, value: -28807807.80757641)
 83%|████████▎ | 1048/1261 [08:14<01:39,  2.13it/s]
Skip Left:  (count: 57, value: -15339.932799544345)
Skip Right: (count: 56, value: -28860730.733543295)
 83%|████████▎ | 1049/1261 [08:14<01:39,  2.13it/s]
Skip Left:  (count: 58, value: -15473.999479648071)
Skip Right: (count: 57, value: -28853982.59343702)
 83%|████████▎ | 1050/1261 [08:15<01:39,  2.12it/s]
Skip Left:  (count: 59, value: -14672.309690480533)
Skip Right: (count: 58, value: -28890356.621014558)
 83%|████████▎ | 1051/1261 [08:15<01:39,  2.12it/s]
Skip Right: (count: 59, value: -28868667.766439684)
 83%|████████▎ | 1052/1261 [08:16<01:38,  2.11it/s]
Skip Right: (count: 60, value: -28861747.86603358)
 84%|████████▎ | 1053/1261 [08:16<01:37,  2.13it/s]
Skip Right: (count: 61, value: -44445049.83939402)
 84%|████████▎ | 1054/1261 [08:17<01:37,  2.13it/s]
Skip Right: (count: 62, value: -5178849.432597252)
 84%|████████▎ | 1055/1261 [08:17<01:37,  2.12it/s]
Skip Left:  (count: 60, value: 14888.172723419993)
Skip Right: (count: 63, value: -5164589.978496086)
 84%|████████▎ | 1056/1261 [08:18<01:37,  2.11it/s]
Skip Left:  (count: 61, value: 19345.05949523289)
Skip Right: (count: 64, value: -1749346.2504681796)
 84%|████████▍ | 1057/1261 [08:18<01:36,  2.12it/s]
Skip Left:  (count: 62, value: 24087.970658690625)
 84%|████████▍ | 1058/1261 [08:19<01:35,  2.13it/s]
Skip Left:  (count: 63, value: 23066.1577899482)
 84%|████████▍ | 1059/1261 [08:19<01:34,  2.13it/s]
Skip Left:  (count: 64, value: 24902.487288277036)
 84%|████████▍ | 1060/1261 [08:19<01:33,  2.15it/s]
Skip Left:  (count: 65, value: 24462.62605895884)
Skip Right: (count: 65, value: -54353.46993232443)
 84%|████████▍ | 1061/1261 [08:20<01:32,  2.15it/s]
Skip Left:  (count: 66, value: 26341.6177775303)
 84%|████████▍ | 1062/1261 [08:20<01:32,  2.15it/s]
Skip Left:  (count: 67, value: 23603.40932034036)
 85%|████████▍ | 1066/1261 [08:22<01:30,  2.15it/s]
Skip Left:  (count: 68, value: -15014.48702295368)
Skip Right: (count: 66, value: 38614.26653775359)
 85%|████████▍ | 1067/1261 [08:23<01:30,  2.15it/s]
Skip Left:  (count: 69, value: -12593.514888804839)
 85%|████████▍ | 1068/1261 [08:23<01:30,  2.14it/s]
Skip Left:  (count: 70, value: -13894.28501618943)
 85%|████████▌ | 1078/1261 [08:28<01:26,  2.13it/s]
Skip Right: (count: 67, value: 55538.75853208285)
 86%|████████▌ | 1079/1261 [08:28<01:25,  2.12it/s]
Skip Right: (count: 68, value: 44475.982312790984)
 86%|████████▌ | 1080/1261 [08:29<01:26,  2.10it/s]
Skip Right: (count: 69, value: 42748.8748096157)
 87%|████████▋ | 1095/1261 [08:36<01:18,  2.12it/s]
Skip Right: (count: 70, value: 93871.36117763573)
 89%|████████▉ | 1123/1261 [08:49<01:05,  2.12it/s]
Skip Left:  (count: 71, value: -10042.196045857272)
 95%|█████████▌| 1200/1261 [09:26<00:29,  2.09it/s]
Skip Left:  (count: 72, value: -11516.338730503561)
 96%|█████████▌| 1206/1261 [09:29<00:26,  2.11it/s]
Skip Left:  (count: 73, value: 10957.372663045113)
 96%|█████████▌| 1207/1261 [09:29<00:25,  2.11it/s]
Skip Left:  (count: 74, value: 14260.162410441058)
 96%|█████████▌| 1208/1261 [09:30<00:25,  2.12it/s]
Skip Left:  (count: 75, value: 12580.703113699577)
 96%|█████████▌| 1209/1261 [09:30<00:24,  2.12it/s]
Skip Left:  (count: 76, value: 16473.84277009231)
 96%|█████████▌| 1210/1261 [09:31<00:24,  2.12it/s]
Skip Left:  (count: 77, value: 13050.630809396474)
 96%|█████████▌| 1211/1261 [09:31<00:23,  2.12it/s]
Skip Left:  (count: 78, value: 11113.501098520665)
 98%|█████████▊| 1236/1261 [09:43<00:11,  2.08it/s]
Skip Right: (count: 71, value: 46961.660718825944)
100%|█████████▉| 1260/1261 [09:54<00:00,  2.10it/s]
Skip Left:  (count: 79, value: -10243.583558640432)

[MoviePy] Done.
[MoviePy] >>>> Video ready: ./out_all.mp4 

CPU times: user 10min 52s, sys: 1min 23s, total: 12min 16s
Wall time: 9min 55s
Out[18]:

In [14]:
import pandas as pd

# Plot and find proper values to threshold diffs on

l = [x for x in LEFT_DIFFS if x > -10000 and x < 10000]

print(len(l))
print(len(LEFT_DIFFS))

left_series = pd.Series(l)
left_series.plot(title="Left Diffs")


1172
1251
Out[14]:
<matplotlib.axes._subplots.AxesSubplot at 0x124584cf8>

In [15]:
# Plot and find proper values to threshold diffs on

r = [x for x in RIGHT_DIFFS if x > -30000 and x < 30000]

print(len(r))
print(len(RIGHT_DIFFS))

right_series = pd.Series(r)
right_series.plot(title="Right Diffs")


1180
1251
Out[15]:
<matplotlib.axes._subplots.AxesSubplot at 0x11e800e48>

In [ ]: