Helge Wrede %!s(int64=9) %!d(string=hai) anos
achega
8e207f5d86
Modificáronse 23 ficheiros con 2300 adicións e 0 borrados
  1. 6 0
      .directory
  2. 3 0
      .gitignore
  3. 55 0
      CMakeLists.txt
  4. 0 0
      README
  5. 37 0
      algo/Definitions.h
  6. 219 0
      algo/TwoStage.cpp
  7. 48 0
      algo/TwoStage.h
  8. 20 0
      core/Definitions.h
  9. 84 0
      core/DetectionSequence.cpp
  10. 57 0
      core/DetectionSequence.h
  11. 53 0
      core/ObjectData.cpp
  12. 48 0
      core/ObjectData.h
  13. 117 0
      core/ObjectDataMap.cpp
  14. 63 0
      core/ObjectDataMap.h
  15. 88 0
      core/Tracklet.cpp
  16. 40 0
      core/Tracklet.h
  17. 53 0
      main/main.cpp
  18. 395 0
      main/preview.cpp
  19. 735 0
      main/preview2.cpp
  20. 92 0
      util/IO.cpp
  21. 33 0
      util/IO.h
  22. 30 0
      util/Parser.cpp
  23. 24 0
      util/Parser.h

+ 6 - 0
.directory

@@ -0,0 +1,6 @@
+[Dolphin]
+Timestamp=2016,4,19,14,29,50
+Version=3
+
+[Settings]
+HiddenFilesShown=true

+ 3 - 0
.gitignore

@@ -0,0 +1,3 @@
+.idea
+Debug
+Release

+ 55 - 0
CMakeLists.txt

