Rover Project Test Notebook

This notebook contains the functions from the lesson and provides the scaffolding you need to test out your mapping methods. The steps you need to complete in this notebook for the project are the following:

  • First just run each of the cells in the notebook, examine the code and the results of each.
  • Run the simulator in "Training Mode" and record some data. Note: the simulator may crash if you try to record a large (longer than a few minutes) dataset, but you don't need a ton of data, just some example images to work with.
  • Change the data directory path (2 cells below) to be the directory where you saved data
  • Test out the functions provided on your data
  • Write new functions (or modify existing ones) to report and map out detections of obstacles and rock samples (yellow rocks)
  • Populate the process_image() function with the appropriate steps/functions to go from a raw image to a worldmap.
  • Run the cell that calls process_image() using moviepy functions to create video output
  • Once you have mapping working, move on to modifying perception.py and decision.py to allow your rover to navigate and map in autonomous mode!

Note: If, at any point, you encounter frozen display windows or other confounding issues, you can always start again with a clean slate by going to the "Kernel" menu above and selecting "Restart & Clear Output".

Run the next cell to get code highlighting in the markdown cells.


In [1]:
%%HTML
<style> code {background-color : orange !important;} </style>



In [2]:
%matplotlib inline
#%matplotlib qt # Choose %matplotlib qt to plot to an interactive window (note it may show up behind your browser)
# Make some of the relevant imports
import cv2 # OpenCV for perspective transform
import numpy as np
import matplotlib.image as mpimg
import matplotlib.pyplot as plt
import scipy.misc # For saving images as needed
import glob  # For reading in a list of images from a folder

Quick Look at the Data

There's some example data provided in the test_dataset folder. This basic dataset is enough to get you up and running but if you want to hone your methods more carefully you should record some data of your own to sample various scenarios in the simulator.

Next, read in and display a random image from the test_dataset folder


In [31]:
path = '../test_dataset/IMG/*'
img_list = glob.glob(path)
# Grab a random image and display it
idx = np.random.randint(0, len(img_list)-1)
image = mpimg.imread(img_list[idx])
plt.imshow(image)


Out[31]:
<matplotlib.image.AxesImage at 0x7f224617a5c0>

In [33]:
path = '../sim_dataset/IMG/*'
img_list = glob.glob(path)
# Grab a random image and display it
idx = np.random.randint(0, len(img_list)-1)
image = mpimg.imread(img_list[idx])
plt.imshow(image)


Out[33]:
<matplotlib.image.AxesImage at 0x7f2245a5a160>

Calibration Data

Read in and display example grid and rock sample calibration images. You'll use the grid for perspective transform and the rock image for creating a new color selection that identifies these samples of interest.


In [34]:
# In the simulator you can toggle on a grid on the ground for calibration
# You can also toggle on the rock samples with the 0 (zero) key.  
# Here's an example of the grid and one of the rocks
example_grid = '../calibration_images/example_grid1.jpg'
example_rock = '../calibration_images/example_rock1.jpg'
grid_img = mpimg.imread(example_grid)
rock_img = mpimg.imread(example_rock)

fig = plt.figure(figsize=(12,3))
plt.subplot(121)
plt.imshow(grid_img)
plt.subplot(122)
plt.imshow(rock_img)


Out[34]:
<matplotlib.image.AxesImage at 0x7f2245a90390>

Perspective Transform

Define the perspective transform function from the lesson and test it on an image.


In [35]:
# Define a function to perform a perspective transform
# I've used the example grid image above to choose source points for the
# grid cell in front of the rover (each grid cell is 1 square meter in the sim)
# Define a function to perform a perspective transform
def perspect_transform(img, src, dst):
           
    M = cv2.getPerspectiveTransform(src, dst)
    warped = cv2.warpPerspective(img, M, (img.shape[1], img.shape[0]))# keep same size as input image
    
    return warped


# Define calibration box in source (actual) and destination (desired) coordinates
# These source and destination points are defined to warp the image
# to a grid where each 10x10 pixel square represents 1 square meter
# The destination box will be 2*dst_size on each side
dst_size = 5 
# Set a bottom offset to account for the fact that the bottom of the image 
# is not the position of the rover but a bit in front of it
# this is just a rough guess, feel free to change it!
bottom_offset = 6
source = np.float32([[14, 140], [301 ,140],[200, 96], [118, 96]])
destination = np.float32([[image.shape[1]/2 - dst_size, image.shape[0] - bottom_offset],
                  [image.shape[1]/2 + dst_size, image.shape[0] - bottom_offset],
                  [image.shape[1]/2 + dst_size, image.shape[0] - 2*dst_size - bottom_offset], 
                  [image.shape[1]/2 - dst_size, image.shape[0] - 2*dst_size - bottom_offset],
                  ])
warped = perspect_transform(grid_img, source, destination)
plt.imshow(warped)
#scipy.misc.imsave('../output/warped_example.jpg', warped)


Out[35]:
<matplotlib.image.AxesImage at 0x7f2245222780>

Color Thresholding

Define the color thresholding function from the lesson and apply it to the warped image

TODO: Ultimately, you want your map to not just include navigable terrain but also obstacles and the positions of the rock samples you're searching for. Modify this function or write a new function that returns the pixel locations of obstacles (areas below the threshold) and rock samples (yellow rocks in calibration images), such that you can map these areas into world coordinates as well.
Hints and Suggestion:

  • For obstacles you can just invert your color selection that you used to detect ground pixels, i.e., if you've decided that everything above the threshold is navigable terrain, then everthing below the threshold must be an obstacle!
  • For rocks, think about imposing a lower and upper boundary in your color selection to be more specific about choosing colors. You can investigate the colors of the rocks (the RGB pixel values) in an interactive matplotlib window to get a feel for the appropriate threshold range (keep in mind you may want different ranges for each of R, G and B!). Feel free to get creative and even bring in functions from other libraries. Here's an example of color selection using OpenCV.

  • Beware However: if you start manipulating images with OpenCV, keep in mind that it defaults to BGR instead of RGB color space when reading/writing images, so things can get confusing.


In [97]:
# Identify pixels above the threshold
# Threshold of RGB > 160 does a nice job of identifying ground pixels only
def color_thresh(img, rgb_thresh=(160, 160, 160)):
    # Create an array of zeros same xy size as img, but single channel
    color_select = np.zeros_like(img[:,:,0])
    # Require that each pixel be above all three threshold values in RGB
    # above_thresh will now contain a boolean array with "True"
    # where threshold was met
    above_thresh = (img[:,:,0] > rgb_thresh[0]) \
                & (img[:,:,1] > rgb_thresh[1]) \
                & (img[:,:,2] > rgb_thresh[2])
    # Index the array of zeros with the boolean array and set to 1
    color_select[above_thresh] = 1
    # Return the binary image
    return color_select

