Skip to content
Snippets Groups Projects
petrack.cpp 145 KiB
Newer Older
  • Learn to ignore specific revisions
  •     // 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); });
    
        mSetTo2p00 = new QAction(tr("&x2"));
    
        connect(mSetTo2p00, &QAction::triggered, mPlayerWidget, [&]() { mPlayerWidget->setSpeedRelativeToRealtime(2.0); });
    
        mSetTo1p75 = new QAction(tr("&x1.75"));
    
        connect(mSetTo1p75, &QAction::triggered, mPlayerWidget, [&]() { mPlayerWidget->setSpeedRelativeToRealtime(1.75); });
    
        mSetTo1p50 = new QAction(tr("&x1.5"));
    
        connect(mSetTo1p50, &QAction::triggered, mPlayerWidget, [&]() { mPlayerWidget->setSpeedRelativeToRealtime(1.5); });
    
        mSetTo1p25 = new QAction(tr("&x1.25"));
    
        connect(mSetTo1p25, &QAction::triggered, mPlayerWidget, [&]() { mPlayerWidget->setSpeedRelativeToRealtime(1.25); });
    
        mSetTo0p75 = new QAction(tr("&x0.75"));
    
        connect(mSetTo0p75, &QAction::triggered, mPlayerWidget, [&]() { mPlayerWidget->setSpeedRelativeToRealtime(0.75); });
    
        mSetTo0p50 = new QAction(tr("&x0.5"));
    
        connect(mSetTo0p50, &QAction::triggered, mPlayerWidget, [&]() { mPlayerWidget->setSpeedRelativeToRealtime(0.5); });
    
        mSetTo0p25 = new QAction(tr("&x0.25"));
    
        connect(mSetTo0p25, &QAction::triggered, mPlayerWidget, [&]() { mPlayerWidget->setSpeedRelativeToRealtime(0.25); });
    
    d.kilic's avatar
    d.kilic committed
    
        mPlayerLooping = new QAction(tr("&Loop"));
        mPlayerLooping->setCheckable(true);
        connect(mPlayerLooping, &QAction::triggered, mPlayerWidget, &Player::setLooping);
    
    d.kilic's avatar
    d.kilic committed
        // -------------------------------------------------------------------------------------------------------
    
    
        mDelPastAct = new QAction(tr("&Past part of all trj."), this);
    
        connect(
            mDelPastAct,
            &QAction::triggered,
            this,
            [this]() { this->deleteTrackPointAll(PersonStorage::Direction::Previous); });
    
    d.kilic's avatar
    d.kilic committed
    
        mDelFutureAct = new QAction(tr("&Future part of all trj."), this);
    
        connect(
            mDelFutureAct,
            &QAction::triggered,
            this,
            [this]() { this->deleteTrackPointAll(PersonStorage::Direction::Following); });
    
    d.kilic's avatar
    d.kilic committed
    
        mDelAllRoiAct = new QAction(tr("&Trj. moving through ROI"), this);
    
        connect(mDelAllRoiAct, &QAction::triggered, this, &Petrack::deleteTrackPointROI);
    
    d.kilic's avatar
    d.kilic committed
        mDelPartRoiAct = new QAction(tr("Part of Trj. inside &ROI"), this);
    
        connect(mDelPartRoiAct, &QAction::triggered, this, &Petrack::deleteTrackPointInsideROI);
    
    d.kilic's avatar
    d.kilic committed
    
        // -------------------------------------------------------------------------------------------------------
    
        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()
     */
    
    d.kilic's avatar
    d.kilic committed
    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(mOpenMoCapAct);
    
        mFileMenu->addAction(mEditMoCapAct);
    
    d.kilic's avatar
    d.kilic committed
        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);
    
    d.kilic's avatar
    d.kilic committed
        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);
    
    d.kilic's avatar
    d.kilic committed
        mViewMenu->addAction(mPlayerLooping);
    
    d.kilic's avatar
    d.kilic committed
        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);
    
    d.kilic's avatar
    d.kilic committed
    
        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
     */
    
    d.kilic's avatar
    d.kilic committed
    void Petrack::createStatusBar()
    {
    
        QFont f("Courier", 12, QFont::Bold); // Times Helvetica, Normal
    
    d.kilic's avatar
    d.kilic committed
        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);
    
    d.kilic's avatar
    d.kilic committed
        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()
    {
    
    d.kilic's avatar
    d.kilic committed
        ///
    
        ///  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
    
    d.kilic's avatar
    d.kilic committed
        ///
        return;
    }
    
    void Petrack::setStatusStereo(float x, float y, float z)
    {
    
        if(mStatusLabelStereo)
    
    d.kilic's avatar
    d.kilic committed
        {
    
    d.kilic's avatar
    d.kilic committed
                mStatusLabelStereo->setText(QString("x= novalue  y= novalue  z= novalue  "));
    
    d.kilic's avatar
    d.kilic committed
            else
    
                mStatusLabelStereo->setText(
                    QString("x=%1cm  y=%2cm  z=%3cm  ").arg(x, 6, 'f', 1).arg(y, 6, 'f', 1).arg(z, 6, 'f', 1));
    
    d.kilic's avatar
    d.kilic committed
        }
    }
    
    void Petrack::setStatusTime()
    {
    
        if(mStatusLabelTime)
    
    d.kilic's avatar
    d.kilic committed
            mStatusLabelTime->setText(mAnimation->getTimeString());
    
    d.kilic's avatar
    d.kilic committed
    }
    
    void Petrack::setStatusFPS()
    {
    
        if(mStatusLabelFPS)
    
    d.kilic's avatar
    d.kilic committed
        {
            mStatusLabelFPS->setText(QString("%1fps  ").arg(mShowFPS, 5, 'f', 1));
    
            QPalette pal = mStatusLabelFPS->palette(); // static moeglich?
    
    d.kilic's avatar
    d.kilic committed
    
    
            double diff    = mShowFPS - mAnimation->getFPS();
            int    opacity = mPlayerWidget->getPlayerSpeedLimited() ? 128 : 20;
    
    d.kilic's avatar
    d.kilic committed
    
    
            if(diff < -6) // very slow ==> red
    
                color.setRgb(200, 0, 0, opacity);
    
            else if(diff < -2) // better ==> yellow
    
                color.setRgb(200, 200, 0, opacity);
    
            else if(diff > -2) // nearly ok ==> green
    
                color.setRgb(0, 200, 0, opacity);
    
    d.kilic's avatar
    d.kilic committed
    
            pal.setColor(QPalette::Window, color);
    
            mStatusLabelFPS->setPalette(pal);
        }
    }
    void Petrack::setShowFPS(double fps)
    {
    
        if((fps == 0.) || (mShowFPS == 0))
    
    d.kilic's avatar
    d.kilic committed
            mShowFPS = fps;
    
    d.kilic's avatar
    d.kilic committed
        else
    
            mShowFPS = mShowFPS * .9 + fps * .1; // glaetten durch Hinzunahme des alten Wertes
    
    d.kilic's avatar
    d.kilic committed
        setStatusFPS();
    }
    
    
    /**
     * @brief Updates the FPS shown to the User
     *
     * This method calculates the FPS by remembering how long
     * it has been since it was called last time. If skipped is
    
     * true, it doesn't directly update the FPS since 2
    
     * skipped frames have essentially a time delay of 0 between
     * them, which would make calculations wonky.
     *
     * @param skipped True, if this is a skipped frame; default false
     */
    
    void Petrack::updateShowFPS(bool skipped)
    {
    
        static QElapsedTimer lastTime;
    
        static int           skippedFrames = 0;
    
            skippedFrames++;
            return;
        }
    
    d.kilic's avatar
    d.kilic committed
    
    
        if(mPlayerWidget->getPaused())
    
    d.kilic's avatar
    d.kilic committed
        {
            setShowFPS(0.);
    
    d.kilic's avatar
    d.kilic committed
        }
        else
        {
    
            if(lastTime.isValid())
    
                if(lastTime.elapsed() > 0)
    
                    int numFrames = skippedFrames > 0 ? skippedFrames + 1 : 1;
                    setShowFPS(numFrames * 1000. / lastTime.elapsed());
    
                    skippedFrames = 0;
    
    d.kilic's avatar
    d.kilic committed
        }
    }
    
    // ohne neue positionsangabe, sinnvoll, wenn berechnungsweise sich in getPosReal geaendert hat
    // gebraucht in control.cpp
    void Petrack::setStatusPosReal() // pos in cm
    {
    
        if(mImageItem)
    
            setStatusPosReal(mWorldImageCorrespondence->getPosReal(mMousePosOnImage, getStatusPosRealHeight()));
    
    d.kilic's avatar
    d.kilic committed
    }
    
    d.kilic's avatar
    d.kilic committed
    void Petrack::setStatusPosReal(const QPointF &pos) // pos in cm
    {
    
        if(mStatusLabelPosReal)
    
    d.kilic's avatar
    d.kilic committed
        {
    
            QChar   deg(0xB0);
            QString labelText = QString(" cm from ground:%1cm,%2cm,%3")
                                    .arg(pos.x(), 6, 'f', 1)
                                    .arg(pos.y(), 6, 'f', 1)
                                    .arg(
    
                                        mWorldImageCorrespondence->getAngleToGround(
    
                                            mMousePosOnImage.x(), mMousePosOnImage.y(), getStatusPosRealHeight()),
                                        5,
                                        'f',
                                        1);
    
    d.kilic's avatar
    d.kilic committed
            labelText.append(deg);
            mStatusLabelPosReal->setText(labelText);
        }
    }
    
    d.kilic's avatar
    d.kilic committed
    void Petrack::setStatusPos(const QPoint &pos) // pos in pixel
    {
        mStatusLabelPos->setText(QString("%1x%2").arg(pos.x(), 4).arg(pos.y(), 4));
    }
    
    d.kilic's avatar
    d.kilic committed
    void Petrack::setStatusColor(const QRgb &col)
    {
        QString s("#%1%2%3"); // static moeglich?
        s = s.arg(qRed(col), 2, 16, QChar('0')).arg(qGreen(col), 2, 16, QChar('0')).arg(qBlue(col), 2, 16, QChar('0'));
    
        if((qRed(col) + qGreen(col) + qBlue(col)) / 3 < 128)
    
    d.kilic's avatar
    d.kilic committed
            mStatusLabelColor->setText(QString("<font color=\"#ffffff\">&nbsp;%1</font>").arg(s));
    
    d.kilic's avatar
    d.kilic committed
        else
    
    d.kilic's avatar
    d.kilic committed
            mStatusLabelColor->setText(QString("<font color=\"#000000\">&nbsp;%1</font>").arg(s));
    
    d.kilic's avatar
    d.kilic committed
    
        QPalette pal = mStatusLabelColor->palette(); // static moeglich?
    
        QColor   color(qRed(col), qGreen(col), qBlue(col));
    
    d.kilic's avatar
    d.kilic committed
    
        pal.setColor(QPalette::Window, color);
        mStatusLabelColor->setPalette(pal);
    
        mControlWidget->getColorPlot()->setCursor(color);
        mControlWidget->getColorPlot()->replot();
    }
    
    d.kilic's avatar
    d.kilic committed
    void Petrack::setStatusColor()
    {
        QPointF pos = getMousePosOnImage();
    
        if(pos.x() >= 0 && pos.x() < mImage->width() && pos.y() > 0 && pos.y() < mImage->height())
    
    d.kilic's avatar
    d.kilic committed
        {
    
            setStatusColor(mImage->pixel(pos.toPoint()));
    
    d.kilic's avatar
    d.kilic committed
        }
    }
    
    double Petrack::getStatusPosRealHeight()
    {
    
        if(mStatusPosRealHeight)
    
    d.kilic's avatar
    d.kilic committed
            return mStatusPosRealHeight->value();
    
    d.kilic's avatar
    d.kilic committed
        else
    
    d.kilic's avatar
    d.kilic committed
            return 0.;
    
    d.kilic's avatar
    d.kilic committed
    }
    
    
    /**
     * @brief Reads (and applies) settings form platform-independent persistent storage
     *
     * The saved size and position of the application window get reconstructed. As well as
     * the options about antialiasing and the usage of OpenGL.
     * mSeqFileName and mProFileName get set, so the "Open Project" and "Open Sequence"
     * dialogues start at correct folder. The old project/sequence is NOT actually loaded.
     */
    
    d.kilic's avatar
    d.kilic committed
    void Petrack::readSettings()
    {
    
        const QSettings settings("Forschungszentrum Juelich GmbH", "PeTrack by Maik Boltes, Daniel Salden");
    
    d.kilic's avatar
    d.kilic committed
        mAntialiasAct->setChecked(settings.value("antialias", false).toBool());
        mOpenGLAct->setChecked(settings.value("opengl", false).toBool());
        mSeqFileName = settings.value("seqFileName", QDir::currentPath()).toString();
    
        setProFileName(settings.value("proFilePath", QDir::currentPath()).toString());
    
        // nicht ganz sauber, da so immer schon zu anfang in calib file list etwas drin steht und somit auto ausgefuehrt
        // werden kann wird aber beim ersten openCalib... ueberschrieben
    
    d.kilic's avatar
    d.kilic committed
        mAutoCalib.addCalibFile(settings.value("calibFile", QDir::currentPath()).toString());
    
        auto geometry = settings.value("geometry").toByteArray();
        restoreGeometry(geometry);
    
    d.kilic's avatar
    d.kilic committed
        antialias();
        opengl();
    
        mSplitter->restoreState(settings.value("controlSplitterSizes").toByteArray());
    
        mAutosave.setPetSaveInterval(settings.value("petSaveInterval", 120).toDouble());
        mAutosave.setChangesTillAutosave(settings.value("changesTillAutosave", 10).toInt());
    
    d.kilic's avatar
    d.kilic committed
    }
    
    
    /**
     * @brief Writes persistent setting.
     * @see Petrack::readSettings
     */
    
    d.kilic's avatar
    d.kilic committed
    void Petrack::writeSettings()
    {
        QSettings settings("Forschungszentrum Juelich GmbH", "PeTrack by Maik Boltes, Daniel Salden");
    
        settings.setValue("geometry", saveGeometry());
    
    d.kilic's avatar
    d.kilic committed
        settings.setValue("antialias", mAntialiasAct->isChecked());
        settings.setValue("opengl", mOpenGLAct->isChecked());
        settings.setValue("seqFileName", mSeqFileName);
        settings.setValue("proFilePath", QFileInfo(mProFileName).path()); // nur path, damit bei saveCurrentProject
    
        if(!mAutoCalib.isEmptyCalibFiles())                               //! mCalibFiles.isEmpty()
    
    d.kilic's avatar
    d.kilic committed
            settings.setValue("calibFile", mAutoCalib.getCalibFile(0));
    
        settings.setValue("controlSplitterSizes", mSplitter->saveState());
    
        settings.setValue("petSaveInterval", mAutosave.getPetSaveInterval());
        settings.setValue("changesTillAutosave", mAutosave.getChangesTillAutosave());
    
    d.kilic's avatar
    d.kilic committed
    }
    
    bool Petrack::maybeSave()
    {
    
        int ret = PWarning(
            this,
            tr("PeTrack"),
            tr("Do you want to save "
               "the current project?\n"
               "Be sure to save trajectories, background "
               "and 3D calibration point separately!"),
            PMessageBox::StandardButton::Yes | PMessageBox::StandardButton::No | PMessageBox::StandardButton::Cancel,
            PMessageBox::StandardButton::Yes);
    
        if(ret == PMessageBox::StandardButton::Yes)
    
    d.kilic's avatar
    d.kilic committed
        {
    
            if(saveSameProject())
    
    d.kilic's avatar
    d.kilic committed
                return true;
    
    d.kilic's avatar
    d.kilic committed
            else
    
    d.kilic's avatar
    d.kilic committed
                return false;
    
    d.kilic's avatar
    d.kilic committed
        }
    
        else if(ret == PMessageBox::StandardButton::Cancel)
    
    d.kilic's avatar
    d.kilic committed
            return false;
    
    d.kilic's avatar
    d.kilic committed
        else
    
    d.kilic's avatar
    d.kilic committed
            return true;
    
    d.kilic's avatar
    d.kilic committed
    }
    
    void Petrack::closeEvent(QCloseEvent *event)
    {
    
        if(maybeSave())
    
    d.kilic's avatar
    d.kilic committed
        {
            writeSettings();
    
            mAutosave.deleteAutosave();
    
    d.kilic's avatar
    d.kilic committed
            mPlayerWidget->pause();
    
    d.kilic's avatar
    d.kilic committed
            event->accept();
        }
        else
    
    d.kilic's avatar
    d.kilic committed
            event->ignore();
    
    d.kilic's avatar
    d.kilic committed
    }
    
    
    /**
     * @brief Sets the mMousePosOnImage member variable and displayed pixel/real coordinates
     *
     * Gets called from ImageItem::hoverMoveEvent() and enables an easy access
     * to the mouse position.
     * @param pos Position of mouse cursor in image pixel coordinates
     */
    
    d.kilic's avatar
    d.kilic committed
    void Petrack::setMousePosOnImage(QPointF pos)
    {
    
    d.kilic's avatar
    d.kilic committed
        {
            mMousePosOnImage = pos;
    
            setStatusPosReal(mWorldImageCorrespondence->getPosReal(pos, getStatusPosRealHeight()));
    
    d.kilic's avatar
    d.kilic committed
    
            // pixel coordinate
    
            QPoint pos1((int) (pos.x()) + 1, (int) (pos.y()) + 1);
    
            setStatusPos(pos1);
    
    d.kilic's avatar
    d.kilic committed
            // pixel color
    
            setStatusColor();
    
    d.kilic's avatar
    d.kilic committed
        }
    }
    
    
    void Petrack::keyPressEvent(QKeyEvent *event)
    {
        switch(event->key())
        {
            case Qt::Key_Left:
                mPlayerWidget->frameBackward();
                break;
            case Qt::Key_Right:
                mPlayerWidget->frameForward();
                break;
            case Qt::Key_Down:
                mViewWidget->zoomOut(1);
                break;
            case Qt::Key_Up:
                mViewWidget->zoomIn(1);
                break;
            case Qt::Key_Space:
                // space wird von buttons, wenn focus drauf ist als Aktivierung vorher abgegriffen und nicht durchgereicht
                mPlayerWidget->togglePlayPause();
                break;
            case Qt::Key_D:
                break;
            default:;
    
    d.kilic's avatar
    d.kilic committed
        }
    }
    
    void Petrack::mousePressEvent(QMouseEvent *event)
    {
        // mouse click in fps status label ?
    
        if(event->pos().x() >= mStatusLabelFPS->pos().x() &&
           event->pos().x() <= mStatusLabelFPS->pos().x() + mStatusLabelFPS->width())
    
    d.kilic's avatar
    d.kilic committed
        {
    
            mPlayerWidget->togglePlayerSpeedLimited();
    
    d.kilic's avatar
    d.kilic committed
            setStatusFPS();
        }
    }
    
    
    const QString &Petrack::getLastTrackerExport() const
    {
        return mLastTrackerExport;
    }
    
    void Petrack::setLastTrackerExport(const QString &newLastTrackerExport)
    {
        mLastTrackerExport = newLastTrackerExport;
    }
    
    
    /// update control widget, if image size changed (especially because of changing border)
    
    void Petrack::updateControlImage(cv::Mat &img)
    
    d.kilic's avatar
    d.kilic committed
    {
        // auch moeglich hoehe und breite von bild stat border veraenderungen zu checken
        static int lastBorderSize = -1;
    
    
        if(isLoading())
    
    d.kilic's avatar
    d.kilic committed
            lastBorderSize = -1;
    
        int diffBorderSize = 0;
    
        if(lastBorderSize != -1)
    
    d.kilic's avatar
    d.kilic committed
        {
    
            diffBorderSize = getImageBorderSize() - lastBorderSize;
    
    d.kilic's avatar
    d.kilic committed
        }
    
        lastBorderSize = getImageBorderSize();
    
    d.kilic's avatar
    d.kilic committed
    
    
        const int imgWidth  = img.cols;
        const int imgHeight = img.rows;
    
    
        // no direct invocation to have correct order of invocations
        // (direct invocation gets executed immediately, i.e. before queued connection)
        QMetaObject::invokeMethod(
            mControlWidget,
            "imageSizeChanged",
            Qt::ConnectionType::QueuedConnection,
            Q_ARG(int, imgWidth),
            Q_ARG(int, imgHeight),
            Q_ARG(int, diffBorderSize));
    
    d.kilic's avatar
    d.kilic committed
    }
    
    
    void Petrack::importTracker(QString dest) // default = ""
    
    d.kilic's avatar
    d.kilic committed
    {
        static QString lastFile;
    
    
        if(lastFile == "")
    
    d.kilic's avatar
    d.kilic committed
            lastFile = mTrcFileName;
    
    d.kilic's avatar
    d.kilic committed
    
        // if no destination file or folder is given
    
        if(dest.isEmpty())
    
    d.kilic's avatar
    d.kilic committed
        {
    
            dest = QFileDialog::getOpenFileName(
                this,
                tr("Select file for importing tracking pathes"),
                lastFile,
                tr("PeTrack tracker (*.trc *.txt);;All files (*.*)"));
    
    d.kilic's avatar
    d.kilic committed
        }
    
    
        if(!dest.isEmpty())
    
    d.kilic's avatar
    d.kilic committed
        {
    
            if(dest.endsWith(".trc", Qt::CaseInsensitive))
    
    d.kilic's avatar
    d.kilic committed
            {
                QFile file(dest);
    
    d.kilic's avatar
    d.kilic committed
    
    
                if(!file.open(QIODevice::ReadOnly | QIODevice::Text))
    
    d.kilic's avatar
    d.kilic committed
                {
                    // errorstring ist je nach Betriebssystem in einer anderen Sprache!!!!
    
                    PCritical(this, tr("PeTrack"), tr("Cannot open %1:\n%2").arg(dest).arg(file.errorString()));
    
    d.kilic's avatar
    d.kilic committed
                    return;
                }
    
    
                setTrackChanged(true); // flag changes of track parameters
    
    d.kilic's avatar
    d.kilic committed
                mTracker->reset();
    
                QTextStream in(&file);
    
                QString     comment;
    
    d.kilic's avatar
    d.kilic committed
    
    
                bool    ok; // shows if int stands in first line - that was in the first version of trc file
    
    d.kilic's avatar
    d.kilic committed
                QString firstLine = in.readLine();
    
                sz                = firstLine.toInt(&ok);
                if(!ok)
    
    d.kilic's avatar
    d.kilic committed
                {
    
                    if(firstLine.contains("version 4", Qt::CaseInsensitive))
    
                    else if(firstLine.contains("version 3", Qt::CaseInsensitive))
    
    d.kilic's avatar
    d.kilic committed
                    {
                        trcVersion = 3;
    
                    else if(firstLine.contains("version 2", Qt::CaseInsensitive))
    
    d.kilic's avatar
    d.kilic committed
                    {
                        trcVersion = 2;
    
    d.kilic's avatar
    d.kilic committed
                    {
    
                        SPDLOG_ERROR("wrong header while reading TRC file.");
    
                        QMessageBox::critical(
                            this,
                            tr("PeTrack"),
                            tr("Could not import tracker:\nNot supported trc version in file: %1.").arg(dest));
    
    d.kilic's avatar
    d.kilic committed
                    }
                    in >> sz;
                }
                else
    
    d.kilic's avatar
    d.kilic committed
                    trcVersion = 1;
    
    d.kilic's avatar
    d.kilic committed
    
    
                if((sz > 0) && (mPersonStorage.nbPersons() != 0))
    
                    SPDLOG_WARN("overlapping trajectories will be joined not until tracking adds new TrackPoints.");
    
                for(i = 0; i < sz; ++i)
    
    d.kilic's avatar
    d.kilic committed
                {
    
                    TrackPerson tp = fromTrc(in);
    
                    mPersonStorage.addPerson(tp);
    
    d.kilic's avatar
    d.kilic committed
                }
    
    
                mControlWidget->setTrackNumberAll(QString("%1").arg(mPersonStorage.nbPersons()));
                mControlWidget->setTrackShowOnlyNr(static_cast<int>(MAX(mPersonStorage.nbPersons(), 1)));
                mControlWidget->setTrackNumberVisible(
    
                    QString("%1").arg(mPersonStorage.visible(mAnimation->getCurrentFrameNum())));
    
                mControlWidget->replotColorplot();
    
    d.kilic's avatar
    d.kilic committed
                file.close();
    
                SPDLOG_INFO("import {} ({} person(s), file version {})", dest, sz, trcVersion);
    
                mTrcFileName =
                    dest; // fuer Project-File, dann koennte track path direkt mitgeladen werden, wenn er noch da ist
            }
    
            else if(dest.endsWith(".txt", Qt::CaseInsensitive)) // 3D Koordinaten als Tracking-Daten importieren
                                                                // Zeilenformat: Personennr, Framenr, x, y, z
    
    d.kilic's avatar
    d.kilic committed
            {
    
                PWarning(
                    this,
                    tr("PeTrack"),
                    tr("Are you sure you want to import 3D data from TXT-File? You have to make sure that the coordinate "
                       "system now is exactly at the same position and orientation than at export time!"));
    
    d.kilic's avatar
    d.kilic committed
    
                QFile file(dest);
    
    
                int numberImportedPersons = 0;
    
    d.kilic's avatar
    d.kilic committed
    
    
                if(!file.open(QIODevice::ReadOnly | QIODevice::Text))
    
    d.kilic's avatar
    d.kilic committed
                {
                    // errorstring ist je nach Betriebssystem in einer anderen Sprache!!!!
    
                    PCritical(this, tr("PeTrack"), tr("Cannot open %1:\n%2").arg(dest).arg(file.errorString()));
    
    d.kilic's avatar
    d.kilic committed
                    return;
                }
    
    
                setTrackChanged(true); // flag changes of track parameters
    
    d.kilic's avatar
    d.kilic committed
                mTracker->reset();
    
                QTextStream in(&file);
    
                TrackPoint  tPoint;
    
    d.kilic's avatar
    d.kilic committed
    
                QString line;
    
                bool    unitFound            = false;
                double  conversionFactorToCM = 1.0;
    
    d.kilic's avatar
    d.kilic committed
    
    
                std::unordered_map<int, std::map<int, Vec3F>> personData;
                QString                                       headerline;
    
    d.kilic's avatar
    d.kilic committed
    
    
                while(in.readLineInto(&line))
                {
    
                    if(line.startsWith("#", Qt::CaseInsensitive))
    
    d.kilic's avatar
    d.kilic committed
                    {
                        headerline = line;
                        continue;
                    }
    
    
                    if((!unitFound) && (!headerline.contains("cm")))
    
    d.kilic's avatar
    d.kilic committed
                    {
    
                        conversionFactorToCM = 100.0;
                        unitFound            = true;
    
                        PWarning(
                            this,
                            tr("PeTrack"),
                            tr("PeTrack will interpret position data as unit [m]. No header with [cm] found."));
    
    d.kilic's avatar
    d.kilic committed
                    }
    
    
                    // Read data from line with format: persNr frameNr x y z
                    int   personNr = -1, frameNr = -1;
                    float x, y, z;
    
    d.kilic's avatar
    d.kilic committed
    
    
                    QTextStream stream(&line);
    
                    stream >> personNr >> frameNr >> x >> y >> z;
    
    d.kilic's avatar
    d.kilic committed
    
                    // convert data to cm
    
                    x = x * conversionFactorToCM;
                    y = y * conversionFactorToCM;
                    z = z * conversionFactorToCM;
    
    d.kilic's avatar
    d.kilic committed
    
    
                    if(personData[personNr].find(frameNr) == personData[personNr].end())
    
    d.kilic's avatar
    d.kilic committed
                    {
    
                        personData[personNr][frameNr] = Vec3F(x, y, z);
    
    d.kilic's avatar
    d.kilic committed
                    }
                    else
                    {
    
                        PCritical(
                            this,
                            "Error importing txt file",
                            tr("Could not import the data from the provided txt file, as the data for person %1 in frame "
                               "%1 is twice in the txt-file.")
                                .arg(personNr)
                                .arg(frameNr));
                        return;
    
    d.kilic's avatar
    d.kilic committed
                    }
    
    d.kilic's avatar
    d.kilic committed
    
    
                for(auto &[persNr, frameData] : personData)
                {
                    std::deque<TrackPoint> pixelPoints;
                    for(auto &[frameNr, realWorldCoordinates] : frameData)
    
    d.kilic's avatar
    d.kilic committed
                    {
    
    d.kilic's avatar
    d.kilic committed
    
    
                        if(mControlWidget->getCalibCoordDimension() == 0)
                        {
                            // compute image point from 3d calibration
                            p2d = mExtrCalibration.getImagePoint(
                                cv::Point3f(realWorldCoordinates.x(), realWorldCoordinates.y(), realWorldCoordinates.z()));
                        }
                        else
                        {
                            // compute image point from 2d calibration
    
                            QPointF pos = mWorldImageCorrespondence->getPosImage(
    
                                QPointF(realWorldCoordinates.x(), realWorldCoordinates.y()), realWorldCoordinates.z());
                            p2d.x = pos.x();
                            p2d.y = pos.y();
                        }
    
                        TrackPoint trackPoint(Vec2F(p2d.x, p2d.y), 100);
                        trackPoint.setSp(
                            realWorldCoordinates.x(),
                            realWorldCoordinates.y(),
                            -mControlWidget->getExtrinsicParameters().trans3 -
                                realWorldCoordinates.z()); // distance to camera as with stereo cameras
                        pixelPoints.push_back(trackPoint);
    
    d.kilic's avatar
    d.kilic committed
                    }
    
    
                    TrackPerson trackPerson(persNr, frameData.begin()->first, pixelPoints.front());
                    trackPerson.setHeight(frameData.begin()->second.z());
                    pixelPoints.pop_front();
    
                    for(const auto &trackPoint : pixelPoints)
    
    d.kilic's avatar
    d.kilic committed
                    {
    
                        trackPerson.append(trackPoint);
    
    d.kilic's avatar
    d.kilic committed
                    }
    
                    mPersonStorage.addPerson(trackPerson);
                    numberImportedPersons++;
    
    d.kilic's avatar
    d.kilic committed
                }
    
    
                mControlWidget->setTrackNumberAll(QString("%1").arg(mPersonStorage.nbPersons()));
                mControlWidget->setTrackShowOnlyNr(static_cast<int>(MAX(mPersonStorage.nbPersons(), 1)));
                mControlWidget->setTrackNumberVisible(
    
                    QString("%1").arg(mPersonStorage.visible(mAnimation->getCurrentFrameNum())));
    
                mControlWidget->replotColorplot();
    
    d.kilic's avatar
    d.kilic committed
                file.close();
    
                SPDLOG_INFO("import {} ({} person(s))", dest, numberImportedPersons);
                mTrcFileName = dest;
    
    d.kilic's avatar
    d.kilic committed
            }
            else
            {
    
                PCritical(this, tr("PeTrack"), tr("Cannot load %1 maybe because of wrong file extension.").arg(dest));
    
    d.kilic's avatar
    d.kilic committed
            }
            lastFile = dest;
        }
    }
    
    int Petrack::calculateRealTracker()
    {
    
        bool autoCorrectOnlyExport = (mReco.getRecoMethod() == reco::RecognitionMethod::MultiColor) && // multicolor
    
                                     mMultiColorMarkerWidget->autoCorrect->isChecked() &&
                                     mMultiColorMarkerWidget->autoCorrectOnlyExport->isChecked();
        int anz = mTrackerReal->calculate(
    
            mControlWidget->getColorPlot(),
    
            mMissingFrames,
    
            getImageBorderSize(),
    
            mControlWidget->getAnaMissingFrames(),
    
            mStereoWidget->stereoUseForExport->isChecked(),
    
            mControlWidget->getTrackAlternateHeight(),
            mControlWidget->getCameraAltitude(),
    
            mStereoWidget->stereoUseCalibrationCenter->isChecked(),
    
            mControlWidget->isExportElimTpChecked(),
            mControlWidget->isExportElimTrjChecked(),
            mControlWidget->isExportSmoothChecked(),
            mControlWidget->isExportViewDirChecked(),
            mControlWidget->isExportAngleOfViewChecked(),
            mControlWidget->isExportMarkerIDChecked(),
    
            autoCorrectOnlyExport);
    
    d.kilic's avatar
    d.kilic committed
    
        mTrackerReal->calcMinMax();
        return anz;
    }
    
    
    void Petrack::exportTracker(QString dest) // default = ""
    
    d.kilic's avatar
    d.kilic committed
    {
    
    d.kilic's avatar
    d.kilic committed
        {
    
    d.kilic's avatar
    d.kilic committed
            // if no destination file or folder is given
    
            if(dest.isEmpty())
    
    d.kilic's avatar
    d.kilic committed
            {
    
                QFileDialog fileDialog(
                    this,
    
                    tr("Select file for exporting tracking paths"),
    
                    tr("Tracker (*.*);;Petrack tracker (*.trc);;Text (*.txt);;Text for gnuplot(*.dat);;XML Travisto "
                       "(*.trav);;All supported types (*.txt *.trc *.dat *.trav *.);;All files (*.*)"));
    
    d.kilic's avatar
    d.kilic committed
                fileDialog.setAcceptMode(QFileDialog::AcceptSave);
                fileDialog.setFileMode(QFileDialog::AnyFile);
                fileDialog.setDefaultSuffix("");
    
    
                if(fileDialog.exec())
                {
    
    d.kilic's avatar
    d.kilic committed
                    dest = fileDialog.selectedFiles().at(0);
                }
    
    d.kilic's avatar
    d.kilic committed
    
    
            QList<int> pers, frame;
            bool autoCorrectOnlyExport = (mReco.getRecoMethod() == reco::RecognitionMethod::MultiColor) && // multicolor
                                         mMultiColorMarkerWidget->autoCorrect->isChecked() &&
                                         mMultiColorMarkerWidget->autoCorrectOnlyExport->isChecked();
    
    d.kilic's avatar
    d.kilic committed
    
    
            if(dest.endsWith(".trc", Qt::CaseInsensitive))
    
    d.kilic's avatar
    d.kilic committed
    
    
                if(!file.open() /*!file.open(QIODevice::WriteOnly | QIODevice::Text)*/)
                {
                    PCritical(this, tr("PeTrack"), tr("Cannot open %1:\n%2.").arg(dest).arg(file.errorString()));
                    return;
                }
                QProgressDialog progress(
                    "Export TRC-File", nullptr, 0, static_cast<int>(mPersonStorage.nbPersons() + 1), this->window());
                progress.setWindowTitle("Export .trc-File");
                progress.setWindowModality(Qt::WindowModal);
                progress.setVisible(true);
                progress.setValue(0);
                progress.setLabelText(QString("Export tracking data ..."));
    
    d.kilic's avatar
    d.kilic committed
    
    
    d.kilic's avatar
    d.kilic committed
    
    
    d.kilic's avatar
    d.kilic committed
    
    
                SPDLOG_INFO(
                    "export tracking data to {} ({} person(s), file version {})",
                    dest,
                    mPersonStorage.nbPersons(),
                    trcVersion);
                QTextStream out(&file);
    
    d.kilic's avatar
    d.kilic committed
    
    
                out << "version " << trcVersion << Qt::endl;
                out << mPersonStorage.nbPersons() << Qt::endl;
                const auto &persons = mPersonStorage.getPersons();
                for(size_t i = 0; i < persons.size(); ++i)
                {
    
                    qApp->processEvents();
    
                    progress.setLabelText(QString("Export person %1 of %2 ...").arg(i + 1).arg(mPersonStorage.nbPersons()));
                    progress.setValue(static_cast<int>(i + 1));
                    out << persons[i] << Qt::endl;
                }
                file.flush();
                file.close();
    
                progress.setLabelText(QString("Save file ..."));
                qApp->processEvents();
    
    d.kilic's avatar
    d.kilic committed
    
    
    d.kilic's avatar
    d.kilic committed
    
    
                if(!file.copy(dest))
                {
                    PCritical(
                        this,
                        tr("PeTrack"),
                        tr("Could not export tracking data.\n"
                           "Please try again!"));
                }
                else
                {
                    statusBar()->showMessage(tr("Saved tracking data to %1.").arg(dest), 5000);
                }
    
    d.kilic's avatar
    d.kilic committed
    
    
                progress.setValue(static_cast<int>(mPersonStorage.nbPersons() + 1));
    
    d.kilic's avatar
    d.kilic committed
    
    
                SPDLOG_INFO("finished.");
                mAutosave.resetTrackPersonCounter();
    
    d.kilic's avatar
    d.kilic committed
    
    
                mTrcFileName =
                    dest; // fuer Project-File, dann koennte track path direkt mitgeladen werden, wenn er// noch da ist
    
            else if(dest.endsWith(".txt", Qt::CaseInsensitive))
    
    d.kilic's avatar
    d.kilic committed
    
    
                if(!file.open())
                {
                    PCritical(this, tr("PeTrack"), tr("Cannot open %1:\n%2.").arg(dest).arg(file.errorString()));
                    return;
                }
    
    d.kilic's avatar
    d.kilic committed
    
    
                SPDLOG_INFO("export tracking data to {} ({} person(s))...", dest, mPersonStorage.nbPersons());
    
    d.kilic's avatar
    d.kilic committed
    
    
                // recalcHeight true, wenn personenhoehe ueber trackpoints neu berechnet werden soll (z.b. um
    
                // waehrend play mehrfachberuecksichtigung von punkten auszuschliessen, aenderungen in altitude neu
                // in berechnung einfliessen zu lassen)
    
                if(mControlWidget->isTrackRecalcHeightChecked())
                {
                    if(mControlWidget->getCalibCoordDimension() == 0) // 3D
    
    d.kilic's avatar
    d.kilic committed
                    {
    
                        ; // Nothing to be done because z already the right height
                    }
                    else // 2D
                    {
                        mPersonStorage.recalcHeight(mControlWidget->getCameraAltitude());
    
    d.kilic's avatar
    d.kilic committed
                    }
    
    d.kilic's avatar
    d.kilic committed
    
    
                mTrackerReal->calculate(
                    this,
                    mTracker,
    
                    mControlWidget->getColorPlot(),
                    mMissingFrames,
                    getImageBorderSize(),
                    mControlWidget->isTrackMissingFramesChecked(),
                    mStereoWidget->stereoUseForExport->isChecked(),
                    mControlWidget->getTrackAlternateHeight(),
                    mControlWidget->getCameraAltitude(),
                    mStereoWidget->stereoUseCalibrationCenter->isChecked(),
                    mControlWidget->isExportElimTpChecked(),
                    mControlWidget->isExportElimTrjChecked(),
                    mControlWidget->isExportSmoothChecked(),
                    mControlWidget->isExportViewDirChecked(),
                    mControlWidget->isExportAngleOfViewChecked(),
                    mControlWidget->isExportMarkerIDChecked(),
                    autoCorrectOnlyExport);
    
    d.kilic's avatar
    d.kilic committed
    
    
    d.kilic's avatar
    d.kilic committed
    
    
                out << "# PeTrack project: " << QFileInfo(getProFileName()).fileName() << Qt::endl;