Skip to content
Snippets Groups Projects
petrack.cpp 142 KiB
Newer Older
/*
 * PeTrack - Software for tracking pedestrians movement in videos
 * Copyright (C) 2010-2020 Forschungszentrum Jülich GmbH,
 * Maik Boltes, Juliane Adrian, Ricardo Martin Brualla, Arne Graf, Paul Häger, Daniel Hillebrand,
 * Deniz Kilic, Paul Lieberenz, Daniel Salden, Tobias Schrödter, Ann Katrin Seemann
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */

#include <QtWidgets>
d.kilic's avatar
d.kilic committed
#include <QtOpenGL>
#include <QSignalMapper>

// Added for Qt5 support
#include <QtPrintSupport/QPrinter>
#include <QtPrintSupport/QPrintDialog>

#include "stereoItem.h"
#include "stereoWidget.h"
#include "colorMarkerItem.h"
#include "multiColorMarkerItem.h"
#include "colorRangeWidget.h"
#include "colorMarkerWidget.h"
#include "codeMarkerWidget.h"
#include "multiColorMarkerWidget.h"
#include "petrack.h"
#include "control.h"
#include "view.h"
#include "gridItem.h"
#include "imageItem.h"
#include "logoItem.h"
#include "animation.h"
#include "player.h"
#include "calibFilter.h"
#include "autoCalib.h"
#include "trackerItem.h"
#include "backgroundItem.h"
#include "moCapItem.h"
d.kilic's avatar
d.kilic committed
#include "tracker.h"
#include "trackerReal.h"
Schrödter, Tobias's avatar
Schrödter, Tobias committed
#include "trackingRoiItem.h"
#include "recognitionRoiItem.h"
#include "openMoCapDialog.h"

#include "aboutDialog.h"
d.kilic's avatar
d.kilic committed
#ifdef AVI
#include "aviFile.h"
#else
#include "aviFileWriter.h"
#endif
#include "person.h"

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 <cmath>
d.kilic's avatar
d.kilic committed

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
Control *cw;

int Petrack::trcVersion = 0;

// Reihenfolge des anlegens der objekte ist sehr wichtig
Petrack::Petrack() : mAuthors(IO::readAuthors(QCoreApplication::applicationDirPath()+ "/.zenodo.json"))
d.kilic's avatar
d.kilic committed
{
    QIcon icon;
    icon.addFile(":/icon"); // about
    icon.addFile(":/icon_smallest"); // window title bar
    setWindowIcon(icon);
d.kilic's avatar
d.kilic committed
    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;
d.kilic's avatar
d.kilic committed
    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;
d.kilic's avatar
d.kilic committed
    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

    mScene = new QGraphicsScene(this);

    mControlWidget = new Control(*this, *mScene, mReco);
d.kilic's avatar
d.kilic committed
    cw =  mControlWidget; // muss spaeter geloescht werden

    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

    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);

    mTrackingRoiItem = new TrackingRoiItem(this);
    mTrackingRoiItem->setZValue(4); // groesser heisst weiter oben

    mRecognitionRoiItem = new RecognitionRoiItem(this);
    mRecognitionRoiItem->setZValue(5); // groesser heisst weiter oben


    mViewWidget = new ViewWidget(this);
    mView = mViewWidget->view();
    mView->setScene(mScene);
    connect(mView, SIGNAL(mouseDoubleClick()), this, SLOT(openSequence()));
    connect(mView, SIGNAL(mouseShiftDoubleClick(QPointF)), this, SLOT(addManualTrackPointOnlyVisible(QPointF))); //const QPoint &pos funktionierte nicht
    connect(mView, SIGNAL(mouseShiftControlDoubleClick(QPointF)), this, SLOT(splitTrackPerson(QPointF))); //const QPoint &pos funktionierte nicht
    connect(mView, SIGNAL(mouseControlDoubleClick(QPointF)), this, SLOT(addOrMoveManualTrackPoint(QPointF))); //const QPoint &pos funktionierte nicht
