From 3175d63af33484a81eb2a9bc2cd6ae21b8fda25c Mon Sep 17 00:00:00 2001
From: "d.kilic" <d.kilic@fz-juelich.de>
Date: Wed, 3 Nov 2021 11:41:59 +0100
Subject: [PATCH] Factor management of trajectories out of Tracker

- Stop Tracker inheriting from QVector, instead save TrackPerson in std::vector
- Save TrackPerson's in another class (new class PersonStorage)
- Move all functions directly accessing the trajectories in a writing/editing manner into this class
- Some small refactorings and documentation-comments
---
 CMakeLists.txt                          |    2 +
 include/colorPlot.h                     |   33 +-
 include/control.h                       |    2 +-
 include/extrCalibration.h               |    9 +-
 include/personStorage.h                 |  126 +++
 include/petrack.h                       |   14 +-
 include/recognition.h                   |    2 +-
 include/tracker.h                       |  241 +----
 include/trackerItem.h                   |   10 +-
 include/trackerReal.h                   |    9 +-
 include/view.h                          |    4 +-
 src/colorPlot.cpp                       |   47 +-
 src/control.cpp                         |   36 +-
 src/extrCalibration.cpp                 |   11 +-
 src/main.cpp                            |   12 +-
 src/openMoCapDialog.cpp                 |    1 +
 src/personStorage.cpp                   | 1214 +++++++++++++++++++++
 src/petrack.cpp                         |  198 ++--
 src/recognition.cpp                     |    2 +-
 src/tracker.cpp                         | 1288 ++++-------------------
 src/trackerItem.cpp                     |  137 ++-
 src/trackerReal.cpp                     |  172 +--
 src/view.cpp                            |    6 +-
 tests/unit_test/tst_moCapController.cpp |    7 +-
 24 files changed, 1853 insertions(+), 1730 deletions(-)
 create mode 100644 include/personStorage.h
 create mode 100644 src/personStorage.cpp

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 5b297389f..d6092e9f0 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -372,6 +372,7 @@ target_sources(petrack_core PRIVATE
     include/moCapPersonMetadata.h
     include/pMessageBox.h
     include/moCapSelectionWidget.h
+    include/personStorage.h
     )
 
 target_sources(petrack_core PRIVATE
@@ -431,6 +432,7 @@ target_sources(petrack_core PRIVATE
     src/moCapPersonMetadata.cpp
     src/pMessageBox.cpp
     src/moCapSelectionWidget.cpp
+    src/personStorage.cpp
     ui/about.ui
     ui/codeMarker.ui
     ui/colorMarker.ui
diff --git a/include/colorPlot.h b/include/colorPlot.h
index c49ea21ab..07bb49bdc 100644
--- a/include/colorPlot.h
+++ b/include/colorPlot.h
@@ -34,26 +34,24 @@ class TrackerPlotItem;
 class Control;
 class Zoomer;
 class RectPlotItem;
+class PersonStorage;
 class ViewColorPlotItem;
 
 
 class TrackerPlotItem : public QwtPlotItem
 {
 public:
-    TrackerPlotItem();
-
     void draw(QPainter *p, const QwtScaleMap &mapX, const QwtScaleMap &mapY, const QRectF &re) const;
 
     void setPen(const QPen &pen);
 
     void setModel(int model, int x, int y);
 
-    void     setTracker(Tracker *tracker);
-    Tracker *getTracker();
+    void setPersonStorage(const PersonStorage *storage);
 
 private:
-    Tracker *mTracker;
-    QPen     mPen;
+    const PersonStorage *mPersonStorage = nullptr;
+    QPen                 mPen;
 };
 
 
@@ -163,7 +161,7 @@ public:
     bool   printDistribution() const;
 
     void setControlWidget(Control *control);
-    void setTracker(Tracker *tracker);
+    void setPersonStorage(const PersonStorage *storage);
     void setScale();
     void generateImage();
 
@@ -180,16 +178,17 @@ public:
     inline RectPlotItem *   getMapItem() const { return mRectItem; }
 
 private:
-    double             mSymbolSize;
-    double             mXMax;
-    double             mYMax;
-    Control *          mControlWidget;
-    ImagePlotItem *    mImageItem;
-    TrackerPlotItem *  mTrackerItem;
-    RectPlotItem *     mRectItem;
-    ViewColorPlotItem *mViewColorItem;
-    Zoomer *           mZoomer;
-    int                mGreyDiff;
+    const PersonStorage *mPersonStorage;
+    double               mSymbolSize;
+    double               mXMax;
+    double               mYMax;
+    Control *            mControlWidget;
+    ImagePlotItem *      mImageItem;
+    TrackerPlotItem *    mTrackerItem;
+    RectPlotItem *       mRectItem;
+    ViewColorPlotItem *  mViewColorItem;
+    Zoomer *             mZoomer;
+    int                  mGreyDiff;
 };
 
 #endif
diff --git a/include/control.h b/include/control.h
index a90897f85..7f3eaa5fd 100644
--- a/include/control.h
+++ b/include/control.h
@@ -21,13 +21,13 @@
 #ifndef CONTROL_H
 #define CONTROL_H
 
+#include "petrack.h"
 #include "recognition.h"
 #include "ui_control.h"
 
 #include <Qt>
 #include <QtWidgets>
 
-class Petrack;
 class QGraphicsScene;
 class QDomElement;
 
diff --git a/include/extrCalibration.h b/include/extrCalibration.h
index 3375ac266..58d5a6759 100644
--- a/include/extrCalibration.h
+++ b/include/extrCalibration.h
@@ -30,7 +30,7 @@
 
 class Petrack;
 class Control;
-
+class PersonStorage;
 
 class ReprojectionError
 {
@@ -144,8 +144,9 @@ public:
 class ExtrCalibration
 {
 private:
-    Petrack *mMainWindow;
-    Control *mControlWidget;
+    Petrack *      mMainWindow;
+    Control *      mControlWidget;
+    PersonStorage &mPersonStorage;
 
     std::vector<cv::Point3f> points3D;
     std::vector<cv::Point2f> points2D;
@@ -164,7 +165,7 @@ private:
     void              init();
 
 public:
-    ExtrCalibration();
+    ExtrCalibration(PersonStorage &storage);
     ~ExtrCalibration();
     void                            setMainWindow(Petrack *mw);
     bool                            isEmptyExtrCalibFile();
diff --git a/include/personStorage.h b/include/personStorage.h
new file mode 100644
index 000000000..352489a23
--- /dev/null
+++ b/include/personStorage.h
@@ -0,0 +1,126 @@
+/*
+ * PeTrack - Software for tracking pedestrians movement in videos
+ * Copyright (C) 2010-2021 Forschungszentrum Jülich GmbH,
+ * Maik Boltes, Juliane Adrian, Ricardo Martin Brualla, Arne Graf, Paul Häger, Daniel Hillebrand,
+ * Deniz Kilic, Paul Lieberenz, Daniel Salden, Tobias Schrödter, Ann Katrin Seemann
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef PERSONSTORAGE_H
+#define PERSONSTORAGE_H
+
+#include "tracker.h"
+
+#include <vector>
+
+class Petrack;
+
+
+class PersonStorage
+{
+public:
+    enum class Direction
+    {
+        Previous,
+        Following,
+        Whole
+    };
+
+    explicit PersonStorage(Petrack &mainWindow) : mMainWindow(mainWindow) {}
+
+    void splitPerson(size_t pers, int frame);
+    bool splitPersonAt(const Vec2F &p, int frame, const QSet<int> &onlyVisible);
+    bool delPointOf(int pers, int direction, int frame);
+    bool delPoint(const Vec2F &p, int direction, int frame, const QSet<int> &onlyVisible);
+    void delPointAll(Direction direction, int frame);
+    void delPointROI();
+    void delPointInsideROI();
+    bool editTrackPersonComment(const Vec2F &p, int frame, const QSet<int> &onlyVisible);
+    bool setTrackPersonHeight(const Vec2F &p, int frame, const QSet<int> &onlyVisible);
+    bool resetTrackPersonHeight(const Vec2F &p, int frame, QSet<int> onlyVisible);
+
+    size_t                          nbPersons() const { return mPersons.size(); }
+    const TrackPerson &             at(size_t i) const { return mPersons.at(i); }
+    void                            addPerson(const TrackPerson &person) { mPersons.push_back(person); }
+    const std::vector<TrackPerson> &getPersons() const { return mPersons; }
+
+    // used for calculation of 3D point for all points in frame
+    // returns number of found points or -1 if no stereoContext available (also points without disp found are counted)
+    int calcPosition(int frame);
+
+    // true, if new traj is inserted with point p and initial frame frame
+    // p in pixel coord
+    // pers wird gesetzt, wenn existierender trackpoint einer person verschoben wird
+    bool addPoint(
+        TrackPoint &            p,
+        int                     frame,
+        const QSet<int> &       onlyVisible,
+        reco::RecognitionMethod method,
+        int *                   pers = nullptr);
+
+    // hier sollte direkt die farbe mit uebergeben werden
+    void addPoints(QList<TrackPoint> &pL, int frame, reco::RecognitionMethod method);
+    int  visible(int frameNum) const;
+    int  largestFirstFrame() const;
+    int  largestLastFrame() const;
+    int  smallestFirstFrame() const;
+    void recalcHeight(float altitude);
+
+    void clear() { mPersons.clear(); }
+
+    void smoothHeight(size_t i, int j);
+
+    void insertFeaturePoint(
+        size_t            person,
+        int               frame,
+        const TrackPoint &point,
+        int               persNr,
+        bool              extrapolate,
+        float             z,
+        float             height);
+    int merge(int pers1, int pers2);
+
+
+    void checkPlausibility(
+        QList<int> &pers,
+        QList<int> &frame,
+        bool        testEqual    = true,
+        bool        testVelocity = true,
+        bool        testInside   = true,
+        bool        testLength   = true);
+
+    void optimizeColor();
+
+    // reset the height of all persons, but not the pos of the trackpoints
+    void resetHeight();
+
+    // reset the pos of the tzrackpoints, but not the heights
+    void resetPos();
+
+    // gibt groessenverteilung der personen auf stdout aus
+    // rueckgabewert false wenn keine hoeheninformationen in tracker datensatz vorliegt
+    bool printHeightDistribution();
+    void setMarkerHeights(const std::unordered_map<int, float> &heights);
+    void setMarkerIDs(const std::unordered_map<int, int> &markerIDs);
+    void purge(int frame);
+
+    void setNrInBg(size_t idx, int nr) { mPersons[idx].setNrInBg(nr); }
+
+private:
+    std::vector<TrackPerson> mPersons;
+    Petrack &                mMainWindow;
+};
+
+#endif // PERSONSTORAGE_H
diff --git a/include/petrack.h b/include/petrack.h
index 3dab62d5d..d231269c6 100644
--- a/include/petrack.h
+++ b/include/petrack.h
@@ -39,6 +39,7 @@
 #include "extrCalibration.h"
 #include "moCapController.h"
 #include "moCapPerson.h"
+#include "personStorage.h"
 #include "recognition.h"
 #include "swapFilter.h"
 
@@ -149,7 +150,7 @@ public slots:
     void setTrackPersonHeight(QPointF pos);
     void resetTrackPersonHeight(QPointF pos);
     void deleteTrackPoint(QPointF pos, int direction);
-    void deleteTrackPointAll(int direction);
+    void deleteTrackPointAll(PersonStorage::Direction direction);
     void deleteTrackPointROI();
     void deleteTrackPointInsideROI();
     //    void showContextMenu(QPointF pos);
@@ -226,6 +227,8 @@ public:
     inline QImage *                getImage() { return mImage; }
     inline cv::Mat                 getImg() { return mImg; }
     inline cv::Mat                 getImageFiltered() { return mImgFiltered; }
+    inline PersonStorage &         getPersonStorage() { return mPersonStorage; }
+    inline const PersonStorage &   getPersonStorage() const { return mPersonStorage; }
     inline Tracker *               getTracker() { return mTracker; }
     inline TrackerReal *           getTrackerReal() { return mTrackerReal; }
     inline ImageItem *             getImageItem() { return mImageItem; }
@@ -454,10 +457,11 @@ private:
 
     reco::Recognizer mReco;
 
-    Tracker *    mTracker;
-    TrackerReal *mTrackerReal;
-    double       mHeadSize;
-    double       mCmPerPixel;
+    PersonStorage mPersonStorage{*this};
+    Tracker *     mTracker;
+    TrackerReal * mTrackerReal;
+    double        mHeadSize;
+    double        mCmPerPixel;
 
     QDomDocument mDefaultSettings;
 
diff --git a/include/recognition.h b/include/recognition.h
index ababbb83e..37aec8e83 100644
--- a/include/recognition.h
+++ b/include/recognition.h
@@ -218,7 +218,7 @@ signals:
 // berechnet pixelverschiebung aufgrund von schraegsicht bei einem farbmarker
 // Maik Dissertation Seite 138
 // boxImageCentre ohne Border
-Vec2F autoCorrectColorMarker(Vec2F &boxImageCentre, Control *controlWidget);
+Vec2F autoCorrectColorMarker(const Vec2F &boxImageCentre, Control *controlWidget);
 
 
 namespace detail
diff --git a/include/tracker.h b/include/tracker.h
index e01553727..d3569c50a 100644
--- a/include/tracker.h
+++ b/include/tracker.h
@@ -21,13 +21,17 @@
 #ifndef TRACKER_H
 #define TRACKER_H
 
-#include "petrack.h"
+#include "recognition.h"
 #include "vector.h"
 
 #include <QColor>
 #include <QList>
+#include <QRegularExpression>
 #include <QTextStream>
 
+class PersonStorage;
+class Petrack;
+
 // war 1.5, aber bei bildauslassungen kann es ungewollt zuschlagen (bei 3 ist ein ausgelassener frame mgl, bei 2 wieder
 // ein problem)
 inline constexpr double EXTRAPOLATE_FACTOR = 3.;
@@ -90,85 +94,9 @@ public:
 };
 
 
-inline QTextStream &operator>>(QTextStream &s, TrackPoint &tp)
-{
-    double d;
-    Vec2F  p;
-    Vec3F  sp;
-    QColor col;
-    int    qual;
-    int    markerID;
-
-    s >> d;
-    tp.setX(d);
-    s >> d;
-    tp.setY(d);
-    if(Petrack::trcVersion > 1)
-    {
-        s >> d;
-        sp.setX(d);
-        s >> d;
-        sp.setY(d);
-        s >> d;
-        sp.setZ(d);
-        tp.setSp(sp);
-    }
-    s >> qual;
-    tp.setQual(qual);
-    s >> d;
-    p.setX(d);
-    s >> d;
-    p.setY(d);
-    tp.setColPoint(p);
-    s >> col;
-    tp.setColor(col);
-    if(Petrack::trcVersion > 2)
-    {
-        s >> markerID;
-        tp.setMarkerID(markerID);
-    }
-    return s;
-}
-inline QTextStream &operator<<(QTextStream &s, const TrackPoint &tp)
-{
-    if(Petrack::trcVersion > 2)
-    {
-        s << tp.x() << " " << tp.y() << " " << tp.sp().x() << " " << tp.sp().y() << " " << tp.sp().z() << " "
-          << tp.qual() << " " << tp.colPoint().x() << " " << tp.colPoint().y() << " " << tp.color() << " "
-          << tp.getMarkerID();
-    }
-    else if(Petrack::trcVersion == 2)
-    {
-        s << tp.x() << " " << tp.y() << " " << tp.sp().x() << " " << tp.sp().y() << " " << tp.sp().z() << " "
-          << tp.qual() << " " << tp.colPoint().x() << " " << tp.colPoint().y() << " " << tp.color();
-    }
-    else
-    {
-        s << tp.x() << " " << tp.y() << " " << tp.qual() << " " << tp.colPoint().x() << " " << tp.colPoint().y() << " "
-          << tp.color();
-    }
-    return s;
-}
-inline std::ostream &operator<<(std::ostream &s, const TrackPoint &tp)
-{
-    if(Petrack::trcVersion > 2)
-    {
-        s << tp.x() << " " << tp.y() << " " << tp.sp().x() << " " << tp.sp().y() << " " << tp.sp().z() << " "
-          << tp.qual() << " " << tp.colPoint().x() << " " << tp.colPoint().y() << " " << tp.color() << " "
-          << tp.getMarkerID();
-    }
-    else if(Petrack::trcVersion > 1)
-    {
-        s << tp.x() << " " << tp.y() << " " << tp.sp().x() << " " << tp.sp().y() << " " << tp.sp().z() << " "
-          << tp.qual() << " " << tp.colPoint().x() << " " << tp.colPoint().y() << " " << tp.color();
-    }
-    else
-    {
-        s << tp.x() << " " << tp.y() << " " << tp.qual() << " " << tp.colPoint().x() << " " << tp.colPoint().y() << " "
-          << tp.color();
-    }
-    return s;
-}
+QTextStream & operator>>(QTextStream &s, TrackPoint &tp);
+QTextStream & operator<<(QTextStream &s, const TrackPoint &tp);
+std::ostream &operator<<(std::ostream &s, const TrackPoint &tp);
 
 //--------------------------------------------------------------------------
 
@@ -219,7 +147,7 @@ public:
         mHeightCount = 0;
     }
     void   recalcHeight(float altitude);
-    double getNearestZ(int i, int *extrapolated);
+    double getNearestZ(int i, int *extrapolated) const;
 
     inline int           getMarkerID() const { return mMarkerID; }
     inline void          setMarkerID(const int markerID) { mMarkerID = markerID; }
@@ -258,86 +186,11 @@ public:
 // mHeightCount wird nicht e3xportiert und auch nicht wieder eingelesen -> nach import auf 0 obwohl auf height ein wert
 // steht, daher immer mheight auf -1 testen!!!
 // keine Konsistenzueberpruefung
-inline QTextStream &operator>>(QTextStream &s, TrackPerson &tp)
-{
-    double     d;
-    QColor     col;
-    int        n;
-    TrackPoint p;
-    int        markerID;
-
-    s.skipWhiteSpace();
-    QString str = s.readLine();
-
-    QTextStream trjInfoLine(&str);
-
-    trjInfoLine >> n;
-    tp.setNr(n);
-    trjInfoLine >> d;
-    tp.setHeight(d);
-    trjInfoLine >> n;
-    tp.setFirstFrame(n);
-    trjInfoLine >> n;
-    tp.setLastFrame(n);
-    trjInfoLine >> n;
-    tp.setColCount(n);
-    trjInfoLine >> col;
-    tp.setColor(col);
-    if(Petrack::trcVersion > 3)
-    {
-        trjInfoLine >> markerID;
-        tp.setMarkerID(markerID);
-    }
-    trjInfoLine >> n;           // size of list
-    if(Petrack::trcVersion > 2) // Reading the comment line
-    {
-        // Kommentarzeile lesen
-        str = s.readLine();
-        tp.setComment(str.replace(QRegularExpression("<br>"), "\n"));
-    }
-
+QTextStream &operator>>(QTextStream &s, TrackPerson &tp);
 
-    for(int i = 0; i < n; ++i)
-    {
-        s >> p;
-        tp.append(p);
-    }
-    return s;
-}
+QTextStream &operator<<(QTextStream &s, const TrackPerson &tp);
 
-inline QTextStream &operator<<(QTextStream &s, const TrackPerson &tp)
-{
-    s << tp.nr() << " " << tp.height() << " " << tp.firstFrame() << " " << tp.lastFrame() << " " << tp.colCount() << " "
-      << tp.color();
-    if(Petrack::trcVersion > 3)
-    {
-        s << " " << tp.getMarkerID();
-    }
-    s << " " << tp.size();
-    s << Qt::endl << tp.serializeComment() << Qt::endl;
-    for(int i = 0; i < tp.size(); ++i)
-    {
-        s << tp.at(i) << Qt::endl;
-    }
-    return s;
-}
-
-inline std::ostream &operator<<(std::ostream &s, const TrackPerson &tp)
-{
-    s << tp.nr() << " " << tp.height() << " " << tp.firstFrame() << " " << tp.lastFrame() << " " << tp.colCount() << " "
-      << tp.color();
-    if(Petrack::trcVersion > 3)
-    {
-        s << " " << tp.getMarkerID();
-    }
-    s << " " << tp.size();
-    s << std::endl << tp.serializeComment() << std::endl;
-    for(int i = 0; i < tp.size(); ++i)
-    {
-        s << tp.at(i) << std::endl;
-    }
-    return s;
-}
+std::ostream &operator<<(std::ostream &s, const TrackPerson &tp);
 
 //----------------------------------------------------------------------------
 
@@ -353,7 +206,7 @@ inline std::ostream &operator<<(std::ostream &s, const TrackPerson &tp)
  * 6. calculate color over tracking (accumulated over tracking while procedure above) path and set height
  * 7. recalc coord with real coord with known height
  */
-class Tracker : public QList<TrackPerson>
+class Tracker
 {
 private:
     Petrack *                mMainWindow;
@@ -365,9 +218,10 @@ private:
     std::vector<int>         mPrevFeaturePointsIdx;
     std::vector<float>       mTrackError;
     cv::TermCriteria         mTermCriteria;
+    PersonStorage &          mPersonStorage;
 
 public:
-    Tracker(QWidget *wParent);
+    Tracker(QWidget *wParent, PersonStorage &storage);
 
     // neben loeschen der liste muessen auch ...
     void init(cv::Size size);
@@ -376,45 +230,6 @@ public:
 
     void resize(cv::Size size);
 
-    void splitPerson(int pers, int frame);
-    bool splitPersonAt(const Vec2F &p, int frame, QSet<int> onlyVisible);
-
-    bool delPointOf(int pers, int direction, int frame);
-    bool delPoint(const Vec2F &p, int direction, int frame, QSet<int> onlyVisible);
-    void delPointAll(int direction, int frame);
-    void delPointROI();
-    void delPointInsideROI();
-    bool editTrackPersonComment(const Vec2F &p, int frame, const QSet<int> &onlyVisible);
-    bool setTrackPersonHeight(const Vec2F &p, int frame, QSet<int> onlyVisible);
-    bool resetTrackPersonHeight(const Vec2F &p, int frame, QSet<int> onlyVisible);
-
-    // used for calculation of 3D point for all points in frame
-    // returns number of found points or -1 if no stereoContext available (also points without disp found are counted)
-    int calcPosition(int frame);
-
-    // true, if new traj is inserted with point p and initial frame frame
-    // p in pixel coord
-    // pers wird gesetzt, wenn existierender trackpoint einer person verschoben wird
-    bool addPoint(
-        TrackPoint &            p,
-        int                     frame,
-        const QSet<int> &       onlyVisible,
-        reco::RecognitionMethod method,
-        int *                   pers = nullptr);
-
-    // hier sollte direkt die farbe mit uebergeben werden
-    void addPoints(QList<TrackPoint> &pL, int frame, reco::RecognitionMethod method);
-
-    // calculate height of person
-
-    // convert all trajectorie coordinates in real coordinate (height needed)
-
-    int visible(int frameNum);
-    int largestFirstFrame();
-    int largestLastFrame();
-    int smallestFirstFrame();
-    int smallestLastFrame();
-
     size_t calcPrevFeaturePoints(
         int       prevFrame,
         cv::Rect &rect,
@@ -439,32 +254,6 @@ public:
         QSet<int>               onlyVisible        = QSet<int>(),
         int                     errorScaleExponent = 0);
 
-
-    void checkPlausibility(
-        QList<int> &pers,
-        QList<int> &frame,
-        bool        testEqual    = true,
-        bool        testVelocity = true,
-        bool        testInside   = true,
-        bool        testLength   = true);
-    void optimizeColor();
-
-    // reset the height of all persons, but not the pos of the trackpoints
-    void resetHeight();
-    // reset the pos of the tzrackpoints, but not the heights
-    void resetPos();
-    void recalcHeight(float altitude);
-
-    void setMarkerHeights(const std::unordered_map<int, float> &heights);
-
-    void setMarkerIDs(const std::unordered_map<int, int> &markerIDs);
-
-    // gibt groessenverteilung der personen auf stdout aus
-    // rueckgabewert false wenn keine hoeheninformationen in tracker datensatz vorliegt
-    bool printHeightDistribution();
-
-    void purge(int frame);
-
 private:
     bool tryMergeTrajectories(const TrackPoint &v, size_t i, int frame);
 
diff --git a/include/trackerItem.h b/include/trackerItem.h
index 1e128fe47..2878032de 100644
--- a/include/trackerItem.h
+++ b/include/trackerItem.h
@@ -25,17 +25,17 @@
 
 class Petrack;
 class Control;
-class Tracker;
+class PersonStorage;
 
 class TrackerItem : public QGraphicsItem
 {
 private:
-    Petrack *mMainWindow;
-    Control *mControlWidget;
-    Tracker *mTracker;
+    Petrack *      mMainWindow;
+    Control *      mControlWidget;
+    PersonStorage &mPersonStorage;
 
 public:
-    TrackerItem(QWidget *wParent, Tracker *tracker, QGraphicsItem *parent = nullptr);
+    TrackerItem(QWidget *wParent, PersonStorage &tracker, QGraphicsItem *parent = nullptr);
     void   contextMenuEvent(QGraphicsSceneContextMenuEvent *event);
     QRectF boundingRect() const;
     void   paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget);
diff --git a/include/trackerReal.h b/include/trackerReal.h
index 63945ea99..45a9af143 100644
--- a/include/trackerReal.h
+++ b/include/trackerReal.h
@@ -28,6 +28,8 @@
 
 #include <QList>
 
+class PersonStorage;
+
 // point in x/y in cm
 class TrackPointReal : public Vec3F
 {
@@ -115,8 +117,9 @@ inline QTextStream &operator<<(QTextStream &s, const TrackPersonReal &tp)
 class TrackerReal : public QList<TrackPersonReal>
 {
 private:
-    double   mXMin, mXMax, mYMin, mYMax;
-    Petrack *mMainWindow;
+    double         mXMin, mXMax, mYMin, mYMax;
+    Petrack *      mMainWindow;
+    PersonStorage &mPersonStorage;
 
 public:
     inline double xMin() const { return mXMin; }
@@ -124,7 +127,7 @@ public:
     inline double yMin() const { return mYMin; }
     inline double yMax() const { return mYMax; }
 
-    TrackerReal(QWidget *wParent);
+    TrackerReal(QWidget *wParent, PersonStorage &storage);
 
 
     // calculate height of person
diff --git a/include/view.h b/include/view.h
index 56d2b23b3..b28ae36ce 100644
--- a/include/view.h
+++ b/include/view.h
@@ -21,6 +21,8 @@
 #ifndef VIEW_H
 #define VIEW_H
 
+#include "personStorage.h" // for Direction
+
 #include <QFrame>
 #include <QGraphicsView>
 #include <QKeyEvent>
@@ -54,7 +56,7 @@ signals:
     void mouseShiftDoubleClick(QPointF pos);
     void mouseControlDoubleClick(QPointF pos);
     void mouseRightDoubleClick(QPointF pos, int direction);
-    void mouseMiddleDoubleClick(int direction);
+    void mouseMiddleDoubleClick(PersonStorage::Direction direction);
     void mouseShiftWheel(int delta);
     void colorSelected();
     void setColorEvent();
diff --git a/src/colorPlot.cpp b/src/colorPlot.cpp
index fa90800f1..d556eb6f9 100644
--- a/src/colorPlot.cpp
+++ b/src/colorPlot.cpp
@@ -109,10 +109,6 @@ private:
 //-----------------------------------------------------------------------------------------
 
 // die kreise liegen mgl nicht genau auf kreuz - noch zu testen
-TrackerPlotItem::TrackerPlotItem()
-{
-    mTracker = nullptr;
-}
 
 /**
  * @brief Draws a circle in the colorplot for every color associated with a trackperson.
@@ -128,7 +124,7 @@ void TrackerPlotItem::draw(QPainter *p, const QwtScaleMap &mapX, const QwtScaleM
     double sy         = mapY.p1() / (mapY.s2() - mapY.s1());
     double yMax       = ((ColorPlot *) plot())->yMax();
     double circleSize = ((ColorPlot *) plot())->symbolSize();
-    int    i, z;
+    int    z;
     int    plotZ = ((ColorPlot *) plot())->zValue();
     double diff;
 
@@ -143,23 +139,22 @@ void TrackerPlotItem::draw(QPainter *p, const QwtScaleMap &mapX, const QwtScaleM
     sx = circleSize / sx;
     sy = circleSize / sy;
 
-    if(mTracker)
+    if(mPersonStorage)
     {
-        for(i = 0; i < mTracker->size(); ++i)
+        const auto &persons = mPersonStorage->getPersons();
+        for(const auto &person : persons)
         {
-            if((*mTracker)[i]
-                   .color()
-                   .isValid()) // insbesondere von hand eingefuegte trackpoint/persons haben keine farbe
+            if(person.color().isValid()) // insbesondere von hand eingefuegte trackpoint/persons haben keine farbe
             {
-                QPoint point = ((ColorPlot *) plot())->getPos((*mTracker)[i].color(), &z);
+                QPoint point = ((ColorPlot *) plot())->getPos(person.color(), &z);
                 diff         = (255. - abs(z - plotZ)) / 255.;
                 rect.setWidth(diff * sx);
                 rect.setHeight(diff * sy);
                 rect.moveLeft(point.x() - diff * sx / 2.);
                 rect.moveTop(point.y() - diff * sy / 2.);
 
-                p->setBrush(QBrush((*mTracker)[i].color()));
-                if(((ColorPlot *) plot())->isGrey((*mTracker)[i].color()))
+                p->setBrush(QBrush(person.color()));
+                if(((ColorPlot *) plot())->isGrey(person.color()))
                 {
                     p->setPen(Qt::red);
                 }
@@ -180,13 +175,9 @@ void TrackerPlotItem::setPen(const QPen &pen)
     mPen = pen;
 }
 
-void TrackerPlotItem::setTracker(Tracker *tracker)
-{
-    mTracker = tracker;
-}
-Tracker *TrackerPlotItem::getTracker()
+void TrackerPlotItem::setPersonStorage(const PersonStorage *storage)
 {
-    return mTracker;
+    mPersonStorage = storage;
 }
 
 
@@ -611,9 +602,7 @@ private:
 
 //-----------------------------------------------------------------------------------------
 
-ColorPlot::ColorPlot(QWidget *parent) // default= NULL
-    :
-    QwtPlot(parent)
+ColorPlot::ColorPlot(QWidget *parent) : QwtPlot(parent)
 {
     mControlWidget = nullptr;
     mGreyDiff      = 50;
@@ -665,14 +654,13 @@ bool ColorPlot::printDistribution() const
 {
     QMap<double, int>                 dict;
     QMap<double, int>::const_iterator j;
-    Tracker *                         tr = mTrackerItem->getTracker();
-    int                               i, anz = 0;
+    int                               anz = 0;
 
-    for(i = 0; i < tr->size(); ++i)
+    for(auto const &person : mPersonStorage->getPersons())
     {
-        if((*tr)[i].color().isValid()) // insbesondere von hand eingefuegte trackpoint/persons haben keine farbe
+        if(person.color().isValid()) // insbesondere von hand eingefuegte trackpoint/persons haben keine farbe
         {
-            ++dict[map((*tr)[i].color())];
+            ++dict[map(person.color())];
         }
     }
     j = dict.constBegin();
@@ -831,9 +819,10 @@ void ColorPlot::setControlWidget(Control *control)
     mControlWidget = control;
 }
 
-void ColorPlot::setTracker(Tracker *tracker)
+void ColorPlot::setPersonStorage(const PersonStorage *storage)
 {
-    mTrackerItem->setTracker(tracker);
+    mPersonStorage = storage;
+    mTrackerItem->setPersonStorage(storage);
 }
 
 void ColorPlot::setScale()
diff --git a/src/control.cpp b/src/control.cpp
index fa2b2bcdb..03be2ed15 100644
--- a/src/control.cpp
+++ b/src/control.cpp
@@ -1247,30 +1247,30 @@ void Control::on_trackShowOnlyNrList_textChanged(const QString & /*arg1*/)
 
 void Control::on_trackGotoNr_clicked()
 {
-    if(mMainWindow->getTracker()->size() >= trackShowOnlyNr->value())
+    if(static_cast<int>(mMainWindow->getPersonStorage().nbPersons()) >= trackShowOnlyNr->value())
     {
         int idx        = trackShowOnlyNr->value() - 1;
-        int firstFrame = mMainWindow->getTracker()->at(idx).firstFrame();
-        int lastFrame  = mMainWindow->getTracker()->at(idx).lastFrame();
+        int firstFrame = mMainWindow->getPersonStorage().at(idx).firstFrame();
+        int lastFrame  = mMainWindow->getPersonStorage().at(idx).lastFrame();
         mMainWindow->getPlayer()->skipToFrame((lastFrame + firstFrame) / 2);
     }
 }
 
 void Control::on_trackGotoStartNr_clicked()
 {
-    if(mMainWindow->getTracker()->size() >= trackShowOnlyNr->value())
+    if(static_cast<int>(mMainWindow->getPersonStorage().nbPersons()) >= trackShowOnlyNr->value())
     {
         int idx = trackShowOnlyNr->value() - 1;
-        mMainWindow->getPlayer()->skipToFrame(mMainWindow->getTracker()->at(idx).firstFrame());
+        mMainWindow->getPlayer()->skipToFrame(mMainWindow->getPersonStorage().at(idx).firstFrame());
     }
 }
 
 void Control::on_trackGotoEndNr_clicked()
 {
-    if(mMainWindow->getTracker()->size() >= trackShowOnlyNr->value())
+    if(static_cast<int>(mMainWindow->getPersonStorage().nbPersons()) >= trackShowOnlyNr->value())
     {
         int idx = trackShowOnlyNr->value() - 1;
-        mMainWindow->getPlayer()->skipToFrame(mMainWindow->getTracker()->at(idx).lastFrame());
+        mMainWindow->getPlayer()->skipToFrame(mMainWindow->getPersonStorage().at(idx).lastFrame());
     }
 }
 
@@ -1285,7 +1285,7 @@ void Control::on_trackShowOnlyListButton_clicked()
     QGridLayout *        layout = (QGridLayout *) nrListBox.layout();
     QVector<QCheckBox *> checkBox;
 
-    for(int i = 0; i < mMainWindow->getTracker()->size(); i++)
+    for(int i = 0; i < static_cast<int>(mMainWindow->getPersonStorage().nbPersons()); i++)
     {
         /// ToDo: parse from lineEdit
         checkBox.push_back(new QCheckBox(QString::number(i + 1)));
@@ -1305,7 +1305,7 @@ void Control::on_trackShowOnlyListButton_clicked()
     {
         QStringList list;
         int         first = -1, last = -1;
-        for(int i = 0; i < mMainWindow->getTracker()->size(); i++)
+        for(int i = 0; i < static_cast<int>(mMainWindow->getPersonStorage().nbPersons()); i++)
         {
             if(checkBox.at(i)->isChecked())
             {
@@ -1378,7 +1378,7 @@ void Control::on_recoShowColor_stateChanged(int i)
 
 void Control::on_recoOptimizeColor_clicked()
 {
-    mMainWindow->getTracker()->optimizeColor();
+    mMainWindow->getPersonStorage().optimizeColor();
     colorPlot->replot();
     mScene->update(); // damit mgl angezeige farbpunkte geaendert/weggenommen werden
 }
@@ -1675,17 +1675,17 @@ void Control::on_mapDistribution_clicked()
 {
     if(!colorPlot->printDistribution())
     {
-        mMainWindow->getTracker()->printHeightDistribution();
+        mMainWindow->getPersonStorage().printHeightDistribution();
     }
 }
 void Control::on_mapResetHeight_clicked()
 {
-    mMainWindow->getTracker()->resetHeight();
+    mMainWindow->getPersonStorage().resetHeight();
     mScene->update();
 }
 void Control::on_mapResetPos_clicked()
 {
-    mMainWindow->getTracker()->resetPos();
+    mMainWindow->getPersonStorage().resetPos();
     mScene->update();
 }
 void Control::on_mapDefaultHeight_valueChanged(double d)
@@ -1706,8 +1706,8 @@ void Control::on_mapReadHeights_clicked()
 
     if(std::holds_alternative<std::unordered_map<int, float>>(heights)) // heights contains the height map
     {
-        mMainWindow->getTracker()->resetHeight();
-        mMainWindow->getTracker()->setMarkerHeights(std::get<std::unordered_map<int, float>>(heights));
+        mMainWindow->getPersonStorage().resetHeight();
+        mMainWindow->getPersonStorage().setMarkerHeights(std::get<std::unordered_map<int, float>>(heights));
         mMainWindow->setHeightFileName(heightFile);
     }
     else // heights contains an error string
@@ -1715,7 +1715,7 @@ void Control::on_mapReadHeights_clicked()
         PCritical(mMainWindow, Petrack::tr("PeTrack"), Petrack::tr(std::get<std::string>(heights).c_str()));
     }
 
-    mMainWindow->getTracker()->printHeightDistribution();
+    mMainWindow->getPersonStorage().printHeightDistribution();
     mScene->update();
 }
 
@@ -1731,7 +1731,7 @@ void Control::on_mapReadMarkerID_clicked()
 
     if(std::holds_alternative<std::unordered_map<int, int>>(markerIDs)) // markerIDs contains the marker information
     {
-        mMainWindow->getTracker()->setMarkerIDs(std::get<std::unordered_map<int, int>>(markerIDs));
+        mMainWindow->getPersonStorage().setMarkerIDs(std::get<std::unordered_map<int, int>>(markerIDs));
         mMainWindow->setMarkerIDFileName(markerFile);
     }
     else // heights contains an error string
@@ -1740,7 +1740,7 @@ void Control::on_mapReadMarkerID_clicked()
             mMainWindow, Petrack::tr("PeTrack"), Petrack::tr(std::get<std::string>(markerIDs).c_str()));
     }
 
-    mMainWindow->getTracker()->printHeightDistribution();
+    mMainWindow->getPersonStorage().printHeightDistribution();
     mScene->update();
 }
 
diff --git a/src/extrCalibration.cpp b/src/extrCalibration.cpp
index a723fe1ef..fd785bfc7 100644
--- a/src/extrCalibration.cpp
+++ b/src/extrCalibration.cpp
@@ -29,7 +29,7 @@
 
 #define MAX_AV_ERROR 20
 
-ExtrCalibration::ExtrCalibration()
+ExtrCalibration::ExtrCalibration(PersonStorage &storage) : mPersonStorage(storage)
 {
     mMainWindow    = nullptr;
     mControlWidget = nullptr;
@@ -316,7 +316,7 @@ bool ExtrCalibration::loadExtrCalibFile()
 bool ExtrCalibration::fetch2DPoints()
 {
     bool all_ok = true;
-    if(!mMainWindow->getTracker() || mMainWindow->getTracker()->size() < 4)
+    if(!mMainWindow->getTracker() || mPersonStorage.nbPersons() < 4)
     {
         PCritical(
             mMainWindow,
@@ -326,7 +326,7 @@ bool ExtrCalibration::fetch2DPoints()
     }
     else
     {
-        size_t sz_2d = mMainWindow->getTracker()->size();
+        size_t sz_2d = mPersonStorage.nbPersons();
 
         if(points3D.size() > 0 && sz_2d != points3D.size())
         {
@@ -346,14 +346,13 @@ bool ExtrCalibration::fetch2DPoints()
                 // debout << "[" << i << "]: (" << mMainWindow->getTracker()->at(i).at(0).x() << ", " <<
                 // mMainWindow->getTracker()->at(i).at(0).y() << ")" << endl;
                 //  Info: Tracker->TrackPerson->TrackPoint->Vec2F
-                points2D.push_back(cv::Point2f(
-                    mMainWindow->getTracker()->at(i).at(0).x(), mMainWindow->getTracker()->at(i).at(0).y()));
+                points2D.push_back(cv::Point2f(mPersonStorage.at(i).at(0).x(), mPersonStorage.at(i).at(0).y()));
             }
         }
     }
     if(all_ok)
     {
-        mMainWindow->getTracker()->clear();
+        mPersonStorage.clear();
         calibExtrParams();
     }
     return all_ok;
diff --git a/src/main.cpp b/src/main.cpp
index 55457abd8..2883520c1 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -210,7 +210,7 @@ int main(int argc, char *argv[])
             auto markerIDs = IO::readMarkerIDFile(autoReadMarkerFile);
             if(std::holds_alternative<std::unordered_map<int, int>>(markerIDs)) // heights contains the height map
             {
-                petrack.getTracker()->setMarkerIDs(std::get<std::unordered_map<int, int>>(markerIDs));
+                petrack.getPersonStorage().setMarkerIDs(std::get<std::unordered_map<int, int>>(markerIDs));
                 petrack.setMarkerIDFileName(autoReadHeightFile);
             }
             else // markerIDs contains an error string
@@ -225,8 +225,8 @@ int main(int argc, char *argv[])
             auto markerHeights = IO::readHeightFile(autoReadHeightFile);
             if(std::holds_alternative<std::unordered_map<int, float>>(markerHeights)) // heights contains the height map
             {
-                petrack.getTracker()->resetHeight();
-                petrack.getTracker()->setMarkerHeights(std::get<std::unordered_map<int, float>>(markerHeights));
+                petrack.getPersonStorage().resetHeight();
+                petrack.getPersonStorage().setMarkerHeights(std::get<std::unordered_map<int, float>>(markerHeights));
                 petrack.setHeightFileName(autoReadHeightFile);
             }
             else // markerHeights contains an error string
@@ -261,7 +261,7 @@ int main(int argc, char *argv[])
         auto markerIDs = IO::readMarkerIDFile(autoReadMarkerFile);
         if(std::holds_alternative<std::unordered_map<int, int>>(markerIDs)) // heights contains the height map
         {
-            petrack.getTracker()->setMarkerIDs(std::get<std::unordered_map<int, int>>(markerIDs));
+            petrack.getPersonStorage().setMarkerIDs(std::get<std::unordered_map<int, int>>(markerIDs));
             petrack.setMarkerIDFileName(autoReadHeightFile);
         }
         else // heights contains an error string
@@ -276,8 +276,8 @@ int main(int argc, char *argv[])
         auto markerHeights = IO::readHeightFile(autoReadHeightFile);
         if(std::holds_alternative<std::unordered_map<int, float>>(markerHeights)) // heights contains the height map
         {
-            petrack.getTracker()->resetHeight();
-            petrack.getTracker()->setMarkerHeights(std::get<std::unordered_map<int, float>>(markerHeights));
+            petrack.getPersonStorage().resetHeight();
+            petrack.getPersonStorage().setMarkerHeights(std::get<std::unordered_map<int, float>>(markerHeights));
             petrack.setHeightFileName(autoReadHeightFile);
         }
         else // heights contains an error string
diff --git a/src/openMoCapDialog.cpp b/src/openMoCapDialog.cpp
index 59b309859..56fb953f9 100644
--- a/src/openMoCapDialog.cpp
+++ b/src/openMoCapDialog.cpp
@@ -25,6 +25,7 @@
 #include "ui_openMoCapDialog.h"
 
 #include <QFileDialog>
+#include <QMessageBox>
 
 OpenMoCapDialog::OpenMoCapDialog(QWidget *parent, MoCapController &controller) :
     QDialog(parent), mUi(new Ui::OpenMoCapDialog), mController(controller), mParent(parent)
diff --git a/src/personStorage.cpp b/src/personStorage.cpp
new file mode 100644
index 000000000..1bc6d97ea
--- /dev/null
+++ b/src/personStorage.cpp
@@ -0,0 +1,1214 @@
+/*
+ * PeTrack - Software for tracking pedestrians movement in videos
+ * Copyright (C) 2010-2021 Forschungszentrum Jülich GmbH,
+ * Maik Boltes, Juliane Adrian, Ricardo Martin Brualla, Arne Graf, Paul Häger, Daniel Hillebrand,
+ * Deniz Kilic, Paul Lieberenz, Daniel Salden, Tobias Schrödter, Ann Katrin Seemann
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "personStorage.h"
+
+#include "animation.h"
+#include "control.h"
+#include "multiColorMarkerWidget.h"
+#include "pMessageBox.h"
+#include "petrack.h"
+#include "recognitionRoiItem.h"
+
+/**
+ * @brief split trajectorie pers before frame frame
+ * @param pers index of person
+ * @param frame frame to split at
+ */
+void PersonStorage::splitPerson(size_t pers, int frame)
+{
+    int j;
+
+    if(mPersons.at(pers).firstFrame() < frame)
+    {
+        mPersons.push_back(mPersons.at(pers));
+
+        // alte trj einkuerzen und ab aktuellem frame zukunft loeschen
+        for(j = 0; j < mPersons.at(pers).lastFrame() - frame + 1; ++j)
+        {
+            mPersons[pers].removeLast();
+        }
+        mPersons[pers].setLastFrame(frame - 1);
+
+        // neu angehaengte/gedoppelte trajektorie
+        for(j = 0; j < frame - mPersons.back().firstFrame(); ++j)
+        {
+            mPersons.back().removeFirst();
+        }
+        mPersons.back().setFirstFrame(frame);
+    }
+}
+
+/**
+ * @brief Split trajectory at point point before given frame
+ *
+ * @param point point where to split trajectory (helpful if onlyVisible isn't set)
+ * @param frame frame at which to split the trajectory
+ * @param onlyVisible set of people for whom to do it (empty means everyone)
+ * @return true if a trajectory was split
+ */
+bool PersonStorage::splitPersonAt(const Vec2F &point, int frame, const QSet<int> &onlyVisible)
+{
+    for(size_t i = 0; i < mPersons.size(); ++i)
+    { // ueber TrackPerson
+        if(((onlyVisible.empty()) || (onlyVisible.contains(i))) &&
+           (mPersons.at(i).trackPointExist(frame) && (mPersons.at(i).trackPointAt(frame).distanceToPoint(point) <
+                                                      mMainWindow.getHeadSize(nullptr, i, frame) / 2.)))
+        {
+            splitPerson(i, frame);
+
+            return true;
+        }
+    }
+    return false;
+}
+
+
+/**
+ * @brief Deletes points of pers
+ * @param pers TrackPerson whose points should be deleted
+ * @param direction notes if previous (-1), following(1) or whole(0) trajectory should be deleted
+ * @param frame
+ * @return true, if deletion occured
+ */
+bool PersonStorage::delPointOf(int pers, int direction, int frame)
+{
+    if(direction == -1)
+    {
+        for(int j = 0; j < frame - mPersons.at(pers).firstFrame(); ++j)
+        {
+            mPersons[pers].removeFirst();
+        }
+        mPersons[pers].setFirstFrame(frame);
+    }
+    else if(direction == 0)
+    {
+        mPersons.erase(mPersons.begin() + pers);
+    }
+    else if(direction == 1)
+    {
+        for(int j = 0; j < mPersons.at(pers).lastFrame() - frame; ++j)
+        {
+            mPersons[pers].removeLast();
+        }
+        mPersons[pers].setLastFrame(frame);
+    }
+
+    return true;
+}
+
+
+/**
+ * @brief Deletes points of a SINGLE person in onlyVisible
+ * @param point point which need to be on the person (helpful if onlyVisible is not properly set)
+ * @param direction notes if previous (-1), following(1) or whole(0) trajectory should be deleted
+ * @param frame
+ * @param onlyVisible set of people whose points could be deleted; empty means everyone
+ * @return true if deletion occured
+ */
+bool PersonStorage::delPoint(const Vec2F &point, int direction, int frame, const QSet<int> &onlyVisible)
+{
+    for(int i = 0; i < static_cast<int>(mPersons.size()); ++i)
+    { // ueber TrackPerson
+        if(((onlyVisible.empty()) || (onlyVisible.contains(i))) &&
+           (mPersons.at(i).trackPointExist(frame) && (mPersons.at(i).trackPointAt(frame).distanceToPoint(point) <
+                                                      mMainWindow.getHeadSize(nullptr, i, frame) / 2.)))
+        {
+            delPointOf(i, direction, frame);
+            return true;
+        }
+    }
+    return false;
+}
+
+
+/**
+ * @brief Deletes trackpoints of all trajectories
+ * @param direction notes if previous, following or whole trajectory should be deleted
+ * @param frame
+ */
+void PersonStorage::delPointAll(Direction direction, int frame)
+{
+    for(size_t i = 0; i < mPersons.size(); ++i) // ueber TrackPerson
+    {
+        if(mPersons.at(i).trackPointExist(frame))
+        {
+            switch(direction)
+            {
+                case Direction::Previous:
+                    for(int j = 0; j < frame - mPersons.at(i).firstFrame(); ++j)
+                    {
+                        mPersons[i].removeFirst();
+                    }
+                    mPersons[i].setFirstFrame(frame);
+                    break;
+                case Direction::Whole:
+                    mPersons.erase(mPersons.begin() + i--); // nach Loeschen wird i um 1 erniedrigt
+                    break;
+                case Direction::Following:
+                    for(int j = 0; j < mPersons.at(i).lastFrame() - frame; ++j)
+                    {
+                        mPersons[i].removeLast();
+                    }
+                    mPersons[i].setLastFrame(frame);
+                    break;
+            }
+        }
+        else if(
+            ((direction == Direction::Previous) && (frame > mPersons.at(i).lastFrame())) ||
+            (direction == Direction::Whole) ||
+            ((direction == Direction::Following) && (frame < mPersons.at(i).firstFrame())))
+        {
+            mPersons.erase(mPersons.begin() + i);
+            i--;
+        }
+    }
+}
+
+
+/**
+ * @brief deletes points of a trajectrory, which are inside ROI
+ *
+ * 1 trajectory can end in 0, 1 or multiple trajectories!!!!!!!!
+ */
+void PersonStorage::delPointInsideROI()
+{
+    QRectF rect = mMainWindow.getRecoRoiItem()->rect();
+    bool   inside;
+
+    for(size_t i = 0; i < mPersons.size(); ++i) // ueber TrackPerson
+    {
+        inside = ((!mPersons.empty()) && rect.contains(mPersons.at(i).at(0).x(), mPersons.at(i).at(0).y()));
+        for(int j = 1; j < mPersons.at(i).size(); ++j)
+        {
+            if(inside != rect.contains(mPersons.at(i).at(j).x(), mPersons.at(i).at(j).y())) // aenderung von inside
+            {
+                splitPerson(i, mPersons.at(i).firstFrame() + j);
+                if(inside)
+                {
+                    mPersons.erase(mPersons.begin() + i);
+                    i--;
+                    inside = !inside;
+                }
+                break;
+            }
+        }
+        if(inside)
+        {
+            // rest loeschen
+            mPersons.erase(mPersons.begin() + i);
+            i--;
+        }
+    }
+}
+
+
+/**
+ * @brief deletes trajectory, if it is partly inside ROI
+ */
+void PersonStorage::delPointROI()
+{
+    int    anz  = 0;
+    QRectF rect = mMainWindow.getRecoRoiItem()->rect();
+
+    for(size_t i = 0; i < mPersons.size(); ++i) // ueber TrackPerson
+    {
+        for(int j = 0; j < mPersons.at(i).size(); ++j)
+        {
+            if(rect.contains(mPersons.at(i).at(j).x(), mPersons.at(i).at(j).y()))
+            {
+                anz++;
+                mPersons.erase(mPersons.begin() + i);
+                i--;
+                break;
+            }
+        }
+    }
+    debout << "deleted " << anz << " trajectories!" << std::endl;
+}
+
+
+/**
+ * @brief Editing the comment of a TrackPerson
+ *
+ * Allows editing the comment of a TrackPerson in a new Dialog. When a new dialog gets opened, it automatically
+ * appends 'Frame {\point frame}: ' to the dialog, if no comment for the frame exists.
+ *
+ * @param point position the user clicked
+ * @param frame current frame number
+ * @param onlyVisible list of visible persons
+ * @return if a comment has been saved
+ */
+bool PersonStorage::editTrackPersonComment(const Vec2F &point, int frame, const QSet<int> &onlyVisible)
+{
+    for(int i = 0; i < static_cast<int>(mPersons.size()); ++i) // ueber TrackPerson
+    {
+        if(((onlyVisible.empty()) || (onlyVisible.contains(i))) &&
+           (mPersons.at(i).trackPointExist(frame) &&
+            (mPersons.at(i).trackPointAt(frame).distanceToPoint(point) <
+             mMainWindow.getHeadSize(nullptr, i, frame) / 2.))) // war: MIN_DISTANCE)) // 30 ist abstand zwischen kopfen
+        {
+            QString displayedComment = mPersons.at(i).comment();
+            QString framePrefix      = "Frame " + QString::number(frame, 'g', 5) + ": ";
+
+            if(displayedComment.isEmpty())
+            {
+                displayedComment.append(framePrefix);
+            }
+            else if(!displayedComment.contains(framePrefix))
+            {
+                displayedComment.append("\n" + framePrefix);
+            }
+
+            bool    ok      = false;
+            QString comment = QInputDialog::getMultiLineText(
+                &mMainWindow, QObject::tr("Add Comment"), QObject::tr("Comment:"), displayedComment, &ok);
+
+            if(ok)
+            {
+                if(comment.isEmpty())
+                {
+                    int ret = PWarning(
+                        &mMainWindow,
+                        QObject::tr("Empty comment"),
+                        QObject::tr("Are you sure you want to save an empty comment?"),
+                        PMessageBox::StandardButton::Save | PMessageBox::StandardButton::Cancel);
+                    if(ret == PMessageBox::StandardButton::Cancel)
+                    {
+                        return false;
+                    }
+                }
+                mPersons[i].setComment(comment);
+                return true;
+            }
+        }
+    }
+    return false;
+}
+
+/**
+ * @brief Sets the height of the TrackPerson visible near the selected point at the selected frame
+ * @param point point near the current TrackPoint of the person
+ * @param frame current frame
+ * @param onlyVisible Set of people which could be selected (empty means everyone can be selected)
+ * @return whether the height of a TrackPerson was successfully changed
+ */
+bool PersonStorage::setTrackPersonHeight(const Vec2F &point, int frame, const QSet<int> &onlyVisible)
+{
+    for(int i = 0; i < static_cast<int>(mPersons.size()); ++i) // ueber TrackPerson
+    {
+        if(((onlyVisible.empty()) || (onlyVisible.contains(i))) &&
+           (mPersons.at(i).trackPointExist(frame) &&
+            (mPersons.at(i).trackPointAt(frame).distanceToPoint(point) <
+             mMainWindow.getHeadSize(nullptr, i, frame) / 2.))) // war: MIN_DISTANCE)) // 30 ist abstand zwischen kopfen
+        {
+            bool ok;
+
+            double col_height;
+            // col_height is negative, if height is determined through color and not yet set manually
+            if(mPersons.at(i).height() < MIN_HEIGHT + 1)
+            {
+                col_height = mPersons.at(i).color().isValid() ?
+                                 -mMainWindow.getControlWidget()->getColorPlot()->map(mPersons.at(i).color()) :
+                                 -mMainWindow.getControlWidget()->mapDefaultHeight->value();
+            }
+            else
+            {
+                col_height = mPersons.at(i).height();
+            }
+
+
+            double height = QInputDialog::getDouble(
+                &mMainWindow,
+                QObject::tr("Set person height"),
+                QObject::tr("Person height[cm]:"),
+                fabs(col_height),
+                -500,
+                500,
+                1,
+                &ok);
+            if(ok)
+            {
+                if(height < 0)
+                {
+                    debout << "Warning: you entered a negative height!" << std::endl; // is not supported!" << endl;
+                    // return false;
+                }
+                // if previous value (col_height) is negative, height was determined thru color. If manually set value
+                // is the color-map value, we do not change anything
+                // @todo: @ar.graf: check if manually set values have side-effects (maybe do not show in statistics)
+                if(!(std::abs(col_height + height) < 0.01))
+                {
+                    mPersons[i].setHeight(height);
+                    return true;
+                }
+                else
+                {
+                    debout << std::endl
+                           << "No height change detected. Color-mapped height will remain set." << std::endl;
+                }
+            }
+        }
+    }
+    return false;
+}
+
+/**
+ * @brief Resets the height of the TrackPerson near the selected point at the selected frame
+ * @param point point near the current TrackPoint of the person
+ * @param frame current frame
+ * @param onlyVisible Set of people which could be selected (empty means everyone can be selected)
+ * @return true if height was successfully reset
+ */
+bool PersonStorage::resetTrackPersonHeight(const Vec2F &point, int frame, QSet<int> onlyVisible)
+{
+    for(int i = 0; i < static_cast<int>(mPersons.size()); ++i) // ueber TrackPerson
+    {
+        if(((onlyVisible.empty()) || (onlyVisible.contains(i))) &&
+           (mPersons.at(i).trackPointExist(frame) &&
+            (mPersons.at(i).trackPointAt(frame).distanceToPoint(point) <
+             mMainWindow.getHeadSize(nullptr, i, frame) / 2.))) // war: MIN_DISTANCE)) // 30 ist abstand zwischen kopfen
+        {
+            mPersons[i].setHeight(MIN_HEIGHT);
+            return true;
+        }
+    }
+    return false;
+}
+
+// used for calculation of 3D point for all points in frame
+// returns number of found points or -1 if no stereoContext available (also points without disp found are counted)
+int PersonStorage::calcPosition(int /*frame*/)
+{
+#ifndef STEREO_DISABLED
+    int                 anz = 0, notFoundDisp = 0;
+    pet::StereoContext *sc = mMainWindow.getStereoContext();
+    float               x, y, z;
+
+    if(sc)
+    {
+        // for every point of a person, which has already identified at this frame
+        for(int i = 0; i < size(); ++i) // ueber TrackPerson
+        {
+            if(at(i).trackPointExist(frame))
+            {
+                ++anz;
+
+                // TrackPoint *point = &(at(i).trackPointAt(frame));
+                //  ACHTUNG: BORDER NICHT BEACHTET bei p.x()...???
+                //  calculate height with disparity map
+                if(sc->getMedianXYZaround(
+                       (int) at(i).trackPointAt(frame).x(),
+                       (int) at(i).trackPointAt(frame).y(),
+                       &x,
+                       &y,
+                       &z)) // nicht myRound, da pixel 0 von 0..0.99 in double geht
+                {
+                    // hier kommt man nur hinein, wenn x, y, z Wert berechnet werden konnten
+                    // statt altitude koennte hier irgendwann die berechnete Bodenhoehe einfliessen
+                    mPersons[i][frame - at(i).firstFrame()].setSp(x, y, z); // setZdistanceToCam(z);
+                    mPersons[i].setHeight(z, mMainWindow.getControlWidget()->coordAltitude->value());
+                }
+                else
+                    ++notFoundDisp;
+                // else // Meldung zu haeufig
+                //     debout << "Warning: No disparity information for person " << i+1 << "." << endl;
+            }
+        }
+        // if (notFoundDisp>0) // Meldung zu haeufig
+        //     debout << "Warning: No disparity information found for " << (100.*notFoundDisp)/anz << " percent of
+        //     points." << endl;
+        return anz;
+    }
+    else
+        return -1;
+#endif
+    return -1;
+}
+
+
+/**
+ * @brief Adds the point to the PersonStorage, either to exising person or creating a new one.
+ *
+ * This function find the nearest person to the given point and if the distance between point
+ * and trajectory is small enough, it gets added to this trajectory. If the point is not fitting to
+ * any trajectory, a new TrackPerson is created.
+ *
+ * It is possible for point to replace existing ones, if the quality is better. (Manual insertion,
+ * reverse tracking,...)
+ *
+ * For multicolor, the color gets added as well. For Aruco, the code of TrackPoint and TrackPerson
+ * gets synchronized.
+ *
+ * This function is used form manual insertions and from recognition.
+ *
+ * @param[in] point TrackPoint to add
+ * @param[in] frame current frame (frame in which point was detected)
+ * @param[in] onlyVisible set of selected persons, see Petrack::getPedestriansToTrack()
+ * @param[out] pers person the point was added to; undefined when new trajectory was created
+ * @return true if new trajectory was created; false otherwise
+ */
+bool PersonStorage::addPoint(
+    TrackPoint &            point,
+    int                     frame,
+    const QSet<int> &       onlyVisible,
+    reco::RecognitionMethod method,
+    int *                   pers)
+{
+    bool  found = false;
+    int   i, iNearest = 0.;
+    float scaleHead;
+    float dist, minDist = 1000000.;
+    float z = -1;
+#ifndef STEREO_DISABLED
+    float x = -1, y = -1;
+
+    // ACHTUNG: BORDER NICHT BEACHTET bei point.x()...
+    // hier wird farbe nur bei reco bestimmt gegebenfalls auch beim tracken interessant
+    // calculate height with disparity map
+    if(mMainWindow.getStereoContext() && mMainWindow.getStereoWidget()->stereoUseForHeight->isChecked())
+    {
+        if(mMainWindow.getStereoContext()->getMedianXYZaround(
+               (int) p.x(), (int) p.y(), &x, &y, &z)) // nicht myRound, da pixel 0 von 0..0.99 in double geht
+        {
+            // statt altitude koennte hier irgendwann die berechnete Bodenhoehe einfliessen
+            p.setSp(x, y, z); // setZdistanceToCam(z);
+        }
+        // cout << " " << point.x()<< " " << point.y() << " " << x << " " << y << " " << z <<endl;
+        // if (i == 10)
+        //     debout << i << " " << mMainWindow.getControlWidget()->coordAltitude->value() - z << " " << z << " " <<
+        //     mPersons[i].height() << endl;
+    }
+#endif
+    // skalierungsfaktor fuer kopfgroesse
+    // fuer multicolor marker groesser, da der schwarze punkt weit am rand liegen kann
+    bool multiColorWithDot = false;
+    if(method == reco::RecognitionMethod::MultiColor &&                // multicolor marker
+       mMainWindow.getMultiColorMarkerWidget()->useDot->isChecked() && // nutzung von black dot
+       !mMainWindow.getMultiColorMarkerWidget()
+            ->ignoreWithoutDot->isChecked()) // muetzen ohne black dot werden auch akzeptiert
+    {
+        multiColorWithDot = true;
+        scaleHead         = 1.3f;
+    }
+    else
+    {
+        scaleHead = 1.0f;
+    }
+
+    for(i = 0; i < static_cast<int>(mPersons.size()); ++i) // !found &&  // ueber TrackPerson
+    {
+        if(((onlyVisible.empty()) || (onlyVisible.contains(i))) && mPersons.at(i).trackPointExist(frame))
+        {
+            dist = mPersons.at(i).trackPointAt(frame).distanceToPoint(point);
+            if((dist < scaleHead * mMainWindow.getHeadSize(nullptr, i, frame) / 2.) ||
+               // fuer multifarbmarker mit schwarzem punkt wird nur farbmarker zur Abstandbetrachtung herangezogen
+               // at(i).trackPointAt(frame).colPoint() existiert nicht an dieser stelle, da bisher nur getrackt
+               // wurde!!!!
+               (multiColorWithDot && point.color().isValid() &&
+                (mPersons.at(i).trackPointAt(frame).distanceToPoint(point.colPoint()) <
+                 mMainWindow.getHeadSize(nullptr, i, frame) / 2.)))
+            {
+                if(found)
+                {
+                    debout << "Warning: more possible trackpoints for point" << std::endl;
+                    debout << "         " << point << " in frame " << frame << " with low distance:" << std::endl;
+                    debout << "         person " << i + 1 << " (distance: " << dist << "), " << std::endl;
+                    debout << "         person " << iNearest + 1 << " (distance: " << minDist << "), " << std::endl;
+                    if(minDist > dist)
+                    {
+                        minDist  = dist;
+                        iNearest = i;
+                    }
+                }
+                else
+                {
+                    minDist  = dist;
+                    iNearest = i;
+                    found    = true;
+                }
+            }
+        }
+    }
+    if(found) // den naechstgelegenen nehmen
+    {
+        // test, if recognition point or tracked point is better is made in at(i).insertAtFrame
+        if(mPersons[iNearest].insertAtFrame(
+               frame,
+               point,
+               iNearest,
+               (mMainWindow.getControlWidget()->trackExtrapolation->checkState() ==
+                Qt::Checked))) // wenn eingefuegt wurde (bessere qualitaet)
+        //|| !at(i).trackPointAt(frame).color().isValid() moeglich, um auch bei schlechterer
+        // qualitaet aber aktuell nicht
+        // vorliegender farbe die ermittelte farbe einzutragen - kommt nicht vor!
+        {
+            // Synchronize TrackPerson.markerID with TrackPoint.markerID
+            mPersons[iNearest].syncTrackPersonMarkerID(point.getMarkerID());
+
+            // set/add color
+            if(point.color().isValid()) // not valid for manual, than old color is used
+            {
+                // if (at(i).trackPointAt(frame).color().isValid()) man koennte alte farbe abziehen - aber nicht noetig,
+                // kommt nicht vor
+                mPersons[iNearest].addColor(point.color());
+            }
+        }
+
+        if(pers != nullptr)
+        {
+            *pers = iNearest;
+        }
+
+        mPersons[iNearest].setNewReco(true);
+    }
+
+    if((onlyVisible.empty()) && !found)
+    {
+        iNearest = static_cast<int>(mPersons.size());
+
+        if(point.qual() > 100) // manual add
+        {
+            point.setQual(100);
+        }
+        mPersons.push_back(TrackPerson(
+            0, frame, point, point.getMarkerID())); // 0 is person number/markerID; newReco is set to true by default
+    }
+    if((z > 0) && ((onlyVisible.empty()) || found))
+    {
+        mPersons[iNearest].setHeight(z, mMainWindow.getControlWidget()->coordAltitude->value()); // , frame
+    }
+    if((!onlyVisible.empty()) && !found)
+    {
+        QMessageBox::warning(
+            nullptr,
+            "PeTrack",
+            "Adding a manual TrackPoint is only possible, when \"show only people\" and \"show only people list\" are "
+            "disabled!\n"
+            "You would not see the newly created TrackPoint otherwise.");
+        debout << "Warning: No manual insertion, because not all trajectories are visible!" << std::endl;
+        return false;
+    }
+
+    return !found;
+}
+
+
+// used from recognition
+/**
+ * @brief Adds multiple points as new trajectories or to existing ones
+ *
+ * Is used by recognition!
+ *
+ * @param pL List of points to add
+ * @param frame current frame/frame the points were detected in
+ * @param method used recognition method/marker type
+ */
+void PersonStorage::addPoints(QList<TrackPoint> &pL, int frame, reco::RecognitionMethod method)
+{
+    // reset newReco
+    for(auto &person : mPersons)
+    {
+        person.setNewReco(false);
+    }
+
+    // ueberprufen ob identisch mit einem Punkt in liste
+    for(auto &point : pL) // ueber PointList
+    {
+        addPoint(point, frame, QSet<int>(), method);
+    }
+}
+
+/// Number of visible (TrackPoint exists in current frame) people
+int PersonStorage::visible(int frameNum) const
+{
+    return static_cast<int>(std::count_if(
+        mPersons.begin(),
+        mPersons.end(),
+        [frameNum](const TrackPerson &pers) { return pers.trackPointExist(frameNum); }));
+}
+
+/// Returns the largest first frame of **all** TrackPersons
+int PersonStorage::largestFirstFrame() const
+{
+    auto maxElement = std::max_element(
+        mPersons.cbegin(),
+        mPersons.cend(),
+        [](const TrackPerson &lhs, const TrackPerson &rhs) { return lhs.firstFrame() < rhs.firstFrame(); });
+    return maxElement != mPersons.cend() ? (*maxElement).firstFrame() : -1;
+}
+
+/// Returns the largest last frame of **all** TrackPersons
+int PersonStorage::largestLastFrame() const
+{
+    auto maxElement = std::max_element(
+        mPersons.cbegin(),
+        mPersons.cend(),
+        [](const TrackPerson &lhs, const TrackPerson &rhs) { return lhs.lastFrame() < rhs.lastFrame(); });
+    return maxElement != mPersons.cend() ? (*maxElement).lastFrame() : -1;
+}
+
+/// Returns the smallest first frame of **all** TrackPersons
+int PersonStorage::smallestFirstFrame() const
+{
+    auto minElement = std::min_element(
+        mPersons.cbegin(),
+        mPersons.cend(),
+        [](const TrackPerson &lhs, const TrackPerson &rhs) { return lhs.firstFrame() < rhs.firstFrame(); });
+    return minElement != mPersons.cend() ? (*minElement).firstFrame() : -1;
+}
+
+
+/**
+ * @brief Recalcs the height of all persons (used with stereo)
+ * @param altitude altitude of the camera (assumes orthogonal view?)
+ */
+void PersonStorage::recalcHeight(float altitude)
+{
+    for(auto &person : mPersons)
+    {
+        person.recalcHeight(altitude);
+    }
+}
+
+/**
+ * @brief Performs different tests to check the plausibility of trajectories.
+ *
+ * This method can check for
+ * <ul><li>shortness (less than 10 points)</li>
+ * <li>start and endpoint (both should be outside the reco ROI
+ * with exceptions for the beginning and end of the video)</li>
+ * <li>Fast variations of speed (4 frame interval)</li>
+ * <li>TrackPoints are too close together</li></ul>
+ *
+ * @param pers[in] list of persons (ID) to check
+ * @param frame[out] list of frames at which "problems" occured
+ * @param testEqual[in] true if warning for very close points are wished
+ * @param testVelocity[in] true if warning for fast speed variations is whished
+ * @param testInside[in] true if warning for start and endpoint in reco ROI is wished
+ * @param testLength[in] true if warning for very short trajectories is wished
+ */
+void PersonStorage::checkPlausibility(
+    QList<int> &pers,
+    QList<int> &frame,
+    bool        testEqual,
+    bool        testVelocity,
+    bool        testInside,
+    bool        testLength)
+{
+    QProgressDialog progress("Check Plausibility", nullptr, 0, 400, mMainWindow.window());
+    progress.setWindowTitle("Check plausibility");
+    progress.setWindowModality(Qt::WindowModal);
+    progress.setVisible(true);
+    progress.setValue(0);
+    progress.setLabelText("Check Plausibility...");
+    static int margin = 30; // rand am bild, ab dem trajectorie verloren sein darf
+    int        i, j;
+    double     x, y;
+    QRectF     rect      = mMainWindow.getRecoRoiItem()->rect();
+    int        lastFrame = mMainWindow.getAnimation()->getNumFrames() - 1;
+
+#ifdef TIME_MEASUREMENT
+    double time1, tstart;
+#endif
+    // test, if the trajectory is very short (less than 10 Trackpoints)
+    if(testLength)
+    {
+        progress.setValue(0);
+        progress.setLabelText("Check trajectories lengths...");
+        qApp->processEvents();
+#ifdef TIME_MEASUREMENT
+        time1  = 0.0;
+        tstart = clock();
+#endif
+        for(i = 0; i < static_cast<int>(mPersons.size()); ++i) // ueber TrackPerson
+        {
+            progress.setValue(static_cast<int>(i * 100. / mPersons.size()));
+            qApp->processEvents();
+            if(mPersons.at(i).size() < 10)
+            {
+                debout << "Warning: Trajectory of person " << i + 1 << " has less than 10 trackpoints!" << std::endl;
+                pers.append(i + 1);
+                frame.append(mPersons[i].firstFrame());
+            }
+        }
+#ifdef TIME_MEASUREMENT
+        time1 += clock() - tstart;
+        time1 = time1 / CLOCKS_PER_SEC;
+        cout << "  time(testLength) = " << time1 << " sec." << endl;
+#endif
+    }
+
+    // check, if trajectory starts and ends outside the recognition area
+    if(testInside)
+    {
+        progress.setValue(100);
+        progress.setLabelText("Check if trajectories are inside image...");
+        qApp->processEvents();
+#ifdef TIME_MEASUREMENT
+        time1  = 0.0;
+        tstart = clock();
+#endif
+        for(i = 0; i < static_cast<int>(mPersons.size()); ++i) // ueber TrackPerson
+        {
+            qApp->processEvents();
+            progress.setValue(100 + i * 100. / mPersons.size());
+            x = mPersons[i].first().x();
+            y = mPersons[i].first().y();
+            // mGrey hat gleiche groesse wie zuletzt getracktes bild
+            if(mPersons[i].firstFrame() != 0 && x >= MAX(margin, rect.x()) &&
+               y >= MAX(margin, rect.y())) //&&
+                                           // x <= MIN(mGrey.cols - 1 - 2 * bS - margin, rect.x() + rect.width()) &&
+                                           // y <= MIN(mGrey.rows - 1 - 2 * bS - margin, rect.y() + rect.height()))
+            {
+                debout << "Warning: Start of trajectory inside picture and recognition area of person " << i + 1 << "!"
+                       << std::endl;
+                pers.append(i + 1);
+                frame.append(mPersons[i].firstFrame());
+            }
+
+            x = mPersons[i].last().x();
+            y = mPersons[i].last().y();
+            // mGrey hat gleiche groesse wie zuletzt getracktes bild
+            if(mPersons[i].lastFrame() != lastFrame && x >= MAX(margin, rect.x()) &&
+               y >= MAX(margin, rect.y())) //&&
+                                           // x <= MIN(mGrey.cols - 1 - 2 * bS - margin, rect.x() + rect.width()) &&
+                                           // y <= MIN(mGrey.rows - 1 - 2 * bS - margin, rect.y() + rect.height()))
+            {
+                debout << "Warning: End of trajectory inside picture and recognition area of person " << i + 1 << "!"
+                       << std::endl;
+                pers.append(i + 1);
+                frame.append(mPersons[i].lastFrame());
+            }
+        }
+#ifdef TIME_MEASUREMENT
+        time1 += clock() - tstart;
+        time1 = time1 / CLOCKS_PER_SEC;
+        cout << "  time(testInside) = " << time1 << " sec." << endl;
+#endif
+    }
+
+    // testen, ob grosse Geschwindigkeitsaenderungen
+    // statt distanz koennte man auch noch vektoren vergleichen, was genauere analyse waer!!!!
+    if(testVelocity)
+    {
+        qApp->processEvents();
+        progress.setValue(200);
+        progress.setLabelText("Check velocity...");
+#ifdef TIME_MEASUREMENT
+        time1  = 0.0;
+        tstart = clock();
+#endif
+        double d01, d12, d23;
+        for(i = 0; i < static_cast<int>(mPersons.size()); ++i) // ueber TrackPerson
+        {
+            qApp->processEvents();
+            progress.setValue(200 + i * 100. / mPersons.size());
+            for(j = 1; j < mPersons.at(i).size() - 2; ++j) // ueber TrackPoint (ohne ersten und letzten beiden)
+            {
+                d01 = mPersons.at(i).at(j).distanceToPoint(mPersons.at(i).at(j - 1));
+                d12 = mPersons.at(i).at(j + 1).distanceToPoint(mPersons.at(i).at(j));
+                d23 = mPersons.at(i).at(j + 2).distanceToPoint(mPersons.at(i).at(j + 1));
+                if(((1.8 * (d01 + d23) / 2.) < d12) &&
+                   ((d12 > 6.) ||
+                    ((d01 + d23) / 2. > 3.))) // geschwindigkeit 1,8-fach && mindestpixelbewegung im schnitt von 3
+                {
+                    debout << "Warning: Fast variation of velocity of person " << i + 1 << " between frame "
+                           << j + mPersons.at(i).firstFrame() << " and " << j + 1 + mPersons.at(i).firstFrame() << "!"
+                           << std::endl;
+                    pers.append(i + 1);
+                    frame.append(j + mPersons.at(i).firstFrame());
+                }
+            }
+        }
+#ifdef TIME_MEASUREMENT
+        time1 += clock() - tstart;
+        time1 = time1 / CLOCKS_PER_SEC;
+        cout << "  time(testVelocity) = " << time1 << " sec." << endl;
+#endif
+    }
+
+    // testen, ob zwei trackpoint sehr nah beieinanderliegen (es gibt trajektorien, die uebereinander liegen, wenn nicht
+    // genmergt wird)
+    if(testEqual)
+    {
+        progress.setValue(300);
+        progress.setLabelText("Check if trajectories are equal...");
+        qApp->processEvents();
+#ifdef TIME_MEASUREMENT
+        time1  = 0.0;
+        tstart = clock();
+#endif
+        int lLF = largestLastFrame();
+        int f;
+        for(f = smallestFirstFrame(); f <= lLF; ++f)
+        {
+            progress.setValue(300 + f * 100. / lLF);
+            qApp->processEvents();
+
+            for(i = 0; i < static_cast<int>(mPersons.size()); ++i)
+            {
+                // if (!pers.contains(i+1)) man koennte nur einmal eine Person aufnehmen, da aufeinanderfolgende frames
+                // oft betroffen
+                for(j = i + 1; j < static_cast<int>(mPersons.size()); ++j)
+                {
+                    if(mPersons.at(i).trackPointExist(f) && mPersons.at(j).trackPointExist(f))
+                    {
+                        if(mPersons.at(i).trackPointAt(f).distanceToPoint(mPersons.at(j).trackPointAt(f)) <
+                           mMainWindow.getHeadSize(nullptr, i, f) / 2.)
+                        {
+                            debout << "Warning: Person " << i + 1 << " and " << j + 1
+                                   << " are very close to each other at frame " << f << "!" << std::endl;
+                            pers.append(i + 1);
+                            frame.append(f);
+                        }
+                    }
+                }
+            }
+        }
+#ifdef TIME_MEASUREMENT
+        time1 += clock() - tstart;
+        time1 = time1 / CLOCKS_PER_SEC;
+        cout << "  time(testEqual) = " << time1 << " sec." << endl;
+#endif
+    }
+}
+
+/// optimize color for all persons
+void PersonStorage::optimizeColor()
+{
+    for(auto &person : mPersons)
+    {
+        if(person.color().isValid())
+        {
+            person.optimizeColor();
+        }
+    }
+}
+
+/// reset the height of all persons, but not the pos of the trackpoints
+void PersonStorage::resetHeight()
+{
+    for(auto &person : mPersons)
+    {
+        person.resetHeight();
+    }
+}
+
+/// reset the pos of the tzrackpoints, but not the heights
+void PersonStorage::resetPos()
+{
+    for(auto &person : mPersons)
+    {
+        for(auto &point : person)
+        {
+            point.setSp(-1., -1., -1.);
+        }
+    }
+}
+
+/**
+ * @brief Prints height distribution to stdout
+ *
+ * @return false if no height information is available, else true
+ */
+bool PersonStorage::printHeightDistribution()
+{
+    debout << std::endl;
+    QMap<double, int>                 dict;
+    QMap<double, int>::const_iterator j;
+    int                               anz        = 0;
+    int                               heightStep = 5;
+    double                            average = 0., avg = 0.;
+    int                               noHeight = 0;
+
+    for(const auto &person : mPersons)
+    {
+        if(person.height() >
+           MIN_HEIGHT) // !=-1// insbesondere von hand eingefuegte trackpoint/persons haben keine farbe
+        {
+            ++dict[(static_cast<int>(person.height()) / heightStep) * heightStep];
+            avg += person.height();
+        }
+        else
+        {
+            ++noHeight;
+        }
+    }
+
+    anz = std::accumulate(dict.cbegin(), dict.cend(), 0);
+
+    debout << "number of persons with measured height                            : " << anz << std::endl;
+    debout << "person without measured height (not included in calculated values): " << noHeight
+           << " (using default height for export)" << std::endl;
+    if(anz == 0)
+    {
+        return false;
+    }
+
+    for(j = dict.constBegin(); j != dict.constEnd(); ++j)
+    {
+        debout << "height " << std::fixed << std::setprecision(1) << std::setw(5) << j.key() << " - "
+               << j.key() + heightStep << " : number " << std::setw(3) << j.value() << " (" << std::setw(4)
+               << (100. * j.value()) / anz << "%)" << std::endl;
+        average += (j.key() + heightStep / 2.) * j.value();
+    }
+
+    debout << "average height (bucket): " << std::fixed << std::setprecision(1) << std::setw(5) << average / anz
+           << std::endl;
+    debout << "average height         : " << std::fixed << std::setprecision(1) << std::setw(5) << avg / anz
+           << std::endl;
+
+    return true;
+}
+
+/**
+ * Sets the heights based on the values contained in \p heights.
+ * @param heights Map between marker ID and corresponding height
+ */
+void PersonStorage::setMarkerHeights(const std::unordered_map<int, float> &heights)
+{
+    for(auto &person : mPersons) // over TrackPerson
+    {
+        for(auto &point : person) // over TrackPoints
+        {
+            // markerID of current person at current TrackPoint:
+            int markerID = point.getMarkerID();
+
+            if(markerID != -1) // when a real markerID is found (not -1)
+            {
+                // find index of mID within List of MarkerIDs that were read from txt-file:
+                if(heights.find(markerID) != std::end(heights))
+                {
+                    person.setHeight(heights.at(markerID));
+                }
+                else
+                {
+                    debout << "Warning, the following markerID was not part of the height-file: " << markerID
+                           << std::endl;
+                    debout << "No height set for personNR: " << person.nr() << std::endl;
+                }
+            }
+        }
+    }
+}
+
+
+/**
+ * Sets the marker IDs based on the internal used IDs (personID).
+ * @param markerIDs Map between internal ID and marker ID
+ */
+void PersonStorage::setMarkerIDs(const std::unordered_map<int, int> &markerIDs)
+{
+    for(int i = 0; i < static_cast<int>(mPersons.size()); ++i) // over TrackPerson
+    {
+        // personID of current person
+        int personID = i + 1;
+        if(markerIDs.find(personID) != std::end(markerIDs))
+        {
+            int markerID = markerIDs.at(personID);
+            mPersons[i].setMarkerID(markerID);
+            for(int j = 0; j < mPersons[i].size(); ++j) // over TrackPoints
+            {
+                mPersons[i][j].setMarkerID(markerID);
+            }
+        }
+        else
+        {
+            debout << "Warning, the following personID was not part of the markerID-file: " << personID << std::endl;
+        }
+    }
+}
+
+/**
+ * @brief Deletes TrackPersons with over 80% solely tracked points
+ *
+ * DOESN'T WORK WITH COLOR MARKERS because they have a quality under
+ * 100 (quality threshold of this method could be changed)
+ *
+ * Only trajectories having a point at the given frame are purged.
+ * Trajectories with less than 10 points are not purged.
+ *
+ * @param frame frame at which all trajectories should be purged
+ */
+void PersonStorage::purge(int frame)
+{
+    int   i, j;
+    float count; ///< number of trackpoints without recognition
+
+    for(i = 0; i < static_cast<int>(mPersons.size()); ++i)
+    {
+        if(mPersons.at(i).size() > 10 && mPersons.at(i).firstFrame() <= frame && mPersons.at(i).lastFrame() >= frame)
+        {
+            count = 0;
+            for(j = 0; j < mPersons.at(i).size(); ++j)
+            {
+                if(mPersons.at(i).at(j).qual() < 100.)
+                {
+                    ++count;
+                }
+            }
+            if(count / mPersons.at(i).size() > 0.8) // Achtung, wenn roi klein, dann viele tp nur getrackt
+            {
+                mPersons.erase(mPersons.begin() + i); // delete trj}
+            }
+        }
+    }
+}
+
+/**
+ * @brief Smoothes the height of a stereo point of a person
+ * @param i index of person whose heights/z-coordinates to smooth
+ * @param j index of the stereo point whose height to smooth
+ */
+void PersonStorage::smoothHeight(size_t i, int j)
+{
+    // ACHTUNG: Aenderungen in Originaltrajektorie, so dass aenderungen auf folgeuntersuchungen einfluss
+    // haben: j auf j+1
+    int  tsize      = mPersons[i].size();
+    auto firstFrame = mPersons[i].firstFrame();
+    if(mPersons[i][j].sp().z() != -1)
+    {
+        int nrFor = 1; // anzahl der ztrackpoint ohne hoeheninfo
+        int nrRew = 1;
+
+        // nach && wird nur ausgefuehrt, wenn erstes true == size() also nicht
+        while((j + nrFor < tsize) && (mPersons[i].at(j + nrFor).sp().z() < 0))
+        {
+            nrFor++;
+        }
+
+        // nach && wird nur ausgefuehrt, wenn erstes true == size() also nicht
+        while((j - nrRew >= 0) && (mPersons[i].at(j - nrRew).sp().z() < 0))
+        {
+            nrRew++;
+        }
+
+        // nur oder eher in Vergangenheit hoeheninfo gefunden
+        if(((j - nrRew >= 0) && (j + nrFor == tsize)) || ((j - nrRew >= 0) && (nrRew < nrFor)))
+        {
+            if(fabs(mPersons[i].at(j - nrRew).sp().z() - mPersons[i].at(j).sp().z()) > nrRew * 40.) // 40cm
+            {
+                mPersons[i][j].setSp(
+                    mPersons[i].at(j).sp().x(), mPersons[i].at(j).sp().y(), mPersons[i].at(j - nrRew).sp().z());
+                debout << "Warning: Trackpoint smoothed height at the end or next to unknown height in "
+                          "the future for trajectory "
+                       << i + 1 << " in frame " << j + firstFrame << "." << std::endl;
+            }
+        }
+        else if(
+            ((j + nrFor != tsize) && (j - nrRew < 0)) ||
+            ((j + nrFor != tsize) && (nrFor < nrRew))) // nur oder eher in der zukunft hoeheninfo gefunden
+        {
+            if(fabs(mPersons[i].at(j + nrFor).sp().z() - mPersons[i].at(j).sp().z()) > nrFor * 40.) // 40cm
+            {
+                mPersons[i][j].setSp(
+                    mPersons[i].at(j).sp().x(), mPersons[i].at(j).sp().y(), mPersons[i].at(j + nrFor).sp().z());
+                debout << "Warning: Trackpoint smoothed height at the beginning or next to unknown "
+                          "height in the past for trajectory "
+                       << i + 1 << " in frame " << j + firstFrame << "." << std::endl;
+            }
+        }
+        else if((j + nrFor != tsize) && (j - nrRew >= 0)) // in beiden richtungen hoeheninfo gefunden
+                                                          // und nrFor==nrRew
+        {
+            // median genommen um zwei fehlmessungen nebeneinander nicht dazu fuehren zu lassen, dass
+            // bessere daten veraendert werden
+            auto zMedian = getMedianOf3(
+                mPersons[i].at(j).sp().z(), mPersons[i].at(j - nrRew).sp().z(), mPersons[i].at(j + nrFor).sp().z());
+            // lineare interpolation
+            if(fabs(zMedian - mPersons[i].at(j).sp().z()) > 20. * (nrFor + nrRew)) // 20cm
+            {
+                mPersons[i][j].setSp(mPersons[i].at(j).sp().x(), mPersons[i].at(j).sp().y(), zMedian);
+                debout << "Warning: Trackpoint smoothed height inside for trajectory " << i + 1 << " in frame "
+                       << j + firstFrame << "." << std::endl;
+            }
+        }
+    }
+}
+
+/**
+ * @brief Inserts the points to the corresponding person
+ * @param person index of person to which to add a point
+ * @param frame frame the TrackPoint is from
+ * @param point the point to add
+ * @param persNr persNr (same as person)
+ * @param extrapolate whether far away point should be extrapolated
+ * @param z z-coordiante of stereo point/distance to camera
+ * @param height altitude of the camera
+ */
+void PersonStorage::insertFeaturePoint(
+    size_t            person,
+    int               frame,
+    const TrackPoint &point,
+    int               persNr,
+    bool              extrapolate,
+    float             z,
+    float             height)
+{
+    if(mPersons.at(person).insertAtFrame(frame, point, persNr, extrapolate) && z > -1)
+    {
+        mPersons[person].setHeight(z, height);
+    }
+}
+
+/**
+ * @brief Merge two trajectories into one
+ * @param pers1 index of first trajectory
+ * @param pers2 index of second trajectory
+ * @return index of trajectory which was deleted in the process
+ */
+int PersonStorage::merge(int pers1, int pers2)
+{
+    auto &     person      = mPersons.at(pers1);
+    auto &     other       = mPersons.at(pers2);
+    const bool extrapolate = mMainWindow.getControlWidget()->trackExtrapolation->checkState() == Qt::Checked;
+    int        deleteIndex;
+    if(other.firstFrame() < person.firstFrame() && other.lastFrame() > person.lastFrame())
+    {
+        for(int k = 0; k < person.size(); ++k)
+        {
+            // bei insertAtFrame wird qual beruecksichtigt, ob vorheriger besser
+            other.insertAtFrame(person.firstFrame() + k, person[k], pers2, extrapolate);
+        }
+        deleteIndex = pers1;
+    }
+    else if(other.firstFrame() < person.firstFrame())
+    {
+        for(int k = other.size() - 1; k > -1; --k)
+        {
+            // bei insertAtFrame wird qual beruecksichtigt, ob vorheriger besser
+            person.insertAtFrame(other.firstFrame() + k, other[k], pers1, extrapolate);
+        }
+        deleteIndex = pers2;
+    }
+    else
+    {
+        for(int k = 0; k < other.size(); ++k)
+        {
+            // bei insertAtFrame wird qual beruecksichtigt, ob vorheriger besser
+            person.insertAtFrame(other.firstFrame() + k, other[k], pers1, extrapolate);
+        }
+        deleteIndex = pers2;
+    }
+    mPersons.erase(mPersons.begin() + deleteIndex);
+
+    return deleteIndex;
+}
diff --git a/src/petrack.cpp b/src/petrack.cpp
index 67586de5e..f1bacbac6 100644
--- a/src/petrack.cpp
+++ b/src/petrack.cpp
@@ -75,7 +75,9 @@ Control *cw;
 int Petrack::trcVersion = 0;
 
 // Reihenfolge des anlegens der objekte ist sehr wichtig
-Petrack::Petrack() : mAuthors(IO::readAuthors(QCoreApplication::applicationDirPath() + "/.zenodo.json"))
+Petrack::Petrack() :
+    mExtrCalibration(mPersonStorage),
+    mAuthors(IO::readAuthors(QCoreApplication::applicationDirPath() + "/.zenodo.json"))
 {
     QIcon icon;
     icon.addFile(":/icon");          // about
@@ -162,33 +164,13 @@ Petrack::Petrack() : mAuthors(IO::readAuthors(QCoreApplication::applicationDirPa
     mViewWidget = new ViewWidget(this);
     mView       = mViewWidget->view();
     mView->setScene(mScene);
-    connect(mView, SIGNAL(mouseDoubleClick()), this, SLOT(openSequence()));
-    connect(
-        mView,
-        SIGNAL(mouseShiftDoubleClick(QPointF)),
-        this,
-        SLOT(addManualTrackPointOnlyVisible(QPointF))); // const QPoint &pos funktionierte nicht
-    connect(
-        mView,
-        SIGNAL(mouseShiftControlDoubleClick(QPointF)),
-        this,
-        SLOT(splitTrackPerson(QPointF))); // const QPoint &pos funktionierte nicht
-    connect(
-        mView,
-        SIGNAL(mouseControlDoubleClick(QPointF)),
-        this,
-        SLOT(addOrMoveManualTrackPoint(QPointF))); // const QPoint &pos funktionierte nicht
-    connect(
-        mView,
-        SIGNAL(mouseRightDoubleClick(QPointF, int)),
-        this,
-        SLOT(deleteTrackPoint(QPointF, int))); // const QPoint &pos funktionierte nicht
-    connect(
-        mView,
-        SIGNAL(mouseMiddleDoubleClick(int)),
-        this,
-        SLOT(deleteTrackPointAll(int))); // const QPoint &pos funktionierte nicht
-    connect(mView, SIGNAL(mouseShiftWheel(int)), this, SLOT(skipToFrameWheel(int)));
+    connect(mView, &GraphicsView::mouseDoubleClick, this, [this]() { this->openSequence(); });
+    connect(mView, &GraphicsView::mouseShiftDoubleClick, this, &Petrack::addManualTrackPointOnlyVisible);
+    connect(mView, &GraphicsView::mouseShiftControlDoubleClick, this, &Petrack::splitTrackPerson);
+    connect(mView, &GraphicsView::mouseControlDoubleClick, this, &Petrack::addOrMoveManualTrackPoint);
+    connect(mView, &GraphicsView::mouseRightDoubleClick, this, &Petrack::deleteTrackPoint);
+    connect(mView, &GraphicsView::mouseMiddleDoubleClick, this, &Petrack::deleteTrackPointAll);
+    connect(mView, &GraphicsView::mouseShiftWheel, this, &Petrack::skipToFrameWheel);
 
     mPlayerWidget = new Player(mAnimation, this);
 
@@ -199,12 +181,12 @@ Petrack::Petrack() : mAuthors(IO::readAuthors(QCoreApplication::applicationDirPa
 
     //---------------------------
 
-    mTracker     = new Tracker(this);
-    mTrackerReal = new TrackerReal(this);
-    mTrackerItem = new TrackerItem(this, mTracker);
+    mTracker     = new Tracker(this, mPersonStorage);
+    mTrackerReal = new TrackerReal(this, mPersonStorage);
+    mTrackerItem = new TrackerItem(this, mPersonStorage);
     mTrackerItem->setZValue(5); // groesser heisst weiter oben
 
-    mControlWidget->getColorPlot()->setTracker(mTracker);
+    mControlWidget->getColorPlot()->setPersonStorage(&mPersonStorage);
 #ifdef QWT
     mControlWidget->getAnalysePlot()->setTrackerReal(mTrackerReal);
 #endif
@@ -579,9 +561,9 @@ void Petrack::openXml(QDomDocument &doc, bool openSeq)
         // mgl zwei trackpoints
         // beim haendischen importieren sind weiterhin parallele trajektorien moeglich (warnung wird ausgegeben)
         frame = 0; // default
-        if((mTracker->largestLastFrame() >= frame) && (mTracker->smallestFirstFrame() <= frame))
+        if((mPersonStorage.largestLastFrame() >= frame) && (getPersonStorage().smallestFirstFrame() <= frame))
         {
-            mTracker->clear();
+            mPersonStorage.clear();
             mTracker->reset();
         }
         importTracker(mTrcFileName);
@@ -1859,22 +1841,25 @@ void Petrack::createActions()
     connect(mPlayerLooping, &QAction::triggered, mPlayerWidget, &Player::setLooping);
     // -------------------------------------------------------------------------------------------------------
 
-    QSignalMapper *signalMapper = new QSignalMapper(this);
 
     mDelPastAct = new QAction(tr("&Past part of all trj."), this);
-    connect(mDelPastAct, SIGNAL(triggered()), signalMapper, SLOT(map()));
-    signalMapper->setMapping(mDelPastAct, -1);
-    connect(signalMapper, SIGNAL(mapped(int)), this, SLOT(deleteTrackPointAll(int))); // -1
+    connect(
+        mDelPastAct,
+        &QAction::triggered,
+        this,
+        [this]() { this->deleteTrackPointAll(PersonStorage::Direction::Previous); });
 
     mDelFutureAct = new QAction(tr("&Future part of all trj."), this);
-    connect(mDelFutureAct, SIGNAL(triggered()), signalMapper, SLOT(map()));
-    signalMapper->setMapping(mDelFutureAct, 1);
-    connect(signalMapper, SIGNAL(mapped(int)), this, SLOT(deleteTrackPointAll(int))); // 1
+    connect(
+        mDelFutureAct,
+        &QAction::triggered,
+        this,
+        [this]() { this->deleteTrackPointAll(PersonStorage::Direction::Following); });
 
     mDelAllRoiAct = new QAction(tr("&Trj. moving through ROI"), this);
-    connect(mDelAllRoiAct, SIGNAL(triggered()), this, SLOT(deleteTrackPointROI()));
+    connect(mDelAllRoiAct, &QAction::triggered, this, &Petrack::deleteTrackPointROI);
     mDelPartRoiAct = new QAction(tr("Part of Trj. inside &ROI"), this);
-    connect(mDelPartRoiAct, SIGNAL(triggered()), this, SLOT(deleteTrackPointInsideROI()));
+    connect(mDelPartRoiAct, &QAction::triggered, this, &Petrack::deleteTrackPointInsideROI);
 
     // -------------------------------------------------------------------------------------------------------
 
@@ -2698,7 +2683,7 @@ void Petrack::importTracker(QString dest) // default = ""
                 trcVersion = 1;
             }
 
-            if((sz > 0) && (mTracker->size() != 0))
+            if((sz > 0) && (mPersonStorage.nbPersons() != 0))
             {
                 debout << "Warning: Overlapping trajectories will be joined not until tracking adds new trackpoints."
                        << std::endl;
@@ -2713,14 +2698,14 @@ void Petrack::importTracker(QString dest) // default = ""
                 {
                     in >> tp;
                 }
-                mTracker->append(tp);
+                mPersonStorage.addPerson(tp);
                 tp.clear(); // loeschen, sonst immer weitere pfade angehangen werden
             }
 
-            mControlWidget->trackNumberAll->setText(QString("%1").arg(mTracker->size()));
-            mControlWidget->trackShowOnlyNr->setMaximum(MAX(mTracker->size(), 1));
+            mControlWidget->trackNumberAll->setText(QString("%1").arg(mPersonStorage.nbPersons()));
+            mControlWidget->trackShowOnlyNr->setMaximum(MAX(mPersonStorage.nbPersons(), 1));
             mControlWidget->trackNumberVisible->setText(
-                QString("%1").arg(mTracker->visible(mAnimation->getCurrentFrameNum())));
+                QString("%1").arg(mPersonStorage.visible(mAnimation->getCurrentFrameNum())));
             mControlWidget->colorPlot->replot();
             file.close();
             debout << "import " << dest << " (" << sz << " person(s), file version " << trcVersion << ")" << std::endl;
@@ -2768,7 +2753,7 @@ void Petrack::importTracker(QString dest) // default = ""
                 if(in.atEnd())
                 {
                     tp.setLastFrame(frameNr);
-                    mTracker->append(tp);
+                    mPersonStorage.addPerson(tp);
                     ++sz;
                     tp.clear();
                     break;
@@ -2826,7 +2811,7 @@ void Petrack::importTracker(QString dest) // default = ""
                 // Neue ID ? ==> letzte Person beendet ==> abspeichern
                 if(personNr > current_personNr)
                 {
-                    mTracker->append(tp);
+                    mPersonStorage.addPerson(tp);
                     ++sz;
                     current_personNr++;
                     tp.clear();
@@ -2847,10 +2832,10 @@ void Petrack::importTracker(QString dest) // default = ""
                 }
             }
 
-            mControlWidget->trackNumberAll->setText(QString("%1").arg(mTracker->size()));
-            mControlWidget->trackShowOnlyNr->setMaximum(MAX(mTracker->size(), 1));
+            mControlWidget->trackNumberAll->setText(QString("%1").arg(mPersonStorage.nbPersons()));
+            mControlWidget->trackShowOnlyNr->setMaximum(MAX(mPersonStorage.nbPersons(), 1));
             mControlWidget->trackNumberVisible->setText(
-                QString("%1").arg(mTracker->visible(mAnimation->getCurrentFrameNum())));
+                QString("%1").arg(mPersonStorage.visible(mAnimation->getCurrentFrameNum())));
             mControlWidget->colorPlot->replot();
             file.close();
             debout << "import " << dest << " (" << sz << " person(s) )" << std::endl;
@@ -2870,7 +2855,7 @@ void Petrack::testTracker()
     static int idx = 0; // index in Fehlerliste, die als letztes angesprungen wurde
     QList<int> pers, frame;
 
-    mTracker->checkPlausibility(
+    mPersonStorage.checkPlausibility(
         pers,
         frame,
         mControlWidget->testEqual->isChecked(),
@@ -2961,14 +2946,13 @@ void Petrack::exportTracker(QString dest) // default = ""
                 tstart       = clock();
 #endif
                 QTemporaryFile file;
-                int            i;
 
                 if(!file.open() /*!file.open(QIODevice::WriteOnly | QIODevice::Text)*/)
                 {
                     PCritical(this, tr("PeTrack"), tr("Cannot open %1:\n%2.").arg(dest).arg(file.errorString()));
                     return;
                 }
-                QProgressDialog progress("Export TRC-File", nullptr, 0, mTracker->size() + 1, this->window());
+                QProgressDialog progress("Export TRC-File", nullptr, 0, mPersonStorage.nbPersons() + 1, this->window());
                 progress.setWindowTitle("Export .trc-File");
                 progress.setWindowModality(Qt::WindowModal);
                 progress.setVisible(true);
@@ -2979,18 +2963,20 @@ void Petrack::exportTracker(QString dest) // default = ""
 
                 trcVersion = 4;
 
-                debout << "export tracking data to " << dest << " (" << mTracker->size() << " person(s), file version "
-                       << trcVersion << ")..." << std::endl;
+                debout << "export tracking data to " << dest << " (" << mPersonStorage.nbPersons()
+                       << " person(s), file version " << trcVersion << ")..." << std::endl;
                 QTextStream out(&file);
 
                 out << "version " << trcVersion << Qt::endl;
-                out << mTracker->size() << Qt::endl;
-                for(i = 0; i < mTracker->size(); ++i)
+                out << mPersonStorage.nbPersons() << Qt::endl;
+                const auto &persons = mPersonStorage.getPersons();
+                for(size_t i = 0; i < persons.size(); ++i)
                 {
                     qApp->processEvents();
-                    progress.setLabelText(QString("Export person %1 of %2 ...").arg(i + 1).arg(mTracker->size()));
+                    progress.setLabelText(
+                        QString("Export person %1 of %2 ...").arg(i + 1).arg(mPersonStorage.nbPersons()));
                     progress.setValue(i + 1);
-                    out << (*mTracker)[i] << Qt::endl;
+                    out << persons[i] << Qt::endl;
                 }
                 file.flush();
                 file.close();
@@ -3023,7 +3009,7 @@ void Petrack::exportTracker(QString dest) // default = ""
                     statusBar()->showMessage(tr("Saved tracking data to %1.").arg(dest), 5000);
                 }
 
-                progress.setValue(mTracker->size() + 1);
+                progress.setValue(mPersonStorage.nbPersons() + 1);
 
                 std::cout << " finished " << std::endl;
 
@@ -3054,7 +3040,7 @@ void Petrack::exportTracker(QString dest) // default = ""
                     return;
                 }
 
-                debout << "export tracking data to " << dest << " (" << mTracker->size() << " person(s))..."
+                debout << "export tracking data to " << dest << " (" << mPersonStorage.nbPersons() << " person(s))..."
                        << std::endl;
 
 #ifdef TIME_MEASUREMENT
@@ -3072,7 +3058,7 @@ void Petrack::exportTracker(QString dest) // default = ""
                     }
                     else // 2D
                     {
-                        mTracker->recalcHeight(mControlWidget->coordAltitude->value());
+                        mPersonStorage.recalcHeight(mControlWidget->coordAltitude->value());
                     }
                 }
 #ifdef TIME_MEASUREMENT
@@ -3125,15 +3111,15 @@ void Petrack::exportTracker(QString dest) // default = ""
                     std::cout << "ID  | Comment" << std::endl;
                     std::cout << "----|----------------" << std::endl;
 
-                    for(int i = 0; i < mTracker->size(); ++i)
+                    for(int i = 0; i < static_cast<int>(mPersonStorage.nbPersons()); ++i)
                     {
-                        auto commentSplit = mTracker->at(i).comment().split("\n", Qt::KeepEmptyParts);
+                        auto commentSplit = mPersonStorage.at(i).comment().split("\n", Qt::KeepEmptyParts);
                         out << "#" << qSetFieldWidth(3) << (i + 1) << qSetFieldWidth(0) << "|" << commentSplit.at(0)
                             << Qt::endl;
                         std::cout << std::setw(4) << (i + 1) << "|" << commentSplit.at(0) << std::endl;
 
                         commentSplit.pop_front();
-                        for(const auto &line : commentSplit)
+                        for(const QString &line : commentSplit)
                         {
                             out << "#" << qSetFieldWidth(3) << " " << qSetFieldWidth(0) << "|" << line << Qt::endl;
                             std::cout << "    |" << line << std::endl;
@@ -3201,7 +3187,7 @@ void Petrack::exportTracker(QString dest) // default = ""
                 // einfliessen zu lassen)
                 if(mControlWidget->trackRecalcHeight->checkState())
                 {
-                    mTracker->recalcHeight(mControlWidget->coordAltitude->value());
+                    mPersonStorage.recalcHeight(mControlWidget->coordAltitude->value());
                 }
                 mTrackerReal->calculate(
                     mTracker,
@@ -3221,7 +3207,7 @@ void Petrack::exportTracker(QString dest) // default = ""
                     mControlWidget->exportMarkerID->isChecked(),
                     autoCorrectOnlyExport);
 
-                debout << "export tracking data to " << dest << " (" << mTracker->size() << " person(s))..."
+                debout << "export tracking data to " << dest << " (" << mPersonStorage.nbPersons() << " person(s))..."
                        << std::endl;
                 QTextStream outDat(&fileDat);
                 mTrackerReal->exportDat(
@@ -3258,7 +3244,7 @@ void Petrack::exportTracker(QString dest) // default = ""
                 // einfliessen zu lassen)
                 if(mControlWidget->trackRecalcHeight->checkState())
                 {
-                    mTracker->recalcHeight(mControlWidget->coordAltitude->value());
+                    mPersonStorage.recalcHeight(mControlWidget->coordAltitude->value());
                 }
 
                 mTrackerReal->calculate(
@@ -3285,7 +3271,7 @@ void Petrack::exportTracker(QString dest) // default = ""
                     PCritical(this, tr("PeTrack"), tr("Cannot open %1:\n%2.").arg(dest).arg(fileXml.errorString()));
                     return;
                 }
-                debout << "export tracking data to " << dest << " (" << mTracker->size() << " person(s))..."
+                debout << "export tracking data to " << dest << " (" << mPersonStorage.nbPersons() << " person(s))..."
                        << std::endl;
                 // already done: mTrackerReal->calculate(mTracker, mImageItem, mControlWidget->getColorPlot(),
                 // getImageBorderSize(), mControlWidget->trackMissingFrames->checkState());
@@ -3295,7 +3281,7 @@ void Petrack::exportTracker(QString dest) // default = ""
                 outXml << "    <header version=\"1.0\">" << Qt::endl;
                 outXml << "        <roomCaption>PeTrack: " << mAnimation->getFileBase() << "</roomCaption>" << Qt::endl;
                 outXml << "        <roomID>0</roomID>" << Qt::endl;
-                outXml << "        <agents>" << mTracker->size() << "</agents>" << Qt::endl;
+                outXml << "        <agents>" << mPersonStorage.nbPersons() << "</agents>" << Qt::endl;
                 outXml << "        <frameRate>" << mAnimation->getFPS() << "</frameRate> <!--per second-->" << Qt::endl;
                 // outXml << "        <timeStep>" << 1000./mAnimation->getFPS() << "</timeStep>   <!-- millisecond-->"
                 // << endl; inverse von
@@ -3411,7 +3397,7 @@ void Petrack::trackAll()
         // zuruecksprinegn an die stelle, wo der letzte trackPath nicht vollstaendig
         // etwas spaeter, da erste punkte in reco path meist nur ellipse ohne markererkennung
         mControlWidget->trackOnlineCalc->setCheckState(Qt::Unchecked);
-        mPlayerWidget->skipToFrame(mTracker->largestFirstFrame() + 5);
+        mPlayerWidget->skipToFrame(mPersonStorage.largestFirstFrame() + 5);
         mControlWidget->trackOnlineCalc->setCheckState(Qt::Checked);
         // progVal = 2*mAnimation->getNumFrames()-memPos-mPlayerWidget->getPos();
         progVal += mAnimation->getNumFrames() - mPlayerWidget->getPos();
@@ -3446,7 +3432,7 @@ void Petrack::trackAll()
 
     if(mAutoTrackOptimizeColor)
     {
-        mTracker->optimizeColor();
+        mPersonStorage.optimizeColor();
     }
 
     mControlWidget->performRecognition->setCheckState(memRecoState);
@@ -3599,10 +3585,10 @@ void Petrack::updateImage(bool imageChanged) // default = false (only true for n
 #endif
 
         // delete track list, if intrinsic param have changed
-        if(calibChanged && mTracker->size() > 0) // mCalibFilter.getEnabled() &&
+        if(calibChanged && mPersonStorage.nbPersons() > 0) // mCalibFilter.getEnabled() &&
         {
             // Evtl. nicht Tracker loeschen sondern entsprechend der neuen Calibration verschieben?!?!?
-            mTracker->clear();
+            mPersonStorage.clear();
             mTracker->reset();
             if(!isLoading())
             {
@@ -3620,7 +3606,7 @@ void Petrack::updateImage(bool imageChanged) // default = false (only true for n
                 // buildt disparity picture if it should be used for height detection
                 mStereoContext->getDisparity();
 
-                mTracker->calcPosition(frameNum);
+                mPersonStorage.calcPosition(frameNum);
             }
 #endif
         }
@@ -3737,12 +3723,12 @@ void Petrack::updateImage(bool imageChanged) // default = false (only true for n
                 //        "==========: "
                 debout << "nach  reco: " << getElapsedTime() << endl;
 #endif
-                mTracker->addPoints(persList, frameNum, mReco.getRecoMethod());
+                mPersonStorage.addPoints(persList, frameNum, mReco.getRecoMethod());
 
                 // folgendes lieber im Anschluss, ggf beim exportieren oder statt test direkt del:
                 if(mStereoContext && mStereoWidget->stereoUseForReco->isChecked())
                 {
-                    mTracker->purge(frameNum); // bereinigen wenn weniger als 0.2 recognition und nur getrackt
+                    mPersonStorage.purge(frameNum); // bereinigen wenn weniger als 0.2 recognition und nur getrackt
                 }
 
                 mControlWidget->recoNumberNow->setText(QString("%1").arg(persList.size()));
@@ -3766,11 +3752,11 @@ void Petrack::updateImage(bool imageChanged) // default = false (only true for n
         }
 
         mControlWidget->trackNumberAll->setText(
-            QString("%1").arg(mTracker->size())); // kann sich durch reco und tracker aendern
+            QString("%1").arg(mPersonStorage.nbPersons())); // kann sich durch reco und tracker aendern
         mControlWidget->trackShowOnlyNr->setMaximum(
-            MAX(mTracker->size(), 1)); // kann sich durch reco und tracker aendern
+            MAX(mPersonStorage.nbPersons(), 1)); // kann sich durch reco und tracker aendern
         mControlWidget->trackNumberVisible->setText(
-            QString("%1").arg(mTracker->visible(frameNum))); // kann sich durch reco und tracker aendern
+            QString("%1").arg(mPersonStorage.visible(frameNum))); // kann sich durch reco und tracker aendern
 
         // in anzuzeigendes Bild kopieren
         // erst hier wird die bildgroesse von mimage an filteredimg mit border angepasst
@@ -3895,13 +3881,15 @@ double Petrack::getHeadSize(QPointF *pos, int pers, int frame)
 {
     double z, h;
 
-    if((pers >= 0) && (pers < mTracker->size()) && mTracker->at(pers).trackPointExist(frame))
+    if((pers >= 0) && (pers < static_cast<int>(mPersonStorage.nbPersons())) &&
+       mPersonStorage.at(pers).trackPointExist(frame))
     {
         if(mControlWidget->getCalibCoordDimension() == 0)
         {
             int         diff;
             cv::Point3f p3d = getExtrCalibration()->get3DPoint(
-                cv::Point2f(mTracker->at(pers).trackPointAt(frame).x(), mTracker->at(pers).trackPointAt(frame).y()),
+                cv::Point2f(
+                    mPersonStorage.at(pers).trackPointAt(frame).x(), mPersonStorage.at(pers).trackPointAt(frame).y()),
                 mControlWidget->mapDefaultHeight->value());
 
             cv::Point2f p3d_x1 =
@@ -3920,8 +3908,8 @@ double Petrack::getHeadSize(QPointF *pos, int pers, int frame)
         }
         else
         {
-            z = mTracker->at(pers).trackPointAt(frame).sp().z();
-            h = mTracker->at(pers).height();
+            z = mPersonStorage.at(pers).trackPointAt(frame).sp().z();
+            h = mPersonStorage.at(pers).height();
             if(z > 0)
             {
                 return (HEAD_SIZE * mControlWidget->coordAltitude->value() / z) / getImageItem()->getCmPerPixel();
@@ -4033,23 +4021,23 @@ void Petrack::addManualTrackPointOnlyVisible(const QPointF &pos)
     int pers = addOrMoveManualTrackPoint(pos) + 1;
     if(pers == 0)
     {
-        pers = mTracker->size() + 1;
+        pers = static_cast<int>(mPersonStorage.nbPersons()) + 1;
     }
-    pers = mControlWidget->trackShowOnlyNr->maximum();
     mControlWidget->trackShowOnlyNr->setValue(pers);
     mControlWidget->trackShowOnly->setChecked(true);
 }
 
 void Petrack::updateControlWidget()
 {
-    mControlWidget->trackNumberAll->setText(QString("%1").arg(mTracker->size()));
-    mControlWidget->trackShowOnlyNr->setMaximum(MAX(mTracker->size(), 1));
-    mControlWidget->trackNumberVisible->setText(QString("%1").arg(mTracker->visible(mAnimation->getCurrentFrameNum())));
+    mControlWidget->trackNumberAll->setText(QString("%1").arg(mPersonStorage.nbPersons()));
+    mControlWidget->trackShowOnlyNr->setMaximum(MAX(mPersonStorage.nbPersons(), 1));
+    mControlWidget->trackNumberVisible->setText(
+        QString("%1").arg(mPersonStorage.visible(mAnimation->getCurrentFrameNum())));
 }
 
 void Petrack::splitTrackPerson(QPointF pos)
 {
-    mTracker->splitPersonAt((Vec2F) pos, mAnimation->getCurrentFrameNum(), getPedestrianUserSelection());
+    mPersonStorage.splitPersonAt((Vec2F) pos, mAnimation->getCurrentFrameNum(), getPedestrianUserSelection());
     updateControlWidget();
 }
 
@@ -4068,7 +4056,7 @@ int Petrack::addOrMoveManualTrackPoint(const QPointF &pos)
     int        pers = -1;
     TrackPoint tP(Vec2F{pos}, 110); // 110 is higher than 100 (max. quality) and gets clamped to 100 after insertion
     // allows replacemet of every point (check for better quality always passes)
-    mTracker->addPoint(
+    mPersonStorage.addPoint(
         tP, mAnimation->getCurrentFrameNum(), getPedestrianUserSelection(), mReco.getRecoMethod(), &pers);
     updateControlWidget();
     return pers;
@@ -4078,43 +4066,45 @@ int Petrack::addOrMoveManualTrackPoint(const QPointF &pos)
 // loeschen von Trackpoints einer Trajektorie
 void Petrack::deleteTrackPoint(QPointF pos, int direction) // const QPoint &pos
 {
-    mTracker->delPoint((Vec2F) pos, direction, mAnimation->getCurrentFrameNum(), getPedestrianUserSelection());
+    mPersonStorage.delPoint((Vec2F) pos, direction, mAnimation->getCurrentFrameNum(), getPedestrianUserSelection());
     updateControlWidget();
 }
 void Petrack::editTrackPersonComment(QPointF pos)
 {
-    mTracker->editTrackPersonComment((Vec2F) pos, mAnimation->getCurrentFrameNum(), getPedestrianUserSelection());
+    mPersonStorage.editTrackPersonComment((Vec2F) pos, mAnimation->getCurrentFrameNum(), getPedestrianUserSelection());
     updateControlWidget();
 }
 void Petrack::setTrackPersonHeight(QPointF pos)
 {
-    mTracker->setTrackPersonHeight((Vec2F) pos, mAnimation->getCurrentFrameNum(), getPedestrianUserSelection());
+    mPersonStorage.setTrackPersonHeight((Vec2F) pos, mAnimation->getCurrentFrameNum(), getPedestrianUserSelection());
     updateControlWidget();
 }
 void Petrack::resetTrackPersonHeight(QPointF pos)
 {
-    mTracker->resetTrackPersonHeight((Vec2F) pos, mAnimation->getCurrentFrameNum(), getPedestrianUserSelection());
+    mPersonStorage.resetTrackPersonHeight((Vec2F) pos, mAnimation->getCurrentFrameNum(), getPedestrianUserSelection());
     updateControlWidget();
 }
 
-// direction zeigt an, ob bis zum aktuellen (-1), ab dem aktuellen (1) oder ganzer trackpath (0)
-// loeschen von Trackpoints aller Trajektorien
-void Petrack::deleteTrackPointAll(int direction) // const QPoint &pos
+/**
+ * @brief Delete the following, previous or whole trajectory of **all** trajectories
+ * @param direction previous, following or whole
+ */
+void Petrack::deleteTrackPointAll(PersonStorage::Direction direction) // const QPoint &pos
 {
-    mTracker->delPointAll(direction, mAnimation->getCurrentFrameNum());
+    mPersonStorage.delPointAll(direction, mAnimation->getCurrentFrameNum());
     updateControlWidget();
 }
 
 void Petrack::deleteTrackPointROI()
 {
-    mTracker->delPointROI();
+    mPersonStorage.delPointROI();
     updateControlWidget();
     mScene->update();
 }
 
 void Petrack::deleteTrackPointInsideROI()
 {
-    mTracker->delPointInsideROI();
+    getPersonStorage().delPointInsideROI();
     updateControlWidget();
     mScene->update();
 }
diff --git a/src/recognition.cpp b/src/recognition.cpp
index 0aeae9efc..0c66ba347 100644
--- a/src/recognition.cpp
+++ b/src/recognition.cpp
@@ -161,7 +161,7 @@ void setColorParameter(const QColor &fromColor, const QColor &toColor, bool inve
  * @param controlWidget
  * @return
  */
-Vec2F autoCorrectColorMarker(Vec2F &boxImageCentre, Control *controlWidget)
+Vec2F autoCorrectColorMarker(const Vec2F &boxImageCentre, Control *controlWidget)
 {
     Petrack *   mainWindow = controlWidget->getMainWindow();
     cv::Point2f tp         = mainWindow->getExtrCalibration()->getImagePoint(cv::Point3f(
diff --git a/src/tracker.cpp b/src/tracker.cpp
index 8fde8295e..18f11c323 100644
--- a/src/tracker.cpp
+++ b/src/tracker.cpp
@@ -267,7 +267,7 @@ void TrackPerson::recalcHeight(float altitude)
 
 // gibt den ersten z-wert um index i heraus der ungleich -1 ist
 // zudem interpolation zwischen Werten!
-double TrackPerson::getNearestZ(int i, int *extrapolated)
+double TrackPerson::getNearestZ(int i, int *extrapolated) const
 {
     *extrapolated = 0;
     if((i < 0) || (i >= size())) // indexueberpruefung
@@ -595,7 +595,7 @@ double TrackPerson::distanceToNextFrame(int frame) const
 // 6. calculate color over tracking (accumulated over tracking while procedure above) path and set height
 // 7. recalc coord with real coord with known height
 
-Tracker::Tracker(QWidget *wParent)
+Tracker::Tracker(QWidget *wParent, PersonStorage &storage) : mPersonStorage(storage)
 {
     mMainWindow = (class Petrack *) wParent;
     mTermCriteria =
@@ -608,7 +608,7 @@ Tracker::Tracker(QWidget *wParent)
 // neben loeschen der liste muessen auch ...
 void Tracker::init(cv::Size size)
 {
-    clear(); // loescht liste aller getrackten personen
+    mPersonStorage.clear(); // loescht liste aller getrackten personen
     // nicht mehr noetig, da nicht mehr in track selber // jetzt start, war prevImg == NULL && prevFrame == -1 zeigt an,
     // dass ein neuer Trackingprozess beginnt / neue Bildfolge
 
@@ -647,658 +647,6 @@ void Tracker::resize(cv::Size size)
     }
 }
 
-/// split trajectorie pers before frame frame
-void Tracker::splitPerson(int pers, int frame)
-{
-    int j;
-
-    if(at(pers).firstFrame() < frame)
-    {
-        append(at(pers));
-
-        // alte trj einkuerzen und ab aktuellem frame zukunft loeschen
-        for(j = 0; j < at(pers).lastFrame() - frame + 1; ++j)
-        {
-            (*this)[pers].removeLast();
-        }
-        (*this)[pers].setLastFrame(frame - 1);
-
-        // neu angehaengte/gedoppelte trajektorie
-        for(j = 0; j < frame - last().firstFrame(); ++j)
-        {
-            last().removeFirst();
-        }
-        last().setFirstFrame(frame);
-    }
-}
-
-/**
- * @brief Split trajectory at point point before given frame
- *
- * @param point point where to split trajectory (helpful if onlyVisible isn't set)
- * @param frame frame at which to split the trajectory
- * @param onlyVisible set of people for whom to do it (empty means everyone)
- * @return true if a trajectory was split
- */
-bool Tracker::splitPersonAt(const Vec2F &point, int frame, QSet<int> onlyVisible)
-{
-    int i;
-
-    for(i = 0; i < size(); ++i) // ueber TrackPerson
-    {
-        if(((onlyVisible.empty()) || (onlyVisible.contains(i))) &&
-           (at(i).trackPointExist(frame) &&
-            (at(i).trackPointAt(frame).distanceToPoint(point) < mMainWindow->getHeadSize(nullptr, i, frame) / 2.)))
-        {
-            splitPerson(i, frame);
-
-            return true;
-        }
-    }
-    return false;
-}
-
-/**
- * @brief Deletes points of pers
- * @param pers TrackPerson whose points should be deleted
- * @param direction notes if previous (-1), following(1) or whole(0) trajectory should be deleted
- * @param frame
- * @return true, if deletion occured
- */
-bool Tracker::delPointOf(int pers, int direction, int frame)
-{
-    int j;
-
-    if(direction == -1)
-    {
-        for(j = 0; j < frame - at(pers).firstFrame(); ++j)
-        {
-            (*this)[pers].removeFirst();
-        }
-        (*this)[pers].setFirstFrame(frame);
-    }
-    else if(direction == 0)
-    {
-        removeAt(pers);
-    }
-    else if(direction == 1)
-    {
-        for(j = 0; j < at(pers).lastFrame() - frame; ++j)
-        {
-            (*this)[pers].removeLast();
-        }
-        (*this)[pers].setLastFrame(frame);
-    }
-
-    return true;
-}
-
-// gibt true zurueck, wenn punkt geloescht werden konnte
-// direction zeigt an, ob bis zum aktuellen (-1), ab dem aktuellen (1) oder ganzer trackpath (0)
-// onlyVisible == -1 : immer alles betrachten, ansonsten nur person onlyVisible
-// loescht trackpoint nur einer trajektorie
-/**
- * @brief Deletes points of a SINGLE person in onlyVisible
- * @param point point which need to be on the person (helpful if onlyVisible is not properly set)
- * @param direction notes if previous (-1), following(1) or whole(0) trajectory should be deleted
- * @param frame
- * @param onlyVisible set of people whose points could be deleted; empty means everyone
- * @return true if deletion occured
- */
-bool Tracker::delPoint(const Vec2F &point, int direction, int frame, QSet<int> onlyVisible)
-{
-    int i;
-
-    for(i = 0; i < size(); ++i) // ueber TrackPerson
-    {
-        if(((onlyVisible.empty()) || (onlyVisible.contains(i))) &&
-           (at(i).trackPointExist(frame) &&
-            (at(i).trackPointAt(frame).distanceToPoint(point) < mMainWindow->getHeadSize(nullptr, i, frame) / 2.)))
-        {
-            delPointOf(i, direction, frame);
-            return true;
-        }
-    }
-    return false;
-}
-
-/**
- * @brief Deletes trackpoints of all trajectories
- * @param direction notes if previous (-1), following(1) or whole(0) trajectory should be deleted
- * @param frame
- */
-void Tracker::delPointAll(int direction, int frame)
-{
-    int i, j;
-
-    for(i = 0; i < size(); ++i) // ueber TrackPerson
-    {
-        if(at(i).trackPointExist(frame)) //
-        {
-            if(direction == -1)
-            {
-                for(j = 0; j < frame - at(i).firstFrame(); ++j)
-                {
-                    (*this)[i].removeFirst();
-                }
-                (*this)[i].setFirstFrame(frame);
-            }
-            else if(direction == 0)
-            {
-                removeAt(i--); // nach Loeschen wird i um 1 erniedrigt
-            }
-            else if(direction == 1)
-            {
-                for(j = 0; j < at(i).lastFrame() - frame; ++j)
-                {
-                    (*this)[i].removeLast();
-                }
-                (*this)[i].setLastFrame(frame);
-            }
-        }
-        else if(
-            ((direction == -1) && (frame > at(i).lastFrame())) || (direction == 0) ||
-            ((direction == 1) && (frame < at(i).firstFrame())))
-        {
-            removeAt(i);
-            i--;
-        }
-    }
-}
-
-
-// deletes points of a trajectrory, which are inside ROI
-// 1 trajectory can end in 0, 1 or multiple trajectories!!!!!!!!
-// man koennte noch unterscheiden, ob trajektorie aktuell in petrack zu sehen sein soll
-void Tracker::delPointInsideROI()
-{
-    int    i, j;
-    QRectF rect = mMainWindow->getRecoRoiItem()->rect();
-    bool   inside;
-
-    for(i = 0; i < size(); ++i) // ueber TrackPerson
-    {
-        inside = ((at(i).size() > 0) && rect.contains(at(i).at(0).x(), at(i).at(0).y()));
-        for(j = 1; j < at(i).size(); ++j)
-        {
-            if(inside != rect.contains(at(i).at(j).x(), at(i).at(j).y())) // aenderung von inside
-            {
-                splitPerson(i, at(i).firstFrame() + j);
-                if(inside)
-                {
-                    removeAt(i);
-                    i--;
-                    inside = !inside;
-                }
-                break;
-            }
-        }
-        if(inside)
-        {
-            // rest loeschen
-            removeAt(i);
-            i--;
-        }
-    }
-}
-
-// deletes trajectory, if it is partly inside ROI
-// man koennte noch unterscheiden, ob trajektorie aktuell in petrack zu sehen sein soll
-void Tracker::delPointROI()
-{
-    int    i, j, anz = 0;
-    QRectF rect = mMainWindow->getRecoRoiItem()->rect();
-
-    for(i = 0; i < size(); ++i) // ueber TrackPerson
-    {
-        for(j = 0; j < at(i).size(); ++j)
-        {
-            if(rect.contains(at(i).at(j).x(), at(i).at(j).y()))
-            {
-                anz++;
-                removeAt(i);
-                i--;
-                break;
-            }
-        }
-    }
-    debout << "deleted " << anz << " trajectories!" << std::endl;
-}
-
-/**
- * @brief Editing the comment of a TrackPerson
- *
- * Allows editing the comment of a TrackPerson in a new Dialog. When a new dialog gets opened, it automatically
- * appends 'Frame {\point frame}: ' to the dialog, if no comment for the frame exists.
- *
- * @param point position the user clicked
- * @param frame current frame number
- * @param onlyVisible list of visible persons
- * @return if a comment has been saved
- */
-bool Tracker::editTrackPersonComment(const Vec2F &point, int frame, const QSet<int> &onlyVisible)
-{
-    for(int i = 0; i < size(); ++i) // ueber TrackPerson
-    {
-        if(((onlyVisible.empty()) || (onlyVisible.contains(i))) &&
-           (at(i).trackPointExist(frame) && (at(i).trackPointAt(frame).distanceToPoint(point) <
-                                             mMainWindow->getHeadSize(nullptr, i, frame) /
-                                                 2.))) // war: MIN_DISTANCE)) // 30 ist abstand zwischen kopfen
-        {
-            QString displayedComment = at(i).comment();
-            QString framePrefix      = "Frame " + QString::number(frame, 'g', 5) + ": ";
-
-            if(displayedComment.isEmpty())
-            {
-                displayedComment.append(framePrefix);
-            }
-            else if(!displayedComment.contains(framePrefix))
-            {
-                displayedComment.append("\n" + framePrefix);
-            }
-
-            bool    ok      = false;
-            QString comment = QInputDialog::getMultiLineText(
-                mMainWindow, QObject::tr("Add Comment"), QObject::tr("Comment:"), displayedComment, &ok);
-
-            if(ok)
-            {
-                if(comment.isEmpty())
-                {
-                    int ret = PWarning(
-                        mMainWindow,
-                        QObject::tr("Empty comment"),
-                        QObject::tr("Are you sure you want to save an empty comment?"),
-                        PMessageBox::StandardButton::Save | PMessageBox::StandardButton::Cancel);
-                    if(ret == PMessageBox::StandardButton::Cancel)
-                    {
-                        return false;
-                    }
-                }
-                (*this)[i].setComment(comment);
-                return true;
-            }
-        }
-    }
-    return false;
-}
-
-bool Tracker::setTrackPersonHeight(const Vec2F &point, int frame, QSet<int> onlyVisible)
-{
-    int i;
-
-    for(i = 0; i < size(); ++i) // ueber TrackPerson
-    {
-        if(((onlyVisible.empty()) || (onlyVisible.contains(i))) &&
-           (at(i).trackPointExist(frame) && (at(i).trackPointAt(frame).distanceToPoint(point) <
-                                             mMainWindow->getHeadSize(nullptr, i, frame) /
-                                                 2.))) // war: MIN_DISTANCE)) // 30 ist abstand zwischen kopfen
-        {
-            bool ok;
-
-            double col_height;
-            // col_height is negative, if height is determined through color and not yet set manually
-            if(at(i).height() < MIN_HEIGHT + 1)
-            {
-                col_height = at(i).color().isValid() ?
-                                 -mMainWindow->getControlWidget()->getColorPlot()->map(at(i).color()) :
-                                 -mMainWindow->getControlWidget()->mapDefaultHeight->value();
-            }
-            else
-            {
-                col_height = at(i).height();
-            }
-
-
-            double height = QInputDialog::getDouble(
-                mMainWindow,
-                QObject::tr("Set person height"),
-                QObject::tr("Person height[cm]:"),
-                fabs(col_height),
-                -500,
-                500,
-                1,
-                &ok);
-            if(ok)
-            {
-                if(height < 0)
-                {
-                    debout << "Warning: you entered a negative height!" << std::endl; // is not supported!" << endl;
-                    // return false;
-                }
-                // if previous value (col_height) is negative, height was determined thru color. If manually set value
-                // is the color-map value, we do not change anything
-                // @todo: @ar.graf: check if manually set values have side-effects (maybe do not show in statistics)
-                if(!(std::abs(col_height + height) < 0.01))
-                {
-                    (*this)[i].setHeight(height);
-                    return true;
-                }
-                else
-                {
-                    debout << std::endl
-                           << "No height change detected. Color-mapped height will remain set." << std::endl;
-                }
-            }
-        }
-    }
-    return false;
-}
-bool Tracker::resetTrackPersonHeight(const Vec2F &point, int frame, QSet<int> onlyVisible)
-{
-    int i;
-
-    for(i = 0; i < size(); ++i) // ueber TrackPerson
-    {
-        if(((onlyVisible.empty()) || (onlyVisible.contains(i))) &&
-           (at(i).trackPointExist(frame) && (at(i).trackPointAt(frame).distanceToPoint(point) <
-                                             mMainWindow->getHeadSize(nullptr, i, frame) /
-                                                 2.))) // war: MIN_DISTANCE)) // 30 ist abstand zwischen kopfen
-        {
-            (*this)[i].setHeight(MIN_HEIGHT);
-            return true;
-        }
-    }
-    return false;
-}
-
-
-// used for calculation of 3D point for all points in frame
-// returns number of found points or -1 if no stereoContext available (also points without disp found are counted)
-int Tracker::calcPosition(int /*frame*/)
-{
-#ifndef STEREO_DISABLED
-    int                 anz = 0, notFoundDisp = 0;
-    pet::StereoContext *sc = mMainWindow->getStereoContext();
-    float               x, y, z;
-
-    if(sc)
-    {
-        // for every point of a person, which has already identified at this frame
-        for(int i = 0; i < size(); ++i) // ueber TrackPerson
-        {
-            if(at(i).trackPointExist(frame))
-            {
-                ++anz;
-
-                // TrackPoint *point = &(at(i).trackPointAt(frame));
-                //  ACHTUNG: BORDER NICHT BEACHTET bei p.x()...???
-                //  calculate height with disparity map
-                if(sc->getMedianXYZaround(
-                       (int) at(i).trackPointAt(frame).x(),
-                       (int) at(i).trackPointAt(frame).y(),
-                       &x,
-                       &y,
-                       &z)) // nicht myRound, da pixel 0 von 0..0.99 in double geht
-                {
-                    // hier kommt man nur hinein, wenn x, y, z Wert berechnet werden konnten
-                    // statt altitude koennte hier irgendwann die berechnete Bodenhoehe einfliessen
-                    (*this)[i][frame - at(i).firstFrame()].setSp(x, y, z); // setZdistanceToCam(z);
-                    (*this)[i].setHeight(z, mMainWindow->getControlWidget()->coordAltitude->value());
-                }
-                else
-                    ++notFoundDisp;
-                // else // Meldung zu haeufig
-                //     debout << "Warning: No disparity information for person " << i+1 << "." << endl;
-            }
-        }
-        // if (notFoundDisp>0) // Meldung zu haeufig
-        //     debout << "Warning: No disparity information found for " << (100.*notFoundDisp)/anz << " percent of
-        //     points." << endl;
-        return anz;
-    }
-    else
-        return -1;
-#endif
-    return -1;
-}
-
-/**
- * @brief Adds the point to the Tracker, either to exising person or creating a new one.
- *
- * This function find the nearest person to the given point and if the distance between point
- * and trajectory is small enough, it gets added to this trajectory. If the point is not fitting to
- * any trajectory, a new TrackPerson is created.
- *
- * It is possible for point to replace existing ones, if the quality is better. (Manual insertion,
- * reverse tracking,...)
- *
- * For multicolor, the color gets added as well. For Aruco, the code of TrackPoint and TrackPerson
- * gets synchronized.
- *
- * This function is used form manual insertions and from recognition.
- *
- * @param[in] point TrackPoint to add
- * @param[in] frame current frame (frame in which point was detected)
- * @param[in] onlyVisible set of selected persons, see Petrack::getPedestriansToTrack()
- * @param[out] pers person the point was added to; undefined when new trajectory was created
- * @return true if new trajectory was created; false otherwise
- */
-bool Tracker::addPoint(
-    TrackPoint &            point,
-    int                     frame,
-    const QSet<int> &       onlyVisible,
-    reco::RecognitionMethod method,
-    int *                   pers)
-{
-    bool  found = false;
-    int   i, iNearest = 0.;
-    float scaleHead;
-    float dist, minDist = 1000000.;
-    float z = -1;
-#ifndef STEREO_DISABLED
-    float x = -1, y = -1;
-
-    // ACHTUNG: BORDER NICHT BEACHTET bei point.x()...
-    // hier wird farbe nur bei reco bestimmt gegebenfalls auch beim tracken interessant
-    // calculate height with disparity map
-    if(mMainWindow->getStereoContext() && mMainWindow->getStereoWidget()->stereoUseForHeight->isChecked())
-    {
-        if(mMainWindow->getStereoContext()->getMedianXYZaround(
-               (int) p.x(), (int) p.y(), &x, &y, &z)) // nicht myRound, da pixel 0 von 0..0.99 in double geht
-        {
-            // statt altitude koennte hier irgendwann die berechnete Bodenhoehe einfliessen
-            p.setSp(x, y, z); // setZdistanceToCam(z);
-        }
-        // cout << " " << point.x()<< " " << point.y() << " " << x << " " << y << " " << z <<endl;
-        // if (i == 10)
-        //     debout << i << " " << mMainWindow->getControlWidget()->coordAltitude->value() - z << " " << z << " " <<
-        //     (*this)[i].height() << endl;
-    }
-#endif
-    // skalierungsfaktor fuer kopfgroesse
-    // fuer multicolor marker groesser, da der schwarze punkt weit am rand liegen kann
-    bool multiColorWithDot = false;
-    if(method == reco::RecognitionMethod::MultiColor &&                 // multicolor marker
-       mMainWindow->getMultiColorMarkerWidget()->useDot->isChecked() && // nutzung von black dot
-       !mMainWindow->getMultiColorMarkerWidget()
-            ->ignoreWithoutDot->isChecked()) // muetzen ohne black dot werden auch akzeptiert
-    {
-        multiColorWithDot = true;
-        scaleHead         = 1.3f;
-    }
-    else
-    {
-        scaleHead = 1.0f;
-    }
-
-    for(i = 0; i < size(); ++i) // !found &&  // ueber TrackPerson
-    {
-        if(((onlyVisible.empty()) || (onlyVisible.contains(i))) && at(i).trackPointExist(frame))
-        {
-            dist = at(i).trackPointAt(frame).distanceToPoint(point);
-            if((dist < scaleHead * mMainWindow->getHeadSize(nullptr, i, frame) / 2.) ||
-               // fuer multifarbmarker mit schwarzem punkt wird nur farbmarker zur Abstandbetrachtung herangezogen
-               // at(i).trackPointAt(frame).colPoint() existiert nicht an dieser stelle, da bisher nur getrackt
-               // wurde!!!!
-               (multiColorWithDot && point.color().isValid() &&
-                (at(i).trackPointAt(frame).distanceToPoint(point.colPoint()) <
-                 mMainWindow->getHeadSize(nullptr, i, frame) / 2.)))
-            {
-                if(found)
-                {
-                    debout << "Warning: more possible trackpoints for point" << std::endl;
-                    debout << "         " << point << " in frame " << frame << " with low distance:" << std::endl;
-                    debout << "         person " << i + 1 << " (distance: " << dist << "), " << std::endl;
-                    debout << "         person " << iNearest + 1 << " (distance: " << minDist << "), " << std::endl;
-                    if(minDist > dist)
-                    {
-                        minDist  = dist;
-                        iNearest = i;
-                    }
-                }
-                else
-                {
-                    minDist  = dist;
-                    iNearest = i;
-                    // WAR: break inner loop
-                    found = true;
-                }
-            }
-        }
-    }
-    if(found) // den naechstgelegenen nehmen
-    {
-        // test, if recognition point or tracked point is better is made in at(i).insertAtFrame
-        if((*this)[iNearest].insertAtFrame(
-               frame,
-               point,
-               iNearest,
-               (mMainWindow->getControlWidget()->trackExtrapolation->checkState() ==
-                Qt::Checked))) // wenn eingefuegt wurde (bessere qualitaet)
-                               //|| !at(i).trackPointAt(frame).color().isValid() moeglich, um auch bei schlechterer
-                               // qualitaet aber aktuell nicht
-                               // vorliegender farbe die ermittelte farbe einzutragen - kommt nicht vor!
-        {
-            // Synchronize TrackPerson.markerID with TrackPoint.markerID
-            (*this)[iNearest].syncTrackPersonMarkerID(point.getMarkerID());
-
-            // set/add color
-            if(point.color().isValid()) // not valid for manual, than old color is used
-            {
-                // if (at(i).trackPointAt(frame).color().isValid()) man koennte alte farbe abziehen - aber nicht noetig,
-                // kommt nicht vor
-                (*this)[iNearest].addColor(point.color());
-            }
-        }
-
-        if(pers != nullptr)
-        {
-            *pers = iNearest;
-        }
-
-        (*this)[iNearest].setNewReco(true);
-    }
-
-    if((onlyVisible.empty()) && !found)
-    {
-        iNearest = size();
-
-        if(point.qual() > 100) // manual add
-        {
-            point.setQual(100);
-        }
-        append(TrackPerson(
-            0, frame, point, point.getMarkerID())); // 0 is person number/markerID; newReco is set to true by default
-    }
-    if((z > 0) && ((onlyVisible.empty()) || found))
-    {
-        (*this)[iNearest].setHeight(z, mMainWindow->getControlWidget()->coordAltitude->value()); // , frame
-    }
-    if((!onlyVisible.empty()) && !found)
-    {
-        QMessageBox::warning(
-            nullptr,
-            "PeTrack",
-            "Adding a manual TrackPoint is only possible, when \"show only people\" and \"show only people list\" are "
-            "disabled!\n"
-            "You would not see the newly created TrackPoint otherwise.");
-        debout << "Warning: No manual insertion, because not all trajectories are visible!" << std::endl;
-        return false;
-    }
-
-    return !found;
-}
-
-// used from recognition
-void Tracker::addPoints(QList<TrackPoint> &pL, int frame, reco::RecognitionMethod method)
-{
-    int i;
-
-    // reset newReco
-    for(i = 0; i < size(); ++i) // ueber TrackPerson
-    {
-        (*this)[i].setNewReco(false);
-    }
-
-    // ueberprufen ob identisch mit einem Punkt in liste
-    for(i = 0; i < pL.size(); ++i) // ueber PointList
-    {
-        addPoint(pL[i], frame, QSet<int>(), method);
-    }
-}
-
-int Tracker::visible(int frameNum)
-{
-    int i, anz = 0;
-    for(i = 0; i < size(); ++i)
-    {
-        if(at(i).trackPointExist(frameNum))
-        {
-            anz++;
-        }
-    }
-    return anz;
-}
-
-int Tracker::largestFirstFrame()
-{
-    int max = -1, i;
-    for(i = 0; i < size(); ++i)
-    {
-        if(at(i).firstFrame() > max)
-        {
-            max = at(i).firstFrame();
-        }
-    }
-    return max;
-}
-int Tracker::largestLastFrame()
-{
-    int max = -1, i;
-    for(i = 0; i < size(); ++i)
-    {
-        if(at(i).lastFrame() > max)
-        {
-            max = at(i).lastFrame();
-        }
-    }
-    return max;
-}
-int Tracker::smallestFirstFrame()
-{
-    int i, min = ((size() > 0) ? at(0).firstFrame() : -1);
-    for(i = 1; i < size(); ++i)
-    {
-        if(at(i).firstFrame() < min)
-        {
-            min = at(i).firstFrame();
-        }
-    }
-    return min;
-}
-int Tracker::smallestLastFrame()
-{
-    int i, min = ((size() > 0) ? at(0).lastFrame() : -1);
-    for(i = 1; i < size(); ++i)
-    {
-        if(at(i).lastFrame() < min)
-        {
-            min = at(i).lastFrame();
-        }
-    }
-    return min;
-}
 
 /**
  * @brief Tracker::calcPrevFeaturePoints calculates all featurePoints(Persons) from the "previous" frame
@@ -1328,13 +676,15 @@ size_t Tracker::calcPrevFeaturePoints(
 
     if(prevFrame != -1)
     {
-        for(int i = 0; i < size(); ++i)
+        const auto &persons = mPersonStorage.getPersons();
+        for(int i = 0; i < static_cast<int>(mPersonStorage.nbPersons()); ++i)
         {
+            const auto &person = persons[i];
             if(!((onlyVisible.empty()) || (onlyVisible.contains(i))))
             {
                 continue;
             }
-            if(!at(i).trackPointExist(prevFrame))
+            if(!person.trackPointExist(prevFrame))
             {
                 continue;
             }
@@ -1352,24 +702,24 @@ size_t Tracker::calcPrevFeaturePoints(
                 int dir = (frame - prevFrame); // direction of tracking - forward/backwards
                 dir /= std::abs(dir);          // theoretically possible to omit MAX_STEP_TRACK frames
                 constexpr int minQualReco = 90;
-                for(int j = 0;
-                    at(i).trackPointExist(frame + j * dir) && at(i).trackPointAt(frame + j * dir).qual() < minQualReco;
+                for(int j = 0; person.trackPointExist(frame + j * dir) &&
+                               person.trackPointAt(frame + j * dir).qual() < minQualReco;
                     ++j)
                 {
-                    if(at(i).trackPointAt(frame + j * dir).qual() < reQual)
+                    if(person.trackPointAt(frame + j * dir).qual() < reQual)
                     {
                         applyReTrack = false;
                         break;
                     }
                 }
             }
-            if(at(i).trackPointExist(frame) && !applyReTrack)
+            if(person.trackPointExist(frame) && !applyReTrack)
             {
                 continue;
             }
 
 
-            Vec2F prevPoint = at(i).at(prevFrame - at(i).firstFrame());
+            Vec2F prevPoint = person.at(prevFrame - person.firstFrame());
             prevPoint += Vec2F(borderSize, borderSize);
             cv::Point2f p2f = prevPoint.toPoint2f();
             if(rect.contains(p2f))
@@ -1489,16 +839,15 @@ int Tracker::insertFeaturePoints(int frame, size_t count, cv::Mat &img, int bord
                         }
                         v.setQual(qual); // qual um 50, damit nur reco-kopf-ellipsen points nicht herauskegeln
                         // bei insertAtFrame wird qual beruecksichtigt, ob vorheiger besser
-                        if((*this)[mPrevFeaturePointsIdx[i]].insertAtFrame(
-                               frame,
-                               v,
-                               mPrevFeaturePointsIdx[i],
-                               (mMainWindow->getControlWidget()->trackExtrapolation->checkState() == Qt::Checked)) &&
-                           (z > 0))
-                        {
-                            (*this)[mPrevFeaturePointsIdx[i]].setHeight(
-                                z, mMainWindow->getControlWidget()->coordAltitude->value()); // , frame
-                        }
+
+                        mPersonStorage.insertFeaturePoint(
+                            mPrevFeaturePointsIdx[i],
+                            frame,
+                            v,
+                            mPrevFeaturePointsIdx[i],
+                            (mMainWindow->getControlWidget()->trackExtrapolation->checkState() == Qt::Checked),
+                            z,
+                            mMainWindow->getControlWidget()->coordAltitude->value());
                     }
 
                     ++inserted;
@@ -1528,65 +877,27 @@ int Tracker::insertFeaturePoints(int frame, size_t count, cv::Mat &img, int bord
  */
 bool Tracker::tryMergeTrajectories(const TrackPoint &v, size_t i, int frame)
 {
-    int  deleteIndex;
-    bool found = false;
-    int  j;
+    bool        found = false;
+    int         j;
+    const auto &persons = mPersonStorage.getPersons();
+    const auto &person  = persons[mPrevFeaturePointsIdx[i]];
     // nach trajektorie suchen, mit der eine verschmelzung erfolgen koennte
-    for(j = 0; !found && j < size(); ++j) // ueber TrackPerson
+    for(j = 0; !found && j < static_cast<int>(mPersonStorage.nbPersons()); ++j) // ueber TrackPerson
     {
-        if(j != mPrevFeaturePointsIdx[i] && at(j).trackPointExist(frame) &&
-           (at(j).trackPointAt(frame).distanceToPoint(v) < mMainWindow->getHeadSize(nullptr, j, frame) / 2.))
+        const auto &other = persons[j];
+        if(j != mPrevFeaturePointsIdx[i] && other.trackPointExist(frame) &&
+           (other.trackPointAt(frame).distanceToPoint(v) < mMainWindow->getHeadSize(nullptr, j, frame) / 2.))
         {
             // um ein fehltracking hin zu einer anderen Trajektorie nicht zum Verschmelzen dieser fuehren zu lassen
             // (die fehlerbehandlung durch interpolation wird in insertAtFrame durchgefuehrt)
-            if(!((at(mPrevFeaturePointsIdx[i]).trackPointExist(frame - 1) &&
-                  (at(mPrevFeaturePointsIdx[i]).trackPointAt(frame - 1).distanceToPoint(v) >
+            if(!((person.trackPointExist(frame - 1) &&
+                  (person.trackPointAt(frame - 1).distanceToPoint(v) >
                    mMainWindow->getHeadSize(nullptr, mPrevFeaturePointsIdx[i], frame - 1) / 2.)) ||
-                 (at(mPrevFeaturePointsIdx[i]).trackPointExist(frame + 1) &&
-                  (at(mPrevFeaturePointsIdx[i]).trackPointAt(frame + 1).distanceToPoint(v) >
+                 (person.trackPointExist(frame + 1) &&
+                  (person.trackPointAt(frame + 1).distanceToPoint(v) >
                    mMainWindow->getHeadSize(nullptr, mPrevFeaturePointsIdx[i], frame + 1) / 2.))))
             {
-                if(at(j).firstFrame() < (*this)[mPrevFeaturePointsIdx[i]].firstFrame() &&
-                   at(j).lastFrame() > (*this)[mPrevFeaturePointsIdx[i]].lastFrame())
-                {
-                    for(int k = 0; k < at(mPrevFeaturePointsIdx[i]).size(); ++k)
-                    {
-                        // bei insertAtFrame wird qual beruecksichtigt, ob vorheriger besser
-                        (*this)[j].insertAtFrame(
-                            at(mPrevFeaturePointsIdx[i]).firstFrame() + k,
-                            at(mPrevFeaturePointsIdx[i]).at(k),
-                            j,
-                            (mMainWindow->getControlWidget()->trackExtrapolation->checkState() == Qt::Checked));
-                    }
-                    deleteIndex = mPrevFeaturePointsIdx[i];
-                }
-                else if(at(j).firstFrame() < (*this)[mPrevFeaturePointsIdx[i]].firstFrame())
-                {
-                    for(int k = at(j).size() - 1; k > -1; --k)
-                    {
-                        // bei insertAtFrame wird qual beruecksichtigt, ob vorheriger besser
-                        (*this)[mPrevFeaturePointsIdx[i]].insertAtFrame(
-                            at(j).firstFrame() + k,
-                            at(j).at(k),
-                            mPrevFeaturePointsIdx[i],
-                            (mMainWindow->getControlWidget()->trackExtrapolation->checkState() == Qt::Checked));
-                    }
-                    deleteIndex = j;
-                }
-                else
-                {
-                    for(int k = 0; k < at(j).size(); ++k)
-                    {
-                        // bei insertAtFrame wird qual beruecksichtigt, ob vorheriger besser
-                        (*this)[mPrevFeaturePointsIdx[i]].insertAtFrame(
-                            at(j).firstFrame() + k,
-                            at(j).at(k),
-                            mPrevFeaturePointsIdx[i],
-                            (mMainWindow->getControlWidget()->trackExtrapolation->checkState() == Qt::Checked));
-                    }
-                    deleteIndex = j;
-                }
-                removeAt(deleteIndex);
+                int deleteIndex = mPersonStorage.merge(mPrevFeaturePointsIdx[i], j);
 
                 // shift index of feature points
                 for(size_t k = 0; k < mPrevFeaturePointsIdx.size(); ++k)
@@ -1732,7 +1043,7 @@ int Tracker::track(
     // trackNumberAll, trackShowOnlyNr werden nicht angepasst, dies wird aber am ende von petrack::updateimage gemacht
     for(int i = 0; i < trjToDel.size(); ++i) // ueber TrackPerson
     {
-        removeAt(trjToDel[i]);
+        mPersonStorage.delPointOf(trjToDel[i], 0, -1);
     }
 
     // numOfPeopleToTrack kann trotz nicht retrack > 0 sein auch bei alten pfaden
@@ -1852,15 +1163,14 @@ void Tracker::refineViaColorPointLK(int level, float errorScale)
 
     for(size_t i = 0; i < mPrevFeaturePointsIdx.size(); ++i)
     {
+        const auto &person = mPersonStorage.at(mPrevFeaturePointsIdx[i]);
         // wenn fehler zu gross, dann Farbmarkerelement nehmen // fuer multicolor marker / farbiger hut mit schwarzem
         // punkt
         if(useColor && mTrackError[i] > errorScale * 150.F &&
-           at(mPrevFeaturePointsIdx[i]).at(mPrevFrame - at(mPrevFeaturePointsIdx[i]).firstFrame()).color().isValid())
+           person.at(mPrevFrame - person.firstFrame()).color().isValid())
         {
-            float prevPointX = static_cast<float>(
-                at(mPrevFeaturePointsIdx[i]).at(mPrevFrame - at(mPrevFeaturePointsIdx[i]).firstFrame()).colPoint().x());
-            float prevPointY = static_cast<float>(
-                at(mPrevFeaturePointsIdx[i]).at(mPrevFrame - at(mPrevFeaturePointsIdx[i]).firstFrame()).colPoint().y());
+            float prevPointX = static_cast<float>(person.at(mPrevFrame - person.firstFrame()).colPoint().x());
+            float prevPointY = static_cast<float>(person.at(mPrevFrame - person.firstFrame()).colPoint().y());
             prevColorFeaturePoint.push_back(cv::Point2f(prevPointX, prevPointY));
             winSize = mMainWindow->winSize(nullptr, mPrevFeaturePointsIdx[i], mPrevFrame, level);
 
@@ -1914,8 +1224,9 @@ void Tracker::useBackgroundFilter(QList<int> &trjToDel, BackgroundFilter *bgFilt
     QRectF     rect   = mMainWindow->getRecoRoiItem()->rect();
     for(size_t i = 0; i < mPrevFeaturePointsIdx.size(); ++i)
     {
-        x = myRound(mFeaturePoints[i].x - .5);
-        y = myRound(mFeaturePoints[i].y - .5);
+        const auto &person = mPersonStorage.at(mPrevFeaturePointsIdx[i]);
+        x                  = myRound(mFeaturePoints[i].x - .5);
+        y                  = myRound(mFeaturePoints[i].y - .5);
 
         // Rahmen, in dem nicht vordergrund pflicht, insbesondere am rechten rand!!!! es wird gruenes von hand
         // angelegtes bounding rect roi genutzt
@@ -1923,11 +1234,10 @@ void Tracker::useBackgroundFilter(QList<int> &trjToDel, BackgroundFilter *bgFilt
            x <= MIN(mGrey.cols - 1 - 2 * bS - margin - 50, rect.x() + rect.width()) && y >= MAX(margin, rect.y()) &&
            y <= MIN(mGrey.rows - 1 - 2 * bS - margin, rect.y() + rect.height()))
         {
-            if(!bgFilter->isForeground(x, y) && at(mPrevFeaturePointsIdx[i]).trackPointAt(mPrevFrame).qual() < 100)
+            if(!bgFilter->isForeground(x, y) && person.trackPointAt(mPrevFrame).qual() < 100)
             {
                 if((mMainWindow->getControlWidget()->filterBgDeleteTrj->checkState() == Qt::Checked) &&
-                   (at(mPrevFeaturePointsIdx[i]).nrInBg() >=
-                    mMainWindow->getControlWidget()->filterBgDeleteNumber->value()))
+                   (person.nrInBg() >= mMainWindow->getControlWidget()->filterBgDeleteNumber->value()))
                 {
                     // nur zum loeschen vormerken und am ende der fkt loeschen, da sonst Seiteneffekte komplex
                     trjToDel += mPrevFeaturePointsIdx[i];
@@ -1938,12 +1248,12 @@ void Tracker::useBackgroundFilter(QList<int> &trjToDel, BackgroundFilter *bgFilt
                 }
                 else
                 {
-                    (*this)[mPrevFeaturePointsIdx[i]].setNrInBg(at(mPrevFeaturePointsIdx[i]).nrInBg() + 1);
+                    mPersonStorage.setNrInBg(mPrevFeaturePointsIdx[i], person.nrInBg() + 1);
                 }
             }
             else // zaehler zuruecksetzen, der anzahl von getrackten Punkten im hintergrund zaehlt
             {
-                (*this)[mPrevFeaturePointsIdx[i]].setNrInBg(0);
+                mPersonStorage.setNrInBg(mPrevFeaturePointsIdx[i], 0);
             }
         }
     }
@@ -2057,422 +1367,190 @@ void Tracker::refineViaNearDarkPoint()
     }
 }
 
-void Tracker::recalcHeight(float altitude)
-{
-    // in TrackPerson: resetHeight();
-    for(int i = 0; i < size(); ++i) // ueber TrackPerson
-    {
-        (*this)[i].recalcHeight(altitude);
-    }
-}
-
 /**
- * @brief Performs different tests to check the plausibility of trajectories.
+ * @brief TrackPerson::syncTrackPersonMarkerID Synchronize TrackPoint.mMarkerID with TrackPerson.mMarkerID
  *
- * This method can check for
- * <ul><li>shortness (less than 10 points)</li>
- * <li>start and endpoint (both should be outside the reco ROI
- * with exceptions for the beginning and end of the video)</li>
- * <li>Fast variations of speed (4 frame interval)</li>
- * <li>TrackPoints are too close together</li></ul>
+ * 1. Function sets PersonMarkerID from TrackPointMarkerID if MarkerID == -1
+ * 2. checks if not other ID was detected and triggers a warning otherwise
  *
- * @param pers[in] list of persons (ID) to check
- * @param frame[out] list of frames at which "problems" occured
- * @param testEqual[in] true if warning for very close points are wished
- * @param testVelocity[in] true if warning for fast speed variations is whished
- * @param testInside[in] true if warning for start and endpoint in reco ROI is wished
- * @param testLength[in] true if warning for very short trajectories is wished
+ * @param markerID integer representing ArucoCodeMarker for currently handled TrackPoint
  */
-void Tracker::checkPlausibility(
-    QList<int> &pers,
-    QList<int> &frame,
-    bool        testEqual,
-    bool        testVelocity,
-    bool        testInside,
-    bool        testLength)
+void TrackPerson::syncTrackPersonMarkerID(int markerID)
 {
-    QProgressDialog progress("Check Plausibility", nullptr, 0, 400, mMainWindow->window());
-    progress.setWindowTitle("Check plausibility");
-    progress.setWindowModality(Qt::WindowModal);
-    progress.setVisible(true);
-    progress.setValue(0);
-    progress.setLabelText("Check Plausibility...");
-    static int margin = 30; // rand am bild, ab dem trajectorie verloren sein darf
-    int        i, j;
-    double     x, y;
-    int        bS        = mMainWindow->getImageBorderSize();
-    QRectF     rect      = mMainWindow->getRecoRoiItem()->rect();
-    int        lastFrame = mMainWindow->getAnimation()->getNumFrames() - 1;
-
-#ifdef TIME_MEASUREMENT
-    double time1, tstart;
-#endif
-    // test, if the trajectory is very short (less than 10 Trackpoints)
-    if(testLength)
-    {
-        progress.setValue(0);
-        progress.setLabelText("Check trajectories lengths...");
-        qApp->processEvents();
-#ifdef TIME_MEASUREMENT
-        time1  = 0.0;
-        tstart = clock();
-#endif
-        for(i = 0; i < size(); ++i) // ueber TrackPerson
-        {
-            progress.setValue(i * 100. / size());
-            qApp->processEvents();
-            if(at(i).size() < 10)
-            {
-                debout << "Warning: Trajectory of person " << i + 1 << " has less than 10 trackpoints!" << std::endl;
-                pers.append(i + 1);
-                frame.append((*this)[i].firstFrame());
-            }
-        }
-#ifdef TIME_MEASUREMENT
-        time1 += clock() - tstart;
-        time1 = time1 / CLOCKS_PER_SEC;
-        cout << "  time(testLength) = " << time1 << " sec." << endl;
-#endif
-    }
-
-    // check, if trajectory starts and ends outside the recognition area
-    if(testInside)
-    {
-        progress.setValue(100);
-        progress.setLabelText("Check if trajectories are inside image...");
-        qApp->processEvents();
-#ifdef TIME_MEASUREMENT
-        time1  = 0.0;
-        tstart = clock();
-#endif
-        for(i = 0; i < size(); ++i) // ueber TrackPerson
-        {
-            qApp->processEvents();
-            progress.setValue(100 + i * 100. / size());
-            x = (*this)[i].first().x();
-            y = (*this)[i].first().y();
-            // mGrey hat gleiche groesse wie zuletzt getracktes bild
-            if((*this)[i].firstFrame() != 0 && x >= MAX(margin, rect.x()) && y >= MAX(margin, rect.y()) &&
-               x <= MIN(mGrey.cols - 1 - 2 * bS - margin, rect.x() + rect.width()) &&
-               y <= MIN(mGrey.rows - 1 - 2 * bS - margin, rect.y() + rect.height()))
-            {
-                debout << "Warning: Start of trajectory inside picture and recognition area of person " << i + 1 << "!"
-                       << std::endl;
-                pers.append(i + 1);
-                frame.append((*this)[i].firstFrame());
-            }
-
-            x = (*this)[i].last().x();
-            y = (*this)[i].last().y();
-            // mGrey hat gleiche groesse wie zuletzt getracktes bild
-            if((*this)[i].lastFrame() != lastFrame && x >= MAX(margin, rect.x()) && y >= MAX(margin, rect.y()) &&
-               x <= MIN(mGrey.cols - 1 - 2 * bS - margin, rect.x() + rect.width()) &&
-               y <= MIN(mGrey.rows - 1 - 2 * bS - margin, rect.y() + rect.height()))
-            {
-                debout << "Warning: End of trajectory inside picture and recognition area of person " << i + 1 << "!"
-                       << std::endl;
-                pers.append(i + 1);
-                frame.append((*this)[i].lastFrame());
-            }
-        }
-#ifdef TIME_MEASUREMENT
-        time1 += clock() - tstart;
-        time1 = time1 / CLOCKS_PER_SEC;
-        cout << "  time(testInside) = " << time1 << " sec." << endl;
-#endif
-    }
+    int tpMarkerID = markerID; // MarkerID of currently handled trackpoint
 
-    // testen, ob grosse Geschwindigkeitsaenderungen
-    // statt distanz koennte man auch noch vektoren vergleichen, was genauere analyse waer!!!!
-    if(testVelocity)
+    if(tpMarkerID != -1) // CodeMarker was recognized
     {
-        qApp->processEvents();
-        progress.setValue(200);
-        progress.setLabelText("Check velocity...");
-#ifdef TIME_MEASUREMENT
-        time1  = 0.0;
-        tstart = clock();
-#endif
-        double d01, d12, d23;
-        for(i = 0; i < size(); ++i) // ueber TrackPerson
+        if(mMarkerID == -1) // first time a Person is found TrackPerson.mMarkerID is -1 by initialisation
         {
-            qApp->processEvents();
-            progress.setValue(200 + i * 100. / size());
-            for(j = 1; j < at(i).size() - 2; ++j) // ueber TrackPoint (ohne ersten und letzten beiden)
-            {
-                d01 = at(i).at(j).distanceToPoint(at(i).at(j - 1));
-                d12 = at(i).at(j + 1).distanceToPoint(at(i).at(j));
-                d23 = at(i).at(j + 2).distanceToPoint(at(i).at(j + 1));
-                if(((1.8 * (d01 + d23) / 2.) < d12) &&
-                   ((d12 > 6.) ||
-                    ((d01 + d23) / 2. > 3.))) // geschwindigkeit 1,8-fach && mindestpixelbewegung im schnitt von 3
-                {
-                    debout << "Warning: Fast variation of velocity of person " << i + 1 << " between frame "
-                           << j + at(i).firstFrame() << " and " << j + 1 + at(i).firstFrame() << "!" << std::endl;
-                    pers.append(i + 1);
-                    frame.append(j + at(i).firstFrame());
-                }
-            }
+            setMarkerID(tpMarkerID); // set TrackPerson MarkerID equal to TrackPoint MarkerID
         }
-#ifdef TIME_MEASUREMENT
-        time1 += clock() - tstart;
-        time1 = time1 / CLOCKS_PER_SEC;
-        cout << "  time(testVelocity) = " << time1 << " sec." << endl;
-#endif
-    }
-
-    // testen, ob zwei trackpoint sehr nah beieinanderliegen (es gibt trajektorien, die uebereinander liegen, wenn nicht
-    // genmergt wird)
-    if(testEqual)
-    {
-        progress.setValue(300);
-        progress.setLabelText("Check if trajectories are equal...");
-        qApp->processEvents();
-#ifdef TIME_MEASUREMENT
-        time1  = 0.0;
-        tstart = clock();
-#endif
-        int lLF = largestLastFrame();
-        int f;
-        for(f = smallestFirstFrame(); f <= lLF; ++f)
+        if(mMarkerID != tpMarkerID)
         {
-            progress.setValue(300 + f * 100. / lLF);
-            qApp->processEvents();
-
-            for(i = 0; i < size(); ++i)
-            {
-                // if (!pers.contains(i+1)) man koennte nur einmal eine Person aufnehmen, da aufeinanderfolgende frames
-                // oft betroffen
-                for(j = i + 1; j < size(); ++j)
-                {
-                    if(at(i).trackPointExist(f) && at(j).trackPointExist(f))
-                    {
-                        if(at(i).trackPointAt(f).distanceToPoint(at(j).trackPointAt(f)) <
-                           mMainWindow->getHeadSize(nullptr, i, f) / 2.)
-                        {
-                            debout << "Warning: Person " << i + 1 << " and " << j + 1
-                                   << " are very close to each other at frame " << f << "!" << std::endl;
-                            pers.append(i + 1);
-                            frame.append(f);
-                        }
-                    }
-                }
-            }
+            std::cout << "ERROR: Two MarkerIDs were found for one trajectory." << std::endl;
         }
-#ifdef TIME_MEASUREMENT
-        time1 += clock() - tstart;
-        time1 = time1 / CLOCKS_PER_SEC;
-        cout << "  time(testEqual) = " << time1 << " sec." << endl;
-#endif
     }
 }
 
-// color optimieren fuer alle personen
-void Tracker::optimizeColor()
+QTextStream &operator>>(QTextStream &s, TrackPoint &tp)
 {
-    int i;
-    for(i = 0; i < size(); ++i) // ueber TrackPerson
-    {
-        if((*this)[i].color().isValid())
-        {
-            (*this)[i].optimizeColor();
-        }
-    }
+    double d;
+    Vec2F  p;
+    Vec3F  sp;
+    QColor col;
+    int    qual;
+    int    markerID;
+
+    s >> d;
+    tp.setX(d);
+    s >> d;
+    tp.setY(d);
+    if(Petrack::trcVersion > 1)
+    {
+        s >> d;
+        sp.setX(d);
+        s >> d;
+        sp.setY(d);
+        s >> d;
+        sp.setZ(d);
+        tp.setSp(sp);
+    }
+    s >> qual;
+    tp.setQual(qual);
+    s >> d;
+    p.setX(d);
+    s >> d;
+    p.setY(d);
+    tp.setColPoint(p);
+    s >> col;
+    tp.setColor(col);
+    if(Petrack::trcVersion > 2)
+    {
+        s >> markerID;
+        tp.setMarkerID(markerID);
+    }
+    return s;
 }
 
-// reset the height of all persons, but not the pos of the trackpoints
-void Tracker::resetHeight()
+QTextStream &operator<<(QTextStream &s, const TrackPoint &tp)
 {
-    for(int i = 0; i < size(); ++i) // ueber TrackPerson
+    if(Petrack::trcVersion > 2)
     {
-        (*this)[i].resetHeight();
+        s << tp.x() << " " << tp.y() << " " << tp.sp().x() << " " << tp.sp().y() << " " << tp.sp().z() << " "
+          << tp.qual() << " " << tp.colPoint().x() << " " << tp.colPoint().y() << " " << tp.color() << " "
+          << tp.getMarkerID();
     }
-}
-
-// reset the pos of the tzrackpoints, but not the heights
-void Tracker::resetPos()
-{
-    for(int i = 0; i < size(); ++i) // ueber TrackPerson
+    else if(Petrack::trcVersion == 2)
     {
-        for(int j = 0; j < (*this)[i].size(); ++j) // ueber TrackPoints
-        {
-            (*this)[i][j].setSp(-1., -1., -1.);
-        }
+        s << tp.x() << " " << tp.y() << " " << tp.sp().x() << " " << tp.sp().y() << " " << tp.sp().z() << " "
+          << tp.qual() << " " << tp.colPoint().x() << " " << tp.colPoint().y() << " " << tp.color();
     }
+    else
+    {
+        s << tp.x() << " " << tp.y() << " " << tp.qual() << " " << tp.colPoint().x() << " " << tp.colPoint().y() << " "
+          << tp.color();
+    }
+    return s;
 }
 
-/**
- * @brief Prints height distribution to stdout
- *
- * @return false if no height information is available, else true
- */
-bool Tracker::printHeightDistribution()
+std::ostream &operator<<(std::ostream &s, const TrackPoint &tp)
 {
-    debout << std::endl;
-    QMap<double, int>                 dict;
-    QMap<double, int>::const_iterator j;
-    int                               i, anz = 0;
-    int                               heightStep = 5;
-    double                            average = 0., avg = 0.;
-    int                               noHeight = 0;
-
-    for(i = 0; i < size(); ++i)
-    {
-        if((*this)[i].height() >
-           MIN_HEIGHT) // !=-1// insbesondere von hand eingefuegte trackpoint/persons haben keine farbe
-        {
-            ++dict[(((int) (*this)[i].height()) / heightStep) * heightStep];
-            avg += (*this)[i].height();
-        }
-        else
-        {
-            ++noHeight;
-        }
-    }
-    j = dict.constBegin();
-    while(j != dict.constEnd())
+    if(Petrack::trcVersion > 2)
     {
-        anz += j.value();
-        ++j;
+        s << tp.x() << " " << tp.y() << " " << tp.sp().x() << " " << tp.sp().y() << " " << tp.sp().z() << " "
+          << tp.qual() << " " << tp.colPoint().x() << " " << tp.colPoint().y() << " " << tp.color() << " "
+          << tp.getMarkerID();
     }
-    debout << "number of persons with measured height                            : " << anz << std::endl;
-    debout << "person without measured height (not included in calculated values): " << noHeight
-           << " (using default height for export)" << std::endl;
-    if(anz == 0)
+    else if(Petrack::trcVersion > 1)
     {
-        return false;
+        s << tp.x() << " " << tp.y() << " " << tp.sp().x() << " " << tp.sp().y() << " " << tp.sp().z() << " "
+          << tp.qual() << " " << tp.colPoint().x() << " " << tp.colPoint().y() << " " << tp.color();
     }
-    j = dict.constBegin();
-    while(j != dict.constEnd())
+    else
     {
-        debout << "height " << std::fixed << std::setprecision(1) << std::setw(5) << j.key() << " - "
-               << j.key() + heightStep << " : number " << std::setw(3) << j.value() << " (" << std::setw(4)
-               << (100. * j.value()) / anz << "%)" << std::endl;
-        average += (j.key() + heightStep / 2.) * j.value();
-        ++j;
+        s << tp.x() << " " << tp.y() << " " << tp.qual() << " " << tp.colPoint().x() << " " << tp.colPoint().y() << " "
+          << tp.color();
     }
-    debout << "average height (bucket): " << std::fixed << std::setprecision(1) << std::setw(5) << average / anz
-           << std::endl;
-    debout << "average height         : " << std::fixed << std::setprecision(1) << std::setw(5) << avg / anz
-           << std::endl;
-
-    return true;
+    return s;
 }
 
-/**
- * Sets the heights based on the values contained in \p heights.
- * @param heights Map between marker ID and corresponding height
- */
-void Tracker::setMarkerHeights(const std::unordered_map<int, float> &heights)
+QTextStream &operator>>(QTextStream &s, TrackPerson &tp)
 {
-    for(int i = 0; i < size(); ++i) // over TrackPerson
-    {
-        for(int j = 0; j < (*this)[i].size(); ++j) // over TrackPoints
-        {
-            // markerID of current person at current TrackPoint:
-            int markerID = (*this)[i][j].getMarkerID();
+    double     d;
+    QColor     col;
+    int        n;
+    TrackPoint p;
+    int        markerID;
 
-            if(markerID != -1) // when a real markerID is found (not -1)
-            {
-                // find index of mID within List of MarkerIDs that were read from txt-file:
-                if(heights.find(markerID) != std::end(heights))
-                {
-                    (*this)[i].setHeight(heights.at(markerID));
-                }
-                else
-                {
-                    debout << "Warning, the following markerID was not part of the height-file: " << markerID
-                           << std::endl;
-                    debout << "No height set for personNR: " << (*this)[i].nr() << std::endl;
-                }
-            }
-        }
+    s.skipWhiteSpace();
+    QString str = s.readLine();
+
+    QTextStream trjInfoLine(&str);
+
+    trjInfoLine >> n;
+    tp.setNr(n);
+    trjInfoLine >> d;
+    tp.setHeight(d);
+    trjInfoLine >> n;
+    tp.setFirstFrame(n);
+    trjInfoLine >> n;
+    tp.setLastFrame(n);
+    trjInfoLine >> n;
+    tp.setColCount(n);
+    trjInfoLine >> col;
+    tp.setColor(col);
+    if(Petrack::trcVersion > 3)
+    {
+        trjInfoLine >> markerID;
+        tp.setMarkerID(markerID);
+    }
+    trjInfoLine >> n;           // size of list
+    if(Petrack::trcVersion > 2) // Reading the comment line
+    {
+        // Kommentarzeile lesen
+        str = s.readLine();
+        tp.setComment(str.replace(QRegularExpression("<br>"), "\n"));
     }
-}
 
-/**
- * Sets the marker IDs based on the internal used IDs (personID).
- * @param markerIDs Map between internal ID and marker ID
- */
-void Tracker::setMarkerIDs(const std::unordered_map<int, int> &markerIDs)
-{
-    for(int i = 0; i < size(); ++i) // over TrackPerson
+
+    for(int i = 0; i < n; ++i)
     {
-        // personID of current person
-        int personID = i + 1;
-        if(markerIDs.find(personID) != std::end(markerIDs))
-        {
-            int markerID = markerIDs.at(personID);
-            (*this)[i].setMarkerID(markerID);
-            for(int j = 0; j < (*this)[i].size(); ++j) // over TrackPoints
-            {
-                (*this)[i][j].setMarkerID(markerID);
-            }
-        }
-        else
-        {
-            debout << "Warning, the following personID was not part of the markerID-file: " << personID << std::endl;
-        }
+        s >> p;
+        tp.append(p);
     }
+    return s;
 }
 
-/**
- * @brief Deletes TrackPersons with over 80% solely tracked points
- *
- * DOESN'T WORK WITH COLOR MARKERS because they have a quality under
- * 100 (quality threshold of this method could be changed)
- *
- * Only trajectories having a point at the given frame are purged.
- * Trajectories with less than 10 points are not purged.
- *
- * @param frame frame at which all trajectories should be purged
- */
-void Tracker::purge(int frame)
+QTextStream &operator<<(QTextStream &s, const TrackPerson &tp)
 {
-    int   i, j;
-    float count; ///< number of trackpoints without recognition
-
-    for(i = 0; i < size(); ++i)
+    s << tp.nr() << " " << tp.height() << " " << tp.firstFrame() << " " << tp.lastFrame() << " " << tp.colCount() << " "
+      << tp.color();
+    if(Petrack::trcVersion > 3)
     {
-        if(at(i).size() > 10 && at(i).firstFrame() <= frame && at(i).lastFrame() >= frame)
-        {
-            count = 0;
-            for(j = 0; j < at(i).size(); ++j)
-            {
-                if(at(i).at(j).qual() < 100.)
-                {
-                    ++count;
-                }
-            }
-            if(count / at(i).size() > 0.8) // Achtung, wenn roi klein, dann viele tp nur getrackt
-            {
-                removeAt(i); // delete trj
-            }
-        }
+        s << " " << tp.getMarkerID();
     }
+    s << " " << tp.size();
+    s << Qt::endl << tp.serializeComment() << Qt::endl;
+    for(int i = 0; i < tp.size(); ++i)
+    {
+        s << tp.at(i) << Qt::endl;
+    }
+    return s;
 }
 
-/**
- * @brief TrackPerson::syncTrackPersonMarkerID Synchronize TrackPoint.mMarkerID with TrackPerson.mMarkerID
- *
- * 1. Function sets PersonMarkerID from TrackPointMarkerID if MarkerID == -1
- * 2. checks if not other ID was detected and triggers a warning otherwise
- *
- * @param markerID integer representing ArucoCodeMarker for currently handled TrackPoint
- */
-void TrackPerson::syncTrackPersonMarkerID(int markerID)
+std::ostream &operator<<(std::ostream &s, const TrackPerson &tp)
 {
-    int tpMarkerID = markerID; // MarkerID of currently handled trackpoint
-
-    if(tpMarkerID != -1) // CodeMarker was recognized
+    s << tp.nr() << " " << tp.height() << " " << tp.firstFrame() << " " << tp.lastFrame() << " " << tp.colCount() << " "
+      << tp.color();
+    if(Petrack::trcVersion > 3)
     {
-        if(mMarkerID == -1) // first time a Person is found TrackPerson.mMarkerID is -1 by initialisation
-        {
-            setMarkerID(tpMarkerID); // set TrackPerson MarkerID equal to TrackPoint MarkerID
-        }
-        if(mMarkerID != tpMarkerID)
-        {
-            std::cout << "ERROR: Two MarkerIDs were found for one trajectory." << std::endl;
-        }
+        s << " " << tp.getMarkerID();
+    }
+    s << " " << tp.size();
+    s << std::endl << tp.serializeComment() << std::endl;
+    for(int i = 0; i < tp.size(); ++i)
+    {
+        s << tp.at(i) << std::endl;
     }
+    return s;
 }
diff --git a/src/trackerItem.cpp b/src/trackerItem.cpp
index f52b503ba..b95a47eca 100644
--- a/src/trackerItem.cpp
+++ b/src/trackerItem.cpp
@@ -22,6 +22,7 @@
 
 #include "animation.h"
 #include "control.h"
+#include "personStorage.h"
 #include "petrack.h"
 #include "recognitionRoiItem.h"
 #include "tracker.h"
@@ -34,11 +35,11 @@
 // in x und y gleichermassen skaliertes koordinatensystem,
 // da von einer vorherigen intrinsischen kamerakalibrierung ausgegenagen wird,
 // so dass pixel quadratisch
-TrackerItem::TrackerItem(QWidget *wParent, Tracker *tracker, QGraphicsItem *parent) : QGraphicsItem(parent)
+TrackerItem::TrackerItem(QWidget *wParent, PersonStorage &storage, QGraphicsItem *parent) :
+    QGraphicsItem(parent), mPersonStorage(storage)
 {
     mMainWindow    = (class Petrack *) wParent;
     mControlWidget = mMainWindow->getControlWidget();
-    mTracker       = tracker;
 }
 
 /**
@@ -72,20 +73,23 @@ void TrackerItem::contextMenuEvent(QGraphicsSceneContextMenuEvent *event)
         TrackPoint p(
             (Vec2F) event->pos(),
             110); // 110 ist ueber 100 (hoechste Qualitaetsstufe) und wird nach einfuegen auf 100 gesetzt
-        bool  found = false;
-        int   i, iNearest   = -1;
+        bool  found    = false;
+        int   iNearest = -1;
         float dist, minDist = 1000000.;
 
         QSet<int> onlyVisible = mMainWindow->getPedestrianUserSelection();
         int       frame       = mMainWindow->getAnimation()->getCurrentFrameNum();
 
-        for(i = 0; i < mTracker->size(); ++i) // !found &&  // ueber TrackPerson
+        const auto &persons = mPersonStorage.getPersons();
+        size_t      i;
+        for(i = 0; i < persons.size(); ++i)
         {
-            if(((onlyVisible.empty()) || (onlyVisible.contains(i))) && mTracker->at(i).trackPointExist(frame))
+            const auto &person = persons[i];
+            if(((onlyVisible.empty()) || (onlyVisible.contains(i))) && person.trackPointExist(frame))
             {
-                dist = mTracker->at(i).trackPointAt(frame).distanceToPoint(p);
+                dist = person.trackPointAt(frame).distanceToPoint(p);
                 if((dist < mMainWindow->getHeadSize(nullptr, i, frame) / 2.) ||
-                   ((mTracker->at(i).trackPointAt(frame).distanceToPoint(p.colPoint()) <
+                   ((person.trackPointAt(frame).distanceToPoint(p.colPoint()) <
                      mMainWindow->getHeadSize(nullptr, i, frame) / 2.)))
                 {
                     if(found)
@@ -120,7 +124,7 @@ void TrackerItem::contextMenuEvent(QGraphicsSceneContextMenuEvent *event)
         if(found)
         {
             i      = iNearest;
-            tp     = mTracker->at(i);
+            tp     = persons[i];
             height = tp.height();
 
             if(height < MIN_HEIGHT + 1)
@@ -328,7 +332,7 @@ void TrackerItem::paint(QPainter *painter, const QStyleOptionGraphicsItem * /*op
     groundPathPen.setWidth(pSGP);
 
 
-    if(mControlWidget->showVoronoiCells->isChecked() && !mTracker->isEmpty())
+    if(mControlWidget->showVoronoiCells->isChecked() && mPersonStorage.nbPersons() != 0)
     {
         // ToDo: adjust subdiv rect to correct area
         QRectF qrect = mMainWindow->getRecoRoiItem()->rect();
@@ -355,24 +359,26 @@ void TrackerItem::paint(QPainter *painter, const QStyleOptionGraphicsItem * /*op
         subdiv.initDelaunay(delaunyROI);
     }
 
-    auto pedestrianToPaint = mMainWindow->getPedestrianUserSelection();
-    for(int i = 0; i < mTracker->size(); ++i) // ueber TrackPerson
+    auto        pedestrianToPaint = mMainWindow->getPedestrianUserSelection();
+    const auto &persons           = mPersonStorage.getPersons();
+    for(size_t i = 0; i < persons.size(); ++i) // ueber TrackPerson
     {
+        const auto &person = persons[i];
         // show current frame
         if(pedestrianToPaint.contains(i) || pedestrianToPaint.empty())
         {
-            if(mTracker->at(i).trackPointExist(curFrame))
+            if(person.trackPointExist(curFrame))
             {
                 if(mControlWidget->trackHeadSized->checkState() == Qt::Checked)
                 {
                     pSP = mMainWindow->getHeadSize(nullptr, i, curFrame); // headSize;
                 }
-                const TrackPoint &tp = (*mTracker)[i][curFrame - mTracker->at(i).firstFrame()];
+                const TrackPoint &tp = person[curFrame - person.firstFrame()];
                 if(mControlWidget->trackShowCurrentPoint->checkState() ==
                    Qt::Checked) //(mControlWidget->recoShowColor->checkState() == Qt::Checked)
                 {
                     painter->setBrush(Qt::NoBrush);
-                    if(mTracker->at(i).newReco())
+                    if(person.newReco())
                     {
                         painter->setPen(Qt::green);
                     }
@@ -415,11 +421,10 @@ void TrackerItem::paint(QPainter *painter, const QStyleOptionGraphicsItem * /*op
                 }
 
                 // berechnung der normalen, die zur positionierung der nummerieung und der gesamtfarbe dient
-                if(((mControlWidget->trackShowColColor->checkState() == Qt::Checked) &&
-                    (mTracker->at(i).color().isValid())) ||
+                if(((mControlWidget->trackShowColColor->checkState() == Qt::Checked) && (person.color().isValid())) ||
                    (mControlWidget->trackShowNumber->checkState() == Qt::Checked) ||
                    ((mControlWidget->trackShowColColor->checkState() == Qt::Checked) &&
-                    ((mTracker->at(i).height() > MIN_HEIGHT) ||
+                    ((person.height() > MIN_HEIGHT) ||
                      ((tp.sp().z() > 0.) && (mControlWidget->trackShowHeightIndividual->checkState() ==
                                              Qt::Checked))))) //  Hoehe kann auf Treppen auch negativ werden, wenn koord
                                                               //  weiter oben angesetzt wird
@@ -438,11 +443,11 @@ void TrackerItem::paint(QPainter *painter, const QStyleOptionGraphicsItem * /*op
                         // man koennte auch lastNormal von anderem trackpath nehmen statt 1, 0
                         normalVector.set(1., 0.);
                         // den vorherigen trackpoint finden, wo reco farbe erzeugt hat und somit colpoint vorliegt
-                        for(int j = curFrame - mTracker->at(i).firstFrame(); j > -1; --j)
+                        for(int j = curFrame - person.firstFrame(); j > -1; --j)
                         {
-                            if(mTracker->at(i).at(j).color().isValid())
+                            if(person.at(j).color().isValid())
                             {
-                                normalVector = (mTracker->at(i).at(j) - mTracker->at(i).at(j).colPoint()).normal();
+                                normalVector = (person.at(j) - person.at(j).colPoint()).normal();
                                 normalVector.normalize();
                                 break;
                             }
@@ -451,11 +456,11 @@ void TrackerItem::paint(QPainter *painter, const QStyleOptionGraphicsItem * /*op
                         // zB wenn rueckwaerts abgespielt wird
                         if((normalVector.x() == 1.) && (normalVector.y() == 0.))
                         {
-                            for(int j = curFrame - mTracker->at(i).firstFrame() + 1; j < mTracker->at(i).size(); ++j)
+                            for(int j = curFrame - person.firstFrame() + 1; j < person.size(); ++j)
                             {
-                                if(mTracker->at(i).at(j).color().isValid())
+                                if(person.at(j).color().isValid())
                                 {
-                                    normalVector = (mTracker->at(i).at(j) - mTracker->at(i).at(j).colPoint()).normal();
+                                    normalVector = (person.at(j) - person.at(j).colPoint()).normal();
                                     normalVector.normalize();
                                     break;
                                 }
@@ -465,13 +470,13 @@ void TrackerItem::paint(QPainter *painter, const QStyleOptionGraphicsItem * /*op
                 }
 
                 // farbe der gesamten trackperson
-                double height = mTracker->at(i).height();
+                double height = person.height();
                 if(mControlWidget->trackShowColColor->checkState() == Qt::Checked)
                 {
                     painter->setPen(numberPen);
                     painter->setBrush(Qt::NoBrush);
                     rect.setRect(tp.x() + 10, tp.y() + 10, 15 * pSC, 10 * pSC);
-                    painter->drawText(rect, mTracker->at(i).comment());
+                    painter->drawText(rect, person.comment());
                     rect.setRect(tp.x() - pSC, tp.y() - pSC, 50, 50);
                     if(tp.getMarkerID() > 0)
                     {
@@ -479,11 +484,10 @@ void TrackerItem::paint(QPainter *painter, const QStyleOptionGraphicsItem * /*op
                     }
                 }
 
-                if((mControlWidget->trackShowColColor->checkState() == Qt::Checked) &&
-                   (mTracker->at(i).color().isValid()))
+                if((mControlWidget->trackShowColColor->checkState() == Qt::Checked) && (person.color().isValid()))
                 {
                     painter->setPen(Qt::NoPen);
-                    painter->setBrush(QBrush(mTracker->at(i).color()));
+                    painter->setBrush(QBrush(person.color()));
                     rect.setRect(
                         tp.x() + (pSP + pSC) * 0.6 * normalVector.x() - pSC / 2.,
                         tp.y() + (pSP + pSC) * 0.6 * normalVector.y() - pSC / 2.,
@@ -496,7 +500,7 @@ void TrackerItem::paint(QPainter *painter, const QStyleOptionGraphicsItem * /*op
                     ((height > MIN_HEIGHT) ||
                      ((tp.sp().z() > 0.) &&
                       (mControlWidget->trackShowHeightIndividual->checkState() ==
-                       Qt::Checked)))) // Hoehe  && (mTracker->at(i).height() > 0.) Hoehe kann auf Treppen auch negativ
+                       Qt::Checked)))) // Hoehe  && (person.height() > 0.) Hoehe kann auf Treppen auch negativ
                                        // werden, wenn koord weiter oben angesetzt wird
                 {
                     painter->setFont(heightFont);
@@ -588,8 +592,7 @@ void TrackerItem::paint(QPainter *painter, const QStyleOptionGraphicsItem * /*op
                         if(height < MIN_HEIGHT + 1)
                         {
                             p3d_height = mMainWindow->getExtrCalibration()->get3DPoint(
-                                cv::Point2f(tp.x(), tp.y()),
-                                mControlWidget->getColorPlot()->map(mTracker->at(i).color()));
+                                cv::Point2f(tp.x(), tp.y()), mControlWidget->getColorPlot()->map(person.color()));
                         }
                         else
                         {
@@ -631,8 +634,7 @@ void TrackerItem::paint(QPainter *painter, const QStyleOptionGraphicsItem * /*op
                         if(height < MIN_HEIGHT + 1)
                         {
                             p3d_height = mMainWindow->getExtrCalibration()->get3DPoint(
-                                cv::Point2f(tp.x(), tp.y()),
-                                mControlWidget->getColorPlot()->map(mTracker->at(i).color()));
+                                cv::Point2f(tp.x(), tp.y()), mControlWidget->getColorPlot()->map(person.color()));
                         }
                         else
                         {
@@ -662,7 +664,7 @@ void TrackerItem::paint(QPainter *painter, const QStyleOptionGraphicsItem * /*op
             if(((mControlWidget->trackShowPoints->checkState() == Qt::Checked) ||
                 (mControlWidget->trackShowPath->checkState() == Qt::Checked) ||
                 (mControlWidget->trackShowGroundPath->checkState() == Qt::Checked)) &&
-               ((mTracker->at(i).trackPointExist(curFrame)) ||
+               ((person.trackPointExist(curFrame)) ||
                 (mControlWidget->trackShowOnlyVisible->checkState() == Qt::Unchecked)))
             {
                 if(mControlWidget->trackShowBefore->value() == -1)
@@ -671,7 +673,7 @@ void TrackerItem::paint(QPainter *painter, const QStyleOptionGraphicsItem * /*op
                 }
                 else
                 {
-                    from = curFrame - mTracker->at(i).firstFrame() - mControlWidget->trackShowBefore->value();
+                    from = curFrame - person.firstFrame() - mControlWidget->trackShowBefore->value();
                     if(from < 0)
                     {
                         from = 0;
@@ -679,14 +681,14 @@ void TrackerItem::paint(QPainter *painter, const QStyleOptionGraphicsItem * /*op
                 }
                 if(mControlWidget->trackShowAfter->value() == -1)
                 {
-                    to = mTracker->at(i).size();
+                    to = person.size();
                 }
                 else
                 {
-                    to = curFrame - mTracker->at(i).firstFrame() + mControlWidget->trackShowAfter->value() + 1;
-                    if(to > mTracker->at(i).size())
+                    to = curFrame - person.firstFrame() + mControlWidget->trackShowAfter->value() + 1;
+                    if(to > person.size())
                     {
-                        to = mTracker->at(i).size();
+                        to = person.size();
                     }
                 }
                 for(int j = from; j < to; ++j) // ueber TrackPoint
@@ -701,15 +703,14 @@ void TrackerItem::paint(QPainter *painter, const QStyleOptionGraphicsItem * /*op
 
                             // nur Linie zeichnen, wenn x oder y sich unterscheidet, sonst Punkt
                             // die Unterscheidung ist noetig, da Qt sonst grosses quadrat beim ranzoomen zeichnet
-                            if((mTracker->at(i).at(j - 1).toQPointF().x() != mTracker->at(i).at(j).toQPointF().x()) ||
-                               (mTracker->at(i).at(j - 1).toQPointF().y() != mTracker->at(i).at(j).toQPointF().y()))
+                            if((person.at(j - 1).toQPointF().x() != person.at(j).toQPointF().x()) ||
+                               (person.at(j - 1).toQPointF().y() != person.at(j).toQPointF().y()))
                             {
-                                painter->drawLine(
-                                    mTracker->at(i).at(j - 1).toQPointF(), mTracker->at(i).at(j).toQPointF());
+                                painter->drawLine(person.at(j - 1).toQPointF(), person.at(j).toQPointF());
                             }
                             else
                             {
-                                painter->drawPoint(mTracker->at(i).at(j - 1).toQPointF());
+                                painter->drawPoint(person.at(j - 1).toQPointF());
                             }
                         }
                     }
@@ -724,34 +725,32 @@ void TrackerItem::paint(QPainter *painter, const QStyleOptionGraphicsItem * /*op
                             if(mControlWidget->getCalibCoordDimension() == 0) // 3D
                             {
                                 cv::Point3f p3d_height_p1, p3d_height_p2;
-                                if(mTracker->at(i).height() < MIN_HEIGHT + 1)
+                                if(person.height() < MIN_HEIGHT + 1)
                                 {
                                     p3d_height_p1 = mMainWindow->getExtrCalibration()->get3DPoint(
-                                        cv::Point2f(mTracker->at(i).at(j - 1).x(), mTracker->at(i).at(j - 1).y()),
-                                        mControlWidget->getColorPlot()->map(mTracker->at(i).color()));
+                                        cv::Point2f(person.at(j - 1).x(), person.at(j - 1).y()),
+                                        mControlWidget->getColorPlot()->map(person.color()));
                                     p3d_height_p2 = mMainWindow->getExtrCalibration()->get3DPoint(
-                                        cv::Point2f(mTracker->at(i).at(j).x(), mTracker->at(i).at(j).y()),
-                                        mControlWidget->getColorPlot()->map(mTracker->at(i).color()));
+                                        cv::Point2f(person.at(j).x(), person.at(j).y()),
+                                        mControlWidget->getColorPlot()->map(person.color()));
                                 }
                                 else
                                 {
-                                    if(mTracker->at(i).at(j - 1).sp().z() > 0 && mTracker->at(i).at(j).sp().z() > 0)
+                                    if(person.at(j - 1).sp().z() > 0 && person.at(j).sp().z() > 0)
                                     {
                                         p3d_height_p1 = mMainWindow->getExtrCalibration()->get3DPoint(
-                                            cv::Point2f(mTracker->at(i).at(j - 1).x(), mTracker->at(i).at(j - 1).y()),
-                                            -mControlWidget->getCalibExtrTrans3() - mTracker->at(i).at(j - 1).sp().z());
+                                            cv::Point2f(person.at(j - 1).x(), person.at(j - 1).y()),
+                                            -mControlWidget->getCalibExtrTrans3() - person.at(j - 1).sp().z());
                                         p3d_height_p2 = mMainWindow->getExtrCalibration()->get3DPoint(
-                                            cv::Point2f(mTracker->at(i).at(j).x(), mTracker->at(i).at(j).y()),
-                                            -mControlWidget->getCalibExtrTrans3() - mTracker->at(i).at(j).sp().z());
+                                            cv::Point2f(person.at(j).x(), person.at(j).y()),
+                                            -mControlWidget->getCalibExtrTrans3() - person.at(j).sp().z());
                                     }
                                     else
                                     {
                                         p3d_height_p1 = mMainWindow->getExtrCalibration()->get3DPoint(
-                                            cv::Point2f(mTracker->at(i).at(j - 1).x(), mTracker->at(i).at(j - 1).y()),
-                                            mTracker->at(i).height());
+                                            cv::Point2f(person.at(j - 1).x(), person.at(j - 1).y()), person.height());
                                         p3d_height_p2 = mMainWindow->getExtrCalibration()->get3DPoint(
-                                            cv::Point2f(mTracker->at(i).at(j).x(), mTracker->at(i).at(j).y()),
-                                            mTracker->at(i).height());
+                                            cv::Point2f(person.at(j).x(), person.at(j).y()), person.height());
                                     }
                                 }
                                 p3d_height_p1.z = 0;
@@ -781,27 +780,21 @@ void TrackerItem::paint(QPainter *painter, const QStyleOptionGraphicsItem * /*op
                     // points before and after
                     if(mControlWidget->trackShowPoints->checkState() == Qt::Checked)
                     {
-                        if(mTracker->at(i).firstFrame() + j != curFrame)
+                        if(person.firstFrame() + j != curFrame)
                         {
                             if((mControlWidget->trackShowPointsColored->checkState() == Qt::Checked) &&
-                               (mTracker->at(i).at(j).color().isValid()))
+                               (person.at(j).color().isValid()))
                             {
                                 painter->setPen(Qt::NoPen);
-                                painter->setBrush(QBrush(mTracker->at(i).at(j).color()));
-                                rect.setRect(
-                                    mTracker->at(i).at(j).x() - pS / 2.,
-                                    mTracker->at(i).at(j).y() - pS / 2.,
-                                    pS,
-                                    pS); // 7
+                                painter->setBrush(QBrush(person.at(j).color()));
+                                rect.setRect(person.at(j).x() - pS / 2., person.at(j).y() - pS / 2., pS,
+                                             pS); // 7
                             }
                             else
                             {
                                 painter->setPen(Qt::red);
                                 painter->setBrush(Qt::NoBrush);
-                                // war noetig fuer alte qt-version: rect.setRect(mTracker->at(i).at(j).x()-(pS-1.)/2.,
-                                // mTracker->at(i).at(j).y()-(pS-1.)/2., pS-1., pS-1.); // 6
-                                rect.setRect(
-                                    mTracker->at(i).at(j).x() - pS / 2., mTracker->at(i).at(j).y() - pS / 2., pS, pS);
+                                rect.setRect(person.at(j).x() - pS / 2., person.at(j).y() - pS / 2., pS, pS);
                             }
                             painter->drawEllipse(rect);
                         }
@@ -812,7 +805,7 @@ void TrackerItem::paint(QPainter *painter, const QStyleOptionGraphicsItem * /*op
     }
 
     // Mat& img, Subdiv2D& subdiv )
-    if(mControlWidget->showVoronoiCells->checkState() == Qt::Checked && !mTracker->isEmpty())
+    if(mControlWidget->showVoronoiCells->checkState() == Qt::Checked && !mPersonStorage.getPersons().empty())
     {
         std::vector<std::vector<cv::Point2f>> facets3D;
         std::vector<cv::Point2f>              centers3D;
diff --git a/src/trackerReal.cpp b/src/trackerReal.cpp
index 16174eb59..e4ae05858 100644
--- a/src/trackerReal.cpp
+++ b/src/trackerReal.cpp
@@ -21,6 +21,8 @@
 #include "trackerReal.h"
 
 #include "helper.h"
+#include "personStorage.h"
+#include "petrack.h"
 #include "recognition.h"
 
 TrackPointReal::TrackPointReal(const Vec3F &p, int frameNum) : Vec3F(p), mFrameNum(frameNum)
@@ -108,7 +110,8 @@ void TrackPersonReal::addEnd(const QPointF &pos, int frame, const QPointF &dir)
 
 //-------------------------------------------------------------------------------------------------------------------------------------
 
-TrackerReal::TrackerReal(QWidget *wParent) : mXMin(100000.), mXMax(-100000.), mYMin(100000.), mYMax(-100000.)
+TrackerReal::TrackerReal(QWidget *wParent, PersonStorage &storage) :
+    mXMin(100000.), mXMax(-100000.), mYMin(100000.), mYMax(-100000.), mPersonStorage(storage)
 {
     mMainWindow = (class Petrack *) wParent;
 }
@@ -139,13 +142,13 @@ int TrackerReal::calculate(
             clear();
         }
 
-        int        i, j, f;
+        int        j, f;
         QList<int> missingList;    // frame nr wo ausgelassen; passend dazu:
         QList<int> missingListAnz; // anzahl ausgelassener frames
         if(missingFramesInserted)
         {
             // finden von nicht aufgenommenen frames
-            int                               largestLastFrame = tracker->largestLastFrame();
+            int                               largestLastFrame = mPersonStorage.largestLastFrame();
             QMap<int, double>                 distanceMap;     // map der distanzen zu frame
             QMap<int, double>                 lastDistanceMap; // map der distanzen zu frame
             QMap<int, double>::const_iterator iter;
@@ -154,17 +157,17 @@ int TrackerReal::calculate(
             // (median neben durchschnittswert als bewertungszahl hinzugenommen,
             // da sonst bei wenigen personen und langsamen bewegungen nur ein kleiner ausreisser
             // einen frame hinzufuegt (insb linienexperiment mit vielen personen))
-            for(f = tracker->smallestFirstFrame(); f <= largestLastFrame; ++f)
+            for(f = mPersonStorage.smallestFirstFrame(); f <= largestLastFrame; ++f)
             {
                 distanceMap.clear();
                 skipAnz = anz = 0;
                 sum           = 0.;
                 // erzeugen distanz-map im aktuellen frame
-                for(i = 0; i < tracker->size(); ++i)
+                for(size_t i = 0; i < mPersonStorage.nbPersons(); ++i)
                 {
-                    if((dist = tracker->at(i).distanceToNextFrame(f)) > -1)
+                    if((dist = mPersonStorage.at(i).distanceToNextFrame(f)) > -1)
                     {
-                        distanceMap[i] = dist;
+                        distanceMap[static_cast<int>(i)] = dist;
                     }
                 }
                 // distanzliste mit vorherigem frame vergleichen
@@ -209,28 +212,27 @@ int TrackerReal::calculate(
         QPointF         center = imageItem->getPosReal(
             QPointF(imageItem->boundingRect().width() / 2., imageItem->boundingRect().height() / 2.), 0.);
         Vec3F   sp;
-        int     nrFor;
-        int     nrRew;
         int     tsize;
-        double  zMedian;
         int     extrapolated;
         QPointF colPos;
         float   angle;
 
-        for(i = 0; i < tracker->size(); ++i) // ueber trajektorien
+        const auto &persons = mPersonStorage.getPersons();
+        for(size_t i = 0; i < persons.size(); ++i) // ueber trajektorien
         {
-            addFrames  = 0;
-            firstFrame = tracker->at(i).firstFrame();
+            const auto &person = persons[i];
+            addFrames          = 0;
+            firstFrame         = person.firstFrame();
 
-            if((*tracker)[i].height() < MIN_HEIGHT + 1)
+            if(person.height() < MIN_HEIGHT + 1)
             {
-                height = colorPlot->map((*tracker)[i].color());
+                height = colorPlot->map(person.color());
             }
             else
             {
-                height = (*tracker)[i].height();
+                height = person.height();
             }
-            //(*tracker)[i].setHeight(height);
+
             if(missingList.size() > 0)
             {
                 tmpMissingList    = missingList;
@@ -243,10 +245,10 @@ int TrackerReal::calculate(
                 }
             }
 
-            int markerID = (*tracker)[i].getMarkerID(); // set markerID to TrackPerson.markerID
+            int markerID = person.getMarkerID();
 
             trackPersonReal.init(firstFrame + addFrames, height, markerID);
-            tsize = tracker->at(i).size();
+            tsize = person.size();
             for(j = 0; (j < tsize); ++j) // ueber trackpoints
             {
                 // ausreisser ausfindig machen (dies geschieht, bevor -1 elemente herausgenommen werden, um die
@@ -255,76 +257,8 @@ int TrackerReal::calculate(
                    (tsize > 1)) // wenn direkt pointgrey hoehe oder eigene hoehenberechnung aber variierend ueber trj
                                 // genommen werden soll
                 {
-                    // ACHTUNG: Aenderungen in Originaltrajektorie, so dass aenderungen auf folgeuntersuchungen einfluss
-                    // haben: j auf j+1
-                    if((*tracker)[i][j].sp().z() != -1)
-                    {
-                        nrFor = 1; // anzahl der ztrackpoint ohne hoeheninfo
-                        nrRew = 1;
-                        while((j + nrFor < tsize) &&
-                              ((*tracker)[i].at(j + nrFor).sp().z() <
-                               0)) // nach && wird nur ausgefuehrt, wenn erstes true == size() also nicht
-                        {
-                            nrFor++;
-                        }
-                        while((j - nrRew >= 0) &&
-                              ((*tracker)[i].at(j - nrRew).sp().z() <
-                               0)) // nach && wird nur ausgefuehrt, wenn erstes true == size() also nicht
-                        {
-                            nrRew++;
-                        }
-
-                        if(((j - nrRew >= 0) && (j + nrFor == tsize)) ||
-                           ((j - nrRew >= 0) && (nrRew < nrFor))) // nur oder eher in Vergangenheit hoeheninfo gefunden
-                        {
-                            if(fabs((*tracker)[i].at(j - nrRew).sp().z() - (*tracker)[i].at(j).sp().z()) >
-                               nrRew * 40.) // 40cm
-                            {
-                                (*tracker)[i][j].setSp(
-                                    (*tracker)[i].at(j).sp().x(),
-                                    (*tracker)[i].at(j).sp().y(),
-                                    (*tracker)[i].at(j - nrRew).sp().z());
-                                debout << "Warning: Trackpoint smoothed height at the end or next to unknown height in "
-                                          "the future for trajectory "
-                                       << i + 1 << " in frame " << j + firstFrame << "." << std::endl;
-                            }
-                        }
-                        else if(
-                            ((j + nrFor != tsize) && (j - nrRew < 0)) ||
-                            ((j + nrFor != tsize) &&
-                             (nrFor < nrRew))) // nur oder eher in der zukunft hoeheninfo gefunden
-                        {
-                            if(fabs((*tracker)[i].at(j + nrFor).sp().z() - (*tracker)[i].at(j).sp().z()) >
-                               nrFor * 40.) // 40cm
-                            {
-                                (*tracker)[i][j].setSp(
-                                    (*tracker)[i].at(j).sp().x(),
-                                    (*tracker)[i].at(j).sp().y(),
-                                    (*tracker)[i].at(j + nrFor).sp().z());
-                                debout << "Warning: Trackpoint smoothed height at the beginning or next to unknown "
-                                          "height in the past for trajectory "
-                                       << i + 1 << " in frame " << j + firstFrame << "." << std::endl;
-                            }
-                        }
-                        else if((j + nrFor != tsize) && (j - nrRew >= 0)) // in beiden richtungen hoeheninfo gefunden
-                                                                          // und nrFor==nrRew
-                        {
-                            // median genommen um zwei fehlmessungen nebeneinander nicht dazu fuehren zu lassen, dass
-                            // bessere daten veraendert werden
-                            zMedian = getMedianOf3(
-                                (*tracker)[i].at(j).sp().z(),
-                                (*tracker)[i].at(j - nrRew).sp().z(),
-                                (*tracker)[i].at(j + nrFor).sp().z());
-                            // lineare interpolation
-                            if(fabs(zMedian - (*tracker)[i].at(j).sp().z()) > 20. * (nrFor + nrRew)) // 20cm
-                            {
-                                (*tracker)[i][j].setSp(
-                                    (*tracker)[i].at(j).sp().x(), (*tracker)[i].at(j).sp().y(), zMedian);
-                                debout << "Warning: Trackpoint smoothed height inside for trajectory " << i + 1
-                                       << " in frame " << j + firstFrame << "." << std::endl;
-                            }
-                        }
-                    }
+                    // changes Trajectories!
+                    mPersonStorage.smoothHeight(i, j);
                 }
                 if(useTrackpoints)
                 {
@@ -333,14 +267,14 @@ int TrackerReal::calculate(
                     {
                         trackPersonReal.addEnd(
                             Vec3F(
-                                (*tracker)[i][j].sp().x() + center.x(),
-                                center.y() - (*tracker)[i][j].sp().y(),
-                                altitude - (*tracker)[i][j].sp().z()),
+                                person[j].sp().x() + center.x(),
+                                center.y() - person[j].sp().y(),
+                                altitude - person[j].sp().z()),
                             firstFrame + j);
                     }
                     else
                     {
-                        trackPersonReal.addEnd((*tracker)[i][j].sp(), firstFrame + j);
+                        trackPersonReal.addEnd(person[j].sp(), firstFrame + j);
                     }
                 }
                 else
@@ -350,14 +284,14 @@ int TrackerReal::calculate(
                     {
                         extrapolated = 0; // 0 == false
                         double bestZ =
-                            (*tracker)[i].getNearestZ(j, &extrapolated); // gibt z wert zurueck bzw wenn -1, dann den
-                                                                         // neben diesem frame ersten z-wert ungleich -1
-                        if(bestZ < 0) // == -1 // es liegt gar keine berechnete hoehe vor
+                            person.getNearestZ(j, &extrapolated); // gibt z wert zurueck bzw wenn -1, dann den
+                                                                  // neben diesem frame ersten z-wert ungleich -1
+                        if(bestZ < 0)                             // == -1 // es liegt gar keine berechnete hoehe vor
                         {
                             if(exportElimTrj)
                             {
                                 debout << "Warning: no calculated height for trackpoint " << j << " (frame "
-                                       << tracker->at(i).firstFrame() + j << ") of person " << i + 1
+                                       << person.firstFrame() + j << ") of person " << i + 1
                                        << ", person is not exported!" << std::endl;
                                 break; // TrackPerson ist angelegt, erhaelt aber keine Points und wird deshalb am ende
                                        // nicht eingefuegt
@@ -367,7 +301,7 @@ int TrackerReal::calculate(
                                 bestZ = height; // wenn gar kein trackpoint ungleich -1 gefunden wird, dann wird zu
                                                 // allerletzt Hoehe genommen
                                 debout << "Warning: no calculated height for trackpoint " << j << " (frame "
-                                       << tracker->at(i).firstFrame() + j << ") of person " << i + 1
+                                       << person.firstFrame() + j << ") of person " << i + 1
                                        << ", default height is used!" << std::endl;
                             }
                         }
@@ -384,7 +318,7 @@ int TrackerReal::calculate(
                                 trackPersonReal.setFirstFrame(trackPersonReal.firstFrame() + 1);
                             }
                             debout << "Warning: no calculated height for trackpoint " << j << " (frame "
-                                   << tracker->at(i).firstFrame() + j << ") of person " << i + 1
+                                   << person.firstFrame() + j << ") of person " << i + 1
                                    << ", extrapolated height not used, trackpoint not inserted!" << std::endl;
                         }
                         else
@@ -392,24 +326,23 @@ int TrackerReal::calculate(
                             if(extrapolated)
                             {
                                 debout << "Warning: no calculated height for trackpoint " << j << " (frame "
-                                       << tracker->at(i).firstFrame() + j << ") of person " << i + 1
+                                       << person.firstFrame() + j << ") of person " << i + 1
                                        << ", extrapolated height is used!" << std::endl;
                             }
 
                             Vec2F moveDir(0, 0);
                             if(exportAutoCorrect)
                             {
-                                moveDir +=
-                                    reco::autoCorrectColorMarker((*tracker)[i][j], mMainWindow->getControlWidget());
+                                moveDir += reco::autoCorrectColorMarker(person[j], mMainWindow->getControlWidget());
                             }
 
-                            pos = imageItem->getPosReal(((*tracker)[i][j] + moveDir + br).toQPointF(), bestZ);
+                            pos = imageItem->getPosReal((person[j] + moveDir + br).toQPointF(), bestZ);
 
                             if((exportViewingDirection) &&
-                               ((*tracker)[i][j].color().isValid())) // wenn blickrichtung mit ausgegeben werden soll
+                               (person[j].color().isValid())) // wenn blickrichtung mit ausgegeben werden soll
                             {
-                                colPos = imageItem->getPosReal(
-                                    ((*tracker)[i][j].colPoint() + moveDir + br).toQPointF(), bestZ);
+                                colPos =
+                                    imageItem->getPosReal((person[j].colPoint() + moveDir + br).toQPointF(), bestZ);
                                 trackPersonReal.addEnd(pos, firstFrame + j, colPos - pos);
                             }
                             else
@@ -423,19 +356,18 @@ int TrackerReal::calculate(
                         Vec2F moveDir(0, 0);
                         if(exportAutoCorrect)
                         {
-                            moveDir += reco::autoCorrectColorMarker((*tracker)[i][j], mMainWindow->getControlWidget());
+                            moveDir += reco::autoCorrectColorMarker(person[j], mMainWindow->getControlWidget());
                         }
 
-                        pos = imageItem->getPosReal(((*tracker)[i][j] + moveDir + br).toQPointF(), height);
+                        pos = imageItem->getPosReal((person[j] + moveDir + br).toQPointF(), height);
                         // die frame nummer der animation wird TrackPoint der PersonReal mitgegeben,
                         // da Index groesser sein kann, da vorher frames hinzugefuegt wurden duch
                         // trackPersonReal.init(firstFrame+addFrames, height) oder aber innerhalb des trackink path mit
                         // for schleife ueber f
                         if((exportViewingDirection) &&
-                           ((*tracker)[i][j].color().isValid())) // wenn blickrichtung mit ausgegeben werden soll
+                           (person[j].color().isValid())) // wenn blickrichtung mit ausgegeben werden soll
                         {
-                            colPos =
-                                imageItem->getPosReal(((*tracker)[i][j].colPoint() + moveDir + br).toQPointF(), height);
+                            colPos = imageItem->getPosReal((person[j].colPoint() + moveDir + br).toQPointF(), height);
                             trackPersonReal.addEnd(pos, firstFrame + j, colPos - pos);
                         }
                         else
@@ -444,22 +376,21 @@ int TrackerReal::calculate(
                         }
                         if(exportAngleOfView)
                         {
-                            angle = (90. - imageItem->getAngleToGround(
-                                               ((*tracker)[i][j] + br).x(), ((*tracker)[i][j] + br).y(), height)) *
+                            angle = (90. -
+                                     imageItem->getAngleToGround((person[j] + br).x(), (person[j] + br).y(), height)) *
                                     PI / 180.;
                             trackPersonReal.last().setAngleOfView(angle);
                         }
                         if(exportMarkerID)
                         {
-                            trackPersonReal.last().setMarkerID((*tracker)[i][j].getMarkerID());
+                            trackPersonReal.last().setMarkerID(person[j].getMarkerID());
                         }
                     }
                 }
 
                 if(tmpMissingList.size() > 0)
                 {
-                    if((tmpMissingList.first() == firstFrame + j) &&
-                       (tracker->at(i).trackPointExist(firstFrame + j + 1)))
+                    if((tmpMissingList.first() == firstFrame + j) && (person.trackPointExist(firstFrame + j + 1)))
                     {
                         tmpMissingList.removeFirst();        // frame
                         anz = tmpMissingListAnz.takeFirst(); // anzahl
@@ -468,8 +399,7 @@ int TrackerReal::calculate(
                             // border unberuecksichtigt
                             for(f = 1; f <= anz; ++f)
                             {
-                                sp = (*tracker)[i][j].sp() +
-                                     f * ((*tracker)[i][j + 1].sp() - (*tracker)[i][j].sp()) / (anz + 1);
+                                sp = person[j].sp() + f * (person[j + 1].sp() - person[j].sp()) / (anz + 1);
                                 if(useCalibrationCenter)
                                 {
                                     trackPersonReal.addEnd(
@@ -496,13 +426,11 @@ int TrackerReal::calculate(
                                 Vec2F moveDir(0, 0);
                                 if(exportAutoCorrect)
                                 {
-                                    moveDir +=
-                                        reco::autoCorrectColorMarker((*tracker)[i][j], mMainWindow->getControlWidget());
+                                    moveDir += reco::autoCorrectColorMarker(person[j], mMainWindow->getControlWidget());
                                 }
 
                                 pos2 =
-                                    (imageItem->getPosReal(((*tracker)[i][j + 1] + moveDir + br).toQPointF(), height) -
-                                     pos) /
+                                    (imageItem->getPosReal((person[j + 1] + moveDir + br).toQPointF(), height) - pos) /
                                     (anz + 1);
                                 for(f = 1; f <= anz; ++f)
                                 {
diff --git a/src/view.cpp b/src/view.cpp
index 27f172d55..0b2177c6b 100644
--- a/src/view.cpp
+++ b/src/view.cpp
@@ -111,15 +111,15 @@ void GraphicsView::mouseDoubleClickEvent(QMouseEvent *event)
     {
         if(event->modifiers() == Qt::ShiftModifier) // nur shift zugelassen ...
         {
-            emit mouseMiddleDoubleClick(-1);
+            emit mouseMiddleDoubleClick(PersonStorage::Direction::Previous);
         }
         else if(event->modifiers() == Qt::ControlModifier)
         {
-            emit mouseMiddleDoubleClick(0);
+            emit mouseMiddleDoubleClick(PersonStorage::Direction::Whole);
         }
         else if(event->modifiers() == Qt::AltModifier)
         {
-            emit mouseMiddleDoubleClick(1);
+            emit mouseMiddleDoubleClick(PersonStorage::Direction::Following);
         }
     }
 
diff --git a/tests/unit_test/tst_moCapController.cpp b/tests/unit_test/tst_moCapController.cpp
index b0173b7ce..1e8591e6f 100644
--- a/tests/unit_test/tst_moCapController.cpp
+++ b/tests/unit_test/tst_moCapController.cpp
@@ -20,6 +20,8 @@
 
 #include "moCapController.h"
 #include "moCapPerson.h"
+#include "personStorage.h"
+#include "petrack.h"
 
 #include <catch2/catch.hpp>
 #include <catch2/trompeloeil.hpp>
@@ -29,6 +31,7 @@ using namespace Catch::Matchers;
 class ExtrCalibMock : public trompeloeil::mock_interface<ExtrCalibration>
 {
 public:
+    ExtrCalibMock(PersonStorage &storage) : trompeloeil::mock_interface<ExtrCalibration>(storage) {}
     MAKE_MOCK1(getImagePoint, cv::Point2f(cv::Point3f), override);
 };
 
@@ -56,7 +59,9 @@ SCENARIO("I want to get the render data with one person loaded", "[ui]")
     person.setSamplerate(1);
     // default time offset 0
 
-    ExtrCalibMock extrCalib;
+    Petrack       pet;
+    PersonStorage st{pet};
+    ExtrCalibMock extrCalib{st};
 
     /*
      * We Mock extrCalib and just return the x and y coordinate
-- 
GitLab