Example #1
0
class SteerDataset(Dataset):
    """
    Returns img, steering_angle pairs
    """
    def __init__(self, datapath, transforms=None):
        """
        datapath: source of final processed & augmented data
        """
        super(SteerDataset, self).__init__()
        self.datapath = datapath
        self.dutils = Data_Utils()
        self.df = self.dutils.get_df(self.datapath)
        self.transforms = transforms

    def __len__(self):
        return len(self.df)

    def __getitem__(self, idx):
        """
        Returns dictionary {"img": Tensor, C x H x W, "angle":float 1-Tensor}
        """
        cv_img, df_row = self.dutils.df_data_fromidx(self.datapath, self.df,
                                                     idx)

        if self.transforms:
            cv_img = self.transforms(cv_img)

        ts_angle = torch.Tensor([df_row[1]]).float()
        ts_img = torch.from_numpy(cv_img).permute(2, 0,
                                                  1).float()  #size (C x H x W)

        data_dict = {"img": ts_img, "angle": ts_angle}
        return data_dict
Example #2
0
 def __init__(self, datapath, transforms=None):
     """
     datapath: source of final processed & augmented data
     """
     super(SteerDataset, self).__init__()
     self.datapath = datapath
     self.dutils = Data_Utils()
     self.df = self.dutils.get_df(self.datapath)
     self.transforms = transforms
 def __init__(self, sess_path=None, writer=None):
     """
     sess_path: current working dir of this session
     writer: Tensorboard SummmaryWriter
     """
     self.gvis = lambda param: session["visualizer"].get(param)
     self.fixangle = lambda angle, units: angle if units == 'rad' else angle * math.pi / 180.0
     self.writer = writer
     self.data_utils = Data_Utils()
    def __init__(self):
        jsonfile = steps.session
        #The state of the session
        self.params_dict = jsonfile["params"]
        self.steplist = jsonfile["steps"]
        self.curr_step_idx = 0
        self.dlist = None  #running list of folders to operate on

        #Session ID & Logger
        self.sess_id, self.sess_path = self._create_session_data(
            self.params_dict["abs_path"], self.params_dict["sess_root"])
        self.writer = self._create_writer(self.sess_path,
                                          'logs',
                                          comment=self.params_dict["comment"])

        #Periphery Stuff for data moving and visualization
        self.data_utils = Data_Utils()
        self.visualizer = Metric_Visualizer(self.sess_path, self.writer)
import os, json, pdb, cv2, math
import numpy as np
from functools import partial
import pandas as pd
from Data_Utils import Data_Utils
dutils = Data_Utils()

def changeName(dest_dict, prefix):
    curr_name = dest_dict.get("row")[0]
    dest_dict["row"][0] = prefix + curr_name
    return dest_dict

def cannyEdge(args, src_dict):
    assert(len(args) == 2), "Incorrect size argument to cannyEdge"
    dest_dict = src_dict
    src_img = src_dict["img"]
    dest_img = cv2.Canny(src_img, args[0], args[1])
    dest_dict["img"] = dest_img
    return changeName(dest_dict, 'cannyedge')

def filterBadData(args, src_dict):
    assert(len(args) == 0), "Incorrect size argument to filterBadData"
    dest_dict = src_dict
    src_img = src_dict.get("img")

    #check to see if picture is largely black
    if np.mean(src_img) < 5:
        dest_dict["flag"] = False
    return changeName(dest_dict, 'fbd')

