Home >> Media Group >> Research >> ViPER
Downloads -- Documentation -- Developers -- Discussion
LAMP     The Language and Media Processing Laboratory

A Hacker's Guide to ViPER

Notes on Extending and Scripting ViPER

Introduction to the Tools

We designed ViPER to be used. What it is lacking in user friendliness it makes up for in its friendliness to scriptwriters and programmers. It is distributed with source code, and now comes with a comprehensive Java API for manipulating the data from Java or any tool that supports it, such as Matlab or Jython. It also sports a number of command line tools for processing the data and evaluation results.

First, this document will describe the data format, allowing you to write simple scripts in perl and XSLT, generate your own result data or write a conversion tool from another metadata format. While the ViPER format is easily explained, it does have some sharp corners that make the ViPER API quite helpful. The command line tools include the evaluation tools, which allow complicated evaluations to be automated; the conversion tools, which take a number of additional arguments to manipulate the data; and the OVERLAY scripts, which draw the visual elements of result data and truth data back on the videos.

Next, there is a section on extending the evaluation tool to support additional data types, metrics, and evaluation paradigms. This requires an understanding of the Java programming language.

Finally, there is a section on extending the ground truth authoring tool. There is a focus on adding new data types, as well as developing general plug-ins for application-specific usage. In addition to programming in Java, you may also have to become familiar with Swing and some libraries that ViPER-GT uses, such as Piccolo.

The ViPER Data Format

Any given set of ViPER data contains two major sections: the configuration, which lays out the me tadata schema the file uses, and the data section, which contains the data itself. In the older format, there was just one data section, whereas the XML format is divided into multiple sections by source file. What this means is that the new format supports multiple media files within the same data file.

ViPER metadata is divided into descriptors. Each descriptor should describe a unique object or event. Each descriptor has a list of attributes, which are simple data types. There are three types of descriptors, FILE descriptors, for describing general information about a source file, CONTENT descriptors, which describe events such as cuts that describe an entire frame or series of frames, and OBJECT descriptors. An OBJECT descriptor exists for some possibly discontinuous amount of time for the life of a single media file (or aggregation of media files specified in the FILE Information attribute and an info file). While FILE and CONTENT attributes are static and maintain the same value for the life of their enclosing descriptor, OBJECT attributes may be dynamic. Dynamic attributes may change over the life of an instance.

The Configuration Section

The configuration section, described in detail in the "ViPER-GT" manual, contains data typing information for a viper file. This is in the form of a list of descriptor types and the associated attribute information. For a detailed description of the configuration section, refer to The Guide to Authoring Ground Truth with ViPER-GT.

The Data Section

Data is stored as a list of records associated with a media file. Each record represents a contiguous appearance of a descriptor instance in a media file. Type, name and identification number uniquely identifies descriptors. Each descriptor instance record has a contiguous frame span, over which the instance is considered valid; note that if a descriptor is occluded or otherwise marked as invalid for a period of time, it is broken into two records with the same identification number and disjoint, non-adjacent frame spans.

Each record contains a (possibly empty) list of attribute/value pairs. The attributes are as defined in the configuration section of the viper file. If an attribute is not listed, it is set to the default value for the frame span of the record. If it takes no value for some span, it is marked as NULL for that span.

Note that while the older .gtf format only allows one media file to be described with each data file (that ViPER-GT can operate on keyframe lists is not reflected in the file), the XML format is arranged in <sourcefile> elements that allow multiple media file metadata to be stored in a single viper file. It is up to the programmer to decide how to use a multisource viper file; in VideoMiner,

The FILE Information Descriptor

It is recommended that all ground truth contain a FILE Information descriptor. This descriptor can include such information as number of frames, frame rate, and image size. If these attributes are included, then the viper tools can use the information during evaluation and to open the media files automatically when opened with viper-gt.

Attribute Name Attribute Type Meaning
NUMFRAMES dvalue Total number of frames the video file (or collection of images) contains.
SOURCEFILES svalue A list of (number, filename) pairs. The number is the frame offset; the file is the media source name.
SOURCEDIR lvalue Type of the media file, either SEQUENCE for a video or FRAMES for a set of frames (e.g. a .info file).
H-FRAME-SIZE dvalue Horizontal width of video, in pixels.
V-FRAME-SIZE dvalue Vertical height of video, in pixels.

Specifics of the XML Format