d.kilic's avatar
d.kilic committed
    connect(mView, SIGNAL(mouseRightDoubleClick(QPointF, int)), this, SLOT(deleteTrackPoint(QPointF, int))); //const QPoint &pos funktionierte nicht
    connect(mView, SIGNAL(mouseMiddleDoubleClick(int)), this, SLOT(deleteTrackPointAll(int))); //const QPoint &pos funktionierte nicht
    connect(mView, SIGNAL(mouseShiftWheel(int)), this, SLOT(skipToFrameWheel(int)));

    mPlayerWidget = new Player(mAnimation, this);

    QVBoxLayout *vLayout = new QVBoxLayout;
    vLayout->setSpacing(space);
    vLayout->addWidget(mViewWidget);
    vLayout->addWidget(mPlayerWidget);

    //---------------------------

    mTracker = new Tracker(this);
    mTrackerReal = new TrackerReal(this);
    mTrackerItem = new TrackerItem(this, mTracker);
    mTrackerItem->setZValue(5); // groesser heisst weiter oben

    mControlWidget->getColorPlot()->setTracker(mTracker);
#ifdef QWT
    mControlWidget->getAnalysePlot()->setTrackerReal(mTrackerReal);
#endif

    //---------------------------

    mStereoItem = new StereoItem(this);
    mStereoItem->setZValue(2);// groesser heisst weiter oben
    mStereoItem->setVisible(false);

    //---------------------------

    mColorMarkerItem = new ColorMarkerItem(this);
    mColorMarkerItem->setZValue(2);// groesser heisst weiter oben
    mColorMarkerItem->setVisible(false);
    //---------------------------

    mCodeMarkerItem = new CodeMarkerItem(this, mReco.getCodeMarkerOptions());
d.kilic's avatar
d.kilic committed
    mCodeMarkerItem->setZValue(2);// groesser heisst weiter oben
    mCodeMarkerItem->setVisible(false);
    //---------------------------

    mMultiColorMarkerItem = new MultiColorMarkerItem(this);
    mMultiColorMarkerItem->setZValue(2);// groesser heisst weiter oben
    mMultiColorMarkerItem->setVisible(false);

    //---------------------------

    mBackgroundItem = new BackgroundItem(this);
    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);
d.kilic's avatar
d.kilic committed
    mSeqFileName = QDir::currentPath(); //fuer allerersten Aufruf des Programms
    readSettings();

    saveXml(mDefaultSettings); //noch nicht noetig, da eh noch nicht fkt

    mShowFPS = 0;

    mTrcFileName = "";

    // initialer Aufruf, damit beim reinen Laden einer Videodatei die Defaultwerte in control genommen werden zum Setzen
    setHeadSize();

    // um im background subtraction filter das hoehenbild zu beruecksichtigen
    mBackgroundFilter.setStereoContext(&mStereoContext);

    mAutoBackTrack = true; // ist der default, dann wenn in XML-Datei nicht drin steht
    mAutoTrackOptimizeColor = false; // ist der default, dann wenn in XML-Datei nicht drin steht

    setLoading(false);
}
Petrack::~Petrack()
{
    delete mImage;
    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())
        event->acceptProposedAction();
}

