Commit 670ffb1152db5e127d12c1c5ae3d3150479c3c12
1 parent
659370e6
Preliminary update to the ct8 plugin
Various changes to make the plugin consistent with the current API. Switch a number of data structures to QT things instead of openCV equivalents. Make FRsdk::OpenCVImageBody construct either an rgb or grayscale representation depending on input image type instead of always building both. Mark failure cases in both the detection and enrollment transforms. Allow the enrollment transform to work even if eye detection hasn't been done yet (the sdk will automatically do detection using the default parameters).
Showing
1 changed file
with
164 additions
and
79 deletions
sdk/plugins/ct8.cpp
| @@ -11,22 +11,31 @@ | @@ -11,22 +11,31 @@ | ||
| 11 | #include <exception> | 11 | #include <exception> |
| 12 | #include <string> | 12 | #include <string> |
| 13 | #include <vector> | 13 | #include <vector> |
| 14 | -#include <mm_plugin.h> | 14 | +#include <openbr_plugin.h> |
| 15 | 15 | ||
| 16 | -#include "model.h" | ||
| 17 | -#include "resource.h" | 16 | +#include "core/resource.h" |
| 18 | 17 | ||
| 19 | using namespace cv; | 18 | using namespace cv; |
| 20 | -using namespace mm; | 19 | +using namespace br; |
| 21 | 20 | ||
| 22 | namespace FRsdk { | 21 | namespace FRsdk { |
| 22 | + // Construct a FaceVACS sdk ImageBody from an opencv Mat | ||
| 23 | struct OpenCVImageBody : public ImageBody | 23 | struct OpenCVImageBody : public ImageBody |
| 24 | { | 24 | { |
| 25 | OpenCVImageBody(const Mat &m, const std::string& _name = "") | 25 | OpenCVImageBody(const Mat &m, const std::string& _name = "") |
| 26 | : w(m.cols), h(m.rows), b(0), rgb(0), n(_name) | 26 | : w(m.cols), h(m.rows), b(0), rgb(0), n(_name) |
| 27 | { | 27 | { |
| 28 | - buildRgbRepresentation(m); | ||
| 29 | - buildByteRepresentation(); | 28 | + // The ImageBody only needs to construct a grayscale or rgb |
| 29 | + // representation (whichever is indicated by isColor()), not both. | ||
| 30 | + if (m.channels() == 1) { | ||
| 31 | + is_color = false; | ||
| 32 | + buildByteRepresentation(m); | ||
| 33 | + } | ||
| 34 | + else { | ||
| 35 | + buildRgbRepresentation(m); | ||
| 36 | + is_color = true; | ||
| 37 | + } | ||
| 38 | + | ||
| 30 | } | 39 | } |
| 31 | 40 | ||
| 32 | ~OpenCVImageBody() | 41 | ~OpenCVImageBody() |
| @@ -35,7 +44,7 @@ namespace FRsdk { | @@ -35,7 +44,7 @@ namespace FRsdk { | ||
| 35 | delete[] b; | 44 | delete[] b; |
| 36 | } | 45 | } |
| 37 | 46 | ||
| 38 | - bool isColor() const { return true; } | 47 | + bool isColor() const { return is_color; } |
| 39 | unsigned int height() const { return h; } | 48 | unsigned int height() const { return h; } |
| 40 | unsigned int width() const { return w; } | 49 | unsigned int width() const { return w; } |
| 41 | const Byte* grayScaleRepresentation() const { return b; } | 50 | const Byte* grayScaleRepresentation() const { return b; } |
| @@ -58,26 +67,20 @@ namespace FRsdk { | @@ -58,26 +67,20 @@ namespace FRsdk { | ||
| 58 | } | 67 | } |
| 59 | } | 68 | } |
| 60 | 69 | ||
| 61 | - void buildByteRepresentation() | 70 | + void buildByteRepresentation(const Mat & m) |
| 62 | { | 71 | { |
| 63 | b = new Byte[w*h]; | 72 | b = new Byte[w*h]; |
| 64 | - Rgb* colorp = rgb; | ||
| 65 | Byte* grayp = b; | 73 | Byte* grayp = b; |
| 66 | for( unsigned int i = 0; i < h; i++ ) { | 74 | for( unsigned int i = 0; i < h; i++ ) { |
| 67 | for( unsigned int k = 0; k < w; k++ ) { | 75 | for( unsigned int k = 0; k < w; k++ ) { |
| 68 | - float f = (float) colorp->r; | ||
| 69 | - f += (float) colorp->g; | ||
| 70 | - f += (float) colorp->b; | ||
| 71 | - f /= 3.0f; | ||
| 72 | - if( f > 255.0f) f = 255.0f; | ||
| 73 | - *grayp = (Byte) f; | ||
| 74 | - colorp++; | 76 | + *grayp = (Byte) m.at<uchar>(i,k); |
| 75 | grayp++; | 77 | grayp++; |
| 76 | } | 78 | } |
| 77 | } | 79 | } |
| 78 | } | 80 | } |
| 79 | 81 | ||
| 80 | private: | 82 | private: |
| 83 | + bool is_color; | ||
| 81 | unsigned int w; | 84 | unsigned int w; |
| 82 | unsigned int h; | 85 | unsigned int h; |
| 83 | Byte* b; | 86 | Byte* b; |
| @@ -86,6 +89,10 @@ namespace FRsdk { | @@ -86,6 +89,10 @@ namespace FRsdk { | ||
| 86 | }; | 89 | }; |
| 87 | 90 | ||
| 88 | 91 | ||
| 92 | + // Enrollment::FeedbackBody subclasses are used as a set of callbacks | ||
| 93 | + // during facevacs enrollment. This class keeps track of whether or not | ||
| 94 | + // enrollment has failed (checkable via firValid()), and the extracted fir | ||
| 95 | + // (getFir) | ||
| 89 | struct EnrolOpenCVFeedback : public Enrollment::FeedbackBody | 96 | struct EnrolOpenCVFeedback : public Enrollment::FeedbackBody |
| 90 | { | 97 | { |
| 91 | EnrolOpenCVFeedback(Mat *_m) | 98 | EnrolOpenCVFeedback(Mat *_m) |
| @@ -94,14 +101,11 @@ namespace FRsdk { | @@ -94,14 +101,11 @@ namespace FRsdk { | ||
| 94 | 101 | ||
| 95 | EnrolOpenCVFeedback() {} | 102 | EnrolOpenCVFeedback() {} |
| 96 | 103 | ||
| 97 | - void start() | ||
| 98 | - { | ||
| 99 | - firvalid = false; | ||
| 100 | - } | 104 | + void start() { firvalid = false; } |
| 101 | 105 | ||
| 102 | void processingImage(const FRsdk::Image& img) { (void) img; } | 106 | void processingImage(const FRsdk::Image& img) { (void) img; } |
| 103 | void eyesFound( const FRsdk::Eyes::Location& eyeLoc) { (void) eyeLoc; } | 107 | void eyesFound( const FRsdk::Eyes::Location& eyeLoc) { (void) eyeLoc; } |
| 104 | - void eyesNotFound() {} | 108 | + void eyesNotFound() { firvalid = false;} |
| 105 | void sampleQualityTooLow() {} | 109 | void sampleQualityTooLow() {} |
| 106 | void sampleQuality(const float& f) { (void) f; } | 110 | void sampleQuality(const float& f) { (void) f; } |
| 107 | 111 | ||
| @@ -113,7 +117,7 @@ namespace FRsdk { | @@ -113,7 +117,7 @@ namespace FRsdk { | ||
| 113 | firvalid = true; | 117 | firvalid = true; |
| 114 | } | 118 | } |
| 115 | 119 | ||
| 116 | - void failure() {} | 120 | + void failure() { firvalid = false; } |
| 117 | void end() {} | 121 | void end() {} |
| 118 | 122 | ||
| 119 | const FRsdk::FIR& getFir() const | 123 | const FRsdk::FIR& getFir() const |
| @@ -122,10 +126,7 @@ namespace FRsdk { | @@ -122,10 +126,7 @@ namespace FRsdk { | ||
| 122 | return *fir; | 126 | return *fir; |
| 123 | } | 127 | } |
| 124 | 128 | ||
| 125 | - bool firValid() const | ||
| 126 | - { | ||
| 127 | - return firvalid; | ||
| 128 | - } | 129 | + bool firValid() const { return firvalid; } |
| 129 | 130 | ||
| 130 | private: | 131 | private: |
| 131 | FRsdk::CountedPtr<FRsdk::FIR> fir; | 132 | FRsdk::CountedPtr<FRsdk::FIR> fir; |
| @@ -139,16 +140,14 @@ struct CT8Initialize : public Initializer | @@ -139,16 +140,14 @@ struct CT8Initialize : public Initializer | ||
| 139 | { | 140 | { |
| 140 | static FRsdk::Configuration* CT8Configuration; | 141 | static FRsdk::Configuration* CT8Configuration; |
| 141 | 142 | ||
| 143 | + // ct8 plugin initialization, load a FRsdk config file, and register | ||
| 144 | + // the shortcut for using FaceVACS feature extraction/comparison | ||
| 142 | void initialize() const | 145 | void initialize() const |
| 143 | { | 146 | { |
| 144 | - QFile file(":/3rdparty/ct8/activationkey.cfg"); | ||
| 145 | - file.open(QFile::ReadOnly); | ||
| 146 | - QByteArray data = file.readAll(); | ||
| 147 | - file.close(); | ||
| 148 | - std::istringstream istream(QString(data).arg(Globals.SDKPath+"/models/ct8").toStdString()); | ||
| 149 | - | ||
| 150 | try { | 147 | try { |
| 151 | - CT8Configuration = new FRsdk::Configuration(istream); | 148 | + // Need to do something different wrt getting the config file location -cao |
| 149 | + CT8Configuration = new FRsdk::Configuration("C:/FVSDK_8_6_0/etc/frsdk.cfg"); | ||
| 150 | + Globals->abbreviations.insert("CT8","Open+CT8Detect!CT8Enroll:CT8Compare"); | ||
| 152 | } catch (std::exception &e) { | 151 | } catch (std::exception &e) { |
| 153 | qWarning("CT8Initialize Exception: %s", e.what()); | 152 | qWarning("CT8Initialize Exception: %s", e.what()); |
| 154 | CT8Configuration = NULL; | 153 | CT8Configuration = NULL; |
| @@ -164,17 +163,20 @@ struct CT8Initialize : public Initializer | @@ -164,17 +163,20 @@ struct CT8Initialize : public Initializer | ||
| 164 | 163 | ||
| 165 | FRsdk::Configuration* CT8Initialize::CT8Configuration = NULL; | 164 | FRsdk::Configuration* CT8Initialize::CT8Configuration = NULL; |
| 166 | 165 | ||
| 167 | -MM_REGISTER(Initializer, CT8Initialize, false) | ||
| 168 | - | 166 | +BR_REGISTER(Initializer, CT8Initialize) |
| 169 | 167 | ||
| 170 | -class CT8EnrollmentProcessorResource : public Resource<FRsdk::Enrollment::Processor> | 168 | +// Adaptor class adding a default constructor to FRsdk::Enrollment::Processor |
| 169 | +// so that it can be used with Resource | ||
| 170 | +class CT8EnrollmentProcessor : public FRsdk::Enrollment::Processor | ||
| 171 | { | 171 | { |
| 172 | - QSharedPointer<FRsdk::Enrollment::Processor> make() const | 172 | +public: |
| 173 | + CT8EnrollmentProcessor() : FRsdk::Enrollment::Processor(*CT8Initialize::CT8Configuration) | ||
| 173 | { | 174 | { |
| 174 | - return QSharedPointer<FRsdk::Enrollment::Processor>(new FRsdk::Enrollment::Processor(*CT8Initialize::CT8Configuration)); | 175 | + // |
| 175 | } | 176 | } |
| 176 | }; | 177 | }; |
| 177 | 178 | ||
| 179 | +typedef Resource<CT8EnrollmentProcessor> CT8EnrollmentProcessorResource; | ||
| 178 | 180 | ||
| 179 | struct CT8Context | 181 | struct CT8Context |
| 180 | { | 182 | { |
| @@ -185,13 +187,12 @@ struct CT8Context | @@ -185,13 +187,12 @@ struct CT8Context | ||
| 185 | eyesFinder = new FRsdk::Eyes::Finder(*CT8Initialize::CT8Configuration); | 187 | eyesFinder = new FRsdk::Eyes::Finder(*CT8Initialize::CT8Configuration); |
| 186 | firBuilder = new FRsdk::FIRBuilder(*CT8Initialize::CT8Configuration); | 188 | firBuilder = new FRsdk::FIRBuilder(*CT8Initialize::CT8Configuration); |
| 187 | facialMatchingEngine = new FRsdk::FacialMatchingEngine(*CT8Initialize::CT8Configuration); | 189 | facialMatchingEngine = new FRsdk::FacialMatchingEngine(*CT8Initialize::CT8Configuration); |
| 188 | - enrollmentProcessors.init(); | ||
| 189 | } catch (std::exception &e) { | 190 | } catch (std::exception &e) { |
| 190 | qFatal("CT8Context Exception: %s", e.what()); | 191 | qFatal("CT8Context Exception: %s", e.what()); |
| 191 | } | 192 | } |
| 192 | } | 193 | } |
| 193 | 194 | ||
| 194 | - ~CT8Context() | 195 | + virtual ~CT8Context() |
| 195 | { | 196 | { |
| 196 | delete faceFinder; | 197 | delete faceFinder; |
| 197 | delete eyesFinder; | 198 | delete eyesFinder; |
| @@ -199,26 +200,62 @@ struct CT8Context | @@ -199,26 +200,62 @@ struct CT8Context | ||
| 199 | delete facialMatchingEngine; | 200 | delete facialMatchingEngine; |
| 200 | } | 201 | } |
| 201 | 202 | ||
| 202 | - void enroll(const FRsdk::Image &img, const FRsdk::Eyes::Location &eyes, Mat *m) const | 203 | + // Enroll an FRsdk::Sample (can be various types, generally an image that |
| 204 | + // maybe has some extra data like detected eye locations). | ||
| 205 | + bool enroll(const FRsdk::Sample &sample, Mat *m) const | ||
| 203 | { | 206 | { |
| 204 | try { | 207 | try { |
| 205 | - FRsdk::Sample sample(FRsdk::AnnotatedImage(img, eyes)); | ||
| 206 | FRsdk::SampleSet sampleSet; | 208 | FRsdk::SampleSet sampleSet; |
| 207 | sampleSet.push_back(sample); | 209 | sampleSet.push_back(sample); |
| 208 | 210 | ||
| 209 | - FRsdk::Enrollment::Feedback enrollmentFeedback(new FRsdk::EnrolOpenCVFeedback(m)); | ||
| 210 | - int index; | ||
| 211 | - QSharedPointer<FRsdk::Enrollment::Processor> enrollmentProcessor = enrollmentProcessors.acquire(index); | 211 | + |
| 212 | + FRsdk::EnrolOpenCVFeedback * feedback_body = new FRsdk::EnrolOpenCVFeedback(m); | ||
| 213 | + FRsdk::CountedPtr<FRsdk::Enrollment::FeedbackBody> feedback_ptr(feedback_body); | ||
| 214 | + | ||
| 215 | + FRsdk::Enrollment::Feedback enrollmentFeedback(feedback_ptr); | ||
| 216 | + | ||
| 217 | + CT8EnrollmentProcessor * enrollmentProcessor = enrollmentProcessors.acquire(); | ||
| 212 | enrollmentProcessor->process(sampleSet.begin(), sampleSet.end(), enrollmentFeedback); | 218 | enrollmentProcessor->process(sampleSet.begin(), sampleSet.end(), enrollmentFeedback); |
| 213 | - enrollmentProcessors.release(index); | 219 | + enrollmentProcessors.release(enrollmentProcessor); |
| 220 | + if (!feedback_body->firValid()) return false; | ||
| 214 | } catch (std::exception &e) { | 221 | } catch (std::exception &e) { |
| 215 | qFatal("CT8Context Exception: %s", e.what()); | 222 | qFatal("CT8Context Exception: %s", e.what()); |
| 223 | + return false; | ||
| 216 | } | 224 | } |
| 225 | + return true; | ||
| 217 | } | 226 | } |
| 218 | 227 | ||
| 219 | - static FRsdk::Position toPosition(const Point2f &point) | 228 | + |
| 229 | + // Input: an image, and pre-detected eye locations, returns false if enrollment fails | ||
| 230 | + bool enroll(const FRsdk::Image &img, const FRsdk::Eyes::Location &eyes, Mat *m) const | ||
| 220 | { | 231 | { |
| 221 | - return FRsdk::Position(point.x, point.y); | 232 | + try { |
| 233 | + FRsdk::Sample sample(FRsdk::AnnotatedImage(img, eyes)); | ||
| 234 | + return enroll(sample, m); | ||
| 235 | + } catch (std::exception &e) { | ||
| 236 | + qFatal("CT8Context Exception: %s", e.what()); | ||
| 237 | + return false; | ||
| 238 | + } | ||
| 239 | + return true; | ||
| 240 | + } | ||
| 241 | + | ||
| 242 | + // Input: an image, no eye locations (facevacs will do detection with | ||
| 243 | + // default parameters. Returns false if enrollment fails | ||
| 244 | + bool enroll(const FRsdk::Image &img, Mat *m) const | ||
| 245 | + { | ||
| 246 | + try { | ||
| 247 | + FRsdk::Sample sample(img); | ||
| 248 | + return enroll(sample, m); | ||
| 249 | + } catch (std::exception &e) { | ||
| 250 | + qFatal("CT8Context Exception: %s", e.what()); | ||
| 251 | + return false; | ||
| 252 | + } | ||
| 253 | + return true; | ||
| 254 | + } | ||
| 255 | + | ||
| 256 | + static FRsdk::Position toPosition(const QPointF &point) | ||
| 257 | + { | ||
| 258 | + return FRsdk::Position(point.x(), point.y()); | ||
| 222 | } | 259 | } |
| 223 | 260 | ||
| 224 | protected: | 261 | protected: |
| @@ -230,30 +267,53 @@ protected: | @@ -230,30 +267,53 @@ protected: | ||
| 230 | }; | 267 | }; |
| 231 | 268 | ||
| 232 | 269 | ||
| 233 | -struct CT8Detect : public UntrainableFeature | 270 | +struct CT8Detect : public UntrainableTransform |
| 234 | , public CT8Context | 271 | , public CT8Context |
| 235 | { | 272 | { |
| 273 | + Q_OBJECT | ||
| 274 | + // Perform face, then eye detection using the facevacs SDK | ||
| 236 | void project(const Template &src, Template &dst) const | 275 | void project(const Template &src, Template &dst) const |
| 237 | { | 276 | { |
| 238 | try { | 277 | try { |
| 278 | + // Build an FRsdk image from the input openCV mat | ||
| 239 | FRsdk::CountedPtr<FRsdk::ImageBody> i(new FRsdk::OpenCVImageBody(src)); | 279 | FRsdk::CountedPtr<FRsdk::ImageBody> i(new FRsdk::OpenCVImageBody(src)); |
| 240 | FRsdk::Image img(i); | 280 | FRsdk::Image img(i); |
| 241 | - FRsdk::Face::LocationSet faceLocations = faceFinder->find(img, 0.01); | 281 | + |
| 282 | + // .01 here should be a parameter -cao | ||
| 283 | + FRsdk::Face::LocationSet faceLocations = faceFinder->find(img, 0.01f); | ||
| 284 | + | ||
| 285 | + // If the face finder doesn't find anything mark the output as a failure | ||
| 286 | + if (faceLocations.empty() ) { | ||
| 287 | + dst.file.setBool("FTE"); | ||
| 288 | + return; | ||
| 289 | + } | ||
| 242 | 290 | ||
| 243 | - QList<Rect> ROIs; | ||
| 244 | - QList<Point2f> landmarks; | 291 | + QList<QRectF> ROIs; |
| 292 | + QList<QPointF> landmarks; | ||
| 245 | FRsdk::Face::LocationSet::const_iterator faceLocationSetIterator = faceLocations.begin(); | 293 | FRsdk::Face::LocationSet::const_iterator faceLocationSetIterator = faceLocations.begin(); |
| 294 | + bool any_eyes = false; | ||
| 295 | + | ||
| 296 | + // Attempt to detect eyes in any face ROIs that were detected | ||
| 246 | while (faceLocationSetIterator != faceLocations.end()) { | 297 | while (faceLocationSetIterator != faceLocations.end()) { |
| 247 | FRsdk::Face::Location faceLocation = *faceLocationSetIterator; faceLocationSetIterator++; | 298 | FRsdk::Face::Location faceLocation = *faceLocationSetIterator; faceLocationSetIterator++; |
| 248 | FRsdk::Eyes::LocationSet currentEyesLocations = eyesFinder->find(img, faceLocation); | 299 | FRsdk::Eyes::LocationSet currentEyesLocations = eyesFinder->find(img, faceLocation); |
| 300 | + | ||
| 249 | if (currentEyesLocations.size() > 0) { | 301 | if (currentEyesLocations.size() > 0) { |
| 250 | - ROIs.append(Rect(faceLocation.pos.x(), faceLocation.pos.y(), faceLocation.width, faceLocation.width)); | ||
| 251 | - landmarks.append(Point2f(currentEyesLocations.front().first.x(), currentEyesLocations.front().first.y())); | ||
| 252 | - landmarks.append(Point2f(currentEyesLocations.front().second.x(), currentEyesLocations.front().second.y())); | 302 | + any_eyes = true; |
| 303 | + ROIs.append(QRectF(faceLocation.pos.x(), faceLocation.pos.y(), faceLocation.width, faceLocation.width)); | ||
| 304 | + landmarks.append(QPointF(currentEyesLocations.front().first.x(), currentEyesLocations.front().first.y())); | ||
| 305 | + landmarks.append(QPointF(currentEyesLocations.front().second.x(), currentEyesLocations.front().second.y())); | ||
| 306 | + | ||
| 253 | dst += src; | 307 | dst += src; |
| 254 | } | 308 | } |
| 255 | 309 | ||
| 256 | - if (!Globals.EnrollAll && !dst.isEmpty()) break; | 310 | + if (any_eyes && !Globals->enrollAll && !dst.isEmpty()) break; |
| 311 | + } | ||
| 312 | + | ||
| 313 | + // If eye detection failed, mark the output as a failure | ||
| 314 | + if (!any_eyes) { | ||
| 315 | + dst.file.setBool("FTE"); | ||
| 316 | + return; | ||
| 257 | } | 317 | } |
| 258 | 318 | ||
| 259 | dst.file.setROIs(ROIs); | 319 | dst.file.setROIs(ROIs); |
| @@ -262,61 +322,84 @@ struct CT8Detect : public UntrainableFeature | @@ -262,61 +322,84 @@ struct CT8Detect : public UntrainableFeature | ||
| 262 | qFatal("CT8Enroll Exception: %s", e.what()); | 322 | qFatal("CT8Enroll Exception: %s", e.what()); |
| 263 | } | 323 | } |
| 264 | 324 | ||
| 265 | - if (!Globals.EnrollAll && dst.isEmpty()) dst += Mat(); | 325 | + if (!Globals->enrollAll && dst.isEmpty()) dst += Mat(); |
| 266 | } | 326 | } |
| 267 | }; | 327 | }; |
| 268 | 328 | ||
| 269 | -MM_REGISTER(Feature, CT8Detect, false) | 329 | +BR_REGISTER(Transform, CT8Detect) |
| 270 | 330 | ||
| 271 | 331 | ||
| 272 | -struct CT8Enroll : public UntrainableFeature | 332 | +struct CT8Enroll : public UntrainableTransform |
| 273 | , public CT8Context | 333 | , public CT8Context |
| 274 | { | 334 | { |
| 335 | + Q_OBJECT | ||
| 336 | + // enroll an image using the facevacs sdk. Generates a facevacs "fir" which | ||
| 337 | + // is their face representation. | ||
| 275 | void project(const Template &src, Template &dst) const | 338 | void project(const Template &src, Template &dst) const |
| 276 | { | 339 | { |
| 277 | try { | 340 | try { |
| 278 | FRsdk::CountedPtr<FRsdk::ImageBody> i(new FRsdk::OpenCVImageBody(src)); | 341 | FRsdk::CountedPtr<FRsdk::ImageBody> i(new FRsdk::OpenCVImageBody(src)); |
| 279 | FRsdk::Image img(i); | 342 | FRsdk::Image img(i); |
| 280 | 343 | ||
| 281 | - QList<Point2f> landmarks = src.file.landmarks(); | 344 | + // If we already have eye locations, use them |
| 345 | + QList<QPointF> landmarks = src.file.landmarks(); | ||
| 346 | + bool enroll_succeeded = false; | ||
| 282 | if (landmarks.size() == 2) { | 347 | if (landmarks.size() == 2) { |
| 283 | - enroll(img, FRsdk::Eyes::Location(toPosition(landmarks[0]), toPosition(landmarks[1])), dst.mp()); | ||
| 284 | - dst.file.insert("CT8_First_Eye_X", landmarks[0].x); | ||
| 285 | - dst.file.insert("CT8_First_Eye_Y", landmarks[0].y); | ||
| 286 | - dst.file.insert("CT8_Second_Eye_X", landmarks[1].x); | ||
| 287 | - dst.file.insert("CT8_Second_Eye_Y", landmarks[1].y); | 348 | + enroll_succeeded = enroll(img, FRsdk::Eyes::Location(toPosition(landmarks[0]), toPosition(landmarks[1])), &(dst.m())); |
| 288 | 349 | ||
| 289 | - QList<Rect> ROIs = src.file.ROIs(); | 350 | + // Transfer previously detectd eye and face locations to the output dst. |
| 351 | + dst.file.insert("CT8_First_Eye_X", landmarks[0].x()); | ||
| 352 | + dst.file.insert("CT8_First_Eye_Y", landmarks[0].y()); | ||
| 353 | + dst.file.insert("CT8_Second_Eye_X", landmarks[1].x()); | ||
| 354 | + dst.file.insert("CT8_Second_Eye_Y", landmarks[1].y()); | ||
| 355 | + | ||
| 356 | + QList<QRectF> ROIs = src.file.ROIs(); | ||
| 290 | if (ROIs.size() == 1) { | 357 | if (ROIs.size() == 1) { |
| 291 | - dst.file.insert("CT8_Face_X", ROIs.first().x); | ||
| 292 | - dst.file.insert("CT8_Face_Y", ROIs.first().y); | ||
| 293 | - dst.file.insert("CT8_Face_Width", ROIs.first().width); | ||
| 294 | - dst.file.insert("CT8_Face_Height", ROIs.first().height); | 358 | + dst.file.insert("CT8_Face_X", ROIs.first().x()); |
| 359 | + dst.file.insert("CT8_Face_Y", ROIs.first().y()); | ||
| 360 | + dst.file.insert("CT8_Face_Width", ROIs.first().width()); | ||
| 361 | + dst.file.insert("CT8_Face_Height", ROIs.first().height()); | ||
| 295 | } | 362 | } |
| 296 | } else { | 363 | } else { |
| 297 | - dst = Mat(); | 364 | + // If we don't have eye locations already, calling enroll here |
| 365 | + // will cause facevacs to perform detection using default | ||
| 366 | + // parameters (and we will not receive the detected locations | ||
| 367 | + // as output). | ||
| 368 | + enroll_succeeded = enroll(img, &(dst.m())); | ||
| 369 | + } | ||
| 370 | + // If enrollment failed, mark this image as a failure. This will | ||
| 371 | + // typically only happen if we aren't using pre-detected eye | ||
| 372 | + // locations | ||
| 373 | + if (!enroll_succeeded) | ||
| 374 | + { | ||
| 375 | + dst.file.setBool("FTE"); | ||
| 376 | + return; | ||
| 298 | } | 377 | } |
| 378 | + | ||
| 299 | } catch (std::exception &e) { | 379 | } catch (std::exception &e) { |
| 300 | qFatal("CT8Enroll Exception: %s", e.what()); | 380 | qFatal("CT8Enroll Exception: %s", e.what()); |
| 301 | } | 381 | } |
| 302 | } | 382 | } |
| 303 | }; | 383 | }; |
| 304 | 384 | ||
| 305 | -MM_REGISTER(Feature, CT8Enroll, false) | 385 | +BR_REGISTER(Transform, CT8Enroll) |
| 306 | 386 | ||
| 307 | 387 | ||
| 308 | -struct CT8Compare : public ComparerBase, | 388 | +struct CT8Compare : public Distance, |
| 309 | public CT8Context | 389 | public CT8Context |
| 310 | { | 390 | { |
| 311 | - float compare(const Mat &srcA, const Mat &srcB) const | 391 | + Q_OBJECT |
| 392 | + | ||
| 393 | + // Compare pre-extracted facevacs templates | ||
| 394 | + float _compare(const Template &srcA, const Template &srcB) const | ||
| 312 | { | 395 | { |
| 313 | const float DefaultNonMatchScore = 0; | 396 | const float DefaultNonMatchScore = 0; |
| 314 | - if (!srcA.data || !srcB.data) return DefaultNonMatchScore; | 397 | + if (!srcA.m().data || !srcB.m().data) return DefaultNonMatchScore; |
| 315 | 398 | ||
| 316 | float score = DefaultNonMatchScore; | 399 | float score = DefaultNonMatchScore; |
| 317 | try { | 400 | try { |
| 318 | - FRsdk::FIR firA = firBuilder->build((FRsdk::Byte*)srcA.data, srcA.cols); | ||
| 319 | - FRsdk::FIR firB = firBuilder->build((FRsdk::Byte*)srcB.data, srcB.cols); | 401 | + FRsdk::FIR firA = firBuilder->build( (FRsdk::Byte *) srcA.m().data, srcA.m().cols); |
| 402 | + FRsdk::FIR firB = firBuilder->build( (FRsdk::Byte *) srcB.m().data, srcB.m().cols); | ||
| 320 | score = (float)facialMatchingEngine->compare(firA, firB); | 403 | score = (float)facialMatchingEngine->compare(firA, firB); |
| 321 | } catch (std::exception &e) { | 404 | } catch (std::exception &e) { |
| 322 | qFatal("CT8Compare Exception: %s", e.what()); | 405 | qFatal("CT8Compare Exception: %s", e.what()); |
| @@ -326,5 +409,7 @@ struct CT8Compare : public ComparerBase, | @@ -326,5 +409,7 @@ struct CT8Compare : public ComparerBase, | ||
| 326 | } | 409 | } |
| 327 | }; | 410 | }; |
| 328 | 411 | ||
| 329 | -MM_REGISTER(Comparer, CT8Compare, false) | ||
| 330 | -MM_REGISTER_ALGORITHM(CT8, "Open+CT8Detect!CT8Enroll:CT8Compare") | 412 | +BR_REGISTER(Distance, CT8Compare) |
| 413 | + | ||
| 414 | +#include "plugins/ct8.moc" | ||
| 415 | + |