diff --git a/sloth/bin/sloth b/sloth/bin/sloth index f9934c1..4675050 100755 --- a/sloth/bin/sloth +++ b/sloth/bin/sloth @@ -1,7 +1,7 @@ #!/usr/bin/env python import sys from os.path import dirname, realpath -sys.path.insert(1, dirname(dirname(dirname(realpath( __file__ ))))) +sys.path.insert(1, dirname(dirname(dirname(realpath(__file__))))) from PyQt4.QtGui import QApplication from sloth.core.labeltool import LabelTool from sloth import APP_NAME, ORGANIZATION_NAME, ORGANIZATION_DOMAIN diff --git a/sloth/core/commands.py b/sloth/core/commands.py index ba95819..065711f 100644 --- a/sloth/core/commands.py +++ b/sloth/core/commands.py @@ -1,5 +1,3 @@ -import sys -import os import sloth import shutil from pprint import pprint @@ -7,8 +5,11 @@ from sloth.core.cli import BaseCommand, CommandError from sloth.annotations.container import * from optparse import make_option import logging + + logger = logging.getLogger(__name__) + class ConvertCommand(BaseCommand): """ Converts a label file from one file format to another. @@ -152,7 +153,6 @@ class MergeFilesCommand(BaseCommand): containerOut = self.labeltool._container_factory.create(output) containerOut.save(an3, output) - def merge_annotations(self, an1, an2): # I could also think of an implementation merging an1 and an2, and flattening the lists of lists @@ -173,7 +173,6 @@ class MergeFilesCommand(BaseCommand): assert(d1['frames'] != None) assert(d2['frames'] != None) - frames1 = d1['frames'] frames2 = d2['frames'] @@ -222,10 +221,12 @@ def _make_writeable(filename): # command dictionary str -> Command _commands = {} + def register_command(name, command): global _commands _commands[name] = command + def get_commands(): global _commands return _commands diff --git a/sloth/core/exceptions.py b/sloth/core/exceptions.py index e1a5109..e786cf3 100644 --- a/sloth/core/exceptions.py +++ b/sloth/core/exceptions.py @@ -1,15 +1,18 @@ """ -Label tool exception classes. +Sloth exception classes. """ + class ImproperlyConfigured(Exception): """There is an error in the configuration.""" pass + class NotImplementedException(Exception): """This function/method/class has not been implemented yet.""" pass + class InvalidArgumentException(Exception): """The argument is invalid.""" pass diff --git a/sloth/core/labeltool.py b/sloth/core/labeltool.py index 03c26cf..605584f 100644 --- a/sloth/core/labeltool.py +++ b/sloth/core/labeltool.py @@ -1,10 +1,8 @@ #!/usr/bin/python """ This is the core labeltool module. - - """ -import sys, os +import os from PyQt4.QtGui import * from PyQt4.QtCore import * from sloth.annotations.model import * @@ -16,13 +14,15 @@ from sloth import VERSION from sloth.core.commands import get_commands from sloth.gui import MainWindow import logging + LOG = logging.getLogger(__name__) try: import okapy.videoio as okv -except: +except ImportError: pass + class LabelTool(QObject): """ This is the main label tool object. It stores the state of the tool, i.e. @@ -43,9 +43,9 @@ class LabelTool(QObject): "Type '%s help ' for help on a specific subcommand.\n\n" # Signals - statusMessage = pyqtSignal(str) - annotationsLoaded = pyqtSignal() - pluginLoaded = pyqtSignal(QAction) + 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 @@ -105,7 +105,8 @@ class LabelTool(QObject): # 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') + 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) @@ -170,7 +171,6 @@ class LabelTool(QObject): else: self.clearAnnotations() - def fetch_command(self, subcommand): """ Tries to fetch the given subcommand, printing a message with the @@ -179,8 +179,8 @@ class LabelTool(QObject): 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.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. @@ -221,13 +221,13 @@ class LabelTool(QObject): ### Annoation file handling ###___________________________________________________________________________________________ def loadAnnotations(self, fname, handleErrors=True): - fname = str(fname) # convert from QString + 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()) + (fname, self._model.root().numFiles(), self._model.root().numAnnotations()) except Exception as e: if handleErrors: msg = "Error: Loading failed (%s)" % str(e) @@ -247,7 +247,6 @@ class LabelTool(QObject): 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 @@ -256,7 +255,7 @@ class LabelTool(QObject): 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()) + (fname, self._model.root().numFiles(), self._model.root().numAnnotations()) success = True self._model.setDirty(False) except Exception as e: @@ -300,7 +299,7 @@ class LabelTool(QObject): else: next_image = next(self._model.iterator(ImageModelItem)) if next_image is not None: - next_image = next_image.getNextSibling(step-1) + next_image = next_image.getNextSibling(step - 1) if next_image is not None: self.setCurrentImage(next_image) @@ -349,18 +348,18 @@ class LabelTool(QObject): def addImageFile(self, fname): fileitem = { - 'filename': fname, - 'class': 'image', - 'annotations': [ ], - } + 'filename': fname, + 'class': 'image', + 'annotations': [], + } return self._model._root.appendFileItem(fileitem) def addVideoFile(self, fname): fileitem = { - 'filename': fname, - 'class': 'video', - 'frames': [ ], - } + '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 @@ -376,17 +375,17 @@ class LabelTool(QObject): LOG.debug("Adding %d frames" % len(timestamps)) fileitem['frames'] = [{'annotations': [], 'num': i, 'timestamp': ts, 'class': 'frame'} - for i, ts in enumerate(timestamps)] + 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' - } + frame = {'annotations': [], + 'num': i, + 'timestamp': ts, + 'class': 'frame' + } fileitem['frames'].append(frame) i += 1 @@ -429,7 +428,7 @@ class LabelTool(QObject): 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() @@ -449,5 +448,4 @@ class LabelTool(QObject): if self._mainwindow is None: return None else: - return self._mainwindow.treeview - + return self._mainwindow.treeview \ No newline at end of file diff --git a/sloth/core/utils.py b/sloth/core/utils.py index dd76ed9..7c1f733 100644 --- a/sloth/core/utils.py +++ b/sloth/core/utils.py @@ -1,6 +1,7 @@ from sloth.core import exceptions from sloth.utils import importlib + def import_callable(module_path_name): """ Import the callable given by ``module_path_name``. diff --git a/sloth/items/factory.py b/sloth/items/factory.py index 255f14b..b511600 100644 --- a/sloth/items/factory.py +++ b/sloth/items/factory.py @@ -1,5 +1,6 @@ from sloth.core.utils import import_callable + class Factory: """ A generic factory for both items and inserters. @@ -34,8 +35,8 @@ class Factory: """ _type = str(_type) if _type in self._items and not replace: - raise Exception("Type %s already has an item: %s" % \ - (_type, str(self._items[_type]))) + raise Exception("Type %s already has an item: %s" % + (_type, str(self._items[_type]))) else: if type(item) == str: item = import_callable(item) @@ -53,7 +54,7 @@ class Factory: """ _type = str(_type) if _type is None: - self._items = {} + self._items = {} else: if _type in self._items: del self._items[_type] @@ -82,4 +83,3 @@ class Factory: if item is None: return None return item(*args, **kwargs) - diff --git a/sloth/items/items.py b/sloth/items/items.py index 1649a2f..b80231e 100644 --- a/sloth/items/items.py +++ b/sloth/items/items.py @@ -1,5 +1,4 @@ import logging -from PyQt4.QtGui import * from PyQt4.Qt import * @@ -99,7 +98,8 @@ class BaseItem(QAbstractGraphicsShapeItem): self._valid = True if len(self.cycleValuesOnKeypress) > 0: - logging.warning("cycleValueOnKeypress is deprecated and will be removed in the future. Set BaseItem.hotkeys instead with cycleValue()") + logging.warning("cycleValueOnKeypress is deprecated and will be removed in the future. " + + "Set BaseItem.hotkeys instead with cycleValue()") self.changeColor() diff --git a/sloth/utils/__init__.py b/sloth/utils/__init__.py index b51841c..785b85a 100644 --- a/sloth/utils/__init__.py +++ b/sloth/utils/__init__.py @@ -5,6 +5,8 @@ import random import colorsys gray_color_table = [qRgb(i, i, i) for i in range(256)] + + def toQImage(im, copy=False): if im is None: return QImage() @@ -17,12 +19,13 @@ def toQImage(im, copy=False): elif len(im.shape) == 3: if im.shape[2] == 3: - qim = QImage(im.data, im.shape[1], im.shape[0], im.strides[0], QImage.Format_RGB888); + qim = QImage(im.data, im.shape[1], im.shape[0], im.strides[0], QImage.Format_RGB888) return qim.copy() if copy else qim elif im.shape[2] == 4: - qim = QImage(im.data, im.shape[1], im.shape[0], im.strides[0], QImage.Format_ARGB32); + qim = QImage(im.data, im.shape[1], im.shape[0], im.strides[0], QImage.Format_ARGB32) return qim.copy() if copy else qim - raise NotImplementedException('no conversion to QImage implemented for given image type (depth: %s, shape: %s)' % (im.dtype, im.shape)) + raise NotImplementedException('no conversion to QImage implemented for given image type (depth: %s, shape: %s)' % + (im.dtype, im.shape)) def gen_colors(s=0.99, v=0.99, h=None, color_space='rgb', _golden_ratio_conjugate=0.618033988749895):