示例#1
0
    def execute_from_commandline(self, argv=None):
        """
        TODO
        """
        self.argv = argv or sys.argv[:]
        self.prog_name = os.path.basename(argv[0])

        # Preprocess options to extract --settings and --pythonpath.
        # These options could affect the commands that are available, so they
        # must be processed early.
        parser = LaxOptionParser(usage=self.usage,
                                 version=VERSION,
                                 option_list=BaseCommand.option_list)
        try:
            options, args = parser.parse_args(self.argv)
        except:
            pass  # Ignore any option errors at this point.

        # Initialize logging
        loglevel = (logging.CRITICAL, logging.ERROR, logging.WARNING,
                    logging.INFO, logging.DEBUG)[int(options.verbosity)]
        logging.basicConfig(
            level=loglevel,
            format='%(asctime)s %(levelname)-8s %(name)-30s %(message)s'
        )  #, datefmt='%H:%M:%S.%m')

        # Disable PyQt log messages
        logging.getLogger("PyQt5").setLevel(logging.WARNING)

        # Handle options common for all commands
        # and initialize the labeltool object from
        # the configuration (default config if not specified)
        if options.pythonpath:
            sys.path.insert(0, options.pythonpath)
        self.init_from_config(options.config)

        # check for commands
        try:
            subcommand = args[1]
        except IndexError:
            subcommand = None

        # handle commands and command line arguments
        if subcommand == 'help':
            if len(args) > 2:
                self.fetch_command(args[2]).print_help(self.prog_name, args[2])
                sys.exit(0)
            else:
                sys.stdout.write(self.main_help_text() + '\n')
                parser.print_lax_help()
                sys.exit(1)

        elif self.argv[1:] == ['--version']:
            # LaxOptionParser already takes care of printing the version.
            sys.exit(0)

        elif self.argv[1:] in (['--help'], ['-h']):
            sys.stdout.write(self.main_help_text() + '\n')
            parser.print_lax_help()
            sys.exit(0)

        elif subcommand in get_commands():
            self.fetch_command(subcommand).run_from_argv(self.argv)
            sys.exit(0)

        else:
            # Setup GUI
            self._mainwindow = MainWindow(self)
            self._mainwindow.show()

            # Load plugins
            self.loadPlugins(config.PLUGINS)

            # check if args contain a labelfile filename to load
            if len(args) > 1:
                try:
                    self.loadAnnotations(args[1], handleErrors=False)

                    # goto to first image
                    self.gotoNext()
                except Exception as e:
                    LOG.fatal("Error loading annotations: %s" % e)
                    if (int(options.verbosity)) > 1:
                        raise
                    else:
                        sys.exit(1)
            else:
                self.clearAnnotations()