/**
 * @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())
    {
        if (event->mimeData()->urls().first().toLocalFile().right(4) == ".pet")
            openProject(event->mimeData()->urls().first().toLocalFile());
        else if (event->mimeData()->urls().first().toLocalFile().right(4) == ".trc")
            importTracker(event->mimeData()->urls().first().toLocalFile());
        else
            openSequence(event->mimeData()->urls().first().toLocalFile());
        event->acceptProposedAction();
    }
}

void Petrack::updateSceneRect()
{
    double iW=0, iH=0, bS=0;

    if (mImage)
    {
        iW = mImage->width();
        iH = mImage->height();
        bS = getImageBorderSize();
    }

    if (mControlWidget->getCalibCoordShow())
    {
        double scale=mControlWidget->getCalibCoordScale()/10.;
        double tX=mControlWidget->getCalibCoordTransX()/10.;
        double tY=mControlWidget->getCalibCoordTransY()/10.;

        // setzen der bounding box der scene
        // Faktor 1.1 dient dazu, dass auch Zahl "1" bei coord gut in sichtbaren Bereich passt
        double xMin = (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);
    }
    else
        mScene->setSceneRect(-bS, -bS, iW, iH);
}

/**
 * @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;
    enum Camera cam = cameraUnset;
    setLoading(true);
    for(QDomElement elem = root.firstChildElement(); !elem.isNull(); elem = elem.nextSiblingElement())
    {
        if (elem.tagName() == "MAIN")
        {
            if (elem.hasAttribute("SRC"))
            {
                seq = elem.attribute("SRC");
                QString tmpSeq = getExistingFile(seq, mProFileName);
                if (tmpSeq != "")
                    seq = tmpSeq;
            }
            if (elem.hasAttribute("STATUS_HEIGHT"))
            {
                if (mStatusPosRealHeight) // null kann eigentlich nicht vorkommen, da im constructor von petrack erzeugt wird
                    mStatusPosRealHeight->setValue(elem.attribute("STATUS_HEIGHT").toDouble());
            }
        }
        else if (elem.tagName() == "STEREO")
        {
            mStereoWidget->getXml(elem);
        }
        else if (elem.tagName() == "COLOR_MARKER")
        {
            mColorMarkerWidget->getXml(elem);
        }
        else if (elem.tagName() == "CODE_MARKER")
        {
            mCodeMarkerWidget->getXml(elem);
        }
        else if (elem.tagName() == "MULTI_COLOR_MARKER")
        {
            mMultiColorMarkerWidget->getXml(elem);
        }
        else if (elem.tagName() == "MOCAP")
        {
            mMoCapController.getXml(elem);
        }
d.kilic's avatar
d.kilic committed
        else if (elem.tagName() == "CONTROL")
        {
            mControlWidget->getXml(elem);
            QDomElement tmpElem = (elem.firstChildElement("TRACKING")).firstChildElement("PATH");
            if (tmpElem.hasAttribute("ONLY_PEOPLE_NR"))
                onlyPeopleNr = tmpElem.attribute("ONLY_PEOPLE_NR").toInt();
            if (tmpElem.hasAttribute("ONLY_PEOPLE_NR_LIST"))
                onlyPeopleNrList = tmpElem.attribute("ONLY_PEOPLE_NR_LIST");
        }
        else if (elem.tagName() == "EXTR_CALIBRATION")
        {
            mExtrCalibration.getXml(elem);
        }
d.kilic's avatar
d.kilic committed
        else if (elem.tagName() == "PLAYER")
        {
            if (elem.hasAttribute("FRAME"))
            {
                frame = elem.attribute("FRAME").toInt();
            }
            if (elem.hasAttribute("FPS"))
            {
                fps = elem.attribute("FPS").toDouble();
            }
            if (elem.hasAttribute("SOURCE_FRAME_IN"))
            {
                sourceFrameIn = elem.attribute("SOURCE_FRAME_IN").toInt();
            }
            if (elem.hasAttribute("SOURCE_FRAME_OUT"))
            {
                sourceFrameOut = elem.attribute("SOURCE_FRAME_OUT").toInt();
            }
            if (elem.hasAttribute("PLAYER_SPEED_FIXED"))
            {
                mPlayerWidget->setPlayerSpeedLimited(elem.attribute("PLAYER_SPEED_FIXED").toInt());
d.kilic's avatar
d.kilic committed
            }
        }
        else if (elem.tagName() == "VIEW")
        {
            if (elem.hasAttribute("ANTIALIAS"))
            {
                mAntialiasAct->setChecked(elem.attribute("ANTIALIAS").toInt() == Qt::Checked);
d.kilic's avatar
d.kilic committed
            }
            if (elem.hasAttribute("OPENGL"))
            {
                mOpenGLAct->setChecked(elem.attribute("OPENGL").toInt() == Qt::Checked);
d.kilic's avatar
d.kilic committed
            }
            if (elem.hasAttribute("SAVE_TRANSFORMED"))
            {
                mCropZoomViewAct->setChecked(elem.attribute("SAVE_TRANSFORMED") == Qt::Checked);
d.kilic's avatar
d.kilic committed
            }
            if (elem.hasAttribute("TRANSFORMATION"))
            {
                QString matStr = elem.attribute("TRANSFORMATION");
                QTextStream in(&matStr);
                in >> zoom >> rotate >> hScroll >> vScroll;
            }
            if (elem.hasAttribute("CAMERA"))
            {
                cam = (enum Camera) elem.attribute("CAMERA").toInt();
            }
            if (elem.hasAttribute("HIDE_CONTROLS"))
            {
                mHideControlsAct->setChecked(elem.attribute("HIDE_CONTROLS").toInt() == Qt::Checked);
d.kilic's avatar
d.kilic committed
            }
        } else if (elem.tagName() == "AUTO_TRACK")
        {
            if (elem.hasAttribute("BACK_TRACK"))
            {
                mAutoBackTrack = elem.attribute("BACK_TRACK").toInt();
            }
            if (elem.hasAttribute("OPTIMZE_COLOR"))
            {
                mAutoTrackOptimizeColor = elem.attribute("OPTIMZE_COLOR").toInt();
            }
        }
        else
            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 != ""))
        openSequence(seq); // wenn leer, dann kommt abfrage hoch, welche datei; abbrechen, wenn aktuelle gewuenscht

    mViewWidget->setZoomLevel(zoom);
    mViewWidget->setRotateLevel(rotate);
    mView->horizontalScrollBar()->setValue(hScroll);
    mView->verticalScrollBar()->setValue(vScroll);

    bool loaded = false;
    if (!mBackgroundFilter.getFilename().isEmpty())
    {
        if (!(loaded = mBackgroundFilter.load(mBackgroundFilter.getFilename())))
            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 );
    mPlayerWidget->update();

    if (frame != -1)
    {
        if (mControlWidget->filterBg->isChecked() && !loaded)// mit dem anfangs geladenen bild wurde bereits faelschlicherweise bg bestimmt
            mBackgroundFilter.reset(); // erst nach dem springen zu einem frame background bestimmen
        mPlayerWidget->skipToFrame(frame); // hier wird updateImage ausgefuehrt
    }
    else if (loaded)
        updateImage();

    // nicht schon in control, sonst loescht opensequence wieder tracker
    if (mTrcFileName != "")
    {
        // 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 ((mTracker->largestLastFrame() >= frame) && (mTracker->smallestFirstFrame() <= frame))
        {
            mTracker->clear();
            mTracker->reset();
        }
        importTracker(mTrcFileName);
    }

    mControlWidget->trackShowOnlyNr->setValue(onlyPeopleNr);
    mControlWidget->trackShowOnlyNrList->setText(onlyPeopleNrList);

    if (cam == cameraLeft)
        mCameraLeftViewAct->setChecked(true);
    else if (cam == cameraRight)
        mCameraRightViewAct->setChecked(true);
    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())
            return;
    // 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 (!fileName.isEmpty())
    {
        QFile file(fileName);
        if (!file.open(QIODevice::ReadOnly))
        {
            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))
        {
            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);
        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 = "";
    if (mImage)
        seq = getFileList(mSeqFileName, mProFileName);

    elem.setAttribute("SRC", seq);
    elem.setAttribute("STATUS_HEIGHT", mStatusPosRealHeight->value());
    root.appendChild(elem);

    // control settings (right control widget)
    elem = doc.createElement("CONTROL");
    mControlWidget->setXml(elem);
    root.appendChild(elem);

    // Reprojection error extrinsic calib
    elem = doc.createElement("EXTR_CALIBRATION");
    mExtrCalibration.setXml(elem);
    root.appendChild(elem);

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);
}

/// 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())
    {
        fileName = QFileDialog::getSaveFileName(this, tr("Select project file"), mProFileName,
                                                tr("PeTrack project file (*.pet);;All files (*.*)"));
    }

    if (!fileName.isEmpty())
    {
        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);
        xmlStream.setAutoFormatting(true);
        xmlStream.setAutoFormattingIndent(4);

        xmlStream.writeStartDocument();
        xmlStream.writeDTD("<!DOCTYPE PETRACK>");

        QDomElement element = doc.documentElement();
        writeXmlElement(xmlStream, element);

        xmlStream.writeEndDocument();

        QFile file(fileName);
        if (!file.open(QFile::WriteOnly | QFile::Truncate | QFile::Text))
        {
            PCritical(this, tr("PeTrack"), tr("Cannot save %1:\n%2.").arg(fileName, file.errorString()));
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
        return false;
}

void Petrack::writeXmlElement(QXmlStreamWriter& xmlStream, QDomElement element)
{
    xmlStream.writeStartElement(element.tagName());

    QVector<QString> attribute_names;
    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
    std::stable_sort(attribute_names.begin(), attribute_names.end()); // for a canonical XML

    // Wants this macro instead of range-based for loop
    foreach(QString name, attribute_names){
        QDomAttr attr = element.attributeNode(name);
        xmlStream.writeAttribute(attr.name(), attr.value());
    }

    // order of child nodes is defined at creation
    if(element.hasChildNodes()){
        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)
    {
        // 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))
    {
        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())
        mLogoItem->fadeOut(1);
    else
        mLogoItem->fadeOut(50);
    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())
    {
        if (!mAnimation->openAnimation(fileName))
        {
            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())
        {
            if (mStereoContext)
                delete mStereoContext;
            mStereoContext = new pet::StereoContext(this);
        }

        bool lastIsStereoVideo = mAnimation->isStereoVideo();
d.kilic's avatar
d.kilic committed
        if (mCalibFilter == NULL || (mAnimation->isStereoVideo() != lastIsStereoVideo))
        {
            bool lastCalibFilterEnabled = false;
            if (mCalibFilter != NULL)
            {
                lastCalibFilterEnabled = mCalibFilter->getEnabled();
                delete mCalibFilter;
            }
            if (mAnimation->isStereoVideo())
            {
                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())
            mLogoItem->fadeOut(1);
        else
            mLogoItem->fadeOut(50);
        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();

    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();
d.kilic's avatar
d.kilic committed
        if (mAnimation->isVideo() || mAnimation->isImageSequence())
            title +=  "; ";
    }
    if (mAnimation->isVideo())
        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
    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);
}

void Petrack::saveVideo()
{
    saveSequence(true, false);
}
void Petrack::saveVideoView()
{
    saveSequence(true, true);
}
void Petrack::saveImageSequence()
{
    saveSequence(false, false);
}
void Petrack::saveViewSequence()
{
    saveSequence(false, true);
}


/**
 * @brief Saves current sequence as avi-file or image sequence
 *
 * Saves the loaded image sequence or video from current frame on till the end.
 * One can save the sequence as is or one can save the view shown in PeTrack.
 *
 * @param saveVideo true, if wanting to save a video. Ignored when dest isn't empty
 * @param saveView true, if sequence should be saved as shown in PeTrack (with trajectories etc.)
 * @param dest destination file; if empty, the user chooses via a dialog
 */