def obstacle_thresh(img, rgb_thresh=(160, 160, 160)):
    # Create an array of zeros same xy size as img, but single channel
    color_select = np.zeros_like(img[:,:,0])
    # Require that each pixel be above all three threshold values in RGB
    # above_thresh will now contain a boolean array with "True"
    # where threshold was met
    above_thresh = (img[:,:,0] <= rgb_thresh[0]) \
                & (img[:,:,1] <= rgb_thresh[1]) \
                & (img[:,:,2] <= rgb_thresh[2])
    # Index the array of zeros with the boolean array and set to 1
    color_select[above_thresh] = 1
    # Return the binary image
    return color_select


def rock_thresh(img, threshold_low=(100, 100, 0), threshold_high=(160, 160, 40)):
    # Create an array of zeros same xy size as img, but single channel
    color_select = np.zeros_like(img[:, :, 0])
    # Require that each pixel be above all three threshold values in RGB
    # above_thresh will now contain a boolean array with "True"
    # where threshold was met
    above_thresh = ~((img[:, :, 0] > threshold_low[0]) & (img[:, :, 0] < threshold_high[0]) \
                     & (img[:, :, 1] > threshold_low[1]) & (img[:, :, 1] < threshold_high[1]) \
                     & (img[:, :, 2] > threshold_low[2]) & (img[:, :, 2] < threshold_high[2]))
    # Index the array of zeros with the boolean array and set to 1
    color_select[above_thresh] = 1
    return color_select

threshed = color_thresh(warped)
plt.imshow(threshed, cmap='gray')
#scipy.misc.imsave('../output/warped_threshed.jpg', threshed*255)


Out[97]:
<matplotlib.image.AxesImage at 0x7f223b5f2a20>

In [98]:
threshed_rock = color_thresh(rock_img, (23,40,133))
plt.imshow(threshed_rock, cmap='gray')


Out[98]:
<matplotlib.image.AxesImage at 0x7f223b5568d0>

Coordinate Transformations

Define the functions used to do coordinate transforms and apply them to an image.


In [100]:
def rover_coords(binary_img):
    # Identify nonzero pixels
    ypos, xpos = binary_img.nonzero()
    # Calculate pixel positions with reference to the rover position being at the 
    # center bottom of the image.  
    x_pixel = np.absolute(ypos - binary_img.shape[0]).astype(np.float)
    y_pixel = -(xpos - binary_img.shape[0]).astype(np.float)
    return x_pixel, y_pixel

# Define a function to convert to radial coords in rover space
def to_polar_coords(x_pixel, y_pixel):
    # Convert (x_pixel, y_pixel) to (distance, angle) 
    # in polar coordinates in rover space
    # Calculate distance to each pixel
    dist = np.sqrt(x_pixel**2 + y_pixel**2)
    # Calculate angle away from vertical for each pixel
    angles = np.arctan2(y_pixel, x_pixel)
    return dist, angles

# Define a function to map rover space pixels to world space
def pix_to_world(xpix, ypix, x_rover, y_rover, yaw_rover, world_size, scale):
    # Map pixels from rover space to world coords
    yaw = yaw_rover * np.pi / 180
    # Perform rotation, translation and clipping all at once
    x_pix_world = np.clip(np.int_((((xpix * np.cos(yaw)) - (ypix * np.sin(yaw)))/scale) + x_rover), 
                            0, world_size - 1)
    y_pix_world = np.clip(np.int_((((xpix * np.sin(yaw)) + (ypix * np.cos(yaw)))/scale) + y_rover), 
                            0, world_size - 1)
  
    return x_pix_world, y_pix_world

# Grab another random image
idx = np.random.randint(0, len(img_list)-1)
image = mpimg.imread(img_list[idx])
warped = perspect_transform(image, source, destination)
threshed = color_thresh(warped)

# Calculate pixel values in rover-centric coords and distance/angle to all pixels
xpix, ypix = rover_coords(threshed)
dist, angles = to_polar_coords(xpix, ypix)
mean_dir = np.mean(angles)

# Do some plotting
fig = plt.figure(figsize=(12,9))
plt.subplot(221)
plt.imshow(image)
plt.subplot(222)
plt.imshow(warped)
plt.subplot(223)
plt.imshow(threshed, cmap='gray')
plt.subplot(224)
plt.plot(xpix, ypix, '.')
plt.ylim(-160, 160)
plt.xlim(0, 160)
arrow_length = 100
x_arrow = arrow_length * np.cos(mean_dir)
y_arrow = arrow_length * np.sin(mean_dir)
plt.arrow(0, 0, x_arrow, y_arrow, color='red', zorder=2, head_width=10, width=2)


Out[100]:
<matplotlib.patches.FancyArrow at 0x7f223b4644e0>

Read in saved data and ground truth map of the world

The next cell is all setup to read your saved data into a pandas dataframe. Here you'll also read in a "ground truth" map of the world, where white pixels (pixel value = 1) represent navigable terrain.

After that, we'll define a class to store telemetry data and pathnames to images. When you instantiate this class (data = Databucket()) you'll have a global variable called data that you can refer to for telemetry and map data within the process_image() function in the following cell.


In [119]:
# Import pandas and read in csv file as a dataframe
import pandas as pd
# Change this path to your data directory
df = pd.read_csv('../sim_2_dataset/robot_log.csv')
img_list_sorted = df["Path"].tolist() # Create list of image pathnames
# Read in ground truth map and create a 3-channel image with it
ground_truth = mpimg.imread('../calibration_images/map_bw.png')
ground_truth_3d = np.dstack((ground_truth*0, ground_truth, ground_truth*0)).astype(np.float)

# Creating a class to be the data container
# Will read in saved data from csv file and populate this object
# Worldmap is instantiated as 200 x 200 grids corresponding 
# to a 200m x 200m space (same size as the ground truth map: 200 x 200 pixels)
# This encompasses the full range of output position values in x and y from the sim
class Databucket():
    def __init__(self):
        self.images = img_list_sorted  
        self.xpos = df["X_Position"].values
        self.ypos = df["Y_Position"].values
        self.yaw = df["Yaw"].values
        self.count = 0
        self.worldmap = np.zeros((200, 200, 3)).astype(np.float)
        self.ground_truth = ground_truth_3d # Ground truth worldmap

# Instantiate a Databucket().. this will be a global variable/object
# that you can refer to in the process_image() function below
data = Databucket()

Write a function to process stored images

Modify the process_image() function below by adding in the perception step processes (functions defined above) to perform image analysis and mapping. The following cell is all set up to use this process_image() function in conjunction with the moviepy video processing package to create a video from the images you saved taking data in the simulator.

In short, you will be passing individual images into process_image() and building up an image called output_image that will be stored as one frame of video. You can make a mosaic of the various steps of your analysis process and add text as you like (example provided below).

To start with, you can simply run the next three cells to see what happens, but then go ahead and modify them such that the output video demonstrates your mapping process. Feel free to get creative!