示例#2
0
class LabelTool(QObject):
    """
    This is the main label tool object.  It stores the state of the tool, i.e.
    the current annotations, the containers responsible for loading and saving
    etc.

    It is also responsible for parsing command line options, call respective
    commands or start the gui.
    """
    usage = "\n" + \
            "  %prog [options] [filename]\n\n" + \
            "  %prog subcommand [options] [args]\n"

    help_text = "Sloth can be started in two different ways.  If the first argument\n" + \
                "is any of the following subcommands, this command is executed.  Otherwise the\n" + \
                "sloth GUI is started and the optionally given label file is loaded.\n" + \
                "\n" + \
                "Type '%s help <subcommand>' for help on a specific subcommand.\n\n"

    # Signals
    statusMessage = pyqtSignal(str)
    annotationsLoaded = pyqtSignal()
    pluginLoaded = pyqtSignal(QAction)
    # This still emits a QModelIndex, because Qt cannot handle emiting
    # a derived class instead of a base class, i.e. ImageFileModelItem
    # instead of ModelItem
    currentImageChanged = pyqtSignal()

    # TODO clean up --> prefix all members with _
    def __init__(self, parent=None):
        """
        Constructor.  Does nothing except resetting everything.
        Initialize the labeltool with either::

            execute_from_commandline()

        or::

            init_from_config()
        """
        QObject.__init__(self, parent)

        self._container_factory = None
        self._container = AnnotationContainer()
        self._current_image = None
        self._model = AnnotationModel([])
        self._mainwindow = None

    def main_help_text(self):
        """
        Returns the labeltool's main help text, as a string.

        Includes a list of all available subcommands.
        """
        usage = self.help_text % self.prog_name
        usage += 'Available subcommands:\n'
        commands = list(get_commands().keys())
        commands.sort()
        for cmd in commands:
            usage += '  %s\n' % cmd
        return usage

    def execute_from_commandline(self, argv=None):
        """
        TODO
        """
        self.argv = argv or sys.argv[:]
        self.prog_name = os.path.basename(argv[0])

        # Preprocess options to extract --settings and --pythonpath.
        # These options could affect the commands that are available, so they
        # must be processed early.
        parser = LaxOptionParser(usage=self.usage,
                                 version=VERSION,
                                 option_list=BaseCommand.option_list)
        try:
            options, args = parser.parse_args(self.argv)
        except:
            pass  # Ignore any option errors at this point.

        # Initialize logging
        loglevel = (logging.CRITICAL, logging.ERROR, logging.WARNING,
                    logging.INFO, logging.DEBUG)[int(options.verbosity)]
        logging.basicConfig(
            level=loglevel,
            format='%(asctime)s %(levelname)-8s %(name)-30s %(message)s'
        )  #, datefmt='%H:%M:%S.%m')

        # Disable PyQt log messages
        logging.getLogger("PyQt5").setLevel(logging.WARNING)

        # Handle options common for all commands
        # and initialize the labeltool object from
        # the configuration (default config if not specified)
        if options.pythonpath:
            sys.path.insert(0, options.pythonpath)
        self.init_from_config(options.config)

        # check for commands
        try:
            subcommand = args[1]
        except IndexError:
            subcommand = None

        # handle commands and command line arguments
        if subcommand == 'help':
            if len(args) > 2:
                self.fetch_command(args[2]).print_help(self.prog_name, args[2])
                sys.exit(0)
            else:
                sys.stdout.write(self.main_help_text() + '\n')
                parser.print_lax_help()
                sys.exit(1)

        elif self.argv[1:] == ['--version']:
            # LaxOptionParser already takes care of printing the version.
            sys.exit(0)

        elif self.argv[1:] in (['--help'], ['-h']):
            sys.stdout.write(self.main_help_text() + '\n')
            parser.print_lax_help()
            sys.exit(0)

        elif subcommand in get_commands():
            self.fetch_command(subcommand).run_from_argv(self.argv)
            sys.exit(0)

        else:
            # Setup GUI
            self._mainwindow = MainWindow(self)
            self._mainwindow.show()

            # Load plugins
            self.loadPlugins(config.PLUGINS)

            # check if args contain a labelfile filename to load
            if len(args) > 1:
                try:
                    self.loadAnnotations(args[1], handleErrors=False)

                    # goto to first image
                    self.gotoNext()
                except Exception as e:
                    LOG.fatal("Error loading annotations: %s" % e)
                    if (int(options.verbosity)) > 1:
                        raise
                    else:
                        sys.exit(1)
            else:
                self.clearAnnotations()

    def fetch_command(self, subcommand):
        """
        Tries to fetch the given subcommand, printing a message with the
        appropriate command called from the command line if it can't be found.
        """
        try:
            app_name = get_commands()[subcommand]
        except KeyError:
            sys.stderr.write(
                "Unknown command: %r\nType '%s help' for usage.\n" %
                (subcommand, self.prog_name))
            sys.exit(1)
        if isinstance(app_name, BaseCommand):
            # If the command is already loaded, use it directly.
            klass = app_name
        else:
            # TODO implement load_command_class
            klass = load_command_class(app_name, subcommand)

        # set labeltool reference
        klass.labeltool = self

        return klass

    def init_from_config(self, config_module_path=""):
        """
        Initializes the labeltool from the given configuration
        at ``config_module_path``.  If empty, the default configuration
        is used.
        """
        # Load config
        if config_module_path:
            config.update(config_module_path)

        # Instatiate container factory
        self._container_factory = AnnotationContainerFactory(config.CONTAINERS)

    def loadPlugins(self, plugins):
        self._plugins = []
        for plugin in plugins:
            if type(plugin) == str:
                plugin = import_callable(plugin)
            p = plugin(self)
            self._plugins.append(p)
            action = p.action()
            self.pluginLoaded.emit(action)

    ###
    ### Annoation file handling
    ###___________________________________________________________________________________________
    def loadAnnotations(self, fname, handleErrors=True):
        fname = str(fname)  # convert from QString

        try:
            self._container = self._container_factory.create(fname)
            self._model = AnnotationModel(self._container.load(fname))
            msg = "Successfully loaded %s (%d files, %d annotations)" % \
                  (fname, self._model.root().numFiles(), self._model.root().numAnnotations())
        except Exception as e:
            if handleErrors:
                msg = "Error: Loading failed (%s)" % str(e)
            else:
                raise

        self.statusMessage.emit(msg)
        self.annotationsLoaded.emit()

    def annotations(self):
        if self._model is None:
            return None
        return self._model.root().getAnnotations()

    def saveAnnotations(self, fname):
        success = False
        try:
            # create new container if the filename is different
            if fname != self._container.filename():
                self._container = self._container_factory.create(fname)

            # Get annotations dict
            ann = self._model.root().getAnnotations()

            self._container.save(ann, fname)
            #self._model.writeback() # write back changes that are cached in the model itself, e.g. mask updates
            msg = "Successfully saved %s (%d files, %d annotations)" % \
                  (fname, self._model.root().numFiles(), self._model.root().numAnnotations())
            success = True
            self._model.setDirty(False)
        except Exception as e:
            msg = "Error: Saving failed (%s)" % str(e)

        self.statusMessage.emit(msg)
        return success

    def clearAnnotations(self):
        self._model = AnnotationModel([])
        #self._model.setBasedir("")
        self.statusMessage.emit('')
        self.annotationsLoaded.emit()

    def getCurrentFilename(self):
        return self._container.filename()

    ###########################################################################
    # Model stuff
    ###########################################################################

    def model(self):
        return self._model

    def gotoIndex(self, idx):
        if self._model is None:
            return

        current = self._current_image
        if current is None:
            current = next(self._model.iterator(ImageModelItem))

        next_image = current.getSibling(idx)
        if next_image is not None:
            self.setCurrentImage(next_image)

    def gotoNext(self, step=1):
        if self._model is not None:
            if self._current_image is not None:
                next_image = self._current_image.getNextSibling(step)
            else:
                next_image = next(self._model.iterator(ImageModelItem))
                if next_image is not None:
                    next_image = next_image.getNextSibling(step - 1)

            if next_image is not None:
                self.setCurrentImage(next_image)

    def gotoPrevious(self, step=1):
        if self._model is not None and self._current_image is not None:
            prev_image = self._current_image.getPreviousSibling(step)

            if prev_image is not None:
                self.setCurrentImage(prev_image)

    def updateModified(self):
        """update all GUI elements which depend on the state of the model,
        e.g. whether it has been modified since the last save"""
        #self.ui.action_Add_Image.setEnabled(self._model is not None)
        # TODO also disable/enable other items
        #self.ui.actionSave.setEnabled(self.annotations.dirty())
        #self.setWindowModified(self.annotations.dirty())
        pass

    def currentImage(self):
        return self._current_image

    def setCurrentImage(self, image):
        if isinstance(image, QModelIndex):
            image = self._model.itemFromIndex(image)
        if isinstance(image, RootModelItem):
            return
        while (image is not None) and (not isinstance(image, ImageModelItem)):
            image = image.parent()
        if image is None:
            raise RuntimeError(
                "Tried to set current image to item that has no Image or Frame as parent!"
            )
        if image != self._current_image:
            self._current_image = image
            self.currentImageChanged.emit()

    def getImage(self, item):
        if item['class'] == 'frame':
            video = item.parent()
            return self._container.loadFrame(video['filename'], item['num'])
        else:
            return self._container.loadImage(item['filename'])

    def getAnnotationFilePatterns(self):
        return self._container_factory.patterns()

    def addImageFile(self, fname):
        fileitem = {
            'filename': fname,
            'class': 'image',
            'annotations': [],
        }
        return self._model._root.appendFileItem(fileitem)

    def addVideoFile(self, fname):
        fileitem = {
            'filename': fname,
            'class': 'video',
            'frames': [],
        }

        # FIXME: OKAPI should provide a method to get all timestamps at once
        # FIXME: Some dialog should be displayed, telling the user that the
        # video is being loaded/indexed and that this might take a while
        LOG.info("Importing frames from %s. This may take a while..." % fname)
        video = okv.createVideoSourceFromString(fname)
        video = okv.toRandomAccessVideoSource(video)

        # try to convert to iseq, getting all timestamps will be significantly faster
        iseq = okv.toImageSeqReader(video)
        if iseq is not None:
            timestamps = iseq.getTimestamps()
            LOG.debug("Adding %d frames" % len(timestamps))
            fileitem['frames'] = [{
                'annotations': [],
                'num': i,
                'timestamp': ts,
                'class': 'frame'
            } for i, ts in enumerate(timestamps)]
        else:
            i = 0
            while video.getNextFrame():
                LOG.debug("Adding frame %d" % i)
                ts = video.getTimestamp()
                frame = {
                    'annotations': [],
                    'num': i,
                    'timestamp': ts,
                    'class': 'frame'
                }
                fileitem['frames'].append(frame)
                i += 1

        self._model._root.appendFileItem(fileitem)

    ###
    ### GUI functions
    ###___________________________________________________________________________________________
    def mainWindow(self):
        return self._mainwindow

    ###
    ### PropertyEditor functions
    ###___________________________________________________________________________________________
    def propertyeditor(self):
        if self._mainwindow is None:
            return None
        else:
            return self._mainwindow.property_editor

    ###
    ### Scene functions
    ###___________________________________________________________________________________________
    def scene(self):
        if self._mainwindow is None:
            return None
        else:
            return self._mainwindow.scene

    def view(self):
        if self._mainwindow is None:
            return None
        else:
            return self._mainwindow.view

    def selectNextAnnotation(self):
        if self._mainwindow is not None:
            return self._mainwindow.scene.selectNextItem()

    def selectPreviousAnnotation(self):
        if self._mainwindow is not None:
            return self._mainwindow.scene.selectNextItem(reverse=True)

    def selectAllAnnotations(self):
        if self._mainwindow is not None:
            return self._mainwindow.scene.selectAllItems()

    def deleteSelectedAnnotations(self):
        if self._mainwindow is not None:
            self._mainwindow.scene.deleteSelectedItems()

    def exitInsertMode(self):
        if self._mainwindow is not None:
            return self._mainwindow.property_editor.endInsertionMode()

    ###
    ### TreeView functions
    ###___________________________________________________________________________________________
    def treeview(self):
        if self._mainwindow is None:
            return None
        else:
            return self._mainwindow.treeview
    def execute_from_commandline(self, argv=None):
        """
        TODO
        """
        self.argv = argv or sys.argv[:]
        self.prog_name = os.path.basename(argv[0])

        # Preprocess options to extract --settings and --pythonpath.
        # These options could affect the commands that are available, so they
        # must be processed early.
        parser = LaxOptionParser(usage=self.usage,
                                 version=VERSION,
                                 option_list=BaseCommand.option_list)
        try:
            options, args = parser.parse_args(self.argv)
        except:
            pass  # Ignore any option errors at this point.

        # Initialize logging
        loglevel = (logging.CRITICAL, logging.ERROR, logging.WARNING, logging.INFO, logging.DEBUG)[int(options.verbosity)]
        logging.basicConfig(level=loglevel, format='%(asctime)s %(levelname)-8s %(name)-30s %(message)s') #, datefmt='%H:%M:%S.%m')

        # Disable PyQt log messages
        logging.getLogger("PyQt4").setLevel(logging.WARNING)

        # Handle options common for all commands
        # and initialize the labeltool object from
        # the configuration (default config if not specified)
        if options.pythonpath:
            sys.path.insert(0, options.pythonpath)
        self.init_from_config(options.config)

        # check for commands
        try:
            subcommand = args[1]
        except IndexError:
            subcommand = None

        # handle commands and command line arguments
        if subcommand == 'help':
            if len(args) > 2:
                self.fetch_command(args[2]).print_help(self.prog_name, args[2])
                sys.exit(0)
            else:
                sys.stdout.write(self.main_help_text() + '\n')
                parser.print_lax_help()
                sys.exit(1)

        elif self.argv[1:] == ['--version']:
            # LaxOptionParser already takes care of printing the version.
            sys.exit(0)

        elif self.argv[1:] in (['--help'], ['-h']):
            sys.stdout.write(self.main_help_text() + '\n')
            parser.print_lax_help()
            sys.exit(0)

        elif subcommand in get_commands():
            self.fetch_command(subcommand).run_from_argv(self.argv)
            sys.exit(0)

        else:
            # Setup GUI
            self._mainwindow = MainWindow(self)
            self._mainwindow.show()

            # Load plugins
            self.loadPlugins(config.PLUGINS)

            # check if args contain a labelfile filename to load
            if len(args) > 1:
                try:
                    self.loadAnnotations(args[1], handleErrors=False)

                    # goto to first image
                    self.gotoNext()
                except Exception as e:
                    LOG.fatal("Error loading annotations: %s" % e)
                    if (int(options.verbosity)) > 1:
                        raise
                    else:
                        sys.exit(1)
            else:
                self.clearAnnotations()
