Commit db2484c2419cc56e76afe506065366d4feb974ab
Merge branch 'master' of https://github.com/biometrics/openbr
Showing
31 changed files
with
455 additions
and
230 deletions
.gitignore
3rdparty/stasm4.0.0/stasm/faceroi.cpp
3rdparty/stasm4.0.0/stasm/pinstart.cpp
| @@ -3,6 +3,7 @@ | @@ -3,6 +3,7 @@ | ||
| 3 | // Copyright (C) 2005-2013, Stephen Milborrow | 3 | // Copyright (C) 2005-2013, Stephen Milborrow |
| 4 | 4 | ||
| 5 | #include "stasm.h" | 5 | #include "stasm.h" |
| 6 | +#include <QDebug> | ||
| 6 | 7 | ||
| 7 | namespace stasm | 8 | namespace stasm |
| 8 | { | 9 | { |
| @@ -267,8 +268,8 @@ void PinnedStartShapeAndRoi( // use the pinned landmarks to init the start sha | @@ -267,8 +268,8 @@ void PinnedStartShapeAndRoi( // use the pinned landmarks to init the start sha | ||
| 267 | const Shape& pinned) // in: manually pinned landmarks | 268 | const Shape& pinned) // in: manually pinned landmarks |
| 268 | { | 269 | { |
| 269 | double rot, yaw; | 270 | double rot, yaw; |
| 270 | - EstRotAndYawFrom5PointShape(rot, yaw, | ||
| 271 | - As5PointShape(pinned, mods[0]->MeanShape_())); | 271 | + EstRotAndYawFrom5PointShape(rot, yaw, As5PointShape(pinned, mods[0]->MeanShape_())); |
| 272 | + | ||
| 272 | const EYAW eyaw = DegreesAsEyaw(yaw, NSIZE(mods)); | 273 | const EYAW eyaw = DegreesAsEyaw(yaw, NSIZE(mods)); |
| 273 | const int imod = EyawAsModIndex(eyaw, mods); // select ASM model based on yaw | 274 | const int imod = EyawAsModIndex(eyaw, mods); // select ASM model based on yaw |
| 274 | if (trace_g) | 275 | if (trace_g) |
3rdparty/stasm4.0.0/stasm/stasm_lib.cpp
| @@ -244,24 +244,21 @@ int stasm_search_single( // wrapper for stasm_search_auto and friends | @@ -244,24 +244,21 @@ int stasm_search_single( // wrapper for stasm_search_auto and friends | ||
| 244 | int stasm_search_pinned( // call after the user has pinned some points | 244 | int stasm_search_pinned( // call after the user has pinned some points |
| 245 | float* landmarks, // out: x0, y0, x1, y1, ..., caller must allocate | 245 | float* landmarks, // out: x0, y0, x1, y1, ..., caller must allocate |
| 246 | const float* pinned, // in: pinned landmarks (0,0 points not pinned) | 246 | const float* pinned, // in: pinned landmarks (0,0 points not pinned) |
| 247 | - const char* img, // in: gray image data, top left corner at 0,0 | 247 | + const char* data, // in: gray image data, top left corner at 0,0 |
| 248 | int width, // in: image width | 248 | int width, // in: image width |
| 249 | int height, // in: image height | 249 | int height, // in: image height |
| 250 | const char* imgpath) // in: image path, used only for err msgs and debug | 250 | const char* imgpath) // in: image path, used only for err msgs and debug |
| 251 | { | 251 | { |
| 252 | - (void) img; | ||
| 253 | (void) width; | 252 | (void) width; |
| 254 | (void) height; | 253 | (void) height; |
| 255 | (void) imgpath; | 254 | (void) imgpath; |
| 256 | 255 | ||
| 257 | int returnval = 1; // assume success | 256 | int returnval = 1; // assume success |
| 258 | - CatchOpenCvErrs(); | ||
| 259 | try | 257 | try |
| 260 | { | 258 | { |
| 261 | - CV_Assert(imgpath && STRNLEN(imgpath, SLEN) < SLEN); | ||
| 262 | CheckStasmInit(); | 259 | CheckStasmInit(); |
| 263 | 260 | ||
| 264 | - //img_g = Image(height, width, (unsigned char*)img); | 261 | + Image img = Image(height, width,(unsigned char*)data); |
| 265 | 262 | ||
| 266 | const Shape pinnedshape(LandmarksAsShape(pinned)); | 263 | const Shape pinnedshape(LandmarksAsShape(pinned)); |
| 267 | 264 | ||
| @@ -271,8 +268,7 @@ int stasm_search_pinned( // call after the user has pinned some points | @@ -271,8 +268,7 @@ int stasm_search_pinned( // call after the user has pinned some points | ||
| 271 | DetPar detpar_roi; // detpar translated to ROI frame | 268 | DetPar detpar_roi; // detpar translated to ROI frame |
| 272 | DetPar detpar; // params returned by pseudo face det, in img frame | 269 | DetPar detpar; // params returned by pseudo face det, in img frame |
| 273 | 270 | ||
| 274 | - /*PinnedStartShapeAndRoi(shape, face_roi, detpar_roi, detpar, pinned_roi, | ||
| 275 | - img_g, mods_g, pinnedshape);*/ | 271 | + PinnedStartShapeAndRoi(shape, face_roi, detpar_roi, detpar, pinned_roi, img, mods_g, pinnedshape); |
| 276 | 272 | ||
| 277 | // now working with maybe flipped ROI and start shape in ROI frame | 273 | // now working with maybe flipped ROI and start shape in ROI frame |
| 278 | const int imod = ABS(EyawAsModIndex(detpar.eyaw, mods_g)); | 274 | const int imod = ABS(EyawAsModIndex(detpar.eyaw, mods_g)); |
| @@ -284,14 +280,11 @@ int stasm_search_pinned( // call after the user has pinned some points | @@ -284,14 +280,11 @@ int stasm_search_pinned( // call after the user has pinned some points | ||
| 284 | RoundMat(shape); | 280 | RoundMat(shape); |
| 285 | ForcePinnedPoints(shape, pinnedshape); // undo above RoundMat on pinned points | 281 | ForcePinnedPoints(shape, pinnedshape); // undo above RoundMat on pinned points |
| 286 | ShapeToLandmarks(landmarks, shape); | 282 | ShapeToLandmarks(landmarks, shape); |
| 287 | - if (trace_g) | ||
| 288 | - lprintf("\n"); | ||
| 289 | } | 283 | } |
| 290 | catch(...) | 284 | catch(...) |
| 291 | { | 285 | { |
| 292 | returnval = 0; // a call was made to Err or a CV_Assert failed | 286 | returnval = 0; // a call was made to Err or a CV_Assert failed |
| 293 | } | 287 | } |
| 294 | - UncatchOpenCvErrs(); | ||
| 295 | return returnval; | 288 | return returnval; |
| 296 | } | 289 | } |
| 297 | 290 |
app/br/br.cpp
| @@ -111,8 +111,8 @@ public: | @@ -111,8 +111,8 @@ public: | ||
| 111 | 111 | ||
| 112 | // Secondary Tasks | 112 | // Secondary Tasks |
| 113 | else if (!strcmp(fun, "fuse")) { | 113 | else if (!strcmp(fun, "fuse")) { |
| 114 | - check(parc >= 5, "Insufficient parameter count for 'fuse'."); | ||
| 115 | - br_fuse(parc-4, parv, parv[parc-4], parv[parc-3], parv[parc-2], parv[parc-1]); | 114 | + check(parc >= 4, "Insufficient parameter count for 'fuse'."); |
| 115 | + br_fuse(parc-3, parv, parv[parc-3], parv[parc-2], parv[parc-1]); | ||
| 116 | } else if (!strcmp(fun, "cluster")) { | 116 | } else if (!strcmp(fun, "cluster")) { |
| 117 | check(parc >= 3, "Insufficient parameter count for 'cluster'."); | 117 | check(parc >= 3, "Insufficient parameter count for 'cluster'."); |
| 118 | br_cluster(parc-2, parv, atof(parv[parc-2]), parv[parc-1]); | 118 | br_cluster(parc-2, parv, atof(parv[parc-2]), parv[parc-1]); |
| @@ -215,7 +215,7 @@ private: | @@ -215,7 +215,7 @@ private: | ||
| 215 | "-plot <file> ... <file> {destination}\n" | 215 | "-plot <file> ... <file> {destination}\n" |
| 216 | "\n" | 216 | "\n" |
| 217 | "==== Other Commands ====\n" | 217 | "==== Other Commands ====\n" |
| 218 | - "-fuse <simmat> ... <simmat> <mask> (None|MinMax|ZScore|WScore) (Min|Max|Sum[W1:W2:...:Wn]|Replace|Difference|None) {simmat}\n" | 218 | + "-fuse <simmat> ... <simmat> (None|MinMax|ZScore|WScore) (Min|Max|Sum[W1:W2:...:Wn]|Replace|Difference|None) {simmat}\n" |
| 219 | "-cluster <simmat> ... <simmat> <aggressiveness> {csv}\n" | 219 | "-cluster <simmat> ... <simmat> <aggressiveness> {csv}\n" |
| 220 | "-makeMask <target_gallery> <query_gallery> {mask}\n" | 220 | "-makeMask <target_gallery> <query_gallery> {mask}\n" |
| 221 | "-combineMasks <mask> ... <mask> {mask} (And|Or)\n" | 221 | "-combineMasks <mask> ... <mask> {mask} (And|Or)\n" |
app/examples/face_recognition.cpp
| @@ -45,7 +45,7 @@ int main(int argc, char *argv[]) | @@ -45,7 +45,7 @@ int main(int argc, char *argv[]) | ||
| 45 | 45 | ||
| 46 | // Initialize templates | 46 | // Initialize templates |
| 47 | br::Template queryA("../data/MEDS/img/S354-01-t10_01.jpg"); | 47 | br::Template queryA("../data/MEDS/img/S354-01-t10_01.jpg"); |
| 48 | - br::Template queryB("../data/MEDS/img/S386-04-t10_01.jpg"); | 48 | + br::Template queryB("../data/MEDS/img/S382-08-t10_01.jpg"); |
| 49 | br::Template target("../data/MEDS/img/S354-02-t10_01.jpg"); | 49 | br::Template target("../data/MEDS/img/S354-02-t10_01.jpg"); |
| 50 | 50 | ||
| 51 | // Enroll templates | 51 | // Enroll templates |
data/INRIAPerson/README.md
0 → 100644
openbr/core/bee.cpp
| @@ -75,6 +75,21 @@ FileList BEE::readSigset(const File &sigset, bool ignoreMetadata) | @@ -75,6 +75,21 @@ FileList BEE::readSigset(const File &sigset, bool ignoreMetadata) | ||
| 75 | else if (!ignoreMetadata) file.set(key, value); | 75 | else if (!ignoreMetadata) file.set(key, value); |
| 76 | } | 76 | } |
| 77 | 77 | ||
| 78 | + // add bounding boxes, if they exist (will be child elements of <presentation>) | ||
| 79 | + if (fileNode.hasChildNodes()) { | ||
| 80 | + QList<QRectF> rects; | ||
| 81 | + QDomNodeList bboxes = fileNode.childNodes(); | ||
| 82 | + for (int i=0; i<bboxes.length(); i++) { | ||
| 83 | + QDomElement bbox = bboxes.at(i).toElement(); | ||
| 84 | + qreal x = bbox.attribute("x").toDouble(); | ||
| 85 | + qreal y = bbox.attribute("y").toDouble(); | ||
| 86 | + qreal width = bbox.attribute("width").toDouble(); | ||
| 87 | + qreal height = bbox.attribute("height").toDouble(); | ||
| 88 | + rects += QRectF(x, y, width, height); | ||
| 89 | + } | ||
| 90 | + file.setRects(rects); | ||
| 91 | + } | ||
| 92 | + | ||
| 78 | if (file.name.isEmpty()) qFatal("Missing file-name in %s.", qPrintable(sigset)); | 93 | if (file.name.isEmpty()) qFatal("Missing file-name in %s.", qPrintable(sigset)); |
| 79 | fileList.append(file); | 94 | fileList.append(file); |
| 80 | 95 | ||
| @@ -112,7 +127,7 @@ void BEE::writeSigset(const QString &sigset, const br::FileList &files, bool ign | @@ -112,7 +127,7 @@ void BEE::writeSigset(const QString &sigset, const br::FileList &files, bool ign | ||
| 112 | metadata.append("Rects=\"["+landmarks.join(",")+"]\""); | 127 | metadata.append("Rects=\"["+landmarks.join(",")+"]\""); |
| 113 | } | 128 | } |
| 114 | } | 129 | } |
| 115 | - lines.append("\t<biometric-signature name=\"" + file.get<QString>("Label",file.fileName()) +"\">"); | 130 | + lines.append("\t<biometric-signature name=\"" + file.get<QString>("Label",file.baseName()) +"\">"); |
| 116 | lines.append("\t\t<presentation file-name=\"" + file.name + "\" " + metadata.join(" ") + "/>"); | 131 | lines.append("\t\t<presentation file-name=\"" + file.name + "\" " + metadata.join(" ") + "/>"); |
| 117 | lines.append("\t</biometric-signature>"); | 132 | lines.append("\t</biometric-signature>"); |
| 118 | } | 133 | } |
| @@ -302,7 +317,6 @@ cv::Mat BEE::makeMask(const br::FileList &targets, const br::FileList &queries, | @@ -302,7 +317,6 @@ cv::Mat BEE::makeMask(const br::FileList &targets, const br::FileList &queries, | ||
| 302 | else if (partitionA != partition) val = DontCare; | 317 | else if (partitionA != partition) val = DontCare; |
| 303 | else if (partitionB == -1) val = NonMatch; | 318 | else if (partitionB == -1) val = NonMatch; |
| 304 | else if (partitionB != partition) val = DontCare; | 319 | else if (partitionB != partition) val = DontCare; |
| 305 | - else if (partitionA != partitionB) val = DontCare; | ||
| 306 | else if (labelA == labelB) val = Match; | 320 | else if (labelA == labelB) val = Match; |
| 307 | else val = NonMatch; | 321 | else val = NonMatch; |
| 308 | mask.at<Mask_t>(i,j) = val; | 322 | mask.at<Mask_t>(i,j) = val; |
openbr/core/core.cpp
| @@ -43,6 +43,11 @@ struct AlgorithmCore | @@ -43,6 +43,11 @@ struct AlgorithmCore | ||
| 43 | { | 43 | { |
| 44 | TemplateList data(TemplateList::fromGallery(input)); | 44 | TemplateList data(TemplateList::fromGallery(input)); |
| 45 | 45 | ||
| 46 | + // set the Train bool metadata, in case a Transform's project | ||
| 47 | + // needs to know if it's called during train or enroll | ||
| 48 | + for (int i=0; i<data.size(); i++) | ||
| 49 | + data[i].file.set("Train", true); | ||
| 50 | + | ||
| 46 | if (transform.isNull()) qFatal("Null transform."); | 51 | if (transform.isNull()) qFatal("Null transform."); |
| 47 | qDebug("%d training files", data.size()); | 52 | qDebug("%d training files", data.size()); |
| 48 | 53 |
openbr/core/fuse.cpp
| @@ -16,6 +16,7 @@ | @@ -16,6 +16,7 @@ | ||
| 16 | 16 | ||
| 17 | #include <QList> | 17 | #include <QList> |
| 18 | #include <QStringList> | 18 | #include <QStringList> |
| 19 | +#include "openbr/core/opencvutils.h" | ||
| 19 | #include <limits> | 20 | #include <limits> |
| 20 | #include <vector> | 21 | #include <vector> |
| 21 | #include <opencv2/core/core.hpp> | 22 | #include <opencv2/core/core.hpp> |
| @@ -36,8 +37,8 @@ static void normalizeMatrix(Mat &matrix, const Mat &mask, const QString &method) | @@ -36,8 +37,8 @@ static void normalizeMatrix(Mat &matrix, const Mat &mask, const QString &method) | ||
| 36 | for (int j=0; j<matrix.cols; j++) { | 37 | for (int j=0; j<matrix.cols; j++) { |
| 37 | float val = matrix.at<float>(i,j); | 38 | float val = matrix.at<float>(i,j); |
| 38 | if ((mask.at<BEE::Mask_t>(i,j) == BEE::DontCare) || | 39 | if ((mask.at<BEE::Mask_t>(i,j) == BEE::DontCare) || |
| 39 | - (val == -std::numeric_limits<float>::infinity()) || | ||
| 40 | - (val == std::numeric_limits<float>::infinity())) | 40 | + (val == -std::numeric_limits<float>::max()) || |
| 41 | + (val == std::numeric_limits<float>::max())) | ||
| 41 | continue; | 42 | continue; |
| 42 | vals.append(val); | 43 | vals.append(val); |
| 43 | } | 44 | } |
| @@ -53,20 +54,20 @@ static void normalizeMatrix(Mat &matrix, const Mat &mask, const QString &method) | @@ -53,20 +54,20 @@ static void normalizeMatrix(Mat &matrix, const Mat &mask, const QString &method) | ||
| 53 | for (int j=0; j<matrix.cols; j++) { | 54 | for (int j=0; j<matrix.cols; j++) { |
| 54 | if (mask.at<BEE::Mask_t>(i,j) == BEE::DontCare) continue; | 55 | if (mask.at<BEE::Mask_t>(i,j) == BEE::DontCare) continue; |
| 55 | float &val = matrix.at<float>(i,j); | 56 | float &val = matrix.at<float>(i,j); |
| 56 | - if (val == -std::numeric_limits<float>::infinity()) val = 0; | ||
| 57 | - else if (val == std::numeric_limits<float>::infinity()) val = 1; | 57 | + if (val == -std::numeric_limits<float>::max()) val = 0; |
| 58 | + else if (val == std::numeric_limits<float>::max()) val = 1; | ||
| 58 | else val = (val - min) / (max - min); | 59 | else val = (val - min) / (max - min); |
| 59 | } | 60 | } |
| 60 | } | 61 | } |
| 61 | } else if (method == "ZScore") { | 62 | } else if (method == "ZScore") { |
| 62 | if (stddev == 0) qFatal("Stddev is 0."); | 63 | if (stddev == 0) qFatal("Stddev is 0."); |
| 63 | - for (int i=0; i<matrix.rows; i++) { | 64 | + for (int i=0; i<matrix.rows; i++) { |
| 64 | for (int j=0; j<matrix.cols; j++) { | 65 | for (int j=0; j<matrix.cols; j++) { |
| 65 | if (mask.at<BEE::Mask_t>(i,j) == BEE::DontCare) continue; | 66 | if (mask.at<BEE::Mask_t>(i,j) == BEE::DontCare) continue; |
| 66 | float &val = matrix.at<float>(i,j); | 67 | float &val = matrix.at<float>(i,j); |
| 67 | - if (val == -std::numeric_limits<float>::infinity()) val = (min - mean) / stddev; | ||
| 68 | - else if (val == std::numeric_limits<float>::infinity()) val = (max - mean) / stddev; | ||
| 69 | - else val = (val - mean) / stddev; | 68 | + if (val == -std::numeric_limits<float>::max()) val = (min - mean) / stddev; |
| 69 | + else if (val == std::numeric_limits<float>::max()) val = (max - mean) / stddev; | ||
| 70 | + else val = (val - mean) / stddev; | ||
| 70 | } | 71 | } |
| 71 | } | 72 | } |
| 72 | } else { | 73 | } else { |
| @@ -74,63 +75,90 @@ static void normalizeMatrix(Mat &matrix, const Mat &mask, const QString &method) | @@ -74,63 +75,90 @@ static void normalizeMatrix(Mat &matrix, const Mat &mask, const QString &method) | ||
| 74 | } | 75 | } |
| 75 | } | 76 | } |
| 76 | 77 | ||
| 77 | -void br::Fuse(const QStringList &inputSimmats, File mask, const QString &normalization, const QString &fusion, const QString &outputSimmat) | 78 | +void br::Fuse(const QStringList &inputSimmats, const QString &normalization, const QString &fusion, const QString &outputSimmat) |
| 78 | { | 79 | { |
| 79 | qDebug("Fusing %d to %s", inputSimmats.size(), qPrintable(outputSimmat)); | 80 | qDebug("Fusing %d to %s", inputSimmats.size(), qPrintable(outputSimmat)); |
| 80 | - QList<Mat> matrices; | ||
| 81 | - foreach (const QString &simmat, inputSimmats) | ||
| 82 | - matrices.append(BEE::readSimmat(simmat)); | ||
| 83 | - if ((matrices.size() < 2) && (fusion != "None")) qFatal("Expected at least two similarity matrices."); | ||
| 84 | - if ((matrices.size() > 1) && (fusion == "None")) qFatal("Expected exactly one similarity matrix."); | ||
| 85 | - | ||
| 86 | - mask.set("rows", matrices.first().rows); | ||
| 87 | - mask.set("columns", matrices.first().cols); | ||
| 88 | - Mat matrix_mask = BEE::readMask(mask); | ||
| 89 | - | ||
| 90 | - for (int i=0; i<matrices.size(); i++) | ||
| 91 | - normalizeMatrix(matrices[i], matrix_mask, normalization); | ||
| 92 | - | ||
| 93 | - Mat fused; | ||
| 94 | - if (fusion == "Max") { | ||
| 95 | - max(matrices[0], matrices[1], fused); | ||
| 96 | - for (int i=2; i<matrices.size(); i++) | ||
| 97 | - max(fused, matrices[i], fused); | ||
| 98 | - } else if (fusion == "Min") { | ||
| 99 | - min(matrices[0], matrices[1], fused); | ||
| 100 | - for (int i=2; i<matrices.size(); i++) | ||
| 101 | - min(fused, matrices[i], fused); | ||
| 102 | - } else if (fusion.startsWith("Sum")) { | ||
| 103 | - QList<float> weights; | ||
| 104 | - QStringList words = fusion.right(fusion.size()-3).split(":", QString::SkipEmptyParts); | ||
| 105 | - if (words.size() == 0) { | ||
| 106 | - for (int k=0; k<matrices.size(); k++) | ||
| 107 | - weights.append(1); | ||
| 108 | - } else if (words.size() == matrices.size()) { | ||
| 109 | - bool ok; | ||
| 110 | - for (int k=0; k<matrices.size(); k++) { | ||
| 111 | - float weight = words[k].toFloat(&ok); | ||
| 112 | - if (!ok) qFatal("Non-numerical weight %s.", qPrintable(words[k])); | ||
| 113 | - weights.append(weight); | 81 | + |
| 82 | + QString target, query, previousTarget, previousQuery; | ||
| 83 | + QList<Mat> originalMatrices; | ||
| 84 | + foreach (const QString &simmat, inputSimmats) { | ||
| 85 | + originalMatrices.append(BEE::readSimmat(simmat,&target,&query)); | ||
| 86 | + // Make we're fusing score matrices for the same set of targets and querys | ||
| 87 | + if (!previousTarget.isEmpty() && !previousQuery.isEmpty() && (previousTarget != target || previousQuery != query)) | ||
| 88 | + qFatal("Target or query files are not the same across fused matrices."); | ||
| 89 | + previousTarget = target; previousQuery = query; | ||
| 90 | + } | ||
| 91 | + | ||
| 92 | + if ((originalMatrices.size() < 2) && (fusion != "None")) qFatal("Expected at least two similarity matrices."); | ||
| 93 | + if ((originalMatrices.size() > 1) && (fusion == "None")) qFatal("Expected exactly one similarity matrix."); | ||
| 94 | + | ||
| 95 | + const FileList targetFiles = TemplateList::fromGallery(target).files(); | ||
| 96 | + const FileList queryFiles = TemplateList::fromGallery(query).files(); | ||
| 97 | + | ||
| 98 | + int partition = 0; | ||
| 99 | + int crossValidate = Globals->crossValidate; | ||
| 100 | + Mat buffer = Mat::zeros(originalMatrices.last().size(),originalMatrices.last().type()); | ||
| 101 | + | ||
| 102 | + do { | ||
| 103 | + QList<Mat> matrices; | ||
| 104 | + foreach (const Mat& matrix, originalMatrices) | ||
| 105 | + matrices.append(matrix.clone()); | ||
| 106 | + | ||
| 107 | + Mat matrix_mask = BEE::makeMask(targetFiles,queryFiles,partition); | ||
| 108 | + for (int i=0; i<matrices.size(); i++) | ||
| 109 | + normalizeMatrix(matrices[i], matrix_mask, normalization); | ||
| 110 | + | ||
| 111 | + Mat fused; | ||
| 112 | + if (fusion == "Max") { | ||
| 113 | + max(matrices[0], matrices[1], fused); | ||
| 114 | + for (int i=2; i<matrices.size(); i++) | ||
| 115 | + max(fused, matrices[i], fused); | ||
| 116 | + } else if (fusion == "Min") { | ||
| 117 | + min(matrices[0], matrices[1], fused); | ||
| 118 | + for (int i=2; i<matrices.size(); i++) | ||
| 119 | + min(fused, matrices[i], fused); | ||
| 120 | + } else if (fusion.startsWith("Sum")) { | ||
| 121 | + QList<float> weights; | ||
| 122 | + QStringList words = fusion.right(fusion.size()-3).split(":", QString::SkipEmptyParts); | ||
| 123 | + if (words.size() == 0) { | ||
| 124 | + for (int k=0; k<matrices.size(); k++) | ||
| 125 | + weights.append(1); | ||
| 126 | + } else if (words.size() == matrices.size()) { | ||
| 127 | + bool ok; | ||
| 128 | + for (int k=0; k<matrices.size(); k++) { | ||
| 129 | + float weight = words[k].toFloat(&ok); | ||
| 130 | + if (!ok) qFatal("Non-numerical weight %s.", qPrintable(words[k])); | ||
| 131 | + weights.append(weight); | ||
| 132 | + } | ||
| 133 | + } else { | ||
| 134 | + qFatal("Number of weights does not match number of similarity matrices."); | ||
| 114 | } | 135 | } |
| 136 | + | ||
| 137 | + addWeighted(matrices[0], weights[0], matrices[1], weights[1], 0, fused); | ||
| 138 | + for (int i=2; i<matrices.size(); i++) | ||
| 139 | + addWeighted(fused, 1, matrices[i], weights[i], 0, fused); | ||
| 140 | + } else if (fusion == "Replace") { | ||
| 141 | + if (matrices.size() != 2) qFatal("Replace fusion requires exactly two matrices."); | ||
| 142 | + fused = matrices.first().clone(); | ||
| 143 | + matrices.last().copyTo(fused, matrix_mask != BEE::DontCare); | ||
| 144 | + } else if (fusion == "Difference") { | ||
| 145 | + if (matrices.size() != 2) qFatal("Difference fusion requires exactly two matrices."); | ||
| 146 | + subtract(matrices[0], matrices[1], fused); | ||
| 147 | + } else if (fusion == "None") { | ||
| 148 | + fused = matrices[0]; | ||
| 115 | } else { | 149 | } else { |
| 116 | - qFatal("Number of weights does not match number of similarity matrices."); | 150 | + qFatal("Invalid fusion method %s.", qPrintable(fusion)); |
| 117 | } | 151 | } |
| 118 | 152 | ||
| 119 | - addWeighted(matrices[0], weights[0], matrices[1], weights[1], 0, fused); | ||
| 120 | - for (int i=2; i<matrices.size(); i++) | ||
| 121 | - addWeighted(fused, 1, matrices[i], weights[i], 0, fused); | ||
| 122 | - } else if (fusion == "Replace") { | ||
| 123 | - if (matrices.size() != 2) qFatal("Replace fusion requires exactly two matrices."); | ||
| 124 | - fused = matrices.first().clone(); | ||
| 125 | - matrices.last().copyTo(fused, matrix_mask != BEE::DontCare); | ||
| 126 | - } else if (fusion == "Difference") { | ||
| 127 | - if (matrices.size() != 2) qFatal("Difference fusion requires exactly two matrices."); | ||
| 128 | - subtract(matrices[0], matrices[1], fused); | ||
| 129 | - } else if (fusion == "None") { | ||
| 130 | - fused = matrices[0]; | ||
| 131 | - } else { | ||
| 132 | - qFatal("Invalid fusion method %s.", qPrintable(fusion)); | ||
| 133 | - } | 153 | + // We don't want to add scores where the mask says we shouldn't care |
| 154 | + Mat buffer_mask = Mat::ones(matrix_mask.size(),CV_8UC1); | ||
| 155 | + buffer_mask.setTo(0,matrix_mask==BEE::DontCare); | ||
| 156 | + | ||
| 157 | + add(buffer,fused,buffer,buffer_mask); | ||
| 158 | + | ||
| 159 | + partition++; | ||
| 160 | + | ||
| 161 | + } while (partition < crossValidate); | ||
| 134 | 162 | ||
| 135 | - BEE::writeSimmat(fused, outputSimmat); | 163 | + BEE::writeSimmat(buffer, outputSimmat); |
| 136 | } | 164 | } |
openbr/core/fuse.h
| @@ -22,7 +22,7 @@ | @@ -22,7 +22,7 @@ | ||
| 22 | 22 | ||
| 23 | namespace br | 23 | namespace br |
| 24 | { | 24 | { |
| 25 | - void Fuse(const QStringList &inputSimmats, File mask, const QString &normalization, const QString &fusion, const QString &outputSimmat); | 25 | + void Fuse(const QStringList &inputSimmats, const QString &normalization, const QString &fusion, const QString &outputSimmat); |
| 26 | } | 26 | } |
| 27 | 27 | ||
| 28 | #endif // BR_FUSE_H | 28 | #endif // BR_FUSE_H |
openbr/openbr.cpp
| @@ -102,10 +102,10 @@ void br_finalize() | @@ -102,10 +102,10 @@ void br_finalize() | ||
| 102 | Context::finalize(); | 102 | Context::finalize(); |
| 103 | } | 103 | } |
| 104 | 104 | ||
| 105 | -void br_fuse(int num_input_simmats, const char *input_simmats[], const char *mask, | 105 | +void br_fuse(int num_input_simmats, const char *input_simmats[], |
| 106 | const char *normalization, const char *fusion, const char *output_simmat) | 106 | const char *normalization, const char *fusion, const char *output_simmat) |
| 107 | { | 107 | { |
| 108 | - Fuse(QtUtils::toStringList(num_input_simmats, input_simmats), mask, normalization, fusion, output_simmat); | 108 | + Fuse(QtUtils::toStringList(num_input_simmats, input_simmats), normalization, fusion, output_simmat); |
| 109 | } | 109 | } |
| 110 | 110 | ||
| 111 | void br_initialize(int &argc, char *argv[], const char *sdk_path) | 111 | void br_initialize(int &argc, char *argv[], const char *sdk_path) |
openbr/openbr.h
| @@ -199,7 +199,7 @@ BR_EXPORT void br_finalize(); | @@ -199,7 +199,7 @@ BR_EXPORT void br_finalize(); | ||
| 199 | * - \c Replace - Replaces scores in the first matrix with scores in the second matrix when the mask is set. | 199 | * - \c Replace - Replaces scores in the first matrix with scores in the second matrix when the mask is set. |
| 200 | * \param output_simmat \ref simmat to contain the fused scores. | 200 | * \param output_simmat \ref simmat to contain the fused scores. |
| 201 | */ | 201 | */ |
| 202 | -BR_EXPORT void br_fuse(int num_input_simmats, const char *input_simmats[], const char *mask, | 202 | +BR_EXPORT void br_fuse(int num_input_simmats, const char *input_simmats[], |
| 203 | const char *normalization, const char *fusion, const char *output_simmat); | 203 | const char *normalization, const char *fusion, const char *output_simmat); |
| 204 | 204 | ||
| 205 | /*! | 205 | /*! |
openbr/openbr_export.cpp
| @@ -29,7 +29,8 @@ | @@ -29,7 +29,8 @@ | ||
| 29 | * \section get_started Get Started | 29 | * \section get_started Get Started |
| 30 | * - \ref introduction - A high-level technical overview of OpenBR. | 30 | * - \ref introduction - A high-level technical overview of OpenBR. |
| 31 | * - \ref installation - A hacker's guide to building, editing, and running OpenBR. | 31 | * - \ref installation - A hacker's guide to building, editing, and running OpenBR. |
| 32 | - * - \ref qmake_integration - Add OpenBR to your Qt <tt>.pro</tt> project. | 32 | + * - \ref examples - Source code illustrating common use cases. |
| 33 | + * - \ref help - Talk to the experts. | ||
| 33 | * | 34 | * |
| 34 | * \section learn_more Learn More | 35 | * \section learn_more Learn More |
| 35 | * - \ref algorithm_grammar - How algorithms are constructed from string descriptions. | 36 | * - \ref algorithm_grammar - How algorithms are constructed from string descriptions. |
| @@ -37,6 +38,7 @@ | @@ -37,6 +38,7 @@ | ||
| 37 | * - \ref c_sdk - High-level API for running algorithms and evaluating results. | 38 | * - \ref c_sdk - High-level API for running algorithms and evaluating results. |
| 38 | * - \ref cpp_plugin_sdk - Plugin API for extending OpenBR functionality. | 39 | * - \ref cpp_plugin_sdk - Plugin API for extending OpenBR functionality. |
| 39 | * - \ref bee - A <a href="http://www.nist.gov/index.html">NIST</a> standard for evaluating biometric algorithms. | 40 | * - \ref bee - A <a href="http://www.nist.gov/index.html">NIST</a> standard for evaluating biometric algorithms. |
| 41 | + * - \ref qmake_integration - Add OpenBR to your Qt <tt>.pro</tt> project. | ||
| 40 | */ | 42 | */ |
| 41 | 43 | ||
| 42 | /*! | 44 | /*! |
| @@ -397,6 +399,12 @@ $ br -help | @@ -397,6 +399,12 @@ $ br -help | ||
| 397 | * \endcode | 399 | * \endcode |
| 398 | */ | 400 | */ |
| 399 | 401 | ||
| 402 | + /*! | ||
| 403 | + * \page help Help | ||
| 404 | + * - Developer mailing list: <a href="https://groups.google.com/forum/?fromgroups#!forum/openbr-dev">openbr-dev@googlegroups.com</a> | ||
| 405 | + * - IRC Channel: <a href="http://webchat.freenode.net/">irc.freenode.net#openbr</a> | ||
| 406 | + */ | ||
| 407 | + | ||
| 400 | /*! | 408 | /*! |
| 401 | * \page qmake_integration QMake Integration | 409 | * \page qmake_integration QMake Integration |
| 402 | * \brief Add OpenBR to your Qt <tt>.pro</tt> project. | 410 | * \brief Add OpenBR to your Qt <tt>.pro</tt> project. |
openbr/openbr_plugin.cpp
| @@ -441,10 +441,10 @@ TemplateList TemplateList::fromGallery(const br::File &gallery) | @@ -441,10 +441,10 @@ TemplateList TemplateList::fromGallery(const br::File &gallery) | ||
| 441 | // of target images to every partition | 441 | // of target images to every partition |
| 442 | newTemplates[i].file.set("Partition", -1); | 442 | newTemplates[i].file.set("Partition", -1); |
| 443 | } else { | 443 | } else { |
| 444 | - // Direct use of "Label" is not general -cao | ||
| 445 | - const QByteArray md5 = QCryptographicHash::hash(newTemplates[i].file.get<QString>("Label").toLatin1(), QCryptographicHash::Md5); | ||
| 446 | - // Select the right 8 hex characters so that it can be represented as a 64 bit integer without overflow | ||
| 447 | - newTemplates[i].file.set("Partition", md5.toHex().right(8).toULongLong(0, 16) % crossValidate); | 444 | + // Direct use of "Label" is not general -cao |
| 445 | + const QByteArray md5 = QCryptographicHash::hash(newTemplates[i].file.get<QString>("Label").toLatin1(), QCryptographicHash::Md5); | ||
| 446 | + // Select the right 8 hex characters so that it can be represented as a 64 bit integer without overflow | ||
| 447 | + newTemplates[i].file.set("Partition", md5.toHex().right(8).toULongLong(0, 16) % crossValidate); | ||
| 448 | } | 448 | } |
| 449 | } | 449 | } |
| 450 | } | 450 | } |
| @@ -996,9 +996,9 @@ void br::Context::messageHandler(QtMsgType type, const QMessageLogContext &conte | @@ -996,9 +996,9 @@ void br::Context::messageHandler(QtMsgType type, const QMessageLogContext &conte | ||
| 996 | switch (type) { | 996 | switch (type) { |
| 997 | case QtWarningMsg: txt = QString("Warning: %1\n" ).arg(msg); break; | 997 | case QtWarningMsg: txt = QString("Warning: %1\n" ).arg(msg); break; |
| 998 | case QtCriticalMsg: txt = QString("Critical: %1\n").arg(msg); break; | 998 | case QtCriticalMsg: txt = QString("Critical: %1\n").arg(msg); break; |
| 999 | - default: txt = QString("Fatal: %1\n" ).arg(msg); | 999 | + default: txt = QString("Fatal: %1\n" ).arg(msg); break; |
| 1000 | } | 1000 | } |
| 1001 | - txt += " File: " + QString(context.file) + "\n Function: " + QString(context.function) + "\n Line: " + QString::number(context.line) + "\n"; | 1001 | + txt += " SDK Path: " + Globals->sdkPath + "\n File: " + QString(context.file) + "\n Function: " + QString(context.function) + "\n Line: " + QString::number(context.line) + "\n"; |
| 1002 | } | 1002 | } |
| 1003 | 1003 | ||
| 1004 | std::cerr << txt.toStdString(); | 1004 | std::cerr << txt.toStdString(); |
openbr/openbr_plugin.h
| @@ -162,6 +162,7 @@ void reset_##NAME() { NAME = DEFAULT; } | @@ -162,6 +162,7 @@ void reset_##NAME() { NAME = DEFAULT; } | ||
| 162 | * Rects | QList<Rect> | List of unnamed rects | 162 | * Rects | QList<Rect> | List of unnamed rects |
| 163 | * Age | float | Age used for demographic filtering | 163 | * Age | float | Age used for demographic filtering |
| 164 | * Gender | QString | Subject gender | 164 | * Gender | QString | Subject gender |
| 165 | + * Train | bool | The data is for training, as opposed to enrollment | ||
| 165 | * _* | * | Reserved for internal use | 166 | * _* | * | Reserved for internal use |
| 166 | */ | 167 | */ |
| 167 | struct BR_EXPORT File | 168 | struct BR_EXPORT File |
openbr/plugins/crop.cpp
| @@ -60,8 +60,13 @@ class ROITransform : public UntrainableTransform | @@ -60,8 +60,13 @@ class ROITransform : public UntrainableTransform | ||
| 60 | 60 | ||
| 61 | void project(const Template &src, Template &dst) const | 61 | void project(const Template &src, Template &dst) const |
| 62 | { | 62 | { |
| 63 | - foreach (const QRectF &rect, src.file.rects()) | ||
| 64 | - dst += src.m()(OpenCVUtils::toRect(rect)); | 63 | + if (src.file.rects().empty()) { |
| 64 | + dst = src; | ||
| 65 | + qWarning("No rects present in file."); | ||
| 66 | + } | ||
| 67 | + else | ||
| 68 | + foreach (const QRectF &rect, src.file.rects()) | ||
| 69 | + dst += src.m()(OpenCVUtils::toRect(rect)); | ||
| 65 | } | 70 | } |
| 66 | }; | 71 | }; |
| 67 | 72 |
openbr/plugins/distance.cpp
| @@ -310,7 +310,7 @@ class OnlineDistance : public Distance | @@ -310,7 +310,7 @@ class OnlineDistance : public Distance | ||
| 310 | Q_PROPERTY(br::Distance* distance READ get_distance WRITE set_distance RESET reset_distance STORED false) | 310 | Q_PROPERTY(br::Distance* distance READ get_distance WRITE set_distance RESET reset_distance STORED false) |
| 311 | Q_PROPERTY(float alpha READ get_alpha WRITE set_alpha RESET reset_alpha STORED false) | 311 | Q_PROPERTY(float alpha READ get_alpha WRITE set_alpha RESET reset_alpha STORED false) |
| 312 | BR_PROPERTY(br::Distance*, distance, NULL) | 312 | BR_PROPERTY(br::Distance*, distance, NULL) |
| 313 | - BR_PROPERTY(float, alpha, 0.1f); | 313 | + BR_PROPERTY(float, alpha, 0.1f) |
| 314 | 314 | ||
| 315 | mutable QHash<QString,float> scoreHash; | 315 | mutable QHash<QString,float> scoreHash; |
| 316 | mutable QMutex mutex; | 316 | mutable QMutex mutex; |
openbr/plugins/filter.cpp
| @@ -139,10 +139,8 @@ class CSDNTransform : public UntrainableTransform | @@ -139,10 +139,8 @@ class CSDNTransform : public UntrainableTransform | ||
| 139 | 139 | ||
| 140 | const int surround = s/2; | 140 | const int surround = s/2; |
| 141 | 141 | ||
| 142 | - for ( int i = 0; i < nRows; i++ ) | ||
| 143 | - { | ||
| 144 | - for ( int j = 0; j < nCols; j++ ) | ||
| 145 | - { | 142 | + for ( int i = 0; i < nRows; i++ ) { |
| 143 | + for ( int j = 0; j < nCols; j++ ) { | ||
| 146 | int width = min( j+surround, nCols ) - max( 0, j-surround ); | 144 | int width = min( j+surround, nCols ) - max( 0, j-surround ); |
| 147 | int height = min( i+surround, nRows ) - max( 0, i-surround ); | 145 | int height = min( i+surround, nRows ) - max( 0, i-surround ); |
| 148 | 146 | ||
| @@ -154,7 +152,7 @@ class CSDNTransform : public UntrainableTransform | @@ -154,7 +152,7 @@ class CSDNTransform : public UntrainableTransform | ||
| 154 | } | 152 | } |
| 155 | } | 153 | } |
| 156 | 154 | ||
| 157 | - dst = m; | 155 | + dst = m; |
| 158 | 156 | ||
| 159 | } | 157 | } |
| 160 | }; | 158 | }; |
openbr/plugins/gallery.cpp
| @@ -119,6 +119,7 @@ class galGallery : public Gallery | @@ -119,6 +119,7 @@ class galGallery : public Gallery | ||
| 119 | { | 119 | { |
| 120 | if (t.isEmpty() && t.file.isNull()) | 120 | if (t.isEmpty() && t.file.isNull()) |
| 121 | return; | 121 | return; |
| 122 | + | ||
| 122 | stream << t; | 123 | stream << t; |
| 123 | } | 124 | } |
| 124 | }; | 125 | }; |
| @@ -856,8 +857,18 @@ class FDDBGallery : public Gallery | @@ -856,8 +857,18 @@ class FDDBGallery : public Gallery | ||
| 856 | for (int i=0; i<numDetects; i++) { | 857 | for (int i=0; i<numDetects; i++) { |
| 857 | const QStringList detect = lines.takeFirst().split(' '); | 858 | const QStringList detect = lines.takeFirst().split(' '); |
| 858 | Template t(fileName); | 859 | Template t(fileName); |
| 859 | - t.file.set("Face", QRectF(detect[0].toFloat(), detect[1].toFloat(), detect[2].toFloat(), detect[3].toFloat())); | ||
| 860 | - t.file.set("Confidence", detect[4].toFloat()); | 860 | + if (detect.size() == 5) { //rectangle |
| 861 | + t.file.set("Face", QRectF(detect[0].toFloat(), detect[1].toFloat(), detect[2].toFloat(), detect[3].toFloat())); | ||
| 862 | + t.file.set("Confidence", detect[4].toFloat()); | ||
| 863 | + } else if (detect.size() == 6) { //ellipse | ||
| 864 | + float x = detect[3].toFloat(), | ||
| 865 | + y = detect[4].toFloat(), | ||
| 866 | + radius = detect[1].toFloat(); | ||
| 867 | + t.file.set("Face", QRectF(x - radius,y - radius,radius * 2.0, radius * 2.0)); | ||
| 868 | + t.file.set("Confidence", detect[5].toFloat()); | ||
| 869 | + } else { | ||
| 870 | + qFatal("Unknown FDDB annotation format."); | ||
| 871 | + } | ||
| 861 | templates.append(t); | 872 | templates.append(t); |
| 862 | } | 873 | } |
| 863 | } | 874 | } |
openbr/plugins/keypoint.cpp
| @@ -97,6 +97,7 @@ class KeyPointDescriptorTransform : public UntrainableTransform | @@ -97,6 +97,7 @@ class KeyPointDescriptorTransform : public UntrainableTransform | ||
| 97 | else | 97 | else |
| 98 | foreach (const QPointF &landmark, src.file.points()) | 98 | foreach (const QPointF &landmark, src.file.points()) |
| 99 | keyPoints.push_back(KeyPoint(landmark.x(), landmark.y(), size)); | 99 | keyPoints.push_back(KeyPoint(landmark.x(), landmark.y(), size)); |
| 100 | + if (keyPoints.empty()) return; | ||
| 100 | descriptorExtractor->compute(src, keyPoints, dst); | 101 | descriptorExtractor->compute(src, keyPoints, dst); |
| 101 | } | 102 | } |
| 102 | }; | 103 | }; |
openbr/plugins/landmarks.cpp
| @@ -269,22 +269,14 @@ class DelaunayTransform : public UntrainableTransform | @@ -269,22 +269,14 @@ class DelaunayTransform : public UntrainableTransform | ||
| 269 | 269 | ||
| 270 | Mat output(src.m().rows,src.m().cols,src.m().type()); | 270 | Mat output(src.m().rows,src.m().cols,src.m().type()); |
| 271 | 271 | ||
| 272 | - // Optimization needed | ||
| 273 | if (i > 0) { | 272 | if (i > 0) { |
| 274 | Mat overlap; | 273 | Mat overlap; |
| 275 | bitwise_and(dst.m(),mask,overlap); | 274 | bitwise_and(dst.m(),mask,overlap); |
| 276 | - for (int j = 0; j < overlap.rows; j++) { | ||
| 277 | - for (int k = 0; k < overlap.cols; k++) { | ||
| 278 | - if (overlap.at<uchar>(j,k) != 0) { | ||
| 279 | - mask.at<uchar>(j,k) = 0; | ||
| 280 | - } | ||
| 281 | - } | ||
| 282 | - } | 275 | + mask.setTo(0, overlap!=0); |
| 283 | } | 276 | } |
| 284 | 277 | ||
| 285 | bitwise_and(buffer,mask,output); | 278 | bitwise_and(buffer,mask,output); |
| 286 | 279 | ||
| 287 | - | ||
| 288 | dst.m() += output; | 280 | dst.m() += output; |
| 289 | } | 281 | } |
| 290 | 282 | ||
| @@ -297,53 +289,6 @@ class DelaunayTransform : public UntrainableTransform | @@ -297,53 +289,6 @@ class DelaunayTransform : public UntrainableTransform | ||
| 297 | 289 | ||
| 298 | BR_REGISTER(Transform, DelaunayTransform) | 290 | BR_REGISTER(Transform, DelaunayTransform) |
| 299 | 291 | ||
| 300 | -/*! | ||
| 301 | - * \ingroup transforms | ||
| 302 | - * \brief Loads a set of fiduciary points from a .dat file | ||
| 303 | - * \author Scott Klum \cite sklum | ||
| 304 | - */ | ||
| 305 | -class LoadLandmarksTransform : public UntrainableTransform | ||
| 306 | -{ | ||
| 307 | - Q_OBJECT | ||
| 308 | - | ||
| 309 | - Q_PROPERTY(QString filePath READ get_filePath WRITE set_filePath RESET reset_filePath STORED false) | ||
| 310 | - BR_PROPERTY(QString, filePath, QString()) | ||
| 311 | - | ||
| 312 | - void project(const Template &src, Template &dst) const | ||
| 313 | - { | ||
| 314 | - dst = src; | ||
| 315 | - | ||
| 316 | - // Assume the fiduciary file has the same basename as src | ||
| 317 | - QString path = filePath + "/" + src.file.baseName() + ".dat"; | ||
| 318 | - | ||
| 319 | - QFile f(path); | ||
| 320 | - if (!f.open(QIODevice::ReadOnly)) qFatal("Unable to open %s for reading.", qPrintable(path)); | ||
| 321 | - | ||
| 322 | - QList<QPointF> landmarks; | ||
| 323 | - while(!f.atEnd()) { | ||
| 324 | - QByteArray line = f.readLine(); | ||
| 325 | - QString pointSet(line); | ||
| 326 | - pointSet = pointSet.simplified(); | ||
| 327 | - if (!pointSet.isEmpty()) { | ||
| 328 | - QStringList points = pointSet.split(" "); | ||
| 329 | - landmarks.append(QPointF(points[0].toFloat(),points[1].toFloat())); | ||
| 330 | - } | ||
| 331 | - } | ||
| 332 | - | ||
| 333 | - if (landmarks.size() < 35) qFatal("Unrecognized landmark set format."); | ||
| 334 | - | ||
| 335 | - dst.file.set("rightEye", landmarks[16]); | ||
| 336 | - dst.file.set("leftEye", landmarks[18]); | ||
| 337 | - | ||
| 338 | - landmarks.removeAt(18); | ||
| 339 | - landmarks.removeAt(16); | ||
| 340 | - | ||
| 341 | - dst.file.appendPoints(landmarks); | ||
| 342 | - } | ||
| 343 | -}; | ||
| 344 | - | ||
| 345 | -BR_REGISTER(Transform, LoadLandmarksTransform) | ||
| 346 | - | ||
| 347 | } // namespace br | 292 | } // namespace br |
| 348 | 293 | ||
| 349 | #include "landmarks.moc" | 294 | #include "landmarks.moc" |
openbr/plugins/misc.cpp
| @@ -382,53 +382,6 @@ BR_REGISTER(Transform, RegexPropertyTransform) | @@ -382,53 +382,6 @@ BR_REGISTER(Transform, RegexPropertyTransform) | ||
| 382 | 382 | ||
| 383 | /*! | 383 | /*! |
| 384 | * \ingroup transforms | 384 | * \ingroup transforms |
| 385 | - * \brief Remove templates with the specified file extension or metadata value. | ||
| 386 | - * \author Josh Klontz \cite jklontz | ||
| 387 | - */ | ||
| 388 | -class RemoveTemplatesTransform : public UntrainableMetaTransform | ||
| 389 | -{ | ||
| 390 | - Q_OBJECT | ||
| 391 | - Q_PROPERTY(QString regexp READ get_regexp WRITE set_regexp RESET reset_regexp STORED false) | ||
| 392 | - Q_PROPERTY(QString key READ get_key WRITE set_key RESET reset_key STORED false) | ||
| 393 | - BR_PROPERTY(QString, regexp, "") | ||
| 394 | - BR_PROPERTY(QString, key, "") | ||
| 395 | - | ||
| 396 | - void project(const Template &src, Template &dst) const | ||
| 397 | - { | ||
| 398 | - const QRegularExpression re(regexp); | ||
| 399 | - const QRegularExpressionMatch match = re.match(key.isEmpty() ? src.file.suffix() : src.file.get<QString>(key)); | ||
| 400 | - if (match.hasMatch()) dst = Template(); | ||
| 401 | - else dst = src; | ||
| 402 | - } | ||
| 403 | -}; | ||
| 404 | - | ||
| 405 | -BR_REGISTER(Transform, RemoveTemplatesTransform) | ||
| 406 | - | ||
| 407 | -/*! | ||
| 408 | - * \ingroup transforms | ||
| 409 | - * \brief Remove template metadata with the specified key(s). | ||
| 410 | - * \author Josh Klontz \cite jklontz | ||
| 411 | - */ | ||
| 412 | -class RemoveMetadataTransform : public UntrainableMetaTransform | ||
| 413 | -{ | ||
| 414 | - Q_OBJECT | ||
| 415 | - Q_PROPERTY(QString regexp READ get_regexp WRITE set_regexp RESET reset_regexp STORED false) | ||
| 416 | - BR_PROPERTY(QString, regexp, "") | ||
| 417 | - | ||
| 418 | - void project(const Template &src, Template &dst) const | ||
| 419 | - { | ||
| 420 | - dst = src; | ||
| 421 | - const QRegularExpression re(regexp); | ||
| 422 | - foreach (const QString &key, dst.file.localKeys()) | ||
| 423 | - if (re.match(key).hasMatch()) | ||
| 424 | - dst.file.remove(key); | ||
| 425 | - } | ||
| 426 | -}; | ||
| 427 | - | ||
| 428 | -BR_REGISTER(Transform, RemoveMetadataTransform) | ||
| 429 | - | ||
| 430 | -/*! | ||
| 431 | - * \ingroup transforms | ||
| 432 | * \brief Store the last matrix of the input template as a metadata key with input property name. | 385 | * \brief Store the last matrix of the input template as a metadata key with input property name. |
| 433 | * \author Charles Otto \cite caotto | 386 | * \author Charles Otto \cite caotto |
| 434 | */ | 387 | */ |
openbr/plugins/output.cpp
| @@ -249,7 +249,8 @@ BR_REGISTER(Output, mtxOutput) | @@ -249,7 +249,8 @@ BR_REGISTER(Output, mtxOutput) | ||
| 249 | /*! | 249 | /*! |
| 250 | * \ingroup outputs | 250 | * \ingroup outputs |
| 251 | * \brief Rank retrieval output. | 251 | * \brief Rank retrieval output. |
| 252 | - * \author Josh Klontz \cite jklontz Scott Klum \cite sklum | 252 | + * \author Josh Klontz \cite jklontz |
| 253 | + * \author Scott Klum \cite sklum | ||
| 253 | */ | 254 | */ |
| 254 | class rrOutput : public MatrixOutput | 255 | class rrOutput : public MatrixOutput |
| 255 | { | 256 | { |
| @@ -267,15 +268,17 @@ class rrOutput : public MatrixOutput | @@ -267,15 +268,17 @@ class rrOutput : public MatrixOutput | ||
| 267 | 268 | ||
| 268 | for (int i=0; i<queryFiles.size(); i++) { | 269 | for (int i=0; i<queryFiles.size(); i++) { |
| 269 | QStringList files; | 270 | QStringList files; |
| 270 | - if (!byLine) files.append(queryFiles[i]); | 271 | + files.append(queryFiles[i]); |
| 271 | 272 | ||
| 272 | typedef QPair<float,int> Pair; | 273 | typedef QPair<float,int> Pair; |
| 273 | foreach (const Pair &pair, Common::Sort(OpenCVUtils::matrixToVector<float>(data.row(i)), true, limit)) { | 274 | foreach (const Pair &pair, Common::Sort(OpenCVUtils::matrixToVector<float>(data.row(i)), true, limit)) { |
| 274 | - if (pair.first < threshold) break; | ||
| 275 | - File target = targetFiles[pair.second]; | ||
| 276 | - target.set("Score", QString::number(pair.first)); | ||
| 277 | - if (simple) files.append(target.baseName() + " " + QString::number(pair.first)); | ||
| 278 | - else files.append(target.flat()); | 275 | + if (Globals->crossValidate > 0 ? (targetFiles[pair.second].get<int>("Partition",-1) == -1 || targetFiles[pair.second].get<int>("Partition",-1) == queryFiles[i].get<int>("Partition",-1)) : true) { |
| 276 | + if (pair.first < threshold) break; | ||
| 277 | + File target = targetFiles[pair.second]; | ||
| 278 | + target.set("Score", QString::number(pair.first)); | ||
| 279 | + if (simple) files.append(target.baseName() + " " + QString::number(pair.first)); | ||
| 280 | + else files.append(target.flat()); | ||
| 281 | + } | ||
| 279 | } | 282 | } |
| 280 | lines.append(files.join(byLine ? "\n" : ",")); | 283 | lines.append(files.join(byLine ? "\n" : ",")); |
| 281 | } | 284 | } |
| @@ -428,7 +431,7 @@ class rankOutput : public MatrixOutput | @@ -428,7 +431,7 @@ class rankOutput : public MatrixOutput | ||
| 428 | typedef QPair<float,int> Pair; | 431 | typedef QPair<float,int> Pair; |
| 429 | int rank = 1; | 432 | int rank = 1; |
| 430 | foreach (const Pair &pair, Common::Sort(OpenCVUtils::matrixToVector<float>(data.row(i)), true)) { | 433 | foreach (const Pair &pair, Common::Sort(OpenCVUtils::matrixToVector<float>(data.row(i)), true)) { |
| 431 | - if (Globals->crossValidate > 0 ? (targetFiles[pair.second].get<int>("Partition",-1) == queryFiles[i].get<int>("Partition",-1)) : true) { | 434 | + if (Globals->crossValidate > 0 ? (targetFiles[pair.second].get<int>("Partition",-1) == -1 || targetFiles[pair.second].get<int>("Partition",-1) == queryFiles[i].get<int>("Partition",-1)) : true) { |
| 432 | if (QString(targetFiles[pair.second]) != QString(queryFiles[i])) { | 435 | if (QString(targetFiles[pair.second]) != QString(queryFiles[i])) { |
| 433 | if (targetFiles[pair.second].get<QString>("Label") == queryFiles[i].get<QString>("Label")) { | 436 | if (targetFiles[pair.second].get<QString>("Label") == queryFiles[i].get<QString>("Label")) { |
| 434 | ranks.append(rank); | 437 | ranks.append(rank); |
openbr/plugins/pp5.cpp
| @@ -154,10 +154,10 @@ struct PP5Context | @@ -154,10 +154,10 @@ struct PP5Context | ||
| 154 | 154 | ||
| 155 | ppr_face_attributes_type face_attributes; | 155 | ppr_face_attributes_type face_attributes; |
| 156 | ppr_get_face_attributes(face, &face_attributes); | 156 | ppr_get_face_attributes(face, &face_attributes); |
| 157 | - metadata.insert("Face", QRectF(face_attributes.position.x - face_attributes.dimensions.width/2, | ||
| 158 | - face_attributes.position.y - face_attributes.dimensions.height/2, | ||
| 159 | - face_attributes.dimensions.width, | ||
| 160 | - face_attributes.dimensions.height)); | 157 | + metadata.insert("FrontalFace", QRectF(face_attributes.position.x - face_attributes.dimensions.width/2, |
| 158 | + face_attributes.position.y - face_attributes.dimensions.height/2, | ||
| 159 | + face_attributes.dimensions.width, | ||
| 160 | + face_attributes.dimensions.height)); | ||
| 161 | metadata.insert("PP5_Face_Confidence", face_attributes.confidence); | 161 | metadata.insert("PP5_Face_Confidence", face_attributes.confidence); |
| 162 | metadata.insert("PP5_Face_Roll", face_attributes.rotation.roll); | 162 | metadata.insert("PP5_Face_Roll", face_attributes.rotation.roll); |
| 163 | metadata.insert("PP5_Face_Pitch", face_attributes.rotation.pitch); | 163 | metadata.insert("PP5_Face_Pitch", face_attributes.rotation.pitch); |
| @@ -240,7 +240,8 @@ class PP5EnrollTransform : public UntrainableMetaTransform | @@ -240,7 +240,8 @@ class PP5EnrollTransform : public UntrainableMetaTransform | ||
| 240 | 240 | ||
| 241 | PP5Context *context = contexts.acquire(); | 241 | PP5Context *context = contexts.acquire(); |
| 242 | 242 | ||
| 243 | - foreach(const Template & src, srcList) { | 243 | + foreach (const Template &src, srcList) { |
| 244 | + bool foundFace = false; | ||
| 244 | if (!src.isEmpty()) { | 245 | if (!src.isEmpty()) { |
| 245 | ppr_raw_image_type raw_image; | 246 | ppr_raw_image_type raw_image; |
| 246 | PP5Context::createRawImage(src, raw_image); | 247 | PP5Context::createRawImage(src, raw_image); |
| @@ -254,6 +255,7 @@ class PP5EnrollTransform : public UntrainableMetaTransform | @@ -254,6 +255,7 @@ class PP5EnrollTransform : public UntrainableMetaTransform | ||
| 254 | int extractable; | 255 | int extractable; |
| 255 | TRY(ppr_is_template_extractable(context->context, face, &extractable)) | 256 | TRY(ppr_is_template_extractable(context->context, face, &extractable)) |
| 256 | if (!extractable && !detectOnly) continue; | 257 | if (!extractable && !detectOnly) continue; |
| 258 | + else foundFace = true; | ||
| 257 | 259 | ||
| 258 | cv::Mat m; | 260 | cv::Mat m; |
| 259 | if (detectOnly) { | 261 | if (detectOnly) { |
| @@ -272,20 +274,18 @@ class PP5EnrollTransform : public UntrainableMetaTransform | @@ -272,20 +274,18 @@ class PP5EnrollTransform : public UntrainableMetaTransform | ||
| 272 | // Found a face, nothing else to do (if we aren't trying to find multiple faces). | 274 | // Found a face, nothing else to do (if we aren't trying to find multiple faces). |
| 273 | if (!Globals->enrollAll) | 275 | if (!Globals->enrollAll) |
| 274 | break; | 276 | break; |
| 275 | - } | 277 | + } |
| 276 | 278 | ||
| 277 | ppr_free_face_list(face_list); | 279 | ppr_free_face_list(face_list); |
| 278 | ppr_free_image(image); | 280 | ppr_free_image(image); |
| 279 | ppr_raw_image_free(raw_image); | 281 | ppr_raw_image_free(raw_image); |
| 280 | } | 282 | } |
| 281 | - } | ||
| 282 | 283 | ||
| 283 | - // No faces were detected, output something with FTE set. | ||
| 284 | - if (dstList.empty()) { | ||
| 285 | - dstList.append(srcList.first()); | ||
| 286 | - dstList.first().file.set("FTE",true); | ||
| 287 | - if (!detectOnly) | ||
| 288 | - dstList.first().m() = cv::Mat(); | 284 | + // No faces were detected when we were expecting one, output something with FTE set. |
| 285 | + if (!foundFace && !Globals->enrollAll) { | ||
| 286 | + dstList.append(Template(src.file, detectOnly ? src.m() : cv::Mat())); | ||
| 287 | + dstList.last().file.set("FTE", true); | ||
| 288 | + } | ||
| 289 | } | 289 | } |
| 290 | 290 | ||
| 291 | contexts.release(context); | 291 | contexts.release(context); |
openbr/plugins/slidingwindow.cpp
0 → 100644
| 1 | +#include "openbr_internal.h" | ||
| 2 | +#include "openbr/core/opencvutils.h" | ||
| 3 | +#include "openbr/core/common.h" | ||
| 4 | + | ||
| 5 | +using namespace cv; | ||
| 6 | + | ||
| 7 | +namespace br | ||
| 8 | +{ | ||
| 9 | + | ||
| 10 | +/*! | ||
| 11 | + * \ingroup transforms | ||
| 12 | + * \brief Applies a transform to a sliding window. | ||
| 13 | + * Discards negative detections. | ||
| 14 | + * \author Austin Blanton \cite imaus10 | ||
| 15 | + */ | ||
| 16 | +class SlidingWindowTransform : public Transform | ||
| 17 | +{ | ||
| 18 | + Q_OBJECT | ||
| 19 | + Q_PROPERTY(br::Transform *transform READ get_transform WRITE set_transform RESET reset_transform STORED false) | ||
| 20 | + Q_PROPERTY(int minSize READ get_minSize WRITE set_minSize RESET reset_minSize STORED false) | ||
| 21 | + Q_PROPERTY(double scaleFactor READ get_scaleFactor WRITE set_scaleFactor RESET reset_scaleFactor STORED false) | ||
| 22 | + Q_PROPERTY(double stepSize READ get_stepSize WRITE set_stepSize RESET reset_stepSize STORED false) | ||
| 23 | + Q_PROPERTY(bool takeLargestScale READ get_takeLargestScale WRITE set_takeLargestScale RESET reset_takeLargestScale STORED false) | ||
| 24 | + Q_PROPERTY(bool negSamples READ get_negSamples WRITE set_negSamples RESET reset_negSamples STORED false) | ||
| 25 | + Q_PROPERTY(int negToPosRatio READ get_negToPosRatio WRITE set_negToPosRatio RESET reset_negToPosRatio STORED false) | ||
| 26 | + Q_PROPERTY(double maxOverlap READ get_maxOverlap WRITE set_maxOverlap RESET reset_maxOverlap STORED false) | ||
| 27 | + BR_PROPERTY(br::Transform *, transform, NULL) | ||
| 28 | + BR_PROPERTY(int, minSize, 8) | ||
| 29 | + BR_PROPERTY(double, scaleFactor, 0.75) | ||
| 30 | + BR_PROPERTY(double, stepSize, 1) | ||
| 31 | + BR_PROPERTY(bool, takeLargestScale, true) | ||
| 32 | + BR_PROPERTY(bool, negSamples, true) | ||
| 33 | + BR_PROPERTY(int, negToPosRatio, 1) | ||
| 34 | + BR_PROPERTY(double, maxOverlap, 0) | ||
| 35 | + | ||
| 36 | +public: | ||
| 37 | + SlidingWindowTransform() : Transform(false, true) {} | ||
| 38 | + | ||
| 39 | +private: | ||
| 40 | + void train(const TemplateList &data) | ||
| 41 | + { | ||
| 42 | + if (transform->trainable) { | ||
| 43 | + TemplateList full; | ||
| 44 | + foreach (const Template &tmpl, data) { | ||
| 45 | + foreach (const Rect &rect, OpenCVUtils::toRects(tmpl.file.rects())) { | ||
| 46 | + Template pos(tmpl.file, Mat(tmpl, rect)); | ||
| 47 | + full += pos; | ||
| 48 | + | ||
| 49 | + // add random negative samples | ||
| 50 | + if (negSamples) { | ||
| 51 | + Mat m = tmpl.m(); | ||
| 52 | + int sample = 0; | ||
| 53 | + while (sample < negToPosRatio) { | ||
| 54 | + int x = Common::RandSample(1, m.cols)[0]; | ||
| 55 | + int y = Common::RandSample(1, m.rows)[0]; | ||
| 56 | + int maxWidth = m.cols - x, maxHeight = m.rows - y; | ||
| 57 | + int maxSize = std::min(maxWidth, maxHeight); | ||
| 58 | + int size = (maxSize <= minSize ? maxSize : Common::RandSample(1, maxSize, minSize)[0]); | ||
| 59 | + Rect negRect(x, y, size, size); | ||
| 60 | + Rect intersect = negRect & rect; | ||
| 61 | + if (intersect.area() > maxOverlap*rect.area()) | ||
| 62 | + continue; | ||
| 63 | + Template neg(tmpl.file, Mat(tmpl, negRect)); | ||
| 64 | + neg.file.set("Label", QString("neg")); | ||
| 65 | + full += neg; | ||
| 66 | + sample++; | ||
| 67 | + } | ||
| 68 | + } | ||
| 69 | + } | ||
| 70 | + } | ||
| 71 | + transform->train(full); | ||
| 72 | + } | ||
| 73 | + } | ||
| 74 | + | ||
| 75 | + void project(const Template &src, Template &dst) const | ||
| 76 | + { | ||
| 77 | + dst = src; | ||
| 78 | + // no need to slide a window over ground truth data | ||
| 79 | + if (src.file.getBool("Train", false)) return; | ||
| 80 | + | ||
| 81 | + dst.file.clearRects(); | ||
| 82 | + int rows = src.m().rows, cols = src.m().cols; | ||
| 83 | + for (double size=std::min(rows, cols); size>=minSize; size*=scaleFactor) { | ||
| 84 | + for (double y=0; y+size<rows; y+=(size*stepSize)) { | ||
| 85 | + for (double x=0; x+size<cols; x+=(size*stepSize)) { | ||
| 86 | + Rect window(x, y, size, size); | ||
| 87 | + Template windowMat(src.file, Mat(src.m(), window)); | ||
| 88 | + Template detect; | ||
| 89 | + transform->project(windowMat, detect); | ||
| 90 | + // the result will be in the Label | ||
| 91 | + if (detect.file.get<QString>(QString("Label")) == "pos") { | ||
| 92 | + dst.file.appendRect(OpenCVUtils::fromRect(window)); | ||
| 93 | + if (takeLargestScale) return; | ||
| 94 | + } | ||
| 95 | + } | ||
| 96 | + } | ||
| 97 | + } | ||
| 98 | + } | ||
| 99 | +}; | ||
| 100 | + | ||
| 101 | +BR_REGISTER(Transform, SlidingWindowTransform) | ||
| 102 | + | ||
| 103 | +} // namespace br | ||
| 104 | + | ||
| 105 | +#include "slidingwindow.moc" |
openbr/plugins/stasm4.cpp
| @@ -56,6 +56,10 @@ class StasmTransform : public UntrainableTransform | @@ -56,6 +56,10 @@ class StasmTransform : public UntrainableTransform | ||
| 56 | 56 | ||
| 57 | Q_PROPERTY(bool stasm3Format READ get_stasm3Format WRITE set_stasm3Format RESET reset_stasm3Format STORED false) | 57 | Q_PROPERTY(bool stasm3Format READ get_stasm3Format WRITE set_stasm3Format RESET reset_stasm3Format STORED false) |
| 58 | BR_PROPERTY(bool, stasm3Format, false) | 58 | BR_PROPERTY(bool, stasm3Format, false) |
| 59 | + Q_PROPERTY(bool clearLandmarks READ get_clearLandmarks WRITE set_clearLandmarks RESET reset_clearLandmarks STORED false) | ||
| 60 | + BR_PROPERTY(bool, clearLandmarks, false) | ||
| 61 | + Q_PROPERTY(QStringList pinEyes READ get_pinEyes WRITE set_pinEyes RESET reset_pinEyes STORED false) | ||
| 62 | + BR_PROPERTY(QStringList, pinEyes, QStringList()) | ||
| 59 | 63 | ||
| 60 | Resource<StasmCascadeClassifier> stasmCascadeResource; | 64 | Resource<StasmCascadeClassifier> stasmCascadeResource; |
| 61 | 65 | ||
| @@ -69,12 +73,49 @@ class StasmTransform : public UntrainableTransform | @@ -69,12 +73,49 @@ class StasmTransform : public UntrainableTransform | ||
| 69 | { | 73 | { |
| 70 | if (src.m().channels() != 1) qFatal("Stasm expects single channel matrices."); | 74 | if (src.m().channels() != 1) qFatal("Stasm expects single channel matrices."); |
| 71 | 75 | ||
| 76 | + dst = src; | ||
| 77 | + | ||
| 72 | StasmCascadeClassifier *stasmCascade = stasmCascadeResource.acquire(); | 78 | StasmCascadeClassifier *stasmCascade = stasmCascadeResource.acquire(); |
| 73 | 79 | ||
| 74 | int foundface; | 80 | int foundface; |
| 75 | int nLandmarks = stasm_NLANDMARKS; | 81 | int nLandmarks = stasm_NLANDMARKS; |
| 76 | float landmarks[2 * stasm_NLANDMARKS]; | 82 | float landmarks[2 * stasm_NLANDMARKS]; |
| 77 | - stasm_search_single(&foundface, landmarks, reinterpret_cast<const char*>(src.m().data), src.m().cols, src.m().rows, *stasmCascade, NULL, NULL); | 83 | + |
| 84 | + if (!pinEyes.isEmpty()) { | ||
| 85 | + // Two use cases are accounted for: | ||
| 86 | + // 1. Pin eyes without normalization: in this case the string list should contain the KEYS for right then left eyes, respectively. | ||
| 87 | + // 2. Pin eyes with normalization: in this case the string list should contain the COORDINATES of the right then left eyes, respectively. | ||
| 88 | + // Note that for case 2, if Affine_0 and Affine_1 are not present (indicating no normalization has taken place), we default to stasm_search_single. | ||
| 89 | + | ||
| 90 | + bool ok = false; | ||
| 91 | + QPointF rightEye; | ||
| 92 | + QPointF leftEye; | ||
| 93 | + | ||
| 94 | + if (src.file.contains("Affine_0") && src.file.contains("Affine_1")) { | ||
| 95 | + rightEye = QtUtils::toPoint(pinEyes.at(0),&ok); | ||
| 96 | + leftEye = QtUtils::toPoint(pinEyes.at(1),&ok); | ||
| 97 | + } | ||
| 98 | + | ||
| 99 | + if (!ok) { | ||
| 100 | + rightEye = QtUtils::toPoint(src.file.get<QString>(pinEyes.at(0), QString()),&ok); | ||
| 101 | + leftEye = QtUtils::toPoint(src.file.get<QString>(pinEyes.at(1), QString()),&ok); | ||
| 102 | + } | ||
| 103 | + | ||
| 104 | + float eyes[2 * stasm_NLANDMARKS]; | ||
| 105 | + | ||
| 106 | + if (ok) { | ||
| 107 | + for (int i = 0; i < nLandmarks; i++) { | ||
| 108 | + if (i == 38) /*Stasm Right Eye*/ { eyes[2*i] = rightEye.x(); eyes[2*i+1] = rightEye.y(); } | ||
| 109 | + else if (i == 39) /*Stasm Left Eye*/ { eyes[2*i] = leftEye.x(); eyes[2*i+1] = leftEye.y(); } | ||
| 110 | + else { eyes[2*i] = 0; eyes[2*i+1] = 0; } | ||
| 111 | + } | ||
| 112 | + } else qFatal("Unable to interpret pinned eyes."); | ||
| 113 | + | ||
| 114 | + stasm_search_pinned(landmarks, eyes, reinterpret_cast<const char*>(src.m().data), src.m().cols, src.m().rows, NULL); | ||
| 115 | + | ||
| 116 | + // The ASM in Stasm is guaranteed to converge in this case | ||
| 117 | + foundface = 1; | ||
| 118 | + } stasm_search_single(&foundface, landmarks, reinterpret_cast<const char*>(src.m().data), src.m().cols, src.m().rows, *stasmCascade, NULL, NULL); | ||
| 78 | 119 | ||
| 79 | if (stasm3Format) { | 120 | if (stasm3Format) { |
| 80 | nLandmarks = 76; | 121 | nLandmarks = 76; |
| @@ -83,13 +124,22 @@ class StasmTransform : public UntrainableTransform | @@ -83,13 +124,22 @@ class StasmTransform : public UntrainableTransform | ||
| 83 | 124 | ||
| 84 | stasmCascadeResource.release(stasmCascade); | 125 | stasmCascadeResource.release(stasmCascade); |
| 85 | 126 | ||
| 86 | - if (!foundface) qWarning("No face found in %s", qPrintable(src.file.fileName())); | ||
| 87 | - else { | ||
| 88 | - for (int i = 0; i < nLandmarks; i++) | ||
| 89 | - dst.file.appendPoint(QPointF(landmarks[2 * i], landmarks[2 * i + 1])); | 127 | + // For convenience, if these are the only points/rects we want to deal with as the algorithm progresses |
| 128 | + if (clearLandmarks) { | ||
| 129 | + dst.file.clearPoints(); | ||
| 130 | + dst.file.clearRects(); | ||
| 90 | } | 131 | } |
| 91 | 132 | ||
| 92 | - dst.m() = src.m(); | 133 | + if (!foundface) { |
| 134 | + qWarning("No face found in %s", qPrintable(src.file.fileName())); | ||
| 135 | + } else { | ||
| 136 | + for (int i = 0; i < nLandmarks; i++) { | ||
| 137 | + QPointF point(landmarks[2 * i], landmarks[2 * i + 1]); | ||
| 138 | + dst.file.appendPoint(point); | ||
| 139 | + if (i == 38) dst.file.set("StasmRightEye",point); | ||
| 140 | + else if (i == 39) dst.file.set("StasmLeftEye", point); | ||
| 141 | + } | ||
| 142 | + } | ||
| 93 | } | 143 | } |
| 94 | }; | 144 | }; |
| 95 | 145 |
openbr/plugins/template.cpp
| 1 | #include "openbr_internal.h" | 1 | #include "openbr_internal.h" |
| 2 | +#include <QRegularExpression> | ||
| 2 | 3 | ||
| 3 | namespace br | 4 | namespace br |
| 4 | { | 5 | { |
| @@ -25,6 +26,30 @@ class RetainTransform : public UntrainableTransform | @@ -25,6 +26,30 @@ class RetainTransform : public UntrainableTransform | ||
| 25 | 26 | ||
| 26 | BR_REGISTER(Transform, RetainTransform) | 27 | BR_REGISTER(Transform, RetainTransform) |
| 27 | 28 | ||
| 29 | +/*! | ||
| 30 | + * \ingroup transforms | ||
| 31 | + * \brief Remove templates with the specified file extension or metadata value. | ||
| 32 | + * \author Josh Klontz \cite jklontz | ||
| 33 | + */ | ||
| 34 | +class RemoveTemplatesTransform : public UntrainableMetaTransform | ||
| 35 | +{ | ||
| 36 | + Q_OBJECT | ||
| 37 | + Q_PROPERTY(QString regexp READ get_regexp WRITE set_regexp RESET reset_regexp STORED false) | ||
| 38 | + Q_PROPERTY(QString key READ get_key WRITE set_key RESET reset_key STORED false) | ||
| 39 | + BR_PROPERTY(QString, regexp, "") | ||
| 40 | + BR_PROPERTY(QString, key, "") | ||
| 41 | + | ||
| 42 | + void project(const Template &src, Template &dst) const | ||
| 43 | + { | ||
| 44 | + const QRegularExpression re(regexp); | ||
| 45 | + const QRegularExpressionMatch match = re.match(key.isEmpty() ? src.file.suffix() : src.file.get<QString>(key)); | ||
| 46 | + if (match.hasMatch()) dst = Template(); | ||
| 47 | + else dst = src; | ||
| 48 | + } | ||
| 49 | +}; | ||
| 50 | + | ||
| 51 | +BR_REGISTER(Transform, RemoveTemplatesTransform) | ||
| 52 | + | ||
| 28 | } // namespace br | 53 | } // namespace br |
| 29 | 54 | ||
| 30 | #include "template.moc" | 55 | #include "template.moc" |
openbr/plugins/validate.cpp
| @@ -75,6 +75,7 @@ class CrossValidateTransform : public MetaTransform | @@ -75,6 +75,7 @@ class CrossValidateTransform : public MetaTransform | ||
| 75 | if (subjectIndices.size() > 1 && subjectIndices.size() <= i) { | 75 | if (subjectIndices.size() > 1 && subjectIndices.size() <= i) { |
| 76 | removed.append(subjectIndices[i%subjectIndices.size()]); | 76 | removed.append(subjectIndices[i%subjectIndices.size()]); |
| 77 | } | 77 | } |
| 78 | + // For the time being, we don't support addition training data added to every fold in the case of leaveOneImageOut | ||
| 78 | else if (partitionsBuffer[j] == i) { | 79 | else if (partitionsBuffer[j] == i) { |
| 79 | removed.append(j); | 80 | removed.append(j); |
| 80 | } | 81 | } |
| @@ -87,7 +88,12 @@ class CrossValidateTransform : public MetaTransform | @@ -87,7 +88,12 @@ class CrossValidateTransform : public MetaTransform | ||
| 87 | } else { | 88 | } else { |
| 88 | j--; | 89 | j--; |
| 89 | } | 90 | } |
| 91 | + } else if (partitions[j] == -1) { | ||
| 92 | + // Keep data for training, but modify the partition so we project into the correct space | ||
| 93 | + partitionedData[j].file.set("Partition",i); | ||
| 94 | + j--; | ||
| 90 | } else if (partitions[j] == i) { | 95 | } else if (partitions[j] == i) { |
| 96 | + // Remove data, it's designated for testing | ||
| 91 | partitionedData.removeAt(j); | 97 | partitionedData.removeAt(j); |
| 92 | j--; | 98 | j--; |
| 93 | } else j--; | 99 | } else j--; |
| @@ -99,7 +105,8 @@ class CrossValidateTransform : public MetaTransform | @@ -99,7 +105,8 @@ class CrossValidateTransform : public MetaTransform | ||
| 99 | } | 105 | } |
| 100 | 106 | ||
| 101 | void project(const Template &src, Template &dst) const | 107 | void project(const Template &src, Template &dst) const |
| 102 | - { | 108 | + { |
| 109 | + qDebug() << src.file.get<int>("Partition", 0); | ||
| 103 | transforms[src.file.get<int>("Partition", 0)]->project(src, dst); | 110 | transforms[src.file.get<int>("Partition", 0)]->project(src, dst); |
| 104 | } | 111 | } |
| 105 | 112 |
scripts/downloadDatasets.sh
| @@ -35,6 +35,24 @@ if [ ! -d ../data/BioID/img ]; then | @@ -35,6 +35,24 @@ if [ ! -d ../data/BioID/img ]; then | ||
| 35 | rm *.eye description.txt BioID-FaceDatabase-V1.2.zip | 35 | rm *.eye description.txt BioID-FaceDatabase-V1.2.zip |
| 36 | fi | 36 | fi |
| 37 | 37 | ||
| 38 | +# INRIA person | ||
| 39 | +if [ ! -d ../data/INRIAPerson/img ]; then | ||
| 40 | + echo "Downloading INRIA person dataset..." | ||
| 41 | + if hash curl 2>/dev/null; then | ||
| 42 | + curl -OL http://pascal.inrialpes.fr/data/human/INRIAPerson.tar | ||
| 43 | + else | ||
| 44 | + wget http://pascal.inrialpes.fr/data/human/INRIAPerson.tar | ||
| 45 | + fi | ||
| 46 | + tar -xf INRIAPerson.tar | ||
| 47 | + mkdir ../data/INRIAPerson/img ../data/INRIAPerson/sigset | ||
| 48 | + ./writeINRIAPersonSigset.sh Train > ../data/INRIAPerson/sigset/train.xml | ||
| 49 | + ./writeINRIAPersonSigset.sh Test > ../data/INRIAPerson/sigset/test.xml | ||
| 50 | + ./writeINRIAPersonSigset.sh train_64x128_H96 > ../data/INRIAPerson/sigset/train_normalized.xml | ||
| 51 | + ./writeINRIAPersonSigset.sh test_64x128_H96 > ../data/INRIAPerson/sigset/test_normalized.xml | ||
| 52 | + mv INRIAPerson/* ../data/INRIAPerson/img | ||
| 53 | + rm -r INRIAPerson* | ||
| 54 | +fi | ||
| 55 | + | ||
| 38 | # KTH | 56 | # KTH |
| 39 | if [ ! -d ../data/KTH/vid ]; then | 57 | if [ ! -d ../data/KTH/vid ]; then |
| 40 | echo "Downloading KTH..." | 58 | echo "Downloading KTH..." |
scripts/writeINRIAPersonSigset.sh
0 → 100755
| 1 | +#!/bin/bash | ||
| 2 | + | ||
| 3 | +# prints out a <data:bbox> element from rectangle coordinates | ||
| 4 | +# (from the ViPER standard: http://viper-toolkit.sourceforge.net/docs/file/) | ||
| 5 | +printBBox() | ||
| 6 | +{ | ||
| 7 | + width=$(($3-$1)) | ||
| 8 | + height=$(($4-$2)) | ||
| 9 | + echo -e "\t\t\t<data:bbox height=\"$height\" width=\"$width\" x=\"$1\" y=\"$2\" />" | ||
| 10 | +} | ||
| 11 | +# export printBBox so xargs can call it using bash -c below | ||
| 12 | +export -f printBBox | ||
| 13 | +SEDREGEX='s/.*(\([0-9]*\), \([0-9]*\)) - (\([0-9]*\), \([0-9]*\))/printBBox \1 \2 \3 \4/' | ||
| 14 | + | ||
| 15 | +echo '<?xml version="1.0" encoding="UTF-8"?>' | ||
| 16 | +echo '<biometric-signature-set>' | ||
| 17 | + | ||
| 18 | +# print out the positive image sigs | ||
| 19 | +for fullpath in INRIAPerson/$1/pos/*; do | ||
| 20 | + # get just the filename, minus the path | ||
| 21 | + filename=$(basename "$fullpath") | ||
| 22 | + echo -e "\t<biometric-signature name=\"${filename%.*}\">" | ||
| 23 | + | ||
| 24 | + # if this folder has annotations, add bounding boxes | ||
| 25 | + echo -en "\t\t<presentation Label=\"pos\" file-name=\"$1/pos/$filename\"" | ||
| 26 | + if [ -d INRIAPerson/$1/annotations ]; then | ||
| 27 | + echo ">" | ||
| 28 | + annotation="INRIAPerson/$1/annotations/${filename%.*}.txt" | ||
| 29 | + grep 'Bounding box' $annotation | sed "$SEDREGEX" | xargs -n 5 bash -c 'printBBox $@' | ||
| 30 | + echo -e "\t\t</presentation>" | ||
| 31 | + # otherwise, just end the presentation | ||
| 32 | + else | ||
| 33 | + echo " />" | ||
| 34 | + fi | ||
| 35 | + | ||
| 36 | + echo -e '\t</biometric-signature>' | ||
| 37 | +done | ||
| 38 | + | ||
| 39 | +# print out the negative image sigs | ||
| 40 | +for fullpath in INRIAPerson/$1/neg/*; do | ||
| 41 | + filename=$(basename "$fullpath") | ||
| 42 | + echo -e "\t<biometric-signature name=\"${filename%.*}\">" | ||
| 43 | + echo -e "\t\t<presentation Label=\"neg\" file-name=\"$1/neg/$filename\" />" | ||
| 44 | + echo -e '\t</biometric-signature>' | ||
| 45 | +done | ||
| 46 | + | ||
| 47 | +echo '</biometric-signature-set>' |