Skip to content
Snippets Groups Projects
petrack.cpp 153 KiB
Newer Older
  • Learn to ignore specific revisions
  • /*
     * 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/>.
     */
    
    
    d.kilic's avatar
    d.kilic committed
    #include <QSignalMapper>
    
    #include <QtOpenGL>
    #include <QtWidgets>
    
    d.kilic's avatar
    d.kilic committed
    
    // Added for Qt5 support
    
    #include "aboutDialog.h"
    #include "animation.h"
    #include "autoCalib.h"
    #include "backgroundItem.h"
    #include "calibFilter.h"
    #include "codeMarkerWidget.h"
    
    d.kilic's avatar
    d.kilic committed
    #include "colorMarkerItem.h"
    #include "colorMarkerWidget.h"
    
    #include "colorRangeWidget.h"
    
    d.kilic's avatar
    d.kilic committed
    #include "control.h"
    #include "gridItem.h"
    
    d.kilic's avatar
    d.kilic committed
    #include "imageItem.h"
    #include "logoItem.h"
    
    #include "moCapItem.h"
    
    #include "multiColorMarkerItem.h"
    #include "multiColorMarkerWidget.h"
    #include "openMoCapDialog.h"
    #include "pMessageBox.h"
    #include "petrack.h"
    #include "player.h"
    
    #include "stereoItem.h"
    #include "stereoWidget.h"
    
    d.kilic's avatar
    d.kilic committed
    #include "tracker.h"
    
    #include "trackerItem.h"
    
    d.kilic's avatar
    d.kilic committed
    #include "trackerReal.h"
    
    #include "view.h"
    
    #include <QtPrintSupport/QPrintDialog>
    #include <QtPrintSupport/QPrinter>
    
    d.kilic's avatar
    d.kilic committed
    #ifdef AVI
    #include "aviFile.h"
    #else
    #include "aviFileWriter.h"
    #endif
    
    #include "IO.h"
    
    d.kilic's avatar
    d.kilic committed
    #include "person.h"
    
    
    #include <cmath>
    
    Schrödter, Tobias's avatar
    Schrödter, Tobias committed
    #include <ctime>
    
    d.kilic's avatar
    d.kilic committed
    #include <iomanip>
    
    Schrödter, Tobias's avatar
    Schrödter, Tobias committed
    #include <opencv2/opencv.hpp>
    
    d.kilic's avatar
    d.kilic committed
    
    
    // temp muss spaeter herausgenommen werden,
    //  dient dazu, in anderen dateien um schnell ueber cw->temp1->value() an einstellbare daten zu kommen
    
    d.kilic's avatar
    d.kilic committed
    Control *cw;
    
    int Petrack::trcVersion = 0;
    
    // Reihenfolge des anlegens der objekte ist sehr wichtig
    
    Petrack::Petrack() :
        mExtrCalibration(mPersonStorage),
        mAuthors(IO::readAuthors(QCoreApplication::applicationDirPath() + "/.zenodo.json"))
    
    d.kilic's avatar
    d.kilic committed
    {
        QIcon icon;
    
        icon.addFile(":/icon");          // about
    
    d.kilic's avatar
    d.kilic committed
        icon.addFile(":/icon_smallest"); // window title bar
    
        setWindowIcon(icon);
    
        mHeadSize            = -1;
        mCmPerPixel          = -1;
        mScene               = nullptr;
        mTracker             = nullptr;
        mTrackerReal         = nullptr; // damit beim zeichnen von control mit analysePlot nicht auf einen feheler laeuft
        mStatusLabelFPS      = nullptr;
    
        mStatusPosRealHeight = nullptr;
    
        mStatusLabelPosReal  = nullptr;
        mImageItem           = nullptr;
        mRecognitionChanged  = true;
        mTrackChanged        = true;
        mCoordItem           = nullptr;
        mImage               = nullptr;
    
    d.kilic's avatar
    d.kilic committed
        setLoading(true);
    
        setAcceptDrops(true);
    
        int space = 2;
    
        mBrightContrastFilter.disable();
        mBorderFilter.disable();
        mSwapFilter.disable();
        mBackgroundFilter.disable();
    
        mStereoContext = nullptr;
    
        mCalibFilter   = new CalibFilter; // schoener waere erst zu erzeugen, wenn video geladen wird, da sonst bei stereo
                                          // erst normealer und dann stereo objekt erzeugt wird
        mCalibFilter->disable(); // aber control widget greift schon bei erzeugung auf alle objekte zur einstellung zurueck
    
    d.kilic's avatar
    d.kilic committed
    
    
        mScene = new QGraphicsScene(this);
    
    
        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
    
        mControlWidget = new Control(*this, *mScene, mReco, *mTrackingRoiItem, *mRecognitionRoiItem);
    
        cw             = mControlWidget; // muss spaeter geloescht werden
    
    d.kilic's avatar
    d.kilic committed
    
        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);
    
    d.kilic's avatar
    d.kilic committed
        mCodeMarkerWidget->setWindowFlags(Qt::Window);
        mCodeMarkerWidget->setWindowTitle("Code marker parameter");
    
    
        mMultiColorMarkerWidget = new MultiColorMarkerWidget(this);
        mMultiColorMarkerWidget->setWindowFlags(Qt::Window);
        mMultiColorMarkerWidget->setWindowTitle("MultiColor marker parameter");
    
        mImageItem = new ImageItem(this); // durch uebergabe von scene wird indirekt ein scene->addItem() aufgerufen
    
    d.kilic's avatar
    d.kilic committed
    
        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
    
    d.kilic's avatar
    d.kilic committed
    
        mExtrCalibration.setMainWindow(this);
    
        mGridItem = new GridItem(this);
        mGridItem->setZValue(2.5); // durch uebergabe von scene wird indirekt ein scene->addItem() aufgerufen
    
        mCoordItem = new CoordItem(this);
        mCoordItem->setZValue(3); // groesser heisst weiter oben
        mImageItem->setCoordItem(mCoordItem);
    
        mViewWidget = new ViewWidget(this);
    
        mView       = mViewWidget->view();
    
    d.kilic's avatar
    d.kilic committed
        mView->setScene(mScene);
    
        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::mouseAltDoubleClick, this, &Petrack::skipToFrameFromTrajectory);
    
    d.kilic's avatar
    d.kilic committed
    
        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);
    
    d.kilic's avatar
    d.kilic committed
        mTrackerItem->setZValue(5); // groesser heisst weiter oben
    
    
        mControlWidget->getColorPlot()->setPersonStorage(&mPersonStorage);
    
    d.kilic's avatar
    d.kilic committed
    #ifdef QWT
        mControlWidget->getAnalysePlot()->setTrackerReal(mTrackerReal);
    #endif
    
        //---------------------------
    
        mStereoItem = new StereoItem(this);
    
        mStereoItem->setZValue(2); // groesser heisst weiter oben
    
    d.kilic's avatar
    d.kilic committed
        mStereoItem->setVisible(false);
    
        //---------------------------
    
        mColorMarkerItem = new ColorMarkerItem(this);
    
        mColorMarkerItem->setZValue(2); // groesser heisst weiter oben
    
    d.kilic's avatar
    d.kilic committed
        mColorMarkerItem->setVisible(false);
        //---------------------------
    
    
        mCodeMarkerItem = new CodeMarkerItem(this, mReco.getCodeMarkerOptions());
    
        mCodeMarkerItem->setZValue(2); // groesser heisst weiter oben
    
    d.kilic's avatar
    d.kilic committed
        mCodeMarkerItem->setVisible(false);
        //---------------------------
    
        mMultiColorMarkerItem = new MultiColorMarkerItem(this);
    
        mMultiColorMarkerItem->setZValue(2); // groesser heisst weiter oben
    
    d.kilic's avatar
    d.kilic committed
        mMultiColorMarkerItem->setVisible(false);
    
        //---------------------------
    
        mBackgroundItem = new BackgroundItem(this);
        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
    
    
    d.kilic's avatar
    d.kilic committed
        /// 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);
    
        mScene->addItem(mMoCapItem);
    
    d.kilic's avatar
    d.kilic committed
    
        //---------------------------
    
        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);
    
    
    
    d.kilic's avatar
    d.kilic committed
        setWindowTitle(tr("PeTrack"));
    
        //---------------------------
        mAutoCalib.setMainWindow(this);
        //---------------------------
    
        createActions();
        createMenus();
        createStatusBar();
    
    
        // 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
    
    d.kilic's avatar
    d.kilic committed
        readSettings();
    
    
        saveXml(mDefaultSettings); // noch nicht noetig, da eh noch nicht fkt
    
    d.kilic's avatar
    d.kilic committed
    
        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
    
    d.kilic's avatar
    d.kilic committed
        mAutoTrackOptimizeColor = false; // ist der default, dann wenn in XML-Datei nicht drin steht
    
        setLoading(false);
    }
    Petrack::~Petrack()
    {
        delete mImage;
        delete cw;
        // 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)
    {
    
        if(event->mimeData()->hasUrls())
    
    d.kilic's avatar
    d.kilic committed
            event->acceptProposedAction();
    
    d.kilic's avatar
    d.kilic committed
    }
    
    
    /**
     * @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
     */
    
    d.kilic's avatar
    d.kilic committed
    void Petrack::dropEvent(QDropEvent *event)
    {
    
        if(event->mimeData()->hasUrls())
    
    d.kilic's avatar
    d.kilic committed
        {
    
            if(event->mimeData()->urls().first().toLocalFile().right(4) == ".pet")
    
    d.kilic's avatar
    d.kilic committed
                openProject(event->mimeData()->urls().first().toLocalFile());
    
            else if(event->mimeData()->urls().first().toLocalFile().right(4) == ".trc")
    
    d.kilic's avatar
    d.kilic committed
                importTracker(event->mimeData()->urls().first().toLocalFile());
    
    d.kilic's avatar
    d.kilic committed
            else
    
    d.kilic's avatar
    d.kilic committed
                openSequence(event->mimeData()->urls().first().toLocalFile());
    
    d.kilic's avatar
    d.kilic committed
            event->acceptProposedAction();
        }
    }
    
    void Petrack::updateSceneRect()
    {
    
        double iW = 0, iH = 0, bS = 0;
    
    d.kilic's avatar
    d.kilic committed
    
    
    d.kilic's avatar
    d.kilic committed
        {
            iW = mImage->width();
            iH = mImage->height();
            bS = getImageBorderSize();
        }
    
    
        if(mControlWidget->getCalibCoordShow())
    
    d.kilic's avatar
    d.kilic committed
        {
    
            double scale = mControlWidget->getCalibCoordScale() / 10.;
            double tX    = mControlWidget->getCalibCoordTransX() / 10.;
            double tY    = mControlWidget->getCalibCoordTransY() / 10.;
    
    d.kilic's avatar
    d.kilic committed
    
            // setzen der bounding box der scene
            // Faktor 1.1 dient dazu, dass auch Zahl "1" bei coord gut in sichtbaren Bereich passt
    
            double xMin = (tX - 1.1 * scale < -bS) ? tX - 1.1 * scale : -bS;
            double yMin = (tY - 1.1 * scale < -bS) ? tY - 1.1 * scale : -bS;
            double xMax = (tX + 1.1 * scale > iW - bS) ? tX + 1.1 * scale : iW - bS;
            double yMax = (tY + 1.1 * scale > iH - bS) ? tY + 1.1 * scale : iH - bS;
            mScene->setSceneRect(xMin, yMin, xMax - xMin, yMax - yMin);
    
    d.kilic's avatar
    d.kilic committed
        }
        else
    
    d.kilic's avatar
    d.kilic committed
            mScene->setSceneRect(-bS, -bS, iW, iH);
    
    d.kilic's avatar
    d.kilic committed
    }
    
    
    /**
     * @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
     */
    
    d.kilic's avatar
    d.kilic committed
    void Petrack::openXml(QDomDocument &doc, bool openSeq)
    {
        QDomElement root = doc.firstChildElement("PETRACK");
    
        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;
    
    d.kilic's avatar
    d.kilic committed
        enum Camera cam = cameraUnset;
        setLoading(true);
        for(QDomElement elem = root.firstChildElement(); !elem.isNull(); elem = elem.nextSiblingElement())
        {
    
            if(elem.tagName() == "MAIN")
    
    d.kilic's avatar
    d.kilic committed
            {
    
                if(elem.hasAttribute("SRC"))
    
    d.kilic's avatar
    d.kilic committed
                {
    
                    seq            = elem.attribute("SRC");
    
    d.kilic's avatar
    d.kilic committed
                    QString tmpSeq = getExistingFile(seq, mProFileName);
    
                    if(tmpSeq != "")
    
    d.kilic's avatar
    d.kilic committed
                        seq = tmpSeq;
    
    d.kilic's avatar
    d.kilic committed
                }
    
                if(elem.hasAttribute("STATUS_HEIGHT"))
    
    d.kilic's avatar
    d.kilic committed
                {
    
                    if(mStatusPosRealHeight) // null kann eigentlich nicht vorkommen, da im constructor von petrack erzeugt
                                             // wird
    
    d.kilic's avatar
    d.kilic committed
                        mStatusPosRealHeight->setValue(elem.attribute("STATUS_HEIGHT").toDouble());
    
    d.kilic's avatar
    d.kilic committed
                }
            }
    
            else if(elem.tagName() == "STEREO")
    
    d.kilic's avatar
    d.kilic committed
            {
                mStereoWidget->getXml(elem);
            }
    
            else if(elem.tagName() == "COLOR_MARKER")
    
    d.kilic's avatar
    d.kilic committed
            {
                mColorMarkerWidget->getXml(elem);
            }
    
            else if(elem.tagName() == "CODE_MARKER")
    
    d.kilic's avatar
    d.kilic committed
            {
                mCodeMarkerWidget->getXml(elem);
            }
    
            else if(elem.tagName() == "MULTI_COLOR_MARKER")
    
    d.kilic's avatar
    d.kilic committed
            {
                mMultiColorMarkerWidget->getXml(elem);
            }
    
            else if(elem.tagName() == "MOCAP")
    
            {
                mMoCapController.getXml(elem);
            }
    
            else if(elem.tagName() == "CONTROL")
    
    d.kilic's avatar
    d.kilic committed
            {
                mControlWidget->getXml(elem);
                QDomElement tmpElem = (elem.firstChildElement("TRACKING")).firstChildElement("PATH");
    
                if(tmpElem.hasAttribute("ONLY_PEOPLE_NR"))
    
    d.kilic's avatar
    d.kilic committed
                    onlyPeopleNr = tmpElem.attribute("ONLY_PEOPLE_NR").toInt();
    
                if(tmpElem.hasAttribute("ONLY_PEOPLE_NR_LIST"))
    
    d.kilic's avatar
    d.kilic committed
                    onlyPeopleNrList = tmpElem.attribute("ONLY_PEOPLE_NR_LIST");
    
    d.kilic's avatar
    d.kilic committed
            }
    
            else if(elem.tagName() == "EXTR_CALIBRATION")
    
            else if(elem.tagName() == "PLAYER")
    
    d.kilic's avatar
    d.kilic committed
            {
    
                if(elem.hasAttribute("FRAME"))
    
    d.kilic's avatar
    d.kilic committed
                {
                    frame = elem.attribute("FRAME").toInt();
                }
    
                if(elem.hasAttribute("FPS"))
    
    d.kilic's avatar
    d.kilic committed
                {
                    fps = elem.attribute("FPS").toDouble();
                }
    
                if(elem.hasAttribute("SOURCE_FRAME_IN"))
    
    d.kilic's avatar
    d.kilic committed
                {
                    sourceFrameIn = elem.attribute("SOURCE_FRAME_IN").toInt();
                }
    
                if(elem.hasAttribute("SOURCE_FRAME_OUT"))
    
    d.kilic's avatar
    d.kilic committed
                {
                    sourceFrameOut = elem.attribute("SOURCE_FRAME_OUT").toInt();
                }
    
                if(elem.hasAttribute("PLAYER_SPEED_FIXED"))
    
    d.kilic's avatar
    d.kilic committed
                {
    
                    mPlayerWidget->setPlayerSpeedLimited(elem.attribute("PLAYER_SPEED_FIXED").toInt());
    
    d.kilic's avatar
    d.kilic committed
                }
            }
    
            else if(elem.tagName() == "VIEW")
    
    d.kilic's avatar
    d.kilic committed
            {
    
                if(elem.hasAttribute("ANTIALIAS"))
    
    d.kilic's avatar
    d.kilic committed
                {
    
                    mAntialiasAct->setChecked(elem.attribute("ANTIALIAS").toInt() == Qt::Checked);
    
    d.kilic's avatar
    d.kilic committed
                }
    
                if(elem.hasAttribute("OPENGL"))
    
    d.kilic's avatar
    d.kilic committed
                {
    
                    mOpenGLAct->setChecked(elem.attribute("OPENGL").toInt() == Qt::Checked);
    
    d.kilic's avatar
    d.kilic committed
                }
    
                if(elem.hasAttribute("SAVE_TRANSFORMED"))
    
    d.kilic's avatar
    d.kilic committed
                {
    
                    mCropZoomViewAct->setChecked(elem.attribute("SAVE_TRANSFORMED") == Qt::Checked);
    
    d.kilic's avatar
    d.kilic committed
                }
    
                if(elem.hasAttribute("TRANSFORMATION"))
    
    d.kilic's avatar
    d.kilic committed
                {
    
                    QString     matStr = elem.attribute("TRANSFORMATION");
    
    d.kilic's avatar
    d.kilic committed
                    QTextStream in(&matStr);
                    in >> zoom >> rotate >> hScroll >> vScroll;
                }
    
                if(elem.hasAttribute("CAMERA"))
    
    d.kilic's avatar
    d.kilic committed
                {
                    cam = (enum Camera) elem.attribute("CAMERA").toInt();
                }
    
                if(elem.hasAttribute("HIDE_CONTROLS"))
    
    d.kilic's avatar
    d.kilic committed
                {
    
                    mHideControlsAct->setChecked(elem.attribute("HIDE_CONTROLS").toInt() == Qt::Checked);
    
    d.kilic's avatar
    d.kilic committed
                }
    
            }
            else if(elem.tagName() == "AUTO_TRACK")
    
    d.kilic's avatar
    d.kilic committed
            {
    
                if(elem.hasAttribute("BACK_TRACK"))
    
    d.kilic's avatar
    d.kilic committed
                {
                    mAutoBackTrack = elem.attribute("BACK_TRACK").toInt();
                }
    
                if(elem.hasAttribute("OPTIMZE_COLOR"))
    
    d.kilic's avatar
    d.kilic committed
                {
                    mAutoTrackOptimizeColor = elem.attribute("OPTIMZE_COLOR").toInt();
                }
            }
            else
    
                debout << "Unknown PETRACK tag " << elem.tagName() << std::endl;
    
    d.kilic's avatar
    d.kilic committed
        }
        // open koennte am schluss passieren, dann wuerde nicht erst unveraendertes bild angezeigt,
        // dafuer koennte es aber sein, dass werte zb bei fx nicht einstellbar sind!
    
        if(openSeq && (seq != ""))
    
    d.kilic's avatar
    d.kilic committed
            openSequence(seq); // wenn leer, dann kommt abfrage hoch, welche datei; abbrechen, wenn aktuelle gewuenscht
    
    d.kilic's avatar
    d.kilic committed
    
        mViewWidget->setZoomLevel(zoom);
        mViewWidget->setRotateLevel(rotate);
        mView->horizontalScrollBar()->setValue(hScroll);
        mView->verticalScrollBar()->setValue(vScroll);
    
        bool loaded = false;
    
        if(!mBackgroundFilter.getFilename().isEmpty())
    
    d.kilic's avatar
    d.kilic committed
        {
    
            if(!(loaded = mBackgroundFilter.load(mBackgroundFilter.getFilename())))
    
                debout << "Error: loading background file " << mBackgroundFilter.getFilename() << "!" << std::endl;
    
    d.kilic's avatar
    d.kilic committed
        }
    
    
        mPlayerWidget->setFrameInNum(sourceFrameIn == -1 ? mAnimation->getSourceInFrameNum() : sourceFrameIn);
        mPlayerWidget->setFrameOutNum(sourceFrameOut == -1 ? mAnimation->getSourceOutFrameNum() : sourceFrameOut);
    
    d.kilic's avatar
    d.kilic committed
        mPlayerWidget->update();
    
    
        if(frame != -1)
    
    d.kilic's avatar
    d.kilic committed
        {
    
            if(mControlWidget->filterBg->isChecked() &&
    
               !loaded) // mit dem anfangs geladenen bild wurde bereits faelschlicherweise bg bestimmt
            {
    
    d.kilic's avatar
    d.kilic committed
                mBackgroundFilter.reset(); // erst nach dem springen zu einem frame background bestimmen
    
    d.kilic's avatar
    d.kilic committed
            mPlayerWidget->skipToFrame(frame); // hier wird updateImage ausgefuehrt
        }
    
        else if(loaded)
    
    d.kilic's avatar
    d.kilic committed
            updateImage();
    
    d.kilic's avatar
    d.kilic committed
    
        // nicht schon in control, sonst loescht opensequence wieder tracker
    
        if(mTrcFileName != "")
    
    d.kilic's avatar
    d.kilic committed
        {
            // 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))
    
    d.kilic's avatar
    d.kilic committed
            {
    
                mPersonStorage.clear();
    
    d.kilic's avatar
    d.kilic committed
                mTracker->reset();
            }
            importTracker(mTrcFileName);
        }
    
        mControlWidget->trackShowOnlyNr->setValue(onlyPeopleNr);
        mControlWidget->trackShowOnlyNrList->setText(onlyPeopleNrList);
    
    
        if(cam == cameraLeft)
    
    d.kilic's avatar
    d.kilic committed
            mCameraLeftViewAct->setChecked(true);
    
        else if(cam == cameraRight)
    
    d.kilic's avatar
    d.kilic committed
            mCameraRightViewAct->setChecked(true);
    
    d.kilic's avatar
    d.kilic committed
        setCamera();
        mPlayerWidget->setFPS(fps); // erst spaet setzen, damit Wert den des geladenen Videos ueberschreiben kann
        setLoading(false);
    }
    
    void Petrack::openProject(QString fileName, bool openSeq) // default fileName="", openSequence = true
    {
    
        if(!QFileInfo(mProFileName).isDir()) // a project is already loaded
    
            if(!maybeSave())
    
    d.kilic's avatar
    d.kilic committed
                return;
    
    d.kilic's avatar
    d.kilic committed
        // if no destination file or folder is given
    
        if(fileName.isEmpty())
    
            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(!fileName.isEmpty())
    
    d.kilic's avatar
    d.kilic committed
        {
            QFile file(fileName);
    
            if(!file.open(QIODevice::ReadOnly))
    
    d.kilic's avatar
    d.kilic committed
            {
    
                PCritical(this, tr("PeTrack"), tr("Cannot open %1:\n%2.").arg(fileName, file.errorString()));
    
    d.kilic's avatar
    d.kilic committed
                return;
            }
            resetSettings();
            QDomDocument doc("PETRACK"); // eigentlich Pfad zu Beschreibungsdatei fuer Dateiaufbau
    
            if(!doc.setContent(&file))
    
    d.kilic's avatar
    d.kilic committed
            {
    
                PCritical(this, tr("PeTrack"), tr("Cannot read content from %1.").arg(fileName));
    
    d.kilic's avatar
    d.kilic committed
                file.close();
                return;
            }
    
    
            debout << "open " << fileName << std::endl;
    
    d.kilic's avatar
    d.kilic committed
            file.close();
    
            setProFileName(fileName);
    
    d.kilic's avatar
    d.kilic committed
    
            QDomElement root = doc.firstChildElement("PETRACK");
    
            if(root.hasAttribute("VERSION"))
    
                if(root.attribute("VERSION") != mPetrackVersion)
    
    d.kilic's avatar
    d.kilic committed
                {
    
                    PWarning(
                        this,
                        tr("PeTrack"),
                        tr("Reading %1:\nDifferent version numbers %2 (application) and %3 (file) may cause problems.")
                            .arg(fileName, mPetrackVersion, root.attribute("VERSION")));
    
    d.kilic's avatar
    d.kilic committed
                }
    
    d.kilic's avatar
    d.kilic committed
            openXml(doc, openSeq);
    
            mLastTrackerExport = mTrcFileName;
    
    
            if(!lessThanVersion(root.attribute("VERSION"), QString("0.9.0")))
    
            {
                PWarning(
                    this,
                    tr("PeTrack"),
    
                    tr("You are using a project version lower than 0.9: Therefore, the extended intrinsic calibration "
                       "model is disabled."));
    
                mControlWidget->setNewModelChecked(false);
            }
    
    d.kilic's avatar
    d.kilic committed
            updateWindowTitle();
        }
    }
    
    void Petrack::saveXml(QDomDocument &doc)
    {
        QDomElement elem;
    
        QDomElement root = doc.createElement("PETRACK");
    
        root.setAttribute("VERSION", mPetrackVersion);
    
    d.kilic's avatar
    d.kilic committed
        doc.appendChild(root);
    
        // main settings (window size, status hight)
    
        elem        = doc.createElement("MAIN");
    
        QString seq = getFileList(mSeqFileName, mProFileName);
    
    d.kilic's avatar
    d.kilic committed
    
        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);
    
    
    d.kilic's avatar
    d.kilic committed
        // 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);
    
    
    d.kilic's avatar
    d.kilic committed
        // 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());
    
    d.kilic's avatar
    d.kilic committed
    
        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
    
    d.kilic's avatar
    d.kilic committed
        elem.setAttribute("CAMERA", mAnimation->getCamera());
    
    #else
        elem.setAttribute("CAMERA", cameraUnset);
    #endif
    
    d.kilic's avatar
    d.kilic committed
        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);
    }
    
    
    /// rueckgabewert zeigt an, ob gesichert werden konnte
    
    d.kilic's avatar
    d.kilic committed
    bool Petrack::saveSameProject()
    {
        return saveProject(mProFileName);
    }
    
    
    d.kilic's avatar
    d.kilic committed
    bool Petrack::saveProjectAs()
    {
        auto fileName = QFileDialog::getSaveFileName(
            this, tr("Select project file"), mProFileName, tr("PeTrack project file (*.pet);;All files (*.*)"));
        return saveProject(fileName);
    }
    
    
    /// rueckgabewert zeigt an, ob gesichert werden konnte
    
    d.kilic's avatar
    d.kilic committed
    bool Petrack::saveProject(QString fileName) // default fileName=""
    {
        // if no destination file or folder is given
    
        if(fileName.isEmpty() && QFileInfo(mProFileName).isDir())
    
    d.kilic's avatar
    d.kilic committed
        {
    
            fileName = QFileDialog::getSaveFileName(
                this, tr("Select project file"), mProFileName, tr("PeTrack project file (*.pet);;All files (*.*)"));
    
    d.kilic's avatar
    d.kilic committed
        }
    
    
        if(!fileName.isEmpty())
    
    d.kilic's avatar
    d.kilic committed
        {
    
            setProFileName(fileName);
    
    d.kilic's avatar
    d.kilic committed
            QDomDocument doc("PETRACK"); // eigentlich Pfad zu Beschreibungsdatei fuer Dateiaufbau
            saveXml(doc);
    
            // file output
    
            QByteArray       byteArray;
            QXmlStreamWriter xmlStream(&byteArray);
    
    d.kilic's avatar
    d.kilic committed
            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))
    
    d.kilic's avatar
    d.kilic committed
            {
    
                PCritical(this, tr("PeTrack"), tr("Cannot save %1:\n%2.").arg(fileName, file.errorString()));
    
    d.kilic's avatar
    d.kilic committed
                file.close();
                return false;
            }
            file.write(byteArray);
            file.close(); // also flushes the file
    
            statusBar()->showMessage(tr("Saved project to %1.").arg(fileName), 5000);
    
            debout << "save project to " << fileName << std::endl;
    
    d.kilic's avatar
    d.kilic committed
    
            updateWindowTitle();
            return true;
        }
        else
    
    d.kilic's avatar
    d.kilic committed
            return false;
    
    d.kilic's avatar
    d.kilic committed
    }
    
    
    void Petrack::writeXmlElement(QXmlStreamWriter &xmlStream, QDomElement element)
    
    d.kilic's avatar
    d.kilic committed
    {
        xmlStream.writeStartElement(element.tagName());
    
    
        QVector<QString>       attribute_names;
    
    d.kilic's avatar
    d.kilic committed
        const QDomNamedNodeMap attributes = element.attributes();
    
        for(int i = 0; i < attributes.size(); ++i)
        {
    
    d.kilic's avatar
    d.kilic committed
            attribute_names.push_back(attributes.item(i).toAttr().name());
        }
    
    
        // TODO: check if sorting of elements fits our needs
    
    d.kilic's avatar
    d.kilic committed
        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)
        {
    
    d.kilic's avatar
    d.kilic committed
            QDomAttr attr = element.attributeNode(name);
            xmlStream.writeAttribute(attr.name(), attr.value());
        }
    
        // order of child nodes is defined at creation
    
        if(element.hasChildNodes())
        {
    
    d.kilic's avatar
    d.kilic committed
            const QDomNodeList children = element.childNodes();
    
            for(int i = 0; i < children.size(); ++i)
            {
    
    d.kilic's avatar
    d.kilic committed
                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*/)
    
    d.kilic's avatar
    d.kilic committed
    {
    
        if(camID == -1)
    
    d.kilic's avatar
    d.kilic committed
        {
            // if more than one camera connected show to choose
    
            // camID = selectedID;
    
            debout << "No camera ID delivered: Set CameraID to 0 (default Camera)" << std::endl;
    
    d.kilic's avatar
    d.kilic committed
            camID = 0; // default
        }
    
        if(!mAnimation->openCameraStream(camID))
    
    d.kilic's avatar
    d.kilic committed
        {
    
            PCritical(this, tr("PeTrack"), tr("Cannot start Camera Livestream."));
    
    d.kilic's avatar
    d.kilic committed
            return;
        }
        mSeqFileName = "camera live stream";
    
        debout << "open " << mSeqFileName << " (" << mAnimation->getNumFrames() << " frames; " << mAnimation->getFPS()
               << " fps; " << mAnimation->getSize().width() << "x" << mAnimation->getSize().height() << " pixel)"
               << std::endl; // size
    
    d.kilic's avatar
    d.kilic committed
        updateSequence();
        updateWindowTitle();
        mPlayerWidget->setFPS(mAnimation->getFPS());
    
        if(mOpenGLAct->isChecked())
    
    d.kilic's avatar
    d.kilic committed
            mLogoItem->fadeOut(1);
    
    d.kilic's avatar
    d.kilic committed
        else
    
    d.kilic's avatar
    d.kilic committed
            mLogoItem->fadeOut(50);
    
    d.kilic's avatar
    d.kilic committed
        updateCoord();
    
    
        mPlayerWidget->play(PlayerState::FORWARD);
    
    d.kilic's avatar
    d.kilic committed
    }
    
    void Petrack::openSequence(QString fileName) // default fileName = ""
    {
    
        if(fileName.isEmpty())
    
            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(!fileName.isEmpty())
    
    d.kilic's avatar
    d.kilic committed
        {
    
            if(!mAnimation->openAnimation(fileName))
    
    d.kilic's avatar
    d.kilic committed
            {
    
                PCritical(this, tr("PeTrack"), tr("Cannot load %1.").arg(fileName));
    
    d.kilic's avatar
    d.kilic committed
                return;
            }
    
            mCameraGroupView->setEnabled(mAnimation->isStereoVideo());
            mCameraMenu->setEnabled(mAnimation->isStereoVideo());
    
    #ifdef STEREO
    
            if(mAnimation->isStereoVideo())
    
    d.kilic's avatar
    d.kilic committed
            {
    
                if(mStereoContext)
    
    d.kilic's avatar
    d.kilic committed
                    delete mStereoContext;
                mStereoContext = new pet::StereoContext(this);
            }
    
    
            bool lastIsStereoVideo = mAnimation->isStereoVideo();
    
            if(mCalibFilter == NULL || (mAnimation->isStereoVideo() != lastIsStereoVideo))
    
    d.kilic's avatar
    d.kilic committed
            {
                bool lastCalibFilterEnabled = false;
    
                if(mCalibFilter != NULL)
    
    d.kilic's avatar
    d.kilic committed
                {
                    lastCalibFilterEnabled = mCalibFilter->getEnabled();
                    delete mCalibFilter;
                }
    
                if(mAnimation->isStereoVideo())
    
    d.kilic's avatar
    d.kilic committed
                {
                    mCalibFilter = new CalibStereoFilter;
                    ((CalibStereoFilter *) mCalibFilter)->setStereoContext(mStereoContext);
                }
                else
                    mCalibFilter = new CalibFilter;
                mCalibFilter->setEnabled(lastCalibFilterEnabled);
            }
    #endif
            mSeqFileName = fileName;
    
            debout << "open " << mSeqFileName << " (" << mAnimation->getNumFrames() << " frames; " << mAnimation->getFPS()
                   << " fps; " << mAnimation->getSize().width() << "x" << mAnimation->getSize().height() << " pixel)"
                   << std::endl; // size
    
    d.kilic's avatar
    d.kilic committed
            updateSequence();
            updateWindowTitle();
            mPlayerWidget->setFPS(mAnimation->getFPS());
    
            if(mOpenGLAct->isChecked())
    
    d.kilic's avatar
    d.kilic committed
                mLogoItem->fadeOut(1);
    
    d.kilic's avatar
    d.kilic committed
            else
    
    d.kilic's avatar
    d.kilic committed
                mLogoItem->fadeOut(50);
    
    d.kilic's avatar
    d.kilic committed
            updateCoord();
        }
    }
    
    
    void Petrack::openMoCapFile()
    {
    
        OpenMoCapDialog dialog(this, mMoCapController);
        dialog.exec();
    }
    
    
    d.kilic's avatar
    d.kilic committed
    void Petrack::updateWindowTitle()
    {
        QString title;
    
        QSize   size = mAnimation->getSize();
    
    d.kilic's avatar
    d.kilic committed
    
    
        if(QFileInfo(mProFileName).isDir())
    
            title = tr("PeTrack (v") + mPetrackVersion + tr("): ");
    
    d.kilic's avatar
    d.kilic committed
        else
        {
    
            title = tr("PeTrack (v") + mPetrackVersion + tr("): ") + QFileInfo(mProFileName).fileName();
    
            if(mAnimation->isVideo() || mAnimation->isImageSequence())
    
                title += "; ";
    
    d.kilic's avatar
    d.kilic committed
        }
    
        if(mAnimation->isVideo())
    
            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)";
    
    d.kilic's avatar
    d.kilic committed
        setWindowTitle(title);