Commit 2a74106657dd866c11660648dbf0e5d8f7e30262
Merge branch 'master' of https://github.com/biometrics/openbr
Showing
12 changed files
with
327 additions
and
53 deletions
openbr/core/bee.cpp
| @@ -234,22 +234,26 @@ void BEE::writeMask(const Mat &m, const QString &mask, const QString &targetSigs | @@ -234,22 +234,26 @@ void BEE::writeMask(const Mat &m, const QString &mask, const QString &targetSigs | ||
| 234 | void BEE::makeMask(const QString &targetInput, const QString &queryInput, const QString &mask) | 234 | void BEE::makeMask(const QString &targetInput, const QString &queryInput, const QString &mask) |
| 235 | { | 235 | { |
| 236 | qDebug("Making mask from %s and %s to %s", qPrintable(targetInput), qPrintable(queryInput), qPrintable(mask)); | 236 | qDebug("Making mask from %s and %s to %s", qPrintable(targetInput), qPrintable(queryInput), qPrintable(mask)); |
| 237 | + FileList targes = TemplateList::fromGallery(targetInput).files(); | ||
| 238 | + FileList queries = (queryInput == ".") ? targes : TemplateList::fromGallery(queryInput).files(); | ||
| 239 | + writeMask(makeMask(targes, queries), mask, targetInput, queryInput); | ||
| 240 | +} | ||
| 237 | 241 | ||
| 238 | - FileList targetFiles = TemplateList::fromGallery(targetInput).files(); | ||
| 239 | - FileList queryFiles = (queryInput == ".") ? targetFiles : TemplateList::fromGallery(queryInput).files(); | ||
| 240 | - QList<float> targetLabels = targetFiles.labels(); | ||
| 241 | - QList<float> queryLabels = queryFiles.labels(); | ||
| 242 | - QList<int> targetPartitions = targetFiles.crossValidationPartitions(); | ||
| 243 | - QList<int> queryPartitions = queryFiles.crossValidationPartitions(); | ||
| 244 | - | ||
| 245 | - Mat vals(queryFiles.size(), targetFiles.size(), CV_8UC1); | ||
| 246 | - for (int i=0; i<queryFiles.size(); i++) { | ||
| 247 | - const QString &fileA = queryFiles[i]; | 242 | +cv::Mat BEE::makeMask(const br::FileList &targets, const br::FileList &queries) |
| 243 | +{ | ||
| 244 | + QList<float> targetLabels = targets.labels(); | ||
| 245 | + QList<float> queryLabels = queries.labels(); | ||
| 246 | + QList<int> targetPartitions = targets.crossValidationPartitions(); | ||
| 247 | + QList<int> queryPartitions = queries.crossValidationPartitions(); | ||
| 248 | + | ||
| 249 | + Mat mask(queries.size(), targets.size(), CV_8UC1); | ||
| 250 | + for (int i=0; i<queries.size(); i++) { | ||
| 251 | + const QString &fileA = queries[i]; | ||
| 248 | const int labelA = queryLabels[i]; | 252 | const int labelA = queryLabels[i]; |
| 249 | const int partitionA = queryPartitions[i]; | 253 | const int partitionA = queryPartitions[i]; |
| 250 | 254 | ||
| 251 | - for (int j=0; j<targetFiles.size(); j++) { | ||
| 252 | - const QString &fileB = targetFiles[j]; | 255 | + for (int j=0; j<targets.size(); j++) { |
| 256 | + const QString &fileB = targets[j]; | ||
| 253 | const int labelB = targetLabels[j]; | 257 | const int labelB = targetLabels[j]; |
| 254 | const int partitionB = targetPartitions[j]; | 258 | const int partitionB = targetPartitions[j]; |
| 255 | 259 | ||
| @@ -260,10 +264,11 @@ void BEE::makeMask(const QString &targetInput, const QString &queryInput, const | @@ -260,10 +264,11 @@ void BEE::makeMask(const QString &targetInput, const QString &queryInput, const | ||
| 260 | else if (partitionA != partitionB) val = DontCare; | 264 | else if (partitionA != partitionB) val = DontCare; |
| 261 | else if (labelA == labelB) val = Match; | 265 | else if (labelA == labelB) val = Match; |
| 262 | else val = NonMatch; | 266 | else val = NonMatch; |
| 263 | - vals.at<Mask_t>(i,j) = val; | 267 | + mask.at<Mask_t>(i,j) = val; |
| 264 | } | 268 | } |
| 265 | } | 269 | } |
| 266 | - writeMask(vals, mask, targetInput, queryInput); | 270 | + |
| 271 | + return mask; | ||
| 267 | } | 272 | } |
| 268 | 273 | ||
| 269 | void BEE::combineMasks(const QStringList &inputMasks, const QString &outputMask, const QString &method) | 274 | void BEE::combineMasks(const QStringList &inputMasks, const QString &outputMask, const QString &method) |
openbr/core/bee.h
| @@ -46,6 +46,7 @@ namespace BEE | @@ -46,6 +46,7 @@ namespace BEE | ||
| 46 | 46 | ||
| 47 | // Write BEE files | 47 | // Write BEE files |
| 48 | void makeMask(const QString &targetInput, const QString &queryInput, const QString &mask); | 48 | void makeMask(const QString &targetInput, const QString &queryInput, const QString &mask); |
| 49 | + cv::Mat makeMask(const br::FileList &targets, const br::FileList &queries); | ||
| 49 | void combineMasks(const QStringList &inputMasks, const QString &outputMask, const QString &method); | 50 | void combineMasks(const QStringList &inputMasks, const QString &outputMask, const QString &method); |
| 50 | } | 51 | } |
| 51 | 52 |
openbr/core/common.h
| @@ -119,14 +119,14 @@ T Max(const QList<T> &vals) | @@ -119,14 +119,14 @@ T Max(const QList<T> &vals) | ||
| 119 | * \brief Returns the mean and standard deviation of a vector of values. | 119 | * \brief Returns the mean and standard deviation of a vector of values. |
| 120 | */ | 120 | */ |
| 121 | template <template<class> class V, typename T> | 121 | template <template<class> class V, typename T> |
| 122 | -void Mean(const V<T> &vals, double *mean) | 122 | +double Mean(const V<T> &vals) |
| 123 | { | 123 | { |
| 124 | const int size = vals.size(); | 124 | const int size = vals.size(); |
| 125 | 125 | ||
| 126 | // Compute Mean | 126 | // Compute Mean |
| 127 | double sum = 0; | 127 | double sum = 0; |
| 128 | foreach (int val, vals) sum += val; | 128 | foreach (int val, vals) sum += val; |
| 129 | - *mean = (size == 0) ? 0 : sum / size; | 129 | + return (size == 0) ? 0 : sum / size; |
| 130 | } | 130 | } |
| 131 | 131 | ||
| 132 | /*! | 132 | /*! |
| @@ -137,7 +137,7 @@ void MeanStdDev(const V<T> &vals, double *mean, double *stddev) | @@ -137,7 +137,7 @@ void MeanStdDev(const V<T> &vals, double *mean, double *stddev) | ||
| 137 | { | 137 | { |
| 138 | const int size = vals.size(); | 138 | const int size = vals.size(); |
| 139 | 139 | ||
| 140 | - Mean(vals, mean); | 140 | + *mean = Mean(vals); |
| 141 | 141 | ||
| 142 | // Compute Standard Deviation | 142 | // Compute Standard Deviation |
| 143 | double variance = 0; | 143 | double variance = 0; |
openbr/core/plot.cpp
| @@ -113,9 +113,6 @@ float Evaluate(const QString &simmat, const QString &mask, const QString &csv) | @@ -113,9 +113,6 @@ float Evaluate(const QString &simmat, const QString &mask, const QString &csv) | ||
| 113 | { | 113 | { |
| 114 | qDebug("Evaluating %s with %s", qPrintable(simmat), qPrintable(mask)); | 114 | qDebug("Evaluating %s with %s", qPrintable(simmat), qPrintable(mask)); |
| 115 | 115 | ||
| 116 | - const int Max_Points = 500; | ||
| 117 | - float result = -1; | ||
| 118 | - | ||
| 119 | // Read files | 116 | // Read files |
| 120 | const Mat scores = BEE::readSimmat(simmat); | 117 | const Mat scores = BEE::readSimmat(simmat); |
| 121 | File maskFile(mask); | 118 | File maskFile(mask); |
| @@ -124,13 +121,21 @@ float Evaluate(const QString &simmat, const QString &mask, const QString &csv) | @@ -124,13 +121,21 @@ float Evaluate(const QString &simmat, const QString &mask, const QString &csv) | ||
| 124 | const Mat masks = BEE::readMask(maskFile); | 121 | const Mat masks = BEE::readMask(maskFile); |
| 125 | if (scores.size() != masks.size()) qFatal("Simmat (%i,%i) / Mask (%i,%i) size mismatch.", scores.rows, scores.cols, masks.rows, masks.cols); | 122 | if (scores.size() != masks.size()) qFatal("Simmat (%i,%i) / Mask (%i,%i) size mismatch.", scores.rows, scores.cols, masks.rows, masks.cols); |
| 126 | 123 | ||
| 124 | + return Evaluate(scores, masks, csv); | ||
| 125 | +} | ||
| 126 | + | ||
| 127 | +float Evaluate(const Mat &simmat, const Mat &mask, const QString &csv) | ||
| 128 | +{ | ||
| 129 | + const int Max_Points = 500; | ||
| 130 | + float result = -1; | ||
| 131 | + | ||
| 127 | // Make comparisons | 132 | // Make comparisons |
| 128 | - QList<Comparison> comparisons; comparisons.reserve(scores.rows*scores.cols); | 133 | + QList<Comparison> comparisons; comparisons.reserve(simmat.rows*simmat.cols); |
| 129 | int genuineCount = 0, impostorCount = 0, numNaNs = 0; | 134 | int genuineCount = 0, impostorCount = 0, numNaNs = 0; |
| 130 | - for (int i=0; i<scores.rows; i++) { | ||
| 131 | - for (int j=0; j<scores.cols; j++) { | ||
| 132 | - const BEE::Mask_t mask_val = masks.at<BEE::Mask_t>(i,j); | ||
| 133 | - const BEE::Simmat_t simmat_val = scores.at<BEE::Simmat_t>(i,j); | 135 | + for (int i=0; i<simmat.rows; i++) { |
| 136 | + for (int j=0; j<simmat.cols; j++) { | ||
| 137 | + const BEE::Mask_t mask_val = mask.at<BEE::Mask_t>(i,j); | ||
| 138 | + const BEE::Simmat_t simmat_val = simmat.at<BEE::Simmat_t>(i,j); | ||
| 134 | if (mask_val == BEE::DontCare) continue; | 139 | if (mask_val == BEE::DontCare) continue; |
| 135 | if (simmat_val != simmat_val) { numNaNs++; continue; } | 140 | if (simmat_val != simmat_val) { numNaNs++; continue; } |
| 136 | comparisons.append(Comparison(simmat_val, j, i, mask_val == BEE::Match)); | 141 | comparisons.append(Comparison(simmat_val, j, i, mask_val == BEE::Match)); |
| @@ -149,7 +154,7 @@ float Evaluate(const QString &simmat, const QString &mask, const QString &csv) | @@ -149,7 +154,7 @@ float Evaluate(const QString &simmat, const QString &mask, const QString &csv) | ||
| 149 | double genuineSum = 0, impostorSum = 0; | 154 | double genuineSum = 0, impostorSum = 0; |
| 150 | QList<OperatingPoint> operatingPoints; | 155 | QList<OperatingPoint> operatingPoints; |
| 151 | QList<float> genuines, impostors; | 156 | QList<float> genuines, impostors; |
| 152 | - QVector<int> firstGenuineReturns(scores.rows, 0); | 157 | + QVector<int> firstGenuineReturns(simmat.rows, 0); |
| 153 | 158 | ||
| 154 | int falsePositives = 0, previousFalsePositives = 0; | 159 | int falsePositives = 0, previousFalsePositives = 0; |
| 155 | int truePositives = 0, previousTruePositives = 0; | 160 | int truePositives = 0, previousTruePositives = 0; |
| @@ -202,11 +207,11 @@ float Evaluate(const QString &simmat, const QString &mask, const QString &csv) | @@ -202,11 +207,11 @@ float Evaluate(const QString &simmat, const QString &mask, const QString &csv) | ||
| 202 | // Write Metadata table | 207 | // Write Metadata table |
| 203 | QStringList lines; | 208 | QStringList lines; |
| 204 | lines.append("Plot,X,Y"); | 209 | lines.append("Plot,X,Y"); |
| 205 | - lines.append("Metadata,"+QString::number(scores.cols)+",Gallery"); | ||
| 206 | - lines.append("Metadata,"+QString::number(scores.rows)+",Probe"); | 210 | + lines.append("Metadata,"+QString::number(simmat.cols)+",Gallery"); |
| 211 | + lines.append("Metadata,"+QString::number(simmat.rows)+",Probe"); | ||
| 207 | lines.append("Metadata,"+QString::number(genuineCount)+",Genuine"); | 212 | lines.append("Metadata,"+QString::number(genuineCount)+",Genuine"); |
| 208 | lines.append("Metadata,"+QString::number(impostorCount)+",Impostor"); | 213 | lines.append("Metadata,"+QString::number(impostorCount)+",Impostor"); |
| 209 | - lines.append("Metadata,"+QString::number(scores.cols*scores.rows-(genuineCount+impostorCount))+",Ignored"); | 214 | + lines.append("Metadata,"+QString::number(simmat.cols*simmat.rows-(genuineCount+impostorCount))+",Ignored"); |
| 210 | 215 | ||
| 211 | // Write Detection Error Tradeoff (DET), PRE, REC | 216 | // Write Detection Error Tradeoff (DET), PRE, REC |
| 212 | int points = qMin(operatingPoints.size(), Max_Points); | 217 | int points = qMin(operatingPoints.size(), Max_Points); |
| @@ -382,7 +387,11 @@ struct RPlot | @@ -382,7 +387,11 @@ struct RPlot | ||
| 382 | "ERR$Y <- as.numeric(as.character(ERR$Y))\n" | 387 | "ERR$Y <- as.numeric(as.character(ERR$Y))\n" |
| 383 | "SD$Y <- as.factor(unique(as.character(SD$Y)))\n" | 388 | "SD$Y <- as.factor(unique(as.character(SD$Y)))\n" |
| 384 | "BC$Y <- as.numeric(as.character(BC$Y))\n" | 389 | "BC$Y <- as.numeric(as.character(BC$Y))\n" |
| 385 | - "CMC$Y <- as.numeric(as.character(CMC$Y))\n"); | 390 | + "CMC$Y <- as.numeric(as.character(CMC$Y))\n" |
| 391 | + "\n" | ||
| 392 | + "# Code to format FAR values\n" | ||
| 393 | + "far_names <- list('0.001'=\"FAR = 0.1%\", '0.01'=\"FAR = 1%\")\n" | ||
| 394 | + "far_labeller <- function(variable,value) { return(far_names[as.character(value)]) }\n"); | ||
| 386 | 395 | ||
| 387 | // Open output device | 396 | // Open output device |
| 388 | file.write(qPrintable(QString("\n" | 397 | file.write(qPrintable(QString("\n" |
| @@ -461,7 +470,7 @@ bool Plot(const QStringList &files, const br::File &destination, bool show) | @@ -461,7 +470,7 @@ bool Plot(const QStringList &files, const br::File &destination, bool show) | ||
| 461 | QString(", xlab=\"False Accept Rate\", ylab=\"True Accept Rate\") + theme_minimal()") + | 470 | QString(", xlab=\"False Accept Rate\", ylab=\"True Accept Rate\") + theme_minimal()") + |
| 462 | (p.major.size > 1 ? getScale("colour", p.major.header, p.major.size) : QString()) + | 471 | (p.major.size > 1 ? getScale("colour", p.major.header, p.major.size) : QString()) + |
| 463 | (p.minor.size > 1 ? QString(" + scale_linetype_discrete(\"%1\")").arg(p.minor.header) : QString()) + | 472 | (p.minor.size > 1 ? QString(" + scale_linetype_discrete(\"%1\")").arg(p.minor.header) : QString()) + |
| 464 | - QString(" + scale_x_log10() + scale_y_continuous(labels=percent)") + | 473 | + QString(" + scale_x_log10(labels=percent) + scale_y_continuous(labels=percent) + annotation_logticks(sides=\"b\")") + |
| 465 | QString("\nggsave(\"%1\")\n").arg(p.subfile("ROC")))); | 474 | QString("\nggsave(\"%1\")\n").arg(p.subfile("ROC")))); |
| 466 | 475 | ||
| 467 | p.file.write(qPrintable(QString("qplot(X, Y, data=DET%1").arg((p.major.smooth || p.minor.smooth) ? ", geom=\"smooth\", method=loess, level=0.99" : ", geom=\"line\"") + | 476 | p.file.write(qPrintable(QString("qplot(X, Y, data=DET%1").arg((p.major.smooth || p.minor.smooth) ? ", geom=\"smooth\", method=loess, level=0.99" : ", geom=\"line\"") + |
| @@ -470,7 +479,7 @@ bool Plot(const QStringList &files, const br::File &destination, bool show) | @@ -470,7 +479,7 @@ bool Plot(const QStringList &files, const br::File &destination, bool show) | ||
| 470 | QString(", xlab=\"False Accept Rate\", ylab=\"False Reject Rate\") + geom_abline(alpha=0.5, colour=\"grey\", linetype=\"dashed\") + theme_minimal()") + | 479 | QString(", xlab=\"False Accept Rate\", ylab=\"False Reject Rate\") + geom_abline(alpha=0.5, colour=\"grey\", linetype=\"dashed\") + theme_minimal()") + |
| 471 | (p.major.size > 1 ? getScale("colour", p.major.header, p.major.size) : QString()) + | 480 | (p.major.size > 1 ? getScale("colour", p.major.header, p.major.size) : QString()) + |
| 472 | (p.minor.size > 1 ? QString(" + scale_linetype_discrete(\"%1\")").arg(p.minor.header) : QString()) + | 481 | (p.minor.size > 1 ? QString(" + scale_linetype_discrete(\"%1\")").arg(p.minor.header) : QString()) + |
| 473 | - QString(" + scale_x_log10() + scale_y_log10()") + | 482 | + QString(" + scale_x_log10(labels=percent) + scale_y_log10(labels=percent) + annotation_logticks()") + |
| 474 | QString("\nggsave(\"%1\")\n").arg(p.subfile("DET")))); | 483 | QString("\nggsave(\"%1\")\n").arg(p.subfile("DET")))); |
| 475 | 484 | ||
| 476 | p.file.write(qPrintable(QString("qplot(X, data=SD, geom=\"histogram\", fill=Y, position=\"identity\", alpha=I(1/2)") + | 485 | p.file.write(qPrintable(QString("qplot(X, data=SD, geom=\"histogram\", fill=Y, position=\"identity\", alpha=I(1/2)") + |
| @@ -495,7 +504,7 @@ bool Plot(const QStringList &files, const br::File &destination, bool show) | @@ -495,7 +504,7 @@ bool Plot(const QStringList &files, const br::File &destination, bool show) | ||
| 495 | QString(", xlab=\"%1False Accept Rate\"").arg(p.major.size > 1 ? p.major.header + " / " : QString()) + | 504 | QString(", xlab=\"%1False Accept Rate\"").arg(p.major.size > 1 ? p.major.header + " / " : QString()) + |
| 496 | QString(", ylab=\"True Accept Rate%1\") + theme_minimal()").arg(p.minor.size > 1 ? " / " + p.minor.header : QString()) + | 505 | QString(", ylab=\"True Accept Rate%1\") + theme_minimal()").arg(p.minor.size > 1 ? " / " + p.minor.header : QString()) + |
| 497 | (p.major.size > 1 ? getScale("fill", p.major.header, p.major.size) : QString()) + | 506 | (p.major.size > 1 ? getScale("fill", p.major.header, p.major.size) : QString()) + |
| 498 | - (p.minor.size > 1 ? QString(" + facet_grid(%2 ~ X)").arg(p.minor.header) : QString(" + facet_wrap(~ X)")) + | 507 | + (p.minor.size > 1 ? QString(" + facet_grid(%2 ~ X)").arg(p.minor.header) : QString(" + facet_grid(. ~ X, labeller=far_labeller)")) + |
| 499 | 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))") + | 508 | 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))") + |
| 500 | QString("\nggsave(\"%1\")\n").arg(p.subfile("BC")))); | 509 | QString("\nggsave(\"%1\")\n").arg(p.subfile("BC")))); |
| 501 | 510 | ||
| @@ -503,7 +512,7 @@ bool Plot(const QStringList &files, const br::File &destination, bool show) | @@ -503,7 +512,7 @@ bool Plot(const QStringList &files, const br::File &destination, bool show) | ||
| 503 | ((p.flip ? p.major.size : p.minor.size) > 1 ? QString(", colour=factor(%1)").arg(p.flip ? p.major.header : p.minor.header) : QString()) + | 512 | ((p.flip ? p.major.size : p.minor.size) > 1 ? QString(", colour=factor(%1)").arg(p.flip ? p.major.header : p.minor.header) : QString()) + |
| 504 | 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()) + | 513 | 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()) + |
| 505 | ((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()) + | 514 | ((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()) + |
| 506 | - QString(" + scale_y_log10()") + | 515 | + QString(" + scale_y_log10(labels=percent) + annotation_logticks(sides=\"l\")") + |
| 507 | ((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()) + | 516 | ((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()) + |
| 508 | QString(" + theme(aspect.ratio=1)") + | 517 | QString(" + theme(aspect.ratio=1)") + |
| 509 | QString("\nggsave(\"%1\")\n").arg(p.subfile("ERR")))); | 518 | QString("\nggsave(\"%1\")\n").arg(p.subfile("ERR")))); |
openbr/core/plot.h
| @@ -27,6 +27,7 @@ namespace br | @@ -27,6 +27,7 @@ namespace br | ||
| 27 | 27 | ||
| 28 | void Confusion(const QString &file, float score, int &true_positives, int &false_positives, int &true_negatives, int &false_negatives); | 28 | void Confusion(const QString &file, float score, int &true_positives, int &false_positives, int &true_negatives, int &false_negatives); |
| 29 | float Evaluate(const QString &simmat, const QString &mask, const QString &csv = ""); // Returns TAR @ FAR = 0.01 | 29 | float Evaluate(const QString &simmat, const QString &mask, const QString &csv = ""); // Returns TAR @ FAR = 0.01 |
| 30 | +float Evaluate(const cv::Mat &scores, const cv::Mat &masks, const QString &csv = ""); | ||
| 30 | bool Plot(const QStringList &files, const br::File &destination, bool show = false); | 31 | bool Plot(const QStringList &files, const br::File &destination, bool show = false); |
| 31 | bool PlotMetadata(const QStringList &files, const QString &destination, bool show = false); | 32 | bool PlotMetadata(const QStringList &files, const QString &destination, bool show = false); |
| 32 | 33 |
openbr/plugins/crop.cpp
| @@ -90,6 +90,25 @@ BR_REGISTER(Transform, LimitSizeTransform) | @@ -90,6 +90,25 @@ BR_REGISTER(Transform, LimitSizeTransform) | ||
| 90 | 90 | ||
| 91 | /*! | 91 | /*! |
| 92 | * \ingroup transforms | 92 | * \ingroup transforms |
| 93 | + * \brief Enforce a multiple of \em n columns. | ||
| 94 | + * \author Josh Klontz \cite jklontz | ||
| 95 | + */ | ||
| 96 | +class DivTransform : public UntrainableTransform | ||
| 97 | +{ | ||
| 98 | + Q_OBJECT | ||
| 99 | + Q_PROPERTY(int n READ get_n WRITE set_n RESET reset_n STORED false) | ||
| 100 | + BR_PROPERTY(int, n, 1) | ||
| 101 | + | ||
| 102 | + void project(const Template &src, Template &dst) const | ||
| 103 | + { | ||
| 104 | + dst = Mat(src, Rect(0,0,n*(src.m().cols/n),src.m().rows)); | ||
| 105 | + } | ||
| 106 | +}; | ||
| 107 | + | ||
| 108 | +BR_REGISTER(Transform, DivTransform) | ||
| 109 | + | ||
| 110 | +/*! | ||
| 111 | + * \ingroup transforms | ||
| 93 | * \brief Crop out black borders | 112 | * \brief Crop out black borders |
| 94 | * \author Josh Klontz \cite jklontz | 113 | * \author Josh Klontz \cite jklontz |
| 95 | */ | 114 | */ |
openbr/plugins/filter.cpp
| @@ -235,7 +235,7 @@ class PowTransform : public UntrainableTransform | @@ -235,7 +235,7 @@ class PowTransform : public UntrainableTransform | ||
| 235 | 235 | ||
| 236 | void project(const Template &src, Template &dst) const | 236 | void project(const Template &src, Template &dst) const |
| 237 | { | 237 | { |
| 238 | - pow(src, power, dst); | 238 | + pow(preserveSign ? abs(src) : src.m(), power, dst); |
| 239 | if (preserveSign) subtract(Scalar::all(0), dst, dst, src.m() < 0); | 239 | if (preserveSign) subtract(Scalar::all(0), dst, dst, src.m() < 0); |
| 240 | } | 240 | } |
| 241 | }; | 241 | }; |
openbr/plugins/integral.cpp
| @@ -38,15 +38,19 @@ class IntegralSamplerTransform : public UntrainableTransform | @@ -38,15 +38,19 @@ class IntegralSamplerTransform : public UntrainableTransform | ||
| 38 | Q_PROPERTY(float scaleFactor READ get_scaleFactor WRITE set_scaleFactor RESET reset_scaleFactor STORED false) | 38 | Q_PROPERTY(float scaleFactor READ get_scaleFactor WRITE set_scaleFactor RESET reset_scaleFactor STORED false) |
| 39 | Q_PROPERTY(float stepFactor READ get_stepFactor WRITE set_stepFactor RESET reset_stepFactor STORED false) | 39 | Q_PROPERTY(float stepFactor READ get_stepFactor WRITE set_stepFactor RESET reset_stepFactor STORED false) |
| 40 | Q_PROPERTY(int minSize READ get_minSize WRITE set_minSize RESET reset_minSize STORED false) | 40 | Q_PROPERTY(int minSize READ get_minSize WRITE set_minSize RESET reset_minSize STORED false) |
| 41 | + Q_PROPERTY(bool secondOrder READ get_secondOrder WRITE set_secondOrder RESET reset_secondOrder STORED false) | ||
| 41 | BR_PROPERTY(int, scales, 6) | 42 | BR_PROPERTY(int, scales, 6) |
| 42 | BR_PROPERTY(float, scaleFactor, 2) | 43 | BR_PROPERTY(float, scaleFactor, 2) |
| 43 | BR_PROPERTY(float, stepFactor, 0.75) | 44 | BR_PROPERTY(float, stepFactor, 0.75) |
| 44 | - BR_PROPERTY(int, minSize, 6) | 45 | + BR_PROPERTY(int, minSize, 8) |
| 46 | + BR_PROPERTY(bool, secondOrder, false) | ||
| 45 | 47 | ||
| 46 | void project(const Template &src, Template &dst) const | 48 | void project(const Template &src, Template &dst) const |
| 47 | { | 49 | { |
| 48 | typedef Eigen::Map< const Eigen::Matrix<qint32,Eigen::Dynamic,1> > InputDescriptor; | 50 | typedef Eigen::Map< const Eigen::Matrix<qint32,Eigen::Dynamic,1> > InputDescriptor; |
| 51 | + typedef Eigen::Map< const Eigen::Matrix<float,Eigen::Dynamic,1> > SecondOrderInputDescriptor; | ||
| 49 | typedef Eigen::Map< Eigen::Matrix<float,Eigen::Dynamic,1> > OutputDescriptor; | 52 | typedef Eigen::Map< Eigen::Matrix<float,Eigen::Dynamic,1> > OutputDescriptor; |
| 53 | + | ||
| 50 | const Mat &m = src.m(); | 54 | const Mat &m = src.m(); |
| 51 | if (m.depth() != CV_32S) qFatal("Expected CV_32S matrix depth."); | 55 | if (m.depth() != CV_32S) qFatal("Expected CV_32S matrix depth."); |
| 52 | const int channels = m.channels(); | 56 | const int channels = m.channels(); |
| @@ -56,8 +60,10 @@ class IntegralSamplerTransform : public UntrainableTransform | @@ -56,8 +60,10 @@ class IntegralSamplerTransform : public UntrainableTransform | ||
| 56 | float idealSize = min(m.rows, m.cols)-1; | 60 | float idealSize = min(m.rows, m.cols)-1; |
| 57 | for (int scale=0; scale<scales; scale++) { | 61 | for (int scale=0; scale<scales; scale++) { |
| 58 | const int currentSize(idealSize); | 62 | const int currentSize(idealSize); |
| 59 | - descriptors += (1+(m.rows-currentSize-1)/int(idealSize*stepFactor)) * | ||
| 60 | - (1+(m.cols-currentSize-1)/int(idealSize*stepFactor)); | 63 | + const int numDown = 1+(m.rows-currentSize-1)/int(idealSize*stepFactor); |
| 64 | + const int numAcross = 1+(m.cols-currentSize-1)/int(idealSize*stepFactor); | ||
| 65 | + descriptors += numDown*numAcross; | ||
| 66 | + if (secondOrder) descriptors += numDown*(numAcross-1) + (numDown-1)*numAcross; | ||
| 61 | idealSize /= scaleFactor; | 67 | idealSize /= scaleFactor; |
| 62 | if (idealSize < minSize) break; | 68 | if (idealSize < minSize) break; |
| 63 | } | 69 | } |
| @@ -81,6 +87,26 @@ class IntegralSamplerTransform : public UntrainableTransform | @@ -81,6 +87,26 @@ class IntegralSamplerTransform : public UntrainableTransform | ||
| 81 | index++; | 87 | index++; |
| 82 | } | 88 | } |
| 83 | } | 89 | } |
| 90 | + if (secondOrder) { | ||
| 91 | + const int numDown = 1+(m.rows-currentSize-1)/currentStep; | ||
| 92 | + const int numAcross = 1+(m.cols-currentSize-1)/currentStep; | ||
| 93 | + const float *dataIn = n.ptr<float>(index - numDown*numAcross); | ||
| 94 | + for (int i=0; i<numDown; i++) { | ||
| 95 | + for (int j=0; j<numAcross; j++) { | ||
| 96 | + SecondOrderInputDescriptor a(dataIn + (i*numAcross+j)*channels, channels, 1); | ||
| 97 | + if (j < numAcross-1) { | ||
| 98 | + OutputDescriptor y(dataOut+(index*channels), channels, 1); | ||
| 99 | + y = a - SecondOrderInputDescriptor(dataIn + (i*numAcross+j+1)*channels, channels, 1); | ||
| 100 | + index++; | ||
| 101 | + } | ||
| 102 | + if (i < numDown-1) { | ||
| 103 | + OutputDescriptor y(dataOut+(index*channels), channels, 1); | ||
| 104 | + y = a - SecondOrderInputDescriptor(dataIn + ((i+1)*numAcross+j)*channels, channels, 1); | ||
| 105 | + index++; | ||
| 106 | + } | ||
| 107 | + } | ||
| 108 | + } | ||
| 109 | + } | ||
| 84 | idealSize /= scaleFactor; | 110 | idealSize /= scaleFactor; |
| 85 | if (idealSize < minSize) break; | 111 | if (idealSize < minSize) break; |
| 86 | } | 112 | } |
openbr/plugins/meta.cpp
| @@ -418,6 +418,8 @@ public: | @@ -418,6 +418,8 @@ public: | ||
| 418 | private: | 418 | private: |
| 419 | void init() | 419 | void init() |
| 420 | { | 420 | { |
| 421 | + if (!transform) return; | ||
| 422 | + | ||
| 421 | trainable = transform->trainable; | 423 | trainable = transform->trainable; |
| 422 | if (!cache.isEmpty()) return; | 424 | if (!cache.isEmpty()) return; |
| 423 | 425 |
openbr/plugins/output.cpp
| @@ -40,6 +40,7 @@ | @@ -40,6 +40,7 @@ | ||
| 40 | #include "openbr/core/bee.h" | 40 | #include "openbr/core/bee.h" |
| 41 | #include "openbr/core/common.h" | 41 | #include "openbr/core/common.h" |
| 42 | #include "openbr/core/opencvutils.h" | 42 | #include "openbr/core/opencvutils.h" |
| 43 | +#include "openbr/core/plot.h" | ||
| 43 | #include "openbr/core/qtutils.h" | 44 | #include "openbr/core/qtutils.h" |
| 44 | 45 | ||
| 45 | namespace br | 46 | namespace br |
| @@ -259,6 +260,23 @@ BR_REGISTER(Output, EmptyOutput) | @@ -259,6 +260,23 @@ BR_REGISTER(Output, EmptyOutput) | ||
| 259 | 260 | ||
| 260 | /*! | 261 | /*! |
| 261 | * \ingroup outputs | 262 | * \ingroup outputs |
| 263 | + * \brief Evaluate the output matrix. | ||
| 264 | + * \author Josh Klontz \cite jklontz | ||
| 265 | + */ | ||
| 266 | +class evalOutput : public MatrixOutput | ||
| 267 | +{ | ||
| 268 | + Q_OBJECT | ||
| 269 | + | ||
| 270 | + ~evalOutput() | ||
| 271 | + { | ||
| 272 | + Evaluate(data, BEE::makeMask(targetFiles, queryFiles), ""); | ||
| 273 | + } | ||
| 274 | +}; | ||
| 275 | + | ||
| 276 | +BR_REGISTER(Output, evalOutput) | ||
| 277 | + | ||
| 278 | +/*! | ||
| 279 | + * \ingroup outputs | ||
| 262 | * \brief Outputs highest ranked matches with scores. | 280 | * \brief Outputs highest ranked matches with scores. |
| 263 | * \author Scott Klum \cite sklum | 281 | * \author Scott Klum \cite sklum |
| 264 | */ | 282 | */ |
openbr/plugins/quality.cpp
| 1 | +#include <QFutureSynchronizer> | ||
| 2 | +#include <QtConcurrent> | ||
| 1 | #include <openbr/openbr_plugin.h> | 3 | #include <openbr/openbr_plugin.h> |
| 2 | 4 | ||
| 3 | #include "openbr/core/common.h" | 5 | #include "openbr/core/common.h" |
| 6 | +#include "openbr/core/opencvutils.h" | ||
| 4 | 7 | ||
| 5 | namespace br | 8 | namespace br |
| 6 | { | 9 | { |
| @@ -32,8 +35,7 @@ class ImpostorUniquenessMeasureTransform : public Transform | @@ -32,8 +35,7 @@ class ImpostorUniquenessMeasureTransform : public Transform | ||
| 32 | QList<float> scores = distance->compare(subset, probe); | 35 | QList<float> scores = distance->compare(subset, probe); |
| 33 | float min, max; | 36 | float min, max; |
| 34 | Common::MinMax(scores, &min, &max); | 37 | Common::MinMax(scores, &min, &max); |
| 35 | - double mean; | ||
| 36 | - Common::Mean(scores, &mean); | 38 | + double mean = Common::Mean(scores); |
| 37 | return (max-mean)/(max-min); | 39 | return (max-mean)/(max-min); |
| 38 | } | 40 | } |
| 39 | 41 | ||
| @@ -199,6 +201,89 @@ class MatchProbabilityDistance : public Distance | @@ -199,6 +201,89 @@ class MatchProbabilityDistance : public Distance | ||
| 199 | BR_REGISTER(Distance, MatchProbabilityDistance) | 201 | BR_REGISTER(Distance, MatchProbabilityDistance) |
| 200 | 202 | ||
| 201 | /*! | 203 | /*! |
| 204 | + * \ingroup transforms | ||
| 205 | + * \brief Normalize by Bhattacharyya coefficient. | ||
| 206 | + * \author Josh Klontz \cite jklontz | ||
| 207 | + */ | ||
| 208 | +class BhattacharyyaTransform : public Transform | ||
| 209 | +{ | ||
| 210 | + Q_OBJECT | ||
| 211 | + cv::Mat scale; | ||
| 212 | + | ||
| 213 | + static float bhattacharyyaCoefficient(const cv::Mat &data, const QList<int> &labels) | ||
| 214 | + { | ||
| 215 | + const QList<float> vals = OpenCVUtils::matrixToVector<float>(data); | ||
| 216 | + if (vals.size() != labels.size()) | ||
| 217 | + qFatal("Logic error."); | ||
| 218 | + | ||
| 219 | + QList<float> genuineScores; genuineScores.reserve(vals.size()); | ||
| 220 | + QList<float> impostorScores; impostorScores.reserve(vals.size()*vals.size()/2); | ||
| 221 | + for (int i=0; i<vals.size(); i++) | ||
| 222 | + for (int j=i+1; j<vals.size(); j++) | ||
| 223 | + if (labels[i] == labels[j]) genuineScores.append(fabs(vals[i]-vals[j])); | ||
| 224 | + else impostorScores.append(fabs(vals[i]-vals[j])); | ||
| 225 | + | ||
| 226 | + genuineScores = Common::Downsample(genuineScores, 256); | ||
| 227 | + impostorScores = Common::Downsample(impostorScores, 256); | ||
| 228 | + double hGenuine = Common::KernelDensityBandwidth(genuineScores); | ||
| 229 | + double hImpostor = Common::KernelDensityBandwidth(impostorScores); | ||
| 230 | + | ||
| 231 | + float genuineMin, genuineMax, impostorMin, impostorMax, min, max; | ||
| 232 | + Common::MinMax(genuineScores, &genuineMin, &genuineMax); | ||
| 233 | + Common::MinMax(impostorScores, &impostorMin, &impostorMax); | ||
| 234 | + min = std::min(genuineMin, impostorMin); | ||
| 235 | + max = std::max(genuineMax, impostorMax); | ||
| 236 | + | ||
| 237 | + const int steps = 512; | ||
| 238 | + double bc = 0; | ||
| 239 | + for (int i=0; i<steps; i++) { | ||
| 240 | + float score = min + i*(max-min)/(steps-1); | ||
| 241 | + bc += sqrt(Common::KernelDensityEstimation(genuineScores, score, hGenuine) * | ||
| 242 | + Common::KernelDensityEstimation(impostorScores, score, hImpostor))/steps; | ||
| 243 | + } | ||
| 244 | + if ((bc <= 0) || (bc > 1)) qFatal("Logic error."); | ||
| 245 | + return -log(bc); | ||
| 246 | + } | ||
| 247 | + | ||
| 248 | + void train(const TemplateList &src) | ||
| 249 | + { | ||
| 250 | + const cv::Mat data = OpenCVUtils::toMat(src.data()); | ||
| 251 | + const QList<int> labels = src.labels<int>(); | ||
| 252 | + | ||
| 253 | + QFutureSynchronizer<float> futures; | ||
| 254 | + QList<float> coefficients; coefficients.reserve(data.cols); | ||
| 255 | + for (int i=0; i<data.cols; i++) | ||
| 256 | + if (Globals->parallelism) futures.addFuture(QtConcurrent::run(&BhattacharyyaTransform::bhattacharyyaCoefficient, data.col(i), labels)); | ||
| 257 | + else coefficients.append( bhattacharyyaCoefficient( data.col(i), labels)); | ||
| 258 | + futures.waitForFinished(); | ||
| 259 | + foreach (const QFuture<float> &future, futures.futures()) | ||
| 260 | + coefficients.append(future.result()); | ||
| 261 | + | ||
| 262 | + scale = cv::Mat(1, coefficients.size(), CV_32FC1); | ||
| 263 | + for (int i=0; i<coefficients.size(); i++) | ||
| 264 | + scale.at<float>(0,i) = coefficients[i]; | ||
| 265 | + | ||
| 266 | + } | ||
| 267 | + | ||
| 268 | + void project(const Template &src, Template &dst) const | ||
| 269 | + { | ||
| 270 | + cv::multiply(src.m().reshape(1,1), scale, dst); | ||
| 271 | + } | ||
| 272 | + | ||
| 273 | + void store(QDataStream &stream) const | ||
| 274 | + { | ||
| 275 | + stream << scale; | ||
| 276 | + } | ||
| 277 | + | ||
| 278 | + void load(QDataStream &stream) | ||
| 279 | + { | ||
| 280 | + stream >> scale; | ||
| 281 | + } | ||
| 282 | +}; | ||
| 283 | + | ||
| 284 | +BR_REGISTER(Transform, BhattacharyyaTransform) | ||
| 285 | + | ||
| 286 | +/*! | ||
| 202 | * \ingroup distances | 287 | * \ingroup distances |
| 203 | * \brief Linear normalizes of a distance so the mean impostor score is 0 and the mean genuine score is 1. | 288 | * \brief Linear normalizes of a distance so the mean impostor score is 0 and the mean genuine score is 1. |
| 204 | * \author Josh Klontz \cite jklontz | 289 | * \author Josh Klontz \cite jklontz |
openbr/plugins/quantize.cpp
| @@ -56,6 +56,72 @@ class QuantizeTransform : public Transform | @@ -56,6 +56,72 @@ class QuantizeTransform : public Transform | ||
| 56 | BR_REGISTER(Transform, QuantizeTransform) | 56 | BR_REGISTER(Transform, QuantizeTransform) |
| 57 | 57 | ||
| 58 | /*! | 58 | /*! |
| 59 | + * \ingroup distances | ||
| 60 | + * \brief Bayesian quantization distance | ||
| 61 | + * \author Josh Klontz \cite jklontz | ||
| 62 | + */ | ||
| 63 | +class BayesianQuantizationDistance : public Distance | ||
| 64 | +{ | ||
| 65 | + Q_OBJECT | ||
| 66 | + QVector<float> loglikelihood; | ||
| 67 | + | ||
| 68 | + void train(const TemplateList &src) | ||
| 69 | + { | ||
| 70 | + if (src.first().size() > 1) | ||
| 71 | + qFatal("Expected sigle matrix templates."); | ||
| 72 | + | ||
| 73 | + Mat data = OpenCVUtils::toMat(src.data()); | ||
| 74 | + QList<int> labels = src.labels<int>(); | ||
| 75 | + | ||
| 76 | + QVector<qint64> genuines(256*256,0), impostors(256*256,0); | ||
| 77 | + for (int i=0; i<labels.size(); i++) { | ||
| 78 | + const uchar *a = data.ptr(i); | ||
| 79 | + for (int j=0; j<labels.size(); j++) { | ||
| 80 | + const uchar *b = data.ptr(j); | ||
| 81 | + const bool genuine = (labels[i] == labels[j]); | ||
| 82 | + for (int k=0; k<data.cols; k++) | ||
| 83 | + genuine ? genuines[256*a[k]+b[k]]++ : impostors[256*a[k]+b[k]]++; | ||
| 84 | + } | ||
| 85 | + } | ||
| 86 | + | ||
| 87 | + qint64 totalGenuines(0), totalImpostors(0); | ||
| 88 | + for (int i=0; i<256*256; i++) { | ||
| 89 | + totalGenuines += genuines[i]; | ||
| 90 | + totalImpostors += impostors[i]; | ||
| 91 | + } | ||
| 92 | + | ||
| 93 | + loglikelihood = QVector<float>(256*256); | ||
| 94 | + for (int i=0; i<256; i++) | ||
| 95 | + for (int j=0; j<256; j++) | ||
| 96 | + loglikelihood[i*256+j] = log((double(genuines[i*256+j]+genuines[j*256+i]+1)/totalGenuines)/ | ||
| 97 | + (double(impostors[i*256+j]+impostors[j*256+i]+1)/totalImpostors)); | ||
| 98 | + } | ||
| 99 | + | ||
| 100 | + float compare(const Template &a, const Template &b) const | ||
| 101 | + { | ||
| 102 | + const uchar *aData = a.m().data; | ||
| 103 | + const uchar *bData = b.m().data; | ||
| 104 | + const int size = a.m().rows * a.m().cols; | ||
| 105 | + float likelihood = 0; | ||
| 106 | + for (int i=0; i<size; i++) | ||
| 107 | + likelihood += loglikelihood[256*aData[i]+bData[i]]; | ||
| 108 | + return likelihood; | ||
| 109 | + } | ||
| 110 | + | ||
| 111 | + void load(QDataStream &stream) | ||
| 112 | + { | ||
| 113 | + stream >> loglikelihood; | ||
| 114 | + } | ||
| 115 | + | ||
| 116 | + void store(QDataStream &stream) const | ||
| 117 | + { | ||
| 118 | + stream << loglikelihood; | ||
| 119 | + } | ||
| 120 | +}; | ||
| 121 | + | ||
| 122 | +BR_REGISTER(Distance, BayesianQuantizationDistance) | ||
| 123 | + | ||
| 124 | +/*! | ||
| 59 | * \ingroup transforms | 125 | * \ingroup transforms |
| 60 | * \brief Approximate floats as signed bit. | 126 | * \brief Approximate floats as signed bit. |
| 61 | * \author Josh Klontz \cite jklontz | 127 | * \author Josh Klontz \cite jklontz |
| @@ -169,6 +235,19 @@ public: | @@ -169,6 +235,19 @@ public: | ||
| 169 | } | 235 | } |
| 170 | 236 | ||
| 171 | private: | 237 | private: |
| 238 | + static void getScores(const QList<int> &indicies, const QList<int> &labels, const Mat &lut, QVector<float> &genuineScores, QVector<float> &impostorScores) | ||
| 239 | + { | ||
| 240 | + genuineScores.clear(); impostorScores.clear(); | ||
| 241 | + genuineScores.reserve(indicies.size()); | ||
| 242 | + impostorScores.reserve(indicies.size()*indicies.size()/2); | ||
| 243 | + for (int i=0; i<indicies.size(); i++) | ||
| 244 | + for (int j=i+1; j<indicies.size(); j++) { | ||
| 245 | + const float score = lut.at<float>(0, indicies[i]*256+indicies[j]); | ||
| 246 | + if (labels[i] == labels[j]) genuineScores.append(score); | ||
| 247 | + else impostorScores.append(score); | ||
| 248 | + } | ||
| 249 | + } | ||
| 250 | + | ||
| 172 | void _train(const Mat &data, const QList<int> &labels, Mat *lut, Mat *center) | 251 | void _train(const Mat &data, const QList<int> &labels, Mat *lut, Mat *center) |
| 173 | { | 252 | { |
| 174 | Mat clusterLabels; | 253 | Mat clusterLabels; |
| @@ -181,14 +260,17 @@ private: | @@ -181,14 +260,17 @@ private: | ||
| 181 | if (!bayesian) return; | 260 | if (!bayesian) return; |
| 182 | 261 | ||
| 183 | QList<int> indicies = OpenCVUtils::matrixToVector<int>(clusterLabels); | 262 | QList<int> indicies = OpenCVUtils::matrixToVector<int>(clusterLabels); |
| 184 | - QVector<float> genuineScores; genuineScores.reserve(data.rows); | ||
| 185 | - QVector<float> impostorScores; impostorScores.reserve(data.rows*data.rows/2); | ||
| 186 | - for (int i=0; i<indicies.size(); i++) | ||
| 187 | - for (int j=i+1; j<indicies.size(); j++) { | ||
| 188 | - const float score = lut->at<float>(0, indicies[i]*256+indicies[j]); | ||
| 189 | - if (labels[i] == labels[j]) genuineScores.append(score); | ||
| 190 | - else impostorScores.append(score); | ||
| 191 | - } | 263 | + QVector<float> genuineScores, impostorScores; |
| 264 | + | ||
| 265 | + // RBF Kernel | ||
| 266 | +// getScores(indicies, labels, *lut, genuineScores, impostorScores); | ||
| 267 | +// float sigma = 1.0 / Common::Mean(impostorScores); | ||
| 268 | +// for (int j=0; j<256; j++) | ||
| 269 | +// for (int k=0; k<256; k++) | ||
| 270 | +// lut->at<float>(0,j*256+k) = exp(-lut->at<float>(0,j*256+k)/(2*pow(sigma, 2.f))); | ||
| 271 | + | ||
| 272 | + // Bayesian PDF | ||
| 273 | + getScores(indicies, labels, *lut, genuineScores, impostorScores); | ||
| 192 | genuineScores = Common::Downsample(genuineScores, 256); | 274 | genuineScores = Common::Downsample(genuineScores, 256); |
| 193 | impostorScores = Common::Downsample(impostorScores, 256); | 275 | impostorScores = Common::Downsample(impostorScores, 256); |
| 194 | 276 | ||
| @@ -199,21 +281,45 @@ private: | @@ -199,21 +281,45 @@ private: | ||
| 199 | for (int k=0; k<256; k++) | 281 | for (int k=0; k<256; k++) |
| 200 | lut->at<float>(0,j*256+k) = log(Common::KernelDensityEstimation(genuineScores, lut->at<float>(0,j*256+k), hGenuine) / | 282 | lut->at<float>(0,j*256+k) = log(Common::KernelDensityEstimation(genuineScores, lut->at<float>(0,j*256+k), hGenuine) / |
| 201 | Common::KernelDensityEstimation(impostorScores, lut->at<float>(0,j*256+k), hImpostor)); | 283 | Common::KernelDensityEstimation(impostorScores, lut->at<float>(0,j*256+k), hImpostor)); |
| 284 | +// lut->at<float>(0,j*256+k) = std::max(0.0, log(Common::KernelDensityEstimation(genuineScores, lut->at<float>(0,j*256+k), hGenuine) / | ||
| 285 | +// Common::KernelDensityEstimation(impostorScores, lut->at<float>(0,j*256+k), hImpostor))); | ||
| 286 | + } | ||
| 287 | + | ||
| 288 | + int getStep(int cols) const | ||
| 289 | + { | ||
| 290 | + if (n > 0) return n; | ||
| 291 | + if (n == 0) return cols; | ||
| 292 | + return ceil(float(cols)/abs(n)); | ||
| 293 | + } | ||
| 294 | + | ||
| 295 | + int getOffset(int cols) const | ||
| 296 | + { | ||
| 297 | + if (n >= 0) return 0; | ||
| 298 | + const int step = getStep(cols); | ||
| 299 | + return (step - cols%step) % step; | ||
| 300 | + } | ||
| 301 | + | ||
| 302 | + int getDims(int cols) const | ||
| 303 | + { | ||
| 304 | + const int step = getStep(cols); | ||
| 305 | + if (n >= 0) return cols/step; | ||
| 306 | + return ceil(float(cols)/step); | ||
| 202 | } | 307 | } |
| 203 | 308 | ||
| 204 | void train(const TemplateList &src) | 309 | void train(const TemplateList &src) |
| 205 | { | 310 | { |
| 206 | Mat data = OpenCVUtils::toMat(src.data()); | 311 | Mat data = OpenCVUtils::toMat(src.data()); |
| 207 | - if (data.cols % n != 0) qFatal("Expected dimensionality to be divisible by n."); | 312 | + const int step = getStep(data.cols); |
| 208 | const QList<int> labels = src.labels<int>(); | 313 | const QList<int> labels = src.labels<int>(); |
| 209 | 314 | ||
| 210 | Mat &lut = ProductQuantizationLUTs[index]; | 315 | Mat &lut = ProductQuantizationLUTs[index]; |
| 211 | - lut = Mat(data.cols/n, 256*256, CV_32FC1); | 316 | + lut = Mat(getDims(data.cols), 256*256, CV_32FC1); |
| 212 | 317 | ||
| 213 | QList<Mat> subdata, subluts; | 318 | QList<Mat> subdata, subluts; |
| 319 | + const int offset = getOffset(data.cols); | ||
| 214 | for (int i=0; i<lut.rows; i++) { | 320 | for (int i=0; i<lut.rows; i++) { |
| 215 | centers.append(Mat()); | 321 | centers.append(Mat()); |
| 216 | - subdata.append(data.colRange(i*n,(i+1)*n)); | 322 | + subdata.append(data.colRange(max(0, i*step-offset), (i+1)*step-offset)); |
| 217 | subluts.append(lut.row(i)); | 323 | subluts.append(lut.row(i)); |
| 218 | } | 324 | } |
| 219 | 325 | ||
| @@ -242,9 +348,11 @@ private: | @@ -242,9 +348,11 @@ private: | ||
| 242 | void project(const Template &src, Template &dst) const | 348 | void project(const Template &src, Template &dst) const |
| 243 | { | 349 | { |
| 244 | Mat m = src.m().reshape(1, 1); | 350 | Mat m = src.m().reshape(1, 1); |
| 245 | - dst = Mat(1, m.cols/n, CV_8UC1); | 351 | + const int step = getStep(m.cols); |
| 352 | + const int offset = getOffset(m.cols); | ||
| 353 | + dst = Mat(1, getDims(m.cols), CV_8UC1); | ||
| 246 | for (int i=0; i<dst.m().cols; i++) | 354 | for (int i=0; i<dst.m().cols; i++) |
| 247 | - dst.m().at<uchar>(0,i) = getIndex(m.colRange(i*n, (i+1)*n), centers[i]); | 355 | + dst.m().at<uchar>(0,i) = getIndex(m.colRange(max(0, i*step-offset), (i+1)*step-offset), centers[i]); |
| 248 | } | 356 | } |
| 249 | 357 | ||
| 250 | void store(QDataStream &stream) const | 358 | void store(QDataStream &stream) const |