diff --git a/doc/Makefile b/doc/Makefile index bbb9748..e5d6a69 100644 --- a/doc/Makefile +++ b/doc/Makefile @@ -12,7 +12,7 @@ PAPEROPT_a4 = -D latex_paper_size=a4 PAPEROPT_letter = -D latex_paper_size=letter ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . -.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest +.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest deploy help: @echo "Please use \`make ' where is one of" @@ -33,6 +33,9 @@ help: @echo " linkcheck to check all external links for integrity" @echo " doctest to run all doctests embedded in the documentation (if enabled)" +deploy: html + cp -r _build/html/* ~baeuml/www/labeltool/doc + clean: -rm -rf $(BUILDDIR)/* diff --git a/doc/_static/sloth.gif b/doc/_static/sloth.gif new file mode 100644 index 0000000..6bde425 Binary files /dev/null and b/doc/_static/sloth.gif differ diff --git a/doc/api.rst b/doc/api.rst index f7e21a1..7e9aa1a 100644 --- a/doc/api.rst +++ b/doc/api.rst @@ -1,6 +1,81 @@ +=== API === +.. todo:: Actually document the code, so that this is not just a bunch of method names. + +annotationmodel +=============== + +.. automodule:: annotationmodel +.. autoclass:: AnnotationModel + :members: + :undoc-members: +.. autoclass:: ModelItem + :members: + :undoc-members: +.. autoclass:: RootModelItem + :members: + :undoc-members: +.. autoclass:: FileModelItem + :members: + :undoc-members: +.. autoclass:: ImageFileModelItem + :members: + :undoc-members: +.. autoclass:: VideoFileModelItem + :members: + :undoc-members: +.. autoclass:: FrameModelItem + :members: + :undoc-members: +.. autoclass:: AnnotationModelItem + :members: + :undoc-members: +.. autoclass:: KeyValueModelItem + :members: + :undoc-members: + + +annotationscene +=============== + .. automodule:: annotationscene .. autoclass:: AnnotationScene :members: + :undoc-members: + +items +===== + +.. automodule:: items + +.. _AnnotationGraphicsItem: + +.. autoclass:: AnnotationGraphicsItem + :members: + :undoc-members: + +.. autoclass:: PointItem + :members: + :undoc-members: + +.. autoclass:: RectItem + :members: + :undoc-members: + +.. .. autoclass:: PolygonItem + :members: + +.. autoclass:: PointItemInserter + :members: + :undoc-members: + +.. autoclass:: RectItemInserter + :members: + :undoc-members: + +.. autoclass:: PolygonItemInserter + :members: + :undoc-members: + diff --git a/doc/concepts.rst b/doc/concepts.rst index 196a7c6..5c01998 100644 --- a/doc/concepts.rst +++ b/doc/concepts.rst @@ -31,47 +31,75 @@ The label tool provides support for a range of standard shape label (for example In order for the label tool to correctly visualize these labels, the labels have to follow a convention, which the keys are for `x`- and `y`-coordinates, `width` and `height` and so on. -The following types are supported out of the box +The following types are supported out of the box, i.e. corresponding visualization items +and inserters will be avaible in the default configuration. Point ..... - :: { - type: "point", - x: 10, - y: 20, + "type": "point", + "x": 10, + "y": 20, } Rect .... - :: { - type: "rect", - x: 10, - y: 20, - width: 20, - height: 20, + "type": "rect", + "x": 10, + "y": 20, + "width": 20, + "height": 20, } Polygon ....... - :: { - type: "polygon", - xn: "10;20;30", - yn: "20;30;40", + "type": "polygon", + "xn": "10;20;30", + "yn": "20;30;40", } User defined labels ------------------- -For many cases, it might suffice to use the predefined labels. But as +In most cases, it will not be sufficient for your labeling need to stick to those simple types. Or +your might want to add further information. Since each label is just a set of key-value pairs, this +is easily possible. For example, by adding an additional ``class`` key, denoting that these points +are the left and right eye, respectively:: + + { + "type": "point", + "class": "left_eye", + x: 50, y: 40, + } + { + "type": "point", + "class": "right_eye", + x: 70, y: 40, + } + +Of course, you can also change the type:: + + { + "type": "triangle", + "x1": 10, + "y1": 20, + "x2": 30, + "y2": 20, + "x3": 20, + "y3": 30, + } + +However, if you do this you will need to tell the label tool in the +configuration how to display this type as well. See section +:doc:`Configuration` on how to do that. Representation is not storage @@ -83,6 +111,6 @@ The label tool does not have *the one* in which way to store the labels. Again, there are some default formats with which the label tool can deal out of the box (one of which will be a yaml file, which resembles the textual representation above). However, you are free to define your own loading and saving routines for your labels (see ***). This -allows you for example to support legacy third-party label formats without the need to convert +allows you for example to support legacy third-party label formats without the need of converting them first. diff --git a/doc/conf.py b/doc/conf.py new file mode 100644 index 0000000..624ffed --- /dev/null +++ b/doc/conf.py @@ -0,0 +1,218 @@ +# -*- coding: utf-8 -*- +# +# Labeltool documentation build configuration file, created by +# sphinx-quickstart on Mon May 9 18:08:02 2011. +# +# This file is execfile()d with the current directory set to its containing dir. +# +# Note that not all possible configuration values are present in this +# autogenerated file. +# +# All configuration values have a default; values that are commented out +# serve to show the default. + +import sys, os + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +sys.path.insert(0, os.path.abspath('..')) + +# -- General configuration ----------------------------------------------------- + +# If your documentation needs a minimal Sphinx version, state it here. +#needs_sphinx = '1.0' + +# Add any Sphinx extension module names here, as strings. They can be extensions +# coming with Sphinx (named 'sphinx.ext.*') or your custom ones. +extensions = ['sphinx.ext.autodoc', 'sphinx.ext.viewcode', 'sphinx.ext.todo'] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['_templates'] + +# The suffix of source filenames. +source_suffix = '.rst' + +# The encoding of source files. +#source_encoding = 'utf-8-sig' + +# The master toctree document. +master_doc = 'index' + +# General information about the project. +project = u'Labeltool' +copyright = u'2011, cv:hci lab, Institute for Anthropomatics, Karlsruhe Institute of Technology' + +# The version info for the project you're documenting, acts as replacement for +# |version| and |release|, also used in various other places throughout the +# built documents. +# +# The short X.Y version. +version = '0.1' +# The full version, including alpha/beta/rc tags. +release = '0.1' + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +#language = None + +# There are two options for replacing |today|: either, you set today to some +# non-false value, then it is used: +#today = '' +# Else, today_fmt is used as the format for a strftime call. +#today_fmt = '%B %d, %Y' + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +exclude_patterns = ['_build'] + +# The reST default role (used for this markup: `text`) to use for all documents. +#default_role = None + +# If true, '()' will be appended to :func: etc. cross-reference text. +#add_function_parentheses = True + +# If true, the current module name will be prepended to all description +# unit titles (such as .. function::). +#add_module_names = True + +# If true, sectionauthor and moduleauthor directives will be shown in the +# output. They are ignored by default. +#show_authors = False + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = 'sphinx' + +# A list of ignored prefixes for module index sorting. +#modindex_common_prefix = [] + + +# -- Options for HTML output --------------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +html_theme = 'nature' + +# Theme options are theme-specific and customize the look and feel of a theme +# further. For a list of options available for each theme, see the +# documentation. +#html_theme_options = {} + +# Add any paths that contain custom themes here, relative to this directory. +#html_theme_path = [] + +# The name for this set of Sphinx documents. If None, it defaults to +# " v documentation". +#html_title = None + +# A shorter title for the navigation bar. Default is the same as html_title. +#html_short_title = None + +# The name of an image file (relative to this directory) to place at the top +# of the sidebar. +#html_logo = None + +# The name of an image file (within the static path) to use as favicon of the +# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 +# pixels large. +#html_favicon = None + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ['_static'] + +# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, +# using the given strftime format. +#html_last_updated_fmt = '%b %d, %Y' + +# If true, SmartyPants will be used to convert quotes and dashes to +# typographically correct entities. +#html_use_smartypants = True + +# Custom sidebar templates, maps document names to template names. +#html_sidebars = {} + +# Additional templates that should be rendered to pages, maps page names to +# template names. +#html_additional_pages = {} + +# If false, no module index is generated. +#html_domain_indices = True + +# If false, no index is generated. +#html_use_index = True + +# If true, the index is split into individual pages for each letter. +#html_split_index = False + +# If true, links to the reST sources are added to the pages. +#html_show_sourcelink = True + +# If true, "Created using Sphinx" is shown in the HTML footer. Default is True. +#html_show_sphinx = True + +# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. +#html_show_copyright = True + +# If true, an OpenSearch description file will be output, and all pages will +# contain a tag referring to it. The value of this option must be the +# base URL from which the finished HTML is served. +#html_use_opensearch = '' + +# This is the file name suffix for HTML files (e.g. ".xhtml"). +#html_file_suffix = None + +# Output file base name for HTML help builder. +htmlhelp_basename = 'Labeltooldoc' + +# todo extension +todo_include_todos = True + +# -- Options for LaTeX output -------------------------------------------------- + +# The paper size ('letter' or 'a4'). +#latex_paper_size = 'letter' + +# The font size ('10pt', '11pt' or '12pt'). +#latex_font_size = '10pt' + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, author, documentclass [howto/manual]). +latex_documents = [ + ('index', 'Labeltool.tex', u'Labeltool Documentation', + u'cv:hci lab, Institute for Anthropomatics, Karlsruhe Institute of Technology', 'manual'), +] + +# The name of an image file (relative to this directory) to place at the top of +# the title page. +#latex_logo = None + +# For "manual" documents, if this is true, then toplevel headings are parts, +# not chapters. +#latex_use_parts = False + +# If true, show page references after internal links. +#latex_show_pagerefs = False + +# If true, show URL addresses after external links. +#latex_show_urls = False + +# Additional stuff for the LaTeX preamble. +#latex_preamble = '' + +# Documents to append as an appendix to all manuals. +#latex_appendices = [] + +# If false, no module index is generated. +#latex_domain_indices = True + + +# -- Options for manual page output -------------------------------------------- + +# One entry per manual page. List of tuples +# (source start file, name, description, authors, manual section). +man_pages = [ + ('index', 'labeltool', u'Labeltool Documentation', + [u'cv:hci lab'], 1) +] diff --git a/doc/configuration.rst b/doc/configuration.rst index 521e3f8..783a898 100644 --- a/doc/configuration.rst +++ b/doc/configuration.rst @@ -14,17 +14,53 @@ This is a list of all available settings. LABELS ------ +Default:: + + ( + ("Rect", {"type": "rect"}), + ("Point", {"type": "point"}), + ("Polygon", {"type": "polygon"}), + ) + +List of labels. This will be used to construct the button area from which the user can select to be created +labels. The second tuple entry is expected to be a python dictionary, which contains at least the key `type`. +All other keys are optional, but are directly used for the newly created label. A value can also be a list. +In this case, the button area displays another list of options for the key as defined in the list. Example:: + + ( + ("Rect", {"type": "rect", "class": "head", "id": ["Martin", "Mika", "Boris"]}), + ) + +Note two things here. First, the comma at the end of the first tuple is mandatory. Otherwise the outer tuple +will not be recognized as one (it will be only parentheses around an object, which will alone not be translated +into a tuple object. Second, the key `head` does not contain a list as value. That means, that this key-value +pair will be used directly as such in a newly created label. For the key `id` the user can choose from the +given list, and only the chosen value will be used for the newly created label. + .. _ITEMS: ITEMS ----- +Default:: + + { + "rect": 'items.RectItem', + "point": 'items.PointItem', + "polygon": 'items.PolygonItem', + } + +Mapping from `type` to the visualization item. The values need to be python callables that create +a new visualization item. They don't neccessarily need to be subclasses of `AnnotationGraphicsItem`. +Nevertheless, the constructor of any subclass of `AnnotationGraphicItem` is of course a python callable +that creates a new visualization item. + .. _HOTKEYS: HOTKEYS ------- -Default:: `()` (Empty tuple) +Default:: ``()`` (Empty tuple) Hold a list of hotkeys for the label inserting. Example:: diff --git a/doc/first_steps.rst b/doc/first_steps.rst index d0b2170..5fc4415 100644 --- a/doc/first_steps.rst +++ b/doc/first_steps.rst @@ -1,3 +1,6 @@ +.. highlight:: python + +=========== First Steps =========== @@ -5,7 +8,7 @@ In this section, you will learn with a simple example, how to load labels and wr The full configuration options will be covered in the next section :doc:`configuration`. Using the default configuration -------------------------------- +=============================== The easiest way to start is using a supported label format, and supported label types only. In this case we just need to start the label tool and supply the label file on the command line:: @@ -14,18 +17,80 @@ we just need to start the label tool and supply the label file on the command li Let's take look at the example label file:: - image1.jpg type rect x 50 y 80 z 20 type rect x 50 y 80 z 20 + image1.jpg type rect x 50 y 80 width 20 height 20 type rect x 50 y 80 width 20 height 80 image2.jpg type point x 70 y 80 We have labeled two images, with two rectangles in image1 and one point in image 2. Since we did not launch -the label tool with a custom configuration, the standard visualizations for rect and point will be used. +the label tool with a custom configuration, the standard visualizations for rect and point will be used. The +label tool displays the two given image, and draw two rectangle at the labeled position in image1, and a +point in image2. Writing a custom configuration ------------------------------- +============================== The configuration file is a python module where the module-level variables represent the settings. The -two most important variable are +most important variables are - * ITEMS: - * LABELS: This defines which new labels can be created interactively by the users. +* :ref:`ITEMS`: This defines how a given label is visualized by the label tool. +* :ref:`LABELS`: This defines *which* new labels can be created interactively by the user. +* :ref:`INSERTERS`: This defines *how* new labels are created by the user. + +We start with a quick example:: + + ITEMS = { + 'rect': 'items.RectItem', + 'point': 'items.PointItem', + 'bbox': 'items.RectItem', + } + + LABELS = ( + ("Rect", {"type": "rect", + "class": "head", + "id": ["Martin", "Mika"]}), + ("Bounding Box", {"type": "bbox", + "class": "body", + "id": ["Martin", "Mika"]}), + ) + +In ``ITEMS`` we specify that all labels of type ``rect`` will be visualized by the class ``items.RectItem`` +(which is one of the predefined visualization items that comes with the label tool). All labels of type +``point`` will be visualized by ``items.PointItem``. Note that we can use any type basically. The type +``bbox`` will also be visualized by a ``items.RectItem``. + +In ``LABELS`` we defined which `new` labels the user can create with the label tool. The variable is +expected to be a list/tuple of tuples. Each of the inner tuples contains first a description of the +label (this will be on the button displayed to the user), and the a description of the label to be +created. In our case, we create a label of type ``rect`` if the user hits the ``Rect`` button. Further, +the newly created label will have the class ``head`` (which is fixed), and the user can choose between +one of the ids from the given list. + +Similarly, the user now can create Bounding Box labels of type ``bbox`` with class ``body``. + +There is a difference between the visualization items and the way the labels are created by the user +interactively. For example, the label tool does *not* know out of the box how to create a label of +type ``bbox``. We have to explicitly specify how to insert this type. We can do this by setting +the ``INSERTERS`` variable:: + + INSERTERS = { + 'rect': 'items.inserters.RectItemInserter', + 'bbox': 'items.inserters.RectItemInserter', + } + +The ``RectItemInserter`` lets the user draw a rectangle with the mouse, and then sets the ``x``, +``y``, ``width`` and ``height`` members of the label accordingly. By mapping the type ``bbox`` +to ``RectItemInserter``, the user will be able to draw a rectangle each time a new Bounding Box +label is created. Note that we also have to add the ``RectItemInserter`` for the type ``rect`` +as well (which would also be in the default configuration) due to the fact that we override +the ``INSERTERS`` variable completely. Otherwise the label tool would not know anymore, how +to insert labels of type ``rect``. + +In order to extend the default configuration and avoid overriding the default values, you can +first import the default configuration and then append your custom mappings (remember that +the configuration is a python module, you can basically execute any valid python code):: + + from conf.default_configuration import INSERTERS + INSERTERS['bbox'] = 'items.inserters.RectItemInserter' + +You can now continue by reading about :doc:`all available configuration options `, +how to write your own :doc:`visualization items ` or how to write :doc:`custom inserters `. diff --git a/doc/index.rst b/doc/index.rst index fd3d709..4f4295a 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -1,3 +1,8 @@ +.. image:: _static/sloth.gif + :width: 350px + :align: right + :alt: sloth + ======================================== Welcome to the label tool documentation! ======================================== diff --git a/doc/inserters.rst b/doc/inserters.rst index 2c3af6c..2201bc1 100644 --- a/doc/inserters.rst +++ b/doc/inserters.rst @@ -1,3 +1,8 @@ +========= Inserters ========= +Inserters are used for creating new labels interactively. When the users selects a label type in the button area, +the corresponding inserter for the label type (as defined in the :ref:`configuration`). + +.. todo:: Write this. It's pretty similar to the items section. diff --git a/doc/items.rst b/doc/items.rst index 449d364..1b771d9 100644 --- a/doc/items.rst +++ b/doc/items.rst @@ -1,2 +1,130 @@ +.. highlight:: python + +===== Items ===== + +Visualization items are reponsible for bringing the labels to the users screen. The +visualization in the label tool is object based, i.e. for each label the label +tool creates a item object that is responsible for drawing the label. + +Predefined items +================ + +The label tool comes with a few predefined visualization items: + +- ``items.PointItem`` + + Draws a point. Expects the label to have keys ``x`` and ``y`` with the coordinates as values. + +- ``items.RectItem`` + + Draws a rectangle. Expects the label to have keys ``x``, ``y``, ``width`` and ``height``. + +- ``items.PolygonItem`` + + Draws a rectangle. Expects the label to have keys ``xn`` and ``yn``, which are ``;``-separated + lists of point coordinates. + +The predefined items can be used in different ways. If you give the class name in +the configuration, the constructor will be called for initializing the item. However, +you can also create and instance of the item, configure for example the color, and then +use this instance in the configuration. The predefined items have their ``__call__`` operator +overloaded and will function as a factory creating new items similar to the current instance. +You can make use of this in the configuration to for example specify the color of the +created rectangles, maybe even different kinds for different label types:: + + # this your custom configuration module + + RedRectItem = items.RectItem() + RedRectItem.setColor(Qt.Red) + GreenRectItem = items.RectItem() + GreenRectItem.setColor(Qt.Green) + + ITEMS = { + "rect" : RedRectItem, + "head" : GreenRectItem, + } + +Write your own visualization item +================================= + +The base class for all visualization item is the :ref:`AnnotationGraphicsItem ` class. In +order to write a new visualization item, you need to subclass this class and implement +a few functions. + +The easiest way to visualize your label is by using some of the existing Qt graphics items. You can initialize +it in the constructor and be done:: + + class MyRectItem(AnnotationGraphicsItem): + def __init__(self, index, data): + # Call the base class constructor. This will make the label + # data available in self.data + AnnotationGraphicsItem.__init__(self, index, data) + + # Create a new rect item and add it as child item. + # This defines what will be displayed for this label, since the + # AnnotationGraphicsItem base class itself does not display anything. + x, y, width, height = map(float, (self.data['x'], self.data['y'], + self.data['width'], self.data['height'])) + self.rect_ = QGraphicsRectItem(x, y, width, height, self) + +For advanced usage, for example allowing the label to be moved by the mouse, we need to +do some more. First, we need to allow the item to be selectable and movable. In the constructor +set the graphics items flags to allow interactive modfications of the item:: + + self.setFlags(QGraphicsItem.ItemIsSelectable | \ + QGraphicsItem.ItemIsMovable | \ + QGraphicsItem.ItemSendsGeometryChanges | \ + QGraphicsItem.ItemSendsScenePositionChanges) + +Then we catch the notifications about item changes by overriding ``ìtemChange``. We especially need +to inform the model about the modification:: + + def itemChange(self, change, value): + if change == QGraphicsItem.ItemScenePositionHasChanged: + self.updateModel() + return AnnotationGraphicsItem.itemChange(self, change, value) + + def updateModel(self): + rect = QRectF(self.scenePos(), self.rect_.size()) + self.data['x'] = rect.topLeft().x() + self.data['y'] = rect.topLeft().y() + self.data['width'] = float(rect.width()) + self.data['height'] = float(rect.height()) + + self.index().model().setData(self.index(), QVariant(self.data), DataRole) + +For even more advanced usage, such as drawing your own shapes, catching keys etc., please consult +Qt's `QGraphicsItem documentation`_. + +.. _QGraphicsItem documentation: http://doc.trolltech.com/latest/qgraphicsitem.html + +Factorize your custom visualization item +======================================== + +The predefined items are implemented in such a way so that they can be used as template +to create new, similar items. In order to implement something similar for your own +visualization items, you need to overload your classes ``__call__`` operator and +return a new visualization item with all properties cloned that you would like +to clone. + +Example:: + + class MyRectItem(AnnotationGraphicsItem): + def __init__(self, index, data): + AnnotationGraphicsItem.__init__(self, index, data) + self.color_ = Qt.Red + + def setColor(self, color): + self.color_ = color + + def __call__(self, index, data): + newitem = MyRectItem(index, data) + newitem.setColor(self.color_) + return newitem + +You can see that the ``__call__`` operator takes the same arguments as the constructor. +In its implementation it first creates a new visualization item, and then sets the +color to the same as its own before returning the new item. +