class LabelTool(QObject):
    """
    This is the main label tool object.  It stores the state of the tool, i.e.
    the current annotations, the containers responsible for loading and saving
    etc.

    It is also responsible for parsing command line options, call respective
    commands or start the gui.
    """
    usage = "\n" + \
            "  %prog [options] [filename]\n\n" + \
            "  %prog subcommand [options] [args]\n"

    help_text = "Sloth can be started in two different ways.  If the first argument\n" + \
                "is any of the following subcommands, this command is executed.  Otherwise the\n" + \
                "sloth GUI is started and the optionally given label file is loaded.\n" + \
                "\n" + \
                "Type '%s help <subcommand>' for help on a specific subcommand.\n\n"

    # Signals
    statusMessage       = pyqtSignal(str)
    annotationsLoaded   = pyqtSignal()
    pluginLoaded        = pyqtSignal(QAction)
    # This still emits a QModelIndex, because Qt cannot handle emiting
    # a derived class instead of a base class, i.e. ImageFileModelItem
    # instead of ModelItem
    currentImageChanged = pyqtSignal()

    # TODO clean up --> prefix all members with _
    def __init__(self, parent=None):
        """
        Constructor.  Does nothing except resetting everything.
        Initialize the labeltool with either::

            execute_from_commandline()

        or::

            init_from_config()
        """
        QObject.__init__(self, parent)

        self._container_factory = None
        self._container = AnnotationContainer()
        self._current_image = None
        self._model = AnnotationModel([])
        self._mainwindow = None

    def main_help_text(self):
        """
        Returns the labeltool's main help text, as a string.

        Includes a list of all available subcommands.
        """
        usage = self.help_text % self.prog_name
        usage += 'Available subcommands:\n'
        commands = list(get_commands().keys())
        commands.sort()
        for cmd in commands:
            usage += '  %s\n' % cmd
        return usage

    def execute_from_commandline(self, argv=None):
        """
        TODO
        """
        self.argv = argv or sys.argv[:]
        self.prog_name = os.path.basename(argv[0])

        # Preprocess options to extract --settings and --pythonpath.
        # These options could affect the commands that are available, so they
        # must be processed early.
        parser = LaxOptionParser(usage=self.usage,
                                 version=VERSION,
                                 option_list=BaseCommand.option_list)
        try:
            options, args = parser.parse_args(self.argv)
        except:
            pass  # Ignore any option errors at this point.

        # Initialize logging
        loglevel = (logging.CRITICAL, logging.ERROR, logging.WARNING, logging.INFO, logging.DEBUG)[int(options.verbosity)]
        logging.basicConfig(level=loglevel, format='%(asctime)s %(levelname)-8s %(name)-30s %(message)s') #, datefmt='%H:%M:%S.%m')

        # Disable PyQt log messages
        logging.getLogger("PyQt4").setLevel(logging.WARNING)

        # Handle options common for all commands
        # and initialize the labeltool object from
        # the configuration (default config if not specified)
        if options.pythonpath:
            sys.path.insert(0, options.pythonpath)
        self.init_from_config(options.config)

        # check for commands
        try:
            subcommand = args[1]
        except IndexError:
            subcommand = None

        # handle commands and command line arguments
        if subcommand == 'help':
            if len(args) > 2:
                self.fetch_command(args[2]).print_help(self.prog_name, args[2])
                sys.exit(0)
            else:
                sys.stdout.write(self.main_help_text() + '\n')
                parser.print_lax_help()
                sys.exit(1)

        elif self.argv[1:] == ['--version']:
            # LaxOptionParser already takes care of printing the version.
            sys.exit(0)

        elif self.argv[1:] in (['--help'], ['-h']):
            sys.stdout.write(self.main_help_text() + '\n')
            parser.print_lax_help()
            sys.exit(0)

        elif subcommand in get_commands():
            self.fetch_command(subcommand).run_from_argv(self.argv)
            sys.exit(0)

        else:
            # Setup GUI
            self._mainwindow = MainWindow(self)
            self._mainwindow.show()

            # Load plugins
            self.loadPlugins(config.PLUGINS)

            # check if args contain a labelfile filename to load
            if len(args) > 1:
                try:
                    self.loadAnnotations(args[1], handleErrors=False)

                    # goto to first image
                    self.gotoNext()
                except Exception as e:
                    LOG.fatal("Error loading annotations: %s" % e)
                    if (int(options.verbosity)) > 1:
                        raise
                    else:
                        sys.exit(1)
            else:
                self.clearAnnotations()


    def fetch_command(self, subcommand):
        """
        Tries to fetch the given subcommand, printing a message with the
        appropriate command called from the command line if it can't be found.
        """
        try:
            app_name = get_commands()[subcommand]
        except KeyError:
            sys.stderr.write("Unknown command: %r\nType '%s help' for usage.\n" % \
                (subcommand, self.prog_name))
            sys.exit(1)
        if isinstance(app_name, BaseCommand):
            # If the command is already loaded, use it directly.
            klass = app_name
        else:
            # TODO implement load_command_class
            klass = load_command_class(app_name, subcommand)

        # set labeltool reference
        klass.labeltool = self

        return klass

    def init_from_config(self, config_module_path=""):
        """
        Initializes the labeltool from the given configuration
        at ``config_module_path``.  If empty, the default configuration
        is used.
        """
        # Load config
        if config_module_path:
            config.update(config_module_path)

        # Instatiate container factory
        self._container_factory = AnnotationContainerFactory(config.CONTAINERS)

    def loadPlugins(self, plugins):
        self._plugins = []
        for plugin in plugins:
            if type(plugin) == str:
                plugin = import_callable(plugin)
            p = plugin(self)
            self._plugins.append(p)
            action = p.action()
            self.pluginLoaded.emit(action)

    ###
    ### Annoation file handling
    ###___________________________________________________________________________________________
    def loadAnnotations(self, fname, handleErrors=True):
        fname = str(fname) # convert from QString

        try:
            self._container = self._container_factory.create(fname)
            self._model = AnnotationModel(self._container.load(fname))
            msg = "Successfully loaded %s (%d files, %d annotations)" % \
                    (fname, self._model.root().numFiles(), self._model.root().numAnnotations())
        except Exception as e:
            if handleErrors:
                msg = "Error: Loading failed (%s)" % str(e)
            else:
                raise
        self.statusMessage.emit(msg)
        self.annotationsLoaded.emit()
        #Abhishek Start: Set first image added as current image
        #return self._model._root.appendFileItem(fileitem)
        currentimage = self.currentImage()
        if currentimage is None:
            firstimage = self._model._root.childAt(0)
            self.setCurrentImage(firstimage)
        #Abhishek End: Set first image added as current image

    def annotations(self):
        if self._model is None:
            return None
        return self._model.root().getAnnotations()

    def saveAnnotations(self, fname):
        success = False
        try:
            # create new container if the filename is different
            if fname != self._container.filename():
                # TODO: skip if it is the same class
                self._container = self._container_factory.create(fname)

            # Get annotations dict
            ann = self._model.root().getAnnotations()

            self._container.save(ann, fname)
            #self._model.writeback() # write back changes that are cached in the model itself, e.g. mask updates
            msg = "Successfully saved %s (%d files, %d annotations)" % \
                    (fname, self._model.root().numFiles(), self._model.root().numAnnotations())
            success = True
            self._model.setDirty(False)
        except Exception as e:
            msg = "Error: Saving failed (%s)" % str(e)

        self.statusMessage.emit(msg)
        return success

    def clearAnnotations(self):
        self._model = AnnotationModel([])
        #self._model.setBasedir("")
        self.statusMessage.emit('')
        self.annotationsLoaded.emit()

    def getCurrentFilename(self):
        return self._container.filename()

    ###########################################################################
    # Model stuff
    ###########################################################################

    def model(self):
        return self._model

    def gotoIndex(self, idx):
        if self._model is None:
            return

        current = self._current_image
        if current is None:
            current = next(self._model.iterator(ImageModelItem))

        next_image = current.getSibling(idx)
        if next_image is not None:
            self.setCurrentImage(next_image)

    def gotoNext(self, step=1):
        if self._model is not None:
            if self._current_image is not None:
                next_image = self._current_image.getNextSibling(step)
            else:
                next_image = next(self._model.iterator(ImageModelItem))
                if next_image is not None:
                    next_image = next_image.getNextSibling(step-1)

            if next_image is not None:
                self.setCurrentImage(next_image)

    def gotoPrevious(self, step=1):
        if self._model is not None and self._current_image is not None:
            prev_image = self._current_image.getPreviousSibling(step)

            if prev_image is not None:
                self.setCurrentImage(prev_image)

    def updateModified(self):
        """update all GUI elements which depend on the state of the model,
        e.g. whether it has been modified since the last save"""
        #self.ui.action_Add_Image.setEnabled(self._model is not None)
        # TODO also disable/enable other items
        #self.ui.actionSave.setEnabled(self.annotations.dirty())
        #self.setWindowModified(self.annotations.dirty())
        pass

    def currentImage(self):
        return self._current_image

    def setCurrentImage(self, image):
        if isinstance(image, QModelIndex):
            image = self._model.itemFromIndex(image)
        if isinstance(image, RootModelItem):
            return
        while (image is not None) and (not isinstance(image, ImageModelItem)):
            image = image.parent()
        if image is None:
            raise RuntimeError("Tried to set current image to item that has no Image or Frame as parent!")
        if image != self._current_image:
            self._current_image = image
            self.currentImageChanged.emit()

    def getImage(self, item):
        if item['class'] == 'frame':
            video = item.parent()
            return self._container.loadFrame(video['filename'], item['num'])
        else:
            return self._container.loadImage(item['filename'])

    def getAnnotationFilePatterns(self):
        return self._container_factory.patterns()

    def addImageFile(self, fname):
        fileitem = {
                'filename': fname,
                'class': 'image',
                'annotations': [ ],
            }
        self._model._root.appendFileItem(fileitem)
        #Abhishek Start: Set first image added as current image
        #return self._model._root.appendFileItem(fileitem)
        currentimage = self.currentImage()
        if currentimage is None:
            firstimage = self._model._root.childAt(0)
            self.setCurrentImage(firstimage)
        #Abhishek End: Set first image added as current image
        return

    def addVideoFile(self, fname):
        fileitem = {
                'filename': fname,
                'class': 'video',
                'frames': [ ],
            }

        # FIXME: OKAPI should provide a method to get all timestamps at once
        # FIXME: Some dialog should be displayed, telling the user that the
        # video is being loaded/indexed and that this might take a while
        LOG.info("Importing frames from %s. This may take a while..." % fname)
        video = okv.createVideoSourceFromString(fname)
        video = okv.toRandomAccessVideoSource(video)

        # try to convert to iseq, getting all timestamps will be significantly faster
        iseq = okv.toImageSeqReader(video)
        if iseq is not None:
            timestamps = iseq.getTimestamps()
            LOG.debug("Adding %d frames" % len(timestamps))
            fileitem['frames'] = [{'annotations': [], 'num': i,
                                   'timestamp': ts, 'class': 'frame'}
                                   for i, ts in enumerate(timestamps)]
        else:
            i = 0
            while video.getNextFrame():
                LOG.debug("Adding frame %d" % i)
                ts = video.getTimestamp()
                frame = { 'annotations': [],
                          'num': i,
                          'timestamp': ts,
                          'class': 'frame'
                        }
                fileitem['frames'].append(frame)
                i += 1

        self._model._root.appendFileItem(fileitem)

    ###
    ### GUI functions
    ###___________________________________________________________________________________________
    def mainWindow(self):
        return self._mainwindow

    ###
    ### PropertyEditor functions
    ###___________________________________________________________________________________________
    def propertyeditor(self):
        if self._mainwindow is None:
            return None
        else:
            return self._mainwindow.property_editor

    ###
    ### Scene functions
    ###___________________________________________________________________________________________
    def scene(self):
        if self._mainwindow is None:
            return None
        else:
            return self._mainwindow.scene

    def view(self):
        if self._mainwindow is None:
            return None
        else:
            return self._mainwindow.view

    def selectNextAnnotation(self):
        if self._mainwindow is not None:
            return self._mainwindow.scene.selectNextItem()

    def selectPreviousAnnotation(self):
        if self._mainwindow is not None:
            return self._mainwindow.scene.selectNextItem(reverse=True)
    
    def selectAllAnnotations(self):
        if self._mainwindow is not None:
            return self._mainwindow.scene.selectAllItems()

    def deleteSelectedAnnotations(self):
        if self._mainwindow is not None:
            self._mainwindow.scene.deleteSelectedItems()

    def exitInsertMode(self):
        if self._mainwindow is not None:
            return self._mainwindow.property_editor.endInsertionMode()

    ###
    ### TreeView functions
    ###___________________________________________________________________________________________
    def treeview(self):
        if self._mainwindow is None:
            return None
        else:
            return self._mainwindow.treeview