d.kilic's avatar
d.kilic committed
void Petrack::saveSequence(bool saveVideo, bool saveView, QString dest) // default saveView= false, dest=""
{
    static QString lastDir;
d.kilic's avatar
d.kilic committed

    // if no destination file or folder is given
    if (dest.isEmpty())
    {
        if (lastDir.isEmpty() && !mSeqFileName.isEmpty())
            lastDir = QFileInfo(mSeqFileName).path();

        if (saveVideo)
        {
            dest = QFileDialog::getSaveFileName(this, tr("Select video file"), lastDir,
                                                tr("Video (*.avi);;All files (*.*)"));  //? *.mpg *.mpeg
        }
        else
        {
            if (saveView)
                dest = QFileDialog::getExistingDirectory(this, tr("Select directory to save view sequence"),
                                                         lastDir, QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks);
            else
                dest = QFileDialog::getExistingDirectory(this, tr("Select directory to save image sequence"),
                                                         lastDir, QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks);
        }
    }
    else // uebergabe von saveVideo spielt keine roll mehr, sondern wird hier analysiert anhand von Dateiendung
    {
//        autoSave = true; // command line option
d.kilic's avatar
d.kilic committed
        if (dest.right(4) == ".avi")
            saveVideo = true;
        else
            saveVideo = false;
    }

    if (!dest.isEmpty() && mImage)
    {
        int rest = mAnimation->getNumFrames()-1;
        int numLength = 1;
        int memPos = mPlayerWidget->getPos();
        QString fileName = "";
#ifdef AVI
        AviFile aviFile;
#else
        AviFileWriter aviFile;
#endif
        bool formatIsSaveAble = false;
        bool saveRet;
        QImage *viewImage = nullptr;
        QPainter *painter = nullptr;
        int progEnd = mAnimation->getSourceOutFrameNum()-mPlayerWidget->getPos(); // nur wenn nicht an anfang gesprungen wird:-mPlayerWidget->getPos()
        cv::Mat iplImgFilteredBGR;
d.kilic's avatar
d.kilic committed
        bool writeFrameRet = false;
        bool convert8To24bit = false;
        int mult;


        if (saveVideo)
        {
            if (saveView)
            {
                if (mCropZoomViewAct->isChecked())
                    viewImage = new QImage(mView->viewport()->width(), mView->viewport()->height(), QImage::Format_RGB32);
                else
                    viewImage = new QImage((int) mScene->width(), (int) mScene->height(), QImage::Format_RGB32);
                painter = new QPainter();
            }
            if (convert8To24bit)
                mult = 3;
            else
                mult = 1;

            bool ok = false;

            if( saveView )
                ok = aviFile.open(dest.toStdString().c_str(), viewImage->width(), viewImage->height(), viewImage->depth(), mAnimation->getFPS());
            else
                ok = aviFile.open(dest.toStdString().c_str(), mImg.cols, mImg.rows, mult*8*mImg.channels(), mAnimation->getFPS());
d.kilic's avatar
d.kilic committed
            if (!ok)
            {
                debout << "Error: opening AVI file: " << dest.toStdString().c_str() << std::endl;
d.kilic's avatar
d.kilic committed
                return;
            }
        }

        if (!saveVideo)
        {
            if (saveView)
            {
                if (mCropZoomViewAct->isChecked())