def opticalFlow(args, src_dict):
class Stepper(object):
    """
    Parses steps.session and executes ins. on data folders accordingly to get it into a final state
    """
    def __init__(self):
        jsonfile = steps.session
        #The state of the session
        self.params_dict = jsonfile["params"]
        self.steplist = jsonfile["steps"]
        self.curr_step_idx = 0
        self.dlist = None  #running list of folders to operate on

        #Session ID & Logger
        self.sess_id, self.sess_path = self._create_session_data(
            self.params_dict["abs_path"], self.params_dict["sess_root"])
        self.writer = self._create_writer(self.sess_path,
                                          'logs',
                                          comment=self.params_dict["comment"])

        #Periphery Stuff for data moving and visualization
        self.data_utils = Data_Utils()
        self.visualizer = Metric_Visualizer(self.sess_path, self.writer)

    def _create_session_data(self, abs_path, sess_root):
        """
        Returns an int representing the unique session ID & makes a folder for the session, along with a string representing the current working directory of the session
        sess_root: root folder containing all the sessions
        """
        sess_path = os.path.join(abs_path, sess_root)
        if not os.path.exists(sess_path):
            os.makedirs(sess_path)
        sess_id = len(os.listdir(sess_path))
        sess_path = os.path.join(sess_path, str(sess_id))
        print("SESSION PATH:", sess_path)
        print("SESSION ID:", sess_id)
        return sess_id, sess_path

    def _create_writer(self, sess_path, log_foldername, comment=''):
        """
        Retruns a summary writer in the right place
        sess_path: current working dir of session
        """
        logdir = os.path.join(sess_path, log_foldername)
        print("LOGDIR:", logdir)
        writer = SummaryWriter(logdir=logdir, comment=comment)
        return writer

    def B_VER(self, ver_path, dlist):
        """
        (B)asic (VER)ification of folder structure in dlist
        ver_path is generally sess_path, but it could also be some other folder
        similarly, dlist is generally self.dlist, but it could also be someother list of folders
        """
        for folder in dlist:
            dpath = os.path.join(ver_path, folder)
            #Ensure folder exists
            assert (
                os.path.exists(dpath)
            ), f"B_VER Error: Folder {folder} cannot be found in {ver_path}"

            csvpath = os.path.join(dpath, 'data.csv')
            #Check for data.csv
            assert (os.path.isfile(csvpath)
                    ), f"B_VER Error: data.csv could not be found in {dpath}"

            #Verify 4 columns in csv
            df = pd.read_csv(csvpath)
            assert (
                len(df.columns) == 4
            ), f"B_VER Error: Need four columns in data.csv in {csvpath}"

            #The number of jpg files in this directory should equal num rows
            num_jpgs = len(glob.glob1(dpath, "*.jpg"))
            assert (
                len(df) == num_jpgs
            ), F"B_VER Error: num_jpgs in {dpath} must = num_rows in data.csv"

            #Index the first 20 and last 20, and ensure that those images exist
            for i in range(20):
                img_name = df.iloc[i, 0]
                framepath = os.path.join(dpath, img_name)
                frame = cv2.imread(framepath)
                assert (
                    os.path.isfile(framepath)
                ), F"B_VER Error: frame {framepath} is not a path, but is in the csv"

            for i in range(20):
                img_name = df.iloc[-i, 0]
                framepath = os.path.join(dpath, img_name)
                frame = cv2.imread(framepath)
                assert (
                    os.path.isfile(framepath)
                ), F"B_VER Error: frame {framepath} is not a path, but is in the csv"

    def default_vis(self, curr_step):
        #visualize data in tensorboard
        for i, folder in enumerate(self.dlist):
            self.visualizer.standard_log(self.sess_path,
                                         folder,
                                         self.curr_step_idx,
                                         global_step=i,
                                         units=curr_step.get("units", 'rad'))

    def exec_init(self, curr_step):
        """
        Initializes self.sess_path with filtered data
        curr_step: dict, as in steps.session
        """
        assert (
            self.curr_step_idx == 0 and self.dlist is None
        ), "Step Error: init instruction can only be called once at the start"

        #verify raw data & dlist
        self.dlist = curr_step["dlist"]
        raw_datadir = os.path.join(self.params_dict["abs_path"],
                                   self.params_dict["raw_data"])
        self.B_VER(raw_datadir, self.dlist)
        print(f"PASSED B_VER FOR STEP {self.curr_step_idx}")
        dest_datadir = self.sess_path
        filter_funclist = curr_step["funclist"]
        #move & filter each folder
        new_dlist = []
        for i, folder in enumerate(self.dlist):
            new_folder = self.data_utils.MOVE(
                raw_datadir,
                folder,
                dest_datadir,
                flist=filter_funclist[i],
                preview=self.params_dict["preview"])
            new_dlist.append(new_folder)

        #visualize data in tensorboard
        self.dlist = new_dlist
        self.default_vis(curr_step)

    def exec_preprocess(self, curr_step):
        """
        Preprocesses data as per curr_step in steps.session
        """
        assert (self.curr_step_idx > 0 and self.dlist
                is not None), "Step Error: Must call init before preprocess"

        #verify raw data & dlist
        self.B_VER(self.sess_path, self.dlist)

        #move & preprocess each folder
        funclist = curr_step["funclist"]
        raw_datadir = self.sess_path
        dest_datadir = self.sess_path
        new_dlist = []
        for i, folder in enumerate(self.dlist):
            flist = funclist[i]
            new_folder = self.data_utils.MOVE(raw_datadir,
                                              folder,
                                              dest_datadir,
                                              flist=flist,
                                              preview=False)
            new_dlist.append(new_folder)
        self.dlist = new_dlist
        self.default_vis(curr_step)

    def exec_augment(self, curr_step):
        """
        Augment data as per curr_step in steps.json
        """
        assert (self.curr_step_idx > 0 and self.dlist
                is not None), "Step Error: Must call init before augment"

        #verify raw data & dlist
        self.B_VER(self.sess_path, self.dlist)

        #move & preprocess each folder
        funclist = curr_step["funclist"]
        raw_datadir = self.sess_path
        dest_datadir = self.sess_path
        for i, folder in enumerate(self.dlist):
            flist = funclist[i]
            self.data_utils.MOVE(raw_datadir,
                                 folder,
                                 dest_datadir,
                                 flist=flist,
                                 preview=False,
                                 op='aug')
        self.default_vis(curr_step)

    def exec_combine(self, curr_step):
        """
        Combines files currently in dlist into a specified folder
        updates dlist to only have this folder
        """
        assert (self.curr_step_idx > 0 and self.dlist
                is not None), "Step Error: Must call init before combine"

        #verify raw data & dlist
        self.B_VER(self.sess_path, self.dlist)

        #move & preprocess each folder
        raw_datadir = self.sess_path
        dest_datadir = self.sess_path
        foldername = curr_step.get("foldername", "main")
        for i, folder in enumerate(self.dlist):
            self.data_utils.MOVE(raw_datadir,
                                 folder,
                                 dest_datadir,
                                 flist=[],
                                 preview=False,
                                 op='combine_choosename|' + foldername)
        self.dlist = [foldername]
        self.default_vis(curr_step)

    def step(self):
        """
        Executes the instruction for the curr_step
        """
        curr_step = self.steplist[self.curr_step_idx]  #dictionary
        insn_type = curr_step["type"]

        #case over insn_types
        if insn_type == "init":
            self.exec_init(curr_step)
            self.curr_step_idx += 1

        elif insn_type == "preprocess":
            self.exec_preprocess(curr_step)
            self.curr_step_idx += 1

        elif insn_type == "augment":
            self.exec_augment(curr_step)
            self.curr_step_idx += 1

        elif insn_type == "combine":
            self.exec_combine(curr_step)
            self.curr_step_idx += 1