A viper XML file is divided into two sections: config and data. The XML schema for each is required. For more information, see the schemata themselves.

Specifics of the GTF Format

descriptor-type descriptor-name id start-frame:end-frame
    attribute1 : "attribute-value"
    attribute2 : "attribute-value"
    attributeN : "attribute-value"

Following assumptions are made with regards to GTF format:

1. Attribute names, types and values must be contained on the same line within a data record. A new line will indicate the start of a new attribute or record.

2. Any blank line or the remainder of any line containing a "//" is a comment and will be ignored.

3. Any line beginning with #include "filename" will be used to include the quoted file.

4. The current reserved words include: FILE, CONTENT, OBJECT, default and static.

  1. Unknown attribute types, and their values, will be ignored.
  2. The GTF file extension is .gtf.

Example Viper File Records

Configurations Section

FILE descriptor example

A FILE descriptor, with descriptor-name "Information", may have a configuration record that looks like this:

FILE Information
    SOURCE : svalue [default CNN]
    COMMENT: svalue
    NCLIPS : dvalue [default 14]
    FRAME-HEIGHT : dvalue [default 320]
    FRAME-WIDTH : dvalue [default 480]

This descriptor will allow the specification of the source of the video, the number of clips, the format and the frame size. Note that in xml, the descriptor definition is far more verbose:

<descriptor name="Information" type="FILE">
    <attribute name="SOURCE" type="svalue">
            <data:svalue value="CNN"/>
    <attribute name="COMMENT" type="svalue"/>
    <attribute name="NCLIPS" type="dvalue">
            <data:dvalue value="14"/>
    <attribute name="FORMAT" type="lvalue">
            <data:lvalue-enum value="MPEG"/>
            <data:lvalue-enum value="MJPEG"/>
            <data:lvalue-enum value="AVI"/>
            <data:lvalue-enum value="MOV"/>
        </ data:lvalue-possibles >
    <attribute name="FRAME-HEIGHT" type="dvalue">
            <data:dvalue value="320"/>
    <attribute name="FRAME-WIDTH" type="dvalue">
            <data:dvalue value="480"/>

CONTENT descriptor example

As another example, suppose we have a CONTENT descriptor, with descriptor-name Shot-Change, which has a single attribute TYPE and a list of possible values. Its configuration record may look like:

CONTENT Shot-Change

This will allow us to describe, by type, a shot change that occurs over a specified range. Note that there can be only one Shot-Change at a time, making it a good candidate for a CONTENT descriptor.

Again, the xml version is considerably longer:

<descriptor name="Shot-Change" type="CONTENT">
    <attribute name="TYPE" type="lvalue">
            <data:lvalue-enum value="CUT"/>
            <data:lvalue-enum value="FADE-IN"/>
            <data:lvalue-enum value="FADE-OUT"/>
            <data:lvalue-enum value="DISSOLVE"/>
        </ data:lvalue-possibles >

OBJECT descriptor example

Recall that OBJECT descriptors are used to represent events where either more than one occurrence of an object can be found, or where an object has attributes which can change over the range of frames where it exists. If we have a person, for example, one may wish to track that person through the scene. We may define an OBJECT descriptor with descriptor-name "Person" as follows:

    NAME: svalue [static]
    GENDER : lvalue [MALE FEMALE] [default MALE] [static]
    AGE : dvalue [default 0] [static]
    HASHAIR : bvalue [default TRUE] [static]
    INCONVERSATION: relation [PERSON] [static]
    NOSE : point
    HEAD : bbox

Expressed in the xml format:

<descriptor name="Person" type="OBJECT">
    <attribute name="NAME" type="svalue" dynamic="false"/>
    <attribute name="GENDER" type="lvalue" dynamic="false">
            <data:lvalue-enum value="MALE"/>
            <data:lvalue-enum value="FEMALE"/>
        </ data:lvalue-possibles >
            <data:lvalue value="MALE"/>
    <attribute name="AGE" type="dvalue" dynamic="false">
            <data:dvalue value="0"/>
    <attribute name="HASHAIR" type="bvalue" dynamic="false">
            <data:bvalue value="true"/>
    <attribute name="INCONVERSATION" type="relation" dynamic="false">
        <data:relation-target value="PERSON"/>
    <attribute name="PERCENTVISIBLE" type="fvalue"/>
    <attribute name="NOSE" type="point"/>
    <attribute name="HEAD" type="bbox"/>

