diff --git a/sdk/core/bee.cpp b/sdk/core/bee.cpp index d5f70d6..fe65fa2 100644 --- a/sdk/core/bee.cpp +++ b/sdk/core/bee.cpp @@ -225,64 +225,6 @@ void BEE::writeMask(const Mat &m, const QString &mask, const QString &targetSigs writeMatrix(m, mask, targetSigset, querySigset); } -template -void matrixToCSV(const QString &matrix, const QString &csv) -{ - qDebug("Converting %s to %s", qPrintable(matrix), qPrintable(csv)); - - QFile out(csv); - out.open(QFile::WriteOnly); - - Mat m = readMatrix(matrix); - for (int i=0; i(i,j)))); - out.write(","); - } - out.write("\n"); - } -} - -void BEE::simmatToCSV(const QString &simmat, const QString &csv) -{ - matrixToCSV(simmat, csv); -} - -void BEE::maskToCSV(const QString &mask, const QString &csv) -{ - matrixToCSV(mask, csv); -} - -template -void CSVToMatrix(const QString &csv, const QString &matrix) -{ - qDebug("Converting %s to %s", qPrintable(csv), qPrintable(matrix)); - - QStringList lines = QtUtils::readLines(csv); - Mat m(lines.size(), lines.first().split(",", QString::SkipEmptyParts).size(), OpenCVType::make()); - - for (int i=0; i(i, j) = words[j].toFloat(&ok); - if (!ok) qFatal("bee.cpp::CSVToMatrix failed to convert %s to floating point format.", qPrintable(words[j])); - } - } - - writeMatrix(m, matrix, "Unknown_Target", "Unknown_Query"); -} - -void BEE::CSVToSimmat(const QString &csv, const QString &simmat) -{ - CSVToMatrix(csv, simmat); -} - -void BEE::CSVToMask(const QString &csv, const QString &mask) -{ - CSVToMatrix(csv, mask); -} - void BEE::makeMask(const QString &targetInput, const QString &queryInput, const QString &mask) { qDebug("Making mask from %s and %s to %s", qPrintable(targetInput), qPrintable(queryInput), qPrintable(mask)); diff --git a/sdk/core/bee.h b/sdk/core/bee.h index b16c9c9..f51c1c3 100644 --- a/sdk/core/bee.h +++ b/sdk/core/bee.h @@ -44,12 +44,6 @@ namespace BEE void writeSimmat(const cv::Mat &m, const QString &simmat, const QString &targetSigset = "Unknown_Target", const QString &querySigset = "Unknown_Query"); void writeMask(const cv::Mat &m, const QString &mask, const QString &targetSigset = "Unknown_Target", const QString &querySigset = "Unknown_Query"); - // CSV IO - void simmatToCSV(const QString &simmat, const QString &csv); - void maskToCSV(const QString &mask, const QString &csv); - void CSVToSimmat(const QString &csv, const QString &simmat); - void CSVToMask(const QString &csv, const QString &mask); - // Write BEE files void makeMask(const QString &targetInput, const QString &queryInput, const QString &mask); void combineMasks(const QStringList &inputMasks, const QString &outputMask, const QString &method); diff --git a/sdk/core/core.cpp b/sdk/core/core.cpp index 48dc432..d26c8e8 100644 --- a/sdk/core/core.cpp +++ b/sdk/core/core.cpp @@ -305,6 +305,13 @@ void br::Compare(const File &targetGallery, const File &queryGallery, const File AlgorithmManager::getAlgorithm(output.getString("algorithm"))->compare(targetGallery, queryGallery, output); } +void br::Convert(const File &src, const File &dst) +{ + QScopedPointer before(Factory::make(src)); + QScopedPointer after(Factory::make(dst)); + after->write(before->read()); +} + QSharedPointer br::Transform::fromAlgorithm(const QString &algorithm) { return AlgorithmManager::getAlgorithm(algorithm)->transform; diff --git a/sdk/core/plot.cpp b/sdk/core/plot.cpp index e12451f..91f3fc8 100644 --- a/sdk/core/plot.cpp +++ b/sdk/core/plot.cpp @@ -29,7 +29,6 @@ #include #include #include -#include #include "plot.h" #include "version.h" @@ -40,9 +39,11 @@ #undef FAR // Windows preprecessor definition using namespace cv; -using namespace br; -void br::Confusion(const QString &file, float score, int &true_positives, int &false_positives, int &true_negatives, int &false_negatives) +namespace br +{ + +void Confusion(const QString &file, float score, int &true_positives, int &false_positives, int &true_negatives, int &false_negatives) { qDebug("Computing confusion matrix of %s at %f", qPrintable(file), score); @@ -123,7 +124,7 @@ static float kernelDensityEstimation(const QList &vals, double x, double return y / (vals.size() * h); } -float br::Evaluate(const QString &simmat, const QString &mask, const QString &csv) +float Evaluate(const QString &simmat, const QString &mask, const QString &csv) { qDebug("Evaluating %s with %s", qPrintable(simmat), qPrintable(mask)); @@ -294,11 +295,21 @@ struct RPlot QFile file; QStringList pivotHeaders; QVector< QSet > pivotItems; - int majorIndex, minorIndex, majorSize, minorSize; - QString majorHeader, minorHeader; bool flip; - RPlot(QStringList files, const QString &destination, bool isEvalFormat = true) + struct Pivot + { + int index, size; + QString header; + bool smooth; + Pivot() : index(-1), size(0), smooth(false) {} + Pivot(int _index, int _size, const QString &_header) + : index(_index), size(_size), header(_header), smooth(false) {} + }; + + Pivot major, minor; + + RPlot(QStringList files, const br::File &destination, bool isEvalFormat = true) { if (files.isEmpty()) qFatal("RPlot::RPlot() empty file list."); qSort(files.begin(), files.end(), sortFiles); @@ -388,23 +399,25 @@ struct RPlot file.write("\n" "# Write figures\n"); - majorIndex = -1, minorIndex = -1, majorSize = 0, minorSize = 0; for (int i=0; i majorSize) { - minorIndex = majorIndex; - minorSize = majorSize; - majorIndex = i; - majorSize = size; - } else if (size > minorSize) { - minorIndex = i; - minorSize = size; + if (size > major.size) { + minor = major; + major = Pivot(i, size, pivotHeaders[i]); + } else if (size > minor.size) { + minor = Pivot(i, size, pivotHeaders[i]); } } - if (majorIndex != -1) majorHeader = pivotHeaders[majorIndex]; - if (minorIndex != -1) minorHeader = pivotHeaders[minorIndex]; - flip = minorHeader == "Algorithm"; + const QString &smooth = destination.getString("smooth", ""); + major.smooth = !smooth.isEmpty() && (major.header == smooth); + minor.smooth = !smooth.isEmpty() && (minor.header == smooth); + if (major.smooth) major.size = 1; + if (minor.smooth) minor.size = 1; + if (major.size < minor.size) + std::swap(major, minor); + + flip = minor.header == "Algorithm"; } bool finalize(bool show = false) @@ -424,74 +437,76 @@ struct RPlot } }; -bool br::Plot(const QStringList &files, const QString &destination, bool show) +bool Plot(const QStringList &files, const br::File &destination, bool show) { qDebug("Plotting %d file(s) to %s", files.size(), qPrintable(destination)); RPlot p(files, destination); - p.file.write(qPrintable(QString("qplot(X, 1-Y, data=DET, geom=\"line\"") + - (p.majorSize > 1 ? QString(", colour=factor(%1)").arg(p.majorHeader) : QString()) + - (p.minorSize > 1 ? QString(", linetype=factor(%1)").arg(p.minorHeader) : QString()) + + p.file.write(qPrintable(QString("qplot(X, 1-Y, data=DET, geom=\"%1\"").arg((p.major.smooth || p.minor.smooth) ? "smooth" : "line") + + (p.major.size > 1 ? QString(", colour=factor(%1)").arg(p.major.header) : QString()) + + (p.minor.size > 1 ? QString(", linetype=factor(%1)").arg(p.minor.header) : QString()) + QString(", xlab=\"False Accept Rate\", ylab=\"True Accept Rate\") + theme_minimal()") + - (p.majorSize > 1 ? getScale("colour", p.majorHeader, p.majorSize) : QString()) + - (p.minorSize > 1 ? QString(" + scale_linetype_discrete(\"%1\")").arg(p.minorHeader) : QString()) + + (p.major.size > 1 ? getScale("colour", p.major.header, p.major.size) : QString()) + + (p.minor.size > 1 ? QString(" + scale_linetype_discrete(\"%1\")").arg(p.minor.header) : QString()) + QString(" + scale_x_log10() + scale_y_continuous(labels=percent)") + QString("\nggsave(\"%1\")\n").arg(p.subfile("ROC")))); - p.file.write(qPrintable(QString("qplot(X, Y, data=DET, geom=\"line\"") + - (p.majorSize > 1 ? QString(", colour=factor(%1)").arg(p.majorHeader) : QString()) + - (p.minorSize > 1 ? QString(", linetype=factor(%1)").arg(p.minorHeader) : QString()) + + p.file.write(qPrintable(QString("qplot(X, Y, data=DET, geom=\"%1\"").arg((p.major.smooth || p.minor.smooth) ? "smooth" : "line") + + (p.major.size > 1 ? QString(", colour=factor(%1)").arg(p.major.header) : QString()) + + (p.minor.size > 1 ? QString(", linetype=factor(%1)").arg(p.minor.header) : QString()) + QString(", xlab=\"False Accept Rate\", ylab=\"False Reject Rate\") + geom_abline(alpha=0.5, colour=\"grey\", linetype=\"dashed\") + theme_minimal()") + - (p.majorSize > 1 ? getScale("colour", p.majorHeader, p.majorSize) : QString()) + - (p.minorSize > 1 ? QString(" + scale_linetype_discrete(\"%1\")").arg(p.minorHeader) : QString()) + + (p.major.size > 1 ? getScale("colour", p.major.header, p.major.size) : QString()) + + (p.minor.size > 1 ? QString(" + scale_linetype_discrete(\"%1\")").arg(p.minor.header) : QString()) + QString(" + scale_x_log10() + scale_y_log10()") + QString("\nggsave(\"%1\")\n").arg(p.subfile("DET")))); p.file.write(qPrintable(QString("qplot(X, data=SD, geom=\"histogram\", fill=Y, position=\"identity\", alpha=I(1/2)") + - QString(", xlab=\"Score%1\"").arg((p.flip ? p.majorSize : p.minorSize) > 1 ? " / " + (p.flip ? p.majorHeader : p.minorHeader) : QString()) + - QString(", ylab=\"Frequency%1\"").arg((p.flip ? p.minorSize : p.majorSize) > 1 ? " / " + (p.flip ? p.minorHeader : p.majorHeader) : QString()) + + QString(", xlab=\"Score%1\"").arg((p.flip ? p.major.size : p.minor.size) > 1 ? " / " + (p.flip ? p.major.header : p.minor.header) : QString()) + + QString(", ylab=\"Frequency%1\"").arg((p.flip ? p.minor.size : p.major.size) > 1 ? " / " + (p.flip ? p.minor.header : p.major.header) : QString()) + QString(") + scale_fill_manual(\"Ground Truth\", values=c(\"blue\", \"red\")) + theme_minimal() + scale_x_continuous(minor_breaks=NULL) + scale_y_continuous(minor_breaks=NULL) + theme(axis.text.y=element_blank(), axis.ticks=element_blank(), axis.text.x=element_text(angle=-90, hjust=0))") + - (p.majorSize > 1 ? (p.minorSize > 1 ? QString(" + facet_grid(%2 ~ %1, scales=\"free\")").arg((p.flip ? p.majorHeader : p.minorHeader), (p.flip ? p.minorHeader : p.majorHeader)) : QString(" + facet_wrap(~ %1, scales = \"free\")").arg(p.majorHeader)) : QString()) + + (p.major.size > 1 ? (p.minor.size > 1 ? QString(" + facet_grid(%2 ~ %1, scales=\"free\")").arg((p.flip ? p.major.header : p.minor.header), (p.flip ? p.minor.header : p.major.header)) : QString(" + facet_wrap(~ %1, scales = \"free\")").arg(p.major.header)) : QString()) + QString(" + theme(aspect.ratio=1)") + QString("\nggsave(\"%1\")\n").arg(p.subfile("SD")))); - p.file.write(qPrintable(QString("qplot(X, Y, data=CMC, geom=\"line\", xlab=\"Rank\", ylab=\"Retrieval Rate\"") + - (p.majorSize > 1 ? QString(", colour=factor(%1)").arg(p.majorHeader) : QString()) + - (p.minorSize > 1 ? QString(", linetype=factor(%1)").arg(p.minorHeader) : QString()) + + p.file.write(qPrintable(QString("qplot(X, Y, data=CMC, geom=\"%1\", xlab=\"Rank\", ylab=\"Retrieval Rate\"").arg((p.major.smooth || p.minor.smooth) ? "smooth" : "line") + + (p.major.size > 1 ? QString(", colour=factor(%1)").arg(p.major.header) : QString()) + + (p.minor.size > 1 ? QString(", linetype=factor(%1)").arg(p.minor.header) : QString()) + QString(") + theme_minimal() + scale_x_continuous(limits = c(1,25), breaks = c(1,5,10,25))") + - (p.majorSize > 1 ? getScale("colour", p.majorHeader, p.majorSize) : QString()) + - (p.minorSize > 1 ? QString(" + scale_linetype_discrete(\"%1\")").arg(p.minorHeader) : QString()) + + (p.major.size > 1 ? getScale("colour", p.major.header, p.major.size) : QString()) + + (p.minor.size > 1 ? QString(" + scale_linetype_discrete(\"%1\")").arg(p.minor.header) : QString()) + QString(" + scale_y_continuous(labels=percent)") + QString("\nggsave(\"%1\")\n").arg(p.subfile("CMC")))); - p.file.write(qPrintable(QString("qplot(factor(%1), data=BC, geom=\"bar\", position=\"dodge\", weight=Y").arg(p.majorHeader) + - (p.majorSize > 1 ? QString(", fill=factor(%1)").arg(p.majorHeader) : QString()) + - QString(", xlab=\"%1False Accept Rate\"").arg(p.majorSize > 1 ? p.majorHeader + " / " : QString()) + - QString(", ylab=\"True Accept Rate%1\") + theme_minimal()").arg(p.minorSize > 1 ? " / " + p.minorHeader : QString()) + - (p.majorSize > 1 ? getScale("fill", p.majorHeader, p.majorSize) : QString()) + - (p.minorSize > 1 ? QString(" + facet_grid(%2 ~ X)").arg(p.minorHeader) : QString(" + facet_wrap(~ X)")) + - QString(" + scale_y_continuous(labels=percent) + theme(legend.position=\"none\", axis.text.x=element_text(angle=-90, hjust=0)) + geom_text(data=BC, aes(label=Y, y=0.05))") + + p.file.write(qPrintable(QString("qplot(factor(%1)%2, data=BC, %3").arg(p.major.header, (p.major.smooth || p.minor.smooth) ? ", Y" : "", (p.major.smooth || p.minor.smooth) ? "geom=\"boxplot\"" : "geom=\"bar\", position=\"dodge\", weight=Y") + + (p.major.size > 1 ? QString(", fill=factor(%1)").arg(p.major.header) : QString()) + + QString(", xlab=\"%1False Accept Rate\"").arg(p.major.size > 1 ? p.major.header + " / " : QString()) + + QString(", ylab=\"True Accept Rate%1\") + theme_minimal()").arg(p.minor.size > 1 ? " / " + p.minor.header : QString()) + + (p.major.size > 1 ? getScale("fill", p.major.header, p.major.size) : QString()) + + (p.minor.size > 1 ? QString(" + facet_grid(%2 ~ X)").arg(p.minor.header) : QString(" + facet_wrap(~ X)")) + + QString(" + scale_y_continuous(labels=percent) + theme(legend.position=\"none\", axis.text.x=element_text(angle=-90, hjust=0))%1").arg((p.major.smooth || p.minor.smooth) ? "" : " + geom_text(data=BC, aes(label=Y, y=0.05))") + QString("\nggsave(\"%1\")\n").arg(p.subfile("BC")))); - p.file.write(qPrintable(QString("qplot(X, Y, data=ERR, geom=\"line\", linetype=Error") + - ((p.flip ? p.majorSize : p.minorSize) > 1 ? QString(", colour=factor(%1)").arg(p.flip ? p.majorHeader : p.minorHeader) : QString()) + - QString(", xlab=\"Score%1\", ylab=\"Error Rate\") + theme_minimal()").arg((p.flip ? p.minorSize : p.majorSize) > 1 ? " / " + (p.flip ? p.minorHeader : p.majorHeader) : QString()) + - ((p.flip ? p.majorSize : p.minorSize) > 1 ? getScale("colour", p.flip ? p.majorHeader : p.minorHeader, p.flip ? p.majorSize : p.minorSize) : QString()) + + p.file.write(qPrintable(QString("qplot(X, Y, data=ERR, geom=\"%1\", linetype=Error").arg((p.major.smooth || p.minor.smooth) ? "smooth" : "line") + + ((p.flip ? p.major.size : p.minor.size) > 1 ? QString(", colour=factor(%1)").arg(p.flip ? p.major.header : p.minor.header) : QString()) + + QString(", xlab=\"Score%1\", ylab=\"Error Rate\") + theme_minimal()").arg((p.flip ? p.minor.size : p.major.size) > 1 ? " / " + (p.flip ? p.minor.header : p.major.header) : QString()) + + ((p.flip ? p.major.size : p.minor.size) > 1 ? getScale("colour", p.flip ? p.major.header : p.minor.header, p.flip ? p.major.size : p.minor.size) : QString()) + QString(" + scale_y_log10()") + - ((p.flip ? p.minorSize : p.majorSize) > 1 ? QString(" + facet_wrap(~ %1, scales=\"free_x\")").arg(p.flip ? p.minorHeader : p.majorHeader) : QString()) + + ((p.flip ? p.minor.size : p.major.size) > 1 ? QString(" + facet_wrap(~ %1, scales=\"free_x\")").arg(p.flip ? p.minor.header : p.major.header) : QString()) + QString(" + theme(aspect.ratio=1)") + QString("\nggsave(\"%1\")\n").arg(p.subfile("ERR")))); return p.finalize(show); } -bool br::PlotMetadata(const QStringList &files, const QString &columns, bool show) +bool PlotMetadata(const QStringList &files, const QString &columns, bool show) { qDebug("Plotting %d metadata file(s) for columns %s", files.size(), qPrintable(columns)); RPlot p(files, "PlotMetadata", false); foreach (const QString &column, columns.split(";")) - p.file.write(qPrintable(QString("qplot(%1, %2, data=data, geom=\"violin\", fill=%1) + coord_flip() + theme_minimal()\nggsave(\"%2.pdf\")\n").arg(p.majorHeader, column))); + p.file.write(qPrintable(QString("qplot(%1, %2, data=data, geom=\"violin\", fill=%1) + coord_flip() + theme_minimal()\nggsave(\"%2.pdf\")\n").arg(p.major.header, column))); return p.finalize(show); } + +} // namespace br diff --git a/sdk/core/plot.h b/sdk/core/plot.h index c51c92a..a6002ab 100644 --- a/sdk/core/plot.h +++ b/sdk/core/plot.h @@ -20,13 +20,14 @@ #include #include #include +#include namespace br { void Confusion(const QString &file, float score, int &true_positives, int &false_positives, int &true_negatives, int &false_negatives); float Evaluate(const QString &simmat, const QString &mask, const QString &csv = ""); // Returns TAR @ FAR = 0.01 -bool Plot(const QStringList &files, const QString &destination, bool show = false); +bool Plot(const QStringList &files, const br::File &destination, bool show = false); bool PlotMetadata(const QStringList &files, const QString &destination, bool show = false); } diff --git a/sdk/openbr.cpp b/sdk/openbr.cpp index 1dac486..b0922d8 100644 --- a/sdk/openbr.cpp +++ b/sdk/openbr.cpp @@ -51,17 +51,9 @@ void br_confusion(const char *file, float score, int *true_positives, int *false return Confusion(file, score, *true_positives, *false_positives, *true_negatives, *false_negatives); } -void br_convert(const char *input_matrix, const char *output_matrix) -{ - QString inputSuffix = QFileInfo(input_matrix).suffix(); - QString outputSuffix = QFileInfo(output_matrix).suffix(); - if (inputSuffix == "csv") { - if (outputSuffix == "mtx") BEE::CSVToSimmat(input_matrix, output_matrix); - else BEE::CSVToMask(input_matrix, output_matrix); - } else { - if (inputSuffix == "mtx") BEE::simmatToCSV(input_matrix, output_matrix); - else BEE::maskToCSV(input_matrix, output_matrix); - } +void br_convert(const char *input, const char *output) +{ + Convert(File(input), File(output)); } void br_enroll(const char *input, const char *gallery) diff --git a/sdk/openbr.h b/sdk/openbr.h index 8cc9af4..d653bd5 100644 --- a/sdk/openbr.h +++ b/sdk/openbr.h @@ -123,11 +123,9 @@ BR_EXPORT void br_confusion(const char *file, float score, int *true_positives, int *false_positives, int *true_negatives, int *false_negatives); /*! - * \brief Converts a .csv file to/from a \ref simmat or \ref mask. - * \param input_matrix The input matrix. - * \param output_matrix The output matrix. + * \brief Wraps br::Convert() */ -BR_EXPORT void br_convert(const char *input_matrix, const char *output_matrix); +BR_EXPORT void br_convert(const char *input, const char *output); /*! * \brief Constructs template(s) from an input. diff --git a/sdk/openbr_plugin.h b/sdk/openbr_plugin.h index 0a0bd66..b46a31e 100644 --- a/sdk/openbr_plugin.h +++ b/sdk/openbr_plugin.h @@ -845,6 +845,7 @@ class BR_EXPORT Format : public Object public: virtual ~Format() {} virtual Template read() const = 0; /*!< \brief Returns a br::Template created by reading #br::Object::file. */ + virtual void write(const Template &t) const = 0; /*!< \brief Writes the br::Template to #br::Object::file. */ }; /*! @@ -1079,6 +1080,13 @@ BR_EXPORT FileList Enroll(const File &input, const File &gallery = File()); */ BR_EXPORT void Compare(const File &targetGallery, const File &queryGallery, const File &output); +/*! + * \brief To convert between matrix/template formats. + * \param input The input matrix or template. + * \param output The output matrix or template. + */ +BR_EXPORT void Convert(const File &input, const File &output); + /*! @}*/ } // namespace br diff --git a/sdk/plugins/format.cpp b/sdk/plugins/format.cpp index 9e1e7b2..7533575 100644 --- a/sdk/plugins/format.cpp +++ b/sdk/plugins/format.cpp @@ -23,6 +23,10 @@ #include #include +#include "core/bee.h" +#include "core/opencvutils.h" +#include "core/qtutils.h" + using namespace br; using namespace cv; @@ -42,12 +46,15 @@ class csvFormat : public Format QStringList lines(QString(f.readAll()).split('\n')); f.close(); + bool isUChar = true; QList< QList > valsList; foreach (const QString &line, lines) { QList vals; foreach (const QString &word, line.split(QRegExp(" *, *"))) { bool ok; - vals.append(word.toFloat(&ok)); assert(ok); + const float val = word.toFloat(&ok); + vals.append(val); + isUChar = isUChar && (val == float(uchar(val))); } valsList.append(vals); } @@ -61,8 +68,26 @@ class csvFormat : public Format } } + if (isUChar) m.convertTo(m, CV_8U); return Template(m); } + + void write(const Template &t) const + { + const Mat &m = t.m(); + if (t.size() != 1) qFatal("csvFormat::write only supports single matrix templates."); + if (m.channels() != 1) qFatal("csvFormat::write only supports single channel matrices."); + + QStringList lines; lines.reserve(m.rows); + for (int r=0; rread(m); return Template(m); } + + void write(const Template &t) const + { + (void) t; + qFatal("webcamFormat::write not supported."); + } }; BR_REGISTER(Format, webcamFormat) @@ -191,6 +271,12 @@ class xmlFormat : public Format return t; } + + void write(const Template &t) const + { + (void) t; + qFatal("xmlFormat::write not supported."); + } }; BR_REGISTER(Format, xmlFormat)