def augment(self, tub_paths, inplace=False): """ :param tub_paths: path list to tubs :param inplace: if tub should be changed or copied :return: None """ cfg = load_config('config.py') tubs = gather_tubs(cfg, tub_paths) for tub in tubs: if inplace: tub.augment_images() else: tub_path = tub.path # remove trailing slash if exits if tub_path[-1] == '/': tub_path = tub_path[:-1] # create new tub path by inserting '_aug' after 'tub_XY' head, tail = os.path.split(tub_path) tail_list = tail.split('_') tail_list.insert(2, 'aug') new_tail = '_'.join(tail_list) new_path = os.path.join(head, new_tail) # copy whole tub to new location and run augmentation shutil.copytree(tub.path, new_path) new_tub = Tub(new_path) new_tub.augment_images()
def test_make_paths_absolute(self): tub = Tub(self.path, inputs=['file_path'], types=['image']) rel_file_name = 'test.jpg' record_dict = {'file_path': rel_file_name} abs_record_dict = tub.make_record_paths_absolute(record_dict) assert abs_record_dict['file_path'] == os.path.join(self.path, rel_file_name)
def test_get_last_ix_for_one_record(tub_path): inputs = ['cam/image_array', 'angle', 'throttle'] types = ['image_array', 'float', 'float'] t = Tub(tub_path, inputs=inputs, types=types) record = create_sample_record() t.put_record(record) assert t.get_last_ix() == 0
def test_tub_put_unknown_type(tub_path): """ Creating a record with unknown type should fail """ inputs = ['user/speed'] types = ['bob'] t=Tub(path=tub_path, inputs=inputs, types=types) with pytest.raises(TypeError): t.put_record({'user/speed': 0.2, })
def test_tub_meta(self): meta = ["location:Here", "task:sometask"] tub = Tub(self.path, inputs=['file_path'], types=['image'], user_meta=meta) t2 = Tub(self.path) assert "location" in tub.meta assert "location" in t2.meta assert "sometask" == t2.meta["task"]
def create_sample_tub(path, records=128): inputs = [ 'cam/image_array', 'user/angle', 'user/throttle', 'location/one_hot_state_array' ] types = ['image_array', 'float', 'float', 'vector'] t = Tub(path, inputs=inputs, types=types) cam = SquareBoxCamera() tel = MovingSquareTelemetry() num_loc = 10 for _ in range(records): x, y = tel.run() img_arr = cam.run(x, y) loc = [0 for i in range(num_loc)] loc[1] = 1.0 t.put_record({ 'cam/image_array': img_arr, 'user/angle': x, 'user/throttle': y, 'location/one_hot_state_array': loc }) global temp_tub_path temp_tub_path = t print("setting temp tub path to:", temp_tub_path) return t
def create_sample_tub(path, records=10): inputs = ['cam/image_array', 'angle', 'throttle'] types = ['image_array', 'float', 'float'] t = Tub(path, inputs=inputs, types=types) for _ in range(records): record = create_sample_record() t.put_record(record) return t
def run(self, args, parser): ''' Load the images from a tub and create a movie from them. Movie ''' if args.tub is None: print("ERR>> --tub argument missing.") parser.print_help() return if args.type is None and args.model is not None: print( "ERR>> --type argument missing. Required when providing a model." ) parser.print_help() return if args.salient: if args.model is None: print( "ERR>> salient visualization requires a model. Pass with the --model arg." ) parser.print_help() conf = os.path.expanduser(args.config) if not os.path.exists(conf): print("No config file at location: %s. Add --config to specify\ location or run from dir containing config.py." % conf) return self.cfg = dk.load_config(conf) self.tub = Tub(args.tub) self.index = self.tub.get_index(shuffled=False) start = args.start self.end = args.end if args.end != -1 else len(self.index) if self.end >= len(self.index): self.end = len(self.index) - 1 num_frames = self.end - start self.iRec = start self.scale = args.scale self.keras_part = None self.do_salient = False if args.model is not None: self.keras_part = get_model_by_type(args.type, cfg=self.cfg) self.keras_part.load(args.model) self.keras_part.compile() if args.salient: self.do_salient = self.init_salient(self.keras_part.model) print('making movie', args.out, 'from', num_frames, 'images') clip = mpy.VideoClip(self.make_frame, duration=((num_frames - 1) / self.cfg.DRIVE_LOOP_HZ)) clip.write_videofile(args.out, fps=self.cfg.DRIVE_LOOP_HZ)
def create_sample_tub(path, records=10): inputs = ['cam/image_array', 'angle', 'throttle'] types = ['image_array', 'float', 'float'] t = Tub(path, inputs=inputs, types=types) cam = SquareBoxCamera() tel = MovingSquareTelemetry() for _ in range(records): x, y = tel.run() img_arr = cam.run(x, y) t.put_record({'cam/image_array': img_arr, 'angle': x, 'throttle': y}) return t
def test_recreating_tub(tub): """ Recreating a Tub should restore it to working state """ assert tub.get_num_records() == 10 assert tub.current_ix == 10 assert tub.get_last_ix() == 9 path = tub.path tub = None inputs = ['cam/image_array', 'angle', 'throttle'] types = ['image_array', 'float', 'float'] t = Tub(path, inputs=inputs, types=types) assert t.get_num_records() == 10 assert t.current_ix == 10 assert t.get_last_ix() == 9
def __init__( self, path ): DriveFormat.__init__(self) if not os.path.exists(path): raise IOError( "TubFormat directory does not exist: {}".format( path ) ) if not os.path.isdir(path): raise IOError( "TubFormat path is not a directory: {}".format( path ) ) self.path = path self.tub = Tub(path) self.meta = self.tub.meta self.edit_list = set() self.shape = None self.auxMeta = {} self.aux_clean = True
def make_tub( apath ): if os.path.isdir(apath): meta_path = os.path.join( apath, "meta.json" ) if os.path.exists(meta_path): return Tub(apath) else: t = None try: t = Tub2(apath, read_only=True) except ValueError as val_ex: pass except FileNotFoundError as nof_ex: pass except Exception as ex: print( f"Failed to open Tub v2: {apath}" ) print( f" Reason: {type(ex)} {ex}" ) if t is None: try: t = EmptyTub(apath) except ValueError as val_ex: pass # Not a tub of any kind return t return None
def rpi2research(initial_angle=160, final_angle=90, final_bits=8, final_rows=224, final_cols=224): """ Inputs: initial_angle : Float. Viewing angle of input_image. final_angle : Float. Desired viewing angle of the output image. final_bits : Integer. The number of bits used to define an individual pixel in the output image. final_rows : Integer. The number of rows in the output image. final_cols : Integer. The number of columns in the output image. Returns: None. """ # Set up the connection to the Raspberry Pi # defines a vehicle to take and record pictures 10 times per second V = Vehicle() #add a camera part #cam = PiCamera() cam = vid2research(0) V.add(cam, outputs=['image'], threaded=True) #add tub part to record images tub = Tub(path='~/mycar/get_started', inputs=['image'], types=['image_array']) V.add(tub, inputs=['image']) #start the drive loop at 10 Hz V.start(rate_hz=10)
def get_tubes_df(tubes_root_path, tubes_name, tubes_extension="", cache_dir=None): tubes = [] i = 0 # get files for tube_name in tubes_name: path = _get_archive(tubes_root_path, tube_name, tubes_extension, cache_dir) tub = Tub(str(path)) tub_df = tub.get_df() tub_df["num_tube"] = i # track the different tubes tubes.append(tub_df) i += 1 return pd.concat(tubes, sort=False)
def check(self, tub_paths, fix=False): """ Check for any problems. Looks at tubs and find problems in any records or images that won't open. If fix is True, then delete images and records that cause problems. """ tubs = [Tub(path) for path in tub_paths] for tub in tubs: tub.check(fix=fix)
def gather_tubs(cfg, tub_names): ''' takes as input the configuration, and the comma seperated list of tub paths returns a list of Tub objects initialized to each path ''' tub_paths = gather_tub_paths(cfg, tub_names) tubs = [Tub(p) for p in tub_paths] return tubs
def create_sample_tub(path, records=128): inputs = ['cam/image_array', 'user/angle', 'user/throttle'] types = ['image_array', 'float', 'float'] t = Tub(path, inputs=inputs, types=types) cam = SquareBoxCamera() tel = MovingSquareTelemetry() for _ in range(records): x, y = tel.run() img_arr = cam.run(x, y) t.put_record({ 'cam/image_array': img_arr, 'user/angle': x, 'user/throttle': y }) global temp_tub_path temp_tub_path = t print("setting temp tub path to:", temp_tub_path) return t
def gather_tubs(cfg, tub_names): ''' takes as input the configuration, and the comma seperated list of tub paths returns a list of Tub objects initialized to each path ''' from donkeycar.parts.datastore import Tub tub_paths = gather_tub_paths(cfg, tub_names) tubs = [Tub(p) for p in tub_paths] return tubs
def augment(tub_names, new_data_dir, args): new_data_dir = os.path.expanduser(new_data_dir) tubgroup = TubGroup(tub_names) # If tub directory does not exist, create directory if not os.path.exists(new_data_dir): os.makedirs(new_data_dir) # If directory does not contain meta.json, copy one from the first source tub if not os.path.exists(os.path.join(new_data_dir, 'meta.json')): copyfile(src=tubgroup.tubs[0].meta_path, dst=os.path.join(new_data_dir, 'meta.json')) new_tub = Tub(new_data_dir) for tub in tubgroup.tubs: for ix in tub.get_index(shuffled=False): record = tub.get_record(ix) for augmented_record in augment_single_record(record, args): new_tub.put_record(augmented_record)
def benchmark(): # Change with a non SSD storage path path = Path('/media/rahulrav/Cruzer/tub') if os.path.exists(path.absolute().as_posix()): shutil.rmtree(path) inputs = ['input'] types = ['int'] tub = Tub(path.absolute().as_posix(), inputs, types) write_count = 1000 for i in range(write_count): record = {'input': i} tub.put_record(record) # old tub starts counting at 1 deletions = set(np.random.randint(1, write_count + 1, 100)) for index in deletions: index = int(index) tub.remove_record(index) files = path.glob('*.json') for record_file in files: contents = record_file.read_text() if contents: contents = json.loads(contents) print('Record %s' % contents)
def pilot2user(path): tub = Tub(path) tub.exclude.clear( ) # we want to do excluded records, just in case they get un-excluded records = tub.gather_records() for rec_path in records: try: with open(rec_path, 'r') as fp: rec = json.load(fp) except UnicodeDecodeError: raise Exception('bad record') except FileNotFoundError: raise except: print("Unexpected error:", sys.exc_info()[0]) raise pa = rec.get('pilot/angle', None) pt = rec.get('pilot/throttle', None) ua = rec.get('user/angle', 0.0) ut = rec.get('user/throttle', 0.0) if (ua != 0.0) or (ut != 0.0): #idx = get_record_index(rec_path) print("Already have user values for {}".format(rec_path)) else: """ copy user to orig if not done already, copy pilot to user """ if pa is not None: if 'orig/angle' not in rec and ua is not None: rec['orig/angle'] = ua rec['user/angle'] = pa if pt is not None: if 'orig/throttle' not in rec and ut is not None: rec['orig/throttle'] = ut rec['user/throttle'] = pt print("Writing record {}".format(rec_path)) with open(rec_path, 'w') as fp: json.dump(rec, fp)
def check(self, tub_paths, fix=False, delete_empty=False): ''' Check for any problems. Looks at tubs and find problems in any records or images that won't open. If fix is True, then delete images and records that cause problems. ''' tubs = [Tub(path) for path in tub_paths] for tub in tubs: tub.check(fix=fix) if delete_empty and tub.get_num_records() == 0: import shutil print("removing empty tub", tub.path) shutil.rmtree(tub.path)
def test_tub_like_driver(self): """ The manage.py/donkey2.py drive command creates a tub using TubHandler, so test that way. """ os.makedirs(self.tempfolder) meta = ["location:Here2", "task:sometask2"] th = TubHandler(self.tempfolder) tub = th.new_tub_writer(inputs=self.inputs, types=self.types, user_meta=meta) t2 = Tub(tub.path) assert tub.meta == t2.meta assert tub.meta['location'] == "Here2" assert t2.meta['inputs'] == self.inputs assert t2.meta['location'] == "Here2"
def test_tub_put_image(tub_path): """ Add an encoded image to the tub """ inputs = ['user/speed', 'cam/image'] types = ['float', 'image'] img = Image.new('RGB', (120, 160)) t=Tub(path=tub_path, inputs=inputs, types=types) t.put_record({'cam/image': img, 'user/speed': 0.2, }) assert t.get_record(t.get_last_ix())['user/speed'] == 0.2
def run(self, args): """ Load the images from a tub and create a movie from them. Movie """ import moviepy.editor as mpy args, parser = self.parse_args(args) if args.tub is None: parser.print_help() return conf = os.path.expanduser(args.config) if not os.path.exists(conf): print("No config file at location: %s. Add --config to specify\ location or run from dir containing config.py." % conf) return try: cfg = dk.load_config(conf) except: print("Exception while loading config from", conf) return self.tub = Tub(args.tub) self.num_rec = self.tub.get_num_records() self.iRec = 0 print('making movie', args.out, 'from', self.num_rec, 'images') clip = mpy.VideoClip(self.make_frame, duration=(self.num_rec//cfg.DRIVE_LOOP_HZ) - 1) clip.write_videofile(args.out,fps=cfg.DRIVE_LOOP_HZ) print('done')
def test_train_localizer(tub, tub_path): t = Tub(tub_path) assert t is not None import donkeycar.templates.cfg_complete as cfg tempfolder = tub_path[:-3] model_path = os.path.join(tempfolder, 'test.h5') cfg_defaults(cfg) tub = tub_path model = model_path transfer = None model_type = "localizer" continuous = False aug = False multi_train(cfg, tub, model, transfer, model_type, continuous, aug)
def setActionForIndex( self, new_action, index ): idx = self.indexes[index] rec = self.records[idx] angle, throttle = Tub.get_angle_throttle(rec) old_action = [angle, throttle] if not np.array_equal( old_action, new_action ): if (rec["user/angle"] != new_action[0]) or (rec["user/throttle"] != new_action[1]): # Save the original values if not already done if "orig/angle" not in rec: rec["orig/angle"] = rec["user/angle"] if "orig/throttle" not in rec: rec["orig/throttle"] = rec["user/throttle"] rec["user/angle"] = new_action[0] rec["user/throttle"] = new_action[1] self.edit_list.add(idx) self.setDirty()
def test_train_seq(tub, tub_path): t = Tub(tub_path) assert t is not None import donkeycar.templates.cfg_complete as cfg tempfolder = tub_path[:-3] model_path = os.path.join(tempfolder, 'test.h5') cfg.MAX_EPOCHS = 1 cfg.BATCH_SIZE = 10 cfg.SHOW_PLOT = False cfg.VEBOSE_TRAIN = False cfg.OPTIMIZER = "adam" tub = tub_path model = model_path transfer = None model_type = "rnn" continuous = False aug = True multi_train(cfg, tub, model, transfer, model_type, continuous, aug)
class MakeMovie(BaseCommand): def parse_args(self, args): parser = argparse.ArgumentParser(prog='makemovie') parser.add_argument('--tub', help='The tub to make movie from') parser.add_argument('--out', default='tub_movie.mp4', help='The movie filename to create. default: tub_movie.mp4') parser.add_argument('--config', default='./config.py', help='location of config file to use. default: ./config.py') parsed_args = parser.parse_args(args) return parsed_args, parser def run(self, args): """ Load the images from a tub and create a movie from them. Movie """ import moviepy.editor as mpy args, parser = self.parse_args(args) if args.tub is None: parser.print_help() return conf = os.path.expanduser(args.config) if not os.path.exists(conf): print("No config file at location: %s. Add --config to specify\ location or run from dir containing config.py." % conf) return try: cfg = dk.load_config(conf) except: print("Exception while loading config from", conf) return self.tub = Tub(args.tub) self.num_rec = self.tub.get_num_records() self.iRec = 0 print('making movie', args.out, 'from', self.num_rec, 'images') clip = mpy.VideoClip(self.make_frame, duration=(self.num_rec//cfg.DRIVE_LOOP_HZ) - 1) clip.write_videofile(args.out,fps=cfg.DRIVE_LOOP_HZ) print('done') def make_frame(self, t): """ Callback to return an image from from our tub records. This is called from the VideoClip as it references a time. We don't use t to reference the frame, but instead increment a frame counter. This assumes sequential access. """ self.iRec = self.iRec + 1 if self.iRec >= self.num_rec - 1: return None rec = self.tub.get_record(self.iRec) image = rec['cam/image_array'] return image # returns a 8-bit RGB array
def run(self, args): ''' Load the images from a tub and create a movie from them. Movie ''' import moviepy.editor as mpy args, parser = self.parse_args(args) if args.tub is None: parser.print_help() return if args.salient: #imported like this, we make TF conditional on use of --salient #and we keep the context maintained throughout our callbacks to #compute the salient mask from keras import backend as K import tensorflow as tf os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3' conf = os.path.expanduser(args.config) if not os.path.exists(conf): print("No config file at location: %s. Add --config to specify\ location or run from dir containing config.py." % conf) return try: cfg = dk.load_config(conf) except: print("Exception while loading config from", conf) return self.tub = Tub(args.tub) self.num_rec = self.tub.get_num_records() if args.start == 1: self.start = self.tub.get_index(shuffled=False)[0] else: self.start = args.start if args.end != -1: self.end = args.end else: self.end = self.num_rec - self.start self.num_rec = self.end - self.start self.iRec = args.start self.scale = args.scale self.keras_part = None self.convolution_part = None if not args.model == "None": self.keras_part = get_model_by_type(args.model_type, cfg=cfg) self.keras_part.load(args.model) self.keras_part.compile() if args.salient: self.init_salient(self.keras_part.model) #This method nested in this way to take the conditional import of TF #in a manner that extends to this callback. Done this way, we avoid #importing in the below method, which triggers a new cuda device allocation #each call. def compute_visualisation_mask(img): #from https://github.com/ermolenkodev/keras-salient-object-visualisation activations = self.functor([np.array([img])]) activations = [ np.reshape( img, (1, img.shape[0], img.shape[1], img.shape[2])) ] + activations upscaled_activation = np.ones((3, 6)) for layer in [5, 4, 3, 2, 1]: averaged_activation = np.mean( activations[layer], axis=3).squeeze(axis=0) * upscaled_activation output_shape = (activations[layer - 1].shape[1], activations[layer - 1].shape[2]) x = tf.constant( np.reshape(averaged_activation, (1, averaged_activation.shape[0], averaged_activation.shape[1], 1)), tf.float32) conv = tf.nn.conv2d_transpose( x, self.layers_kernels[layer], output_shape=(1, output_shape[0], output_shape[1], 1), strides=self.layers_strides[layer], padding='VALID') with tf.Session() as session: result = session.run(conv) upscaled_activation = np.reshape(result, output_shape) final_visualisation_mask = upscaled_activation return (final_visualisation_mask - np.min(final_visualisation_mask)) / ( np.max(final_visualisation_mask) - np.min(final_visualisation_mask)) self.compute_visualisation_mask = compute_visualisation_mask print('making movie', args.out, 'from', self.num_rec, 'images') clip = mpy.VideoClip(self.make_frame, duration=(self.num_rec // cfg.DRIVE_LOOP_HZ) - 1) clip.write_videofile(args.out, fps=cfg.DRIVE_LOOP_HZ) print('done')