Example #1
0
def stitch_tracklets(
    config_path,
    pickle_file,
    n_tracks=None,
    min_length=10,
    split_tracklets=True,
    prestitch_residuals=True,
    max_gap=None,
    weight_func=None,
    output_name="",
):
    """
    Stitch sparse tracklets into full tracks via a graph-based,
    minimum-cost flow optimization problem.

    Parameters
    ----------
    config_path : str
        Path to the main project config.yaml file.

    pickle_file : str
        Path to the pickle file containing the tracklets.
        It is obtained after deeplabcut.convert_detections2tracklets()
        and typically ends with _bx or _sk.pickle.

    n_tracks : int, optional
        Number of tracks to reconstruct. By default, taken as the number
        of individuals defined in the config.yaml. Another number can be
        passed if the number of animals in the video is different from
        the number of animals the model was trained on.

    min_length : int, optional
        Tracklets less than `min_length` frames of length
        are considered to be residuals; i.e., they do not participate
        in building the graph and finding the solution to the
        optimization problem, but are rather added last after
        "almost-complete" tracks are formed. The higher the value,
        the lesser the computational cost, but the higher the chance of
        discarding relatively long and reliable tracklets that are
        essential to solving the stitching task.
        Default is 10, and must be 3 at least.

    split_tracklets : bool, optional
        By default, tracklets whose time indices are not consecutive integers
        are split in shorter tracklets whose time continuity is guaranteed.
        This is for example very powerful to get rid of tracking errors
        (e.g., identity switches) which are often signaled by a missing
        time frame at the moment they occur. Note though that for long
        occlusions where tracker re-identification capability can be trusted,
        setting `split_tracklets` to False is preferable.

    prestitch_residuals : bool, optional
        Residuals will by default be grouped together according to their
        temporal proximity prior to being added back to the tracks.
        This is done to improve robustness and simultaneously reduce complexity.

    max_gap : int, optional
        Maximal temporal gap to allow between a pair of tracklets.
        This is automatically determined by the TrackletStitcher by default.

    weight_func : callable, optional
        Function accepting two tracklets as arguments and returning a scalar
        that must be inversely proportional to the likelihood that the tracklets
        belong to the same track; i.e., the higher the confidence that the
        tracklets should be stitched together, the lower the returned value.

    output_name : str, optional
        Name of the output h5 file.
        By default, tracks are automatically stored into the same directory
        as the pickle file and with its name.

    Returns
    -------
    A TrackletStitcher object
    """
    cfg = read_config(config_path)
    animal_names = cfg["individuals"]
    if n_tracks is None:
        n_tracks = len(animal_names)
    stitcher = TrackletStitcher.from_pickle(pickle_file, n_tracks, min_length,
                                            split_tracklets,
                                            prestitch_residuals)
    with_id = any(tracklet.identity != -1 for tracklet in stitcher)
    if with_id and weight_func is None:
        # Add in identity weighing before building the graph
        def weight_func(t1, t2):
            w = 0.01 if t1.identity == t2.identity else 1
            return w * stitcher.calculate_edge_weight(t1, t2)

    stitcher.build_graph(max_gap=max_gap, weight_func=weight_func)
    stitcher.stitch()
    stitcher.write_tracks(output_name, animal_names)
    return stitcher
