Commit 1ed68d6d110c1d0befd8da1a4e4cf7b4bf129381

Authored by Scott Klum
2 parents 5e6b30e9 9d711fd6

Merge branch 'master' of https://github.com/biometrics/openbr

openbr/CMakeLists.txt
... ... @@ -34,7 +34,7 @@ set(JANUS_BUILD_PP5_WRAPPER ${BR_WITH_PP5} CACHE BOOL "Build Janus implementatio
34 34 set(JANUS_BUILD_DOCS ${BR_BUILD_DOCUMENTATION} CACHE BOOL "Build Janus HTML Doxygen documentation")
35 35 mark_as_advanced(JANUS_BUILD_PP5_WRAPPER)
36 36 mark_as_advanced(JANUS_BUILD_DOCS)
37   -set(JANUS_TEST_IMPLEMENTATION openbr)
  37 +set(JANUS_IMPLEMENTATION openbr)
38 38 add_subdirectory(janus)
39 39  
40 40 # Install
... ...
openbr/core/bee.cpp
... ... @@ -172,6 +172,8 @@ Mat BEE::readMat(const br::File &matrix, QString *targetSigset, QString *querySi
172 172 qint64 read = file.read((char*)m.data, bytesExpected);
173 173 if (read != bytesExpected)
174 174 qFatal("Invalid matrix size.");
  175 + if (!file.atEnd())
  176 + qFatal("Expected matrix end of file.");
175 177 file.close();
176 178  
177 179 Mat result;
... ...
1   -Subproject commit 0eaa00f19256fee2f24033ff68b526ea6320624d
  1 +Subproject commit 02fd545b2dbb8ea2ba10dbf67e429da598545d75
... ...
openbr/janus.cpp
... ... @@ -3,140 +3,138 @@
3 3 #endif
4 4  
5 5 #include "janus.h"
  6 +#include "janus_io.h"
6 7 #include "openbr_plugin.h"
7 8  
8   -// Use the provided default implementation of some functions
9   -#include "janus/src/janus.cpp"
10   -
11 9 using namespace br;
12 10  
13 11 static QSharedPointer<Transform> transform;
14 12 static QSharedPointer<Distance> distance;
15 13  
  14 +size_t janus_max_template_size()
  15 +{
  16 + return JANUS_MAX_TEMPLATE_SIZE_LIMIT;
  17 +}
  18 +
16 19 janus_error janus_initialize(const char *sdk_path, const char *model_file)
17 20 {
18 21 int argc = 1;
19 22 const char *argv[1] = { "janus" };
20 23 Context::initialize(argc, (char**)argv, sdk_path);
21 24 QString algorithm = model_file;
22   - if (algorithm.isEmpty()) algorithm = "Cvt(Gray)+Affine(88,88,0.25,0.35)+<FaceRecognitionExtraction>+<FaceRecognitionEmbedding>+<FaceRecognitionQuantization>:ByteL1";
23   - transform = Transform::fromAlgorithm(algorithm, false);
24   - distance = Distance::fromAlgorithm(algorithm);
  25 + if (algorithm.isEmpty()) {
  26 + transform = Transform::fromAlgorithm("Cvt(Gray)+Affine(88,88,0.25,0.35)+<FaceRecognitionExtraction>+<FaceRecognitionEmbedding>+<FaceRecognitionQuantization>", false);
  27 + distance = Distance::fromAlgorithm("FaceRecognition");
  28 + } else if (algorithm == "PP5") {
  29 + transform.reset(Transform::make("PP5Enroll", NULL));
  30 + distance.reset(Distance::make("PP5Compare", NULL));
  31 + } else {
  32 + transform = Transform::fromAlgorithm(algorithm, false);
  33 + distance = Distance::fromAlgorithm(algorithm);
  34 + }
25 35 return JANUS_SUCCESS;
26 36 }
27 37  
28 38 janus_error janus_finalize()
29 39 {
  40 + transform.reset();
  41 + distance.reset();
30 42 Context::finalize();
31 43 return JANUS_SUCCESS;
32 44 }
33 45  
34   -struct janus_incomplete_template_type
35   -{
36   - QList<cv::Mat> data;
37   -};
  46 +struct janus_template_type : public Template
  47 +{};
38 48  
39   -janus_error janus_initialize_template(janus_incomplete_template *incomplete_template)
  49 +janus_error janus_initialize_template(janus_template *template_)
40 50 {
41   - *incomplete_template = new janus_incomplete_template_type();
  51 + *template_ = new janus_template_type();
42 52 return JANUS_SUCCESS;
43 53 }
44 54  
45   -janus_error janus_add_image(const janus_image image, const janus_attribute_list attributes, janus_incomplete_template incomplete_template)
  55 +janus_error janus_augment(const janus_image image, const janus_attribute_list attributes, janus_template template_)
46 56 {
47 57 Template t;
48 58 t.append(cv::Mat(image.height,
49 59 image.width,
50   - image.color_space == JANUS_GRAY8 ? CV_8UC1 : CV_8UC1,
  60 + image.color_space == JANUS_GRAY8 ? CV_8UC1 : CV_8UC3,
51 61 image.data));
52 62 for (size_t i=0; i<attributes.size; i++)
53 63 t.file.set(janus_attribute_to_string(attributes.attributes[i]), attributes.values[i]);
54 64  
55   - if (!t.file.contains("JANUS_RIGHT_EYE_X") ||
56   - !t.file.contains("JANUS_RIGHT_EYE_Y") ||
57   - !t.file.contains("JANUS_LEFT_EYE_X") ||
58   - !t.file.contains("JANUS_LEFT_EYE_Y"))
  65 + if (!t.file.contains("RIGHT_EYE_X") ||
  66 + !t.file.contains("RIGHT_EYE_Y") ||
  67 + !t.file.contains("LEFT_EYE_X") ||
  68 + !t.file.contains("LEFT_EYE_Y"))
59 69 return JANUS_SUCCESS;
60 70  
61   - t.file.set("Affine_0", QPointF(t.file.get<float>("JANUS_RIGHT_EYE_X"), t.file.get<float>("JANUS_RIGHT_EYE_Y")));
62   - t.file.set("Affine_1", QPointF(t.file.get<float>("JANUS_LEFT_EYE_X"), t.file.get<float>("JANUS_LEFT_EYE_Y")));
  71 + t.file.set("Affine_0", QPointF(t.file.get<float>("RIGHT_EYE_X"), t.file.get<float>("RIGHT_EYE_Y")));
  72 + t.file.set("Affine_1", QPointF(t.file.get<float>("LEFT_EYE_X"), t.file.get<float>("LEFT_EYE_Y")));
63 73 Template u;
64 74 transform->project(t, u);
65   - incomplete_template->data.append(u);
  75 + template_->append(u);
66 76 return JANUS_SUCCESS;
67 77 }
68 78  
69   -janus_error janus_finalize_template(janus_incomplete_template incomplete_template, janus_template template_, size_t *bytes)
  79 +janus_error janus_finalize_template(janus_template template_, janus_flat_template flat_template, size_t *bytes)
70 80 {
71   - size_t templateBytes = 0;
72   - size_t numTemplates = 0;
73   - *bytes = sizeof(templateBytes) + sizeof(numTemplates);
74   - janus_template pos = template_ + *bytes;
75   -
76   - foreach (const cv::Mat &m, incomplete_template->data) {
  81 + foreach (const cv::Mat &m, *template_) {
77 82 assert(m.isContinuous());
78   - const size_t currentTemplateBytes = m.rows * m.cols * m.elemSize();
79   - if (templateBytes == 0)
80   - templateBytes = currentTemplateBytes;
81   - if (templateBytes != currentTemplateBytes)
82   - return JANUS_UNKNOWN_ERROR;
83   - if (*bytes + templateBytes > janus_max_template_size())
  83 + const size_t templateBytes = m.rows * m.cols * m.elemSize();
  84 + if (*bytes + sizeof(size_t) + templateBytes > janus_max_template_size())
84 85 break;
85   - memcpy(pos, m.data, templateBytes);
86   - *bytes += templateBytes;
87   - pos = pos + templateBytes;
88   - numTemplates++;
  86 + memcpy(flat_template, &templateBytes, sizeof(templateBytes));
  87 + flat_template += sizeof(templateBytes);
  88 + memcpy(flat_template, m.data, templateBytes);
  89 + flat_template += templateBytes;
  90 + *bytes += sizeof(size_t) + templateBytes;
89 91 }
90 92  
91   - *(reinterpret_cast<size_t*>(template_)+0) = templateBytes;
92   - *(reinterpret_cast<size_t*>(template_)+1) = numTemplates;
93   - delete incomplete_template;
  93 + delete template_;
94 94 return JANUS_SUCCESS;
95 95 }
96 96  
97   -janus_error janus_verify(const janus_template a, const size_t a_bytes, const janus_template b, const size_t b_bytes, double *similarity)
  97 +janus_error janus_verify(const janus_flat_template a, const size_t a_bytes, const janus_flat_template b, const size_t b_bytes, double *similarity)
98 98 {
99   - (void) a_bytes;
100   - (void) b_bytes;
101   -
102   - size_t a_template_bytes, a_templates, b_template_bytes, b_templates;
103   - a_template_bytes = *(reinterpret_cast<size_t*>(a)+0);
104   - a_templates = *(reinterpret_cast<size_t*>(a)+1);
105   - b_template_bytes = *(reinterpret_cast<size_t*>(b)+0);
106   - b_templates = *(reinterpret_cast<size_t*>(b)+1);
107   - if (a_template_bytes != b_template_bytes)
108   - return JANUS_UNKNOWN_ERROR;
  99 + *similarity = 0;
109 100  
110   - float dist = 0;
111   - for (size_t i=0; i<a_templates; i++)
112   - for (size_t j=0; j<b_templates; j++)
113   - dist += distance->compare(cv::Mat(1, a_template_bytes, CV_8UC1, a+2*sizeof(size_t)+i*a_template_bytes),
114   - cv::Mat(1, b_template_bytes, CV_8UC1, b+2*sizeof(size_t)+i*b_template_bytes));
115   - *similarity = a_templates * b_templates / dist;
116   - return JANUS_SUCCESS;
117   -}
  101 + int comparisons = 0;
  102 + janus_flat_template a_template = a;
  103 + while (a_template < a + a_bytes) {
  104 + const size_t a_template_bytes = *reinterpret_cast<size_t*>(a_template);
  105 + a_template += sizeof(a_template_bytes);
118 106  
119   -struct janus_incomplete_gallery_type
120   -{
121   - QList< QPair<janus_template, janus_template_id> > templates;
122   -};
  107 + janus_flat_template b_template = b;
  108 + while (b_template < b + b_bytes) {
  109 + const size_t b_template_bytes = *reinterpret_cast<size_t*>(b_template);
  110 + b_template += sizeof(b_template_bytes);
123 111  
124   -janus_error janus_initialize_gallery(janus_incomplete_gallery *incomplete_gallery)
125   -{
126   - *incomplete_gallery = new janus_incomplete_gallery_type();
127   - return JANUS_SUCCESS;
128   -}
  112 + *similarity += distance->compare(cv::Mat(1, a_template_bytes, CV_8UC1, a_template),
  113 + cv::Mat(1, b_template_bytes, CV_8UC1, b_template));
  114 + comparisons++;
129 115  
130   -janus_error janus_add_template(const janus_template template_, const size_t bytes, const janus_template_id template_id, janus_incomplete_gallery incomplete_gallery)
131   -{
132   - (void) bytes;
133   - incomplete_gallery->templates.append(QPair<janus_template, janus_template_id>(template_, template_id));
  116 + b_template += b_template_bytes;
  117 + }
  118 +
  119 + a_template += a_template_bytes;
  120 + }
  121 +
  122 + if (*similarity != *similarity) // True for NaN
  123 + return JANUS_UNKNOWN_ERROR;
  124 +
  125 + *similarity /= comparisons;
134 126 return JANUS_SUCCESS;
135 127 }
136 128  
137   -janus_error janus_finalize_gallery(janus_incomplete_gallery incomplete_gallery, const char *gallery_file)
  129 +janus_error janus_enroll(const janus_template template_, const janus_template_id template_id, janus_gallery gallery)
138 130 {
139   - (void) incomplete_gallery;
140   - (void) gallery_file;
  131 + template_->file.set("TEMPLATE_ID", template_id);
  132 + QFile file(gallery);
  133 + if (!file.open(QFile::WriteOnly | QFile::Append))
  134 + return JANUS_WRITE_ERROR;
  135 + QDataStream stream(&file);
  136 + stream << *template_;
  137 + file.close();
  138 + delete template_;
141 139 return JANUS_SUCCESS;
142 140 }
... ...
openbr/openbr.cpp
... ... @@ -383,7 +383,9 @@ bool br_img_is_empty(br_template tmpl)
383 383 const char* br_get_filename(br_template tmpl)
384 384 {
385 385 Template *t = reinterpret_cast<Template*>(tmpl);
386   - return t->file.name.toStdString().c_str();
  386 + QByteArray s = t->file.name.toLocal8Bit();
  387 + char *buffer = s.data();
  388 + return buffer;
387 389 }
388 390  
389 391 void br_set_filename(br_template tmpl, const char *filename)
... ...
openbr/openbr_plugin.h
... ... @@ -1374,7 +1374,7 @@ BR_EXPORT void Cat(const QStringList &amp;inputGalleries, const QString &amp;outputGalle
1374 1374  
1375 1375 /*!
1376 1376 * \brief Deduplicate a gallery.
1377   - * \param inputGalleries Gallery to deduplicate.
  1377 + * \param inputGallery Gallery to deduplicate.
1378 1378 * \param outputGallery Gallery to store the deduplicated result.
1379 1379 * \param threshold Match score threshold to determine duplicates.
1380 1380 */
... ...
openbr/plugins/algorithms.cpp
... ... @@ -52,6 +52,7 @@ class AlgorithmsInitializer : public Initializer
52 52 Globals->abbreviations.insert("AgeGenderDemo", "Stream(SaveMat(original)+Cvt(Gray)+Cascade(FrontalFace)+Expand+<FaceClassificationRegistration>+<FaceClassificationExtraction>+<AgeRegressor>/<GenderClassifier>+Discard+RestoreMat(original)+Draw(inPlace=true)+DrawPropertiesPoint([Age,Gender],Affine_0,inPlace=true)+SaveMat(original)+Discard+Contract+RestoreMat(original)+FPSCalc+Show(false,[AvgFPS,Age,Gender])+Discard)");
53 53 Globals->abbreviations.insert("ShowOpticalFlowField", "Stream(SaveMat(original)+AggregateFrames(2)+OpticalFlow(useMagnitude=false)+Grid(100,100)+DrawOpticalFlow+FPSLimit(30)+Show(false)+Discard)");
54 54 Globals->abbreviations.insert("ShowOpticalFlowMagnitude", "Stream(AggregateFrames(2)+OpticalFlow+Normalize(Range,false,0,255)+Cvt(Color)+Draw+FPSLimit(30)+Show(false)+Discard)");
  55 + Globals->abbreviations.insert("ShowMotionSegmentation", "Stream(DropFrames(5)+SaveMat(original)+AggregateFrames(2)+OpticalFlow+CvtUChar+Segmentation+DrawSegmentation+Draw+FPSLimit(30)+Show(false)+Discard)");
55 56  
56 57 Globals->abbreviations.insert("HOG", "Stream(DropFrames(5)+Cvt(Gray)+Grid(5,5)+ROIFromPts(32,24)+Expand+Resize(32,32)+Gradient+RectRegions+Bin(0,360,8)+Hist(8)+Cat)+Contract+CatRows+KMeans(500)+Hist(500)+SVM");
57 58 Globals->abbreviations.insert("HOF", "Stream(DropFrames(5)+Grid(5,5)+AggregateFrames(2)+OpticalFlow+ROIFromPts(32,24)+Expand+Resize(32,32)+Gradient+RectRegions+Bin(0,360,8)+Hist(8)+Cat)+Contract+CatRows+KMeans(500)+Hist(500)");
... ... @@ -78,7 +79,7 @@ class AlgorithmsInitializer : public Initializer
78 79 Globals->abbreviations.insert("ColoredLBP", "Open+Affine(128,128,0.37,0.45)+Cvt(Gray)+Blur(1.1)+Gamma(0.2)+DoG(1,2)+ContrastEq(0.1,10)+LBP(1,2)+ColoredU2");
79 80  
80 81 // Transforms
81   - Globals->abbreviations.insert("FaceDetection", "(Open+Cvt(Gray)+Cascade(FrontalFace))");
  82 + Globals->abbreviations.insert("FaceDetection", "Open+Cvt(Gray)+Cascade(FrontalFace)");
82 83 Globals->abbreviations.insert("DenseLBP", "(Blur(1.1)+Gamma(0.2)+DoG(1,2)+ContrastEq(0.1,10)+LBP(1,2)+RectRegions(8,8,6,6)+Hist(59))");
83 84 Globals->abbreviations.insert("DenseSIFT", "(Grid(10,10)+SIFTDescriptor(12)+ByRow)");
84 85 Globals->abbreviations.insert("FaceRecognitionRegistration", "(ASEFEyes+Affine(88,88,0.25,0.35)+DownsampleTraining(FTE(DFFS),instances=1))");
... ...
openbr/plugins/draw.cpp
... ... @@ -374,6 +374,56 @@ class DrawOpticalFlow : public UntrainableTransform
374 374 };
375 375 BR_REGISTER(Transform, DrawOpticalFlow)
376 376  
  377 +/*!
  378 + * \ingroup transforms
  379 + * \brief Fill in the segmentations or draw a line between intersecting segments.
  380 + * \author Austin Blanton \cite imaus10
  381 + */
  382 +class DrawSegmentation : public UntrainableTransform
  383 +{
  384 + Q_OBJECT
  385 + Q_PROPERTY(bool fillSegment READ get_fillSegment WRITE set_fillSegment RESET reset_fillSegment STORED false)
  386 + BR_PROPERTY(bool, fillSegment, true)
  387 + Q_PROPERTY(QString original READ get_original WRITE set_original RESET reset_original STORED false)
  388 + BR_PROPERTY(QString, original, "original")
  389 +
  390 + void project(const Template &src, Template &dst) const
  391 + {
  392 + if (!src.file.contains("SegmentsMask") || !src.file.contains("NumSegments")) qFatal("Must supply a Contours object in the metadata to drawContours.");
  393 + Mat segments = src.file.get<Mat>("SegmentsMask");
  394 + int numSegments = src.file.get<int>("NumSegments");
  395 +
  396 + dst.file = src.file;
  397 + Mat drawn;
  398 + if (fillSegment) {
  399 + drawn = Mat(segments.size(), CV_8UC3, Scalar::all(0));
  400 + } else {
  401 + if (!dst.file.contains(original)) qFatal("You must store the original image in the metadata with SaveMat.");
  402 + drawn = dst.file.get<Mat>(original);
  403 + dst.file.remove(original);
  404 + }
  405 +
  406 + for (int i=1; i<numSegments+1; i++) {
  407 + Mat mask = segments == i;
  408 + if (fillSegment) { // color the whole segment
  409 + // set to a random color - get ready for a craaaazy acid trip
  410 + int b = theRNG().uniform(0, 255);
  411 + int g = theRNG().uniform(0, 255);
  412 + int r = theRNG().uniform(0, 255);
  413 + drawn.setTo(Scalar(r,g,b), mask);
  414 + } else { // draw lines where there's a color change
  415 + vector<vector<Point> > contours;
  416 + Scalar color(0,255,0);
  417 + findContours(mask, contours, CV_RETR_LIST, CV_CHAIN_APPROX_NONE);
  418 + drawContours(drawn, contours, -1, color);
  419 + }
  420 + }
  421 +
  422 + dst.m() = drawn;
  423 + }
  424 +};
  425 +BR_REGISTER(Transform, DrawSegmentation)
  426 +
377 427 // TODO: re-implement EditTransform using Qt
378 428 #if 0
379 429 /*!
... ...
openbr/plugins/segmentation.cpp 0 โ†’ 100644
  1 +#include <opencv2/imgproc/imgproc.hpp>
  2 +#include "openbr_internal.h"
  3 +#include "openbr/core/opencvutils.h"
  4 +
  5 +using namespace cv;
  6 +
  7 +namespace br
  8 +{
  9 +
  10 +/*!
  11 + * \ingroup transforms
  12 + * \brief Applies watershed segmentation.
  13 + * \author Austin Blanton \cite imaus10
  14 + */
  15 +class WatershedSegmentationTransform : public UntrainableTransform
  16 +{
  17 + Q_OBJECT
  18 + void project(const Template &src, Template &dst) const
  19 + {
  20 + dst = src;
  21 +
  22 + Mat mod;
  23 +// adaptiveThreshold(src.m(), src.m(), 255, ADAPTIVE_THRESH_GAUSSIAN_C, THRESH_BINARY, 33, 5);
  24 + threshold(src.m(), mod, 0, 255, THRESH_BINARY+THRESH_OTSU);
  25 +
  26 + // findContours requires an 8-bit 1-channel image
  27 + // and modifies its source image
  28 + if (mod.depth() != CV_8U) OpenCVUtils::cvtUChar(mod, mod);
  29 + if (mod.channels() != 1) OpenCVUtils::cvtGray(mod, mod);
  30 + vector<vector<Point> > contours;
  31 + vector<Vec4i> hierarchy;
  32 + findContours(mod, contours, hierarchy, CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE);
  33 +
  34 + // draw the contour delineations as 1,2,3... for input to watershed
  35 + Mat markers = Mat::zeros(mod.size(), CV_32S);
  36 + int compCount=0;
  37 + for (int idx=0; idx>=0; idx=hierarchy[idx][0], compCount++) {
  38 + drawContours(markers, contours, idx, Scalar::all(compCount+1), -1, 8, hierarchy, INT_MAX);
  39 + }
  40 +
  41 + Mat orig = src.m();
  42 + // watershed requires a 3-channel 8-bit image
  43 + if (orig.channels() == 1) cvtColor(orig, orig, CV_GRAY2BGR);
  44 + watershed(orig, markers);
  45 + dst.file.set("SegmentsMask", QVariant::fromValue(markers));
  46 + dst.file.set("NumSegments", compCount);
  47 + }
  48 +};
  49 +BR_REGISTER(Transform, WatershedSegmentationTransform)
  50 +
  51 +} // namespace br
  52 +
  53 +#include "segmentation.moc"
... ...
openbr/plugins/stream.cpp
... ... @@ -347,10 +347,11 @@ public:
347 347 }
348 348  
349 349 // first 4 bytes store 0xEDFE, next 24 store 'Norpix seq '
350   - char *firstFour = new char[4], *nextTwentyFour;
  350 + char firstFour[4];
351 351 seqFile.seekg(0, ios::beg);
352 352 seqFile.read(firstFour, 4);
353   - nextTwentyFour = readText(24);
  353 + char nextTwentyFour[24];
  354 + readText(24, nextTwentyFour);
354 355 if (firstFour[0] != (char)0xED || firstFour[1] != (char)0xFE || strncmp(nextTwentyFour, "Norpix seq", 10) != 0) {
355 356 qDebug("Invalid header in seq file");
356 357 return false;
... ... @@ -363,7 +364,8 @@ public:
363 364 qDebug("Invalid header size");
364 365 return false;
365 366 }
366   - char *desc = readText(512);
  367 + char desc[512];
  368 + readText(512, desc);
367 369 basis.file.set("Description", QString(desc));
368 370  
369 371 width = readInt();
... ... @@ -414,9 +416,9 @@ public:
414 416 // but there might be 16 extra bytes instead of 8...
415 417 if (i == 1) {
416 418 seqFile.seekg(s, ios::beg);
417   - char *zero = new char[1];
418   - seqFile.read(zero, 1);
419   - if (zero[0] == 0) {
  419 + char zero;
  420 + seqFile.read(&zero, 1);
  421 + if (zero == 0) {
420 422 s += 8;
421 423 extra += 8;
422 424 }
... ... @@ -498,14 +500,13 @@ private:
498 500 // apparently the text in seq files is 16 bit characters (UTF-16?)
499 501 // since we don't really need the last byte, snad since it gets interpreted as
500 502 // a terminating char, let's just grab the first byte for storage
501   - char* readText(int bytes)
  503 + void readText(int bytes, char * buffer)
502 504 {
503   - char *text = new char[bytes], *ret = new char[bytes/2];
504   - seqFile.read(text, bytes);
  505 + seqFile.read(buffer, bytes);
505 506 for (int i=0; i<bytes; i+=2) {
506   - ret[i/2] = text[i];
  507 + buffer[i/2] = buffer[i];
507 508 }
508   - return ret;
  509 + buffer[bytes/2] = '\0';
509 510 }
510 511  
511 512 protected:
... ...