Browse Source

initial commit

Kolja Strohm 7 năm trước cách đây
commit
625d1aaf8b
113 tập tin đã thay đổi với 15690 bổ sung0 xóa
  1. 110 0
      GUI Anleitung.txt
  2. 12 0
      README.txt
  3. 34 0
      annotationGUI/CMakeLists.txt
  4. 41 0
      annotationGUI/CSVReader.cpp
  5. 34 0
      annotationGUI/CSVReader.h
  6. 72 0
      annotationGUI/DetectionClient.cpp
  7. 23 0
      annotationGUI/DetectionClient.hh
  8. 86 0
      annotationGUI/ZMQClient.cpp
  9. 25 0
      annotationGUI/ZMQClient.hh
  10. 481 0
      annotationGUI/annotationxml.cpp
  11. 98 0
      annotationGUI/annotationxml.h
  12. 245 0
      annotationGUI/arbeitsview.cpp
  13. 50 0
      annotationGUI/arbeitsview.h
  14. 177 0
      annotationGUI/changemask.cpp
  15. 99 0
      annotationGUI/changemask.h
  16. 125 0
      annotationGUI/changemask.ui
  17. 141 0
      annotationGUI/changepacket.cpp
  18. 66 0
      annotationGUI/changepacket.h
  19. 184 0
      annotationGUI/changepacket.ui
  20. 100 0
      annotationGUI/classoptions.cpp
  21. 60 0
      annotationGUI/classoptions.h
  22. 62 0
      annotationGUI/classoptions.ui
  23. 576 0
      annotationGUI/controller.cpp
  24. 157 0
      annotationGUI/controller.h
  25. 414 0
      annotationGUI/frame.cpp
  26. 159 0
      annotationGUI/frame.h
  27. 45 0
      annotationGUI/frametree.cpp
  28. 45 0
      annotationGUI/frametree.h
  29. 188 0
      annotationGUI/frametreemodel.cpp
  30. 57 0
      annotationGUI/frametreemodel.h
  31. 18 0
      annotationGUI/icons.qrc
  32. BIN
      annotationGUI/icons/before.png
  33. BIN
      annotationGUI/icons/black.png
  34. BIN
      annotationGUI/icons/cut.png
  35. BIN
      annotationGUI/icons/delete.png
  36. BIN
      annotationGUI/icons/lupe_in.png
  37. BIN
      annotationGUI/icons/lupe_out.png
  38. BIN
      annotationGUI/icons/move.png
  39. BIN
      annotationGUI/icons/next.png
  40. BIN
      annotationGUI/icons/pipette.png
  41. BIN
      annotationGUI/icons/polygon.png
  42. BIN
      annotationGUI/icons/preview.png
  43. BIN
      annotationGUI/icons/select.png
  44. BIN
      annotationGUI/icons/warning.png
  45. BIN
      annotationGUI/icons/white.png
  46. 104 0
      annotationGUI/kamera.cpp
  47. 46 0
      annotationGUI/kamera.h
  48. 38 0
      annotationGUI/kmath.cpp
  49. 15 0
      annotationGUI/kmath.h
  50. 54 0
      annotationGUI/main.cpp
  51. 620 0
      annotationGUI/mainwindow.cpp
  52. 99 0
      annotationGUI/mainwindow.h
  53. 605 0
      annotationGUI/mainwindow.ui
  54. 84 0
      annotationGUI/mask.cpp
  55. 41 0
      annotationGUI/mask.h
  56. 434 0
      annotationGUI/model.cpp
  57. 217 0
      annotationGUI/model.h
  58. 56 0
      annotationGUI/newsequenz.cpp
  59. 38 0
      annotationGUI/newsequenz.h
  60. 77 0
      annotationGUI/newsequenz.ui
  61. 19 0
      annotationGUI/newsequenz3.ui
  62. 313 0
      annotationGUI/object.cpp
  63. 117 0
      annotationGUI/object.h
  64. 85 0
      annotationGUI/requestfromserver.cpp
  65. 63 0
      annotationGUI/requestfromserver.h
  66. 49 0
      annotationGUI/requestfromserver.ui
  67. 16 0
      annotationGUI/segmentationtest.cpp
  68. 393 0
      annotationGUI/sequenz.cpp
  69. 89 0
      annotationGUI/sequenz.h
  70. 2741 0
      annotationGUI/tinyxml2.cpp
  71. 2264 0
      annotationGUI/tinyxml2.h
  72. 485 0
      annotationGUI/uncrustify.cfg
  73. 22 0
      annotationGUI/usermode.h
  74. 339 0
      annotationGUI/zmq_alt.hpp
  75. 17 0
      schriftlich/Makefile
  76. 4 0
      schriftlich/README.txt
  77. 338 0
      schriftlich/bilder/Datenhaltung_vortrag.svg
  78. BIN
      schriftlich/bilder/anzahl_bilder.png
  79. BIN
      schriftlich/bilder/anzahl_objekte.png
  80. BIN
      schriftlich/bilder/arbeitsansicht.png
  81. BIN
      schriftlich/bilder/bevorzugt.png
  82. BIN
      schriftlich/bilder/bild_von_kamera.jpg
  83. BIN
      schriftlich/bilder/controller.pdf
  84. 301 0
      schriftlich/bilder/controller.svg
  85. BIN
      schriftlich/bilder/datenhaltung.pdf
  86. 408 0
      schriftlich/bilder/datenhaltung.svg
  87. BIN
      schriftlich/bilder/ergebnis.jpg
  88. BIN
      schriftlich/bilder/fragebogen.pdf
  89. BIN
      schriftlich/bilder/gui_mit_ids.png
  90. BIN
      schriftlich/bilder/hauptansicht.png
  91. BIN
      schriftlich/bilder/klassenansicht.png
  92. BIN
      schriftlich/bilder/maske_von_kamera.jpg
  93. BIN
      schriftlich/bilder/maskenansicht.png
  94. BIN
      schriftlich/bilder/mvc.png
  95. BIN
      schriftlich/bilder/mvc.xcf
  96. BIN
      schriftlich/bilder/nutzer_bewertung.png
  97. BIN
      schriftlich/bilder/nutzung.png
  98. BIN
      schriftlich/bilder/objectbaum.png
  99. BIN
      schriftlich/bilder/objektzuweisung.png
  100. BIN
      schriftlich/bilder/server_return.png
  101. BIN
      schriftlich/bilder/werkzeuge.png
  102. BIN
      schriftlich/bilder/zerteiltesobjekt.png
  103. 16 0
      schriftlich/kapitel/1.5relwork.tex
  104. 36 0
      schriftlich/kapitel/1einleitung.tex
  105. 145 0
      schriftlich/kapitel/2annotation.tex
  106. 212 0
      schriftlich/kapitel/3funktionen.tex
  107. 103 0
      schriftlich/kapitel/4implementierung.tex
  108. 175 0
      schriftlich/kapitel/5ergebnis.tex
  109. 12 0
      schriftlich/kapitel/6Schluss.tex
  110. 3 0
      schriftlich/kapitel/7anhang.tex
  111. 139 0
      schriftlich/schriftlich.bib
  112. BIN
      schriftlich/schriftlich.pdf
  113. 274 0
      schriftlich/schriftlich.tex

+ 110 - 0
GUI Anleitung.txt

@@ -0,0 +1,110 @@
+Anleitung:
+
+Eine Neue Sequenz erstellen:
+Klicken sie auf Öffnen.
+Wählen sie in dem Dialog den Ordner aus, der die Bilder der Sequenz enthält.
+Klicken sie im Dialog auf Open.
+Es sollte ein Dialog erscheinen mit der Frage, ob eine neue Sequenz erstellt werden soll.
+Klicken sie auf Yes.
+Falls in dem ausgewählten Ordner eine CSV Datei existiert, wird die Sequenz von der CSV Datei genneriert.
+Falls keine CSV Datei existiert, so wird ein Dialog erscheinen, mit der Frage, ob die Sequenz trotzdem erstellt werden soll.
+Klicken sie auf Yes.
+Es wird nun eine Sequenz erstellt, in der die Bilder im ausgewählten Ordner alphabetisch sortiert sind. Die Namen der Unterordner werden als Kameranamen verwendet.
+Es erscheint ein Dialog wo sie den Speicherort der Sequenz festlegen können. 
+Wählen sie einen leeren Ordner aus. In diesem Ordner werden dann die Unterordner "Annotations", "JPEGImages", "SourceImages", "SourceMasks" automatisch generiert.
+Es erscheint nun ein Dialog, in dem ausgewählt werden kann welche Bilder in die neue Sequenz übertragen werden sollen. Standartmäßig werden alle Bilder übertragen.
+
+Eine bestehende Sequenz öffnen:
+Klicken sie auf Öffnen.
+Wählen sie in dem Dialog den Ordner aus, der die Sequenz enthält.
+Die vier Ordner "Annotations", "JPEGImages", "SourceImages" und "SourceMasks" müssen direkte Unterordner des ausgewählten Ordners sein.
+Klicken sie in dem Dialog auf Open.
+Die Sequenz wird nun geladen und es wird das erste Bild der ersten Kamera angezeigt.
+
+Ein neues Objekt erstellen:
+Wählen sie das Werkzeug "Neues Objekt" aus oder drücken sie "n" um es auszuwählen.
+Klicken sie nun der Reihe nach alle Eckpunkte des Objektes an.
+Um den Vorgang abzuschließen, klicken sie erneut auf den zuerst angeklickten Punkt oder drücken sie "n".
+
+Ecke eines Objektes verschieben oder neue Ecke einfügen:
+Wählen sie das Werkzeug "Verschieben" aus oder drücken sie "m" um es auszuwählen.
+Bewegen sie ihre Maus in die Nähe der Ecke, die sie verschieben möchten.
+Die ecke, die verschoben wird, leuchtet grün auf.
+Halten sie die linke Maustaste gedrückt, um die Ecke zu verschieben.
+
+Löschen von Eckpunkten oder von Objekten:
+Wählen sie das Werkzeug "Löschen" aus oder drücken sie "d" um es auszuwählen.
+Halten sie die linke Maustaste gedrückt, um einen Bereich festzulegen, in dem alle Eckpunkte gelöscht werden sollen.
+Wenn durch das löschen dieser Eckpunkte ein Objekt weniger als 3 Eckpunkte hat, wird es automatisch gelöscht.
+
+Löschen von ganzen Objekten:
+Klicken sie mit der rechten Maustaste auf das Objekt.
+Wählen sie in dem erschienenen Menü den Eintrag "Objekt löschen".
+
+Objekte verstecken:
+Wählen sie das Werkzeug "Verstecken" aus oder drücken sie "s" um es auszuwählen.
+Klicken sie nun auf ein Objekt um es zu verstecken.
+Wenn sie erneut auf das Objekt klicken, wird es wieder angezeigt.
+Andere Werkzeuge haben keinen Effekt auf versteckte Objekte.
+
+Objekte zerteilen:
+Wählen sie das Werkzeug "Zerschneiden" aus oder drücken sie "x" um es auszuwählen.
+Klicken sie an einem Polygon zwei Eckpunkte an.
+Die Eckpunkte dürfen nicht nebeneinander liegen.
+Beim klick auf den zweiten Eckpunkt wird das Objekt anhand der beiden Eckpunkte zerteilt.
+
+Objekt ID ändern:
+Machen sie einen Rechtsklick auf das Objekt.
+Rählen sie in dem erschienenen Menü den Eintrag "Objekt ID bearbeiten ..." aus.
+Es erschein nun ein Dialog, wo in einem Textfeld die neue Objekt ID eingegeben werden kann.
+Unter diesem Textfeld ist das Objekt abgebildet, dessen ID geändert werden soll.
+Auf der rechten Seite des Dialogs kann das Objekt mit anderen Objekten verglichen werden.
+Dabei kann in einer Liste ein Objekt ausgewählt werden.
+Mit zwei Knöpfen kann man sich verschiedene Bilder von dem ausgewählten Vergleichsobjekt anzeigen lassen.
+Dabei können auch die Pfeiltasten zur Navigation verwendet werden.
+Klicken sie auf speichern, um dem Objekt die neue ID zuzuweisen.
+ObjektIDs können auch mit dem Kopieren und Einfügen Werkzeug zugewiesen werden (siehe unten).
+
+Objekte kopieren:
+Wählen sie das Werkzeug "Kopieren" aus oder drücken sie "c" um es auszuwählen.
+Klicken sie auf das Objekt, welches kopiert werden soll.
+Das kopierte Objekt wird grün markiert.
+Halten sie an einer freien Stelle die Rechte Maustaste gedrückt um das kopierte Objekt zu drehen.
+Klicken sie (z. B. auf einem anderen Bild) auf eine freie Stelle, um das Objekt dort einzufügen.
+Klicken sie (z. B. auf einem anderen Bild) auf ein vorhandenes Objekt, um festzulegen, dass es sich dabei um das gleiche Objekt handelt, wie das kopierte.
+Dabei erhält das Objekt die Objekt ID des kopierten Objektes. Um ein neues Objekt zu kopieren drücken sie erneut "c".
+
+Objektklassen verwalten:
+Klicken sie auf Optionen.
+Klicken sie auf "Klassen verwalten".
+Es erschein ein Dialog, mit allen Objektklassen, die in der geöffneten Sequenz vorkommen.
+Mit einem Doppelklick auf den Namen einer Klasse, kann diese umbenannt werden.
+Mit einem Klick auf "Neue Klasse" können Klassen hinzugefügt werden.
+Mit einem Klick auf "Klasse löschen" wird die in der Liste ausgewählte Klasse entfernt.
+
+Kamera Maske erstellen:
+Wählen sie oben in der Leiste den Eintrag Maske bearbeiten, wenn sie die Maske zu dem Bild der aktuellen Kamera bearbeiten wollen.
+Andernfalls klicken sie in dem Objektbaum auf der linken Seite des Hauptfensters mit der rechten Maustaste auf eine Kamera.
+Wählen sie in dem erschienenen Menü den Eintrag "Maske bearbeiten ..." aus.
+Es erscheint ein Dialog, in dem die Maske bearbeitet werden kann.
+Wählen sie für das spezifizieren von schwarzen Flächen den Knopf mit dem schwarzen Polygon aus.
+Wählen sie für das spezifizieren von weißen Flächen den Knopf mit dem weißen Polygon aus.
+Klicken sie nun in dem angezeigten Bild auf die Eckpunkte der Fläche, die sie einfärben wollen.
+Um den Vorgang abzuschließen klicken sie auf die zuerst angeklickte Ecke.
+Klicken sie auf "speichern", um die Maske zu speichern.
+Die Maske wird anschließend auf alle Bilder der Kamera angewendet.
+Alle Objekte, die sich vollständig im schwarzen Bereich der Maske befinden, werden automatisch entfernt.
+
+Objekte vom Server abfragen:
+Falls ein Bild ausgewählt wird, welches zuvor noch nicht annotiert wurde, werden die Objekte automatisch vom Server abgefragt.
+Wenn der Vorgang zu einem späteren Zeitpunkt manuell wiederholt wird, gehen dabei alle bereits annotierten Objekte auf dem Bild verloren.
+Um den Vorgang manuell auszuführen, machen sie einen Rechtsklick auf das Bild im Objektbaum auf der linken Seite des Hauptfensters.
+Wählen sie in dem erscheinenden Menü den Eintrag "Vom Server abfragen ...".
+Wenn der Vorgang länger dauert, erscheint ein Dialog, in dem der Vorgang abgebrochen werden kann.
+
+Server Adresse ändern:
+Klicken sie auf Optionen.
+Wählen sie den Eintrag "Server adresse" aus.
+Es erschein ein Dialog mit einem Textfeld.
+Geben sie dort die neue Server Adresse ein.
+Falls hier eine leere Adresse eingegeben wird, werden keine Vorabannotationen mehr vom Server abgefragt.

+ 12 - 0
README.txt

@@ -0,0 +1,12 @@
+Installationsanleitung für die GUI für Ubuntu 16.04
+
+Kopieren sie den ordner annotationGUI an eine beliebige Stelle mit schreibzugriff
+Führen sie ein Terminal in dem neuen Ordner aus.
+Benutzen sie folgende Befehle um die GUI zu compilieren:
+
+sudo apt-get install cmake make libopencv-dev qt5-default libzmq3-dev   (Abhängigkeiten installieren)
+cmake                                                                   (Zum erstellen des MakeFiles)
+make                                                                    (Zum compilieren)
+./annotation_gui                                                        (zum starten der Anwendung)
+
+Eine Bedienungsanleitung befindet sich in "GUI Anleitung.txt"

+ 34 - 0
annotationGUI/CMakeLists.txt

@@ -0,0 +1,34 @@
+cmake_minimum_required(VERSION 2.8.11)
+
+project(annotationGUI)
+
+# Find includes in corresponding build directories
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+# Instruct CMake to run moc automatically when needed.
+set(CMAKE_AUTOMOC ON)
+set(CMAKE_AUTOUIC ON)
+set(CMAKE_AUTORCC ON)
+set(CMAKE_CXX_STANDARD 11)
+
+# Find the QtWidgets library
+find_package(Qt5Widgets)
+#find_package(tinyxml2)
+
+find_package( OpenCV REQUIRED )
+
+set(QT_USE_QTGUI TRUE)
+set(QT_USE_QTXML TRUE)
+
+# Tell CMake to create the helloworld executable
+add_executable(annotation_gui WIN32 model.cpp controller.cpp requestfromserver.cpp requestfromserver.ui newsequenz.cpp newsequenz.ui classoptions.cpp classoptions.ui CSVReader.cpp ZMQClient.cpp DetectionClient.cpp tinyxml2.cpp main.cpp mainwindow.cpp changepacket.cpp changepacket.ui changemask.cpp changemask.ui mask.cpp mainwindow.ui frametree.cpp kmath.cpp frametreemodel.cpp annotationxml.cpp frame.cpp arbeitsview.cpp sequenz.cpp object.cpp kamera.cpp icons.qrc)
+
+# Use the Widgets module from Qt 5.
+target_link_libraries(annotation_gui Qt5::Widgets ${OpenCV_LIBS} zmq)
+
+
+
+
+
+#add_executable(segtest segmentationtest.cpp DetectionClient.cpp DetectionClient.hh ZMQClient.cpp ZMQClient.hh )
+
+#target_link_libraries(segtest  ${OpenCV_LIBS} zmq)

+ 41 - 0
annotationGUI/CSVReader.cpp

@@ -0,0 +1,41 @@
+#include "CSVReader.h"
+#include <sstream>
+#include <algorithm>
+
+// Erstellt den Leser
+//  path: Der Pfad der CSV Datei
+CSVReader::CSVReader( std::string path )
+    : stream( path )
+{
+    stream.seekg( 0, std::ios_base::end );
+    byteCount = stream.tellg();
+    stream.seekg( 0, std::ios_base::beg );
+}
+
+// Gibt die nächste Zeile der CSV Datei zurück
+std::vector< std::string > CSVReader::getNextRow()
+{
+    std::string line;
+    int tmp = stream.tellg();
+    std::getline( stream, line );
+    line.erase(std::remove(line.begin(), line.end(), '\n'), line.end());
+    byteCount -= (int)stream.tellg() - tmp;
+    std::stringstream lineStream( line );
+    std::vector< std::string > result;
+    std::string cell;
+    while(std::getline(lineStream,cell, ';'))
+        result.push_back(cell);
+    return result;
+}
+
+// Gibt true zurück, falls es noch eine Zeile gibt
+bool CSVReader::hasNext() const
+{
+    return stream && !stream.eof() && byteCount > 0;
+}
+
+// Gibt den Fortschritt des lesens in Prozent zurück
+int CSVReader::getProgress()
+{
+    return (int)(((int)stream.tellg() / ((int)stream.tellg() + (double)byteCount)) * 100);
+}

+ 34 - 0
annotationGUI/CSVReader.h

@@ -0,0 +1,34 @@
+#ifndef CSVREADER_H
+#define CSVREADER_H
+
+#include <string>
+#include <vector>
+#include <fstream>
+
+/*
+ * Ließt eine CSV Datei
+ */
+class CSVReader
+{
+private:
+
+    std::ifstream stream; // Der Stream, mit dem die Datei gelesen wird
+    int byteCount; // Die Anzahl der Bytes in der Datei
+
+public:
+
+    // Erstellt den Leser
+    //  path: Der Pfad der CSV Datei
+    CSVReader(std::string path);
+
+    // Gibt die nächste Zeile der CSV Datei zurück
+    std::vector<std::string>getNextRow();
+
+    // Gibt true zurück, falls es noch eine Zeile gibt
+    bool                    hasNext() const;
+
+    // Gibt den Fortschritt des lesens in Prozent zurück
+    int                     getProgress();
+};
+
+#endif // CSVREADER_H

+ 72 - 0
annotationGUI/DetectionClient.cpp

@@ -0,0 +1,72 @@
+#include "DetectionClient.hh"
+#include <sstream>
+#include <tinyxml2.h>
+#include <stdexcept>
+#include "ZMQClient.hh"
+#include <chrono>
+
+DetectionClient::DetectionClient() :
+  ZMQClient()
+{}
+
+DetectionClient::~DetectionClient() {
+    cancel = 1;
+}
+
+// Gibt die Polygone aller gefundenen Objekte zurück
+//  image: Das Bild das nach Objekten durchsucht werden soll
+std::vector< std::vector<cv::Point> > *DetectionClient::getContours(cv::Mat image){
+
+    cv::Mat segmentImage = cv::Mat::zeros(image.rows,image.cols,CV_8U);
+
+    std::chrono::steady_clock::time_point begin = std::chrono::steady_clock::now();
+
+
+    sendStringToServer("array", true);
+    std::string replyMessage = sendImageToServer(image, false);
+    std::stringstream sstream(replyMessage);
+
+    std::chrono::steady_clock::time_point end= std::chrono::steady_clock::now();
+
+    std::cout << "Time get Contour = "
+              << (static_cast<float>(std::chrono::duration_cast<std::chrono::milliseconds>(end - begin).count())/1000.f)
+              << " sec" << std::endl;
+
+    if(!segmentImage.isContinuous()){
+      segmentImage = segmentImage.clone();
+    }
+    sstream.read((char *)segmentImage.data, image.cols * image.rows);
+    std::vector< std::vector<cv::Point> > *contours = new std::vector< std::vector<cv::Point> >();
+    double mind, maxd;
+//  cv::imshow("Test", segmentImage * 10);
+    cv::minMaxIdx(segmentImage, &mind, &maxd);
+    unsigned int max = static_cast<unsigned int>(maxd);
+
+    // the segments are scanned individually with compare and then added to contours, because findContours looks for non zero segments
+    for(unsigned int j = 1; j <= max; j++){
+      std::vector< std::vector<cv::Point> > contour;
+      cv::Mat singleSegment = cv::Mat::zeros(image.rows, image.cols, CV_8U);
+      cv::compare(segmentImage, j, singleSegment, cv::CMP_EQ);
+      cv::findContours(singleSegment, contour, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE);
+      if (!contour.empty()) {
+        contours->push_back(contour[0]);
+      }
+    }
+
+    return contours;
+}
+
+// Gibt das Segemtierte Bild zurück
+//  image: Das Bild, welches segmentiert werden soll
+cv::Mat DetectionClient::getSegmentation(cv::Mat image)
+{
+  cv::Mat segmentImage = cv::Mat::zeros(image.rows,image.cols,CV_8U);
+  sendStringToServer("array", true);
+  std::string replyMessage = sendImageToServer(image, false);
+  std::stringstream sstream(replyMessage);
+  if(!segmentImage.isContinuous()){
+    segmentImage = segmentImage.clone();
+  }
+  sstream.read((char *)segmentImage.data, image.cols * image.rows);
+  return segmentImage;
+}

+ 23 - 0
annotationGUI/DetectionClient.hh

@@ -0,0 +1,23 @@
+#pragma once
+
+#include <vector>
+#include <opencv2/opencv.hpp>
+#include "ZMQClient.hh"
+
+/*
+ * Wird verwendet, um den Server nach Objekten auf einem Bild zu fragen
+ */
+class DetectionClient : public ZMQClient
+{
+public:
+    DetectionClient();
+    ~DetectionClient();
+
+    // Gibt die Polygone aller gefundenen Objekte zurück
+    //  image: Das Bild das nach Objekten durchsucht werden soll
+    std::vector<std::vector<cv::Point> >* getContours(cv::Mat image);
+
+    // Gibt das Segemtierte Bild zurück
+    //  image: Das Bild, welches segmentiert werden soll
+    cv::Mat getSegmentation(cv::Mat image);
+};

+ 86 - 0
annotationGUI/ZMQClient.cpp

@@ -0,0 +1,86 @@
+#include "ZMQClient.hh"
+
+#include <iostream>
+
+ZMQClient::ZMQClient() :
+    cancel( 0 ),
+    socket(context, ZMQ_REQ)
+{
+    addr = "";
+}
+
+ZMQClient::~ZMQClient()
+{
+    socket.disconnect(addr);
+    socket.close();
+}
+
+void ZMQClient::connect(std::string address){
+    addr = address;
+    if(address != ""){
+        socket.connect(address.c_str());
+        int timeout = 0;
+        socket.setsockopt( ZMQ_LINGER, timeout );
+    }
+}
+
+std::string ZMQClient::sendMessageToServer(const void *messageData, int messageLength, bool sendMore){
+  zmq::message_t message(messageLength);
+  memcpy((void *)message.data(), messageData, messageLength);
+  if( !socket.send(message, sendMore ? ZMQ_SNDMORE : 0) )
+      return "";
+  return sendMore ? "" : receiveMessage();
+}
+
+std::string ZMQClient::receiveMessage(){
+  // Wait for reply
+  zmq::message_t reply;
+  int ok;
+  do {
+      ok = socket.recv(&reply, ZMQ_DONTWAIT);
+  } while( ok == 0 && !cancel );
+  if( ok < 1 || cancel )
+      return "";
+  std::string replyMessage = std::string(static_cast<char *>(reply.data()), reply.size());
+  return replyMessage;
+  /*zmq::message_t reply;
+  socket.recv(&reply);
+  std::string replyMessage = std::string(static_cast<char *>(reply.data()), reply.size());
+  return replyMessage;*/
+}
+
+std::string ZMQClient::sendStringToServer(std::string messageStr, bool sendMore){
+  return sendMessageToServer(messageStr.c_str(), messageStr.size(), sendMore);
+}
+
+std::string ZMQClient::sendStringsToServer(std::vector<std::string> messages, bool sendMore){
+  for(int unsigned i = 0; i < messages.size() - 1; i++){
+    sendStringToServer(messages[i], true);
+  }
+  return sendStringToServer(messages.back(), sendMore);
+}
+
+/* Veraltet und bringt Server zum absturtz
+std::string ZMQClient::sendImageToServer(cv::Mat image, bool sendMore){
+  if(!image.isContinuous())
+  {
+    image = image.clone();
+  }
+  sendStringsToServer({std::to_string(image.cols), std::to_string(image.rows), "3"}, true);
+  return sendMessageToServer(image.data, image.cols * image.rows * 3, sendMore);
+}*/
+
+std::string ZMQClient::sendImageToServer(cv::Mat image, bool sendMore){
+  if(!image.isContinuous())
+  {
+    image = image.clone();
+  }
+  sendStringsToServer({std::to_string(image.cols), std::to_string(image.rows), "3"}, true);
+  std::vector<uchar> buff;//buffer for coding
+  std::vector<int> param(2);
+  param[0] = cv::IMWRITE_JPEG_QUALITY;
+  param[1] = 60;//default(95) 0-100
+  cv::imencode(".jpg", image, buff, param);
+  return sendMessageToServer(buff.data(), buff.size(), sendMore);
+  //return sendMessageToServer(image.data, image.cols * image.rows * 3, sendMore);
+}

+ 25 - 0
annotationGUI/ZMQClient.hh

@@ -0,0 +1,25 @@
+#pragma once
+
+#include "zmq.hpp"
+#include "opencv2/opencv.hpp"
+
+class ZMQClient
+{
+public:
+    bool cancel;
+  ZMQClient();
+  ~ZMQClient();
+
+  void connect(std::string address);
+  std::string receiveMessage();
+  // the sendMore parameter determines, whether this message is part of a multi-part-message
+  std::string sendMessageToServer(const void *messageData, int messageLength, bool sendMore);
+  std::string sendStringToServer(std::string messageStr, bool sendMore);
+  std::string sendStringsToServer(std::vector<std::string> messages, bool sendMore);
+  std::string sendImageToServer(cv::Mat image, bool sendMore);
+private:
+  std::string addr;
+  zmq::context_t context;
+  zmq::socket_t socket;
+};
+

+ 481 - 0
annotationGUI/annotationxml.cpp

@@ -0,0 +1,481 @@
+#include <QFile>
+#include <QDebug>
+#include <QApplication>
+#include <QDir>
+#include <QTime>
+#include <QCoreApplication>
+#include <QLayout>
+#include <QMessageBox>
+#include <QFileDialog>
+#include <QMap>
+
+#include "CSVReader.h"
+#include "tinyxml2.h"
+#include "annotationxml.h"
+#include "object.h"
+#include "newsequenz.h"
+
+// Lädt eine Bildsequenz
+//  directory: Der Pfad zum Ordner, der die Bildsequenz enthält
+//  status: Im Verlauf des Ladens wird der Statustext in diesem Label
+//          gesetzt
+AnnotationLoader::AnnotationLoader( QString file, QLabel *status )
+{
+    sequenz = 0;
+    error = "Pfad nicht gefunden.";
+    QDir dir( file + "/Annotations" );
+    QFileInfoList fileInfos = dir.entryInfoList();
+    int fileCount = fileInfos.count();
+    QList< Kamera* > cams;
+    QList< Object > objects;
+    QList< QString > classes;
+    QProgressDialog progress( "Annotation wird geladen...", "", 0, fileCount );
+    progress.setWindowModality(Qt::WindowModal);
+    progress.setCancelButton( 0 );
+    for( int i = 2; i < fileCount; i++ )
+    { // Schleife durch alle xml Dateien mit Annotationen
+        status->setText( "frame " + QString::number( i - 1 ) + "/" + QString::number( fileCount - 2 ) + ", found cameras: " + QString::number( cams.count() ) + ", found packets: " + QString::number( objects.count() ) + " . . ." );
+        status->setGeometry( status->x(), status->y(), status->fontMetrics().width( status->text() ) + 10, status->height() );
+        status->repaint(); // Aktualisiere Status Text
+        tinyxml2::XMLDocument doc;
+        doc.LoadFile(fileInfos.at( i ).absoluteFilePath().toStdString().c_str() );
+        tinyxml2::XMLElement *camera = doc.FirstChildElement()->FirstChildElement("camera_id");
+        QString camName = camera->GetText();
+        Kamera *c = 0;
+        for( auto cam = cams.begin(); cam != cams.end(); cam++ )
+        { // Suche in den Kameras, ob die Kamera des aktuellen Bildes bereits existiert
+            if( (*cam)->getName() == camName )
+                c = *cam;
+        }
+        if( !c )
+        { // Falls die Kamera noch nicht existiert wird diese erstellt
+            c = new Kamera( camName, file + "/SourceMasks/" + QString::number( cams.size() ) + ".jpg", cams.size() );
+            cams.append( c );
+        }
+        Frame *f = new Frame( file + "/SourceImages/" + doc.FirstChildElement()->FirstChildElement("filename")->GetText(),
+                              doc.FirstChildElement()->FirstChildElement("timestamp")->GetText(), c->getChildCount(), c, doc.FirstChildElement()->FirstChildElement( "noobject" ) != 0 );
+        c->addFrame( f );
+        tinyxml2::XMLElement *object = doc.FirstChildElement()->FirstChildElement("object");
+        if( object )
+        {
+            do
+            { // Schleife durch alle annotierten Objekte des Bildes
+                int truncated = 0;
+                QString className = object->FirstChildElement("name")->GetText();
+                int classId = classes.indexOf( className );
+                if( classId < 0 )
+                {
+                    classId = classes.size();
+                    classes.append( className );
+                }
+                object->FirstChildElement("truncated")->QueryIntText(&truncated);
+                QList< QPolygon > polygons;
+                tinyxml2::XMLElement *polygon = object->FirstChildElement( "polygon" );
+                if( polygon )
+                {
+                    do
+                    { // Schleife durch alle Polygone des Objektes
+                        QPolygon pol;
+                        tinyxml2::XMLElement *point = polygon->FirstChildElement( "point" );
+                        if( point )
+                        {
+                            do
+                            { // Schleife durch alle Punkte des Polygons
+                                int x, y;
+                                point->FirstChildElement( "x" )->QueryIntText( &x );
+                                point->FirstChildElement( "y" )->QueryIntText( &y );
+                                pol.push_back( QPoint( x, y ) );
+                            } while( (point = point->NextSiblingElement() ) != NULL );
+                            polygons.append( pol );
+                        }
+                    } while( (polygon = polygon->NextSiblingElement() ) != NULL );
+                }
+                QString pName = object->FirstChildElement("id")->GetText();
+                if( objects.indexOf( Object( pName, classId ) ) < 0 )
+                    objects.append( Object( pName, classId ) );
+                f->addObject( pName, truncated != 0, polygons );
+            } while( ( object = object->NextSiblingElement() ) != NULL );
+        }
+        f->wasChangedSinceLastSave();
+        progress.setValue( progress.value() + 1 );
+    }
+    sequenz = new Sequenz( file, cams, objects );
+    if( !cams.size() )
+    {
+        sequenz->refRelease();
+        sequenz = 0;
+    }
+    foreach( QString className, classes )
+    {
+        sequenz->addClass( className );
+    }
+}
+
+AnnotationLoader::~AnnotationLoader()
+{
+    if( sequenz )
+        sequenz->refRelease();
+}
+
+// Gibt die geladene Sequenz zurück
+//  0 falls beim Laden ein Fehler aufgetreten ist
+Sequenz *AnnotationLoader::getSequenz()
+{
+    if( sequenz )
+        sequenz->refNew();
+    return sequenz;
+}
+
+// Gibt eine Fehlermeldung zurück, falls beim Laden ein Fehler aufgetreten
+// ist
+QString AnnotationLoader::getErrorMessage()
+{
+    return error;
+}
+
+// Erstellt eine neue Sequenz und Lädt diese
+//  directory: Der Pfad zu dem Ordner, welche die Bilder der neuen Sequenz
+// enthält
+//  status: Im Verlauf des Erstellens wird der Statustext in diesem Label
+// gesetzt
+AnnotationCreator::AnnotationCreator( QString directory, QLabel *status )
+{
+    QDir dir( directory );
+    QFileInfoList fileInfos = dir.entryInfoList();
+    int fileCount = fileInfos.count();
+    QString pathToCSV = "";
+    for( int i = 2; i < fileCount; i++ )
+    { // Suche nach der CSV Datei im angegebenen Ordner
+        if( fileInfos.at( i ).suffix().toLower() == "csv" )
+        {
+            CSVReader reader( fileInfos.at( i ).absoluteFilePath().toStdString() );
+            bool ok = false;
+            while( reader.hasNext() )
+            { // Suche nach dem Info Bild in der csv Datei
+                std::vector< std::string > row = reader.getNextRow();
+                if( row.size() < 3 )
+                { // Prüfe, ob die csv Datei das passende Vormat hat
+                    ok = false;
+                    break;
+                }
+                if( row.at( 1 ) == "Info Bild" )
+                    ok = true;
+            }
+            if( ok )
+                pathToCSV = fileInfos.at( i ).absoluteFilePath();
+        }
+    }
+    QString targetDir;
+    if( pathToCSV == "" )
+    { // Es wurde keine csv Datei gefunden
+        QMessageBox::StandardButton reply = QMessageBox::question(0, "Achtung", "In dem ausgewählten Ordner wurde keine CSV Datei mit Informationen über die neue Sequenz gefunden. Soll der Ordner manuell nach Bildern durchsucht werden? Die Bilder würden der Sequenz in Alphabetischer Reihenfolge hinzugefügt werden.",
+                                                                  QMessageBox::Yes|QMessageBox::No);
+        if (reply == QMessageBox::Yes) {
+            do {
+                targetDir = QFileDialog::getExistingDirectory(0,
+                                                              "Wo soll die neue Sequenz gespeichert werden?",
+                                                              "/studi-tmp/kstrohm",
+                                                              QFileDialog::ShowDirsOnly |
+                                                              QFileDialog::DontResolveSymlinks);
+                if( !QDir( targetDir ).count() )
+                    QMessageBox::critical(0,"Fehler","Bitte wählen sie einen leeren Ordner aus.");
+            } while( !QDir( targetDir ).count() );
+            status->setText("Suche Nach Bildern ...");
+            status->setGeometry( status->x(), status->y(), status->fontMetrics().width( status->text() ) + 10, status->height() );
+            status->repaint(); // Aktualisiere Status Text
+            numFrames = 0;
+            for( int i = 2; i < fileCount; i++ )
+            { // Zähle alle Bilder im angegebenen Ordner
+                numFrames += getImageCount( fileInfos.at( i ) );
+            }
+            if( !numFrames )
+            {
+                QMessageBox::critical(0,"Fehler","Es wurden keine Bilder gefunden. Unterstützte Bildformate sind: jpg, png und bmp.");
+            }
+            else
+            {
+                NewSequenz nseq( numFrames );
+                nseq.exec(); // Frage den Nutzer, welche Bilder er in die Neue Bildsequenz übernehmen möchte
+                int offset = nseq.getOffset(); // Startindex des ersten Bildes
+                int limit = nseq.getLimit(); // Anzahl der zu übernehmenden Bilder
+                numFrames = limit;
+                if( limit > 0 && offset >= 0 )
+                { // Falls das erstellen nicht abgebrochen wurde
+                    status->setText("Erstelle Annotation ...");
+                    status->setGeometry( status->x(), status->y(), status->fontMetrics().width( status->text() ) + 10, status->height() );
+                    status->repaint();
+                    int index = 0;
+                    // Erzteuge Ordnerstruktur
+                    QDir().mkpath( targetDir + "/Annotations" );
+                    QDir().mkpath( targetDir + "/SourceImages" );
+                    QDir().mkpath( targetDir + "/SourceMasks" );
+                    QDir().mkpath( targetDir + "/JPEGImages" );
+                    int nLen = 0;
+                    int count = limit;
+                    while( count > 0 )
+                    { // Ermittle die maximale Länge des Bildnamens (Die bilder werden 00..01-99..99 nummerriert)
+                        count /= 10;
+                        nLen++;
+                    }
+                    QProgressDialog progress( "Annotation wird erstellt...", "", 0, limit );
+                    progress.setWindowModality(Qt::WindowModal);
+                    progress.setCancelButton( 0 );
+                    int kamId = 0;
+                    for( int i = 2; i < fileCount; i++ )
+                    { // Erzeuge die neue Bildsequenz
+                        createAnnotationFor( fileInfos.at( i ), index, "", targetDir, nLen, status, offset, limit, progress, kamId );
+                    }
+                    AnnotationLoader loader( targetDir, status );
+                    sequenz = loader.getSequenz(); // lade die Bildsequenz
+                }
+            }
+        }
+    }
+    else
+    { // Falls eine CSV Datei existiert
+        do {
+            targetDir = QFileDialog::getExistingDirectory(0,
+                                                          "Wo soll die neue Sequenz gespeichert werden?",
+                                                          "/studi-tmp/kstrohm",
+                                                          QFileDialog::ShowDirsOnly |
+                                                          QFileDialog::DontResolveSymlinks);
+            if( !QDir( targetDir ).count() )
+                QMessageBox::critical(0,"Fehler","Bitte wählen sie einen leeren Ordner aus.");
+        } while( !QDir( targetDir ).count() );
+        // Erzeuge die Ordnerstruktur
+        QDir().mkpath( targetDir + "/Annotations" );
+        QDir().mkpath( targetDir + "/SourceImages" );
+        QDir().mkpath( targetDir + "/SourceMasks" );
+        QDir().mkpath( targetDir + "/JPEGImages" );
+        CSVReader reader( pathToCSV.toStdString() );
+        status->setText("Suche Nach Bildern ...");
+        int numFrames = 0;
+        while( reader.hasNext() )
+        { // Zahle die Anzahl der Bilder (ohne Info Bild)
+            std::vector< std::string > row = reader.getNextRow();
+            if(row.size() < 3 || row.at( 1 ) == "Info Bild")
+                continue;
+            numFrames++;
+        }
+        NewSequenz nseq( numFrames );
+        nseq.exec(); // Frage den Nutzer welche Bilder in die neue Sequenz übernommen werden sollen
+        int offset = nseq.getOffset();
+        int limit = nseq.getLimit();
+        int max = limit;
+        if( limit > 0 && offset >= 0 )
+        { // falls die Erstellung nicht abgebrochen wurde
+            reader = CSVReader( pathToCSV.toStdString() );
+            int nLen = 0;
+            int count = limit;
+            while( count > 0 )
+            { // Ermittle die Maximale Länge des Bildnamens
+                count /= 10;
+                nLen++;
+            }
+            int index = 0;
+            QProgressDialog progress( "Annotation wird erstellt...", "", 0, limit );
+            progress.setWindowModality(Qt::WindowModal);
+            progress.setCancelButton( 0 );
+            int maskCount = -1;
+            QMap< QString, int > cams;
+            while( reader.hasNext() )
+            { // Schleife durch jede Zeile der CSV Datei
+                std::vector< std::string > row = reader.getNextRow();
+                if(row.size() < 3 || row.at( 1 ) == "Info Bild")
+                    continue;
+                if( offset > 0 )
+                {
+                    offset--;
+                    continue;
+                }
+                if( !limit )
+                    break;
+                else
+                    limit--;
+                QString name = QString::number( index++ );
+                while( name.length() < nLen )
+                    name = "0" + name;
+                QString camName;
+                if( !cams.contains( QString( row.at( 1 ).c_str() ) ) )
+                {
+                    cams.insert( QString( row.at( 1 ).c_str() ), ++maskCount );
+                    camName = QString::number( maskCount );
+                }
+                else
+                {
+                    camName = QString::number( cams.take( QString( row.at( 1 ).c_str() ) ) );
+                }
+                QString sourceImagePath = targetDir + "/SourceImages/" + name + "." + row.at( 0 ).substr(row.at( 0 ).size() - 3).c_str();
+                QString jpegImagePath = targetDir + "/JPEGImages/" + name + "." + row.at( 0 ).substr(row.at( 0 ).size() - 3).c_str();
+                QString sourceMaskPath = targetDir + "/SourceMasks/" + camName + ".jpg";
+                QString imagePath = dir.absolutePath() + "/" + row.at( 0 ).c_str();
+                QFile( imagePath ).copy( sourceImagePath );
+                QFile( imagePath ).copy( jpegImagePath );
+                if( !QFileInfo(sourceMaskPath).exists() )
+                { // Erzeuge die Maske der Kamera falls sie noch nicht existiert
+                    QImage mask( imagePath );
+                    mask.fill( 0xFFFFFFFF );
+                    mask.save( sourceMaskPath );
+                }
+                Frame currentFrame( sourceImagePath, QString( row.at( 2 ).c_str() ), 0, 0, 1 );
+                tinyxml2::XMLDocument doc; // Erstelle xml Datei
+                doc.LinkEndChild( doc.NewDeclaration( "xml version=\"1.0\" " ) );
+                tinyxml2::XMLElement *annotation = doc.NewElement( "annotation");
+                annotation->LinkEndChild( doc.NewElement( "folder" ) )->LinkEndChild( doc.NewText( "VOC2007" ) );
+                annotation->LinkEndChild( doc.NewElement( "filename" ) )->LinkEndChild( doc.NewText( currentFrame.getName().toStdString().c_str() ) );
+                tinyxml2::XMLElement *source = doc.NewElement( "source" );
+                source->LinkEndChild( doc.NewElement( "database" ) )->LinkEndChild( doc.NewText( "The VOC2007 Database" ) );
+                source->LinkEndChild( doc.NewElement( "annotation" ) )->LinkEndChild( doc.NewText( "PASCAL VOC 2007" ) );
+                source->LinkEndChild( doc.NewElement( "image" ) )->LinkEndChild( doc.NewText( currentFrame.getName().toStdString().c_str() ) );
+                annotation->LinkEndChild( source );
+                annotation->LinkEndChild( doc.NewElement( "owner" ) )->LinkEndChild( doc.NewElement( "name" ) )->LinkEndChild( doc.NewText( "Kolja Strohm" ) );
+                annotation->LinkEndChild( doc.NewElement( "camera_id" ) )->LinkEndChild( doc.NewText( row.at( 1 ).c_str() ) );
+                tinyxml2::XMLElement *size = doc.NewElement( "size" );
+                QImage img = currentFrame.getImage();
+                size->LinkEndChild( doc.NewElement( "width" ) )->LinkEndChild( doc.NewText( std::to_string( img.width() ).c_str() ) );
+                size->LinkEndChild( doc.NewElement( "height" ) )->LinkEndChild( doc.NewText( std::to_string( img.height() ).c_str() ) );
+                size->LinkEndChild( doc.NewElement( "depth" ) )->LinkEndChild( doc.NewText( std::to_string( img.depth() ).c_str() ) );
+                annotation->LinkEndChild( size );
+                annotation->LinkEndChild( doc.NewElement( "segmented" ) )->LinkEndChild( doc.NewText( "0" ) );
+                annotation->LinkEndChild( doc.NewElement( "timestamp" ) )->LinkEndChild( doc.NewText( currentFrame.getTimestamp().toStdString().c_str() ) );
+                annotation->LinkEndChild( doc.NewElement( "noobject" ) )->LinkEndChild( doc.NewText( "need Annotation" ) );
+                doc.LinkEndChild( annotation );
+                name = currentFrame.getName();
+                doc.SaveFile( (targetDir + "/Annotations/" + name.mid( 0, name.lastIndexOf( '.' ) ) + ".xml").toStdString().c_str() );
+
+                status->setText( "create Annotation from csv ... " + QString::number((int)((max - limit) / (float)max * 100)) + "% complete" );
+                status->setGeometry( status->x(), status->y(), status->fontMetrics().width( status->text() ) + 10, status->height() );
+                status->repaint();
+                progress.setValue( progress.value() + 1 );
+            }
+            AnnotationLoader loader( targetDir, status );
+            sequenz = loader.getSequenz();
+        }
+    }
+}
+
+// Zählt alle Bilder in einem Ordner
+//  f: die Informationen zu dem Ordner
+int AnnotationCreator::getImageCount( QFileInfo f )
+{
+    if( f.isDir() )
+    {
+        int count = 0;
+        QFileInfoList fileInfos = QDir( f.absoluteFilePath() ).entryInfoList();
+        for( QFileInfo f : fileInfos )
+        {
+            if( f.fileName() == "." || f.fileName() == ".." )
+                continue;
+            count += getImageCount( f );
+        }
+        return count;
+    }
+    if( f.suffix().toLower() == "jpg" || f.suffix().toLower() == "png" || f.suffix().toLower() == "bmp" )
+        return 1;
+    return 0;
+}
+
+// Erstellt Annotationen zu allen Bildern in einem Ordner
+//  file: Die Information zur Datei, zu der Annotationen erstellt werden sollen
+//  index: Der Index des Bildes
+//  kamera: Der Name der aktuellen Kamera
+//  targetDir: Der Pfad zum Ziel Ordner
+//  nLen: Die maximale Länge des Namens
+//  status: Ein Zeiger auf den Status Text
+//  offset: Der Index des sersten Bildes der Sequenz
+//  limit: Die Anzahl der Bilder in der Sequenz
+//  progress: Ein Dialog mit einer Fortschrittsleiste
+//  kamId: Die Id der momentanen Kamera
+void AnnotationCreator::createAnnotationFor( QFileInfo file, int &index, QString kamera, QString targetDir, int nLen, QLabel *status, int &offset, int &limit, QProgressDialog &progress, int &kamId )
+{
+    if( limit == 0 )
+        return;
+    if( file.isDir() )
+    { // Falls die Datei ein Ordner ist, erfolgt ein recursiver Aufruf für alle Dateien darin
+        QString kamera = file.fileName();
+        QDir kamDir( file.absoluteFilePath() );
+        QFileInfoList kfInfos = kamDir.entryInfoList();
+        for( QFileInfo file : kfInfos )
+        {
+            QString newKam = kamera;
+            if( newKam != "" )
+                newKam += "/";
+            newKam += file.fileName();
+            createAnnotationFor( file, index, newKam, targetDir, nLen, status, offset, limit, progress, kamId );
+        }
+        kamId++;
+    }
+    else
+    { // Falls die Datei kein Ordner ist
+        if( offset > 0 )
+        {
+            offset--;
+            return;
+        }
+        if( limit > 0 )
+            limit--;
+        if( file.suffix().toLower() == "jpg" || file.suffix().toLower() == "png" || file.suffix().toLower() == "bmp" )
+        { // Falls die Datei ein Bild ist, wird eine Annotation für sie erstellt
+            QString name = QString::number( index );
+            while( name.length() < nLen )
+                name = "0" + name;
+            if( kamera == "" )
+                kamera = "Ohne Kamera";
+            QString sourceImagePath = targetDir + "/SourceImages/" + name + +"." + file.suffix().toLower();
+            QString jpegImagePath = targetDir + "/JPEGImages/" + name + +"." + file.suffix().toLower();
+            QString sourceMaskPath = targetDir + "/SourceMasks/" + QString::number( kamId ) + +".jpg";
+            QFile( file.absoluteFilePath() ).copy( sourceImagePath );
+            QFile( file.absoluteFilePath() ).copy( jpegImagePath );
+            if( !QFileInfo(sourceMaskPath).exists() )
+            { // Falls die Maske der Kamera noch nicht existiert wird sie erstellt
+                QImage mask( file.absoluteFilePath() );
+                mask.fill( 0xFFFFFFFF );
+                mask.save( sourceMaskPath );
+            }
+            Frame currentFrame( sourceImagePath, file.created().toString(Qt::SystemLocaleLongDate), 0, 0, 1 );
+            tinyxml2::XMLDocument doc;
+            doc.LinkEndChild( doc.NewDeclaration( "xml version=\"1.0\" " ) );
+            tinyxml2::XMLElement *annotation = doc.NewElement( "annotation");
+            annotation->LinkEndChild( doc.NewElement( "folder" ) )->LinkEndChild( doc.NewText( "VOC2007" ) );
+            annotation->LinkEndChild( doc.NewElement( "filename" ) )->LinkEndChild( doc.NewText( currentFrame.getName().toStdString().c_str() ) );
+            tinyxml2::XMLElement *source = doc.NewElement( "source" );
+            source->LinkEndChild( doc.NewElement( "database" ) )->LinkEndChild( doc.NewText( "The VOC2007 Database" ) );
+            source->LinkEndChild( doc.NewElement( "annotation" ) )->LinkEndChild( doc.NewText( "PASCAL VOC 2007" ) );
+            source->LinkEndChild( doc.NewElement( "image" ) )->LinkEndChild( doc.NewText( currentFrame.getName().toStdString().c_str() ) );
+            annotation->LinkEndChild( source );
+            annotation->LinkEndChild( doc.NewElement( "owner" ) )->LinkEndChild( doc.NewElement( "name" ) )->LinkEndChild( doc.NewText( "Kolja Strohm" ) );
+            annotation->LinkEndChild( doc.NewElement( "camera_id" ) )->LinkEndChild( doc.NewText( kamera.toStdString().c_str() ) );
+            tinyxml2::XMLElement *size = doc.NewElement( "size" );
+            QImage img = currentFrame.getImage();
+            size->LinkEndChild( doc.NewElement( "width" ) )->LinkEndChild( doc.NewText( std::to_string( img.width() ).c_str() ) );
+            size->LinkEndChild( doc.NewElement( "height" ) )->LinkEndChild( doc.NewText( std::to_string( img.height() ).c_str() ) );
+            size->LinkEndChild( doc.NewElement( "depth" ) )->LinkEndChild( doc.NewText( std::to_string( img.depth() ).c_str() ) );
+            annotation->LinkEndChild( size );
+            annotation->LinkEndChild( doc.NewElement( "segmented" ) )->LinkEndChild( doc.NewText( "0" ) );
+            annotation->LinkEndChild( doc.NewElement( "timestamp" ) )->LinkEndChild( doc.NewText( currentFrame.getTimestamp().toStdString().c_str() ) );
+            annotation->LinkEndChild( doc.NewElement( "noobject" ) )->LinkEndChild( doc.NewText( "need Annotation" ) );
+            doc.LinkEndChild( annotation );
+            doc.SaveFile( (targetDir + "/Annotations/" + name + ".xml").toStdString().c_str() );
+            status->setText( "create Annotation without csv ... " + QString::number((int)((numFrames - limit) / (float)numFrames * 100)) + "% complete" );
+            status->setGeometry( status->x(), status->y(), status->fontMetrics().width( status->text() ) + 10, status->height() );
+            status->repaint();
+            index++;
+            progress.setValue( progress.value() + 1 );
+        }
+    }
+}
+
+AnnotationCreator::~AnnotationCreator()
+{
+    if( sequenz )
+        sequenz->refRelease();
+}
+
+// Gibt die geladene neu erstellte Sequenz zurück
+//  0 falls beim Erstellen oder beim Laden ein Fehler aufgetreten ist
+Sequenz *AnnotationCreator::getSequenz()
+{
+    if( sequenz )
+        sequenz->refNew();
+    return sequenz;
+}

+ 98 - 0
annotationGUI/annotationxml.h

@@ -0,0 +1,98 @@
+#ifndef ANNOTATIONXML_H
+#define ANNOTATIONXML_H
+
+#include <QString>
+#include <QRect>
+#include <QLinkedList>
+#include <QPolygon>
+#include "frame.h"
+#include "sequenz.h"
+#include <QLabel>
+#include <QFileInfo>
+#include <QProgressDialog>
+
+/*
+ * Diese Klasse lädt die annotierten Bildsequenzen
+ */
+class AnnotationLoader
+{
+private:
+
+    Sequenz *sequenz; // Enthält die geladene Sequenz
+    QString  error;   // Enthält eine Fehlermeldung, falls beim laden ein Fehler
+                      // aufgetreten ist
+
+public:
+
+    // Lädt eine Bildsequenz
+    //  directory: Der Pfad zum Ordner, der die Bildsequenz enthält
+    //  status: Im Verlauf des Ladens wird der Statustext in diesem Label
+    //          gesetzt
+    AnnotationLoader(QString directory,
+                     QLabel *status);
+    ~AnnotationLoader();
+
+    // Gibt die geladene Sequenz zurück
+    //  0 falls beim Laden ein Fehler aufgetreten ist
+    Sequenz* getSequenz();
+
+    // Gibt eine Fehlermeldung zurück, falls beim Laden ein Fehler aufgetreten
+    // ist
+    QString getErrorMessage();
+};
+
+/*
+ * Diese Klasse erstellt eine neue Bildsequenz aus einem Ordner mit Bildern,
+ * oder einer csv Datei
+ */
+class AnnotationCreator
+{
+private:
+
+    Sequenz *sequenz;
+    int numFrames;
+
+    // Zählt alle Bilder in einem Ordner
+    //  f: die Informationen zu dem Ordner
+    int getImageCount(QFileInfo f);
+
+    // Erstellt Annotationen zu allen Bildern in einem Ordner
+    //  file: Die Information zur Datei, zu der Annotationen erstellt werden
+    //        sollen
+    //  index: Der Index des Bildes
+    //  kamera: Der Name der aktuellen Kamera
+    //  targetDir: Der Pfad zum Ziel Ordner
+    //  nLen: Die maximale Länge des Namens
+    //  status: Ein Zeiger auf den Status Text
+    //  offset: Der Index des sersten Bildes der Sequenz
+    //  limit: Die Anzahl der Bilder in der Sequenz
+    //  progress: Ein Dialog mit einer Fortschrittsleiste
+    //  kamId: Die Id der momentanen Kamera
+    void createAnnotationFor(QFileInfo        file,
+                             int            & index,
+                             QString          kamera,
+                             QString          targetDir,
+                             int              nLen,
+                             QLabel          *status,
+                             int            & offset,
+                             int            & limit,
+                             QProgressDialog& progress,
+                             int            & kamId);
+
+public:
+
+    // Erstellt eine neue Sequenz und Lädt diese
+    //  directory: Der Pfad zu dem Ordner, welche die Bilder der neuen Sequenz
+    //             enthält
+    //  status: Im Verlauf des Erstellens wird der Statustext in diesem Label
+    //          gesetzt
+    AnnotationCreator(QString directory,
+                      QLabel *status);
+    ~AnnotationCreator();
+
+    // Gibt die geladene neu erstellte Sequenz zurück
+    //  0 falls beim Erstellen oder beim Laden ein Fehler aufgetreten ist
+    Sequenz* getSequenz();
+};
+
+#endif // ANNOTATIONXML_H

+ 245 - 0
annotationGUI/arbeitsview.cpp

@@ -0,0 +1,245 @@
+#include "arbeitsview.h"
+#include <QLayout>
+#include "object.h"
+#include "frametreemodel.h"
+#include "mainwindow.h"
+#include <QDebug>
+#include "kmath.h"
+#include <QToolTip>
+
+// Errechnet die Farbe eines Objektes, so dass Objekte mit ähnlicher ID unterschiedliche Farben bekommen
+//  start: Die Farbe des ersten Objektes
+//  index: Die Id des Objektes, zu dem die Farbe ermittelt werden soll
+QColor colorFunction( QColor start, int index )
+{
+    if( index < 0 )
+        return start;
+    if( index % 2 == 0 )
+        return colorFunction( QColor( (start.green() + 10 * index) % 255, (start.blue() + 10 * index) % 255, (start.red() + 10 * index) % 255, start.alpha() ), index - 1 );
+    else
+        return colorFunction( QColor( (start.green() * index + 70) % 255, (start.blue() * index + 40) % 255, (start.red() * index + 60) % 255, start.alpha() ), index - 1 );
+}
+
+// Errechnet den Mittelpunkt eines Polygons
+QPointF centerOfPolygon( QPolygon p )
+{
+    QPointF res;
+    for( QPoint po : p )
+    {
+        res.setX( po.x() + res.x() );
+        res.setY( po.y() + res.y() );
+    }
+    res /= p.size();
+    return res;
+}
+
+// Inhalt der Arbeitsview Klasse
+//-------------------------------
+ArbeitsView::ArbeitsView(ArbeitsModel *model, QWidget *parent)
+    : QWidget(parent),
+      m( model ),
+      c( 0 ),
+      selectArea( 0 )
+{
+    parent->layout()->addWidget( this );
+    this->setMouseTracking( true );
+}
+
+ArbeitsView::~ArbeitsView()
+{
+    delete selectArea;
+}
+
+// Setzt den Controller, der die Nutzereingaben verarbeiten soll
+void ArbeitsView::setController( ArbeitsController *controller )
+{
+    c = controller;
+}
+
+// Zeichnet die View neu
+void ArbeitsView::paintEvent(QPaintEvent *)
+{
+    m->setView( pos(), size() );
+    QPainter painter( this );
+    if( m->getFrame() )
+    {
+        if( m->getDeleteField() )
+        {
+            if( !selectArea )
+            {
+                selectArea = new QRubberBand( QRubberBand::Rectangle, this );
+                selectArea->show();
+            }
+            selectArea->setGeometry( *m->getDeleteField() );
+        }
+        else if( selectArea )
+        {
+            selectArea->hide();
+            delete selectArea;
+            selectArea = 0;
+        }
+        // Zeichne Hintergrund Bild
+        painter.drawImage( QRect( m->translate( QPoint( 0, 0 ) ), m->translate( QPoint( m->getImage().width(), m->getImage().height() ) ) ), m->getImage() );
+        if( m->isMaskShown() && !m->getMask().isNull() )
+        { // Zeichne Masken Overlay
+            QPixmap img = m->getMask();
+            painter.setOpacity( 0.75 );
+            painter.drawPixmap( QRect( m->translate( QPoint( 0, 0 ) ), m->translate( QPoint( img.width(), img.height() ) ) ), img );
+            painter.setOpacity( 1 );
+        }
+        QList<ObjectPolygon> objects = m->getFrame()->getObjects();
+        QPen white( QColor( 255, 255, 255 ), 2 ); // Erstellt Stifte zum Zeichnen
+        QPen grey( QColor( 200, 200, 200 ), 1 );
+        QPen green( QColor( 0, 255, 0 ), 2 );
+        QPen lightGreen( QColor( 100, 255, 100 ), 2 );
+        QBrush blackBrush( QColor( 0, 0, 0 ), Qt::SolidPattern );
+        for( ObjectPolygon o : objects )
+        { // Schleife durch alle Objekte des Bildes
+            if( o->isSelected() )
+            {
+                painter.setPen( white );
+                int pIndex;
+                if( o == m->getCopyedObject() || ( !m->getCopyedObject().isNull() && o->getId() != "-1" && o->getId() == m->getCopyedObject()->getId() ) )
+                    painter.setPen( green );
+                else if( o == m->getFrame()->getObjectAt( m->inverseTranslate( m->getMousePoint() ), pIndex ) )
+                    painter.setPen( lightGreen );
+                pIndex = 0;
+                for( QPolygon pol : o->getPolygonList() )
+                { // Schleife durch alle Polygone des Bildes
+                    QBrush tmpB = painter.brush();
+                    if( m->areColoresShown() )
+                        painter.setBrush( QBrush( colorFunction( QColor( 255, 127, 0, 127 ), o->getId().toInt() ), Qt::SolidPattern ) );
+                    painter.drawPolygon( m->translatePolygon( pol ) ); // Zeichne Polygon
+                    if( m->areIdsShown() )
+                    { // Zeichne Objekt ID
+                        QFont f = painter.font();
+                        f.setPointSize( 30 );
+                        f.setWeight(QFont::DemiBold);
+                        QPainterPath tPath;
+                        tPath.addText( centerOfPolygon( m->translatePolygon( pol ) ), f, o->getId() );
+                        painter.setBrush( blackBrush );
+                        painter.drawPath( tPath );
+                    }
+                    painter.setBrush(tmpB);
+                    int index = 0;
+                    for( QPoint p : pol )
+                    { // Schleife durch alle Eckpunkte des Polygons
+                        QPen tmp = painter.pen();
+                        if( ( o == m->getCopyedObject() ) ||
+                            ( o == m->getMoveObject() && m->getMoveIndex() == index && m->getMovePolygon() == pIndex ) ||
+                            ( index == m->getCutIndex() && o == m->getCutObject() && m->getCutPolygon() == pIndex ) )
+                            painter.setPen( green );
+                        painter.drawEllipse( m->translate( p ), 5, 5 ); // Zeichne Eckpunkt
+                        index++;
+                        painter.setPen( tmp );
+                    }
+                    pIndex++;
+                }
+            }
+            else
+            { // Falls das Objekt versteckt ist werden nur die Polygone gezeichnet
+                painter.setPen( grey );
+                if( o == m->getCopyedObject() )
+                    painter.setPen( green );
+                for( QPolygon pol : o->getPolygonList() )
+                {
+                    painter.drawPolygon( m->translatePolygon( pol ) );
+                }
+            }
+        }
+        if( m->getMode() == NEW )
+        { // Zeichnen des neuen Objektes, welches noch nicht fertig erstellt wurde
+            painter.setPen( QPen( QColor( 0, 255, 0 ), 2, Qt::DotLine ) );
+            if( !m->getNewPolygon() )
+                painter.drawEllipse( (int)(m->getMousePoint().x()) - 2, (int)(m->getMousePoint().y()) - 2, 5, 5 );
+            else
+            {
+                for( auto p = m->getNewPolygon()->begin(); p != m->getNewPolygon()->end() - 1; p++ )
+                    painter.drawLine( m->translate( *p ), m->translate( *(p + 1) ) );
+                if( m->inverseTranslate( m->getMousePoint() ).x() > m->getNewPolygon()->begin()->x() - 10 &&
+                        m->inverseTranslate( m->getMousePoint() ).x() < m->getNewPolygon()->begin()->x() + 10 &&
+                        m->inverseTranslate( m->getMousePoint() ).y() > m->getNewPolygon()->begin()->y() - 10 &&
+                        m->inverseTranslate( m->getMousePoint() ).y() < m->getNewPolygon()->begin()->y() + 10 )
+                {
+                    painter.drawLine( m->translate( *m->getNewPolygon()->begin() ), m->translate( *(m->getNewPolygon()->end() - 1) ) );
+                }
+                else
+                {
+                    painter.drawLine( m->translate( *m->getNewPolygon()->begin() ), m->getMousePoint() );
+                    painter.drawLine( m->getMousePoint(), m->translate( *(m->getNewPolygon()->end() - 1) ) );
+                    painter.drawEllipse( (int)(m->getMousePoint().x()) - 2, (int)(m->getMousePoint().y()) - 2, 5, 5 );
+                }
+            }
+        }
+        if( !m->getCopyedObject().isNull() )
+        { // Zeichnet das Kopierte Objekt um die Maus herum
+            QPoint mousePos = m->inverseTranslate( m->getMousePoint() );
+            int pIndex;
+            if( m->getFrame()->getObjectAt( mousePos, pIndex ).isNull() )
+            {
+                QMatrix m;
+                m.QMatrix::translate( mousePos.x(), mousePos.y() );
+                m.rotate( this->m->getCopyedRotation() );
+                m.translate( -this->m->getCopyedCenter().x(), -this->m->getCopyedCenter().y() );
+                painter.setPen( QPen( QColor( 0, 255, 0 ), 2, Qt::DotLine ) );
+                for( QPolygon pol : this->m->getCopyedObject()->getPolygonList() )
+                    painter.drawPolygon( this->m->translatePolygon( m.map( pol ) ) );
+            }
+        }
+        if( !m->getMoveObject().isNull() && m->getInsertIndex() >= 0 )
+        { // Zeichnet beim Bewegen den zu bewegenden Eckpunkt
+            painter.setPen( green );
+            painter.drawEllipse( m->translate(m->getNewVertex() ), 5, 5 );
+        }
+    }
+}
+
+// Leitet nutzereingaben an den Controller weiter
+void ArbeitsView::mousePressEvent( QMouseEvent *e )
+{
+    if( c && m->getFrame() )
+        c->mousePressEvent( e );
+}
+
+// Leitet nutzereingaben an den Controller weiter
+void ArbeitsView::mouseMoveEvent( QMouseEvent *e )
+{
+    if( c && m->getFrame() )
+        c->mouseMoveEvent( e );
+}
+
+// Leitet nutzereingaben an den Controller weiter
+void ArbeitsView::mouseReleaseEvent( QMouseEvent *e )
+{
+    if( c && m->getFrame() )
+        c->mouseReleaseEvent( e );
+}
+
+// Leitet nutzereingaben an den Controller weiter
+void ArbeitsView::wheelEvent(QWheelEvent *e)
+{
+    if( c && m->getFrame() )
+        c->wheelEvent( e );
+}
+
+// Zeigt tooltip für Objekte an
+bool ArbeitsView::event( QEvent *e )
+{
+    if (e->type() == QEvent::ToolTip)
+    {
+        QHelpEvent *helpEvent = static_cast<QHelpEvent *>(e);
+        int pIndex;
+        if( m->getFrame() )
+        {
+            ObjectPolygon p = m->getFrame()->getObjectAt( m->inverseTranslate( helpEvent->pos() ), pIndex );
+            if ( !p.isNull() ) {
+                QToolTip::showText(helpEvent->globalPos(), "Objekt: " + p->getId(), this);
+            } else {
+                QToolTip::hideText();
+                e->ignore();
+            }
+            return true;
+        }
+    }
+    return QWidget::event( e );
+}

+ 50 - 0
annotationGUI/arbeitsview.h

@@ -0,0 +1,50 @@
+#ifndef VIEW_H
+#define VIEW_H
+
+#include <QWidget>
+#include <QRubberBand>
+#include <QMouseEvent>
+#include <QPainter>
+#include <QList>
+#include <QTreeView>
+
+#include "frame.h"
+#include "model.h"
+#include "controller.h"
+
+/*
+ * Verwaltet die Arbeitsfläche
+ */
+class ArbeitsView : public QWidget
+{
+    Q_OBJECT
+
+private:
+    ArbeitsModel * m;
+    ArbeitsController *c;
+    QRubberBand *selectArea;
+
+    // Zeichnet die View neu
+    void paintEvent(QPaintEvent *e) override;
+
+public:
+    explicit ArbeitsView(ArbeitsModel *model,
+                  QWidget      *parent = nullptr);
+    ~ArbeitsView();
+
+    // Setzt den Controller, der die Nutzereingaben verarbeiten soll
+    void setController(ArbeitsController *controller);
+
+    // Leitet nutzereingaben an den Controller weiter
+    void wheelEvent(QWheelEvent *e) override;
+    // Leitet nutzereingaben an den Controller weiter
+    void mouseReleaseEvent(QMouseEvent *e) override;
+    // Leitet nutzereingaben an den Controller weiter
+    void mousePressEvent(QMouseEvent *e) override;
+    // Leitet nutzereingaben an den Controller weiter
+    void mouseMoveEvent(QMouseEvent *e) override;
+    // Zeigt tooltip für Objekte an
+    bool event(QEvent *e) override;
+};
+
+#endif // VIEW_H

+ 177 - 0
annotationGUI/changemask.cpp

@@ -0,0 +1,177 @@
+#include "changemask.h"
+#include "ui_changemask.h"
+#include <QPainter>
+#include <QPolygon>
+#include <QMouseEvent>
+#include <QToolButton>
+
+// Erstellt den Dialog
+//  m: Die Maske, welche editiert werden soll
+//  cam: Die Kamera, zu welcher die Maske gehört
+//  parent: Der Eltern QWidget von Qt
+ChangeMask::ChangeMask( Mask *m, Kamera *cam, QWidget *parent ) :
+    QDialog(parent),
+    ui(new Ui::ChangeMask),
+    cam( cam )
+{
+    ui->setupUi(this);
+    setWindowTitle( "Maske von " + cam->getName() );
+    Frame *f = cam->getFrame( 0 );
+    m->loadMask();
+    if( m->getMask()->isNull() )
+        m->createMask( f->getImage().size() );
+    view = new ChangeMaskView( f->getImage(), m->getMask(), ui->widget );
+}
+
+ChangeMask::~ChangeMask()
+{
+    delete ui;
+    delete view;
+}
+
+// Setzt die Farbe zukünftiger Polygone auf schwarz
+void ChangeMask::on_black_clicked()
+{
+    ui->black->setChecked( true );
+    ui->white->setChecked( false );
+    view->setBlack( true );
+}
+
+// Setzt die Farbe zukünftiger Polygone auf Weiß
+void ChangeMask::on_white_clicked()
+{
+    ui->white->setChecked( true );
+    ui->black->setChecked( false );
+    view->setBlack( false );
+}
+
+// Bricht das Ändern der Maske ab ohne sie zu speichern
+void ChangeMask::on_abort_clicked()
+{
+    this->close();
+}
+
+// Beendet das Ändern der Maske und speichert sie
+void ChangeMask::on_save_clicked()
+{
+    this->close();
+    cam->saveMask();
+}
+
+// Inhalt der ChangeMaskView Klasse
+//----------------------------------
+// Erstellt die Ansicht
+//  background: Ein Bild der Kamera, zu der die Maske editiert werden soll
+//  mask: Das Schwarz-Weiß Bild, welches die momentane Maske darstellt
+//  parent: Der Eltern QWidget von Qt
+ChangeMaskView::ChangeMaskView(QImage background, QImage *mask,QWidget *parent)
+    : QWidget( parent ),
+      background( background ),
+      mask( mask ),
+      xScaleFactor( 1 ),
+      yScaleFactor( 1 ),
+      black( true )
+{
+    parent->layout()->addWidget( this );
+    this->setMouseTracking( true );
+}
+
+ChangeMaskView::~ChangeMaskView()
+{}
+
+// Wandelt einen Punkt aus Bildkoordinaten in Bildschirmkoordinaten um
+QPoint ChangeMaskView::translate( QPoint p )
+{
+    return QPoint( (int)(p.x() * xScaleFactor + 20), (int)(p.y() * yScaleFactor + 20) );
+}
+
+// Wandelt einen Punkt aus Bildschirmkoordinaten in Bildkoordinaten um
+QPoint ChangeMaskView::inverseTranslate( QPoint p )
+{
+    return QPoint( (int)((p.x()-20) / xScaleFactor), (int)((p.y()-20) / yScaleFactor) );
+}
+
+// Setzt die Zeichenfarbe
+//  black: Falls 0 wird in Zukunft mit weiß gezeichnet, andernfalls mit
+// schwarz
+void ChangeMaskView::setBlack( bool black )
+{
+    this->black = black;
+}
+
+// Zeichnet die Ansicht (wird automatisch vom Qt Framework aufgerufen)
+void ChangeMaskView::paintEvent(QPaintEvent *e)
+{
+    QSize s = size() - QSize( 40, 40 );
+    QRect imgRect( QPoint( 20, 20 ), s );
+    QSize imgS = background.size();
+    if( imgS.width() > s.width() )
+    {
+        imgS.scale( s, Qt::KeepAspectRatio );
+        imgRect.setSize( imgS );
+    }
+    // Aktualisiere die Skallierungs Faktoren
+    xScaleFactor = imgRect.width() / (float)background.width();
+    yScaleFactor = imgRect.height() / (float)background.height();
+    QPainter painter( this );
+    // Zeichne Hintergrundbild un Maske
+    painter.drawImage( QRect( translate( QPoint( 0, 0 ) ), translate( QPoint( background.width(), background.height() ) ) ), background );
+    painter.setOpacity( 0.75 );
+    painter.drawImage( QRect( translate( QPoint( 0, 0 ) ), translate( QPoint( mask->width(), mask->height() ) ) ), *mask );
+    painter.setOpacity( 1 );
+    painter.setPen( QPen( QColor( 0, 255, 0 ), 2, Qt::DotLine ) );
+    if( polygon.size() == 0 )
+        painter.drawEllipse( (int)(mousePos.x()) - 2, (int)(mousePos.y()) - 2, 5, 5 );
+    else
+    { // Zeichne das aktuelle Polygon
+        for( auto p = polygon.begin(); p != polygon.end() - 1; p++ )
+            painter.drawLine( translate( *p ), translate( *(p + 1) ) );
+        if( inverseTranslate( mousePos ).x() > polygon.begin()->x() - 30 && inverseTranslate( mousePos ).x() < polygon.begin()->x() + 30 &&
+            inverseTranslate( mousePos ).y() > polygon.begin()->y() - 30 && inverseTranslate( mousePos ).y() < polygon.begin()->y() + 30 )
+        {
+            painter.drawLine( translate( *polygon.begin() ), translate( *(polygon.end() - 1) ) );
+        }
+        else
+        {
+            painter.drawLine( translate( *polygon.begin() ), mousePos );
+            painter.drawLine( mousePos, translate( *(polygon.end() - 1) ) );
+            painter.drawEllipse( (int)(mousePos.x()) - 2, (int)(mousePos.y()) - 2, 5, 5 );
+        }
+    }
+}
+
+// Verarbeitet Maus Bewegungen (wird automatisch vom Qt Framework
+// aufgerufen)
+void ChangeMaskView::mouseMoveEvent( QMouseEvent *e )
+{
+    mousePos = e->pos();
+    repaint();
+}
+
+// Verarbeitet Maus Tastendrücke (wird automatisch vom Qt Framework
+// aufgerufen)
+void ChangeMaskView::mouseReleaseEvent( QMouseEvent *e )
+{
+    QPoint pos = inverseTranslate( e->pos() );
+    if( pos.x() > polygon.begin()->x() - 30 && pos.x() < polygon.begin()->x() + 30 &&
+        pos.y() > polygon.begin()->y() - 30 && pos.y() < polygon.begin()->y() + 30 )
+    {
+        QPainter painter;
+        painter.begin( mask );
+        if( black )
+        {
+            painter.setBrush( QBrush( QColor( 0, 0, 0 ), Qt::SolidPattern ) );
+            painter.setPen( QPen( QColor( 0, 0, 0 ) ) );
+        }
+        else
+        {
+            painter.setBrush( QBrush( QColor( 255, 255, 255), Qt::SolidPattern ) );
+            painter.setPen( QPen( QColor( 255, 255, 255 ) ) );
+        }
+        painter.drawPolygon( polygon );
+        polygon.clear();
+    }
+    else
+        polygon.append( pos );
+    repaint();
+}

+ 99 - 0
annotationGUI/changemask.h

@@ -0,0 +1,99 @@
+#ifndef CHANGEMASK_H
+#define CHANGEMASK_H
+
+#include <QDialog>
+#include "kamera.h"
+
+namespace Ui
+{
+    class ChangeMask;
+}
+class ChangeMaskView; // siehe weiter unten
+
+/*
+ * Verwaltet einen GUI-Dialog, welcher zum editieren einer Maske verwendet wird
+ */
+class ChangeMask : public QDialog
+{
+    Q_OBJECT
+
+public:
+
+    // Erstellt den Dialog
+    //  m: Die Maske, welche editiert werden soll
+    //  cam: Die Kamera, zu welcher die Maske gehört
+    //  parent: Der Eltern QWidget von Qt
+    explicit ChangeMask(Mask    *m,
+                        Kamera  *cam,
+                        QWidget *parent = 0);
+    ~ChangeMask();
+
+private slots:
+    // Setzt die Farbe zukünftiger Polygone auf schwarz
+    void on_black_clicked();
+    // Setzt die Farbe zukünftiger Polygone auf Weiß
+    void on_white_clicked();
+    // Bricht das Ändern der Maske ab ohne sie zu speichern
+    void on_abort_clicked();
+    // Beendet das Ändern der Maske und speichert sie
+    void on_save_clicked();
+
+private:
+
+    Ui::ChangeMask *ui; // Zeiger auf die in changemask.ui spezifizierten Objekte
+    ChangeMaskView *view; // Zeiger auf die View für das Editieren der Maske
+    Kamera *cam; // Zeiger auf die Kamera, deren Maske bearbeitet wird
+};
+
+/*
+ * Verwaltet die Ansicht, in der die Maske editiert werden kann
+ */
+class ChangeMaskView : public QWidget
+{
+    Q_OBJECT
+
+private:
+
+    QImage background; // Das Bild, welches im Hintergrund angezeigt werden soll
+    QImage  *mask; // Das Maskenbild, welches editiert werden soll
+    QPolygon polygon; // Das Polygon, welches vom Nutzer angegeben wird
+    float    xScaleFactor; // Ein Skallierungsfaktor in x Richtung
+    float    yScaleFactor; // Ein Skallierungsfaktor in y Richtung
+    QPoint   mousePos; // Die Position der Maus
+    bool     black; // 1, falls schwarz gezeichnet wird, 0, falls weiß gezeichnet wird
+    // Wandelt einen Punkt aus Bildkoordinaten in Bildschirmkoordinaten um
+    QPoint translate(QPoint p);
+    // Wandelt einen Punkt aus Bildschirmkoordinaten in Bildkoordinaten um
+    QPoint inverseTranslate(QPoint p);
+
+public:
+
+    // Erstellt die Ansicht
+    //  background: Ein Bild der Kamera, zu der die Maske editiert werden soll
+    //  mask: Das Schwarz-Weiß Bild, welches die momentane Maske darstellt
+    //  parent: Der Eltern QWidget von Qt
+    explicit ChangeMaskView(QImage   background,
+                            QImage  *mask,
+                            QWidget *parent = 0);
+    ~ChangeMaskView();
+
+    // Setzt die Zeichenfarbe
+    //  black: Falls 0 wird in Zukunft mit weiß gezeichnet, andernfalls mit
+    // schwarz
+    void setBlack(bool black);
+
+protected:
+
+    // Zeichnet die Ansicht (wird automatisch vom Qt Framework aufgerufen)
+    void paintEvent(QPaintEvent *e) override;
+
+    // Verarbeitet Maus Bewegungen (wird automatisch vom Qt Framework
+    // aufgerufen)
+    void mouseMoveEvent(QMouseEvent *e) override;
+
+    // Verarbeitet Maus Tastendrücke (wird automatisch vom Qt Framework
+    // aufgerufen)
+    void mouseReleaseEvent(QMouseEvent *e) override;
+};
+
+#endif // CHANGEMASK_H

+ 125 - 0
annotationGUI/changemask.ui

@@ -0,0 +1,125 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>ChangeMask</class>
+ <widget class="QDialog" name="ChangeMask">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>500</width>
+    <height>400</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Dialog</string>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout">
+   <item>
+    <layout class="QHBoxLayout" name="horizontalLayout_2" stretch="0,0,0,0">
+     <item>
+      <spacer name="horizontalSpacer">
+       <property name="orientation">
+        <enum>Qt::Horizontal</enum>
+       </property>
+       <property name="sizeHint" stdset="0">
+        <size>
+         <width>40</width>
+         <height>20</height>
+        </size>
+       </property>
+      </spacer>
+     </item>
+     <item>
+      <widget class="QToolButton" name="black">
+       <property name="text">
+        <string/>
+       </property>
+       <property name="icon">
+        <iconset resource="icons.qrc">
+         <normaloff>:/icons/black.png</normaloff>:/icons/black.png</iconset>
+       </property>
+       <property name="checkable">
+        <bool>true</bool>
+       </property>
+       <property name="checked">
+        <bool>true</bool>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QToolButton" name="white">
+       <property name="autoFillBackground">
+        <bool>false</bool>
+       </property>
+       <property name="text">
+        <string/>
+       </property>
+       <property name="icon">
+        <iconset resource="icons.qrc">
+         <normaloff>:/icons/white.png</normaloff>:/icons/white.png</iconset>
+       </property>
+       <property name="checkable">
+        <bool>true</bool>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <spacer name="horizontalSpacer_2">
+       <property name="orientation">
+        <enum>Qt::Horizontal</enum>
+       </property>
+       <property name="sizeHint" stdset="0">
+        <size>
+         <width>40</width>
+         <height>20</height>
+        </size>
+       </property>
+      </spacer>
+     </item>
+    </layout>
+   </item>
+   <item>
+    <widget class="QWidget" name="widget" native="true">
+     <property name="sizePolicy">
+      <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+       <horstretch>0</horstretch>
+       <verstretch>4</verstretch>
+      </sizepolicy>
+     </property>
+     <property name="maximumSize">
+      <size>
+       <width>16777215</width>
+       <height>16777215</height>
+      </size>
+     </property>
+     <layout class="QVBoxLayout" name="verticalLayout_2"/>
+    </widget>
+   </item>
+   <item>
+    <layout class="QHBoxLayout" name="horizontalLayout">
+     <property name="spacing">
+      <number>2</number>
+     </property>
+     <item>
+      <widget class="QPushButton" name="abort">
+       <property name="text">
+        <string>Abbrechen</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QPushButton" name="save">
+       <property name="text">
+        <string>Speichern</string>
+       </property>
+      </widget>
+     </item>
+    </layout>
+   </item>
+  </layout>
+ </widget>
+ <resources>
+  <include location="icons.qrc"/>
+ </resources>
+ <connections/>
+</ui>

+ 141 - 0
annotationGUI/changepacket.cpp

@@ -0,0 +1,141 @@
+#include "changepacket.h"
+#include "ui_changepacket.h"
+#include <QLabel>
+#include <QLineEdit>
+#include <QComboBox>
+#include <QDebug>
+#include <QResizeEvent>
+#include <QIntValidator>
+#include <QMessageBox>
+
+int ChangePacket::lastId = 0;
+
+// Erstellt den Dialog
+//  p: Das Polygon des Objektes, wessen ID zugewiesen werden soll
+//  s: Die Sequenz, welche momentan annotiert wird
+//  parent: Das Eltern QWidget von Qt
+ChangePacket::ChangePacket(ObjectPolygon p, Sequenz *s, QWidget *parent) :
+    QDialog(parent),
+    ui(new Ui::ChangePacket),
+    seq( s ),
+    object( p ),
+    count( -1 )
+{
+    ui->setupUi(this);
+    setWindowTitle( "Paket zuweisen" );
+    show();
+    Frame *f = (Frame*)p->getParent();
+    ui->packet->setPixmap( QPixmap::fromImage( f->getObjectImage( p ).scaled(ui->packet->size(),Qt::KeepAspectRatio) ) );
+    ui->name->setText( p->getId() );
+    QList< QString > objects = s->getObjectNames();
+    if( objects.indexOf( "-1" ) < 0 )
+        ui->packetList->addItem( "-1" );
+    max = -1;
+    for( auto p = objects.begin(); p != objects.end(); p++ )
+    {
+        ui->packetList->addItem( *p );
+        if( p->toInt() > max )
+            max = p->toInt();
+    }
+    ui->name->setValidator( new QIntValidator( -1, max + 1 ) );
+    if( p->getId() == "-1" && lastId <= max + 1 && lastId >= 0 )
+        ui->name->setText( QString::number( lastId ) );
+    updatePreview();
+    ui->name->clearFocus();
+}
+
+ChangePacket::~ChangePacket()
+{
+    delete ui;
+}
+
+// verarbeitet Tastatur Events (wird automatisch vom Qt Framework
+// aufgerufen)
+void ChangePacket::keyReleaseEvent(QKeyEvent *e)
+{
+    int current = ui->name->text().toInt();
+    if( e->key() == Qt::Key_Down && current != -1 )
+        current--;
+    if( e->key() == Qt::Key_Up && current < max + 1 )
+        current++;
+    if( e->key() == Qt::Key_Down || e->key() == Qt::Key_Up )
+        ui->name->setText( QString::number( current ) );
+    updatePreview();
+}
+
+// verarbeitet Tastatur Events (wird automatisch vom Qt Framework
+// aufgerufen)
+void ChangePacket::mousePressEvent(QMouseEvent *e)
+{
+    ui->name->clearFocus();
+}
+
+// Zeigt das neu ausgewählte Vergleichsobjekt an
+void ChangePacket::on_packetList_currentIndexChanged(const QString &packet)
+{
+    ui->packetCompare->setPixmap( QPixmap() );
+    if( packet != "-1" )
+    {
+        ui->name->setText( packet );
+        ui->packetCompare->setPixmap( QPixmap::fromImage( seq->previousObjectImage( packet, count ).scaled(ui->packetCompare->size(),Qt::KeepAspectRatio) ) );
+    }
+}
+
+// Zeigt das Vergleichsobjekt aus einem vorherigen Bild an
+void ChangePacket::on_previousePicture_clicked()
+{
+    count--;
+    if( count == 0 )
+        count--;
+    ui->packetCompare->setPixmap( QPixmap::fromImage( seq->previousObjectImage( ui->packetList->currentText(), count ).scaled(ui->packetCompare->size(),Qt::KeepAspectRatio) ) );
+}
+
+// Zeigt das Vergleichobjekt aus einem nachfolgenden Bild an
+void ChangePacket::on_nextPicture_clicked()
+{
+    count++;
+    if( count == 0 )
+        count++;
+    ui->packetCompare->setPixmap( QPixmap::fromImage( seq->previousObjectImage( ui->packetList->currentText(), count ).scaled(ui->packetCompare->size(),Qt::KeepAspectRatio) ) );
+}
+
+// Bricht das Zuweisen der Objekt-ID ab ohne sie zu speichern
+void ChangePacket::on_abbrechen_clicked()
+{
+    this->close();
+}
+
+// Beendet das Zuweisen der Objekt-ID un speichert die neue ID
+void ChangePacket::on_speichern_clicked()
+{
+    if( ui->name->text() == "-1" || ui->name->text() == "" )
+    {
+        QMessageBox::critical( this, "Fehler", "Ungültige Objekt Id." );
+        return;
+    }
+    if( ui->name->text() != object->getId() )
+    {
+        bool needQ = ((Frame*)object->getParent())->hasObject( ui->name->text() );
+        object->setId( ui->name->text() );
+        lastId = ui->name->text().toInt();
+        seq->addObjectName( ui->name->text() );
+        if( needQ )
+        {
+            QMessageBox::StandardButton reply = QMessageBox::question(0, "Warnung", "Auf diesem Bild gibt es bereits ein Objekt mit der gleichen Id. Möchten sie beide Objekte vereinen?",
+                                                                      QMessageBox::Yes|QMessageBox::No);
+            if (reply == QMessageBox::Yes)
+                ((Frame*)object->getParent())->connectObjects( ui->name->text() );
+        }
+    }
+    this->close();
+}
+
+// Wählt automatisch das Vergleichsobjekt aus, welches die ID hat, welche im Textfeld eingegeben wurde
+void ChangePacket::updatePreview()
+{
+    int index = ui->packetList->findText( ui->name->text() );
+    if( index < 0 )
+        index = 0;
+    ui->packetList->setCurrentIndex( index );
+    count = -1;
+}

+ 66 - 0
annotationGUI/changepacket.h

@@ -0,0 +1,66 @@
+#ifndef CHANGEPACKET_H
+#define CHANGEPACKET_H
+
+#include <QDialog>
+#include "sequenz.h"
+
+namespace Ui
+{
+class ChangePacket;
+}
+
+/*
+ * Verwaltet din GUI Dialog, welcher verwendet wird um den Objekten eine ID
+ *zuzuweisen
+ */
+class ChangePacket : public QDialog
+{
+    Q_OBJECT
+
+public:
+
+    // Erstellt den Dialog
+    //  p: Das Polygon des Objektes, wessen ID zugewiesen werden soll
+    //  s: Die Sequenz, welche momentan annotiert wird
+    //  parent: Das Eltern QWidget von Qt
+    explicit ChangePacket(ObjectPolygon p,
+                          Sequenz       *s,
+                          QWidget       *parent = 0);
+    ~ChangePacket();
+
+protected:
+
+    // verarbeitet Tastatur Events (wird automatisch vom Qt Framework
+    // aufgerufen)
+    void keyReleaseEvent(QKeyEvent *e) override;
+
+    // verarbeitet Tastatur Events (wird automatisch vom Qt Framework
+    // aufgerufen)
+    void mousePressEvent(QMouseEvent *e) override;
+
+private slots:
+
+    // Zeigt das neu ausgewählte Vergleichsobjekt an
+    void on_packetList_currentIndexChanged(const QString& arg1);
+    // Zeigt das Vergleichsobjekt aus einem vorherigen Bild an
+    void on_previousePicture_clicked();
+    // Zeigt das Vergleichobjekt aus einem nachfolgenden Bild an
+    void on_nextPicture_clicked();
+    // Bricht das Zuweisen der Objekt-ID ab ohne sie zu speichern
+    void on_abbrechen_clicked();
+    // Beendet das Zuweisen der Objekt-ID un speichert die neue ID
+    void on_speichern_clicked();
+
+private:
+    // Wählt automatisch das Vergleichsobjekt aus, welches die ID hat, welche im Textfeld eingegeben wurde
+    void updatePreview();
+
+    Ui::ChangePacket *ui; // Ein Zeiger auf die in changepacket.ui spezifizierten Objekte
+    Sequenz *seq; // Ein Zeiger auf die annotierte Bildsequenz
+    ObjectPolygon object; // Das ObjektPolygon, dessen Objekt-ID geändert werden soll
+    int count; // Der index des Vergleichsbildes ausgehend von dem aktuellen Bild (z.B. -1:=1 bild vorher)
+    int max; // Die maximale ID eines Objektes
+    static int lastId; // Die zuletzt vergebene ID (wird als startwert für neue Zuweisuzngen verwendet)
+};
+
+#endif // CHANGEPACKET_H

+ 184 - 0
annotationGUI/changepacket.ui

@@ -0,0 +1,184 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>ChangePacket</class>
+ <widget class="QDialog" name="ChangePacket">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>640</width>
+    <height>400</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Dialog</string>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout">
+   <item>
+    <widget class="QWidget" name="widget" native="true">
+     <property name="sizePolicy">
+      <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+       <horstretch>0</horstretch>
+       <verstretch>6</verstretch>
+      </sizepolicy>
+     </property>
+     <layout class="QHBoxLayout" name="horizontalLayout_3">
+      <item>
+       <widget class="QWidget" name="widget_2" native="true">
+        <property name="sizePolicy">
+         <sizepolicy hsizetype="Expanding" vsizetype="Preferred">
+          <horstretch>0</horstretch>
+          <verstretch>0</verstretch>
+         </sizepolicy>
+        </property>
+        <layout class="QVBoxLayout" name="verticalLayout_2">
+         <item>
+          <layout class="QHBoxLayout" name="horizontalLayout_4" stretch="0,0">
+           <item>
+            <widget class="QLabel" name="label">
+             <property name="text">
+              <string>ID:</string>
+             </property>
+            </widget>
+           </item>
+           <item>
+            <widget class="QLineEdit" name="name">
+             <property name="enabled">
+              <bool>true</bool>
+             </property>
+            </widget>
+           </item>
+          </layout>
+         </item>
+         <item>
+          <widget class="QLabel" name="packet">
+           <property name="sizePolicy">
+            <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+             <horstretch>1</horstretch>
+             <verstretch>5</verstretch>
+            </sizepolicy>
+           </property>
+           <property name="text">
+            <string/>
+           </property>
+          </widget>
+         </item>
+        </layout>
+       </widget>
+      </item>
+      <item>
+       <widget class="QWidget" name="widget_3" native="true">
+        <property name="sizePolicy">
+         <sizepolicy hsizetype="Expanding" vsizetype="Preferred">
+          <horstretch>0</horstretch>
+          <verstretch>0</verstretch>
+         </sizepolicy>
+        </property>
+        <layout class="QVBoxLayout" name="verticalLayout_3">
+         <item>
+          <layout class="QHBoxLayout" name="horizontalLayout_5" stretch="1,0,0">
+           <item>
+            <widget class="QComboBox" name="packetList"/>
+           </item>
+           <item>
+            <widget class="QToolButton" name="previousePicture">
+             <property name="text">
+              <string>...</string>
+             </property>
+             <property name="icon">
+              <iconset resource="icons.qrc">
+               <normaloff>:/icons/before.png</normaloff>:/icons/before.png</iconset>
+             </property>
+             <property name="shortcut">
+              <string>Left</string>
+             </property>
+            </widget>
+           </item>
+           <item>
+            <widget class="QToolButton" name="nextPicture">
+             <property name="text">
+              <string>...</string>
+             </property>
+             <property name="icon">
+              <iconset resource="icons.qrc">
+               <normaloff>:/icons/next.png</normaloff>:/icons/next.png</iconset>
+             </property>
+             <property name="shortcut">
+              <string>Right</string>
+             </property>
+            </widget>
+           </item>
+          </layout>
+         </item>
+         <item>
+          <widget class="QLabel" name="packetCompare">
+           <property name="sizePolicy">
+            <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+             <horstretch>1</horstretch>
+             <verstretch>6</verstretch>
+            </sizepolicy>
+           </property>
+           <property name="text">
+            <string/>
+           </property>
+          </widget>
+         </item>
+        </layout>
+       </widget>
+      </item>
+     </layout>
+    </widget>
+   </item>
+   <item>
+    <layout class="QHBoxLayout" name="horizontalLayout">
+     <item>
+      <widget class="QPushButton" name="abbrechen">
+       <property name="text">
+        <string>Abbrechen</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QPushButton" name="speichern">
+       <property name="text">
+        <string>Speichern</string>
+       </property>
+       <property name="default">
+        <bool>true</bool>
+       </property>
+      </widget>
+     </item>
+    </layout>
+   </item>
+  </layout>
+ </widget>
+ <tabstops>
+  <tabstop>name</tabstop>
+  <tabstop>packetList</tabstop>
+  <tabstop>previousePicture</tabstop>
+  <tabstop>nextPicture</tabstop>
+  <tabstop>abbrechen</tabstop>
+  <tabstop>speichern</tabstop>
+ </tabstops>
+ <resources>
+  <include location="icons.qrc"/>
+ </resources>
+ <connections>
+  <connection>
+   <sender>name</sender>
+   <signal>returnPressed()</signal>
+   <receiver>speichern</receiver>
+   <slot>click()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>124</x>
+     <y>43</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>426</x>
+     <y>381</y>
+    </hint>
+   </hints>
+  </connection>
+ </connections>
+</ui>

+ 100 - 0
annotationGUI/classoptions.cpp

@@ -0,0 +1,100 @@
+#include "classoptions.h"
+#include "ui_classoptions.h"
+#include <QListWidget>
+#include <QPushButton>
+#include <QDebug>
+#include <QMessageBox>
+
+// erstellt den DIalog
+//  seq: Die Sequenz, welche momentan annotiert wird
+//  parent: Das Eltern QWidget von Qt
+ClassOptions::ClassOptions( Sequenz *seq, QWidget *parent) :
+    QDialog(parent),
+    ui(new Ui::ClassOptions),
+    seq( seq )
+{
+    ui->setupUi(this);
+    setWindowTitle( "Klassen bearbeiten" );
+    QList< Sequenz::SegmentationClass > classes = seq->getClasses();
+    for( auto c = classes.begin(); c != classes.end(); c++ )
+    {
+        QListWidgetItem *item = new QListWidgetItem( c->name );
+        item->setFlags(item->flags() | Qt::ItemIsEditable);
+        items.append( { c->id, item } );
+        ui->classList->addItem(item);
+    }
+}
+
+ClassOptions::~ClassOptions()
+{
+    delete ui;
+}
+
+// Erstellt eine neue Klasse von Objekten
+void ClassOptions::on_newClass_clicked()
+{
+    QListWidgetItem *item = new QListWidgetItem("New Class");
+    item->setFlags(item->flags() | Qt::ItemIsEditable);
+    int id = seq->addClass(item->text());
+    if( id == -1 )
+    {
+        delete item;
+        QMessageBox messageBox;
+        messageBox.critical(0,"Error","Es existiert bereits eine andere Klasse mit dem Namen '" + item->text() + "'.");
+        messageBox.show();
+        return;
+    }
+    items.append( { id, item } );
+    ui->classList->addItem(item);
+}
+
+// Löscht eine Klasse von Objekten
+void ClassOptions::on_removeClass_clicked()
+{
+    QList<QListWidgetItem*> items = ui->classList->selectedItems();
+    foreach(QListWidgetItem *item, items){
+        if( seq->removeClass( getItemId( item ) ) )
+            ui->classList->removeItemWidget(item);
+        else
+        {
+            QMessageBox messageBox;
+            messageBox.critical(0,"Error","Die Klasse '" + item->text() + " konnte nicht gelöscht werden.");
+            messageBox.show();
+        }
+    }
+}
+
+// Erlaubt das Klicken auf Löschen
+void ClassOptions::on_classList_itemSelectionChanged()
+{
+    ui->removeClass->setEnabled( ui->classList->selectedItems().size() > 0 );
+}
+
+// Benennt die ausgewählte Klasse um
+void ClassOptions::on_classList_itemChanged(QListWidgetItem *item)
+{
+    if( !seq->setClassName( getItemId( item ), item->text() ) )
+    {
+        QMessageBox messageBox;
+        messageBox.critical(0,"Error","Es existiert bereits eine andere Klasse mit dem Namen '" + item->text() + "'.");
+        messageBox.show();
+        item->setText( seq->getClassName( getItemId( item ) ) );
+    }
+}
+
+// Beendet das Verwalten der Klassen
+void ClassOptions::on_fertig_clicked()
+{
+    this->close();
+}
+
+// Gibt den Index einer Klasse aus der Liste zurück
+int ClassOptions::getItemId( QListWidgetItem *item )
+{
+    for( auto i = items.begin(); i != items.end(); i++ )
+    {
+        if( i->item == item )
+            return i->id;
+    }
+    return -1;
+}

+ 60 - 0
annotationGUI/classoptions.h

@@ -0,0 +1,60 @@
+#ifndef CLASSOPTIONS_H
+#define CLASSOPTIONS_H
+
+#include <QDialog>
+#include <QListWidgetItem>
+#include "sequenz.h"
+
+namespace Ui
+{
+class ClassOptions;
+}
+
+/*
+ * Verwaltet den GUI Dialog, welcher zur Verwaltung der Objekt Klassen verwendet
+ *wird
+ */
+class ClassOptions : public QDialog
+{
+    Q_OBJECT
+
+    /*
+     * Ein Eintrag in der Editierbaren Liste mit Klassen
+     */
+    struct ListItem
+    {
+        int              id; // Die Id der Klasse des Eintrags
+        QListWidgetItem *item; // Der Listeneintrag
+    };
+
+public:
+
+    // erstellt den DIalog
+    //  seq: Die Sequenz, welche momentan annotiert wird
+    //  parent: Das Eltern QWidget von Qt
+    explicit ClassOptions(Sequenz *seq,
+                          QWidget *parent = 0);
+    ~ClassOptions();
+
+private slots:
+    // Erstellt eine neue Klasse von Objekten
+    void on_newClass_clicked();
+    // Löscht eine Klasse von Objekten
+    void on_removeClass_clicked();
+    // Erlaubt das Klicken auf Löschen
+    void on_classList_itemSelectionChanged();
+    // Benennt die ausgewählte Klasse um
+    void on_classList_itemChanged(QListWidgetItem *item);
+    // Beendet das Verwalten der Klassen
+    void on_fertig_clicked();
+
+private:
+    // Gibt den Index einer Klasse aus der Liste zurück
+    int getItemId(QListWidgetItem *item);
+
+    Sequenz *seq; // Ein zeiger auf die annotierte Bildsequenz
+    QList<ListItem>   items; // Eine Liste mit Listeneinträgen
+    Ui::ClassOptions *ui; // Ein Zeiger auf alle in classoptions.ui spezifizierten Objekte
+};
+
+#endif // CLASSOPTIONS_H

+ 62 - 0
annotationGUI/classoptions.ui

@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>ClassOptions</class>
+ <widget class="QDialog" name="ClassOptions">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>400</width>
+    <height>480</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Dialog</string>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout_2">
+   <item>
+    <layout class="QHBoxLayout" name="horizontalLayout_2">
+     <property name="spacing">
+      <number>6</number>
+     </property>
+     <property name="bottomMargin">
+      <number>0</number>
+     </property>
+     <item>
+      <widget class="QPushButton" name="newClass">
+       <property name="text">
+        <string>Neue Klasse</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QPushButton" name="removeClass">
+       <property name="enabled">
+        <bool>false</bool>
+       </property>
+       <property name="text">
+        <string>Klasse löschen</string>
+       </property>
+      </widget>
+     </item>
+    </layout>
+   </item>
+   <item>
+    <widget class="QListWidget" name="classList"/>
+   </item>
+   <item>
+    <layout class="QHBoxLayout" name="horizontalLayout">
+     <item>
+      <widget class="QPushButton" name="fertig">
+       <property name="text">
+        <string>Schließen</string>
+       </property>
+      </widget>
+     </item>
+    </layout>
+   </item>
+  </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>

+ 576 - 0
annotationGUI/controller.cpp

@@ -0,0 +1,576 @@
+#include "controller.h"
+#include "arbeitsview.h"
+#include "mainwindow.h"
+#include "kmath.h"
+#include <QRubberBand>
+#include <QDebug>
+
+ArbeitsController *ArbeitsController::controller[ USERMODE_COUNT ];
+
+// gibt den Controller zurück, welcher in einem Bestimmten Modus verwendet
+// wird
+//  m: der Modus, bei dem der gesuchte Controller benutzt wird
+ArbeitsController *ArbeitsController::getController( UserMode m )
+{
+    return controller[ m ];
+}
+
+// Erstellt die Controller, welche bei den verschiedenen Modis benutzt
+// werden sollen
+//  m: Das Modell, welches alle nötigen Daten vür die Arbeitsansicht enthält
+void ArbeitsController::initStaticController( ArbeitsModel *m )
+{
+    controller[ SELECT ] = new HideController( m );
+    controller[ MOVE ] = new MoveController( m );
+    controller[ NEW ] = new NewController( m );
+    controller[ PIPETTE_SELECT ] = new CopyController( m );
+    controller[ PIPETTE_SET ] = new PasteController( m );
+    controller[ DELETE ] = new DeleteController( m );
+    controller[ CUT ] = new CutController( m );
+    controller[ ZOOM_IN ] = new ZoomInController( m );
+}
+
+ArbeitsController::ArbeitsController( ArbeitsModel *m )
+{
+    model = m;
+}
+
+// Setzt den Controller zurück
+void ArbeitsController::restartController()
+{
+    model->notifyViews();
+}
+
+// verwaltet das drehen des Mausrades
+void ArbeitsController::wheelEvent(QWheelEvent *e)
+{
+    if( !model->getFrame() )
+        return;
+    // Ermitteln der View Größe und der Bildgröße
+    QSize s = model->getViewSize();
+    QRect imgRect( QPoint( 0, 0 ), s );
+    QSize imgS = model->getImage().size();
+    if( imgS.width() > s.width() )
+    {
+        imgS.scale( s, Qt::KeepAspectRatio );
+        imgRect.setSize( imgS );
+    }
+    QPoint bottomRight = model->inverseTranslate( imgRect.bottomRight() );
+    int xOffset = model->getXOffset(); // Aktualisieren des aus dem Bild angezeigten gebietes
+    int yOffset = model->getYOffset();
+    xOffset += (int)( e->angleDelta().y() * (imgRect.width() / 500.0) ) / 2;
+    yOffset += (int)( e->angleDelta().y() * (imgRect.height() / 500.0) ) / 2;
+    QPoint nBottomRight( bottomRight.x() - (int)( e->angleDelta().y() * (imgRect.width() / 500.0) ) / 2,
+                         bottomRight.y() - (int)( e->angleDelta().y() * (imgRect.height() / 500.0) ) / 2 );
+    if( e->angleDelta().y() > 0 )
+    {
+        QPoint center( model->getViewPos().x() + model->getViewSize().width() / 2, model->getViewPos().y() + model->getViewSize().height() / 2 );
+        QPoint move = e->pos() - center;
+        xOffset += move.x();
+        yOffset += move.y();
+        nBottomRight.setX( nBottomRight.x() + move.x() );
+        nBottomRight.setY( nBottomRight.y() + move.y() );
+    }
+    if( xOffset < 0 )
+    {
+        nBottomRight.setX( nBottomRight.x() - xOffset );
+        xOffset -= xOffset;
+    }
+    if( yOffset < 0 )
+    {
+        nBottomRight.setY( nBottomRight.y() - yOffset );
+        yOffset -= yOffset;
+    }
+    if( nBottomRight.x() > model->getImage().width() )
+    {
+        xOffset -= nBottomRight.x() - model->getImage().width();
+        nBottomRight.setX( model->getImage().width() );
+    }
+    if( nBottomRight.y() > model->getImage().height() )
+    {
+        yOffset -= nBottomRight.y() - model->getImage().height();
+        nBottomRight.setY( model->getImage().height() );
+    }
+    if( xOffset >= nBottomRight.x() || yOffset >= nBottomRight.y() )
+        return; // abbruch
+    // Aktualisieren der Sklallierungsfaktoren
+    if( xOffset < 0 || yOffset < 0 )
+    {
+        xOffset = 0;
+        yOffset = 0;
+        model->setScaleFactor(imgRect.width() / (float)model->getImage().width(), imgRect.height() / (float)model->getImage().height());
+        model->notifyViews();
+        return;
+    }
+    model->setScaleFactor( imgRect.width() / (float)(nBottomRight.x() - xOffset), imgRect.height() / (float)(nBottomRight.y() - yOffset));
+    model->setOffset( xOffset, yOffset );
+    model->notifyViews();
+}
+
+// verwaltet das Drücken einer Maustaste
+void ArbeitsController::mousePressEvent( QMouseEvent *e )
+{
+    model->setMouseButtonPressed( e->button(), e->pos() );
+    model->notifyViews();
+}
+
+// verwaltet das Bewegen der Maus
+void ArbeitsController::mouseMoveEvent( QMouseEvent *e )
+{
+    if( model->isMouseButtonPressed( Qt::MidButton ) )
+    { // Falls die mittlere Masutaste gedrückt ist, kann der nutzer den angezeigten Bereich des Bildes verschieben
+        QPoint diff = e->pos() - model->getMousePoint();
+        diff = QPoint( (int)(diff.x() / model->getXScaleFactor()) * 5, (int)(diff.y() / model->getYScaleFactor()) * 5 );
+        if( diff != QPoint( 0, 0 ) )
+        {
+            QSize s = model->getViewSize();
+            QRect imgRect( QPoint( 0, 0 ), s );
+            QSize imgS = model->getImage().size();
+            if( imgS.width() > s.width() )
+            {
+                imgS.scale( s, Qt::KeepAspectRatio );
+                imgRect.setSize( imgS );
+            }
+            QPoint bottomRight = model->inverseTranslate( imgRect.bottomRight() );
+            int xOffset = model->getXOffset(), yOffset = model->getYOffset();
+            bottomRight.setX( bottomRight.x() + diff.x() );
+            bottomRight.setY( bottomRight.y() + diff.y() );
+            xOffset += diff.x();
+            yOffset += diff.y();
+            if( xOffset < 0 )
+            {
+                bottomRight.setX( bottomRight.x() - xOffset );
+                xOffset -= xOffset;
+            }
+            if( yOffset < 0 )
+            {
+                bottomRight.setY( bottomRight.y() - yOffset );
+                yOffset -= yOffset;
+            }
+            if( bottomRight.x() > model->getImage().width() )
+            {
+                xOffset -= bottomRight.x() - model->getImage().width();
+                bottomRight.setX( model->getImage().width() );
+            }
+            if( bottomRight.y() > model->getImage().height() )
+            {
+                yOffset -= bottomRight.y() - model->getImage().height();
+                bottomRight.setY( model->getImage().height() );
+            }
+            if( xOffset >= bottomRight.x() || yOffset >= bottomRight.y() )
+                return;
+            model->setOffset( xOffset, yOffset );
+            model->notifyViews();
+        }
+    }
+    model->setMousePoint( e->pos() );
+    model->notifyViews();
+}
+
+// verwaltet das Loslassen einer Maustaste
+void ArbeitsController::mouseReleaseEvent( QMouseEvent *e )
+{
+    model->setMousePressed( false );
+    model->notifyViews();
+}
+
+
+/*
+ * Controller für den SELECT Modus
+ */
+
+HideController::HideController( ArbeitsModel *m )
+    : ArbeitsController( m )
+{}
+
+void HideController::mouseReleaseEvent( QMouseEvent *e )
+{ // Verstecken oder sichtbarmachen des Objektes, auf das geklickt wurde
+    if( model->isMouseButtonPressed( Qt::LeftButton ) && e )
+    {
+        model->getFrame()->selectObjectAt( model->inverseTranslate( e->pos() ) );
+        model->getWindow()->setFrameTreeSelection();
+    }
+    ArbeitsController::mouseReleaseEvent( e );
+}
+
+/*
+ * Controller für den MOVE Modus
+ */
+
+MoveController::MoveController( ArbeitsModel *m )
+    : ArbeitsController( m )
+{}
+
+void MoveController::mousePressEvent( QMouseEvent *e )
+{ // Auswahl des Punktes der verschoben werden soll
+    if( e->button() == Qt::LeftButton && !model->getMoveObject().isNull() && model->getInsertIndex() >= 0 )
+    {
+        model->getMoveObject()->insertVertex( model->getInsertIndex(), model->getNewVertex(), model->getMovePolygon() );
+        model->setMoveIndex( model->getInsertIndex() );
+        model->setInsertIndex( -1 );
+    }
+    ArbeitsController::mousePressEvent( e );
+}
+
+void MoveController::mouseMoveEvent( QMouseEvent *e )
+{ // Verschieben des ausgewählten Punktes und findes des der Maus nächstgelegenen Punktes
+    if( e )
+    {
+        if( !model->isMousePressed() )
+        { // findes des der Maus nächstgelegenen Punktes
+            bool foundNp = false;
+            QPoint mousePos = model->inverseTranslate(e->pos());
+            double mdiff = 0;
+            for( ObjectPolygon obj : model->getFrame()->getObjects() )
+            {
+                if( obj->isSelected() )
+                {
+                    int pIndex = 0;
+                    for( QPolygon o : obj->getPolygonList() )
+                    {
+                        QPoint last = o.last();
+                        int index = 0;
+                        for( QPoint point : o )
+                        {
+                            QPoint tmp = Math::getNearestPointOnLine( mousePos, point, last );
+                            QPoint dir = mousePos - tmp;
+                            int tmpDiff = dir.x() * dir.x() + dir.y() * dir.y();
+                            if( !foundNp || tmpDiff < mdiff )
+                            {
+                                if( Math::diffSquare( tmp, point ) < 500 )
+                                {
+                                    model->setMoveObject( obj, pIndex );
+                                    model->setMoveIndex( index );
+                                    model->setInsertIndex( -1 );
+                                }
+                                else if( Math::diffSquare( tmp, last ) < 500 )
+                                {
+                                    model->setMoveObject( obj, pIndex );
+                                    model->setMoveIndex( index - 1 );
+                                    model->setInsertIndex( -1 );
+                                    if( index - 1 < 0 )
+                                        model->setMoveIndex( o.count() - 1 );
+                                }
+                                else
+                                {
+                                    model->setMoveObject( obj, pIndex );
+                                    model->setInsertIndex( index );
+                                    model->setMoveIndex( -1 );
+                                    model->setNewVertex( tmp );
+                                }
+                                foundNp = true;
+                                mdiff = tmpDiff;
+                            }
+                            last = point;
+                            index++;
+                        }
+                        pIndex++;
+                    }
+                }
+            }
+            if( !foundNp || mdiff > 500 )
+            {
+                model->setInsertIndex( -1 );
+                model->setMoveIndex( -1 );
+            }
+        }
+        // move
+        if( model->isMouseButtonPressed( Qt::LeftButton ) && model->getMoveIndex() >= 0 && !model->getMoveObject().isNull() )
+        {
+            model->getMoveObject()->moveVertex( model->getMoveIndex(), model->inverseTranslate( e->pos() ), model->getImage().size(), model->getMovePolygon() );
+        }
+    }
+    ArbeitsController::mouseMoveEvent( e );
+}
+
+/*
+ * Controller für den CUT Modus
+ */
+
+CutController::CutController( ArbeitsModel *m )
+    : ArbeitsController( m )
+{}
+
+void CutController::mouseReleaseEvent( QMouseEvent *e )
+{
+    if( model->isMouseButtonPressed( Qt::LeftButton ) && e )
+    { // Auswahl der Punkte an denen das Objekt zerschnitten werden soll
+        QPoint pos = model->inverseTranslate( e->pos() );
+        QList< ObjectPolygon > objects = model->getFrame()->getObjects();
+        if( model->getCutObject().isNull() )
+        { // Auswahl des ersten Punktes
+            for( auto p = objects.begin(); p != objects.end(); p++ )
+            {
+                int pIndex = 0;
+                for( QPolygon pol : (*p)->getPolygonList() )
+                {
+                    int index = 0;
+                    for( QPoint point : pol )
+                    {
+                        if( pos.x() > point.x() - 10 && pos.x() < point.x() + 10 && pos.y() > point.y() - 10 && pos.y() < point.y() + 10 )
+                        {
+                            model->setCutObject( *p, pIndex );
+                            model->setCutIndex( index );
+                        }
+                        index++;
+                    }
+                    if( !model->getCutObject().isNull() )
+                        break;
+                    pIndex++;
+                }
+            }
+        }
+        else
+        { // Auswahl des zweiten Punktes
+            if( !model->getCutObject().isNull() && model->getFrame() )
+            {
+                int index = 0;
+                foreach( QPoint point, model->getCutObject()->getPolygonList().at( model->getCutPolygon() ) )
+                {
+                    if( pos.x() > point.x() - 10 && pos.x() < point.x() + 10 && pos.y() > point.y() - 10 && pos.y() < point.y() + 10 )
+                        break;
+                    index++;
+                }
+                if( index >= model->getCutIndex() - 1 && index <= model->getCutIndex() + 1 )
+                    return;
+                if( ( model->getCutIndex() == 0 && index == model->getCutObject()->getPolygonList().at( model->getCutPolygon() ).size() - 1 ) ||
+                        ( index == 0 && model->getCutIndex() == model->getCutObject()->getPolygonList().at( model->getCutPolygon() ).size() - 1 ) ||
+                        index == model->getCutObject()->getPolygonList().at( model->getCutPolygon() ).size() )
+                    return;
+                // Zerschneiden des Objektes an den zwei eckpunkten
+                model->getFrame()->splitObject( model->getCutObject(), model->getCutIndex(), index, model->getCutPolygon() );
+                model->setCutObject( ObjectPolygon(), 0 );
+                model->setCutIndex( -1 );
+                model->getWindow()->setupFrameTree();
+            }
+        }
+    }
+    ArbeitsController::mouseReleaseEvent( e );
+}
+
+
+/*
+ * Controller für den PIPETTE_SELECT Modus
+ */
+
+CopyController::CopyController( ArbeitsModel *m )
+    : ArbeitsController( m )
+{}
+
+void CopyController::mouseReleaseEvent( QMouseEvent *e )
+{ // Auswahl des Objektes, welches kopiert werden soll
+    if( model->isMouseButtonPressed( Qt::LeftButton ) && e )
+    {
+        if( model->getFrame() )
+        {
+            int pIndex;
+            model->setCopyedObject( model->getFrame()->getObjectAt( model->inverseTranslate( e->pos() ), pIndex ), model->inverseTranslate( e->pos() ) );
+            model->setCopyedRotation( 0 );
+            if( !model->getCopyedObject().isNull() )
+                model->getWindow()->setMode( PIPETTE_SET );
+        }
+    }
+    ArbeitsController::mouseReleaseEvent( e );
+}
+
+/*
+ * Controller für den PIPETTE_SET Modus
+ */
+
+PasteController::PasteController( ArbeitsModel *m )
+    : ArbeitsController( m )
+{}
+
+void PasteController::mouseMoveEvent( QMouseEvent *e )
+{ // Setzen der Position und der Rotierung des kopierten Objektes
+    if( model->isMouseButtonPressed( Qt::RightButton ) )
+    {
+        if( !model->getCopyedObject().isNull() )
+            model->setCopyedRotation( model->getCopyedRotation() + e->pos().x() - model->getMousePoint().x() );
+    }
+    ArbeitsController::mouseMoveEvent( e );
+}
+
+void PasteController::mouseReleaseEvent( QMouseEvent *e )
+{ // Einfpgen des Objektes oder der Objekt-ID
+    if( model->isMouseButtonPressed( Qt::LeftButton ) && e )
+    {
+        if( model->getFrame() )
+        {
+            model->getFrame()->setObjectAt( model->inverseTranslate( e->pos() ), model->getCopyedObject(), model->getCopyedCenter(), model->getCopyedRotation() );
+            model->getWindow()->setupFrameTree();
+            int pIndex;
+            model->setCopyedObject( model->getFrame()->getObjectAt( model->inverseTranslate( e->pos() ), pIndex ), model->inverseTranslate( e->pos() ) );
+            model->setCopyedRotation( 0 );
+        }
+    }
+    ArbeitsController::mouseReleaseEvent( e );
+}
+
+/*
+ * Controller für den NEW Modus
+ */
+
+NewController::NewController( ArbeitsModel *m )
+    : ArbeitsController( m )
+{}
+
+void NewController::mouseReleaseEvent( QMouseEvent *e )
+{
+    if( model->isMouseButtonPressed( Qt::LeftButton ) && e )
+    { // Hinzufügen von Punkten zum neuen Polygon
+        if( !model->getNewPolygon() )
+            model->setNewPolygon( new QPolygon() );
+        QPoint pos = model->inverseTranslate( e->pos() );
+        if( model->getNewPolygon()->size() > 3 &&
+            pos.x() > model->getNewPolygon()->begin()->x() - 10 && pos.x() < model->getNewPolygon()->begin()->x() + 10 &&
+            pos.y() > model->getNewPolygon()->begin()->y() - 10 && pos.y() < model->getNewPolygon()->begin()->y() + 10 )
+        { // Polygon ist fertig und wird zur Sequenz hinzugefügt
+            QList<QPolygon> polygons;
+            polygons.append( *model->getNewPolygon() );
+            if( model->getFrame() )
+                model->getFrame()->addObject( "-1", 0, polygons );
+            model->setNewPolygon( 0 );
+            model->getWindow()->setupFrameTree();
+        }
+        else
+            model->getNewPolygon()->append( pos );
+    }
+    if( model->isMouseButtonPressed( Qt::RightButton ) && e )
+    { // Letzten Punkt vom Polygon entfernen
+        if( model->getNewPolygon() )
+        {
+            if( model->getNewPolygon()->size() == 1 )
+                model->setNewPolygon( 0 );
+            else
+                model->getNewPolygon()->pop_back();
+        }
+    }
+    ArbeitsController::mouseReleaseEvent( e );
+}
+
+void NewController::restartController()
+{ // Neues Polygon beenden und zur Sequenz hinzufügen
+    if( model->getNewPolygon() && model->getNewPolygon()->size() >= 3 )
+    {
+        QList<QPolygon> polygons;
+        polygons.append( *model->getNewPolygon() );
+        model->getFrame()->addObject( "-1", 0, polygons );
+        model->setNewPolygon( 0 );
+        model->getWindow()->setupFrameTree();
+        ArbeitsController::restartController();
+    }
+}
+
+/*
+ * Controller für den DELETE Modus
+ */
+
+DeleteController::DeleteController( ArbeitsModel *m )
+    : ArbeitsController( m )
+{}
+
+void DeleteController::mousePressEvent( QMouseEvent *e )
+{ // Setzt den Beginn des auswahl feldes
+    if( e && e->button() == Qt::LeftButton )
+    {
+        if (!model->getDeleteField() )
+            model->setDeleteField( new QRect(e->pos(), QSize()) );
+    }
+    ArbeitsController::mousePressEvent( e );
+}
+
+void DeleteController::mouseMoveEvent( QMouseEvent *e )
+{ // Setze das Ende des Auswahl Feldes
+    if( e && model->isMouseButtonPressed( Qt::LeftButton ) && model->getDeleteField() )
+    {
+        *model->getDeleteField() = QRect( model->getMousePressPoint(), e->pos() ).normalized();
+    }
+    ArbeitsController::mouseMoveEvent( e );
+}
+
+void DeleteController::mouseReleaseEvent( QMouseEvent *e )
+{ // Lösche alle Eckpunkte im ausgewählten Feld
+    if( model->isMouseButtonPressed( Qt::LeftButton ) && e && model->getDeleteField() && model->getFrame() )
+    {
+        if( model->getDeleteField()->size() == QSize( 0, 0 ) )
+            model->getFrame()->selectObjectAt( model->inverseTranslate( e->pos() ) );
+        else
+        {
+            QRect field = *model->getDeleteField();
+            model->getFrame()->removeSelectedVertices(QRect( model->inverseTranslate( field.topLeft() ), model->inverseTranslate( field.bottomRight() ) ));
+            delete model->getDeleteField();
+            model->setDeleteField( 0 );
+            model->getWindow()->setupFrameTree();
+        }
+    }
+    ArbeitsController::mouseReleaseEvent( e );
+}
+
+/*
+ * Controller für den ZOOM_IN Modus
+ */
+
+ZoomInController::ZoomInController( ArbeitsModel *m )
+    : ArbeitsController( m )
+{}
+
+void ZoomInController::mousePressEvent( QMouseEvent *e )
+{ // Setzte den Startpunkt des sichtbaren Gebiets
+    if( e->button() == Qt::LeftButton )
+    {
+        if (!model->getDeleteField() )
+            model->setDeleteField( new QRect(e->pos(), QSize()) );
+    }
+    ArbeitsController::mousePressEvent( e );
+}
+
+void ZoomInController::mouseMoveEvent( QMouseEvent *e )
+{ // Setzte den Endpunkt des sichtbaren Gebiets
+    if( model->isMouseButtonPressed( Qt::LeftButton ) && model->getDeleteField() && e )
+    {
+        *model->getDeleteField() = QRect( model->getMousePressPoint(), e->pos() ).normalized();
+    }
+    ArbeitsController::mouseMoveEvent( e );
+}
+
+void ZoomInController::mouseReleaseEvent( QMouseEvent *e )
+{ // Aktualisiere die Ansicht auf das ausgewählte Gebiet
+    if( model->isMouseButtonPressed( Qt::LeftButton ) && e && model->getFrame() && model->getDeleteField() )
+    {
+        if( model->getDeleteField()->size() == QSize( 0, 0 ) )
+            model->getFrame()->selectObjectAt( model->inverseTranslate( e->pos() ) );
+        else
+        {
+            QRect field = *model->getDeleteField();
+            QPoint leftTop = model->inverseTranslate( field.topLeft() );
+            int xOffset = leftTop.x(), yOffset = leftTop.y();
+            if( xOffset < 0 )
+                xOffset = 0;
+            if( yOffset < 0 )
+                yOffset = 0;
+            QPoint bottomRight = model->inverseTranslate( field.bottomRight() );
+            if( bottomRight.x() >= model->getImage().width() )
+                bottomRight.setX( model->getImage().width() );
+            if( bottomRight.y() >= model->getImage().height() )
+                bottomRight.setY( model->getImage().height() );
+            if( bottomRight.x() > xOffset && bottomRight.y() > yOffset )
+            {
+                QSize s = model->getViewSize();
+                QRect imgRect( QPoint( 0, 0 ), s );
+                QSize imgS = model->getImage().size();
+                if( imgS.width() > s.width() )
+                {
+                    imgS.scale( s, Qt::KeepAspectRatio );
+                    imgRect.setSize( imgS );
+                }
+                model->setScaleFactor( imgRect.width() / (float)(bottomRight.x() - xOffset), imgRect.height() / (float)(bottomRight.y() - yOffset));
+                model->setOffset( xOffset, yOffset );
+            }
+            delete model->getDeleteField();
+            model->setDeleteField( 0 );
+            model->getWindow()->setupFrameTree();
+        }
+    }
+    ArbeitsController::mouseReleaseEvent( e );
+}

+ 157 - 0
annotationGUI/controller.h

@@ -0,0 +1,157 @@
+#ifndef CONTROLLER_H
+#define CONTROLLER_H
+
+#include <QWheelEvent>
+
+#include "usermode.h"
+
+class ArbeitsModel;
+
+/*
+ * Dieser Controller verwaltet die Benutzereingaben in der Arbeitsfläche der GUI
+ */
+class ArbeitsController
+{
+protected:
+    ArbeitsController(ArbeitsModel *m);
+
+    ArbeitsModel *model; // Ein Zeiger auf das ArbeitsModel, welches die Daten der ArbeitsView enthält
+
+private:
+
+    static ArbeitsController *controller[USERMODE_COUNT]; // Eine Liste mit allen Controller Objekten, die es gibt
+
+public:
+    // Setzt den Controller zurück
+    virtual void restartController();
+
+    // verwaltet das drehen des Mausrades
+    virtual void wheelEvent(QWheelEvent *e);
+
+    // verwaltet das Drücken einer Maustaste
+    virtual void mousePressEvent(QMouseEvent *e);
+
+    // verwaltet das Bewegen der Maus
+    virtual void mouseMoveEvent(QMouseEvent *e);
+
+    // verwaltet das Loslassen einer Maustaste
+    virtual void mouseReleaseEvent(QMouseEvent *e);
+
+    // gibt den Controller zurück, welcher in einem Bestimmten Modus verwendet
+    // wird
+    //  m: der Modus, bei dem der gesuchte Controller benutzt wird
+    static ArbeitsController* getController(UserMode m);
+
+    // Erstellt die Controller, welche bei den verschiedenen Modis benutzt
+    // werden sollen
+    //  m: Das Modell, welches alle nötigen Daten vür die Arbeitsansicht enthält
+    static void initStaticController(ArbeitsModel *m);
+};
+
+/*
+ * Controller für den SELECT Modus
+ */
+class HideController : public ArbeitsController
+{
+public:
+
+    HideController(ArbeitsModel *m);
+
+    void mouseReleaseEvent(QMouseEvent *e) override;
+};
+
+/*
+ * Controller für den MOVE Modus
+ */
+class MoveController : public ArbeitsController
+{
+public:
+
+    MoveController(ArbeitsModel *m);
+
+    void mousePressEvent(QMouseEvent *e) override;
+    void mouseMoveEvent(QMouseEvent *e) override;
+
+private:
+};
+
+/*
+ * Controller für den CUT Modus
+ */
+class CutController : public ArbeitsController
+{
+public:
+
+    CutController(ArbeitsModel *m);
+
+    void mouseReleaseEvent(QMouseEvent *e) override;
+};
+
+/*
+ * Controller für den PIPETTE_SELECT Modus
+ */
+class CopyController : public ArbeitsController
+{
+public:
+
+    CopyController(ArbeitsModel *m);
+
+    void mouseReleaseEvent(QMouseEvent *e) override;
+};
+
+/*
+ * Controller für den PIPETTE_SET Modus
+ */
+class PasteController : public ArbeitsController
+{
+public:
+
+    PasteController(ArbeitsModel *m);
+
+    void mouseMoveEvent(QMouseEvent *e) override;
+    void mouseReleaseEvent(QMouseEvent *e) override;
+};
+
+/*
+ * Controller für den NEW Modus
+ */
+class NewController : public ArbeitsController
+{
+public:
+
+    NewController(ArbeitsModel *m);
+
+    void mouseReleaseEvent(QMouseEvent *e) override;
+
+    void restartController() override;
+};
+
+/*
+ * Controller für den DELETE Modus
+ */
+class DeleteController : public ArbeitsController
+{
+public:
+
+    DeleteController(ArbeitsModel *m);
+
+    void mousePressEvent(QMouseEvent *e) override;
+    void mouseMoveEvent(QMouseEvent *e) override;
+    void mouseReleaseEvent(QMouseEvent *e) override;
+};
+
+/*
+ * Controller für den ZOOM_IN Modus
+ */
+class ZoomInController : public ArbeitsController
+{
+public:
+
+    ZoomInController(ArbeitsModel *m);
+
+    void mousePressEvent(QMouseEvent *e) override;
+    void mouseMoveEvent(QMouseEvent *e) override;
+    void mouseReleaseEvent(QMouseEvent *e) override;
+};
+
+#endif // CONTROLLER_H

+ 414 - 0
annotationGUI/frame.cpp

@@ -0,0 +1,414 @@
+#include "frame.h"
+#include "kamera.h"
+#include <QImage>
+#include <QMessageBox>
+#include <QDebug>
+
+
+// Erstellt das Bild
+//  imgPath: Der Pfad zu dem Quellbild
+//  timestamp: Der Zeitpunkt der Aufnahme des Bildes
+//  index: Der Index des Bildes in der Liste aller Bilder der Kamera
+//  kam: Die Kamera, die das Bild aufgenommen hat
+//  needAnnotation: true, falls das Bild noch nicht annotiert wurde
+Frame::Frame( QString imgPath, QString timestamp, int index, Kamera *kam, bool needAnnotation )
+    : FrameTreeNode( index, kam, 1 ),
+      path( imgPath ),
+      timestamp( timestamp ),
+      needAnnotation( needAnnotation ),
+      size( QSize( 0, 0 ) ),
+      needSave( 0 )
+{}
+
+Frame::~Frame()
+{}
+
+// Gibt this zurück
+void *Frame::getNodeObject() const
+{
+    return (void*)this;
+}
+
+// Gibt die Anzahl aller Objekte auf dem Bild zurück
+int Frame::getChildCount() const
+{
+    return objects.size();
+}
+
+// Entfernt alle Eckpunkte in einem Gebit
+//  area: Das Gebiet, in dem die Eckpunkte gelöscht werden sollen
+void Frame::removeSelectedVertices( QRect area )
+{
+    needSave = 1;
+    int anz = objects.size();
+    for( int i = 0; i < anz; i++ )
+    {
+        ObjectPolygon o = objects.at( i );
+        if( o->isSelected() )
+        {
+            if( !o->removeVertices( area ) )
+            {
+                objects.removeAt( i );
+                i--;
+                anz--;
+            }
+        }
+    }
+}
+
+// Löscht ein bestimmtes Objekt
+//  o: Das Objekt, welches gelöscht werden soll
+void Frame::removeObject( ObjectPolygon o )
+{
+    needSave = 1;
+    objects.removeOne( o );
+}
+
+// Fügt ein Objekt zu dem Bild hinzu. Das Objekt wird automatisch am
+// Bildrand abgeschnitten
+//  name: Die ID des Objektes
+//  truncated: true, falls das Objekt abgeschnitten ist
+//  po: Eine Liste mit Polygonen, welche die Umrisse des Objektes sind
+void Frame::addObject( QString id, bool truncated, QList< QPolygon > po )
+{
+    needSave = 1;
+    if( po.size() < 1 )
+        return;
+    if( size != QSize( 0, 0 ) )
+    {
+        QPolygon cliped = clipPolygon( po.at( 0 ) );
+        ObjectPolygon newObject( id, cliped, truncated || cliped.boundingRect() != po.at( 0 ).boundingRect(), objects.size(), this );
+        int pAnz = po.count();
+        for( int i = 1; i < pAnz; i++ )
+        {
+            cliped = clipPolygon( po.at( i ) );
+            if( cliped.boundingRect() != po.at( i ).boundingRect() )
+                newObject->setTruncated( true );
+            newObject->getPolygonList().append( cliped );
+        }
+        objects.append( newObject );
+    }
+    else
+    {
+        ObjectPolygon newObject( id, po.at( 0 ), truncated, objects.size(), this );
+        int pAnz = po.count();
+        for( int i = 1; i < pAnz; i++ )
+            newObject->getPolygonList().append( po.at( i ) );
+        objects.append( newObject );
+    }
+}
+
+// Wendet die Maske auf das Bild an und löscht alle Objekte, welche sich in
+// verbotenen Bereichen befinden
+//  m: die Maske
+void Frame::applyMask( Mask &m )
+{
+    needSave = 1;
+    int count = objects.count();
+    for( int i = 0; i < count; i++ )
+    {
+        int pAnz = objects.at( i )->getPolygonList().count();
+        for( int j = 0; j < pAnz; j++  )
+        {
+            if( !m.isPolygonInside( objects.at( i )->getPolygonList().at( j ) ) )
+            {
+                pAnz--;
+                objects.at( i )->getPolygonList().removeAt( j-- );
+                if( pAnz <= 0 )
+                {
+                    count--;
+                    objects.removeAt( i-- );
+                }
+            }
+        }
+    }
+}
+
+// Spaltet ein Objekt in zwei Teile
+//  object: Das Objekt welches zerteilt werden soll
+//  beginn: Der Index des Start Eckpunktes
+//  end: Der Index des End Eckpunktes
+//  pIndex: Der Index des Polygons, welches zerteilt werden soll
+void Frame::splitObject( ObjectPolygon object, int begin, int end, int pIndex )
+{
+    needSave = 1;
+    QPolygon newP = object->split( begin, end, pIndex );
+    QList< QPolygon > newPols;
+    newPols.append( newP );
+    addObject( object->getId(), object->isTruncated(), newPols );
+}
+
+// gibt den Namen des Bildes zurück
+QString Frame::getName() const
+{
+    return path.mid( path.lastIndexOf( '/' ) + 1 );
+}
+
+// Gibt den Aufnahmezeitpunkt des Bildes zurück
+QString Frame::getTimestamp() const
+{
+    return timestamp;
+}
+
+// Gibt eine Liste mit Objekten zurück, welche sich auf dem Bild befinden
+QList< ObjectPolygon > &Frame::getObjects()
+{
+    return objects;
+}
+
+// Setzt die Objekte, welche sich auf dem Bild befinden. Alle vorherigen
+// Objekte werden entfernt.
+//  objects: die Liste mit den neuen Objekten
+void Frame::setObjects( std::vector< std::vector< cv::Point > > obs )
+{
+    needSave = 1;
+    needAnnotation = false;
+    objects.clear();
+    for( auto p = obs.begin(); p != obs.end(); p++ )
+    {
+        std::vector< cv::Point > pol;
+        cv::approxPolyDP( *p, pol, 15, 1 );
+        if( pol.size() > 2 )
+        {
+            QPolygon polygon;
+            int count = 0;
+            for( auto point = pol.begin(); point != pol.end(); point++, count++ )
+            {
+                polygon.append( QPoint( point->x, point->y ) );
+            }
+            QList< QPolygon > newPols;
+            newPols.append( polygon );
+            addObject( "-1", 0, newPols );
+        }
+    }
+}
+
+// Gibt das geladene Bild als Objekt des Qt Frameworks zurück
+QImage Frame::getImage()
+{
+    QImage img( path );
+    size = img.size();
+    return img;
+}
+
+// Gibt das geladene Bild als Objekt der OpenCV Bibliotek zurück
+cv::Mat Frame::getImageMatrix() const
+{
+    return cv::imread( path.toStdString() );
+}
+
+// Gibt den Bildausschnitt zurück, auf dem ein Objekt komplett sichtbar ist
+//  objectId: die ID des Objektes, welches auf dem Bild sein soll
+QImage Frame::getObjectImage( QString objectId )
+{
+    for( ObjectPolygon o : objects )
+    {
+        if( o->getId() == objectId )
+        {
+            QImage ret = getImage().copy( o->getBoundingBox() );
+            return ret;
+        }
+    }
+    return QImage();
+}
+
+// Gibt den Bildausschnitt zurück, auf dem ein Objekt komplett sichtbar ist
+//  object: das Objekt, welches auf dem Bild sein soll
+QImage Frame::getObjectImage( ObjectPolygon o )
+{
+    QImage ret = getImage().copy( o->getBoundingBox() );
+    return ret;
+}
+
+// Prüft ob ein Objekt auf dem Bild vorhanden ist
+//  id: Die ID des Objektes
+bool Frame::hasObject( QString id )
+{
+    for( ObjectPolygon o : objects )
+    {
+        if( o->getId() == id )
+            return true;
+    }
+    return false;
+}
+
+// Gibt true zurück, falls kein Fehler in der Annotation des Bildes gefunden
+// wurde
+bool Frame::isCorrectAnnotated() const
+{
+    bool good = true;
+    for( ObjectPolygon o1 : objects )
+    {
+        for( ObjectPolygon o2 : objects)
+        {
+            if( o1 == o2 )
+                continue;
+            good &= o2->getId() != o1->getId();
+        }
+        good &= o1->getId() != "-1";
+    }
+    return good;
+}
+
+// Gibt true zurück, falls das Bild noch nicht annotiert wurde
+bool Frame::isNotAnnotated() const
+{
+    return needAnnotation;
+}
+
+// Gibt das Objekt zurück, welches an einer bestimmten Position im Bild ist.
+//  0 falls an der Stelle kein Objekt ist
+//  pos: die Position, an der ein Objekt gesucht werden soll
+//  pIndex: wird auf den Index des Polygons gesetzt, welches an der Stelle
+// ist, falls dort ein Objekt existiert
+ObjectPolygon Frame::getObjectAt( QPoint pos, int &pIndex ) const
+{
+    for( ObjectPolygon o : objects )
+    {
+        pIndex = 0;
+        for( QPolygon pol : o->getPolygonList() )
+        {
+            if( pol.containsPoint( pos, Qt::FillRule::OddEvenFill ) )
+                return o;
+            pIndex++;
+        }
+    }
+    return ObjectPolygon();
+}
+
+// Setzt das Objekt an einer bestimmten Position im Bild.
+// Fals an der Position bereits ein Objekt existiert, so wird nur die Objekt
+// ID gesetzt.
+//  pos: die Position
+//  object: Das neue Objekt
+//  center: Der Mittelpunkt des neuen Objektes
+//  rotation: Die Drehung des neuen Objektes um den Mittelpunkt
+void Frame::setObjectAt( QPoint pos, ObjectPolygon copy, QPoint center, float rotation )
+{
+    needSave = 1;
+    for( ObjectPolygon o : objects )
+    {
+        for( QPolygon pol : o->getPolygonList() )
+        {
+            if( pol.containsPoint( pos, Qt::FillRule::OddEvenFill ) )
+            {
+                bool needQ = false;
+                if( copy->getId() != "-1" )
+                {
+                    for( ObjectPolygon o2 : objects )
+                    {
+                        if( o2 != o && o2->getId() == copy->getId() )
+                            needQ = 1;
+                    }
+                }
+                o->setId( copy->getId() );
+                if( needQ )
+                {
+                    QMessageBox::StandardButton reply = QMessageBox::question(0, "Warnung", "Auf diesem Bild gibt es bereits ein Objekt mit der gleichen Id. Möchten sie beide Objekte vereinen?",
+                                                                              QMessageBox::Yes|QMessageBox::No);
+                    if (reply == QMessageBox::Yes)
+                        connectObjects( copy->getId() );
+                }
+                return;
+            }
+        }
+    }
+    QMatrix m;
+    m.QMatrix::translate( pos.x(), pos.y() );
+    m.rotate( rotation );
+    m.translate( -center.x(), -center.y() );
+    QList< QPolygon > newPols;
+    for( QPolygon p : copy->getPolygonList() )
+        newPols.append( m.map( p ) );
+    bool needQ = false;
+    if( copy->getId() != "-1" )
+    {
+        for( ObjectPolygon o2 : objects )
+        {
+            if( o2->getId() == copy->getId() )
+                needQ = 1;
+        }
+    }
+    addObject( copy->getId(), copy->isTruncated(), newPols );
+    if( needQ )
+    {
+        QMessageBox::StandardButton reply = QMessageBox::question(0, "Warnung", "Auf diesem Bild gibt es bereits ein Objekt mit der gleichen Id. Möchten sie beide Objekte vereinen?",
+                                                                  QMessageBox::Yes|QMessageBox::No);
+        if (reply == QMessageBox::Yes)
+            connectObjects( copy->getId() );
+    }
+}
+
+// Macht ein Objekt an einer bestimmten Stelle sichtbar oder unsichtbar
+//  pos: Die Position
+void Frame::selectObjectAt( QPoint pos )
+{
+    for( ObjectPolygon o : objects )
+    {
+        for( QPolygon pol : o->getPolygonList() )
+        {
+            if( pol.containsPoint( pos, Qt::FillRule::OddEvenFill ) )
+                o->setSelected( !o->isSelected() );
+        }
+    }
+}
+
+// Schneidet ein Polygon am rand des Bildes ab und gibt das Ergebnis zurück
+//  uncliped: Das nicht abgeschnittene Polygon
+QPolygon Frame::clipPolygon( QPolygon uncliped ) const
+{
+    QPolygon cliped = uncliped.intersected( QPolygon( QRect( QPoint( 0, 0 ), size ), true ) );
+    if( !cliped.isEmpty() )
+        cliped.pop_back();
+    return cliped;
+}
+
+// Vereiniegt verschiedene Objekte mit der gleichen Objekt ID zu einem
+//  id: Die ID, deren Objekte vereiniegt werden sollen
+void Frame::connectObjects( QString id )
+{
+    needSave = 1;
+    ObjectPolygon o;
+    int oAnz = objects.count();
+    for( int i = 0; i < oAnz; i++ )
+    {
+        ObjectPolygon o2 = objects.at( i );
+        if( o2 != o && o2->getId() == id )
+        {
+            if( o.isNull() )
+                o = o2;
+            else
+            {
+                o->getPolygonList().append( o2->getPolygonList() );
+                o->setTruncated( o->isTruncated() || o2->isTruncated() );
+                o->setSelected( o->isSelected() || o2->isSelected() );
+                objects.removeAt( i );
+                i--;
+                oAnz--;
+            }
+        }
+    }
+}
+
+// Zerteilt ein Objekt, welches aus mehreren Polygonen besteht
+//  o: Das Objekt welches zerteilt werden soll
+void Frame::disconnectObject( ObjectPolygon o )
+{
+    needSave = 1;
+    for( QPolygon p : o->getPolygonList() )
+    {
+        QList< QPolygon > plist;
+        plist.append( p );
+        addObject( o->getId(), o->isTruncated(), plist );
+    }
+    removeObject( o );
+}
+
+// Gibt 1 zurück, falls die Annotation zu dem Bild gespeichert werden muss
+// und setzt den Flag auf 0, so dass erst nach einer änderung wieder 1 zurückgegeben wird
+bool Frame::wasChangedSinceLastSave()
+{
+    bool ret = needSave;
+    needSave = 0;
+    return ret;
+}

+ 159 - 0
annotationGUI/frame.h

@@ -0,0 +1,159 @@
+#ifndef FRAME_H
+#define FRAME_H
+
+#include <QString>
+#include <QPolygon>
+#include "object.h"
+#include "mask.h"
+#include "frametree.h"
+#include "opencv2/opencv.hpp"
+
+class Kamera;
+
+/*
+ * Verwaltet ein Bild einer Sequenz
+ */
+class Frame : public FrameTreeNode
+{
+private:
+
+    QString path; // Der Pfad zum Bild
+    QList<ObjectPolygon> objects; // Eine Liste mit Annotierten Objekten
+    QString timestamp; // Der Zeitpunkt, zu dem das Bild aufgenommen wurde
+    QSize   size; // Die Größe des Bildes
+    bool    needAnnotation; // 1, falls das Bild noch nicht vom Nutzer annotiert wurde
+    bool    needSave; // 1, falls die Annotationen auf dem Bild seit dem letzten Speichern verändert wurden
+
+public:
+
+    // Erstellt das Bild
+    //  imgPath: Der Pfad zu dem Quellbild
+    //  timestamp: Der Zeitpunkt der Aufnahme des Bildes
+    //  index: Der Index des Bildes in der Liste aller Bilder der Kamera
+    //  kam: Die Kamera, die das Bild aufgenommen hat
+    //  needAnnotation: true, falls das Bild noch nicht annotiert wurde
+    Frame(QString imgPath,
+          QString timestamp,
+          int     index,
+          Kamera *kam,
+          bool    needAnnotation);
+    ~Frame();
+
+    // Gibt this zurück
+    void* getNodeObject() const override;
+
+    // Gibt die Anzahl aller Objekte auf dem Bild zurück
+    int   getChildCount() const override;
+
+    // Entfernt alle Eckpunkte in einem Gebit
+    //  area: Das Gebiet, in dem die Eckpunkte gelöscht werden sollen
+    void removeSelectedVertices(QRect area);
+
+    // Löscht ein bestimmtes Objekt
+    //  o: Das Objekt, welches gelöscht werden soll
+    void removeObject(ObjectPolygon o);
+
+    // Fügt ein Objekt zu dem Bild hinzu. Das Objekt wird automatisch am
+    // Bildrand abgeschnitten
+    //  name: Die ID des Objektes
+    //  truncated: true, falls das Objekt abgeschnitten ist
+    //  po: Eine Liste mit Polygonen, welche die Umrisse des Objektes sind
+    void addObject(QString        name,
+                   bool           truncated,
+                   QList<QPolygon>po);
+
+    // Wendet die Maske auf das Bild an und löscht alle Objekte, welche sich in
+    // verbotenen Bereichen befinden
+    //  m: die Maske
+    void applyMask(Mask& m);
+
+    // Spaltet ein Objekt in zwei Teile
+    //  object: Das Objekt welches zerteilt werden soll
+    //  beginn: Der Index des Start Eckpunktes
+    //  end: Der Index des End Eckpunktes
+    //  pIndex: Der Index des Polygons, welches zerteilt werden soll
+    void splitObject(ObjectPolygon object,
+                     int            begin,
+                     int            end,
+                     int            pIndex);
+
+    // gibt den Namen des Bildes zurück
+    QString                 getName() const;
+
+    // Gibt den Aufnahmezeitpunkt des Bildes zurück
+    QString                 getTimestamp() const;
+
+    // Gibt eine Liste mit Objekten zurück, welche sich auf dem Bild befinden
+    QList<ObjectPolygon>& getObjects();
+
+    // Setzt die Objekte, welche sich auf dem Bild befinden. Alle vorherigen
+    // Objekte werden entfernt.
+    //  objects: die Liste mit den neuen Objekten
+    void    setObjects(std::vector<std::vector<cv::Point> >objects);
+
+    // Gibt das geladene Bild als Objekt des Qt Frameworks zurück
+    QImage  getImage();
+
+    // Gibt das geladene Bild als Objekt der OpenCV Bibliotek zurück
+    cv::Mat getImageMatrix() const;
+
+    // Gibt den Bildausschnitt zurück, auf dem ein Objekt komplett sichtbar ist
+    //  objectId: die ID des Objektes, welches auf dem Bild sein soll
+    QImage getObjectImage(QString objectId);
+
+    // Gibt den Bildausschnitt zurück, auf dem ein Objekt komplett sichtbar ist
+    //  object: das Objekt, welches auf dem Bild sein soll
+    QImage getObjectImage(ObjectPolygon object);
+
+    // Prüft ob ein Objekt auf dem Bild vorhanden ist
+    //  id: Die ID des Objektes
+    bool hasObject(QString id);
+
+    // Gibt true zurück, falls kein Fehler in der Annotation des Bildes gefunden
+    // wurde
+    bool isCorrectAnnotated() const;
+
+    // Gibt true zurück, falls das Bild noch nicht annotiert wurde
+    bool isNotAnnotated() const;
+
+    // Gibt das Objekt zurück, welches an einer bestimmten Position im Bild ist.
+    //  0 falls an der Stelle kein Objekt ist
+    //  pos: die Position, an der ein Objekt gesucht werden soll
+    //  pIndex: wird auf den Index des Polygons gesetzt, welches an der Stelle
+    // ist, falls dort ein Objekt existiert
+    ObjectPolygon getObjectAt(QPoint pos,
+                               int  & pIndex) const;
+
+    // Setzt das Objekt an einer bestimmten Position im Bild.
+    // Fals an der Position bereits ein Objekt existiert, so wird nur die Objekt
+    // ID gesetzt.
+    //  pos: die Position
+    //  object: Das neue Objekt
+    //  center: Der Mittelpunkt des neuen Objektes
+    //  rotation: Die Drehung des neuen Objektes um den Mittelpunkt
+    void setObjectAt(QPoint pos,
+                     ObjectPolygon object,
+                     QPoint center = QPoint(0, 0),
+                     float rotation = 0);
+
+    // Macht ein Objekt an einer bestimmten Stelle sichtbar oder unsichtbar
+    //  pos: Die Position
+    void selectObjectAt(QPoint pos);
+
+    // Schneidet ein Polygon am rand des Bildes ab und gibt das Ergebnis zurück
+    //  uncliped: Das nicht abgeschnittene Polygon
+    QPolygon clipPolygon(QPolygon uncliped) const;
+
+    // Vereiniegt verschiedene Objekte mit der gleichen Objekt ID zu einem
+    //  id: Die ID, deren Objekte vereiniegt werden sollen
+    void connectObjects(QString id);
+
+    // Zerteilt ein Objekt, welches aus mehreren Polygonen besteht
+    //  o: Das Objekt welches zerteilt werden soll
+    void disconnectObject( ObjectPolygon o );
+    // Gibt 1 zurück, falls die Annotation zu dem Bild gespeichert werden muss
+    // und setzt den Flag auf 0, so dass erst nach einer änderung wieder 1 zurückgegeben wird
+    bool wasChangedSinceLastSave();
+};
+
+#endif // FRAME_H

+ 45 - 0
annotationGUI/frametree.cpp

@@ -0,0 +1,45 @@
+#include "frametree.h"
+
+// Erstellt den Knoten
+//  index: Die Position des Knotens in der Liste mit Kindknoten des
+// Elternknotens
+//  parent: Der Elternknoten
+//  depth: Der Abstand des Knotens zur Baumwurzel
+FrameTreeNode::FrameTreeNode( int index, FrameTreeNode *parent, int depth )
+    : index( index ),
+      parent( parent ),
+      depth( depth )
+{
+}
+
+FrameTreeNode::~FrameTreeNode()
+{
+    index = -1;
+    parent = 0;
+    depth = -1;
+}
+
+// Gibt den Index des Knotens in der Liste mit Kindknoten des Elternknotens
+// zurück
+int FrameTreeNode::getIndex() const
+{
+    return index;
+}
+
+// Gibt den Elternknoten zurück
+FrameTreeNode *FrameTreeNode::getParent() const
+{
+    return parent;
+}
+
+// Gibt den Abstand zur Wurzel des Baumes zurück
+int FrameTreeNode::getDepth() const
+{
+    return depth;
+}
+
+// Gibt die Anzahl an Kindknoten zurück
+int FrameTreeNode::getChildCount() const
+{
+    return 0;
+}

+ 45 - 0
annotationGUI/frametree.h

@@ -0,0 +1,45 @@
+#ifndef FRAMETREE_H
+#define FRAMETREE_H
+
+/*
+ * Eine Abstrakte Basisklasse für einen Knoten in dem Objekt Baum der GUI
+ */
+class FrameTreeNode
+{
+protected:
+
+    int index; // Der Index des Knotens in der Liste aller Kindknoten des Elternknotens
+    FrameTreeNode *parent; // Der Elternknoten
+    int depth; // Die Tieve des Knotens im Baum
+
+public:
+
+    // Erstellt den Knoten
+    //  index: Die Position des Knotens in der Liste mit Kindknoten des
+    // Elternknotens
+    //  parent: Der Elternknoten
+    //  depth: Der Abstand des Knotens zur Baumwurzel
+    FrameTreeNode(int            index,
+                  FrameTreeNode *parent,
+                  int            depth);
+    ~FrameTreeNode();
+
+    // Gibt den Index des Knotens in der Liste mit Kindknoten des Elternknotens
+    // zurück
+    int            getIndex() const;
+
+    // Gibt den Elternknoten zurück
+    FrameTreeNode* getParent() const;
+
+    // Gibt den Abstand zur Wurzel des Baumes zurück
+    int            getDepth() const;
+
+    // Gibt die Anzahl an Kindknoten zurück
+    virtual int    getChildCount() const;
+
+    // Gibt den aktuellen Knoten zurück (this Zeiger auf das Objekt der Klasse
+    // welche von dieser erbt)
+    virtual void*  getNodeObject() const = 0;
+};
+
+#endif // FRAMETREE_H

+ 188 - 0
annotationGUI/frametreemodel.cpp

@@ -0,0 +1,188 @@
+#include "frametreemodel.h"
+#include <QDebug>
+#include "object.h"
+#include <QIcon>
+
+FrameTreeModel::FrameTreeModel()
+    : QAbstractItemModel(),
+      seq( 0 )
+{
+
+}
+
+FrameTreeModel::~FrameTreeModel()
+{
+    if( seq )
+        seq->refRelease();
+}
+
+// Prüft, ob ein Index valide ist
+bool FrameTreeModel::isIndexValid( const QModelIndex &i ) const
+{
+    return i.internalPointer() != 0 && ((FrameTreeNode*)i.internalPointer())->getDepth() <= 2 &&
+            ((FrameTreeNode*)i.internalPointer())->getDepth() >= 0 &&
+            ((FrameTreeNode*)i.internalPointer())->getIndex() >= 0;
+}
+
+// Setzt die Sequenz, welche momenatan annotiert wird
+//  s: die neue Sequenz
+void FrameTreeModel::setSequenz( Sequenz *s )
+{
+    if( seq )
+        seq->refRelease();
+    seq = s;
+    this->dataChanged( createIndex( 0, 0 ), createIndex( 1, 1 ) );
+}
+
+// Gibt zurück, was für einen bestimten Knoten angezeigt werden soll
+//  index: Der Index des Knotens
+//  role: Welche daten des Knotens abgefragt werden sollen (z.B.:Name oder Hintergrundfarbe...)
+QVariant FrameTreeModel::data(const QModelIndex &index, int role) const
+{
+    switch( role )
+    {
+    case Qt::DisplayRole:
+        if (!index.isValid() || role != Qt::DisplayRole || seq == 0 )
+            return QVariant();
+        if( ((FrameTreeNode*)index.internalPointer())->getDepth() == 0 && index.column() == 0 )
+            return QVariant( ((Kamera*)((FrameTreeNode*)index.internalPointer()))->getName() );
+        if( ((FrameTreeNode*)index.internalPointer())->getDepth() == 1 )
+            return QVariant( ((Frame*)index.internalPointer())->getName() );
+        if( ((FrameTreeNode*)index.internalPointer())->getDepth() == 2 && index.column() == 0 )
+        {
+            QString objectId = ((_ObjectPolygon*)((FrameTreeNode*)index.internalPointer()))->getId();
+            return QVariant( objectId + " " + seq->getClassName( seq->getClassOfObject( objectId ) ) );
+        }
+    case Qt::DecorationRole:
+        if( ((FrameTreeNode*)index.internalPointer())->getDepth() == 1 )
+        {
+            if( !((Frame*)index.internalPointer())->isCorrectAnnotated() )
+                return QIcon(":/icons/warning.png");
+        }
+        if( ((FrameTreeNode*)index.internalPointer())->getDepth() == 0 )
+        {
+            if( !((Kamera*)index.internalPointer())->isCorrectAnnotated() )
+                return QIcon(":/icons/warning.png");
+        }
+        break;
+    case Qt::BackgroundRole:
+        if( ((FrameTreeNode*)index.internalPointer())->getDepth() == 1 )
+        {
+            if( ((Frame*)index.internalPointer())->isNotAnnotated() )
+                return QColor( 0x50, 0x50, 0x50 );
+            if( seq && ((Frame*)index.internalPointer())->getIndex() == seq->getSelectedFrame() )
+                return QColor( 48, 140, 198 );
+        }
+    }
+    return QVariant();
+}
+
+// Gibt die Flags eines Knotens zurück
+//  index: Der Index des Knotens
+Qt::ItemFlags FrameTreeModel::flags(const QModelIndex &index) const
+{
+    if (!index.isValid() || seq == 0)
+        return 0;
+    return QAbstractItemModel::flags(index);
+}
+
+// Gibt die Grunddaten zurück, welche für das zeichnen benötigt werden
+QVariant FrameTreeModel::headerData(int section, Qt::Orientation orientation,
+                    int role) const
+{
+    if( orientation == Qt::Horizontal && role == Qt::DisplayRole )
+        return QVariant( "Kameras" );
+    return QVariant();
+}
+QModelIndex FrameTreeModel::index(int row, int column,
+                  const QModelIndex &parent) const
+{
+    if (!hasIndex(row, column, parent) || seq == 0)
+        return QModelIndex();
+
+    if( !parent.isValid() )
+    {
+        return createIndex( row, column, seq->getCameras().at( row ) );
+    }
+    else
+    {
+        if( ((FrameTreeNode*)parent.internalPointer())->getDepth() == 0 )
+            return createIndex( row, column, ((Kamera*)((FrameTreeNode*)parent.internalPointer()))->getFrame( row )->getNodeObject() );
+        if( ((FrameTreeNode*)parent.internalPointer())->getDepth() == 1 )
+            return createIndex( row, column, ((Frame*)((FrameTreeNode*)parent.internalPointer()))->getObjects().at( row )->getNodeObject() );
+    }
+    return QModelIndex();
+}
+
+// gibt den Index des Elternknotens zurück
+QModelIndex FrameTreeModel::parent(const QModelIndex &index) const
+{
+    FrameTreeNode *node = (FrameTreeNode*)index.internalPointer();
+    if (!index.isValid() || seq == 0 || node == 0 || node->getParent() == 0 || !isIndexValid( index ) )
+        return QModelIndex();
+    return createIndex( node->getParent()->getIndex(), 0, node->getParent()->getNodeObject() );
+}
+
+// gibt die Anzahl der Kindknoten zurück
+int FrameTreeModel::rowCount(const QModelIndex &parent) const
+{
+    if( seq == 0 )
+        return 0;
+    if( !parent.isValid() || parent.internalPointer() == 0 || !isIndexValid( parent ))
+        return seq->getCameras().count();
+    else
+        return ((FrameTreeNode*)parent.internalPointer())->getChildCount();
+}
+
+// gibt die Anzahl der Attribute eines Kindknotens zurück
+int FrameTreeModel::columnCount(const QModelIndex &parent) const
+{
+    if( seq == 0 )
+        return 0;
+    return 1;
+}
+
+// Ermittelt die zugehörige Kamera und das Zugehörige Bild eines Knotens
+bool FrameTreeModel::clickedOnFrame( const QModelIndex &index, int &camera, int &frame ) const
+{
+    if( ((FrameTreeNode*)index.internalPointer())->getDepth() == 1 )
+    {
+        frame = ((FrameTreeNode*)index.internalPointer())->getIndex();
+        camera = ((FrameTreeNode*)index.internalPointer())->getParent()->getIndex();
+        return true;
+    }
+    return false;
+}
+
+// Gibt den Index des ausgewählten Bildes zurück
+QModelIndex FrameTreeModel::getFrameSelectionIndex() const
+{
+    Frame *selection = seq->getFrame();
+    return createIndex( selection->getIndex(), 0, selection );
+}
+
+// Gibt den Index der ausgewählten Kamera zurück
+QModelIndex FrameTreeModel::getCameraSelectionIndex() const
+{
+    return createIndex( seq->getSelectedCamera(), 0, (void*) &seq->getCameras().at( seq->getSelectedCamera() ) );
+}
+
+// Gibt eine Liste mit ausgewählten Objekten zurück
+QList<QModelIndex> FrameTreeModel::getSelectedPackages() const
+{
+    QList<QModelIndex> list;
+    Frame *selectedFrame = seq->getFrame();
+    QList<ObjectPolygon> objects = selectedFrame->getObjects();
+    for( int i = 0; i < objects.size(); i++ )
+    {
+        if( objects.at( i )->isSelected() )
+            list.append( createIndex( i, 0, objects.at( i )->getNodeObject() ) );
+    }
+    return list;
+}
+
+// Aktualisiert die Tree View
+void FrameTreeModel::update()
+{
+    this->dataChanged( createIndex( 0, 0, seq->getCameras().at( 0 ) ), createIndex( seq->getCameras().size() - 1, 0, seq->getCameras().last() ) );
+}

+ 57 - 0
annotationGUI/frametreemodel.h

@@ -0,0 +1,57 @@
+#ifndef FRAMETREEMODEL_H
+#define FRAMETREEMODEL_H
+
+#include <QAbstractItemModel>
+#include "sequenz.h"
+
+/*
+ * Das Modell des Objekt Baumes, welches alle Daten enthält, die dort angezeigt werden sollen
+ */
+class FrameTreeModel : public QAbstractItemModel
+{
+    Q_OBJECT
+
+private:
+    Sequenz *seq;
+
+    // Prüft, ob ein Index valide ist
+    bool isIndexValid( const QModelIndex &i ) const;
+
+public:
+    explicit FrameTreeModel();
+    ~FrameTreeModel();
+    // Setzt die Sequenz, welche momenatan annotiert wird
+    //  s: die neue Sequenz
+    void setSequenz( Sequenz *s );
+    // Gibt zurück, was für einen bestimten Knoten angezeigt werden soll
+    //  index: Der Index des Knotens
+    //  role: Welche daten des Knotens abgefragt werden sollen (z.B.:Name oder Hintergrundfarbe...)
+    QVariant data(const QModelIndex &index, int role) const override;
+    // Gibt die Flags eines Knotens zurück
+    //  index: Der Index des Knotens
+    Qt::ItemFlags flags(const QModelIndex &index) const override;
+    // Gibt die Grunddaten zurück, welche für das zeichnen benötigt werden
+    QVariant headerData(int section, Qt::Orientation orientation,
+                        int role = Qt::DisplayRole) const override;
+    // gibt den Index eines Knotens zurück
+    QModelIndex index(int row, int column,
+                      const QModelIndex &parent = QModelIndex()) const override;
+    // gibt den Index des Elternknotens zurück
+    QModelIndex parent(const QModelIndex &index) const override;
+    // gibt die Anzahl der Kindknoten zurück
+    int rowCount(const QModelIndex &parent = QModelIndex()) const override;
+    // gibt die Anzahl der Attribute eines Kindknotens zurück
+    int columnCount(const QModelIndex &parent = QModelIndex()) const override;
+    // Ermittelt die zugehörige Kamera und das Zugehörige Bild eines Knotens
+    bool clickedOnFrame( const QModelIndex &index, int &camera, int &frame ) const;
+    // Gibt den Index des ausgewählten Bildes zurück
+    QModelIndex getFrameSelectionIndex() const;
+    // Gibt den Index der ausgewählten Kamera zurück
+    QModelIndex getCameraSelectionIndex() const;
+    // Gibt eine Liste mit ausgewählten Objekten zurück
+    QList< QModelIndex > getSelectedPackages() const;
+    // Aktualisiert die Tree View
+    void update();
+};
+
+#endif // FRAMETREEMODEL_H

+ 18 - 0
annotationGUI/icons.qrc

@@ -0,0 +1,18 @@
+<RCC>
+    <qresource prefix="/">
+        <file>icons/white.png</file>
+        <file>icons/warning.png</file>
+        <file>icons/select.png</file>
+        <file>icons/preview.png</file>
+        <file>icons/polygon.png</file>
+        <file>icons/pipette.png</file>
+        <file>icons/next.png</file>
+        <file>icons/move.png</file>
+        <file>icons/lupe_out.png</file>
+        <file>icons/lupe_in.png</file>
+        <file>icons/delete.png</file>
+        <file>icons/cut.png</file>
+        <file>icons/black.png</file>
+        <file>icons/before.png</file>
+    </qresource>
+</RCC>

BIN
annotationGUI/icons/before.png


BIN
annotationGUI/icons/black.png


BIN
annotationGUI/icons/cut.png


BIN
annotationGUI/icons/delete.png


BIN
annotationGUI/icons/lupe_in.png


BIN
annotationGUI/icons/lupe_out.png


BIN
annotationGUI/icons/move.png


BIN
annotationGUI/icons/next.png


BIN
annotationGUI/icons/pipette.png


BIN
annotationGUI/icons/polygon.png


BIN
annotationGUI/icons/preview.png


BIN
annotationGUI/icons/select.png


BIN
annotationGUI/icons/warning.png


BIN
annotationGUI/icons/white.png


+ 104 - 0
annotationGUI/kamera.cpp

@@ -0,0 +1,104 @@
+#include "kamera.h"
+#include <QProgressDialog>
+
+Kamera::Kamera( QString n, QString maskPath, int index )
+    : FrameTreeNode( index, 0, 0 ),
+      ref( 1 ),
+      name( n ),
+      m( maskPath )
+{}
+
+Kamera::~Kamera()
+{
+    for( auto f = frames.begin(); f != frames.end(); f++ )
+    {
+        delete *f;
+    }
+}
+
+// Gibt 1 zurück, falls keine Objekte ohne ID existieren und auf keinem Bild eine ID mehrfach vergeben wurde
+bool Kamera::isCorrectAnnotated() const
+{
+    for( Frame *f : frames )
+    {
+        if( !f->isCorrectAnnotated() )
+            return false;
+    }
+    return true;
+}
+
+// Gibt this zurück
+void *Kamera::getNodeObject() const
+{
+    return (void*)this;
+}
+
+// gibt die Anzahl der Bilder zurück
+int Kamera::getChildCount() const
+{
+    return frames.size();
+}
+
+// Gibt das Bild mit Index index zurück
+Frame *Kamera::getFrame( int index ) const
+{
+    return frames.at( index );
+}
+
+// fügt der Kamera das Bild f hinzu
+QString Kamera::getName() const
+{
+    return name;
+}
+
+// Wendet die Maske der Kamera auf alle Bilder an
+void Kamera::addFrame( Frame *f )
+{
+    frames.append( f );
+    f->applyMask( m );
+}
+
+// Gibt den Namen der Kamera zurück
+void Kamera::applyMask()
+{
+    for( auto f = frames.begin(); f != frames.end(); f++ )
+    {
+        (*f)->applyMask( m );
+    }
+}
+
+// Gibt die Maske der Kamera zurück
+Mask *Kamera::getMask() const
+{
+    return (Mask*)&m;
+}
+
+// Speichert die Maske der Kamera und erzeugt die Bilder in JPEG Images
+void Kamera::saveMask()
+{
+    m.save();
+    QProgressDialog progress( "Maske wird gespeichert...", "", 0, frames.size() );
+    progress.setWindowModality(Qt::WindowModal);
+    progress.setCancelButton( 0 );
+    for( auto f = frames.begin(); f != frames.end(); f++ )
+    {
+        m.saveToFrameMask( (*f)->getImageMatrix(), (*f)->getName() );
+        progress.setValue( progress.value() + 1 );
+        progress.repaint();
+    }
+    applyMask();
+    progress.close();
+}
+
+// Erhöht den Reference Counter um 1
+void Kamera::refNew()
+{
+    ref++;
+}
+
+// Verringert den Reference Counter um 1 (bei 0 löscht sich das Objekt selbst)
+void Kamera::refRelease()
+{
+    if( !--ref )
+        delete this;
+}

+ 46 - 0
annotationGUI/kamera.h

@@ -0,0 +1,46 @@
+#ifndef KAMERA_H
+#define KAMERA_H
+
+#include "frame.h"
+#include <QList>
+#include "mask.h"
+#include <QString>
+#include "frametree.h"
+
+class Kamera : public FrameTreeNode
+{
+private:
+    int ref; // reference Counter
+    QList< Frame* > frames; // Liste mit Bildern
+    Mask m; // Die Maske der Kamera
+    QString name; // Der Name der Kamera)
+
+public:
+    Kamera( QString name, QString maskPath, int index );
+    ~Kamera();
+
+    // Gibt 1 zurück, falls keine Objekte ohne ID existieren und auf keinem Bild eine ID mehrfach vergeben wurde
+    bool isCorrectAnnotated() const;
+    // Gibt this zurück
+    void *getNodeObject() const override;
+    // gibt die Anzahl der Bilder zurück
+    int getChildCount() const override;
+    // Gibt das Bild mit Index index zurück
+    Frame *getFrame( int index ) const;
+    // fügt der Kamera das Bild f hinzu
+    void addFrame( Frame *f );
+    // Wendet die Maske der Kamera auf alle Bilder an
+    void applyMask();
+    // Gibt den Namen der Kamera zurück
+    QString getName() const;
+    // Gibt die Maske der Kamera zurück
+    Mask *getMask() const;
+    // Speichert die Maske der Kamera und erzeugt die Bilder in JPEG Images
+    void saveMask();
+    // Erhöht den Reference Counter um 1
+    void refNew();
+    // Verringert den Reference Counter um 1 (bei 0 löscht sich das Objekt selbst)
+    void refRelease();
+};
+
+#endif // KAMERA_H

+ 38 - 0
annotationGUI/kmath.cpp

@@ -0,0 +1,38 @@
+#include "kmath.h"
+
+
+// Ermittelt von einem Punkt p aus den Nächstgelegenen Punkt auf einer Linie zwischen zwei Punkten a und b
+QPoint Math::getNearestPointOnLine( QPoint p, QPoint a, QPoint b )
+{
+    double xp = p.x(), yp = p.y(), xa = a.x(), ya = a.y(), xb = b.x(), yb = b.y(), xr, yr;
+    if( xb == xa )
+    {
+        xr = 1;
+        yr = 0;
+    }
+    else if( yb == ya )
+    {
+        xr = 0;
+        yr = 1;
+    }
+    else
+    {
+        xr = 1;
+        yr = -1 / ( (yb - ya) / (xb - xa) );
+    }
+    double xd = xb - xa, yd = yb - ya;
+    double x = ((yp-ya)*xr + (xa-xp)*yr) / (yd*xr - xd*yr);
+    if( x >= 0 && x <= 1 )
+        return QPoint( (int)( xa + x * xd ), (int)( ya + x * yd ) );
+    if( (xa - xp) * (xa - xp) + (ya - yp) * (ya - yp) > (xb - xp) * (xb - xp) + (yb - yp) * (yb - yp) )
+        return b;
+    else
+        return a;
+}
+
+// Ermittelt das Quadrat von dem Abstand zwischen den Punkten a und b
+int Math::diffSquare( QPoint a, QPoint b )
+{
+    QPoint tmp = b - a;
+    return tmp.x() * tmp.x() + tmp.y() * tmp.y();
+}

+ 15 - 0
annotationGUI/kmath.h

@@ -0,0 +1,15 @@
+#ifndef KMATH_H
+#define KMATH_H
+
+#include <QPoint>
+
+class Math
+{
+public:
+    // Ermittelt von einem Punkt p aus den Nächstgelegenen Punkt auf einer Linie zwischen zwei Punkten a und b
+    static QPoint getNearestPointOnLine( QPoint p, QPoint a, QPoint b );
+    // Ermittelt das Quadrat von dem Abstand zwischen den Punkten a und b
+    static int diffSquare( QPoint a, QPoint b );
+};
+
+#endif // MATH_H

+ 54 - 0
annotationGUI/main.cpp

@@ -0,0 +1,54 @@
+#include "mainwindow.h"
+#include <QApplication>
+#include <QResource>
+#include <QDebug>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <QMessageBox>
+
+MainWindow *wnd;
+
+// Absturtz Handler zum speichern der Annotationen
+void segfault_sigaction(int signal, siginfo_t *si, void *arg)
+{
+    if( wnd->getSequenz() )
+    {
+        QMessageBox::StandardButton reply =
+                QMessageBox::question(0, "Fehler", QString( "Es ist ein unerwarteter Fehler aufgetreten und das Programm muss "
+                                                                                        "geschlossen werden (Zugriffsfehler auf Speicheradresse " ) +
+                                      QString("0x%1").arg((quintptr)si->si_addr, QT_POINTER_SIZE * 2, 16, QChar('0')) + "). Möchten sie die Annotationen speichern?",
+                                                                  QMessageBox::Yes|QMessageBox::No);
+        if (reply == QMessageBox::Yes) {
+            wnd->getSequenz()->saveToPath( 0 );
+        }
+    }
+    exit(0);
+}
+
+void doStuff(int argc, char *argv[])
+{
+    QResource::registerResource("icons.rcc");
+    QApplication a(argc, argv);
+    MainWindow w( &a );
+    wnd = &w;
+    struct sigaction sa;
+    memset(&sa, 0, sizeof(struct sigaction));
+    sigemptyset(&sa.sa_mask);
+    sa.sa_sigaction = segfault_sigaction;
+    sa.sa_flags   = SA_SIGINFO;
+    sigaction(SIGSEGV, &sa, 0);
+
+    w.show();
+    a.exec();
+}
+
+// Start des Programms
+int main(int argc, char *argv[])
+{
+    numObjects = 0;
+    doStuff(argc, argv);
+    qDebug() << "Number of counted memory leaks: " << numObjects;
+    return 0;
+}

+ 620 - 0
annotationGUI/mainwindow.cpp

@@ -0,0 +1,620 @@
+#include "mainwindow.h"
+#include "annotationxml.h"
+#include "ui_mainwindow.h"
+#include <QImage>
+#include <QDebug>
+#include <QLabel>
+#include <QWidget>
+#include <QPainter>
+#include <QFileDialog>
+#include <QToolButton>
+#include <QGridLayout>
+#include <QMessageBox>
+#include <QStatusBar>
+#include <QTreeView>
+#include <QMenu>
+#include <QCursor>
+#include <QInputDialog>
+#include <QDateTime>
+#include <CSVReader.h>
+#include "changemask.h"
+#include "changepacket.h"
+#include "tinyxml2.h"
+#include "classoptions.h"
+#include "requestfromserver.h"
+
+// Hilfstexte
+QString toolTitel[] = { "Objekte Verstecken", "Verschieben", "Neues Objekt", "Kopieren", "Einfügen", "Löschen", "Zerschneiden", "Vergrößern", "Allgemeine Information" };
+QString toolBeschreibung[] {"Klicken sie mit der Linken Maustaste auf ein Objekt um es zu verstecken, oder wieder sichtbar zu machen.\nVersteckte Objekte werden von anderen Werkzeugen ignoriert.",
+                            "Halten sie die linke Maustaste gedrückt, um die Ecke eines Objektes zu verschieben, wenn sich die Maus in der Nähe befindet.\nBefindet sich keine Ecke in der Nähe der Maus, so wird eine neue Ecke erzeugt.",
+                            "Klicken sie mit der Linken Maustaste an die Stellen, wo die Ecken des neuen Objektes sind.\nUm den Vorgang abzuschließen klicken sie erneut auf die erste Ecke.\nMit Rechtsklick kann die vorherige Ecke wieder entfernt werden.",
+                            "Klicken sie mit der linken Maustaste auf das Objekt, welches sie kopieren möchten.",
+                            "Klicken sie mit der linken Maustaste auf ein Objekt, um die ID und die Klasse des kopierten Objektes auf dieses zu übertragen.\nKlicken sie an eine freie Stelle um eine Kopie des kopierten Objektes einzufügen.\nHalten sie die rechte Maustaste gedrückt, um das kopierte Objekt zu drehen.",
+                            "Halten sie die linke Maustaste gedrückt, um einen Bereich festzulegen, in dem alle Eckpunkte gelöscht werden sollen.",
+                            "Klicken sie auf zwei Eckpunkte eines Objektes, um es in zwei verschiedene Objekte zu zerschneiden.",
+                            "Halte die linke Maustaste gedrückt um ein Gebiet zu makieren, an welches herangezoomt werden soll.",
+                            "Klicken sie auf Öffnen und wählen sie einen Ordner aus, um eine annotierte Sequenz zu laden oder eine neue Sequenz zu erstellen."};
+
+// Inhalt der MainWindow Klasse
+//------------------------------
+
+MainWindow::MainWindow( QApplication *app, QWidget *parent ) :
+    QMainWindow(parent),
+    app( app ),
+    ui(new Ui::MainWindow),
+    contextMenu( 0 ),
+    seq( 0 ),
+    m( 0 ),
+    serverAddress( "tcp://ob:55556" )
+{
+    ui->setupUi(this);
+    setWindowTitle( "Annotation GUI v. 1.0" );
+    status = new QLabel( "fertig" );
+    ui->statusBar->layout()->setSizeConstraint(QLayout::SetMinimumSize);
+    status->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum );
+    ui->statusBar->layout()->addWidget( status );
+    ui->statusBar->addWidget( status );
+
+    workModel = new ArbeitsModel( this );
+    ArbeitsController::initStaticController( workModel );
+    v = new ArbeitsView( workModel, ui->viewWidget );
+    v->setController( ArbeitsController::getController( MOVE ) );
+    workModel->addView( v );
+    ui->toolTitel->setText( toolTitel[ USERMODE_COUNT ] );
+    ui->toolBeschreibung->setText( toolBeschreibung[ USERMODE_COUNT ] );
+
+    ui->framesTree->setContextMenuPolicy( Qt::CustomContextMenu );
+    ui->viewWidget->setContextMenuPolicy( Qt::CustomContextMenu );
+
+    QList<QToolButton *> btnList = ui->buttons->findChildren<QToolButton*>();
+    for( auto i = btnList.begin(); i != btnList.end(); i++ )
+    {
+        (*i)->setIconSize( (*i)->size() );
+    }
+}
+
+MainWindow::~MainWindow()
+{
+    if( seq )
+    {
+        QMessageBox::StandardButton reply = QMessageBox::question(this, "Achtung", "Möchten sie die Annotationen speichern?",
+                                                                  QMessageBox::Yes|QMessageBox::No);
+        if (reply == QMessageBox::Yes) {
+            seq->saveToPath( status );
+        }
+        seq->refRelease();
+    }
+    delete status;
+    delete ui;
+    delete m;
+    delete contextMenu;
+}
+
+// Erstellt den Navigationsbaum
+void MainWindow::setupFrameTree()
+{
+    ui->framesTree->setUpdatesEnabled(false);
+    if( !seq )
+        return;
+    if( !m )
+    {
+        m = new FrameTreeModel();
+        seq->refNew();
+        m->setSequenz( seq );
+        ui->framesTree->setModel( m );
+        ui->framesTree->setSelectionMode( ui->framesTree->MultiSelection );
+    }
+    ui->framesTree->collapseAll();
+    setFrameTreeSelection();
+    repaintFrameTree();
+    ui->framesTree->setUpdatesEnabled(true);
+}
+
+// Aktualisiert die Ausgefählten Objekte im Navigationsbaum
+void MainWindow::setFrameTreeSelection()
+{
+    ui->framesTree->expand( m->getCameraSelectionIndex() );
+    ui->framesTree->expand( m->getFrameSelectionIndex() );
+    ui->framesTree->selectionModel()->select( m->getCameraSelectionIndex(), QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows );
+    ui->framesTree->selectionModel()->select( m->getFrameSelectionIndex(), QItemSelectionModel::Select | QItemSelectionModel::Rows );
+    QList<QModelIndex> indices = m->getSelectedPackages();
+    for( auto index = indices.begin(); index != indices.end(); index++ )
+        ui->framesTree->selectionModel()->select( *index, QItemSelectionModel::Select | QItemSelectionModel::Rows );
+    ui->framesTree->scrollTo(m->getFrameSelectionIndex());
+    ui->framesTree->repaint();
+}
+
+// Zeichnet den Navigazionsbaum neu
+void MainWindow::repaintFrameTree()
+{
+    m->update();
+    ui->framesTree->repaint();
+}
+
+// Setzt den Controller der Arbeitsfläche
+void MainWindow::setMode( UserMode m )
+{
+    if( workModel->getMode() == m )
+        ArbeitsController::getController( m )->restartController();
+    workModel->setMode( m );
+    v->setController( ArbeitsController::getController( m ) );
+    ui->toolTitel->setText( toolTitel[ workModel->getMode() ] );
+    ui->toolBeschreibung->setText( toolBeschreibung[ workModel->getMode() ] );
+}
+
+// Gibt die geladene Sequenz zurück
+Sequenz *MainWindow::getSequenz()
+{
+    return seq;
+}
+
+// Wählt alle Buttons für die Werkzeuge ab
+void MainWindow::unselectButttons()
+{
+    QList<QToolButton *> btnList = ui->buttons->findChildren<QToolButton*>();
+    for( auto i = btnList.begin(); i != btnList.end(); i++ )
+    {
+        (*i)->setChecked( false );
+    }
+}
+
+// Fragt nach Vorabannotationen beim Server
+void MainWindow::requestFromServer()
+{
+    if( serverAddress != "" )
+    {
+        Frame *f = seq->getFrame();
+        RequestFromServer rfs( serverAddress, f );
+        rfs.exec();
+        f->setObjects( rfs.getObjects() );
+        f->applyMask(*((Kamera*)f->getParent())->getMask());
+        if( !seq->hasAnnotatedObjects() )
+        {
+            int id = 0;
+            for( auto obj = f->getObjects().begin(); obj != f->getObjects().end(); obj++ )
+            {
+                (*obj)->setId( QString::number( id ) );
+                seq->addObjectName( QString::number( id ) );
+                id++;
+            }
+        }
+    }
+    else
+        seq->getFrame()->setObjects( std::vector< std::vector< cv::Point > >());
+    setupFrameTree();
+    v->repaint();
+}
+
+// Öffnet eine Sequenz
+void MainWindow::on_actionOpen_triggered()
+{
+    QString path = QFileDialog::getExistingDirectory(this,
+                                                     tr("Open Image Directory"),
+                                                     "/studi-tmp/kstrohm",
+                                                     QFileDialog::ShowDirsOnly |
+                                                     QFileDialog::DontResolveSymlinks);
+    if( path == "" )
+        return;
+    status->setText( "Lade Annotation ..." );
+    AnnotationLoader loader( path, status );
+    if( seq )
+        seq->refRelease();
+    seq = loader.getSequenz();
+    if( !seq )
+    {
+        QMessageBox::StandardButton reply = QMessageBox::question(this, "Achtung", "In dem ausgewählten Ordner wurde keine Annotierte Sequenz gefunden. Möchten sie eine neue Sequenz erstellen?",
+                                                                  QMessageBox::Yes|QMessageBox::No);
+        if (reply == QMessageBox::Yes) {
+            AnnotationCreator creator( path, status );
+            seq = creator.getSequenz();
+            if( !seq )
+            {
+                QMessageBox::critical( this, "Fehler", "Es ist ein Fehler beim erstellen der Sequenz aufgetreten." );
+            }
+        }
+    }
+    status->setText( "fertig" );
+    if( seq )
+    {
+        ui->framesTree->setModel( 0 );
+        delete m;
+        m = 0;
+        Frame *f = seq->getFrame();
+        workModel->setFrame( f );
+        workModel->setMask( seq->getCameras().at( seq->getSelectedCamera() )->getMask()->getDrawableImage() );
+        workModel->notifyViews();
+        if( f->isNotAnnotated() )
+            requestFromServer();
+        else
+            setupFrameTree();
+        setMode( MOVE );
+    }
+}
+
+// Wechselt zum nächsten Bild
+void MainWindow::on_actionNext_triggered()
+{
+    ui->framesTree->selectionModel()->clear();
+    int oldCam = seq->getSelectedCamera();
+    QModelIndex camIndex = m->getCameraSelectionIndex();
+    ui->framesTree->collapse( m->getFrameSelectionIndex() );
+    seq->nextFrame();
+    if(oldCam != seq->getSelectedCamera())
+        ui->framesTree->collapse( camIndex );
+    Frame *f = seq->getFrame();
+    workModel->setFrame( f );
+    workModel->setMask( seq->getCameras().at( seq->getSelectedCamera() )->getMask()->getDrawableImage() );
+    workModel->notifyViews();
+    if( f->isNotAnnotated() )
+        requestFromServer();
+    else
+        setFrameTreeSelection();
+}
+
+// Wechselt zum vorherigen Bild
+void MainWindow::on_actionBefore_triggered()
+{
+    ui->framesTree->selectionModel()->clear();
+    int oldCam = seq->getSelectedCamera();
+    QModelIndex camIndex = m->getCameraSelectionIndex();
+    ui->framesTree->collapse( m->getFrameSelectionIndex() );
+    seq->previousFrame();
+    if(oldCam != seq->getSelectedCamera())
+        ui->framesTree->collapse( camIndex );
+    Frame *f = seq->getFrame();
+    workModel->setFrame( f );
+    workModel->setMask( seq->getCameras().at( seq->getSelectedCamera() )->getMask()->getDrawableImage() );
+    workModel->notifyViews();
+    if( f->isNotAnnotated() )
+        requestFromServer();
+    else
+        setFrameTreeSelection();
+}
+
+// Wechselt zum nächsten Bild
+void MainWindow::on_next_clicked()
+{
+    on_actionNext_triggered();
+}
+
+// Wechselt zum vorherigen Bild
+void MainWindow::on_before_clicked()
+{
+   on_actionBefore_triggered();
+}
+
+// Setzt die Vergrößerung der Arbeitsfläche zurück
+void MainWindow::on_zoom_out_clicked()
+{
+    workModel->resetZoom();
+    workModel->notifyViews();
+}
+
+// Wechselt in den ZOOM_IN Modus
+void MainWindow::on_zoom_in_clicked()
+{
+    unselectButttons();
+    ui->zoom_in->setChecked( true );
+    setMode( ZOOM_IN );
+    v->repaint();
+}
+
+// Wechselt in den DELETE Modus
+void MainWindow::on_remove_clicked()
+{
+    unselectButttons();
+    ui->remove->setChecked( true );
+    setMode( DELETE );
+    v->repaint();
+}
+
+// Wechselt in den NEW Modus
+void MainWindow::on_polygon_clicked()
+{
+    unselectButttons();
+    ui->polygon->setChecked( true );
+    setMode( NEW );
+}
+
+// Wechselt in den MOVE Modus
+void MainWindow::on_move_clicked()
+{
+    unselectButttons();
+    ui->move->setChecked( true );
+    setMode( MOVE );
+}
+
+// Wechselt zum angeklickten Bild des Navigationsbaumes
+void MainWindow::on_framesTree_clicked(const QModelIndex &index)
+{
+    if( ((FrameTreeNode*)index.internalPointer())->getDepth() == 2 )
+        ((_ObjectPolygon*)index.internalPointer())->setSelected( ui->framesTree->selectionModel()->isSelected( index ) );
+    ui->framesTree->selectionModel()->clear();
+    int cam, fr;
+    if( m && m->clickedOnFrame( index, cam, fr ) )
+    {
+        if( cam != seq->getSelectedCamera() || fr != seq->getSelectedFrame() )
+        {
+            if(cam != seq->getSelectedCamera())
+                ui->framesTree->collapse( m->getCameraSelectionIndex() );
+            ui->framesTree->collapse( m->getFrameSelectionIndex() );
+            seq->selectFrame( cam, fr );
+            Frame *f = seq->getFrame();
+            workModel->setFrame( f );
+            workModel->setMask( seq->getCameras().at( seq->getSelectedCamera() )->getMask()->getDrawableImage() );
+            workModel->notifyViews();
+            if( f->isNotAnnotated() )
+                requestFromServer();
+        }
+    }
+    if( ((FrameTreeNode*)index.internalPointer())->getDepth() == 0 )
+    {
+        if( ((FrameTreeNode*)index.internalPointer())->getIndex() != seq->getSelectedCamera() )
+        {
+            ui->framesTree->collapse( m->getCameraSelectionIndex() );
+            ui->framesTree->collapse( m->getFrameSelectionIndex() );
+            seq->selectFrame( ((FrameTreeNode*)index.internalPointer())->getIndex(), 0 );
+            Frame *f = seq->getFrame();
+            workModel->setFrame( f );
+            workModel->setMask( seq->getCameras().at( seq->getSelectedCamera() )->getMask()->getDrawableImage() );
+            workModel->notifyViews();
+            if( f->isNotAnnotated() )
+                requestFromServer();
+        }
+    }
+    if( ((FrameTreeNode*)index.internalPointer())->getDepth() == 2 )
+    {
+        if( ((FrameTreeNode*)index.internalPointer())->getParent()->getParent()->getIndex() != seq->getSelectedCamera() )
+            ui->framesTree->collapse( m->getCameraSelectionIndex() );
+        if( ((FrameTreeNode*)index.internalPointer())->getParent()->getIndex() != seq->getSelectedFrame() )
+            ui->framesTree->collapse( m->getFrameSelectionIndex() );
+        seq->selectFrame( ((FrameTreeNode*)index.internalPointer())->getParent()->getParent()->getIndex(),
+                ((FrameTreeNode*)index.internalPointer())->getParent()->getIndex() );
+        Frame *f = seq->getFrame();
+        workModel->setFrame( f );
+        workModel->setMask( seq->getCameras().at( seq->getSelectedCamera() )->getMask()->getDrawableImage() );
+        workModel->notifyViews();
+        if( f->isNotAnnotated() )
+            requestFromServer();
+    }
+    setFrameTreeSelection();
+    v->repaint();
+}
+
+// Wechselt in den SELECT Modus
+void MainWindow::on_select_clicked()
+{
+    unselectButttons();
+    ui->select->setChecked( true );
+    setMode( SELECT );
+}
+
+// Zeigt ein Rechtsklick Menü im Navigationsbaum an
+void MainWindow::on_framesTree_customContextMenuRequested(const QPoint &pos)
+{
+    QModelIndex index = ui->framesTree->indexAt( pos );
+    ui->framesTree->selectionModel()->select( index, QItemSelectionModel::Select | QItemSelectionModel::Rows );
+    on_framesTree_clicked( index );
+    QAction *trunc = 0;
+    if( contextMenu )
+        delete contextMenu;
+    contextMenu = new QMenu( ui->framesTree );
+    switch( ((FrameTreeNode*)index.internalPointer())->getDepth() )
+    {
+    case 0:
+        contextMenu->addAction( "Maske Bearbeiten ..." );
+        break;
+    case 1:
+        contextMenu->addAction( "Vom Server abfragen ..." );
+        break;
+    case 2:
+        contextMenu->addAction( "Objekt ID bearbeiten ..." );
+        contextMenu->addAction( "Objekt Klasse bearbeiten ..." );
+        contextMenu->addAction( "Objekt löschen" );
+        if( ((_ObjectPolygon*)index.internalPointer())->getPolygonList().size() > 1 )
+            contextMenu->addAction( "Objekt nach Polygonen aufteilen" );
+        trunc = contextMenu->addAction( "Abgeschnitten" );
+        trunc->setCheckable( true );
+        trunc->setText( "Abgeschnitten" );
+        trunc->setChecked( ((_ObjectPolygon*)index.internalPointer())->isTruncated() );
+        break;
+    }
+    QAction *action = contextMenu->exec( QCursor::pos() );
+    if( !action )
+        return;
+    if( action->text() == "Maske Bearbeiten ..." )
+    {
+        ChangeMask chm( ((Kamera*)index.internalPointer())->getMask(), (Kamera*)index.internalPointer() );
+        chm.exec();
+        ((Kamera*)index.internalPointer())->getMask()->unloadMask();
+        setupFrameTree();
+    }
+    if( action->text() == "Vom Server abfragen ..." )
+        requestFromServer();
+    if( action->text() == "Objekt ID bearbeiten ..." )
+    {
+        ChangePacket chp( ObjectPolygon( (_ObjectPolygon*)index.internalPointer() ), seq );
+        chp.exec();
+        workModel->notifyViews();
+        setupFrameTree();
+    }
+    if( action->text() == "Objekt Klasse bearbeiten ..." )
+    {
+        QList< Sequenz::SegmentationClass > classes = seq->getClasses();
+        QList< QString > classNames;
+        QString packetId = ((_ObjectPolygon*)index.internalPointer())->getId();
+        int currentClass = seq->getClassOfObject( packetId );
+        int classId = -1;
+        foreach( auto c, classes )
+        {
+            if( currentClass == c.id )
+                classId = classNames.size();
+            classNames.append( c.name );
+        }
+        QString className = QInputDialog::getItem( this, "Klasse Bearbeiten", "Klasse von Objekt " + packetId, classNames, classId, false );
+        seq->setClassOfObject( packetId, seq->getClassId( className ) );
+        setupFrameTree();
+    }
+    if( action->text() == "Objekt Löschen" )
+    {
+        Frame *f = seq->getFrame();
+        f->removeObject( ObjectPolygon( (_ObjectPolygon*)index.internalPointer() ) );
+        setupFrameTree();
+        v->repaint();
+    }
+    if( action->text() == "Objekt nach Polygonen aufteilen" )
+    {
+        seq->getFrame()->disconnectObject( ObjectPolygon( ((_ObjectPolygon*)index.internalPointer()) ) );
+        setupFrameTree();
+        v->repaint();
+    }
+    if( action == trunc )
+        ((_ObjectPolygon*)index.internalPointer())->setTruncated( !((_ObjectPolygon*)index.internalPointer())->isTruncated() );
+}
+
+// Speichert die geladene Sequenz
+void MainWindow::on_actionSave_triggered()
+{
+    if( !seq )
+        return;
+    status->setText( "speichere Annotation ..." );
+    seq->saveToPath( status );
+    status->setText( "fertig" );
+}
+
+// Wechselt in den Modus PIPETTE_SELECT
+void MainWindow::on_pipette_clicked()
+{
+    unselectButttons();
+    ui->pipette->setChecked( true );
+    setMode( PIPETTE_SELECT );
+}
+
+// Wechselt in den CUT Modus
+void MainWindow::on_cut_clicked()
+{
+    unselectButttons();
+    ui->cut->setChecked( true );
+    setMode( CUT );
+}
+
+// Zeigt die Option zum einstellen der Serveradresse an
+void MainWindow::on_actionServer_address_triggered()
+{
+    serverAddress = QInputDialog::getText( this, "Server Adresse ändern", "Server Adresse:", QLineEdit::Normal, serverAddress );
+}
+
+// Zeigt die Klassen Verwaltungsoberfläche an
+void MainWindow::on_actionKlassen_verwalten_triggered()
+{
+    if( seq )
+    {
+        ClassOptions cop( seq, this );
+        cop.exec();
+    }
+    else
+        QMessageBox::critical( this, "Fehler", "Sie müssen zunächst eine Sequenz geöffnet haben, bevor sie die Klassen verwalten können." );
+}
+
+// Zeigt ein Rechtsklick Menü auf der Arbeitsfläche an
+void MainWindow::on_viewWidget_customContextMenuRequested(const QPoint &pos)
+{
+    QPoint rPos = pos - v->pos();
+    if( !seq || !seq->getFrame() )
+    {
+        v->mouseReleaseEvent( 0 );
+        return;
+    }
+    QPoint tPos = workModel->inverseTranslate( rPos );
+    int pIndex;
+    ObjectPolygon obj = seq->getFrame()->getObjectAt( tPos, pIndex );
+    if( obj.isNull() )
+        return;
+    v->mouseReleaseEvent( 0 );
+    if( contextMenu )
+        delete contextMenu;
+    contextMenu = new QMenu( ui->viewWidget );
+    contextMenu->addAction( "Objekt ID bearbeiten ..." );
+    contextMenu->addAction( "Objekt Klasse bearbeiten ..." );
+    contextMenu->addAction( "Objekt Loeschen" );
+    if( obj->getPolygonList().size() > 1 )
+        contextMenu->addAction( "Objekt nach Polygonen aufteilen" );
+    QAction *trunc = contextMenu->addAction( "Abgeschnitten" );
+    trunc->setCheckable( true );
+    trunc->setText( "Abgeschnitten" );
+    trunc->setChecked( obj->isTruncated() );
+
+    QAction *action = contextMenu->exec( QCursor::pos() );
+    if( !action )
+        return;
+    if( action->text() == "Objekt ID bearbeiten ..." )
+    {
+        ChangePacket chp( obj, seq );
+        chp.exec();
+        workModel->notifyViews();
+        setupFrameTree();
+    }
+    if( action->text() == "Objekt Klasse bearbeiten ..." )
+    {
+        QList< Sequenz::SegmentationClass > classes = seq->getClasses();
+        QList< QString > classNames;
+        QString packetId = obj->getId();
+        int currentClass = seq->getClassOfObject( packetId );
+        int classId = -1;
+        foreach( auto c, classes )
+        {
+            if( currentClass == c.id )
+                classId = classNames.size();
+            classNames.append( c.name );
+        }
+        QString className = QInputDialog::getItem( this, "Klasse Bearbeiten", "Klasse von Objekt " + packetId, classNames, classId, false );
+        seq->setClassOfObject( packetId, seq->getClassId( className ) );
+        setupFrameTree();
+    }
+    if( action->text() == "Objekt Loeschen" )
+    {
+        Frame *f = seq->getFrame();
+        f->removeObject( obj );
+        setupFrameTree();
+        v->repaint();
+    }
+    if( action->text() == "Objekt nach Polygonen aufteilen" )
+    {
+        seq->getFrame()->disconnectObject( obj );
+        setupFrameTree();
+        v->repaint();
+    }
+    if( action == trunc )
+        obj->setTruncated( !obj->isTruncated() );
+}
+
+// Setzt das anzeigen der Maske in der Arbeitsfläche
+void MainWindow::on_actionMake_anzeigen_toggled(bool checked)
+{
+    workModel->setShowMask( checked );
+    workModel->notifyViews();
+}
+
+// Setzt die Option des einfärbens von Objekten in der Arbeitsfläche
+void MainWindow::on_actionObjekte_faerben_triggered(bool checked)
+{
+    workModel->setShowColors( checked );
+    workModel->notifyViews();
+}
+
+// Setzt die Option des Anzeigens von IDs in der Arbeitsfläche
+void MainWindow::on_actionIDs_anzeigen_triggered(bool checked)
+{
+    workModel->setShowId( checked );
+    workModel->notifyViews();
+}
+
+// Zeigt die Maskenbearbeitungsoberfläche
+void MainWindow::on_actionMaske_bearbeiten_triggered()
+{
+    if( !seq )
+        return;
+    ChangeMask chm( seq->getCameras().at( seq->getSelectedCamera() )->getMask(), seq->getCameras().at( seq->getSelectedCamera() ) );
+    chm.exec();
+    setupFrameTree();
+}

+ 99 - 0
annotationGUI/mainwindow.h

@@ -0,0 +1,99 @@
+#ifndef MAINWINDOW_H
+#define MAINWINDOW_H
+
+#include <QMainWindow>
+#include "arbeitsview.h"
+#include "sequenz.h"
+#include <QLabel>
+#include "frametreemodel.h"
+#include <QItemSelection>
+#include <QApplication>
+
+namespace Ui {
+class MainWindow;
+}
+
+class MainWindow : public QMainWindow
+{
+    Q_OBJECT
+
+public:
+    explicit MainWindow(QApplication *app, QWidget *parent = 0);
+    ~MainWindow();
+    // Erstellt den Navigationsbaum
+    void setupFrameTree();
+    // Aktualisiert die Ausgefählten Objekte im Navigationsbaum
+    void setFrameTreeSelection();
+    // Zeichnet den Navigazionsbaum neu
+    void repaintFrameTree();
+    // Setzt den Controller der Arbeitsfläche
+    void setMode( UserMode m );
+    // Gibt die geladene Sequenz zurück
+    Sequenz *getSequenz();
+
+private slots:
+    // Öffnet eine Sequenz
+    void on_actionOpen_triggered();
+    // Wechselt zum nächsten Bild
+    void on_actionNext_triggered();
+    // Wechselt zum vorherigen Bild
+    void on_actionBefore_triggered();
+    // Wechselt zum nächsten Bild
+    void on_next_clicked();
+    // Wechselt zum vorherigen Bild
+    void on_before_clicked();
+    // Setzt die Vergrößerung der Arbeitsfläche zurück
+    void on_zoom_out_clicked();
+    // Wechselt in den ZOOM_IN Modus
+    void on_zoom_in_clicked();
+    // Wechselt in den DELETE Modus
+    void on_remove_clicked();
+    // Wechselt in den NEW Modus
+    void on_polygon_clicked();
+    // Wechselt in den MOVE Modus
+    void on_move_clicked();
+    // Wechselt zum angeklickten Bild des Navigationsbaumes
+    void on_framesTree_clicked(const QModelIndex &index);
+    // Wechselt in den SELECT Modus
+    void on_select_clicked();
+    // Zeigt ein Rechtsklick Menü im Navigationsbaum an
+    void on_framesTree_customContextMenuRequested(const QPoint &pos);
+    // Speichert die geladene Sequenz
+    void on_actionSave_triggered();
+    // Wechselt in den Modus PIPETTE_SELECT
+    void on_pipette_clicked();
+    // Wechselt in den CUT Modus
+    void on_cut_clicked();
+    // Zeigt die Option zum einstellen der Serveradresse an
+    void on_actionServer_address_triggered();
+    // Zeigt die Klassen Verwaltungsoberfläche an
+    void on_actionKlassen_verwalten_triggered();
+    // Zeigt ein Rechtsklick Menü auf der Arbeitsfläche an
+    void on_viewWidget_customContextMenuRequested(const QPoint &pos);
+    // Setzt das anzeigen der Maske in der Arbeitsfläche
+    void on_actionMake_anzeigen_toggled(bool checked);
+    // Setzt die Option des einfärbens von Objekten in der Arbeitsfläche
+    void on_actionObjekte_faerben_triggered(bool checked);
+    // Setzt die Option des Anzeigens von IDs in der Arbeitsfläche
+    void on_actionIDs_anzeigen_triggered(bool checked);
+    // Zeigt die Maskenbearbeitungsoberfläche
+    void on_actionMaske_bearbeiten_triggered();
+
+private:
+    // Wählt alle Buttons für die Werkzeuge ab
+    void unselectButttons();
+    // Fragt nach Vorabannotationen beim Server
+    void requestFromServer();
+
+    ArbeitsModel *workModel; // Das Model der Arbaits View
+    ArbeitsView *v; // Die Arbeitsview
+    Ui::MainWindow *ui; // Ein zeiger auf die Objekte, welche in mainwindow.ui spezifiziert sind
+    Sequenz *seq; // Die annotierte Bildsequenz
+    QLabel *status; // Der Status Text
+    FrameTreeModel *m; // Ein Wrapper der Sequenz für die QTreeView
+    QString serverAddress; // Die Adresse des Annotations Servers
+    QMenu *contextMenu; // Ein Kontextmenü, das beim rechtsklick erscheint
+    QApplication *app; // Die QT Application
+};
+
+#endif // MAINWINDOW_H

+ 605 - 0
annotationGUI/mainwindow.ui

@@ -0,0 +1,605 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>MainWindow</class>
+ <widget class="QMainWindow" name="MainWindow">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>1119</width>
+    <height>702</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>MainWindow</string>
+  </property>
+  <widget class="QWidget" name="centralWidget">
+   <property name="enabled">
+    <bool>true</bool>
+   </property>
+   <property name="sizePolicy">
+    <sizepolicy hsizetype="Maximum" vsizetype="Maximum">
+     <horstretch>0</horstretch>
+     <verstretch>0</verstretch>
+    </sizepolicy>
+   </property>
+   <layout class="QVBoxLayout" name="verticalLayout">
+    <item>
+     <layout class="QHBoxLayout" name="main_container">
+      <item>
+       <layout class="QVBoxLayout" name="left_toolbar" stretch="1,0">
+        <property name="sizeConstraint">
+         <enum>QLayout::SetMinimumSize</enum>
+        </property>
+        <property name="topMargin">
+         <number>9</number>
+        </property>
+        <item>
+         <widget class="QWidget" name="buttons" native="true">
+          <property name="sizePolicy">
+           <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
+            <horstretch>1</horstretch>
+            <verstretch>0</verstretch>
+           </sizepolicy>
+          </property>
+          <property name="minimumSize">
+           <size>
+            <width>0</width>
+            <height>80</height>
+           </size>
+          </property>
+          <property name="maximumSize">
+           <size>
+            <width>300</width>
+            <height>16777215</height>
+           </size>
+          </property>
+          <property name="baseSize">
+           <size>
+            <width>300</width>
+            <height>0</height>
+           </size>
+          </property>
+          <layout class="QGridLayout" name="gridLayout">
+           <item row="0" column="2">
+            <widget class="QToolButton" name="zoom_in">
+             <property name="sizePolicy">
+              <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+               <horstretch>0</horstretch>
+               <verstretch>0</verstretch>
+              </sizepolicy>
+             </property>
+             <property name="toolTip">
+              <string>Vergrößern (z)</string>
+             </property>
+             <property name="text">
+              <string>...</string>
+             </property>
+             <property name="icon">
+              <iconset resource="icons.qrc">
+               <normaloff>:/icons/lupe_in.png</normaloff>:/icons/lupe_in.png</iconset>
+             </property>
+             <property name="shortcut">
+              <string>Z</string>
+             </property>
+             <property name="checkable">
+              <bool>true</bool>
+             </property>
+            </widget>
+           </item>
+           <item row="0" column="6">
+            <widget class="QToolButton" name="move">
+             <property name="sizePolicy">
+              <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+               <horstretch>0</horstretch>
+               <verstretch>0</verstretch>
+              </sizepolicy>
+             </property>
+             <property name="toolTip">
+              <string>Verschieben (m)</string>
+             </property>
+             <property name="text">
+              <string>...</string>
+             </property>
+             <property name="icon">
+              <iconset resource="icons.qrc">
+               <normaloff>:/icons/move.png</normaloff>:/icons/move.png</iconset>
+             </property>
+             <property name="iconSize">
+              <size>
+               <width>20</width>
+               <height>20</height>
+              </size>
+             </property>
+             <property name="shortcut">
+              <string>M</string>
+             </property>
+             <property name="checkable">
+              <bool>true</bool>
+             </property>
+             <property name="checked">
+              <bool>true</bool>
+             </property>
+             <property name="popupMode">
+              <enum>QToolButton::DelayedPopup</enum>
+             </property>
+             <property name="toolButtonStyle">
+              <enum>Qt::ToolButtonIconOnly</enum>
+             </property>
+             <property name="autoRaise">
+              <bool>false</bool>
+             </property>
+            </widget>
+           </item>
+           <item row="0" column="5">
+            <widget class="QToolButton" name="polygon">
+             <property name="sizePolicy">
+              <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+               <horstretch>0</horstretch>
+               <verstretch>0</verstretch>
+              </sizepolicy>
+             </property>
+             <property name="toolTip">
+              <string>Neues Objekt (n)</string>
+             </property>
+             <property name="text">
+              <string>...</string>
+             </property>
+             <property name="icon">
+              <iconset resource="icons.qrc">
+               <normaloff>:/icons/polygon.png</normaloff>:/icons/polygon.png</iconset>
+             </property>
+             <property name="shortcut">
+              <string>N</string>
+             </property>
+             <property name="checkable">
+              <bool>true</bool>
+             </property>
+            </widget>
+           </item>
+           <item row="0" column="1">
+            <widget class="QToolButton" name="zoom_out">
+             <property name="sizePolicy">
+              <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+               <horstretch>0</horstretch>
+               <verstretch>0</verstretch>
+              </sizepolicy>
+             </property>
+             <property name="toolTip">
+              <string>Vergrößerung zurücksetzen</string>
+             </property>
+             <property name="text">
+              <string>...</string>
+             </property>
+             <property name="icon">
+              <iconset resource="icons.qrc">
+               <normaloff>:/icons/lupe_out.png</normaloff>:/icons/lupe_out.png</iconset>
+             </property>
+             <property name="checkable">
+              <bool>false</bool>
+             </property>
+             <property name="checked">
+              <bool>false</bool>
+             </property>
+             <property name="autoRaise">
+              <bool>false</bool>
+             </property>
+            </widget>
+           </item>
+           <item row="1" column="2">
+            <widget class="QToolButton" name="next">
+             <property name="sizePolicy">
+              <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+               <horstretch>0</horstretch>
+               <verstretch>0</verstretch>
+              </sizepolicy>
+             </property>
+             <property name="toolTip">
+              <string>nächstes Bild</string>
+             </property>
+             <property name="text">
+              <string>...</string>
+             </property>
+             <property name="icon">
+              <iconset resource="icons.qrc">
+               <normaloff>:/icons/next.png</normaloff>:/icons/next.png</iconset>
+             </property>
+             <property name="shortcut">
+              <string>Right</string>
+             </property>
+             <property name="checkable">
+              <bool>false</bool>
+             </property>
+            </widget>
+           </item>
+           <item row="1" column="5">
+            <widget class="QToolButton" name="pipette">
+             <property name="sizePolicy">
+              <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+               <horstretch>0</horstretch>
+               <verstretch>0</verstretch>
+              </sizepolicy>
+             </property>
+             <property name="toolTip">
+              <string>Kopieren (c)</string>
+             </property>
+             <property name="text">
+              <string>...</string>
+             </property>
+             <property name="icon">
+              <iconset resource="icons.qrc">
+               <normaloff>:/icons/pipette.png</normaloff>:/icons/pipette.png</iconset>
+             </property>
+             <property name="shortcut">
+              <string>C</string>
+             </property>
+             <property name="checkable">
+              <bool>true</bool>
+             </property>
+            </widget>
+           </item>
+           <item row="1" column="6">
+            <widget class="QToolButton" name="select">
+             <property name="sizePolicy">
+              <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+               <horstretch>0</horstretch>
+               <verstretch>0</verstretch>
+              </sizepolicy>
+             </property>
+             <property name="toolTip">
+              <string>Objekte Verstecken (s)</string>
+             </property>
+             <property name="text">
+              <string>...</string>
+             </property>
+             <property name="icon">
+              <iconset resource="icons.qrc">
+               <normaloff>:/icons/select.png</normaloff>:/icons/select.png</iconset>
+             </property>
+             <property name="shortcut">
+              <string>S</string>
+             </property>
+             <property name="checkable">
+              <bool>true</bool>
+             </property>
+             <property name="checked">
+              <bool>false</bool>
+             </property>
+            </widget>
+           </item>
+           <item row="1" column="1">
+            <widget class="QToolButton" name="before">
+             <property name="sizePolicy">
+              <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+               <horstretch>0</horstretch>
+               <verstretch>0</verstretch>
+              </sizepolicy>
+             </property>
+             <property name="toolTip">
+              <string>vorheriges Bild</string>
+             </property>
+             <property name="text">
+              <string>...</string>
+             </property>
+             <property name="icon">
+              <iconset resource="icons.qrc">
+               <normaloff>:/icons/before.png</normaloff>:/icons/before.png</iconset>
+             </property>
+             <property name="shortcut">
+              <string>Left</string>
+             </property>
+             <property name="checkable">
+              <bool>false</bool>
+             </property>
+            </widget>
+           </item>
+           <item row="1" column="4">
+            <widget class="QToolButton" name="remove">
+             <property name="sizePolicy">
+              <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+               <horstretch>0</horstretch>
+               <verstretch>0</verstretch>
+              </sizepolicy>
+             </property>
+             <property name="toolTip">
+              <string>Löschen (d)</string>
+             </property>
+             <property name="text">
+              <string>...</string>
+             </property>
+             <property name="icon">
+              <iconset resource="icons.qrc">
+               <normaloff>:/icons/delete.png</normaloff>:/icons/delete.png</iconset>
+             </property>
+             <property name="shortcut">
+              <string>D</string>
+             </property>
+             <property name="checkable">
+              <bool>true</bool>
+             </property>
+            </widget>
+           </item>
+           <item row="0" column="4">
+            <widget class="QToolButton" name="cut">
+             <property name="sizePolicy">
+              <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+               <horstretch>0</horstretch>
+               <verstretch>0</verstretch>
+              </sizepolicy>
+             </property>
+             <property name="toolTip">
+              <string>Zerschneiden (x)</string>
+             </property>
+             <property name="text">
+              <string>...</string>
+             </property>
+             <property name="icon">
+              <iconset resource="icons.qrc">
+               <normaloff>:/icons/cut.png</normaloff>:/icons/cut.png</iconset>
+             </property>
+             <property name="shortcut">
+              <string>X</string>
+             </property>
+             <property name="checkable">
+              <bool>true</bool>
+             </property>
+            </widget>
+           </item>
+          </layout>
+         </widget>
+        </item>
+        <item>
+         <layout class="QVBoxLayout" name="verticalLayout_3">
+          <item>
+           <widget class="QTreeView" name="framesTree"/>
+          </item>
+         </layout>
+        </item>
+       </layout>
+      </item>
+      <item>
+       <widget class="QWidget" name="Widget" native="true">
+        <property name="sizePolicy">
+         <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+          <horstretch>70</horstretch>
+          <verstretch>0</verstretch>
+         </sizepolicy>
+        </property>
+        <layout class="QVBoxLayout" name="verticalLayout_4">
+         <property name="spacing">
+          <number>0</number>
+         </property>
+         <property name="leftMargin">
+          <number>0</number>
+         </property>
+         <property name="topMargin">
+          <number>0</number>
+         </property>
+         <property name="rightMargin">
+          <number>0</number>
+         </property>
+         <property name="bottomMargin">
+          <number>0</number>
+         </property>
+         <item>
+          <layout class="QVBoxLayout" name="verticalLayout_2">
+           <item>
+            <widget class="QWidget" name="viewWidget" native="true">
+             <property name="sizePolicy">
+              <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+               <horstretch>3</horstretch>
+               <verstretch>0</verstretch>
+              </sizepolicy>
+             </property>
+             <layout class="QVBoxLayout" name="verticalLayout_6"/>
+            </widget>
+           </item>
+           <item>
+            <widget class="QWidget" name="textWidget" native="true">
+             <property name="maximumSize">
+              <size>
+               <width>16777215</width>
+               <height>100</height>
+              </size>
+             </property>
+             <property name="baseSize">
+              <size>
+               <width>0</width>
+               <height>0</height>
+              </size>
+             </property>
+             <layout class="QGridLayout" name="gridLayout_3">
+              <item row="0" column="0">
+               <widget class="QLabel" name="toolTitel">
+                <property name="maximumSize">
+                 <size>
+                  <width>16777215</width>
+                  <height>20</height>
+                 </size>
+                </property>
+                <property name="text">
+                 <string>TextLabel</string>
+                </property>
+               </widget>
+              </item>
+              <item row="1" column="0">
+               <widget class="QLabel" name="toolBeschreibung">
+                <property name="text">
+                 <string>TextLabel</string>
+                </property>
+               </widget>
+              </item>
+             </layout>
+            </widget>
+           </item>
+          </layout>
+         </item>
+        </layout>
+       </widget>
+      </item>
+     </layout>
+    </item>
+   </layout>
+  </widget>
+  <widget class="QMenuBar" name="menuBar">
+   <property name="geometry">
+    <rect>
+     <x>0</x>
+     <y>0</y>
+     <width>1119</width>
+     <height>27</height>
+    </rect>
+   </property>
+   <widget class="QMenu" name="menuFile">
+    <property name="title">
+     <string>Datei</string>
+    </property>
+    <addaction name="actionOpen"/>
+    <addaction name="actionSave"/>
+   </widget>
+   <widget class="QMenu" name="menuOptionen">
+    <property name="title">
+     <string>Optionen</string>
+    </property>
+    <addaction name="actionServer_address"/>
+    <addaction name="actionKlassen_verwalten"/>
+   </widget>
+   <addaction name="menuFile"/>
+   <addaction name="menuOptionen"/>
+  </widget>
+  <widget class="QToolBar" name="mainToolBar">
+   <attribute name="toolBarArea">
+    <enum>TopToolBarArea</enum>
+   </attribute>
+   <attribute name="toolBarBreak">
+    <bool>false</bool>
+   </attribute>
+   <addaction name="actionOpen"/>
+   <addaction name="actionSave"/>
+   <addaction name="actionMaske_bearbeiten"/>
+   <addaction name="separator"/>
+   <addaction name="actionMaske_anzeigen"/>
+   <addaction name="actionObjekte_faerben"/>
+   <addaction name="actionIDs_anzeigen"/>
+  </widget>
+  <widget class="QStatusBar" name="statusBar"/>
+  <action name="actionOpen">
+   <property name="checkable">
+    <bool>false</bool>
+   </property>
+   <property name="checked">
+    <bool>false</bool>
+   </property>
+   <property name="text">
+    <string>Öffnen</string>
+   </property>
+   <property name="toolTip">
+    <string>Open annotation</string>
+   </property>
+  </action>
+  <action name="actionNext">
+   <property name="text">
+    <string>Vor</string>
+   </property>
+   <property name="toolTip">
+    <string>got to next picture</string>
+   </property>
+   <property name="shortcut">
+    <string>Right</string>
+   </property>
+  </action>
+  <action name="actionBefore">
+   <property name="text">
+    <string>zurück</string>
+   </property>
+   <property name="toolTip">
+    <string>Go to previous picture</string>
+   </property>
+   <property name="shortcut">
+    <string>Left</string>
+   </property>
+  </action>
+  <action name="actionSave">
+   <property name="text">
+    <string>Speichern</string>
+   </property>
+  </action>
+  <action name="actionServer_address">
+   <property name="text">
+    <string>Server Adresse</string>
+   </property>
+  </action>
+  <action name="actionKlassen_verwalten">
+   <property name="text">
+    <string>Klassen verwalten</string>
+   </property>
+  </action>
+  <action name="actionMaske_anzeigen">
+   <property name="checkable">
+    <bool>true</bool>
+   </property>
+   <property name="checked">
+    <bool>true</bool>
+   </property>
+   <property name="text">
+    <string>Maske anzeigen</string>
+   </property>
+   <property name="toolTip">
+    <string>Zeigt die Maske in der Arbeitsansicht an</string>
+   </property>
+   <property name="shortcut">
+    <string>A</string>
+   </property>
+  </action>
+  <action name="actionObjekte_faerben">
+   <property name="checkable">
+    <bool>true</bool>
+   </property>
+   <property name="checked">
+    <bool>false</bool>
+   </property>
+   <property name="text">
+    <string>Objekte färben</string>
+   </property>
+   <property name="toolTip">
+    <string>Ordnet den Objekten je nach Objekt Id eine Farbe zu</string>
+   </property>
+   <property name="shortcut">
+    <string>F</string>
+   </property>
+  </action>
+  <action name="actionIDs_anzeigen">
+   <property name="checkable">
+    <bool>true</bool>
+   </property>
+   <property name="checked">
+    <bool>true</bool>
+   </property>
+   <property name="text">
+    <string>IDs anzeigen</string>
+   </property>
+   <property name="shortcut">
+    <string>I</string>
+   </property>
+   <property name="visible">
+    <bool>true</bool>
+   </property>
+  </action>
+  <action name="actionMaske_bearbeiten">
+   <property name="text">
+    <string>Maske bearbeiten</string>
+   </property>
+   <property name="shortcut">
+    <string>E</string>
+   </property>
+   <property name="visible">
+    <bool>true</bool>
+   </property>
+  </action>
+ </widget>
+ <layoutdefault spacing="6" margin="11"/>
+ <resources>
+  <include location="icons.qrc"/>
+ </resources>
+ <connections/>
+</ui>

+ 84 - 0
annotationGUI/mask.cpp

@@ -0,0 +1,84 @@
+#include "mask.h"
+#include <QDebug>
+#include <QPixmap>
+#include <QBitmap>
+
+Mask::Mask( QString p )
+    : path( p ),
+      mask( 0 )
+{}
+
+Mask::~Mask()
+{
+    unloadMask();
+}
+
+// Lädt das Masken Bild
+void Mask::loadMask()
+{
+    if( !mask )
+    {
+        mask = new QImage( path );
+    }
+}
+
+// Gibt das Maskenbild zurück
+QImage *Mask::getMask()
+{
+    return mask;
+}
+
+// Gibt ein Bild zurück, indem der Weiße Teil transparent ist
+QPixmap Mask::getDrawableImage()
+{
+    QPixmap mp( path );
+    mp.setMask( mp.createMaskFromColor( QColor( 0, 0, 0 ), Qt::MaskOutColor ) );
+    return mp;
+}
+
+// Prüft, ob ein Polygon im verbotenen Bereich liegt
+bool Mask::isPolygonInside( QPolygon p )
+{
+    loadMask();
+    int pCount = p.count();
+    for( int i = 0; i< pCount; i++ )
+    {
+        if( mask->pixel( p.at( i ).x(), p.at( i ).y() ) != 0xFF000000 )
+            return true;
+    }
+    return false;
+}
+
+// Erstellt eine neue Maske
+void Mask::createMask( QSize s )
+{
+    if( mask )
+        delete mask;
+    mask = new QImage( s, QImage::Format_RGB32 );
+    mask->fill( Qt::white );
+}
+
+// Speichert die Maske
+void Mask::save() const
+{
+    mask->save( path );
+}
+
+// Erzeugt für ein Bild das JPEGImage bild
+//  img: Das Bild zu dem das JPEGImage generiert werden soll
+//  frameName: Der Name des Bildes
+void Mask::saveToFrameMask( cv::Mat img, QString frameName ) const
+{
+    cv::Mat result;
+    cv::Mat mask = cv::imread( path.toStdString() );
+    cv::bitwise_and( img, mask, result );
+    qDebug() << path.mid( 0, path.lastIndexOf( "/", path.lastIndexOf( "/" ) - 1 ) + 1 ) + "JPEGImages/" + frameName;
+    cv::imwrite( (path.mid( 0, path.lastIndexOf( "/", path.lastIndexOf( "/" ) - 1 ) + 1 ) + "JPEGImages/" + frameName).toStdString(), result );
+}
+
+// Löscht die Maske aus dem Speicher
+void Mask::unloadMask()
+{
+    delete mask;
+    mask = 0;
+}

+ 41 - 0
annotationGUI/mask.h

@@ -0,0 +1,41 @@
+#ifndef MASK_H
+#define MASK_H
+
+#include <QImage>
+#include <QString>
+#include <opencv2/opencv.hpp>
+
+/*
+ * Verwaltet die Maske einer Kamera
+ */
+class Mask
+{
+private:
+    QString path; // Der Pfad zum Maskenbild
+    QImage *mask; // Das Maskenbild
+
+public:
+    Mask( QString p );
+    ~Mask();
+
+    // Lädt das Masken Bild
+    void loadMask();
+    // Gibt das Maskenbild zurück
+    QImage *getMask();
+    // Gibt ein Bild zurück, indem der Weiße Teil transparent ist
+    QPixmap getDrawableImage();
+    // Prüft, ob ein Polygon im verbotenen Bereich liegt
+    bool isPolygonInside( QPolygon p );
+    // Erstellt eine neue Maske
+    void createMask( QSize s );
+    // Speichert die Maske
+    void save() const;
+    // Erzeugt für ein Bild das JPEGImage bild
+    //  img: Das Bild zu dem das JPEGImage generiert werden soll
+    //  frameName: Der Name des Bildes
+    void saveToFrameMask( cv::Mat img, QString frameName ) const;
+    // Löscht die Maske aus dem Speicher
+    void unloadMask();
+};
+
+#endif // MASK_H

+ 434 - 0
annotationGUI/model.cpp

@@ -0,0 +1,434 @@
+#include "model.h"
+#include "arbeitsview.h"
+
+Model::Model()
+{}
+
+// Fügt eine View hinzu
+void Model::addView( QWidget *v )
+{
+    views.append( v );
+}
+
+// Aktualisiert die Views
+void Model::notifyViews()
+{
+    for( QWidget *v : views )
+        v->update();
+}
+
+// Inhalt der ArbeitsModel Klasse
+//--------------------------------
+
+ArbeitsModel::ArbeitsModel( MainWindow *w )
+    : f( 0 ),
+    deleteField( 0 ),
+    mode( MOVE ),
+    newPolygon( 0 ),
+    window( w ),
+    xScaleFactor( 1 ),
+    yScaleFactor( 1 ),
+    xOffset( 0 ),
+    yOffset( 0 ),
+    mousePressed( false ),
+    rotation( 0 ),
+    cutIndex( -1 ),
+    showMask( 1 ),
+    insertIndex( -1 ),
+    moveIndex( -1 ),
+    showColors( 0 ),
+    showIds( 1 )
+{}
+
+ArbeitsModel::~ArbeitsModel()
+{
+    delete deleteField;
+}
+
+// Transformiert einen Punkt von Bildkoordinaten nach Bildschirmkoordinaten
+QPoint ArbeitsModel::translate( QPoint p )
+{
+    return QPoint( (int)((p.x()-xOffset) * xScaleFactor), (int)((p.y()-yOffset) * yScaleFactor) );
+}
+
+// Transformiert ein Polygon von Bildkoordinaten nach Bildschirmkoordinaten
+QPolygon ArbeitsModel::translatePolygon( QPolygon p )
+{
+    QPolygon result;
+    for( auto point = p.begin(); point != p.end(); point++ )
+        result.append( translate( *point ) );
+    return result;
+}
+
+// Transformiert einen Punkt von Bildschirmkoordinaten nach Bildkoordinaten
+QPoint ArbeitsModel::inverseTranslate( QPoint p )
+{
+    return QPoint( (int)(p.x() / xScaleFactor + xOffset), (int)(p.y() / yScaleFactor + yOffset) );
+}
+
+// Setzt das ausgewählte Frame
+void ArbeitsModel::setFrame( Frame *frame )
+{
+    this->f = frame;
+    if( f )
+    {
+        image = f->getImage();
+        if( xScaleFactor == INFINITY || yScaleFactor == INFINITY )
+            resetZoom();
+    }
+}
+
+// Setzt den ausgewählten Modus
+void ArbeitsModel::setMode( UserMode mode )
+{
+    moveObject = ObjectPolygon();
+    cutObject = ObjectPolygon();
+    cutIndex = 0;
+    if( mode != PIPETTE_SET )
+        copyedObject = ObjectPolygon();
+    this->mode = mode;
+    if( mode != SELECT && mode != DELETE && deleteField )
+    {
+        delete deleteField;
+        deleteField = 0;
+    }
+}
+
+// Gibt den ausgewählten Modus zurück
+UserMode ArbeitsModel::getMode() const
+{
+    return mode;
+}
+
+// Legt fest, ob die Objekt IDs angezeigt werden sollen
+void ArbeitsModel::setShowId( bool sid )
+{
+   showIds = sid;
+}
+
+// Gibt 1 zurück, falls die ObjektIDs angezeigt werden sollen
+bool ArbeitsModel::areIdsShown() const
+{
+    return showIds;
+}
+
+// Legt fest, ob die Maske angezeigt werden soll
+void ArbeitsModel::setShowMask( bool sm )
+{
+    showMask = sm;
+}
+
+// Gibt 1 zurück, wenn die Maske angezeigt werden soll
+bool ArbeitsModel::isMaskShown() const
+{
+    return showMask;
+}
+
+// Legt fest, ob die Objekte eingefärbt werden sollen
+void ArbeitsModel::setShowColors( bool sc )
+{
+    showColors = sc;
+}
+
+// Gibt 1 zurück, falls die Objekte eingefärbt werden sollen
+bool ArbeitsModel::areColoresShown() const
+{
+    return showColors;
+}
+
+// Setzt das Maskenbild
+void ArbeitsModel::setMask( QPixmap m )
+{
+    this->m = m;
+}
+
+// Gibt das Maskenbild zurück
+QPixmap ArbeitsModel::getMask() const
+{
+    return m;
+}
+
+
+// Gibt das ausgewählte Bild zurück
+Frame *ArbeitsModel::getFrame() const
+{
+    return f;
+}
+
+// Gibt das Hauptfenster zurück
+MainWindow *ArbeitsModel::getWindow() const
+{
+    return window;
+}
+
+// Gibt das angezeigte Bild zurück
+QImage ArbeitsModel::getImage() const
+{
+    return image;
+}
+
+
+// Legt fest, ob die Maus gedrück wird
+void ArbeitsModel::setMousePressed( bool pressed )
+{
+    mousePressed = pressed;
+}
+
+// Legt die Maustaste fest
+void ArbeitsModel::setMouseButtonPressed( Qt::MouseButton btn, QPoint pos )
+{
+    mousePressed = true;
+    mouseButton = btn;
+    mouseStart = pos;
+    mousePos = pos;
+}
+
+// Setzt die Position der Maus fest
+void ArbeitsModel::setMousePoint( QPoint mp )
+{
+    mousePos = mp;
+}
+
+// Gibt 1 zurück, falls die MAus gedrückt wird
+bool ArbeitsModel::isMousePressed() const
+{
+    return mousePressed;
+}
+
+// Gibt 1 zurück, falls der angegebene Maus Knopf gedrückt wird
+bool ArbeitsModel::isMouseButtonPressed( Qt::MouseButton btn ) const
+{
+    return mouseButton == btn && mousePressed;
+}
+
+// Gibt die Position der Maus zurück, als diese gedrückt wurde
+QPoint ArbeitsModel::getMousePressPoint() const
+{
+    return mouseStart;
+}
+
+// Gibt die aktuelle Position der Maus zurück
+QPoint ArbeitsModel::getMousePoint() const
+{
+    return mousePos;
+}
+
+// Setzt die Position im Bild, die links oben an der View erscheinen soll
+void ArbeitsModel::setOffset( int xo, int yo )
+{
+    xOffset = xo;
+    yOffset = yo;
+}
+
+// Setzt den Skallierungsfaktor, mit dem das Bild vergrößert werden soll
+void ArbeitsModel::setScaleFactor( float xs, float ys )
+{
+    xScaleFactor = xs;
+    yScaleFactor = ys;
+}
+
+// Setzt die Position und die Größe der View auf dem Bildschirm
+void ArbeitsModel::setView( QPoint pos, QSize size )
+{
+    if( viewPos != pos || viewSize != size )
+    {
+        viewPos = pos;
+        viewSize = size;
+        resetZoom();
+    }
+}
+
+// Gibt die Größe der View zurück
+QSize ArbeitsModel::getViewSize() const
+{
+    return viewSize;
+}
+
+// Gibt die x Position im Bild zurück, die links an der View erscheinen soll
+int ArbeitsModel::getXOffset() const
+{
+    return xOffset;
+}
+
+// Gibt die y Position im Bild zurück, die oben an der View erscheinen soll
+int ArbeitsModel::getYOffset() const
+{
+    return yOffset;
+}
+
+// Gibt den Skallierungsfaktor in x Richtung zurück
+float ArbeitsModel::getXScaleFactor() const
+{
+    return xScaleFactor;
+}
+
+// Gibt den Skallierungsfaktor in y Richtung zurück
+float ArbeitsModel::getYScaleFactor() const
+{
+    return yScaleFactor;
+}
+
+// Gibt die Position der View zurück
+QPoint ArbeitsModel::getViewPos() const
+{
+    return viewPos;
+}
+
+// Setzt die Skallierung zurück, so dass wieder alles sichtbar ist
+void ArbeitsModel::resetZoom()
+{
+    QRect imgRect( QPoint( 0, 0 ), viewSize );
+    QSize imgS = image.size();
+    if( imgS.width() > viewSize.width() )
+    {
+        imgS.scale( viewSize, Qt::KeepAspectRatio );
+        imgRect.setSize( imgS );
+    }
+    xScaleFactor = imgRect.width() / (float)image.width();
+    yScaleFactor = imgRect.height() / (float)image.height();
+    xOffset = 0;
+    yOffset = 0;
+}
+
+// Legt fest, welches Polygon verschoben werden soll
+void ArbeitsModel::setMoveObject( ObjectPolygon mo, int pIndex )
+{
+    moveObject = mo;
+    movePolygon = pIndex;
+}
+
+// Legt fest, welcher Eckpunkt verschoben werden soll
+void ArbeitsModel::setMoveIndex( int index )
+{
+    moveIndex = index;
+}
+
+// Legt fest, an welcher Stelle eine Eckpunkt eingefügt werden soll
+void ArbeitsModel::setInsertIndex( int index )
+{
+    insertIndex = index;
+}
+
+// Legt die Position des neuen Eckpunktes fest
+void ArbeitsModel::setNewVertex( QPoint vertex )
+{
+    newVertex = vertex;
+}
+
+// Gibt das Objekt zurück, welches verschoben wird
+ObjectPolygon ArbeitsModel::getMoveObject() const
+{
+    return moveObject;
+}
+
+// Gibt den Index des Eckpunktes zurück, der verschoben wird
+int ArbeitsModel::getMoveIndex() const
+{
+    return moveIndex;
+}
+
+// Gibt den Index des neuen Eckpunktes zurück
+int ArbeitsModel::getInsertIndex() const
+{
+    return insertIndex;
+}
+
+// Gibt die Position des neuen Eckpunktes zurück
+QPoint ArbeitsModel::getNewVertex() const
+{
+    return newVertex;
+}
+
+// Gibt den Index des Polygons zurück, welches verschoben wird
+int ArbeitsModel::getMovePolygon() const
+{
+    return movePolygon;
+}
+
+// Gibt das Objekt zurück, welches zerteilt werden soll
+ObjectPolygon ArbeitsModel::getCutObject() const
+{
+    return cutObject;
+}
+
+// Gibt den Index des Eckpunktes zurück, an dem das Objekt zerteilt werden soll
+int ArbeitsModel::getCutIndex() const
+{
+    return cutIndex;
+}
+
+// Gibt den Index des Polygons zurück, das zerteilt werden soll
+int ArbeitsModel::getCutPolygon() const
+{
+    return cutPolygon;
+}
+
+// Setzt das Polygon, welches zerteilt werden soll
+void ArbeitsModel::setCutObject( ObjectPolygon o, int pIndex )
+{
+    cutObject = o;
+    cutPolygon = pIndex;
+}
+
+// Setzt den Index des Eckpunktes, an dem das Polygon zerteilt werden soll
+void ArbeitsModel::setCutIndex( int index )
+{
+    cutIndex = index;
+}
+
+// Gibt das kopierte Objekt zurück
+ObjectPolygon ArbeitsModel::getCopyedObject() const
+{
+    return copyedObject;
+}
+
+// Gibt die Rotierung des kopierten Objektes zurück
+float ArbeitsModel::getCopyedRotation() const
+{
+    return rotation;
+}
+
+// Gibt den Mittelpunkt des Kopierten Objektes zurück
+QPoint ArbeitsModel::getCopyedCenter() const
+{
+    return pSCenter;
+}
+
+// Setzt das Kopierte Objekt
+void ArbeitsModel::setCopyedObject( ObjectPolygon o, QPoint c )
+{
+    copyedObject = o;
+    pSCenter = c;
+}
+
+// Setzt die Rotierung des kopierten Objektes
+void ArbeitsModel::setCopyedRotation( float r )
+{
+    rotation = r;
+}
+
+// Gibt das neue Polygon zurück, welches mit dem NEW Werkzeug erstellt wird
+QPolygon *ArbeitsModel::getNewPolygon() const
+{
+    return newPolygon;
+}
+
+// Setzt das neue Polygon
+void ArbeitsModel::setNewPolygon( QPolygon *p )
+{
+    if( newPolygon )
+        delete newPolygon;
+    newPolygon = p;
+}
+
+// Gibt das Feld zurück, in dem ale Eckpunkte gelöscht werden sollen
+QRect *ArbeitsModel::getDeleteField() const
+{
+    return deleteField;
+}
+
+// Setzt das Feld, in dem alle Echpunkte gelöscht werden sollen
+void ArbeitsModel::setDeleteField( QRect *f )
+{
+    deleteField = f;
+}

+ 217 - 0
annotationGUI/model.h

@@ -0,0 +1,217 @@
+#ifndef MODEL_H
+#define MODEL_H
+
+#include <QList>
+#include <QWidget>
+#include "sequenz.h"
+#include "usermode.h"
+
+/*
+ * Eine Abstrakte Model Klasse
+ */
+class Model
+{
+private:
+    QList< QWidget* > views; // Liste mit Views, welche aktualisiert werden sollen, falls sich das Model ändert
+
+public:
+    Model();
+
+    // Fügt eine View hinzu
+    void addView( QWidget *v );
+    // Aktualisiert die Views
+    void notifyViews();
+};
+
+class MainWindow; // aus mainwindow.h
+
+/*
+ * Enthält alle Daten, die für die Arbeitsfläche relevant sind
+ */
+class ArbeitsModel : public Model
+{
+private:
+
+    Frame  *f; // Einen Zeiger auf das ausgewählte Bild aus der Bildsequenz
+    QPixmap m; // Das Maskenbild
+    QImage  image; // Das ausgewählte Bild
+    bool    showIds; // 1, falls Objekt IDs angezeigt werden sollen
+    bool    showMask; // 1, falls das Maskenbild angezeigt werden soll
+    bool    showColors; // 1, falls die Objkte eingefärbt werden sollen
+    MainWindow *window; // Einen Zeiger auf das Hauptfenster
+
+    // delete
+    QRect *deleteField; // Das gebiet, welches zum löschen ausgewählt wird
+
+    // new
+    QPolygon *newPolygon; // Das neue Polygon, welches mit dem Werkzeug New erstellt wird
+
+    // copy
+    ObjectPolygon copyedObject; // Das copierte Polygon, welches mit dem Kopieren und Einfügen Werkzeug kopiert wurde
+    float  rotation; // Die Rotation des kopierten Objektes
+    QPoint pSCenter; // Der Mittelpunkt des kopierten Objektes
+
+    // Cut
+    ObjectPolygon cutObject; // Das Objekt, welches zerteilt werden soll
+    int cutPolygon; // Der Index des Polygons, welches zerteilt werden soll
+    int cutIndex; // Der Index des Eckpunktes, an dem das Objekt zerteilt werden soll
+
+    // Move
+    ObjectPolygon moveObject; // Das Objekt, wessen Eckpunkt gerade verschoben wird
+    int movePolygon; // Der Index des Polygones, dessen Eckpunkt verschoben wird
+    QPoint newVertex; // Der Punkt, an dem ein neuer Vertex eingefügt werden würde
+    int    insertIndex; // Der Index in dem Polygon, an dem der Eckpunkt eingefügt wird
+    int    moveIndex; // Der Index des Eckpunktes, der verschoben werden soll
+
+    // viewport Information
+    QPoint viewPos; // Die Position der View auf dem Bildschirm
+    QSize  viewSize; // Die Größe der View auf dem Bildschirm
+    float  xScaleFactor; // Der x Skallierungsfaktor, mit dem die ansicht vergrößert wird
+    float  yScaleFactor; // Der y Skallierungsfaktor, mit dem die ansicht vergrößert wird
+    int    xOffset; // Die x Position im Bild, welche links oben an der View gezeichnet wird
+    int    yOffset; // Die y Position im Bild, welche links oben an der View gezeichnet wird
+
+    // controller Information
+    bool mousePressed; // 1, falls eine Maustaste gedrückt wird
+    Qt::MouseButton mouseButton; // Der gedrpckte Mausbutton
+    UserMode mode; // Der Modus, in dem sich der Nutzer momentan befindet
+    QPoint   mouseStart; // Die Startposition der Maus beim Klicken
+    QPoint   mousePos; // Die momentane Mausposition
+
+public:
+
+    ArbeitsModel(MainWindow *w);
+    ~ArbeitsModel();
+
+    // Transformiert einen Punkt von Bildkoordinaten nach Bildschirmkoordinaten
+    QPoint        translate(QPoint p);
+    // Transformiert ein Polygon von Bildkoordinaten nach Bildschirmkoordinaten
+    QPolygon      translatePolygon(QPolygon p);
+    // Transformiert einen Punkt von Bildschirmkoordinaten nach Bildkoordinaten
+    QPoint        inverseTranslate(QPoint p);
+    // Setzt das ausgewählte Frame
+    void          setFrame(Frame *f);
+    // Setzt den ausgewählten Modus
+    void          setMode(UserMode mode);
+    // Gibt den ausgewählten Modus zurück
+    UserMode      getMode() const;
+    // Legt fest, ob die Objekt IDs angezeigt werden sollen
+    void          setShowId(bool sid);
+    // Gibt 1 zurück, falls die ObjektIDs angezeigt werden sollen
+    bool          areIdsShown() const;
+    // Legt fest, ob die Maske angezeigt werden soll
+    void          setShowMask(bool sm);
+    // Gibt 1 zurück, wenn die Maske angezeigt werden soll
+    bool          isMaskShown() const;
+    // Legt fest, ob die Objekte eingefärbt werden sollen
+    void          setShowColors(bool sc);
+    // Gibt 1 zurück, falls die Objekte eingefärbt werden sollen
+    bool          areColoresShown() const;
+    // Setzt das Maskenbild
+    void          setMask(QPixmap m);
+    // Gibt das Maskenbild zurück
+    QPixmap       getMask() const;
+
+    // Gibt das ausgewählte Bild zurück
+    Frame*        getFrame() const;
+    // Gibt das Hauptfenster zurück
+    MainWindow*   getWindow() const;
+    // Gibt das angezeigte Bild zurück
+    QImage        getImage() const;
+
+    // Legt fest, ob die Maus gedrück wird
+    void          setMousePressed(bool pressed);
+    // Legt die Maustaste fest
+    void          setMouseButtonPressed(Qt::MouseButton btn,
+                                        QPoint          pos);
+    // Setzt die Position der Maus fest
+    void          setMousePoint(QPoint mp);
+    // Gibt 1 zurück, falls die MAus gedrückt wird
+    bool          isMousePressed() const;
+    // Gibt 1 zurück, falls der angegebene Maus Knopf gedrückt wird
+    bool          isMouseButtonPressed(Qt::MouseButton btn) const;
+    // Gibt die Position der Maus zurück, als diese gedrückt wurde
+    QPoint        getMousePressPoint() const;
+    // Gibt die aktuelle Position der Maus zurück
+    QPoint        getMousePoint() const;
+
+    // Setzt die Position im Bild, die links oben an der View erscheinen soll
+    void          setOffset(int xo,
+                            int yo);
+    // Setzt den Skallierungsfaktor, mit dem das Bild vergrößert werden soll
+    void          setScaleFactor(float xs,
+                                 float ys);
+    // Setzt die Position und die Größe der View auf dem Bildschirm
+    void          setView(QPoint pos,
+                          QSize  size);
+    // Gibt die Größe der View zurück
+    QSize         getViewSize() const;
+    // Gibt die x Position im Bild zurück, die links an der View erscheinen soll
+    int           getXOffset() const;
+    // Gibt die y Position im Bild zurück, die oben an der View erscheinen soll
+    int           getYOffset() const;
+    // Gibt den Skallierungsfaktor in x Richtung zurück
+    float         getXScaleFactor() const;
+    // Gibt den Skallierungsfaktor in y Richtung zurück
+    float         getYScaleFactor() const;
+    // Gibt die Position der View zurück
+    QPoint        getViewPos() const;
+    // Setzt die Skallierung zurück, so dass wieder alles sichtbar ist
+    void          resetZoom();
+
+    // Legt fest, welches Polygon verschoben werden soll
+    void          setMoveObject(ObjectPolygon mo,
+                                int           pIndex);
+    // Legt fest, welcher Eckpunkt verschoben werden soll
+    void          setMoveIndex(int index);
+    // Legt fest, an welcher Stelle eine Eckpunkt eingefügt werden soll
+    void          setInsertIndex(int index);
+    // Legt die Position des neuen Eckpunktes fest
+    void          setNewVertex(QPoint vertex);
+    // Gibt das Objekt zurück, welches verschoben wird
+    ObjectPolygon getMoveObject() const;
+    // Gibt den Index des Eckpunktes zurück, der verschoben wird
+    int           getMoveIndex() const;
+    // Gibt den Index des neuen Eckpunktes zurück
+    int           getInsertIndex() const;
+    // Gibt die Position des neuen Eckpunktes zurück
+    QPoint        getNewVertex() const;
+    // Gibt den Index des Polygons zurück, welches verschoben wird
+    int           getMovePolygon() const;
+
+    // Gibt das Objekt zurück, welches zerteilt werden soll
+    ObjectPolygon getCutObject() const;
+    // Gibt den Index des Eckpunktes zurück, an dem das Objekt zerteilt werden soll
+    int           getCutIndex() const;
+    // Gibt den Index des Polygons zurück, das zerteilt werden soll
+    int           getCutPolygon() const;
+    // Setzt das Polygon, welches zerteilt werden soll
+    void          setCutObject(ObjectPolygon o,
+                               int           pIndex);
+    // Setzt den Index des Eckpunktes, an dem das Polygon zerteilt werden soll
+    void          setCutIndex(int index);
+
+    // Gibt das kopierte Objekt zurück
+    ObjectPolygon getCopyedObject() const;
+    // Gibt die Rotierung des kopierten Objektes zurück
+    float         getCopyedRotation() const;
+    // Gibt den Mittelpunkt des Kopierten Objektes zurück
+    QPoint        getCopyedCenter() const;
+    // Setzt das Kopierte Objekt
+    void          setCopyedObject(ObjectPolygon o,
+                                  QPoint        c);
+    // Setzt die Rotierung des kopierten Objektes
+    void          setCopyedRotation(float r);
+
+    // Gibt das neue Polygon zurück, welches mit dem NEW Werkzeug erstellt wird
+    QPolygon*     getNewPolygon() const;
+    // Setzt das neue Polygon
+    void          setNewPolygon(QPolygon *p);
+
+    // Gibt das Feld zurück, in dem ale Eckpunkte gelöscht werden sollen
+    QRect*        getDeleteField() const;
+    // Setzt das Feld, in dem alle Echpunkte gelöscht werden sollen
+    void          setDeleteField(QRect *f);
+};
+
+#endif // MODEL_H

+ 56 - 0
annotationGUI/newsequenz.cpp

@@ -0,0 +1,56 @@
+#include "newsequenz.h"
+#include "ui_newsequenz.h"
+#include <QLineEdit>
+#include <QIntValidator>
+
+NewSequenz::NewSequenz( int max, QWidget *parent) :
+    QDialog(parent),
+    ui(new Ui::NewSequenz),
+    offset( 0 ),
+    limit( 0 )
+{
+    ui->setupUi(this);
+    ui->label->setText( "Es wurden " + QString::number( max ) + " Bilder gefunden." );
+    ui->min->setValidator( new QIntValidator(1, max, this) );
+    ui->max->setValidator( new QIntValidator(1, max, this) );
+    ui->min->setText( "1" );
+    ui->max->setText( QString::number( max ) );
+}
+
+NewSequenz::~NewSequenz()
+{
+    delete ui;
+}
+
+// Gibt das Offset zurück, ab dem die Bilder in den neuen Datensatz übertragen werden sollen
+int NewSequenz::getOffset() const
+{
+    return offset;
+}
+
+// Gibt die Anzahl der Bilder zurück, die in den neuen Datensatz übertragen werden sollen
+int NewSequenz::getLimit() const
+{
+    return limit;
+}
+
+// Schließt das Dialogfenster
+void NewSequenz::on_ok_clicked()
+{
+    if( !ui->min->text().length() || !ui->max->text().length() )
+        return;
+    int offset = ui->min->text().toInt() - 1;
+    int limit = ui->max->text().toInt() - offset;
+    if( limit > 0 && offset >= 0 )
+    {
+        this->offset = offset;
+        this->limit = limit;
+        this->close();
+    }
+}
+
+// Schließt das Dialogfenster ohne angabe von Limit und Offset
+void NewSequenz::on_abbrechen_clicked()
+{
+    this->close();
+}

+ 38 - 0
annotationGUI/newsequenz.h

@@ -0,0 +1,38 @@
+#ifndef NEWSEQUENZ_H
+#define NEWSEQUENZ_H
+
+#include <QDialog>
+
+namespace Ui {
+class NewSequenz;
+}
+
+/*
+ * Fragt den Nutzer, welche Bilder in eine neue Sequenz übernommen werden sollen
+ */
+class NewSequenz : public QDialog
+{
+    Q_OBJECT
+
+public:
+    explicit NewSequenz( int max, QWidget *parent = 0);
+    ~NewSequenz();
+
+    // Gibt das Offset zurück, ab dem die Bilder in den neuen Datensatz übertragen werden sollen
+    int getOffset() const;
+    // Gibt die Anzahl der Bilder zurück, die in den neuen Datensatz übertragen werden sollen
+    int getLimit() const;
+
+private slots:
+    // Schließt das Dialogfenster
+    void on_ok_clicked();
+    // Schließt das Dialogfenster ohne angabe von Limit und Offset
+    void on_abbrechen_clicked();
+
+private:
+    Ui::NewSequenz *ui; // Ein Zeiger auf alle in newsequenz.ui spezifizierten Objekte
+    int offset; // Das vom Nutzer angegebene Offset
+    int limit; // Die vom Nutzer angegebene Bildanzahl
+};
+
+#endif // NEWSEQUENZ_H

+ 77 - 0
annotationGUI/newsequenz.ui

@@ -0,0 +1,77 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>NewSequenz</class>
+ <widget class="QDialog" name="NewSequenz">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>455</width>
+    <height>105</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Dialog</string>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout">
+   <item>
+    <widget class="QLabel" name="label">
+     <property name="text">
+      <string>Es wurden x Bilder gefunden.</string>
+     </property>
+    </widget>
+   </item>
+   <item>
+    <layout class="QHBoxLayout" name="horizontalLayout">
+     <item>
+      <widget class="QLabel" name="label_2">
+       <property name="text">
+        <string>Neue Sequenz mit den Bildern von </string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QLineEdit" name="min"/>
+     </item>
+     <item>
+      <widget class="QLabel" name="label_3">
+       <property name="text">
+        <string>bis</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QLineEdit" name="max"/>
+     </item>
+     <item>
+      <widget class="QLabel" name="label_4">
+       <property name="text">
+        <string>erstellen?</string>
+       </property>
+      </widget>
+     </item>
+    </layout>
+   </item>
+   <item>
+    <layout class="QHBoxLayout" name="horizontalLayout_2">
+     <item>
+      <widget class="QPushButton" name="abbrechen">
+       <property name="text">
+        <string>Abbrechen</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QPushButton" name="ok">
+       <property name="text">
+        <string>Ok</string>
+       </property>
+      </widget>
+     </item>
+    </layout>
+   </item>
+  </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>

+ 19 - 0
annotationGUI/newsequenz3.ui

@@ -0,0 +1,19 @@
+<?xml version='1.0'?>
+<ui version="4.0">
+ <class>NewSequenz</class>
+ <widget class="QDialog" name="NewSequenz">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>640</width>
+    <height>480</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Dialog</string>
+  </property>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>

+ 313 - 0
annotationGUI/object.cpp

@@ -0,0 +1,313 @@
+#define Global
+#include "object.h"
+#include <limits>
+
+Object::Object( QString id, int classId )
+    : id( id ),
+      classId( classId )
+{}
+
+// Setzt die Objekt-ID
+void Object::setId( QString id )
+{
+    this->id = id;
+}
+
+// Gibt die Objekt-ID zurück
+QString Object::getId() const
+{
+    return id;
+}
+
+// Setzt die ID der Klasse
+void Object::setClassId( int id )
+{
+    classId = id;
+}
+
+// Gibt die ID der Klasse zurück
+int Object::getClassId() const
+{
+    return classId;
+}
+
+// Vergleicht zwei Objekte anhand ihrer ID
+bool operator == (const Object &p1, const Object &p2)
+{
+    return p1.getId() == p2.getId();
+}
+
+// Inhalt der _ObjektPolygon Klasse
+//----------------------------------
+
+_ObjectPolygon::_ObjectPolygon( QString id, QPolygon po, bool truncated, int index, FrameTreeNode *parent )
+    : FrameTreeNode( index, parent, 2 ),
+      id( id ),
+      truncated( truncated ),
+      selected( 1 ),
+      ref( 1 )
+{
+    polygon.append( po );
+}
+
+// Setzt die Objekt-ID
+void _ObjectPolygon::setId( QString id )
+{
+    this->id = id;
+}
+
+// Legt fest, ob das Objekt versteckt ist (falls s = 0)
+void _ObjectPolygon::setSelected( bool s )
+{
+    this->selected = s;
+}
+
+// Entfernt alle Ecken in einer Region r. Gibt 0 zurück, fall das Objekt komplett entfernt werden soll
+bool _ObjectPolygon::removeVertices( QRect r )
+{
+    for( auto p = polygon.begin(); p != polygon.end(); p++ )
+    {
+        QPolygon &pol = *p;
+        int anz = pol.size();
+        for( int i = 0; i < anz; i++ )
+        {
+            QPoint v = pol.at( i );
+            if( r.contains( v ) )
+            {
+                pol.removeAt( i );
+                i--;
+                anz--;
+            }
+        }
+    }
+    int pAnz = polygon.size();
+    for( int j = 0; j < pAnz; j++ )
+    {
+        int anz = polygon.at( j ).size();
+        if( anz < 3 )
+        {
+            polygon.removeAt( j );
+            j--;
+            pAnz--;
+        }
+    }
+    return pAnz >= 1;
+}
+
+// Verschiebt den intex-ten Vertex aus dem pIndex-ten Polygon auf Position newPos.
+//  max: Die maximale Größe des Bildes. Falls newPos außerhalb des Bildes liegt, wird automatisch der nächste Punkt im Bild genommen.
+void _ObjectPolygon::moveVertex( int index, QPoint newPos, QSize max, int pIndex )
+{
+    int pI = 0;
+    for( auto p = polygon.begin(); p != polygon.end(); p++ )
+    {
+        QPolygon &pol = *p;
+        if( pI == pIndex )
+        {
+            int i = 0;
+            for( auto point = pol.begin(); point != pol.end(); point++, i++ )
+            {
+                if( i == index )
+                {
+                    *point = newPos;
+                    if( point->x() < 0 )
+                        point->setX( 0 );
+                    if( point->y() < 0 )
+                        point->setY( 0 );
+                    if( point->x() >= max.width() )
+                        point->setX( max.width() - 1 );
+                    if( point->y() >= max.height() )
+                        point->setY( max.height() - 1 );
+                }
+            }
+        }
+        pI++;
+    }
+}
+
+// Fügt an Slette index im pIndexten Polygon den Eckpunkt v ein
+void _ObjectPolygon::insertVertex( int index, QPoint v, int pIndex )
+{
+    int pI = 0;
+    for( auto p = polygon.begin(); p != polygon.end(); p++ )
+    {
+        QPolygon &pol = *p;
+        if( pI == pIndex )
+            pol.insert( index, v );
+        pI++;
+    }
+}
+
+// Spaltet das pIndexte Polygon indem zwischen dem begin-ten Eckpunkt und dem end-ten Eckpunkt geschnitten wird.
+//  Gibt das neu entstandene Polygon zurück (eine hälfte des alten, die aus diesem entfernt wurde)
+QPolygon _ObjectPolygon::split( int begin, int end, int pIndex )
+{
+    int pI = 0;
+    for( auto p = polygon.begin(); p != polygon.end(); p++ )
+    {
+        QPolygon &pol = *p;
+        if( pI == pIndex )
+        {
+            QPolygon newP;
+            int size = pol.size();
+            for( int i = begin; true; i++ )
+            {
+                if( i >= size )
+                    i = 0;
+                newP.append( pol.at( i ) );
+                if( i == end )
+                    break;
+            }
+            int removeIndex = begin + 1;
+            for( int i = begin + 1; true; i++ )
+            {
+                if( i >= size )
+                {
+                    i = 0;
+                    removeIndex = 0;
+                }
+                if( i == end )
+                    break;
+                pol.removeAt( removeIndex );
+            }
+            return newP;
+        }
+    }
+}
+
+// Setzt den Truncated Flag
+void _ObjectPolygon::setTruncated( bool truncated )
+{
+    this->truncated = truncated;
+}
+
+// Gibt 1 zurück, wenn das Objekt nicht Versteckt ist
+bool _ObjectPolygon::isSelected() const
+{
+    return selected;
+}
+
+// Gibt die Bounding Box zurück
+QRect _ObjectPolygon::getBoundingBox() const
+{
+    int minX = INT_MAX, minY = INT_MAX, maxX = 0, maxY = 0;
+    for( QPolygon pol : polygon )
+    {
+
+        for( QPoint point : pol )
+        {
+            if( point.x() < minX )
+                minX = point.x();
+            if( point.y() < minY )
+                minY = point.y();
+            if( point.x() > maxX )
+                maxX = point.x();
+            if( point.y() > maxY )
+                maxY = point.y();
+        }
+    }
+    return QRect( minX, minY, maxX - minX, maxY - minY );
+}
+
+// Gibt this zurück
+void *_ObjectPolygon::getNodeObject() const
+{
+    return (void*)this;
+}
+
+// Gibt 1 zurück, falls Truncated gesetzt wurde
+bool _ObjectPolygon::isTruncated() const
+{
+    return truncated;
+}
+
+// Gibt die Objekt-ID zurück
+QString _ObjectPolygon::getId() const
+{
+    return id;
+}
+
+// Gibt eine Liste mit den Polygonen zurück
+QList< QPolygon > &_ObjectPolygon::getPolygonList()
+{
+    return polygon;
+}
+
+// Inhalt der ObjektPolygon Klasse
+//---------------------------------
+
+// Erstellt ein auf Null zeigendes ObjektPolygon
+ObjectPolygon::ObjectPolygon()
+{
+    ptr = 0;
+}
+
+// Erzeugt ein neues Objekt Polygon, welches auf ein bestimmtes Objekt zeigt
+ObjectPolygon::ObjectPolygon( _ObjectPolygon *ptr )
+{
+    this->ptr = ptr;
+    if( this->ptr )
+        this->ptr->ref++;
+}
+
+// Erzeugt ein neues Objekt Polygon mit neuem Objekt
+//  id: Die Objekt ID
+//  po: Das Polygon
+//  truncated: 1, falls das Objekt abgeschnitten ist
+//  parent: Der Elternknoten im Baum der Sequenz (das Bild zu dem das Objekt gehört)
+ObjectPolygon::ObjectPolygon( QString id, QPolygon po, bool truncated, int index, FrameTreeNode *parent )
+{
+    ptr = new _ObjectPolygon( id, po, truncated, index, parent );
+    numObjects++;
+}
+
+// Copy Constructor
+ObjectPolygon::ObjectPolygon( const ObjectPolygon &op )
+    : ObjectPolygon( op.ptr )
+{}
+
+ObjectPolygon::~ObjectPolygon()
+{
+    if( ptr && !--ptr->ref )
+    {
+        delete ptr;
+        numObjects--;
+    }
+}
+
+// Operator um auf das unterliegende _ObjectPolygon zuzugreifen
+_ObjectPolygon *ObjectPolygon::operator->() const
+{
+    return ptr;
+}
+
+// Gibt 1 zurück, falls auf das gleiche Objekt verwiesen wird
+bool ObjectPolygon::operator==( const ObjectPolygon &op ) const
+{
+    return ptr == op.ptr;
+}
+
+// Gibt 1 zurück, falls nicht auf das gleiche Objekt verwiesen wird
+bool ObjectPolygon::operator!=( const ObjectPolygon &op ) const
+{
+    return ptr != op.ptr;
+}
+
+// Setzt den Zeiger auf das _ObjectPolygon Objekt
+bool ObjectPolygon::operator=( const ObjectPolygon &op )
+{
+    if( ptr && !--ptr->ref )
+    {
+        delete ptr;
+        numObjects--;
+    }
+    ptr = op.ptr;
+    if( ptr )
+        ptr->ref++;
+}
+
+// Gibt 1 zurück, falls der Zeiger nicht inizialisiert wurde
+bool ObjectPolygon::isNull() const
+{
+    return ptr == 0;
+}

+ 117 - 0
annotationGUI/object.h

@@ -0,0 +1,117 @@
+#ifndef PACKET_H
+#define PACKET_H
+
+#include <QString>
+#include <QPolygon>
+#include "frametree.h"
+
+#ifndef Global
+#define Global extern
+#endif
+Global int numObjects;
+
+/*
+ * Ein Objekt, welches in der Bildsequenz auf beliebig vielen Bildern auftreten kann
+ */
+class Object
+{
+private:
+    QString id; // Die Objekt ID
+    int classId; // Die ID der Objekt Klasse
+
+public:
+    Object( QString id, int classId );
+    // Setzt die Objekt-ID
+    void setId( QString id );
+    // Gibt die Objekt-ID zurück
+    QString getId() const;
+    // Setzt die ID der Klasse
+    int getClassId() const;
+    // Gibt die ID der Klasse zurück
+    void setClassId( int id );
+};
+
+// Vergleicht zwei Objekte anhand ihrer ID
+bool operator == (const Object &p1, const Object &p2);
+
+/*
+ * Vorkommen eines Objektes auf einem Bild
+ */
+class _ObjectPolygon : public FrameTreeNode
+{
+private:
+    QString id; // Die Id des Objektes
+    bool truncated; // 1, falls das Objekt abgeschnitten ist
+    QList< QPolygon > polygon; // Eine Liste mit Polygonen des Objektes
+    bool selected; // 1, falls das Objekt nicht versteckt ist
+    int ref; // Reference Counter
+
+    _ObjectPolygon( QString id, QPolygon po, bool truncated, int index, FrameTreeNode *parent );
+
+public:
+    // Setzt die Objekt-ID
+    void setId( QString id );
+    // Legt fest, ob das Objekt versteckt ist (falls s = 0)
+    void setSelected( bool s );
+    // Entfernt alle Ecken in einer Region r. Gibt 0 zurück, fall das Objekt komplett entfernt werden soll
+    bool removeVertices( QRect r );
+    // Verschiebt den intex-ten Vertex aus dem pIndex-ten Polygon auf Position newPos.
+    //  max: Die maximale Größe des Bildes. Falls newPos außerhalb des Bildes liegt, wird automatisch der nächste Punkt im Bild genommen.
+    void moveVertex( int index, QPoint newPos, QSize max, int pIndex );
+    // Fügt an Slette index im pIndexten Polygon den Eckpunkt v ein
+    void insertVertex( int index, QPoint v, int pIndex );
+    // Spaltet das pIndexte Polygon indem zwischen dem begin-ten Eckpunkt und dem end-ten Eckpunkt geschnitten wird.
+    //  Gibt das neu entstandene Polygon zurück (eine hälfte des alten, die aus diesem entfernt wurde)
+    QPolygon split( int begin, int end, int pIndex );
+    // Setzt den Truncated Flag
+    void setTruncated( bool truncated );
+    // Gibt 1 zurück, wenn das Objekt nicht Versteckt ist
+    bool isSelected() const;
+    // Gibt die Bounding Box zurück
+    QRect getBoundingBox() const;
+    // Gibt this zurück
+    void *getNodeObject() const override;
+    // Gibt 1 zurück, falls Truncated gesetzt wurde
+    bool isTruncated() const;
+    // Gibt die Objekt-ID zurück
+    QString getId() const;
+    // Gibt eine Liste mit den Polygonen zurück
+    QList< QPolygon > &getPolygonList();
+
+    friend class ObjectPolygon;
+};
+
+// Ein intelegenter Zeiger auf ein _ObjektPolygon Objekt, der reference Counting verwendet
+class ObjectPolygon
+{
+private:
+    _ObjectPolygon *ptr; // Ein Zeiger auf das verwaltete Objekt
+
+public:
+    // Erstellt ein auf Null zeigendes ObjektPolygon
+    ObjectPolygon();
+    // Erzeugt ein neues Objekt Polygon, welches auf ein bestimmtes Objekt zeigt
+    ObjectPolygon( _ObjectPolygon *ptr );
+    // Erzeugt ein neues Objekt Polygon mit neuem Objekt
+    //  id: Die Objekt ID
+    //  po: Das Polygon
+    //  truncated: 1, falls das Objekt abgeschnitten ist
+    //  parent: Der Elternknoten im Baum der Sequenz (das Bild zu dem das Objekt gehört)
+    ObjectPolygon( QString id, QPolygon po, bool truncated, int index, FrameTreeNode *parent );
+    // Copy Constructor
+    ObjectPolygon( const ObjectPolygon &op );
+    ~ObjectPolygon();
+
+    // Operator um auf das unterliegende _ObjectPolygon zuzugreifen
+    _ObjectPolygon *operator->() const;
+    // Gibt 1 zurück, falls auf das gleiche Objekt verwiesen wird
+    bool operator==( const ObjectPolygon &op ) const;
+    // Gibt 1 zurück, falls nicht auf das gleiche Objekt verwiesen wird
+    bool operator!=( const ObjectPolygon &op ) const;
+    // Setzt den Zeiger auf das _ObjectPolygon Objekt
+    bool operator=( const ObjectPolygon &op );
+    // Gibt 1 zurück, falls der Zeiger nicht inizialisiert wurde
+    bool isNull() const;
+};
+
+#endif // PACKET_H

+ 85 - 0
annotationGUI/requestfromserver.cpp

@@ -0,0 +1,85 @@
+#include "requestfromserver.h"
+#include "ui_requestfromserver.h"
+#include <QDebug>
+#include <QPushButton>
+
+Q_DECLARE_METATYPE( std::vector< std::vector< cv::Point > >* )
+
+RequestFromServerThread::RequestFromServerThread(QString serverAddress, Frame *f)
+    : QThread(),
+      serverAddress( serverAddress ),
+      f( f )
+{
+    this->client = 0;
+    this->setTerminationEnabled();
+}
+
+// Fragt die Daten vom Server ab
+void RequestFromServerThread::run()
+{
+    try {
+        DetectionClient client;
+        this->client = &client;
+        this->setTerminationEnabled();
+        client.connect( serverAddress.toStdString() );
+        std::vector< std::vector< cv::Point > > *objects = client.getContours( f->getImageMatrix() );
+        QThread::sleep(1);
+        threadReady( objects );
+        this->client = 0;
+        return;
+    }
+    catch( ... )
+    {
+        qDebug() << "no connection to Server";
+    }
+    QThread::sleep(1);
+    threadReady( new std::vector< std::vector< cv::Point > >() );
+    this->client = 0;
+}
+
+// Inhalt der RequestFromServer Klasse
+//-------------------------------------
+
+RequestFromServer::RequestFromServer(QString serverAddress, Frame *f, QWidget *parent) :
+    QDialog(parent),
+    ui(new Ui::RequestFromServer),
+    rfst( new RequestFromServerThread( serverAddress, f ) )
+{
+    ui->setupUi(this);
+    connect(rfst, &RequestFromServerThread::threadReady, this, &RequestFromServer::onThreadReady);
+    rfst->start();
+
+}
+
+RequestFromServer::~RequestFromServer()
+{
+    delete rfst;
+    delete ui;
+}
+
+std::vector< std::vector< cv::Point > > RequestFromServer::getObjects()
+{
+    return objects;
+}
+
+// Abfragen von Vorabannotationen abbrechen
+void RequestFromServer::on_abbrechen_clicked()
+{
+    disconnect(rfst, &RequestFromServerThread::threadReady, this, &RequestFromServer::onThreadReady);
+    qDebug() << "disconnected";
+    ui->label->setText( "Vorgang wird abgebrochen..." );
+    if( rfst->client )
+        rfst->client->cancel = 1;
+    rfst->wait();
+    this->close();
+}
+
+// Abfragen von Vorabannotationen abgeschlossen
+void RequestFromServer::onThreadReady( std::vector< std::vector< cv::Point > > *objects )
+{
+    qDebug() << "onThreadReady";
+    ui->abbrechen->setEnabled( false );
+    this->objects = *objects;
+    delete objects;
+    this->close();
+}

+ 63 - 0
annotationGUI/requestfromserver.h

@@ -0,0 +1,63 @@
+#ifndef REQUESTFROMSERVER_H
+#define REQUESTFROMSERVER_H
+
+#include <QDialog>
+#include "frame.h"
+#include <QThread>
+#include "DetectionClient.hh"
+
+/*
+ * Ein Thread, der die Vorabannotationen vom Server Abfragt
+ */
+class RequestFromServerThread : public QThread
+{
+    Q_OBJECT
+
+    // Fragt die Daten vom Server ab
+    void run() override;
+
+public:
+    RequestFromServerThread(QString serverAddress, Frame *f);
+
+    DetectionClient *client; // Ein zeiger auf den Detection CLient
+
+signals:
+    // Wird aufgerufen, sobald der Thread regulär beendet wurde
+    void threadReady( std::vector< std::vector< cv::Point > > *objects );
+
+private:
+    QString serverAddress; // Die Adresse des Servers
+    Frame *f; // Das Bild, das beim Server abgefragt wird
+};
+
+namespace Ui {
+class RequestFromServer;
+}
+
+/*
+ * Verwaltet das Fenster, das bei der Abfrage von Vorabannotationen vom Server erscheint.
+ */
+class RequestFromServer : public QDialog
+{
+    Q_OBJECT
+
+public:
+    explicit RequestFromServer( QString serverAddress, Frame *f, QWidget *parent = 0);
+    ~RequestFromServer();
+
+    std::vector< std::vector< cv::Point > > getObjects();
+
+private slots:
+    // Abfragen von Vorabannotationen abbrechen
+    void on_abbrechen_clicked();
+    // Abfragen von Vorabannotationen abgeschlossen
+    void onThreadReady( std::vector< std::vector< cv::Point > > *objects );
+
+
+private:
+    Ui::RequestFromServer *ui; // Ein Zeiger auf alle in requestfromserver.ui spezifizierten Objekte
+    std::vector< std::vector< cv::Point > > objects; // Eine Liste mit Objekt Polygonen (antwort vom server)
+    RequestFromServerThread *rfst; // Der Thread, in dem die Daten vom Server abgefragt werden
+};
+
+#endif // REQUESTFROMSERVER_H

+ 49 - 0
annotationGUI/requestfromserver.ui

@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>RequestFromServer</class>
+ <widget class="QDialog" name="RequestFromServer">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>286</width>
+    <height>75</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Dialog</string>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout_2">
+   <item>
+    <widget class="QLabel" name="label">
+     <property name="text">
+      <string>Das Bild wird vom Server abgefragt...</string>
+     </property>
+    </widget>
+   </item>
+   <item>
+    <widget class="QProgressBar" name="progressBar">
+     <property name="maximum">
+      <number>0</number>
+     </property>
+     <property name="value">
+      <number>0</number>
+     </property>
+    </widget>
+   </item>
+   <item>
+    <layout class="QHBoxLayout" name="horizontalLayout">
+     <item>
+      <widget class="QPushButton" name="abbrechen">
+       <property name="text">
+        <string>Abbrechen</string>
+       </property>
+      </widget>
+     </item>
+    </layout>
+   </item>
+  </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>

+ 16 - 0
annotationGUI/segmentationtest.cpp

@@ -0,0 +1,16 @@
+#include "DetectionClient.hh"
+#include "opencv2/opencv.hpp"
+
+int main(){
+
+    DetectionClient client("tcp://ob:55556");
+    cv::Mat newmat = cv::imread("/studi-tmp/kstrohm/AnnotierteSequenzen/SZ_09445899065805/JPEGImages/000100.jpg");
+    cv::imshow("test2",newmat);
+
+    cv::Mat segmentation = client.getSegmentation(newmat);
+    cv::imshow("test",segmentation * 50);
+    cv::waitKey();
+
+
+
+}

+ 393 - 0
annotationGUI/sequenz.cpp

@@ -0,0 +1,393 @@
+#include "sequenz.h"
+#include <tinyxml2.h>
+#include <QtConcurrent/QtConcurrent>
+#include <QProgressDialog>
+#include <fstream>
+
+Sequenz::Sequenz( QString p, QList< Kamera* > c, QList< Object > o )
+    : ref( 1 ),
+    path( p ),
+    frameIndex( 0 ),
+    cameraIndex( 0 ),
+    cams( c ),
+    objects( o )
+{
+    addClass( "packingbox" );
+}
+
+Sequenz::~Sequenz()
+{
+    for( auto k = cams.begin(); k != cams.end(); k++ )
+        (*k)->refRelease();
+}
+
+// Gibt eine Liste mit allen bekannten Objekt Klassen zurück
+QList< Sequenz::SegmentationClass > Sequenz::getClasses() const
+{
+    return classes;
+}
+
+// Setzt den Namen der Objekt Klasse mit der ID id auf name
+bool Sequenz::setClassName( int id, QString name )
+{
+    int i = getClassId( name );
+    if( i == id )
+        return true;
+    if( i != -1 )
+        return false;
+    for( auto c = classes.begin(); c != classes.end(); c++ )
+    {
+        if( c->id == id )
+        {
+            c->name = name;
+            return true;
+        }
+    }
+    return false;
+}
+
+// Prüft, ob bereits annotierte Objekte in der Sequenz vorhanden sind
+QString Sequenz::getClassName( int id ) const
+{
+    for( auto c = classes.begin(); c != classes.end(); c++ )
+    {
+        if( c->id == id )
+            return c->name;
+    }
+    return "NULL";
+}
+
+// Gibt den Namen der Objekt Klasse mit der ID id zurück
+int Sequenz::getClassId( QString name ) const
+{
+    for( auto c = classes.begin(); c != classes.end(); c++ )
+    {
+        if( c->name == name )
+            return c->id;
+    }
+    return -1;
+}
+
+// Gibt die ID der Objekt Klasse mit dem Namen name zurück
+int Sequenz::getClassOfObject( QString id ) const
+{
+    for( auto obj = objects.begin(); obj != objects.end(); obj++ )
+    {
+        if( obj->getId() == id )
+            return obj->getClassId();
+    }
+    return -1;
+}
+
+// Gibt die ID der Klasse des Objektes mit der ID objektId zurück
+void Sequenz::setClassOfObject( QString id, int classId )
+{
+    for( auto obj = objects.begin(); obj != objects.end(); obj++ )
+    {
+        if( obj->getId() == id )
+            obj->setClassId( classId );
+    }
+}
+
+// Setzt die ID der Klasse des Objektes mit der ID objektId auf classId
+bool Sequenz::hasAnnotatedObjects() const
+{
+    for( Object o : objects )
+    {
+        if( o.getId() != "-1" )
+            return true;
+    }
+    return false;
+}
+
+// Fügt eine neue Objekt Klasse mit Namen name hinzu
+int Sequenz::addClass( QString name )
+{
+    if( getClassId( name ) != -1 )
+        return -1;
+    int max = -1;
+    for( auto c = classes.begin(); c != classes.end(); c++ )
+    {
+        if( c->id > max )
+            max = c->id;
+    }
+    max++;
+    classes.append( { max, name } );
+    return max;
+}
+
+// Entfernt die Objektklasse mit der ID id
+bool Sequenz::removeClass( int id )
+{
+    for( auto c = classes.begin(); c != classes.end(); c++ )
+    {
+        if( c->id == id )
+        {
+            classes.erase( c );
+            int newId = 0;
+            if( classes.size() > 0 )
+                newId = classes.first().id;
+            for( auto obj = objects.begin(); obj != objects.end(); obj++ )
+            {
+                if( obj->getClassId() == id )
+                    obj->setClassId(newId);
+            }
+            return true;
+        }
+    }
+    return false;
+}
+
+// Wählt das frame-te Bild der cam-ten Kamera aus
+void Sequenz::selectFrame( int cam, int frame )
+{
+    cameraIndex = cam;
+    frameIndex = frame;
+}
+
+// Gibt das ausgewählte Bild zurück
+Frame *Sequenz::getFrame() const
+{
+    return cams.at( cameraIndex )->getFrame( frameIndex );
+}
+
+// Gibt den Index der ausgewählten Kamera zurück
+int Sequenz::getSelectedCamera() const
+{
+    return cameraIndex;
+}
+
+// Gibt den Index des Ausgewählten Bildes (innerhalb der Kamera) zurück
+int Sequenz::getSelectedFrame() const
+{
+    return frameIndex;
+}
+
+// Gibt eine Liste mit Kameras zurück
+const QList< Kamera* > &Sequenz::getCameras() const
+{
+    return cams;
+}
+
+// Wählt das nachfolgende Bild aus
+void Sequenz::nextFrame()
+{
+    if( !hasNextFrame() )
+        return;
+    if( ++frameIndex == cams.at( cameraIndex )->getChildCount() )
+    {
+        frameIndex = 0;
+        cameraIndex++;
+    }
+}
+
+// Wählt das vorherige Bild aus
+void Sequenz::previousFrame()
+{
+    if( !hasPreviousFrame() )
+        return;
+    if( --frameIndex < 0 )
+        frameIndex = cams.at( --cameraIndex )->getChildCount() - 1;
+}
+
+// Gibt 1 zurück, wenn in der Sequenz ein nachfolgendes Bild existiert
+bool Sequenz::hasNextFrame() const
+{
+    return cameraIndex < cams.count() - 1 || frameIndex < cams.at( cameraIndex )->getChildCount() - 1;
+}
+
+// Gibt 1 zurück, wenn in der Sequenz ein vorheriges Bild existiert
+bool Sequenz::hasPreviousFrame() const
+{
+    return frameIndex > 0 || cameraIndex > 0;
+}
+
+// Gibt eine Liste mit allen vergebenen ObjektIds zurück
+QList< QString > Sequenz::getObjectNames() const
+{
+    QList<QString> result;
+    foreach( Object obj, objects )
+        result.append( obj.getId() );
+    return result;
+}
+
+// Gibt von dem ausgewählten Bild aus das count-nächste Bild von dem Objekt mit ID objektId zurück.
+QImage Sequenz::previousObjectImage( QString packetName, int count ) const
+{
+    int currCam = cameraIndex;
+    int currFrame = frameIndex;
+    QImage img;
+    int dir = 1;
+    if( count < 0 )
+    { // falls count negativ ist, wird weiter vorne in der sequenz gesucht
+        dir = -1;
+        count = -count;
+    }
+    int oldCOunt = count;
+    do
+    { // Schleife durch alle bilder (in die angegebene Richtung) bis ein Bild gefunden wird auf dem das Objekt vorkommt
+        if( (currFrame += dir) < 0 || currFrame >= cams.at( currCam )->getChildCount() )
+        {
+            if( (currCam += dir) < 0 || currCam >= cams.size() )
+            {
+                currCam = cams.size() - 1;
+                if( dir > 0 )
+                    currCam = 0;
+            }
+            currFrame = cams.at( currCam )->getChildCount() - 1;
+            if( dir > 0 )
+                currFrame = 0;
+        }
+        if( currFrame == frameIndex && currCam == cameraIndex )
+        {
+            if( oldCOunt == count )
+                break;
+        }
+        Frame *f = cams.at( currCam )->getFrame( currFrame );
+        if( f->hasObject( packetName ) )
+        {
+            count--;
+            if( count == 0 ) // das gesuchte Bild wurde erreicht
+                img = f->getObjectImage( packetName );
+        }
+    } while( count > 0 );
+    return img;
+}
+
+// Fügt eine neue ObjektID mit der Klassen ID classID hinzu
+void Sequenz::addObjectName( QString name, int classId )
+{
+    if( objects.indexOf( Object( name, classId ) ) < 0 )
+        objects.append( Object( name, classId ) );
+}
+
+// Fügt eine neue ObjektID hinzu
+void Sequenz::addObjectName( QString name )
+{
+    if( classes.size() > 0 )
+        addObjectName( name, classes.first().id );
+    else
+        addObjectName( name, 0 );
+}
+
+// Speichert die Sequenz.
+//  status: Ein Label, in denen Fortschrittsinformationen geschrieben werden
+void Sequenz::saveToPath( QLabel *status ) const
+{
+    int numOps = 0;
+    for( Kamera *k : cams )
+        numOps += k->getChildCount();
+    QProgressDialog progress( "Annotation wird gespeichert...", "", 0, numOps );
+    progress.setWindowModality(Qt::WindowModal);
+    progress.setCancelButton( 0 );
+
+    if( !QFile( path + "/ImageSets/Main/train.txt" ).exists() )
+        QDir( path + "/ImageSets/Main" ).mkpath(".");
+    std::ofstream trainTxt( (path + "/ImageSets/Main/train.txt").toStdString().c_str() );
+    for( int cam = 0; cam < cams.size(); cam++ )
+    { // Schleife durch alle Kameras
+        Kamera *currentCam = cams.at( cam );
+        int frameCount = currentCam->getChildCount();
+        for( int frame = 0; frame < frameCount; frame++ )
+        { // Schleife durch alle Bilder
+            if( status )
+            {
+                QString text = "save Annotations " + QString::number( cam + 1 ) + "/" + QString::number( cams.size() ) + " " + QString::number( frame + 1 ) + "/" + QString::number( cams.at( cam )->getChildCount() );
+                QMetaObject::invokeMethod( status, "setText",  Q_ARG( QString, text ) );
+            }
+            Frame *currentFrame = currentCam->getFrame( frame );
+            if( !currentFrame->isNotAnnotated() || currentFrame->getObjects().size() > 0 )
+            {
+                trainTxt << ( currentFrame->getName().mid( 0, currentFrame->getName().indexOf( "." ) ) + "\n" ).toStdString().c_str();
+            }
+            if( currentFrame->wasChangedSinceLastSave() )
+            { // Falls das Bild seid dem letzten speichern verändert wurde
+                tinyxml2::XMLDocument doc; // Erstelle die xml datei
+                doc.LinkEndChild( doc.NewDeclaration( "xml version=\"1.0\" " ) );
+                tinyxml2::XMLElement *annotation = doc.NewElement( "annotation");
+                annotation->LinkEndChild( doc.NewElement( "folder" ) )->LinkEndChild( doc.NewText( "VOC2007" ) );
+                annotation->LinkEndChild( doc.NewElement( "filename" ) )->LinkEndChild( doc.NewText( currentFrame->getName().toStdString().c_str() ) );
+                tinyxml2::XMLElement *source = doc.NewElement( "source" );
+                source->LinkEndChild( doc.NewElement( "database" ) )->LinkEndChild( doc.NewText( "The VOC2007 Database" ) );
+                source->LinkEndChild( doc.NewElement( "annotation" ) )->LinkEndChild( doc.NewText( "PASCAL VOC 2007" ) );
+                source->LinkEndChild( doc.NewElement( "image" ) )->LinkEndChild( doc.NewText( currentFrame->getName().toStdString().c_str() ) );
+                annotation->LinkEndChild( source );
+                annotation->LinkEndChild( doc.NewElement( "owner" ) )->LinkEndChild( doc.NewElement( "name" ) )->LinkEndChild( doc.NewText( "Kolja Strohm" ) );
+                annotation->LinkEndChild( doc.NewElement( "camera_id" ) )->LinkEndChild( doc.NewText( currentCam->getName().toStdString().c_str() ) );
+                tinyxml2::XMLElement *size = doc.NewElement( "size" );
+                QImage img = currentFrame->getImage();
+                size->LinkEndChild( doc.NewElement( "width" ) )->LinkEndChild( doc.NewText( std::to_string( img.width() ).c_str() ) );
+                size->LinkEndChild( doc.NewElement( "height" ) )->LinkEndChild( doc.NewText( std::to_string( img.height() ).c_str() ) );
+                size->LinkEndChild( doc.NewElement( "depth" ) )->LinkEndChild( doc.NewText( std::to_string( img.depth() ).c_str() ) );
+                annotation->LinkEndChild( size );
+                annotation->LinkEndChild( doc.NewElement( "segmented" ) )->LinkEndChild( doc.NewText( "0" ) );
+                annotation->LinkEndChild( doc.NewElement( "timestamp" ) )->LinkEndChild( doc.NewText( currentFrame->getTimestamp().toStdString().c_str() ) );
+                QList<ObjectPolygon> objects = currentFrame->getObjects();
+                for( ObjectPolygon o : objects )
+                { // Schleife durch alle Objekte
+                    tinyxml2::XMLElement *object = doc.NewElement( "object" );
+                    object->LinkEndChild( doc.NewElement( "name" ) )->LinkEndChild( doc.NewText( getClassName( getClassOfObject( o->getId() ) ).toStdString().c_str() ) );
+                    object->LinkEndChild( doc.NewElement( "id" ) )->LinkEndChild( doc.NewText( o->getId().toStdString().c_str() ) );
+                    object->LinkEndChild( doc.NewElement( "pose" ) )->LinkEndChild( doc.NewText( "Unspecified" ) );
+                    object->LinkEndChild( doc.NewElement( "truncated" ) )->LinkEndChild( doc.NewText( std::to_string( (int)o->isTruncated() ).c_str() ) );
+                    object->LinkEndChild( doc.NewElement( "difficult" ) )->LinkEndChild( doc.NewText( "0" ) );
+                    int xMin = img.width();
+                    int yMin = img.height();
+                    int xMax = 0;
+                    int yMax = 0;
+                    std::vector< tinyxml2::XMLElement* > polygons;
+                    for( QPolygon pol : o->getPolygonList() )
+                    { // Schleife durch alle Polygone
+                        tinyxml2::XMLElement *polygon = doc.NewElement( "polygon" );
+                        for( QPoint point : pol )
+                        { // Schleife durch alle Eckpunkte
+                            tinyxml2::XMLElement *pointE = doc.NewElement( "point" );
+                            pointE->LinkEndChild( doc.NewElement( "x" ) )->LinkEndChild( doc.NewText( std::to_string( point.x() ).c_str() ) );
+                            pointE->LinkEndChild( doc.NewElement( "y" ) )->LinkEndChild( doc.NewText( std::to_string( point.y() ).c_str() ) );
+                            polygon->LinkEndChild( pointE );
+                            if( xMin > point.x() ) // ermittle die Bounding box
+                                xMin = point.x();
+                            if( yMin > point.y() )
+                                yMin = point.y();
+                            if( xMax < point.x() )
+                                xMax = point.x();
+                            if( yMax < point.y() )
+                                yMax = point.y();
+                        }
+                        polygons.push_back( polygon );
+                    }
+                    tinyxml2::XMLElement *bndbox = doc.NewElement( "bndbox" );
+                    bndbox->LinkEndChild( doc.NewElement( "xmin" ) )->LinkEndChild( doc.NewText( std::to_string( xMin ).c_str() ) );
+                    bndbox->LinkEndChild( doc.NewElement( "ymin" ) )->LinkEndChild( doc.NewText( std::to_string( yMin ).c_str() ) );
+                    bndbox->LinkEndChild( doc.NewElement( "xmax" ) )->LinkEndChild( doc.NewText( std::to_string( xMax ).c_str() ) );
+                    bndbox->LinkEndChild( doc.NewElement( "ymax" ) )->LinkEndChild( doc.NewText( std::to_string( yMax ).c_str() ) );
+                    object->LinkEndChild( bndbox );
+                    for( tinyxml2::XMLElement *polygon : polygons )
+                        object->LinkEndChild( polygon );
+                    annotation->LinkEndChild( object );
+                }
+                if( objects.size() == 0 && currentFrame->isNotAnnotated() )
+                    annotation->LinkEndChild( doc.NewElement( "noobject" ) )->LinkEndChild( doc.NewText( "need Annotation" ) );
+                doc.LinkEndChild( annotation );
+                QString name = currentFrame->getName();
+                QDir().mkpath( path + "/Annotations" );
+                doc.SaveFile( (path + "/Annotations/" + name.mid( 0, name.lastIndexOf( '.' ) ) + ".xml").toStdString().c_str() );
+            }
+            progress.setValue( progress.value() + 1 );
+        }
+    }
+    trainTxt.close();
+}
+
+// Erhöht den Reference Counter um 1
+void Sequenz::refNew()
+{
+    ref++;
+}
+
+// Verringert den Reference Counter um 1 (bei 0 löscht sich das Objekt selbst)
+void Sequenz::refRelease()
+{
+    if( !--ref )
+        delete this;
+}

+ 89 - 0
annotationGUI/sequenz.h

@@ -0,0 +1,89 @@
+#ifndef SEQUENZ_H
+#define SEQUENZ_H
+
+#include <QString>
+#include <kamera.h>
+#include <object.h>
+#include <QList>
+#include <QLabel>
+
+/*
+ * Verwaltet alle Daten einer Bildsequenz
+ */
+class Sequenz
+{
+public:
+    // Eine Datenstruktur, welche eine Objekt-Klasse repräsentiert
+    struct SegmentationClass
+    {
+        int id;
+        QString name;
+    };
+
+private:
+    QString path; // Der Pfad zum Ordner der Sequenz
+    int ref; // Reference Counting
+    int frameIndex; // Der Index des ausgewählten Bildes
+    int cameraIndex; // Der Index der ausgewählten Kamera
+    QList< Kamera* > cams; // Eine Liste mit Kameras
+    QList< Object > objects; // Eine Liste mit Objekten
+    QList< SegmentationClass > classes; // Eine Liste mit Objekt Klassen
+
+public:
+    Sequenz( QString p, QList< Kamera* > c, QList< Object > pa );
+    ~Sequenz();
+
+    // Gibt eine Liste mit allen bekannten Objekt Klassen zurück
+    QList< SegmentationClass > getClasses() const;
+    // Setzt den Namen der Objekt Klasse mit der ID id auf name
+    bool setClassName( int id, QString name );
+    // Prüft, ob bereits annotierte Objekte in der Sequenz vorhanden sind
+    bool hasAnnotatedObjects() const;
+    // Gibt den Namen der Objekt Klasse mit der ID id zurück
+    QString getClassName( int id ) const;
+    // Gibt die ID der Objekt Klasse mit dem Namen name zurück
+    int getClassId( QString name ) const;
+    // Gibt die ID der Klasse des Objektes mit der ID objektId zurück
+    int getClassOfObject( QString objektId ) const;
+    // Setzt die ID der Klasse des Objektes mit der ID objektId auf classId
+    void setClassOfObject( QString objektId, int classId );
+    // Fügt eine neue Objekt Klasse mit Namen name hinzu
+    int addClass( QString name );
+    // Entfernt die Objektklasse mit der ID id
+    bool removeClass( int id );
+    // Wählt das frame-te Bild der cam-ten Kamera aus
+    void selectFrame( int cam, int frame );
+    // Gibt das ausgewählte Bild zurück
+    Frame *getFrame() const;
+    // Gibt den Index der ausgewählten Kamera zurück
+    int getSelectedCamera() const;
+    // Gibt den Index des Ausgewählten Bildes (innerhalb der Kamera) zurück
+    int getSelectedFrame() const;
+    // Gibt eine Liste mit Kameras zurück
+    const QList< Kamera* > &getCameras() const;
+    // Wählt das nachfolgende Bild aus
+    void nextFrame();
+    // Wählt das vorherige Bild aus
+    void previousFrame();
+    // Gibt 1 zurück, wenn in der Sequenz ein nachfolgendes Bild existiert
+    bool hasNextFrame() const;
+    // Gibt 1 zurück, wenn in der Sequenz ein vorheriges Bild existiert
+    bool hasPreviousFrame() const;
+    // Gibt eine Liste mit allen vergebenen ObjektIds zurück
+    QList< QString > getObjectNames() const;
+    // Gibt von dem ausgewählten Bild aus das count-nächste Bild von dem Objekt mit ID objektId zurück.
+    QImage previousObjectImage( QString objektId, int count = 1 ) const;
+    // Fügt eine neue ObjektID mit der Klassen ID classID hinzu
+    void addObjectName( QString objektId, int classId );
+    // Fügt eine neue ObjektID hinzu
+    void addObjectName( QString objektId );
+    // Speichert die Sequenz.
+    //  status: Ein Label, in denen Fortschrittsinformationen geschrieben werden
+    void saveToPath( QLabel *status ) const;
+    // Erhöht den Reference Counter um 1
+    void refNew();
+    // Verringert den Reference Counter um 1 (bei 0 löscht sich das Objekt selbst)
+    void refRelease();
+};
+
+#endif // SEQUENZ_H

+ 2741 - 0
annotationGUI/tinyxml2.cpp

@@ -0,0 +1,2741 @@
+/*
+Original code by Lee Thomason (www.grinninglizard.com)
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any
+damages arising from the use of this software.
+
+Permission is granted to anyone to use this software for any
+purpose, including commercial applications, and to alter it and
+redistribute it freely, subject to the following restrictions:
+
+1. The origin of this software must not be misrepresented; you must
+not claim that you wrote the original software. If you use this
+software in a product, an acknowledgment in the product documentation
+would be appreciated but is not required.
+
+2. Altered source versions must be plainly marked as such, and
+must not be misrepresented as being the original software.
+
+3. This notice may not be removed or altered from any source
+distribution.
+*/
+
+#include "tinyxml2.h"
+
+#include <new>		// yes, this one new style header, is in the Android SDK.
+#if defined(ANDROID_NDK) || defined(__BORLANDC__) || defined(__QNXNTO__)
+#   include <stddef.h>
+#   include <stdarg.h>
+#else
+#   include <cstddef>
+#   include <cstdarg>
+#endif
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1400 ) && (!defined WINCE)
+	// Microsoft Visual Studio, version 2005 and higher. Not WinCE.
+	/*int _snprintf_s(
+	   char *buffer,
+	   size_t sizeOfBuffer,
+	   size_t count,
+	   const char *format [,
+		  argument] ...
+	);*/
+	static inline int TIXML_SNPRINTF( char* buffer, size_t size, const char* format, ... )
+	{
+		va_list va;
+		va_start( va, format );
+		int result = vsnprintf_s( buffer, size, _TRUNCATE, format, va );
+		va_end( va );
+		return result;
+	}
+
+	static inline int TIXML_VSNPRINTF( char* buffer, size_t size, const char* format, va_list va )
+	{
+		int result = vsnprintf_s( buffer, size, _TRUNCATE, format, va );
+		return result;
+	}
+
+	#define TIXML_VSCPRINTF	_vscprintf
+	#define TIXML_SSCANF	sscanf_s
+#elif defined _MSC_VER
+	// Microsoft Visual Studio 2003 and earlier or WinCE
+	#define TIXML_SNPRINTF	_snprintf
+	#define TIXML_VSNPRINTF _vsnprintf
+	#define TIXML_SSCANF	sscanf
+	#if (_MSC_VER < 1400 ) && (!defined WINCE)
+		// Microsoft Visual Studio 2003 and not WinCE.
+		#define TIXML_VSCPRINTF   _vscprintf // VS2003's C runtime has this, but VC6 C runtime or WinCE SDK doesn't have.
+	#else
+		// Microsoft Visual Studio 2003 and earlier or WinCE.
+		static inline int TIXML_VSCPRINTF( const char* format, va_list va )
+		{
+			int len = 512;
+			for (;;) {
+				len = len*2;
+				char* str = new char[len]();
+				const int required = _vsnprintf(str, len, format, va);
+				delete[] str;
+				if ( required != -1 ) {
+					TIXMLASSERT( required >= 0 );
+					len = required;
+					break;
+				}
+			}
+			TIXMLASSERT( len >= 0 );
+			return len;
+		}
+	#endif
+#else
+	// GCC version 3 and higher
+	//#warning( "Using sn* functions." )
+	#define TIXML_SNPRINTF	snprintf
+	#define TIXML_VSNPRINTF	vsnprintf
+	static inline int TIXML_VSCPRINTF( const char* format, va_list va )
+	{
+		int len = vsnprintf( 0, 0, format, va );
+		TIXMLASSERT( len >= 0 );
+		return len;
+	}
+	#define TIXML_SSCANF   sscanf
+#endif
+
+
+static const char LINE_FEED				= (char)0x0a;			// all line endings are normalized to LF
+static const char LF = LINE_FEED;
+static const char CARRIAGE_RETURN		= (char)0x0d;			// CR gets filtered out
+static const char CR = CARRIAGE_RETURN;
+static const char SINGLE_QUOTE			= '\'';
+static const char DOUBLE_QUOTE			= '\"';
+
+// Bunch of unicode info at:
+//		http://www.unicode.org/faq/utf_bom.html
+//	ef bb bf (Microsoft "lead bytes") - designates UTF-8
+
+static const unsigned char TIXML_UTF_LEAD_0 = 0xefU;
+static const unsigned char TIXML_UTF_LEAD_1 = 0xbbU;
+static const unsigned char TIXML_UTF_LEAD_2 = 0xbfU;
+
+namespace tinyxml2
+{
+
+struct Entity {
+    const char* pattern;
+    int length;
+    char value;
+};
+
+static const int NUM_ENTITIES = 5;
+static const Entity entities[NUM_ENTITIES] = {
+    { "quot", 4,	DOUBLE_QUOTE },
+    { "amp", 3,		'&'  },
+    { "apos", 4,	SINGLE_QUOTE },
+    { "lt",	2, 		'<'	 },
+    { "gt",	2,		'>'	 }
+};
+
+
+StrPair::~StrPair()
+{
+    Reset();
+}
+
+
+void StrPair::TransferTo( StrPair* other )
+{
+    if ( this == other ) {
+        return;
+    }
+    // This in effect implements the assignment operator by "moving"
+    // ownership (as in auto_ptr).
+
+    TIXMLASSERT( other != 0 );
+    TIXMLASSERT( other->_flags == 0 );
+    TIXMLASSERT( other->_start == 0 );
+    TIXMLASSERT( other->_end == 0 );
+
+    other->Reset();
+
+    other->_flags = _flags;
+    other->_start = _start;
+    other->_end = _end;
+
+    _flags = 0;
+    _start = 0;
+    _end = 0;
+}
+
+void StrPair::Reset()
+{
+    if ( _flags & NEEDS_DELETE ) {
+        delete [] _start;
+    }
+    _flags = 0;
+    _start = 0;
+    _end = 0;
+}
+
+
+void StrPair::SetStr( const char* str, int flags )
+{
+    TIXMLASSERT( str );
+    Reset();
+    size_t len = strlen( str );
+    TIXMLASSERT( _start == 0 );
+    _start = new char[ len+1 ];
+    memcpy( _start, str, len+1 );
+    _end = _start + len;
+    _flags = flags | NEEDS_DELETE;
+}
+
+
+char* StrPair::ParseText( char* p, const char* endTag, int strFlags, int* curLineNumPtr )
+{
+    TIXMLASSERT( p );
+    TIXMLASSERT( endTag && *endTag );
+	TIXMLASSERT(curLineNumPtr);
+
+    char* start = p;
+    char  endChar = *endTag;
+    size_t length = strlen( endTag );
+
+    // Inner loop of text parsing.
+    while ( *p ) {
+        if ( *p == endChar && strncmp( p, endTag, length ) == 0 ) {
+            Set( start, p, strFlags );
+            return p + length;
+        } else if (*p == '\n') {
+            ++(*curLineNumPtr);
+        }
+        ++p;
+        TIXMLASSERT( p );
+    }
+    return 0;
+}
+
+
+char* StrPair::ParseName( char* p )
+{
+    if ( !p || !(*p) ) {
+        return 0;
+    }
+    if ( !XMLUtil::IsNameStartChar( *p ) ) {
+        return 0;
+    }
+
+    char* const start = p;
+    ++p;
+    while ( *p && XMLUtil::IsNameChar( *p ) ) {
+        ++p;
+    }
+
+    Set( start, p, 0 );
+    return p;
+}
+
+
+void StrPair::CollapseWhitespace()
+{
+    // Adjusting _start would cause undefined behavior on delete[]
+    TIXMLASSERT( ( _flags & NEEDS_DELETE ) == 0 );
+    // Trim leading space.
+    _start = XMLUtil::SkipWhiteSpace( _start, 0 );
+
+    if ( *_start ) {
+        const char* p = _start;	// the read pointer
+        char* q = _start;	// the write pointer
+
+        while( *p ) {
+            if ( XMLUtil::IsWhiteSpace( *p )) {
+                p = XMLUtil::SkipWhiteSpace( p, 0 );
+                if ( *p == 0 ) {
+                    break;    // don't write to q; this trims the trailing space.
+                }
+                *q = ' ';
+                ++q;
+            }
+            *q = *p;
+            ++q;
+            ++p;
+        }
+        *q = 0;
+    }
+}
+
+
+const char* StrPair::GetStr()
+{
+    TIXMLASSERT( _start );
+    TIXMLASSERT( _end );
+    if ( _flags & NEEDS_FLUSH ) {
+        *_end = 0;
+        _flags ^= NEEDS_FLUSH;
+
+        if ( _flags ) {
+            const char* p = _start;	// the read pointer
+            char* q = _start;	// the write pointer
+
+            while( p < _end ) {
+                if ( (_flags & NEEDS_NEWLINE_NORMALIZATION) && *p == CR ) {
+                    // CR-LF pair becomes LF
+                    // CR alone becomes LF
+                    // LF-CR becomes LF
+                    if ( *(p+1) == LF ) {
+                        p += 2;
+                    }
+                    else {
+                        ++p;
+                    }
+                    *q = LF;
+                    ++q;
+                }
+                else if ( (_flags & NEEDS_NEWLINE_NORMALIZATION) && *p == LF ) {
+                    if ( *(p+1) == CR ) {
+                        p += 2;
+                    }
+                    else {
+                        ++p;
+                    }
+                    *q = LF;
+                    ++q;
+                }
+                else if ( (_flags & NEEDS_ENTITY_PROCESSING) && *p == '&' ) {
+                    // Entities handled by tinyXML2:
+                    // - special entities in the entity table [in/out]
+                    // - numeric character reference [in]
+                    //   &#20013; or &#x4e2d;
+
+                    if ( *(p+1) == '#' ) {
+                        const int buflen = 10;
+                        char buf[buflen] = { 0 };
+                        int len = 0;
+                        char* adjusted = const_cast<char*>( XMLUtil::GetCharacterRef( p, buf, &len ) );
+                        if ( adjusted == 0 ) {
+                            *q = *p;
+                            ++p;
+                            ++q;
+                        }
+                        else {
+                            TIXMLASSERT( 0 <= len && len <= buflen );
+                            TIXMLASSERT( q + len <= adjusted );
+                            p = adjusted;
+                            memcpy( q, buf, len );
+                            q += len;
+                        }
+                    }
+                    else {
+                        bool entityFound = false;
+                        for( int i = 0; i < NUM_ENTITIES; ++i ) {
+                            const Entity& entity = entities[i];
+                            if ( strncmp( p + 1, entity.pattern, entity.length ) == 0
+                                    && *( p + entity.length + 1 ) == ';' ) {
+                                // Found an entity - convert.
+                                *q = entity.value;
+                                ++q;
+                                p += entity.length + 2;
+                                entityFound = true;
+                                break;
+                            }
+                        }
+                        if ( !entityFound ) {
+                            // fixme: treat as error?
+                            ++p;
+                            ++q;
+                        }
+                    }
+                }
+                else {
+                    *q = *p;
+                    ++p;
+                    ++q;
+                }
+            }
+            *q = 0;
+        }
+        // The loop below has plenty going on, and this
+        // is a less useful mode. Break it out.
+        if ( _flags & NEEDS_WHITESPACE_COLLAPSING ) {
+            CollapseWhitespace();
+        }
+        _flags = (_flags & NEEDS_DELETE);
+    }
+    TIXMLASSERT( _start );
+    return _start;
+}
+
+
+
+
+// --------- XMLUtil ----------- //
+
+const char* XMLUtil::writeBoolTrue  = "true";
+const char* XMLUtil::writeBoolFalse = "false";
+
+void XMLUtil::SetBoolSerialization(const char* writeTrue, const char* writeFalse)
+{
+	static const char* defTrue  = "true";
+	static const char* defFalse = "false";
+
+	writeBoolTrue = (writeTrue) ? writeTrue : defTrue;
+	writeBoolFalse = (writeFalse) ? writeFalse : defFalse;
+}
+
+
+const char* XMLUtil::ReadBOM( const char* p, bool* bom )
+{
+    TIXMLASSERT( p );
+    TIXMLASSERT( bom );
+    *bom = false;
+    const unsigned char* pu = reinterpret_cast<const unsigned char*>(p);
+    // Check for BOM:
+    if (    *(pu+0) == TIXML_UTF_LEAD_0
+            && *(pu+1) == TIXML_UTF_LEAD_1
+            && *(pu+2) == TIXML_UTF_LEAD_2 ) {
+        *bom = true;
+        p += 3;
+    }
+    TIXMLASSERT( p );
+    return p;
+}
+
+
+void XMLUtil::ConvertUTF32ToUTF8( unsigned long input, char* output, int* length )
+{
+    const unsigned long BYTE_MASK = 0xBF;
+    const unsigned long BYTE_MARK = 0x80;
+    const unsigned long FIRST_BYTE_MARK[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
+
+    if (input < 0x80) {
+        *length = 1;
+    }
+    else if ( input < 0x800 ) {
+        *length = 2;
+    }
+    else if ( input < 0x10000 ) {
+        *length = 3;
+    }
+    else if ( input < 0x200000 ) {
+        *length = 4;
+    }
+    else {
+        *length = 0;    // This code won't convert this correctly anyway.
+        return;
+    }
+
+    output += *length;
+
+    // Scary scary fall throughs.
+    switch (*length) {
+        case 4:
+            --output;
+            *output = (char)((input | BYTE_MARK) & BYTE_MASK);
+            input >>= 6;
+        case 3:
+            --output;
+            *output = (char)((input | BYTE_MARK) & BYTE_MASK);
+            input >>= 6;
+        case 2:
+            --output;
+            *output = (char)((input | BYTE_MARK) & BYTE_MASK);
+            input >>= 6;
+        case 1:
+            --output;
+            *output = (char)(input | FIRST_BYTE_MARK[*length]);
+            break;
+        default:
+            TIXMLASSERT( false );
+    }
+}
+
+
+const char* XMLUtil::GetCharacterRef( const char* p, char* value, int* length )
+{
+    // Presume an entity, and pull it out.
+    *length = 0;
+
+    if ( *(p+1) == '#' && *(p+2) ) {
+        unsigned long ucs = 0;
+        TIXMLASSERT( sizeof( ucs ) >= 4 );
+        ptrdiff_t delta = 0;
+        unsigned mult = 1;
+        static const char SEMICOLON = ';';
+
+        if ( *(p+2) == 'x' ) {
+            // Hexadecimal.
+            const char* q = p+3;
+            if ( !(*q) ) {
+                return 0;
+            }
+
+            q = strchr( q, SEMICOLON );
+
+            if ( !q ) {
+                return 0;
+            }
+            TIXMLASSERT( *q == SEMICOLON );
+
+            delta = q-p;
+            --q;
+
+            while ( *q != 'x' ) {
+                unsigned int digit = 0;
+
+                if ( *q >= '0' && *q <= '9' ) {
+                    digit = *q - '0';
+                }
+                else if ( *q >= 'a' && *q <= 'f' ) {
+                    digit = *q - 'a' + 10;
+                }
+                else if ( *q >= 'A' && *q <= 'F' ) {
+                    digit = *q - 'A' + 10;
+                }
+                else {
+                    return 0;
+                }
+                TIXMLASSERT( digit < 16 );
+                TIXMLASSERT( digit == 0 || mult <= UINT_MAX / digit );
+                const unsigned int digitScaled = mult * digit;
+                TIXMLASSERT( ucs <= ULONG_MAX - digitScaled );
+                ucs += digitScaled;
+                TIXMLASSERT( mult <= UINT_MAX / 16 );
+                mult *= 16;
+                --q;
+            }
+        }
+        else {
+            // Decimal.
+            const char* q = p+2;
+            if ( !(*q) ) {
+                return 0;
+            }
+
+            q = strchr( q, SEMICOLON );
+
+            if ( !q ) {
+                return 0;
+            }
+            TIXMLASSERT( *q == SEMICOLON );
+
+            delta = q-p;
+            --q;
+
+            while ( *q != '#' ) {
+                if ( *q >= '0' && *q <= '9' ) {
+                    const unsigned int digit = *q - '0';
+                    TIXMLASSERT( digit < 10 );
+                    TIXMLASSERT( digit == 0 || mult <= UINT_MAX / digit );
+                    const unsigned int digitScaled = mult * digit;
+                    TIXMLASSERT( ucs <= ULONG_MAX - digitScaled );
+                    ucs += digitScaled;
+                }
+                else {
+                    return 0;
+                }
+                TIXMLASSERT( mult <= UINT_MAX / 10 );
+                mult *= 10;
+                --q;
+            }
+        }
+        // convert the UCS to UTF-8
+        ConvertUTF32ToUTF8( ucs, value, length );
+        return p + delta + 1;
+    }
+    return p+1;
+}
+
+
+void XMLUtil::ToStr( int v, char* buffer, int bufferSize )
+{
+    TIXML_SNPRINTF( buffer, bufferSize, "%d", v );
+}
+
+
+void XMLUtil::ToStr( unsigned v, char* buffer, int bufferSize )
+{
+    TIXML_SNPRINTF( buffer, bufferSize, "%u", v );
+}
+
+
+void XMLUtil::ToStr( bool v, char* buffer, int bufferSize )
+{
+    TIXML_SNPRINTF( buffer, bufferSize, "%s", v ? writeBoolTrue : writeBoolFalse);
+}
+
+/*
+	ToStr() of a number is a very tricky topic.
+	https://github.com/leethomason/tinyxml2/issues/106
+*/
+void XMLUtil::ToStr( float v, char* buffer, int bufferSize )
+{
+    TIXML_SNPRINTF( buffer, bufferSize, "%.8g", v );
+}
+
+
+void XMLUtil::ToStr( double v, char* buffer, int bufferSize )
+{
+    TIXML_SNPRINTF( buffer, bufferSize, "%.17g", v );
+}
+
+
+void XMLUtil::ToStr(int64_t v, char* buffer, int bufferSize)
+{
+	// horrible syntax trick to make the compiler happy about %lld
+	TIXML_SNPRINTF(buffer, bufferSize, "%lld", (long long)v);
+}
+
+
+bool XMLUtil::ToInt( const char* str, int* value )
+{
+    if ( TIXML_SSCANF( str, "%d", value ) == 1 ) {
+        return true;
+    }
+    return false;
+}
+
+bool XMLUtil::ToUnsigned( const char* str, unsigned *value )
+{
+    if ( TIXML_SSCANF( str, "%u", value ) == 1 ) {
+        return true;
+    }
+    return false;
+}
+
+bool XMLUtil::ToBool( const char* str, bool* value )
+{
+    int ival = 0;
+    if ( ToInt( str, &ival )) {
+        *value = (ival==0) ? false : true;
+        return true;
+    }
+    if ( StringEqual( str, "true" ) ) {
+        *value = true;
+        return true;
+    }
+    else if ( StringEqual( str, "false" ) ) {
+        *value = false;
+        return true;
+    }
+    return false;
+}
+
+
+bool XMLUtil::ToFloat( const char* str, float* value )
+{
+    if ( TIXML_SSCANF( str, "%f", value ) == 1 ) {
+        return true;
+    }
+    return false;
+}
+
+
+bool XMLUtil::ToDouble( const char* str, double* value )
+{
+    if ( TIXML_SSCANF( str, "%lf", value ) == 1 ) {
+        return true;
+    }
+    return false;
+}
+
+
+bool XMLUtil::ToInt64(const char* str, int64_t* value)
+{
+	long long v = 0;	// horrible syntax trick to make the compiler happy about %lld
+	if (TIXML_SSCANF(str, "%lld", &v) == 1) {
+		*value = (int64_t)v;
+		return true;
+	}
+	return false;
+}
+
+
+char* XMLDocument::Identify( char* p, XMLNode** node )
+{
+    TIXMLASSERT( node );
+    TIXMLASSERT( p );
+    char* const start = p;
+    int const startLine = _parseCurLineNum;
+    p = XMLUtil::SkipWhiteSpace( p, &_parseCurLineNum );
+    if( !*p ) {
+        *node = 0;
+        TIXMLASSERT( p );
+        return p;
+    }
+
+    // These strings define the matching patterns:
+    static const char* xmlHeader		= { "<?" };
+    static const char* commentHeader	= { "<!--" };
+    static const char* cdataHeader		= { "<![CDATA[" };
+    static const char* dtdHeader		= { "<!" };
+    static const char* elementHeader	= { "<" };	// and a header for everything else; check last.
+
+    static const int xmlHeaderLen		= 2;
+    static const int commentHeaderLen	= 4;
+    static const int cdataHeaderLen		= 9;
+    static const int dtdHeaderLen		= 2;
+    static const int elementHeaderLen	= 1;
+
+    TIXMLASSERT( sizeof( XMLComment ) == sizeof( XMLUnknown ) );		// use same memory pool
+    TIXMLASSERT( sizeof( XMLComment ) == sizeof( XMLDeclaration ) );	// use same memory pool
+    XMLNode* returnNode = 0;
+    if ( XMLUtil::StringEqual( p, xmlHeader, xmlHeaderLen ) ) {
+        returnNode = CreateUnlinkedNode<XMLDeclaration>( _commentPool );
+        returnNode->_parseLineNum = _parseCurLineNum;
+        p += xmlHeaderLen;
+    }
+    else if ( XMLUtil::StringEqual( p, commentHeader, commentHeaderLen ) ) {
+        returnNode = CreateUnlinkedNode<XMLComment>( _commentPool );
+        returnNode->_parseLineNum = _parseCurLineNum;
+        p += commentHeaderLen;
+    }
+    else if ( XMLUtil::StringEqual( p, cdataHeader, cdataHeaderLen ) ) {
+        XMLText* text = CreateUnlinkedNode<XMLText>( _textPool );
+        returnNode = text;
+        returnNode->_parseLineNum = _parseCurLineNum;
+        p += cdataHeaderLen;
+        text->SetCData( true );
+    }
+    else if ( XMLUtil::StringEqual( p, dtdHeader, dtdHeaderLen ) ) {
+        returnNode = CreateUnlinkedNode<XMLUnknown>( _commentPool );
+        returnNode->_parseLineNum = _parseCurLineNum;
+        p += dtdHeaderLen;
+    }
+    else if ( XMLUtil::StringEqual( p, elementHeader, elementHeaderLen ) ) {
+        returnNode =  CreateUnlinkedNode<XMLElement>( _elementPool );
+        returnNode->_parseLineNum = _parseCurLineNum;
+        p += elementHeaderLen;
+    }
+    else {
+        returnNode = CreateUnlinkedNode<XMLText>( _textPool );
+        returnNode->_parseLineNum = _parseCurLineNum; // Report line of first non-whitespace character
+        p = start;	// Back it up, all the text counts.
+        _parseCurLineNum = startLine;
+    }
+
+    TIXMLASSERT( returnNode );
+    TIXMLASSERT( p );
+    *node = returnNode;
+    return p;
+}
+
+
+bool XMLDocument::Accept( XMLVisitor* visitor ) const
+{
+    TIXMLASSERT( visitor );
+    if ( visitor->VisitEnter( *this ) ) {
+        for ( const XMLNode* node=FirstChild(); node; node=node->NextSibling() ) {
+            if ( !node->Accept( visitor ) ) {
+                break;
+            }
+        }
+    }
+    return visitor->VisitExit( *this );
+}
+
+
+// --------- XMLNode ----------- //
+
+XMLNode::XMLNode( XMLDocument* doc ) :
+    _document( doc ),
+    _parent( 0 ),
+    _parseLineNum( 0 ),
+    _firstChild( 0 ), _lastChild( 0 ),
+    _prev( 0 ), _next( 0 ),
+	_userData( 0 ),
+    _memPool( 0 )
+{
+}
+
+
+XMLNode::~XMLNode()
+{
+    DeleteChildren();
+    if ( _parent ) {
+        _parent->Unlink( this );
+    }
+}
+
+const char* XMLNode::Value() const 
+{
+    // Edge case: XMLDocuments don't have a Value. Return null.
+    if ( this->ToDocument() )
+        return 0;
+    return _value.GetStr();
+}
+
+void XMLNode::SetValue( const char* str, bool staticMem )
+{
+    if ( staticMem ) {
+        _value.SetInternedStr( str );
+    }
+    else {
+        _value.SetStr( str );
+    }
+}
+
+XMLNode* XMLNode::DeepClone(XMLDocument* target) const
+{
+	XMLNode* clone = this->ShallowClone(target);
+	if (!clone) return 0;
+
+	for (const XMLNode* child = this->FirstChild(); child; child = child->NextSibling()) {
+		XMLNode* childClone = child->DeepClone(target);
+		TIXMLASSERT(childClone);
+		clone->InsertEndChild(childClone);
+	}
+	return clone;
+}
+
+void XMLNode::DeleteChildren()
+{
+    while( _firstChild ) {
+        TIXMLASSERT( _lastChild );
+        DeleteChild( _firstChild );
+    }
+    _firstChild = _lastChild = 0;
+}
+
+
+void XMLNode::Unlink( XMLNode* child )
+{
+    TIXMLASSERT( child );
+    TIXMLASSERT( child->_document == _document );
+    TIXMLASSERT( child->_parent == this );
+    if ( child == _firstChild ) {
+        _firstChild = _firstChild->_next;
+    }
+    if ( child == _lastChild ) {
+        _lastChild = _lastChild->_prev;
+    }
+
+    if ( child->_prev ) {
+        child->_prev->_next = child->_next;
+    }
+    if ( child->_next ) {
+        child->_next->_prev = child->_prev;
+    }
+	child->_next = 0;
+	child->_prev = 0;
+	child->_parent = 0;
+}
+
+
+void XMLNode::DeleteChild( XMLNode* node )
+{
+    TIXMLASSERT( node );
+    TIXMLASSERT( node->_document == _document );
+    TIXMLASSERT( node->_parent == this );
+    Unlink( node );
+	TIXMLASSERT(node->_prev == 0);
+	TIXMLASSERT(node->_next == 0);
+	TIXMLASSERT(node->_parent == 0);
+    DeleteNode( node );
+}
+
+
+XMLNode* XMLNode::InsertEndChild( XMLNode* addThis )
+{
+    TIXMLASSERT( addThis );
+    if ( addThis->_document != _document ) {
+        TIXMLASSERT( false );
+        return 0;
+    }
+    InsertChildPreamble( addThis );
+
+    if ( _lastChild ) {
+        TIXMLASSERT( _firstChild );
+        TIXMLASSERT( _lastChild->_next == 0 );
+        _lastChild->_next = addThis;
+        addThis->_prev = _lastChild;
+        _lastChild = addThis;
+
+        addThis->_next = 0;
+    }
+    else {
+        TIXMLASSERT( _firstChild == 0 );
+        _firstChild = _lastChild = addThis;
+
+        addThis->_prev = 0;
+        addThis->_next = 0;
+    }
+    addThis->_parent = this;
+    return addThis;
+}
+
+
+XMLNode* XMLNode::InsertFirstChild( XMLNode* addThis )
+{
+    TIXMLASSERT( addThis );
+    if ( addThis->_document != _document ) {
+        TIXMLASSERT( false );
+        return 0;
+    }
+    InsertChildPreamble( addThis );
+
+    if ( _firstChild ) {
+        TIXMLASSERT( _lastChild );
+        TIXMLASSERT( _firstChild->_prev == 0 );
+
+        _firstChild->_prev = addThis;
+        addThis->_next = _firstChild;
+        _firstChild = addThis;
+
+        addThis->_prev = 0;
+    }
+    else {
+        TIXMLASSERT( _lastChild == 0 );
+        _firstChild = _lastChild = addThis;
+
+        addThis->_prev = 0;
+        addThis->_next = 0;
+    }
+    addThis->_parent = this;
+    return addThis;
+}
+
+
+XMLNode* XMLNode::InsertAfterChild( XMLNode* afterThis, XMLNode* addThis )
+{
+    TIXMLASSERT( addThis );
+    if ( addThis->_document != _document ) {
+        TIXMLASSERT( false );
+        return 0;
+    }
+
+    TIXMLASSERT( afterThis );
+
+    if ( afterThis->_parent != this ) {
+        TIXMLASSERT( false );
+        return 0;
+    }
+
+    if ( afterThis->_next == 0 ) {
+        // The last node or the only node.
+        return InsertEndChild( addThis );
+    }
+    InsertChildPreamble( addThis );
+    addThis->_prev = afterThis;
+    addThis->_next = afterThis->_next;
+    afterThis->_next->_prev = addThis;
+    afterThis->_next = addThis;
+    addThis->_parent = this;
+    return addThis;
+}
+
+
+
+
+const XMLElement* XMLNode::FirstChildElement( const char* name ) const
+{
+    for( const XMLNode* node = _firstChild; node; node = node->_next ) {
+        const XMLElement* element = node->ToElementWithName( name );
+        if ( element ) {
+            return element;
+        }
+    }
+    return 0;
+}
+
+
+const XMLElement* XMLNode::LastChildElement( const char* name ) const
+{
+    for( const XMLNode* node = _lastChild; node; node = node->_prev ) {
+        const XMLElement* element = node->ToElementWithName( name );
+        if ( element ) {
+            return element;
+        }
+    }
+    return 0;
+}
+
+
+const XMLElement* XMLNode::NextSiblingElement( const char* name ) const
+{
+    for( const XMLNode* node = _next; node; node = node->_next ) {
+        const XMLElement* element = node->ToElementWithName( name );
+        if ( element ) {
+            return element;
+        }
+    }
+    return 0;
+}
+
+
+const XMLElement* XMLNode::PreviousSiblingElement( const char* name ) const
+{
+    for( const XMLNode* node = _prev; node; node = node->_prev ) {
+        const XMLElement* element = node->ToElementWithName( name );
+        if ( element ) {
+            return element;
+        }
+    }
+    return 0;
+}
+
+
+char* XMLNode::ParseDeep( char* p, StrPair* parentEndTag, int* curLineNumPtr )
+{
+    // This is a recursive method, but thinking about it "at the current level"
+    // it is a pretty simple flat list:
+    //		<foo/>
+    //		<!-- comment -->
+    //
+    // With a special case:
+    //		<foo>
+    //		</foo>
+    //		<!-- comment -->
+    //
+    // Where the closing element (/foo) *must* be the next thing after the opening
+    // element, and the names must match. BUT the tricky bit is that the closing
+    // element will be read by the child.
+    //
+    // 'endTag' is the end tag for this node, it is returned by a call to a child.
+    // 'parentEnd' is the end tag for the parent, which is filled in and returned.
+
+    while( p && *p ) {
+        XMLNode* node = 0;
+
+        p = _document->Identify( p, &node );
+        TIXMLASSERT( p );
+        if ( node == 0 ) {
+            break;
+        }
+
+        int initialLineNum = node->_parseLineNum;
+
+        StrPair endTag;
+        p = node->ParseDeep( p, &endTag, curLineNumPtr );
+        if ( !p ) {
+            DeleteNode( node );
+            if ( !_document->Error() ) {
+                _document->SetError( XML_ERROR_PARSING, 0, 0, initialLineNum);
+            }
+            break;
+        }
+
+        XMLDeclaration* decl = node->ToDeclaration();
+        if ( decl ) {
+            // Declarations are only allowed at document level
+            bool wellLocated = ( ToDocument() != 0 );
+            if ( wellLocated ) {
+                // Multiple declarations are allowed but all declarations
+                // must occur before anything else
+                for ( const XMLNode* existingNode = _document->FirstChild(); existingNode; existingNode = existingNode->NextSibling() ) {
+                    if ( !existingNode->ToDeclaration() ) {
+                        wellLocated = false;
+                        break;
+                    }
+                }
+            }
+            if ( !wellLocated ) {
+                _document->SetError( XML_ERROR_PARSING_DECLARATION, decl->Value(), 0, initialLineNum);
+                DeleteNode( node );
+                break;
+            }
+        }
+
+        XMLElement* ele = node->ToElement();
+        if ( ele ) {
+            // We read the end tag. Return it to the parent.
+            if ( ele->ClosingType() == XMLElement::CLOSING ) {
+                if ( parentEndTag ) {
+                    ele->_value.TransferTo( parentEndTag );
+                }
+                node->_memPool->SetTracked();   // created and then immediately deleted.
+                DeleteNode( node );
+                return p;
+            }
+
+            // Handle an end tag returned to this level.
+            // And handle a bunch of annoying errors.
+            bool mismatch = false;
+            if ( endTag.Empty() ) {
+                if ( ele->ClosingType() == XMLElement::OPEN ) {
+                    mismatch = true;
+                }
+            }
+            else {
+                if ( ele->ClosingType() != XMLElement::OPEN ) {
+                    mismatch = true;
+                }
+                else if ( !XMLUtil::StringEqual( endTag.GetStr(), ele->Name() ) ) {
+                    mismatch = true;
+                }
+            }
+            if ( mismatch ) {
+                _document->SetError( XML_ERROR_MISMATCHED_ELEMENT, ele->Name(), 0, initialLineNum);
+                DeleteNode( node );
+                break;
+            }
+        }
+        InsertEndChild( node );
+    }
+    return 0;
+}
+
+/*static*/ void XMLNode::DeleteNode( XMLNode* node )
+{
+    if ( node == 0 ) {
+        return;
+    }
+	TIXMLASSERT(node->_document);
+	if (!node->ToDocument()) {
+		node->_document->MarkInUse(node);
+	}
+
+    MemPool* pool = node->_memPool;
+    node->~XMLNode();
+    pool->Free( node );
+}
+
+void XMLNode::InsertChildPreamble( XMLNode* insertThis ) const
+{
+    TIXMLASSERT( insertThis );
+    TIXMLASSERT( insertThis->_document == _document );
+
+	if (insertThis->_parent) {
+        insertThis->_parent->Unlink( insertThis );
+	}
+	else {
+		insertThis->_document->MarkInUse(insertThis);
+        insertThis->_memPool->SetTracked();
+	}
+}
+
+const XMLElement* XMLNode::ToElementWithName( const char* name ) const
+{
+    const XMLElement* element = this->ToElement();
+    if ( element == 0 ) {
+        return 0;
+    }
+    if ( name == 0 ) {
+        return element;
+    }
+    if ( XMLUtil::StringEqual( element->Name(), name ) ) {
+       return element;
+    }
+    return 0;
+}
+
+// --------- XMLText ---------- //
+char* XMLText::ParseDeep( char* p, StrPair*, int* curLineNumPtr )
+{
+    const char* start = p;
+    if ( this->CData() ) {
+        p = _value.ParseText( p, "]]>", StrPair::NEEDS_NEWLINE_NORMALIZATION, curLineNumPtr );
+        if ( !p ) {
+            _document->SetError( XML_ERROR_PARSING_CDATA, start, 0, _parseLineNum );
+        }
+        return p;
+    }
+    else {
+        int flags = _document->ProcessEntities() ? StrPair::TEXT_ELEMENT : StrPair::TEXT_ELEMENT_LEAVE_ENTITIES;
+        if ( _document->WhitespaceMode() == COLLAPSE_WHITESPACE ) {
+            flags |= StrPair::NEEDS_WHITESPACE_COLLAPSING;
+        }
+
+        p = _value.ParseText( p, "<", flags, curLineNumPtr );
+        if ( p && *p ) {
+            return p-1;
+        }
+        if ( !p ) {
+            _document->SetError( XML_ERROR_PARSING_TEXT, start, 0, _parseLineNum );
+        }
+    }
+    return 0;
+}
+
+
+XMLNode* XMLText::ShallowClone( XMLDocument* doc ) const
+{
+    if ( !doc ) {
+        doc = _document;
+    }
+    XMLText* text = doc->NewText( Value() );	// fixme: this will always allocate memory. Intern?
+    text->SetCData( this->CData() );
+    return text;
+}
+
+
+bool XMLText::ShallowEqual( const XMLNode* compare ) const
+{
+    TIXMLASSERT( compare );
+    const XMLText* text = compare->ToText();
+    return ( text && XMLUtil::StringEqual( text->Value(), Value() ) );
+}
+
+
+bool XMLText::Accept( XMLVisitor* visitor ) const
+{
+    TIXMLASSERT( visitor );
+    return visitor->Visit( *this );
+}
+
+
+// --------- XMLComment ---------- //
+
+XMLComment::XMLComment( XMLDocument* doc ) : XMLNode( doc )
+{
+}
+
+
+XMLComment::~XMLComment()
+{
+}
+
+
+char* XMLComment::ParseDeep( char* p, StrPair*, int* curLineNumPtr )
+{
+    // Comment parses as text.
+    const char* start = p;
+    p = _value.ParseText( p, "-->", StrPair::COMMENT, curLineNumPtr );
+    if ( p == 0 ) {
+        _document->SetError( XML_ERROR_PARSING_COMMENT, start, 0, _parseLineNum );
+    }
+    return p;
+}
+
+
+XMLNode* XMLComment::ShallowClone( XMLDocument* doc ) const
+{
+    if ( !doc ) {
+        doc = _document;
+    }
+    XMLComment* comment = doc->NewComment( Value() );	// fixme: this will always allocate memory. Intern?
+    return comment;
+}
+
+
+bool XMLComment::ShallowEqual( const XMLNode* compare ) const
+{
+    TIXMLASSERT( compare );
+    const XMLComment* comment = compare->ToComment();
+    return ( comment && XMLUtil::StringEqual( comment->Value(), Value() ));
+}
+
+
+bool XMLComment::Accept( XMLVisitor* visitor ) const
+{
+    TIXMLASSERT( visitor );
+    return visitor->Visit( *this );
+}
+
+
+// --------- XMLDeclaration ---------- //
+
+XMLDeclaration::XMLDeclaration( XMLDocument* doc ) : XMLNode( doc )
+{
+}
+
+
+XMLDeclaration::~XMLDeclaration()
+{
+    //printf( "~XMLDeclaration\n" );
+}
+
+
+char* XMLDeclaration::ParseDeep( char* p, StrPair*, int* curLineNumPtr )
+{
+    // Declaration parses as text.
+    const char* start = p;
+    p = _value.ParseText( p, "?>", StrPair::NEEDS_NEWLINE_NORMALIZATION, curLineNumPtr );
+    if ( p == 0 ) {
+        _document->SetError( XML_ERROR_PARSING_DECLARATION, start, 0, _parseLineNum );
+    }
+    return p;
+}
+
+
+XMLNode* XMLDeclaration::ShallowClone( XMLDocument* doc ) const
+{
+    if ( !doc ) {
+        doc = _document;
+    }
+    XMLDeclaration* dec = doc->NewDeclaration( Value() );	// fixme: this will always allocate memory. Intern?
+    return dec;
+}
+
+
+bool XMLDeclaration::ShallowEqual( const XMLNode* compare ) const
+{
+    TIXMLASSERT( compare );
+    const XMLDeclaration* declaration = compare->ToDeclaration();
+    return ( declaration && XMLUtil::StringEqual( declaration->Value(), Value() ));
+}
+
+
+
+bool XMLDeclaration::Accept( XMLVisitor* visitor ) const
+{
+    TIXMLASSERT( visitor );
+    return visitor->Visit( *this );
+}
+
+// --------- XMLUnknown ---------- //
+
+XMLUnknown::XMLUnknown( XMLDocument* doc ) : XMLNode( doc )
+{
+}
+
+
+XMLUnknown::~XMLUnknown()
+{
+}
+
+
+char* XMLUnknown::ParseDeep( char* p, StrPair*, int* curLineNumPtr )
+{
+    // Unknown parses as text.
+    const char* start = p;
+
+    p = _value.ParseText( p, ">", StrPair::NEEDS_NEWLINE_NORMALIZATION, curLineNumPtr );
+    if ( !p ) {
+        _document->SetError( XML_ERROR_PARSING_UNKNOWN, start, 0, _parseLineNum );
+    }
+    return p;
+}
+
+
+XMLNode* XMLUnknown::ShallowClone( XMLDocument* doc ) const
+{
+    if ( !doc ) {
+        doc = _document;
+    }
+    XMLUnknown* text = doc->NewUnknown( Value() );	// fixme: this will always allocate memory. Intern?
+    return text;
+}
+
+
+bool XMLUnknown::ShallowEqual( const XMLNode* compare ) const
+{
+    TIXMLASSERT( compare );
+    const XMLUnknown* unknown = compare->ToUnknown();
+    return ( unknown && XMLUtil::StringEqual( unknown->Value(), Value() ));
+}
+
+
+bool XMLUnknown::Accept( XMLVisitor* visitor ) const
+{
+    TIXMLASSERT( visitor );
+    return visitor->Visit( *this );
+}
+
+// --------- XMLAttribute ---------- //
+
+const char* XMLAttribute::Name() const 
+{
+    return _name.GetStr();
+}
+
+const char* XMLAttribute::Value() const 
+{
+    return _value.GetStr();
+}
+
+char* XMLAttribute::ParseDeep( char* p, bool processEntities, int* curLineNumPtr )
+{
+    // Parse using the name rules: bug fix, was using ParseText before
+    p = _name.ParseName( p );
+    if ( !p || !*p ) {
+        return 0;
+    }
+
+    // Skip white space before =
+    p = XMLUtil::SkipWhiteSpace( p, curLineNumPtr );
+    if ( *p != '=' ) {
+        return 0;
+    }
+
+    ++p;	// move up to opening quote
+    p = XMLUtil::SkipWhiteSpace( p, curLineNumPtr );
+    if ( *p != '\"' && *p != '\'' ) {
+        return 0;
+    }
+
+    char endTag[2] = { *p, 0 };
+    ++p;	// move past opening quote
+
+    p = _value.ParseText( p, endTag, processEntities ? StrPair::ATTRIBUTE_VALUE : StrPair::ATTRIBUTE_VALUE_LEAVE_ENTITIES, curLineNumPtr );
+    return p;
+}
+
+
+void XMLAttribute::SetName( const char* n )
+{
+    _name.SetStr( n );
+}
+
+
+XMLError XMLAttribute::QueryIntValue( int* value ) const
+{
+    if ( XMLUtil::ToInt( Value(), value )) {
+        return XML_SUCCESS;
+    }
+    return XML_WRONG_ATTRIBUTE_TYPE;
+}
+
+
+XMLError XMLAttribute::QueryUnsignedValue( unsigned int* value ) const
+{
+    if ( XMLUtil::ToUnsigned( Value(), value )) {
+        return XML_SUCCESS;
+    }
+    return XML_WRONG_ATTRIBUTE_TYPE;
+}
+
+
+XMLError XMLAttribute::QueryInt64Value(int64_t* value) const
+{
+	if (XMLUtil::ToInt64(Value(), value)) {
+		return XML_SUCCESS;
+	}
+	return XML_WRONG_ATTRIBUTE_TYPE;
+}
+
+
+XMLError XMLAttribute::QueryBoolValue( bool* value ) const
+{
+    if ( XMLUtil::ToBool( Value(), value )) {
+        return XML_SUCCESS;
+    }
+    return XML_WRONG_ATTRIBUTE_TYPE;
+}
+
+
+XMLError XMLAttribute::QueryFloatValue( float* value ) const
+{
+    if ( XMLUtil::ToFloat( Value(), value )) {
+        return XML_SUCCESS;
+    }
+    return XML_WRONG_ATTRIBUTE_TYPE;
+}
+
+
+XMLError XMLAttribute::QueryDoubleValue( double* value ) const
+{
+    if ( XMLUtil::ToDouble( Value(), value )) {
+        return XML_SUCCESS;
+    }
+    return XML_WRONG_ATTRIBUTE_TYPE;
+}
+
+
+void XMLAttribute::SetAttribute( const char* v )
+{
+    _value.SetStr( v );
+}
+
+
+void XMLAttribute::SetAttribute( int v )
+{
+    char buf[BUF_SIZE];
+    XMLUtil::ToStr( v, buf, BUF_SIZE );
+    _value.SetStr( buf );
+}
+
+
+void XMLAttribute::SetAttribute( unsigned v )
+{
+    char buf[BUF_SIZE];
+    XMLUtil::ToStr( v, buf, BUF_SIZE );
+    _value.SetStr( buf );
+}
+
+
+void XMLAttribute::SetAttribute(int64_t v)
+{
+	char buf[BUF_SIZE];
+	XMLUtil::ToStr(v, buf, BUF_SIZE);
+	_value.SetStr(buf);
+}
+
+
+
+void XMLAttribute::SetAttribute( bool v )
+{
+    char buf[BUF_SIZE];
+    XMLUtil::ToStr( v, buf, BUF_SIZE );
+    _value.SetStr( buf );
+}
+
+void XMLAttribute::SetAttribute( double v )
+{
+    char buf[BUF_SIZE];
+    XMLUtil::ToStr( v, buf, BUF_SIZE );
+    _value.SetStr( buf );
+}
+
+void XMLAttribute::SetAttribute( float v )
+{
+    char buf[BUF_SIZE];
+    XMLUtil::ToStr( v, buf, BUF_SIZE );
+    _value.SetStr( buf );
+}
+
+
+// --------- XMLElement ---------- //
+XMLElement::XMLElement( XMLDocument* doc ) : XMLNode( doc ),
+    _closingType( OPEN ),
+    _rootAttribute( 0 )
+{
+}
+
+
+XMLElement::~XMLElement()
+{
+    while( _rootAttribute ) {
+        XMLAttribute* next = _rootAttribute->_next;
+        DeleteAttribute( _rootAttribute );
+        _rootAttribute = next;
+    }
+}
+
+
+const XMLAttribute* XMLElement::FindAttribute( const char* name ) const
+{
+    for( XMLAttribute* a = _rootAttribute; a; a = a->_next ) {
+        if ( XMLUtil::StringEqual( a->Name(), name ) ) {
+            return a;
+        }
+    }
+    return 0;
+}
+
+
+const char* XMLElement::Attribute( const char* name, const char* value ) const
+{
+    const XMLAttribute* a = FindAttribute( name );
+    if ( !a ) {
+        return 0;
+    }
+    if ( !value || XMLUtil::StringEqual( a->Value(), value )) {
+        return a->Value();
+    }
+    return 0;
+}
+
+int XMLElement::IntAttribute(const char* name, int defaultValue) const 
+{
+	int i = defaultValue;
+	QueryIntAttribute(name, &i);
+	return i;
+}
+
+unsigned XMLElement::UnsignedAttribute(const char* name, unsigned defaultValue) const 
+{
+	unsigned i = defaultValue;
+	QueryUnsignedAttribute(name, &i);
+	return i;
+}
+
+int64_t XMLElement::Int64Attribute(const char* name, int64_t defaultValue) const 
+{
+	int64_t i = defaultValue;
+	QueryInt64Attribute(name, &i);
+	return i;
+}
+
+bool XMLElement::BoolAttribute(const char* name, bool defaultValue) const 
+{
+	bool b = defaultValue;
+	QueryBoolAttribute(name, &b);
+	return b;
+}
+
+double XMLElement::DoubleAttribute(const char* name, double defaultValue) const 
+{
+	double d = defaultValue;
+	QueryDoubleAttribute(name, &d);
+	return d;
+}
+
+float XMLElement::FloatAttribute(const char* name, float defaultValue) const 
+{
+	float f = defaultValue;
+	QueryFloatAttribute(name, &f);
+	return f;
+}
+
+const char* XMLElement::GetText() const
+{
+    if ( FirstChild() && FirstChild()->ToText() ) {
+        return FirstChild()->Value();
+    }
+    return 0;
+}
+
+
+void	XMLElement::SetText( const char* inText )
+{
+	if ( FirstChild() && FirstChild()->ToText() )
+		FirstChild()->SetValue( inText );
+	else {
+		XMLText*	theText = GetDocument()->NewText( inText );
+		InsertFirstChild( theText );
+	}
+}
+
+
+void XMLElement::SetText( int v ) 
+{
+    char buf[BUF_SIZE];
+    XMLUtil::ToStr( v, buf, BUF_SIZE );
+    SetText( buf );
+}
+
+
+void XMLElement::SetText( unsigned v ) 
+{
+    char buf[BUF_SIZE];
+    XMLUtil::ToStr( v, buf, BUF_SIZE );
+    SetText( buf );
+}
+
+
+void XMLElement::SetText(int64_t v)
+{
+	char buf[BUF_SIZE];
+	XMLUtil::ToStr(v, buf, BUF_SIZE);
+	SetText(buf);
+}
+
+
+void XMLElement::SetText( bool v )
+{
+    char buf[BUF_SIZE];
+    XMLUtil::ToStr( v, buf, BUF_SIZE );
+    SetText( buf );
+}
+
+
+void XMLElement::SetText( float v ) 
+{
+    char buf[BUF_SIZE];
+    XMLUtil::ToStr( v, buf, BUF_SIZE );
+    SetText( buf );
+}
+
+
+void XMLElement::SetText( double v ) 
+{
+    char buf[BUF_SIZE];
+    XMLUtil::ToStr( v, buf, BUF_SIZE );
+    SetText( buf );
+}
+
+
+XMLError XMLElement::QueryIntText( int* ival ) const
+{
+    if ( FirstChild() && FirstChild()->ToText() ) {
+        const char* t = FirstChild()->Value();
+        if ( XMLUtil::ToInt( t, ival ) ) {
+            return XML_SUCCESS;
+        }
+        return XML_CAN_NOT_CONVERT_TEXT;
+    }
+    return XML_NO_TEXT_NODE;
+}
+
+
+XMLError XMLElement::QueryUnsignedText( unsigned* uval ) const
+{
+    if ( FirstChild() && FirstChild()->ToText() ) {
+        const char* t = FirstChild()->Value();
+        if ( XMLUtil::ToUnsigned( t, uval ) ) {
+            return XML_SUCCESS;
+        }
+        return XML_CAN_NOT_CONVERT_TEXT;
+    }
+    return XML_NO_TEXT_NODE;
+}
+
+
+XMLError XMLElement::QueryInt64Text(int64_t* ival) const
+{
+	if (FirstChild() && FirstChild()->ToText()) {
+		const char* t = FirstChild()->Value();
+		if (XMLUtil::ToInt64(t, ival)) {
+			return XML_SUCCESS;
+		}
+		return XML_CAN_NOT_CONVERT_TEXT;
+	}
+	return XML_NO_TEXT_NODE;
+}
+
+
+XMLError XMLElement::QueryBoolText( bool* bval ) const
+{
+    if ( FirstChild() && FirstChild()->ToText() ) {
+        const char* t = FirstChild()->Value();
+        if ( XMLUtil::ToBool( t, bval ) ) {
+            return XML_SUCCESS;
+        }
+        return XML_CAN_NOT_CONVERT_TEXT;
+    }
+    return XML_NO_TEXT_NODE;
+}
+
+
+XMLError XMLElement::QueryDoubleText( double* dval ) const
+{
+    if ( FirstChild() && FirstChild()->ToText() ) {
+        const char* t = FirstChild()->Value();
+        if ( XMLUtil::ToDouble( t, dval ) ) {
+            return XML_SUCCESS;
+        }
+        return XML_CAN_NOT_CONVERT_TEXT;
+    }
+    return XML_NO_TEXT_NODE;
+}
+
+
+XMLError XMLElement::QueryFloatText( float* fval ) const
+{
+    if ( FirstChild() && FirstChild()->ToText() ) {
+        const char* t = FirstChild()->Value();
+        if ( XMLUtil::ToFloat( t, fval ) ) {
+            return XML_SUCCESS;
+        }
+        return XML_CAN_NOT_CONVERT_TEXT;
+    }
+    return XML_NO_TEXT_NODE;
+}
+
+int XMLElement::IntText(int defaultValue) const
+{
+	int i = defaultValue;
+	QueryIntText(&i);
+	return i;
+}
+
+unsigned XMLElement::UnsignedText(unsigned defaultValue) const
+{
+	unsigned i = defaultValue;
+	QueryUnsignedText(&i);
+	return i;
+}
+
+int64_t XMLElement::Int64Text(int64_t defaultValue) const
+{
+	int64_t i = defaultValue;
+	QueryInt64Text(&i);
+	return i;
+}
+
+bool XMLElement::BoolText(bool defaultValue) const
+{
+	bool b = defaultValue;
+	QueryBoolText(&b);
+	return b;
+}
+
+double XMLElement::DoubleText(double defaultValue) const
+{
+	double d = defaultValue;
+	QueryDoubleText(&d);
+	return d;
+}
+
+float XMLElement::FloatText(float defaultValue) const
+{
+	float f = defaultValue;
+	QueryFloatText(&f);
+	return f;
+}
+
+
+XMLAttribute* XMLElement::FindOrCreateAttribute( const char* name )
+{
+    XMLAttribute* last = 0;
+    XMLAttribute* attrib = 0;
+    for( attrib = _rootAttribute;
+            attrib;
+            last = attrib, attrib = attrib->_next ) {
+        if ( XMLUtil::StringEqual( attrib->Name(), name ) ) {
+            break;
+        }
+    }
+    if ( !attrib ) {
+        attrib = CreateAttribute();
+        TIXMLASSERT( attrib );
+        if ( last ) {
+            TIXMLASSERT( last->_next == 0 );
+            last->_next = attrib;
+        }
+        else {
+            TIXMLASSERT( _rootAttribute == 0 );
+            _rootAttribute = attrib;
+        }
+        attrib->SetName( name );
+    }
+    return attrib;
+}
+
+
+void XMLElement::DeleteAttribute( const char* name )
+{
+    XMLAttribute* prev = 0;
+    for( XMLAttribute* a=_rootAttribute; a; a=a->_next ) {
+        if ( XMLUtil::StringEqual( name, a->Name() ) ) {
+            if ( prev ) {
+                prev->_next = a->_next;
+            }
+            else {
+                _rootAttribute = a->_next;
+            }
+            DeleteAttribute( a );
+            break;
+        }
+        prev = a;
+    }
+}
+
+
+char* XMLElement::ParseAttributes( char* p, int* curLineNumPtr )
+{
+    const char* start = p;
+    XMLAttribute* prevAttribute = 0;
+
+    // Read the attributes.
+    while( p ) {
+        p = XMLUtil::SkipWhiteSpace( p, curLineNumPtr );
+        if ( !(*p) ) {
+            _document->SetError( XML_ERROR_PARSING_ELEMENT, start, Name(), _parseLineNum );
+            return 0;
+        }
+
+        // attribute.
+        if (XMLUtil::IsNameStartChar( *p ) ) {
+            XMLAttribute* attrib = CreateAttribute();
+            TIXMLASSERT( attrib );
+            attrib->_parseLineNum = _document->_parseCurLineNum;
+
+            int attrLineNum = attrib->_parseLineNum;
+
+            p = attrib->ParseDeep( p, _document->ProcessEntities(), curLineNumPtr );
+            if ( !p || Attribute( attrib->Name() ) ) {
+                DeleteAttribute( attrib );
+                _document->SetError( XML_ERROR_PARSING_ATTRIBUTE, start, p, attrLineNum );
+                return 0;
+            }
+            // There is a minor bug here: if the attribute in the source xml
+            // document is duplicated, it will not be detected and the
+            // attribute will be doubly added. However, tracking the 'prevAttribute'
+            // avoids re-scanning the attribute list. Preferring performance for
+            // now, may reconsider in the future.
+            if ( prevAttribute ) {
+                TIXMLASSERT( prevAttribute->_next == 0 );
+                prevAttribute->_next = attrib;
+            }
+            else {
+                TIXMLASSERT( _rootAttribute == 0 );
+                _rootAttribute = attrib;
+            }
+            prevAttribute = attrib;
+        }
+        // end of the tag
+        else if ( *p == '>' ) {
+            ++p;
+            break;
+        }
+        // end of the tag
+        else if ( *p == '/' && *(p+1) == '>' ) {
+            _closingType = CLOSED;
+            return p+2;	// done; sealed element.
+        }
+        else {
+            _document->SetError( XML_ERROR_PARSING_ELEMENT, start, p, _parseLineNum );
+            return 0;
+        }
+    }
+    return p;
+}
+
+void XMLElement::DeleteAttribute( XMLAttribute* attribute )
+{
+    if ( attribute == 0 ) {
+        return;
+    }
+    MemPool* pool = attribute->_memPool;
+    attribute->~XMLAttribute();
+    pool->Free( attribute );
+}
+
+XMLAttribute* XMLElement::CreateAttribute()
+{
+    TIXMLASSERT( sizeof( XMLAttribute ) == _document->_attributePool.ItemSize() );
+    XMLAttribute* attrib = new (_document->_attributePool.Alloc() ) XMLAttribute();
+    TIXMLASSERT( attrib );
+    attrib->_memPool = &_document->_attributePool;
+    attrib->_memPool->SetTracked();
+    return attrib;
+}
+
+//
+//	<ele></ele>
+//	<ele>foo<b>bar</b></ele>
+//
+char* XMLElement::ParseDeep( char* p, StrPair* parentEndTag, int* curLineNumPtr )
+{
+    // Read the element name.
+    p = XMLUtil::SkipWhiteSpace( p, curLineNumPtr );
+
+    // The closing element is the </element> form. It is
+    // parsed just like a regular element then deleted from
+    // the DOM.
+    if ( *p == '/' ) {
+        _closingType = CLOSING;
+        ++p;
+    }
+
+    p = _value.ParseName( p );
+    if ( _value.Empty() ) {
+        return 0;
+    }
+
+    p = ParseAttributes( p, curLineNumPtr );
+    if ( !p || !*p || _closingType != OPEN ) {
+        return p;
+    }
+
+    p = XMLNode::ParseDeep( p, parentEndTag, curLineNumPtr );
+    return p;
+}
+
+
+
+XMLNode* XMLElement::ShallowClone( XMLDocument* doc ) const
+{
+    if ( !doc ) {
+        doc = _document;
+    }
+    XMLElement* element = doc->NewElement( Value() );					// fixme: this will always allocate memory. Intern?
+    for( const XMLAttribute* a=FirstAttribute(); a; a=a->Next() ) {
+        element->SetAttribute( a->Name(), a->Value() );					// fixme: this will always allocate memory. Intern?
+    }
+    return element;
+}
+
+
+bool XMLElement::ShallowEqual( const XMLNode* compare ) const
+{
+    TIXMLASSERT( compare );
+    const XMLElement* other = compare->ToElement();
+    if ( other && XMLUtil::StringEqual( other->Name(), Name() )) {
+
+        const XMLAttribute* a=FirstAttribute();
+        const XMLAttribute* b=other->FirstAttribute();
+
+        while ( a && b ) {
+            if ( !XMLUtil::StringEqual( a->Value(), b->Value() ) ) {
+                return false;
+            }
+            a = a->Next();
+            b = b->Next();
+        }
+        if ( a || b ) {
+            // different count
+            return false;
+        }
+        return true;
+    }
+    return false;
+}
+
+
+bool XMLElement::Accept( XMLVisitor* visitor ) const
+{
+    TIXMLASSERT( visitor );
+    if ( visitor->VisitEnter( *this, _rootAttribute ) ) {
+        for ( const XMLNode* node=FirstChild(); node; node=node->NextSibling() ) {
+            if ( !node->Accept( visitor ) ) {
+                break;
+            }
+        }
+    }
+    return visitor->VisitExit( *this );
+}
+
+
+// --------- XMLDocument ----------- //
+
+// Warning: List must match 'enum XMLError'
+const char* XMLDocument::_errorNames[XML_ERROR_COUNT] = {
+    "XML_SUCCESS",
+    "XML_NO_ATTRIBUTE",
+    "XML_WRONG_ATTRIBUTE_TYPE",
+    "XML_ERROR_FILE_NOT_FOUND",
+    "XML_ERROR_FILE_COULD_NOT_BE_OPENED",
+    "XML_ERROR_FILE_READ_ERROR",
+    "UNUSED_XML_ERROR_ELEMENT_MISMATCH",
+    "XML_ERROR_PARSING_ELEMENT",
+    "XML_ERROR_PARSING_ATTRIBUTE",
+    "UNUSED_XML_ERROR_IDENTIFYING_TAG",
+    "XML_ERROR_PARSING_TEXT",
+    "XML_ERROR_PARSING_CDATA",
+    "XML_ERROR_PARSING_COMMENT",
+    "XML_ERROR_PARSING_DECLARATION",
+    "XML_ERROR_PARSING_UNKNOWN",
+    "XML_ERROR_EMPTY_DOCUMENT",
+    "XML_ERROR_MISMATCHED_ELEMENT",
+    "XML_ERROR_PARSING",
+    "XML_CAN_NOT_CONVERT_TEXT",
+    "XML_NO_TEXT_NODE"
+};
+
+
+XMLDocument::XMLDocument( bool processEntities, Whitespace whitespaceMode ) :
+    XMLNode( 0 ),
+    _writeBOM( false ),
+    _processEntities( processEntities ),
+    _errorID(XML_SUCCESS),
+    _whitespaceMode( whitespaceMode ),
+    _errorLineNum( 0 ),
+    _charBuffer( 0 ),
+    _parseCurLineNum( 0 )
+{
+    // avoid VC++ C4355 warning about 'this' in initializer list (C4355 is off by default in VS2012+)
+    _document = this;
+}
+
+
+XMLDocument::~XMLDocument()
+{
+    Clear();
+}
+
+
+void XMLDocument::MarkInUse(XMLNode* node)
+{
+	TIXMLASSERT(node);
+	TIXMLASSERT(node->_parent == 0);
+
+	for (int i = 0; i < _unlinked.Size(); ++i) {
+		if (node == _unlinked[i]) {
+			_unlinked.SwapRemove(i);
+			break;
+		}
+	}
+}
+
+void XMLDocument::Clear()
+{
+    DeleteChildren();
+	while( _unlinked.Size()) {
+		DeleteNode(_unlinked[0]);	// Will remove from _unlinked as part of delete.
+	}
+
+#ifdef DEBUG
+    const bool hadError = Error();
+#endif
+    ClearError();
+
+    delete [] _charBuffer;
+    _charBuffer = 0;
+
+#if 0
+    _textPool.Trace( "text" );
+    _elementPool.Trace( "element" );
+    _commentPool.Trace( "comment" );
+    _attributePool.Trace( "attribute" );
+#endif
+    
+#ifdef DEBUG
+    if ( !hadError ) {
+        TIXMLASSERT( _elementPool.CurrentAllocs()   == _elementPool.Untracked() );
+        TIXMLASSERT( _attributePool.CurrentAllocs() == _attributePool.Untracked() );
+        TIXMLASSERT( _textPool.CurrentAllocs()      == _textPool.Untracked() );
+        TIXMLASSERT( _commentPool.CurrentAllocs()   == _commentPool.Untracked() );
+    }
+#endif
+}
+
+
+void XMLDocument::DeepCopy(XMLDocument* target)
+{
+	TIXMLASSERT(target);
+    if (target == this) {
+        return; // technically success - a no-op.
+    }
+
+	target->Clear();
+	for (const XMLNode* node = this->FirstChild(); node; node = node->NextSibling()) {
+		target->InsertEndChild(node->DeepClone(target));
+	}
+}
+
+XMLElement* XMLDocument::NewElement( const char* name )
+{
+    XMLElement* ele = CreateUnlinkedNode<XMLElement>( _elementPool );
+    ele->SetName( name );
+    return ele;
+}
+
+
+XMLComment* XMLDocument::NewComment( const char* str )
+{
+    XMLComment* comment = CreateUnlinkedNode<XMLComment>( _commentPool );
+    comment->SetValue( str );
+    return comment;
+}
+
+
+XMLText* XMLDocument::NewText( const char* str )
+{
+    XMLText* text = CreateUnlinkedNode<XMLText>( _textPool );
+    text->SetValue( str );
+    return text;
+}
+
+
+XMLDeclaration* XMLDocument::NewDeclaration( const char* str )
+{
+    XMLDeclaration* dec = CreateUnlinkedNode<XMLDeclaration>( _commentPool );
+    dec->SetValue( str ? str : "xml version=\"1.0\" encoding=\"UTF-8\"" );
+    return dec;
+}
+
+
+XMLUnknown* XMLDocument::NewUnknown( const char* str )
+{
+    XMLUnknown* unk = CreateUnlinkedNode<XMLUnknown>( _commentPool );
+    unk->SetValue( str );
+    return unk;
+}
+
+static FILE* callfopen( const char* filepath, const char* mode )
+{
+    TIXMLASSERT( filepath );
+    TIXMLASSERT( mode );
+#if defined(_MSC_VER) && (_MSC_VER >= 1400 ) && (!defined WINCE)
+    FILE* fp = 0;
+    errno_t err = fopen_s( &fp, filepath, mode );
+    if ( err ) {
+        return 0;
+    }
+#else
+    FILE* fp = fopen( filepath, mode );
+#endif
+    return fp;
+}
+    
+void XMLDocument::DeleteNode( XMLNode* node )	{
+    TIXMLASSERT( node );
+    TIXMLASSERT(node->_document == this );
+    if (node->_parent) {
+        node->_parent->DeleteChild( node );
+    }
+    else {
+        // Isn't in the tree.
+        // Use the parent delete.
+        // Also, we need to mark it tracked: we 'know'
+        // it was never used.
+        node->_memPool->SetTracked();
+        // Call the static XMLNode version:
+        XMLNode::DeleteNode(node);
+    }
+}
+
+
+XMLError XMLDocument::LoadFile( const char* filename )
+{
+    Clear();
+    FILE* fp = callfopen( filename, "rb" );
+    if ( !fp ) {
+        SetError( XML_ERROR_FILE_NOT_FOUND, filename, 0, 0 );
+        return _errorID;
+    }
+    LoadFile( fp );
+    fclose( fp );
+    return _errorID;
+}
+
+// This is likely overengineered template art to have a check that unsigned long value incremented
+// by one still fits into size_t. If size_t type is larger than unsigned long type
+// (x86_64-w64-mingw32 target) then the check is redundant and gcc and clang emit
+// -Wtype-limits warning. This piece makes the compiler select code with a check when a check
+// is useful and code with no check when a check is redundant depending on how size_t and unsigned long
+// types sizes relate to each other.
+template
+<bool = (sizeof(unsigned long) >= sizeof(size_t))>
+struct LongFitsIntoSizeTMinusOne {
+    static bool Fits( unsigned long value )
+    {
+        return value < (size_t)-1;
+    }
+};
+
+template <>
+struct LongFitsIntoSizeTMinusOne<false> {
+    static bool Fits( unsigned long )
+    {
+        return true;
+    }
+};
+
+XMLError XMLDocument::LoadFile( FILE* fp )
+{
+    Clear();
+
+    fseek( fp, 0, SEEK_SET );
+    if ( fgetc( fp ) == EOF && ferror( fp ) != 0 ) {
+        SetError( XML_ERROR_FILE_READ_ERROR, 0, 0, 0 );
+        return _errorID;
+    }
+
+    fseek( fp, 0, SEEK_END );
+    const long filelength = ftell( fp );
+    fseek( fp, 0, SEEK_SET );
+    if ( filelength == -1L ) {
+        SetError( XML_ERROR_FILE_READ_ERROR, 0, 0, 0 );
+        return _errorID;
+    }
+    TIXMLASSERT( filelength >= 0 );
+
+    if ( !LongFitsIntoSizeTMinusOne<>::Fits( filelength ) ) {
+        // Cannot handle files which won't fit in buffer together with null terminator
+        SetError( XML_ERROR_FILE_READ_ERROR, 0, 0, 0 );
+        return _errorID;
+    }
+
+    if ( filelength == 0 ) {
+        SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0, 0 );
+        return _errorID;
+    }
+
+    const size_t size = filelength;
+    TIXMLASSERT( _charBuffer == 0 );
+    _charBuffer = new char[size+1];
+    size_t read = fread( _charBuffer, 1, size, fp );
+    if ( read != size ) {
+        SetError( XML_ERROR_FILE_READ_ERROR, 0, 0, 0 );
+        return _errorID;
+    }
+
+    _charBuffer[size] = 0;
+
+    Parse();
+    return _errorID;
+}
+
+
+XMLError XMLDocument::SaveFile( const char* filename, bool compact )
+{
+    FILE* fp = callfopen( filename, "w" );
+    if ( !fp ) {
+        SetError( XML_ERROR_FILE_COULD_NOT_BE_OPENED, filename, 0, 0 );
+        return _errorID;
+    }
+    SaveFile(fp, compact);
+    fclose( fp );
+    return _errorID;
+}
+
+
+XMLError XMLDocument::SaveFile( FILE* fp, bool compact )
+{
+    // Clear any error from the last save, otherwise it will get reported
+    // for *this* call.
+    ClearError();
+    XMLPrinter stream( fp, compact );
+    Print( &stream );
+    return _errorID;
+}
+
+
+XMLError XMLDocument::Parse( const char* p, size_t len )
+{
+    Clear();
+
+    if ( len == 0 || !p || !*p ) {
+        SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0, 0 );
+        return _errorID;
+    }
+    if ( len == (size_t)(-1) ) {
+        len = strlen( p );
+    }
+    TIXMLASSERT( _charBuffer == 0 );
+    _charBuffer = new char[ len+1 ];
+    memcpy( _charBuffer, p, len );
+    _charBuffer[len] = 0;
+
+    Parse();
+    if ( Error() ) {
+        // clean up now essentially dangling memory.
+        // and the parse fail can put objects in the
+        // pools that are dead and inaccessible.
+        DeleteChildren();
+        _elementPool.Clear();
+        _attributePool.Clear();
+        _textPool.Clear();
+        _commentPool.Clear();
+    }
+    return _errorID;
+}
+
+
+void XMLDocument::Print( XMLPrinter* streamer ) const
+{
+    if ( streamer ) {
+        Accept( streamer );
+    }
+    else {
+        XMLPrinter stdoutStreamer( stdout );
+        Accept( &stdoutStreamer );
+    }
+}
+
+
+void XMLDocument::SetError( XMLError error, const char* str1, const char* str2, int lineNum )
+{
+    TIXMLASSERT( error >= 0 && error < XML_ERROR_COUNT );
+    _errorID = error;
+	
+	_errorStr1.Reset();
+	_errorStr2.Reset();
+    _errorLineNum = lineNum;
+
+	if (str1)
+		_errorStr1.SetStr(str1);
+	if (str2)
+		_errorStr2.SetStr(str2);
+}
+
+/*static*/ const char* XMLDocument::ErrorIDToName(XMLError errorID)
+{
+	TIXMLASSERT( errorID >= 0 && errorID < XML_ERROR_COUNT );
+    const char* errorName = _errorNames[errorID];
+    TIXMLASSERT( errorName && errorName[0] );
+    return errorName;
+}
+
+const char* XMLDocument::GetErrorStr1() const 
+{
+	return _errorStr1.GetStr();
+}
+
+const char* XMLDocument::GetErrorStr2() const 
+{
+	return _errorStr2.GetStr();
+}
+
+const char* XMLDocument::ErrorName() const
+{
+    return ErrorIDToName(_errorID);
+}
+
+void XMLDocument::PrintError() const
+{
+    if ( Error() ) {
+        static const int LEN = 20;
+        char buf1[LEN] = { 0 };
+        char buf2[LEN] = { 0 };
+
+        if ( !_errorStr1.Empty() ) {
+            TIXML_SNPRINTF( buf1, LEN, "%s", _errorStr1.GetStr() );
+        }
+        if ( !_errorStr2.Empty() ) {
+            TIXML_SNPRINTF( buf2, LEN, "%s", _errorStr2.GetStr() );
+        }
+
+        // Should check INT_MIN <= _errorID && _errorId <= INT_MAX, but that
+        // causes a clang "always true" -Wtautological-constant-out-of-range-compare warning
+        TIXMLASSERT( 0 <= _errorID && XML_ERROR_COUNT - 1 <= INT_MAX );
+        printf( "XMLDocument error id=%d '%s' str1=%s str2=%s line=%d\n",
+                static_cast<int>( _errorID ), ErrorName(), buf1, buf2, _errorLineNum );
+    }
+}
+
+void XMLDocument::Parse()
+{
+    TIXMLASSERT( NoChildren() ); // Clear() must have been called previously
+    TIXMLASSERT( _charBuffer );
+    _parseCurLineNum = 1;
+    _parseLineNum = 1;
+    char* p = _charBuffer;
+    p = XMLUtil::SkipWhiteSpace( p, &_parseCurLineNum );
+    p = const_cast<char*>( XMLUtil::ReadBOM( p, &_writeBOM ) );
+    if ( !*p ) {
+        SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0, 0 );
+        return;
+    }
+    ParseDeep(p, 0, &_parseCurLineNum );
+}
+
+XMLPrinter::XMLPrinter( FILE* file, bool compact, int depth ) :
+    _elementJustOpened( false ),
+    _firstElement( true ),
+    _fp( file ),
+    _depth( depth ),
+    _textDepth( -1 ),
+    _processEntities( true ),
+    _compactMode( compact )
+{
+    for( int i=0; i<ENTITY_RANGE; ++i ) {
+        _entityFlag[i] = false;
+        _restrictedEntityFlag[i] = false;
+    }
+    for( int i=0; i<NUM_ENTITIES; ++i ) {
+        const char entityValue = entities[i].value;
+        TIXMLASSERT( ((unsigned char)entityValue) < ENTITY_RANGE );
+        _entityFlag[ (unsigned char)entityValue ] = true;
+    }
+    _restrictedEntityFlag[(unsigned char)'&'] = true;
+    _restrictedEntityFlag[(unsigned char)'<'] = true;
+    _restrictedEntityFlag[(unsigned char)'>'] = true;	// not required, but consistency is nice
+    _buffer.Push( 0 );
+}
+
+
+void XMLPrinter::Print( const char* format, ... )
+{
+    va_list     va;
+    va_start( va, format );
+
+    if ( _fp ) {
+        vfprintf( _fp, format, va );
+    }
+    else {
+        const int len = TIXML_VSCPRINTF( format, va );
+        // Close out and re-start the va-args
+        va_end( va );
+        TIXMLASSERT( len >= 0 );
+        va_start( va, format );
+        TIXMLASSERT( _buffer.Size() > 0 && _buffer[_buffer.Size() - 1] == 0 );
+        char* p = _buffer.PushArr( len ) - 1;	// back up over the null terminator.
+		TIXML_VSNPRINTF( p, len+1, format, va );
+    }
+    va_end( va );
+}
+
+
+void XMLPrinter::PrintSpace( int depth )
+{
+    for( int i=0; i<depth; ++i ) {
+        Print( "    " );
+    }
+}
+
+
+void XMLPrinter::PrintString( const char* p, bool restricted )
+{
+    // Look for runs of bytes between entities to print.
+    const char* q = p;
+
+    if ( _processEntities ) {
+        const bool* flag = restricted ? _restrictedEntityFlag : _entityFlag;
+        while ( *q ) {
+            TIXMLASSERT( p <= q );
+            // Remember, char is sometimes signed. (How many times has that bitten me?)
+            if ( *q > 0 && *q < ENTITY_RANGE ) {
+                // Check for entities. If one is found, flush
+                // the stream up until the entity, write the
+                // entity, and keep looking.
+                if ( flag[(unsigned char)(*q)] ) {
+                    while ( p < q ) {
+                        const size_t delta = q - p;
+                        // %.*s accepts type int as "precision"
+                        const int toPrint = ( INT_MAX < delta ) ? INT_MAX : (int)delta;
+                        Print( "%.*s", toPrint, p );
+                        p += toPrint;
+                    }
+                    bool entityPatternPrinted = false;
+                    for( int i=0; i<NUM_ENTITIES; ++i ) {
+                        if ( entities[i].value == *q ) {
+                            Print( "&%s;", entities[i].pattern );
+                            entityPatternPrinted = true;
+                            break;
+                        }
+                    }
+                    if ( !entityPatternPrinted ) {
+                        // TIXMLASSERT( entityPatternPrinted ) causes gcc -Wunused-but-set-variable in release
+                        TIXMLASSERT( false );
+                    }
+                    ++p;
+                }
+            }
+            ++q;
+            TIXMLASSERT( p <= q );
+        }
+    }
+    // Flush the remaining string. This will be the entire
+    // string if an entity wasn't found.
+    TIXMLASSERT( p <= q );
+    if ( !_processEntities || ( p < q ) ) {
+        Print( "%s", p );
+    }
+}
+
+
+void XMLPrinter::PushHeader( bool writeBOM, bool writeDec )
+{
+    if ( writeBOM ) {
+        static const unsigned char bom[] = { TIXML_UTF_LEAD_0, TIXML_UTF_LEAD_1, TIXML_UTF_LEAD_2, 0 };
+        Print( "%s", bom );
+    }
+    if ( writeDec ) {
+        PushDeclaration( "xml version=\"1.0\"" );
+    }
+}
+
+
+void XMLPrinter::OpenElement( const char* name, bool compactMode )
+{
+    SealElementIfJustOpened();
+    _stack.Push( name );
+
+    if ( _textDepth < 0 && !_firstElement && !compactMode ) {
+        Print( "\n" );
+    }
+    if ( !compactMode ) {
+        PrintSpace( _depth );
+    }
+
+    Print( "<%s", name );
+    _elementJustOpened = true;
+    _firstElement = false;
+    ++_depth;
+}
+
+
+void XMLPrinter::PushAttribute( const char* name, const char* value )
+{
+    TIXMLASSERT( _elementJustOpened );
+    Print( " %s=\"", name );
+    PrintString( value, false );
+    Print( "\"" );
+}
+
+
+void XMLPrinter::PushAttribute( const char* name, int v )
+{
+    char buf[BUF_SIZE];
+    XMLUtil::ToStr( v, buf, BUF_SIZE );
+    PushAttribute( name, buf );
+}
+
+
+void XMLPrinter::PushAttribute( const char* name, unsigned v )
+{
+    char buf[BUF_SIZE];
+    XMLUtil::ToStr( v, buf, BUF_SIZE );
+    PushAttribute( name, buf );
+}
+
+
+void XMLPrinter::PushAttribute(const char* name, int64_t v)
+{
+	char buf[BUF_SIZE];
+	XMLUtil::ToStr(v, buf, BUF_SIZE);
+	PushAttribute(name, buf);
+}
+
+
+void XMLPrinter::PushAttribute( const char* name, bool v )
+{
+    char buf[BUF_SIZE];
+    XMLUtil::ToStr( v, buf, BUF_SIZE );
+    PushAttribute( name, buf );
+}
+
+
+void XMLPrinter::PushAttribute( const char* name, double v )
+{
+    char buf[BUF_SIZE];
+    XMLUtil::ToStr( v, buf, BUF_SIZE );
+    PushAttribute( name, buf );
+}
+
+
+void XMLPrinter::CloseElement( bool compactMode )
+{
+    --_depth;
+    const char* name = _stack.Pop();
+
+    if ( _elementJustOpened ) {
+        Print( "/>" );
+    }
+    else {
+        if ( _textDepth < 0 && !compactMode) {
+            Print( "\n" );
+            PrintSpace( _depth );
+        }
+        Print( "</%s>", name );
+    }
+
+    if ( _textDepth == _depth ) {
+        _textDepth = -1;
+    }
+    if ( _depth == 0 && !compactMode) {
+        Print( "\n" );
+    }
+    _elementJustOpened = false;
+}
+
+
+void XMLPrinter::SealElementIfJustOpened()
+{
+    if ( !_elementJustOpened ) {
+        return;
+    }
+    _elementJustOpened = false;
+    Print( ">" );
+}
+
+
+void XMLPrinter::PushText( const char* text, bool cdata )
+{
+    _textDepth = _depth-1;
+
+    SealElementIfJustOpened();
+    if ( cdata ) {
+        Print( "<![CDATA[%s]]>", text );
+    }
+    else {
+        PrintString( text, true );
+    }
+}
+
+void XMLPrinter::PushText( int64_t value )
+{
+    char buf[BUF_SIZE];
+    XMLUtil::ToStr( value, buf, BUF_SIZE );
+    PushText( buf, false );
+}
+
+void XMLPrinter::PushText( int value )
+{
+    char buf[BUF_SIZE];
+    XMLUtil::ToStr( value, buf, BUF_SIZE );
+    PushText( buf, false );
+}
+
+
+void XMLPrinter::PushText( unsigned value )
+{
+    char buf[BUF_SIZE];
+    XMLUtil::ToStr( value, buf, BUF_SIZE );
+    PushText( buf, false );
+}
+
+
+void XMLPrinter::PushText( bool value )
+{
+    char buf[BUF_SIZE];
+    XMLUtil::ToStr( value, buf, BUF_SIZE );
+    PushText( buf, false );
+}
+
+
+void XMLPrinter::PushText( float value )
+{
+    char buf[BUF_SIZE];
+    XMLUtil::ToStr( value, buf, BUF_SIZE );
+    PushText( buf, false );
+}
+
+
+void XMLPrinter::PushText( double value )
+{
+    char buf[BUF_SIZE];
+    XMLUtil::ToStr( value, buf, BUF_SIZE );
+    PushText( buf, false );
+}
+
+
+void XMLPrinter::PushComment( const char* comment )
+{
+    SealElementIfJustOpened();
+    if ( _textDepth < 0 && !_firstElement && !_compactMode) {
+        Print( "\n" );
+        PrintSpace( _depth );
+    }
+    _firstElement = false;
+    Print( "<!--%s-->", comment );
+}
+
+
+void XMLPrinter::PushDeclaration( const char* value )
+{
+    SealElementIfJustOpened();
+    if ( _textDepth < 0 && !_firstElement && !_compactMode) {
+        Print( "\n" );
+        PrintSpace( _depth );
+    }
+    _firstElement = false;
+    Print( "<?%s?>", value );
+}
+
+
+void XMLPrinter::PushUnknown( const char* value )
+{
+    SealElementIfJustOpened();
+    if ( _textDepth < 0 && !_firstElement && !_compactMode) {
+        Print( "\n" );
+        PrintSpace( _depth );
+    }
+    _firstElement = false;
+    Print( "<!%s>", value );
+}
+
+
+bool XMLPrinter::VisitEnter( const XMLDocument& doc )
+{
+    _processEntities = doc.ProcessEntities();
+    if ( doc.HasBOM() ) {
+        PushHeader( true, false );
+    }
+    return true;
+}
+
+
+bool XMLPrinter::VisitEnter( const XMLElement& element, const XMLAttribute* attribute )
+{
+    const XMLElement* parentElem = 0;
+    if ( element.Parent() ) {
+        parentElem = element.Parent()->ToElement();
+    }
+    const bool compactMode = parentElem ? CompactMode( *parentElem ) : _compactMode;
+    OpenElement( element.Name(), compactMode );
+    while ( attribute ) {
+        PushAttribute( attribute->Name(), attribute->Value() );
+        attribute = attribute->Next();
+    }
+    return true;
+}
+
+
+bool XMLPrinter::VisitExit( const XMLElement& element )
+{
+    CloseElement( CompactMode(element) );
+    return true;
+}
+
+
+bool XMLPrinter::Visit( const XMLText& text )
+{
+    PushText( text.Value(), text.CData() );
+    return true;
+}
+
+
+bool XMLPrinter::Visit( const XMLComment& comment )
+{
+    PushComment( comment.Value() );
+    return true;
+}
+
+bool XMLPrinter::Visit( const XMLDeclaration& declaration )
+{
+    PushDeclaration( declaration.Value() );
+    return true;
+}
+
+
+bool XMLPrinter::Visit( const XMLUnknown& unknown )
+{
+    PushUnknown( unknown.Value() );
+    return true;
+}
+
+}   // namespace tinyxml2
+

+ 2264 - 0
annotationGUI/tinyxml2.h

@@ -0,0 +1,2264 @@
+/*
+Original code by Lee Thomason (www.grinninglizard.com)
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any
+damages arising from the use of this software.
+
+Permission is granted to anyone to use this software for any
+purpose, including commercial applications, and to alter it and
+redistribute it freely, subject to the following restrictions:
+
+1. The origin of this software must not be misrepresented; you must
+not claim that you wrote the original software. If you use this
+software in a product, an acknowledgment in the product documentation
+would be appreciated but is not required.
+
+2. Altered source versions must be plainly marked as such, and
+must not be misrepresented as being the original software.
+
+3. This notice may not be removed or altered from any source
+distribution.
+*/
+
+#ifndef TINYXML2_INCLUDED
+#define TINYXML2_INCLUDED
+
+#if defined(ANDROID_NDK) || defined(__BORLANDC__) || defined(__QNXNTO__)
+#   include <ctype.h>
+#   include <limits.h>
+#   include <stdio.h>
+#   include <stdlib.h>
+#   include <string.h>
+#	if defined(__PS3__)
+#		include <stddef.h>
+#	endif
+#else
+#   include <cctype>
+#   include <climits>
+#   include <cstdio>
+#   include <cstdlib>
+#   include <cstring>
+#endif
+#include <stdint.h>
+
+/*
+   TODO: intern strings instead of allocation.
+*/
+/*
+	gcc:
+        g++ -Wall -DDEBUG tinyxml2.cpp xmltest.cpp -o gccxmltest.exe
+
+    Formatting, Artistic Style:
+        AStyle.exe --style=1tbs --indent-switches --break-closing-brackets --indent-preprocessor tinyxml2.cpp tinyxml2.h
+*/
+
+#if defined( _DEBUG ) || defined (__DEBUG__)
+#   ifndef DEBUG
+#       define DEBUG
+#   endif
+#endif
+
+#ifdef _MSC_VER
+#   pragma warning(push)
+#   pragma warning(disable: 4251)
+#endif
+
+#ifdef _WIN32
+#   ifdef TINYXML2_EXPORT
+#       define TINYXML2_LIB __declspec(dllexport)
+#   elif defined(TINYXML2_IMPORT)
+#       define TINYXML2_LIB __declspec(dllimport)
+#   else
+#       define TINYXML2_LIB
+#   endif
+#elif __GNUC__ >= 4
+#   define TINYXML2_LIB __attribute__((visibility("default")))
+#else
+#   define TINYXML2_LIB
+#endif
+
+
+#if defined(DEBUG)
+#   if defined(_MSC_VER)
+#       // "(void)0," is for suppressing C4127 warning in "assert(false)", "assert(true)" and the like
+#       define TIXMLASSERT( x )           if ( !((void)0,(x))) { __debugbreak(); }
+#   elif defined (ANDROID_NDK)
+#       include <android/log.h>
+#       define TIXMLASSERT( x )           if ( !(x)) { __android_log_assert( "assert", "grinliz", "ASSERT in '%s' at %d.", __FILE__, __LINE__ ); }
+#   else
+#       include <assert.h>
+#       define TIXMLASSERT                assert
+#   endif
+#else
+#   define TIXMLASSERT( x )               {}
+#endif
+
+
+/* Versioning, past 1.0.14:
+	http://semver.org/
+*/
+static const int TIXML2_MAJOR_VERSION = 5;
+static const int TIXML2_MINOR_VERSION = 0;
+static const int TIXML2_PATCH_VERSION = 1;
+
+namespace tinyxml2
+{
+class XMLDocument;
+class XMLElement;
+class XMLAttribute;
+class XMLComment;
+class XMLText;
+class XMLDeclaration;
+class XMLUnknown;
+class XMLPrinter;
+
+/*
+	A class that wraps strings. Normally stores the start and end
+	pointers into the XML file itself, and will apply normalization
+	and entity translation if actually read. Can also store (and memory
+	manage) a traditional char[]
+*/
+class StrPair
+{
+public:
+    enum {
+        NEEDS_ENTITY_PROCESSING			= 0x01,
+        NEEDS_NEWLINE_NORMALIZATION		= 0x02,
+        NEEDS_WHITESPACE_COLLAPSING     = 0x04,
+
+        TEXT_ELEMENT		            = NEEDS_ENTITY_PROCESSING | NEEDS_NEWLINE_NORMALIZATION,
+        TEXT_ELEMENT_LEAVE_ENTITIES		= NEEDS_NEWLINE_NORMALIZATION,
+        ATTRIBUTE_NAME		            = 0,
+        ATTRIBUTE_VALUE		            = NEEDS_ENTITY_PROCESSING | NEEDS_NEWLINE_NORMALIZATION,
+        ATTRIBUTE_VALUE_LEAVE_ENTITIES  = NEEDS_NEWLINE_NORMALIZATION,
+        COMMENT							= NEEDS_NEWLINE_NORMALIZATION
+    };
+
+    StrPair() : _flags( 0 ), _start( 0 ), _end( 0 ) {}
+    ~StrPair();
+
+    void Set( char* start, char* end, int flags ) {
+        TIXMLASSERT( start );
+        TIXMLASSERT( end );
+        Reset();
+        _start  = start;
+        _end    = end;
+        _flags  = flags | NEEDS_FLUSH;
+    }
+
+    const char* GetStr();
+
+    bool Empty() const {
+        return _start == _end;
+    }
+
+    void SetInternedStr( const char* str ) {
+        Reset();
+        _start = const_cast<char*>(str);
+    }
+
+    void SetStr( const char* str, int flags=0 );
+
+    char* ParseText( char* in, const char* endTag, int strFlags, int* curLineNumPtr );
+    char* ParseName( char* in );
+
+    void TransferTo( StrPair* other );
+	void Reset();
+
+private:
+    void CollapseWhitespace();
+
+    enum {
+        NEEDS_FLUSH = 0x100,
+        NEEDS_DELETE = 0x200
+    };
+
+    int     _flags;
+    char*   _start;
+    char*   _end;
+
+    StrPair( const StrPair& other );	// not supported
+    void operator=( StrPair& other );	// not supported, use TransferTo()
+};
+
+
+/*
+	A dynamic array of Plain Old Data. Doesn't support constructors, etc.
+	Has a small initial memory pool, so that low or no usage will not
+	cause a call to new/delete
+*/
+template <class T, int INITIAL_SIZE>
+class DynArray
+{
+public:
+    DynArray() {
+        _mem = _pool;
+        _allocated = INITIAL_SIZE;
+        _size = 0;
+    }
+
+    ~DynArray() {
+        if ( _mem != _pool ) {
+            delete [] _mem;
+        }
+    }
+
+    void Clear() {
+        _size = 0;
+    }
+
+    void Push( T t ) {
+        TIXMLASSERT( _size < INT_MAX );
+        EnsureCapacity( _size+1 );
+        _mem[_size] = t;
+        ++_size;
+    }
+
+    T* PushArr( int count ) {
+        TIXMLASSERT( count >= 0 );
+        TIXMLASSERT( _size <= INT_MAX - count );
+        EnsureCapacity( _size+count );
+        T* ret = &_mem[_size];
+        _size += count;
+        return ret;
+    }
+
+    T Pop() {
+        TIXMLASSERT( _size > 0 );
+        --_size;
+        return _mem[_size];
+    }
+
+    void PopArr( int count ) {
+        TIXMLASSERT( _size >= count );
+        _size -= count;
+    }
+
+    bool Empty() const					{
+        return _size == 0;
+    }
+
+    T& operator[](int i)				{
+        TIXMLASSERT( i>= 0 && i < _size );
+        return _mem[i];
+    }
+
+    const T& operator[](int i) const	{
+        TIXMLASSERT( i>= 0 && i < _size );
+        return _mem[i];
+    }
+
+    const T& PeekTop() const            {
+        TIXMLASSERT( _size > 0 );
+        return _mem[ _size - 1];
+    }
+
+    int Size() const					{
+        TIXMLASSERT( _size >= 0 );
+        return _size;
+    }
+
+    int Capacity() const				{
+        TIXMLASSERT( _allocated >= INITIAL_SIZE );
+        return _allocated;
+    }
+
+	void SwapRemove(int i) {
+		TIXMLASSERT(i >= 0 && i < _size);
+		TIXMLASSERT(_size > 0);
+		_mem[i] = _mem[_size - 1];
+		--_size;
+	}
+
+    const T* Mem() const				{
+        TIXMLASSERT( _mem );
+        return _mem;
+    }
+
+    T* Mem()							{
+        TIXMLASSERT( _mem );
+        return _mem;
+    }
+
+private:
+    DynArray( const DynArray& ); // not supported
+    void operator=( const DynArray& ); // not supported
+
+    void EnsureCapacity( int cap ) {
+        TIXMLASSERT( cap > 0 );
+        if ( cap > _allocated ) {
+            TIXMLASSERT( cap <= INT_MAX / 2 );
+            int newAllocated = cap * 2;
+            T* newMem = new T[newAllocated];
+            TIXMLASSERT( newAllocated >= _size );
+            memcpy( newMem, _mem, sizeof(T)*_size );	// warning: not using constructors, only works for PODs
+            if ( _mem != _pool ) {
+                delete [] _mem;
+            }
+            _mem = newMem;
+            _allocated = newAllocated;
+        }
+    }
+
+    T*  _mem;
+    T   _pool[INITIAL_SIZE];
+    int _allocated;		// objects allocated
+    int _size;			// number objects in use
+};
+
+
+/*
+	Parent virtual class of a pool for fast allocation
+	and deallocation of objects.
+*/
+class MemPool
+{
+public:
+    MemPool() {}
+    virtual ~MemPool() {}
+
+    virtual int ItemSize() const = 0;
+    virtual void* Alloc() = 0;
+    virtual void Free( void* ) = 0;
+    virtual void SetTracked() = 0;
+    virtual void Clear() = 0;
+};
+
+
+/*
+	Template child class to create pools of the correct type.
+*/
+template< int ITEM_SIZE >
+class MemPoolT : public MemPool
+{
+public:
+    MemPoolT() : _root(0), _currentAllocs(0), _nAllocs(0), _maxAllocs(0), _nUntracked(0)	{}
+    ~MemPoolT() {
+        Clear();
+    }
+    
+    void Clear() {
+        // Delete the blocks.
+        while( !_blockPtrs.Empty()) {
+            Block* b  = _blockPtrs.Pop();
+            delete b;
+        }
+        _root = 0;
+        _currentAllocs = 0;
+        _nAllocs = 0;
+        _maxAllocs = 0;
+        _nUntracked = 0;
+    }
+
+    virtual int ItemSize() const	{
+        return ITEM_SIZE;
+    }
+    int CurrentAllocs() const		{
+        return _currentAllocs;
+    }
+
+    virtual void* Alloc() {
+        if ( !_root ) {
+            // Need a new block.
+            Block* block = new Block();
+            _blockPtrs.Push( block );
+
+            Item* blockItems = block->items;
+            for( int i = 0; i < ITEMS_PER_BLOCK - 1; ++i ) {
+                blockItems[i].next = &(blockItems[i + 1]);
+            }
+            blockItems[ITEMS_PER_BLOCK - 1].next = 0;
+            _root = blockItems;
+        }
+        Item* const result = _root;
+        TIXMLASSERT( result != 0 );
+        _root = _root->next;
+
+        ++_currentAllocs;
+        if ( _currentAllocs > _maxAllocs ) {
+            _maxAllocs = _currentAllocs;
+        }
+        ++_nAllocs;
+        ++_nUntracked;
+        return result;
+    }
+    
+    virtual void Free( void* mem ) {
+        if ( !mem ) {
+            return;
+        }
+        --_currentAllocs;
+        Item* item = static_cast<Item*>( mem );
+#ifdef DEBUG
+        memset( item, 0xfe, sizeof( *item ) );
+#endif
+        item->next = _root;
+        _root = item;
+    }
+    void Trace( const char* name ) {
+        printf( "Mempool %s watermark=%d [%dk] current=%d size=%d nAlloc=%d blocks=%d\n",
+                name, _maxAllocs, _maxAllocs * ITEM_SIZE / 1024, _currentAllocs,
+                ITEM_SIZE, _nAllocs, _blockPtrs.Size() );
+    }
+
+    void SetTracked() {
+        --_nUntracked;
+    }
+
+    int Untracked() const {
+        return _nUntracked;
+    }
+
+	// This number is perf sensitive. 4k seems like a good tradeoff on my machine.
+	// The test file is large, 170k.
+	// Release:		VS2010 gcc(no opt)
+	//		1k:		4000
+	//		2k:		4000
+	//		4k:		3900	21000
+	//		16k:	5200
+	//		32k:	4300
+	//		64k:	4000	21000
+    // Declared public because some compilers do not accept to use ITEMS_PER_BLOCK
+    // in private part if ITEMS_PER_BLOCK is private
+    enum { ITEMS_PER_BLOCK = (4 * 1024) / ITEM_SIZE };
+
+private:
+    MemPoolT( const MemPoolT& ); // not supported
+    void operator=( const MemPoolT& ); // not supported
+
+    union Item {
+        Item*   next;
+        char    itemData[ITEM_SIZE];
+    };
+    struct Block {
+        Item items[ITEMS_PER_BLOCK];
+    };
+    DynArray< Block*, 10 > _blockPtrs;
+    Item* _root;
+
+    int _currentAllocs;
+    int _nAllocs;
+    int _maxAllocs;
+    int _nUntracked;
+};
+
+
+
+/**
+	Implements the interface to the "Visitor pattern" (see the Accept() method.)
+	If you call the Accept() method, it requires being passed a XMLVisitor
+	class to handle callbacks. For nodes that contain other nodes (Document, Element)
+	you will get called with a VisitEnter/VisitExit pair. Nodes that are always leafs
+	are simply called with Visit().
+
+	If you return 'true' from a Visit method, recursive parsing will continue. If you return
+	false, <b>no children of this node or its siblings</b> will be visited.
+
+	All flavors of Visit methods have a default implementation that returns 'true' (continue
+	visiting). You need to only override methods that are interesting to you.
+
+	Generally Accept() is called on the XMLDocument, although all nodes support visiting.
+
+	You should never change the document from a callback.
+
+	@sa XMLNode::Accept()
+*/
+class TINYXML2_LIB XMLVisitor
+{
+public:
+    virtual ~XMLVisitor() {}
+
+    /// Visit a document.
+    virtual bool VisitEnter( const XMLDocument& /*doc*/ )			{
+        return true;
+    }
+    /// Visit a document.
+    virtual bool VisitExit( const XMLDocument& /*doc*/ )			{
+        return true;
+    }
+
+    /// Visit an element.
+    virtual bool VisitEnter( const XMLElement& /*element*/, const XMLAttribute* /*firstAttribute*/ )	{
+        return true;
+    }
+    /// Visit an element.
+    virtual bool VisitExit( const XMLElement& /*element*/ )			{
+        return true;
+    }
+
+    /// Visit a declaration.
+    virtual bool Visit( const XMLDeclaration& /*declaration*/ )		{
+        return true;
+    }
+    /// Visit a text node.
+    virtual bool Visit( const XMLText& /*text*/ )					{
+        return true;
+    }
+    /// Visit a comment node.
+    virtual bool Visit( const XMLComment& /*comment*/ )				{
+        return true;
+    }
+    /// Visit an unknown node.
+    virtual bool Visit( const XMLUnknown& /*unknown*/ )				{
+        return true;
+    }
+};
+
+// WARNING: must match XMLDocument::_errorNames[]
+enum XMLError {
+    XML_SUCCESS = 0,
+    XML_NO_ATTRIBUTE,
+    XML_WRONG_ATTRIBUTE_TYPE,
+    XML_ERROR_FILE_NOT_FOUND,
+    XML_ERROR_FILE_COULD_NOT_BE_OPENED,
+    XML_ERROR_FILE_READ_ERROR,
+    UNUSED_XML_ERROR_ELEMENT_MISMATCH,	// remove at next major version
+    XML_ERROR_PARSING_ELEMENT,
+    XML_ERROR_PARSING_ATTRIBUTE,
+    UNUSED_XML_ERROR_IDENTIFYING_TAG,	// remove at next major version
+    XML_ERROR_PARSING_TEXT,
+    XML_ERROR_PARSING_CDATA,
+    XML_ERROR_PARSING_COMMENT,
+    XML_ERROR_PARSING_DECLARATION,
+    XML_ERROR_PARSING_UNKNOWN,
+    XML_ERROR_EMPTY_DOCUMENT,
+    XML_ERROR_MISMATCHED_ELEMENT,
+    XML_ERROR_PARSING,
+    XML_CAN_NOT_CONVERT_TEXT,
+    XML_NO_TEXT_NODE,
+
+	XML_ERROR_COUNT
+};
+
+
+/*
+	Utility functionality.
+*/
+class TINYXML2_LIB XMLUtil
+{
+public:
+    static const char* SkipWhiteSpace( const char* p, int* curLineNumPtr )	{
+        TIXMLASSERT( p );
+
+        while( IsWhiteSpace(*p) ) {
+            if (curLineNumPtr && *p == '\n') {
+                ++(*curLineNumPtr);
+            }
+            ++p;
+        }
+        TIXMLASSERT( p );
+        return p;
+    }
+    static char* SkipWhiteSpace( char* p, int* curLineNumPtr )				{
+        return const_cast<char*>( SkipWhiteSpace( const_cast<const char*>(p), curLineNumPtr ) );
+    }
+
+    // Anything in the high order range of UTF-8 is assumed to not be whitespace. This isn't
+    // correct, but simple, and usually works.
+    static bool IsWhiteSpace( char p )					{
+        return !IsUTF8Continuation(p) && isspace( static_cast<unsigned char>(p) );
+    }
+    
+    inline static bool IsNameStartChar( unsigned char ch ) {
+        if ( ch >= 128 ) {
+            // This is a heuristic guess in attempt to not implement Unicode-aware isalpha()
+            return true;
+        }
+        if ( isalpha( ch ) ) {
+            return true;
+        }
+        return ch == ':' || ch == '_';
+    }
+    
+    inline static bool IsNameChar( unsigned char ch ) {
+        return IsNameStartChar( ch )
+               || isdigit( ch )
+               || ch == '.'
+               || ch == '-';
+    }
+
+    inline static bool StringEqual( const char* p, const char* q, int nChar=INT_MAX )  {
+        if ( p == q ) {
+            return true;
+        }
+        TIXMLASSERT( p );
+        TIXMLASSERT( q );
+        TIXMLASSERT( nChar >= 0 );
+        return strncmp( p, q, nChar ) == 0;
+    }
+    
+    inline static bool IsUTF8Continuation( char p ) {
+        return ( p & 0x80 ) != 0;
+    }
+
+    static const char* ReadBOM( const char* p, bool* hasBOM );
+    // p is the starting location,
+    // the UTF-8 value of the entity will be placed in value, and length filled in.
+    static const char* GetCharacterRef( const char* p, char* value, int* length );
+    static void ConvertUTF32ToUTF8( unsigned long input, char* output, int* length );
+
+    // converts primitive types to strings
+    static void ToStr( int v, char* buffer, int bufferSize );
+    static void ToStr( unsigned v, char* buffer, int bufferSize );
+    static void ToStr( bool v, char* buffer, int bufferSize );
+    static void ToStr( float v, char* buffer, int bufferSize );
+    static void ToStr( double v, char* buffer, int bufferSize );
+	static void ToStr(int64_t v, char* buffer, int bufferSize);
+
+    // converts strings to primitive types
+    static bool	ToInt( const char* str, int* value );
+    static bool ToUnsigned( const char* str, unsigned* value );
+    static bool	ToBool( const char* str, bool* value );
+    static bool	ToFloat( const char* str, float* value );
+    static bool ToDouble( const char* str, double* value );
+	static bool ToInt64(const char* str, int64_t* value);
+
+	// Changes what is serialized for a boolean value.
+	// Default to "true" and "false". Shouldn't be changed
+	// unless you have a special testing or compatibility need.
+	// Be careful: static, global, & not thread safe.
+	// Be sure to set static const memory as parameters.
+	static void SetBoolSerialization(const char* writeTrue, const char* writeFalse);
+
+private:
+	static const char* writeBoolTrue;
+	static const char* writeBoolFalse;
+};
+
+
+/** XMLNode is a base class for every object that is in the
+	XML Document Object Model (DOM), except XMLAttributes.
+	Nodes have siblings, a parent, and children which can
+	be navigated. A node is always in a XMLDocument.
+	The type of a XMLNode can be queried, and it can
+	be cast to its more defined type.
+
+	A XMLDocument allocates memory for all its Nodes.
+	When the XMLDocument gets deleted, all its Nodes
+	will also be deleted.
+
+	@verbatim
+	A Document can contain:	Element	(container or leaf)
+							Comment (leaf)
+							Unknown (leaf)
+							Declaration( leaf )
+
+	An Element can contain:	Element (container or leaf)
+							Text	(leaf)
+							Attributes (not on tree)
+							Comment (leaf)
+							Unknown (leaf)
+
+	@endverbatim
+*/
+class TINYXML2_LIB XMLNode
+{
+    friend class XMLDocument;
+    friend class XMLElement;
+public:
+
+    /// Get the XMLDocument that owns this XMLNode.
+    const XMLDocument* GetDocument() const	{
+        TIXMLASSERT( _document );
+        return _document;
+    }
+    /// Get the XMLDocument that owns this XMLNode.
+    XMLDocument* GetDocument()				{
+        TIXMLASSERT( _document );
+        return _document;
+    }
+
+    /// Safely cast to an Element, or null.
+    virtual XMLElement*		ToElement()		{
+        return 0;
+    }
+    /// Safely cast to Text, or null.
+    virtual XMLText*		ToText()		{
+        return 0;
+    }
+    /// Safely cast to a Comment, or null.
+    virtual XMLComment*		ToComment()		{
+        return 0;
+    }
+    /// Safely cast to a Document, or null.
+    virtual XMLDocument*	ToDocument()	{
+        return 0;
+    }
+    /// Safely cast to a Declaration, or null.
+    virtual XMLDeclaration*	ToDeclaration()	{
+        return 0;
+    }
+    /// Safely cast to an Unknown, or null.
+    virtual XMLUnknown*		ToUnknown()		{
+        return 0;
+    }
+
+    virtual const XMLElement*		ToElement() const		{
+        return 0;
+    }
+    virtual const XMLText*			ToText() const			{
+        return 0;
+    }
+    virtual const XMLComment*		ToComment() const		{
+        return 0;
+    }
+    virtual const XMLDocument*		ToDocument() const		{
+        return 0;
+    }
+    virtual const XMLDeclaration*	ToDeclaration() const	{
+        return 0;
+    }
+    virtual const XMLUnknown*		ToUnknown() const		{
+        return 0;
+    }
+
+    /** The meaning of 'value' changes for the specific type.
+    	@verbatim
+    	Document:	empty (NULL is returned, not an empty string)
+    	Element:	name of the element
+    	Comment:	the comment text
+    	Unknown:	the tag contents
+    	Text:		the text string
+    	@endverbatim
+    */
+    const char* Value() const;
+
+    /** Set the Value of an XML node.
+    	@sa Value()
+    */
+    void SetValue( const char* val, bool staticMem=false );
+
+    /// Gets the line number the node is in, if the document was parsed from a file.
+    int GetLineNum() const { return _parseLineNum; }
+
+    /// Get the parent of this node on the DOM.
+    const XMLNode*	Parent() const			{
+        return _parent;
+    }
+
+    XMLNode* Parent()						{
+        return _parent;
+    }
+
+    /// Returns true if this node has no children.
+    bool NoChildren() const					{
+        return !_firstChild;
+    }
+
+    /// Get the first child node, or null if none exists.
+    const XMLNode*  FirstChild() const		{
+        return _firstChild;
+    }
+
+    XMLNode*		FirstChild()			{
+        return _firstChild;
+    }
+
+    /** Get the first child element, or optionally the first child
+        element with the specified name.
+    */
+    const XMLElement* FirstChildElement( const char* name = 0 ) const;
+
+    XMLElement* FirstChildElement( const char* name = 0 )	{
+        return const_cast<XMLElement*>(const_cast<const XMLNode*>(this)->FirstChildElement( name ));
+    }
+
+    /// Get the last child node, or null if none exists.
+    const XMLNode*	LastChild() const						{
+        return _lastChild;
+    }
+
+    XMLNode*		LastChild()								{
+        return _lastChild;
+    }
+
+    /** Get the last child element or optionally the last child
+        element with the specified name.
+    */
+    const XMLElement* LastChildElement( const char* name = 0 ) const;
+
+    XMLElement* LastChildElement( const char* name = 0 )	{
+        return const_cast<XMLElement*>(const_cast<const XMLNode*>(this)->LastChildElement(name) );
+    }
+
+    /// Get the previous (left) sibling node of this node.
+    const XMLNode*	PreviousSibling() const					{
+        return _prev;
+    }
+
+    XMLNode*	PreviousSibling()							{
+        return _prev;
+    }
+
+    /// Get the previous (left) sibling element of this node, with an optionally supplied name.
+    const XMLElement*	PreviousSiblingElement( const char* name = 0 ) const ;
+
+    XMLElement*	PreviousSiblingElement( const char* name = 0 ) {
+        return const_cast<XMLElement*>(const_cast<const XMLNode*>(this)->PreviousSiblingElement( name ) );
+    }
+
+    /// Get the next (right) sibling node of this node.
+    const XMLNode*	NextSibling() const						{
+        return _next;
+    }
+
+    XMLNode*	NextSibling()								{
+        return _next;
+    }
+
+    /// Get the next (right) sibling element of this node, with an optionally supplied name.
+    const XMLElement*	NextSiblingElement( const char* name = 0 ) const;
+
+    XMLElement*	NextSiblingElement( const char* name = 0 )	{
+        return const_cast<XMLElement*>(const_cast<const XMLNode*>(this)->NextSiblingElement( name ) );
+    }
+
+    /**
+    	Add a child node as the last (right) child.
+		If the child node is already part of the document,
+		it is moved from its old location to the new location.
+		Returns the addThis argument or 0 if the node does not
+		belong to the same document.
+    */
+    XMLNode* InsertEndChild( XMLNode* addThis );
+
+    XMLNode* LinkEndChild( XMLNode* addThis )	{
+        return InsertEndChild( addThis );
+    }
+    /**
+    	Add a child node as the first (left) child.
+		If the child node is already part of the document,
+		it is moved from its old location to the new location.
+		Returns the addThis argument or 0 if the node does not
+		belong to the same document.
+    */
+    XMLNode* InsertFirstChild( XMLNode* addThis );
+    /**
+    	Add a node after the specified child node.
+		If the child node is already part of the document,
+		it is moved from its old location to the new location.
+		Returns the addThis argument or 0 if the afterThis node
+		is not a child of this node, or if the node does not
+		belong to the same document.
+    */
+    XMLNode* InsertAfterChild( XMLNode* afterThis, XMLNode* addThis );
+
+    /**
+    	Delete all the children of this node.
+    */
+    void DeleteChildren();
+
+    /**
+    	Delete a child of this node.
+    */
+    void DeleteChild( XMLNode* node );
+
+    /**
+    	Make a copy of this node, but not its children.
+    	You may pass in a Document pointer that will be
+    	the owner of the new Node. If the 'document' is
+    	null, then the node returned will be allocated
+    	from the current Document. (this->GetDocument())
+
+    	Note: if called on a XMLDocument, this will return null.
+    */
+    virtual XMLNode* ShallowClone( XMLDocument* document ) const = 0;
+
+	/**
+		Make a copy of this node and all its children.
+
+		If the 'target' is null, then the nodes will
+		be allocated in the current document. If 'target' 
+        is specified, the memory will be allocated is the 
+        specified XMLDocument.
+
+		NOTE: This is probably not the correct tool to 
+		copy a document, since XMLDocuments can have multiple
+		top level XMLNodes. You probably want to use
+        XMLDocument::DeepCopy()
+	*/
+	XMLNode* DeepClone( XMLDocument* target ) const;
+
+    /**
+    	Test if 2 nodes are the same, but don't test children.
+    	The 2 nodes do not need to be in the same Document.
+
+    	Note: if called on a XMLDocument, this will return false.
+    */
+    virtual bool ShallowEqual( const XMLNode* compare ) const = 0;
+
+    /** Accept a hierarchical visit of the nodes in the TinyXML-2 DOM. Every node in the
+    	XML tree will be conditionally visited and the host will be called back
+    	via the XMLVisitor interface.
+
+    	This is essentially a SAX interface for TinyXML-2. (Note however it doesn't re-parse
+    	the XML for the callbacks, so the performance of TinyXML-2 is unchanged by using this
+    	interface versus any other.)
+
+    	The interface has been based on ideas from:
+
+    	- http://www.saxproject.org/
+    	- http://c2.com/cgi/wiki?HierarchicalVisitorPattern
+
+    	Which are both good references for "visiting".
+
+    	An example of using Accept():
+    	@verbatim
+    	XMLPrinter printer;
+    	tinyxmlDoc.Accept( &printer );
+    	const char* xmlcstr = printer.CStr();
+    	@endverbatim
+    */
+    virtual bool Accept( XMLVisitor* visitor ) const = 0;
+
+	/** 
+		Set user data into the XMLNode. TinyXML-2 in 
+		no way processes or interprets user data.
+		It is initially 0.
+	*/
+	void SetUserData(void* userData)	{ _userData = userData; }
+
+	/**
+		Get user data set into the XMLNode. TinyXML-2 in
+		no way processes or interprets user data.
+		It is initially 0.
+	*/
+	void* GetUserData() const			{ return _userData; }
+
+protected:
+    XMLNode( XMLDocument* );
+    virtual ~XMLNode();
+
+    virtual char* ParseDeep( char* p, StrPair* parentEndTag, int* curLineNumPtr);
+
+    XMLDocument*	_document;
+    XMLNode*		_parent;
+    mutable StrPair	_value;
+    int             _parseLineNum;
+
+    XMLNode*		_firstChild;
+    XMLNode*		_lastChild;
+
+    XMLNode*		_prev;
+    XMLNode*		_next;
+
+	void*			_userData;
+
+private:
+    MemPool*		_memPool;
+    void Unlink( XMLNode* child );
+    static void DeleteNode( XMLNode* node );
+    void InsertChildPreamble( XMLNode* insertThis ) const;
+    const XMLElement* ToElementWithName( const char* name ) const;
+
+    XMLNode( const XMLNode& );	// not supported
+    XMLNode& operator=( const XMLNode& );	// not supported
+};
+
+
+/** XML text.
+
+	Note that a text node can have child element nodes, for example:
+	@verbatim
+	<root>This is <b>bold</b></root>
+	@endverbatim
+
+	A text node can have 2 ways to output the next. "normal" output
+	and CDATA. It will default to the mode it was parsed from the XML file and
+	you generally want to leave it alone, but you can change the output mode with
+	SetCData() and query it with CData().
+*/
+class TINYXML2_LIB XMLText : public XMLNode
+{
+    friend class XMLDocument;
+public:
+    virtual bool Accept( XMLVisitor* visitor ) const;
+
+    virtual XMLText* ToText()			{
+        return this;
+    }
+    virtual const XMLText* ToText() const	{
+        return this;
+    }
+
+    /// Declare whether this should be CDATA or standard text.
+    void SetCData( bool isCData )			{
+        _isCData = isCData;
+    }
+    /// Returns true if this is a CDATA text element.
+    bool CData() const						{
+        return _isCData;
+    }
+
+    virtual XMLNode* ShallowClone( XMLDocument* document ) const;
+    virtual bool ShallowEqual( const XMLNode* compare ) const;
+
+protected:
+    XMLText( XMLDocument* doc )	: XMLNode( doc ), _isCData( false )	{}
+    virtual ~XMLText()												{}
+
+    char* ParseDeep( char* p, StrPair* parentEndTag, int* curLineNumPtr );
+
+private:
+    bool _isCData;
+
+    XMLText( const XMLText& );	// not supported
+    XMLText& operator=( const XMLText& );	// not supported
+};
+
+
+/** An XML Comment. */
+class TINYXML2_LIB XMLComment : public XMLNode
+{
+    friend class XMLDocument;
+public:
+    virtual XMLComment*	ToComment()					{
+        return this;
+    }
+    virtual const XMLComment* ToComment() const		{
+        return this;
+    }
+
+    virtual bool Accept( XMLVisitor* visitor ) const;
+
+    virtual XMLNode* ShallowClone( XMLDocument* document ) const;
+    virtual bool ShallowEqual( const XMLNode* compare ) const;
+
+protected:
+    XMLComment( XMLDocument* doc );
+    virtual ~XMLComment();
+
+    char* ParseDeep( char* p, StrPair* parentEndTag, int* curLineNumPtr);
+
+private:
+    XMLComment( const XMLComment& );	// not supported
+    XMLComment& operator=( const XMLComment& );	// not supported
+};
+
+
+/** In correct XML the declaration is the first entry in the file.
+	@verbatim
+		<?xml version="1.0" standalone="yes"?>
+	@endverbatim
+
+	TinyXML-2 will happily read or write files without a declaration,
+	however.
+
+	The text of the declaration isn't interpreted. It is parsed
+	and written as a string.
+*/
+class TINYXML2_LIB XMLDeclaration : public XMLNode
+{
+    friend class XMLDocument;
+public:
+    virtual XMLDeclaration*	ToDeclaration()					{
+        return this;
+    }
+    virtual const XMLDeclaration* ToDeclaration() const		{
+        return this;
+    }
+
+    virtual bool Accept( XMLVisitor* visitor ) const;
+
+    virtual XMLNode* ShallowClone( XMLDocument* document ) const;
+    virtual bool ShallowEqual( const XMLNode* compare ) const;
+
+protected:
+    XMLDeclaration( XMLDocument* doc );
+    virtual ~XMLDeclaration();
+
+    char* ParseDeep( char* p, StrPair* parentEndTag, int* curLineNumPtr );
+
+private:
+    XMLDeclaration( const XMLDeclaration& );	// not supported
+    XMLDeclaration& operator=( const XMLDeclaration& );	// not supported
+};
+
+
+/** Any tag that TinyXML-2 doesn't recognize is saved as an
+	unknown. It is a tag of text, but should not be modified.
+	It will be written back to the XML, unchanged, when the file
+	is saved.
+
+	DTD tags get thrown into XMLUnknowns.
+*/
+class TINYXML2_LIB XMLUnknown : public XMLNode
+{
+    friend class XMLDocument;
+public:
+    virtual XMLUnknown*	ToUnknown()					{
+        return this;
+    }
+    virtual const XMLUnknown* ToUnknown() const		{
+        return this;
+    }
+
+    virtual bool Accept( XMLVisitor* visitor ) const;
+
+    virtual XMLNode* ShallowClone( XMLDocument* document ) const;
+    virtual bool ShallowEqual( const XMLNode* compare ) const;
+
+protected:
+    XMLUnknown( XMLDocument* doc );
+    virtual ~XMLUnknown();
+
+    char* ParseDeep( char* p, StrPair* parentEndTag, int* curLineNumPtr );
+
+private:
+    XMLUnknown( const XMLUnknown& );	// not supported
+    XMLUnknown& operator=( const XMLUnknown& );	// not supported
+};
+
+
+
+/** An attribute is a name-value pair. Elements have an arbitrary
+	number of attributes, each with a unique name.
+
+	@note The attributes are not XMLNodes. You may only query the
+	Next() attribute in a list.
+*/
+class TINYXML2_LIB XMLAttribute
+{
+    friend class XMLElement;
+public:
+    /// The name of the attribute.
+    const char* Name() const;
+
+    /// The value of the attribute.
+    const char* Value() const;
+
+    /// Gets the line number the attribute is in, if the document was parsed from a file.
+    int GetLineNum() const { return _parseLineNum; }
+
+    /// The next attribute in the list.
+    const XMLAttribute* Next() const {
+        return _next;
+    }
+
+    /** IntValue interprets the attribute as an integer, and returns the value.
+        If the value isn't an integer, 0 will be returned. There is no error checking;
+    	use QueryIntValue() if you need error checking.
+    */
+	int	IntValue() const {
+		int i = 0;
+		QueryIntValue(&i);
+		return i;
+	}
+
+	int64_t Int64Value() const {
+		int64_t i = 0;
+		QueryInt64Value(&i);
+		return i;
+	}
+
+    /// Query as an unsigned integer. See IntValue()
+    unsigned UnsignedValue() const			{
+        unsigned i=0;
+        QueryUnsignedValue( &i );
+        return i;
+    }
+    /// Query as a boolean. See IntValue()
+    bool	 BoolValue() const				{
+        bool b=false;
+        QueryBoolValue( &b );
+        return b;
+    }
+    /// Query as a double. See IntValue()
+    double 	 DoubleValue() const			{
+        double d=0;
+        QueryDoubleValue( &d );
+        return d;
+    }
+    /// Query as a float. See IntValue()
+    float	 FloatValue() const				{
+        float f=0;
+        QueryFloatValue( &f );
+        return f;
+    }
+
+    /** QueryIntValue interprets the attribute as an integer, and returns the value
+    	in the provided parameter. The function will return XML_SUCCESS on success,
+    	and XML_WRONG_ATTRIBUTE_TYPE if the conversion is not successful.
+    */
+    XMLError QueryIntValue( int* value ) const;
+    /// See QueryIntValue
+    XMLError QueryUnsignedValue( unsigned int* value ) const;
+	/// See QueryIntValue
+	XMLError QueryInt64Value(int64_t* value) const;
+	/// See QueryIntValue
+    XMLError QueryBoolValue( bool* value ) const;
+    /// See QueryIntValue
+    XMLError QueryDoubleValue( double* value ) const;
+    /// See QueryIntValue
+    XMLError QueryFloatValue( float* value ) const;
+
+    /// Set the attribute to a string value.
+    void SetAttribute( const char* value );
+    /// Set the attribute to value.
+    void SetAttribute( int value );
+    /// Set the attribute to value.
+    void SetAttribute( unsigned value );
+	/// Set the attribute to value.
+	void SetAttribute(int64_t value);
+	/// Set the attribute to value.
+    void SetAttribute( bool value );
+    /// Set the attribute to value.
+    void SetAttribute( double value );
+    /// Set the attribute to value.
+    void SetAttribute( float value );
+
+private:
+    enum { BUF_SIZE = 200 };
+
+    XMLAttribute() : _parseLineNum( 0 ), _next( 0 ), _memPool( 0 ) {}
+    virtual ~XMLAttribute()	{}
+
+    XMLAttribute( const XMLAttribute& );	// not supported
+    void operator=( const XMLAttribute& );	// not supported
+    void SetName( const char* name );
+
+    char* ParseDeep( char* p, bool processEntities, int* curLineNumPtr );
+
+    mutable StrPair _name;
+    mutable StrPair _value;
+    int             _parseLineNum;
+    XMLAttribute*   _next;
+    MemPool*        _memPool;
+};
+
+
+/** The element is a container class. It has a value, the element name,
+	and can contain other elements, text, comments, and unknowns.
+	Elements also contain an arbitrary number of attributes.
+*/
+class TINYXML2_LIB XMLElement : public XMLNode
+{
+    friend class XMLDocument;
+public:
+    /// Get the name of an element (which is the Value() of the node.)
+    const char* Name() const		{
+        return Value();
+    }
+    /// Set the name of the element.
+    void SetName( const char* str, bool staticMem=false )	{
+        SetValue( str, staticMem );
+    }
+
+    virtual XMLElement* ToElement()				{
+        return this;
+    }
+    virtual const XMLElement* ToElement() const {
+        return this;
+    }
+    virtual bool Accept( XMLVisitor* visitor ) const;
+
+    /** Given an attribute name, Attribute() returns the value
+    	for the attribute of that name, or null if none
+    	exists. For example:
+
+    	@verbatim
+    	const char* value = ele->Attribute( "foo" );
+    	@endverbatim
+
+    	The 'value' parameter is normally null. However, if specified,
+    	the attribute will only be returned if the 'name' and 'value'
+    	match. This allow you to write code:
+
+    	@verbatim
+    	if ( ele->Attribute( "foo", "bar" ) ) callFooIsBar();
+    	@endverbatim
+
+    	rather than:
+    	@verbatim
+    	if ( ele->Attribute( "foo" ) ) {
+    		if ( strcmp( ele->Attribute( "foo" ), "bar" ) == 0 ) callFooIsBar();
+    	}
+    	@endverbatim
+    */
+    const char* Attribute( const char* name, const char* value=0 ) const;
+
+    /** Given an attribute name, IntAttribute() returns the value
+    	of the attribute interpreted as an integer. The default
+        value will be returned if the attribute isn't present,
+        or if there is an error. (For a method with error
+    	checking, see QueryIntAttribute()).
+    */
+	int IntAttribute(const char* name, int defaultValue = 0) const;
+    /// See IntAttribute()
+	unsigned UnsignedAttribute(const char* name, unsigned defaultValue = 0) const;
+	/// See IntAttribute()
+	int64_t Int64Attribute(const char* name, int64_t defaultValue = 0) const;
+	/// See IntAttribute()
+	bool BoolAttribute(const char* name, bool defaultValue = false) const;
+    /// See IntAttribute()
+	double DoubleAttribute(const char* name, double defaultValue = 0) const;
+    /// See IntAttribute()
+	float FloatAttribute(const char* name, float defaultValue = 0) const;
+
+    /** Given an attribute name, QueryIntAttribute() returns
+    	XML_SUCCESS, XML_WRONG_ATTRIBUTE_TYPE if the conversion
+    	can't be performed, or XML_NO_ATTRIBUTE if the attribute
+    	doesn't exist. If successful, the result of the conversion
+    	will be written to 'value'. If not successful, nothing will
+    	be written to 'value'. This allows you to provide default
+    	value:
+
+    	@verbatim
+    	int value = 10;
+    	QueryIntAttribute( "foo", &value );		// if "foo" isn't found, value will still be 10
+    	@endverbatim
+    */
+    XMLError QueryIntAttribute( const char* name, int* value ) const				{
+        const XMLAttribute* a = FindAttribute( name );
+        if ( !a ) {
+            return XML_NO_ATTRIBUTE;
+        }
+        return a->QueryIntValue( value );
+    }
+
+	/// See QueryIntAttribute()
+    XMLError QueryUnsignedAttribute( const char* name, unsigned int* value ) const	{
+        const XMLAttribute* a = FindAttribute( name );
+        if ( !a ) {
+            return XML_NO_ATTRIBUTE;
+        }
+        return a->QueryUnsignedValue( value );
+    }
+
+	/// See QueryIntAttribute()
+	XMLError QueryInt64Attribute(const char* name, int64_t* value) const {
+		const XMLAttribute* a = FindAttribute(name);
+		if (!a) {
+			return XML_NO_ATTRIBUTE;
+		}
+		return a->QueryInt64Value(value);
+	}
+
+	/// See QueryIntAttribute()
+    XMLError QueryBoolAttribute( const char* name, bool* value ) const				{
+        const XMLAttribute* a = FindAttribute( name );
+        if ( !a ) {
+            return XML_NO_ATTRIBUTE;
+        }
+        return a->QueryBoolValue( value );
+    }
+    /// See QueryIntAttribute()
+    XMLError QueryDoubleAttribute( const char* name, double* value ) const			{
+        const XMLAttribute* a = FindAttribute( name );
+        if ( !a ) {
+            return XML_NO_ATTRIBUTE;
+        }
+        return a->QueryDoubleValue( value );
+    }
+    /// See QueryIntAttribute()
+    XMLError QueryFloatAttribute( const char* name, float* value ) const			{
+        const XMLAttribute* a = FindAttribute( name );
+        if ( !a ) {
+            return XML_NO_ATTRIBUTE;
+        }
+        return a->QueryFloatValue( value );
+    }
+
+	
+    /** Given an attribute name, QueryAttribute() returns
+    	XML_SUCCESS, XML_WRONG_ATTRIBUTE_TYPE if the conversion
+    	can't be performed, or XML_NO_ATTRIBUTE if the attribute
+    	doesn't exist. It is overloaded for the primitive types,
+		and is a generally more convenient replacement of
+		QueryIntAttribute() and related functions.
+		
+		If successful, the result of the conversion
+    	will be written to 'value'. If not successful, nothing will
+    	be written to 'value'. This allows you to provide default
+    	value:
+
+    	@verbatim
+    	int value = 10;
+    	QueryAttribute( "foo", &value );		// if "foo" isn't found, value will still be 10
+    	@endverbatim
+    */
+	int QueryAttribute( const char* name, int* value ) const {
+		return QueryIntAttribute( name, value );
+	}
+
+	int QueryAttribute( const char* name, unsigned int* value ) const {
+		return QueryUnsignedAttribute( name, value );
+	}
+
+	int QueryAttribute(const char* name, int64_t* value) const {
+		return QueryInt64Attribute(name, value);
+	}
+
+	int QueryAttribute( const char* name, bool* value ) const {
+		return QueryBoolAttribute( name, value );
+	}
+
+	int QueryAttribute( const char* name, double* value ) const {
+		return QueryDoubleAttribute( name, value );
+	}
+
+	int QueryAttribute( const char* name, float* value ) const {
+		return QueryFloatAttribute( name, value );
+	}
+
+	/// Sets the named attribute to value.
+    void SetAttribute( const char* name, const char* value )	{
+        XMLAttribute* a = FindOrCreateAttribute( name );
+        a->SetAttribute( value );
+    }
+    /// Sets the named attribute to value.
+    void SetAttribute( const char* name, int value )			{
+        XMLAttribute* a = FindOrCreateAttribute( name );
+        a->SetAttribute( value );
+    }
+    /// Sets the named attribute to value.
+    void SetAttribute( const char* name, unsigned value )		{
+        XMLAttribute* a = FindOrCreateAttribute( name );
+        a->SetAttribute( value );
+    }
+
+	/// Sets the named attribute to value.
+	void SetAttribute(const char* name, int64_t value) {
+		XMLAttribute* a = FindOrCreateAttribute(name);
+		a->SetAttribute(value);
+	}
+
+	/// Sets the named attribute to value.
+    void SetAttribute( const char* name, bool value )			{
+        XMLAttribute* a = FindOrCreateAttribute( name );
+        a->SetAttribute( value );
+    }
+    /// Sets the named attribute to value.
+    void SetAttribute( const char* name, double value )		{
+        XMLAttribute* a = FindOrCreateAttribute( name );
+        a->SetAttribute( value );
+    }
+    /// Sets the named attribute to value.
+    void SetAttribute( const char* name, float value )		{
+        XMLAttribute* a = FindOrCreateAttribute( name );
+        a->SetAttribute( value );
+    }
+
+    /**
+    	Delete an attribute.
+    */
+    void DeleteAttribute( const char* name );
+
+    /// Return the first attribute in the list.
+    const XMLAttribute* FirstAttribute() const {
+        return _rootAttribute;
+    }
+    /// Query a specific attribute in the list.
+    const XMLAttribute* FindAttribute( const char* name ) const;
+
+    /** Convenience function for easy access to the text inside an element. Although easy
+    	and concise, GetText() is limited compared to getting the XMLText child
+    	and accessing it directly.
+
+    	If the first child of 'this' is a XMLText, the GetText()
+    	returns the character string of the Text node, else null is returned.
+
+    	This is a convenient method for getting the text of simple contained text:
+    	@verbatim
+    	<foo>This is text</foo>
+    		const char* str = fooElement->GetText();
+    	@endverbatim
+
+    	'str' will be a pointer to "This is text".
+
+    	Note that this function can be misleading. If the element foo was created from
+    	this XML:
+    	@verbatim
+    		<foo><b>This is text</b></foo>
+    	@endverbatim
+
+    	then the value of str would be null. The first child node isn't a text node, it is
+    	another element. From this XML:
+    	@verbatim
+    		<foo>This is <b>text</b></foo>
+    	@endverbatim
+    	GetText() will return "This is ".
+    */
+    const char* GetText() const;
+
+    /** Convenience function for easy access to the text inside an element. Although easy
+    	and concise, SetText() is limited compared to creating an XMLText child
+    	and mutating it directly.
+
+    	If the first child of 'this' is a XMLText, SetText() sets its value to
+		the given string, otherwise it will create a first child that is an XMLText.
+
+    	This is a convenient method for setting the text of simple contained text:
+    	@verbatim
+    	<foo>This is text</foo>
+    		fooElement->SetText( "Hullaballoo!" );
+     	<foo>Hullaballoo!</foo>
+		@endverbatim
+
+    	Note that this function can be misleading. If the element foo was created from
+    	this XML:
+    	@verbatim
+    		<foo><b>This is text</b></foo>
+    	@endverbatim
+
+    	then it will not change "This is text", but rather prefix it with a text element:
+    	@verbatim
+    		<foo>Hullaballoo!<b>This is text</b></foo>
+    	@endverbatim
+		
+		For this XML:
+    	@verbatim
+    		<foo />
+    	@endverbatim
+    	SetText() will generate
+    	@verbatim
+    		<foo>Hullaballoo!</foo>
+    	@endverbatim
+    */
+	void SetText( const char* inText );
+    /// Convenience method for setting text inside an element. See SetText() for important limitations.
+    void SetText( int value );
+    /// Convenience method for setting text inside an element. See SetText() for important limitations.
+    void SetText( unsigned value );  
+	/// Convenience method for setting text inside an element. See SetText() for important limitations.
+	void SetText(int64_t value);
+	/// Convenience method for setting text inside an element. See SetText() for important limitations.
+    void SetText( bool value );  
+    /// Convenience method for setting text inside an element. See SetText() for important limitations.
+    void SetText( double value );  
+    /// Convenience method for setting text inside an element. See SetText() for important limitations.
+    void SetText( float value );  
+
+    /**
+    	Convenience method to query the value of a child text node. This is probably best
+    	shown by example. Given you have a document is this form:
+    	@verbatim
+    		<point>
+    			<x>1</x>
+    			<y>1.4</y>
+    		</point>
+    	@endverbatim
+
+    	The QueryIntText() and similar functions provide a safe and easier way to get to the
+    	"value" of x and y.
+
+    	@verbatim
+    		int x = 0;
+    		float y = 0;	// types of x and y are contrived for example
+    		const XMLElement* xElement = pointElement->FirstChildElement( "x" );
+    		const XMLElement* yElement = pointElement->FirstChildElement( "y" );
+    		xElement->QueryIntText( &x );
+    		yElement->QueryFloatText( &y );
+    	@endverbatim
+
+    	@returns XML_SUCCESS (0) on success, XML_CAN_NOT_CONVERT_TEXT if the text cannot be converted
+    			 to the requested type, and XML_NO_TEXT_NODE if there is no child text to query.
+
+    */
+    XMLError QueryIntText( int* ival ) const;
+    /// See QueryIntText()
+    XMLError QueryUnsignedText( unsigned* uval ) const;
+	/// See QueryIntText()
+	XMLError QueryInt64Text(int64_t* uval) const;
+	/// See QueryIntText()
+    XMLError QueryBoolText( bool* bval ) const;
+    /// See QueryIntText()
+    XMLError QueryDoubleText( double* dval ) const;
+    /// See QueryIntText()
+    XMLError QueryFloatText( float* fval ) const;
+
+	int IntText(int defaultValue = 0) const;
+
+	/// See QueryIntText()
+	unsigned UnsignedText(unsigned defaultValue = 0) const;
+	/// See QueryIntText()
+	int64_t Int64Text(int64_t defaultValue = 0) const;
+	/// See QueryIntText()
+	bool BoolText(bool defaultValue = false) const;
+	/// See QueryIntText()
+	double DoubleText(double defaultValue = 0) const;
+	/// See QueryIntText()
+	float FloatText(float defaultValue = 0) const;
+
+    // internal:
+    enum ElementClosingType {
+        OPEN,		// <foo>
+        CLOSED,		// <foo/>
+        CLOSING		// </foo>
+    };
+    ElementClosingType ClosingType() const {
+        return _closingType;
+    }
+    virtual XMLNode* ShallowClone( XMLDocument* document ) const;
+    virtual bool ShallowEqual( const XMLNode* compare ) const;
+
+protected:
+    char* ParseDeep( char* p, StrPair* parentEndTag, int* curLineNumPtr );
+
+private:
+    XMLElement( XMLDocument* doc );
+    virtual ~XMLElement();
+    XMLElement( const XMLElement& );	// not supported
+    void operator=( const XMLElement& );	// not supported
+
+    XMLAttribute* FindAttribute( const char* name ) {
+        return const_cast<XMLAttribute*>(const_cast<const XMLElement*>(this)->FindAttribute( name ));
+    }
+    XMLAttribute* FindOrCreateAttribute( const char* name );
+    //void LinkAttribute( XMLAttribute* attrib );
+    char* ParseAttributes( char* p, int* curLineNumPtr );
+    static void DeleteAttribute( XMLAttribute* attribute );
+    XMLAttribute* CreateAttribute();
+
+    enum { BUF_SIZE = 200 };
+    ElementClosingType _closingType;
+    // The attribute list is ordered; there is no 'lastAttribute'
+    // because the list needs to be scanned for dupes before adding
+    // a new attribute.
+    XMLAttribute* _rootAttribute;
+};
+
+
+enum Whitespace {
+    PRESERVE_WHITESPACE,
+    COLLAPSE_WHITESPACE
+};
+
+
+/** A Document binds together all the functionality.
+	It can be saved, loaded, and printed to the screen.
+	All Nodes are connected and allocated to a Document.
+	If the Document is deleted, all its Nodes are also deleted.
+*/
+class TINYXML2_LIB XMLDocument : public XMLNode
+{
+    friend class XMLElement;
+public:
+    /// constructor
+    XMLDocument( bool processEntities = true, Whitespace whitespaceMode = PRESERVE_WHITESPACE );
+    ~XMLDocument();
+
+    virtual XMLDocument* ToDocument()				{
+        TIXMLASSERT( this == _document );
+        return this;
+    }
+    virtual const XMLDocument* ToDocument() const	{
+        TIXMLASSERT( this == _document );
+        return this;
+    }
+
+    /**
+    	Parse an XML file from a character string.
+    	Returns XML_SUCCESS (0) on success, or
+    	an errorID.
+
+    	You may optionally pass in the 'nBytes', which is
+    	the number of bytes which will be parsed. If not
+    	specified, TinyXML-2 will assume 'xml' points to a
+    	null terminated string.
+    */
+    XMLError Parse( const char* xml, size_t nBytes=(size_t)(-1) );
+
+    /**
+    	Load an XML file from disk.
+    	Returns XML_SUCCESS (0) on success, or
+    	an errorID.
+    */
+    XMLError LoadFile( const char* filename );
+
+    /**
+    	Load an XML file from disk. You are responsible
+    	for providing and closing the FILE*. 
+     
+        NOTE: The file should be opened as binary ("rb")
+        not text in order for TinyXML-2 to correctly
+        do newline normalization.
+
+    	Returns XML_SUCCESS (0) on success, or
+    	an errorID.
+    */
+    XMLError LoadFile( FILE* );
+
+    /**
+    	Save the XML file to disk.
+    	Returns XML_SUCCESS (0) on success, or
+    	an errorID.
+    */
+    XMLError SaveFile( const char* filename, bool compact = false );
+
+    /**
+    	Save the XML file to disk. You are responsible
+    	for providing and closing the FILE*.
+
+    	Returns XML_SUCCESS (0) on success, or
+    	an errorID.
+    */
+    XMLError SaveFile( FILE* fp, bool compact = false );
+
+    bool ProcessEntities() const		{
+        return _processEntities;
+    }
+    Whitespace WhitespaceMode() const	{
+        return _whitespaceMode;
+    }
+
+    /**
+    	Returns true if this document has a leading Byte Order Mark of UTF8.
+    */
+    bool HasBOM() const {
+        return _writeBOM;
+    }
+    /** Sets whether to write the BOM when writing the file.
+    */
+    void SetBOM( bool useBOM ) {
+        _writeBOM = useBOM;
+    }
+
+    /** Return the root element of DOM. Equivalent to FirstChildElement().
+        To get the first node, use FirstChild().
+    */
+    XMLElement* RootElement()				{
+        return FirstChildElement();
+    }
+    const XMLElement* RootElement() const	{
+        return FirstChildElement();
+    }
+
+    /** Print the Document. If the Printer is not provided, it will
+        print to stdout. If you provide Printer, this can print to a file:
+    	@verbatim
+    	XMLPrinter printer( fp );
+    	doc.Print( &printer );
+    	@endverbatim
+
+    	Or you can use a printer to print to memory:
+    	@verbatim
+    	XMLPrinter printer;
+    	doc.Print( &printer );
+    	// printer.CStr() has a const char* to the XML
+    	@endverbatim
+    */
+    void Print( XMLPrinter* streamer=0 ) const;
+    virtual bool Accept( XMLVisitor* visitor ) const;
+
+    /**
+    	Create a new Element associated with
+    	this Document. The memory for the Element
+    	is managed by the Document.
+    */
+    XMLElement* NewElement( const char* name );
+    /**
+    	Create a new Comment associated with
+    	this Document. The memory for the Comment
+    	is managed by the Document.
+    */
+    XMLComment* NewComment( const char* comment );
+    /**
+    	Create a new Text associated with
+    	this Document. The memory for the Text
+    	is managed by the Document.
+    */
+    XMLText* NewText( const char* text );
+    /**
+    	Create a new Declaration associated with
+    	this Document. The memory for the object
+    	is managed by the Document.
+
+    	If the 'text' param is null, the standard
+    	declaration is used.:
+    	@verbatim
+    		<?xml version="1.0" encoding="UTF-8"?>
+    	@endverbatim
+    */
+    XMLDeclaration* NewDeclaration( const char* text=0 );
+    /**
+    	Create a new Unknown associated with
+    	this Document. The memory for the object
+    	is managed by the Document.
+    */
+    XMLUnknown* NewUnknown( const char* text );
+
+    /**
+    	Delete a node associated with this document.
+    	It will be unlinked from the DOM.
+    */
+    void DeleteNode( XMLNode* node );
+
+    void SetError( XMLError error, const char* str1, const char* str2, int lineNum );
+
+    void ClearError() {
+        SetError(XML_SUCCESS, 0, 0, 0);
+    }
+
+    /// Return true if there was an error parsing the document.
+    bool Error() const {
+        return _errorID != XML_SUCCESS;
+    }
+    /// Return the errorID.
+    XMLError  ErrorID() const {
+        return _errorID;
+    }
+	const char* ErrorName() const;
+    static const char* ErrorIDToName(XMLError errorID);
+
+    /// Return a possibly helpful diagnostic location or string.
+	const char* GetErrorStr1() const;
+
+    /// Return a possibly helpful secondary diagnostic location or string.
+	const char* GetErrorStr2() const;
+
+    /// Return the line where the error occured, or zero if unknown.
+    int GetErrorLineNum() const
+    {
+        return _errorLineNum;
+    }
+    /// If there is an error, print it to stdout.
+    void PrintError() const;
+    
+    /// Clear the document, resetting it to the initial state.
+    void Clear();
+
+	/**
+		Copies this document to a target document.
+		The target will be completely cleared before the copy.
+		If you want to copy a sub-tree, see XMLNode::DeepClone().
+
+		NOTE: that the 'target' must be non-null.
+	*/
+	void DeepCopy(XMLDocument* target);
+
+	// internal
+    char* Identify( char* p, XMLNode** node );
+
+	// internal
+	void MarkInUse(XMLNode*);
+
+    virtual XMLNode* ShallowClone( XMLDocument* /*document*/ ) const	{
+        return 0;
+    }
+    virtual bool ShallowEqual( const XMLNode* /*compare*/ ) const	{
+        return false;
+    }
+
+private:
+    XMLDocument( const XMLDocument& );	// not supported
+    void operator=( const XMLDocument& );	// not supported
+
+    bool			_writeBOM;
+    bool			_processEntities;
+    XMLError		_errorID;
+    Whitespace		_whitespaceMode;
+    mutable StrPair	_errorStr1;
+    mutable StrPair	_errorStr2;
+    int             _errorLineNum;
+    char*			_charBuffer;
+    int				_parseCurLineNum;
+	// Memory tracking does add some overhead.
+	// However, the code assumes that you don't
+	// have a bunch of unlinked nodes around.
+	// Therefore it takes less memory to track
+	// in the document vs. a linked list in the XMLNode,
+	// and the performance is the same.
+	DynArray<XMLNode*, 10> _unlinked;
+
+    MemPoolT< sizeof(XMLElement) >	 _elementPool;
+    MemPoolT< sizeof(XMLAttribute) > _attributePool;
+    MemPoolT< sizeof(XMLText) >		 _textPool;
+    MemPoolT< sizeof(XMLComment) >	 _commentPool;
+
+	static const char* _errorNames[XML_ERROR_COUNT];
+
+    void Parse();
+
+    template<class NodeType, int PoolElementSize>
+    NodeType* CreateUnlinkedNode( MemPoolT<PoolElementSize>& pool );
+};
+
+template<class NodeType, int PoolElementSize>
+inline NodeType* XMLDocument::CreateUnlinkedNode( MemPoolT<PoolElementSize>& pool )
+{
+    TIXMLASSERT( sizeof( NodeType ) == PoolElementSize );
+    TIXMLASSERT( sizeof( NodeType ) == pool.ItemSize() );
+    NodeType* returnNode = new (pool.Alloc()) NodeType( this );
+    TIXMLASSERT( returnNode );
+    returnNode->_memPool = &pool;
+
+	_unlinked.Push(returnNode);
+    return returnNode;
+}
+
+/**
+	A XMLHandle is a class that wraps a node pointer with null checks; this is
+	an incredibly useful thing. Note that XMLHandle is not part of the TinyXML-2
+	DOM structure. It is a separate utility class.
+
+	Take an example:
+	@verbatim
+	<Document>
+		<Element attributeA = "valueA">
+			<Child attributeB = "value1" />
+			<Child attributeB = "value2" />
+		</Element>
+	</Document>
+	@endverbatim
+
+	Assuming you want the value of "attributeB" in the 2nd "Child" element, it's very
+	easy to write a *lot* of code that looks like:
+
+	@verbatim
+	XMLElement* root = document.FirstChildElement( "Document" );
+	if ( root )
+	{
+		XMLElement* element = root->FirstChildElement( "Element" );
+		if ( element )
+		{
+			XMLElement* child = element->FirstChildElement( "Child" );
+			if ( child )
+			{
+				XMLElement* child2 = child->NextSiblingElement( "Child" );
+				if ( child2 )
+				{
+					// Finally do something useful.
+	@endverbatim
+
+	And that doesn't even cover "else" cases. XMLHandle addresses the verbosity
+	of such code. A XMLHandle checks for null pointers so it is perfectly safe
+	and correct to use:
+
+	@verbatim
+	XMLHandle docHandle( &document );
+	XMLElement* child2 = docHandle.FirstChildElement( "Document" ).FirstChildElement( "Element" ).FirstChildElement().NextSiblingElement();
+	if ( child2 )
+	{
+		// do something useful
+	@endverbatim
+
+	Which is MUCH more concise and useful.
+
+	It is also safe to copy handles - internally they are nothing more than node pointers.
+	@verbatim
+	XMLHandle handleCopy = handle;
+	@endverbatim
+
+	See also XMLConstHandle, which is the same as XMLHandle, but operates on const objects.
+*/
+class TINYXML2_LIB XMLHandle
+{
+public:
+    /// Create a handle from any node (at any depth of the tree.) This can be a null pointer.
+    XMLHandle( XMLNode* node )												{
+        _node = node;
+    }
+    /// Create a handle from a node.
+    XMLHandle( XMLNode& node )												{
+        _node = &node;
+    }
+    /// Copy constructor
+    XMLHandle( const XMLHandle& ref )										{
+        _node = ref._node;
+    }
+    /// Assignment
+    XMLHandle& operator=( const XMLHandle& ref )							{
+        _node = ref._node;
+        return *this;
+    }
+
+    /// Get the first child of this handle.
+    XMLHandle FirstChild() 													{
+        return XMLHandle( _node ? _node->FirstChild() : 0 );
+    }
+    /// Get the first child element of this handle.
+    XMLHandle FirstChildElement( const char* name = 0 )						{
+        return XMLHandle( _node ? _node->FirstChildElement( name ) : 0 );
+    }
+    /// Get the last child of this handle.
+    XMLHandle LastChild()													{
+        return XMLHandle( _node ? _node->LastChild() : 0 );
+    }
+    /// Get the last child element of this handle.
+    XMLHandle LastChildElement( const char* name = 0 )						{
+        return XMLHandle( _node ? _node->LastChildElement( name ) : 0 );
+    }
+    /// Get the previous sibling of this handle.
+    XMLHandle PreviousSibling()												{
+        return XMLHandle( _node ? _node->PreviousSibling() : 0 );
+    }
+    /// Get the previous sibling element of this handle.
+    XMLHandle PreviousSiblingElement( const char* name = 0 )				{
+        return XMLHandle( _node ? _node->PreviousSiblingElement( name ) : 0 );
+    }
+    /// Get the next sibling of this handle.
+    XMLHandle NextSibling()													{
+        return XMLHandle( _node ? _node->NextSibling() : 0 );
+    }
+    /// Get the next sibling element of this handle.
+    XMLHandle NextSiblingElement( const char* name = 0 )					{
+        return XMLHandle( _node ? _node->NextSiblingElement( name ) : 0 );
+    }
+
+    /// Safe cast to XMLNode. This can return null.
+    XMLNode* ToNode()							{
+        return _node;
+    }
+    /// Safe cast to XMLElement. This can return null.
+    XMLElement* ToElement() 					{
+        return ( _node ? _node->ToElement() : 0 );
+    }
+    /// Safe cast to XMLText. This can return null.
+    XMLText* ToText() 							{
+        return ( _node ? _node->ToText() : 0 );
+    }
+    /// Safe cast to XMLUnknown. This can return null.
+    XMLUnknown* ToUnknown() 					{
+        return ( _node ? _node->ToUnknown() : 0 );
+    }
+    /// Safe cast to XMLDeclaration. This can return null.
+    XMLDeclaration* ToDeclaration() 			{
+        return ( _node ? _node->ToDeclaration() : 0 );
+    }
+
+private:
+    XMLNode* _node;
+};
+
+
+/**
+	A variant of the XMLHandle class for working with const XMLNodes and Documents. It is the
+	same in all regards, except for the 'const' qualifiers. See XMLHandle for API.
+*/
+class TINYXML2_LIB XMLConstHandle
+{
+public:
+    XMLConstHandle( const XMLNode* node )											{
+        _node = node;
+    }
+    XMLConstHandle( const XMLNode& node )											{
+        _node = &node;
+    }
+    XMLConstHandle( const XMLConstHandle& ref )										{
+        _node = ref._node;
+    }
+
+    XMLConstHandle& operator=( const XMLConstHandle& ref )							{
+        _node = ref._node;
+        return *this;
+    }
+
+    const XMLConstHandle FirstChild() const											{
+        return XMLConstHandle( _node ? _node->FirstChild() : 0 );
+    }
+    const XMLConstHandle FirstChildElement( const char* name = 0 ) const				{
+        return XMLConstHandle( _node ? _node->FirstChildElement( name ) : 0 );
+    }
+    const XMLConstHandle LastChild()	const										{
+        return XMLConstHandle( _node ? _node->LastChild() : 0 );
+    }
+    const XMLConstHandle LastChildElement( const char* name = 0 ) const				{
+        return XMLConstHandle( _node ? _node->LastChildElement( name ) : 0 );
+    }
+    const XMLConstHandle PreviousSibling() const									{
+        return XMLConstHandle( _node ? _node->PreviousSibling() : 0 );
+    }
+    const XMLConstHandle PreviousSiblingElement( const char* name = 0 ) const		{
+        return XMLConstHandle( _node ? _node->PreviousSiblingElement( name ) : 0 );
+    }
+    const XMLConstHandle NextSibling() const										{
+        return XMLConstHandle( _node ? _node->NextSibling() : 0 );
+    }
+    const XMLConstHandle NextSiblingElement( const char* name = 0 ) const			{
+        return XMLConstHandle( _node ? _node->NextSiblingElement( name ) : 0 );
+    }
+
+
+    const XMLNode* ToNode() const				{
+        return _node;
+    }
+    const XMLElement* ToElement() const			{
+        return ( _node ? _node->ToElement() : 0 );
+    }
+    const XMLText* ToText() const				{
+        return ( _node ? _node->ToText() : 0 );
+    }
+    const XMLUnknown* ToUnknown() const			{
+        return ( _node ? _node->ToUnknown() : 0 );
+    }
+    const XMLDeclaration* ToDeclaration() const	{
+        return ( _node ? _node->ToDeclaration() : 0 );
+    }
+
+private:
+    const XMLNode* _node;
+};
+
+
+/**
+	Printing functionality. The XMLPrinter gives you more
+	options than the XMLDocument::Print() method.
+
+	It can:
+	-# Print to memory.
+	-# Print to a file you provide.
+	-# Print XML without a XMLDocument.
+
+	Print to Memory
+
+	@verbatim
+	XMLPrinter printer;
+	doc.Print( &printer );
+	SomeFunction( printer.CStr() );
+	@endverbatim
+
+	Print to a File
+
+	You provide the file pointer.
+	@verbatim
+	XMLPrinter printer( fp );
+	doc.Print( &printer );
+	@endverbatim
+
+	Print without a XMLDocument
+
+	When loading, an XML parser is very useful. However, sometimes
+	when saving, it just gets in the way. The code is often set up
+	for streaming, and constructing the DOM is just overhead.
+
+	The Printer supports the streaming case. The following code
+	prints out a trivially simple XML file without ever creating
+	an XML document.
+
+	@verbatim
+	XMLPrinter printer( fp );
+	printer.OpenElement( "foo" );
+	printer.PushAttribute( "foo", "bar" );
+	printer.CloseElement();
+	@endverbatim
+*/
+class TINYXML2_LIB XMLPrinter : public XMLVisitor
+{
+public:
+    /** Construct the printer. If the FILE* is specified,
+    	this will print to the FILE. Else it will print
+    	to memory, and the result is available in CStr().
+    	If 'compact' is set to true, then output is created
+    	with only required whitespace and newlines.
+    */
+    XMLPrinter( FILE* file=0, bool compact = false, int depth = 0 );
+    virtual ~XMLPrinter()	{}
+
+    /** If streaming, write the BOM and declaration. */
+    void PushHeader( bool writeBOM, bool writeDeclaration );
+    /** If streaming, start writing an element.
+        The element must be closed with CloseElement()
+    */
+    void OpenElement( const char* name, bool compactMode=false );
+    /// If streaming, add an attribute to an open element.
+    void PushAttribute( const char* name, const char* value );
+    void PushAttribute( const char* name, int value );
+    void PushAttribute( const char* name, unsigned value );
+	void PushAttribute(const char* name, int64_t value);
+	void PushAttribute( const char* name, bool value );
+    void PushAttribute( const char* name, double value );
+    /// If streaming, close the Element.
+    virtual void CloseElement( bool compactMode=false );
+
+    /// Add a text node.
+    void PushText( const char* text, bool cdata=false );
+    /// Add a text node from an integer.
+    void PushText( int value );
+    /// Add a text node from an unsigned.
+    void PushText( unsigned value );
+	/// Add a text node from an unsigned.
+	void PushText(int64_t value);
+	/// Add a text node from a bool.
+    void PushText( bool value );
+    /// Add a text node from a float.
+    void PushText( float value );
+    /// Add a text node from a double.
+    void PushText( double value );
+
+    /// Add a comment
+    void PushComment( const char* comment );
+
+    void PushDeclaration( const char* value );
+    void PushUnknown( const char* value );
+
+    virtual bool VisitEnter( const XMLDocument& /*doc*/ );
+    virtual bool VisitExit( const XMLDocument& /*doc*/ )			{
+        return true;
+    }
+
+    virtual bool VisitEnter( const XMLElement& element, const XMLAttribute* attribute );
+    virtual bool VisitExit( const XMLElement& element );
+
+    virtual bool Visit( const XMLText& text );
+    virtual bool Visit( const XMLComment& comment );
+    virtual bool Visit( const XMLDeclaration& declaration );
+    virtual bool Visit( const XMLUnknown& unknown );
+
+    /**
+    	If in print to memory mode, return a pointer to
+    	the XML file in memory.
+    */
+    const char* CStr() const {
+        return _buffer.Mem();
+    }
+    /**
+    	If in print to memory mode, return the size
+    	of the XML file in memory. (Note the size returned
+    	includes the terminating null.)
+    */
+    int CStrSize() const {
+        return _buffer.Size();
+    }
+    /**
+    	If in print to memory mode, reset the buffer to the
+    	beginning.
+    */
+    void ClearBuffer() {
+        _buffer.Clear();
+        _buffer.Push(0);
+		_firstElement = true;
+    }
+
+protected:
+	virtual bool CompactMode( const XMLElement& )	{ return _compactMode; }
+
+	/** Prints out the space before an element. You may override to change
+	    the space and tabs used. A PrintSpace() override should call Print().
+	*/
+    virtual void PrintSpace( int depth );
+    void Print( const char* format, ... );
+
+    void SealElementIfJustOpened();
+    bool _elementJustOpened;
+    DynArray< const char*, 10 > _stack;
+
+private:
+    void PrintString( const char*, bool restrictedEntitySet );	// prints out, after detecting entities.
+
+    bool _firstElement;
+    FILE* _fp;
+    int _depth;
+    int _textDepth;
+    bool _processEntities;
+	bool _compactMode;
+
+    enum {
+        ENTITY_RANGE = 64,
+        BUF_SIZE = 200
+    };
+    bool _entityFlag[ENTITY_RANGE];
+    bool _restrictedEntityFlag[ENTITY_RANGE];
+
+    DynArray< char, 20 > _buffer;
+};
+
+
+}	// tinyxml2
+
+#if defined(_MSC_VER)
+#   pragma warning(pop)
+#endif
+
+#endif // TINYXML2_INCLUDED

+ 485 - 0
annotationGUI/uncrustify.cfg

@@ -0,0 +1,485 @@
+indent_align_string=true
+
+indent_braces=false
+
+indent_braces_no_func=false
+
+indent_brace_parent=false
+
+indent_namespace=false
+
+indent_extern=false
+
+indent_class=true
+
+indent_class_colon=true
+
+indent_else_if=false
+
+indent_func_call_param=false
+
+indent_func_def_param=false
+
+indent_func_proto_param=false
+
+indent_func_class_param=false
+
+indent_func_ctor_var_param=false
+
+indent_template_param=false
+
+indent_func_param_double=false
+
+indent_relative_single_line_comments=false
+
+indent_col1_comment=true
+
+indent_access_spec_body=false
+
+indent_paren_nl=false
+
+indent_comma_paren=false
+
+indent_bool_paren=false
+
+indent_square_nl=false
+
+indent_preserve_sql=false
+
+indent_align_assign=true
+
+sp_balance_nested_parens=false
+
+align_keep_tabs=false
+
+align_with_tabs=false
+
+align_on_tabstop=false
+
+align_number_left=true
+
+align_func_params=true
+
+align_same_func_call_params=true
+
+align_var_def_colon=true
+
+align_var_def_attribute=true
+
+align_var_def_inline=true
+
+align_right_cmt_mix=false
+
+align_on_operator=true
+
+align_mix_var_proto=false
+
+align_single_line_func=true
+
+align_single_line_brace=true
+
+align_nl_cont=true
+
+align_left_shift=true
+
+nl_collapse_empty_body=true
+
+nl_assign_leave_one_liners=false
+
+nl_class_leave_one_liners=true
+
+nl_enum_leave_one_liners=false
+
+nl_getset_leave_one_liners=false
+
+nl_func_leave_one_liners=false
+
+nl_if_leave_one_liners=false
+
+nl_multi_line_cond=false
+
+nl_multi_line_define=false
+
+nl_before_case=true
+
+nl_after_case=false
+
+nl_after_return=true
+
+nl_after_semicolon=false
+
+nl_after_brace_open=false
+
+nl_after_brace_open_cmt=false
+
+nl_after_vbrace_open=false
+
+nl_after_brace_close=false
+
+nl_define_macro=true
+
+nl_squeeze_ifdef=false
+
+nl_ds_struct_enum_cmt=true
+
+nl_ds_struct_enum_close_brace=true
+
+nl_create_if_one_liner=true
+
+nl_create_for_one_liner=true
+
+nl_create_while_one_liner=true
+
+ls_for_split_full=false
+
+ls_func_split_full=true
+
+nl_after_multiline_comment=true
+
+eat_blanks_after_open_brace=true
+
+eat_blanks_before_close_brace=true
+
+mod_pawn_semicolon=false
+
+mod_full_paren_if_bool=true
+
+mod_remove_extra_semicolon=true
+
+mod_sort_import=false
+
+mod_sort_using=false
+
+mod_sort_include=false
+
+mod_move_case_break=true
+
+mod_remove_empty_return=true
+
+cmt_indent_multi=true
+
+cmt_c_group=false
+
+cmt_c_nl_start=false
+
+cmt_c_nl_end=false
+
+cmt_cpp_group=false
+
+cmt_cpp_nl_start=false
+
+cmt_cpp_nl_end=false
+
+cmt_cpp_to_c=false
+
+cmt_star_cont=false
+
+cmt_multi_check_last=true
+
+cmt_insert_before_preproc=false
+
+pp_indent_at_level=false
+
+pp_region_indent_code=false
+
+pp_if_indent_code=false
+
+pp_define_at_level=false
+
+indent_columns=4
+
+align_var_def_span=1
+
+align_var_def_star_style=2
+
+align_var_def_amp_style=2
+
+align_var_def_thresh=3
+
+align_var_def_gap=1
+
+align_assign_span=1
+
+align_enum_equ_span=1
+
+align_var_struct_span=1
+
+align_struct_init_span=1
+
+align_typedef_span=1
+
+align_typedef_star_style=2
+
+align_typedef_amp_style=2
+
+align_right_cmt_span=4
+
+align_right_cmt_at_col=1
+
+align_func_proto_span=3
+
+nl_end_of_file_min=1
+
+nl_func_var_def_blk=1
+
+code_width=82
+
+nl_max=3
+
+nl_after_func_proto=0
+
+nl_after_func_body=2
+
+nl_after_func_body_one_liner=2
+
+nl_before_block_comment=2
+
+nl_before_c_comment=2
+
+nl_before_cpp_comment=2
+
+nl_before_access_spec=2
+
+nl_after_access_spec=2
+
+nl_comment_func_def=1
+
+nl_after_try_catch_finally=1
+
+mod_full_brace_nl=1
+
+mod_add_long_ifdef_endif_comment=1
+
+mod_add_long_ifdef_else_comment=1
+
+cmt_width=80
+
+newlines=auto
+
+indent_with_tabs=0
+
+sp_arith=add
+
+sp_assign=add
+
+sp_enum_assign=add
+
+sp_pp_concat=add
+
+sp_pp_stringify=add
+
+sp_bool=add
+
+sp_compare=add
+
+sp_inside_paren=remove
+
+sp_paren_paren=remove
+
+sp_paren_brace=add
+
+sp_before_ptr_star=add
+
+sp_before_unnamed_ptr_star=add
+
+sp_between_ptr_star=remove
+
+sp_after_ptr_star=remove
+
+sp_after_ptr_star_func=add
+
+sp_before_ptr_star_func=remove
+
+sp_before_byref=remove
+
+sp_before_unnamed_byref=remove
+
+sp_after_byref=add
+
+sp_after_byref_func=add
+
+sp_before_byref_func=remove
+
+sp_after_type=add
+
+sp_before_angle=remove
+
+sp_inside_angle=remove
+
+sp_after_angle=remove
+
+sp_angle_paren=remove
+
+sp_angle_word=remove
+
+sp_before_sparen=add
+
+sp_inside_sparen=remove
+
+sp_sparen_brace=add
+
+sp_special_semi=remove
+
+sp_before_semi=remove
+
+sp_before_semi_for=remove
+
+sp_before_semi_for_empty=remove
+
+sp_after_semi_for_empty=remove
+
+sp_before_square=remove
+
+sp_before_squares=remove
+
+sp_inside_square=remove
+
+sp_after_comma=add
+
+sp_before_comma=remove
+
+sp_after_class_colon=add
+
+sp_before_class_colon=add
+
+sp_before_case_colon=remove
+
+sp_after_operator=remove
+
+sp_after_operator_sym=remove
+
+sp_after_cast=remove
+
+sp_inside_paren_cast=remove
+
+sp_cpp_cast_paren=remove
+
+sp_sizeof_paren=remove
+
+sp_inside_braces_enum=add
+
+sp_inside_braces_struct=add
+
+sp_inside_braces=add
+
+sp_inside_braces_empty=remove
+
+sp_type_func=add
+
+sp_func_proto_paren=remove
+
+sp_func_def_paren=remove
+
+sp_inside_fparens=remove
+
+sp_inside_fparen=remove
+
+sp_square_fparen=remove
+
+sp_fparen_brace=add
+
+sp_func_call_paren=remove
+
+sp_func_call_user_paren=remove
+
+sp_func_class_paren=remove
+
+sp_return_paren=add
+
+sp_attribute_paren=remove
+
+sp_defined_paren=remove
+
+sp_throw_paren=remove
+
+sp_macro=add
+
+sp_macro_func=remove
+
+sp_else_brace=add
+
+sp_brace_else=add
+
+sp_brace_typedef=add
+
+sp_catch_brace=add
+
+sp_brace_catch=add
+
+sp_finally_brace=add
+
+sp_brace_finally=add
+
+sp_try_brace=add
+
+sp_getset_brace=add
+
+sp_before_dc=remove
+
+sp_after_dc=remove
+
+sp_not=remove
+
+sp_inv=remove
+
+sp_addr=remove
+
+sp_member=remove
+
+sp_deref=remove
+
+sp_sign=remove
+
+sp_incdec=remove
+
+sp_before_nl_cont=remove
+
+sp_after_oc_scope=remove
+
+sp_after_oc_colon=remove
+
+sp_before_oc_colon=remove
+
+sp_after_send_oc_colon=add
+
+sp_before_send_oc_colon=remove
+
+sp_after_oc_type=remove
+
+sp_cond_colon=add
+
+sp_cond_question=add
+
+sp_cmt_cpp_start=add
+
+nl_end_of_file=add
+
+nl_namespace_brace=remove
+
+#nl_class_brace=remove
+
+#nl_class_init_args=remove
+
+nl_func_decl_args=add
+
+nl_before_if=add
+
+nl_before_for=add
+
+nl_before_while=add
+
+nl_before_switch=add
+
+nl_before_do=add
+
+mod_full_brace_do=remove
+
+mod_full_brace_for=remove
+
+mod_full_brace_if=remove
+
+mod_full_brace_while=remove
+
+mod_paren_on_return=remove
+
+pp_space=add

+ 22 - 0
annotationGUI/usermode.h

@@ -0,0 +1,22 @@
+#ifndef USERMODE_H
+#define USERMODE_H
+
+/*
+ * Definiert die verschiedenen Modi, in denen die Arbeitsfläche der GUI
+ ***unterschiedlich bedient werden kann
+ */
+enum UserMode
+{
+    SELECT,         // Modus zum verstecken / sichtbar machen von Objekten
+    MOVE,           // Modus zum verschieben von Objekt Ecken
+    NEW,            // Modus zum hinzufügen eindes neuen Objektes
+    PIPETTE_SELECT, // Modus zum Kopieren eines Objektes
+    PIPETTE_SET,    // Modus zum Einfügen eines Objektes
+    DELETE,         // Modus zum Löschen von Objekt Ecken
+    CUT,            // Modus zum aufspalten eines Objektes in verschiedene
+                    // Objekte
+    ZOOM_IN,        // Modus zum heranzoomen
+    USERMODE_COUNT  // Anzahl der Modi
+};
+
+#endif // USERMODE_H

+ 339 - 0
annotationGUI/zmq_alt.hpp

@@ -0,0 +1,339 @@
+/*
+    Copyright (c) 2007-2011 iMatix Corporation
+    Copyright (c) 2007-2011 Other contributors as noted in the AUTHORS file
+
+    This file is part of 0MQ.
+
+    0MQ is free software; you can redistribute it and/or modify it under
+    the terms of the GNU Lesser General Public License as published by
+    the Free Software Foundation; either version 3 of the License, or
+    (at your option) any later version.
+
+    0MQ is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef __ZMQ_HPP_INCLUDED__
+#define __ZMQ_HPP_INCLUDED__
+
+#include <zmq.h>
+
+#include <algorithm>
+#include <cassert>
+#include <cstring>
+#include <exception>
+
+// Detect whether the compiler supports C++11 rvalue references.
+#if (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ > 2)) && defined(__GXX_EXPERIMENTAL_CXX0X__))
+#   define ZMQ_HAS_RVALUE_REFS
+#endif
+#if (defined(__clang__))
+#   if __has_feature(cxx_rvalue_references)
+#       define ZMQ_HAS_RVALUE_REFS
+#   endif
+#endif
+#if (defined(_MSC_VER) && (_MSC_VER >= 1600))
+#   define ZMQ_HAS_RVALUE_REFS
+#endif
+
+// In order to prevent unused variable warnings when building in non-debug
+// mode use this macro to make assertions.
+#ifndef NDEBUG
+#   define ZMQ_ASSERT(expression) assert(expression)
+#else
+#   define ZMQ_ASSERT(expression) (expression)
+#endif
+
+namespace zmq
+{
+
+    typedef zmq_free_fn free_fn;
+    typedef zmq_pollitem_t pollitem_t;
+
+    inline int poll (zmq_pollitem_t *items_, int nitems_, long timeout_ = -1)
+    {
+        int rc = zmq_poll (items_, nitems_, timeout_);
+        if (rc < 0)
+            return -1;
+        return rc;
+    }
+
+    inline bool device (int device_, void * insocket_, void* outsocket_)
+    {
+        int rc = zmq_device (device_, insocket_, outsocket_);
+        if (rc != 0)
+            return 0;
+        return 1;
+    }
+
+    inline void version (int *major_, int *minor_, int *patch_)
+    {
+        zmq_version (major_, minor_, patch_);
+    }
+
+    class message_t : private zmq_msg_t
+    {
+        friend class socket_t;
+
+    public:
+
+        inline message_t ()
+        {
+            int rc = zmq_msg_init (this);
+        }
+
+        inline message_t (size_t size_)
+        {
+            int rc = zmq_msg_init_size (this, size_);
+        }
+
+        inline message_t (void *data_, size_t size_, free_fn *ffn_,
+            void *hint_ = NULL)
+        {
+            int rc = zmq_msg_init_data (this, data_, size_, ffn_, hint_);
+        }
+
+        inline ~message_t ()
+        {
+            int rc = zmq_msg_close (this);
+            ZMQ_ASSERT (rc == 0);
+        }
+
+        inline bool rebuild ()
+        {
+            int rc = zmq_msg_close (this);
+            if (rc != 0)
+                return 0;
+            rc = zmq_msg_init (this);
+            if (rc != 0)
+                return 0;
+            return 1;
+        }
+
+        inline bool rebuild (size_t size_)
+        {
+            int rc = zmq_msg_close (this);
+            if (rc != 0)
+                return 0;
+            rc = zmq_msg_init_size (this, size_);
+            if (rc != 0)
+                return 0;
+            return 1;
+        }
+
+        inline bool rebuild (void *data_, size_t size_, free_fn *ffn_,
+            void *hint_ = NULL)
+        {
+            int rc = zmq_msg_close (this);
+            if (rc != 0)
+                return 0;
+            rc = zmq_msg_init_data (this, data_, size_, ffn_, hint_);
+            if (rc != 0)
+                return 0;
+            return 1;
+        }
+
+        inline bool move (message_t *msg_)
+        {
+            int rc = zmq_msg_move (this, (zmq_msg_t*) msg_);
+            if (rc != 0)
+                return 0;
+            return 1;
+        }
+
+        inline bool copy (message_t *msg_)
+        {
+            int rc = zmq_msg_copy (this, (zmq_msg_t*) msg_);
+            if (rc != 0)
+                return 0;
+            return 1;
+        }
+
+        inline void *data ()
+        {
+            return zmq_msg_data (this);
+        }
+
+        inline size_t size ()
+        {
+            return zmq_msg_size (this);
+        }
+
+    private:
+
+        //  Disable implicit message copying, so that users won't use shared
+        //  messages (less efficient) without being aware of the fact.
+        message_t (const message_t&);
+        void operator = (const message_t&);
+    };
+
+    class context_t
+    {
+        friend class socket_t;
+
+    public:
+
+        inline context_t (int io_threads_)
+        {
+            ptr = zmq_init (io_threads_);
+        }
+
+#ifdef ZMQ_HAS_RVALUE_REFS
+        inline context_t(context_t&& rhs) : ptr(rhs.ptr)
+        {
+            rhs.ptr = NULL;
+        }
+        inline context_t& operator=(context_t&& rhs)
+        {
+            std::swap(ptr, rhs.ptr);
+            return *this;
+        }
+#endif
+
+        inline ~context_t ()
+        {
+            if (ptr == NULL)
+                return;
+            int rc = zmq_term (ptr);
+            ZMQ_ASSERT (rc == 0);
+        }
+
+        //  Be careful with this, it's probably only useful for
+        //  using the C api together with an existing C++ api.
+        //  Normally you should never need to use this.
+        inline operator void* ()
+        {
+            return ptr;
+        }
+
+    private:
+
+        void *ptr;
+
+        context_t (const context_t&);
+        void operator = (const context_t&);
+    };
+
+    class socket_t
+    {
+    public:
+
+        inline socket_t (context_t &context_, int type_)
+        {
+            err = 0;
+            ptr = zmq_socket (context_.ptr, type_);
+            if (ptr == NULL)
+                err = 1;
+        }
+
+#ifdef ZMQ_HAS_RVALUE_REFS
+        inline socket_t(socket_t&& rhs) : ptr(rhs.ptr)
+        {
+            err = 0;
+            rhs.ptr = NULL;
+        }
+        inline socket_t& operator=(socket_t&& rhs)
+        {
+            err = 0;
+            std::swap(ptr, rhs.ptr);
+            return *this;
+        }
+#endif
+
+        inline ~socket_t ()
+        {
+            close();
+        }
+
+        inline operator void* ()
+        {
+            return ptr;
+        }
+
+        inline bool isOk()
+        {
+            return !err;
+        }
+
+        inline bool close()
+        {
+            if(ptr == NULL)
+                // already closed
+                return 1;
+            int rc = zmq_close (ptr);
+            if (rc != 0)
+                return 0;
+            ptr = 0 ;
+            return 1;
+        }
+
+        inline bool setsockopt (int option_, const void *optval_,
+            size_t optvallen_)
+        {
+            int rc = zmq_setsockopt (ptr, option_, optval_, optvallen_);
+            if (rc != 0)
+                return 0;
+            return 1;
+        }
+
+        inline bool getsockopt (int option_, void *optval_,
+            size_t *optvallen_)
+        {
+            int rc = zmq_getsockopt (ptr, option_, optval_, optvallen_);
+            if (rc != 0)
+                return 0;
+            return 1;
+        }
+
+        inline bool bind (const char *addr_)
+        {
+            int rc = zmq_bind (ptr, addr_);
+            if (rc != 0)
+                return 0;
+            return 1;
+        }
+
+        inline bool connect (const char *addr_)
+        {
+            int rc = zmq_connect (ptr, addr_);
+            if (rc != 0)
+                return 0;
+            return 1;
+        }
+
+        inline bool send (message_t &msg_, int flags_ = 0)
+        {
+            int rc = zmq_send (ptr, &msg_, flags_);
+            if (rc == 0)
+                return true;
+            if (rc == -1 && zmq_errno () == EAGAIN)
+                return false;
+            return false;
+        }
+
+        inline bool recv (message_t *msg_, int flags_ = 0)
+        {
+            int rc = zmq_recv (ptr, msg_, flags_);
+            if (rc == 0)
+                return true;
+            if (rc == -1 && zmq_errno () == EAGAIN)
+                return false;
+            return false;
+        }
+
+    private:
+        bool err;
+        void *ptr;
+
+        socket_t (const socket_t&);
+        void operator = (const socket_t&);
+    };
+
+}
+
+#endif

+ 17 - 0
schriftlich/Makefile

@@ -0,0 +1,17 @@
+.PHONY: clean
+clean:
+	del *.lof *.lot *.aux *.bbl *.blg *.dvi *.idx *.log *.out *.toc *.backup kapitel\*.aux
+
+.PHONY: cleanall
+cleanall: clean
+	del *.pdf
+
+pdf: schriftlich.tex
+	pdflatex schriftlich --shell-escape
+	bibtex schriftlich
+	pdflatex schriftlich --shell-escape
+	pdflatex schriftlich --shell-escape
+	pdflatex schriftlich --shell-escape
+	pdflatex schriftlich --shell-escape
+	pdflatex schriftlich --shell-escape
+	pdflatex schriftlich --shell-escape

+ 4 - 0
schriftlich/README.txt

@@ -0,0 +1,4 @@
+In diesem Ordner befindet sich der schriftliche Teil der Arbeit.
+Eine Kompilierte Version befindet sich in schriftlich.pdf
+Zum Kompilieren wird eine Latex Version benötigt.
+Auf einem Windows Computer kann zum kompilieren make benutzt werden.

+ 338 - 0
schriftlich/bilder/Datenhaltung_vortrag.svg

@@ -0,0 +1,338 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE svg PUBLIC '-//W3C//DTD SVG 1.0//EN'
+          'http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd'>
+<svg fill-opacity="0" xmlns:xlink="http://www.w3.org/1999/xlink" color-rendering="auto" color-interpolation="auto" text-rendering="auto" stroke="rgb(0,0,0)" stroke-linecap="square" width="605" stroke-miterlimit="10" shape-rendering="auto" stroke-opacity="0" fill="rgb(0,0,0)" stroke-dasharray="none" font-weight="normal" stroke-width="1" height="271" xmlns="http://www.w3.org/2000/svg" font-family="&apos;Dialog&apos;" font-style="normal" stroke-linejoin="miter" font-size="12" stroke-dashoffset="0" image-rendering="auto"
+><!--Generated by the Batik Graphics2D SVG Generator--><defs id="genericDefs"
+  /><g
+  ><defs id="defs1"
+    ><clipPath clipPathUnits="userSpaceOnUse" id="clipPath1"
+      ><path d="M-7 -7 L191 -7 L191 105 L-7 105 L-7 -7 Z"
+      /></clipPath
+      ><clipPath clipPathUnits="userSpaceOnUse" id="clipPath2"
+      ><path d="M0 0 L0 79 L180 79 L180 0 Z"
+      /></clipPath
+      ><clipPath clipPathUnits="userSpaceOnUse" id="clipPath3"
+      ><path d="M0 0 L0 15 L181 15 L181 0 Z"
+      /></clipPath
+      ><clipPath clipPathUnits="userSpaceOnUse" id="clipPath4"
+      ><path d="M-7 -7 L148 -7 L148 94 L-7 94 L-7 -7 Z"
+      /></clipPath
+      ><clipPath clipPathUnits="userSpaceOnUse" id="clipPath5"
+      ><path d="M0 0 L0 68 L137 68 L137 0 Z"
+      /></clipPath
+      ><clipPath clipPathUnits="userSpaceOnUse" id="clipPath6"
+      ><path d="M0 0 L0 15 L137 15 L137 0 Z"
+      /></clipPath
+      ><clipPath clipPathUnits="userSpaceOnUse" id="clipPath7"
+      ><path d="M-7 -7 L135 -7 L135 66 L-7 66 L-7 -7 Z"
+      /></clipPath
+      ><clipPath clipPathUnits="userSpaceOnUse" id="clipPath8"
+      ><path d="M0 0 L0 40 L124 40 L124 0 Z"
+      /></clipPath
+      ><clipPath clipPathUnits="userSpaceOnUse" id="clipPath9"
+      ><path d="M0 0 L0 15 L124 15 L124 0 Z"
+      /></clipPath
+      ><clipPath clipPathUnits="userSpaceOnUse" id="clipPath10"
+      ><path d="M-7 -7 L148 -7 L148 83 L-7 83 L-7 -7 Z"
+      /></clipPath
+      ><clipPath clipPathUnits="userSpaceOnUse" id="clipPath11"
+      ><path d="M0 0 L0 57 L137 57 L137 0 Z"
+      /></clipPath
+      ><clipPath clipPathUnits="userSpaceOnUse" id="clipPath12"
+      ><path d="M-7 -7 L168 -7 L168 94 L-7 94 L-7 -7 Z"
+      /></clipPath
+      ><clipPath clipPathUnits="userSpaceOnUse" id="clipPath13"
+      ><path d="M0 0 L0 68 L157 68 L157 0 Z"
+      /></clipPath
+      ><clipPath clipPathUnits="userSpaceOnUse" id="clipPath14"
+      ><path d="M0 0 L0 15 L157 15 L157 0 Z"
+      /></clipPath
+      ><clipPath clipPathUnits="userSpaceOnUse" id="clipPath15"
+      ><path d="M-7 -7 L91 -7 L91 73 L-7 73 L-7 -7 Z"
+      /></clipPath
+      ><clipPath clipPathUnits="userSpaceOnUse" id="clipPath16"
+      ><path d="M0 0 L0 47 L80 47 L80 0 Z"
+      /></clipPath
+      ><clipPath clipPathUnits="userSpaceOnUse" id="clipPath17"
+      ><path d="M0 0 L0 15 L80 15 L80 0 Z"
+      /></clipPath
+      ><clipPath clipPathUnits="userSpaceOnUse" id="clipPath18"
+      ><path d="M-7 -7 L38 -7 L38 26 L-7 26 L-7 -7 Z"
+      /></clipPath
+      ><clipPath clipPathUnits="userSpaceOnUse" id="clipPath19"
+      ><path d="M0 0 L0 15 L27 15 L27 0 Z"
+      /></clipPath
+      ><clipPath clipPathUnits="userSpaceOnUse" id="clipPath20"
+      ><path d="M-7 -7 L48 -7 L48 26 L-7 26 L-7 -7 Z"
+      /></clipPath
+      ><clipPath clipPathUnits="userSpaceOnUse" id="clipPath21"
+      ><path d="M0 0 L0 15 L37 15 L37 0 Z"
+      /></clipPath
+      ><clipPath clipPathUnits="userSpaceOnUse" id="clipPath22"
+      ><path d="M0 0 L135 0 L135 54 L0 54 L0 0 Z"
+      /></clipPath
+      ><clipPath clipPathUnits="userSpaceOnUse" id="clipPath23"
+      ><path d="M0 0 L0 54 L135 54 L135 0 Z"
+      /></clipPath
+      ><clipPath clipPathUnits="userSpaceOnUse" id="clipPath24"
+      ><path d="M0 0 L54 0 L54 113 L0 113 L0 0 Z"
+      /></clipPath
+      ><clipPath clipPathUnits="userSpaceOnUse" id="clipPath25"
+      ><path d="M0 0 L0 113 L54 113 L54 0 Z"
+      /></clipPath
+      ><clipPath clipPathUnits="userSpaceOnUse" id="clipPath26"
+      ><path d="M0 0 L129 0 L129 54 L0 54 L0 0 Z"
+      /></clipPath
+      ><clipPath clipPathUnits="userSpaceOnUse" id="clipPath27"
+      ><path d="M0 0 L0 54 L129 54 L129 0 Z"
+      /></clipPath
+      ><clipPath clipPathUnits="userSpaceOnUse" id="clipPath28"
+      ><path d="M0 0 L283 0 L283 54 L0 54 L0 0 Z"
+      /></clipPath
+      ><clipPath clipPathUnits="userSpaceOnUse" id="clipPath29"
+      ><path d="M0 0 L0 54 L283 54 L283 0 Z"
+      /></clipPath
+      ><clipPath clipPathUnits="userSpaceOnUse" id="clipPath30"
+      ><path d="M0 0 L172 0 L172 54 L0 54 L0 0 Z"
+      /></clipPath
+      ><clipPath clipPathUnits="userSpaceOnUse" id="clipPath31"
+      ><path d="M0 0 L0 54 L172 54 L172 0 Z"
+      /></clipPath
+    ></defs
+    ><g fill="white" text-rendering="geometricPrecision" fill-opacity="1" stroke-opacity="1" stroke="white"
+    ><rect x="0" width="605" height="271" y="0" stroke="none"
+    /></g
+    ><g font-size="11" transform="translate(2.5,14)" fill-opacity="1" fill="rgb(122,207,245)" text-rendering="geometricPrecision" image-rendering="optimizeQuality" font-family="sans-serif" stroke="rgb(122,207,245)" font-weight="bold" stroke-opacity="1"
+    ><rect x="0" width="180" height="94" y="0" clip-path="url(#clipPath1)" stroke="none"
+    /></g
+    ><g fill-opacity="1" text-rendering="geometricPrecision" stroke="black" stroke-linecap="butt" transform="translate(2.5,14)" stroke-miterlimit="0" stroke-opacity="1" fill="black" font-weight="bold" font-family="sans-serif" stroke-linejoin="round" font-size="11" image-rendering="optimizeQuality"
+    ><rect fill="none" x="0" width="180" height="94" y="0" clip-path="url(#clipPath1)"
+      /><line y2="15" fill="none" x1="0" clip-path="url(#clipPath1)" x2="180" y1="15"
+    /></g
+    ><g font-size="11" transform="matrix(1,0,0,1,2.5,29)" fill-opacity="1" fill="black" text-rendering="geometricPrecision" image-rendering="optimizeQuality" font-family="sans-serif" stroke="black" stroke-opacity="1"
+    ><text x="2" xml:space="preserve" y="14" clip-path="url(#clipPath2)" stroke="none"
+      >-path : String</text
+    ></g
+    ><g font-size="11" transform="matrix(1,0,0,1,2.5,29)" fill-opacity="1" fill="black" text-rendering="geometricPrecision" image-rendering="optimizeQuality" font-family="sans-serif" stroke="black" stroke-opacity="1"
+    ><text x="2" xml:space="preserve" y="29" clip-path="url(#clipPath2)" stroke="none"
+      >-ref : Int</text
+    ></g
+    ><g font-size="11" transform="matrix(1,0,0,1,2.5,29)" fill-opacity="1" fill="black" text-rendering="geometricPrecision" image-rendering="optimizeQuality" font-family="sans-serif" stroke="black" stroke-opacity="1"
+    ><text x="2" xml:space="preserve" y="44" clip-path="url(#clipPath2)" stroke="none"
+      >-frameIndex : Int</text
+    ></g
+    ><g font-size="11" transform="matrix(1,0,0,1,2.5,29)" fill-opacity="1" fill="black" text-rendering="geometricPrecision" image-rendering="optimizeQuality" font-family="sans-serif" stroke="black" stroke-opacity="1"
+    ><text x="2" xml:space="preserve" y="59" clip-path="url(#clipPath2)" stroke="none"
+      >-cameraIndex : Int</text
+    ></g
+    ><g font-size="11" transform="translate(2.5,14)" fill-opacity="1" fill="black" text-rendering="geometricPrecision" image-rendering="optimizeQuality" font-family="sans-serif" stroke="black" font-weight="bold" stroke-opacity="1"
+    ><text x="67" xml:space="preserve" y="12" clip-path="url(#clipPath3)" stroke="none"
+      >Sequenz</text
+    ></g
+    ><g font-size="11" transform="translate(17.5,168)" fill-opacity="1" fill="rgb(122,207,245)" text-rendering="geometricPrecision" image-rendering="optimizeQuality" font-family="sans-serif" stroke="rgb(122,207,245)" font-weight="bold" stroke-opacity="1"
+    ><rect x="0" width="137" height="83" y="0" clip-path="url(#clipPath4)" stroke="none"
+    /></g
+    ><g fill-opacity="1" text-rendering="geometricPrecision" stroke="black" stroke-linecap="butt" transform="translate(17.5,168)" stroke-miterlimit="0" stroke-opacity="1" fill="black" font-weight="bold" font-family="sans-serif" stroke-linejoin="round" font-size="11" image-rendering="optimizeQuality"
+    ><rect fill="none" x="0" width="137" height="83" y="0" clip-path="url(#clipPath4)"
+      /><line y2="15" fill="none" x1="0" clip-path="url(#clipPath4)" x2="137" y1="15"
+    /></g
+    ><g font-size="11" transform="matrix(1,0,0,1,17.5,183)" fill-opacity="1" fill="black" text-rendering="geometricPrecision" image-rendering="optimizeQuality" font-family="sans-serif" stroke="black" stroke-opacity="1"
+    ><text x="2" xml:space="preserve" y="14" clip-path="url(#clipPath5)" stroke="none"
+      >-name : String</text
+    ></g
+    ><g font-size="11" transform="translate(17.5,168)" fill-opacity="1" fill="black" text-rendering="geometricPrecision" image-rendering="optimizeQuality" font-family="sans-serif" stroke="black" font-weight="bold" stroke-opacity="1"
+    ><text x="47" xml:space="preserve" y="12" clip-path="url(#clipPath6)" stroke="none"
+      >Kamera</text
+    ></g
+    ><g font-size="11" transform="translate(237.5,118)" fill-opacity="1" fill="rgb(122,207,245)" text-rendering="geometricPrecision" image-rendering="optimizeQuality" font-family="sans-serif" stroke="rgb(122,207,245)" font-weight="bold" stroke-opacity="1"
+    ><rect x="0" width="124" height="55" y="0" clip-path="url(#clipPath7)" stroke="none"
+    /></g
+    ><g fill-opacity="1" text-rendering="geometricPrecision" stroke="black" stroke-linecap="butt" transform="translate(237.5,118)" stroke-miterlimit="0" stroke-opacity="1" fill="black" font-weight="bold" font-family="sans-serif" stroke-linejoin="round" font-size="11" image-rendering="optimizeQuality"
+    ><rect fill="none" x="0" width="124" height="55" y="0" clip-path="url(#clipPath7)"
+      /><line y2="15" fill="none" x1="0" clip-path="url(#clipPath7)" x2="124" y1="15"
+    /></g
+    ><g font-size="11" transform="matrix(1,0,0,1,237.5,133)" fill-opacity="1" fill="black" text-rendering="geometricPrecision" image-rendering="optimizeQuality" font-family="sans-serif" stroke="black" stroke-opacity="1"
+    ><text x="2" xml:space="preserve" y="14" clip-path="url(#clipPath8)" stroke="none"
+      >-path : String</text
+    ></g
+    ><g font-size="11" transform="matrix(1,0,0,1,237.5,133)" fill-opacity="1" fill="black" text-rendering="geometricPrecision" image-rendering="optimizeQuality" font-family="sans-serif" stroke="black" stroke-opacity="1"
+    ><text x="2" xml:space="preserve" y="29" clip-path="url(#clipPath8)" stroke="none"
+      >-mask : Image</text
+    ></g
+    ><g font-size="11" transform="translate(237.5,118)" fill-opacity="1" fill="black" text-rendering="geometricPrecision" image-rendering="optimizeQuality" font-family="sans-serif" stroke="black" font-weight="bold" stroke-opacity="1"
+    ><text x="48" xml:space="preserve" y="12" clip-path="url(#clipPath9)" stroke="none"
+      >Mask</text
+    ></g
+    ><g font-size="11" transform="translate(231.5,192)" fill-opacity="1" fill="rgb(122,207,245)" text-rendering="geometricPrecision" image-rendering="optimizeQuality" font-family="sans-serif" stroke="rgb(122,207,245)" font-weight="bold" stroke-opacity="1"
+    ><rect x="0" width="137" height="72" y="0" clip-path="url(#clipPath10)" stroke="none"
+    /></g
+    ><g fill-opacity="1" text-rendering="geometricPrecision" stroke="black" stroke-linecap="butt" transform="translate(231.5,192)" stroke-miterlimit="0" stroke-opacity="1" fill="black" font-weight="bold" font-family="sans-serif" stroke-linejoin="round" font-size="11" image-rendering="optimizeQuality"
+    ><rect fill="none" x="0" width="137" height="72" y="0" clip-path="url(#clipPath10)"
+      /><line y2="15" fill="none" x1="0" clip-path="url(#clipPath10)" x2="137" y1="15"
+    /></g
+    ><g font-size="11" transform="matrix(1,0,0,1,231.5,207)" fill-opacity="1" fill="black" text-rendering="geometricPrecision" image-rendering="optimizeQuality" font-family="sans-serif" stroke="black" stroke-opacity="1"
+    ><text x="2" xml:space="preserve" y="14" clip-path="url(#clipPath11)" stroke="none"
+      >-path : String</text
+    ></g
+    ><g font-size="11" transform="matrix(1,0,0,1,231.5,207)" fill-opacity="1" fill="black" text-rendering="geometricPrecision" image-rendering="optimizeQuality" font-family="sans-serif" stroke="black" stroke-opacity="1"
+    ><text x="2" xml:space="preserve" y="29" clip-path="url(#clipPath11)" stroke="none"
+      >-timestamp : String</text
+    ></g
+    ><g font-size="11" transform="matrix(1,0,0,1,231.5,207)" fill-opacity="1" fill="black" text-rendering="geometricPrecision" image-rendering="optimizeQuality" font-family="sans-serif" stroke="black" stroke-opacity="1"
+    ><text x="2" xml:space="preserve" y="44" clip-path="url(#clipPath11)" stroke="none"
+      >-needAnnotation : Boolean</text
+    ></g
+    ><g font-size="11" transform="translate(231.5,192)" fill-opacity="1" fill="black" text-rendering="geometricPrecision" image-rendering="optimizeQuality" font-family="sans-serif" stroke="black" font-weight="bold" stroke-opacity="1"
+    ><text x="51" xml:space="preserve" y="12" clip-path="url(#clipPath6)" stroke="none"
+      >Frame</text
+    ></g
+    ><g font-size="11" transform="translate(444.5,181)" fill-opacity="1" fill="rgb(122,207,245)" text-rendering="geometricPrecision" image-rendering="optimizeQuality" font-family="sans-serif" stroke="rgb(122,207,245)" font-weight="bold" stroke-opacity="1"
+    ><rect x="0" width="157" height="83" y="0" clip-path="url(#clipPath12)" stroke="none"
+    /></g
+    ><g fill-opacity="1" text-rendering="geometricPrecision" stroke="black" stroke-linecap="butt" transform="translate(444.5,181)" stroke-miterlimit="0" stroke-opacity="1" fill="black" font-weight="bold" font-family="sans-serif" stroke-linejoin="round" font-size="11" image-rendering="optimizeQuality"
+    ><rect fill="none" x="0" width="157" height="83" y="0" clip-path="url(#clipPath12)"
+      /><line y2="15" fill="none" x1="0" clip-path="url(#clipPath12)" x2="157" y1="15"
+    /></g
+    ><g font-size="11" transform="matrix(1,0,0,1,444.5,196)" fill-opacity="1" fill="black" text-rendering="geometricPrecision" image-rendering="optimizeQuality" font-family="sans-serif" stroke="black" stroke-opacity="1"
+    ><text x="2" xml:space="preserve" y="14" clip-path="url(#clipPath13)" stroke="none"
+      >-polygon : Polygon</text
+    ></g
+    ><g font-size="11" transform="matrix(1,0,0,1,444.5,196)" fill-opacity="1" fill="black" text-rendering="geometricPrecision" image-rendering="optimizeQuality" font-family="sans-serif" stroke="black" stroke-opacity="1"
+    ><text x="2" xml:space="preserve" y="29" clip-path="url(#clipPath13)" stroke="none"
+      >-id : String</text
+    ></g
+    ><g font-size="11" transform="matrix(1,0,0,1,444.5,196)" fill-opacity="1" fill="black" text-rendering="geometricPrecision" image-rendering="optimizeQuality" font-family="sans-serif" stroke="black" stroke-opacity="1"
+    ><text x="2" xml:space="preserve" y="44" clip-path="url(#clipPath13)" stroke="none"
+      >-selected : Boolean</text
+    ></g
+    ><g font-size="11" transform="matrix(1,0,0,1,444.5,196)" fill-opacity="1" fill="black" text-rendering="geometricPrecision" image-rendering="optimizeQuality" font-family="sans-serif" stroke="black" stroke-opacity="1"
+    ><text x="2" xml:space="preserve" y="59" clip-path="url(#clipPath13)" stroke="none"
+      >-truncated : Boolean</text
+    ></g
+    ><g font-size="11" transform="translate(444.5,181)" fill-opacity="1" fill="black" text-rendering="geometricPrecision" image-rendering="optimizeQuality" font-family="sans-serif" stroke="black" font-weight="bold" stroke-opacity="1"
+    ><text x="39" xml:space="preserve" y="12" clip-path="url(#clipPath14)" stroke="none"
+      >ObjectPolygon</text
+    ></g
+    ><g font-size="11" transform="translate(413.5,71)" fill-opacity="1" fill="rgb(122,207,245)" text-rendering="geometricPrecision" image-rendering="optimizeQuality" font-family="sans-serif" stroke="rgb(122,207,245)" font-weight="bold" stroke-opacity="1"
+    ><rect x="0" width="80" height="62" y="0" clip-path="url(#clipPath15)" stroke="none"
+    /></g
+    ><g fill-opacity="1" text-rendering="geometricPrecision" stroke="black" stroke-linecap="butt" transform="translate(413.5,71)" stroke-miterlimit="0" stroke-opacity="1" fill="black" font-weight="bold" font-family="sans-serif" stroke-linejoin="round" font-size="11" image-rendering="optimizeQuality"
+    ><rect fill="none" x="0" width="80" height="62" y="0" clip-path="url(#clipPath15)"
+      /><line y2="15" fill="none" x1="0" clip-path="url(#clipPath15)" x2="80" y1="15"
+    /></g
+    ><g font-size="11" transform="matrix(1,0,0,1,413.5,86)" fill-opacity="1" fill="black" text-rendering="geometricPrecision" image-rendering="optimizeQuality" font-family="sans-serif" stroke="black" stroke-opacity="1"
+    ><text x="2" xml:space="preserve" y="14" clip-path="url(#clipPath16)" stroke="none"
+      >-id : String</text
+    ></g
+    ><g font-size="11" transform="matrix(1,0,0,1,413.5,86)" fill-opacity="1" fill="black" text-rendering="geometricPrecision" image-rendering="optimizeQuality" font-family="sans-serif" stroke="black" stroke-opacity="1"
+    ><text x="2" xml:space="preserve" y="29" clip-path="url(#clipPath16)" stroke="none"
+      >-classId : Int</text
+    ></g
+    ><g font-size="11" transform="translate(413.5,71)" fill-opacity="1" fill="black" text-rendering="geometricPrecision" image-rendering="optimizeQuality" font-family="sans-serif" stroke="black" font-weight="bold" stroke-opacity="1"
+    ><text x="22" xml:space="preserve" y="12" clip-path="url(#clipPath17)" stroke="none"
+      >Objekt</text
+    ></g
+    ><g font-size="11" transform="translate(302.5,2)" fill-opacity="1" fill="rgb(122,207,245)" text-rendering="geometricPrecision" image-rendering="optimizeQuality" font-family="sans-serif" stroke="rgb(122,207,245)" font-weight="bold" stroke-opacity="1"
+    ><rect x="0" width="80" height="62" y="0" clip-path="url(#clipPath15)" stroke="none"
+    /></g
+    ><g fill-opacity="1" text-rendering="geometricPrecision" stroke="black" stroke-linecap="butt" transform="translate(302.5,2)" stroke-miterlimit="0" stroke-opacity="1" fill="black" font-weight="bold" font-family="sans-serif" stroke-linejoin="round" font-size="11" image-rendering="optimizeQuality"
+    ><rect fill="none" x="0" width="80" height="62" y="0" clip-path="url(#clipPath15)"
+      /><line y2="15" fill="none" x1="0" clip-path="url(#clipPath15)" x2="80" y1="15"
+    /></g
+    ><g font-size="11" transform="matrix(1,0,0,1,302.5,17)" fill-opacity="1" fill="black" text-rendering="geometricPrecision" image-rendering="optimizeQuality" font-family="sans-serif" stroke="black" stroke-opacity="1"
+    ><text x="2" xml:space="preserve" y="14" clip-path="url(#clipPath16)" stroke="none"
+      >-id : Int</text
+    ></g
+    ><g font-size="11" transform="matrix(1,0,0,1,302.5,17)" fill-opacity="1" fill="black" text-rendering="geometricPrecision" image-rendering="optimizeQuality" font-family="sans-serif" stroke="black" stroke-opacity="1"
+    ><text x="2" xml:space="preserve" y="29" clip-path="url(#clipPath16)" stroke="none"
+      >-name : String</text
+    ></g
+    ><g font-size="11" transform="translate(302.5,2)" fill-opacity="1" fill="black" text-rendering="geometricPrecision" image-rendering="optimizeQuality" font-family="sans-serif" stroke="black" font-weight="bold" stroke-opacity="1"
+    ><text x="8" xml:space="preserve" y="12" clip-path="url(#clipPath17)" stroke="none"
+      >ObjectClass</text
+    ></g
+    ><g font-size="11" transform="translate(106.5,113)" fill-opacity="1" fill="black" text-rendering="geometricPrecision" image-rendering="optimizeQuality" font-family="sans-serif" stroke="black" stroke-opacity="1"
+    ><text x="10" xml:space="preserve" y="12" clip-path="url(#clipPath19)" stroke="none"
+      >1</text
+    ></g
+    ><g font-size="11" transform="translate(59.5,148)" fill-opacity="1" fill="black" text-rendering="geometricPrecision" image-rendering="optimizeQuality" font-family="sans-serif" stroke="black" stroke-opacity="1"
+    ><text x="10" xml:space="preserve" y="12" clip-path="url(#clipPath21)" stroke="none"
+      >1..*</text
+    ></g
+    ><g font-size="11" transform="translate(157.5,143)" fill-opacity="1" fill="black" text-rendering="geometricPrecision" image-rendering="optimizeQuality" font-family="sans-serif" stroke="black" stroke-opacity="1"
+    ><text x="10" xml:space="preserve" y="12" clip-path="url(#clipPath19)" stroke="none"
+      >1</text
+    ></g
+    ><g font-size="11" transform="translate(199.5,170)" fill-opacity="1" fill="black" text-rendering="geometricPrecision" image-rendering="optimizeQuality" font-family="sans-serif" stroke="black" stroke-opacity="1"
+    ><text x="10" xml:space="preserve" y="12" clip-path="url(#clipPath19)" stroke="none"
+      >1</text
+    ></g
+    ><g font-size="11" transform="translate(188.5,63)" fill-opacity="1" fill="black" text-rendering="geometricPrecision" image-rendering="optimizeQuality" font-family="sans-serif" stroke="black" stroke-opacity="1"
+    ><text x="10" xml:space="preserve" y="12" clip-path="url(#clipPath19)" stroke="none"
+      >1</text
+    ></g
+    ><g font-size="11" transform="translate(371.5,87)" fill-opacity="1" fill="black" text-rendering="geometricPrecision" image-rendering="optimizeQuality" font-family="sans-serif" stroke="black" stroke-opacity="1"
+    ><text x="10" xml:space="preserve" y="12" clip-path="url(#clipPath21)" stroke="none"
+      >1..*</text
+    ></g
+    ><g font-size="11" transform="translate(160.5,227)" fill-opacity="1" fill="black" text-rendering="geometricPrecision" image-rendering="optimizeQuality" font-family="sans-serif" stroke="black" stroke-opacity="1"
+    ><text x="10" xml:space="preserve" y="12" clip-path="url(#clipPath19)" stroke="none"
+      >1</text
+    ></g
+    ><g font-size="11" transform="translate(189.5,253)" fill-opacity="1" fill="black" text-rendering="geometricPrecision" image-rendering="optimizeQuality" font-family="sans-serif" stroke="black" stroke-opacity="1"
+    ><text x="10" xml:space="preserve" y="12" clip-path="url(#clipPath21)" stroke="none"
+      >1..*</text
+    ></g
+    ><g font-size="11" transform="translate(187.5,10)" fill-opacity="1" fill="black" text-rendering="geometricPrecision" image-rendering="optimizeQuality" font-family="sans-serif" stroke="black" stroke-opacity="1"
+    ><text x="10" xml:space="preserve" y="12" clip-path="url(#clipPath19)" stroke="none"
+      >1</text
+    ></g
+    ><g font-size="11" transform="translate(259.5,33)" fill-opacity="1" fill="black" text-rendering="geometricPrecision" image-rendering="optimizeQuality" font-family="sans-serif" stroke="black" stroke-opacity="1"
+    ><text x="10" xml:space="preserve" y="12" clip-path="url(#clipPath21)" stroke="none"
+      >1..*</text
+    ></g
+    ><g font-size="11" transform="translate(373.5,202)" fill-opacity="1" fill="black" text-rendering="geometricPrecision" image-rendering="optimizeQuality" font-family="sans-serif" stroke="black" stroke-opacity="1"
+    ><text x="10" xml:space="preserve" y="12" clip-path="url(#clipPath19)" stroke="none"
+      >1</text
+    ></g
+    ><g font-size="11" transform="translate(402.5,227)" fill-opacity="1" fill="black" text-rendering="geometricPrecision" image-rendering="optimizeQuality" font-family="sans-serif" stroke="black" stroke-opacity="1"
+    ><text x="10" xml:space="preserve" y="12" clip-path="url(#clipPath21)" stroke="none"
+      >1..*</text
+    ></g
+    ><g stroke-linecap="butt" font-size="11" transform="translate(130.5,148)" fill-opacity="1" fill="black" text-rendering="geometricPrecision" image-rendering="optimizeQuality" font-family="sans-serif" stroke-linejoin="round" stroke="black" stroke-opacity="1"
+    ><line y2="25" fill="none" x1="49" clip-path="url(#clipPath23)" x2="106" y1="25"
+      /><polygon points=" 25 25 37 31 49 25 37 19" clip-path="url(#clipPath23)" stroke="none"
+      /><polygon fill="none" points=" 25 25 37 31 49 25 37 19" clip-path="url(#clipPath23)"
+    /></g
+    ><g stroke-linecap="butt" font-size="11" transform="translate(73.5,83)" fill-opacity="1" fill="black" text-rendering="geometricPrecision" image-rendering="optimizeQuality" font-family="sans-serif" stroke-linejoin="round" stroke="black" stroke-opacity="1"
+    ><line y2="84" fill="none" x1="25" clip-path="url(#clipPath25)" x2="25" y1="49"
+      /><polygon points=" 25 25 19 37 25 49 31 37" clip-path="url(#clipPath25)" stroke="none"
+      /><polygon fill="none" points=" 25 25 19 37 25 49 31 37" clip-path="url(#clipPath25)"
+    /></g
+    ><g stroke-linecap="butt" font-size="11" transform="translate(130.5,226)" fill-opacity="1" fill="black" text-rendering="geometricPrecision" image-rendering="optimizeQuality" font-family="sans-serif" stroke-linejoin="round" stroke="black" stroke-opacity="1"
+    ><line y2="25" fill="none" x1="49" clip-path="url(#clipPath27)" x2="100" y1="25"
+      /><polygon points=" 25 25 37 31 49 25 37 19" clip-path="url(#clipPath27)" stroke="none"
+      /><polygon fill="none" points=" 25 25 37 31 49 25 37 19" clip-path="url(#clipPath27)"
+    /></g
+    ><g stroke-linecap="butt" font-size="11" transform="translate(343.5,202)" fill-opacity="1" fill="black" text-rendering="geometricPrecision" image-rendering="optimizeQuality" font-family="sans-serif" stroke-linejoin="round" stroke="black" stroke-opacity="1"
+    ><line y2="25" fill="none" x1="49" clip-path="url(#clipPath27)" x2="100" y1="25"
+      /><polygon points=" 25 25 37 31 49 25 37 19" clip-path="url(#clipPath27)" stroke="none"
+      /><polygon fill="none" points=" 25 25 37 31 49 25 37 19" clip-path="url(#clipPath27)"
+    /></g
+    ><g stroke-linecap="butt" font-size="11" transform="translate(158.5,62)" fill-opacity="1" fill="black" text-rendering="geometricPrecision" image-rendering="optimizeQuality" font-family="sans-serif" stroke-linejoin="round" stroke="black" stroke-opacity="1"
+    ><line y2="25" fill="none" x1="49" clip-path="url(#clipPath29)" x2="254" y1="25"
+      /><polygon points=" 25 25 37 31 49 25 37 19" clip-path="url(#clipPath29)" stroke="none"
+      /><polygon fill="none" points=" 25 25 37 31 49 25 37 19" clip-path="url(#clipPath29)"
+    /></g
+    ><g stroke-linecap="butt" font-size="11" transform="translate(158.5,8)" fill-opacity="1" fill="black" text-rendering="geometricPrecision" image-rendering="optimizeQuality" font-family="sans-serif" stroke-linejoin="round" stroke="black" stroke-opacity="1"
+    ><line y2="25" fill="none" x1="49" clip-path="url(#clipPath31)" x2="143" y1="25"
+      /><polygon points=" 25 25 37 31 49 25 37 19" clip-path="url(#clipPath31)" stroke="none"
+      /><polygon fill="none" points=" 25 25 37 31 49 25 37 19" clip-path="url(#clipPath31)"
+    /></g
+    ><g font-size="8" fill-opacity="1" fill="rgb(120,120,120)" text-rendering="geometricPrecision" image-rendering="optimizeQuality" stroke="rgb(120,120,120)" stroke-opacity="1"
+    ><text x="0" xml:space="preserve" y="10" stroke="none"
+      >Visual Paradigm Standard(kolja(University of Kiel))</text
+    ></g
+  ></g
+></svg
+>

BIN
schriftlich/bilder/anzahl_bilder.png


BIN
schriftlich/bilder/anzahl_objekte.png


BIN
schriftlich/bilder/arbeitsansicht.png


BIN
schriftlich/bilder/bevorzugt.png


BIN
schriftlich/bilder/bild_von_kamera.jpg


BIN
schriftlich/bilder/controller.pdf


+ 301 - 0
schriftlich/bilder/controller.svg

@@ -0,0 +1,301 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE svg PUBLIC '-//W3C//DTD SVG 1.0//EN'
+          'http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd'>
+<svg fill-opacity="0" xmlns:xlink="http://www.w3.org/1999/xlink" color-rendering="auto" color-interpolation="auto" text-rendering="auto" stroke="rgb(0,0,0)" stroke-linecap="square" width="469" stroke-miterlimit="10" shape-rendering="auto" stroke-opacity="0" fill="rgb(0,0,0)" stroke-dasharray="none" font-weight="normal" stroke-width="1" height="294" xmlns="http://www.w3.org/2000/svg" font-family="&apos;Dialog&apos;" font-style="normal" stroke-linejoin="miter" font-size="12" stroke-dashoffset="0" image-rendering="auto"
+><!--Generated by the Batik Graphics2D SVG Generator--><defs id="genericDefs"
+  /><g
+  ><defs id="defs1"
+    ><clipPath clipPathUnits="userSpaceOnUse" id="clipPath1"
+      ><path d="M-7 -7 L218 -7 L218 126 L-7 126 L-7 -7 Z"
+      /></clipPath
+      ><clipPath clipPathUnits="userSpaceOnUse" id="clipPath2"
+      ><path d="M0 0 L0 100 L207 100 L207 0 Z"
+      /></clipPath
+      ><clipPath clipPathUnits="userSpaceOnUse" id="clipPath3"
+      ><path d="M0 0 L0 15 L207 15 L207 0 Z"
+      /></clipPath
+      ><clipPath clipPathUnits="userSpaceOnUse" id="clipPath4"
+      ><path d="M-7 -7 L219 -7 L219 53 L-7 53 L-7 -7 Z"
+      /></clipPath
+      ><clipPath clipPathUnits="userSpaceOnUse" id="clipPath5"
+      ><path d="M0 0 L0 27 L208 27 L208 0 Z"
+      /></clipPath
+      ><clipPath clipPathUnits="userSpaceOnUse" id="clipPath6"
+      ><path d="M0 0 L0 15 L209 15 L209 0 Z"
+      /></clipPath
+      ><clipPath clipPathUnits="userSpaceOnUse" id="clipPath7"
+      ><path d="M-7 -7 L219 -7 L219 62 L-7 62 L-7 -7 Z"
+      /></clipPath
+      ><clipPath clipPathUnits="userSpaceOnUse" id="clipPath8"
+      ><path d="M0 0 L0 36 L208 36 L208 0 Z"
+      /></clipPath
+      ><clipPath clipPathUnits="userSpaceOnUse" id="clipPath9"
+      ><path d="M-7 -7 L219 -7 L219 51 L-7 51 L-7 -7 Z"
+      /></clipPath
+      ><clipPath clipPathUnits="userSpaceOnUse" id="clipPath10"
+      ><path d="M0 0 L0 25 L208 25 L208 0 Z"
+      /></clipPath
+      ><clipPath clipPathUnits="userSpaceOnUse" id="clipPath11"
+      ><path d="M0 0 L0 15 L208 15 L208 0 Z"
+      /></clipPath
+      ><clipPath clipPathUnits="userSpaceOnUse" id="clipPath12"
+      ><path d="M-7 -7 L212 -7 L212 51 L-7 51 L-7 -7 Z"
+      /></clipPath
+      ><clipPath clipPathUnits="userSpaceOnUse" id="clipPath13"
+      ><path d="M0 0 L0 25 L201 25 L201 0 Z"
+      /></clipPath
+      ><clipPath clipPathUnits="userSpaceOnUse" id="clipPath14"
+      ><path d="M0 0 L0 15 L201 15 L201 0 Z"
+      /></clipPath
+      ><clipPath clipPathUnits="userSpaceOnUse" id="clipPath15"
+      ><path d="M-7 -7 L212 -7 L212 77 L-7 77 L-7 -7 Z"
+      /></clipPath
+      ><clipPath clipPathUnits="userSpaceOnUse" id="clipPath16"
+      ><path d="M0 0 L0 51 L201 51 L201 0 Z"
+      /></clipPath
+      ><clipPath clipPathUnits="userSpaceOnUse" id="clipPath17"
+      ><path d="M0 0 L101 0 L101 54 L0 54 L0 0 Z"
+      /></clipPath
+      ><clipPath clipPathUnits="userSpaceOnUse" id="clipPath18"
+      ><path d="M0 0 L0 54 L101 54 L101 0 Z"
+      /></clipPath
+      ><clipPath clipPathUnits="userSpaceOnUse" id="clipPath19"
+      ><path d="M0 0 L101 0 L101 96 L0 96 L0 0 Z"
+      /></clipPath
+      ><clipPath clipPathUnits="userSpaceOnUse" id="clipPath20"
+      ><path d="M0 0 L0 96 L101 96 L101 0 Z"
+      /></clipPath
+      ><clipPath clipPathUnits="userSpaceOnUse" id="clipPath21"
+      ><path d="M0 0 L101 0 L101 166 L0 166 L0 0 Z"
+      /></clipPath
+      ><clipPath clipPathUnits="userSpaceOnUse" id="clipPath22"
+      ><path d="M0 0 L0 166 L101 166 L101 0 Z"
+      /></clipPath
+      ><clipPath clipPathUnits="userSpaceOnUse" id="clipPath23"
+      ><path d="M0 0 L101 0 L101 198 L0 198 L0 0 Z"
+      /></clipPath
+      ><clipPath clipPathUnits="userSpaceOnUse" id="clipPath24"
+      ><path d="M0 0 L0 198 L101 198 L101 0 Z"
+      /></clipPath
+      ><clipPath clipPathUnits="userSpaceOnUse" id="clipPath25"
+      ><path d="M0 0 L54 0 L54 103 L0 103 L0 0 Z"
+      /></clipPath
+      ><clipPath clipPathUnits="userSpaceOnUse" id="clipPath26"
+      ><path d="M0 0 L0 103 L54 103 L54 0 Z"
+      /></clipPath
+      ><clipPath clipPathUnits="userSpaceOnUse" id="clipPath27"
+      ><path d="M0 0 L77 0 L77 184 L0 184 L0 0 Z"
+      /></clipPath
+      ><clipPath clipPathUnits="userSpaceOnUse" id="clipPath28"
+      ><path d="M0 0 L0 184 L77 184 L77 0 Z"
+      /></clipPath
+    ></defs
+    ><g fill="white" text-rendering="geometricPrecision" fill-opacity="1" stroke-opacity="1" stroke="white"
+    ><rect x="0" width="469" height="294" y="0" stroke="none"
+    /></g
+    ><g font-size="11" transform="translate(2.5,2.5)" fill-opacity="1" fill="rgb(122,207,245)" text-rendering="geometricPrecision" image-rendering="optimizeQuality" font-family="sans-serif" stroke="rgb(122,207,245)" font-weight="bold" stroke-opacity="1"
+    ><rect x="0" width="207" height="115" y="0" clip-path="url(#clipPath1)" stroke="none"
+    /></g
+    ><g fill-opacity="1" text-rendering="geometricPrecision" stroke="black" stroke-linecap="butt" transform="translate(2.5,2.5)" stroke-miterlimit="0" stroke-opacity="1" fill="black" font-weight="bold" font-family="sans-serif" stroke-linejoin="round" font-size="11" image-rendering="optimizeQuality"
+    ><rect fill="none" x="0" width="207" height="115" y="0" clip-path="url(#clipPath1)"
+      /><line y2="15" fill="none" x1="0" clip-path="url(#clipPath1)" x2="207" y1="15"
+    /></g
+    ><g font-size="11" transform="matrix(1,0,0,1,2.5,17.5)" fill-opacity="1" fill="black" text-rendering="geometricPrecision" image-rendering="optimizeQuality" font-family="sans-serif" stroke="black" stroke-opacity="1"
+    ><text x="2" xml:space="preserve" y="14" clip-path="url(#clipPath2)" stroke="none"
+      >#model : ArbeitsModel</text
+    ></g
+    ><g stroke-linecap="butt" font-size="11" transform="matrix(1,0,0,1,2.5,17.5)" fill-opacity="1" fill="black" text-rendering="geometricPrecision" image-rendering="optimizeQuality" font-family="sans-serif" stroke-linejoin="bevel" stroke="black" stroke-opacity="1" stroke-miterlimit="0"
+    ><line y2="19" fill="none" x1="0" clip-path="url(#clipPath2)" x2="207" y1="19"
+    /></g
+    ><g font-size="11" transform="matrix(1,0,0,1,2.5,17.5)" fill-opacity="1" fill="black" text-rendering="geometricPrecision" image-rendering="optimizeQuality" font-family="sans-serif" font-style="italic" stroke="black" stroke-opacity="1"
+    ><text x="2" xml:space="preserve" y="33" clip-path="url(#clipPath2)" stroke="none"
+      >+wheelEvent(QWheelEvent e)</text
+    ></g
+    ><g font-size="11" transform="matrix(1,0,0,1,2.5,17.5)" fill-opacity="1" fill="black" text-rendering="geometricPrecision" image-rendering="optimizeQuality" font-family="sans-serif" font-style="italic" stroke="black" stroke-opacity="1"
+    ><text x="2" xml:space="preserve" y="48" clip-path="url(#clipPath2)" stroke="none"
+      >+mousePressEvent(QMouseEvent e)</text
+    ></g
+    ><g font-size="11" transform="matrix(1,0,0,1,2.5,17.5)" fill-opacity="1" fill="black" text-rendering="geometricPrecision" image-rendering="optimizeQuality" font-family="sans-serif" font-style="italic" stroke="black" stroke-opacity="1"
+    ><text x="2" xml:space="preserve" y="63" clip-path="url(#clipPath2)" stroke="none"
+      >+mouseMoveEvent(QMouseEvent e)</text
+    ></g
+    ><g font-size="11" transform="matrix(1,0,0,1,2.5,17.5)" fill-opacity="1" fill="black" text-rendering="geometricPrecision" image-rendering="optimizeQuality" font-family="sans-serif" font-style="italic" stroke="black" stroke-opacity="1"
+    ><text x="2" xml:space="preserve" y="78" clip-path="url(#clipPath2)" stroke="none"
+      >+mouseReleaseEvent(QMouseEvent e)</text
+    ></g
+    ><g font-size="11" transform="translate(2.5,2.5)" fill-opacity="1" fill="black" text-rendering="geometricPrecision" image-rendering="optimizeQuality" font-family="sans-serif" stroke="black" font-weight="bold" stroke-opacity="1"
+    ><text x="57" xml:space="preserve" y="12" clip-path="url(#clipPath3)" stroke="none"
+      >ArneitsController</text
+    ></g
+    ><g font-size="11" transform="translate(257.5,2.5)" fill-opacity="1" fill="rgb(122,207,245)" text-rendering="geometricPrecision" image-rendering="optimizeQuality" font-family="sans-serif" stroke="rgb(122,207,245)" font-weight="bold" stroke-opacity="1"
+    ><rect x="0" width="208" height="42" y="0" clip-path="url(#clipPath4)" stroke="none"
+    /></g
+    ><g fill-opacity="1" text-rendering="geometricPrecision" stroke="black" stroke-linecap="butt" transform="translate(257.5,2.5)" stroke-miterlimit="0" stroke-opacity="1" fill="black" font-weight="bold" font-family="sans-serif" stroke-linejoin="round" font-size="11" image-rendering="optimizeQuality"
+    ><rect fill="none" x="0" width="208" height="42" y="0" clip-path="url(#clipPath4)"
+      /><line y2="15" fill="none" x1="0" clip-path="url(#clipPath4)" x2="208" y1="15"
+    /></g
+    ><g font-size="11" transform="matrix(1,0,0,1,257.5,17.5)" fill-opacity="1" fill="black" text-rendering="geometricPrecision" image-rendering="optimizeQuality" font-family="sans-serif" stroke="black" stroke-opacity="1"
+    ><text x="2" xml:space="preserve" y="14" clip-path="url(#clipPath5)" stroke="none"
+      >+mouseReleaseEvent(QMouseEvent e)</text
+    ></g
+    ><g font-size="11" transform="translate(257.5,2.5)" fill-opacity="1" fill="black" text-rendering="geometricPrecision" image-rendering="optimizeQuality" font-family="sans-serif" stroke="black" font-weight="bold" stroke-opacity="1"
+    ><text x="65" xml:space="preserve" y="12" clip-path="url(#clipPath6)" stroke="none"
+      >HideController</text
+    ></g
+    ><g font-size="11" transform="translate(257.5,51.5)" fill-opacity="1" fill="rgb(122,207,245)" text-rendering="geometricPrecision" image-rendering="optimizeQuality" font-family="sans-serif" stroke="rgb(122,207,245)" font-weight="bold" stroke-opacity="1"
+    ><rect x="0" width="208" height="51" y="0" clip-path="url(#clipPath7)" stroke="none"
+    /></g
+    ><g fill-opacity="1" text-rendering="geometricPrecision" stroke="black" stroke-linecap="butt" transform="translate(257.5,51.5)" stroke-miterlimit="0" stroke-opacity="1" fill="black" font-weight="bold" font-family="sans-serif" stroke-linejoin="round" font-size="11" image-rendering="optimizeQuality"
+    ><rect fill="none" x="0" width="208" height="51" y="0" clip-path="url(#clipPath7)"
+      /><line y2="15" fill="none" x1="0" clip-path="url(#clipPath7)" x2="208" y1="15"
+    /></g
+    ><g font-size="11" transform="matrix(1,0,0,1,257.5,66.5)" fill-opacity="1" fill="black" text-rendering="geometricPrecision" image-rendering="optimizeQuality" font-family="sans-serif" stroke="black" stroke-opacity="1"
+    ><text x="2" xml:space="preserve" y="14" clip-path="url(#clipPath8)" stroke="none"
+      >+mousePressEvent(QMouseEvent e)</text
+    ></g
+    ><g font-size="11" transform="matrix(1,0,0,1,257.5,66.5)" fill-opacity="1" fill="black" text-rendering="geometricPrecision" image-rendering="optimizeQuality" font-family="sans-serif" stroke="black" stroke-opacity="1"
+    ><text x="2" xml:space="preserve" y="29" clip-path="url(#clipPath8)" stroke="none"
+      >+mouseMoveEvent(QMouseEvent e)</text
+    ></g
+    ><g font-size="11" transform="translate(257.5,51.5)" fill-opacity="1" fill="black" text-rendering="geometricPrecision" image-rendering="optimizeQuality" font-family="sans-serif" stroke="black" font-weight="bold" stroke-opacity="1"
+    ><text x="64" xml:space="preserve" y="12" clip-path="url(#clipPath6)" stroke="none"
+      >MoveController</text
+    ></g
+    ><g font-size="11" transform="translate(257.5,108.5)" fill-opacity="1" fill="rgb(122,207,245)" text-rendering="geometricPrecision" image-rendering="optimizeQuality" font-family="sans-serif" stroke="rgb(122,207,245)" font-weight="bold" stroke-opacity="1"
+    ><rect x="0" width="208" height="40" y="0" clip-path="url(#clipPath9)" stroke="none"
+    /></g
+    ><g fill-opacity="1" text-rendering="geometricPrecision" stroke="black" stroke-linecap="butt" transform="translate(257.5,108.5)" stroke-miterlimit="0" stroke-opacity="1" fill="black" font-weight="bold" font-family="sans-serif" stroke-linejoin="round" font-size="11" image-rendering="optimizeQuality"
+    ><rect fill="none" x="0" width="208" height="40" y="0" clip-path="url(#clipPath9)"
+      /><line y2="15" fill="none" x1="0" clip-path="url(#clipPath9)" x2="208" y1="15"
+    /></g
+    ><g font-size="11" transform="matrix(1,0,0,1,257.5,123.5)" fill-opacity="1" fill="black" text-rendering="geometricPrecision" image-rendering="optimizeQuality" font-family="sans-serif" stroke="black" stroke-opacity="1"
+    ><text x="2" xml:space="preserve" y="14" clip-path="url(#clipPath10)" stroke="none"
+      >+mouseReleaseEvent(QMouseEvent e)</text
+    ></g
+    ><g font-size="11" transform="translate(257.5,108.5)" fill-opacity="1" fill="black" text-rendering="geometricPrecision" image-rendering="optimizeQuality" font-family="sans-serif" stroke="black" font-weight="bold" stroke-opacity="1"
+    ><text x="68" xml:space="preserve" y="12" clip-path="url(#clipPath11)" stroke="none"
+      >CutController</text
+    ></g
+    ><g font-size="11" transform="translate(257.5,186.5)" fill-opacity="1" fill="rgb(122,207,245)" text-rendering="geometricPrecision" image-rendering="optimizeQuality" font-family="sans-serif" stroke="rgb(122,207,245)" font-weight="bold" stroke-opacity="1"
+    ><rect x="0" width="208" height="42" y="0" clip-path="url(#clipPath4)" stroke="none"
+    /></g
+    ><g fill-opacity="1" text-rendering="geometricPrecision" stroke="black" stroke-linecap="butt" transform="translate(257.5,186.5)" stroke-miterlimit="0" stroke-opacity="1" fill="black" font-weight="bold" font-family="sans-serif" stroke-linejoin="round" font-size="11" image-rendering="optimizeQuality"
+    ><rect fill="none" x="0" width="208" height="42" y="0" clip-path="url(#clipPath4)"
+      /><line y2="15" fill="none" x1="0" clip-path="url(#clipPath4)" x2="208" y1="15"
+    /></g
+    ><g font-size="11" transform="matrix(1,0,0,1,257.5,201.5)" fill-opacity="1" fill="black" text-rendering="geometricPrecision" image-rendering="optimizeQuality" font-family="sans-serif" stroke="black" stroke-opacity="1"
+    ><text x="2" xml:space="preserve" y="14" clip-path="url(#clipPath5)" stroke="none"
+      >+mouseReleaseEvent(QMouseEvent e)</text
+    ></g
+    ><g font-size="11" transform="translate(257.5,186.5)" fill-opacity="1" fill="black" text-rendering="geometricPrecision" image-rendering="optimizeQuality" font-family="sans-serif" stroke="black" font-weight="bold" stroke-opacity="1"
+    ><text x="63" xml:space="preserve" y="12" clip-path="url(#clipPath6)" stroke="none"
+      >CopyController</text
+    ></g
+    ><g font-size="11" transform="translate(257.5,239.5)" fill-opacity="1" fill="rgb(122,207,245)" text-rendering="geometricPrecision" image-rendering="optimizeQuality" font-family="sans-serif" stroke="rgb(122,207,245)" font-weight="bold" stroke-opacity="1"
+    ><rect x="0" width="208" height="51" y="0" clip-path="url(#clipPath7)" stroke="none"
+    /></g
+    ><g fill-opacity="1" text-rendering="geometricPrecision" stroke="black" stroke-linecap="butt" transform="translate(257.5,239.5)" stroke-miterlimit="0" stroke-opacity="1" fill="black" font-weight="bold" font-family="sans-serif" stroke-linejoin="round" font-size="11" image-rendering="optimizeQuality"
+    ><rect fill="none" x="0" width="208" height="51" y="0" clip-path="url(#clipPath7)"
+      /><line y2="15" fill="none" x1="0" clip-path="url(#clipPath7)" x2="208" y1="15"
+    /></g
+    ><g font-size="11" transform="matrix(1,0,0,1,257.5,254.5)" fill-opacity="1" fill="black" text-rendering="geometricPrecision" image-rendering="optimizeQuality" font-family="sans-serif" stroke="black" stroke-opacity="1"
+    ><text x="2" xml:space="preserve" y="14" clip-path="url(#clipPath8)" stroke="none"
+      >+mouseReleaseEvent(QMouseEvent e)</text
+    ></g
+    ><g font-size="11" transform="matrix(1,0,0,1,257.5,254.5)" fill-opacity="1" fill="black" text-rendering="geometricPrecision" image-rendering="optimizeQuality" font-family="sans-serif" stroke="black" stroke-opacity="1"
+    ><text x="2" xml:space="preserve" y="29" clip-path="url(#clipPath8)" stroke="none"
+      >+mouseMoveEvent(QMouseEvent e)</text
+    ></g
+    ><g font-size="11" transform="translate(257.5,239.5)" fill-opacity="1" fill="black" text-rendering="geometricPrecision" image-rendering="optimizeQuality" font-family="sans-serif" stroke="black" font-weight="bold" stroke-opacity="1"
+    ><text x="63" xml:space="preserve" y="12" clip-path="url(#clipPath6)" stroke="none"
+      >PasteController</text
+    ></g
+    ><g font-size="11" transform="translate(2.5,167.5)" fill-opacity="1" fill="rgb(122,207,245)" text-rendering="geometricPrecision" image-rendering="optimizeQuality" font-family="sans-serif" stroke="rgb(122,207,245)" font-weight="bold" stroke-opacity="1"
+    ><rect x="0" width="201" height="40" y="0" clip-path="url(#clipPath12)" stroke="none"
+    /></g
+    ><g fill-opacity="1" text-rendering="geometricPrecision" stroke="black" stroke-linecap="butt" transform="translate(2.5,167.5)" stroke-miterlimit="0" stroke-opacity="1" fill="black" font-weight="bold" font-family="sans-serif" stroke-linejoin="round" font-size="11" image-rendering="optimizeQuality"
+    ><rect fill="none" x="0" width="201" height="40" y="0" clip-path="url(#clipPath12)"
+      /><line y2="15" fill="none" x1="0" clip-path="url(#clipPath12)" x2="201" y1="15"
+    /></g
+    ><g font-size="11" transform="matrix(1,0,0,1,2.5,182.5)" fill-opacity="1" fill="black" text-rendering="geometricPrecision" image-rendering="optimizeQuality" font-family="sans-serif" stroke="black" stroke-opacity="1"
+    ><text x="2" xml:space="preserve" y="14" clip-path="url(#clipPath13)" stroke="none"
+      >+mouseReleaseEvent(QMouseEvent e)</text
+    ></g
+    ><g font-size="11" transform="translate(2.5,167.5)" fill-opacity="1" fill="black" text-rendering="geometricPrecision" image-rendering="optimizeQuality" font-family="sans-serif" stroke="black" font-weight="bold" stroke-opacity="1"
+    ><text x="62" xml:space="preserve" y="12" clip-path="url(#clipPath14)" stroke="none"
+      >NewController</text
+    ></g
+    ><g font-size="11" transform="translate(2.5,224.5)" fill-opacity="1" fill="rgb(122,207,245)" text-rendering="geometricPrecision" image-rendering="optimizeQuality" font-family="sans-serif" stroke="rgb(122,207,245)" font-weight="bold" stroke-opacity="1"
+    ><rect x="0" width="201" height="66" y="0" clip-path="url(#clipPath15)" stroke="none"
+    /></g
+    ><g fill-opacity="1" text-rendering="geometricPrecision" stroke="black" stroke-linecap="butt" transform="translate(2.5,224.5)" stroke-miterlimit="0" stroke-opacity="1" fill="black" font-weight="bold" font-family="sans-serif" stroke-linejoin="round" font-size="11" image-rendering="optimizeQuality"
+    ><rect fill="none" x="0" width="201" height="66" y="0" clip-path="url(#clipPath15)"
+      /><line y2="15" fill="none" x1="0" clip-path="url(#clipPath15)" x2="201" y1="15"
+    /></g
+    ><g font-size="11" transform="matrix(1,0,0,1,2.5,239.5)" fill-opacity="1" fill="black" text-rendering="geometricPrecision" image-rendering="optimizeQuality" font-family="sans-serif" stroke="black" stroke-opacity="1"
+    ><text x="2" xml:space="preserve" y="14" clip-path="url(#clipPath16)" stroke="none"
+      >+mouseReleaseEvent(QMouseEvent e)</text
+    ></g
+    ><g font-size="11" transform="matrix(1,0,0,1,2.5,239.5)" fill-opacity="1" fill="black" text-rendering="geometricPrecision" image-rendering="optimizeQuality" font-family="sans-serif" stroke="black" stroke-opacity="1"
+    ><text x="2" xml:space="preserve" y="29" clip-path="url(#clipPath16)" stroke="none"
+      >+mouseMoveEvent(QMouseEvent e)</text
+    ></g
+    ><g font-size="11" transform="matrix(1,0,0,1,2.5,239.5)" fill-opacity="1" fill="black" text-rendering="geometricPrecision" image-rendering="optimizeQuality" font-family="sans-serif" stroke="black" stroke-opacity="1"
+    ><text x="2" xml:space="preserve" y="44" clip-path="url(#clipPath16)" stroke="none"
+      >+mousePressEvent(QMouseEvent e)</text
+    ></g
+    ><g font-size="11" transform="translate(2.5,224.5)" fill-opacity="1" fill="black" text-rendering="geometricPrecision" image-rendering="optimizeQuality" font-family="sans-serif" stroke="black" font-weight="bold" stroke-opacity="1"
+    ><text x="57" xml:space="preserve" y="12" clip-path="url(#clipPath14)" stroke="none"
+      >DeleteController</text
+    ></g
+    ><g stroke-linecap="butt" font-size="11" transform="translate(184.5,-1.5)" fill-opacity="1" fill="black" text-rendering="geometricPrecision" image-rendering="optimizeQuality" font-family="sans-serif" stroke-linejoin="round" stroke="black" stroke-opacity="1"
+    ><line y2="25" fill="none" x1="25" clip-path="url(#clipPath18)" x2="72" y1="25"
+      /><polygon fill="white" clip-path="url(#clipPath18)" points=" 25 25 37 31 37 19" stroke="none"
+      /><polygon fill="none" points=" 25 25 37 31 37 19" clip-path="url(#clipPath18)"
+    /></g
+    ><g stroke-linecap="butt" font-size="11" transform="translate(184.5,34.5)" fill-opacity="1" fill="black" text-rendering="geometricPrecision" image-rendering="optimizeQuality" font-family="sans-serif" stroke-linejoin="round" stroke="black" stroke-opacity="1"
+    ><line y2="25" fill="none" x1="25" clip-path="url(#clipPath18)" x2="72" y1="25"
+      /><polygon fill="white" clip-path="url(#clipPath18)" points=" 25 25 37 31 37 19" stroke="none"
+      /><polygon fill="none" points=" 25 25 37 31 37 19" clip-path="url(#clipPath18)"
+    /></g
+    ><g stroke-linecap="butt" font-size="11" transform="translate(184.5,52.5)" fill-opacity="1" fill="black" text-rendering="geometricPrecision" image-rendering="optimizeQuality" font-family="sans-serif" stroke-linejoin="round" stroke="black" stroke-opacity="1"
+    ><line y2="25" fill="none" x1="25" clip-path="url(#clipPath20)" x2="57" y1="25"
+      /><line y2="68" fill="none" x1="57" clip-path="url(#clipPath20)" x2="64" y1="25"
+      /><line y2="68" fill="none" x1="64" clip-path="url(#clipPath20)" x2="72" y1="68"
+      /><polygon fill="white" clip-path="url(#clipPath20)" points=" 25 25 37 31 37 19" stroke="none"
+      /><polygon fill="none" points=" 25 25 37 31 37 19" clip-path="url(#clipPath20)"
+    /></g
+    ><g stroke-linecap="butt" font-size="11" transform="translate(184.5,70.5)" fill-opacity="1" fill="black" text-rendering="geometricPrecision" image-rendering="optimizeQuality" font-family="sans-serif" stroke-linejoin="round" stroke="black" stroke-opacity="1"
+    ><line y2="25" fill="none" x1="25" clip-path="url(#clipPath22)" x2="51" y1="25"
+      /><line y2="138" fill="none" x1="51" clip-path="url(#clipPath22)" x2="64" y1="25"
+      /><line y2="138" fill="none" x1="64" clip-path="url(#clipPath22)" x2="72" y1="138"
+      /><polygon fill="white" clip-path="url(#clipPath22)" points=" 25 25 37 31 37 19" stroke="none"
+      /><polygon fill="none" points=" 25 25 37 31 37 19" clip-path="url(#clipPath22)"
+    /></g
+    ><g stroke-linecap="butt" font-size="11" transform="translate(184.5,86.5)" fill-opacity="1" fill="black" text-rendering="geometricPrecision" image-rendering="optimizeQuality" font-family="sans-serif" stroke-linejoin="round" stroke="black" stroke-opacity="1"
+    ><line y2="25" fill="none" x1="25" clip-path="url(#clipPath24)" x2="46" y1="25"
+      /><line y2="169" fill="none" x1="46" clip-path="url(#clipPath24)" x2="57" y1="25"
+      /><line y2="169" fill="none" x1="57" clip-path="url(#clipPath24)" x2="72" y1="169"
+      /><polygon fill="white" clip-path="url(#clipPath24)" points=" 25 25 37 31 37 19" stroke="none"
+      /><polygon fill="none" points=" 25 25 37 31 37 19" clip-path="url(#clipPath24)"
+    /></g
+    ><g stroke-linecap="butt" font-size="11" transform="translate(79.5,92.5)" fill-opacity="1" fill="black" text-rendering="geometricPrecision" image-rendering="optimizeQuality" font-family="sans-serif" stroke-linejoin="round" stroke="black" stroke-opacity="1"
+    ><line y2="74" fill="none" x1="25" clip-path="url(#clipPath26)" x2="25" y1="25"
+      /><polygon fill="white" clip-path="url(#clipPath26)" points=" 25 25 19 37 31 37" stroke="none"
+      /><polygon fill="none" points=" 25 25 19 37 31 37" clip-path="url(#clipPath26)"
+    /></g
+    ><g stroke-linecap="butt" font-size="11" transform="translate(170.5,92.5)" fill-opacity="1" fill="black" text-rendering="geometricPrecision" image-rendering="optimizeQuality" font-family="sans-serif" stroke-linejoin="round" stroke="black" stroke-opacity="1"
+    ><line y2="49" fill="none" x1="25" clip-path="url(#clipPath28)" x2="25" y1="25"
+      /><line y2="49" fill="none" x1="25" clip-path="url(#clipPath28)" x2="48" y1="49"
+      /><line y2="156" fill="none" x1="48" clip-path="url(#clipPath28)" x2="48" y1="49"
+      /><line y2="156" fill="none" x1="48" clip-path="url(#clipPath28)" x2="33" y1="156"
+      /><polygon fill="white" clip-path="url(#clipPath28)" points=" 25 25 19 37 31 37" stroke="none"
+      /><polygon fill="none" points=" 25 25 19 37 31 37" clip-path="url(#clipPath28)"
+    /></g
+    ><g font-size="8" fill-opacity="1" fill="rgb(120,120,120)" text-rendering="geometricPrecision" image-rendering="optimizeQuality" stroke="rgb(120,120,120)" stroke-opacity="1"
+    ><text x="0" xml:space="preserve" y="10" stroke="none"
+      >Visual Paradigm Standard(kolja(University of Kiel))</text
+    ></g
+  ></g
+></svg
+>

BIN
schriftlich/bilder/datenhaltung.pdf


+ 408 - 0
schriftlich/bilder/datenhaltung.svg

@@ -0,0 +1,408 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE svg PUBLIC '-//W3C//DTD SVG 1.0//EN'
+          'http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd'>
+<svg fill-opacity="0" xmlns:xlink="http://www.w3.org/1999/xlink" color-rendering="auto" color-interpolation="auto" text-rendering="auto" stroke="rgb(0,0,0)" stroke-linecap="square" width="367" stroke-miterlimit="10" shape-rendering="auto" stroke-opacity="0" fill="rgb(0,0,0)" stroke-dasharray="none" font-weight="normal" stroke-width="1" height="485" xmlns="http://www.w3.org/2000/svg" font-family="&apos;Dialog&apos;" font-style="normal" stroke-linejoin="miter" font-size="12" stroke-dashoffset="0" image-rendering="auto"
+><!--Generated by the Batik Graphics2D SVG Generator--><defs id="genericDefs"
+  /><g
+  ><defs id="defs1"
+    ><clipPath clipPathUnits="userSpaceOnUse" id="clipPath1"
+      ><path d="M-7 -7 L215 -7 L215 239 L-7 239 L-7 -7 Z"
+      /></clipPath
+      ><clipPath clipPathUnits="userSpaceOnUse" id="clipPath2"
+      ><path d="M0 0 L0 213 L204 213 L204 0 Z"
+      /></clipPath
+      ><clipPath clipPathUnits="userSpaceOnUse" id="clipPath3"
+      ><path d="M0 0 L0 15 L204 15 L204 0 Z"
+      /></clipPath
+      ><clipPath clipPathUnits="userSpaceOnUse" id="clipPath4"
+      ><path d="M-7 -7 L180 -7 L180 87 L-7 87 L-7 -7 Z"
+      /></clipPath
+      ><clipPath clipPathUnits="userSpaceOnUse" id="clipPath5"
+      ><path d="M0 0 L0 61 L169 61 L169 0 Z"
+      /></clipPath
+      ><clipPath clipPathUnits="userSpaceOnUse" id="clipPath6"
+      ><path d="M0 0 L0 15 L169 15 L169 0 Z"
+      /></clipPath
+      ><clipPath clipPathUnits="userSpaceOnUse" id="clipPath7"
+      ><path d="M-7 -7 L135 -7 L135 113 L-7 113 L-7 -7 Z"
+      /></clipPath
+      ><clipPath clipPathUnits="userSpaceOnUse" id="clipPath8"
+      ><path d="M0 0 L0 87 L124 87 L124 0 Z"
+      /></clipPath
+      ><clipPath clipPathUnits="userSpaceOnUse" id="clipPath9"
+      ><path d="M0 0 L0 15 L125 15 L125 0 Z"
+      /></clipPath
+      ><clipPath clipPathUnits="userSpaceOnUse" id="clipPath10"
+      ><path d="M-7 -7 L148 -7 L148 83 L-7 83 L-7 -7 Z"
+      /></clipPath
+      ><clipPath clipPathUnits="userSpaceOnUse" id="clipPath11"
+      ><path d="M0 0 L0 57 L137 57 L137 0 Z"
+      /></clipPath
+      ><clipPath clipPathUnits="userSpaceOnUse" id="clipPath12"
+      ><path d="M0 0 L0 15 L137 15 L137 0 Z"
+      /></clipPath
+      ><clipPath clipPathUnits="userSpaceOnUse" id="clipPath13"
+      ><path d="M-7 -7 L168 -7 L168 94 L-7 94 L-7 -7 Z"
+      /></clipPath
+      ><clipPath clipPathUnits="userSpaceOnUse" id="clipPath14"
+      ><path d="M0 0 L0 68 L157 68 L157 0 Z"
+      /></clipPath
+      ><clipPath clipPathUnits="userSpaceOnUse" id="clipPath15"
+      ><path d="M0 0 L0 15 L157 15 L157 0 Z"
+      /></clipPath
+      ><clipPath clipPathUnits="userSpaceOnUse" id="clipPath16"
+      ><path d="M-7 -7 L91 -7 L91 73 L-7 73 L-7 -7 Z"
+      /></clipPath
+      ><clipPath clipPathUnits="userSpaceOnUse" id="clipPath17"
+      ><path d="M0 0 L0 47 L80 47 L80 0 Z"
+      /></clipPath
+      ><clipPath clipPathUnits="userSpaceOnUse" id="clipPath18"
+      ><path d="M0 0 L0 15 L80 15 L80 0 Z"
+      /></clipPath
+      ><clipPath clipPathUnits="userSpaceOnUse" id="clipPath19"
+      ><path d="M-7 -7 L91 -7 L91 74 L-7 74 L-7 -7 Z"
+      /></clipPath
+      ><clipPath clipPathUnits="userSpaceOnUse" id="clipPath20"
+      ><path d="M0 0 L0 48 L80 48 L80 0 Z"
+      /></clipPath
+      ><clipPath clipPathUnits="userSpaceOnUse" id="clipPath21"
+      ><path d="M-7 -7 L38 -7 L38 26 L-7 26 L-7 -7 Z"
+      /></clipPath
+      ><clipPath clipPathUnits="userSpaceOnUse" id="clipPath22"
+      ><path d="M0 0 L0 15 L27 15 L27 0 Z"
+      /></clipPath
+      ><clipPath clipPathUnits="userSpaceOnUse" id="clipPath23"
+      ><path d="M-7 -7 L48 -7 L48 26 L-7 26 L-7 -7 Z"
+      /></clipPath
+      ><clipPath clipPathUnits="userSpaceOnUse" id="clipPath24"
+      ><path d="M0 0 L0 15 L37 15 L37 0 Z"
+      /></clipPath
+      ><clipPath clipPathUnits="userSpaceOnUse" id="clipPath25"
+      ><path d="M0 0 L121 0 L121 54 L0 54 L0 0 Z"
+      /></clipPath
+      ><clipPath clipPathUnits="userSpaceOnUse" id="clipPath26"
+      ><path d="M0 0 L0 54 L121 54 L121 0 Z"
+      /></clipPath
+      ><clipPath clipPathUnits="userSpaceOnUse" id="clipPath27"
+      ><path d="M0 0 L54 0 L54 104 L0 104 L0 0 Z"
+      /></clipPath
+      ><clipPath clipPathUnits="userSpaceOnUse" id="clipPath28"
+      ><path d="M0 0 L0 104 L54 104 L54 0 Z"
+      /></clipPath
+      ><clipPath clipPathUnits="userSpaceOnUse" id="clipPath29"
+      ><path d="M0 0 L54 0 L54 94 L0 94 L0 0 Z"
+      /></clipPath
+      ><clipPath clipPathUnits="userSpaceOnUse" id="clipPath30"
+      ><path d="M0 0 L0 94 L54 94 L54 0 Z"
+      /></clipPath
+      ><clipPath clipPathUnits="userSpaceOnUse" id="clipPath31"
+      ><path d="M0 0 L120 0 L120 54 L0 54 L0 0 Z"
+      /></clipPath
+      ><clipPath clipPathUnits="userSpaceOnUse" id="clipPath32"
+      ><path d="M0 0 L0 54 L120 54 L120 0 Z"
+      /></clipPath
+      ><clipPath clipPathUnits="userSpaceOnUse" id="clipPath33"
+      ><path d="M0 0 L116 0 L116 54 L0 54 L0 0 Z"
+      /></clipPath
+      ><clipPath clipPathUnits="userSpaceOnUse" id="clipPath34"
+      ><path d="M0 0 L0 54 L116 54 L116 0 Z"
+      /></clipPath
+    ></defs
+    ><g fill="white" text-rendering="geometricPrecision" fill-opacity="1" stroke-opacity="1" stroke="white"
+    ><rect x="0" width="367" height="485" y="0" stroke="none"
+    /></g
+    ><g font-size="11" transform="translate(2,2)" fill-opacity="1" fill="rgb(122,207,245)" text-rendering="geometricPrecision" image-rendering="optimizeQuality" font-family="sans-serif" stroke="rgb(122,207,245)" font-weight="bold" stroke-opacity="1"
+    ><rect x="0" width="204" height="228" y="0" clip-path="url(#clipPath1)" stroke="none"
+    /></g
+    ><g fill-opacity="1" text-rendering="geometricPrecision" stroke="black" stroke-linecap="butt" transform="translate(2,2)" stroke-miterlimit="0" stroke-opacity="1" fill="black" font-weight="bold" font-family="sans-serif" stroke-linejoin="round" font-size="11" image-rendering="optimizeQuality"
+    ><rect fill="none" x="0" width="204" height="228" y="0" clip-path="url(#clipPath1)"
+      /><line y2="15" fill="none" x1="0" clip-path="url(#clipPath1)" x2="204" y1="15"
+    /></g
+    ><g font-size="11" transform="matrix(1,0,0,1,2,17)" fill-opacity="1" fill="black" text-rendering="geometricPrecision" image-rendering="optimizeQuality" font-family="sans-serif" stroke="black" stroke-opacity="1"
+    ><text x="2" xml:space="preserve" y="14" clip-path="url(#clipPath2)" stroke="none"
+      >-path : String</text
+    ></g
+    ><g font-size="11" transform="matrix(1,0,0,1,2,17)" fill-opacity="1" fill="black" text-rendering="geometricPrecision" image-rendering="optimizeQuality" font-family="sans-serif" stroke="black" stroke-opacity="1"
+    ><text x="2" xml:space="preserve" y="29" clip-path="url(#clipPath2)" stroke="none"
+      >-ref : Int</text
+    ></g
+    ><g font-size="11" transform="matrix(1,0,0,1,2,17)" fill-opacity="1" fill="black" text-rendering="geometricPrecision" image-rendering="optimizeQuality" font-family="sans-serif" stroke="black" stroke-opacity="1"
+    ><text x="2" xml:space="preserve" y="44" clip-path="url(#clipPath2)" stroke="none"
+      >-frameIndex : Int</text
+    ></g
+    ><g font-size="11" transform="matrix(1,0,0,1,2,17)" fill-opacity="1" fill="black" text-rendering="geometricPrecision" image-rendering="optimizeQuality" font-family="sans-serif" stroke="black" stroke-opacity="1"
+    ><text x="2" xml:space="preserve" y="59" clip-path="url(#clipPath2)" stroke="none"
+      >-cameraIndex : Int</text
+      ><line stroke-linecap="butt" clip-path="url(#clipPath2)" fill="none" x1="0" x2="204" y1="64" y2="64" stroke-linejoin="bevel" stroke-miterlimit="0"
+    /></g
+    ><g font-size="11" transform="matrix(1,0,0,1,2,17)" fill-opacity="1" fill="black" text-rendering="geometricPrecision" image-rendering="optimizeQuality" font-family="sans-serif" stroke="black" stroke-opacity="1"
+    ><text x="2" xml:space="preserve" y="78" clip-path="url(#clipPath2)" stroke="none"
+      >+getClassOfObject(objectId String) : Int</text
+    ></g
+    ><g font-size="11" transform="matrix(1,0,0,1,2,17)" fill-opacity="1" fill="black" text-rendering="geometricPrecision" image-rendering="optimizeQuality" font-family="sans-serif" stroke="black" stroke-opacity="1"
+    ><text x="2" xml:space="preserve" y="93" clip-path="url(#clipPath2)" stroke="none"
+      >+selectFrame(cam Int, frame Int)</text
+    ></g
+    ><g font-size="11" transform="matrix(1,0,0,1,2,17)" fill-opacity="1" fill="black" text-rendering="geometricPrecision" image-rendering="optimizeQuality" font-family="sans-serif" stroke="black" stroke-opacity="1"
+    ><text x="2" xml:space="preserve" y="108" clip-path="url(#clipPath2)" stroke="none"
+      >+getFrame() : Frame</text
+    ></g
+    ><g font-size="11" transform="matrix(1,0,0,1,2,17)" fill-opacity="1" fill="black" text-rendering="geometricPrecision" image-rendering="optimizeQuality" font-family="sans-serif" stroke="black" stroke-opacity="1"
+    ><text x="2" xml:space="preserve" y="123" clip-path="url(#clipPath2)" stroke="none"
+      >+getSelectedCamera() : Int</text
+    ></g
+    ><g font-size="11" transform="matrix(1,0,0,1,2,17)" fill-opacity="1" fill="black" text-rendering="geometricPrecision" image-rendering="optimizeQuality" font-family="sans-serif" stroke="black" stroke-opacity="1"
+    ><text x="2" xml:space="preserve" y="138" clip-path="url(#clipPath2)" stroke="none"
+      >+getSelectedFrame() : Int</text
+    ></g
+    ><g font-size="11" transform="matrix(1,0,0,1,2,17)" fill-opacity="1" fill="black" text-rendering="geometricPrecision" image-rendering="optimizeQuality" font-family="sans-serif" stroke="black" stroke-opacity="1"
+    ><text x="2" xml:space="preserve" y="153" clip-path="url(#clipPath2)" stroke="none"
+      >+nextFrame()</text
+    ></g
+    ><g font-size="11" transform="matrix(1,0,0,1,2,17)" fill-opacity="1" fill="black" text-rendering="geometricPrecision" image-rendering="optimizeQuality" font-family="sans-serif" stroke="black" stroke-opacity="1"
+    ><text x="2" xml:space="preserve" y="168" clip-path="url(#clipPath2)" stroke="none"
+      >+previousFrame()</text
+    ></g
+    ><g font-size="11" transform="matrix(1,0,0,1,2,17)" fill-opacity="1" fill="black" text-rendering="geometricPrecision" image-rendering="optimizeQuality" font-family="sans-serif" stroke="black" stroke-opacity="1"
+    ><text x="2" xml:space="preserve" y="183" clip-path="url(#clipPath2)" stroke="none"
+      >+hasNextFrame() : Boolean</text
+    ></g
+    ><g font-size="11" transform="matrix(1,0,0,1,2,17)" fill-opacity="1" fill="black" text-rendering="geometricPrecision" image-rendering="optimizeQuality" font-family="sans-serif" stroke="black" stroke-opacity="1"
+    ><text x="2" xml:space="preserve" y="198" clip-path="url(#clipPath2)" stroke="none"
+      >+hasPreviousFrame() : Boolean</text
+    ></g
+    ><g font-size="11" transform="translate(2,2)" fill-opacity="1" fill="black" text-rendering="geometricPrecision" image-rendering="optimizeQuality" font-family="sans-serif" stroke="black" font-weight="bold" stroke-opacity="1"
+    ><text x="78" xml:space="preserve" y="12" clip-path="url(#clipPath3)" stroke="none"
+      >Sequenz</text
+    ></g
+    ><g font-size="11" transform="translate(2,281)" fill-opacity="1" fill="rgb(122,207,245)" text-rendering="geometricPrecision" image-rendering="optimizeQuality" font-family="sans-serif" stroke="rgb(122,207,245)" font-weight="bold" stroke-opacity="1"
+    ><rect x="0" width="169" height="76" y="0" clip-path="url(#clipPath4)" stroke="none"
+    /></g
+    ><g fill-opacity="1" text-rendering="geometricPrecision" stroke="black" stroke-linecap="butt" transform="translate(2,281)" stroke-miterlimit="0" stroke-opacity="1" fill="black" font-weight="bold" font-family="sans-serif" stroke-linejoin="round" font-size="11" image-rendering="optimizeQuality"
+    ><rect fill="none" x="0" width="169" height="76" y="0" clip-path="url(#clipPath4)"
+      /><line y2="15" fill="none" x1="0" clip-path="url(#clipPath4)" x2="169" y1="15"
+    /></g
+    ><g font-size="11" transform="matrix(1,0,0,1,2,296)" fill-opacity="1" fill="black" text-rendering="geometricPrecision" image-rendering="optimizeQuality" font-family="sans-serif" stroke="black" stroke-opacity="1"
+    ><text x="2" xml:space="preserve" y="14" clip-path="url(#clipPath5)" stroke="none"
+      >-name : String</text
+    ></g
+    ><g stroke-linecap="butt" font-size="11" transform="matrix(1,0,0,1,2,296)" fill-opacity="1" fill="black" text-rendering="geometricPrecision" image-rendering="optimizeQuality" font-family="sans-serif" stroke-linejoin="bevel" stroke="black" stroke-opacity="1" stroke-miterlimit="0"
+    ><line y2="19" fill="none" x1="0" clip-path="url(#clipPath5)" x2="169" y1="19"
+    /></g
+    ><g font-size="11" transform="matrix(1,0,0,1,2,296)" fill-opacity="1" fill="black" text-rendering="geometricPrecision" image-rendering="optimizeQuality" font-family="sans-serif" stroke="black" stroke-opacity="1"
+    ><text x="2" xml:space="preserve" y="33" clip-path="url(#clipPath5)" stroke="none"
+      >+isCorrectAnnotated() : Boolean</text
+    ></g
+    ><g font-size="11" transform="matrix(1,0,0,1,2,296)" fill-opacity="1" fill="black" text-rendering="geometricPrecision" image-rendering="optimizeQuality" font-family="sans-serif" stroke="black" stroke-opacity="1"
+    ><text x="2" xml:space="preserve" y="48" clip-path="url(#clipPath5)" stroke="none"
+      >+applyMask()</text
+    ></g
+    ><g font-size="11" transform="translate(2,281)" fill-opacity="1" fill="black" text-rendering="geometricPrecision" image-rendering="optimizeQuality" font-family="sans-serif" stroke="black" font-weight="bold" stroke-opacity="1"
+    ><text x="63" xml:space="preserve" y="12" clip-path="url(#clipPath6)" stroke="none"
+      >Kamera</text
+    ></g
+    ><g font-size="11" transform="translate(239,258)" fill-opacity="1" fill="rgb(122,207,245)" text-rendering="geometricPrecision" image-rendering="optimizeQuality" font-family="sans-serif" stroke="rgb(122,207,245)" font-weight="bold" stroke-opacity="1"
+    ><rect x="0" width="124" height="102" y="0" clip-path="url(#clipPath7)" stroke="none"
+    /></g
+    ><g fill-opacity="1" text-rendering="geometricPrecision" stroke="black" stroke-linecap="butt" transform="translate(239,258)" stroke-miterlimit="0" stroke-opacity="1" fill="black" font-weight="bold" font-family="sans-serif" stroke-linejoin="round" font-size="11" image-rendering="optimizeQuality"
+    ><rect fill="none" x="0" width="124" height="102" y="0" clip-path="url(#clipPath7)"
+      /><line y2="15" fill="none" x1="0" clip-path="url(#clipPath7)" x2="124" y1="15"
+    /></g
+    ><g font-size="11" transform="matrix(1,0,0,1,239,273)" fill-opacity="1" fill="black" text-rendering="geometricPrecision" image-rendering="optimizeQuality" font-family="sans-serif" stroke="black" stroke-opacity="1"
+    ><text x="2" xml:space="preserve" y="14" clip-path="url(#clipPath8)" stroke="none"
+      >-path : String</text
+    ></g
+    ><g font-size="11" transform="matrix(1,0,0,1,239,273)" fill-opacity="1" fill="black" text-rendering="geometricPrecision" image-rendering="optimizeQuality" font-family="sans-serif" stroke="black" stroke-opacity="1"
+    ><text x="2" xml:space="preserve" y="29" clip-path="url(#clipPath8)" stroke="none"
+      >-mask : Image</text
+      ><line stroke-linecap="butt" clip-path="url(#clipPath8)" fill="none" x1="0" x2="124" y1="34" y2="34" stroke-linejoin="bevel" stroke-miterlimit="0"
+    /></g
+    ><g font-size="11" transform="matrix(1,0,0,1,239,273)" fill-opacity="1" fill="black" text-rendering="geometricPrecision" image-rendering="optimizeQuality" font-family="sans-serif" stroke="black" stroke-opacity="1"
+    ><text x="2" xml:space="preserve" y="48" clip-path="url(#clipPath8)" stroke="none"
+      >+Mask(path String)</text
+    ></g
+    ><g font-size="11" transform="matrix(1,0,0,1,239,273)" fill-opacity="1" fill="black" text-rendering="geometricPrecision" image-rendering="optimizeQuality" font-family="sans-serif" stroke="black" stroke-opacity="1"
+    ><text x="2" xml:space="preserve" y="63" clip-path="url(#clipPath8)" stroke="none"
+      >+loadMask()</text
+    ></g
+    ><g font-size="11" transform="matrix(1,0,0,1,239,273)" fill-opacity="1" fill="black" text-rendering="geometricPrecision" image-rendering="optimizeQuality" font-family="sans-serif" stroke="black" stroke-opacity="1"
+    ><text x="2" xml:space="preserve" y="78" clip-path="url(#clipPath8)" stroke="none"
+      >+unloadMask()</text
+    ></g
+    ><g font-size="11" transform="translate(239,258)" fill-opacity="1" fill="black" text-rendering="geometricPrecision" image-rendering="optimizeQuality" font-family="sans-serif" stroke="black" font-weight="bold" stroke-opacity="1"
+    ><text x="48" xml:space="preserve" y="12" clip-path="url(#clipPath9)" stroke="none"
+      >Mask</text
+    ></g
+    ><g font-size="11" transform="translate(2,399)" fill-opacity="1" fill="rgb(122,207,245)" text-rendering="geometricPrecision" image-rendering="optimizeQuality" font-family="sans-serif" stroke="rgb(122,207,245)" font-weight="bold" stroke-opacity="1"
+    ><rect x="0" width="137" height="72" y="0" clip-path="url(#clipPath10)" stroke="none"
+    /></g
+    ><g fill-opacity="1" text-rendering="geometricPrecision" stroke="black" stroke-linecap="butt" transform="translate(2,399)" stroke-miterlimit="0" stroke-opacity="1" fill="black" font-weight="bold" font-family="sans-serif" stroke-linejoin="round" font-size="11" image-rendering="optimizeQuality"
+    ><rect fill="none" x="0" width="137" height="72" y="0" clip-path="url(#clipPath10)"
+      /><line y2="15" fill="none" x1="0" clip-path="url(#clipPath10)" x2="137" y1="15"
+    /></g
+    ><g font-size="11" transform="matrix(1,0,0,1,2,414)" fill-opacity="1" fill="black" text-rendering="geometricPrecision" image-rendering="optimizeQuality" font-family="sans-serif" stroke="black" stroke-opacity="1"
+    ><text x="2" xml:space="preserve" y="14" clip-path="url(#clipPath11)" stroke="none"
+      >-path : String</text
+    ></g
+    ><g font-size="11" transform="matrix(1,0,0,1,2,414)" fill-opacity="1" fill="black" text-rendering="geometricPrecision" image-rendering="optimizeQuality" font-family="sans-serif" stroke="black" stroke-opacity="1"
+    ><text x="2" xml:space="preserve" y="29" clip-path="url(#clipPath11)" stroke="none"
+      >-timestamp : String</text
+    ></g
+    ><g font-size="11" transform="matrix(1,0,0,1,2,414)" fill-opacity="1" fill="black" text-rendering="geometricPrecision" image-rendering="optimizeQuality" font-family="sans-serif" stroke="black" stroke-opacity="1"
+    ><text x="2" xml:space="preserve" y="44" clip-path="url(#clipPath11)" stroke="none"
+      >-needAnnotation : Boolean</text
+    ></g
+    ><g font-size="11" transform="translate(2,399)" fill-opacity="1" fill="black" text-rendering="geometricPrecision" image-rendering="optimizeQuality" font-family="sans-serif" stroke="black" font-weight="bold" stroke-opacity="1"
+    ><text x="51" xml:space="preserve" y="12" clip-path="url(#clipPath12)" stroke="none"
+      >Frame</text
+    ></g
+    ><g font-size="11" transform="translate(206,399)" fill-opacity="1" fill="rgb(122,207,245)" text-rendering="geometricPrecision" image-rendering="optimizeQuality" font-family="sans-serif" stroke="rgb(122,207,245)" font-weight="bold" stroke-opacity="1"
+    ><rect x="0" width="157" height="83" y="0" clip-path="url(#clipPath13)" stroke="none"
+    /></g
+    ><g fill-opacity="1" text-rendering="geometricPrecision" stroke="black" stroke-linecap="butt" transform="translate(206,399)" stroke-miterlimit="0" stroke-opacity="1" fill="black" font-weight="bold" font-family="sans-serif" stroke-linejoin="round" font-size="11" image-rendering="optimizeQuality"
+    ><rect fill="none" x="0" width="157" height="83" y="0" clip-path="url(#clipPath13)"
+      /><line y2="15" fill="none" x1="0" clip-path="url(#clipPath13)" x2="157" y1="15"
+    /></g
+    ><g font-size="11" transform="matrix(1,0,0,1,206,414)" fill-opacity="1" fill="black" text-rendering="geometricPrecision" image-rendering="optimizeQuality" font-family="sans-serif" stroke="black" stroke-opacity="1"
+    ><text x="2" xml:space="preserve" y="14" clip-path="url(#clipPath14)" stroke="none"
+      >-polygon : Polygon</text
+    ></g
+    ><g font-size="11" transform="matrix(1,0,0,1,206,414)" fill-opacity="1" fill="black" text-rendering="geometricPrecision" image-rendering="optimizeQuality" font-family="sans-serif" stroke="black" stroke-opacity="1"
+    ><text x="2" xml:space="preserve" y="29" clip-path="url(#clipPath14)" stroke="none"
+      >-id : String</text
+    ></g
+    ><g font-size="11" transform="matrix(1,0,0,1,206,414)" fill-opacity="1" fill="black" text-rendering="geometricPrecision" image-rendering="optimizeQuality" font-family="sans-serif" stroke="black" stroke-opacity="1"
+    ><text x="2" xml:space="preserve" y="44" clip-path="url(#clipPath14)" stroke="none"
+      >-selected : Boolean</text
+    ></g
+    ><g font-size="11" transform="matrix(1,0,0,1,206,414)" fill-opacity="1" fill="black" text-rendering="geometricPrecision" image-rendering="optimizeQuality" font-family="sans-serif" stroke="black" stroke-opacity="1"
+    ><text x="2" xml:space="preserve" y="59" clip-path="url(#clipPath14)" stroke="none"
+      >-truncated : Boolean</text
+    ></g
+    ><g font-size="11" transform="translate(206,399)" fill-opacity="1" fill="black" text-rendering="geometricPrecision" image-rendering="optimizeQuality" font-family="sans-serif" stroke="black" font-weight="bold" stroke-opacity="1"
+    ><text x="39" xml:space="preserve" y="12" clip-path="url(#clipPath15)" stroke="none"
+      >ObjectPolygon</text
+    ></g
+    ><g font-size="11" transform="translate(270,2)" fill-opacity="1" fill="rgb(122,207,245)" text-rendering="geometricPrecision" image-rendering="optimizeQuality" font-family="sans-serif" stroke="rgb(122,207,245)" font-weight="bold" stroke-opacity="1"
+    ><rect x="0" width="80" height="62" y="0" clip-path="url(#clipPath16)" stroke="none"
+    /></g
+    ><g fill-opacity="1" text-rendering="geometricPrecision" stroke="black" stroke-linecap="butt" transform="translate(270,2)" stroke-miterlimit="0" stroke-opacity="1" fill="black" font-weight="bold" font-family="sans-serif" stroke-linejoin="round" font-size="11" image-rendering="optimizeQuality"
+    ><rect fill="none" x="0" width="80" height="62" y="0" clip-path="url(#clipPath16)"
+      /><line y2="15" fill="none" x1="0" clip-path="url(#clipPath16)" x2="80" y1="15"
+    /></g
+    ><g font-size="11" transform="matrix(1,0,0,1,270,17)" fill-opacity="1" fill="black" text-rendering="geometricPrecision" image-rendering="optimizeQuality" font-family="sans-serif" stroke="black" stroke-opacity="1"
+    ><text x="2" xml:space="preserve" y="14" clip-path="url(#clipPath17)" stroke="none"
+      >-id : String</text
+    ></g
+    ><g font-size="11" transform="matrix(1,0,0,1,270,17)" fill-opacity="1" fill="black" text-rendering="geometricPrecision" image-rendering="optimizeQuality" font-family="sans-serif" stroke="black" stroke-opacity="1"
+    ><text x="2" xml:space="preserve" y="29" clip-path="url(#clipPath17)" stroke="none"
+      >-classId : Int</text
+    ></g
+    ><g font-size="11" transform="translate(270,2)" fill-opacity="1" fill="black" text-rendering="geometricPrecision" image-rendering="optimizeQuality" font-family="sans-serif" stroke="black" font-weight="bold" stroke-opacity="1"
+    ><text x="22" xml:space="preserve" y="12" clip-path="url(#clipPath18)" stroke="none"
+      >Objekt</text
+    ></g
+    ><g font-size="11" transform="translate(270,73)" fill-opacity="1" fill="rgb(122,207,245)" text-rendering="geometricPrecision" image-rendering="optimizeQuality" font-family="sans-serif" stroke="rgb(122,207,245)" font-weight="bold" stroke-opacity="1"
+    ><rect x="0" width="80" height="63" y="0" clip-path="url(#clipPath19)" stroke="none"
+    /></g
+    ><g fill-opacity="1" text-rendering="geometricPrecision" stroke="black" stroke-linecap="butt" transform="translate(270,73)" stroke-miterlimit="0" stroke-opacity="1" fill="black" font-weight="bold" font-family="sans-serif" stroke-linejoin="round" font-size="11" image-rendering="optimizeQuality"
+    ><rect fill="none" x="0" width="80" height="63" y="0" clip-path="url(#clipPath19)"
+      /><line y2="15" fill="none" x1="0" clip-path="url(#clipPath19)" x2="80" y1="15"
+    /></g
+    ><g font-size="11" transform="matrix(1,0,0,1,270,88)" fill-opacity="1" fill="black" text-rendering="geometricPrecision" image-rendering="optimizeQuality" font-family="sans-serif" stroke="black" stroke-opacity="1"
+    ><text x="2" xml:space="preserve" y="14" clip-path="url(#clipPath20)" stroke="none"
+      >-id : Int</text
+    ></g
+    ><g font-size="11" transform="matrix(1,0,0,1,270,88)" fill-opacity="1" fill="black" text-rendering="geometricPrecision" image-rendering="optimizeQuality" font-family="sans-serif" stroke="black" stroke-opacity="1"
+    ><text x="2" xml:space="preserve" y="29" clip-path="url(#clipPath20)" stroke="none"
+      >-name : String</text
+    ></g
+    ><g font-size="11" transform="translate(270,73)" fill-opacity="1" fill="black" text-rendering="geometricPrecision" image-rendering="optimizeQuality" font-family="sans-serif" stroke="black" font-weight="bold" stroke-opacity="1"
+    ><text x="8" xml:space="preserve" y="12" clip-path="url(#clipPath18)" stroke="none"
+      >ObjectClass</text
+    ></g
+    ><g font-size="11" transform="translate(93,235)" fill-opacity="1" fill="black" text-rendering="geometricPrecision" image-rendering="optimizeQuality" font-family="sans-serif" stroke="black" stroke-opacity="1"
+    ><text x="10" xml:space="preserve" y="12" clip-path="url(#clipPath22)" stroke="none"
+      >1</text
+    ></g
+    ><g font-size="11" transform="translate(204,306)" fill-opacity="1" fill="black" text-rendering="geometricPrecision" image-rendering="optimizeQuality" font-family="sans-serif" stroke="black" stroke-opacity="1"
+    ><text x="10" xml:space="preserve" y="12" clip-path="url(#clipPath22)" stroke="none"
+      >1</text
+    ></g
+    ><g font-size="11" transform="translate(176,279)" fill-opacity="1" fill="black" text-rendering="geometricPrecision" image-rendering="optimizeQuality" font-family="sans-serif" stroke="black" stroke-opacity="1"
+    ><text x="10" xml:space="preserve" y="12" clip-path="url(#clipPath22)" stroke="none"
+      >1</text
+    ></g
+    ><g font-size="11" transform="translate(93,363)" fill-opacity="1" fill="black" text-rendering="geometricPrecision" image-rendering="optimizeQuality" font-family="sans-serif" stroke="black" stroke-opacity="1"
+    ><text x="10" xml:space="preserve" y="12" clip-path="url(#clipPath22)" stroke="none"
+      >1</text
+    ></g
+    ><g font-size="11" transform="translate(144,417)" fill-opacity="1" fill="black" text-rendering="geometricPrecision" image-rendering="optimizeQuality" font-family="sans-serif" stroke="black" stroke-opacity="1"
+    ><text x="10" xml:space="preserve" y="12" clip-path="url(#clipPath22)" stroke="none"
+      >1</text
+    ></g
+    ><g font-size="11" transform="translate(211,2)" fill-opacity="1" fill="black" text-rendering="geometricPrecision" image-rendering="optimizeQuality" font-family="sans-serif" stroke="black" stroke-opacity="1"
+    ><text x="10" xml:space="preserve" y="12" clip-path="url(#clipPath22)" stroke="none"
+      >1</text
+    ></g
+    ><g font-size="11" transform="translate(205,86)" fill-opacity="1" fill="black" text-rendering="geometricPrecision" image-rendering="optimizeQuality" font-family="sans-serif" stroke="black" stroke-opacity="1"
+    ><text x="10" xml:space="preserve" y="12" clip-path="url(#clipPath22)" stroke="none"
+      >1</text
+    ></g
+    ><g font-size="11" transform="translate(232,26)" fill-opacity="1" fill="black" text-rendering="geometricPrecision" image-rendering="optimizeQuality" font-family="sans-serif" stroke="black" stroke-opacity="1"
+    ><text x="10" xml:space="preserve" y="12" clip-path="url(#clipPath24)" stroke="none"
+      >1..*</text
+    ></g
+    ><g font-size="11" transform="translate(232,109)" fill-opacity="1" fill="black" text-rendering="geometricPrecision" image-rendering="optimizeQuality" font-family="sans-serif" stroke="black" stroke-opacity="1"
+    ><text x="10" xml:space="preserve" y="12" clip-path="url(#clipPath24)" stroke="none"
+      >1..*</text
+    ></g
+    ><g font-size="11" transform="translate(46,265)" fill-opacity="1" fill="black" text-rendering="geometricPrecision" image-rendering="optimizeQuality" font-family="sans-serif" stroke="black" stroke-opacity="1"
+    ><text x="10" xml:space="preserve" y="12" clip-path="url(#clipPath24)" stroke="none"
+      >1..*</text
+    ></g
+    ><g font-size="11" transform="translate(46,383)" fill-opacity="1" fill="black" text-rendering="geometricPrecision" image-rendering="optimizeQuality" font-family="sans-serif" stroke="black" stroke-opacity="1"
+    ><text x="10" xml:space="preserve" y="12" clip-path="url(#clipPath24)" stroke="none"
+      >1..*</text
+    ></g
+    ><g font-size="11" transform="translate(169,441)" fill-opacity="1" fill="black" text-rendering="geometricPrecision" image-rendering="optimizeQuality" font-family="sans-serif" stroke="black" stroke-opacity="1"
+    ><text x="10" xml:space="preserve" y="12" clip-path="url(#clipPath24)" stroke="none"
+      >1..*</text
+    ></g
+    ><g stroke-linecap="butt" font-size="11" transform="translate(146,282)" fill-opacity="1" fill="black" text-rendering="geometricPrecision" image-rendering="optimizeQuality" font-family="sans-serif" stroke-linejoin="round" stroke="black" stroke-opacity="1"
+    ><line y2="25" fill="none" x1="49" clip-path="url(#clipPath26)" x2="92" y1="25"
+      /><polygon points=" 25 25 37 31 49 25 37 19" clip-path="url(#clipPath26)" stroke="none"
+      /><polygon fill="none" points=" 25 25 37 31 49 25 37 19" clip-path="url(#clipPath26)"
+    /></g
+    ><g stroke-linecap="butt" font-size="11" transform="translate(58,205)" fill-opacity="1" fill="black" text-rendering="geometricPrecision" image-rendering="optimizeQuality" font-family="sans-serif" stroke-linejoin="round" stroke="black" stroke-opacity="1"
+    ><line y2="76" fill="none" x1="25" clip-path="url(#clipPath28)" x2="25" y1="49"
+      /><polygon points=" 25 25 19 37 25 49 31 37" clip-path="url(#clipPath28)" stroke="none"
+      /><polygon fill="none" points=" 25 25 19 37 25 49 31 37" clip-path="url(#clipPath28)"
+    /></g
+    ><g stroke-linecap="butt" font-size="11" transform="translate(58,333)" fill-opacity="1" fill="black" text-rendering="geometricPrecision" image-rendering="optimizeQuality" font-family="sans-serif" stroke-linejoin="round" stroke="black" stroke-opacity="1"
+    ><line y2="65" fill="none" x1="25" clip-path="url(#clipPath30)" x2="25" y1="49"
+      /><polygon points=" 25 25 19 37 25 49 31 37" clip-path="url(#clipPath30)" stroke="none"
+      /><polygon fill="none" points=" 25 25 19 37 25 49 31 37" clip-path="url(#clipPath30)"
+    /></g
+    ><g stroke-linecap="butt" font-size="11" transform="translate(115,415)" fill-opacity="1" fill="black" text-rendering="geometricPrecision" image-rendering="optimizeQuality" font-family="sans-serif" stroke-linejoin="round" stroke="black" stroke-opacity="1"
+    ><line y2="25" fill="none" x1="49" clip-path="url(#clipPath32)" x2="91" y1="25"
+      /><polygon points=" 25 25 37 31 49 25 37 19" clip-path="url(#clipPath32)" stroke="none"
+      /><polygon fill="none" points=" 25 25 37 31 49 25 37 19" clip-path="url(#clipPath32)"
+    /></g
+    ><g stroke-linecap="butt" font-size="11" transform="translate(182,1)" fill-opacity="1" fill="black" text-rendering="geometricPrecision" image-rendering="optimizeQuality" font-family="sans-serif" stroke-linejoin="round" stroke="black" stroke-opacity="1"
+    ><line y2="25" fill="none" x1="49" clip-path="url(#clipPath34)" x2="87" y1="25"
+      /><polygon points=" 25 25 37 31 49 25 37 19" clip-path="url(#clipPath34)" stroke="none"
+      /><polygon fill="none" points=" 25 25 37 31 49 25 37 19" clip-path="url(#clipPath34)"
+    /></g
+    ><g stroke-linecap="butt" font-size="11" transform="translate(182,83)" fill-opacity="1" fill="black" text-rendering="geometricPrecision" image-rendering="optimizeQuality" font-family="sans-serif" stroke-linejoin="round" stroke="black" stroke-opacity="1"
+    ><line y2="25" fill="none" x1="49" clip-path="url(#clipPath34)" x2="87" y1="25"
+      /><polygon points=" 25 25 37 31 49 25 37 19" clip-path="url(#clipPath34)" stroke="none"
+      /><polygon fill="none" points=" 25 25 37 31 49 25 37 19" clip-path="url(#clipPath34)"
+    /></g
+    ><g font-size="8" fill-opacity="1" fill="rgb(120,120,120)" text-rendering="geometricPrecision" image-rendering="optimizeQuality" stroke="rgb(120,120,120)" stroke-opacity="1"
+    ><text x="0" xml:space="preserve" y="10" stroke="none"
+      >Visual Paradigm Standard(kolja(University of Kiel))</text
+    ></g
+  ></g
+></svg
+>

BIN
schriftlich/bilder/ergebnis.jpg


BIN
schriftlich/bilder/fragebogen.pdf


BIN
schriftlich/bilder/gui_mit_ids.png


BIN
schriftlich/bilder/hauptansicht.png


BIN
schriftlich/bilder/klassenansicht.png


BIN
schriftlich/bilder/maske_von_kamera.jpg


BIN
schriftlich/bilder/maskenansicht.png


BIN
schriftlich/bilder/mvc.png


BIN
schriftlich/bilder/mvc.xcf


BIN
schriftlich/bilder/nutzer_bewertung.png


BIN
schriftlich/bilder/nutzung.png


BIN
schriftlich/bilder/objectbaum.png


BIN
schriftlich/bilder/objektzuweisung.png


BIN
schriftlich/bilder/server_return.png


BIN
schriftlich/bilder/werkzeuge.png


BIN
schriftlich/bilder/zerteiltesobjekt.png


+ 16 - 0
schriftlich/kapitel/1.5relwork.tex

@@ -0,0 +1,16 @@
+Im Internet gibt es bereits viele Anwendungen, welche zur Annotation von einzelnen Bildern oder Videos verwendet werden können.
+Zum Beispiel LabelMe~\cite{russell_labelme:_2008} erlaubt einem das Annotieren direkt im Browser vorzunehmen. 
+Dies hat jedoch den großen Nachteil, dass die Bilder hochgeladen werden müssen, was bei sehr vielen Bildern sehr viel Zeit in Anspruch nehmen würde.
+LabelMe erlaubt außerdem nur das Hochladen von 20 Bildern zurzeit.
+
+Ein weiteres Werkzeug zur Annotation von Bildern ist LabelImg\footnote{https://github.com/tzutalin/labelImg.git}.
+LabelImg unterstützt aber nur die Annotation von Boundingboxen und nicht die von Polygonen.
+
+Außerdem unterstützen die online verfügbaren Werkzeuge nicht die Annotation von Bildsequenzen (genauer in Abschnitt \ref{ch:annotationVonBildsequenzen}), 
+sondern nur die Annotation von einzelnen Bildern.
+Zu diesem Zweck wurde bisher ein internes Werkzeug verwendet, welches allerdings keine GUI beinhaltet. 
+Dieses Werkzeug muss mit Tastatur bedient werden und die Bedeutung der einzelnen Tasten muss auswendig gelernt werden.
+
+Die in dieser Arbeit vorgestellte GUI unterstützt sowohl das Annotieren von Bildsequenzen, als auch das Annotieren einzelner Bilder.
+Außerdem werden die Objekt Polygone und die Boundingboxen gespeichert.
+Daher ist diese GUI einzigartig und bietet neue Möglichkeiten Bilder zu annotieren.

+ 36 - 0
schriftlich/kapitel/1einleitung.tex

@@ -0,0 +1,36 @@
+In den vergangenen Jahren hat sich im Forschungsgebiet der Computer Vision viel getan. 
+Im Groben geht es dabei darum, verschiedene Aufgaben, welche bis heute noch nur von Menschen erledigt werden können, 
+auf Computer zu übertragen. Zum Beispiel das Fahren eines Autos kann vermutlich schon bald vollständig von einem Computer übernommen werden.
+Um diese Aufgaben erledigen zu können, müssen die Computer lernen zu sehen und müssen dabei genau wissen, was sie sehen.
+
+In dem Bereich des maschinellen Lernens gibt es verschiedene Ansätze, um einen Computer für solche Aufgaben zu trainieren. 
+Zum Beispiel können Deep Convolutional Neuronal Networks für die Klassifizierung von Bildern in verschiedene Kategorien 
+und zur Detektion von Objekten in Bildern verwendet werden~\cite{sermanet_overfeat:_2013,NIPS2012_4824}.
+Bei der Detektion von Objekten in Bildern geht es darum, verschiedene Kategorien von Objekten auf Bildern zu erkennen und zu unterscheiden.
+
+So muss ein autonomes Auto beispielsweise andere Autos, Fußgänger und Radfahrer erkennen können und darf diese dabei nicht miteinander verwechseln.
+Um einen Computer darauf zu trainieren, wird eine spezielle Trainingsmethode namens Supervised Learning verwendet.
+Dabei wird das Training wie das Lernen mit einem Lehrer gestaltet: Dem Computer wird eine Frage gestellt, woraufhin der Computer eine Antwort errechnet.
+Dann wird dem Computer die korrekte Antwort auf die Frage gegeben, so dass er anhand der Differenz zwischen der korrekten Antwort und der errechneten Antwort 
+seine Parameter so anpassen kann, dass beim nächsten Mal eine bessere Antwort errechnet werden kann.
+Um damit ein gutes Ergebnis zu erzielen, wird ein möglichst großer Datensatz an Trainingsbildern benötigt. 
+
+Bei der Detektion von Objekten in Bildern muss für die Bilder in dem zum Training verwendeten Datensatz bekannt sein, wo sich auf welchem Bild welches Objekt befindet.
+Für einige häufiger verwendete Kategorien von Objekten, gibt es bereits große Datensätze, die für das Training eines Computers verwendet werden können.
+Zum Beispiel enthält der Datensatz Microsoft Common Objects in Context (Microsoft COCO) bis zu 91 verschiedene Objektklassen des Alltags wie Fahrzeuge, 
+Tiere und Essbares~\cite{fleet_microsoft_2014}. Weitere bekannte Datensätze sind Pascal Visual Object Classes (Pascal VOC)~\cite{everingham_pascal_2010} 
+und ImageNet~\cite{jia_deng_imagenet:_2009}.
+
+Wenn es aber darum geht, Objekte aus spezielleren Kategorien zu erkennen, reichen diese Datensätze nicht aus.
+In diesem Fall müssen die Datensätze erst noch erstellt werden, das heißt, es muss eine große Menge an Bilder von Hand annotiert werden.
+
+In dieser Arbeit wird eine GUI, welche die Erstellung solcher Datensätze unterstützt, entworfen und implementiert.
+Die Arbeit entstand im Kontext eines Projektes, bei dem Pakete in einer Sortierstation mit Hilfe von Überwachungskameras live von einem Computer verfolgt wurden.
+Für diesen Zweck mussten die Trainingsbilder per Hand annotiert werden, da es keinen so großen Datensatz gab, der ausreichte um alle Arten von Paketen zu erkennen. 
+Die GUI ist daher optimiert für die Annotation von Bildsequenzen, wie sie von Überwachungskameras erzeugt werden.
+Sie bietet aber auch komfortable Features zur Annotation von Bildern ohne zeitlichen Zusammenhang an.
+
+Im folgenden Abschnitt wird das Annotieren von Bildsequenzen genauer erklärt und Unterschiede zwischen Bildsequenzen und einzelnen Bildern werden genannt.
+In Abschnitt \ref{ch:guifunktionen} und \ref{ch:implementierung} wird die in dieser Arbeit entworfene GUI vorgestellt und es werden Kernpunkte der Implementierung erläutert.
+In Abschnitt \ref{ch:ergebnis} werden die Ergebnisse einer Untersuchung zum Vergleich der GUI mit dem oben erwähnten internen Werkzeug analysiert.
+In Abschnitt \ref{ch:schluss} befindet sich eine kurze Zusammenfassung der Ergebnisse.

+ 145 - 0
schriftlich/kapitel/2annotation.tex

@@ -0,0 +1,145 @@
+Wie oben bereits erwähnt, ist die Annotation von Bildern ein notwendiger Prozess, um einem Computer beizubringen, Objekte automatisch zu erkennen.
+Je nachdem welche Aufgabe der Computer später übernehmen soll, müssen dabei verschiedene Objekte annotiert werden.
+Wenn der Computer zum Beispiel lernen soll, Menschen und Tiere zu erkennen, dann würde es nichts bringen Autos zu annotieren.
+Die verschiedenen Objektkategorien, deren Objekte annotiert werden sollen, werden Klassen genannt. 
+In diesem Beispiel würden die Klassen \glq Mensch\grq{ } und \glq Tier\grq{ } ausreichen. Je nach Anwendungsfall 
+könnte es aber auch sinnvoll sein diese Klassen noch weiter aufzuteilen.
+
+In der GUI, welche in dieser Arbeit vorgestellt wird, im nachfolgenden Text nur noch GUI genannt, 
+geht es jedoch nicht nur um die Annotation von Objekten auf Bildern, sondern auch um die Annotation von Bildsequenzen.
+Eine Bildsequenz ist eine geordnete Abfolge von Bildern, die von verschiedenen Kameras aufgenommen wurden.
+Dabei sind die Bilder zunächst nach der Kamera und dann nach dem Aufnahmenzeitpunkt geordnet.
+Diese besondere Ordnung der Bilder erleichtert den Prozess des Annotierens, 
+da sich die Objekte in den verschiedenen Bildern einer Kamera immer nur ein Stück in eine Richtung bewegen.
+Es ist für den Nutzer der GUI daher leicht, dieselben Objekte auf den Bildern wiederzuerkennen.
+Außerdem ändern sich die Objekte nur selten in ihrer Form, weshalb die Umrisse eines Objektes oft von einem Bild zum nächsten kopiert werden können.
+
+Beispiel: 
+Es sind zwei Kameras A und B vorhanden, welche alle 2 Sekunden ein Bild machen. Die Objekte, die von den Kameras erfasst werden, 
+bewegen sich zunächst durch das Sichtfeld von Kamera A und dann durch das Sichtfeld von Kamera B.
+Eine Bildsequenz dieser beiden Kameras enthält nun zuerst alle Bilder von Kamera A, in zeitlich sortierter Reihenfolge,
+und dann alle Bilder von Kamera B, welche im gleichen Zeitfenster aufgenommen wurden.
+
+Der Nutzer kann nun das erste Bild von Kamera A annotieren. 
+Dann kann der Nutzer jedes annotierte Objekt kopieren und auf dem nächsten Bild leicht verschoben oder gedreht wieder einfügen.
+
+Bei der Annotierung von einzelnen Bildern reichen bei der Objekterkennung als Trainingsdaten die Daten aus, 
+wo sich auf welchem Bild ein Objekt einer bestimmten Klasse befindet.
+Bei einer Sequenz von Bildern spielen jedoch auch andere Daten eine Rolle. Zum Beispiel ist es sinnvoll zu wissen, 
+bei welchen Objekten auf unterschiedlichen Bildern es sich um dasselbe Objekt handelt, 
+welches zu einem anderen Zeitpunkt oder aus einer anderen Blickrichtung aufgenommen wurde.
+Daher bekommt in der GUI jedes Objekt eine eindeutige ID, an welcher man das Objekt auf verschiedenen Bildern wiedererkennen kann.
+Jeder Objekt-ID wird dann eine Objektklasse zugeordnet. So ist sichergestellt, dass dasselbe Objekt nicht auf unterschiedlichen Bildern 
+unterschiedlichen Klassen zugeordnet werden kann.
+Es kann sogar vorkommen, dass dasselbe Objekt an mehreren unterschiedlichen Stellen im gleichen Bild vorkommt.
+Das passiert genau dann, wenn die Sicht auf das Objekt durch einen Gegenstand versperrt wird und das Objekt nur teilweise sichtbar ist (siehe Abbildung \ref{fig:zerteiltesobjekt}).
+\graphicsfigure{bilder/zerteiltesobjekt}{Mehrfaches Vorkommen desselben Objektes auf einem Bild}{fig:zerteiltesobjekt}{0.33\textwidth}
+Außerdem gibt es bei einer Bildsequenz für eine Kamera oft Bereiche, in denen sich kein Objekt befinden kann.
+Solche Bereiche können in der GUI mit Masken festgelegt werden.
+
+Um die Annotation zu speichern, können verschiedene Formate benutzt werden. Dabei ist es sinnvoll sich für ein Format zu entscheiden,
+welches möglichst viele andere Personen auch benutzen, da dann möglichst viele von den annotierten Bildern profitieren können.
+Im folgenden Abschnitt wird das von der GUI verwendete Format zum Speichern von Annotationen 
+und die unterstützten Formate für das Erstellen neuer annotierter Bildsequenzen erklärt.
+
+\section{Annotationsformat}\label{sec:annotationsformat}
+Damit möglichst viele Forscher die von der GUI erstellten Annotationen verwenden können, 
+wird zur Speicherung der Annotationen ein Format verwendet, welches sich sehr stark an dem Format des PascalVOC 2007 Datensatzes orientiert~\cite{pascal-voc-2007}.
+Es werden lediglich zusätzliche Informationen wie zum Beispiel Kamera und Objekt-ID gespeichert.
+Zu jeder annotierten Bildsequenz gehören dabei die vier Ordner \glq Annotations\grq, \glq SourceImages\grq, \glq SourceMasks\grq{ } und \glq JPEGImages\grq.
+Im Ordner \glq Annotations\grq{ } wird für jedes Bild eine XML Datei erstellt, 
+welche alle annotierten Objekte auf dem Bild enthält. Zusätzlich werden hier auch Informationen zu dem zugehörigen Bild 
+wie zum Beispiel Größe und Aufnahmezeitpunkt gespeichert.
+Der Inhalt einer XML Datei könnte für ein Bild mit nur einem Objekt zum Beispiel wie folgt aussehen:
+
+\lstset{language=XML}
+\begin{lstlisting}
+<?xml version="1.0" encoding="utf-8"?>
+<annotation>
+    <folder>VOC2007</folder>
+    <filename>0001.jpg</filename>
+    <source>
+        <database>The VOC2007 Database</database>
+        <annotation>PASCAL VOC 2007</annotation>
+        <image>0001.jpg</image>
+    </source>
+    <camera_id>Scan E 01</camera_id>
+    <size>
+        <width>1920</width>
+        <height>1080</height>
+        <depth>32</depth>
+    </size>
+    <segmented>0</segmented>
+    <timestamp>2017-02-07 21:07:47.275</timestamp>
+    <object>
+        <name>packingbox</name>
+        <id>1</id>
+        <pose>Unspecified</pose>
+        <truncated>0</truncated>
+        <difficult>0</difficult>
+        <bndbox>
+            <xmin>100</xmin>
+            <ymin>100</ymin>
+            <xmax>150</xmax>
+            <ymax>150</ymax>
+        </bndbox>
+        <polygon>
+            <point>
+                <x>100</x>
+                <y>100</y>
+            </point>
+            <point>
+                <x>150</x>
+                <y>100</y>
+            </point>
+            <point>
+                <x>150</x>
+                <y>100</y>
+            </point>
+        </polygon>
+    </object>
+</annotation>
+\end{lstlisting}
+
+Dabei wird der Name der Kamera in dem camera\_id Tag (im Beispiel Zeile 10) angegeben. Der Zeitpunkt, zudem das Bild aufgenommen wurde, 
+wird durch timestamp (im Beispiel Zeile 17) angegeben. Die Objekt-ID, an der die Objekte auf unterschiedlichen Bildern wiedererkannt werden, 
+wird im id Tag (im Beispiel Zeile 20) angegeben. Der Objektumriss wird im polygon Tag (im Beispiel Zeile 30-43) als Liste von Punkten angegeben, 
+welche in Pixelkoordinaten angegeben werden. Falls das Objekt mehrmals auf dem Bild zu sehen ist, 
+werden die Polygone als Liste hintereinander abgespeichert.
+
+Im Ordner \glq SourceImages\grq{ } werden die originalen Bilder der Sequenz gespeichert.
+Sie werden von der GUI benötigt, da der Nutzer auf diesen Bildern die Objekte annotiert.
+
+Im Ordner \glq SourceMasks\grq{ } wird für jede Kamera eine Maske gespeichert.
+Eine Maske ist ein schwarz-weiß Bild mit gleicher Größe wie das zugehörige originale Bild.
+Weiße Flächen in der Maske bedeuten, dass innerhalb dieser Fläche im originalen Bild ein Objekt vorkommen darf.
+Schwarze Flächen bedeuten, dass sich an diesen Stellen im originalen Bild kein Objekt befinden kann.
+
+\begin{figure}[H]
+  \centering
+  \subfigure[Source Image]{\includegraphics[width=0.25\textwidth]{bilder/bild_von_kamera.jpg}}
+  \subfigure[Source Mask]{\includegraphics[width=0.25\textwidth]{bilder/maske_von_kamera.jpg}}
+  \subfigure[JPEG Image]{\includegraphics[width=0.25\textwidth]{bilder/ergebnis.jpg}}
+  \caption{Beispiel für eine Kameramaske} 
+\end{figure}
+
+Im Ordner \glq JPEGImages\grq{ } werden die Bilder gespeichert, welche man erhält, wenn man die Masken auf die originalen Bilder anwendet.
+Dabei werden die schwarzen Flächen der Maske auf das originale Bild übertragen. 
+Die Bilder werden von der GUI generiert, sobald die Maske einer Kamera durch den Nutzer verändert wird.
+Die Bilder in diesem Ordner werden später für das Training verwendet, da hier keine Objekte außerhalb der Maske mehr vorkommen.
+
+\section{Format neuer Annotationen}\label{sec:formatNeuerAnnotationen}
+Wie oben bereits erwähnt, entstand diese GUI im Kontext einer Arbeit, 
+bei der es darum ging ein neuronales Netzwerk darauf zu trainieren, 
+automatisch Pakete durch Überwachungskameras zu verfolgen.
+Die GUI hat daher eine Funktion, die es dem Nutzer erlaubt eine Bildsequenz, wie sie von den Überwachungskameras erzeugt wird zu importieren.
+Damit die GUI eine solche Sequenz öffnen kann, müssen die Bilder in folgendem Format vorliegen: 
+\begin{enumerate}
+  \item Alle Bilder befinden sich in einem Ordner
+  \item Die Bilder sind aufsteigend nummeriert und im JPG Format gespeichert.
+  \item Es existiert eine CSV Datei mit Informationen über Aufnahmezeitpunkt und verwendeter Kamera für jedes Bild im Ordner.
+\end{enumerate}
+Falls die Bilder im obigen Format vorliegen, kann die GUI daraus automatisch eine annotierte Bildsequenz im Format aus Abschnitt \ref{sec:annotationsformat} generieren.
+Natürlich soll die GUI auch für andere Zwecke verwendet werden können.
+Sobald in dem Ordner keine CSV Datei gefunden wird, wird der Ordner von der GUI rekursiv nach Bildern durchsucht.
+Dabei werden die Namen der Unterordner als Kameranamen und das Datum der Dateierstellung als Aufnahmezeitpunkt übernommen.

+ 212 - 0
schriftlich/kapitel/3funktionen.tex

@@ -0,0 +1,212 @@
+In diesem Abschnitt der Arbeit werden die Hauptfunktionen der GUI vorgestellt und erklärt.
+Beim Design der GUI wurde besonders darauf geachtet, dass die GUI übersichtlich und leicht zu benutzen ist.
+Daher können verschiedene Funktionen sowohl mit der Tastatur, als auch über Buttons aktiviert werden, je nachdem wie es dem Nutzer am besten gefällt.
+
+Die GUI wurde zudem so entworfen, dass die Annotation möglichst automatisch erfolgen kann. Der Nutzer muss die automatischen Annotationen dann nur noch leicht anpassen.
+Das ist möglich, da im Prozess des maschinellen Lernens das verwendete unfertig trainierte System recht schnell schon Vorhersagen darüber treffen kann, wo sich auf 
+einem neuen Bild vermutlich Objekte befinden. Diese Vorhersagen werden in dieser Arbeit als Vorabannotationen bezeichnet.
+Für diesen Zweck enthält die GUI einen Client, der über ein Netzwerk (z.B. Internet) nach Vorabannotationen fragen kann.
+Dabei wird das Bild an einen Server gesendet, welcher mit dem lernenden System kommuniziert. 
+Der Server antwortet schließlich mit einem segmentierten Bild, auf dem alle Pixel mit der gleichen Farbe zu einem erkannten Objekt gehören.
+Aus diesem Bild extrahiert der Client dann die Eckpunkte der Objekte.
+Auf diese Weise muss der Nutzer der GUI nur noch die erkannten Objekte anpassen. Dafür benötigt die GUI also nicht nur Funktionen zur Erstellung von Annotationen, 
+sondern auch Funktionen zur nachträglichen Veränderung von Annotationen.
+
+Um eine leicht benutzbare GUI zu entwerfen, müssen die verschiedenen grafischen Oberflächen gut strukturiert und durchdacht sein.
+Wenn es zum Beispiel zu viele verschiedene Knöpfe oder zu viele verschiedene Oberflächen gibt, dann verliert der Nutzer sehr schnell den Überblick.
+Wenn es aber zu wenig verschiedene Oberflächen gibt, dann ist die Steuerung oft zu kompliziert, da alle Funktionen auf einer Oberfläche angeboten werden müssen.
+Die im Rahmen dieser Arbeit entworfene GUI bietet für folgende Funktionen eigene Oberflächen:
+
+\begin{compactenum}[(i)]
+ \item Das Erstellen und Verändern von annotierten Objekten (Hauptoberfläche \ref{sec:hauptansicht})
+ \item Das Erstellen von Masken für die Kameras (Maskenoberfläche \ref{sec:maskenansicht})
+ \item Das Zuweisen von Objekt-IDs für dieselben Objekte auf unterschiedlichen Bildern (Objekt-Zuweisungsoberfläche \ref{sec:objektZuweisungsansicht})
+ \item Das Verwalten von verschiedenen Objektklassen, welche in einer Sequenz annotiert werden sollen (Klassenoberfläche \ref{sec:klassenAnsicht})
+\end{compactenum}
+In den folgenden Abschnitten wird auf das Design dieser Oberflächen näher eingegangen und es werden ihre Funktionalitäten erläutert.
+
+\section{Hauptoberfläche}\label{sec:hauptansicht}
+Dies ist die Oberfläche, die der Nutzer beim Starten der Anwendung zu Gesicht bekommt.
+Ihre volle Funktionalität erhält die Oberfläche jedoch erst, 
+sobald der Nutzer eine Sequenz zum Annotieren geladen hat.
+Im oberen Bereich befindet sich dafür eine Leiste mit Befehlen, wie zum Beispiel das Laden und Speichern einer Sequenz.
+Auch einige Anzeigeoptionen können über diese Leiste gesteuert werden.
+Die Leiste wurde dort platziert, weil es bei vielen anderen GUIs genauso gemacht wird (zum Beispiel bei den OpenOffice Anwendungen). 
+Der Nutzer ist es also schon durch andere Anwendungen gewohnt in diesem Bereich eine solche Leiste vorzufinden. 
+Die Oberfläche darunter ist in drei weitere Oberflächen aufgeteilt:
+\begin{compactenum}[(i)]
+\item Eine Arbeitsfläche \ref{sec:arbeitsansicht}
+\item Einen Navigationsbaum \ref{sec:objectBaum}
+\item Ein Hilfstext \ref{sec:hilfstext}
+\item Eine Werkzeugpalette \ref{sec:werkzeuge}
+\end{compactenum}
+
+Die Arbeitsfläche nimmt den größten Teil der Hauptoberfläche ein, da hier die Bilder angezeigt werden, welche annotiert werden sollen.
+Dabei sollen auch große originale Bilder noch gut erkennbar sein.
+Links oben befindet sich die Werkzeugpalette, damit der Nutzer immer sehen kann welches Werkzeug momentan aktiviert ist und welche Werkzeuge er zur Verfügung hat.
+Links unten befindet sich der Navigationsbaum, mit dem der Nutzer eine Übersicht über alle Bilder und Kameras der geöffneten Sequenz erhält.
+Rechts unten befindet sich ein Hilfstext, der dem Nutzer eine Hilfestellung zum ausgewählten Werkzeug gibt.
+Ein Bild hiervon befindet sich in Abbildung \ref{fig:hauptansicht}.
+
+\graphicsfigure{bilder/hauptansicht}{Die Hauptansicht der GUI}{fig:hauptansicht}{0.75\textwidth}
+
+\section{Arbeitsfläche}\label{sec:arbeitsansicht}
+Die Arbeitsfläche ist die Fläche, mit der der Nutzer am meisten interagieren muss, da hier viele Annotationsprozesse stattfinden.
+Je nach dem welches Werkzeug aus der Werkzeugpalette ausgewählt ist, kann der Nutzer hier neue Objekte erstellen, 
+einzelne Eckpunkte verschieben oder löschen oder ganze Objekte kopieren.
+Damit der Nutzer das korrekt tun kann, wird hier im Hintergrund das Bild aus der Sequenz angezeigt, welches gerade annotiert wird.
+Auf dem Bild werden die Umrisse und Eckpunkte annotierter Objekte angezeigt. 
+Dabei ist es möglich die Eckpunkte einzelner Objekte auf dem Bild auszublenden, 
+um einen besseren Überblick über die anderen Objekte zu erhalten. 
+
+Die Interaktion mit der Arbeitsfläche findet überwiegend mit der Maus statt. 
+Dabei wird je nach ausgewähltem Werkzeug in grün eine Vorschau angezeigt, damit der Nutzer sehen kann, was er ändern würde.
+Auf diese Weise können viele Fehler, die beim Annotieren auftreten könnten, verhindert werden, wodurch viel Zeit erspart werden kann.
+In Abbildung \ref{fig:arbeitsansicht} ist die Arbeitsfläche mit der Vorschau für das Einfügen eines Objektes zu sehen.
+
+\graphicsfigure{bilder/arbeitsansicht}{Die Arbeitsfläche der GUI}{fig:arbeitsansicht}{0.50\textwidth}
+
+\section{Navigationsbaum}\label{sec:objectBaum}
+Der Navigationsbaum dient dazu, dem Nutzer einen Überblick über die gesamte Sequenz zu verschaffen, die er momentan annotiert.
+Dafür wird die Sequenz als aufklappbarer Baum angezeigt. Dadurch weiß der Nutzer, welche Bilder zu welcher Kamera gehöhren.
+Durch einen Klick auf ein Bild kann der Nutzer das Bild in der Arbeitsfläche anzeigen lassen und es anschließend annotieren.
+Das momentan ausgewählte Bild ist in dem Baum markiert und die Objekte auf dem Bild werden in dem Baum angezeigt.
+
+Des Weiteren zeigt der Objektbaum dem Nutzer an, welche Bilder noch nicht fertig annotiert wurden, und welche Bilder noch nie betrachtet worden sind.
+Bilder, die noch nicht betrachtet wurden, werden mit dunkelgrauem Hintergrund angezeigt.
+Bilder, die noch nicht fertig annotiert sind bekommen auf der linken Seite ein Warndreieck.
+Dabei wird ein Bild von der GUI genau dann als unfertig annotiert betrachtet, wenn noch keine Objekt-IDs vergeben wurden, 
+oder wenn die gleiche ID ungewollt mehreren Objekten zugewiesen wurde.
+In Abbildung \ref{fig:objektbaum} ist ein Beispiel mit noch nicht annotierten Bildern und unfertig annotierten Bildern zu sehen.
+\graphicsfigure{bilder/objectbaum}{Der Navigationsbaum der GUI}{fig:objektbaum}{0.50\textwidth}
+
+Mit Hilfe der rechten Maustaste kann der Nutzer auf dem Navigationsbaum ein Menü erscheinen lassen.
+Dieses Menü zeigt dem Nutzer, je nach dem auf welchem Objekt der Klick erfolgt ist, spezifische Operationen.
+Beim Klicken auf eine Kamera kann der Nutzer die Maskenoberfläche \ref{sec:maskenansicht} aufrufen und so die Maske der Kamera verändern.
+Beim Klicken auf ein Bild kann der Nutzer manuell Vorabannotationen vom Annotationsserver abfragen.
+Beim Klicken auf ein Objekt kann der Nutzer die Objekt-ID und Objektklasse festlegen oder das Objekt löschen.
+
+\section{Hilfstext}\label{sec:hilfstext}
+Unter der Arbeitsfläche wird in der GUI immer ein Hilfstext eingeblendet, 
+der den Nutzer darüber informiert, auf welche Weise er die Arbeitsfläche mit dem ausgewählten Werkzeug bedienen kann.
+Dies erleichtert die Bedienung der GUI für unerfahrene Nutzer.
+Auch kann dadurch Zeit eingespart werden, da die unerfahrenen Nutzer einfach nur lesen müssen und nicht erst einen erfahrenen Nutzer fragen müssen.
+
+\section{Werkzeugpalette}\label{sec:werkzeuge}
+Die Werkzeugpalette dient dem Nutzer zur Auswahl der verschiedenen Werkzeuge. 
+Dabei bietet jedes Werkzeug dem Nutzer eine andere Möglichkeit die Annotation einer Bildsequenz zu bearbeiten order zu erstellen.
+Das momentan ausgewählte Werkzeug wird dabei mit grauem Hintergrund markiert.
+Jedes Werkzeug hat ein Icon, mit dem es dargestellt wird.
+Außerdem hat jedes Werkzeug einen Namen, der erscheint, falls der Nutzer über einen längeren Zeitraum mit der Maus darüber verweilt.
+In Abbildung \ref{fig:werkzeuge} ist ein Bild der Wergzeugpalette.
+Im Folgenden werden die verschiedenen Werkzeuge kurz vorgestellt.
+\graphicsfigure{bilder/werkzeuge}{Werkzeuge der GUI}{fig:werkzeuge}{0.50\textwidth}
+  
+\subsection{Vergrößern}\label{subsec:vergroessern}
+Dieses Werkzeug ermöglicht es dem Nutzer das momentan angezeigte Bild in der Arbeitsansicht zu vergrößern, 
+so dass bestimmte Details besser erkennbar werden. Dadurch können die Objekte dann viel genauer annotiert werden.
+Der Nutzer kann sich den Bereich, den er vergrößert sehen möchte, dabei selbst aussuchen, indem er diesen Bereich in der Arbeitsfläche mit der linken Maustaste markiert.
+
+\subsection{Verkleinern}\label{subsec:verkleinern}
+Dies ist das Gegenstück zum Vergrößern. Wenn man das Bild mit dem Vergrößern-Werkzeug vergrößert hat, 
+kann man es mit diesem Werkzeug wieder verkleinern. Dabei wird es mit nur einem Klick auf das Werkzeug so verkleinert, 
+dass wieder alles komplett sichtbar ist. Dieses Werkzeug bleibt nach der Benutzung nicht aktiviert, 
+da der Nutzer das Bild nur einmal verkleinern braucht, damit wieder alles sichtbar ist.
+
+\subsection{Nächstes Bild}\label{subsec:nächstesBild}
+Dieses Werkzeug erlaubt es, zum nachfolgenden Bild in der Sequenz zu wechseln.
+Genau wie beim Verkleinern-Werkzeug ist dies eine einmalige Operation, so dass das Werkzeug nicht aktiviert bleibt und direkt nach der 
+Benutzung wieder das vorherige Werkzeug aktiviert ist.
+
+\subsection{Vorheriges Bild}\label{subsec:vorherigesBild}
+Mit diesem Werkzeug ist es möglich, zum vorherigen Bild in der Sequenz zu wechseln. 
+Die Werkzeuge Nächstes Bild und Vorheriges Bild können auch über die Pfeiltasten aktiviert werden.
+Auch dieses Werkzeug ist eine einmalige Operation.
+
+\subsection{Objekt zerteilen}\label{subsec:objektZerteilen}
+Durch die Vorabannotationen kann es vorkommen, dass zwei verschiedene Objekte fälschlicherweise als ein einzelnes Objekt erkannt werden.
+In diesem Fall ist es hilfreich, wenn ein Objekt in zwei Objekte zerteilt werden kann.
+Dafür gibt es dieses Werkzeug. Ist dieses Werkzeug ausgewählt, so kann der Nutzer in der Arbeitsfläche zwei 
+Eckpunkte eines Objektes auswählen, an denen es zerteilt werden soll.
+
+\subsection{Löschen}\label{subsec:löschen}
+Mit diesem Werkzeug ist es möglich einzelne Eckpunkte oder ganze Objekte zu löschen.
+Dafür kann man in der Arbeitsfläche einen Bereich markieren, in dem alle Eckpunkte gelöscht werden sollen. 
+Entstehen dadurch Objekte mit weniger als 3 Eckpunkten, werden diese Objekte entfernt, da ein Objekt mit nur zwei Ecken kein sinnvolles Objekt darstellt.
+
+\subsection{Verschieben}\label{subsec:verschieben}
+Mit diesem Werkzeug ist es möglich die Eckpunkte der Objekte in der Arbeitsansicht zu verschieben, 
+oder neue Eckpunkte einzufügen. Dabei wird der Eckpunkt, der verschoben wird, grün markiert. Befindet sich die Maus nicht nah genug an einem Eckpunkt, 
+so wird an dem nächstgelegenen Randpunkt eines Objektes ein neuer Eckpunkt hinzugefügt, der dann gleich verschoben werden kann.
+
+\subsection{Neues Objekt}\label{subsec:newObject}
+Dieses Werkzeug dient dazu neue Objekte zu annotieren. Dafür kann der Nutzer jeden Eckpunkt eines Objektes der Reihenfolge nach anklicken. 
+Um das neue Objekt zu vollenden kann der Nutzer einmal auf den Startpunkt klicken. 
+Während dieses Prozesses wird eine Vorschau des Polygons des neuen Objektes mit grün gestrichelten Linien angezeigt, damit der Nutzer einen Fehler schnell erkennen kann.
+Falls ein Eckpunkt unabsichtlich gesetzt wurde, kann er mit der rechten Maustaste wieder entfernt werden.
+
+\subsection{Kopieren und Einfügen}\label{subsec:kopyAndPaste}
+Mit diesem Werkzeug können Objekteigenschaften bildübergreifend kopiert und eingefügt werden. 
+Dafür kann der Nutzer in der Arbeitsansicht zunächst ein Objekt auswählen, welches kopiert werden soll. 
+Auf einem anderen Bild der Sequenz kann der Nutzer dann andere Objekte auswählen, 
+die die Eigenschaften des kopierten Objektes übernehmen sollen. So lässt sich einfach die 
+Objekt-ID und die Klasse der Objekte auf nachfolgenden Bildern festlegen. Der Nutzer kann auch das gesamte kopierte Objekt in ein 
+anderes Bild einfügen, indem er dort auf eine leere Stelle klickt, wo sich das Objekt befinden soll. 
+Während dieses Vorgangs kann der Nutzer auch das kopierte Objekt mit der rechten Maustaste drehen und es so den Bewegungen anpassen, die das Objekt in der Bildsequenz macht.
+Durch dieses Werkzeug kann der Nutzer viel Zeit sparen, da nicht jedes Objekt auf jedem Bild neu annotiert werden muss.
+
+\subsection{Verstecken}\label{subsec:verstecken}
+Mit diesem Werkzeug kann man Objekte verstecken, so dass sie von den anderen Werkzeugen ignoriert werden. 
+Außerdem können versteckte Objekte wieder sichtbar gemacht werden. 
+Versteckte Objekte werden in der Arbeitsansicht ausgegraut angezeigt. 
+Die Eckpunkte von versteckten Objekten werden nicht angezeigt.
+Dieses Werkzeug ist vor allem dann nützlich, wenn sich mehrere Objekte direkt nebeneinander befinden, so dass es schwierig ist die Eckpunkte auseinanderzuhalten.
+
+\section{Maskenoberfläche}\label{sec:maskenansicht}
+Diese Oberfläche dient dem Nutzer dazu Masken für Kameras zu erstellen. 
+Sie kann über den Objektbaum für eine bestimmte Kamera aufgerufen werden. 
+In dieser Oberfläche kann der Nutzer schwarze oder weiße Polygone zeichnen, 
+wobei in den schwarzen Bereichen später keine Objekte vorkommen dürfen. 
+
+Beim Öffnen der Ansicht, wird die aktuelle Maske der Kamera geladen und angezeigt. 
+Existiert noch keine Maske für die ausgewählte Kamera, wird eine komplett weiße Maske erstellt, da standardmäßig überall auf einem Bild Objekte sein könnten.
+Um dem Nutzer das Zeichnen der Maske zu vereinfachen, wird im Hintergrund der Ansicht das erste 
+Bild der Kamera aus der Sequenz angezeigt. Der Nutzer braucht also nur noch den Bereich des 
+Bildes mit schwarzen Polygonen zu markieren, auf dem sich keine Objekte befinden können.
+
+Die Angabe von Masken ist vor allem wegen den Vorabannotationen hilfreich. 
+Ohne eine Maske müssten auf vielen Bildern immer wieder die gleichen fälschlicherweise erkannten Objekte per Hand gelöscht werden.
+Mit einer Maske geschieht dies meist automatisch und es kann viel Zeit erspart werden.
+In Abbildung \ref{fig:maskenansicht} befindet sich ein Bild der Maskenoberfläche.
+\graphicsfigure{bilder/maskenansicht}{Maskenoberfläche der GUI}{fig:maskenansicht}{0.50\textwidth}
+
+\section{Objekt-Zuweisungsoberfläche}\label{sec:objektZuweisungsansicht}
+Neben dem \glq Kopieren und Einfügen\grq{ } Werkzeug, kann man die Objekt-ID eines Objektes auch mit der 
+Objekt-Zuweisungsoberfläche anpassen. Diese Oberfläche ist notwendig, 
+da neu vorkommende Objekte zu Beginn eine ID zugewiesen bekommen müssen und diese nicht von anderen Objekten kopiert werden kann. 
+Außerdem erleichtert die Oberfläche das Zuweisen der Objekt-IDs nach einem Kamerawechsel, 
+da hier weit auseinanderliegende Bilder einer Sequenz miteinander verglichen werden müssen.
+Die Objekt-Zuweisungoberfläche bietet dem Nutzer daher die Möglichkeit das Objekt mit allen bereits bekannten 
+Objekten zu vergleichen und so das passende Objekt zu finden.
+
+Für diesen Zweck ist die Ansicht in zwei Teile geteilt. Links befindet sich das Objekt, 
+dem die richtige ID zugewiesen werden soll und rechts befinden sich andere Objekte aus vorherigen 
+Bildern zum Vergleich. Der Nutzer kann rechts ein Objekt auswählen, 
+welches er vergleichen will. Dazu gibt es eine Liste mit allen Objekt-IDs. 
+Wird eine dieser Objekt-IDs ausgewählt, so wird das Objekt auf dem letzten Bild mit der 
+ID zum Vergleich angezeigt. 
+
+Da die Bilder einer Bildsequenz unscharf sein können, kann es vorkommen, dass das Objekt, welches als Vergleich angezeigt wird, nicht gut zu erkennen ist.
+Für diesen Fall gibt es zwei Knöpfe, um durch die Sequenz zu navigieren. Der Eine bewirkt dabei, dass das ausgewählte 
+Objekt aus einem Bild weiter vorne in der Sequenz zum Vergleich angezeigt wird. 
+Der Andere zeigt das Objekt auf einem Bild weiter hinten in der Sequenz an.
+In Abbildung  \ref{fig:objektzuweisung} befindet sich ein Bild der Objekt-Zuweisungsoberfläche.
+\graphicsfigure{bilder/objektzuweisung}{Objekt-Zuweisungsoberfläche der GUI}{fig:objektzuweisung}{0.50\textwidth}
+
+\section{Klassenoberfläche}\label{sec:klassenAnsicht}
+Diese Oberfläche dient zur Übersicht und zur Bearbeitung der verschiedenen Annotationsklassen, deren Objekte in der Sequenz annotiert werden sollen. 
+Sie kann vom Nutzer im Menü der GUI unter der Kategorie Optionen geöffnet werden.
+Die Oberfläche zeigt eine Liste mit allen Klassen an, die in der Sequenz vorkommen. 
+Der Nutzer kann eine Klasse umbenennen, neue Klassen hinzufügen oder Klassen löschen.
+Beim Löschen einer Klasse erhalten alle Objekte dieser Klasse automatisch die Klasse zugewiesen, welche sich in der Liste ganz oben befindet.
+\graphicsfigure{bilder/klassenansicht}{Klassenoberfläche der GUI}{fig:klassenAnsicht}{0.50\textwidth}

+ 103 - 0
schriftlich/kapitel/4implementierung.tex

@@ -0,0 +1,103 @@
+In diesem Abschnitt der Arbeit wird die GUI aus Implementationssicht betrachtet und es wird erläutert, wie genau die einzelnen Features, die in 
+Abschnitt \ref{ch:guifunktionen} erwähnt wurden, umgesetzt worden sind.
+Bei der Implementation einer Anwendung ist es üblich für bestimmte Bereiche fertige Bibliotheken zu verwenden. 
+Auf diese Weise muss nicht jeder Entwickler immer wieder den gleichen Code schreiben und es wird viel Zeit gespart.
+Des Weiteren ist es üblich den Code gut zu strukturieren, damit er auch später noch gut verstanden und verändert werden kann.
+Hierfür verwendet man bestimmte Architektur- und Entwurfsmuster, welche sich im Laufe der Jahre als vorteilhaft etabliert haben.
+In den folgenden Abschnitten werden die verwendeten Bibliotheken und Entwurfs- und Architekturmuster vorgestellt.
+
+\section{Verwendete Bibliotheken}\label{sec:bibliotheken}
+Für die Implementierung der GUI wurde die objektorientierte Programmiersprache C++~\cite{perry1996introduction} gewählt. 
+Objektorientierte Programmiersprachen bieten den Vorteil, dass das Programm besser strukturiert werden kann, da man es in verschiedene Klassen aufteilen kann.
+
+Für die Gestaltung der GUI Oberflächen wurde das Qt-Framework\footnote{http://doc.qt.io/qt-5/index.html} verwendet. Dies bietet den Vorteil, 
+dass es plattformübergreifend ist und daher nicht für jedes Betriebssystem neuer Code geschrieben werden muss.
+Das Qt-Framework bietet außerdem viele praktische Funktionen um eine GUI zu entwerfen.
+Als Entwicklungsumgebung wurde der in Qt enthaltene QtCreator verwendet. 
+Dieser bietet speziell für das Qt-Framework ausgelegte Funktionen, die es einem Entwickler erheblich erleichtern eine Anwendung mit dem Qt-Framework zu entwickeln.
+Der QtCreator erleichtert den Entwurf einer GUI vor allem durch die Möglichkeit diese grafisch zu erstellen. 
+Es muss also nicht jedes GUI Element erst aufwendig im Quellcode erstellt werden, sondern kann in einem grafischen GUI Editor platziert werden.
+
+Für die Kommunikation mit dem oben erwähnten Server, welcher die Vorabannotationen bereitstellt, 
+wurde die ZeroMQ Bibliothek\footnote{http://zguide.zeromq.org/page:all} verwendet.
+ZeroMQ ist eine bekannte Bibliothek für die Kommunikation zwischen verschiedenen Anwendungen. 
+ZeroMQ läuft auf den meisten modernen Betriebssystemen und ist zudem einfach und schnell zu benutzen~\cite{Dworak:1391410}.
+
+Für das Arbeiten mit Bildern, wie zum Beispiel für die Extraktion der Objekt-Polygone aus einem Bild, wurde die OpenCV Bibliothek\footnote{https://opencv.org/} verwendet.
+OpenCV steht für Open Source Computer Vision und bietet viele nützliche Algorithmen für Bild- und Videoanalyse an~\cite{culjak2012brief}.
+
+Für das Lesen und Speichern der Annotationen im XML Format wurde TinyXML-2\footnote{http://leethomason.github.io/tinyxml2/} verwendet. 
+TinyXML-2 ist eine kleine Bibliothek zum Laden und Speichern von XML Dateien in C++, welche einfach einzubinden ist.
+
+\section{Architekturmuster}\label{sec:muster}
+Bei der Implementierung der GUI wurde besonders darauf geachtet, dass die Programmstruktur gut wartbar und veränderbar ist.
+Hierfür wurde das Model-View-Controller Architekturmuster~\cite{deacon2009model} verwendet.
+Dabei wird zwischen der Datenhaltung (Model), der Darstellung (View) und den Schnittstellen für die Nutzereingaben (Controller) unterschieden. 
+Abbildung \ref{fig:mvc} zeigt eine grafische Darstellung des Model-View-Controller Architekturmusters.
+Hierbei sind die Klassen für Model, View und Controller abstrakt, so dass es verschiedene Umsetzungen von ihnen in einer Anwendung geben kann.
+Das hat den Vorteil, dass man ohne viel Aufwand verschiedene Views implementieren kann, welche die gleichen Daten unterschiedlich darstellen. 
+Auch kann man verschiedene Controller implementieren, welche die Nutzereingaben unterschiedlich verarbeiten und die Daten auf andere Weise ändern.
+Auf diese Weise erhält man eine hohe Flexibilität des Codes.
+Durch die Aufteilung des Codes in die drei Bereiche ist der Code außerdem gut strukturiert und kann leicht außeinandergehalten werden.
+Wenn zum Beispiel die Oberfläche der Anwendung angepasst werden soll, muss auch nur Code in diesem Bereich verändert werden.
+\graphicsfigure{bilder/mvc}{Model-View-Controller}{fig:mvc}{0.50\textwidth}
+
+Im Folgenden wird beschrieben wie das Model-View-Controller Architekturmuster zur Implementierung der GUI eingesetzt wurde.
+
+\subsection{Datenhaltung}\label{sub:datenhaltung}
+Im Wesentlichen gibt es in der GUI zwei unterschiedliche Model-Klassen.
+Die Eine, das Arbeitsmodel, enthält lediglich Daten, die für die korrekte Darstellung der Arbeitsfläche notwendig sind, aber nicht zur geöffneten Bildsequenz gehören.
+Dazu gehört beispielsweise ein unvollendetes Polygon, welches vom Nutzer erst noch fertig erstellt wird, bis es schließlich zu einem neuen Objekt in der Sequenz wird 
+(mit dem \glq Neues Objekt\grq{ } Werkzeug aus Abschnitt \ref{subsec:newObject}).
+Auch wird dort ein Objekt gespeichert, welches vom Nutzer kopiert wurde, aber noch nicht eingefügt wurde (mit dem \glq Kopieren und Einfügen\grq{ } Werkzeug aus Abschnitt \ref{subsec:kopyAndPaste}).
+
+\svgfigure{bilder/datenhaltung}{Klassendiagramm der Datenhaltung-Klassen für eine Bildsequenz}{fig:datenhaltung}
+Die andere Model-Klasse, die Sequenz Klasse, enthält alle Daten der geöffneten Bildsequenz. 
+Dafür benutzt sie wegen der Vielzahl an verschiedenen Daten in einer Sequenz einige weitere Hilfsklassen.
+In Abbildung \ref{fig:datenhaltung} befindet sich dazu ein gekürztes UML (Unified Modeling Language) Klassendiagramm.
+
+Da die GUI dazu verwendet werden soll große Bildsequenzen mit mehreren tausend Bildern zu annotieren, 
+ist es nicht möglich alle Bilder gleichzeitig in den Arbeitsspeicher zu laden.
+Daher lädt die GUI zunächst nur die Pfade zu den Bild-Dateien, und lädt die Bilder nur, wenn sie gebraucht werden.
+Dazu gibt es eine Frame Klasse, welche für das Laden von Bildern zuständig ist. Außerdem enthält sie eine Liste mit Objekt-Polygonen zu einem Bild.
+Des Weiteren gibt es eine Mask Klasse, welche für das Laden und Speichern der Masken Bilder zuständig ist.
+Zur Verwaltung dieser Klassen gibt es eine Kamera Klasse, welche eine Liste von Frame Objekten, ein Mask Objekt und den Namen einer Kamera enthält.
+Die verschiedenen Kamera Objekte werden von der Sequenz Klasse verwaltet.
+
+Zusätzlich enthält die Sequenz Klasse auch noch eine Liste mit allen Objektklassen und eine Liste mit allen in der Sequenz vorkommenden Objekten.
+Letzteres wurde dabei aus Performance Gründen hinzugefügt. Wenn festgestellt werden soll, ob ein bestimmtes Objekt existiert, 
+müssen so nicht immer alle Kameras und Bilder durchsucht werden. Das wird zum Beispiel von der Objekt-Zuweisungsoberfläche aus Abschnitt 
+\ref{sec:objektZuweisungsansicht} benötigt, in der eine Liste mit allen vorhandenen Objekten angezeigt wird.
+Zwei Views, welche die Sequenz Klasse als Model verwenden, sind der Navigationsbaum aus Abschnitt \ref{sec:objectBaum} 
+und die Arbeitsfläche aus Abschnitt \ref{sec:arbeitsansicht}. Letztere greift dabei hauptsächlich auf die Frame Klasse zu, welche das aktuell ausgewählte Bild enthält.
+
+\subsection{Controller}\label{sub:controller}
+Im Wesentlichen ist in der GUI immer ein Haupt-Controller aktiv, welcher für die Auswahl der Werkzeuge und für Aktionen wie zum Beispiel das 
+Laden einer Sequenz in der Hauptansicht \ref{sec:hauptansicht} zuständig ist.
+Dieser Controller aktiviert dann je nach ausgewähltem Werkzeug andere Controller, 
+die nur für die Interaktion des Nutzers mit der Arbeitsfläche aus Abschnitt \ref{sec:arbeitsansicht} zuständig sind.
+Dafür erben diese Controller von einer ArbeitsController Klasse, die bereits über grundlegende Funktionalitäten verfügt, welche bei allen Controllern gleich sind.
+Dazu gehört zum Beispiel die Möglichkeit mit dem Mausrad näher ran oder weiter weg zu zoomen.
+Die erbenden Controller erweitern diese Funktionen, so dass sie die Funktionen von jeweils einem Werkzeug anbieten.
+Eine Ausnahmen ist dabei das \glq Kopieren und Einfügen\grq{ } Werkzeug, welches in zwei Controller, dem CopyController und dem PasteController, aufgespalten ist.
+Dies ist als UML Klassendiagramm in Abbildung \ref{fig:controller} verkürzt dargestellt.
+\svgfigure{bilder/controller}{Klassendiagramm der Controller-Klassen}{fig:controller}
+
+\subsection{Entwurfsmuster}\label{sub:entwurfsmuster}
+Bei der Implementierung der GUI wurden neben dem Model-View-Controller Architekturmuster auch noch einige Entwurfsmuster eingesetzt.
+Entwurfsmuster werden dafür eingesetzt, wiederkehrende Probleme beim Design von Software zu lösen. 
+Dabei wird beim Entwurf nach einem bestimmten Muster vorgegangen, welches sich für eine bestimmte Problemstellung bewährt hat.
+
+Wie oben bereits erwähnt, ist die Datenhaltung in der GUI ein komplexes System von Klassen, die miteinander interagieren.
+Der Navigationsbaum der GUI, welcher mit Hilfe des Qt-Frameworks implementiert wurde, 
+braucht Zugriff auf dieses komplexe System, damit er die Daten korrekt darstellen kann.
+Um dies effizient zu gewährleisten, wurde das Fassade Entwurfsmuster eingesetzt.
+Bei diesem wird eine Klasse als Schnittstelle für ein komplexes System eingesetzt~\cite{gamma2011entwurfsmuster}.
+
+Das Fassade Entwurfsmuster sorgt für eine gute Strukturierung des Codes, da die Anwendung einfacher in ihre verschiedenen Komponenten zerlegt werden kann.
+
+Ein weiteres verwendetes Entwurfsmuster ist das Singleton Entwurfsmuster. Bei diesem Entwurfsmuster geht es darum sicherzustellen, 
+dass zur Laufzeit von einer Klasse immer nur ein einziges Objekt existiert~\cite{gamma2011entwurfsmuster}. 
+Dieses Entwurfsmuster wurde bei der GUI für die Controller-Klassen aus Abbildung \ref{fig:controller} verwendet.
+Durch die Verwendung des Singleton Entwurfsmusters an dieser Stelle wird sichergestellt, 
+dass immer nur ein einzelnes Controller Objekt von einer Klasse existiert. Dies ist sinnvoll, weil die Nutzereingaben nicht mehrfach verarbeitet werden sollen.

+ 175 - 0
schriftlich/kapitel/5ergebnis.tex

@@ -0,0 +1,175 @@
+Um den Nutzen der oben vorgestellten GUI zu belegen, wurde im Verlauf dieser Arbeit eine Untersuchung durchgeführt.
+Die Untersuchung verglich die GUI mit einem internen Werkzeug zur Annotation von Bildsequenzen. 
+Das interne Werkzeug wurde bis zur Fertigstellung dieser Arbeit für die Annotation von Bildsequenzen verwendet, 
+und soll nun durch die vorgestellte GUI abgelöst werden.
+
+Das interne Werkzeug besteht aus verschiedenen Python Scripten, welche aufwendig per Hand über die Konsole gestartet werden müssen.
+Es ist dabei darauf beschränkt Packete zu annotieren und lässt keine weiteren Objektklassen zu. 
+Außerdem ist es nicht möglich bereits annotierte Objekte nachträglich zu verändern.
+Dieses interne Werkzeug zeigt dem Nutzer immer ein Bild, welches er in verschiedenen Modis bearbeiten kann.
+Dabei muss der Nutzer viele Tastenbefehle auswendig kennen, da keine GUI mit Knöpfen vorhanden ist.
+
+In diesem Kapitel werden der Ablauf und die Auswertung der Tests, sowie die Reaktion auf die Tests beschrieben.
+
+\section{Ablauf der Tests}\label{sec:ablauf}
+Um zu belegen, dass mit der neuen GUI bessere Ergebnisse erzielt werden können,
+wurden 12 freiwillige Testpersonen (Studenten) herangezogen. 
+Diese wurden zunächst in beide Anwendungen eingeführt, 
+dann annotierten sie zehn Minuten lang eine Bildsequenz mit dem internen Werkzeug.
+Daraufhin annotierten sie dieselbe Bildsequenz zehn Minuten lang mit der in dieser Arbeit vorgestellten GUI. 
+Die Bildsequenzen, die für die Tests verwendet wurden, bestanden jeweils aus einer Kamera mit 50 Bildern.
+Dies wurde so gewählt, weil für das Annotieren einer Sequenz mit mehreren Kameras mehr Zeit benötigt wird als für das Annotieren einer Sequenz mit nur einer Kamera.
+Die Tests sollten aber nicht zu viel Zeit in Anspruch nehmen, da sich sonst weniger Testpersonen freiwillig gemeldet hätten.
+
+Am Ende erhielten die Testpersonen einen Fragebogen zur Bewertung der beiden Anwendungen.
+Während die Testpersonen die Bildsequenzen annotierten wurde gemessen, wie viele Objekte sie mit der GUI und wie viele sie mit dem internen Tool in der Zeit annotieren konnten.
+Bei der GUI wurde auch die Nutzung der unterschiedlichen Werkzeuge protokolliert, so dass sich feststellen lässt welche Werkzeuge am häufigsten genutzt wurden.
+
+\section{Fragebogen}\label{sec:fragebogen}
+Mit Hilfe eines Fragebogens sollte bei der Untersuchung die persönliche Meinung der Testpersonen ermittelt werden.
+Dies war notwendig, weil die persönliche Meinung nicht automatisch gemessen werden kann, wie zum Beispiel die Annotationsgeschwindigkeit.
+Der Fragebogen enthielt sowohl Fragen zum direkten Vergleich der beiden Anwendungen, 
+als auch Fragen zur Bewertung von Funktionen, welche nur in der hier vorgestellten GUI vorhanden waren. Dazu gehört zum Beispiel das Benutzen von verschiedenen Objektklassen.
+Des Weiteren gab es Freitextfelder, wo die Testpersonen die vorhandenen Features loben oder kritisieren konnten und Vorschläge für weitere Features liefern konnten.
+Der vollständige Fragebogen befindet sich im Anhang.
+
+\section{Auswertung}\label{sec:auswertung}
+Im Folgenden werden die Untersuchungsergebnisse beschrieben. Außerdem werden die Ergebnisse, die mit der GUI erzielt wurden, mit denen des internen Werkzeugs verglichen.
+
+\subsection{Anzahl annotierter Objekte}\label{subsec:annotierteObjekte}
+In Abbildung \ref{fig:annotierteObjekte} wird die Anzahl der im Durchschnitt von den Testpersonen annotierten Objekte verglichen.
+Dabei wird zwischen annotierten Objekten mit zugewiesener Objekt-ID und zwischen annotierten Objekten ohne Objekt-ID unterschieden.
+Es hat sich herausgestellt, dass mit der GUI insgesamt etwa doppelt so viele Objekte annotiert wurden wie mit dem internen Werkzeug.
+Dies lässt sich auch bei der durchschnittlichen Anzahl annotierter Bilder beobachten.
+Es wurde aber nur etwa der Hälfte der in der GUI annotierten Objekte eine ID zugewiesen. 
+Die Anzahl der annotierten Objekte ohne ID ist bei dem internen Werkzeug geringer, 
+da hier alle Objekte per Hand annotiert werden mussten. 
+Bei der GUI wurden die Objekte als Vorabannotation automatisch erkannt, 
+so dass nur noch eine ID zugewiesen werden musste.
+
+Aus dem Ergebnis lässt sich schließen, 
+dass das Zuweisen der Objekt-ID in der GUI noch verbessert werden kann, 
+da es vermutlich mehr Zeit in Anspruch genommen hat als das Zuweisen der Objekt-ID im internen Werkzeug.
+Dennoch wurden in der GUI auch mehr Objekte mit ID annotiert als mit dem internen Werkzeug, 
+da die Nutzer hier mehr Zeit für das Zuweisen der Objekt-IDs hatten als beim internen Werkzeug.
+Bei dem Annotieren einer großen Sequenz lohnt es sich also die GUI zu benutzen.
+\begin{figure}
+  \centering
+  \subfigure[Anzahl der annotierten Objekte]{\includegraphics[height=200px]{bilder/anzahl_objekte.png}}
+  \subfigure[Anzahl der annotierten Bilder]{\includegraphics[height=200px]{bilder/anzahl_bilder.png}}
+  \caption{Vergleich der annotierten Bilder und Objekte zwischen GUI und internem Werkzeug}
+  \label{fig:annotierteObjekte}
+\end{figure}
+
+\subsection{Bewertung durch die Testpersonen}\label{subsec:bewertung}
+In dem Fragebogen wurden die Testpersonen gebeten, das Annotieren von neuen Objekten und das Zuweisen der Objekt-ID auf einer Skala von 1 bis 10 zu bewerten.
+Dabei stand 1 für sehr umständlich und 10 für überhaupt nicht umständlich. 
+Ein Vergleich der durchschnittlichen Bewertung für die GUI und für das interne Werkzeug befindet sich in Abbildung \ref{fig:bewertungVergleich}.
+In diesem Vergleich schneidet die GUI insgesamt besser ab als das interne Werkzeug.
+
+Bei der Annotation neuer Objekte schneidet die GUI mit einer Bewertung von 8,5 am besten ab und liegt hier mit 2,5 Punkten vor der Bewertung des internen Werkzeug.
+Dies liegt vermutlich daran, dass den Testpersonen hier durch die Vorabannotationen viel Arbeit erspart wurde.
+Auch das Zuweisen der Objekt-ID hat mit 8,1 um 2,7 Punkte besser abgeschnitten als das interne Werkzeug.
+
+Zu der GUI sollten die Testpersonen auch noch das Korrigieren von Vorabannotationen und das Zuweisen der Objektklasse bewerten.
+Bei den Tests hat sich dann jedoch herausgestellt, dass das Zuweisen der Objektklasse in den für die Tests verwendeten Sequenzen nicht benötigt wurde.
+Daher wurde die Frage von den Testpersonen nicht beantwortet und kann somit auch nicht ausgewertet werden.
+Das Korrigieren der Vorabannotationen bekam von den Testpersonen eine durchschnittliche Bewertung von 8,4.
+
+Von den Features, die von der GUI mit dem Fragebogen bewertet wurden, 
+hat das Zuweisen der Objekt-ID etwas schlechter abgeschnitten als das Annotieren neuer Objekte oder das Korrigieren von Vorabannotationen. 
+Das könnte daran liegen, dass für das Zuweisen der Objekt-ID mehrere Bilder angesehen werden mussten, um festzustellen, welche ID ein Objekt hat.
+Außerdem werden die Objekt-IDs in der GUI erst angezeigt, wenn der Nutzer mit der Maus auf das Objekt zeigt, was vermutlich als zu umständlich angesehen wurde.
+
+\graphicsfigure{bilder/nutzer_bewertung}{Auswertung der ersten beiden Fragen des Fragebogens}{fig:bewertungVergleich}{0.75\textwidth}
+
+\subsection{Bevorzugte Anwendung}\label{subsec:bevorzugt}
+In dem Fragebogen haben die Testpersonen außerdem angegeben, welche Anwendung sie für eine große Sequenz von Bildern lieber verwenden würden.
+Das Ergebnis ist in Abbildung \ref{fig:bevorzugt} dargestellt. Von den 12 Testpersonen würden 11 Personen lieber die hier vorgestellte GUI verwenden.
+Dies wurde von den Testpersonen in einem Freitextfeld des Fragebogens unterschiedlich begründet. Die am häufigsten vorkommenden Begründungen waren, 
+dass durch die Grafische Benutzeroberfläche das Bedienen einfacher ist und dass durch die Vorabannotationen und durch die Kopierfunktion von Objekten 
+viel Zeit gespart werden kann.
+
+Eine Testperson gab an lieber das interne Werkzeug zu benutzen, mit der Begründung, dass es einfacher zu bedienen ist, weil es weniger Features hat.
+Dies ist aber vermutlich eine Sache der Gewöhnung. Hinzu kommt, 
+dass den Testpersonen für jede Anwendung nur zehn Minuten Zeit zur Verfügung standen und sie sich nicht länger damit beschäftigen konnten. 
+Für ein simples Programm wie das interne Werkzeug reicht diese Zeit aus, um alle Funktionen zu verstehen und zu benutzen.
+Bei der GUI wird mehr Zeit benötigt um ein Gefühl dafür zu bekommen, wie man am effizientesten damit arbeiten kann.
+
+Dennoch zeigt das Ergebnis, dass die GUI aufgrund der vielen Features für unerfahrene Benutzer nicht so leicht zu bedienen ist 
+und eventuell mehr Zeit benötigt wird um sich einzuarbeiten.
+\graphicsfigure{bilder/bevorzugt}{Bevorzugte Anwendung der Testpersonen}{fig:bevorzugt}{0.50\textwidth}
+
+\subsection{Weitere Angaben}\label{subsec:angaben}
+Die Testpersonen wurden im Fragebogen auch nach fehlenden oder besonders hilfreichen Features gefragt. An dieser Stelle wurde bemängelt, 
+dass bei der GUI die Vergabe von neuen Objekt-IDs nicht komfortabel genug sei und dass die Objekt-IDs in der Arbeitsfläche nicht gut genug sichtbar seien.
+
+Es wurde vorgeschlagen die Objekt-IDs in der Arbeitsfläche anzeigen zu lassen, so dass es für den Nutzer einfacher ist die ID eines Objektes zu sehen.
+Dieser Vorschlag wurde nachträglich in die GUI integriert, wie es in Abschnitt \ref{sec:reaktion} beschrieben wird. 
+Des Weiteren gab es den Vorschlag das Kopieren von mehreren Objekten gleichzeitig zuzulassen, so dass der Nutzer nicht so oft zwischen den Bildern springen muss.
+Dieser Vorschlag wurde mangels Zeit noch nicht umgesetzt, da eine vorschnelle Umsetzung eine Verringerung der Intuitivität zufolge haben könnte.
+
+Bei den besonders hilfreichen Features wurde noch einmal betont, was als Begründung für die bevorzugte Nutzung der GUI angegeben wurde:
+\begin{enumerate}
+  \item die Grafische Benutzeroberfläche
+  \item die Vorabannotationen
+  \item die Kopierfunktion von Objekten 
+\end{enumerate}
+
+\subsection{Nutzung einzelner GUI Features}
+\graphicsfigure{bilder/nutzung}{Prozentuale Nutzung der einzelnen GUI Funktionen nach Zeit}{fig:nutzung}{0.75\textwidth}
+Wie oben bereits erwähnt, wurde während der Tests analysiert, welche Werkzeuge und Funktionen der GUI von den Testpersonen hauptsächlich benutzt wurden.
+Mit den Ergebnissen kann analysiert werden, welche Funktionen für eine GUI zur Annotation von Bildsequenzen am wichtigsten sind.
+In Abbildung \ref{fig:nutzung} wird die Zeit, die die Testpersonen mit den einzelnen Features verbracht haben, verglichen.
+Das \glq Objekt zerteilen\grq{ } Werkzeug wird hier nicht betrachtet, da es nur in dem Sonderfall benötigt wird, 
+wo in den Vorabannotationen zwei Objekte als eins erkannt werden. Dieser Sonderfall ist während der Tests nur sehr selten aufgetreten.
+
+Insgesamt haben die Testpersonen 30\% der Zeit mit dem \glq Kopieren und Einfügen\grq{ } Werkzeug gearbeitet. 
+Zusammen mit den obigen Daten lässt sich hiermit der hohe Nutzen dieses Werkzeugs beim Annotieren von Bildsequenzen belegen.
+Bemerkenswert ist außerdem, dass die Testpersonen über fünf mal mehr Zeit damit verbracht haben Objekte einzufügen, 
+als sie Zeit benötigten, diese zu kopieren. Hieraus lässt sich folgern, dass ein kopiertes Objekt viele Mahle eingefügt wurde.
+
+Die hohe Nutzung des \glq Kopieren und Einfügen\grq{ } Werkzeugs lässt sich vermutlich darauf zurückführen, 
+dass mit dem Werkzeug, neben dem Kopieren und Einfügen von Objekten, auch das Zuweisen der Objekt-IDs bewältigt werden kann.
+Dabei kopiert der Nutzer ein Objekt mit ID und weist diese dann beim Einfügen anderen Objekten mit nur einem Klick zu.
+
+24\% der Zeit wurde von den Testpersonen dafür verwendet neue Objekte manuell zu annotieren, welche von den Vorabannotationen nicht automatisch erkannt wurden. 
+Dies zeigt, dass die Vorabannotationen zusammen mit dem \glq Kopieren und Einfügen\grq{ } Werkzeug
+alleine noch nicht für eine voll funktionsfähige GUI zum Annotieren von Bildsequenzen ausreichen würden.
+
+22\% der Zeit verbrachten die Testpersonen damit Eckpunkte von Objekten zu verschieben. 
+Dabei handelte es sich mit großer Wahrscheinlichkeit um nicht ganz korrekte Vorabannotationen, 
+welche durch das Verschieben von Ecken angepasst werden können. 
+Dies zeigt, dass durch die Vorabannotationen zwar viel Zeit gespart werden kann, 
+die sonst benötigt werden würde um die Objekte einzeln zu annotieren.
+Andererseits muss aber auch etwas Zeit dafür verwendet werden um die Vorabannotationen zu korrigieren.
+
+Das Bearbeiten der Maske benötigt nur wenig Zeit, da es nur einmal zu Beginn gemacht werden muss. 
+Daher mussten die Testpersonen nur 7\% der Zeit dafür verwenden.
+
+Ebenfalls 7\% der Zeit hat das Zuweisen von Objekt-IDs über die Objekt\hyp{}Zuweisungsoberfläche aus Abschnitt \ref{sec:objektZuweisungsansicht} gekostet.
+Hierbei muss beachtet werden, dass dies nur ein Teil der Zeit ist, die mit dem Zuweisen von Objekt-IDs verbracht wurde, 
+da man die Objekt-IDs auch mit dem \glq Kopieren und Einfügen\grq{ } Werkzeug zuweisen kann.
+
+Die Werkzeuge Vergrößern, Löschen und Verstecken wurden nur wenig verwendet. 
+Dies zeigt, dass sie nur in Sonderfällen benötigt werden und für eine voll funktionsfähige GUI 
+zum Annotieren von Bildsequenzen nicht unbedingt notwendig sind.
+Dabei muss beachtet werden, dass in der GUI neben dem Löschen Werkzeug ein Objekt auch mittels Rechtsklick gelöscht werden kann 
+und das Löschen Werkzeug nur für das Löschen einzelner Eckpunkte benötigt wird.
+
+\section{Integration der Testergebnisse in die GUI}\label{sec:reaktion}
+\graphicsfigure{bilder/gui_mit_ids}{Die angepasste GUI mit angezeigten Objekt-IDs}{fig:guiMitIds}{0.75\textwidth}
+Als Reaktion auf die Testergebnisse wurden einzelne GUI Funktionen etwas angepasst, 
+so dass die Hauptkritikpunkte der Testpersonen behoben sind.
+
+Ein Hauptkritikpunkt an der GUI war, dass die Objekt-IDs in der Arbeitsfläche nicht gut genug sichtbar waren.
+Um dieses Problem zu beheben, wurde die Option hinzugefügt, sich die Objekt-IDs in der Arbeitsfläche anzeigen zu lassen.
+Ein Bild hiervon befindet sich in Abbildung \ref{fig:guiMitIds}.
+
+Ein weiterer Kritikpunkt war das Zuweisen von neuen Objekt-IDs über die Objekt-Zuweisungsoberfläche. 
+Diese war den Testpersonen zu umständlich gestaltet.
+Hier wurde die Bedienung vereinfacht, indem zum Beispiel die Objekt-ID des ausgewählten Vergleichsobjektes direkt im ID Textfeld übernommen wird.
+Sobald der Nutzer das richtige Vergleichobjekt gefunden hat, braucht er also nur noch auf Speichern zu klicken. 
+Auch wird nun bei der Eingabe einer ID direkt ein Vergleichsobjekt mit dieser ID angezeigt.
+Des Weiteren lässt sich die ID jetzt auch mit den Pfeiltasten um eins erhöhen oder verringern. 
+Je nach Nutzervorliebe kann das Vergleichsobjekt und somit die korrekte Objekt-ID also jetzt auch nur mit der Tastatur gefunden und zugewiesen werden.

+ 12 - 0
schriftlich/kapitel/6Schluss.tex

@@ -0,0 +1,12 @@
+In dieser Arbeit wurde eine neue GUI für die Annotation von Bildsequenzen entworfen und implementiert.
+Die entworfene GUI unterstützt den Nutzer bei der Annotation, indem sie Vorabannotationen bereitstellt, welche den Prozess des Annotierens beschleunigen.
+
+Mit Hilfe der GUI können Datensätze erstellt werden, welche für das Training eines Computers zur Erkennung von Objekten auf Bildern verwendet werden können.
+Es werden dabei sowohl die Boundingboxen, als auch die Polygone der Objekte in dem Datensatz gespeichert. Es kann also später noch entschieden werden, 
+welche Daten für das Training verwendet werden sollen.
+Auf diese Weise kann die GUI für viele unterschiedliche Forschungsprojekte verwendet werden, bei denen eine automatische Objekterkennung eine Rolle spielt.
+
+Durch eine Untersuchung mit Testpersonen wurde belegt, dass mit der GUI einfacher und effizienter gearbeitet werden kann, 
+als mit einem internen Werkzeug, welches zuvor für die Annotation von Bildsequenzen verwendet wurde.
+Die in dieser Arbeit vorgestellte GUI hat dabei im direkten Vergleich deutlich besser abgeschnitten als das interne Werkzeug.
+Es wurde also bewiesen, dass die GUI zur effizienten Annotation von Bildsequenzen geeignet ist und andere Anwendungen ablösen kann.

+ 3 - 0
schriftlich/kapitel/7anhang.tex

@@ -0,0 +1,3 @@
+Der Quelltext der GUI befindet sich Anbei auf einer CD oder kann im Internet unter http://gogs.koljastrohm-games.com/kolja/AnnotationGUI eingesehen werden.
+
+\includepdf[pages={1,2}]{bilder/fragebogen.pdf}

+ 139 - 0
schriftlich/schriftlich.bib

@@ -0,0 +1,139 @@
+
+@incollection{fleet_microsoft_2014,
+	address = {Cham},
+	title = {Microsoft {COCO}: {Common} {Objects} in {Context}},
+	volume = {8693},
+	isbn = {978-3-319-10601-4 978-3-319-10602-1},
+	shorttitle = {Microsoft {COCO}},
+	url = {http://link.springer.com/10.1007/978-3-319-10602-1_48},
+	urldate = {2018-01-18},
+	booktitle = {Computer {Vision} – {ECCV} 2014},
+	publisher = {Springer International Publishing},
+	author = {Lin, Tsung-Yi and Maire, Michael and Belongie, Serge and Hays, James and Perona, Pietro and Ramanan, Deva and Dollár, Piotr and Zitnick, C. Lawrence},
+	editor = {Fleet, David and Pajdla, Tomas and Schiele, Bernt and Tuytelaars, Tinne},
+	year = {2014},
+	note = {DOI: 10.1007/978-3-319-10602-1\_48},
+	pages = {740--755}
+}
+
+@article{everingham_pascal_2010,
+	title = {The {Pascal} {Visual} {Object} {Classes} ({VOC}) {Challenge}},
+	volume = {88},
+	issn = {0920-5691, 1573-1405},
+	url = {http://link.springer.com/10.1007/s11263-009-0275-4},
+	doi = {10.1007/s11263-009-0275-4},
+	language = {en},
+	number = {2},
+	urldate = {2018-01-18},
+	journal = {International Journal of Computer Vision},
+	author = {Everingham, Mark and Van Gool, Luc and Williams, Christopher K. I. and Winn, John and Zisserman, Andrew},
+	month = jun,
+	year = {2010},
+	pages = {303--338}
+}
+
+@inproceedings{jia_deng_imagenet:_2009,
+	title = {{ImageNet}: {A} large-scale hierarchical image database},
+	isbn = {978-1-4244-3992-8},
+	shorttitle = {{ImageNet}},
+	url = {http://ieeexplore.ieee.org/document/5206848/},
+	doi = {10.1109/CVPR.2009.5206848},
+	urldate = {2018-01-18},
+	publisher = {IEEE},
+	author = {{Jia Deng} and {Wei Dong} and Socher, R. and {Li-Jia Li} and {Kai Li} and {Li Fei-Fei}},
+	month = jun,
+	year = {2009},
+	pages = {248--255}
+}
+
+@article{russell_labelme:_2008,
+	title = {{LabelMe}: {A} {Database} and {Web}-{Based} {Tool} for {Image} {Annotation}},
+	volume = {77},
+	issn = {0920-5691, 1573-1405},
+	shorttitle = {{LabelMe}},
+	url = {http://link.springer.com/10.1007/s11263-007-0090-8},
+	doi = {10.1007/s11263-007-0090-8},
+	language = {en},
+	number = {1-3},
+	urldate = {2018-01-18},
+	journal = {International Journal of Computer Vision},
+	author = {Russell, Bryan C. and Torralba, Antonio and Murphy, Kevin P. and Freeman, William T.},
+	month = may,
+	year = {2008},
+	pages = {157--173}
+}
+
+@article{sermanet_overfeat:_2013,
+	title = {{OverFeat}: {Integrated} {Recognition}, {Localization} and {Detection} using {Convolutional} {Networks}},
+	shorttitle = {{OverFeat}},
+	url = {http://arxiv.org/abs/1312.6229},
+	abstract = {We present an integrated framework for using Convolutional Networks for classification, localization and detection. We show how a multiscale and sliding window approach can be efficiently implemented within a ConvNet. We also introduce a novel deep learning approach to localization by learning to predict object boundaries. Bounding boxes are then accumulated rather than suppressed in order to increase detection confidence. We show that different tasks can be learned simultaneously using a single shared network. This integrated framework is the winner of the localization task of the ImageNet Large Scale Visual Recognition Challenge 2013 (ILSVRC2013) and obtained very competitive results for the detection and classifications tasks. In post-competition work, we establish a new state of the art for the detection task. Finally, we release a feature extractor from our best model called OverFeat.},
+	urldate = {2018-01-19},
+	journal = {arXiv:1312.6229 [cs]},
+	author = {Sermanet, Pierre and Eigen, David and Zhang, Xiang and Mathieu, Michael and Fergus, Rob and LeCun, Yann},
+	month = dec,
+	year = {2013},
+	note = {arXiv: 1312.6229},
+	keywords = {Computer Science - Computer Vision and Pattern Recognition},
+	file = {arXiv\:1312.6229 PDF:C\:\\Users\\kolja\\Zotero\\storage\\TLDA7V7U\\Sermanet et al. - 2013 - OverFeat Integrated Recognition, Localization and.pdf:application/pdf;arXiv.org Snapshot:C\:\\Users\\kolja\\Zotero\\storage\\BGIMQR4S\\1312.html:text/html}
+}
+
+@misc{pascal-voc-2007,
+	author = "Everingham, M. and Van~Gool, L. and Williams, C. K. I. and Winn, J. and Zisserman, A.",
+	title = "The {PASCAL} {V}isual {O}bject {C}lasses {C}hallenge 2007 {(VOC2007)} {R}esults",
+	howpublished = "http://www.pascal-network.org/challenges/VOC/voc2007/workshop/index.html"
+}
+
+@misc{deacon2009model,
+  title="Model-view-controller (mvc) architecture",
+  author="Deacon, John",
+  howpublished="http://www.jdl.co.uk/briefings/index.html"
+}
+
+@book{perry1996introduction,
+  title={An introduction to object-oriented design in C++},
+  author={Perry, Jo Ellen and Levin, Harold D},
+  year={1996},
+  publisher={Addison-Wesley}
+}
+
+@incollection{NIPS2012_4824,
+  title = {ImageNet Classification with Deep Convolutional Neural Networks},
+  author = {Alex Krizhevsky and Sutskever, Ilya and Hinton, Geoffrey E}, 
+  booktitle = {Advances in Neural Information Processing Systems 25},
+  editor = {F. Pereira and C. J. C. Burges and L. Bottou and K. Q. Weinberger},
+  pages = {1097--1105},
+  year = {2012},
+  publisher = {Curran Associates, Inc.},
+  url = {http://papers.nips.cc/paper/4824-imagenet-classification-with-deep-convolutional-neural-networks.pdf}
+}
+
+@article{Dworak:1391410,
+      author        = "Dworak, A and Charrue, P and Ehm, F and Sliwinski, W and
+                       Sobczak, M",
+      title         = "{Middleware Trends And Market Leaders 2011}",
+      journal       = "Conf. Proc.",
+      number        = "CERN-ATS-2011-196",
+      volume        = "C111010",
+      pages         = "FRBHMULT05. 4 p",
+      month         = "Oct",
+      year          = "2011",
+      reportNumber  = "CERN-ATS-2011-196",
+      url           = "http://cds.cern.ch/record/1391410",
+}
+
+@inproceedings{culjak2012brief,
+  title={A brief introduction to OpenCV},
+  author={Culjak, Ivan and Abram, David and Pribanic, Tomislav and Dzapo, Hrvoje and Cifrek, Mario},
+  booktitle={MIPRO, 2012 proceedings of the 35th international convention},
+  pages={1725--1730},
+  year={2012},
+  organization={IEEE}
+
+  }
+@book{gamma2011entwurfsmuster,
+  title={Entwurfsmuster: Elemente wiederverwendbarer objektorientierter Software},
+  author={Gamma, Erich and Johnson, Ralph and Helm, Richard and Vlissides, John},
+  year={2011},
+  publisher={Pearson Deutschland GmbH}
+}

BIN
schriftlich/schriftlich.pdf


+ 274 - 0
schriftlich/schriftlich.tex

@@ -0,0 +1,274 @@
+\documentclass[fontsize=12pt, paper=a4, headinclude, twoside=false, parskip=half+, pagesize=auto, numbers=noenddot, open=right, toc=listof, toc=bibliography]{scrreprt}
+%\overfullrule=20mm
+%\usepackage{showframe}
+% PDF-Kompression
+\pdfminorversion=5
+\pdfobjcompresslevel=1
+% Allgemeines
+\usepackage[automark]{scrpage2} % Kopf- und Fußzeilen
+\usepackage{amsmath,marvosym} % Mathesachen
+\usepackage[T1]{fontenc} % Ligaturen, richtige Umlaute im PDF
+\usepackage{lmodern}
+\usepackage[utf8]{inputenc}% UTF8-Kodierung für Umlaute usw
+% Schriften
+\usepackage{mathpazo} % Palatino für Mathemodus
+%\usepackage{mathpazo,tgpagella} % auch sehr schöne Schriften
+\usepackage{setspace} % Zeilenabstand
+\onehalfspacing % 1,5 Zeilen
+% Schriften-Größen
+\usepackage{subfigure}
+\setkomafont{chapter}{\Huge\rmfamily} % Überschrift der Ebene
+\setkomafont{section}{\Large\rmfamily}
+\setkomafont{subsection}{\large\rmfamily}
+\setkomafont{subsubsection}{\large\rmfamily}
+\setkomafont{chapterentry}{\large\rmfamily} % Überschrift der Ebene in Inhaltsverzeichnis
+\setkomafont{descriptionlabel}{\bfseries\rmfamily} % für description-Umgebungen
+\setkomafont{captionlabel}{\small\bfseries}
+\setkomafont{caption}{\small}
+% Sprache: Deutsch
+\usepackage[german]{babel} % Silbentrennung
+\usepackage[german]{todonotes}
+\newcommand{\TODO}[1]{\todo[inline]{TODO: #1}}
+% PDF
+\newcommand{\Thema}{Entwurf und Implementierung einer GUI zur unterstützenden Annotation von Bildsequenzen}
+\usepackage[german,pdfauthor={Kolja Samuel Strohm}, pdfauthor={Kolja Samuel Strohm}, pdftitle={\Thema}, breaklinks=true]{hyperref}
+\usepackage[final]{microtype} % mikrotypographische Optimierungen
+\usepackage{url} % ermögliche Links (URLs)
+\usepackage{pdflscape} % einzelne Seiten drehen können
+% Tabellen
+\usepackage{multirow} % Tabellen-Zellen über mehrere Zeilen
+\usepackage{multicol} % mehrere Spalten auf eine Seite
+\usepackage{tabularx} % für Tabellen mit vorgegeben Größen
+\usepackage{longtable} % Tabellen über mehrere Seiten
+\usepackage{array}
+\usepackage{paralist}    
+\usepackage{pdfpages}    
+                             
+                             
+                             %\usepackage{svg} % Svg-Grafiken
+                             
+                             
+                             
+%  Bibliographie
+% \usepackage{bibgerm} % Umlaute in BibTeX
+% Bilder
+\usepackage{float}
+\usepackage{graphicx} % Bilder
+\usepackage{color} % Farben
+\usepackage[autostyle=true]{csquotes} % enquote command
+\graphicspath{{img/}} % Lege den Standardpfad mit Bilder fest
+\DeclareGraphicsExtensions{.pdf,.png,.jpg} % bevorzuge pdf-Dateien vor den anderen
+%\usepackage{subcaption}  % mehrere Abbildungen nebeneinander/übereinander
+\usepackage[all]{hypcap} % Beim Klicken auf Links zum Bild und nicht zu Caption gehen
+% Bildunterschrift
+\setcapindent{0em} % kein Einrücken der Caption von Figures und Tabellen
+\setcapwidth{0.9\textwidth} % Breite der Caption nur 90% der Textbreite, damit sie sich vom restlichen Text abhebt
+\setlength{\abovecaptionskip}{0.2cm} % Abstand der zwischen Bild- und Bildunterschrift
+\usepackage{enumitem} % Referenzen auf Item in enumerate-Blocks
+% Quellcode
+% für Formatierung in Quelltexten, hier im Anhang
+\usepackage{listings}
+
+\usepackage{hyphenat}
+\hyphenation{Zu-wei-sungs-o-ber-flä-che}
+
+\definecolor{grau}{gray}{0.25}
+\lstset{
+extendedchars=true,
+basicstyle=\tiny\ttfamily,
+%basicstyle=\footnotesize\ttfamily,
+tabsize=2,
+keywordstyle=\textbf,
+commentstyle=\color{grau},
+stringstyle=\textit,
+numbers=left,
+numberstyle=\tiny,
+% für schönen Zeilenumbruch
+breakautoindent = true,
+breakindent = 2em,
+breaklines = true,
+postbreak = ,
+showstringspaces=false,
+prebreak = \raisebox{-.8ex}[0ex][0ex]{\Righttorque},
+}
+\lstdefinelanguage{XML}
+{
+  morestring=[b]",
+  morestring=[s]{>}{<},
+  morecomment=[s]{<?}{?>},
+  stringstyle=\color{black},
+  identifierstyle=\color{blue},
+  keywordstyle=\color{cyan},
+  morekeywords={xmlns,version,type}% list your attributes here
+}
+% linksbündige Fußboten
+\deffootnote{1.5em}{1em}{\makebox[1.5em][l]{\thefootnotemark}}
+
+\typearea{14} % typearea berechnet einen sinnvollen Satzspiegel (das heißt die Seitenränder usw.) siehe auch http://www.ctan.org/pkg/typearea. Diese Berechnung befindet sich am Schluss, damit die Einstellungen von oben berücksichtigt werden
+
+% \usepackage{scrhack} % Vermeidung einer Warnung
+%SVG Grafiken
+\newcommand{\executeiffilenewer}[3]{%
+  \ifnum\pdfstrcmp{\pdffilemoddate{#1}}%
+  {\pdffilemoddate{#2}}>0%
+  {\immediate\write18{#3}}\fi%
+}
+% includesvg[scale]{file} command (linux-version)
+\newcommand{\includesvg}[2][1]{%
+  \executeiffilenewer{#2.svg}{#2.pdf}{%
+  C:/Programme/inkscape/inkscape -z -D --file="#2.svg" --export-pdf="#2.pdf"}%
+  \scalebox{#1}{\includegraphics{#2.pdf}}%
+}
+
+\newcommand{\todobild}[2]{
+\begin{figure}[!hbt]
+    \begin{center}
+        \vspace{2ex}
+        \includegraphics[width=6cm]{img/todobild}
+        \caption{\label{#1} \todotext{#2}}
+        \vspace{2ex}
+    \end{center}
+\end{figure}
+}
+
+\newcommand{\svgfigure}[3]{
+\begin{figure}[t]
+    \centering
+    \includesvg{#1}
+    \caption{#2}\label{#3}
+\end{figure}
+}
+
+\newcommand{\svgfigurewidthheight}[5]{
+\begin{figure}[t]
+    \centering
+    \resizebox{#4}{#5}{
+    \includesvg{#1}
+    }
+    \caption{#2}\label{#3}
+\end{figure}
+}
+
+\newcommand{\graphicsfigure}[4]{
+\begin{figure}[t]
+    \centering
+    \includegraphics[width=#4]{#1}
+    \caption{#2}\label{#3}
+\end{figure}
+}
+
+%\usepackage{showframe} % overfull hboxes deutlich sichtbar machen
+
+%
+%\usepackage{everypage}
+%\usepackage{datetime}
+%\AddEverypageHook{%
+%\begin{picture}
+%    (0,0)%
+%    \put(140,30){\fbox{\textbf{Entwurf vom ~\today~\currenttime}}}
+%    %
+%\end{picture}\hfill
+%}
+
+\begin{document} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+    \pagenumbering{Roman} % Seitenummerierung mit großen römischen Zahlen
+    \pagestyle{empty} % keine Kopf- oder Fußzeilen auf den ersten Seiten
+
+    % Titelseite
+    \clearscrheadings\clearscrplain
+    \begin{center}
+    {\Large Christian-Albrechts-Universität zu Kiel}
+        \\
+
+        \vspace{20mm}
+        \begin{Large}
+            \Thema
+        \end{Large}
+        \vspace{8mm}
+        
+        
+        Bachelorarbeit\\
+        \vspace{0.4cm}
+        \vspace{2 cm}
+        Kolja Samuel Strohm \\
+        März 2018\\
+ %       Matrikel-Nummer 1036051\\
+        \vspace{9cm}
+%        \begin{tabular}{rl}
+%        {\bfseries Betreuer}
+%            & M.\ Sc.\ Claudius Zelenka\\
+%            {\bfseries Erstprüfer}& Prof.\ Dr.-Ing.\ Reinhard Koch\\
+        Betreut durch Prof.\ Dr.-Ing.\ Reinhard Koch\\
+        und M.\ Sc.\ Claudius Zelenka
+%        \end{tabular}
+
+    \end{center}
+    \clearpage\null\clearpage
+
+     \pagestyle{useheadings}
+    %\pagestyle{scrheadings} % normale Kopf- und Fußzeilen für den Rest
+    %\cfoot*{\pagemark}
+    %\chead{}
+    %\ofoot{}
+    %\ohead{\headmark}
+    
+    \chapter*{Erklärung}\label{ch:erklärung}
+    Hiermit erkläre ich an Eides statt, dass ich die vorliegende Arbeit selbstständig verfasst und keine anderen als die angegebenen Quellen und Hilfsmittel benutzt habe, dass alle Stellen der Arbeit, die wörtlich oder sinngemäß aus anderen Quellen übernommen wurden, als solche kenntlich gemacht und dass die Arbeit in gleicher oder ähnlicher Form noch keiner Prüfungsbehörde vorgelegt wurde.
+
+    \vspace{3cm}
+    Ort, Datum \hspace{5cm} Unterschrift\\
+   
+    \chapter*{Zusammenfassung}
+    In dieser Arbeit wurde eine GUI zur Annotation von Bildsequenzen entworfen und implementiert.
+    Die fertiggestellte GUI schnitt bei einer Untersuchung im Vergleich mit einem anderen Werkzeug zur Annotation von Bildsequenzen deutlich besser ab und konnte 
+    mit Hilfe der Egebnisse sogar noch verbessert werden.
+    Die resultierende GUI kann für das effiziente Erstellen von Trainingsdaten für das Erkennen von Objekten in Bildern verwendet werden und ist somit in vielen Gebieten anwendbar.
+
+    
+    \tableofcontents % erstelle hier das Inhaltsverzeichnis
+    \listoffigures % erstelle hier das Abbildungsverzeichnis
+ %   \listoftables % erstelle hier das Tabellenverzeichnis
+
+    %    \addchap{Symbolverzeichnis}\label{s.sym} % vergebe für das Symbolverzeichnis keine Kapitelnummer
+    %    \section*{Allgemeine Symbole}\label{s.sym.alg}
+    %    \begin{flushleft}
+    %        \begin{tabularx}{\textwidth}{l|X}
+    %            Symbol & Bedeutung\\\hline
+    %            $a$ & der Skalar $a$ \\
+    %            $\vec{x}$ & der Vektor $\vec{x}$\\
+    %            $\mat{A}$ & die Matrix $\mat{A}$\\
+    %        \end{tabularx}
+    %    \end{flushleft}
+
+    % richtiger Inhalt    
+    
+    \chapter{Einleitung}\label{ch:Einleitung}
+    \pagenumbering{arabic} % ab jetzt die normale arabische Nummerierung
+    \input{kapitel/1einleitung}
+    
+    \chapter{Verwandte Arbeiten}\label{ch:relWork}
+    \input{kapitel/1.5relwork}
+
+    \chapter{Annotation von Bildsequenzen}\label{ch:annotationVonBildsequenzen}
+    \input{kapitel/2annotation}
+
+    \chapter{GUI Funktionen}\label{ch:guifunktionen}
+    \input{kapitel/3funktionen}
+
+    \chapter{Implementierung}\label{ch:implementierung}
+    \input{kapitel/4implementierung}
+
+    \chapter{Untersuchung}\label{ch:ergebnis}
+    \input{kapitel/5ergebnis}
+
+    \chapter{Schluss}\label{ch:schluss}
+    \input{kapitel/6schluss}
+    
+    \chapter{Anhang}\label{ch:anhang}
+    \input{kapitel/7anhang}
+ 
+    \bibliographystyle{ieeetran}
+    \bibliography{schriftlich}
+
+ %   \TODO{remove this note}
+\end{document}