@@ -0,0 +1,55 @@
+cmake_minimum_required(VERSION 3.5)
+project(GBMOT)
+
+set(HOME_LOCAL /home/wrede/local)
+
+include_directories(${HOME_LOCAL}/include)
+
+find_package(Doxygen)
+
+set(SOURCE_FILES
+        main/main.cpp
+        core/DetectionSequence.cpp
+        core/DetectionSequence.h
+        core/ObjectData.cpp
+        core/ObjectData.h
+        core/ObjectDataMap.cpp
+        core/ObjectDataMap.h
+        core/Definitions.h
+        core/Tracklet.cpp
+        core/Tracklet.h
+        util/IO.cpp
+        util/IO.h
+        util/Parser.cpp
+        util/Parser.h
+        algo/TwoStage.cpp
+        algo/TwoStage.h
+        algo/Definitions.h)
+
+if(DOXYGEN_FOUND)
+
+    set(DOXYGEN_INPUT ${SOURCE_FILES})
+    set(DOXYGEN_OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile)
+
+    add_custom_command(
+            OUTPUT ${DOXYGEN_OUTPUT}
+            COMMAND ${CMAKE_COMMAND} -E echo_append "Building API Documentation..."
+            COMMAND ${DOXYGEN_EXECUTABLE} ${DOXYGEN_INPUT}
+            COMMAND ${CMAKE_COMMAND} -E echo "Done."
+            WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
+            DEPENDS ${DOXYGEN_INPUT}
+    )
+
+    add_custom_target(apidoc ALL DEPENDS ${DOXYGEN_OUTPUT})
+
+    add_custom_target(apidoc_forced
+            COMMAND ${CMAKE_COMMAND} -E echo_append "Building API Documentation..."
+            COMMAND ${DOXYGEN_EXECUTABLE} ${DOXYGEN_INPUT}
+            COMMAND ${CMAKE_COMMAND} -E echo "Done."
+            WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
+
+endif(DOXYGEN_FOUND)
+
+set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
+
+add_executable(GBMOT ${SOURCE_FILES})

+ 0 - 0
README


+ 37 - 0
algo/Definitions.h

@@ -0,0 +1,37 @@
+//
+// Created by wrede on 25.04.16.
+//
+
+#ifndef GBMOT_DEFINITIONS_H
+#define GBMOT_DEFINITIONS_H
+
+
+#include <boost/graph/properties.hpp>
+#include <boost/graph/graph_traits.hpp>
+#include <boost/graph/adjacency_list.hpp>
+#include "../core/ObjectData.h"
+#include "../core/Tracklet.h"
+
+namespace algo
+{
+    //TODO read minimal example
+    //TODO check types (move to core)
+    typedef boost::property<boost::edge_weight_t, double> EdgeProp;
+    typedef boost::property<boost::vertex_name_t, core::ObjectData>
+            VertexProp;
+    typedef boost::adjacency_list<
+            boost::listS, boost::vecS, boost::directedS,
+            VertexProp, EdgeProp>
+            DirectedGraph;
+    typedef boost::graph_traits<DirectedGraph>::vertex_descriptor Vertex;
+    typedef boost::property_map<DirectedGraph, boost::vertex_index_t>::type
+            VertexIndexMap;
+    typedef boost::property_map<DirectedGraph, boost::vertex_name_t >::type
+            VertexValueMap;
+    typedef boost::iterator_property_map<Vertex*, VertexIndexMap, Vertex, Vertex&>
+            PredecessorMap;
+    typedef boost::iterator_property_map<double*, VertexIndexMap, double, double&>
+            DistanceMap;
+};
+
+#endif //GBMOT_DEFINITIONS_H

+ 219 - 0
algo/TwoStage.cpp

@@ -0,0 +1,219 @@
+//
+// Created by wrede on 25.04.16.
+//
+
+#include "TwoStage.h"
+
+namespace algo
+{
+    TwoStage::TwoStage(size_t max_frame_skip, double penalty_value,
+                       size_t max_tracklet_count)
+    {
+        max_frame_skip_ = max_frame_skip;
+        penalty_value_ = penalty_value;
+        max_tracklet_count_ = max_tracklet_count;
+    }
+
+    DirectedGraph TwoStage::CreateObjectGraph(core::DetectionSequence detections)
+    {
+        DirectedGraph graph;
+        std::vector<std::vector<Vertex>> layers;
+
+        // Add source as the vertex with the lowest index
+        Vertex source = boost::add_vertex(core::ObjectData(), graph);
+
+        // Add vertices from detection sequence to directed graph
+        // Save the vertices which are in one frame/layer for later use to
+        // link easily between vertices in adjacent frames/layers
+        for (size_t i = 0; i < detections.GetFrameCount(); ++i)
+        {
+            std::vector<Vertex> layer;
+
+            for (size_t j = 0; j < detections.GetObjectCount(i); ++j)
+            {
+                Vertex v =
+                        boost::add_vertex(detections.GetObject(i, j), graph);
+
+                layer.push_back(v);
+            }
+
+            layers.push_back(layer);
+        }
+
+        // Add sink as the vertex with the highest index
+        Vertex sink = boost::add_vertex(core::ObjectData(), graph);
+
+        // Vertex objects
+        VertexValueMap values = boost::get(boost::vertex_name, graph);
+
+        // Create edges
+        for (size_t i = 0; i < layers.size(); ++i)
+        {
+            // For each edge in this frame/layer
+            for (size_t j = 0; j < layers[i].size(); ++j)
+            {
+                Vertex u = layers[i][j];
+
+                // For each next frame/layer until maxFrameSkip or end
+                for (size_t k = 1;
+                     k < max_frame_skip_ && i + k < layers.size();
+                     ++k)
+                {
+                    // To every edge in the next frame/layer
+                    for (size_t l = 0; l < layers[i + k].size(); ++l)
+                    {
+                        Vertex v = layers[i + k][l];
+
+                        boost::add_edge(u, v,
+                                        values[u].CompareTo(&values[v]),
+                                        graph);
+                    }
+                }
+
+                // From source to vertex and from vertex to sink
+                boost::add_edge(source, u,
+                                (i + 1) * penalty_value_,
+                                graph);
+
+                boost::add_edge(u, sink,
+                                (layers.size() - i) * penalty_value_,
+                                graph);
+            }
+        }
+
+        obj_graph_ = graph;
+        frame_count_ = layers.size();
+
+        return graph;
+    }
+
+    DirectedGraph TwoStage::CreateTrackletGraph()
+    {
+        return CreateTrackletGraph(obj_graph_, frame_count_);
+    }
+
+    DirectedGraph TwoStage::CreateTrackletGraph(DirectedGraph obj_graph,
+                                                size_t frame_count)
+    {
+        DirectedGraph tlt_graph;
+
+        // Add source to tracklet graph
+        Vertex tlt_src = boost::add_vertex(core::ObjectData(), tlt_graph);
+
+        // Prepare parameter for dijkstra
+        size_t obj_graph_size = boost::num_vertices(obj_graph);
+        std::vector<Vertex> obj_pred_list(obj_graph_size);
+        std::vector<double> obj_dist_list(obj_graph_size);
+        VertexIndexMap obj_indices = boost::get(boost::vertex_index, obj_graph);
+        VertexValueMap obj_values = boost::get(boost::vertex_name, obj_graph);
+        PredecessorMap obj_pred_map(&obj_pred_list[0], obj_indices);
+        DistanceMap obj_dist_map(&obj_dist_list[0], obj_indices);
+
+        // Source and sink of the object graph
+        Vertex obj_src = obj_indices[0];
+        Vertex obj_snk = obj_indices[obj_graph_size - 1];
+
+        // Iteratively run dijkstra to extract tracklets
+        for (size_t i = 0; i < max_tracklet_count_; ++i)
+        {
+            boost::dijkstra_shortest_paths(obj_graph, obj_src,
+                                           boost::predecessor_map(obj_pred_map)
+                                                   .distance_map(obj_dist_map));
+
+            // Create the tracklet
+            core::Tracklet tracklet;
+            Vertex v = obj_snk;
+            for (Vertex u = obj_pred_map[obj_snk];
+                 u != v;
+                 v = u, u = obj_pred_map[v])
+            {
+                tracklet.AddPathObjectFirst(obj_values[u]);
+
+                // Leave source and sink untouched
+                if (!obj_values[u].IsVirtual())
+                {
+                    // Remove the path by setting all used edges to a weight of
+                    // infinity
+                    std::pair<DirectedGraph::out_edge_iterator,
+                              DirectedGraph::out_edge_iterator>
+                            edge_iter = boost::out_edges(u, obj_graph);
+
+                    for (DirectedGraph::out_edge_iterator iter = edge_iter.first;
+                         iter != edge_iter.second;
+                         ++iter)
+                    {
+                        boost::get(boost::edge_weight, obj_graph, *iter)
+                                = std::numeric_limits<double>::infinity();
+                    }
+                }
+            }
+
+            // Add tracklet into tracklet graph
+            boost::add_vertex(tracklet, tlt_graph);
+        }
+
+        // Add sink to tracklet graph
+        Vertex tlt_snk = boost::add_vertex(core::ObjectData(), tlt_graph);
+
+        // Create edges
+        size_t tlt_graph_size = boost::num_vertices(tlt_graph);
+        VertexIndexMap tlt_indices = boost::get(boost::vertex_index, tlt_graph);
+        VertexValueMap tlt_values = boost::get(boost::vertex_name, tlt_graph);
+
+        // For every tracklet but source and sink
+        for (size_t i = 1; i < tlt_graph_size - 1; ++i)
+        {
+            Vertex u = tlt_indices[i];
+            size_t u_first_frame =
+                    ((core::Tracklet)tlt_values[u]).GetFirstFrameIndex();
+            size_t u_last_frame =
+                    ((core::Tracklet)tlt_values[u]).GetLastFrameIndex();
+
+            // Create edges between tracklets
+            for (size_t j = 1; j < tlt_graph_size - 1; ++j)
+            {
+                if (i != j)
+                {
+                    Vertex v = tlt_indices[j];
+                    size_t v_first_frame =
+                            ((core::Tracklet)tlt_values[v]).GetFirstFrameIndex();
+
+                    if (u_last_frame < v_first_frame)
+                    {
+                        boost::add_edge(u, v,
+                                        tlt_values[u].CompareTo(&tlt_values[v]),
+                                        tlt_graph);
+                    }
+                }
+            }
+
+            // From source
+            boost::add_edge(tlt_src, u,
+                            (u_first_frame + 1) * penalty_value_,
+                            tlt_graph);
+
+            // To sink
+            boost::add_edge(u, tlt_snk,
+                            (frame_count - u_last_frame) * penalty_value_,
+                            tlt_graph);
+        }
+
+        tlt_graph_ = tlt_graph;
+
+        return tlt_graph;
+    }
+
+    std::vector<core::Tracklet> TwoStage::ExtractTracks()
+    {
+        return ExtractTracks(tlt_graph_);
+    }
+
+    std::vector<core::Tracklet> TwoStage::ExtractTracks(DirectedGraph tlt_graph)
+    {
+        std::vector<core::Tracklet> tracks;
+
+        //TODO convert tracklets graph into list of tracklets/tracks
+
+        return tracks;
+    }
+}

+ 48 - 0
algo/TwoStage.h

@@ -0,0 +1,48 @@
+//
+// Created by wrede on 25.04.16.
+//
+
+#ifndef GBMOT_KOERNERTRACKING_H
+#define GBMOT_KOERNERTRACKING_H
+
+#include "Definitions.h"
+#include "../core/DetectionSequence.h"
+#include "../core/Tracklet.h"
+#include <boost/graph/dijkstra_shortest_paths.hpp>
+
+namespace algo
+{
+    class TwoStage
+    {
+    private:
+        // Maximum edge length to link frames
+        size_t max_frame_skip_;
+        // Edge value to link to source and sink
+        double penalty_value_;
+        // Maximum dijkstra iterations / number of tracklets to create
+        size_t max_tracklet_count_;
+        // The last created object graph
+        DirectedGraph obj_graph_;
+        // The last created tracklet graph
+        DirectedGraph tlt_graph_;
+        // The frame count of the last read sequence
+        size_t frame_count_;
+    public:
+        TwoStage(size_t max_frame_skip, double penalty_value,
+                 size_t max_tracklet_count);
+
+        // Creates a graph with vertices for every detected object
+        DirectedGraph CreateObjectGraph(core::DetectionSequence detections);
+
+        // Reduces the object graph into linked tracklets,
+        // if no graph is given, the last created is used
+        DirectedGraph CreateTrackletGraph();
+        DirectedGraph CreateTrackletGraph(DirectedGraph obj_graph, size_t frameCount);
+
+        std::vector<core::Tracklet> ExtractTracks();
+        std::vector<core::Tracklet> ExtractTracks(DirectedGraph tlt_graph);
+    };
+}
+
+
+#endif //GBMOT_KOERNERTRACKING_H

+ 20 - 0
core/Definitions.h

@@ -0,0 +1,20 @@
+//
+// Created by wrede on 22.04.16.
+//
+
+#ifndef GBMOT_CONSTANTS_H
+#define GBMOT_CONSTANTS_H
+
+#include <vector>
+#include <boost/graph/properties.hpp>
+#include <boost/graph/adjacency_list.hpp>
+#include <boost/graph/graph_selectors.hpp>
+#include "ObjectData.h"
+
+namespace core
+{
+    typedef std::vector<std::vector<std::vector<double>>> Vector3d;
+    typedef std::vector<std::vector<double>> Vector2d;
+}
+
+#endif //GBMOT_CONSTANTS_H

+ 84 - 0
core/DetectionSequence.cpp

@@ -0,0 +1,84 @@
+//
+// Created by wrede on 19.04.16.
+//
+
+#include "DetectionSequence.h"
+
+namespace core
+{
+    DetectionSequence::DetectionSequence(std::string name)
+    {
+        name_ = name;
+        objects_ = std::vector<std::vector<ObjectData>>();
+    }
+
+    void DetectionSequence::AddObject(ObjectData object_data)
+    {
+        if (object_data.GetFrameIndex() >= objects_.size())
+        {
+            objects_.push_back(std::vector<ObjectData>());
+        }
+        objects_[object_data.GetFrameIndex()].push_back(object_data);
+    }
+
+    void DetectionSequence::Clear()
+    {
+        objects_.clear();
+    }
+
+    std::string DetectionSequence::GetName() const
+    {
+        return name_;
+    }
+
+    ObjectData DetectionSequence::GetObject(
+            size_t frame_index, size_t object_index)
+    {
+        return objects_[frame_index][object_index];
+    }
+
+    size_t DetectionSequence::GetFrameCount() const
+    {
+        return objects_.size();
+    }
+
+    size_t DetectionSequence::GetObjectCount(size_t frame_index) const
+    {
+        return objects_[frame_index].size();
+    }
+
+    void DetectionSequence::Print(std::ostream &os) const
+    {
+        for (size_t frame_i = 0; frame_i < objects_.size(); ++frame_i)
+        {
+            os << "Frame: " << frame_i << std::endl;
+
+            for (size_t object_i = 0;
+                 object_i < objects_[frame_i].size();
+                 ++object_i)
+            {
+                os << objects_[frame_i][object_i] << std::endl;
+            }
+        }
+
+        os << std::endl;
+
+    }
+
+    std::ostream& operator<<(std::ostream &os, const DetectionSequence &obj)
+    {
+        obj.Print(os);
+        return os;
+    }
+}
+
+
+
+
+
+
+
+
+
+
+

+ 57 - 0
core/DetectionSequence.h

@@ -0,0 +1,57 @@
+//
+// Created by wrede on 19.04.16.
+//
+
+#ifndef GBMOT_DETECTIONSEQUENCE_H
+#define GBMOT_DETECTIONSEQUENCE_H
+
+
+#include "Definitions.h"
+#include <string>
+#include "ObjectData.h"
+#include "ObjectDataMap.h"
+
+namespace core
+{
+    // Class for storing a full sequence of frames,
+    // each with multiple detected objects.
+    class DetectionSequence
+    {
+    private:
+        // Name for display or identification purposes
+        std::string name_;
+        // Two-dimensional vector of smart pointers to all stored objects
+        std::vector<std::vector<ObjectData>> objects_;
+    protected:
+        // Print function, used in << operator
+        virtual void Print(std::ostream& os) const;
+    public:
+        // Creates a new DetectionSequence with the given name
+        // Initializes the storage for detected objects
+        DetectionSequence(std::string name);
+
+        // Adds a new object, creates a new frame vector if the given objects
+        // frame index is greater than the current frame vector size.
+        void AddObject(ObjectData object_data);
+
+        // Removes all objects.
+        void Clear();
+
+        // Gets the name
+        std::string GetName() const;
+
+        // Gets the object in the given frame with the index in that frame.
+        ObjectData GetObject(size_t frame_index, size_t object_index);
+
+        // Gets the frame count
+        size_t GetFrameCount() const;
+
+        // Gets the object count in the given frame
+        size_t GetObjectCount(size_t frame_index) const;
+
+        friend std::ostream& operator<<(std::ostream& os, const DetectionSequence& obj);
+    };
+}
+
+
+#endif //GBMOT_DETECTIONSEQUENCE_H

+ 53 - 0
core/ObjectData.cpp

@@ -0,0 +1,53 @@
+//
+// Created by wrede on 19.04.16.
+//
+
+#include "ObjectData.h"
+
+namespace core
+{
+    ObjectData::ObjectData()
+    {
+        frame_index_ = 0;
+        is_virtual_ = true;
+    }
+
+    ObjectData::ObjectData(std::size_t frame_index)
+    {
+        frame_index_ = frame_index;
+        is_virtual_ = false;
+    }
+
+    std::size_t ObjectData::GetFrameIndex() const
+    {
+        return frame_index_;
+    }
+
+    bool ObjectData::IsVirtual() const
+    {
+        return is_virtual_;
+    }
+
+    void ObjectData::Print(std::ostream &os) const
+    {
+        os << "Object in frame " << frame_index_;
+    }
+
+    double ObjectData::CompareTo(ObjectData *obj)
+    {
+        return 0.0;
+    }
+
+    std::ostream& operator<<(std::ostream &os, const ObjectData &obj)
+    {
+        obj.Print(os);
+        return os;
+    }
+}
+
+
+
+
+
+
+

+ 48 - 0
core/ObjectData.h

@@ -0,0 +1,48 @@
+//
+// Created by wrede on 19.04.16.
+//
+
+#ifndef GBMOT_NODEDATA_H
+#define GBMOT_NODEDATA_H
+
+
+#include <string>
+#include <unordered_map>
+#include <iostream>
+
+namespace core
+{
+    //TODO ObjectDataBase / ObjectBase / DataBase / AObject ...
+    // Base class for all detected objects.
+    // Stores the corresponding frame index.
+    class ObjectData
+    {
+    private:
+        bool is_virtual_;
+    protected:
+        // The frame the object was detected in
+        std::size_t frame_index_;
+        // Print function, used in << operator
+        virtual void Print(std::ostream& os) const;
+    public:
+        // Creates a new empty ObjectData (e.g. for virtual objects)
+        ObjectData();
+
+        // Creates a new ObjectData with the given frame index
+        ObjectData(std::size_t frame_index);
+
+        // Gets the corresponding frame index
+        std::size_t GetFrameIndex() const;
+
+        bool IsVirtual() const;
+
+        //TODO ObjectDataComparable / IComparable ...
+        //TODO find a better name
+        virtual double CompareTo(ObjectData *obj);
+
+        friend std::ostream& operator<<(std::ostream& os, const ObjectData& obj);
+    };
+}
+
+
+#endif //GBMOT_NODEDATA_H

+ 117 - 0
core/ObjectDataMap.cpp

@@ -0,0 +1,117 @@
+//
+// Created by wrede on 22.04.16.
+//
+
+#include "ObjectDataMap.h"
+
+namespace core
+{
+    void ObjectDataMap::Print(std::ostream& os) const
+    {
+        os << "ObjectDataMap{" << GetFrameIndex();
+
+        for (auto it = value_weight_map_.begin(); it != value_weight_map_.end(); ++it)
+        {
+            os << "," << it->first
+            << ":" << it->second.first
+            << "*" << it->second.second;
+        }
+
+        os << "}";
+    }
+
+    ObjectDataMap::ObjectDataMap(size_t frame_index)
+            : ObjectData(frame_index)
+    {
+        value_weight_map_ = std::unordered_map<std::string, std::pair<double, double>>();
+    }
+
+    ObjectDataMap::ObjectDataMap(size_t frame_index,
+                                 std::vector<std::string> keys,
+                                 std::vector<double> value_list)
+            : ObjectDataMap(frame_index)
+    {
+        double weight = 1.0 / value_list.size();
+
+        for (size_t i = 0; i < value_list.size() && i < keys.size(); ++i)
+        {
+            value_weight_map_[keys[i]] = std::make_pair(value_list[i], weight);
+        }
+    }
+
+    ObjectDataMap::ObjectDataMap(size_t frame_index,
+                                 std::vector<std::string> keys,
+                                 std::vector<double> value_list,
+                                 std::vector<double> weight_list)
+            : ObjectDataMap(frame_index)
+    {
+        for (size_t i = 0;
+             i < value_list.size() && i < keys.size() && i < weight_list.size();
+             ++i)
+        {
+            value_weight_map_[keys[i]] = std::make_pair(value_list[i], weight_list[i]);
+        }
+    }
+
+    ObjectDataMap::ObjectDataMap(size_t frame_index,
+                                 std::vector<std::string> keys,
+                                 std::vector<std::pair<double, double>> value_weight_list)
+            : ObjectDataMap(frame_index)
+    {
+        for (size_t i = 0; i < value_weight_list.size() && i < keys.size(); ++i)
+        {
+            value_weight_map_[keys[i]] = value_weight_list[i];
+        }
+    }
+
+    double ObjectDataMap::GetValue(std::string key)
+    {
+        return value_weight_map_[key].first;
+    }
+
+    double ObjectDataMap::GetWeight(std::string key)
+    {
+        return value_weight_map_[key].second;
+    }
+
+    void ObjectDataMap::PutValueWeight(std::string key, double value,
+                                       double weight)
+    {
+        value_weight_map_[key] = std::make_pair(value, weight);
+    }
+
+    void ObjectDataMap::PutValueWeight(std::string key,
+                                       std::pair<double, double> value_weight)
+    {
+        value_weight_map_[key] = value_weight;
+    }
+
+    double ObjectDataMap::CompareTo(ObjectData *obj)
+    {
+        ObjectDataMap* obj_dm = dynamic_cast<ObjectDataMap*>(obj);
+
+        if (obj_dm)
+        {
+            return CompareTo(obj_dm);
+        }
+        else
+        {
+            return 0.0;
+        }
+    }
+
+    double ObjectDataMap::CompareTo(ObjectDataMap *obj)
+    {
+        double diff = 0.0;
+
+        for (auto it = value_weight_map_.begin(); it != value_weight_map_.end(); ++it)
+        {
+            // |other_value - this_value| * this_weight;
+            diff += fabs(obj->value_weight_map_[it->first].first - it->second.first)
+                    * it->second.second;
+        }
+
+        return diff;
+    }
+}
+

+ 63 - 0
core/ObjectDataMap.h

@@ -0,0 +1,63 @@
+//
+// Created by wrede on 22.04.16.
+//
+
+#ifndef GBMOT_OBJECTDATAMAP_H
+#define GBMOT_OBJECTDATAMAP_H
+
+#include <string>
+#include <unordered_map>
+#include <vector>
+#include <cmath>
+#include "ObjectData.h"
+
+namespace core
+{
+    // Stores an map of key-value pairs for fast access and unified use.
+    class ObjectDataMap : public ObjectData
+    {
+    private:
+        // The stored value-weight pairs
+        std::unordered_map<std::string, std::pair<double, double>> value_weight_map_;
+    protected:
+        // Print function, used in << operator
+        virtual void Print(std::ostream& os) const;
+    public:
+        // Creates a new empty ObjectDataMap
+        ObjectDataMap(size_t frame_index);
+
+        // Creates a new ObjectDataMap with the given keys and values
+        // stored as triple with an equal weight for every value
+        ObjectDataMap(
+                size_t frame_index,
+                std::vector<std::string> keys,
+                std::vector<double> value_list);
+
+        ObjectDataMap(
+                size_t frame_index,
+                std::vector<std::string> keys,
+                std::vector<double> value_list,
+                std::vector<double> weight_list);
+
+        ObjectDataMap(
+                size_t frame_index,
+                std::vector<std::string> keys,
+                std::vector<std::pair<double, double>> value_weight_list);
+
+        // Gets the value
+        double GetValue(std::string key);
+
+        // Gets the weight
+        double GetWeight(std::string key);
+
+        //TODO find a better name
+        void PutValueWeight(std::string key, double value, double weight);
+        void PutValueWeight(std::string key, std::pair<double, double> value_weight);
+
+        virtual double CompareTo(ObjectData *obj);
+        virtual double CompareTo(ObjectDataMap *obj);
+    };
+}
+
+
+#endif //GBMOT_OBJECTDATAMAP_H

+ 88 - 0
core/Tracklet.cpp

@@ -0,0 +1,88 @@
+//
+// Created by wrede on 25.04.16.
+//
+
+#include "Tracklet.h"
+
+namespace core
+{
+    Tracklet::Tracklet() : ObjectData(0)
+    {
+        path_objects_ = std::vector<ObjectData>();
+        last_frame_index_ = 0;
+    }
+
+    Tracklet::Tracklet(ObjectData firstObject)
+            : ObjectData(firstObject.GetFrameIndex())
+    {
+        path_objects_ = std::vector<ObjectData>();
+        path_objects_.push_back(firstObject);
+
+        last_frame_index_ = firstObject.GetFrameIndex();
+    }
+
+    Tracklet::Tracklet(std::vector<ObjectData> path_objects,
+                       size_t first_frame_index, size_t last_frame_index)
+            : ObjectData(first_frame_index)
+    {
+        path_objects_ = path_objects;
+        last_frame_index_ = last_frame_index;
+    }
+
+    size_t Tracklet::GetFirstFrameIndex()
+    {
+        return frame_index_;
+    }
+
+    size_t Tracklet::GetLastFrameIndex()
+    {
+        return last_frame_index_;
+    }
+
+    ObjectData Tracklet::GetPathObject(size_t i)
+    {
+        return path_objects_[i];
+    }
+
+    void Tracklet::AddPathObjectFirst(ObjectData obj)
+    {
+        if (!obj.IsVirtual())
+        {
+            path_objects_.insert(path_objects_.begin(), obj);
+
+            if (obj.GetFrameIndex() < frame_index_)
+            {
+                frame_index_ = obj.GetFrameIndex();
+            }
+            else if (obj.GetFrameIndex() > last_frame_index_)
+            {
+                last_frame_index_ = obj.GetFrameIndex();
+            }
+        }
+    }
+
+    void Tracklet::AddPathObjectLast(ObjectData obj)
+    {
+        if (!obj.IsVirtual())
+        {
+            path_objects_.push_back(obj);
+
+            if (obj.GetFrameIndex() > last_frame_index_)
+            {
+                last_frame_index_ = obj.GetFrameIndex();
+            }
+            else if (obj.GetFrameIndex() < frame_index_)
+            {
+                frame_index_ = obj.GetFrameIndex();
+            }
+        }
+    }
+
+    double Tracklet::CompareTo(ObjectData *obj)
+    {
+        return 0.0;
+    }
+}
+
+
+

+ 40 - 0
core/Tracklet.h

@@ -0,0 +1,40 @@
+//
+// Created by wrede on 25.04.16.
+//
+
+#ifndef GBMOT_TRACKLET_H
+#define GBMOT_TRACKLET_H
+
+#include <cstdlib>
+#include <vector>
+#include "ObjectData.h"
+
+namespace core
+{
+    class Tracklet : public ObjectData
+    {
+    private:
+        std::vector<ObjectData> path_objects_;
+        size_t last_frame_index_;
+    public:
+        Tracklet();
+
+        Tracklet(ObjectData firstObject);
+
+        Tracklet(std::vector<ObjectData> path_objects,
+                 size_t first_frame_index, size_t last_frame_index);
+
+        void AddPathObjectFirst(ObjectData obj);
+        void AddPathObjectLast(ObjectData obj);
+        size_t GetFirstFrameIndex();
+        size_t GetLastFrameIndex();
+        ObjectData GetPathObject(size_t i);
+
+        //TODO point interpolation -> derived class
+        virtual double CompareTo(ObjectData *obj);
+        //TODO implement CompareTo -> derived class
+    };
+}
+
+
+#endif //GBMOT_TRACKLET_H

+ 53 - 0
main/main.cpp

@@ -0,0 +1,53 @@
+//
+// Created by wrede on 19.04.16.
+//
+//
+#include "../core/Definitions.h"
+#include "../core/DetectionSequence.h"
+#include "../util/IO.h"
+#include "../util/Parser.h"
+#include "../algo/TwoStage.h"
+
+int main(void)
+{
+    std::cout << "Initializing values\n";
+    const char delimiter = ';';
+    const std::string full_file("/home/wrede/Dokumente/tmt_detections.csv");
+    const std::string test_file("/home/wrede/Dokumente/test.csv");
+    const std::string sequence_name("TMT_Detections_Raw");
+    size_t max_frame_skip = 1;
+    double penalty_value = 20.0;
+    size_t max_tracklet_count = 1;
+    std::vector<std::string> keys;
+    core::Vector3d values;
+
+    keys.push_back("angle");
+    keys.push_back("score");
+    keys.push_back("x");
+    keys.push_back("y");
+
+    std::cout << "Initializing sequence\n";
+    core::DetectionSequence sequence(sequence_name);
+
+    std::cout << "Reading CSV file\n";
+    util::io::ReadCSV(full_file, delimiter, values);
+
+    std::cout << "Parsing data\n";
+    util::parser::ParseObjectDataMap(keys, values, sequence);
+
+    std::cout << "Initializing algorithm\n";
+    algo::TwoStage two_stage(max_frame_skip, penalty_value, max_tracklet_count);
+
+    std::cout << "Creating object graph\n";
+    two_stage.CreateObjectGraph(sequence);
+
+    std::cout << "Creating tracklet graph twice\n";
+    two_stage.CreateTrackletGraph(two_stage.CreateTrackletGraph(), sequence.GetFrameCount());
+
+    std::cout << "Extracting final paths\n";
+    two_stage.ExtractTracks();
+
+    std::cout << "Finished successfully\n" << std::flush;
+
+    return 0;
+}

+ 395 - 0
main/preview.cpp

@@ -0,0 +1,395 @@
+/*
+ * markerAssociationDP.cpp
+ *
+ *  Created on: Feb 6, 2013
+ *      Author: koerner
+ */
+
+
+#include <boost/config.hpp>
+
+#include <boost/graph/adjacency_list.hpp>
+#include <boost/graph/dijkstra_shortest_paths.hpp>
+#include <boost/graph/graph_traits.hpp>
+#include <boost/graph/iteration_macros.hpp>
+#include <boost/graph/properties.hpp>
+
+#include <boost/property_map/property_map.hpp>
+
+#include <boost/program_options.hpp>
+#include <boost/format.hpp>
+
+#include <opencv2/opencv.hpp>
+
+#include <limits>
+#include <iostream>
+#include <fstream>
+#include <utility>
+#include <vector>
+
+// command line parameters
+struct Params {
+    // filename of 3d point filen
+    std::string strPointClouds;
+
+    // file in which the results are saved
+    std::string outputFilename;
+
+    // max link distance
+    double maxLinkDistance;
+
+    // max angular distance
+    double maxAngularDistance;
+
+    // number of tracklets to be extracted
+    size_t trackletCount;
+
+    // number of allowed frames which can be skipped within one tracklet
+    size_t allowedFrameSkip;
+};
+
+// each node contains this struct to provide information about the 3d point
+struct NodeInfo {
+    // creates virtual marker
+    NodeInfo() {
+        this->position = cv::Point3d(0, 0, 0);
+        this->frame = std::numeric_limits<size_t>::max();
+    }
+
+    // create normal marker info
+    NodeInfo(const cv::Point3d &_position, const size_t &_frame) {
+        this->position = _position;
+        this->frame = _frame;
+    }
+
+    //
+    bool isVirtual() {
+        return (this->frame == std::numeric_limits<size_t>::max());
+    }
+
+    //
+    cv::Point3d position;
+    size_t frame;
+};
+
+// get command line parameters
+bool evalCmdLine(int argc, char **argv, Params &p) {
+    // define parameters
+    boost::program_options::options_description desc("Allowed options");
+    desc.add_options()
+            ("help", "produce help message")
+            ("pointFile,p",
+             boost::program_options::value< std::string >(),
+             "3d point clouds file containing the detections for each frame")
+            ("outputFile,o",
+             boost::program_options::value< std::string >(),
+             "output file for the generated tracklets")
+            ("trackletCount,c",
+             boost::program_options::value< size_t >()->default_value(1),
+             "number of tracklets to be extracted")
+            ("maxLinkDistance,d",
+             boost::program_options::value< double >()->default_value(1.0),
+             "maximum Euclidean distance (in px) allowed for two detections to get linked")
+            ("maxAngularDistance,a",
+             boost::program_options::value< double >()->default_value(20.0),
+             "maximum angular distance (in deg) allowed for two detections to get linked")
+            ("allowedFrameSkip,s",
+             boost::program_options::value< size_t >()->default_value(1),
+             "number of allowed frames which can be skipped within one tracklet (>= 1)")
+            ;
+
+    // parse parameters
+    boost::program_options::variables_map vm;
+    boost::program_options::store(boost::program_options::parse_command_line(argc, argv, desc), vm);
+    boost::program_options::notify(vm);
+
+    if (vm.count("help")) {
+        std::cout << desc << std::endl;
+        exit(EXIT_SUCCESS);
+    }
+
+    // check parameters
+    if (vm.count("pointFile")) {
+        p.strPointClouds = vm["pointFile"].as< std::string >();
+    } else {
+        std::cerr << "No points file passed!" << std::endl;
+        std::cout << desc << std::endl;
+        exit(EXIT_SUCCESS);
+    }
+    if (vm.count("outputFile")) {
+        p.outputFilename = vm["outputFile"].as< std::string >();
+    } else {
+        std::cerr << "No output file passed!" << std::endl;
+        std::cout << desc << std::endl;
+        exit(EXIT_SUCCESS);
+    }
+
+    p.maxLinkDistance = vm["maxLinkDistance"].as< double >();
+    p.maxAngularDistance = vm["maxAngularDistance"].as< double >();
+    p.trackletCount = vm["trackletCount"].as< size_t >();
+    p.allowedFrameSkip = vm["allowedFrameSkip"].as< size_t >();
+
+    return true;
+}
+
+void readPoints3d(const std::string &filename,
+                  std::vector< std::vector< cv::Point3d > > &clouds) {
+    std::cout << "Reading points from file " << filename << std::endl;
+    std::ifstream pointFile(filename.c_str(), std::ios_base::in);
+    if (!pointFile.is_open()) {
+        std::cerr << "Could not open file " << std::endl;
+        return;
+    }
+
+    uint frame = 0;
+    uint detections = 0;
+    std::string line;
+    while (std::getline(pointFile, line)) {
+        std::cerr << "\rReading points of frame " << frame + 1 << "... ";
+
+        std::vector< cv::Point3d > cloud;
+
+        std::stringstream split(line + " ");
+        double tmpValue;
+        int tmpDim = 0;
+        cv::Point3d tmpPoint;
+
+        while ((split >> tmpValue).good()) {
+            switch (tmpDim) {
+                case 0:
+                    tmpPoint.x = tmpValue;
+                    break;
+                case 1:
+                    tmpPoint.y = tmpValue;
+                    break;
+                case 2:
+                    tmpPoint.z = tmpValue;
+                    cloud.push_back(tmpPoint);
+                    //std::cout << cv::Mat(tmpPoint) << std::endl;
+                    detections++;
+                    break;
+            }
+            tmpDim = (tmpDim + 1) % 3;
+        }
+
+        clouds.push_back(cloud);
+
+        std::cout << cloud.size() << std::flush;
+
+        frame++;
+    }
+
+    uint nFrames = clouds.size();
+    std::cerr << std::endl << "Read " << nFrames << " frames (" << detections << " detections)" << std::endl;
+}
+
+void createTracklets(const std::vector< std::vector< cv::Point3d > > &clouds, const Params &params) {
+    typedef double Weight;
+    typedef boost::property< boost::edge_weight_t, Weight > WeightProperty;
+//  typedef boost::property< boost::vertex_name_t, std::string > NameProperty;
+    typedef boost::property< boost::vertex_name_t, NodeInfo > NameProperty;
+
+    typedef boost::adjacency_list<
+            boost::listS,
+            boost::vecS,
+            boost::directedS,
+            NameProperty,
+            WeightProperty > Graph;
+
+    typedef boost::graph_traits< Graph >::vertex_descriptor Vertex;
+
+    typedef boost::property_map< Graph, boost::vertex_index_t >::type IndexMap;
+    typedef boost::property_map< Graph, boost::vertex_name_t >::type NameMap;
+
+    typedef boost::iterator_property_map< Vertex*, IndexMap, Vertex, Vertex& > PredecessorMap;
+    typedef boost::iterator_property_map< Weight*, IndexMap, Weight, Weight& > DistanceMap;
+
+    // Create a graph
+    Graph g;
+    std::vector< std::vector< Vertex > > vertices;
+
+    size_t frameStart = 0;
+    size_t frameMax = clouds.size();
+//  frameMax = 900;
+
+    // adding vertices
+    for (size_t frame = frameStart; frame < frameMax; ++frame) {
+        std::cout << "\rAdding layer for frame " << frame + 1; // << std::flush;
+
+        std::vector< cv::Point3d > p = clouds[frame];
+        std::vector< Vertex > layer;
+
+        for (size_t marker = 0; marker < p.size(); ++marker) {
+            Vertex v = boost::add_vertex(NodeInfo(p[marker], frame), g);
+            layer.push_back(v);
+        }
+        vertices.push_back(layer);
+        std::cout << ": " << p.size() << ", " << layer.size() << ", " << vertices.rbegin()->size() << std::flush;
+    }
+
+    // virtual source and sink
+    Vertex vSource = boost::add_vertex(NodeInfo(), g);
+    Vertex vSink = boost::add_vertex(NodeInfo(), g);
+
+    NameMap nameMap = boost::get(boost::vertex_name, g);
+
+    std::cout << std::endl;
+
+    // adding edges (layer eq frame)
+    for (size_t iLayers = 0; iLayers < vertices.size(); ++iLayers) {
+        // all points of frame "iLayers"
+        std::vector< Vertex > p = vertices[iLayers];
+
+        // for each point of layer "iLayers"
+        for (size_t iP = 0; iP < p.size(); ++iP) {
+            Vertex vP = p[iP];
+
+            // create edges to source and sink
+            std::cout << "\rAdding edges " << iLayers << "-> vSink" << std::flush;
+if (iLayers <= 100)
+ boost::add_edge(vSource, vP, (iLayers + 1) * (params.maxLinkDistance + params.maxAngularDistance), g);
+if (iLayers + 100 >= vertices.size())
+ boost::add_edge(vP, vSink, (vertices.size() - iLayers) * (params.maxLinkDistance + params.maxAngularDistance), g);
+
+            // create shortcuts to next-next layers
+            for (size_t iOffset = 1; iOffset <= params.allowedFrameSkip; ++iOffset) {
+                if (iLayers + iOffset < vertices.size() - 1) {
+                    std::cout << "\rAdding edges for frames " << iLayers << "->" << (iLayers + iOffset) << std::flush;
+
+                    // all points of frame "iLayers" + "iOffset"
+                    std::vector< Vertex > q = vertices[iLayers + iOffset];
+
+                    // for each node of neighboring layers (in time)
+                    for (size_t iQ = 0; iQ < q.size(); ++iQ) {
+                        Vertex vQ = q[iQ];
+
+                        // euclidean distance between iP and iQ
+                        cv::Point3d ptP = nameMap[vP].position;
+                        cv::Point3d ptQ = nameMap[vQ].position;
+                        double dx = ptP.x - ptQ.x;
+                        double dy = ptP.y - ptQ.y;
+                        double dz = ptP.z - ptQ.z;
+                        Weight distPosition = sqrt(dx * dx + dy * dy);
+                        Weight distAngular = std::max(0.0, abs(dz) - 5.0);
+
+                        // edge weight
+                        //std::cout << " x" << params.maxLinkDistance << "," << params.maxAngularDistance << "; " << distPosition << "," << distAngular << "," << iOffset << " = " << 0.1 * (distPosition + 0.5 * distAngular) * double(iOffset) << std::endl;
+                        if (false || (distPosition < params.maxLinkDistance && distAngular < params.maxAngularDistance)) {
+                            // if we discard one layer, the edge weight should be higher!
+                            boost::add_edge(vP, vQ, 0.1 * (distPosition + 0.5 * distAngular) * double(iOffset), g);
+                            //boost::add_edge(vP, vQ, 0.001 * Weight((1.0 + 0.33 * distPosition) * (1.0 + distAngular) * Weight(1.0 + iOffset)), g);
+                            //std::cout << "x" << params.maxAngularDistance << "," << distPosition << "," << distAngular << "," << iOffset << std::endl;
+                            //boost::add_edge(vP, vQ, Weight(dist * Weight(1 + 0.5 * iOffset)), g);
+                            //              boost::add_edge(vP, vQ, Weight(dist), g);
+                        }
+                    }
+                }
+            }
+        }
+    }
+    std::cout << std::endl;
+
+    // Create things for Dijkstra
+    std::vector< Vertex > predecessors(boost::num_vertices(g));  // To store parents
+    std::vector< Weight > distances(boost::num_vertices(g));  // To store distances
+
+    IndexMap indexMap = boost::get(boost::vertex_index, g);
+    PredecessorMap predecessorMap(&predecessors[0], indexMap);
+    DistanceMap distanceMap(&distances[0], indexMap);
+
+    // output file
+    std::ofstream f(params.outputFilename.c_str());
+
+    // Compute shortest paths from input layer vertices to the sink
+    for (size_t tracklet = 0; tracklet < params.trackletCount; ++tracklet) {
+        boost::dijkstra_shortest_paths(g,         // the graph
+                                       vSource,   // the source vertex
+                                       boost::distance_map(distanceMap).predecessor_map(predecessorMap));
+
+        // Output results
+//    std::cout << "distances and parents:" << std::endl;
+//  NameMap nameMap = boost::get(boost::vertex_name, g);
+
+//  BGL_FORALL_VERTICES(v, g, Graph) {
+//    for (size_t i = 0; i < vertices.size(); ++i) {
+//      for (size_t j = 0; j < vertices[i].size(); ++j) {
+//        Vertex *v = &vertices[i][j];
+//        std::cout << "distance(" << nameMap[vSink] << ", " << nameMap[*v] << ") = " << distanceMap[*v] << ", ";
+//        std::cout << "predecessor(" << nameMap[*v] << ") = " << nameMap[predecessorMap[*v]] << std::endl;
+//      }
+//    }
+//
+//    std::cout << std::endl;
+
+
+        // Extract the shortest path
+
+        typedef std::vector< Graph::edge_descriptor > PathType;
+
+        PathType path;
+
+        Vertex v = vSink;  // We want to start at the sink and work our way back to the source
+        for (Vertex u = predecessorMap[v]; u != v; v = u, u = predecessorMap[v]) {
+            std::pair< Graph::edge_descriptor, bool > edgePair = boost::edge(u, v, g);
+            Graph::edge_descriptor edge = edgePair.first;
+            path.push_back(edge);
+        }
+
+        // Write shortest path
+        //int c = 0;
+        bool vFirst = true;
+        //std::cout << "Shortest path from vSource to vSink: " << std::endl;
+        for (PathType::reverse_iterator pathIterator = path.rbegin(); pathIterator != path.rend(); ++pathIterator) {
+            // print all non-virtual nodes of this path
+            if (!nameMap[boost::source(*pathIterator, g)].isVirtual()) {
+                //std::cout << nameMap[boost::source(*pathIterator, g)].position << " -> " << nameMap[boost::target(*pathIterator, g)].position
+                //        << " = "
+                //        << boost::get(boost::edge_weight, g, *pathIterator) << std::endl;
+
+                f << nameMap[boost::source(*pathIterator, g)].position.x << " "
+                << nameMap[boost::source(*pathIterator, g)].position.y << " "
+                << nameMap[boost::source(*pathIterator, g)].position.z << " "
+                << nameMap[boost::source(*pathIterator, g)].frame << " ";
+            }
+
+            // for each node of the path, set the weights of all outgoing edges to Inf (for Dijkstra equivalent to deletion of the node)
+            if (!vFirst) {
+                std::pair<Graph::out_edge_iterator, Graph::out_edge_iterator> edgeIts = boost::out_edges(boost::source(*pathIterator, g), g);
+                //size_t nEdges = 0;
+                for (Graph::out_edge_iterator edgeIt = edgeIts.first; edgeIt != edgeIts.second; edgeIt++) {
+                    boost::get(boost::edge_weight, g, *edgeIt) = std::numeric_limits<Weight>::infinity();
+                    //std::cout << "w = " << boost::get(boost::edge_weight, g, *edgeIt) << std::endl;
+                    //nEdges++;
+                }
+                //std::cout << "Found " << nEdges << " out edges for this node" << std::endl;
+                //getchar();
+            }
+
+            vFirst = false;
+        }
+
+        f << "\n";
+
+        std::cout << "Found tracklet #" << tracklet + 1 << "/" << params.trackletCount << ": " << path.size() << " nodes, distance: " << distanceMap[vSink] << std::endl;
+    }
+    f.close();
+}
+
+int main(int argc, char** argv) {
+    // get command line parameters
+    Params p;
+    if (!evalCmdLine(argc, argv, p)) {
+        std::cerr << "Error while parsing arguments!" << std::endl;
+        return 1;
+    }
+
+    std::vector< std::vector< cv::Point3d > > points3dLists;
+    if (!p.strPointClouds.empty()) {
+        readPoints3d(p.strPointClouds, points3dLists);
+    }
+
+    createTracklets(points3dLists, p);
+
+    return EXIT_SUCCESS;
+}
+

+ 735 - 0
main/preview2.cpp

@@ -0,0 +1,735 @@
+/*
+ * markerAssociationDP.cpp
+ *
+ *  Created on: Feb 6, 2013
+ *      Author: koerner
+ */
+
+#include <boost/config.hpp>
+
+#include <boost/graph/adjacency_list.hpp>
+#include <boost/graph/dijkstra_shortest_paths.hpp>
+#include <boost/graph/graph_traits.hpp>
+#include <boost/graph/graphviz.hpp>
+#include <boost/graph/iteration_macros.hpp>
+#include <boost/graph/properties.hpp>
+
+#include <boost/property_map/property_map.hpp>
+
+#include <boost/program_options.hpp>
+#include <boost/format.hpp>
+
+#include <opencv2/opencv.hpp>
+
+#include <limits>
+#include <iostream>
+#include <fstream>
+#include <utility>
+#include <vector>
+
+/**
+ * command line parameters
+ */
+
+// command line parameters
+struct Params {
+    // filename of 3d point filen
+    std::string strTracklets;
+
+    // output filename
+    std::string outputFilename;
+
+    // number of tracklets to be extracted
+    size_t trackCount;
+
+    // maximum temporal distance (in frames) for two tracklets to get linked
+    size_t maxTemporalDist;
+
+    // maximum spatial distance (in mm) for two tracklets to get linked
+    double maxSpatialDist;
+
+    // minimum length of tracks to be selected
+    double minTrackLength;
+};
+
+// get command line parameters
+bool evalCmdLine(int argc, char **argv, Params &p) {
+    // define parameters
+    boost::program_options::options_description desc("Allowed options");
+    desc.add_options()
+            ("help", "produce help message")
+            ("trackletFile,t", boost::program_options::value< std::string >(), "tracklet input file (as generated by \"graphBasedTracklets\")")
+            ("outputFile,o", boost::program_options::value< std::string >(), "output file")
+            ("trackCount,c", boost::program_options::value< size_t >()->default_value(1), "number of tracks to be extracted")
+            ("maxSpatialDist,d",
+             boost::program_options::value< double >()->default_value(1.5),
+             "maximum spatial distance (in mm) for two tracklets to get linked")
+            ("maxTemporalDist,s",
+             boost::program_options::value< size_t >()->default_value(150),
+             "maximum temporal distance (in frames) for two tracklets to get linked")
+            ("minTrackLength,l", boost::program_options::value< double >()->default_value(0.85), "minimum length of tracks to be selected (if value is <= 1, it is interpreted as fraction of the sequence length)")
+            ;
+
+    // parse parameters
+    boost::program_options::variables_map vm;
+    boost::program_options::store(boost::program_options::parse_command_line(argc, argv, desc), vm);
+    boost::program_options::notify(vm);
+
+    if (vm.count("help") || vm.empty()) {
+        std::cout << desc << std::endl;
+        exit(EXIT_SUCCESS);
+    }
+
+    // default parameters
+    if (vm.count("trackletFile")) {
+        p.strTracklets = vm["trackletFile"].as< std::string >();
+    } else {
+        std::cerr << "No tracklet input file passed!\n";
+        std::cout << desc << std::endl;
+        exit(EXIT_SUCCESS);
+    }
+    if (vm.count("outputFile")) {
+        p.outputFilename = vm["outputFile"].as< std::string >();
+    } else {
+        std::cerr << "No output file passed!" << std::endl;
+        std::cout << desc << std::endl;
+        exit(EXIT_SUCCESS);
+    }
+
+    p.trackCount = vm["trackCount"].as< size_t >();
+    p.maxTemporalDist = vm["maxTemporalDist"].as< size_t >();
+    p.maxSpatialDist = vm["maxSpatialDist"].as< double >();
+    p.minTrackLength = vm["minTrackLength"].as< double >();
+
+    return true;
+}
+
+/**
+ * helper functions
+ */
+
+double mean(const std::vector< double > &values) {
+    if (values.size() == 0) {
+        return 0.0;
+    }
+
+    double sum = 0.0;
+    for (size_t k = 0; k < values.size(); k++) {
+        sum += values[k];
+    }
+
+    return sum / double(values.size());
+}
+
+/**
+ * tracklet handling
+ */
+
+// tracklet class
+class Tracklet {
+public:
+    // constructors
+    Tracklet() {
+        this->rawPoints.clear();
+        this->ids.clear();
+    }
+
+    Tracklet(const Tracklet &tracklet) {
+        this->rawPoints = tracklet.rawPoints;
+        this->ids = tracklet.ids;
+    }
+
+    Tracklet& operator=(const Tracklet &tracklet) {
+        this->rawPoints = tracklet.rawPoints;
+        this->ids = tracklet.ids;
+        return *this;
+    }
+
+    // getters/setters
+    bool isVirtual() const {
+        return this->rawPoints.empty();
+    }
+
+    std::string getIdString() const {
+        std::stringstream ss;
+        for (size_t k = 0; k < this->ids.size(); k++) {
+            if (k > 0) {
+                ss << ",";
+            }
+            ss << ids[k];
+        }
+        return ss.str();
+    }
+
+    std::vector< size_t > &getIds() {
+        return this->ids;
+    }
+
+    const std::vector< size_t > &getIds() const {
+        return this->ids;
+    }
+
+    void appendId(size_t _id) {
+        this->ids.push_back(_id);
+    }
+
+    void appendIds(std::vector< size_t > _ids) {
+        this->ids.insert(this->ids.end(), _ids.begin(), _ids.end());
+    }
+
+    size_t getFirstFrame() const {
+        //return this->firstFrame;
+        return this->rawPoints.begin()->first;
+    }
+
+    size_t getLastFrame() const {
+        return this->rawPoints.rbegin()->first;
+    }
+
+    size_t getRange() const {
+        return this->getLastFrame() - this->getFirstFrame() + 1;
+    }
+
+    const cv::Point3d &getFirstPoint() const {
+        return this->rawPoints.begin()->second;
+    }
+
+    const cv::Point3d &getLastPoint() const {
+        return this->rawPoints.rbegin()->second;
+    }
+
+    void printDebugInfo() const {
+        std::cout << "=== DEBUG ===" << std::endl;
+        std::cout << "IDs: " << this->getIdString() << std::endl;
+        std::cout << "frames: (" << this->getFirstFrame() << "," << this->getLastFrame() << ")" << std::endl;
+        std::cout << "raw point count: " << this->pRawPoints()->size() << std::endl;
+        std::cout << "first point: " << this->rawPoints.begin()->second << std::endl;
+    }
+
+    void interpolatePoints(void) {
+        for (std::map< size_t, cv::Point3d >::iterator it = this->rawPoints.begin();
+             it != this->rawPoints.end(); ++it) {
+            cv::Point3d p = it->second;
+
+//        std::cout << it->first << p;
+
+            std::map< size_t, cv::Point3d >::iterator itNext = it;
+            std::advance(itNext, 1);
+
+            if (itNext != this->rawPoints.end()) {
+                size_t dT = itNext->first - it->first;
+
+                cv::Point3d q = itNext->second;
+                cv::Point3d v = (q - p) * (1.0 / (double)(dT));
+
+                if (dT > 0) {
+                    for (size_t i = 1; i < dT; ++i) {
+//              std::cout << " -> " << it->first + i << (p + v * (double)i);
+                        this->rawPoints.insert(std::make_pair(it->first + i, p + v * (double)i));
+                    }
+                }
+            }
+            //std::cout << std::endl;
+        }
+    }
+
+    const double getMatchingScore(const Tracklet &tracklet) {
+        size_t tStart = std::max(this->getFirstFrame(), tracklet.getFirstFrame());
+        size_t tEnd = std::min(this->getLastFrame(), tracklet.getLastFrame());
+
+        int tDiff = tEnd - tStart;
+        double tRatio = 2.0 * (double)tDiff / (double)(this->pRawPoints()->size() + tracklet.pRawPoints()->size() - 2);
+
+        if (tRatio > 0.0) {
+            const std::map< size_t, cv::Point3d >* p = this->pRawPoints();
+            const std::map< size_t, cv::Point3d >* q = tracklet.pRawPoints();
+            double dist = 0.0f;
+            for (size_t i = tStart; i <= tEnd; ++i) {
+                dist += cv::norm(p->at(i) - q->at(i));
+            }
+            dist /= (double)tDiff;
+
+            return dist;
+        } else {
+            return std::numeric_limits< double >::max();
+        }
+    }
+
+    const std::map< size_t, cv::Point3d >* pRawPoints() const {
+        return &this->rawPoints;
+    }
+
+    void addPoint(size_t _frame, cv::Point3d &_point) {
+        this->rawPoints[_frame] = _point;
+    }
+
+    void append(Tracklet &tracklet) {
+        this->rawPoints.insert(tracklet.pRawPoints()->begin(), tracklet.pRawPoints()->end());
+        this->appendIds(tracklet.getIds());
+    }
+
+    void getVelocities(std::vector< double > &velocities) {
+        // clear object to be returned
+        velocities = std::vector< double >(0);
+
+        // need at least two points
+        if (this->rawPoints.size() <= 1) {
+            return;
+        }
+
+        // for consecutive points (in time), calculate magnitude of difference (a.k.a. velocity)
+        for (std::map< size_t, cv::Point3d >::iterator it = this->rawPoints.begin(); it != this->rawPoints.end(); it++) {
+            std::map< size_t, cv::Point3d >::iterator it2 = it;
+            it2++;
+            if (it2 != this->rawPoints.end()) {
+                double dx = it->second.x - it2->second.x;
+                double dy = it->second.y - it2->second.y;
+                double v = sqrt(dx * dx + dy * dy);
+                velocities.push_back(v);
+            }
+        }
+    }
+
+    // linking cost to another tracklet (assuming "this" comes before "target" in time)
+    double linkingCost(Tracklet &target, const Params &params) {
+        // temporal distance (if "target" does not start after "this" ends, linking is not possible)
+        int dTemporal = int(target.getFirstFrame()) - this->getLastFrame();
+        if ((dTemporal <= 0) || (dTemporal > int(params.maxTemporalDist))) {
+            return std::numeric_limits< double >::infinity();
+        }
+
+        // distance between nearest end-points
+        double dx = target.getFirstPoint().x - this->getLastPoint().x;
+        double dy = target.getFirstPoint().y - this->getLastPoint().y;
+        double dSpatial = sqrt(dx * dx + dy * dy);
+        if (dSpatial > params.maxSpatialDist) {
+            return std::numeric_limits< double >::infinity();
+        }
+
+        // angular distance between nearest end-points
+        double dz = target.getFirstPoint().z - this->getLastPoint().z;
+        double dAngular = std::max(0.0, abs(dz) - 5.0);
+        if (dAngular > 25) {
+            return std::numeric_limits< double >::infinity();
+        }
+
+        // difference between mean velocities
+        std::vector< double > velocitiesThis, velocitiesTarget;
+        this->getVelocities(velocitiesThis);
+        target.getVelocities(velocitiesTarget);
+        double dMeanVelocities = std::abs(mean(velocitiesThis) - mean(velocitiesTarget));
+
+        //
+
+        // final cost
+        return 0.1 * double(dTemporal) * (dAngular + dSpatial + dMeanVelocities);
+    }
+
+protected:
+    std::map< size_t, cv::Point3d > rawPoints;
+    std::vector< size_t > ids;
+};
+
+void mergeDuplicates(std::vector< Tracklet > &tracklets, const double maxScore = 2.0) {
+    // iterate over all tracklets
+    for (std::vector< Tracklet >::iterator it1 = tracklets.begin(); it1 != tracklets.end(); ++it1) {
+
+        // get maximum time span
+        size_t tMin = it1->getFirstFrame();
+        size_t tMax = it1->getLastFrame();
+
+        // collect tracklets to be merged
+        std::vector< std::vector< Tracklet >::iterator > toBeMerged;
+
+        // first one by default
+        toBeMerged.push_back(it1);
+
+        // iterate over all succeeding tracklets
+        for (std::vector< Tracklet >::iterator it2 = it1 + 1; it2 != tracklets.end(); ++it2) {
+            double match = it1->getMatchingScore(*it2);
+            if (match < maxScore) {
+                /*
+                 std::cout
+                 << std::distance(tracklets.begin(), it1) << " " << it1->getIdString() << " -> "
+                 << std::distance(tracklets.begin(), it2) << " " << it2->getIdString() << ": "
+                 << match << std::endl;
+                 */
+
+                // update time span
+                tMin = std::min(tMin, it2->getFirstFrame());
+                tMax = std::max(tMax, it2->getLastFrame());
+
+                // add to merge candidates
+                toBeMerged.push_back(it2);
+            }
+        }
+
+        if (toBeMerged.size() > 1) {
+            std::cout << "Merging " << toBeMerged.size() << " tracks!" << std::endl;
+            //std::cout << "tMin: " << tMin << ", tMax: " << tMax << std::endl;
+
+            // our new tracklet
+            Tracklet mergedTracklet;
+
+            // iterate over complete time span
+            for (size_t t = tMin; t <= tMax; ++t) {
+                cv::Point3d p;
+                size_t nP = 0;
+
+                // get point for current time step of each merging candidate
+                for (size_t i = 0; i < toBeMerged.size(); ++i) {
+                    std::map< size_t, cv::Point3d >::const_iterator itT = toBeMerged[i]->pRawPoints()->find(t);
+                    if (itT != toBeMerged[i]->pRawPoints()->end()) {
+                        p = p + itT->second;
+                        ++nP;
+                    }
+                }
+
+                // result is the average of all candidate points
+                p *= 1.0 / (double)nP;
+
+                // add to new tracklet
+                mergedTracklet.addPoint(t, p);
+            }
+
+            // delete merged tracks
+            for (int i = toBeMerged.size() - 1; i >= 0; --i) {
+                mergedTracklet.appendIds(toBeMerged[i]->getIds());
+                if (i > 0) {
+                    tracklets.erase(toBeMerged[i]);
+                }
+            }
+
+            // replace first tracklet by the merged one
+            *it1 = mergedTracklet;
+        }
+    }
+}
+
+void readTracklets(const std::string &filename,
+                   std::vector< Tracklet > &tracklets,
+                   size_t &nFrames) {
+    // open file
+    std::cout << "Reading tracklets from file " << filename << std::endl;
+    std::ifstream pointFile(filename.c_str(), std::ios_base::in);
+    if (!pointFile.is_open()) {
+        std::cerr << "Could not open file " << std::endl;
+        return;
+    }
+
+    // result object
+    tracklets.clear();
+
+    // read in all 3d points
+    std::string line;
+
+    // for each line...
+    nFrames = 0;
+    size_t id = 0;
+    while (std::getline(pointFile, line)) {
+        // print current frame number
+        //std::cerr << "\rReading points... " << std::flush;
+
+        // points of the current frame
+        std::vector< cv::Point3d > tracklet;
+
+        // read 3d points for each camera
+        Tracklet tmpTracklet;
+        tmpTracklet.appendId(id++);
+
+        std::stringstream split(line);
+        cv::Point3d tmpPoint;
+        size_t frame = 0;
+
+        // for each point...
+        while (split.good()) {
+            // read all points of this line
+            if ((split >> tmpPoint.x >> tmpPoint.y >> tmpPoint.z >> frame).good()) {
+                tmpTracklet.addPoint(frame, tmpPoint);
+
+                // update global frame count
+                nFrames = std::max(nFrames, tmpTracklet.getLastFrame() + 1);
+            }
+        }
+
+        // add tracklet to list
+        tmpTracklet.interpolatePoints();
+        tracklets.push_back(tmpTracklet);
+    }
+    uint nTracklets = tracklets.size();
+    std::cerr << std::endl << "Read " << nTracklets << " tracklets" << std::endl;
+
+    // merge duplicate tracklets
+    mergeDuplicates(tracklets);
+    nTracklets = tracklets.size();
+    std::cerr << std::endl << "Merged to " << nTracklets << " tracklets" << std::endl;
+}
+
+/**
+ * graph-based typedefs
+ */
+
+typedef double Weight;
+typedef boost::property< boost::edge_weight_t, Weight > WeightProperty;
+typedef boost::property< boost::vertex_name_t, Tracklet > NameProperty;
+
+typedef boost::adjacency_list<
+        boost::listS,
+        boost::vecS,
+        boost::directedS,
+        NameProperty,
+        WeightProperty > Graph;
+
+typedef boost::graph_traits< Graph >::vertex_descriptor Vertex;
+
+typedef boost::property_map< Graph, boost::vertex_index_t >::type IndexMap;
+typedef boost::property_map< Graph, boost::vertex_name_t >::type NameMap;
+
+typedef boost::iterator_property_map< Vertex*, IndexMap, Vertex, Vertex& > PredecessorMap;
+typedef boost::iterator_property_map< Weight*, IndexMap, Weight, Weight& > DistanceMap;
+
+//
+template< class G >
+class VertexWriter {
+public:
+    VertexWriter(G _g) :
+            g(_g) {
+        this->nameMap = boost::get(boost::vertex_name, g);
+    }
+
+    template< class VertexOrEdge >
+    void operator()(std::ostream& out, const VertexOrEdge& v) const {
+        if (nameMap[v].isVirtual()) {
+            out << "[label=\"" << nameMap[v].getIdString() << "\",shape=\"circle\"]";
+        } else {
+            out << "[label=\"" << nameMap[v].getIdString() << ":\\n(" << nameMap[v].getFirstFrame() << ","
+            << nameMap[v].getLastFrame() << ")\",shape=\"box\"]";
+        }
+        //
+    }
+private:
+    G g;
+    NameMap nameMap;
+};
+
+//
+template< class G >
+class EdgeWriter {
+public:
+    EdgeWriter(G _g) :
+            g(_g) {
+    }
+    template< class VertexOrEdge >
+    void operator()(std::ostream& out, const VertexOrEdge& v) const {
+        out << "[label=\"" << boost::get(boost::edge_weight, g, v) << "\"]";
+    }
+private:
+    G g;
+};
+
+// tracklet graph to dot file
+void trackletsToDot(std::string filename, const Graph &g) {
+    std::cout << "Writing graph to file \"" << filename << "\"" << std::endl;
+    std::ofstream graphFile(filename.c_str());
+    if (!graphFile.is_open()) {
+        std::cerr << "Could not open file \"" << filename << "\"" << std::endl;
+        return;
+    }
+
+    VertexWriter< Graph > vertexWriter(g);
+    EdgeWriter< Graph > edgeWriter(g);
+
+    boost::write_graphviz(graphFile, g, vertexWriter, edgeWriter);
+
+    graphFile.close();
+}
+
+/**
+ * main tracking functions
+ */
+
+// nFrames: global frame count (needed to calculated weights to vSink)
+// return tracks
+void createTracksFromTracklets(const std::vector< Tracklet > &tracklets,
+                               const Params &params,
+                               const size_t nFrames,
+                               std::vector< Tracklet > &tracks) {
+    //
+    // build graph
+    //
+
+    Graph g;
+    std::vector< Vertex > vertices;
+
+    size_t trackletStart = 0;
+    size_t trackletMax = tracklets.size();
+
+    // adding vertices
+    for (size_t iTracklet = trackletStart; iTracklet < trackletMax; ++iTracklet) {
+        std::cout << "\rAdding node for tracklet " << iTracklet + 1 << std::flush;
+        Vertex v = boost::add_vertex(tracklets[iTracklet], g);
+        vertices.push_back(v);
+    }
+
+    // virtual source and sink
+    Vertex vSource = boost::add_vertex(Tracklet(), g);
+    Vertex vSink = boost::add_vertex(Tracklet(), g);
+
+    // debug
+    ///boost::add_edge(vSource, vSink, 0, g);
+
+    NameMap nameMap = boost::get(boost::vertex_name, g);
+
+    std::cout << std::endl;
+
+    // adding edges (layer eq tracklets)
+    for (size_t iTracklet = 0; iTracklet < vertices.size(); ++iTracklet) {
+        // all points of frame "iLayers"
+        Vertex tracklet1 = vertices[iTracklet];
+
+        // create edges to source and sink
+        std::cout << "\rAdding edges vSource -> " << iTracklet + 1 << " -> vSink" << std::flush;
+        boost::add_edge(vSource, tracklet1, (nameMap[tracklet1].getFirstFrame() + 1) * 1, g);
+        boost::add_edge(tracklet1, vSink, (nFrames - nameMap[tracklet1].getLastFrame()) * 1, g);
+
+        for (size_t jTracklet = 0; jTracklet < vertices.size(); ++jTracklet) {
+            // get second tracklet for linking
+            Vertex tracklet2 = vertices[jTracklet];
+
+            // this is where the magic happens... the cost function!
+            double ijCost = nameMap[tracklet1].linkingCost(nameMap[tracklet2], params);
+
+            // create edge between both nodes
+            if (ijCost < std::numeric_limits< double >::infinity()) {
+                boost::add_edge(tracklet1, tracklet2, ijCost, g);
+            }
+        }
+    }
+    std::cout << std::endl;
+
+    //
+    // find tracks (shortest paths in tracklet graph)
+    //
+
+    // Create things for Dijkstra
+    std::vector< Vertex > predecessors(boost::num_vertices(g));  // To store parents
+    std::vector< Weight > distances(boost::num_vertices(g));  // To store distances
+
+    IndexMap indexMap = boost::get(boost::vertex_index, g);
+    PredecessorMap predecessorMap(&predecessors[0], indexMap);
+    DistanceMap distanceMap(&distances[0], indexMap);
+
+    // export graph to graphviz dot file
+    //trackletsToDot("tmp2.dot", g);
+
+    // each track consists of merged tracklets (which in turn is again a tracklet)
+    tracks.clear();
+
+    // Compute shortest paths from input layer vertices to the sink
+    for (size_t tracklet = 0; tracklet < params.trackCount; ++tracklet) {
+        // preparations for Dijkstra
+        boost::dijkstra_shortest_paths(g,         // the graph
+                                       vSource,   // the source vertex
+                                       boost::distance_map(distanceMap).predecessor_map(predecessorMap));
+
+        // stop if no more tracks can be found
+        if (distanceMap[vSink] == std::numeric_limits<double>::max()) {
+            std::cout << "No more tracks can be found" << std::endl;
+            break;
+        }
+
+        // Extract the shortest path
+        typedef std::vector< Graph::edge_descriptor > PathType;
+
+        PathType path;
+
+        Vertex v = vSink;  // We want to start at the sink and work our way back to the source
+        for (Vertex u = predecessorMap[v]; u != v; v = u, u = predecessorMap[v]) {
+            std::pair< Graph::edge_descriptor, bool > edgePair = boost::edge(u, v, g);
+            Graph::edge_descriptor edge = edgePair.first;
+            path.push_back(edge);
+        }
+
+        // traverse shortest path (actually: traverse reverse path in a reverse direction)
+        Tracklet currentTrack;
+        bool vFirst = true;
+        for (PathType::reverse_iterator pathIterator = path.rbegin(); pathIterator != path.rend(); ++pathIterator) {
+            // append non-virtual tracklets to this path
+            if (!nameMap[boost::source(*pathIterator, g)].isVirtual()) {
+                currentTrack.append(nameMap[boost::source(*pathIterator, g)]);
+            }
+
+            // for each node of the path (but not the first, i.e. vSource), set the weights of all outgoing edges to Inf (for Dijkstra equivalent to deletion of the node)
+            if (!vFirst) {
+                std::pair< Graph::out_edge_iterator, Graph::out_edge_iterator > edgeIts = boost::out_edges(boost::source(*pathIterator,
+                                                                                                                         g),
+                                                                                                           g);
+                for (Graph::out_edge_iterator edgeIt = edgeIts.first; edgeIt != edgeIts.second; edgeIt++) {
+                    boost::get(boost::edge_weight, g, *edgeIt) = std::numeric_limits< Weight >::infinity();
+                }
+            }
+            vFirst = false;
+        }
+
+        // interpolate
+        currentTrack.interpolatePoints();
+
+        tracks.push_back(currentTrack);
+
+        std::cout << "Found track #" << tracklet + 1 << "/" << params.trackCount << ": " << path.size() - 1 << " tracklet(s), cost: "
+        << distanceMap[vSink] << std::endl;
+    }
+
+    // merge duplicate tracks
+    mergeDuplicates(tracks);
+    std::cerr << std::endl << "Merged to " << tracks.size() << " tracks" << std::endl;
+}
+
+void writeTracks(std::string filename, std::vector< Tracklet > &tracks, size_t nFrames, double minTrackLength) {
+    // open file
+    std::ofstream f(filename.c_str());
+
+    // get minimum track length
+    size_t minLength;
+    if (minTrackLength <= 1.0) {
+        minLength = std::min(size_t(minTrackLength * double(nFrames)), nFrames);
+    } else {
+        minLength = std::min(size_t(minTrackLength), nFrames);
+    }
+
+    // print each track
+    size_t finalCount = 0;
+    for (size_t track = 0; track < tracks.size(); ++track) {
+        if (tracks[track].getRange() >= minLength) {
+            const std::map< size_t, cv::Point3d > *p = tracks[track].pRawPoints();
+            for (std::map< size_t, cv::Point3d >::const_iterator it = p->begin(); it != p->end(); it++) {
+                f << it->second.x << " " << it->second.y << " " << it->second.z << " " << it->first << " ";
+            }
+            //f << tracks[track].getIdString()
+            f << std::endl;
+            finalCount++;
+        }
+    }
+
+    f.close();
+
+    std::cout << finalCount << " tracks with length >= " << minLength << " were found" << std::endl;
+}
+
+int main(int argc, char** argv) {
+    // get command line parameters
+    Params p;
+    if (!evalCmdLine(argc, argv, p)) {
+        std::cerr << "Error while parsing arguments!" << std::endl;
+        return 1;
+    }
+
+    size_t nFrames;
+    std::vector< Tracklet > tracklets, tracks;
+    readTracklets(p.strTracklets, tracklets, nFrames);
+    createTracksFromTracklets(tracklets, p, nFrames, tracks);
+    writeTracks(p.outputFilename, tracks, nFrames, p.minTrackLength);
+
+    return EXIT_SUCCESS;
+}
+

+ 92 - 0
util/IO.cpp

@@ -0,0 +1,92 @@
+//
+// Created by wrede on 19.04.16.
+//
+
+#include "IO.h"
+
+namespace util
+{
+    namespace io
+    {
+        void ReadCSV(
+                const std::string &filename,
+                const char &delimiter,
+                core::Vector3d &values)
+        {
+            std::ifstream in(filename, std::ifstream::in);
+            std::string line;
+
+            // Read lines while the reader is in good condition
+            while (in.good() && !in.eof())
+            {
+                getline(in, line);
+
+                // Ignore empty lines
+                if (line.size() == 0) continue;
+
+                // Get frame index
+                size_t dIndex = line.find(delimiter);
+                size_t frameIndex = std::stoul(line.substr(0, dIndex).c_str()) - 1;
+
+                // Extract point values
+                std::vector<double> pointValues;
+                while (dIndex != std::string::npos)
+                {
+                    line = line.substr(dIndex + 1);
+                    dIndex = line.find(delimiter);
+                    pointValues.push_back(std::stof(line.substr(0, dIndex).c_str()));
+                }
+
+                // Add point data to detection data
+                if (frameIndex >= values.size())
+                {
+                    values.push_back(std::vector<std::vector<double>>());
+                }
+
+                values[frameIndex].push_back(pointValues);
+            }
+
+            in.close();
+        }
+
+        void ReadCSV(
+                const std::string &filename,
+                const char &delimiter,
+                core::Vector2d &values)
+        {
+            std::ifstream in(filename, std::ifstream::in);
+            std::string line;
+
+            // Read lines while the reader is in good condition and the
+            // end of file is not reached
+            while (in.good() && !in.eof())
+            {
+                getline(in, line);
+
+                // Ignore empty lines
+                if (line.size() == 0) continue;
+
+                // Extract point values
+                size_t dIndex;
+                std::vector<double> pointValues;
+                do
+                {
+                    dIndex = line.find(delimiter);
+
+                    pointValues.push_back(
+                            std::stof(line.substr(0, dIndex).c_str()));
+
+                    line = line.substr(dIndex + 1);
+                }
+                while (dIndex != std::string::npos);
+
+                // Add point data to detection data
+                values.push_back(pointValues);
+            }
+
+            in.close();
+        }
+
+    }
+}
+

+ 33 - 0
util/IO.h

@@ -0,0 +1,33 @@
+//
+// Created by wrede on 19.04.16.
+//
+
+#ifndef GBMOT_FILEIO_H
+#define GBMOT_FILEIO_H
+
+#include "../core/Definitions.h"
+#include <string>
+#include <fstream>
+
+namespace util
+{
+    // Utility class for file in- and output.
+    namespace io
+    {
+        // Reads the CSV file and stores the values in a 3D array:
+        // [frame_index][object_in_frame_index][value_in_object_index]
+        void ReadCSV(
+                const std::string &filename,
+                const char &delimiter,
+                core::Vector3d &values);
+        // Reads the CSV file and stores the values in a 2D array:
+        // [row_index][value_index]
+        void ReadCSV(
+                const std::string &filename,
+                const char &delimiter,
+                core::Vector2d &values);
+    };
+}
+
+
+#endif //GBMOT_FILEIO_H

+ 30 - 0
util/Parser.cpp

@@ -0,0 +1,30 @@
+//
+// Created by wrede on 22.04.16.
+//
+
+#include "Parser.h"
+
+namespace util
+{
+    namespace parser
+    {
+        void ParseObjectDataMap(
+                const std::vector<std::string> &keys,
+                const core::Vector3d &values,
+                core::DetectionSequence &sequence)
+        {
+            for (size_t frame_i = 0; frame_i < values.size(); ++frame_i)
+            {
+                for (size_t object_i = 0; object_i < values[frame_i].size();
+                     ++object_i)
+                {
+                    core::ObjectDataMap object(frame_i, keys, values[frame_i][object_i]);
+
+                    sequence.AddObject(object);
+                }
+            }
+        }
+    }
+}
+
+

+ 24 - 0
util/Parser.h

@@ -0,0 +1,24 @@
+//
+// Created by wrede on 22.04.16.
+//
+
+#ifndef GBMOT_PARSE_H
+#define GBMOT_PARSE_H
+
+#include "../core/Definitions.h"
+#include "../core/DetectionSequence.h"
+
+namespace util
+{
+    namespace parser
+    {
+        // Parses the given values with the given keys into the given sequence
+        void ParseObjectDataMap(
+                const std::vector<std::string> &keys,
+                const core::Vector3d &values,
+                core::DetectionSequence &sequence);
+    };
+}
+
+
+#endif //GBMOT_PARSE_H