Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found
Select Git revision

Target

Select target project
  • ped-dyn-emp/petrack
1 result
Select Git revision
Show changes
Commits on Source (22)
Showing with 1729 additions and 1532 deletions
......@@ -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
......
......@@ -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
......@@ -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;
......
......@@ -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();
......
/*
* 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
......@@ -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;
......
......@@ -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
......
......@@ -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);
......
......@@ -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);
......
......@@ -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
......
......@@ -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();
......
......@@ -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()
......
......@@ -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();
}
......
......@@ -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;
......
......@@ -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
......
......@@ -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)
......
/*
* 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;
}
......@@ -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();
}
......
......@@ -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(
......
......@@ -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;
}