diff --git a/CMakeLists.txt b/CMakeLists.txt index 73fd37693f35e5528edbb07aa81cd1489e3cd5ce..843bc7e81f2e4ba63b2e9335561020e8b5154456 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -377,6 +377,8 @@ target_sources(petrack_core PRIVATE include/moCapSelectionWidget.h include/personStorage.h include/autosave.h + include/manualTrackpointMover.h + include/frameRange.h ) target_sources(petrack_core PRIVATE @@ -437,6 +439,7 @@ target_sources(petrack_core PRIVATE src/moCapSelectionWidget.cpp src/personStorage.cpp src/autosave.cpp + src/manualTrackpointMover.cpp ui/about.ui ui/codeMarker.ui ui/colorMarker.ui diff --git a/include/frameRange.h b/include/frameRange.h new file mode 100644 index 0000000000000000000000000000000000000000..b532d1d96127c94c388116a8a90d7e8af2e7a3a0 --- /dev/null +++ b/include/frameRange.h @@ -0,0 +1,31 @@ +/* + * PeTrack - Software for tracking pedestrians movement in videos + * Copyright (C) 2010-2022 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 FRAMERANGE_H +#define FRAMERANGE_H + +struct FrameRange +{ + int before = 0; + int after = 0; + int current = 0; +}; + +#endif // FRAMERANGE_H diff --git a/include/manualTrackpointMover.h b/include/manualTrackpointMover.h new file mode 100644 index 0000000000000000000000000000000000000000..b1df17693a10bde7122295e8d92222a57b5dcd3a --- /dev/null +++ b/include/manualTrackpointMover.h @@ -0,0 +1,42 @@ +/* + * PeTrack - Software for tracking pedestrians movement in videos + * Copyright (C) 2010-2022 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 MANUALTRACKPOINTMOVER_H +#define MANUALTRACKPOINTMOVER_H + +#include "frameRange.h" +#include "personStorage.h" + +class ManualTrackpointMover +{ +public: + bool selectTrackPoint( + const QPointF &pos, + const PersonStorage &personStore, + const QSet<int> &peds, + const FrameRange &range); + void moveTrackPoint(const QPointF &pos, PersonStorage &personStore) const; + void setTrackPoint(); + +private: + PersonFrame mSelectedPerson = PersonFrame{-1, -1}; +}; + +#endif // MANUALTRACKPOINTMOVER_H diff --git a/include/personStorage.h b/include/personStorage.h index 2798fe197471661e449a6a2b200bca694b7332d8..2b1286a88ea4d63a457949b9c5bebe33ff58f189 100644 --- a/include/personStorage.h +++ b/include/personStorage.h @@ -21,6 +21,7 @@ #ifndef PERSONSTORAGE_H #define PERSONSTORAGE_H +#include "frameRange.h" #include "tracker.h" #include <vector> @@ -56,6 +57,7 @@ public: 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); + void moveTrackPoint(int personID, int frame, const Vec2F &newPosition); size_t nbPersons() const { return mPersons.size(); } const TrackPerson &at(size_t i) const { return mPersons.at(i); } @@ -82,8 +84,8 @@ public: int largestFirstFrame() const; int largestLastFrame() const; int smallestFirstFrame() const; - std::vector<PersonFrame> - getProximalPersons(const QPointF &pos, int frame, QSet<int> selected, int before, int after) const; + [[nodiscard]] std::vector<PersonFrame> + getProximalPersons(const QPointF &pos, QSet<int> selected, const FrameRange &frameRange) const; void recalcHeight(float altitude); void clear() { mPersons.clear(); } diff --git a/include/petrack.h b/include/petrack.h index 59431550dca0612708628123f2d49fedd960d12e..f9278b2b844df116b6129da2c2c2aa2d2a7fa553 100644 --- a/include/petrack.h +++ b/include/petrack.h @@ -39,6 +39,7 @@ #include "brightContrastFilter.h" #include "coordItem.h" #include "extrCalibration.h" +#include "manualTrackpointMover.h" #include "moCapController.h" #include "moCapPerson.h" #include "personStorage.h" @@ -156,6 +157,9 @@ public slots: void deleteTrackPointAll(PersonStorage::Direction direction); void deleteTrackPointROI(); void deleteTrackPointInsideROI(); + void moveTrackPoint(QPointF pos); + void selectPersonForMoveTrackPoint(QPointF pos); + void releaseTrackPoint(); // void showContextMenu(QPointF pos); void updateSourceInOutFrames(); void skipToFrameWheel(int delta); @@ -497,6 +501,8 @@ private: double mHeadSize; double mCmPerPixel; + ManualTrackpointMover mManualTrackPointMover; + double mShowFPS; bool mAutoBackTrack; diff --git a/include/view.h b/include/view.h index 9dac4763ce83dcb17c2ad02bcfe12ba8224b5937..115e81661fb9b7032f9de849e70566e88955bd40 100644 --- a/include/view.h +++ b/include/view.h @@ -48,7 +48,10 @@ public: void wheelEvent(QWheelEvent *event) override; void mouseDoubleClickEvent(QMouseEvent *event) override; void keyPressEvent(QKeyEvent *event) override; + void keyReleaseEvent(QKeyEvent *event) override; void mousePressEvent(QMouseEvent *event) override; + void mouseReleaseEvent(QMouseEvent *event) override; + void mouseMoveEvent(QMouseEvent *event) override; signals: void mouseDoubleClick(); @@ -59,6 +62,10 @@ signals: void mouseRightDoubleClick(QPointF pos, int direction); void mouseMiddleDoubleClick(PersonStorage::Direction direction); void mouseShiftWheel(int delta); + void mouseAltPressed(QPointF pos); + void mouseAltReleased(QPointF pos); + void altReleased(); + void mouseAltMoved(QPointF pos); void colorSelected(); void setColorEvent(); }; diff --git a/src/manualTrackpointMover.cpp b/src/manualTrackpointMover.cpp new file mode 100644 index 0000000000000000000000000000000000000000..52f76df9fdaef2ea7f64fbfefce5db28b6e6a52c --- /dev/null +++ b/src/manualTrackpointMover.cpp @@ -0,0 +1,60 @@ +/* + * PeTrack - Software for tracking pedestrians movement in videos + * Copyright (C) 2010-2022 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 "manualTrackpointMover.h" + +#include "pMessageBox.h" + +bool ManualTrackpointMover::selectTrackPoint( + const QPointF &pos, + const PersonStorage &personStore, + const QSet<int> &peds, + const FrameRange &range) +{ + auto res = personStore.getProximalPersons(pos, peds, range); + + bool successfulSelection = res.size() == 1; + + if(successfulSelection) + { + mSelectedPerson = res.front(); + } + else if(res.size() > 1) + { + PWarning( + nullptr, + "Too many trajectories", + "PeTrack can't determine which point you meant. Try selecting fewer trajectories first."); + } + return successfulSelection; +} + +void ManualTrackpointMover::moveTrackPoint(const QPointF &pos, PersonStorage &personStore) const +{ + if(mSelectedPerson.personID != -1 && mSelectedPerson.frame != -1) + { + personStore.moveTrackPoint(mSelectedPerson.personID, mSelectedPerson.frame, Vec2F{pos}); + } +} + +void ManualTrackpointMover::setTrackPoint() +{ + mSelectedPerson = {-1, -1}; +} diff --git a/src/personStorage.cpp b/src/personStorage.cpp index 8a7a091f5717163c444e2ea3a844a1370f497a07..fa08541d8d3c1fc11a4e6f21c6902889ea788f0c 100644 --- a/src/personStorage.cpp +++ b/src/personStorage.cpp @@ -404,6 +404,25 @@ bool PersonStorage::resetTrackPersonHeight(const Vec2F &point, int frame, QSet<i return false; } +void PersonStorage::moveTrackPoint(int personID, int frame, const Vec2F &newPosition) +{ + auto &person = mPersons.at(personID); + TrackPoint newPoint; + newPoint = newPosition; + newPoint.setQual(100); + if(person.trackPointExist(frame)) + { + int idx = frame - person.firstFrame(); + person[idx] = newPoint; + } + else + { + // only logging since this is a software bug, not a user bug; precondition of function not fulfilled + debout << "Warning: Trying to move nonexisting trackpoint of person " << personID << " at frame " << frame + << std::endl; + } +} + // 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*/) @@ -711,7 +730,7 @@ int PersonStorage::smallestFirstFrame() const * @return list of the id of all proximal persons with the frame at which they are nearest to pos */ std::vector<PersonFrame> -PersonStorage::getProximalPersons(const QPointF &pos, int frame, QSet<int> selected, int before, int after) const +PersonStorage::getProximalPersons(const QPointF &pos, QSet<int> selected, const FrameRange &frameRange) const { std::vector<PersonFrame> result; for(int i = 0; i < static_cast<int>(mPersons.size()); ++i) @@ -723,14 +742,14 @@ PersonStorage::getProximalPersons(const QPointF &pos, int frame, QSet<int> selec double minDist = std::numeric_limits<double>::max(); int minFrame = -1; - for(int f = frame - before; f <= frame + after; ++f) + for(int f = frameRange.current - frameRange.before; f <= frameRange.current + frameRange.after; ++f) { if(!mPersons[i].trackPointExist(f)) { continue; } auto dist = mPersons[i].trackPointAt(f).distanceToPoint(pos); - if(dist < minDist && dist < (mMainWindow.getHeadSize(nullptr, i, frame) / 2.)) + if(dist < minDist && dist < (mMainWindow.getHeadSize(nullptr, i, frameRange.current) / 2.)) { minDist = dist; minFrame = f; diff --git a/src/petrack.cpp b/src/petrack.cpp index 169a7e6aae89e9437c352b226e9f2a2157df157f..22cdc5b2f46c318aa91128ae3d47b15b11346814 100644 --- a/src/petrack.cpp +++ b/src/petrack.cpp @@ -173,6 +173,11 @@ Petrack::Petrack() : connect(mView, &GraphicsView::mouseMiddleDoubleClick, this, &Petrack::deleteTrackPointAll); connect(mView, &GraphicsView::mouseShiftWheel, this, &Petrack::skipToFrameWheel); connect(mView, &GraphicsView::mouseAltDoubleClick, this, &Petrack::skipToFrameFromTrajectory); + connect(mView, &GraphicsView::mouseAltMoved, this, &Petrack::moveTrackPoint); + connect(mView, &GraphicsView::mouseAltPressed, this, &Petrack::selectPersonForMoveTrackPoint); + connect(mView, &GraphicsView::altReleased, this, &Petrack::releaseTrackPoint); + connect(mView, &GraphicsView::mouseAltReleased, this, &Petrack::releaseTrackPoint); + mPlayerWidget = new Player(mAnimation, this); @@ -1619,7 +1624,9 @@ void Petrack::keyBindings() "<dt><kbd>Alt + double-click middle mouse button</kbd></dt><dd>deletes the future part of all trajectories</dd>" "<dt><kbd>Shift + t</kbd></dt><dd>toggles tracking online calculation</dd>" "<dt><kbd>Shift + double-click left mouse button</kbd></dt><dd>inserts new or moves near trackpoint and " - "enables showing only the modified trajectory</dd></dl>" + "enables showing only the modified trajectory</dd>" + "<dt><kbd>Alt + double-click left mouse button</kbd></dt><dd>jumps to frame of trackpoint under cursor</dd>" + "<dt><kbd>Alt + holding left mouse button</kbd></dt><dd>moves trackpoint under cursor</dd></dl>" "<p>Further key bindings you will find next to the entries of the menus.</p>"); PMessageBox *mb = new PMessageBox(this, tr("Key Bindings"), out, QIcon()); @@ -4214,6 +4221,35 @@ void Petrack::deleteTrackPointInsideROI() updateControlWidget(); mScene->update(); } + +void Petrack::moveTrackPoint(QPointF pos) +{ + mManualTrackPointMover.moveTrackPoint(pos, mPersonStorage); + mScene->update(); +} + +void Petrack::selectPersonForMoveTrackPoint(QPointF pos) +{ + FrameRange range; + range.before = mControlWidget->trackShowBefore->value(); + range.after = mControlWidget->trackShowAfter->value(); + range.current = mPlayerWidget->getPos(); + auto successfullySelected = + mManualTrackPointMover.selectTrackPoint(pos, mPersonStorage, getPedestrianUserSelection(), range); + + if(successfullySelected) + { + setCursor(QCursor{Qt::CursorShape::DragMoveCursor}); + } +} + +void Petrack::releaseTrackPoint() +{ + mManualTrackPointMover.setTrackPoint(); + mAutosave.trackPersonModified(); + setCursor(QCursor{}); +} + void Petrack::updateSourceInOutFrames() { mPlayerWidget->setFrameInNum(mAnimation->getSourceInFrameNum()); @@ -4232,8 +4268,9 @@ void Petrack::skipToFrameFromTrajectory(QPointF pos) const auto before = mControlWidget->trackShowBefore->value(); const auto after = mControlWidget->trackShowAfter->value(); const auto currFrame = mPlayerWidget->getPos(); + FrameRange frameRange{before, after, currFrame}; - auto res = mPersonStorage.getProximalPersons(pos, currFrame, peds, before, after); + auto res = mPersonStorage.getProximalPersons(pos, peds, frameRange); if(res.size() == 1) { diff --git a/src/view.cpp b/src/view.cpp index 841c10e78c9af67e84d3996b43fe03929b04c42a..43d94aa2f572dae06a35996d951c397adbe09877 100644 --- a/src/view.cpp +++ b/src/view.cpp @@ -154,6 +154,16 @@ void GraphicsView::keyPressEvent(QKeyEvent *event) } } +void GraphicsView::keyReleaseEvent(QKeyEvent *event) +{ + switch(event->key()) + { + case Qt::Key_Alt: + emit altReleased(); + break; + } +} + void GraphicsView::mousePressEvent(QMouseEvent *event) { if(event->modifiers() & Qt::ShiftModifier) @@ -164,9 +174,35 @@ void GraphicsView::mousePressEvent(QMouseEvent *event) { emit colorSelected(); } + + if(event->modifiers().testFlag(Qt::AltModifier) && event->button() == Qt::LeftButton) + { + emit mouseAltPressed(mapToScene(event->pos())); + } QGraphicsView::mousePressEvent(event); } +void GraphicsView::mouseReleaseEvent(QMouseEvent *event) +{ + if(event->modifiers().testFlag(Qt::AltModifier)) + { + emit mouseAltReleased(mapToScene(event->pos())); + } + QGraphicsView::mouseReleaseEvent(event); +} + +void GraphicsView::mouseMoveEvent(QMouseEvent *event) +{ + if(event->modifiers().testFlag(Qt::AltModifier)) + { + emit mouseAltMoved(mapToScene(event->pos())); + } + else + { + QGraphicsView::mouseMoveEvent(event); + } +} + //---------------------------------------------------------------------