Example #2
0
    def __init__(self, parent, gui_size, cfg):
        """Constructor"""
        wx.Panel.__init__(self, parent=parent)

        # variable initilization
        self.config = cfg
        self.cfg = utils.read_config(cfg)
        self.filelist = []
        # design the panel
        self.sizer = wx.GridBagSizer(5, 5)

        text = wx.StaticText(
            self, label="DeepLabCut - Step 8. Extract outlier frames")
        self.sizer.Add(text,
                       pos=(0, 0),
                       flag=wx.TOP | wx.LEFT | wx.BOTTOM,
                       border=15)
        # Add logo of DLC
        icon = wx.StaticBitmap(self, bitmap=wx.Bitmap(logo))
        self.sizer.Add(icon,
                       pos=(0, 4),
                       flag=wx.TOP | wx.RIGHT | wx.ALIGN_RIGHT,
                       border=5)

        line1 = wx.StaticLine(self)
        self.sizer.Add(line1,
                       pos=(1, 0),
                       span=(1, 5),
                       flag=wx.EXPAND | wx.BOTTOM,
                       border=10)

        self.cfg_text = wx.StaticText(self, label="Select the config file")
        self.sizer.Add(self.cfg_text,
                       pos=(2, 0),
                       flag=wx.TOP | wx.LEFT,
                       border=5)

        if sys.platform == "darwin":
            self.sel_config = wx.FilePickerCtrl(
                self,
                path="",
                style=wx.FLP_USE_TEXTCTRL,
                message="Choose the config.yaml file",
                wildcard="*.yaml",
            )
        else:
            self.sel_config = wx.FilePickerCtrl(
                self,
                path="",
                style=wx.FLP_USE_TEXTCTRL,
                message="Choose the config.yaml file",
                wildcard="config.yaml",
            )
        # self.sel_config = wx.FilePickerCtrl(self, path="",style=wx.FLP_USE_TEXTCTRL,message="Choose the config.yaml file", wildcard="config.yaml")
        self.sizer.Add(self.sel_config,
                       pos=(2, 1),
                       span=(1, 3),
                       flag=wx.TOP | wx.EXPAND,
                       border=5)
        self.sel_config.SetPath(self.config)
        self.sel_config.Bind(wx.EVT_FILEPICKER_CHANGED, self.select_config)

        self.vids = wx.StaticText(self, label="Choose the videos")
        self.sizer.Add(self.vids, pos=(3, 0), flag=wx.TOP | wx.LEFT, border=10)

        self.sel_vids = wx.Button(self, label="Select videos to analyze")
        self.sizer.Add(self.sel_vids,
                       pos=(3, 1),
                       flag=wx.TOP | wx.EXPAND,
                       border=5)
        self.sel_vids.Bind(wx.EVT_BUTTON, self.select_videos)

        sb = wx.StaticBox(self, label="Optional Attributes")
        boxsizer = wx.StaticBoxSizer(sb, wx.VERTICAL)

        hbox1 = wx.BoxSizer(wx.HORIZONTAL)
        hbox2 = wx.BoxSizer(wx.HORIZONTAL)

        videotype_text = wx.StaticBox(self, label="Specify the videotype")
        videotype_text_boxsizer = wx.StaticBoxSizer(videotype_text,
                                                    wx.VERTICAL)
        videotypes = [".avi", ".mp4", ".mov"]
        self.videotype = wx.ComboBox(self,
                                     choices=videotypes,
                                     style=wx.CB_READONLY)
        self.videotype.SetValue(".avi")
        videotype_text_boxsizer.Add(self.videotype, 1,
                                    wx.EXPAND | wx.TOP | wx.BOTTOM, 10)

        shuffles_text = wx.StaticBox(self, label="Specify the shuffle")
        shuffles_text_boxsizer = wx.StaticBoxSizer(shuffles_text, wx.VERTICAL)
        self.shuffles = wx.SpinCtrl(self, value="1", min=0, max=100)
        shuffles_text_boxsizer.Add(self.shuffles, 1,
                                   wx.EXPAND | wx.TOP | wx.BOTTOM, 10)

        trainingindex = wx.StaticBox(self,
                                     label="Specify the trainingset index")
        trainingindex_boxsizer = wx.StaticBoxSizer(trainingindex, wx.VERTICAL)
        self.trainingindex = wx.SpinCtrl(self, value="0", min=0, max=100)
        trainingindex_boxsizer.Add(self.trainingindex, 1,
                                   wx.EXPAND | wx.TOP | wx.BOTTOM, 10)

        outlier_algo_text = wx.StaticBox(self, label="Specify the algorithm")
        outlier_algo_text_boxsizer = wx.StaticBoxSizer(outlier_algo_text,
                                                       wx.VERTICAL)
        algotypes = ["jump", "fitting", "uncertain", "manual"]
        self.algotype = wx.ComboBox(self,
                                    choices=algotypes,
                                    style=wx.CB_READONLY)
        self.algotype.SetValue("jump")
        outlier_algo_text_boxsizer.Add(self.algotype, 1,
                                       wx.EXPAND | wx.TOP | wx.BOTTOM, 10)

        hbox1.Add(videotype_text_boxsizer, 10, wx.EXPAND | wx.TOP | wx.BOTTOM,
                  5)
        hbox1.Add(shuffles_text_boxsizer, 10, wx.EXPAND | wx.TOP | wx.BOTTOM,
                  5)
        hbox1.Add(trainingindex_boxsizer, 10, wx.EXPAND | wx.TOP | wx.BOTTOM,
                  5)
        hbox2.Add(outlier_algo_text_boxsizer, 10,
                  wx.EXPAND | wx.TOP | wx.BOTTOM, 5)

        if self.cfg.get("multianimalproject", False):
            tracker_text = wx.StaticBox(self,
                                        label="Specify the Tracker Method!")
            tracker_text_boxsizer = wx.StaticBoxSizer(tracker_text,
                                                      wx.VERTICAL)
            trackertypes = ["skeleton", "box"]
            self.trackertypes = wx.ComboBox(self,
                                            choices=trackertypes,
                                            style=wx.CB_READONLY)
            self.trackertypes.SetValue("box")
            tracker_text_boxsizer.Add(self.trackertypes, 1,
                                      wx.EXPAND | wx.TOP | wx.BOTTOM, 10)
            hbox2.Add(tracker_text_boxsizer, 5, wx.EXPAND | wx.TOP | wx.BOTTOM,
                      5)

        boxsizer.Add(hbox1, 0, wx.EXPAND | wx.TOP | wx.BOTTOM, 10)
        boxsizer.Add(hbox2, 0, wx.EXPAND | wx.TOP | wx.BOTTOM, 10)

        self.sizer.Add(
            boxsizer,
            pos=(4, 0),
            span=(1, 5),
            flag=wx.EXPAND | wx.TOP | wx.LEFT | wx.RIGHT,
            border=10,
        )

        self.help_button = wx.Button(self, label="Help")
        self.sizer.Add(self.help_button, pos=(6, 0), flag=wx.LEFT, border=10)
        self.help_button.Bind(wx.EVT_BUTTON, self.help_function)

        self.ok = wx.Button(self, label="Ok")
        self.sizer.Add(self.ok, pos=(6, 4))
        self.ok.Bind(wx.EVT_BUTTON, self.extract_outlier_frames)

        self.reset = wx.Button(self, label="Reset")
        self.sizer.Add(self.reset,
                       pos=(6, 1),
                       span=(1, 1),
                       flag=wx.BOTTOM | wx.RIGHT,
                       border=10)
        self.reset.Bind(wx.EVT_BUTTON, self.reset_extract_outlier_frames)

        self.sizer.AddGrowableCol(2)

        self.SetSizer(self.sizer)
        self.sizer.Fit(self)
