/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Copyright 2012 The MITRE Corporation * * * * Licensed under the Apache License, Version 2.0 (the "License"); * * you may not use this file except in compliance with the License. * * You may obtain a copy of the License at * * * * http://www.apache.org/licenses/LICENSE-2.0 * * * * Unless required by applicable law or agreed to in writing, software * * distributed under the License is distributed on an "AS IS" BASIS, * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * * See the License for the specific language governing permissions and * * limitations under the License. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #include #include #include #include #include #include #include #include #ifndef BR_EMBEDDED #include #include #include #endif // BR_EMBEDDED #include #include #include #include #include #include #include #include #include #include "core/bee.h" #include "core/common.h" #include "core/opencvutils.h" #include "core/qtutils.h" using namespace br; /*! * \ingroup outputs * \brief Comma separated values output. * \author Josh Klontz \cite jklontz */ class csvOutput : public MatrixOutput { Q_OBJECT ~csvOutput() { if (file.isNull() || targetFiles.isEmpty() || queryFiles.isEmpty()) return; QFile out(file); bool success = out.open(QFile::WriteOnly); if (!success) qFatal("Output::saveCSV failed to open %s for writing.", qPrintable((QString)file)); out.write("File,"); out.write(qPrintable(targetFiles.names().join(","))); out.write("\n"); for (int i=0; i args = file.localMetadata(); args.remove("Genuine"); args.remove("Impostor"); QString keys; foreach (const QString &key, args.keys()) keys += "," + key; QString values; foreach (const QVariant &value, args.values()) values += "," + value.toString(); QStringList lines; if (file.baseName() != "terminal") lines.append(QString("Query,Target,Mask,Similarity%1").arg(keys)); QList queryLabels = queryFiles.labels(); QList targetLabels = targetFiles.labels(); for (int i=0; i(i,j)), values)); } } QtUtils::writeFile(file, lines); } }; BR_REGISTER(Output, meltOutput) /*! * \ingroup outputs * \brief \ref simmat output. * \author Josh Klontz \cite jklontz */ class mtxOutput : public MatrixOutput { Q_OBJECT ~mtxOutput() { if (file.isNull() || targetFiles.isEmpty() || queryFiles.isEmpty()) return; BEE::writeSimmat(data, file.name); } }; BR_REGISTER(Output, mtxOutput) /*! * \ingroup outputs * \brief Rank retrieval output. * \author Josh Klontz \cite jklontz */ class rrOutput : public MatrixOutput { Q_OBJECT ~rrOutput() { if (file.isNull() || targetFiles.isEmpty() || queryFiles.isEmpty()) return; const int limit = file.getInt("limit", 20); const bool flat = file.getBool("flat"); const bool index = file.getBool("index"); const bool score = file.getBool("score"); const bool invert = file.getBool("invert"); QStringList lines; for (int i=0; i Pair; foreach (const Pair &pair, Common::Sort(OpenCVUtils::matrixToVector(data.row(i)), !invert).mid(0, limit)) files.append((index ? QString::number(pair.second) : targetFiles[pair.second].name) + (score ? "=" + QString::number(pair.first) : "")); lines.append(files.join(flat ? "\n" : ",")); } QtUtils::writeFile(file, lines); } }; BR_REGISTER(Output, rrOutput) /*! * \ingroup outputs * \brief Text file output. * \author Josh Klontz \cite jklontz */ class txtOutput : public MatrixOutput { Q_OBJECT ~txtOutput() { if (file.isNull() || targetFiles.isEmpty() || queryFiles.isEmpty()) return; QStringList lines; foreach (const File &file, queryFiles) lines.append(file.name + " " + file.subject()); QtUtils::writeFile(file, lines); } }; BR_REGISTER(Output, txtOutput) /*! * \ingroup outputs * \brief Output to the terminal. * \author Josh Klontz \cite jklontz */ class EmptyOutput : public MatrixOutput { Q_OBJECT static QString bufferString(const QString &string, int length) { if (string.size() >= length) return string.left(length); QString buffer; buffer.fill(' ', length-string.size()); return string+buffer; } ~EmptyOutput() { if (targetFiles.isEmpty() || queryFiles.isEmpty()) return; QString result; if ((queryFiles.size() == 1) && (targetFiles.size() == 1)) { result = toString(0,0) + "\n"; } else { const int CELL_SIZE = 12; result = bufferString(" ", CELL_SIZE) + " "; foreach (const QString &targetName, targetFiles.names()) result += bufferString(targetName, CELL_SIZE) + " "; result += "\n"; for (int i=0; i comparisons; QMutex comparisonsLock; ~tailOutput() { if (file.isNull() || comparisons.isEmpty()) return; QStringList lines; lines.reserve(comparisons.size()+1); lines.append("Value,Target,Query"); foreach (const Comparison &duplicate, comparisons) lines.append(duplicate.toString(args)); QtUtils::writeFile(file, lines); } void initialize(const FileList &targetFiles, const FileList &queryFiles) { Output::initialize(targetFiles, queryFiles); threshold = file.getFloat("threshold", -std::numeric_limits::max()); atLeast = file.getInt("atLeast", 1); atMost = file.getInt("atMost", std::numeric_limits::max()); args = file.getBool("args"); lastValue = -std::numeric_limits::max(); } void set(float value, int i, int j) { // Return early for self similar matrices if (selfSimilar && (i <= j)) return; // Consider only values passing the criteria if ((value < threshold) && (value <= lastValue) && (comparisons.size() >= atLeast)) return; comparisonsLock.lock(); if (comparisons.isEmpty() || (value < comparisons.last().value)) { // Special cases comparisons.append(Comparison(queryFiles[i], targetFiles[j], value)); } else { // General case for (int k=0; k atMost) comparisons.removeLast(); while ((comparisons.size() > atLeast) && (comparisons.last().value < threshold)) comparisons.removeLast(); lastValue = comparisons.last().value; comparisonsLock.unlock(); } }; BR_REGISTER(Output, tailOutput) /*! * \ingroup outputs * \brief The highest scoring matches. * \author Josh Klontz \cite jklontz */ class bestOutput : public Output { Q_OBJECT typedef QPair< float, QPair > BestMatch; QList bestMatches; ~bestOutput() { if (file.isNull() || bestMatches.isEmpty()) return; qSort(bestMatches); QStringList lines; lines.reserve(bestMatches.size()+1); lines.append("Value,Target,Query"); for (int i=bestMatches.size()-1; i>=0; i--) lines.append(QString::number(bestMatches[i].first) + "," + targetFiles[bestMatches[i].second.second] + "," + queryFiles[bestMatches[i].second.first]); QtUtils::writeFile(file, lines); } void initialize(const FileList &targetFiles, const FileList &queryFiles) { Output::initialize(targetFiles, queryFiles); bestMatches.reserve(queryFiles.size()); for (int i=0; i::max(), QPair(-1, -1))); } void set(float value, int i, int j) { static QMutex lock; // Return early for self similar matrices if (selfSimilar && (i == j)) return; if (value > bestMatches[i].first) { lock.lock(); if (value > bestMatches[i].first) bestMatches[i] = BestMatch(value, QPair(i,j)); lock.unlock(); } } }; BR_REGISTER(Output, bestOutput) /*! * \ingroup outputs * \brief Score histogram. * \author Josh Klontz \cite jklontz */ class histOutput : public Output { Q_OBJECT float min, max, step; QVector bins; ~histOutput() { if (file.isNull() || bins.isEmpty()) return; QStringList counts; foreach (int count, bins) counts.append(QString::number(count)); const QString result = counts.join(","); QtUtils::writeFile(file, result); } void initialize(const FileList &targetFiles, const FileList &queryFiles) { Output::initialize(targetFiles, queryFiles); min = file.getFloat("min", -5); max = file.getFloat("max", 5); step = file.getFloat("step", 0.1); bins = QVector((max-min)/step, 0); } void set(float value, int i, int j) { (void) i; (void) j; if ((value < min) || (value >= max)) return; bins[(value-min)/step]++; // This should technically be locked to ensure atomic increment } }; BR_REGISTER(Output, histOutput) #include "output.moc"