class Metric_Visualizer(object):
    """
    Visualize metrics in Tensorboard
    """
    def __init__(self, sess_path=None, writer=None):
        """
        sess_path: current working dir of this session
        writer: Tensorboard SummmaryWriter
        """
        self.gvis = lambda param: session["visualizer"].get(param)
        self.fixangle = lambda angle, units: angle if units == 'rad' else angle * math.pi / 180.0
        self.writer = writer
        self.data_utils = Data_Utils()

    def vis_steer_point(self,
                        frame,
                        angle,
                        cx,
                        cy,
                        r,
                        size=10,
                        color=(0, 0, 0)):
        """
        Tiny point on big steering graphic that shows the steering angle
        """
        x = (cx + r * math.cos(-1.0 * angle + math.pi / 2.))
        y = (cy - r * math.sin(-1.0 * angle + math.pi / 2.))
        cv2.circle(frame, (int(x), int(y)), size, color, -1)

    def vis_textdata(self, frame, scalar, label, pos):
        """
        Visualize text data. pos = vertical interval on the frame
        """
        scalar = float(scalar) if float(scalar) != -0.0 else 0.0

        #TEXT PARAMETERS
        color = (255, 255, 255)
        font = cv2.FONT_HERSHEY_SIMPLEX
        font_size = 0.5
        font_thickness = 1
        v_space = 20
        cx, cy = 20, pos * v_space
        #draw text on frame
        text = label + '%.3f' % (float(scalar))
        cv2.putText(frame, text, (cx, cy), font, font_size, color,
                    font_thickness)

    def vis_frame(self,
                  frame,
                  angle,
                  speed,
                  timestamp,
                  pred=None,
                  show_steer=False):
        """
        Vis an image w/ text info log + steering_angle_graphic & display it 
        """
        #log text data in the top left
        self.vis_textdata(frame, angle, "angle:", 1)
        self.vis_textdata(frame, speed, "speed:", 2)
        self.vis_textdata(frame, timestamp, "time:", 3)
        if pred is not None:
            self.vis_textdata(frame, timestamp, "pred", 4)

        if show_steer:
            #Big Steering Graphic
            h, w, c = frame.shape
            cx, cy, r = int(w / 2), h, int((h * 80) / 480)
            cv2.circle(frame, (cx, cy), r, (255, 255, 255), 2)
            big_steerpoint = int(math.ceil(r * 10.0 / 80.0))
            angle_extra = angle * 2
            if pred is not None:
                pred_extra = pred * 2
            #SMALL steering point graphic (angle must be in radians)
            self.vis_steer_point(frame,
                                 angle_extra,
                                 cx,
                                 cy,
                                 r,
                                 size=big_steerpoint,
                                 color=(218, 165, 32))
            if pred is not None:
                self.vis_steer_point(frame,
                                     pred_extra,
                                     cx,
                                     cy,
                                     r,
                                     size=int(math.ceil(big_steerpoint / 2.0)),
                                     color=(0, 0, 0))

    def vis_framelist(self,
                      labelname,
                      framelist,
                      angle_list,
                      global_step=0,
                      show_steer=False,
                      vel_list=None,
                      predangle_list=None,
                      timestamp_list=None):
        """
        Visualize a list of frames (cv2) w/ angles (radians, floats)
        """
        vislist = []
        tryget = lambda arr, index, default: default if arr is None else arr[
            index]
        for i, frame in enumerate(framelist):
            curr_timestamp = tryget(timestamp_list, i, 0)
            curr_predangle = tryget(predangle_list, i, None)
            curr_vel = tryget(vel_list, i, 0)
            frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
            self.vis_frame(frame,
                           angle_list[i],
                           curr_vel,
                           curr_timestamp,
                           curr_predangle,
                           show_steer=True)
            frame = cv2.bitwise_not(frame)
            vislist.append(frame)
        self.writer.add_images(labelname,
                               vislist,
                               global_step=global_step,
                               dataformats='HWC')

    def framelist_from_path(self,
                            dpath,
                            stepname,
                            idx,
                            show_steer=False,
                            units='rad'):
        """
        Send a list of frames to Tensorboard from a path
        """
        interesting_idxs = self.data_utils.get_interesting_idxs(dpath, 5)
        df = self.data_utils.get_df(dpath)
        datalist = list(
            map(lambda x: self.data_utils.df_data_fromidx(dpath, df, x),
                interesting_idxs))  #list of image, row pairs
        framelist = list(map(lambda x: x[0], datalist))
        rowlist = list(map(lambda x: x[1], datalist))
        splitrow = lambda idx: list(map(lambda x: x[idx], rowlist))
        angle_list = splitrow(1)
        angle_list = list(map(lambda x: self.fixangle(x, units), angle_list))
        vel_list = splitrow(2)
        timestamp_list = splitrow(3)
        self.vis_framelist(stepname,
                           framelist,
                           angle_list,
                           global_step=idx,
                           show_steer=show_steer,
                           vel_list=vel_list,
                           timestamp_list=timestamp_list)

    def vid_from_path(self,
                      dpath,
                      stepname,
                      idx,
                      show_steer=False,
                      units='rad'):
        """
        Send annotated video to Tensorboard
        dpath: abs_path to data folder containing images & csv
        labelname: str name for the label
        global_step: global_step to record for slider functionality
        """
        framebuffer = []
        #get dataframe
        csvpath = os.path.join(dpath, "data.csv")
        df = pd.read_csv(csvpath)
        # num_rows = int(0.1 * len(df)) #display about 10% of the frames
        num_rows = len(df)
        for i in range(num_rows):
            if i % 4 == 0:
                img_name, angle, speed, timestamp = df.iloc[i, 0], df.iloc[
                    i, 1], df.iloc[i, 2], df.iloc[i, 3]
                angle = self.fixangle(angle, units)
                framepath = os.path.join(dpath, img_name)
                frame = cv2.imread(framepath)
                self.vis_frame(frame,
                               angle,
                               speed,
                               timestamp,
                               show_steer=show_steer)
                framebuffer.append(frame.copy())

        self.writer.add_video(stepname,
                              framebuffer,
                              fps=10,
                              global_step=idx,
                              as_np_framebuffer=True)

    def plot_anglehist(self, dpath, tag, idx):
        csvpath = os.path.join(dpath, "data.csv")
        df = pd.read_csv(csvpath)
        angle_column = df.iloc[:, 1].values
        num_bins = 100
        #save plot w/ matplotlib
        fig = plt.figure()
        plt.hist(angle_column, num_bins, color='green')
        self.writer.add_figure(tag, fig, global_step=idx)

    def text_table(self,
                   dpath,
                   labelname,
                   foldername='',
                   angle_unit='',
                   global_step=0):
        df = self.data_utils.get_df(dpath)
        h, w = self.data_utils._get_image_size(dpath)
        text = f"Folder | Shape | Units | Num Images\n-----|-----|-----|-----\n{foldername}|({h}, {w})|{angle_unit}|{len(df)}"
        self.writer.add_text(labelname, text, global_step=global_step)

    def visualize_batch(self,
                        ts_imgbatch,
                        ts_anglebatch,
                        ts_predanglebatch,
                        global_step=0):
        self.writer.add_images("Sample Batch",
                               ts_imgbatch[:5],
                               global_step=global_step)

    def dict_to_table(self, my_dict):
        """
        Converts a python dict into a table
        TODO: Consider the numerous edge cases (what if dict is already array)
        """
        tabulate_dict = {}
        for key in my_dict:
            tabulate_dict[key] = [str(my_dict[key])]
        text = tabulate(tabulate_dict, headers="keys", tablefmt="github")
        return text

    def log_training(self, config, train_id, best_train_loss, best_valid_loss):
        final_dict = config
        final_dict["train_id"] = train_id
        final_dict["t_loss"] = best_train_loss
        final_dict["v_loss"] = best_valid_loss
        text = self.dict_to_table(final_dict)
        self.writer.add_text('Train Summary', text, global_step=0)

    def standard_log(self,
                     datadir,
                     folder,
                     curr_step,
                     global_step=0,
                     units=''):
        """
        Log "Standard" things in Tensorboard
        datadir: abs_path of directory containing data folders
        folder: data folder name
        curr_step: progress in steps.json
        global_step: The "step" value to log into tensorboard (this allows for the cool slider functionality)
        units: 'rad' or 'deg'
        """
        labelname = f"STEP-{curr_step}"
        dpath = os.path.join(datadir, folder)
        self.plot_anglehist(dpath, labelname, global_step)
        if self.gvis("vis_type") == 'video':
            self.vid_from_path(dpath,
                               labelname,
                               global_step,
                               show_steer=True,
                               units=units)
        elif self.gvis("vis_type") == 'framelist':
            self.framelist_from_path(dpath,
                                     labelname,
                                     global_step,
                                     show_steer=True,
                                     units=units)
        self.text_table(dpath,
                        labelname,
                        foldername=folder,
                        angle_unit=units,
                        global_step=global_step)