Example #3
0
    def __init__(self, parent, gui_size, cfg):
        """Constructor"""
        wx.Panel.__init__(self, parent=parent)

        # variable initialization
        self.config = cfg
        self.cfg = utils.read_config(cfg)
        self.filelist = []
        # design the panel
        self.sizer = wx.GridBagSizer(5, 5)

        text = wx.StaticText(
            self,
            label=
            "DeepLabCut - OPTIONAL: Unsupervised ID Tracking with Transformer")
        self.sizer.Add(text,
                       pos=(0, 0),
                       flag=wx.TOP | wx.LEFT | wx.BOTTOM,
                       border=15)
        # Add logo of DLC
        icon = wx.StaticBitmap(self, bitmap=wx.Bitmap(LOGO_PATH))
        self.sizer.Add(icon,
                       pos=(0, 4),
                       flag=wx.TOP | wx.RIGHT | wx.ALIGN_RIGHT,
                       border=5)

        line1 = wx.StaticLine(self)
        self.sizer.Add(line1,
                       pos=(1, 0),
                       span=(1, 5),
                       flag=wx.EXPAND | wx.BOTTOM,
                       border=10)

        self.cfg_text = wx.StaticText(self, label="Select the config file")
        self.sizer.Add(self.cfg_text,
                       pos=(2, 0),
                       flag=wx.TOP | wx.LEFT,
                       border=5)

        if sys.platform == "darwin":
            self.sel_config = wx.FilePickerCtrl(
                self,
                path="",
                style=wx.FLP_USE_TEXTCTRL,
                message="Choose the config.yaml file",
                wildcard="*.yaml",
            )
        else:
            self.sel_config = wx.FilePickerCtrl(
                self,
                path="",
                style=wx.FLP_USE_TEXTCTRL,
                message="Choose the config.yaml file",
                wildcard="config.yaml",
            )
        # self.sel_config = wx.FilePickerCtrl(self, path="",style=wx.FLP_USE_TEXTCTRL,message="Choose the config.yaml file", wildcard="config.yaml")
        self.sizer.Add(self.sel_config,
                       pos=(2, 1),
                       span=(1, 3),
                       flag=wx.TOP | wx.EXPAND,
                       border=5)
        self.sel_config.SetPath(self.config)
        self.sel_config.Bind(wx.EVT_FILEPICKER_CHANGED, self.select_config)

        self.vids = wx.StaticText(self, label="Choose the videos")
        self.sizer.Add(self.vids, pos=(3, 0), flag=wx.TOP | wx.LEFT, border=10)

        self.sel_vids = wx.Button(self, label="Select videos to analyze")
        self.sizer.Add(self.sel_vids,
                       pos=(3, 1),
                       flag=wx.TOP | wx.EXPAND,
                       border=5)
        self.sel_vids.Bind(wx.EVT_BUTTON, self.select_videos)

        sb = wx.StaticBox(self, label="Optional Attributes")
        boxsizer = wx.StaticBoxSizer(sb, wx.VERTICAL)

        hbox1 = wx.BoxSizer(wx.HORIZONTAL)
        hbox2 = wx.BoxSizer(wx.HORIZONTAL)

        videotype_text = wx.StaticBox(self, label="Specify the videotype")
        videotype_text_boxsizer = wx.StaticBoxSizer(videotype_text,
                                                    wx.VERTICAL)
        videotypes = [".avi", ".mp4", ".mov"]
        self.videotype = wx.ComboBox(self,
                                     choices=videotypes,
                                     style=wx.CB_READONLY)
        self.videotype.SetValue(".avi")
        videotype_text_boxsizer.Add(self.videotype, 1,
                                    wx.EXPAND | wx.TOP | wx.BOTTOM, 1)

        shuffles_text = wx.StaticBox(self, label="Specify the shuffle")
        shuffles_text_boxsizer = wx.StaticBoxSizer(shuffles_text, wx.VERTICAL)
        self.shuffles = wx.SpinCtrl(self, value="1", min=0, max=100)
        shuffles_text_boxsizer.Add(self.shuffles, 1,
                                   wx.EXPAND | wx.TOP | wx.BOTTOM, 1)

        ntracks_text = wx.StaticBox(self, label="Specify the no. of animals")
        ntracks_text_boxsizer = wx.StaticBoxSizer(ntracks_text, wx.VERTICAL)
        self.n_tracks = wx.SpinCtrl(self, value="2", min=0, max=100)
        ntracks_text_boxsizer.Add(self.n_tracks, 2,
                                  wx.EXPAND | wx.TOP | wx.BOTTOM, 1)

        ntriplets_text = wx.StaticBox(self, label="Specify the no. triplets")
        ntriplets_text_boxsizer = wx.StaticBoxSizer(ntriplets_text,
                                                    wx.VERTICAL)
        self.ntriplets = wx.SpinCtrl(self, value="1000", min=100, max=5000)
        ntriplets_text_boxsizer.Add(self.ntriplets, 2,
                                    wx.EXPAND | wx.TOP | wx.BOTTOM, 1)

        trackertype_text = wx.StaticBox(
            self, label="Specify the tracking type (ellipse recommended)")
        trackertype_text_boxsizer = wx.StaticBoxSizer(trackertype_text,
                                                      wx.VERTICAL)
        trackertype = ["ellipse", "box"]
        self.trackertype = wx.ComboBox(self,
                                       choices=trackertype,
                                       style=wx.CB_READONLY)
        self.trackertype.SetValue("ellipse")
        trackertype_text_boxsizer.Add(self.trackertype, 1,
                                      wx.EXPAND | wx.TOP | wx.BOTTOM, 1)

        hbox1.Add(videotype_text_boxsizer, 10, wx.EXPAND | wx.TOP | wx.BOTTOM,
                  5)
        hbox1.Add(shuffles_text_boxsizer, 10, wx.EXPAND | wx.TOP | wx.BOTTOM,
                  5)
        hbox2.Add(ntracks_text_boxsizer, 10, wx.EXPAND | wx.TOP | wx.BOTTOM, 5)
        hbox2.Add(ntriplets_text_boxsizer, 10, wx.EXPAND | wx.TOP | wx.BOTTOM,
                  5)
        hbox2.Add(trackertype_text_boxsizer, 10,
                  wx.EXPAND | wx.TOP | wx.BOTTOM, 5)

        boxsizer.Add(hbox1, 0, wx.EXPAND | wx.TOP | wx.BOTTOM, 10)
        boxsizer.Add(hbox2, 0, wx.EXPAND | wx.TOP | wx.BOTTOM, 10)

        self.sizer.Add(
            boxsizer,
            pos=(4, 0),
            span=(1, 5),
            flag=wx.EXPAND | wx.TOP | wx.LEFT | wx.RIGHT,
            border=10,
        )

        ### train and go ###

        self.help_button = wx.Button(self, label="Help")
        self.sizer.Add(self.help_button, pos=(6, 0), flag=wx.LEFT, border=10)
        self.help_button.Bind(wx.EVT_BUTTON, self.help_function)

        self.ok = wx.Button(self, label="Run Transformer")
        self.sizer.Add(self.ok, pos=(6, 4))
        self.ok.Bind(wx.EVT_BUTTON, self.transformer_reID)

        self.reset = wx.Button(self, label="Reset")
        self.sizer.Add(self.reset,
                       pos=(6, 1),
                       span=(1, 1),
                       flag=wx.BOTTOM | wx.RIGHT,
                       border=10)
        self.reset.Bind(wx.EVT_BUTTON, self.reset_transformer_reID)

        self.sizer.AddGrowableCol(2)

        self.SetSizer(self.sizer)
        self.sizer.Fit(self)