In [124]:
# Define a function to pass stored images to
# reading rover position and yaw angle from csv file
# This function will be used by moviepy to create an output video
def process_image(img):
    
    # Example of how to use the Databucket() object defined in the previous cell
    # print(data.xpos[0], data.ypos[0], data.yaw[0])
    warp = perspect_transform(img, source, destination)
    color_thre = color_thresh(warp, rgb_thresh=(160, 160, 160))
    obstacle_thre = obstacle_thresh(warp)
    rock_thre = rock_thresh(warp)
    
    xpix0, ypix0 = rover_coords(obstacle_thre)    
    xpix1, ypix1 = rover_coords(rock_thre)    
    xpix2, ypix2 = rover_coords(color_thre) 
    
    scale = 20
    worldsize = 200
    
    rover_obstacle = pix_to_world(xpix0, ypix0, data.xpos[data.count], data.ypos[data.count],data.yaw[data.count], worldsize, scale)
    rover_rock = pix_to_world(xpix1, ypix1, data.xpos[data.count], data.ypos[data.count],data.yaw[data.count], worldsize, scale)
    rover_terrain = pix_to_world(xpix2, ypix2, data.xpos[data.count], data.ypos[data.count],data.yaw[data.count], worldsize, scale)
    
    
    dist, angles = to_polar_coords(xpix, ypix)
    
    color_thre = np.dstack((color_thre, color_thre, color_thre)).astype(np.float)
    
    x_rover = data.xpos[data.count]
    y_rover = data.ypos[data.count]
    yaw_rover = data.yaw[data.count]
    
    data.worldmap[rover_obstacle[1], rover_obstacle[0], :] = [255,0,0]   
    data.worldmap[rover_rock[1]    , rover_rock[0]    , :] = [0,255,255]  
    data.worldmap[rover_terrain[1] , rover_terrain[0] , :] = [0,255,0]  
    
    output_image = np.zeros((img.shape[0] + data.worldmap.shape[0], img.shape[1]*2, 3))
    
    output_image[0:img.shape[0], 0:img.shape[1]] = img
    output_image[0:img.shape[0], img.shape[1]:2*img.shape[1]] = warp
    
    output_image[img.shape[0]:img.shape[0]*2, 0:img.shape[1]] = color_thre *255  #bottom left
    output_image[img.shape[0]:img.shape[0]+data.worldmap.shape[0], img.shape[1]:img.shape[1]+ data.worldmap.shape[1],:] = data.worldmap #bottom right
    cv2.putText(output_image, "Rover Camera View", (20, 20), 
                cv2.FONT_HERSHEY_COMPLEX, 0.4, (255, 255, 255), 1)
    cv2.putText(output_image, "Birds Eye View", (350, 20), 
                cv2.FONT_HERSHEY_COMPLEX, 0.4, (255, 255, 255), 1)
    cv2.putText(output_image, "Threshold", (20, 180), 
                cv2.FONT_HERSHEY_COMPLEX, 0.4, (255, 255, 255), 1)
    cv2.putText(output_image, "WorldMap", (350, 180), 
                cv2.FONT_HERSHEY_COMPLEX, 0.4, (255, 255, 255), 1)
    data.count += 1 # Keep track of the index in the Databucket()
    if data.count == 921:  # to overcome some error for index out of bounds
        data.count =0
        
    return output_image

Make a video from processed image data

Use the moviepy library to process images and create a video.


In [125]:
# Import everything needed to edit/save/watch video clips
from moviepy.editor import VideoFileClip
from moviepy.editor import ImageSequenceClip


# Define pathname to save the output video
output = '../output/test_mapping2.mp4'
data = Databucket() # Re-initialize data in case you're running this cell multiple times
clip = ImageSequenceClip(data.images, fps=60) # Note: output video will be sped up because 
                                          # recording rate in simulator is fps=25
new_clip = clip.fl_image(process_image) #NOTE: this function expects color images!!
%time new_clip.write_videofile(output, audio=False)