Data Section

Static attribute example

For CONTENT descriptors and OBJECT descriptors with static attributes, the attributevalue is a single entity. For example, a shot-change data record may appear as:

CONTENT Shot-Change 45 101:108

Or, in xml as:

<content name=" Shot-Change" id="45" framespan="101:108">
    <attribute name="TYPE">
        <data:lvalue value=""DISSOLVE"/>

Dynamic attribute example

For OBJECT types which dynamic attributes, the attributes are described as a list of M attribute values separated by commas:

OBJECT ObjectDesc ID I:I+M
    attribute1 : "attribute-value1,1" , -, "attribute-value1,M"
    attribute2 : "attribute-value2,1" , -, "attribute-value2,M"
    attributeN : "attribute-valueN,1" , -, "attribute-valueN,M"

A multiplier option is available to compact values that occur over a number of frames:

attribute1 : "k1*(attribute-value1,1)", \.\.\.
attribute2 : "k2*(attribute-value2,1)", \.\.\.
attributeN : "kn*(attribute-valueN,1)", \.\.\.

For example, recall the Person configuration record defined above where NAME, GENDER, AGE, HASHAIR and INCONVERSATION are static attributes and PERCENTVISABLE, NOSE and HEAD are dynamic attributes. A data record may appear as:

OBJECT Person 22 110:114
    AGE : "24"
    INCONVERSATION: "2 3 45"
    PERCENTVISABLE : 5*("100")
    NOSE : "14 20", 3*("15 20"), "15 21"
    HEAD : "1 1 30 40", 3*("1 1 32 23"), "2 2 33 34"

The xml format is similar. Note that it uses the descriptor type as the main element for the record. Also note that it is possible to specify the multiplier in two ways that cannot be shared on a descriptor: as span, which operates as the .gtf multiplier, or as a framespan, which looks like the descriptor frame span. The benefit of the second one is that it allows values to become null implicitly by skipping frames, while the other format forces the use of <data:null> elements. However, note that there is no default framespan value, while span defaults to 1.

<object name="Person" id="22" framespan="110:114">
    <attribute name="NAME">
        <data:svalue value="JOHN JONES" />
    <attribute name="GENDER">
        <data:lvalue value="MALE"/>
    <attribute name="AGE">
        <data:dvalue value="24"/>
    <attribute name="HASHAIR">
        <data:bvalue value="TRUE"/>
    <attribute name="INCONVERSATION">
        <data:relation value="2 3 45"/>
    <attribute name="PERCENTVISABLE">
        <data:fvalue value="100" span="5"/>
    <attribute name="NOSE">
        <data:point x="14" y="20"/>
        <data:point x="15" y="20" span="3"/>
        <data:point x="15" y="21"/>
    <attribute name="HEAD">
        <data:bbox x="1" y="1" width="30" height="40"/>
        <data:bbox x="1" y="1" width="32" height="23" span="3"/>
        <data:bbox x="2" y="2" width="33" height="34"/>

Accessing the Data through the ViPER API

The ViPER API offers a set of Java interfaces and classes for manipulating data in the ViPER XML format. It offers a hierarchical view of the data, allowing manipulation of the configuration and the instance data. The main package of interest is viper.api, which contains the ViperData and supporting interfaces. The implementation is stored in viper.api.impl, and the current implemented attribute data types are stored in viper.api.datatypes. This section presumes an understanding of the ViPER data model, described above and in the ViPER-GT manual.

Creating an Instance of ViperData

Here is a simple java example of accessing ViPER data:

ViperData data = new ViperDataImpl();
Config fileInfo
    = data.createConfig(Config.FILE, "Information");
fileInfo.createAttrConfig("Framerate", "fvalue", false,
                          new Integer(29.997), null);
Sourcefile f1 = data.createSourcefile("sample.mpg");
Descriptor first = f1.createDescriptor(fileInfo);
     .setAttrValue (new Integer(25));