Example #4
0
def stitch_tracklets(
    config_path,
    videos,
    videotype="avi",
    shuffle=1,
    trainingsetindex=0,
    n_tracks=None,
    min_length=10,
    split_tracklets=True,
    prestitch_residuals=True,
    max_gap=None,
    weight_func=None,
    track_method="ellipse",
    destfolder=None,
    modelprefix="",
    output_name="",
):
    """
    Stitch sparse tracklets into full tracks via a graph-based,
    minimum-cost flow optimization problem.

    Parameters
    ----------
    config_path : str
        Path to the main project config.yaml file.

    videos : list
        A list of strings containing the full paths to videos for analysis or a path to the directory, where all the videos with same extension are stored.

    videotype: string, optional
        Checks for the extension of the video in case the input to the video is a directory.\n Only videos with this extension are analyzed. The default is ``.avi``

    shuffle: int, optional
        An integer specifying the shuffle index of the training dataset used for training the network. The default is 1.

    trainingsetindex: int, optional
        Integer specifying which TrainingsetFraction to use. By default the first (note that TrainingFraction is a list in config.yaml).

    n_tracks : int, optional
        Number of tracks to reconstruct. By default, taken as the number
        of individuals defined in the config.yaml. Another number can be
        passed if the number of animals in the video is different from
        the number of animals the model was trained on.

    min_length : int, optional
        Tracklets less than `min_length` frames of length
        are considered to be residuals; i.e., they do not participate
        in building the graph and finding the solution to the
        optimization problem, but are rather added last after
        "almost-complete" tracks are formed. The higher the value,
        the lesser the computational cost, but the higher the chance of
        discarding relatively long and reliable tracklets that are
        essential to solving the stitching task.
        Default is 10, and must be 3 at least.

    split_tracklets : bool, optional
        By default, tracklets whose time indices are not consecutive integers
        are split in shorter tracklets whose time continuity is guaranteed.
        This is for example very powerful to get rid of tracking errors
        (e.g., identity switches) which are often signaled by a missing
        time frame at the moment they occur. Note though that for long
        occlusions where tracker re-identification capability can be trusted,
        setting `split_tracklets` to False is preferable.

    prestitch_residuals : bool, optional
        Residuals will by default be grouped together according to their
        temporal proximity prior to being added back to the tracks.
        This is done to improve robustness and simultaneously reduce complexity.

    max_gap : int, optional
        Maximal temporal gap to allow between a pair of tracklets.
        This is automatically determined by the TrackletStitcher by default.

    weight_func : callable, optional
        Function accepting two tracklets as arguments and returning a scalar
        that must be inversely proportional to the likelihood that the tracklets
        belong to the same track; i.e., the higher the confidence that the
        tracklets should be stitched together, the lower the returned value.

    track_method: str, optional
        Method used to track animals, either 'box', 'skeleton', or 'ellipse' (default).

    destfolder: string, optional
        Specifies the destination folder for analysis data (default is the path of the video). Note that for subsequent analysis this
        folder also needs to be passed.

    output_name : str, optional
        Name of the output h5 file.
        By default, tracks are automatically stored into the same directory
        as the pickle file and with its name.

    Returns
    -------
    A TrackletStitcher object
    """
    vids = auxiliaryfunctions.Getlistofvideos(videos, videotype)
    if not vids:
        print("No video(s) found. Please check your path!")
        return

    cfg = read_config(config_path)
    animal_names = cfg["individuals"]
    if n_tracks is None:
        n_tracks = len(animal_names)

    DLCscorer, _ = auxiliaryfunctions.GetScorerName(
        cfg,
        shuffle,
        cfg["TrainingFraction"][trainingsetindex],
        modelprefix=modelprefix,
    )

    for video in vids:
        print("Processing... ", video)
        videofolder = str(Path(video).parents[0])
        dest = destfolder or videofolder
        auxiliaryfunctions.attempttomakefolder(dest)
        vname = Path(video).stem
        dataname = os.path.join(dest, vname + DLCscorer + ".h5")
        if track_method == "ellipse":
            method = "el"
        elif track_method == "box":
            method = "bx"
        else:
            method = "sk"
        pickle_file = dataname.split(".h5")[0] + f"_{method}.pickle"
        try:
            stitcher = TrackletStitcher.from_pickle(pickle_file, n_tracks,
                                                    min_length,
                                                    split_tracklets,
                                                    prestitch_residuals)
            with_id = any(tracklet.identity != -1 for tracklet in stitcher)
            if with_id and weight_func is None:
                # Add in identity weighing before building the graph
                def weight_func(t1, t2):
                    w = 0.01 if t1.identity == t2.identity else 1
                    return w * stitcher.calculate_edge_weight(t1, t2)

            stitcher.build_graph(max_gap=max_gap, weight_func=weight_func)
            stitcher.stitch()
            stitcher.write_tracks(output_name, animal_names)
        except FileNotFoundError as e:
            print(e, "\nSkipping...")
    def __init__(self, parent, gui_size, cfg):
        """Constructor"""
        wx.Panel.__init__(self, parent=parent)

        # variable initialization
        self.config = cfg
        self.cfg = utils.read_config(cfg)
        self.filelist = []
        # design the panel
        self.sizer = wx.GridBagSizer(5, 5)

        text = wx.StaticText(
            self,
            label=
            "DeepLabCut - OPTIONAL: Extract and Refine labels on Outlier Frames"
        )
        self.sizer.Add(text,
                       pos=(0, 0),
                       flag=wx.TOP | wx.LEFT | wx.BOTTOM,
                       border=15)
        # Add logo of DLC
        icon = wx.StaticBitmap(self, bitmap=wx.Bitmap(LOGO_PATH))
        self.sizer.Add(icon,
                       pos=(0, 4),
                       flag=wx.TOP | wx.RIGHT | wx.ALIGN_RIGHT,
                       border=5)

        line1 = wx.StaticLine(self)
        self.sizer.Add(line1,
                       pos=(1, 0),
                       span=(1, 5),
                       flag=wx.EXPAND | wx.BOTTOM,
                       border=10)

        self.cfg_text = wx.StaticText(self, label="Select the config file")
        self.sizer.Add(self.cfg_text,
                       pos=(2, 0),
                       flag=wx.TOP | wx.LEFT,
                       border=5)

        if sys.platform == "darwin":
            self.sel_config = wx.FilePickerCtrl(
                self,
                path="",
                style=wx.FLP_USE_TEXTCTRL,
                message="Choose the config.yaml file",
                wildcard="*.yaml",
            )
        else:
            self.sel_config = wx.FilePickerCtrl(
                self,
                path="",
                style=wx.FLP_USE_TEXTCTRL,
                message="Choose the config.yaml file",
                wildcard="config.yaml",
            )
        # self.sel_config = wx.FilePickerCtrl(self, path="",style=wx.FLP_USE_TEXTCTRL,message="Choose the config.yaml file", wildcard="config.yaml")
        self.sizer.Add(self.sel_config,
                       pos=(2, 1),
                       span=(1, 3),
                       flag=wx.TOP | wx.EXPAND,
                       border=5)
        self.sel_config.SetPath(self.config)
        self.sel_config.Bind(wx.EVT_FILEPICKER_CHANGED, self.select_config)

        self.vids = wx.StaticText(self, label="Choose the videos")
        self.sizer.Add(self.vids, pos=(3, 0), flag=wx.TOP | wx.LEFT, border=10)

        self.sel_vids = wx.Button(self, label="Select videos to analyze")
        self.sizer.Add(self.sel_vids,
                       pos=(3, 1),
                       flag=wx.TOP | wx.EXPAND,
                       border=5)
        self.sel_vids.Bind(wx.EVT_BUTTON, self.select_videos)

        sb = wx.StaticBox(self, label="Optional Attributes")
        boxsizer = wx.StaticBoxSizer(sb, wx.VERTICAL)

        hbox1 = wx.BoxSizer(wx.HORIZONTAL)

        videotype_text = wx.StaticBox(self, label="Specify the videotype")
        videotype_text_boxsizer = wx.StaticBoxSizer(videotype_text,
                                                    wx.VERTICAL)
        self.videotype = wx.ComboBox(self,
                                     choices=("", ) + SUPPORTED_VIDEOS,
                                     style=wx.CB_READONLY)
        self.videotype.SetValue("")
        videotype_text_boxsizer.Add(self.videotype, 1,
                                    wx.EXPAND | wx.TOP | wx.BOTTOM, 1)

        shuffles_text = wx.StaticBox(self, label="Specify the shuffle")
        shuffles_text_boxsizer = wx.StaticBoxSizer(shuffles_text, wx.VERTICAL)
        self.shuffles = wx.SpinCtrl(self, value="1", min=0, max=100)
        shuffles_text_boxsizer.Add(self.shuffles, 1,
                                   wx.EXPAND | wx.TOP | wx.BOTTOM, 1)

        outlier_algo_text = wx.StaticBox(self, label="Specify the algorithm")
        outlier_algo_text_boxsizer = wx.StaticBoxSizer(outlier_algo_text,
                                                       wx.VERTICAL)
        algotypes = ["jump", "fitting", "uncertain", "manual"]
        self.algotype = wx.ComboBox(self,
                                    choices=algotypes,
                                    style=wx.CB_READONLY)
        self.algotype.SetValue("jump")
        outlier_algo_text_boxsizer.Add(self.algotype, 1,
                                       wx.EXPAND | wx.TOP | wx.BOTTOM, 1)

        hbox1.Add(videotype_text_boxsizer, 10, wx.EXPAND | wx.TOP | wx.BOTTOM,
                  5)
        hbox1.Add(shuffles_text_boxsizer, 10, wx.EXPAND | wx.TOP | wx.BOTTOM,
                  5)
        hbox1.Add(outlier_algo_text_boxsizer, 10,
                  wx.EXPAND | wx.TOP | wx.BOTTOM, 5)
        boxsizer.Add(hbox1, 0, wx.EXPAND | wx.TOP | wx.BOTTOM, 10)

        self.sizer.Add(
            boxsizer,
            pos=(4, 0),
            span=(1, 5),
            flag=wx.EXPAND | wx.TOP | wx.LEFT | wx.RIGHT,
            border=10,
        )

        ### LABELING ###

        self.ok = wx.Button(self, label="LAUNCH GUI")
        self.sizer.Add(self.ok, pos=(7, 4))
        self.ok.Bind(wx.EVT_BUTTON, self.refine_labels)

        self.merge = wx.Button(self, label="Merge dataset")
        self.sizer.Add(self.merge,
                       pos=(7, 3),
                       flag=wx.BOTTOM | wx.RIGHT,
                       border=10)
        self.merge.Bind(wx.EVT_BUTTON, self.merge_dataset)
        self.merge.Enable(False)

        self.help_button = wx.Button(self, label="Help")
        self.sizer.Add(self.help_button, pos=(6, 0), flag=wx.LEFT, border=10)
        self.help_button.Bind(wx.EVT_BUTTON, self.help_function)

        self.ok = wx.Button(self, label="EXTRACT FRAMES")
        self.sizer.Add(self.ok, pos=(6, 4))
        self.ok.Bind(wx.EVT_BUTTON, self.extract_outlier_frames)

        self.reset = wx.Button(self, label="Reset")
        self.sizer.Add(self.reset,
                       pos=(6, 1),
                       span=(1, 1),
                       flag=wx.BOTTOM | wx.RIGHT,
                       border=10)
        self.reset.Bind(wx.EVT_BUTTON, self.reset_extract_outlier_frames)

        self.sizer.AddGrowableCol(2)

        self.SetSizer(self.sizer)
        self.sizer.Fit(self)