[MoviePy] >>>> Building video ../output/test_mapping2.mp4
[MoviePy] Writing video ../output/test_mapping2.mp4
  0%|          | 0/922 [00:00<?, ?it/s]
  0%|          | 2/922 [00:00<00:46, 19.69it/s]
  0%|          | 4/922 [00:00<00:49, 18.64it/s]
  1%|          | 6/922 [00:00<00:48, 18.81it/s]
  1%|          | 8/922 [00:00<00:49, 18.54it/s]
  1%|          | 10/922 [00:00<00:52, 17.32it/s]
  1%|▏         | 12/922 [00:00<00:53, 17.13it/s]
  2%|▏         | 14/922 [00:00<00:52, 17.13it/s]
  2%|▏         | 16/922 [00:00<00:53, 17.09it/s]
  2%|▏         | 18/922 [00:01<00:52, 17.25it/s]
  2%|▏         | 20/922 [00:01<00:51, 17.42it/s]
  2%|▏         | 22/922 [00:01<00:52, 17.03it/s]
  3%|▎         | 24/922 [00:01<00:50, 17.81it/s]
  3%|▎         | 26/922 [00:01<00:51, 17.37it/s]
  3%|▎         | 28/922 [00:01<00:56, 15.73it/s]
  3%|▎         | 30/922 [00:01<00:54, 16.27it/s]
  3%|▎         | 32/922 [00:01<00:53, 16.73it/s]
  4%|▎         | 34/922 [00:01<00:52, 17.03it/s]
  4%|▍         | 36/922 [00:02<00:51, 17.21it/s]
  4%|▍         | 38/922 [00:02<00:49, 17.82it/s]
  4%|▍         | 40/922 [00:02<00:49, 17.81it/s]
  5%|▍         | 42/922 [00:02<00:48, 18.05it/s]
  5%|▍         | 44/922 [00:02<00:53, 16.30it/s]
  5%|▍         | 46/922 [00:02<00:50, 17.21it/s]
  5%|▌         | 48/922 [00:02<00:51, 17.08it/s]
  5%|▌         | 50/922 [00:02<00:51, 16.78it/s]
  6%|▌         | 52/922 [00:03<00:52, 16.70it/s]
  6%|▌         | 54/922 [00:03<00:52, 16.69it/s]
  6%|▌         | 56/922 [00:03<00:53, 16.14it/s]
  6%|▋         | 58/922 [00:03<00:52, 16.37it/s]
  7%|▋         | 60/922 [00:03<00:55, 15.67it/s]
  7%|▋         | 62/922 [00:03<00:54, 15.76it/s]
  7%|▋         | 64/922 [00:03<00:54, 15.89it/s]
  7%|▋         | 66/922 [00:03<00:52, 16.24it/s]
  7%|▋         | 68/922 [00:04<00:52, 16.34it/s]
  8%|▊         | 70/922 [00:04<00:51, 16.71it/s]
  8%|▊         | 72/922 [00:04<00:50, 16.75it/s]
  8%|▊         | 74/922 [00:04<00:49, 17.18it/s]
  8%|▊         | 77/922 [00:04<00:46, 18.34it/s]
  9%|▊         | 79/922 [00:04<00:47, 17.72it/s]
  9%|▉         | 81/922 [00:04<00:48, 17.31it/s]
  9%|▉         | 83/922 [00:04<00:50, 16.72it/s]
  9%|▉         | 85/922 [00:05<00:52, 15.94it/s]
  9%|▉         | 87/922 [00:05<00:52, 15.91it/s]
 10%|▉         | 89/922 [00:05<00:53, 15.66it/s]
 10%|▉         | 91/922 [00:05<00:53, 15.51it/s]
 10%|█         | 93/922 [00:05<00:54, 15.29it/s]
 10%|█         | 95/922 [00:05<00:54, 15.08it/s]
 11%|█         | 97/922 [00:05<00:54, 15.25it/s]
 11%|█         | 99/922 [00:05<00:53, 15.41it/s]
 11%|█         | 101/922 [00:06<00:53, 15.46it/s]
 11%|█         | 103/922 [00:06<00:50, 16.23it/s]
 11%|█▏        | 105/922 [00:06<00:48, 16.85it/s]
 12%|█▏        | 107/922 [00:06<00:49, 16.37it/s]
 12%|█▏        | 109/922 [00:06<00:50, 16.07it/s]
 12%|█▏        | 112/922 [00:06<00:46, 17.30it/s]
 12%|█▏        | 114/922 [00:06<00:47, 17.17it/s]
 13%|█▎        | 116/922 [00:06<00:45, 17.80it/s]
 13%|█▎        | 119/922 [00:07<00:43, 18.54it/s]
 13%|█▎        | 121/922 [00:07<00:46, 17.33it/s]
 13%|█▎        | 123/922 [00:07<00:45, 17.39it/s]
 14%|█▎        | 125/922 [00:07<00:46, 17.24it/s]
 14%|█▍        | 127/922 [00:07<00:48, 16.29it/s]
 14%|█▍        | 129/922 [00:07<00:47, 16.82it/s]
 14%|█▍        | 131/922 [00:07<00:49, 15.96it/s]
 14%|█▍        | 133/922 [00:07<00:48, 16.38it/s]
 15%|█▍        | 135/922 [00:08<00:49, 15.85it/s]
 15%|█▍        | 137/922 [00:08<00:47, 16.47it/s]
 15%|█▌        | 139/922 [00:08<00:49, 15.78it/s]
 15%|█▌        | 141/922 [00:08<00:50, 15.39it/s]
 16%|█▌        | 143/922 [00:08<00:50, 15.29it/s]
 16%|█▌        | 145/922 [00:08<00:48, 15.89it/s]
 16%|█▌        | 147/922 [00:08<00:48, 15.97it/s]
 16%|█▌        | 149/922 [00:08<00:48, 15.78it/s]
 16%|█▋        | 151/922 [00:09<00:53, 14.31it/s]
 17%|█▋        | 153/922 [00:09<00:55, 13.98it/s]
 17%|█▋        | 155/922 [00:09<00:58, 13.08it/s]
 17%|█▋        | 157/922 [00:09<00:57, 13.40it/s]
 17%|█▋        | 159/922 [00:09<00:54, 13.96it/s]
 17%|█▋        | 161/922 [00:09<00:53, 14.21it/s]
 18%|█▊        | 163/922 [00:09<00:51, 14.61it/s]
 18%|█▊        | 165/922 [00:10<00:52, 14.56it/s]
 18%|█▊        | 167/922 [00:10<00:50, 14.82it/s]
 18%|█▊        | 169/922 [00:10<00:48, 15.69it/s]
 19%|█▊        | 171/922 [00:10<00:46, 16.10it/s]
 19%|█▉        | 173/922 [00:10<00:46, 16.22it/s]
 19%|█▉        | 175/922 [00:10<00:48, 15.47it/s]
 19%|█▉        | 177/922 [00:10<00:45, 16.20it/s]
 19%|█▉        | 179/922 [00:10<00:44, 16.64it/s]
 20%|█▉        | 181/922 [00:11<00:45, 16.22it/s]
 20%|█▉        | 183/922 [00:11<00:46, 16.05it/s]
 20%|██        | 185/922 [00:11<00:49, 14.80it/s]
 20%|██        | 187/922 [00:11<00:55, 13.33it/s]
 20%|██        | 189/922 [00:11<00:50, 14.50it/s]
 21%|██        | 192/922 [00:11<00:48, 15.02it/s]
 21%|██        | 194/922 [00:11<00:48, 15.00it/s]
 21%|██▏       | 196/922 [00:12<00:50, 14.35it/s]
 21%|██▏       | 198/922 [00:12<00:48, 15.01it/s]
 22%|██▏       | 200/922 [00:12<00:48, 14.77it/s]
 22%|██▏       | 202/922 [00:12<00:46, 15.52it/s]
 22%|██▏       | 204/922 [00:12<00:50, 14.25it/s]
 22%|██▏       | 206/922 [00:12<00:50, 14.06it/s]
 23%|██▎       | 208/922 [00:12<00:49, 14.46it/s]
 23%|██▎       | 210/922 [00:13<00:45, 15.67it/s]
 23%|██▎       | 212/922 [00:13<00:44, 15.94it/s]
 23%|██▎       | 214/922 [00:13<00:46, 15.19it/s]
 23%|██▎       | 216/922 [00:13<00:46, 15.23it/s]
 24%|██▎       | 218/922 [00:13<00:44, 15.78it/s]
 24%|██▍       | 220/922 [00:13<00:43, 15.97it/s]
 24%|██▍       | 222/922 [00:13<00:43, 16.26it/s]
 24%|██▍       | 224/922 [00:13<00:46, 15.04it/s]
 25%|██▍       | 226/922 [00:14<00:50, 13.89it/s]
 25%|██▍       | 228/922 [00:14<00:48, 14.29it/s]
 25%|██▍       | 230/922 [00:14<00:46, 14.84it/s]
 25%|██▌       | 232/922 [00:14<00:43, 15.93it/s]
 25%|██▌       | 234/922 [00:14<00:42, 16.00it/s]
 26%|██▌       | 236/922 [00:14<00:43, 15.68it/s]
 26%|██▌       | 238/922 [00:14<00:43, 15.81it/s]
 26%|██▌       | 240/922 [00:15<00:44, 15.24it/s]
 26%|██▋       | 243/922 [00:15<00:41, 16.25it/s]
 27%|██▋       | 245/922 [00:15<00:41, 16.15it/s]
 27%|██▋       | 247/922 [00:15<00:43, 15.54it/s]
 27%|██▋       | 249/922 [00:15<00:47, 14.04it/s]
 27%|██▋       | 251/922 [00:15<00:46, 14.47it/s]
 27%|██▋       | 253/922 [00:15<00:44, 15.20it/s]
 28%|██▊       | 255/922 [00:15<00:43, 15.18it/s]
 28%|██▊       | 257/922 [00:16<00:43, 15.21it/s]
 28%|██▊       | 259/922 [00:16<00:43, 15.17it/s]
 28%|██▊       | 261/922 [00:16<00:42, 15.67it/s]
 29%|██▊       | 263/922 [00:16<00:44, 14.81it/s]
 29%|██▊       | 265/922 [00:16<00:44, 14.87it/s]
 29%|██▉       | 267/922 [00:16<00:43, 15.09it/s]
 29%|██▉       | 269/922 [00:16<00:43, 15.04it/s]
 29%|██▉       | 271/922 [00:17<00:43, 15.11it/s]
 30%|██▉       | 273/922 [00:17<00:43, 14.93it/s]
 30%|██▉       | 275/922 [00:17<00:44, 14.45it/s]
 30%|███       | 277/922 [00:17<00:42, 15.24it/s]
 30%|███       | 280/922 [00:17<00:39, 16.40it/s]
 31%|███       | 282/922 [00:17<00:40, 15.95it/s]
 31%|███       | 284/922 [00:17<00:38, 16.36it/s]
 31%|███       | 286/922 [00:17<00:38, 16.49it/s]
 31%|███       | 288/922 [00:18<00:42, 15.07it/s]
 31%|███▏      | 290/922 [00:18<00:40, 15.64it/s]
 32%|███▏      | 292/922 [00:18<00:38, 16.22it/s]
 32%|███▏      | 294/922 [00:18<00:44, 14.13it/s]
 32%|███▏      | 296/922 [00:18<00:43, 14.23it/s]
 32%|███▏      | 298/922 [00:18<00:43, 14.39it/s]
 33%|███▎      | 300/922 [00:18<00:40, 15.44it/s]
 33%|███▎      | 302/922 [00:19<00:41, 14.79it/s]
 33%|███▎      | 304/922 [00:19<00:42, 14.45it/s]
 33%|███▎      | 306/922 [00:19<00:48, 12.79it/s]
 33%|███▎      | 308/922 [00:19<00:44, 13.78it/s]
 34%|███▎      | 310/922 [00:19<00:44, 13.82it/s]
 34%|███▍      | 312/922 [00:19<00:42, 14.23it/s]
 34%|███▍      | 314/922 [00:19<00:42, 14.34it/s]
 34%|███▍      | 316/922 [00:20<00:42, 14.41it/s]
 34%|███▍      | 318/922 [00:20<00:41, 14.69it/s]
 35%|███▍      | 320/922 [00:20<00:38, 15.52it/s]
 35%|███▍      | 322/922 [00:20<00:39, 15.38it/s]
 35%|███▌      | 324/922 [00:20<00:41, 14.47it/s]
 35%|███▌      | 326/922 [00:20<00:41, 14.40it/s]
 36%|███▌      | 328/922 [00:20<00:38, 15.58it/s]
 36%|███▌      | 330/922 [00:21<00:38, 15.30it/s]
 36%|███▌      | 332/922 [00:21<00:39, 15.12it/s]
 36%|███▌      | 334/922 [00:21<00:39, 14.96it/s]
 36%|███▋      | 336/922 [00:21<00:39, 14.66it/s]
 37%|███▋      | 338/922 [00:21<00:40, 14.36it/s]
 37%|███▋      | 340/922 [00:21<00:37, 15.55it/s]
 37%|███▋      | 342/922 [00:21<00:36, 15.85it/s]
 37%|███▋      | 344/922 [00:21<00:34, 16.83it/s]
 38%|███▊      | 346/922 [00:22<00:37, 15.52it/s]
 38%|███▊      | 348/922 [00:22<00:36, 15.67it/s]
 38%|███▊      | 350/922 [00:22<00:34, 16.40it/s]
 38%|███▊      | 353/922 [00:22<00:32, 17.32it/s]
 39%|███▊      | 355/922 [00:22<00:31, 17.96it/s]
 39%|███▊      | 357/922 [00:22<00:32, 17.17it/s]
 39%|███▉      | 359/922 [00:22<00:41, 13.50it/s]
 39%|███▉      | 361/922 [00:23<00:41, 13.66it/s]
 39%|███▉      | 363/922 [00:23<00:43, 12.94it/s]
 40%|███▉      | 365/922 [00:23<00:42, 13.23it/s]
 40%|███▉      | 367/922 [00:23<00:43, 12.90it/s]
 40%|████      | 369/922 [00:23<00:42, 13.03it/s]
 40%|████      | 371/922 [00:23<00:42, 13.00it/s]
 40%|████      | 373/922 [00:23<00:40, 13.43it/s]
 41%|████      | 375/922 [00:24<00:39, 13.86it/s]
 41%|████      | 377/922 [00:24<00:38, 14.21it/s]
 41%|████      | 379/922 [00:24<00:39, 13.64it/s]
 41%|████▏     | 381/922 [00:24<00:39, 13.83it/s]
 42%|████▏     | 383/922 [00:24<00:38, 14.11it/s]
 42%|████▏     | 385/922 [00:24<00:41, 13.06it/s]
 42%|████▏     | 387/922 [00:24<00:38, 13.87it/s]
 42%|████▏     | 389/922 [00:25<00:39, 13.36it/s]
 42%|████▏     | 391/922 [00:25<00:42, 12.53it/s]
 43%|████▎     | 393/922 [00:25<00:39, 13.32it/s]
 43%|████▎     | 395/922 [00:25<00:38, 13.57it/s]
 43%|████▎     | 397/922 [00:25<00:38, 13.67it/s]
 43%|████▎     | 399/922 [00:25<00:38, 13.53it/s]
 43%|████▎     | 401/922 [00:26<00:38, 13.44it/s]
 44%|████▎     | 403/922 [00:26<00:36, 14.22it/s]
 44%|████▍     | 405/922 [00:26<00:37, 13.89it/s]
 44%|████▍     | 407/922 [00:26<00:37, 13.58it/s]
 44%|████▍     | 409/922 [00:26<00:39, 13.08it/s]
 45%|████▍     | 411/922 [00:26<00:36, 14.11it/s]
 45%|████▍     | 413/922 [00:26<00:33, 15.39it/s]
 45%|████▌     | 415/922 [00:26<00:33, 15.15it/s]
 45%|████▌     | 418/922 [00:27<00:30, 16.50it/s]
 46%|████▌     | 420/922 [00:27<00:30, 16.39it/s]
 46%|████▌     | 422/922 [00:27<00:30, 16.63it/s]
 46%|████▌     | 424/922 [00:27<00:29, 16.65it/s]
 46%|████▌     | 426/922 [00:27<00:29, 16.77it/s]
 46%|████▋     | 428/922 [00:27<00:29, 16.79it/s]
 47%|████▋     | 431/922 [00:27<00:27, 17.96it/s]
 47%|████▋     | 433/922 [00:27<00:28, 17.17it/s]
 47%|████▋     | 435/922 [00:28<00:28, 16.86it/s]
 47%|████▋     | 437/922 [00:28<00:29, 16.59it/s]
 48%|████▊     | 439/922 [00:28<00:28, 16.83it/s]
 48%|████▊     | 441/922 [00:28<00:28, 16.95it/s]
 48%|████▊     | 443/922 [00:28<00:28, 16.84it/s]
 48%|████▊     | 445/922 [00:28<00:27, 17.51it/s]
 48%|████▊     | 447/922 [00:28<00:26, 17.61it/s]
 49%|████▊     | 449/922 [00:28<00:28, 16.63it/s]
 49%|████▉     | 451/922 [00:29<00:27, 16.82it/s]
 49%|████▉     | 453/922 [00:29<00:27, 16.96it/s]
 49%|████▉     | 455/922 [00:29<00:29, 16.05it/s]
 50%|████▉     | 457/922 [00:29<00:32, 14.16it/s]
 50%|████▉     | 459/922 [00:29<00:32, 14.07it/s]
 50%|█████     | 461/922 [00:29<00:32, 14.20it/s]
 50%|█████     | 463/922 [00:29<00:32, 13.95it/s]
 50%|█████     | 465/922 [00:30<00:32, 14.09it/s]
 51%|█████     | 467/922 [00:30<00:31, 14.42it/s]
 51%|█████     | 469/922 [00:30<00:30, 14.92it/s]
 51%|█████     | 471/922 [00:30<00:28, 15.84it/s]
 51%|█████▏    | 473/922 [00:30<00:28, 15.97it/s]
 52%|█████▏    | 475/922 [00:30<00:27, 16.13it/s]
 52%|█████▏    | 477/922 [00:30<00:26, 17.09it/s]
 52%|█████▏    | 479/922 [00:30<00:25, 17.16it/s]
 52%|█████▏    | 481/922 [00:31<00:27, 15.95it/s]
 52%|█████▏    | 483/922 [00:31<00:28, 15.62it/s]
 53%|█████▎    | 485/922 [00:31<00:27, 15.79it/s]
 53%|█████▎    | 487/922 [00:31<00:28, 15.44it/s]
 53%|█████▎    | 489/922 [00:31<00:28, 15.36it/s]
 53%|█████▎    | 491/922 [00:31<00:27, 15.57it/s]
 53%|█████▎    | 493/922 [00:31<00:26, 16.20it/s]
 54%|█████▎    | 495/922 [00:31<00:25, 16.90it/s]
 54%|█████▍    | 497/922 [00:31<00:24, 17.30it/s]
 54%|█████▍    | 499/922 [00:32<00:24, 17.09it/s]
 54%|█████▍    | 501/922 [00:32<00:26, 15.68it/s]
 55%|█████▍    | 503/922 [00:32<00:27, 15.02it/s]
 55%|█████▍    | 505/922 [00:32<00:28, 14.61it/s]
 55%|█████▍    | 507/922 [00:32<00:28, 14.54it/s]
 55%|█████▌    | 509/922 [00:32<00:26, 15.37it/s]
 55%|█████▌    | 511/922 [00:32<00:27, 15.14it/s]
 56%|█████▌    | 513/922 [00:33<00:27, 15.00it/s]
 56%|█████▌    | 515/922 [00:33<00:26, 15.38it/s]
 56%|█████▌    | 517/922 [00:33<00:27, 14.48it/s]
 56%|█████▋    | 519/922 [00:33<00:29, 13.47it/s]
 57%|█████▋    | 521/922 [00:33<00:28, 13.99it/s]
 57%|█████▋    | 523/922 [00:33<00:29, 13.31it/s]
 57%|█████▋    | 525/922 [00:33<00:28, 13.87it/s]
 57%|█████▋    | 527/922 [00:34<00:27, 14.35it/s]
 57%|█████▋    | 529/922 [00:34<00:26, 14.85it/s]
 58%|█████▊    | 531/922 [00:34<00:27, 14.33it/s]
 58%|█████▊    | 533/922 [00:34<00:26, 14.43it/s]
 58%|█████▊    | 535/922 [00:34<00:33, 11.65it/s]
 58%|█████▊    | 537/922 [00:34<00:29, 13.07it/s]
 58%|█████▊    | 539/922 [00:34<00:28, 13.29it/s]
 59%|█████▊    | 541/922 [00:35<00:26, 14.60it/s]
 59%|█████▉    | 543/922 [00:35<00:26, 14.19it/s]
 59%|█████▉    | 545/922 [00:35<00:26, 14.46it/s]
 59%|█████▉    | 547/922 [00:35<00:26, 14.09it/s]
 60%|█████▉    | 549/922 [00:35<00:24, 14.93it/s]
 60%|█████▉    | 551/922 [00:35<00:26, 13.97it/s]
 60%|█████▉    | 553/922 [00:35<00:26, 13.81it/s]
 60%|██████    | 555/922 [00:36<00:26, 14.08it/s]
 60%|██████    | 557/922 [00:36<00:24, 14.88it/s]
 61%|██████    | 559/922 [00:36<00:23, 15.37it/s]
 61%|██████    | 561/922 [00:36<00:23, 15.53it/s]
 61%|██████    | 563/922 [00:36<00:21, 16.45it/s]
 61%|██████▏   | 565/922 [00:36<00:22, 15.88it/s]
 61%|██████▏   | 567/922 [00:36<00:21, 16.30it/s]
 62%|██████▏   | 569/922 [00:36<00:23, 15.19it/s]
 62%|██████▏   | 571/922 [00:37<00:22, 15.53it/s]
 62%|██████▏   | 573/922 [00:37<00:21, 16.11it/s]
 62%|██████▏   | 575/922 [00:37<00:28, 12.29it/s]
 63%|██████▎   | 577/922 [00:37<00:27, 12.72it/s]
 63%|██████▎   | 579/922 [00:37<00:25, 13.38it/s]
 63%|██████▎   | 582/922 [00:37<00:22, 15.08it/s]
 63%|██████▎   | 584/922 [00:38<00:22, 15.27it/s]
 64%|██████▎   | 586/922 [00:38<00:23, 14.24it/s]
 64%|██████▍   | 588/922 [00:38<00:22, 14.55it/s]
 64%|██████▍   | 590/922 [00:38<00:22, 15.07it/s]
 64%|██████▍   | 592/922 [00:38<00:21, 15.02it/s]
 64%|██████▍   | 594/922 [00:38<00:21, 15.06it/s]
 65%|██████▍   | 596/922 [00:38<00:20, 16.06it/s]
 65%|██████▍   | 598/922 [00:38<00:20, 15.48it/s]
 65%|██████▌   | 600/922 [00:39<00:20, 16.03it/s]
 65%|██████▌   | 602/922 [00:39<00:19, 16.01it/s]
 66%|██████▌   | 604/922 [00:39<00:19, 16.11it/s]
 66%|██████▌   | 606/922 [00:39<00:20, 15.49it/s]
 66%|██████▌   | 608/922 [00:39<00:21, 14.30it/s]
 66%|██████▌   | 610/922 [00:39<00:21, 14.20it/s]
 66%|██████▋   | 612/922 [00:39<00:22, 14.00it/s]
 67%|██████▋   | 614/922 [00:40<00:20, 14.70it/s]
 67%|██████▋   | 616/922 [00:40<00:22, 13.86it/s]
 67%|██████▋   | 618/922 [00:40<00:22, 13.74it/s]
 67%|██████▋   | 620/922 [00:40<00:21, 14.35it/s]
 67%|██████▋   | 622/922 [00:40<00:19, 15.63it/s]
 68%|██████▊   | 624/922 [00:40<00:19, 15.65it/s]
 68%|██████▊   | 626/922 [00:40<00:18, 15.76it/s]
 68%|██████▊   | 628/922 [00:40<00:19, 15.28it/s]
 68%|██████▊   | 630/922 [00:41<00:20, 14.55it/s]
 69%|██████▊   | 632/922 [00:41<00:21, 13.80it/s]
 69%|██████▉   | 634/922 [00:41<00:20, 14.16it/s]
 69%|██████▉   | 636/922 [00:41<00:20, 13.90it/s]
 69%|██████▉   | 638/922 [00:41<00:20, 13.79it/s]
 69%|██████▉   | 640/922 [00:41<00:19, 14.18it/s]
 70%|██████▉   | 642/922 [00:41<00:19, 14.11it/s]
 70%|██████▉   | 644/922 [00:42<00:19, 14.29it/s]
 70%|███████   | 646/922 [00:42<00:19, 14.03it/s]
 70%|███████   | 648/922 [00:42<00:18, 14.50it/s]
 70%|███████   | 650/922 [00:42<00:18, 14.56it/s]
 71%|███████   | 652/922 [00:42<00:19, 13.66it/s]
 71%|███████   | 654/922 [00:42<00:18, 14.68it/s]
 71%|███████   | 656/922 [00:42<00:19, 13.95it/s]
 71%|███████▏  | 658/922 [00:43<00:19, 13.24it/s]
 72%|███████▏  | 660/922 [00:43<00:18, 14.48it/s]
 72%|███████▏  | 662/922 [00:43<00:19, 13.05it/s]
 72%|███████▏  | 664/922 [00:43<00:20, 12.70it/s]
 72%|███████▏  | 666/922 [00:43<00:19, 13.14it/s]
 72%|███████▏  | 668/922 [00:43<00:18, 13.79it/s]
 73%|███████▎  | 670/922 [00:43<00:17, 14.52it/s]
 73%|███████▎  | 672/922 [00:44<00:17, 14.05it/s]
 73%|███████▎  | 674/922 [00:44<00:18, 13.38it/s]
 73%|███████▎  | 676/922 [00:44<00:17, 13.94it/s]
 74%|███████▎  | 678/922 [00:44<00:16, 14.67it/s]
 74%|███████▍  | 680/922 [00:44<00:15, 15.37it/s]
 74%|███████▍  | 682/922 [00:44<00:17, 14.04it/s]
 74%|███████▍  | 684/922 [00:44<00:16, 14.63it/s]
 74%|███████▍  | 686/922 [00:45<00:15, 15.17it/s]
 75%|███████▍  | 688/922 [00:45<00:15, 15.16it/s]
 75%|███████▍  | 690/922 [00:45<00:15, 15.44it/s]
 75%|███████▌  | 692/922 [00:45<00:14, 15.79it/s]
 75%|███████▌  | 694/922 [00:45<00:13, 16.56it/s]
 75%|███████▌  | 696/922 [00:45<00:13, 17.17it/s]
 76%|███████▌  | 698/922 [00:45<00:13, 16.70it/s]
 76%|███████▌  | 700/922 [00:45<00:13, 16.22it/s]
 76%|███████▌  | 702/922 [00:46<00:13, 16.06it/s]
 76%|███████▋  | 704/922 [00:46<00:15, 14.44it/s]
 77%|███████▋  | 706/922 [00:46<00:14, 14.70it/s]
 77%|███████▋  | 708/922 [00:46<00:15, 14.12it/s]
 77%|███████▋  | 710/922 [00:46<00:15, 13.70it/s]
 77%|███████▋  | 712/922 [00:46<00:15, 13.64it/s]
 77%|███████▋  | 714/922 [00:46<00:14, 14.56it/s]
 78%|███████▊  | 716/922 [00:47<00:13, 15.54it/s]
 78%|███████▊  | 719/922 [00:47<00:12, 16.61it/s]
 78%|███████▊  | 721/922 [00:47<00:11, 16.95it/s]
 78%|███████▊  | 723/922 [00:47<00:12, 15.41it/s]
 79%|███████▊  | 725/922 [00:47<00:13, 14.40it/s]
 79%|███████▉  | 727/922 [00:47<00:12, 15.08it/s]
 79%|███████▉  | 729/922 [00:47<00:12, 15.62it/s]
 79%|███████▉  | 731/922 [00:47<00:12, 15.67it/s]
 80%|███████▉  | 733/922 [00:48<00:11, 16.23it/s]
 80%|███████▉  | 735/922 [00:48<00:11, 16.89it/s]
 80%|███████▉  | 737/922 [00:48<00:11, 16.35it/s]
 80%|████████  | 739/922 [00:48<00:11, 16.15it/s]
 80%|████████  | 741/922 [00:48<00:11, 15.49it/s]
 81%|████████  | 743/922 [00:48<00:11, 15.97it/s]
 81%|████████  | 745/922 [00:48<00:10, 16.12it/s]
 81%|████████  | 747/922 [00:48<00:11, 15.74it/s]
 81%|████████  | 749/922 [00:49<00:10, 16.17it/s]
 81%|████████▏ | 751/922 [00:49<00:11, 15.53it/s]
 82%|████████▏ | 753/922 [00:49<00:11, 15.34it/s]
 82%|████████▏ | 755/922 [00:49<00:11, 14.11it/s]
 82%|████████▏ | 757/922 [00:49<00:11, 14.05it/s]
 82%|████████▏ | 759/922 [00:49<00:11, 13.96it/s]
 83%|████████▎ | 761/922 [00:49<00:11, 14.21it/s]
 83%|████████▎ | 763/922 [00:50<00:10, 14.82it/s]
 83%|████████▎ | 765/922 [00:50<00:11, 14.15it/s]
 83%|████████▎ | 767/922 [00:50<00:10, 14.74it/s]
 83%|████████▎ | 769/922 [00:50<00:10, 14.46it/s]
 84%|████████▎ | 771/922 [00:50<00:10, 14.89it/s]
 84%|████████▍ | 773/922 [00:50<00:09, 15.84it/s]
 84%|████████▍ | 775/922 [00:50<00:09, 15.19it/s]
 84%|████████▍ | 777/922 [00:51<00:09, 15.16it/s]
 84%|████████▍ | 779/922 [00:51<00:09, 15.66it/s]
 85%|████████▍ | 781/922 [00:51<00:09, 15.27it/s]
 85%|████████▍ | 783/922 [00:51<00:08, 15.83it/s]
 85%|████████▌ | 785/922 [00:51<00:08, 16.58it/s]
 85%|████████▌ | 787/922 [00:51<00:08, 16.78it/s]
 86%|████████▌ | 789/922 [00:51<00:07, 16.74it/s]
 86%|████████▌ | 791/922 [00:51<00:08, 16.07it/s]
 86%|████████▌ | 793/922 [00:51<00:08, 15.56it/s]
 86%|████████▌ | 795/922 [00:52<00:08, 15.61it/s]
 86%|████████▋ | 797/922 [00:52<00:08, 14.88it/s]
 87%|████████▋ | 799/922 [00:52<00:08, 14.78it/s]
 87%|████████▋ | 801/922 [00:52<00:08, 14.70it/s]
 87%|████████▋ | 803/922 [00:52<00:07, 15.60it/s]
 87%|████████▋ | 805/922 [00:52<00:07, 15.82it/s]
 88%|████████▊ | 807/922 [00:52<00:06, 16.67it/s]
 88%|████████▊ | 809/922 [00:53<00:06, 16.22it/s]
 88%|████████▊ | 811/922 [00:53<00:06, 17.01it/s]
 88%|████████▊ | 813/922 [00:53<00:06, 16.96it/s]
 88%|████████▊ | 815/922 [00:53<00:07, 14.75it/s]
 89%|████████▊ | 817/922 [00:53<00:07, 13.91it/s]
 89%|████████▉ | 819/922 [00:53<00:07, 14.47it/s]
 89%|████████▉ | 821/922 [00:53<00:07, 14.31it/s]
 89%|████████▉ | 823/922 [00:53<00:06, 14.76it/s]
 89%|████████▉ | 825/922 [00:54<00:06, 14.08it/s]
 90%|████████▉ | 827/922 [00:54<00:06, 14.46it/s]
 90%|████████▉ | 829/922 [00:54<00:06, 14.97it/s]
 90%|█████████ | 832/922 [00:54<00:05, 16.19it/s]
 90%|█████████ | 834/922 [00:54<00:05, 16.20it/s]
 91%|█████████ | 837/922 [00:54<00:05, 16.72it/s]
 91%|█████████ | 839/922 [00:54<00:04, 17.18it/s]
 91%|█████████ | 841/922 [00:55<00:04, 16.75it/s]
 91%|█████████▏| 843/922 [00:55<00:05, 14.75it/s]
 92%|█████████▏| 845/922 [00:55<00:04, 15.52it/s]
 92%|█████████▏| 847/922 [00:55<00:05, 14.86it/s]
 92%|█████████▏| 849/922 [00:55<00:04, 15.15it/s]
 92%|█████████▏| 851/922 [00:55<00:05, 13.87it/s]
 93%|█████████▎| 853/922 [00:55<00:04, 13.94it/s]
 93%|█████████▎| 855/922 [00:56<00:04, 14.61it/s]
 93%|█████████▎| 857/922 [00:56<00:04, 14.88it/s]
 93%|█████████▎| 860/922 [00:56<00:03, 16.49it/s]
 93%|█████████▎| 862/922 [00:56<00:03, 15.75it/s]
 94%|█████████▎| 864/922 [00:56<00:03, 16.43it/s]
 94%|█████████▍| 866/922 [00:56<00:03, 15.63it/s]
 94%|█████████▍| 868/922 [00:56<00:03, 16.44it/s]
 94%|█████████▍| 870/922 [00:56<00:03, 16.82it/s]
 95%|█████████▍| 872/922 [00:57<00:02, 17.28it/s]
 95%|█████████▍| 875/922 [00:57<00:02, 18.08it/s]
 95%|█████████▌| 877/922 [00:57<00:02, 16.43it/s]
 95%|█████████▌| 879/922 [00:57<00:02, 14.59it/s]
 96%|█████████▌| 881/922 [00:57<00:02, 14.14it/s]
 96%|█████████▌| 883/922 [00:57<00:02, 13.18it/s]
 96%|█████████▌| 885/922 [00:57<00:02, 13.20it/s]
 96%|█████████▌| 887/922 [00:58<00:02, 13.61it/s]
 96%|█████████▋| 889/922 [00:58<00:02, 13.00it/s]
 97%|█████████▋| 891/922 [00:58<00:02, 13.52it/s]
 97%|█████████▋| 893/922 [00:58<00:01, 14.55it/s]
 97%|█████████▋| 895/922 [00:58<00:02, 13.45it/s]
 97%|█████████▋| 897/922 [00:58<00:01, 14.13it/s]
 98%|█████████▊| 899/922 [00:58<00:01, 14.68it/s]
 98%|█████████▊| 901/922 [00:59<00:01, 14.28it/s]
 98%|█████████▊| 903/922 [00:59<00:01, 14.60it/s]
 98%|█████████▊| 905/922 [00:59<00:01, 14.73it/s]
 98%|█████████▊| 907/922 [00:59<00:01, 13.21it/s]
 99%|█████████▊| 909/922 [00:59<00:00, 13.09it/s]
 99%|█████████▉| 911/922 [00:59<00:00, 14.03it/s]
 99%|█████████▉| 913/922 [00:59<00:00, 14.19it/s]
 99%|█████████▉| 915/922 [01:00<00:00, 13.61it/s]
 99%|█████████▉| 917/922 [01:00<00:00, 13.50it/s]
100%|█████████▉| 919/922 [01:00<00:00, 14.43it/s]
100%|█████████▉| 921/922 [01:00<00:00, 14.00it/s]
100%|██████████| 922/922 [01:00<00:00, 15.21it/s]
[MoviePy] Done.
[MoviePy] >>>> Video ready: ../output/test_mapping2.mp4 

CPU times: user 40.6 s, sys: 1.05 s, total: 41.6 s
Wall time: 1min 1s

This next cell should function as an inline video player

If this fails to render the video, try running the following cell (alternative video rendering method). You can also simply have a look at the saved mp4 in your /output folder


In [126]:
from IPython.display import HTML
HTML("""
<video width="960" height="540" controls>
  <source src="{0}">
</video>
""".format(output))


Out[126]:

Below is an alternative way to create a video in case the above cell did not work.


In [127]:
import io
import base64
video = io.open(output, 'r+b').read()
encoded_video = base64.b64encode(video)
HTML(data='''<video alt="test" controls>
                <source src="data:video/mp4;base64,{0}" type="video/mp4" />
             </video>'''.format(encoded_video.decode('ascii')))


Out[127]:

In [ ]: