Commit 3c6b8c3c45410062f1da4ae2a77931bdf83794f3

Authored by Charles Otto
2 parents d6b19edd 69d0258d

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

3rdparty/stasm4.0.0/CMakeLists.txt
@@ -32,8 +32,12 @@ if(MSVC) @@ -32,8 +32,12 @@ if(MSVC)
32 endif() 32 endif()
33 33
34 add_subdirectory(${PROJECT_SOURCE_DIR}/stasm) 34 add_subdirectory(${PROJECT_SOURCE_DIR}/stasm)
  35 +if(MSVC)
  36 + add_library(stasm STATIC ${SOURCE})
  37 +else()
  38 + add_library(stasm SHARED ${SOURCE})
  39 +endif()
35 40
36 -add_library(stasm SHARED ${SOURCE})  
37 qt5_use_modules(stasm ${QT_DEPENDENCIES}) 41 qt5_use_modules(stasm ${QT_DEPENDENCIES})
38 set(SOURCE ${SOURCE} PARENT_SCOPE) 42 set(SOURCE ${SOURCE} PARENT_SCOPE)
39 target_link_libraries(stasm ${OpenCV_LIBS} ${Qt5Core_QTMAIN_LIBRARIES}) 43 target_link_libraries(stasm ${OpenCV_LIBS} ${Qt5Core_QTMAIN_LIBRARIES})
3rdparty/stasm4.0.0/stasm/stasm_lib.cpp
@@ -157,7 +157,6 @@ int stasm_search_auto_ext( // extended version of stasm_search_auto @@ -157,7 +157,6 @@ int stasm_search_auto_ext( // extended version of stasm_search_auto
157 { 157 {
158 int returnval = 1; // assume success 158 int returnval = 1; // assume success
159 *foundface = 0; // but assume no face found 159 *foundface = 0; // but assume no face found
160 - CatchOpenCvErrs();  
161 try 160 try
162 { 161 {
163 CheckStasmInit(); 162 CheckStasmInit();
@@ -204,7 +203,6 @@ int stasm_search_auto_ext( // extended version of stasm_search_auto @@ -204,7 +203,6 @@ int stasm_search_auto_ext( // extended version of stasm_search_auto
204 { 203 {
205 returnval = 0; // a call was made to Err or a CV_Assert failed 204 returnval = 0; // a call was made to Err or a CV_Assert failed
206 } 205 }
207 - UncatchOpenCvErrs();  
208 return returnval; 206 return returnval;
209 } 207 }
210 208
CMakeLists.txt
@@ -17,7 +17,10 @@ set(CPACK_RESOURCE_FILE_README "${CMAKE_CURRENT_SOURCE_DIR}/README.md") @@ -17,7 +17,10 @@ set(CPACK_RESOURCE_FILE_README "${CMAKE_CURRENT_SOURCE_DIR}/README.md")
17 set(CMAKE_MODULE_PATH "${BR_SHARE_DIR}/cmake" ${CMAKE_MODULE_PATH}) 17 set(CMAKE_MODULE_PATH "${BR_SHARE_DIR}/cmake" ${CMAKE_MODULE_PATH})
18 set(PACKAGE_YEAR 2013) 18 set(PACKAGE_YEAR 2013)
19 19
20 -if("${CMAKE_VERSION}" VERSION_GREATER 2.8.10) 20 +if(${CMAKE_VERSION} VERSION_EQUAL 2.8.11)
  21 + cmake_policy(SET CMP0020 OLD)
  22 +endif()
  23 +if(${CMAKE_VERSION} VERSION_GREATER 2.8.11)
21 cmake_policy(SET CMP0020 OLD) 24 cmake_policy(SET CMP0020 OLD)
22 endif() 25 endif()
23 26
openbr/core/bee.cpp
@@ -43,9 +43,15 @@ FileList BEE::readSigset(const File &sigset, bool ignoreMetadata) @@ -43,9 +43,15 @@ FileList BEE::readSigset(const File &sigset, bool ignoreMetadata)
43 QFile file(sigset.resolved()); 43 QFile file(sigset.resolved());
44 bool success; 44 bool success;
45 success = file.open(QIODevice::ReadOnly); if (!success) qFatal("Unable to open %s for reading.", qPrintable(sigset)); 45 success = file.open(QIODevice::ReadOnly); if (!success) qFatal("Unable to open %s for reading.", qPrintable(sigset));
46 - success = doc.setContent(&file); if (!success) qFatal("Unable to parse %s.", qPrintable(sigset)); 46 + success = doc.setContent(&file);
  47 +
47 file.close(); 48 file.close();
48 49
  50 + if (!success) {
  51 + qWarning("Unable to parse %s.", qPrintable(sigset));
  52 + return fileList;
  53 + }
  54 +
49 QDomElement docElem = doc.documentElement(); 55 QDomElement docElem = doc.documentElement();
50 if (docElem.nodeName() != "biometric-signature-set") 56 if (docElem.nodeName() != "biometric-signature-set")
51 return fileList; 57 return fileList;
openbr/core/core.cpp
@@ -167,7 +167,6 @@ struct AlgorithmCore @@ -167,7 +167,6 @@ struct AlgorithmCore
167 } 167 }
168 168
169 const float speed = 1000 * Globals->totalSteps / Globals->startTime.elapsed() / std::max(1, abs(Globals->parallelism)); 169 const float speed = 1000 * Globals->totalSteps / Globals->startTime.elapsed() / std::max(1, abs(Globals->parallelism));
170 - qDebug() << Globals->startTime.elapsed();  
171 if (!Globals->quiet && (Globals->totalSteps > 1)) 170 if (!Globals->quiet && (Globals->totalSteps > 1))
172 fprintf(stderr, "\rSPEED=%.1e SIZE=%.4g FAILURES=%d/%d \n", 171 fprintf(stderr, "\rSPEED=%.1e SIZE=%.4g FAILURES=%d/%d \n",
173 speed, totalBytes/totalCount, failureCount, totalCount); 172 speed, totalBytes/totalCount, failureCount, totalCount);
openbr/core/plot.cpp
@@ -247,8 +247,8 @@ float Evaluate(const Mat &amp;simmat, const Mat &amp;mask, const QString &amp;csv) @@ -247,8 +247,8 @@ float Evaluate(const Mat &amp;simmat, const Mat &amp;mask, const QString &amp;csv)
247 } 247 }
248 248
249 // Write FAR/TAR Bar Chart (BC) 249 // Write FAR/TAR Bar Chart (BC)
250 - lines.append(qPrintable(QString("BC,0.001,%1").arg(QString::number(result = getTAR(operatingPoints, 0.001), 'f', 3))));  
251 - lines.append(qPrintable(QString("BC,0.01,%1").arg(QString::number(getTAR(operatingPoints, 0.01), 'f', 3)))); 250 + lines.append(qPrintable(QString("BC,0.001,%1").arg(QString::number(getTAR(operatingPoints, 0.001), 'f', 3))));
  251 + lines.append(qPrintable(QString("BC,0.01,%1").arg(QString::number(result = getTAR(operatingPoints, 0.01), 'f', 3))));
252 252
253 // Write SD & KDE 253 // Write SD & KDE
254 points = qMin(qMin(Max_Points, genuines.size()), impostors.size()); 254 points = qMin(qMin(Max_Points, genuines.size()), impostors.size());
@@ -269,7 +269,7 @@ float Evaluate(const Mat &amp;simmat, const Mat &amp;mask, const QString &amp;csv) @@ -269,7 +269,7 @@ float Evaluate(const Mat &amp;simmat, const Mat &amp;mask, const QString &amp;csv)
269 } 269 }
270 270
271 // Write Cumulative Match Characteristic (CMC) curve 271 // Write Cumulative Match Characteristic (CMC) curve
272 - const int Max_Retrieval = 100; 272 + const int Max_Retrieval = 200;
273 const int Report_Retrieval = 5; 273 const int Report_Retrieval = 5;
274 274
275 float reportRetrievalRate = -1; 275 float reportRetrievalRate = -1;
@@ -287,7 +287,7 @@ float Evaluate(const Mat &amp;simmat, const Mat &amp;mask, const QString &amp;csv) @@ -287,7 +287,7 @@ float Evaluate(const Mat &amp;simmat, const Mat &amp;mask, const QString &amp;csv)
287 } 287 }
288 288
289 if (!csv.isEmpty()) QtUtils::writeFile(csv, lines); 289 if (!csv.isEmpty()) QtUtils::writeFile(csv, lines);
290 - qDebug("TAR @ FAR = 0.001: %.3f\nRetrieval Rate @ Rank = %d: %.3f", result, Report_Retrieval, reportRetrievalRate); 290 + qDebug("TAR @ FAR = 0.01: %.3f\nRetrieval Rate @ Rank = %d: %.3f", result, Report_Retrieval, reportRetrievalRate);
291 return result; 291 return result;
292 } 292 }
293 293
openbr/openbr_plugin.cpp
@@ -368,7 +368,7 @@ TemplateList TemplateList::fromGallery(const br::File &amp;gallery) @@ -368,7 +368,7 @@ TemplateList TemplateList::fromGallery(const br::File &amp;gallery)
368 QScopedPointer<Gallery> i(Gallery::make(file)); 368 QScopedPointer<Gallery> i(Gallery::make(file));
369 TemplateList newTemplates = i->read(); 369 TemplateList newTemplates = i->read();
370 370
371 - // If file is a Format not a Gallery 371 + // If file is a Format not a Gallery (e.g. XML Format vs. XML Gallery)
372 if (newTemplates.isEmpty()) 372 if (newTemplates.isEmpty())
373 newTemplates.append(file); 373 newTemplates.append(file);
374 374
@@ -1193,6 +1193,33 @@ void Transform::backProject(const TemplateList &amp;dst, TemplateList &amp;src) const @@ -1193,6 +1193,33 @@ void Transform::backProject(const TemplateList &amp;dst, TemplateList &amp;src) const
1193 futures.waitForFinished(); 1193 futures.waitForFinished();
1194 } 1194 }
1195 1195
  1196 +QList<Transform *> Transform::getChildren() const
  1197 +{
  1198 + QList<Transform *> output;
  1199 + for (int i=0; i < metaObject()->propertyCount(); i++) {
  1200 + const char * prop_name = metaObject()->property(i).name();
  1201 + const QVariant & variant = this->property(prop_name);
  1202 +
  1203 + if (variant.canConvert<Transform *>())
  1204 + output.append(variant.value<Transform *>());
  1205 + if (variant.canConvert<QList<Transform *> >())
  1206 + output.append(variant.value<QList<Transform *> >());
  1207 + }
  1208 + return output;
  1209 +}
  1210 +
  1211 +TemplateEvent * Transform::getEvent(const QString & name)
  1212 +{
  1213 + foreach(Transform * child, getChildren())
  1214 + {
  1215 + TemplateEvent * probe = child->getEvent(name);
  1216 + if (probe)
  1217 + return probe;
  1218 + }
  1219 +
  1220 + return NULL;
  1221 +}
  1222 +
1196 /* Distance - public methods */ 1223 /* Distance - public methods */
1197 Distance *Distance::make(QString str, QObject *parent) 1224 Distance *Distance::make(QString str, QObject *parent)
1198 { 1225 {
openbr/openbr_plugin.h
@@ -1040,6 +1040,21 @@ private: @@ -1040,6 +1040,21 @@ private:
1040 * @{ 1040 * @{
1041 */ 1041 */
1042 1042
  1043 +class TemplateEvent : public QObject
  1044 +{
  1045 + Q_OBJECT
  1046 +
  1047 +public:
  1048 + void pulseSignal(const Template & output) const
  1049 + {
  1050 + emit theSignal(output);
  1051 + }
  1052 +
  1053 +signals:
  1054 + void theSignal(const Template & output) const;
  1055 +};
  1056 +
  1057 +
1043 /*! 1058 /*!
1044 * \brief Plugin base class for processing a template. 1059 * \brief Plugin base class for processing a template.
1045 * 1060 *
@@ -1144,6 +1159,17 @@ public: @@ -1144,6 +1159,17 @@ public:
1144 */ 1159 */
1145 virtual Transform * smartCopy() { return this;} 1160 virtual Transform * smartCopy() { return this;}
1146 1161
  1162 + /*!
  1163 + * \brief Recursively retrieve a named event, returns NULL if an event is not found.
  1164 + */
  1165 + virtual TemplateEvent * getEvent(const QString & name);
  1166 +
  1167 + /*!
  1168 + * \brief Get a list of child transforms of this transform, child transforms are considered to be
  1169 + * any transforms stored as properties of this transform.
  1170 + */
  1171 + QList<Transform *> getChildren() const;
  1172 +
1147 protected: 1173 protected:
1148 Transform(bool independent = true, bool trainable = true); /*!< \brief Construct a transform. */ 1174 Transform(bool independent = true, bool trainable = true); /*!< \brief Construct a transform. */
1149 inline Transform *make(const QString &description) { return make(description, this); } /*!< \brief Make a subtransform. */ 1175 inline Transform *make(const QString &description) { return make(description, this); } /*!< \brief Make a subtransform. */
openbr/plugins/crop.cpp
@@ -71,18 +71,32 @@ BR_REGISTER(Transform, ROITransform) @@ -71,18 +71,32 @@ BR_REGISTER(Transform, ROITransform)
71 * \ingroup transforms 71 * \ingroup transforms
72 * \brief Resize the template 72 * \brief Resize the template
73 * \author Josh Klontz \cite jklontz 73 * \author Josh Klontz \cite jklontz
  74 + * \note Method: Area should be used for shrinking an image, Cubic for slow but accurate enlargment, Bilin for fast enlargement.
74 */ 75 */
75 class ResizeTransform : public UntrainableTransform 76 class ResizeTransform : public UntrainableTransform
76 { 77 {
77 Q_OBJECT 78 Q_OBJECT
  79 + Q_ENUMS(Method)
  80 +
  81 +public:
  82 + /*!< */
  83 + enum Method { Near = INTER_NEAREST,
  84 + Area = INTER_AREA,
  85 + Bilin = INTER_LINEAR,
  86 + Cubic = INTER_CUBIC,
  87 + Lanczo = INTER_LANCZOS4};
  88 +
  89 +private:
78 Q_PROPERTY(int rows READ get_rows WRITE set_rows RESET reset_rows STORED false) 90 Q_PROPERTY(int rows READ get_rows WRITE set_rows RESET reset_rows STORED false)
79 Q_PROPERTY(int columns READ get_columns WRITE set_columns RESET reset_columns STORED false) 91 Q_PROPERTY(int columns READ get_columns WRITE set_columns RESET reset_columns STORED false)
  92 + Q_PROPERTY(Method method READ get_method WRITE set_method RESET reset_method STORED false)
80 BR_PROPERTY(int, rows, -1) 93 BR_PROPERTY(int, rows, -1)
81 BR_PROPERTY(int, columns, -1) 94 BR_PROPERTY(int, columns, -1)
  95 + BR_PROPERTY(Method, method, Bilin)
82 96
83 void project(const Template &src, Template &dst) const 97 void project(const Template &src, Template &dst) const
84 { 98 {
85 - resize(src, dst, Size((columns == -1) ? src.m().cols*rows/src.m().rows : columns, rows)); 99 + resize(src, dst, Size((columns == -1) ? src.m().cols*rows/src.m().rows : columns, rows), 0, 0, method);
86 } 100 }
87 }; 101 };
88 102
openbr/plugins/distance.cpp
@@ -298,6 +298,33 @@ class IdenticalDistance : public Distance @@ -298,6 +298,33 @@ class IdenticalDistance : public Distance
298 298
299 BR_REGISTER(Distance, IdenticalDistance) 299 BR_REGISTER(Distance, IdenticalDistance)
300 300
301 -} // namespace br  
302 301
  302 +/*!
  303 + * \ingroup distances
  304 + * \brief Online distance metric to attenuate match scores across multiple frames
  305 + * \author Brendan klare \cite bklare
  306 + */
  307 +class OnlineDistance : public Distance
  308 +{
  309 + Q_OBJECT
  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)
  312 + BR_PROPERTY(br::Distance*, distance, NULL)
  313 + BR_PROPERTY(float, alpha, 0.1f);
  314 +
  315 + mutable QHash<QString,float> scoreHash;
  316 + mutable QMutex mutex;
  317 +
  318 + float compare(const Template &target, const Template &query) const
  319 + {
  320 + float currentScore = distance->compare(target, query);
  321 +
  322 + QMutexLocker mutexLocker(&mutex);
  323 + return scoreHash[target.file.name] = (1.0- alpha) * scoreHash[target.file.name] + alpha * currentScore;
  324 + }
  325 +};
  326 +
  327 +BR_REGISTER(Distance, OnlineDistance)
  328 +
  329 +} // namespace br
303 #include "distance.moc" 330 #include "distance.moc"
openbr/plugins/format.cpp
@@ -660,7 +660,7 @@ class xmlFormat : public Format @@ -660,7 +660,7 @@ class xmlFormat : public Format
660 QFile f(fileName); 660 QFile f(fileName);
661 661
662 if (!f.open(QIODevice::ReadOnly)) qFatal("Unable to open %s for reading.", qPrintable(file.flat())); 662 if (!f.open(QIODevice::ReadOnly)) qFatal("Unable to open %s for reading.", qPrintable(file.flat()));
663 - if (!doc.setContent(&f)) qFatal("Unable to parse %s.", qPrintable(file.flat())); 663 + if (!doc.setContent(&f)) qWarning("Unable to parse %s.", qPrintable(file.flat()));
664 f.close(); 664 f.close();
665 665
666 QDomElement docElem = doc.documentElement(); 666 QDomElement docElem = doc.documentElement();
openbr/plugins/gui.cpp
@@ -220,16 +220,16 @@ signals: @@ -220,16 +220,16 @@ signals:
220 220
221 BR_REGISTER(Transform, ShowTransform) 221 BR_REGISTER(Transform, ShowTransform)
222 222
223 -class FPSSynch : public TimeVaryingTransform 223 +class FPSLimit : public TimeVaryingTransform
224 { 224 {
225 Q_OBJECT 225 Q_OBJECT
226 Q_PROPERTY(int targetFPS READ get_targetFPS WRITE set_targetFPS RESET reset_targetFPS STORED false) 226 Q_PROPERTY(int targetFPS READ get_targetFPS WRITE set_targetFPS RESET reset_targetFPS STORED false)
227 BR_PROPERTY(int, targetFPS, 30) 227 BR_PROPERTY(int, targetFPS, 30)
228 228
229 public: 229 public:
230 - FPSSynch() : TimeVaryingTransform(false, false) {} 230 + FPSLimit() : TimeVaryingTransform(false, false) {}
231 231
232 - ~FPSSynch() {} 232 + ~FPSLimit() {}
233 233
234 void train(const TemplateList &data) { (void) data; } 234 void train(const TemplateList &data) { (void) data; }
235 235
@@ -237,16 +237,18 @@ public: @@ -237,16 +237,18 @@ public:
237 void projectUpdate(const TemplateList &src, TemplateList &dst) 237 void projectUpdate(const TemplateList &src, TemplateList &dst)
238 { 238 {
239 dst = src; 239 dst = src;
240 - qint64 time_delta = timer.elapsed(); 240 + qint64 current_time = timer.elapsed();
  241 + qint64 target_time = last_time + target_wait;
  242 + qint64 wait_time = target_time - current_time;
241 243
242 - qint64 wait_time = target_wait - time_delta;  
243 - timer.start(); 244 + last_time = current_time;
244 245
245 if (wait_time < 0) { 246 if (wait_time < 0) {
246 return; 247 return;
247 } 248 }
248 249
249 QThread::msleep(wait_time); 250 QThread::msleep(wait_time);
  251 + last_time = timer.elapsed();
250 } 252 }
251 253
252 void finalize(TemplateList & output) 254 void finalize(TemplateList & output)
@@ -256,15 +258,17 @@ public: @@ -256,15 +258,17 @@ public:
256 258
257 void init() 259 void init()
258 { 260 {
259 - target_wait = 1000 / targetFPS; 261 + target_wait = 1000.0 / targetFPS;
260 timer.start(); 262 timer.start();
  263 + last_time = timer.elapsed();
261 } 264 }
262 265
263 protected: 266 protected:
264 QElapsedTimer timer; 267 QElapsedTimer timer;
265 qint64 target_wait; 268 qint64 target_wait;
  269 + qint64 last_time;
266 }; 270 };
267 -BR_REGISTER(Transform, FPSSynch) 271 +BR_REGISTER(Transform, FPSLimit)
268 272
269 class FPSCalc : public TimeVaryingTransform 273 class FPSCalc : public TimeVaryingTransform
270 { 274 {
openbr/plugins/landmarks.cpp
@@ -21,6 +21,9 @@ class ProcrustesTransform : public Transform @@ -21,6 +21,9 @@ class ProcrustesTransform : public Transform
21 { 21 {
22 Q_OBJECT 22 Q_OBJECT
23 23
  24 + Q_PROPERTY(bool warp READ get_warp WRITE set_warp RESET reset_warp STORED false)
  25 + BR_PROPERTY(bool, warp, true)
  26 +
24 Eigen::MatrixXf meanShape; 27 Eigen::MatrixXf meanShape;
25 28
26 void train(const TemplateList &data) 29 void train(const TemplateList &data)
@@ -30,23 +33,33 @@ class ProcrustesTransform : public Transform @@ -30,23 +33,33 @@ class ProcrustesTransform : public Transform
30 // Normalize all sets of points 33 // Normalize all sets of points
31 foreach (br::Template datum, data) { 34 foreach (br::Template datum, data) {
32 QList<QPointF> points = datum.file.points(); 35 QList<QPointF> points = datum.file.points();
  36 + QList<QRectF> rects = datum.file.rects();
  37 +
  38 + if (points.empty() || rects.empty()) continue;
33 39
34 - if (points.empty()) continue; 40 + // Assume rect appended last was bounding box
  41 + points.append(rects.last().topLeft());
  42 + points.append(rects.last().topRight());
  43 + points.append(rects.last().bottomLeft());
  44 + points.append(rects.last().bottomRight());
35 45
36 - cv::Scalar mean = cv::mean(OpenCVUtils::toPoints(points).toVector().toStdVector()); 46 + // Center shape at origin
  47 + Scalar mean = cv::mean(OpenCVUtils::toPoints(points).toVector().toStdVector());
37 for (int i = 0; i < points.size(); i++) points[i] -= QPointF(mean[0],mean[1]); 48 for (int i = 0; i < points.size(); i++) points[i] -= QPointF(mean[0],mean[1]);
38 49
  50 + // Remove scale component
39 float norm = cv::norm(OpenCVUtils::toPoints(points).toVector().toStdVector()); 51 float norm = cv::norm(OpenCVUtils::toPoints(points).toVector().toStdVector());
40 for (int i = 0; i < points.size(); i++) points[i] /= norm; 52 for (int i = 0; i < points.size(); i++) points[i] /= norm;
41 53
42 normalizedPoints.append(points); 54 normalizedPoints.append(points);
43 } 55 }
44 56
45 - // Determine mean shape  
46 - Eigen::MatrixXf shapeBuffer(normalizedPoints[0].size(), 2); 57 + if (normalizedPoints.empty()) qFatal("Unable to calculate normalized points");
47 58
48 - for (int i = 0; i < normalizedPoints[0].size(); i++) { 59 + // Determine mean shape, assuming all shapes contain the same number of points
  60 + meanShape = Eigen::MatrixXf(normalizedPoints[0].size(), 2);
49 61
  62 + for (int i = 0; i < normalizedPoints[0].size(); i++) {
50 double x = 0; 63 double x = 0;
51 double y = 0; 64 double y = 0;
52 65
@@ -58,39 +71,58 @@ class ProcrustesTransform : public Transform @@ -58,39 +71,58 @@ class ProcrustesTransform : public Transform
58 x /= (double)normalizedPoints.size(); 71 x /= (double)normalizedPoints.size();
59 y /= (double)normalizedPoints.size(); 72 y /= (double)normalizedPoints.size();
60 73
61 - shapeBuffer(i,0) = x;  
62 - shapeBuffer(i,1) = y; 74 + meanShape(i,0) = x;
  75 + meanShape(i,1) = y;
63 } 76 }
64 -  
65 - meanShape = shapeBuffer;  
66 } 77 }
67 78
68 void project(const Template &src, Template &dst) const 79 void project(const Template &src, Template &dst) const
69 { 80 {
70 QList<QPointF> points = src.file.points(); 81 QList<QPointF> points = src.file.points();
  82 + QList<QRectF> rects = src.file.rects();
71 83
72 - cv::Scalar mean = cv::mean(OpenCVUtils::toPoints(points).toVector().toStdVector()); 84 + if (points.empty() || rects.empty()) {
  85 + dst = src;
  86 + qWarning("Procrustes alignment failed because points or rects are empty.");
  87 + return;
  88 + }
  89 +
  90 + // Assume rect appended last was bounding box
  91 + points.append(rects.last().topLeft());
  92 + points.append(rects.last().topRight());
  93 + points.append(rects.last().bottomLeft());
  94 + points.append(rects.last().bottomRight());
  95 +
  96 + Scalar mean = cv::mean(OpenCVUtils::toPoints(points).toVector().toStdVector());
73 for (int i = 0; i < points.size(); i++) points[i] -= QPointF(mean[0],mean[1]); 97 for (int i = 0; i < points.size(); i++) points[i] -= QPointF(mean[0],mean[1]);
74 98
  99 + Eigen::MatrixXf srcMat(points.size(), 2);
75 float norm = cv::norm(OpenCVUtils::toPoints(points).toVector().toStdVector()); 100 float norm = cv::norm(OpenCVUtils::toPoints(points).toVector().toStdVector());
76 - Eigen::MatrixXf srcPoints(points.size(), 2);  
77 -  
78 for (int i = 0; i < points.size(); i++) { 101 for (int i = 0; i < points.size(); i++) {
79 - srcPoints(i,0) = points[i].x()/(norm/150.)+50;  
80 - srcPoints(i,1) = points[i].y()/(norm/150.)+50; 102 + points[i] /= norm;
  103 + srcMat(i,0) = points[i].x();
  104 + srcMat(i,1) = points[i].y();
81 } 105 }
82 106
83 - Eigen::JacobiSVD<Eigen::MatrixXf> svd(srcPoints.transpose()*meanShape, Eigen::ComputeThinU | Eigen::ComputeThinV);  
84 - 107 + Eigen::JacobiSVD<Eigen::MatrixXf> svd(srcMat.transpose()*meanShape, Eigen::ComputeThinU | Eigen::ComputeThinV);
85 Eigen::MatrixXf R = svd.matrixU()*svd.matrixV().transpose(); 108 Eigen::MatrixXf R = svd.matrixU()*svd.matrixV().transpose();
86 109
87 - Eigen::MatrixXf dstPoints = srcPoints*R;  
88 -  
89 - points.clear(); 110 + dst = src;
90 111
91 - for (int i = 0; i < dstPoints.rows(); i++) points.append(QPointF(dstPoints(i,0),dstPoints(i,1))); 112 + if (warp) {
  113 + Eigen::MatrixXf dstMat = srcMat*R;
  114 + for (int i = 0; i < dstMat.rows(); i++) {
  115 + dst.file.appendPoint(QPointF(dstMat(i,0),dstMat(i,1)));
  116 + }
  117 + }
92 118
93 - dst.file.appendPoints(points); 119 + dst.file.set("Procrustes_0_0", R(0,0));
  120 + dst.file.set("Procrustes_1_0", R(1,0));
  121 + dst.file.set("Procrustes_1_1", R(1,1));
  122 + dst.file.set("Procrustes_0_1", R(0,1));
  123 + dst.file.set("Procrustes_mean_0", mean[0]);
  124 + dst.file.set("Procrustes_mean_1", mean[1]);
  125 + dst.file.set("Procrustes_norm", norm);
94 } 126 }
95 127
96 void store(QDataStream &stream) const 128 void store(QDataStream &stream) const
@@ -109,60 +141,202 @@ BR_REGISTER(Transform, ProcrustesTransform) @@ -109,60 +141,202 @@ BR_REGISTER(Transform, ProcrustesTransform)
109 141
110 /*! 142 /*!
111 * \ingroup transforms 143 * \ingroup transforms
112 - * \brief Wraps STASM key point detector 144 + * \brief Creates a Delaunay triangulation based on a set of points
113 * \author Scott Klum \cite sklum 145 * \author Scott Klum \cite sklum
114 */ 146 */
115 -class DelauneyTransform : public UntrainableTransform 147 +class DelaunayTransform : public UntrainableTransform
116 { 148 {
117 Q_OBJECT 149 Q_OBJECT
118 150
  151 + Q_PROPERTY(float scaleFactor READ get_scaleFactor WRITE set_scaleFactor RESET reset_scaleFactor STORED false)
  152 + Q_PROPERTY(QString widthCrop READ get_widthCrop WRITE set_widthCrop RESET reset_widthCrop STORED false)
  153 + Q_PROPERTY(QString heightCrop READ get_heightCrop WRITE set_heightCrop RESET reset_heightCrop STORED false)
  154 + Q_PROPERTY(bool warp READ get_warp WRITE set_warp RESET reset_warp STORED false)
119 Q_PROPERTY(bool draw READ get_draw WRITE set_draw RESET reset_draw STORED false) 155 Q_PROPERTY(bool draw READ get_draw WRITE set_draw RESET reset_draw STORED false)
  156 + BR_PROPERTY(float, scaleFactor, 1)
  157 + BR_PROPERTY(QString, widthCrop, QString())
  158 + BR_PROPERTY(QString, heightCrop, QString())
  159 + BR_PROPERTY(bool, warp, true)
120 BR_PROPERTY(bool, draw, false) 160 BR_PROPERTY(bool, draw, false)
121 161
122 void project(const Template &src, Template &dst) const 162 void project(const Template &src, Template &dst) const
123 { 163 {
124 - dst = src;  
125 -  
126 Subdiv2D subdiv(Rect(0,0,src.m().cols,src.m().rows)); 164 Subdiv2D subdiv(Rect(0,0,src.m().cols,src.m().rows));
127 165
128 - foreach(const cv::Point2f& point, OpenCVUtils::toPoints(src.file.points())) subdiv.insert(point); 166 + QList<QPointF> points = src.file.points();
  167 + QList<QRectF> rects = src.file.rects();
  168 +
  169 + if (points.empty() || rects.empty()) {
  170 + dst = src;
  171 + qWarning("Delauney triangulation failed because points or rects are empty.");
  172 + return;
  173 + }
  174 +
  175 + // Assume rect appended last was bounding box
  176 + points.append(rects.last().topLeft());
  177 + points.append(rects.last().topRight());
  178 + points.append(rects.last().bottomLeft());
  179 + points.append(rects.last().bottomRight());
  180 +
  181 + for (int i = 0; i < points.size(); i++) {
  182 + if (points[i].x() < 0 || points[i].y() < 0 || points[i].y() >= src.m().rows || points[i].x() >= src.m().cols) {
  183 + dst = src;
  184 + qWarning("Delauney triangulation failed because points lie on boundary.");
  185 + return;
  186 + }
  187 + subdiv.insert(OpenCVUtils::toPoint(points[i]));
  188 + }
129 189
130 vector<Vec6f> triangleList; 190 vector<Vec6f> triangleList;
131 subdiv.getTriangleList(triangleList); 191 subdiv.getTriangleList(triangleList);
132 - vector<Point> pt(3);  
133 192
134 - Scalar delaunay_color(0, 0, 0); 193 + QList< QList<Point> > validTriangles;
  194 +
  195 + for (size_t i = 0; i < triangleList.size(); ++i) {
  196 +
  197 + vector<Point> pt(3);
  198 + pt[0] = Point(cvRound(triangleList[i][0]), cvRound(triangleList[i][1]));
  199 + pt[1] = Point(cvRound(triangleList[i][2]), cvRound(triangleList[i][3]));
  200 + pt[2] = Point(cvRound(triangleList[i][4]), cvRound(triangleList[i][5]));
  201 +
  202 + bool inside = true;
  203 + for (int j = 0; j < 3; j++) {
  204 + if (pt[j].x > src.m().cols || pt[j].y > src.m().rows || pt[j].x < 0 || pt[j].y < 0) inside = false;
  205 + }
  206 +
  207 + if (inside) validTriangles.append(QList<Point>()<< pt[0] << pt[1] << pt[2]);
  208 + }
  209 +
  210 + dst.m() = src.m().clone();
135 211
136 if (draw) { 212 if (draw) {
137 - int count = 0;  
138 - for(size_t i = 0; i < triangleList.size(); ++i) {  
139 - Vec6f t = triangleList[i];  
140 -  
141 - pt[0] = Point(cvRound(t[0]), cvRound(t[1]));  
142 - pt[1] = Point(cvRound(t[2]), cvRound(t[3]));  
143 - pt[2] = Point(cvRound(t[4]), cvRound(t[5]));  
144 -  
145 - bool inside = true;  
146 - for (int i = 0; i < 3; i++) {  
147 - if(pt[i].x > dst.m().cols || pt[i].y > dst.m().rows || pt[i].x <= 0 || pt[i].y <= 0) {  
148 - inside = false;  
149 - } 213 + foreach(const QList<Point>& triangle, validTriangles) {
  214 + line(dst.m(), triangle[0], triangle[1], Scalar(0,0,0), 1);
  215 + line(dst.m(), triangle[1], triangle[2], Scalar(0,0,0), 1);
  216 + line(dst.m(), triangle[2], triangle[0], Scalar(0,0,0), 1);
  217 + }
  218 + }
  219 +
  220 + if (warp) {
  221 + Eigen::MatrixXf R(2,2);
  222 + R(0,0) = src.file.get<float>("Procrustes_0_0");
  223 + R(1,0) = src.file.get<float>("Procrustes_1_0");
  224 + R(1,1) = src.file.get<float>("Procrustes_1_1");
  225 + R(0,1) = src.file.get<float>("Procrustes_0_1");
  226 +
  227 + cv::Scalar mean(2);
  228 + mean[0] = src.file.get<float>("Procrustes_mean_0");
  229 + mean[1] = src.file.get<float>("Procrustes_mean_1");
  230 +
  231 + float norm = src.file.get<float>("Procrustes_norm");
  232 +
  233 + dst.m() = Mat::zeros(src.m().rows,src.m().cols,src.m().type());
  234 +
  235 + QList<Point2f> mappedPoints;
  236 +
  237 + for (int i = 0; i < validTriangles.size(); i++) {
  238 + Eigen::MatrixXf srcMat(validTriangles[i].size(), 2);
150 239
  240 + for (int j = 0; j < 3; j++) {
  241 + srcMat(j,0) = (validTriangles[i][j].x-mean[0])/norm;
  242 + srcMat(j,1) = (validTriangles[i][j].y-mean[1])/norm;
151 } 243 }
152 - if (inside) {  
153 - count++;  
154 - //qDebug() << count << pt[0] << pt[1] << pt[2] << "Area" << contourArea(pt);  
155 - line(dst.m(), pt[0], pt[1], delaunay_color, 1);  
156 - line(dst.m(), pt[1], pt[2], delaunay_color, 1);  
157 - line(dst.m(), pt[2], pt[0], delaunay_color, 1); 244 +
  245 + Eigen::MatrixXf dstMat = srcMat*R;
  246 +
  247 + Point2f srcPoints[3];
  248 + for (int j = 0; j < 3; j++) srcPoints[j] = validTriangles[i][j];
  249 +
  250 + Point2f dstPoints[3];
  251 + for (int j = 0; j < 3; j++) {
  252 + // Scale and shift destination points
  253 + Point2f warpedPoint = Point2f(dstMat(j,0)*scaleFactor+src.m().cols/2,dstMat(j,1)*scaleFactor+src.m().rows/2);
  254 + dstPoints[j] = warpedPoint;
  255 + mappedPoints.append(warpedPoint);
  256 + }
  257 +
  258 + Mat buffer(src.m().rows,src.m().cols,src.m().type());
  259 +
  260 + warpAffine(src.m(), buffer, getAffineTransform(srcPoints, dstPoints), Size(src.m().cols,src.m().rows));
  261 +
  262 + Mat mask = Mat::zeros(src.m().rows, src.m().cols, CV_8UC1);
  263 + Point maskPoints[1][3];
  264 + maskPoints[0][0] = dstPoints[0];
  265 + maskPoints[0][1] = dstPoints[1];
  266 + maskPoints[0][2] = dstPoints[2];
  267 + const Point* ppt = { maskPoints[0] };
  268 +
  269 + fillConvexPoly(mask, ppt, 3, Scalar(255,255,255), 8);
  270 +
  271 + Mat output(src.m().rows,src.m().cols,src.m().type());
  272 +
  273 + // Optimization needed
  274 + if (i > 0) {
  275 + Mat overlap;
  276 + bitwise_and(dst.m(),mask,overlap);
  277 + for (int j = 0; j < overlap.rows; j++) {
  278 + for (int k = 0; k < overlap.cols; k++) {
  279 + if (overlap.at<uchar>(k,j) != 0) {
  280 + mask.at<uchar>(k,j) = 0;
  281 + }
  282 + }
  283 + }
158 } 284 }
  285 +
  286 + bitwise_and(buffer,mask,output);
  287 +
  288 + dst.m() += output;
159 } 289 }
  290 +
  291 + Rect boundingBox = boundingRect(mappedPoints.toVector().toStdVector());
  292 +
  293 + boundingBox.x += boundingBox.width * QtUtils::toPoint(widthCrop).x();
  294 + boundingBox.y += boundingBox.height * QtUtils::toPoint(heightCrop).x();
  295 + boundingBox.width *= 1-QtUtils::toPoint(widthCrop).y();
  296 + boundingBox.height *= 1-QtUtils::toPoint(heightCrop).y();
  297 +
  298 + dst.m() = Mat(dst.m(), boundingBox);
160 } 299 }
161 } 300 }
162 301
163 }; 302 };
164 303
165 -BR_REGISTER(Transform, DelauneyTransform) 304 +BR_REGISTER(Transform, DelaunayTransform)
  305 +
  306 +/*!
  307 + * \ingroup transforms
  308 + * \brief Computes the mean of a set of templates.
  309 + * \note Suitable for visualization only as it sets every projected template to the mean template.
  310 + * \author Scott Klum \cite sklum
  311 + */
  312 +class MeanTransform : public Transform
  313 +{
  314 + Q_OBJECT
  315 +
  316 + Mat mean;
  317 +
  318 + void train(const TemplateList &data)
  319 + {
  320 + mean = Mat::zeros(data[0].m().rows,data[0].m().cols,CV_32F);
  321 +
  322 + for (int i = 0; i < data.size(); i++) {
  323 + Mat converted;
  324 + data[i].m().convertTo(converted, CV_32F);
  325 + mean += converted;
  326 + }
  327 +
  328 + mean /= data.size();
  329 + }
  330 +
  331 + void project(const Template &src, Template &dst) const
  332 + {
  333 + dst = src;
  334 + dst.m() = mean;
  335 + }
  336 +
  337 +};
  338 +
  339 +BR_REGISTER(Transform, MeanTransform)
166 340
167 } // namespace br 341 } // namespace br
168 342
openbr/plugins/misc.cpp
@@ -35,6 +35,7 @@ class OpenTransform : public UntrainableMetaTransform @@ -35,6 +35,7 @@ class OpenTransform : public UntrainableMetaTransform
35 35
36 void project(const Template &src, Template &dst) const 36 void project(const Template &src, Template &dst) const
37 { 37 {
  38 + if (!src.isEmpty()) { dst = src; return; }
38 if (Globals->verbose) qDebug("Opening %s", qPrintable(src.file.flat())); 39 if (Globals->verbose) qDebug("Opening %s", qPrintable(src.file.flat()));
39 dst.file = src.file; 40 dst.file = src.file;
40 foreach (const File &file, src.file.split()) { 41 foreach (const File &file, src.file.split()) {
@@ -561,6 +562,28 @@ class ExpandRectTransform : public UntrainableTransform @@ -561,6 +562,28 @@ class ExpandRectTransform : public UntrainableTransform
561 562
562 BR_REGISTER(Transform, ExpandRectTransform) 563 BR_REGISTER(Transform, ExpandRectTransform)
563 564
  565 +
  566 +class EventTransform : public UntrainableMetaTransform
  567 +{
  568 + Q_OBJECT
  569 + Q_PROPERTY(QString eventName READ get_eventName WRITE set_eventName RESET reset_eventName STORED false)
  570 + BR_PROPERTY(QString, eventName, "")
  571 +
  572 + TemplateEvent event;
  573 +
  574 + void project(const Template &src, Template &dst) const
  575 + {
  576 + dst = src;
  577 + event.pulseSignal(dst);
  578 + }
  579 +
  580 + TemplateEvent * getEvent(const QString & name)
  581 + {
  582 + return name == eventName ? &event : NULL;
  583 + }
  584 +};
  585 +BR_REGISTER(Transform, EventTransform)
  586 +
564 } 587 }
565 588
566 #include "misc.moc" 589 #include "misc.moc"
openbr/plugins/pp5.cpp
@@ -97,8 +97,8 @@ struct PP5Context @@ -97,8 +97,8 @@ struct PP5Context
97 97
98 static void createRawImage(const cv::Mat &src, ppr_raw_image_type &dst) 98 static void createRawImage(const cv::Mat &src, ppr_raw_image_type &dst)
99 { 99 {
100 - if (!src.isContinuous()) qFatal("PP5Context::createRawImage requires continuous data.");  
101 - if (src.channels() == 3) ppr_raw_image_create(&dst, src.cols, src.rows, PPR_RAW_IMAGE_BGR24); 100 + if (!src.isContinuous()) qFatal("PP5Context::createRawImage requires continuous data.");
  101 + else if (src.channels() == 3) ppr_raw_image_create(&dst, src.cols, src.rows, PPR_RAW_IMAGE_BGR24);
102 else if (src.channels() == 1) ppr_raw_image_create(&dst, src.cols, src.rows, PPR_RAW_IMAGE_GRAY8); 102 else if (src.channels() == 1) ppr_raw_image_create(&dst, src.cols, src.rows, PPR_RAW_IMAGE_GRAY8);
103 else qFatal("PP5Context::createRawImage invalid channel count."); 103 else qFatal("PP5Context::createRawImage invalid channel count.");
104 memcpy(dst.data, src.data, src.channels()*src.rows*src.cols); 104 memcpy(dst.data, src.data, src.channels()*src.rows*src.cols);
@@ -241,41 +241,43 @@ class PP5EnrollTransform : public UntrainableMetaTransform @@ -241,41 +241,43 @@ class PP5EnrollTransform : public UntrainableMetaTransform
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 - ppr_raw_image_type raw_image;  
245 - PP5Context::createRawImage(src, raw_image);  
246 - ppr_image_type image;  
247 - TRY(ppr_create_image(raw_image, &image))  
248 - ppr_face_list_type face_list;  
249 - TRY(ppr_detect_faces(context->context, image, &face_list))  
250 -  
251 - for (int i=0; i<face_list.length; i++) {  
252 - ppr_face_type face = face_list.faces[i];  
253 - int extractable;  
254 - TRY(ppr_is_template_extractable(context->context, face, &extractable))  
255 - if (!extractable && !detectOnly) continue;  
256 -  
257 - cv::Mat m;  
258 - if (detectOnly) {  
259 - m = src;  
260 - } else {  
261 - TRY(ppr_extract_face_template(context->context, image, &face))  
262 - context->createMat(face, m);  
263 - }  
264 - Template dst;  
265 - dst.file = src.file;  
266 -  
267 - dst.file.append(PP5Context::toMetadata(face));  
268 - dst += m;  
269 - dstList.append(dst);  
270 -  
271 - // Found a face, nothing else to do (if we aren't trying to find multiple faces).  
272 - if (!Globals->enrollAll)  
273 - break; 244 + if (!src.isEmpty()) {
  245 + ppr_raw_image_type raw_image;
  246 + PP5Context::createRawImage(src, raw_image);
  247 + ppr_image_type image;
  248 + TRY(ppr_create_image(raw_image, &image))
  249 + ppr_face_list_type face_list;
  250 + TRY(ppr_detect_faces(context->context, image, &face_list))
  251 +
  252 + for (int i=0; i<face_list.length; i++) {
  253 + ppr_face_type face = face_list.faces[i];
  254 + int extractable;
  255 + TRY(ppr_is_template_extractable(context->context, face, &extractable))
  256 + if (!extractable && !detectOnly) continue;
  257 +
  258 + cv::Mat m;
  259 + if (detectOnly) {
  260 + m = src;
  261 + } else {
  262 + TRY(ppr_extract_face_template(context->context, image, &face))
  263 + context->createMat(face, m);
  264 + }
  265 + Template dst;
  266 + dst.file = src.file;
  267 +
  268 + dst.file.append(PP5Context::toMetadata(face));
  269 + dst += m;
  270 + dstList.append(dst);
  271 +
  272 + // Found a face, nothing else to do (if we aren't trying to find multiple faces).
  273 + if (!Globals->enrollAll)
  274 + break;
  275 + }
  276 +
  277 + ppr_free_face_list(face_list);
  278 + ppr_free_image(image);
  279 + ppr_raw_image_free(raw_image);
274 } 280 }
275 -  
276 - ppr_free_face_list(face_list);  
277 - ppr_free_image(image);  
278 - ppr_raw_image_free(raw_image);  
279 } 281 }
280 282
281 // No faces were detected, output something with FTE set. 283 // No faces were detected, output something with FTE set.
@@ -305,10 +307,14 @@ class PP5CompareDistance : public Distance @@ -305,10 +307,14 @@ class PP5CompareDistance : public Distance
305 307
306 float compare(const Template &target, const Template &query) const 308 float compare(const Template &target, const Template &query) const
307 { 309 {
308 - (void) target;  
309 - (void) query;  
310 - qFatal("Compare single templates should never be called!");  
311 - return 0; 310 + TemplateList targetList;
  311 + targetList.append(target);
  312 + TemplateList queryList;
  313 + queryList.append(query);
  314 + MatrixOutput *score = MatrixOutput::make(targetList.files(), queryList.files());
  315 + compare(targetList, queryList, score);
  316 + return score->data.at<float>(0);
  317 +
312 } 318 }
313 319
314 void compare(const TemplateList &target, const TemplateList &query, Output *output) const 320 void compare(const TemplateList &target, const TemplateList &query, Output *output) const
openbr/plugins/regions.cpp
@@ -95,8 +95,9 @@ class CatTransform : public UntrainableMetaTransform @@ -95,8 +95,9 @@ class CatTransform : public UntrainableMetaTransform
95 for (int i=0; i<src.size(); i++) 95 for (int i=0; i<src.size(); i++)
96 sizes[i%partitions] += src[i].total(); 96 sizes[i%partitions] += src[i].total();
97 97
98 - foreach (int size, sizes)  
99 - dst.append(Mat(1, size, src.m().type())); 98 + if (!src.empty())
  99 + foreach (int size, sizes)
  100 + dst.append(Mat(1, size, src.m().type()));
100 101
101 QVector<int> offsets(partitions, 0); 102 QVector<int> offsets(partitions, 0);
102 for (int i=0; i<src.size(); i++) { 103 for (int i=0; i<src.size(); i++) {
@@ -230,6 +231,7 @@ class RectFromPointsTransform : public UntrainableTransform @@ -230,6 +231,7 @@ class RectFromPointsTransform : public UntrainableTransform
230 if (src.file.points()[index].y() > maxY) maxY = src.file.points()[index].y(); 231 if (src.file.points()[index].y() > maxY) maxY = src.file.points()[index].y();
231 points.append(src.file.points()[index]); 232 points.append(src.file.points()[index]);
232 } 233 }
  234 + else qFatal("Incorrect indices");
233 } 235 }
234 236
235 double width = maxX-minX; 237 double width = maxX-minX;
@@ -238,12 +240,15 @@ class RectFromPointsTransform : public UntrainableTransform @@ -238,12 +240,15 @@ class RectFromPointsTransform : public UntrainableTransform
238 240
239 double height = maxY-minY; 241 double height = maxY-minY;
240 double deltaHeight = width/aspectRatio - height; 242 double deltaHeight = width/aspectRatio - height;
241 - height += deltaHeight; 243 + height += deltaHeight;
242 244
243 dst.file.setPoints(points); 245 dst.file.setPoints(points);
244 246
245 if (crop) dst.m() = src.m()(Rect(std::max(0.0, minX - deltaWidth/2.0), std::max(0.0, minY - deltaHeight/2.0), std::min((double)src.m().cols, width), std::min((double)src.m().rows, height))); 247 if (crop) dst.m() = src.m()(Rect(std::max(0.0, minX - deltaWidth/2.0), std::max(0.0, minY - deltaHeight/2.0), std::min((double)src.m().cols, width), std::min((double)src.m().rows, height)));
246 - else dst.m() = src.m(); 248 + else {
  249 + dst.file.appendRect(QRectF(std::max(0.0, minX - deltaWidth/2.0), std::max(0.0, minY - deltaHeight/2.0), std::min((double)src.m().cols, width), std::min((double)src.m().rows, height)));
  250 + dst.m() = src.m();
  251 + }
247 } 252 }
248 }; 253 };
249 254
openbr/plugins/register.cpp
@@ -28,10 +28,22 @@ namespace br @@ -28,10 +28,22 @@ namespace br
28 * \ingroup transforms 28 * \ingroup transforms
29 * \brief Performs a two or three point registration. 29 * \brief Performs a two or three point registration.
30 * \author Josh Klontz \cite jklontz 30 * \author Josh Klontz \cite jklontz
  31 + * \note Method: Area should be used for shrinking an image, Cubic for slow but accurate enlargment, Bilin for fast enlargement.
31 */ 32 */
32 class AffineTransform : public UntrainableTransform 33 class AffineTransform : public UntrainableTransform
33 { 34 {
34 Q_OBJECT 35 Q_OBJECT
  36 + Q_ENUMS(Method)
  37 +
  38 +public:
  39 + /*!< */
  40 + enum Method { Near = INTER_NEAREST,
  41 + Area = INTER_AREA,
  42 + Bilin = INTER_LINEAR,
  43 + Cubic = INTER_CUBIC,
  44 + Lanczo = INTER_LANCZOS4};
  45 +
  46 +private:
35 Q_PROPERTY(int width READ get_width WRITE set_width RESET reset_width STORED false) 47 Q_PROPERTY(int width READ get_width WRITE set_width RESET reset_width STORED false)
36 Q_PROPERTY(int height READ get_height WRITE set_height RESET reset_height STORED false) 48 Q_PROPERTY(int height READ get_height WRITE set_height RESET reset_height STORED false)
37 Q_PROPERTY(float x1 READ get_x1 WRITE set_x1 RESET reset_x1 STORED false) 49 Q_PROPERTY(float x1 READ get_x1 WRITE set_x1 RESET reset_x1 STORED false)
@@ -40,6 +52,7 @@ class AffineTransform : public UntrainableTransform @@ -40,6 +52,7 @@ class AffineTransform : public UntrainableTransform
40 Q_PROPERTY(float y2 READ get_y2 WRITE set_y2 RESET reset_y2 STORED false) 52 Q_PROPERTY(float y2 READ get_y2 WRITE set_y2 RESET reset_y2 STORED false)
41 Q_PROPERTY(float x3 READ get_x3 WRITE set_x3 RESET reset_x3 STORED false) 53 Q_PROPERTY(float x3 READ get_x3 WRITE set_x3 RESET reset_x3 STORED false)
42 Q_PROPERTY(float y3 READ get_y3 WRITE set_y3 RESET reset_y3 STORED false) 54 Q_PROPERTY(float y3 READ get_y3 WRITE set_y3 RESET reset_y3 STORED false)
  55 + Q_PROPERTY(Method method READ get_method WRITE set_method RESET reset_method STORED false)
43 BR_PROPERTY(int, width, 64) 56 BR_PROPERTY(int, width, 64)
44 BR_PROPERTY(int, height, 64) 57 BR_PROPERTY(int, height, 64)
45 BR_PROPERTY(float, x1, 0) 58 BR_PROPERTY(float, x1, 0)
@@ -48,6 +61,7 @@ class AffineTransform : public UntrainableTransform @@ -48,6 +61,7 @@ class AffineTransform : public UntrainableTransform
48 BR_PROPERTY(float, y2, -1) 61 BR_PROPERTY(float, y2, -1)
49 BR_PROPERTY(float, x3, -1) 62 BR_PROPERTY(float, x3, -1)
50 BR_PROPERTY(float, y3, -1) 63 BR_PROPERTY(float, y3, -1)
  64 + BR_PROPERTY(Method, method, Bilin)
51 65
52 static Point2f getThirdAffinePoint(const Point2f &a, const Point2f &b) 66 static Point2f getThirdAffinePoint(const Point2f &a, const Point2f &b)
53 { 67 {
@@ -72,9 +86,6 @@ class AffineTransform : public UntrainableTransform @@ -72,9 +86,6 @@ class AffineTransform : public UntrainableTransform
72 (src.file.contains("Affine_2") || twoPoints)) { 86 (src.file.contains("Affine_2") || twoPoints)) {
73 srcPoints[0] = OpenCVUtils::toPoint(src.file.get<QPointF>("Affine_0")); 87 srcPoints[0] = OpenCVUtils::toPoint(src.file.get<QPointF>("Affine_0"));
74 srcPoints[1] = OpenCVUtils::toPoint(src.file.get<QPointF>("Affine_1")); 88 srcPoints[1] = OpenCVUtils::toPoint(src.file.get<QPointF>("Affine_1"));
75 -  
76 - dst.file.set("Affine_0", OpenCVUtils::fromPoint(dstPoints[0]));  
77 - dst.file.set("Affine_1", OpenCVUtils::fromPoint(dstPoints[1]));  
78 if (!twoPoints) srcPoints[2] = OpenCVUtils::toPoint(src.file.get<QPointF>("Affine_2")); 89 if (!twoPoints) srcPoints[2] = OpenCVUtils::toPoint(src.file.get<QPointF>("Affine_2"));
79 } else { 90 } else {
80 const QList<Point2f> landmarks = OpenCVUtils::toPoints(src.file.points()); 91 const QList<Point2f> landmarks = OpenCVUtils::toPoints(src.file.points());
@@ -89,13 +100,12 @@ class AffineTransform : public UntrainableTransform @@ -89,13 +100,12 @@ class AffineTransform : public UntrainableTransform
89 100
90 dst.file.set("Affine_0", OpenCVUtils::fromPoint(landmarks[0])); 101 dst.file.set("Affine_0", OpenCVUtils::fromPoint(landmarks[0]));
91 dst.file.set("Affine_1", OpenCVUtils::fromPoint(landmarks[1])); 102 dst.file.set("Affine_1", OpenCVUtils::fromPoint(landmarks[1]));
92 -  
93 if (!twoPoints) dst.file.set("Affine_2", OpenCVUtils::fromPoint(landmarks[2])); 103 if (!twoPoints) dst.file.set("Affine_2", OpenCVUtils::fromPoint(landmarks[2]));
94 } 104 }
95 } 105 }
96 if (twoPoints) srcPoints[2] = getThirdAffinePoint(srcPoints[0], srcPoints[1]); 106 if (twoPoints) srcPoints[2] = getThirdAffinePoint(srcPoints[0], srcPoints[1]);
97 107
98 - warpAffine(src, dst, getAffineTransform(srcPoints, dstPoints), Size(width, height)); 108 + warpAffine(src, dst, getAffineTransform(srcPoints, dstPoints), Size(width, height), method);
99 } 109 }
100 }; 110 };
101 111
openbr/plugins/stasm4.cpp
@@ -38,8 +38,8 @@ class StasmInitializer : public Initializer @@ -38,8 +38,8 @@ class StasmInitializer : public Initializer
38 { 38 {
39 Globals->abbreviations.insert("RectFromStasmEyes","RectFromPoints([29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47],0.125,6.0)+Resize(44,164)"); 39 Globals->abbreviations.insert("RectFromStasmEyes","RectFromPoints([29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47],0.125,6.0)+Resize(44,164)");
40 Globals->abbreviations.insert("RectFromStasmBrow","RectFromPoints([17, 18, 19, 20, 21, 22, 23, 24],0.15,6)+Resize(28,132)"); 40 Globals->abbreviations.insert("RectFromStasmBrow","RectFromPoints([17, 18, 19, 20, 21, 22, 23, 24],0.15,6)+Resize(28,132)");
41 - Globals->abbreviations.insert("RectFromStasmNose","RectFromPoints([48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58],0.15,1.25)+Resize(60,60)");  
42 - Globals->abbreviations.insert("RectFromStasmMouth","RectFromPoints([59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76],0.3,3.0)+Resize(28,68)"); 41 + Globals->abbreviations.insert("RectFromStasmNose","RectFromPoints([48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58],0.15,1.25)");
  42 + Globals->abbreviations.insert("RectFromStasmMouth","RectFromPoints([59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76],0.3,2.5)");
43 } 43 }
44 }; 44 };
45 45
openbr/plugins/stream.cpp
@@ -241,7 +241,7 @@ public: @@ -241,7 +241,7 @@ public:
241 } 241 }
242 242
243 virtual void close() = 0; 243 virtual void close() = 0;
244 - virtual bool open(Template & output) = 0; 244 + virtual bool open(Template & output, int start_index=0) = 0;
245 virtual bool isOpen() = 0; 245 virtual bool isOpen() = 0;
246 246
247 virtual bool getNext(FrameData & input) = 0; 247 virtual bool getNext(FrameData & input) = 0;
@@ -262,13 +262,13 @@ class VideoDataSource : public DataSource @@ -262,13 +262,13 @@ class VideoDataSource : public DataSource
262 public: 262 public:
263 VideoDataSource(int maxFrames) : DataSource(maxFrames) {} 263 VideoDataSource(int maxFrames) : DataSource(maxFrames) {}
264 264
265 - bool open(Template &input) 265 + bool open(Template &input, int start_index=0)
266 { 266 {
267 final_frame = -1; 267 final_frame = -1;
268 last_issued = -2; 268 last_issued = -2;
269 last_received = -3; 269 last_received = -3;
270 270
271 - next_idx = 0; 271 + next_idx = start_index;
272 basis = input; 272 basis = input;
273 bool is_int = false; 273 bool is_int = false;
274 int anInt = input.file.name.toInt(&is_int); 274 int anInt = input.file.name.toInt(&is_int);
@@ -336,11 +336,11 @@ public: @@ -336,11 +336,11 @@ public:
336 } 336 }
337 bool data_ok; 337 bool data_ok;
338 338
339 - bool open(Template &input) 339 + bool open(Template &input, int start_index=0)
340 { 340 {
341 basis = input; 341 basis = input;
342 current_idx = 0; 342 current_idx = 0;
343 - next_sequence = 0; 343 + next_sequence = start_index;
344 final_frame = -1; 344 final_frame = -1;
345 last_issued = -2; 345 last_issued = -2;
346 last_received = -3; 346 last_received = -3;
@@ -406,22 +406,31 @@ public: @@ -406,22 +406,31 @@ public:
406 } 406 }
407 } 407 }
408 408
409 - bool open(Template & input) 409 + bool open(TemplateList & input)
  410 + {
  411 + currentIdx = 0;
  412 + templates = input;
  413 +
  414 + return open(templates[currentIdx]);
  415 + }
  416 +
  417 + bool open(Template & input, int start_index=0)
410 { 418 {
411 close(); 419 close();
412 final_frame = -1; 420 final_frame = -1;
413 last_issued = -2; 421 last_issued = -2;
414 last_received = -3; 422 last_received = -3;
  423 + next_frame = start_index;
415 424
416 // Input has no matrices? Its probably a video that hasn't been loaded yet 425 // Input has no matrices? Its probably a video that hasn't been loaded yet
417 if (input.empty()) { 426 if (input.empty()) {
418 actualSource = new VideoDataSource(0); 427 actualSource = new VideoDataSource(0);
419 - actualSource->open(input); 428 + actualSource->open(input, next_frame);
420 } 429 }
421 else { 430 else {
422 // create frame dealer 431 // create frame dealer
423 actualSource = new TemplateDataSource(0); 432 actualSource = new TemplateDataSource(0);
424 - actualSource->open(input); 433 + actualSource->open(input, next_frame);
425 } 434 }
426 if (!isOpen()) { 435 if (!isOpen()) {
427 delete actualSource; 436 delete actualSource;
@@ -434,10 +443,31 @@ public: @@ -434,10 +443,31 @@ public:
434 bool isOpen() { return !actualSource ? false : actualSource->isOpen(); } 443 bool isOpen() { return !actualSource ? false : actualSource->isOpen(); }
435 444
436 protected: 445 protected:
  446 + int currentIdx;
  447 + int next_frame;
  448 + TemplateList templates;
437 DataSource * actualSource; 449 DataSource * actualSource;
438 bool getNext(FrameData & output) 450 bool getNext(FrameData & output)
439 { 451 {
440 - return actualSource->getNext(output); 452 + bool res = actualSource->getNext(output);
  453 + if (res) {
  454 + next_frame = output.sequenceNumber+1;
  455 + return true;
  456 + }
  457 +
  458 + while(!res) {
  459 + currentIdx++;
  460 +
  461 + if (currentIdx >= templates.size())
  462 + return false;
  463 + bool open_res = open(templates[currentIdx], next_frame);
  464 + if (!open_res)
  465 + return false;
  466 + res = actualSource->getNext(output);
  467 + }
  468 +
  469 + next_frame = output.sequenceNumber+1;
  470 + return res;
441 } 471 }
442 472
443 }; 473 };
@@ -796,11 +826,9 @@ public: @@ -796,11 +826,9 @@ public:
796 // start processing 826 // start processing
797 void projectUpdate(const TemplateList & src, TemplateList & dst) 827 void projectUpdate(const TemplateList & src, TemplateList & dst)
798 { 828 {
799 - if (src.size() != 1)  
800 - qFatal("Expected single template input to stream");  
801 -  
802 dst = src; 829 dst = src;
803 - bool res = readStage->dataSource.open(dst[0]); 830 +
  831 + bool res = readStage->dataSource.open(dst);
804 if (!res) return; 832 if (!res) return;
805 833
806 QThreadPool::globalInstance()->releaseThread(); 834 QThreadPool::globalInstance()->releaseThread();
share/openbr/cmake/InstallDependencies.cmake
@@ -10,7 +10,8 @@ function(install_opencv_library lib) @@ -10,7 +10,8 @@ function(install_opencv_library lib)
10 if(NOT MSVC) 10 if(NOT MSVC)
11 set(BR_INSTALL_DEPENDENCIES_PREFIX "lib") 11 set(BR_INSTALL_DEPENDENCIES_PREFIX "lib")
12 endif() 12 endif()
13 - install(FILES ${OpenCV_DIR}/bin/${BR_INSTALL_DEPENDENCIES_PREFIX}${lib}${OpenCV_VERSION_MAJOR}${OpenCV_VERSION_MINOR}${OpenCV_VERSION_PATCH}${BR_INSTALL_DEPENDENCIES_SUFFIX}.dll DESTINATION bin) 13 + list(GET OpenCV_LIB_DIR 0 cv_lib_stripped)
  14 + install(FILES ${cv_lib_stripped}/../bin/${BR_INSTALL_DEPENDENCIES_PREFIX}${lib}${OpenCV_VERSION_MAJOR}${OpenCV_VERSION_MINOR}${OpenCV_VERSION_PATCH}${BR_INSTALL_DEPENDENCIES_SUFFIX}.dll DESTINATION bin)
14 else() 15 else()
15 set(OpenCV_LIB_DIR "/usr/local/lib") 16 set(OpenCV_LIB_DIR "/usr/local/lib")
16 install(FILES ${OpenCV_LIB_DIR}/lib${lib}.${OpenCV_VERSION_MAJOR}.${OpenCV_VERSION_MINOR}.${OpenCV_VERSION_PATCH}${CMAKE_SHARED_LIBRARY_SUFFIX} DESTINATION lib) 17 install(FILES ${OpenCV_LIB_DIR}/lib${lib}.${OpenCV_VERSION_MAJOR}.${OpenCV_VERSION_MINOR}.${OpenCV_VERSION_PATCH}${CMAKE_SHARED_LIBRARY_SUFFIX} DESTINATION lib)