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