System.out.println ("Framerate is: " +

The above code creates a new instance of a ViperData object. A ViperData object is analogous to the entirety of a single ViPER XML file. It then creates the configuration for a File descriptor with the name "Information". As described above, a descriptor with this name and type is important to our tools. It then adds a single "dvalue", or integer, attribute to the descriptor, and then creates an instance of it. Note that it must be attached to a media source file, although that file may have no name.

Reading a File into the API

Here is a sample of code that reads in viper data from a file:

FileInputStream inFile = new FileInputStream ("gt.xml");
Element documentEl = DocumentBuilderFactory.newInstance()
ViperData newFile = ViperParser.parseDoc (documentEl);

The above code opens the file "gt.xml" for parsing, parses it into the XML DOM using the javax.xml package, and then extracts the ViperData using viper.api.impl.ViperParser. The Xerces parser from xml.apache.org will also work.

Saving to a File

The current implementation does not support saving to files, only reading and manipulation of the data.

Setting Up a Complex Evaluation with runEvaluation.pl

The viper-pe command line tool takes a minimum of four arguments, and only performs one comparison. It is common to desire a set of comparisons, using similar options on the same or different result data files. The runEvaluation.pl tool is designed to run multiple evaluations with similar options; it also supports graph generation through the makeGraph.pl tool. This section presumes knowledge of the viper-pe command line tool, described in depth in the Performance Evaluation with ViPER manual. However, the use of the makeGraph.pl tool should be transparent to the evaluator. For more information, see the runEvaluation.howto file in the viper/scripts/docs directory.

Generating Data Sets with Convert

The convert tools are in the java program viper.descriptors.Convert, and can be invoked with the scripts gtf2xml, xml2gtf, gtf2gtf, and xml2xml. They are included in the viper/pe program, and are pure java code with no dependencies. The scripts take a variety of arguments, allowing them to do more than just convert between the old and new ground truth formats. All of them take either a list of files, or read files from the input stream, and output a single file to the output stream. This allows the user to easily aggregate ground truth files.

Command Line Switch Meaning
Clip Tells the conversion utility to print out only the first frame of each object. This is useful for doing tracking tests; for some tests, we distribute first-frame information, indicating which objects to track. Each descriptor also has a unique name we use for TRACKING_EVALUATION. (See ViPER-PE and Case Studies documents.)
Split Tells the conversion utility to split objects that are not contiguous in time into separate objects with unique identification numbers that are contiguous in time. Note that this splits both on regions that are marked invalid in viper-gt as well as regions where all dynamic attributes are set to NULL. (See ViPER-GT documentation for definition of NULL and description of validity.) This is useful when identity of an object is not important, but tracking is.
shift<N> Shift the frames by the desired amount. For example, gtf2gtf -shift-100 takes the first 100 frames off the file. Note that frame 0 is allowed, although ViPER-GT produces 1-based data, so you may want to run gtf2gtf -shift1 immediately following the above command.
filter <f> Takes a file name as an argument. The file should contain a filter, as described in the ViPER-PE document. This allows the user to generate a subset of the ground truth, or even to modify it somewhat as specified in the below Modification section.


As mentioned above and in the other documents, ViPER data files come in two format: a plain text format and an XML format. Both are usable in ViPER-PE, but ViPER-GT requires use of import and export to access data in the older format. While the XML format is the recommended one, it may be simpler to use the old GTF file format for your software; some of the scripts listed in this document still make use of the older format. To address the problem, the gtf2xml and xml2gtf shell scripts, which invoke the viper.descriptors.Convert main method, convert to and from the two video metadata formats.

However, since the XML makes explicit references to the names of the video files it represent(s), it is often prudent to add a FILE Information descriptor to an old format GTF file before running gtf2xml. Convert requires this descriptor to apply the proper names in the sourcefile tag of the XML files. ViPER treats FILE Information descriptors as special descriptors, taking into account NUMFRAMES and FRAME-SIZE attributes, although the only necessary attribute for conversion is the SOURCEFILES attribute.

FILE Information
    SOURCEDIR: svalue
    SOURCEFILES: svalue
    NUMFRAMES: dvalue

The source directory attribute, SOURCEDIR, provides the base for the relative path names specified in the SOURCEFILES attribute. The SOURCEFILES attribute is a list mapping frames to files. In the case of a single MPEG, a possible value would be "1 vid.mpeg", while in the case of a list of jpeg files it would read "1 a.jpg 2 b.jpg 3 c.jpg".

Note that the number is the start frame of the media file specified in the following token. Currently, the GTF parser will fail on file names that contain white space characters.


Often, it is quicker and easier to compare not just one file to another via viper-pe, but to instead evaluate software on a whole group of files, such as a set of keyframes, pages of a document, or other related media. While ViPER-GT can currently handle only lists of keyframes, the new XML format allows sets of any media type. For an example of this, see the Case Studies document.

There are a few caveats for aggregation. One is to make sure that each input file refers to the correct file name. The software is case sensitive, so it is important to make sure that the source media names are each the same case in all locations, which may not occur in windows or when transferring files via CD-R. The sourcefile information is stored in different locations in the two viper data formats. In the classic, more human readable ground truth format, it is stored in the FILE Information descriptor as described both above and in the appendix for the ViPER-GT manual. For the newer XML format, the source media file name is found in the sourcefile elementÂ's filename attribute.


As listed in the table of command line arguments above, the Converter utility allows several different types of modification. While many modifications should be able to be performed with the ViPER API, or via a nice Perl or XSL:T script, Converter allows some of the more common modifications directly, as well as some interesting ones via a filter.

If a filter is passed to a Converter, it must contain a FILTER region, marked with #BEGIN_FILTER and #END FILTER. The filter file format is specified in the ViPER-PE manual. While most of the filters, like size, contains and ==, determine if a descriptor is to be output, a few filters, like resize and cropTo, are designed to modify the attribute values. For example, to modify a ground truth file that has bbox attributes that go out side the frame, pass a cropTo ("0", "0", "640", "480") to the appropriate attribute of the descriptor in the FILTER, where the frame size is 640x480.

Overlaying Shapes on Source Images with OVERLAY Scripts

The OVERLAY scripts are designed to draw boxes on a movie using a result or ground truth data file as a reference, and include the ability to color code the boxes as false, missed, and correct. These scripts are included in the viper/viz directory and require Image MagickÂ's convert and mogrify, as well as perl and csh. It is a fairly old program and does not yet support the newer XML format or video files, instead relying on the older ground truth format and lists of .ppm or .jpeg files as specified in the FILE Information descriptorÂ's SOURCEFILES attribute.

Adding Your Own Data Types

Adding Data Types to ViPER-GT

To add a data type to ViPER-GT, you must add: the necessary extensions to the parser and serializer to XML, the Java JTable cell renderer and editor, any extended support for editing the attribute configuration in the schema editor, and, finally, any canvas views and controllers or timeline renderers if requested.

The first extension is simply reading in the data. This involves adding a new data wrapper. You can see some examples in the viper.api.datatypes package. For a simple example, see the viper.api.datatypes.Bvalue , while the viper.api.datatypes.Polygon is perhaps the most complex example. The attribute wrapper must implement AttrValueParser to support serialization and deserialization, and DefaultedAttrValueWrapper to support taking default values. (If you wish to specify parameters for the data in the schema section, implement ExtendedAttrValueParser instead of AttrValueParser .)

An AttrValueWrapper is a utility object that enforces typing on the attributes. It can also convert from an internal format (say, an integer lookup into a table, in the case of an enumeration) to an external format (such as a String ). This allows space savings and possibly allows the use of == instead of .equals() . The interface we are implementing, AttrValueParser , extends this to support serialization to an XML DOM object. Note that the de/serialization functions use the encoded format of the data.

Perhaps it is a good idea to mention something about how best to formulate the XML syntax for encoding your data. I would recommend developing a schema data type for your attribute, as I have done for each of the currently included data types in the the ViPER Data Type schema . Again, it is simple enough to stuff everything into a 'value' attribute of an XML element, but you may wish to have multiple XML attributes (e.g. the bounding boxes' x , y , width and height ), or you may wish to have nested elements (like the polygons' data:point elements). To add the data type to the list of allowed data types, you can either modify the ViperDataFactoryImpl class, or you can add your classes using the ViperDataFactoryImpl.addType method. You will have to do this before parsing. If you wish to modify ViPER-GT to support your data type, you can add this by modfying the 'datatypes' bean declaration in the gt-config.n3 file. [TODO:: explain how; possibly requires modifying apploader]

After you implement the api, you can run the Java command line tool viper.api.impl.ViperParser.FileTranscriberTest , which parses a file and serializes it again. This should allow you to check for errors both in your format, assuming your code is written correctly, and your code, assuming your XML is valid.

[TODO:: add information about adding jtable cell renderer and editor]

[TODO:: add information about adding new data type to video frame]

For information on adding your own wizards and views, see the Extending GT section below.

Adding Data Types to ViPER-PE

To add an attribute data type to ViPER-PE, a programmer must subclass viper.descriptors.attributes.Attribute (or one of its subclasses) and implement a set of methods. The class name must have the form: viper.descriptors.attributes.Attribute_*datatype*, where datatype is replaced with the name of the attribute type. This class acts as a proxy for the actual data type, which must implement the AttributeValue interface, and may implement the ``ExtendedAttributeValue` interface`_ if the attribute takes additional information in its configuration section. Most of the mechanics of the data type are performed in the root Attribute class; the proxy subclass must only follow a few rules.

These rules include implementing three constructors, all three of which must first pass their parameters to the super constructor, and then do what is necessary to call setArchetype. The three constructors are the default constructor, a constructor that takes a single boolean indicating the attribute is dynamic, and a constructor that takes an Attribute, from which to create a duplicate. The archetype is an instance of the AttributeValue class that the new data type will use, and the root class uses its factory methods to create additional instances without having to know about its type. Note that instances of AttributeValue should be immutable. The Attribute class must also implement a clone() method, but all other methods are optional.

If your data type takes extended information, such as the list of possible values for an lvalue, its archetype must implement the ExtendedAttributeValue interface, which gives methods for getting the extended config string for output. Since AttributeValues must be immutable, there is no method to modify the extended attributes. Instead, the Attribute proxy class must implement setArchetype (String) and setArchetype (Element) (or only one or the other if you donÂ't wish to support both data formats).

If you wish to make your data type composable, for use with the MULTIPLE OBJECT_EVALUATION, have the data type implement the ``Composable` interface`_.

Extending the ViPER-GT Annotation Tool

In developping large amounts of ground truth for specific applications, it is often cost-effective to develop custom panels, or even data types, to extends ViPER-GT in support of your annotation. For example, it might be useful to build a face annotation wizard to select face points, or a zone tool that allows the user to quickly label zones of text.

An Example: Labelled Text Zones

One common form of ground truth is labelled text zones, for example, in a document. This form of ground truth is often, these days, generated directly from source. However, we are finding it again useful to test camera based OCR, new script identification algorithms, and any system that works on older data.

We will use a simple example schema, with a single OBJECT descriptor type called ZONE, with three attributes: an enumeration indicating the zone type, a bounding box indicating its location, and a relation indicating its place in a directed graph of other ZONE descriptors.

The Zone Editor Panel

The first step is adding a panel designed specifically for ZONE objects, to act both as the main UI for this mode of editing, but also to act behind the scenes with the Limn3 framework as a plug-in.

We'll create a JPanel extension called ZoneEditor, which will mainly work presenting a button selecting the type of zone the user wishes to draw. Clicking the same one twice will get out of zone editor mode, so the user should still be able to access the standard GT functionality. It will also feature a checkbox allowing the user to quickly hide all other spatial data, as well as some other features to be added later. It will also require editing the system.n3 configuration file to load the plug-in, so we won't have to edit the source code of the application itself, or at least, not yet.

For ease of the example, the ZoneEditor uses BoxLayout, although it should more likely use some sort of toolbar mechanism. It features a single check-box which hides the other shapes, and a set of buttons that is derived at run-time from the list of values the ZONE Type attribute may take. In order to do its work, the checkbox uses the mediator's hiding functionality. The buttons, however, are somewhat more complicated.

Since ViPER-GT, in its rigid object-oriented approach, decided that a user must first create an object before bestowing it with features, a bit of a hack must exist in our button's action listener. When the user first clicks a ZONE button, the action listener creates a ZONE descriptor instance of that Type, and sets the user's selection to the NULL Location attribute of the object. This way, the video frame view knows that a click will mean the start of a box creation event for that descriptor. Clicking on another ZONE Type button changes the type of the unlocated ZONE, and clicking the same button deletes it. When a ZONE is placed on the video frame view, a ViperMediatorChangeListener will know to create a new unlocated ZONE.

Adding Color to the Types

[TODO: ask Charles how to do this]

Linking with DocLib

In order to be really useful, it makes sense to link the program with a segmentation and labelling system to automate most of the process. DocLib offers a Java interface to a powerful set of document processing functionality.

[TODO: ask Stefan how to do this]

Bugs Remain

Saving without unselecting a button results in an extra unlocated ZONE descriptor being saved into the annotation file.