#include #include #include #include #include #include #include #include #include #include #include #include #include "MovieAnalyzer.h" #include "ProjectModel.h" using namespace cv; using namespace std; MovieAnalyzer::MovieAnalyzer(QWidget *parent) : QWidget(parent) { m_processor = new VideoFrameProcessor; } MovieAnalyzer::~MovieAnalyzer() { } bool MovieAnalyzer::extractVideoFrames(const QString &fName) { int id; m_cap.release(); m_cap.open(fName.toStdString()); QProgressDialog progress(tr("Extracting frames..."), tr("Cancel"), 0, m_cap.get(CV_CAP_PROP_FRAME_COUNT), this); progress.setWindowModality(Qt::WindowModal); emit setResolution(QSize(m_cap.get(CV_CAP_PROP_FRAME_WIDTH), m_cap.get(CV_CAP_PROP_FRAME_HEIGHT))); emit setFps(m_cap.get(CV_CAP_PROP_FPS)); while (m_cap.grab()) { id = m_cap.get(CV_CAP_PROP_POS_FRAMES); progress.setValue(id); if (progress.wasCanceled()) return false; emit appendVideoFrame(id, m_cap.get(CV_CAP_PROP_POS_MSEC)); } return true; } bool MovieAnalyzer::extractShots(const QString &fName, int histoType, int nVBins, int nHBins, int nSBins, int metrics, qreal threshold1, qreal threshold2, int nVBlock, int nHBlock, bool viewProgress) { m_cap.release(); m_cap.open(fName.toStdString()); // current frame Mat frame; // subimages of current frame QVector blocks(nVBlock * nHBlock); // corresponding local histograms QVector locHisto(nVBlock * nHBlock); // distances between current and previous local histograms QVector distance(nVBlock * nHBlock); // distance between current and past frame qreal dist; // frames considered to detect cut QList window; // current frame position and index qint64 position(0); int n(0); // previous frame position qint64 prevPosition(0); // indicates that no more frame is available in video capture bool open(true); // normalized local copies of thresholds qreal thresh1(threshold1 / 100.0); qreal thresh2(threshold2 / 100.0); // activate appropriate metrics switch (metrics) { case 4: m_processor->activL1(); break; case 5: m_processor->activL2(); break; case CV_COMP_CORREL: m_processor->activCorrel(); break; case CV_COMP_CHISQR: m_processor->activChiSqr(); break; case CV_COMP_INTERSECT: m_processor->activIntersect(); break; case CV_COMP_HELLINGER: m_processor->activHellinger(); break; } // progress bar QProgressDialog progress(tr("Extracting shots..."), tr("Cancel"), 0, m_cap.get(CV_CAP_PROP_FRAME_COUNT), this); if (viewProgress) { progress.setWindowModality(Qt::WindowModal); } while (open) { // retrieve frame position n = m_cap.get(CV_CAP_PROP_POS_FRAMES); position = m_cap.get(CV_CAP_PROP_POS_MSEC); open = m_cap.read(frame); if (open) { // retrieving previous shot frame blocks blocks = splitImage(frame, nVBlock, nHBlock); // looping over frame blocks for (int i(0); i < blocks.size(); i++) { // compute V/HS/HSV histogram for each frame block switch (histoType) { case 0: locHisto[i] = m_processor->genVHisto(blocks[i], nVBins); break; case 1: locHisto[i] = m_processor->genHsHisto(blocks[i], nHBins, nSBins); break; case 2: locHisto[i] = m_processor->genHsvHisto(blocks[i], nHBins, nSBins, nVBins); break; } // compute distance from previous frame for each block if (!m_prevLocHisto.isEmpty()) distance[i] = m_processor->distanceFromPrev(locHisto[i], m_prevLocHisto[i]); } dist = meanDistance(distance); /*** for displaying purpose ***/ /* imshow("test1", frame); waitKey(0); qDebug(); for (int i(0); i < nHBlock; i++) { for (int j(0); j < nVBlock; j++) { printf("%4.4f\t", 1 - distance[i * nVBlock + j]); } cout << endl; } qDebug(); qDebug() << (1 - dist); */ // append current distance to the window window.push_back(dist); if (window.size() > 3) window.pop_front(); // insert shot at current position if (window[0] <= thresh2 && window[1] >= thresh1 && window[2] <= thresh2) { emit insertShot(prevPosition, Segment::Automatic); } // updating previous local histograms to current ones m_prevLocHisto.resize(blocks.size()); m_prevLocHisto = locHisto; // updating previous position to current prevPosition = position; } // update progress bar if (viewProgress) { progress.setValue(n); if (progress.wasCanceled()) return false; } } return true; } bool MovieAnalyzer::labelSimilarShots(QString fName, int histoType, int nVBins, int nHBins, int nSBins, int metrics, qreal maxDist, int windowSize, const QList &shotPositions, int nVBlock, int nHBlock, bool viewProgress) { m_cap.release(); m_cap.open(fName.toStdString()); // distance matrix between shots Mat dist(shotPositions.size(), shotPositions.size(), CV_32FC1, Scalar(-1.0)); // previous and current shot frame Mat prevShotFrame; Mat currShotFrame; // subimages of current and past frames QVector prevBlocks; QVector currBlocks; // local histogram of each frame block QVector locHisto(nVBlock * nHBlock); // accumulator of previous lists of local histograms QList> locHistoBuffer; // distance between first frame of current shot and last frame of past shot QVector distance(nVBlock * nHBlock); /*** for displaying purpose ***/ QList frameBuffer; // frame duration // doesn't work anymore after updating Ubuntu // int frameDur = 1 / m_cap.get(CV_CAP_PROP_FPS) * 1000; int frameDur = 1 / 25.0 * 1000; // distance between local histograms qreal locDistance; // minimum distance from current frame observed so far qreal minDistance; // corresponding index int jMin; // current camera label int nCamera(0); // camera label for each shot QVector shotCamera(shotPositions.size()); // activate appropriate metrics switch (metrics) { case 4: m_processor->activL1(); break; case 5: m_processor->activL2(); break; case CV_COMP_CORREL: m_processor->activCorrel(); break; case CV_COMP_CHISQR: m_processor->activChiSqr(); break; case CV_COMP_INTERSECT: m_processor->activIntersect(); break; case CV_COMP_HELLINGER: m_processor->activHellinger(); break; } // progress bar QProgressDialog progress(tr("Retrieving similar shots..."), tr("Cancel"), 0, shotPositions.size(), this); if (viewProgress) progress.setWindowModality(Qt::WindowModal); // normalizing max distance required to link two shots maxDist /= 100.0; // looping over shot positions shotCamera[0] = nCamera; for (int i(1); i < shotPositions.size(); i++) { /****************************/ /* processing previous shot */ /****************************/ m_cap.set(CV_CAP_PROP_POS_MSEC, shotPositions[i] - frameDur); m_cap >> prevShotFrame; // retrieving previous shot frame blocks prevBlocks = splitImage(prevShotFrame, nVBlock, nHBlock); // resizing local histograms vector if necessary if (prevBlocks.size() != nVBlock * nHBlock) locHisto.resize(prevBlocks.size()); // looping over previous shot frame blocks for (int j(0); j < prevBlocks.size(); j++) // compute V/HS/HSV histogram for each block of previous shot last frame switch (histoType) { case 0: locHisto[j] = m_processor->genVHisto(prevBlocks[j], nVBins); break; case 1: locHisto[j] = m_processor->genHsHisto(prevBlocks[j], nHBins, nSBins); break; case 2: locHisto[j] = m_processor->genHsvHisto(prevBlocks[j], nHBins, nSBins, nVBins); break; } /*** for displaying purpose ***/ /* Mat frameCopy; prevShotFrame.copyTo(frameCopy); frameBuffer.push_front(frameCopy); if (frameBuffer.size() == windowSize + 1) frameBuffer.pop_back(); */ // saving list of corresponding histograms locHistoBuffer.push_front(locHisto); // update buffer boundaries if (locHistoBuffer.size() == windowSize + 1) locHistoBuffer.pop_back(); /***************************/ /* processing current shot */ /***************************/ m_cap >> currShotFrame; /*** for displaying purpose ***/ // imshow("present", currShotFrame); // retrieving current shot frame blocks currBlocks = splitImage(currShotFrame, nVBlock, nHBlock); // resizing local histograms vector if necessary if (currBlocks.size() != nVBlock * nHBlock) { locHisto.resize(currBlocks.size()); distance.resize(currBlocks.size()); } // looping over current shot frame blocks for (int j(0); j < currBlocks.size(); j++) // compute V/HS/HSV histogram for each block of current shot last frame switch (histoType) { case 0: locHisto[j] = m_processor->genVHisto(currBlocks[j], nVBins); break; case 1: locHisto[j] = m_processor->genHsHisto(currBlocks[j], nHBins, nSBins); break; case 2: locHisto[j] = m_processor->genHsvHisto(currBlocks[j], nHBins, nSBins, nVBins); break; } // compute distance between current and past frame local histograms minDistance = 1.0; jMin = 0; for (int j(0); j < locHistoBuffer.size(); j++) { // retrieving past list of local histograms QVector prevLocHisto = locHistoBuffer[j]; // looping over list of local histograms and computing local distances for (int k(0); k < prevLocHisto.size(); k++) distance[k] = m_processor->distanceFromPrev(locHisto[k], prevLocHisto[k]); // averaging local distances locDistance = meanDistance(distance); /*** for displaying purpose ***/ /* qDebug(); for (int k(0); k < nHBlock; k++) { for (int l(0); l < nVBlock; l++) { printf("%4.4f\t", 1 - distance[k * nVBlock + l]); } cout << endl; } qDebug(); qDebug() << (1 - locDistance); imshow("past", frameBuffer[j]); waitKey(0); */ if (locDistance < minDistance) { minDistance = locDistance; jMin = j; } // updating distance matrix dist.at(i, i-j-1) = locDistance; } // similar shot retrieved among previous shots if (minDistance <= maxDist) { int iSim = i - (jMin + 1); shotCamera[i] = shotCamera[iSim]; emit labelSimShot(shotPositions[iSim], shotCamera[iSim], Segment::Automatic); emit labelSimShot(shotPositions[i], shotCamera[i], Segment::Automatic); } // no similar shot retrieved: new camera label else shotCamera[i] = nCamera++; // updating progress bar if (viewProgress) { progress.setValue(i); if (progress.wasCanceled()) return false; } } return true; } bool MovieAnalyzer::localSpkDiar(QList> subBound, QMap>> shotPatterns, QMap>> strictShotPattBound, UtteranceTree::DistType dist, bool norm, UtteranceTree::AgrCrit agr, UtteranceTree::PartMeth partMeth, bool weight, bool sigma, const QString &baseName) { QMap>>::const_iterator it = shotPatterns.begin(); QMap uttLabels; // speaker label assigned to each utterance QString pattLabel; // pattern label int uttIdx; // utterance index qreal uttStart; // utterance beginning in s qreal uttEnd; // utterance ending in s UtteranceTree tree; // dendogram corresponding to local clustering QList> partition; // optimal partition of the tree qreal beginning, end; // boundaries of movie subpart considered QString workPath("spkDiarization/"); QProcess process; QString program; QStringList arguments; QString args; int out; m_baseName = baseName; qDebug() << "Local agglomerative clustering..."; // initializing boundaries beginning = subBound[subBound.size() - 1].second / 1000.0; end = subBound[0].first / 1000.0; // output file QString fName = workPath + "lblLocalSegmentation/" + m_baseName + ".lbl"; QFile lblFile(fName); if (!lblFile.open(QIODevice::WriteOnly | QIODevice::Text)) return false; QTextStream lblOut(&lblFile); // parameterizing tree tree.setDist(dist); tree.setAgr(agr); tree.setPartMeth(partMeth); // setting covariance matrix if (sigma) CovInv = pinv(Sigma); else CovInv = pinv(CovW); // normalize E if necessary if (norm) normalize(dist); // looping over shot patterns while (it != shotPatterns.end()) { // current pattern label pattLabel = it.key(); // pattern boundaries QList> shotPattBound = strictShotPattBound[pattLabel]; // utterances covered by current pattern QList> utter = it.value(); // indices of utterances in current pattern arma::umat V(1, utter.size()); for (int i(0); i < utter.size(); i++) V(0, i) = utter[i].first; // matrix containing utterance i-vectors for current pattern arma::mat S = E.rows(V); // weighting utterances arma::mat W(1, utter.size(), arma::fill::ones); if (weight) for (int i(0); i < utter.size(); i++) W(0, i) = utter[i].second; else W.ones(1, utter.size()); W /= arma::accu(W); // setting tree tree.setTree(S, W, CovInv); // retrieving optimal partition partition = tree.getPartition(); // looping over partition for labeling purpose for (int i(0); i < partition.size(); i++) { for (int j(0); j < partition[i].size(); j++) { // index of current utterance uttIdx = utter[partition[i][j]].first; // adjusting utterance boundaries to shot pattern ones QPair pair = adjustSubBoundaries(subBound[uttIdx].first, subBound[uttIdx].second, shotPattBound); uttStart = static_cast(pair.first) / 1000.0; uttEnd = static_cast(pair.second) / 1000.0; // labelling current utterance QString currSpk = pattLabel + "_S" + QString::number(i); lblOut << uttStart << " " << uttEnd << " " << currSpk << endl; emit setSpeaker(static_cast(uttStart * 1000), static_cast(uttEnd * 1000), currSpk, VideoFrame::Hyp1); // updating boundaries of speech segments if (uttStart < beginning) beginning = uttStart; if (uttEnd > end) end = uttEnd; } } it++; } qDebug() << "Done."; // converting hypotheses .lbl file into .rttm one program = "perl"; arguments << workPath + "scripts/SpkMoulinette.pl" << workPath + "lblLocalSegmentation/" + m_baseName + ".lbl" << workPath + "rttmTmp/" + m_baseName + ".rttm"; process.start(program, arguments); process.waitForFinished(); arguments.clear(); // evaluating hypotheses program = "perl "; args = workPath + "scripts/md-eval-v21.pl" + " -af -m -1 -c 0.25 -r " + workPath + "data/ref/seg/" + m_baseName + ".rttm" + " -s " + workPath + "rttmTmp/" + m_baseName + ".rttm" + " -u " + workPath + "data/ref/local/" + m_baseName + ".uem > " + workPath + "score/local/" + m_baseName + ".nistres"; out = std::system(qPrintable(program + args)); return true; } bool MovieAnalyzer::globalSpkDiar(const QString &baseName) { UtteranceTree tree; // dendogram corresponding to global clustering QList> partition; // selected partition of the tree QList> spkFeatures; // speakers references and weights qreal weight; QMap spkWeight; QString speaker; m_baseName = baseName; // weighting speakers QMap>>::const_iterator it = m_utterances.begin(); while (it != m_utterances.end()) { speaker = it.key(); QList> utterances = it.value(); spkWeight[speaker] = 0.0; for (int i(0); i < utterances.size(); i++) spkWeight[speaker] += utterances[i].second - utterances[i].first; it++; } // setting features it = m_utterances.begin(); spkFeatures.clear(); while (it != m_utterances.end()) { speaker = it.key(); weight = spkWeight[speaker]; spkFeatures.push_back(QPair(m_speakers.indexOf(speaker), weight)); it++; } // sending parameters to tree monitor m_treeMonitor = new SpkDiarMonitor(900, 300, true); m_treeMonitor->setDiarData(E, Sigma, CovW); m_treeMonitor->setSpeakers(m_speakers, spkWeight); m_treeMonitor->getCurrentPattern(spkFeatures); m_treeMonitor->show(); // receiving playing signal from dendogram widget connect(m_treeMonitor, SIGNAL(playSub(QList)), this, SLOT(playSpeakers(QList))); // get partition for default parameters connect(m_treeMonitor, SIGNAL(setSpeakerPartition(QList>)), this, SLOT(setSpeakerPartition(QList>))); // sending scores obtained to diarization monitor connect(this, SIGNAL(setLocalDer(const QString &)), m_treeMonitor, SLOT(setLocalDer(const QString &))); connect(this, SIGNAL(setGlobalDer(const QString &)), m_treeMonitor, SLOT(setGlobalDer(const QString &))); m_treeMonitor->exportResults(); return true; } bool MovieAnalyzer::localSpkDiarBaseline(QMap>> shotPatterns, QList> subBound, QMap>> shotUtterances, QMap>> strictShotPattBound, const QString &baseName) { QMap>>::const_iterator it = shotPatterns.begin(); QString patternLabel; QString currSpk; QStringList labelList; QString firstLabel; QString secondLabel; qreal uttStart; qreal uttEnd; QPair patternShots; QString workPath("spkDiarization/"); QProcess process; QString program; QStringList arguments; QString args; int out; m_baseName = baseName; // output file QString fName = workPath + "lblLocalSegmentation/" + m_baseName + ".lbl"; QFile lblFile(fName); if (!lblFile.open(QIODevice::WriteOnly | QIODevice::Text)) return false; QTextStream lblOut(&lblFile); // looping over shot patterns while (it != shotPatterns.end()) { // current pattern label patternLabel = it.key(); // pattern boundaries QList> shotPattBound = strictShotPattBound[patternLabel]; // retrieving each pattern part labelList = patternLabel.split("_"); firstLabel = labelList[0]; secondLabel = labelList[1]; // remove additional characters QRegularExpression openPar(QRegularExpression::escape("(")); QRegularExpression closePar(QRegularExpression::escape(")")); firstLabel.replace(openPar, ""); firstLabel.replace(closePar, ""); secondLabel.replace(openPar, ""); secondLabel.replace(closePar, ""); // equivalent shots in each pattern part patternShots.first = firstLabel.split("|"); patternShots.second = secondLabel.split("|"); // looping over pattern utterances QList> utterances = it.value(); for (int i(0); i < utterances.size(); i++) { int uttIdx = utterances[i].first; // adjusting utterance boundaries to shot pattern ones QPair pair = adjustSubBoundaries(subBound[uttIdx].first, subBound[uttIdx].second, shotPattBound); uttStart = static_cast(pair.first) / 1000.0; uttEnd = static_cast(pair.second) / 1000.0; // labelling current utterance according to the corresponding shot bool found(false); int j(0); while (j < patternShots.first.size() && !found) { if (shotUtterances[patternShots.first[j]].contains(utterances[i])) { currSpk = patternLabel + "_S0"; found = true; } j++; } j = 0; while (j < patternShots.second.size() && !found) { if (shotUtterances[patternShots.second[j]].contains(utterances[i])) { currSpk = patternLabel + "_S1"; found = true; } j++; } lblOut << uttStart << " " << uttEnd << " " << currSpk << endl; emit setSpeaker(static_cast(uttStart * 1000), static_cast(uttEnd * 1000), currSpk, VideoFrame::Hyp1); } it++; } // converting hypotheses .lbl file into .rttm one program = "perl"; arguments << workPath + "scripts/SpkMoulinette.pl" << workPath + "lblLocalSegmentation/" + m_baseName + ".lbl" << workPath + "rttmTmp/" + m_baseName + ".rttm"; process.start(program, arguments); process.waitForFinished(); arguments.clear(); // evaluating hypotheses program = "perl "; args = workPath + "scripts/md-eval-v21.pl" + " -af -m -1 -c 0.25 -r " + workPath + "data/ref/seg/" + m_baseName + ".rttm" + " -s " + workPath + "rttmTmp/" + m_baseName + ".rttm" + " -u " + workPath + "data/ref/local/" + m_baseName + ".uem > " + workPath + "score/local/" + m_baseName + ".nistres"; out = std::system(qPrintable(program + args)); return true; } QVector MovieAnalyzer::splitImage(const Mat &frame, int nVBlock, int nHBlock) { QVector blocks; int blockVSize(frame.rows / nVBlock); int blockHSize(frame.cols / nHBlock); int width(0); int height(0); for (int i(0); i < frame.rows; i += blockVSize) { height = (i + blockVSize > frame.rows) ? (frame.rows - i) : blockVSize; for (int j(0); j < frame.cols; j += blockHSize) { width = (j + blockHSize > frame.cols) ? (frame.cols - j) : blockHSize; blocks.push_back(frame(Rect(j, i, width, height))); } } return blocks; } qreal MovieAnalyzer::meanDistance(const QVector &distance) { qreal sum(0.0); for (int i(0); i < distance.size(); i++) sum += abs(1 - distance[i]); return 1 - sum / distance.size(); } /////////////////////// // auxiliary methods // /////////////////////// void MovieAnalyzer::setDiarData(const arma::mat &E, const arma::mat &Sigma, const arma::mat &W) { this->E = E; this->Sigma = Sigma; this->CovW = W; } void MovieAnalyzer::setDiarData(const arma::mat &E, const arma::mat &Sigma, const arma::mat &W, QMap>> speakers) { setDiarData(E, Sigma, W); m_utterances = speakers; QMap>>::const_iterator it = m_utterances.begin(); m_speakers.clear(); while (it != m_utterances.end()) { m_speakers.push_back(it.key()); it++; } } void MovieAnalyzer::normalize(UtteranceTree::DistType dist) { qreal normFac; for (arma::uword i(0); i < E.n_rows; i++) { switch (dist) { case UtteranceTree::L2: normFac = arma::as_scalar(E.row(i) * E.row(i).t()); break; case UtteranceTree::Mahal: normFac = arma::as_scalar(E.row(i) * CovInv * E.row(i).t()); break; } E.row(i) = E.row(i) / normFac; } } QPair MovieAnalyzer::adjustSubBoundaries(qint64 subStart, qint64 subEnd, QList> shotPattBound) { bool found(false); int i(0); while (i < shotPattBound.size() && !found) { // truncate utterance at the beginning if (subStart < shotPattBound[i].first && subEnd > shotPattBound[i].first) { subStart = shotPattBound[i].first; found = true; } // utterance included in shot else if (subStart >= shotPattBound[i].first && subEnd <= shotPattBound[i].second) found = true; // truncate utterance at the end else if (subStart < shotPattBound[i].second && subEnd > shotPattBound[i].second) { subEnd = shotPattBound[i].second; found = true; } i++; } return QPair(subStart, subEnd); } /////////// // slots // /////////// void MovieAnalyzer::setSpeakerPartition(QList> partition) { QString speaker; QList> utterances; QString workPath("spkDiarization/"); QProcess process; QString program; QStringList arguments; QString args; int out; QString fName = workPath + "lblGlobalSegmentation/" + m_baseName; QFile lblFile(fName + ".lbl"); if (!lblFile.open(QIODevice::WriteOnly | QIODevice::Text)) return; QTextStream lblOut(&lblFile); // looping over the partition elements for (int i(0); i < partition.size(); i++) { QList part = partition[i]; // looping over each part for (int j(0); j < part.size(); j++) { speaker = m_speakers[part[j]]; utterances = m_utterances[speaker]; // looping over the utterances of each speaker for (int k(0); k < utterances.size(); k++) { lblOut << utterances[k].first << " " << utterances[k].second << " " << ("S" + QString::number(i)) << endl; emit setSpeaker(static_cast(utterances[k].first * 1000), static_cast(utterances[k].second * 1000), "S" + QString::number(i), VideoFrame::Hyp1); } } } // converting hypotheses .lbl file into .rttm one program = "perl"; arguments << workPath + "scripts/SpkMoulinette.pl" << workPath + "lblGlobalSegmentation/" + m_baseName + ".lbl" << workPath + "rttmTmp/" + m_baseName + ".rttm"; process.start(program, arguments); process.waitForFinished(); arguments.clear(); // evaluating hypotheses program = "perl "; args = workPath + "scripts/md-eval-v21.pl" + " -af -m -1 -c 0.25 -r " + workPath + "data/ref/local/" + m_baseName + ".rttm" + " -s " + workPath + "rttmTmp/" + m_baseName + ".rttm" + " -u " + workPath + "data/ref/local/" + m_baseName + ".uem > " + workPath + "score/global/" + m_baseName + ".nistres"; out = std::system(qPrintable(program + args)); // retrieving local and global DER emit setLocalDer(retrieveDer(workPath + "score/local/" + m_baseName + ".nistres")); emit setGlobalDer(retrieveDer(workPath + "score/global/" + m_baseName + ".nistres")); // removing files QFile::remove(workPath + "lblLocalSegmentation/" + m_baseName + ".lbl"); QFile::remove(workPath + "lblGlobalSegmentation/" + m_baseName + ".lbl"); QFile::remove(workPath + "rttmTmp/" + m_baseName + ".rttm"); } void MovieAnalyzer::playSpeakers(QList speakers) { QList> utterances; QList> segments; QString speaker; for (int i(0); i < speakers.size(); i++) { speaker = m_speakers[speakers[i]]; utterances = m_utterances[speaker]; for (int j(0); j < utterances.size(); j++) segments.push_back(QPair(utterances[j].first * 1000, utterances[j].second * 1000)); } emit playSegments(segments); } QString MovieAnalyzer::retrieveDer(const QString fName) { QRegularExpression score("OVERALL SPEAKER DIARIZATION ERROR = (\\d{2}.\\d{2})"); QFile file(fName); if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) return "00.00"; QTextStream in(&file); // parsing score file while (!in.atEnd()) { QString line = in.readLine(); QRegularExpressionMatch match = score.match(line); if (match.hasMatch()) return match.captured(1); } return "00.00"; }