From 9d4077a20438a407b096c4cca83c30b72d1afd22 Mon Sep 17 00:00:00 2001 From: Martin Baeuml Date: Thu, 12 May 2011 15:18:52 +0200 Subject: [PATCH] finish 1st draft of documentation --- doc/Makefile | 5 +- doc/_static/sloth.gif | Bin 0 -> 57283 bytes doc/api.rst | 75 +++++++++++++++ doc/concepts.rst | 62 ++++++++---- doc/conf.py | 218 ++++++++++++++++++++++++++++++++++++++++++ doc/configuration.rst | 38 +++++++- doc/first_steps.rst | 79 +++++++++++++-- doc/index.rst | 5 + doc/inserters.rst | 5 + doc/items.rst | 128 +++++++++++++++++++++++++ 10 files changed, 589 insertions(+), 26 deletions(-) create mode 100644 doc/_static/sloth.gif create mode 100644 doc/conf.py 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 0000000000000000000000000000000000000000..6bde4251c442370b794bdcefb05f772cf9fc2c72 GIT binary patch literal 57283 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

7sfMyrB?ctE*H75C0VXdhK>|ff`xay+2MoXQ~ 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 literal 0 HcmV?d00001 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. +