示例#5
0
#!/usr/bin/env python
import os, sys
from PyQt4.QtGui import QApplication
from sloth.gui import MainWindow
from sloth.core.labeltool import LabelTool
from sloth import APP_NAME, ORGANIZATION_NAME, ORGANIZATION_DOMAIN
from pymodeltest.modeltest import ModelTest

SAMPLE_DATA = os.path.join(os.path.dirname(__file__), 'data', 'example1_labels.json')

if __name__ == '__main__':
    app = QApplication(sys.argv)
    app.setOrganizationName(ORGANIZATION_NAME)
    app.setOrganizationDomain(ORGANIZATION_DOMAIN)
    app.setApplicationName(APP_NAME)

    labeltool = LabelTool()
    if len(sys.argv) < 2:
        sys.argv.append(SAMPLE_DATA)
    labeltool.execute_from_commandline(sys.argv)
    labeltool.modeltest = ModelTest(labeltool._model, labeltool)

    wnd = MainWindow(labeltool)
    labeltool._mainwindow = wnd
    wnd.show()

    sys.exit(app.exec_())
示例#6
0
#!/usr/bin/env python
import os, sys
from PyQt4.QtGui import QApplication
from sloth.gui import MainWindow
from sloth.core.labeltool import LabelTool
from sloth import APP_NAME, ORGANIZATION_NAME, ORGANIZATION_DOMAIN
from pymodeltest.modeltest import ModelTest

SAMPLE_DATA = os.path.join(os.path.dirname(__file__), 'data',
                           'example1_labels.json')

if __name__ == '__main__':
    app = QApplication(sys.argv)
    app.setOrganizationName(ORGANIZATION_NAME)
    app.setOrganizationDomain(ORGANIZATION_DOMAIN)
    app.setApplicationName(APP_NAME)

    labeltool = LabelTool()
    if len(sys.argv) < 2:
        sys.argv.append(SAMPLE_DATA)
    labeltool.execute_from_commandline(sys.argv)
    labeltool.modeltest = ModelTest(labeltool._model, labeltool)

    wnd = MainWindow(labeltool)
    labeltool._mainwindow = wnd
    wnd.show()

    sys.exit(app.exec_())