Newer
Older
/*
* PeTrack - Software for tracking pedestrians movement in videos
* Copyright (C) 2023 Forschungszentrum Jülich GmbH, IAS-7
*
* 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 <QtOpenGL>
#include <QtWidgets>
#include "alignmentGridBox.h"
#include "analysePlot.h"
#include "animation.h"
#include "autoCalib.h"
#include "autosaveSettings.h"
#include "backgroundItem.h"
#include "calibFilter.h"
#include "codeMarkerWidget.h"
#include "coordItem.h"
#include "coordinateSystemBox.h"
#include "editMoCapDialog.h"
#include "extrinsicBox.h"
#include "helper.h"
#include "intrinsicBox.h"
#include "multiColorMarkerItem.h"
#include "multiColorMarkerWidget.h"
#include "openMoCapDialog.h"
#include "pMessageBox.h"
#include "petrack.h"
#include "player.h"
#include "roiItem.h"
#include "stereoItem.h"
#include "stereoWidget.h"
#include "worldImageCorrespondence.h"
#include <QtPrintSupport/QPrintDialog>
#include <QtPrintSupport/QPrinter>
#include <cmath>
int Petrack::trcVersion = 0;
// Reihenfolge des anlegens der objekte ist sehr wichtig
Petrack::Petrack(QString petrackVersion) :
mExtrCalibration(mPersonStorage),
mPetrackVersion(std::move(petrackVersion)),
mAuthors(IO::readAuthors(QCoreApplication::applicationDirPath() + "/.zenodo.json"))
mHeadSize = -1;
mCmPerPixel = -1;
mScene = nullptr;
mTracker = nullptr;
mTrackerReal = nullptr; // damit beim zeichnen von control mit analysePlot nicht auf einen feheler laeuft
mStatusLabelFPS = nullptr;
mStatusLabelPosReal = nullptr;
mImageItem = nullptr;
mRecognitionChanged = true;
mTrackChanged = true;
mCoordItem = nullptr;
mImage = nullptr;
setLoading(true);
setAcceptDrops(true);
int space = 2;
mBrightContrastFilter.disable();
mBorderFilter.disable();
mSwapFilter.disable();
mBackgroundFilter.disable();
mTrackingRoiItem = new RoiItem(this, Qt::blue);
connect(mTrackingRoiItem, &RoiItem::changed, this, [=]() { this->setTrackChanged(true); });
mTrackingRoiItem->setZValue(4); // groesser heisst weiter oben
mRecognitionRoiItem = new RoiItem(this, Qt::green);
connect(mRecognitionRoiItem, &RoiItem::changed, this, [=]() { this->setRecognitionChanged(true); });
mRecognitionRoiItem->setZValue(5); // groesser heisst weiter oben
// setup control
auto updateImageCallback = [this]()
{
if(!isLoading())
{
updateImage();
}
};
auto updateStatusPos = [this]() { setStatusPosReal(); };
auto updateHeadSize = [this]() { setHeadSize(); };
auto getBorderSize = [this]() { return getImageBorderSize(); };
auto *filterBeforeBox = new FilterBeforeBox(
nullptr, // reparented when added to layout
*getBackgroundFilter(),
*getBrightContrastFilter(),
*getBorderFilter(),
*getSwapFilter(),
updateImageCallback);
auto *intrinsicBox = new IntrinsicBox(this, *getAutoCalib(), *getCalibFilter(), updateImageCallback);
auto *extrinsicBox = new ExtrinsicBox(this, *getExtrCalibration());
mImageItem = new ImageItem(this, nullptr);
auto *coordSysBox = new CoordinateSystemBox(
this,
updateStatusPos,
updateHeadSize,
getBorderSize,
*intrinsicBox,
*extrinsicBox,
*mImageItem,
mExtrCalibration);
auto *gridBox = new AlignmentGridBox(this);
mControlWidget = new Control(
*this,
*mScene,
mReco,
*mTrackingRoiItem,
*mRecognitionRoiItem,
mMissingFrames,
filterBeforeBox,
intrinsicBox,
extrinsicBox,
coordSysBox,
gridBox);
connect(mImageItem, &ImageItem::imageChanged, mControlWidget, &Control::imageSizeChanged);
mWorldImageCorrespondence = &mControlWidget->getWorldImageCorrespondence();
mStereoWidget = new StereoWidget(this);
mStereoWidget->setWindowFlags(Qt::Window);
mStereoWidget->setWindowTitle("Stereo parameter");
mColorRangeWidget = new ColorRangeWidget(this);
mColorRangeWidget->setWindowFlags(Qt::Window);
mColorRangeWidget->setWindowTitle("Color range");
mColorMarkerWidget = new ColorMarkerWidget(this);
mColorMarkerWidget->setWindowFlags(Qt::Window);
mColorMarkerWidget->setWindowTitle("Color marker parameter");
mCodeMarkerWidget = new CodeMarkerWidget(this, mReco.getCodeMarkerOptions(), nullptr);
mCodeMarkerWidget->setWindowFlags(Qt::Window);
mCodeMarkerWidget->setWindowTitle("Code marker parameter");
mMultiColorMarkerWidget = new MultiColorMarkerWidget(this);
mMultiColorMarkerWidget->setWindowFlags(Qt::Window);
mMultiColorMarkerWidget->setWindowTitle("MultiColor marker parameter");
mAnimation = new Animation(this);
mLogoItem = new LogoItem(this); // durch uebergabe von scene wird indirekt ein scene->addItem() aufgerufen
mLogoItem->setZValue(6); // groesser heisst weiter oben
mGridItem = new GridItem(this, nullptr, coordSysBox, gridBox);
mGridItem->setZValue(2.5); // durch uebergabe von scene wird indirekt ein scene->addItem() aufgerufen
mCoordItem = new CoordItem(this, nullptr, coordSysBox);
mCoordItem->setZValue(3); // groesser heisst weiter oben
mViewWidget = new ViewWidget(this);
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);
connect(mView, &GraphicsView::mouseCtrlAltDoubleClick, this, &Petrack::skipToFrameFromTrajectory);
d.kilic
committed
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);
connect(mView, &GraphicsView::mouseCtrlWheel, this, &Petrack::scrollShowOnly);
mLogWindow = new LogWindow(this, nullptr);
mLogWindow->setWindowFlags(Qt::Window);
mLogWindow->setWindowTitle("Log");
mPlayerWidget = new Player(mAnimation, this);
QVBoxLayout *vLayout = new QVBoxLayout;
vLayout->setSpacing(space);
vLayout->addWidget(mViewWidget);
vLayout->addWidget(mPlayerWidget);
//---------------------------
mTracker = new Tracker(this, mPersonStorage);
mTrackerReal = new TrackerReal(this, mPersonStorage);
mTrackerItem = new TrackerItem(this, mPersonStorage);
mControlWidget->getColorPlot()->setPersonStorage(&mPersonStorage);
#ifdef QWT
mControlWidget->getAnalysePlot()->setTrackerReal(mTrackerReal);
#endif
//---------------------------
mStereoItem = new StereoItem(this);
mStereoItem->setZValue(2); // groesser heisst weiter oben
mStereoItem->setVisible(false);
//---------------------------
mColorMarkerItem = new ColorMarkerItem(this);
mColorMarkerItem->setZValue(2); // groesser heisst weiter oben
mColorMarkerItem->setVisible(false);
//---------------------------
mCodeMarkerItem = new CodeMarkerItem(this, mReco.getCodeMarkerOptions());
mCodeMarkerItem->setZValue(2); // groesser heisst weiter oben
mCodeMarkerItem->setVisible(false);
//---------------------------
mMultiColorMarkerItem = new MultiColorMarkerItem(this);
mMultiColorMarkerItem->setZValue(2); // groesser heisst weiter oben
mMultiColorMarkerItem->setVisible(false);
//---------------------------
mBackgroundItem = new BackgroundItem(this, nullptr, *filterBeforeBox);
mBackgroundItem->setZValue(2.2); // um so groesser um so hoeher um so eher zu sehen
mBackgroundItem->setVisible(false);
//---------------------------
mMoCapItem = new MoCapItem(*this, *mAnimation, mMoCapController);
mMoCapItem->setZValue(3); // um so groesser um so hoeher um so eher zu sehen
/// Add Items
mScene->addItem(mImageItem);
mScene->addItem(mLogoItem);
mScene->addItem(mGridItem);
mScene->addItem(mCoordItem);
mScene->addItem(mTrackingRoiItem);
mScene->addItem(mRecognitionRoiItem);
mScene->addItem(mTrackerItem);
mScene->addItem(mStereoItem);
mScene->addItem(mColorMarkerItem);
mScene->addItem(mCodeMarkerItem);
mScene->addItem(mMultiColorMarkerItem);
mScene->addItem(mBackgroundItem);
//---------------------------
mCentralLayout = new QHBoxLayout;
mCentralLayout->setMargin(space);
mCentralWidget = new QFrame;
mCentralWidget->setFrameStyle(QFrame::Sunken | QFrame::StyledPanel);
mCentralWidget->setLayout(mCentralLayout);
setCentralWidget(mCentralWidget);
mSplitter = new QSplitter(this);
// create playAndView-Widget to wrap layout, since QSplitter only accepts widgets
QWidget *playAndView = new QWidget(this);
playAndView->setLayout(vLayout);
mSplitter->addWidget(playAndView);
mSplitter->addWidget(mControlWidget);
mSplitter->setStretchFactor(0, 1);
mSplitter->setStretchFactor(1, 0);
mCentralLayout->addWidget(mSplitter);
setWindowTitle(tr("PeTrack"));
//---------------------------
mAutoCalib.setMainWindow(this);
//---------------------------
createActions();
createMenus();
createStatusBar();
auto *exportShortCut = new QShortcut{QKeySequence("Ctrl+e"), this};
connect(exportShortCut, &QShortcut::activated, this, [=]() { exportTracker(); });
auto *toggleOnlineTracking = new QShortcut{QKeySequence("Shift+t"), this};
connect(toggleOnlineTracking, &QShortcut::activated, this, [=]() { mControlWidget->toggleOnlineTracking(); });
// TODO delete once we get Options to be value only (i.e. no Pointer/Ref anymore)
mReco.getCodeMarkerOptions().setControlWidget(mControlWidget);
mReco.getCodeMarkerOptions().setCodeMarkerItem(mCodeMarkerItem);
mSeqFileName = QDir::currentPath(); // fuer allerersten Aufruf des Programms
saveXml(mDefaultSettings); // noch nicht noetig, da eh noch nicht fkt
mShowFPS = 0;
mTrcFileName = "";
// initialer Aufruf, damit beim reinen Laden einer Videodatei die Defaultwerte in control genommen werden zum Setzen
setHeadSize();
// um im background subtraction filter das hoehenbild zu beruecksichtigen
mBackgroundFilter.setStereoContext(&mStereoContext);
mAutoBackTrack = true; // ist der default, dann wenn in XML-Datei nicht drin steht
mAutoTrackOptimizeColor = false; // ist der default, dann wenn in XML-Datei nicht drin steht
setLoading(false);
}
Petrack::~Petrack()
{
delete mImage;
// hier muessten weitere stehen insb die im konstruktor erzeugt werden
// aber da petrack nur vernichtet wird, wenn programm beendet wird, kann man sich das auch schenken
}
void Petrack::dragEnterEvent(QDragEnterEvent *event)
{
/**
* @brief Accepts dropped .pet, .trc and media files
*
* Opens the project for a .pet. Imports the trajectories for a .trc
* and tries to open the sequence for any other kind of file.
*
* @param event
*/
if(event->mimeData()->urls().first().toLocalFile().endsWith(".pet", Qt::CaseInsensitive))
else if(event->mimeData()->urls().first().toLocalFile().endsWith(".trc", Qt::CaseInsensitive))
event->acceptProposedAction();
}
}
void Petrack::updateSceneRect()
{
if(mImage && !mImage->isNull())
{
iW = mImage->width();
iH = mImage->height();
bS = getImageBorderSize();
}
else
{
mScene->setSceneRect(mScene->itemsBoundingRect());
return;
}
if(mControlWidget->getCalibCoordShow())
double scale = mControlWidget->getCalibCoordScale() / 10.;
auto t = mControlWidget->getCalibCoord2DTrans() / 10.;
// setzen der bounding box der scene
// Faktor 1.1 dient dazu, dass auch Zahl "1" bei coord gut in sichtbaren Bereich passt
double xMin = (t.x() - 1.1 * scale < -bS) ? t.x() - 1.1 * scale : -bS;
double yMin = (t.y() - 1.1 * scale < -bS) ? t.y() - 1.1 * scale : -bS;
double xMax = (t.x() + 1.1 * scale > iW - bS) ? t.x() + 1.1 * scale : iW - bS;
double yMax = (t.y() + 1.1 * scale > iH - bS) ? t.y() + 1.1 * scale : iH - bS;
mScene->setSceneRect(xMin, yMin, xMax - xMin, yMax - yMin);
/**
* @brief Loads the content of a .pet file into petrack
*
* @param doc the DOM of the .pet file
* @param openSeq true, if sequence given in doc should be opened
*/
mMissingFrames.reset();
bool missingFramesExecuted = false;
std::vector<MissingFrame> missingFrames{};
QString seq;
int frame = -1, sourceFrameIn = -1, sourceFrameOut = -1;
double fps = DEFAULT_FPS;
int onlyPeopleNr = 1;
QString onlyPeopleNrList = "1";
int zoom = 250, rotate = 0, hScroll = 0, vScroll = 0;
auto petVersion = root.attribute("VERSION");
for(QDomElement elem = root.firstChildElement(); !elem.isNull(); elem = elem.nextSiblingElement())
{
// will show undistorted image until calibration is loaded as well
// but image changes maximum values e.g. for cx; need to set this first to ensure that the
// correct values can be read in
mSeqFileName = seq;
if(openSeq)
{
if(seq != "")
{
openSequence(seq);
}
else
{
mAnimation->reset();
mImg = cv::Mat();
mImgFiltered = cv::Mat();
delete mImage;
mImage = nullptr;
updateSequence();
mLogoItem->ensureVisible();
mLogoItem->fadeIn();
}
}
if(elem.hasAttribute("STATUS_HEIGHT"))
if(mStatusPosRealHeight) // null kann eigentlich nicht vorkommen, da im constructor von petrack erzeugt
// wird
mStatusPosRealHeight->setValue(elem.attribute("STATUS_HEIGHT").toDouble());
else if(elem.tagName() == "STEREO")
else if(elem.tagName() == "COLOR_MARKER")
else if(elem.tagName() == "CODE_MARKER")
else if(elem.tagName() == "MULTI_COLOR_MARKER")
else if(elem.tagName() == "MOCAP")
{
mMoCapController.getXml(elem);
}
else if(elem.tagName() == "CONTROL")
mControlWidget->getXml(elem, petVersion);
QDomElement tmpElem = (elem.firstChildElement("TRACKING")).firstChildElement("PATH");
if(tmpElem.hasAttribute("ONLY_PEOPLE_NR"))
if(tmpElem.hasAttribute("ONLY_PEOPLE_NR_LIST"))
else if(elem.tagName() == "EXTR_CALIBRATION")
{
mExtrCalibration.getXml(elem);
}
else if(elem.tagName() == "PLAYER")
if(elem.hasAttribute("SOURCE_FRAME_IN"))
{
sourceFrameIn = elem.attribute("SOURCE_FRAME_IN").toInt();
}
if(elem.hasAttribute("SOURCE_FRAME_OUT"))
{
sourceFrameOut = elem.attribute("SOURCE_FRAME_OUT").toInt();
}
if(elem.hasAttribute("PLAYER_SPEED_FIXED"))
mPlayerWidget->setPlayerSpeedLimited(elem.attribute("PLAYER_SPEED_FIXED").toInt());
else if(elem.tagName() == "VIEW")
if(elem.hasAttribute("ANTIALIAS"))
mAntialiasAct->setChecked(elem.attribute("ANTIALIAS").toInt() == Qt::Checked);
mOpenGLAct->setChecked(elem.attribute("OPENGL").toInt() == Qt::Checked);
if(elem.hasAttribute("SAVE_TRANSFORMED"))
mCropZoomViewAct->setChecked(elem.attribute("SAVE_TRANSFORMED") == Qt::Checked);
if(elem.hasAttribute("TRANSFORMATION"))
QString matStr = elem.attribute("TRANSFORMATION");
QTextStream in(&matStr);
in >> zoom >> rotate >> hScroll >> vScroll;
}
{
cam = (enum Camera) elem.attribute("CAMERA").toInt();
}
if(elem.hasAttribute("HIDE_CONTROLS"))
mHideControlsAct->setChecked(elem.attribute("HIDE_CONTROLS").toInt() == Qt::Checked);
}
else if(elem.tagName() == "AUTO_TRACK")
if(elem.hasAttribute("BACK_TRACK"))
{
mAutoBackTrack = elem.attribute("BACK_TRACK").toInt();
}
if(elem.hasAttribute("OPTIMZE_COLOR"))
{
mAutoTrackOptimizeColor = elem.attribute("OPTIMZE_COLOR").toInt();
}
}
else if(elem.tagName() == "MISSING_FRAMES")
{
if((elem.hasAttribute("executed")) && (elem.attribute("executed").toInt() == 1))
{
missingFramesExecuted = true;
auto node = elem.firstChildElement("FRAME");
for(; !node.isNull(); node = node.nextSiblingElement("FRAME"))
{
size_t num = node.attribute("NUM_FRAME").toUInt();
int count = node.attribute("NUM_MISSING").toInt();
missingFrames.push_back(MissingFrame{num, count});
}
}
}
SPDLOG_ERROR("Unknown PETRACK tag {}", elem.tagName());
mMissingFrames.setExecuted(missingFramesExecuted);
mMissingFrames.setMissingFrames(missingFrames);
mViewWidget->setZoomLevel(zoom);
mViewWidget->setRotateLevel(rotate);
mView->horizontalScrollBar()->setValue(hScroll);
mView->verticalScrollBar()->setValue(vScroll);
bool loaded = false;
if(!mBackgroundFilter.getFilename().isEmpty())
if(!(loaded = mBackgroundFilter.load(mBackgroundFilter.getFilename())))
SPDLOG_ERROR("Error: loading background file {}!", mBackgroundFilter.getFilename());
mPlayerWidget->setFrameInNum(sourceFrameIn == -1 ? mAnimation->getSourceInFrameNum() : sourceFrameIn);
mPlayerWidget->setFrameOutNum(sourceFrameOut == -1 ? mAnimation->getSourceOutFrameNum() : sourceFrameOut);
// used first loaded image to determine bg, should not have happened
if(mControlWidget->isFilterBgChecked() && !loaded)
// reset background and first skip to selected frame
mBackgroundFilter.reset();
// will call updateImage and update bg
mPlayerWidget->skipToFrame(frame);
// nicht schon in control, sonst loescht opensequence wieder tracker
{
// vorher loeschen aller trajektorien, da sonst nach start im ersten bild
// mgl zwei trackpoints
// beim haendischen importieren sind weiterhin parallele trajektorien moeglich (warnung wird ausgegeben)
frame = 0; // default
if((mPersonStorage.largestLastFrame() >= frame) && (getPersonStorage().smallestFirstFrame() <= frame))
mTracker->reset();
}
importTracker(mTrcFileName);
}
mControlWidget->setTrackShowOnlyNr(onlyPeopleNr);
mControlWidget->trackShowOnlyNrList()->setText(onlyPeopleNrList);
setCamera();
mPlayerWidget->setFPS(fps); // erst spaet setzen, damit Wert den des geladenen Videos ueberschreiben kann
updateImage(); // needed to undistort, draw border, etc. for first display
/**
* Open a petrack project.
* When mProFileName is set and a file, this method will ask to save the project before opening the new one.
* If it is not a file, the path is used as directory for the filedialog.
*
* If the given filename is empty, a dialog opens that asks for a file to open.
*
* @param fileName the filename to open
* @param openSeq true, if sequence given in project should be opened
*/
void Petrack::openProject(QString fileName, bool openSeq) // default fileName="", openSequence = true
{
if(QFileInfo(mProFileName).isFile()) // a project is already loaded
else if(!QFileInfo(mProFileName).isDir())
{
SPDLOG_INFO("mProFileName neither file nor directory - resetting");
mProFileName = "";
}
fileName = QFileDialog::getOpenFileName(
this,
tr("Select project file"),
QFileInfo(mProFileName).path(),
tr("PeTrack project file (*.pet);;All files (*.*)"));
if(Autosave::autosaveExists(fileName) && fileName != mProFileName)
{
auto ret = PQuestion(this, "Autosave detected", "An autosave was detected.\nDo you want to load the Autosave?");
if(ret == PMessageBox::StandardButton::Yes)
{
setProFileName(fileName);
mAutosave.loadAutosave();
return;
}
}
if(!file.open(QIODevice::ReadOnly))
PCritical(this, tr("PeTrack"), tr("Cannot open %1:\n%2.").arg(fileName, file.errorString()));
QDomDocument oldSettings;
QString oldProFilename = mProFileName;
saveXml(oldSettings);
resetSettings();
QDomDocument doc("PETRACK"); // eigentlich Pfad zu Beschreibungsdatei fuer Dateiaufbau
PCritical(this, tr("PeTrack"), tr("Cannot read content from %1.").arg(fileName));
if(root.attribute("VERSION") != mPetrackVersion)
PWarning(
this,
tr("PeTrack"),
tr("Reading %1:\nDifferent version numbers %2 (application) and %3 (file) may cause problems.")
.arg(fileName, mPetrackVersion, root.attribute("VERSION")));
try
{
openXml(doc, openSeq);
}
catch(std::domain_error &e)
{
// reset to settings before trying to load new file
openXml(oldSettings);
setProFileName(oldProFilename);
auto errorStr = QString{"Error during reading of pet file:\n%1"}.arg(e.what());
PCritical(this, "Could not read pet-file", errorStr);
return;
}
mLastTrackerExport = mTrcFileName;
updateWindowTitle();
}
}
void Petrack::saveXml(QDomDocument &doc)
{
QDomElement elem;
QDomElement root = doc.createElement("PETRACK");
root.setAttribute("VERSION", mPetrackVersion);
doc.appendChild(root);
// main settings (window size, status hight)
elem = doc.createElement("MAIN");
QString seq = getFileList(mSeqFileName, mProFileName);
elem.setAttribute("SRC", seq);
elem.setAttribute("STATUS_HEIGHT", mStatusPosRealHeight->value());
root.appendChild(elem);
// control settings (right control widget)
elem = doc.createElement("CONTROL");
mControlWidget->setXml(elem);
root.appendChild(elem);
// Reprojection error extrinsic calib
elem = doc.createElement("EXTR_CALIBRATION");
mExtrCalibration.setXml(elem);
root.appendChild(elem);
// settings for stereo
elem = doc.createElement("STEREO");
mStereoWidget->setXml(elem);
root.appendChild(elem);
// settings for color marker
elem = doc.createElement("COLOR_MARKER");
mColorMarkerWidget->setXml(elem);
root.appendChild(elem);
// settings for code marker
elem = doc.createElement("CODE_MARKER");
mCodeMarkerWidget->setXml(elem);
root.appendChild(elem);
// settings for multicolor marker
elem = doc.createElement("MULTI_COLOR_MARKER");
mMultiColorMarkerWidget->setXml(elem);
root.appendChild(elem);
// settings for MoCap-Visualization
elem = doc.createElement("MOCAP");
mMoCapController.setXml(elem);
root.appendChild(elem);
// player settings (which frame, frame range)
elem = doc.createElement("PLAYER");
elem.setAttribute("FRAME", mPlayerWidget->getPos()); // == mAnimation->getCurrentFrameNum()
elem.setAttribute("FPS", mAnimation->getFPS());
elem.setAttribute("SOURCE_FRAME_IN", mPlayerWidget->getFrameInNum());
elem.setAttribute("SOURCE_FRAME_OUT", mPlayerWidget->getFrameOutNum());
elem.setAttribute("PLAYER_SPEED_FIXED", mPlayerWidget->getPlayerSpeedLimited());
root.appendChild(elem);
// view settings (zoom, rotate, alias, opengl)
elem = doc.createElement("VIEW");
elem.setAttribute("ANTIALIAS", mAntialiasAct->isChecked());
elem.setAttribute("OPENGL", mOpenGLAct->isChecked());
elem.setAttribute("SAVE_TRANSFORMED", mCropZoomViewAct->isChecked());
elem.setAttribute(
"TRANSFORMATION",
QString("%1 %2 %3 %4")
.arg(mViewWidget->getZoomLevel())
.arg(mViewWidget->getRotateLevel())
.arg(mView->horizontalScrollBar()->value())
.arg(mView->verticalScrollBar()->value()));
#ifndef STEREO_DISABLED
#else
elem.setAttribute("CAMERA", cameraUnset);
#endif
elem.setAttribute("HIDE_CONTROLS", mHideControlsAct->isChecked());
root.appendChild(elem);
// auto track settings
elem = doc.createElement("AUTO_TRACK");
elem.setAttribute("BACK_TRACK", mAutoBackTrack);
elem.setAttribute("OPTIMZE_COLOR", mAutoTrackOptimizeColor);
root.appendChild(elem);
elem = doc.createElement("MISSING_FRAMES");
elem.setAttribute("executed", mMissingFrames.isExecuted());
for(const auto &missingFrame : mMissingFrames.getMissingFrames())
{
auto frame = doc.createElement("FRAME");
frame.setAttribute("NUM_FRAME", static_cast<int>(missingFrame.mNumber));
frame.setAttribute("NUM_MISSING", missingFrame.mCount);
elem.appendChild(frame);
}
root.appendChild(elem);
/// rueckgabewert zeigt an, ob gesichert werden konnte
bool Petrack::saveSameProject()
{
return saveProject(mProFileName);
}
/**
* Ask the user to provide a filename to save project into
* @return true if the saving process was done successfully
*/
// empty filename will trigger file selection box
return saveProject("");
/**
* Save the project to the given filename
* @param fileName the name of the file to save the project into
* @return true if the saving process was done successfully
*/
bool Petrack::saveProject(QString fileName) // default fileName=""
{
fileName = QFileDialog::getSaveFileName(
this, tr("Select project file"), mProFileName, tr("PeTrack project file (*.pet);;All files (*.*)"));
// selection was cancelled
if(fileName.isEmpty())
setProFileName(fileName);
QDomDocument doc("PETRACK"); // eigentlich Pfad zu Beschreibungsdatei fuer Dateiaufbau
saveXml(doc);
// file output
QByteArray byteArray;
QXmlStreamWriter xmlStream(&byteArray);
xmlStream.setAutoFormatting(true);
xmlStream.setAutoFormattingIndent(4);
xmlStream.writeStartDocument();
xmlStream.writeDTD("<!DOCTYPE PETRACK>");
QDomElement element = doc.documentElement();
writeXmlElement(xmlStream, element);
xmlStream.writeEndDocument();
QFile file(fileName);
if(!file.open(QFile::WriteOnly | QFile::Truncate | QFile::Text))
PCritical(this, tr("PeTrack"), tr("Cannot save %1:\n%2.").arg(fileName, file.errorString()));
file.close();
file.write(byteArray);
file.close(); // also flushes the file
statusBar()->showMessage(tr("Saved project to %1.").arg(fileName), 5000);
SPDLOG_INFO("save project to {}", fileName);
updateWindowTitle();
return true;
void Petrack::writeXmlElement(QXmlStreamWriter &xmlStream, QDomElement element)
QVector<QString> attribute_names;
for(int i = 0; i < attributes.size(); ++i)
{
attribute_names.push_back(attributes.item(i).toAttr().name());
}
// TODO: check if sorting of elements fits our needs
std::stable_sort(attribute_names.begin(), attribute_names.end()); // for a canonical XML
// Wants this macro instead of range-based for loop
foreach(QString name, attribute_names)
{
QDomAttr attr = element.attributeNode(name);
xmlStream.writeAttribute(attr.name(), attr.value());
}
// order of child nodes is defined at creation
if(element.hasChildNodes())
{
for(int i = 0; i < children.size(); ++i)
{
writeXmlElement(xmlStream, children.at(i).toElement());
}
}
xmlStream.writeEndElement();
}
/**
* @brief Opens camera livestream from cam with camID
* @param camID id of camera to use (defaults to 0)
*/
void Petrack::openCameraLiveStream(int camID /* =-1*/)
SPDLOG_INFO("No camera ID delivered: Set CameraID to 0 (default Camera)");
if(!mAnimation->openCameraStream(camID))
PCritical(this, tr("PeTrack"), tr("Cannot start Camera Livestream."));
SPDLOG_INFO(
"open {} ({} frames; {} fps; {} x {} pixel)",
mSeqFileName,
mAnimation->getNumFrames(),
mAnimation->getFPS(),
mAnimation->getSize().width(),
mAnimation->getSize().height());
updateSequence();
updateWindowTitle();
mPlayerWidget->setFPS(mAnimation->getFPS());
mPlayerWidget->play(PlayerState::FORWARD);
}
void Petrack::openSequence(QString fileName) // default fileName = ""
{
fileName = QFileDialog::getOpenFileName(
this,
tr("Open video or image sequence"),
QFileInfo(mSeqFileName).path(),
tr("All supported types (*.avi *.mpg *.mts *.m2t *.m2ts *.wmv *.mp4 *.mov *.mxf *.bmp *.dib *.jpeg *.jpg "
"*.jpe *.png *.pbm *.pgm *.ppm *.sr *.ras *.tiff *.tif *.exr *.jp2);;Video (*.avi *.mpg *.mts *.m2ts "
"*.m2t *.wmv *.mov *.mp4 *.mxf);;Images (*.bmp *.dib *.jpeg *.jpg *.jpe *.png *.pbm *.pgm *.ppm *.sr "
"*.ras *.tiff *.tif *.exr *.jp2);;Windows bitmaps (*.bmp *.dib);;JPEG (*.jpeg *.jpg *.jpe);;Portable "
"network graphics (*.png);;Portable image format (*.pbm *.pgm *.ppm);;Sun rasters (*.sr *.ras);;TIFF "
"(*.tiff *.tif);;OpenEXR HDR (*.exr);;JPEG 2000 (*.jp2);;All files (*.*)"));
if(!mAnimation->openAnimation(fileName))
PCritical(this, tr("PeTrack"), tr("Cannot load %1.").arg(fileName));
return;
}
mCameraGroupView->setEnabled(mAnimation->isStereoVideo());
mCameraMenu->setEnabled(mAnimation->isStereoVideo());
#ifdef STEREO
delete mStereoContext;
mStereoContext = new pet::StereoContext(this);
}
bool lastIsStereoVideo = mAnimation->isStereoVideo();
if(mCalibFilter == NULL || (mAnimation->isStereoVideo() != lastIsStereoVideo))
{
lastCalibFilterEnabled = mCalibFilter->getEnabled();
delete mCalibFilter;
}
{
mCalibFilter = new CalibStereoFilter;
((CalibStereoFilter *) mCalibFilter)->setStereoContext(mStereoContext);
}
else
mCalibFilter = new CalibFilter;
mCalibFilter->setEnabled(lastCalibFilterEnabled);
}
#endif
mSeqFileName = fileName;
SPDLOG_INFO(
"open {} ({} frames; {} fps; {} x {} pixel)",
mSeqFileName,
mAnimation->getNumFrames(),
mAnimation->getFPS(),
mAnimation->getSize().width(),
mAnimation->getSize().height());
updateSequence();
updateWindowTitle();
mPlayerWidget->setFPS(mAnimation->getFPS());
void Petrack::openMoCapFile()
{
OpenMoCapDialog dialog(this, mMoCapController);
dialog.exec();
}
void Petrack::editMoCapSettings()
{
auto *dialog = new EditMoCapDialog(this, mMoCapStorage, [this]() { mScene->update(); });
dialog->show();
}
QSize size = mAnimation->getSize();
if(QFileInfo(mProFileName).isDir())
title = tr("PeTrack (v") + mPetrackVersion + tr("): ");
title = tr("PeTrack (v") + mPetrackVersion + tr("): ") + QFileInfo(mProFileName).fileName();
if(mAnimation->isVideo() || mAnimation->isImageSequence())
title += "sequence: " + mAnimation->getCurrentFileName() + tr(" (%1").arg(mAnimation->getNumFrames()) +
tr(" frames; %1x%2").arg(size.width()).arg(size.height()) + " pixel)";
else if(mAnimation->isImageSequence())
title += "sequence: " + mAnimation->getCurrentFileName() + tr(" ... (%1").arg(mAnimation->getNumFrames()) +
tr(" frames; %1x%2").arg(size.width()).arg(size.height()) + " pixel)";
setWindowTitle(title);
}
void Petrack::saveVideo()
{
saveSequence(true, false);
}
void Petrack::saveVideoView()
{
saveSequence(true, true);
}
void Petrack::saveImageSequence()
{
saveSequence(false, false);
}
void Petrack::saveViewSequence()
{
saveSequence(false, true);
}
/**
* @brief Saves current sequence as avi-file or image sequence
*
* Saves the loaded image sequence or video from current frame on till the end.
* One can save the sequence as is or one can save the view shown in PeTrack.
*
* @param saveVideo true, if wanting to save a video. Ignored when dest isn't empty
* @param saveView true, if sequence should be saved as shown in PeTrack (with trajectories etc.)
* @param dest destination file; if empty, the user chooses via a dialog
*/
void Petrack::saveSequence(bool saveVideo, bool saveView, QString dest) // default saveView= false, dest=""
{
static QString lastDir;
// if no destination file or folder is given
if(lastDir.isEmpty() && !mSeqFileName.isEmpty())
dest = QFileDialog::getSaveFileName(
this,
tr("Select video file"),
lastDir,
tr("Video (*.mp4 *.avi);;All files (*.*)")); //? *.mpg *.mpeg
dest = QFileDialog::getExistingDirectory(
this,
tr("Select directory to save view sequence"),
lastDir,
QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks);
dest = QFileDialog::getExistingDirectory(
this,
tr("Select directory to save image sequence"),
lastDir,
QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks);
auto extension = dest.right(4).toLower();
int fourcc = -1;
if(extension == ".mp4")
fourcc = cv::VideoWriter::fourcc('m', 'p', '4', 'v');
saveVideo = true;
}
else if(extension == ".avi")
{
fourcc = cv::VideoWriter::fourcc('M', 'J', 'P', 'G');
saveVideo = true;
}
else
{
saveVideo = false;
int rest = mAnimation->getNumFrames() - 1;
int numLength = 1;
int memPos = mPlayerWidget->getPos();
QString fileName = "";
bool formatIsSaveAble = false;
bool saveRet;
QPainter *painter = nullptr;
int progEnd = mAnimation->getSourceOutFrameNum() -
mPlayerWidget->getPos(); // nur wenn nicht an anfang gesprungen wird:-mPlayerWidget->getPos()
cv::Mat iplImgFilteredBGR;
bool writeFrameRet = false;
cv::VideoWriter outputVideo;
if(mCropZoomViewAct->isChecked())
viewImage =
new QImage(mView->viewport()->width(), mView->viewport()->height(), QImage::Format_RGB32);
viewImage = new QImage((int) mScene->width(), (int) mScene->height(), QImage::Format_RGB32);
outputVideo = cv::VideoWriter(
dest.toStdString(),
fourcc,
mAnimation->getFPS(),
cv::Size(viewImage->width(), viewImage->height()));
bool colored = (mImg.channels() > 1);
outputVideo = cv::VideoWriter(
dest.toStdString(), fourcc, mAnimation->getFPS(), cv::Size(mImg.cols, mImg.rows), colored);
if(mCropZoomViewAct->isChecked())
viewImage =
new QImage(mView->viewport()->width(), mView->viewport()->height(), QImage::Format_RGB32);
viewImage = new QImage((int) mScene->width(), (int) mScene->height(), QImage::Format_RGB32);
painter = new QPainter();
}
// test, if fileformat is supported
fileName = (dest + "/" + mAnimation->getFileBase() + "%1.png")
.arg(mPlayerWidget->getPos(), numLength, 10, QChar('0'));
if(mCropZoomViewAct->isChecked())
if(viewImage->save(fileName)) //, const char * format = 0 (format wird aus dateinamen geholt), int
// quality = -1 default normal (0..100)
{
formatIsSaveAble = true;
mPlayerWidget->frameForward();
}
}
else if(mImage->save(fileName)) //, const char * format = 0 (format wird aus dateinamen geholt), int quality
//= -1 default normal (0..100)
{
formatIsSaveAble = true;
mPlayerWidget->frameForward();
}
}
else if((mImgFiltered.channels() == 1) /*&& convert8To24bit*/)
size.height = mImgFiltered.rows;
iplImgFilteredBGR.create(size, CV_8UC3);
}
QProgressDialog progress("", "Abort save", 0, progEnd, this);
progress.setWindowModality(Qt::WindowModal); // blocks main window
progress.setValue(
mPlayerWidget->getPos() - memPos); // -mempos nur, wenn nicht an den anfang gesprungen wird
if(mCropZoomViewAct->isChecked())
cv::Mat frame(
viewImage->height(),
viewImage->width(),
CV_8UC4,
(unsigned char *) viewImage->bits(),
viewImage->bytesPerLine());
cv::cvtColor(frame, frame, cv::COLOR_RGBA2RGB); // need for right image interpretation
outputVideo.write(frame);
writeFrameRet = true;
cv::Mat frame = mImg.clone();
outputVideo.write(frame);
writeFrameRet = true;
PCritical(
this,
tr("PeTrack"),
tr("Cannot save %1 maybe because of wrong file extension or unsupported codec.").arg(dest));
break;
}
}
else
{
// single frame sequence
if(mCropZoomViewAct->isChecked())
fileName = (dest + "/" + mAnimation->getFileBase() + "%1.png")
.arg(mPlayerWidget->getPos(), numLength, 10, QChar('0'));
if(saveView)
{
fileName = dest + "/" + mAnimation->getCurrentFileName();
}
else
{
fileName = dest + "/" + QFileInfo(mAnimation->getCurrentFileName()).completeBaseName() + ".png";
saveRet = mImage->save(fileName, "PNG"); //, int quality = -1 default normal (0..100)
PCritical(this, tr("PeTrack"), tr("Cannot save %1.").arg(fileName));
} while(mPlayerWidget->frameForward());
{
delete viewImage;
delete painter;
}
// bei abbruch koennen es auch mPlayerWidget->getPos() frames sein, die bisher geschrieben wurden
//-memPos nur, wenn nicht an den anfang gesprungen wird
SPDLOG_INFO("wrote {} of {} frames.", mPlayerWidget->getPos() + 1 - memPos, mAnimation->getNumFrames());
}
mPlayerWidget->skipToFrame(memPos);
lastDir = dest;
}
}
/**
* @brief Saves the current View, including visualizations, in a file (.g. pdf)
*
* @param dest name of the saved file; if empty, a dialogue for the user opens
*/
void Petrack::saveView(QString dest) // default = ""
{
static QString lastFile;
if(mImage)
{
// if no destination file or folder is given
if(lastFile.isEmpty() && !mSeqFileName.isEmpty())
// alle unetrstuetzen fileformate erhaelt man mit
// QImageReader::supportedImageFormats() and QImageWriter::supportedImageFormats()
// gif muss nicht dabei sein, dazu muss qt mit -qt-gif uebersetzt worden sein
dest = QFileDialog::getSaveFileName(
this,
tr("Select image file"),
lastFile,
tr("PDF (*.pdf);;Postscript (*.ps *.eps);;Windows bitmaps (*.bmp);;JPEG (*.jpeg *.jpg);;Portable "
"network graphics (*.png);;Portable image format (*.pbm *.pgm *.ppm);;X11 Bitmap or Pixmap (*.xbm "
"*.xpm);;Pixel Images (*.bmp *.jpeg *.jpg *.png *.pbm *.pgm *.ppm *.xbm *.xpm);;All supported types "
"(*pdf *ps *.eps *.bmp *.jpeg *.jpg *.png *.pbm *.pgm *.ppm *.xbm *.xpm);;All files (*.*)"));
if(dest.endsWith(".pdf", Qt::CaseInsensitive) || dest.endsWith(".ps", Qt::CaseInsensitive) ||
dest.endsWith(".eps", Qt::CaseInsensitive))
QPdfWriter pdfWriter(dest);
pdfWriter.setPageMargins({0, 0, 0, 0});
QPageSize pageSize{mImage->size()};
pdfWriter.setPageSize(pageSize);
QPainter painter(&pdfWriter);
if(mCropZoomViewAct->isChecked())
}
else
{
// schwarzer rand links und unten?!
QImage *img;
if(mCropZoomViewAct->isChecked())
img = new QImage(mView->viewport()->width(), mView->viewport()->height(), QImage::Format_RGB32);
img = new QImage((int) mScene->width(), (int) mScene->height(), QImage::Format_RGB32);
if(mCropZoomViewAct->isChecked())
PCritical(
this, tr("PeTrack"), tr("Cannot save %1 maybe because of wrong file extension.").arg(dest));
delete img;
}
lastFile = dest;
}
}
}
void Petrack::saveImage(QString dest) // default = ""
{
static QString lastFile;
if(mImage)
{
// if no destination file or folder is given
if(lastFile.isEmpty() && !mSeqFileName.isEmpty())
// alle unetrstuetzen fileformate erhaelt man mit
// QImageReader::supportedImageFormats() and QImageWriter::supportedImageFormats()
// gif muss nict dabei sein, dazu muss qt mit -qt-gif uebesetz worden sein
dest = QFileDialog::getSaveFileName(
this,
tr("Select image file"),
lastFile,
tr("PDF (*.pdf);;Postscript (*.ps *.eps);;Windows bitmaps (*.bmp);;JPEG (*.jpeg *.jpg);;Portable "
"network graphics (*.png);;Portable image format (*.pbm *.pgm *.ppm);;X11 Bitmap or Pixmap (*.xbm "
"*.xpm);;Pixel Images (*.bmp *.jpeg *.jpg *.png *.pbm *.pgm *.ppm *.xbm *.xpm);;All supported types "
"(*pdf *ps *.eps *.bmp *.jpeg *.jpg *.png *.pbm *.pgm *.ppm *.xbm *.xpm);;All files (*.*)"));
if(dest.endsWith(".pdf", Qt::CaseInsensitive) || dest.endsWith(".ps", Qt::CaseInsensitive) ||
dest.endsWith(".eps", Qt::CaseInsensitive))
QPdfWriter pdfWriter(dest);
pdfWriter.setPageMargins({0, 0, 0, 0});
QPageSize pageSize{mImage->size()};
pdfWriter.setPageSize(pageSize);
QPainter painter(&pdfWriter);
QRect rect = painter.viewport();
QSize size = mImage->size();
size.scale(rect.size(), Qt::KeepAspectRatio);
painter.setViewport(rect.x(), rect.y(), size.width(), size.height());
painter.setWindow(mImage->rect());
painter.drawImage(0, 0, *mImage);
}
else
{
if(!mImage->save(dest)) //, "PNG"
PCritical(
this, tr("PeTrack"), tr("Cannot save %1 maybe because of wrong file extension.").arg(dest));
}
lastFile = dest;
}
}
}
void Petrack::print()
{
if(mImage)
{
// HighResolution font zu gross! und laengere laufzeit und eher overflow
// aber so pixelig und keine schoenen linien
QPrinter printer(QPrinter::ScreenResolution); // ScreenResolution, HighResolution// liefert zu hause:
// QWin32PrintEngine::initialize: GetPrinter failed ()
printer.setPageSize(QPageSize{QPageSize::PageSizeId::A4});
QPainter painter(&printer);
mView->render(&painter);
}
}
else
PCritical(this, tr("PeTrack"), tr("Nothing to print!"));
}
void Petrack::resetSettings()
{
mAnimation->reset();
openXml(mDefaultSettings, false);
mControlWidget->resetCorrection();
auto about = new AboutDialog(
this,
mPetrackVersion,
mGitCommitID,
mGitCommitDate,
mGitCommitBranch,
mCompilerID,
mCompilerVersion,
mCompileDate,
mAuthors);
about->show();
PMessageBox *mb = new PMessageBox{
this,
tr("Command line options"),
commandLineOptionsString,
QIcon(),
QString(),
PMessageBox::StandardButton::Yes};
mb->setModal(false);
mb->show();
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
QString ctrlSign = (mCompileOS != "Darwin") ? "⌃ Ctrl" : "⌘ Cmd";
QString shiftSign = "⇧ Shift";
QString altSign = (mCompileOS != "Darwin") ? "⎇ Alt" : "⌥ Option";
QString arrowUp = "Arrow up ↑";
QString arrowDown = "Arrow down ↓";
QString arrowLeft = "Arrow left ←";
QString arrowRight = "Arrow right →";
const QString out =
tr("<p>Beside the space bar all bindings only affect inside the image.</p>"
"<dl><dt><kbd>Space bar</kbd></dt><dd>toggles between pause and last play direction</dd>"
"<dt><kbd>Mouse scroll wheel</kbd></dt><dd>zooms in and out to or from the pixel of the image at the "
"position "
"of the mouse pointer</dd>"
"<dt><kbd>%2 + mouse scroll wheel</kbd></dt><dd>plays forwards or backwards frame by frame</dd>"
"<dt><kbd>Holding left mouse button</kbd></dt><dd>moves image</dd>"
"<dt><kbd>%4/%5</kbd></dt><dd>zoom in/out</dd>"
"<dt><kbd>%6/%7</kbd></dt><dd>frame back/forward</dd>"
"<dt><kbd>Double-click left mouse button</kbd></dt><dd>opens video or image sequence</dd>"
"<dt><kbd>%1 + double-click left mouse button</kbd></dt><dd>inserts new or moves near trackpoint</dd>"
"<dt><kbd>%1 + %2 + double-click left mouse button</kbd></dt><dd>splits near trackpoint before current "
"frame</dd>"
"<dt><kbd>%1 + double-click right mouse button</kbd></dt><dd>deletes a trajectory of a near trackpoint</dd>"
"<dt><kbd>%2 + double-click right mouse button</kbd></dt><dd>deletes the past part of a trajectory of a "
"near trackpoint</dd>"
"<dt><kbd>%3 + double-click right mouse button</kbd></dt><dd>deletes the future part of a trajectory of a "
"near trackpoint</dd>"
"<dt><kbd>%1 + double-click middle mouse button</kbd></dt><dd>deletes all trajectories</dd>"
"<dt><kbd>%2 + double-click middle mouse button</kbd></dt><dd>deletes the past part of all trajectories</dd>"
"<dt><kbd>%3 + double-click middle mouse button</kbd></dt><dd>deletes the future part of all "
"trajectories</dd>"
"<dt><kbd>%2 + t</kbd></dt><dd>toggles tracking online calculation</dd>"
"<dt><kbd>%2 + double-click left mouse button</kbd></dt><dd>inserts new or moves near trackpoint and "
"enables showing only the modified trajectory</dd>"
"<dt><kbd>%1 + %3 + double-click left mouse button</kbd></dt><dd>jumps to frame of trackpoint under "
"cursor</dd>"
"<dt><kbd>%3 + holding left mouse button</kbd></dt><dd>moves trackpoint under cursor</dd>"
"<dt><kbd>%1 + e</kbd></dt><dd>export trajectories</dd>"
"<dt><kbd>%1 + mouse scroll wheel</kbd></dt><dd>change the displayed person (if show only people "
"enabled)</dd></dl>"
"<p>Further key bindings you will find next to the entries of the menus.</p>")
.arg(ctrlSign)
.arg(shiftSign)
.arg(altSign)
.arg(arrowUp)
.arg(arrowDown)
.arg(arrowLeft)
.arg(arrowRight);
PMessageBox *mb =
new PMessageBox(this, tr("Key Bindings"), out, QIcon(), QString(), PMessageBox::StandardButton::Yes);
mb->setModal(false);
mb->show();
}
void Petrack::onlineHelp()
{
static QUrl url("https://jugit.fz-juelich.de/ped-dyn-emp/petrack/-/wikis/home");
if(!(QDesktopServices::openUrl(url)))
PCritical(this, tr("PeTrack"), tr("Cannot open external browser<br>with url ") + url.toString() + "!");
}
void Petrack::antialias()
{
mView->setRenderHint(QPainter::Antialiasing, mAntialiasAct->isChecked());
}
void Petrack::opengl()
{
mView->setViewport(mOpenGLAct->isChecked() ? new QGLWidget(QGLFormat(QGL::SampleBuffers)) : new QWidget);
// need full viewport update for fade out animation of LogoItem to work
mView->setViewportUpdateMode(QGraphicsView::FullViewportUpdate);
}
void Petrack::reset()
{
mViewWidget->resetView();
}
void Petrack::fitInView()
{
mViewWidget->fitInView();
}
void Petrack::fitInROI()
{
mViewWidget->fitInROI(getRecoRoiItem()->rect());
// what about trackingRoi???
}
void Petrack::setGlobalFont()
{
this->setFont(font); // font is set to the font the user selected
// the user canceled the dialog; font is set to the initial
// value, in this case Times, 12.
}
}
void Petrack::showHideControlWidget()
{
// show | hide Control
mViewWidget->hideControls(mHideControlsAct->isChecked());
}
void Petrack::showLogWindow()
{
mLogWindow->show();
}
#ifndef STEREO_DISABLED
if(mCameraLeftViewAct->isChecked())
if((mAnimation->getCamera()) != cameraLeft)
mAnimation->setCamera(cameraLeft); // war: hier wird direkt bei Umstellung neu gelesen
else
return;
}
else if(mCameraRightViewAct->isChecked())
if((mAnimation->getCamera()) != cameraRight)
mAnimation->setCamera(cameraRight); // war: hier wird direkt bei Umstellung neu gelesen
else
return;
}
else // kann eigentlich nicht vorkommen
{
mAnimation->setCamera(cameraUnset);
return;
}
updateImage(mAnimation->getFrameAtIndex(
mAnimation->getCurrentFrameNum())); // wird nur aufgerufen, wenn left / right sich geaendert hat
// mPlayerWidget->updateImage();
// mPlayerWidget->skipToFrame(mPlayerWidget->getPos()); // machtpasue!!
// updateImage(true); // nur dies aufrufen, wenn nicht links rechts gleichzeitig gehalten wird
/**
* @brief Helper function to create Actions for the menu bar
* @see Petrack::createMenus()
*/
void Petrack::createActions()
{
mOpenSeqAct = new QAction(tr("&Open Sequence"), this);
mOpenSeqAct->setShortcut(tr("Ctrl+Shift+O"));
connect(mOpenSeqAct, SIGNAL(triggered()), this, SLOT(openSequence()));
mOpenCameraAct = new QAction(tr("Open Camera Stream"), this);
// mOpenCameraAct->setShortcut(tr("Ctrl+C")); // because of some reason it is sometimes fired with
// Ctrl+LeftMouseButton ==> so disabled (it's also not really needed)
connect(mOpenCameraAct, SIGNAL(triggered()), this, SLOT(openCameraLiveStream()));
mOpenMoCapAct = new QAction(tr("Manage MoCap Files"), this);
connect(mOpenMoCapAct, &QAction::triggered, this, &Petrack::openMoCapFile);
mEditMoCapAct = new QAction(tr("Edit MoCap Settings"), this);
connect(mEditMoCapAct, &QAction::triggered, this, &Petrack::editMoCapSettings);
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
mSaveSeqVidAct = new QAction(tr("Save Video"), this);
mSaveSeqVidAct->setEnabled(false);
connect(mSaveSeqVidAct, SIGNAL(triggered()), this, SLOT(saveVideo()));
mSaveSeqVidViewAct = new QAction(tr("Save Video View"), this);
mSaveSeqVidViewAct->setEnabled(false);
connect(mSaveSeqVidViewAct, SIGNAL(triggered()), this, SLOT(saveVideoView()));
mSaveSeqImgAct = new QAction(tr("Save Image S&equence"), this);
mSaveSeqImgAct->setShortcut(tr("Ctrl+F"));
mSaveSeqImgAct->setEnabled(false);
connect(mSaveSeqImgAct, SIGNAL(triggered()), this, SLOT(saveImageSequence()));
mSaveSeqViewAct = new QAction(tr("Save View S&equence"), this);
// mSaveSeqViewAct->setShortcut(tr("Ctrl+F"));
mSaveSeqViewAct->setEnabled(false);
connect(mSaveSeqViewAct, SIGNAL(triggered()), this, SLOT(saveViewSequence()));
mOpenPrAct = new QAction(tr("&Open Project"), this);
mOpenPrAct->setShortcut(tr("Ctrl+O"));
connect(mOpenPrAct, SIGNAL(triggered()), this, SLOT(openProject()));
mSavePrAct = new QAction(tr("&Save Project As"), this);
mSavePrAct->setShortcut(tr("Ctrl+Shift+S"));
connect(mSavePrAct, &QAction::triggered, this, &Petrack::saveProjectAs);
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
mSaveAct = new QAction(tr("&Save Project"), this);
mSaveAct->setShortcut(tr("Ctrl+S"));
connect(mSaveAct, SIGNAL(triggered()), this, SLOT(saveSameProject()));
mSaveImageAct = new QAction(tr("&Save Image"), this);
mSaveImageAct->setShortcut(tr("Ctrl+I"));
mSaveImageAct->setEnabled(false);
connect(mSaveImageAct, SIGNAL(triggered()), this, SLOT(saveImage()));
mSaveViewAct = new QAction(tr("&Save View"), this);
mSaveViewAct->setShortcut(tr("Ctrl+V"));
mSaveViewAct->setEnabled(false);
connect(mSaveViewAct, SIGNAL(triggered()), this, SLOT(saveView()));
mPrintAct = new QAction(tr("&Print"), this);
mPrintAct->setShortcut(tr("Ctrl+P"));
mPrintAct->setEnabled(false);
connect(mPrintAct, SIGNAL(triggered()), this, SLOT(print()));
mResetSettingsAct = new QAction(tr("&Reset Settings"), this);
// mResetSettingsAct->setShortcut(tr("Ctrl+R"));
mResetSettingsAct->setEnabled(false); // da es noch nicht fehlerfrei funktioniert
connect(mResetSettingsAct, SIGNAL(triggered()), this, SLOT(resetSettings()));
mAutosaveSettings = new QAction(tr("Autosave Settings"), this);
connect(mAutosaveSettings, &QAction::triggered, this, &Petrack::openAutosaveSettings);
mExitAct = new QAction(tr("E&xit"), this);
mExitAct->setShortcut(tr("Ctrl+Q"));
connect(mExitAct, SIGNAL(triggered()), this, SLOT(close()));
mAntialiasAct = new QAction(tr("&Antialias"), this);
mAntialiasAct->setShortcut(tr("Ctrl+A"));
mAntialiasAct->setCheckable(true);
connect(mAntialiasAct, SIGNAL(triggered()), this, SLOT(antialias()));
mFontAct = new QAction(tr("&Font"), this);
connect(mFontAct, SIGNAL(triggered()), this, SLOT(setGlobalFont()));
mHideControlsAct = new QAction(tr("&Hide controls"), this);
mHideControlsAct->setShortcut(tr("Ctrl+H"));
mHideControlsAct->setCheckable(true);
connect(mHideControlsAct, SIGNAL(triggered()), this, SLOT(showHideControlWidget()));
connect(mHideControlsAct, SIGNAL(changed()), this, SLOT(showHideControlWidget()));
mShowLogWindowAct = new QAction(tr("&Show log window"), this);
connect(mShowLogWindowAct, &QAction::triggered, this, &Petrack::showLogWindow);
mCropZoomViewAct = new QAction(tr("&Transform while saving"), this); // Crop and zoom while saving
mCropZoomViewAct->setCheckable(true);
mOpenGLAct = new QAction(tr("Open&GL"), this);
mOpenGLAct->setShortcut(tr("Ctrl+G"));
mOpenGLAct->setCheckable(true);
connect(mOpenGLAct, SIGNAL(triggered()), this, SLOT(opengl()));
mResetAct = new QAction(tr("&Reset"), this);
mResetAct->setShortcut(tr("Ctrl+R"));
connect(mResetAct, SIGNAL(triggered()), this, SLOT(reset()));
mFitViewAct =
new QAction(tr("Fit in window"), this); // Resize to window; fit in view; show all; in fenster einpassen
mFitViewAct->setShortcut(tr("Ctrl+0"));
connect(mFitViewAct, SIGNAL(triggered()), this, SLOT(fitInView()));
mFitROIAct = new QAction(tr("Fit in region of interest"), this); // Resize ROI to window; fit in view;
mFitROIAct->setShortcut(tr("Ctrl+1"));
connect(mFitROIAct, SIGNAL(triggered()), this, SLOT(fitInROI()));
mCameraGroupView = new QActionGroup(this);
// mCameraGroupView->addAction(mCameraLeftViewAct);
// mCameraGroupView->addAction(mCameraRightViewAct);
mCameraLeftViewAct = new QAction(tr("&Left"), mCameraGroupView);
mCameraLeftViewAct->setShortcut(tr("Ctrl++Shift+L"));
mCameraLeftViewAct->setCheckable(true);
connect(mCameraLeftViewAct, SIGNAL(triggered()), this, SLOT(setCamera()));
mCameraRightViewAct = new QAction(tr("&Right"), mCameraGroupView);
mCameraRightViewAct->setShortcut(tr("Ctrl++Shift+R"));
mCameraRightViewAct->setCheckable(true);
connect(mCameraRightViewAct, SIGNAL(triggered()), this, SLOT(setCamera()));
mCameraRightViewAct->setChecked(true); // right wird als default genommen, da reference image in triclops auch right
// ist // erste trj wurden mit left gerechnet
mLimitPlaybackSpeed = new QAction(tr("&Limit playback speed"));
// Not checkable like Fix since this is also controlled through clicking on FPS and syncing currently would be
// bothersome
connect(
mLimitPlaybackSpeed,
&QAction::triggered,
mPlayerWidget,
[&]() { mPlayerWidget->setPlayerSpeedLimited(!mPlayerWidget->getPlayerSpeedLimited()); });
mFixPlaybackSpeed = new QAction(tr("&Fix playback speed"));
mFixPlaybackSpeed->setCheckable(true);
connect(mFixPlaybackSpeed, &QAction::toggled, mPlayerWidget, &Player::setPlayerSpeedFixed);
mSetToRealtime = new QAction(tr("&Realtime"));
connect(
mSetToRealtime, &QAction::triggered, mPlayerWidget, [&]() { mPlayerWidget->setSpeedRelativeToRealtime(1.0); });
connect(mSetTo2p00, &QAction::triggered, mPlayerWidget, [&]() { mPlayerWidget->setSpeedRelativeToRealtime(2.0); });
connect(mSetTo1p75, &QAction::triggered, mPlayerWidget, [&]() { mPlayerWidget->setSpeedRelativeToRealtime(1.75); });
connect(mSetTo1p50, &QAction::triggered, mPlayerWidget, [&]() { mPlayerWidget->setSpeedRelativeToRealtime(1.5); });
connect(mSetTo1p25, &QAction::triggered, mPlayerWidget, [&]() { mPlayerWidget->setSpeedRelativeToRealtime(1.25); });
connect(mSetTo0p75, &QAction::triggered, mPlayerWidget, [&]() { mPlayerWidget->setSpeedRelativeToRealtime(0.75); });
connect(mSetTo0p50, &QAction::triggered, mPlayerWidget, [&]() { mPlayerWidget->setSpeedRelativeToRealtime(0.5); });
connect(mSetTo0p25, &QAction::triggered, mPlayerWidget, [&]() { mPlayerWidget->setSpeedRelativeToRealtime(0.25); });
mPlayerLooping = new QAction(tr("&Loop"));
mPlayerLooping->setCheckable(true);
connect(mPlayerLooping, &QAction::triggered, mPlayerWidget, &Player::setLooping);
// -------------------------------------------------------------------------------------------------------
mDelPastAct = new QAction(tr("&Past part of all trj."), this);
connect(
mDelPastAct,
&QAction::triggered,
this,
[this]() { this->deleteTrackPointAll(PersonStorage::Direction::Previous); });
mDelFutureAct = new QAction(tr("&Future part of all trj."), this);
connect(
mDelFutureAct,
&QAction::triggered,
this,
[this]() { this->deleteTrackPointAll(PersonStorage::Direction::Following); });
mDelAllRoiAct = new QAction(tr("&Trj. moving through ROI"), this);
connect(mDelAllRoiAct, &QAction::triggered, this, &Petrack::deleteTrackPointROI);
mDelPartRoiAct = new QAction(tr("Part of Trj. inside &ROI"), this);
connect(mDelPartRoiAct, &QAction::triggered, this, &Petrack::deleteTrackPointInsideROI);
// -------------------------------------------------------------------------------------------------------
mCommandAct = new QAction(tr("&Command line options"), this);
connect(mCommandAct, SIGNAL(triggered()), this, SLOT(commandLineOptions()));
mKeyAct = new QAction(tr("&Key bindings"), this);
connect(mKeyAct, SIGNAL(triggered()), this, SLOT(keyBindings()));
mAboutAct = new QAction(tr("&About"), this);
connect(mAboutAct, SIGNAL(triggered()), this, SLOT(about()));
mOnlineHelpAct = new QAction(tr("Online &Help"), this);
mOnlineHelpAct->setShortcut(tr("Ctrl+H"));
connect(mOnlineHelpAct, SIGNAL(triggered()), this, SLOT(onlineHelp()));
}
/**
* @brief Helper function building menues out of QActions
* @see Petrack::createActions()
*/
void Petrack::createMenus()
{
mFileMenu = new QMenu(tr("&File"), this);
mFileMenu->addAction(mOpenPrAct);
mFileMenu->addAction(mSaveAct);
mFileMenu->addAction(mSavePrAct);
mFileMenu->addSeparator();
mFileMenu->addAction(mOpenSeqAct);
mFileMenu->addAction(mOpenCameraAct);
mFileMenu->addAction(mEditMoCapAct);
mFileMenu->addAction(mSaveSeqVidAct);
mFileMenu->addAction(mSaveSeqVidViewAct);
mFileMenu->addAction(mSaveImageAct);
mFileMenu->addAction(mSaveSeqImgAct);
mFileMenu->addAction(mSaveViewAct);
mFileMenu->addAction(mSaveSeqViewAct);
mFileMenu->addAction(mPrintAct);
mFileMenu->addSeparator();
mFileMenu->addAction(mResetSettingsAct);
mFileMenu->addAction(mAutosaveSettings);
mFileMenu->addSeparator();
mFileMenu->addAction(mExitAct);
mViewMenu = new QMenu(tr("&View"), this);
mViewMenu->addAction(mAntialiasAct);
mViewMenu->addAction(mOpenGLAct);
mViewMenu->addAction(mCropZoomViewAct);
mCameraMenu = mViewMenu->addMenu(tr("&Camera"));
mCameraMenu->addAction(mCameraLeftViewAct);
mCameraMenu->addAction(mCameraRightViewAct);
mViewMenu->addAction(mFixPlaybackSpeed);
mViewMenu->addAction(mLimitPlaybackSpeed);
mPlaybackSpeedMenu = mViewMenu->addMenu(tr("&Playback speed"));
mPlaybackSpeedMenu->addAction(mSetToRealtime);
mPlaybackSpeedMenu->addAction(mSetTo2p00);
mPlaybackSpeedMenu->addAction(mSetTo1p75);
mPlaybackSpeedMenu->addAction(mSetTo1p50);
mPlaybackSpeedMenu->addAction(mSetTo1p25);
mPlaybackSpeedMenu->addAction(mSetTo0p75);
mPlaybackSpeedMenu->addAction(mSetTo0p50);
mPlaybackSpeedMenu->addAction(mSetTo0p25);
mViewMenu->addSeparator();
mViewMenu->addAction(mFitViewAct);
mViewMenu->addAction(mFitROIAct);
mViewMenu->addAction(mResetAct);
mViewMenu->addSeparator();
mViewMenu->addAction(mFontAct);
mViewMenu->addSeparator();
mViewMenu->addAction(mHideControlsAct);
mViewMenu->addSeparator();
mViewMenu->addAction(mShowLogWindowAct);
2126
2127
2128
2129
2130
2131
2132
2133
2134
2135
2136
2137
2138
2139
2140
2141
2142
2143
2144
2145
2146
mDeleteMenu = new QMenu(tr("&Delete"), this);
mDeleteMenu->addAction(mDelPastAct);
mDeleteMenu->addAction(mDelFutureAct);
mDeleteMenu->addAction(mDelAllRoiAct);
mDeleteMenu->addAction(mDelPartRoiAct);
mHelpMenu = new QMenu(tr("&Help"), this);
mHelpMenu->addAction(mCommandAct);
mHelpMenu->addAction(mKeyAct);
mHelpMenu->addAction(mAboutAct);
mHelpMenu->addAction(mOnlineHelpAct);
menuBar()->addMenu(mFileMenu);
menuBar()->addMenu(mViewMenu);
menuBar()->addMenu(mDeleteMenu);
menuBar()->addMenu(mHelpMenu);
mCameraMenu->setEnabled(false);
}
/**
* @brief Helper function to create status bar at the bottom of the window
*/
QFont f("Courier", 12, QFont::Bold); // Times Helvetica, Normal
statusBar()->setMaximumHeight(28);
statusBar()->showMessage(tr("Ready"));
statusBar()->addPermanentWidget(mStatusLabelStereo = new QLabel(" "));
statusBar()->addPermanentWidget(mStatusLabelTime = new QLabel(" "));
statusBar()->addPermanentWidget(mStatusLabelFPS = new QLabel(" "));
statusBar()->addPermanentWidget(mStatusPosRealHeight = new QDoubleSpinBox());
connect(mStatusPosRealHeight, SIGNAL(valueChanged(double)), this, SLOT(setStatusPosReal()));
statusBar()->addPermanentWidget(mStatusLabelPosReal = new QLabel(" "));
statusBar()->addPermanentWidget(mStatusLabelPos = new QLabel(" "));
statusBar()->addPermanentWidget(mStatusLabelColor = new QLabel(" "));
mStatusLabelStereo->setFont(f);
mStatusLabelStereo->setMinimumWidth(200);
mStatusLabelTime->setFont(f);
mStatusLabelTime->setMinimumWidth(200);
mStatusLabelFPS->setFont(f);
mStatusLabelFPS->setMinimumWidth(80);
mStatusLabelFPS->setAutoFillBackground(true);
mStatusLabelFPS->setToolTip("Click to adapt play rate to fps rate");
mStatusPosRealHeight->setRange(-999.9, 9999.9); // in cm
mStatusPosRealHeight->setDecimals(1);
mStatusPosRealHeight->setFont(f);
mStatusLabelPosReal->setFont(f);
mStatusLabelPosReal->setMinimumWidth(340);
mStatusLabelPos->setFont(f);
mStatusLabelPos->setMinimumWidth(100);
mStatusLabelColor->setFont(f);
mStatusLabelColor->setMinimumWidth(90);
mStatusLabelColor->setAutoFillBackground(true);
}
void Petrack::resetUI()
{
/// Reset all UI elements to default settings
/// Noetig damit alle UI Elemente, welche in der neu geladenen Projekt-Datei z.B. noch nicht vorhanden sind, auf
/// sinnvolle Werte gesetzt werden. Anderenfalls kommt es evtl. beim nacheinander laden verschiedener Projekte zu
/// einem Programmabsturz
///
return;
}
void Petrack::setStatusStereo(float x, float y, float z)
{
Loading
Loading full blame...