Commit 3c6b8c3c45410062f1da4ae2a77931bdf83794f3
Merge branch 'master' of https://github.com/biometrics/openbr into specificity
Showing
20 changed files
with
490 additions
and
135 deletions
3rdparty/stasm4.0.0/CMakeLists.txt
| ... | ... | @@ -32,8 +32,12 @@ if(MSVC) |
| 32 | 32 | endif() |
| 33 | 33 | |
| 34 | 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 | 41 | qt5_use_modules(stasm ${QT_DEPENDENCIES}) |
| 38 | 42 | set(SOURCE ${SOURCE} PARENT_SCOPE) |
| 39 | 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 | 157 | { |
| 158 | 158 | int returnval = 1; // assume success |
| 159 | 159 | *foundface = 0; // but assume no face found |
| 160 | - CatchOpenCvErrs(); | |
| 161 | 160 | try |
| 162 | 161 | { |
| 163 | 162 | CheckStasmInit(); |
| ... | ... | @@ -204,7 +203,6 @@ int stasm_search_auto_ext( // extended version of stasm_search_auto |
| 204 | 203 | { |
| 205 | 204 | returnval = 0; // a call was made to Err or a CV_Assert failed |
| 206 | 205 | } |
| 207 | - UncatchOpenCvErrs(); | |
| 208 | 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 | 17 | set(CMAKE_MODULE_PATH "${BR_SHARE_DIR}/cmake" ${CMAKE_MODULE_PATH}) |
| 18 | 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 | 24 | cmake_policy(SET CMP0020 OLD) |
| 22 | 25 | endif() |
| 23 | 26 | ... | ... |
openbr/core/bee.cpp
| ... | ... | @@ -43,9 +43,15 @@ FileList BEE::readSigset(const File &sigset, bool ignoreMetadata) |
| 43 | 43 | QFile file(sigset.resolved()); |
| 44 | 44 | bool success; |
| 45 | 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 | 48 | file.close(); |
| 48 | 49 | |
| 50 | + if (!success) { | |
| 51 | + qWarning("Unable to parse %s.", qPrintable(sigset)); | |
| 52 | + return fileList; | |
| 53 | + } | |
| 54 | + | |
| 49 | 55 | QDomElement docElem = doc.documentElement(); |
| 50 | 56 | if (docElem.nodeName() != "biometric-signature-set") |
| 51 | 57 | return fileList; | ... | ... |
openbr/core/core.cpp
| ... | ... | @@ -167,7 +167,6 @@ struct AlgorithmCore |
| 167 | 167 | } |
| 168 | 168 | |
| 169 | 169 | const float speed = 1000 * Globals->totalSteps / Globals->startTime.elapsed() / std::max(1, abs(Globals->parallelism)); |
| 170 | - qDebug() << Globals->startTime.elapsed(); | |
| 171 | 170 | if (!Globals->quiet && (Globals->totalSteps > 1)) |
| 172 | 171 | fprintf(stderr, "\rSPEED=%.1e SIZE=%.4g FAILURES=%d/%d \n", |
| 173 | 172 | speed, totalBytes/totalCount, failureCount, totalCount); | ... | ... |
openbr/core/plot.cpp
| ... | ... | @@ -247,8 +247,8 @@ float Evaluate(const Mat &simmat, const Mat &mask, const QString &csv) |
| 247 | 247 | } |
| 248 | 248 | |
| 249 | 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 | 253 | // Write SD & KDE |
| 254 | 254 | points = qMin(qMin(Max_Points, genuines.size()), impostors.size()); |
| ... | ... | @@ -269,7 +269,7 @@ float Evaluate(const Mat &simmat, const Mat &mask, const QString &csv) |
| 269 | 269 | } |
| 270 | 270 | |
| 271 | 271 | // Write Cumulative Match Characteristic (CMC) curve |
| 272 | - const int Max_Retrieval = 100; | |
| 272 | + const int Max_Retrieval = 200; | |
| 273 | 273 | const int Report_Retrieval = 5; |
| 274 | 274 | |
| 275 | 275 | float reportRetrievalRate = -1; |
| ... | ... | @@ -287,7 +287,7 @@ float Evaluate(const Mat &simmat, const Mat &mask, const QString &csv) |
| 287 | 287 | } |
| 288 | 288 | |
| 289 | 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 | 291 | return result; |
| 292 | 292 | } |
| 293 | 293 | ... | ... |
openbr/openbr_plugin.cpp
| ... | ... | @@ -368,7 +368,7 @@ TemplateList TemplateList::fromGallery(const br::File &gallery) |
| 368 | 368 | QScopedPointer<Gallery> i(Gallery::make(file)); |
| 369 | 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 | 372 | if (newTemplates.isEmpty()) |
| 373 | 373 | newTemplates.append(file); |
| 374 | 374 | |
| ... | ... | @@ -1193,6 +1193,33 @@ void Transform::backProject(const TemplateList &dst, TemplateList &src) const |
| 1193 | 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 | 1223 | /* Distance - public methods */ |
| 1197 | 1224 | Distance *Distance::make(QString str, QObject *parent) |
| 1198 | 1225 | { | ... | ... |
openbr/openbr_plugin.h
| ... | ... | @@ -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 | 1059 | * \brief Plugin base class for processing a template. |
| 1045 | 1060 | * |
| ... | ... | @@ -1144,6 +1159,17 @@ public: |
| 1144 | 1159 | */ |
| 1145 | 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 | 1173 | protected: |
| 1148 | 1174 | Transform(bool independent = true, bool trainable = true); /*!< \brief Construct a transform. */ |
| 1149 | 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 | 71 | * \ingroup transforms |
| 72 | 72 | * \brief Resize the template |
| 73 | 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 | 76 | class ResizeTransform : public UntrainableTransform |
| 76 | 77 | { |
| 77 | 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 | 90 | Q_PROPERTY(int rows READ get_rows WRITE set_rows RESET reset_rows STORED false) |
| 79 | 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 | 93 | BR_PROPERTY(int, rows, -1) |
| 81 | 94 | BR_PROPERTY(int, columns, -1) |
| 95 | + BR_PROPERTY(Method, method, Bilin) | |
| 82 | 96 | |
| 83 | 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 | 298 | |
| 299 | 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 | 330 | #include "distance.moc" | ... | ... |
openbr/plugins/format.cpp
| ... | ... | @@ -660,7 +660,7 @@ class xmlFormat : public Format |
| 660 | 660 | QFile f(fileName); |
| 661 | 661 | |
| 662 | 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 | 664 | f.close(); |
| 665 | 665 | |
| 666 | 666 | QDomElement docElem = doc.documentElement(); | ... | ... |
openbr/plugins/gui.cpp
| ... | ... | @@ -220,16 +220,16 @@ signals: |
| 220 | 220 | |
| 221 | 221 | BR_REGISTER(Transform, ShowTransform) |
| 222 | 222 | |
| 223 | -class FPSSynch : public TimeVaryingTransform | |
| 223 | +class FPSLimit : public TimeVaryingTransform | |
| 224 | 224 | { |
| 225 | 225 | Q_OBJECT |
| 226 | 226 | Q_PROPERTY(int targetFPS READ get_targetFPS WRITE set_targetFPS RESET reset_targetFPS STORED false) |
| 227 | 227 | BR_PROPERTY(int, targetFPS, 30) |
| 228 | 228 | |
| 229 | 229 | public: |
| 230 | - FPSSynch() : TimeVaryingTransform(false, false) {} | |
| 230 | + FPSLimit() : TimeVaryingTransform(false, false) {} | |
| 231 | 231 | |
| 232 | - ~FPSSynch() {} | |
| 232 | + ~FPSLimit() {} | |
| 233 | 233 | |
| 234 | 234 | void train(const TemplateList &data) { (void) data; } |
| 235 | 235 | |
| ... | ... | @@ -237,16 +237,18 @@ public: |
| 237 | 237 | void projectUpdate(const TemplateList &src, TemplateList &dst) |
| 238 | 238 | { |
| 239 | 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 | 246 | if (wait_time < 0) { |
| 246 | 247 | return; |
| 247 | 248 | } |
| 248 | 249 | |
| 249 | 250 | QThread::msleep(wait_time); |
| 251 | + last_time = timer.elapsed(); | |
| 250 | 252 | } |
| 251 | 253 | |
| 252 | 254 | void finalize(TemplateList & output) |
| ... | ... | @@ -256,15 +258,17 @@ public: |
| 256 | 258 | |
| 257 | 259 | void init() |
| 258 | 260 | { |
| 259 | - target_wait = 1000 / targetFPS; | |
| 261 | + target_wait = 1000.0 / targetFPS; | |
| 260 | 262 | timer.start(); |
| 263 | + last_time = timer.elapsed(); | |
| 261 | 264 | } |
| 262 | 265 | |
| 263 | 266 | protected: |
| 264 | 267 | QElapsedTimer timer; |
| 265 | 268 | qint64 target_wait; |
| 269 | + qint64 last_time; | |
| 266 | 270 | }; |
| 267 | -BR_REGISTER(Transform, FPSSynch) | |
| 271 | +BR_REGISTER(Transform, FPSLimit) | |
| 268 | 272 | |
| 269 | 273 | class FPSCalc : public TimeVaryingTransform |
| 270 | 274 | { | ... | ... |
openbr/plugins/landmarks.cpp
| ... | ... | @@ -21,6 +21,9 @@ class ProcrustesTransform : public Transform |
| 21 | 21 | { |
| 22 | 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 | 27 | Eigen::MatrixXf meanShape; |
| 25 | 28 | |
| 26 | 29 | void train(const TemplateList &data) |
| ... | ... | @@ -30,23 +33,33 @@ class ProcrustesTransform : public Transform |
| 30 | 33 | // Normalize all sets of points |
| 31 | 34 | foreach (br::Template datum, data) { |
| 32 | 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 | 48 | for (int i = 0; i < points.size(); i++) points[i] -= QPointF(mean[0],mean[1]); |
| 38 | 49 | |
| 50 | + // Remove scale component | |
| 39 | 51 | float norm = cv::norm(OpenCVUtils::toPoints(points).toVector().toStdVector()); |
| 40 | 52 | for (int i = 0; i < points.size(); i++) points[i] /= norm; |
| 41 | 53 | |
| 42 | 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 | 63 | double x = 0; |
| 51 | 64 | double y = 0; |
| 52 | 65 | |
| ... | ... | @@ -58,39 +71,58 @@ class ProcrustesTransform : public Transform |
| 58 | 71 | x /= (double)normalizedPoints.size(); |
| 59 | 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 | 79 | void project(const Template &src, Template &dst) const |
| 69 | 80 | { |
| 70 | 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 | 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 | 100 | float norm = cv::norm(OpenCVUtils::toPoints(points).toVector().toStdVector()); |
| 76 | - Eigen::MatrixXf srcPoints(points.size(), 2); | |
| 77 | - | |
| 78 | 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 | 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 | 128 | void store(QDataStream &stream) const |
| ... | ... | @@ -109,60 +141,202 @@ BR_REGISTER(Transform, ProcrustesTransform) |
| 109 | 141 | |
| 110 | 142 | /*! |
| 111 | 143 | * \ingroup transforms |
| 112 | - * \brief Wraps STASM key point detector | |
| 144 | + * \brief Creates a Delaunay triangulation based on a set of points | |
| 113 | 145 | * \author Scott Klum \cite sklum |
| 114 | 146 | */ |
| 115 | -class DelauneyTransform : public UntrainableTransform | |
| 147 | +class DelaunayTransform : public UntrainableTransform | |
| 116 | 148 | { |
| 117 | 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 | 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 | 160 | BR_PROPERTY(bool, draw, false) |
| 121 | 161 | |
| 122 | 162 | void project(const Template &src, Template &dst) const |
| 123 | 163 | { |
| 124 | - dst = src; | |
| 125 | - | |
| 126 | 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 | 190 | vector<Vec6f> triangleList; |
| 131 | 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 | 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 | 341 | } // namespace br |
| 168 | 342 | ... | ... |
openbr/plugins/misc.cpp
| ... | ... | @@ -35,6 +35,7 @@ class OpenTransform : public UntrainableMetaTransform |
| 35 | 35 | |
| 36 | 36 | void project(const Template &src, Template &dst) const |
| 37 | 37 | { |
| 38 | + if (!src.isEmpty()) { dst = src; return; } | |
| 38 | 39 | if (Globals->verbose) qDebug("Opening %s", qPrintable(src.file.flat())); |
| 39 | 40 | dst.file = src.file; |
| 40 | 41 | foreach (const File &file, src.file.split()) { |
| ... | ... | @@ -561,6 +562,28 @@ class ExpandRectTransform : public UntrainableTransform |
| 561 | 562 | |
| 562 | 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 | 589 | #include "misc.moc" | ... | ... |
openbr/plugins/pp5.cpp
| ... | ... | @@ -97,8 +97,8 @@ struct PP5Context |
| 97 | 97 | |
| 98 | 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 | 102 | else if (src.channels() == 1) ppr_raw_image_create(&dst, src.cols, src.rows, PPR_RAW_IMAGE_GRAY8); |
| 103 | 103 | else qFatal("PP5Context::createRawImage invalid channel count."); |
| 104 | 104 | memcpy(dst.data, src.data, src.channels()*src.rows*src.cols); |
| ... | ... | @@ -241,41 +241,43 @@ class PP5EnrollTransform : public UntrainableMetaTransform |
| 241 | 241 | PP5Context *context = contexts.acquire(); |
| 242 | 242 | |
| 243 | 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 | 283 | // No faces were detected, output something with FTE set. |
| ... | ... | @@ -305,10 +307,14 @@ class PP5CompareDistance : public Distance |
| 305 | 307 | |
| 306 | 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 | 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 | 95 | for (int i=0; i<src.size(); i++) |
| 96 | 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 | 102 | QVector<int> offsets(partitions, 0); |
| 102 | 103 | for (int i=0; i<src.size(); i++) { |
| ... | ... | @@ -230,6 +231,7 @@ class RectFromPointsTransform : public UntrainableTransform |
| 230 | 231 | if (src.file.points()[index].y() > maxY) maxY = src.file.points()[index].y(); |
| 231 | 232 | points.append(src.file.points()[index]); |
| 232 | 233 | } |
| 234 | + else qFatal("Incorrect indices"); | |
| 233 | 235 | } |
| 234 | 236 | |
| 235 | 237 | double width = maxX-minX; |
| ... | ... | @@ -238,12 +240,15 @@ class RectFromPointsTransform : public UntrainableTransform |
| 238 | 240 | |
| 239 | 241 | double height = maxY-minY; |
| 240 | 242 | double deltaHeight = width/aspectRatio - height; |
| 241 | - height += deltaHeight; | |
| 243 | + height += deltaHeight; | |
| 242 | 244 | |
| 243 | 245 | dst.file.setPoints(points); |
| 244 | 246 | |
| 245 | 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 | 28 | * \ingroup transforms |
| 29 | 29 | * \brief Performs a two or three point registration. |
| 30 | 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 | 33 | class AffineTransform : public UntrainableTransform |
| 33 | 34 | { |
| 34 | 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 | 47 | Q_PROPERTY(int width READ get_width WRITE set_width RESET reset_width STORED false) |
| 36 | 48 | Q_PROPERTY(int height READ get_height WRITE set_height RESET reset_height STORED false) |
| 37 | 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 | 52 | Q_PROPERTY(float y2 READ get_y2 WRITE set_y2 RESET reset_y2 STORED false) |
| 41 | 53 | Q_PROPERTY(float x3 READ get_x3 WRITE set_x3 RESET reset_x3 STORED false) |
| 42 | 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 | 56 | BR_PROPERTY(int, width, 64) |
| 44 | 57 | BR_PROPERTY(int, height, 64) |
| 45 | 58 | BR_PROPERTY(float, x1, 0) |
| ... | ... | @@ -48,6 +61,7 @@ class AffineTransform : public UntrainableTransform |
| 48 | 61 | BR_PROPERTY(float, y2, -1) |
| 49 | 62 | BR_PROPERTY(float, x3, -1) |
| 50 | 63 | BR_PROPERTY(float, y3, -1) |
| 64 | + BR_PROPERTY(Method, method, Bilin) | |
| 51 | 65 | |
| 52 | 66 | static Point2f getThirdAffinePoint(const Point2f &a, const Point2f &b) |
| 53 | 67 | { |
| ... | ... | @@ -72,9 +86,6 @@ class AffineTransform : public UntrainableTransform |
| 72 | 86 | (src.file.contains("Affine_2") || twoPoints)) { |
| 73 | 87 | srcPoints[0] = OpenCVUtils::toPoint(src.file.get<QPointF>("Affine_0")); |
| 74 | 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 | 89 | if (!twoPoints) srcPoints[2] = OpenCVUtils::toPoint(src.file.get<QPointF>("Affine_2")); |
| 79 | 90 | } else { |
| 80 | 91 | const QList<Point2f> landmarks = OpenCVUtils::toPoints(src.file.points()); |
| ... | ... | @@ -89,13 +100,12 @@ class AffineTransform : public UntrainableTransform |
| 89 | 100 | |
| 90 | 101 | dst.file.set("Affine_0", OpenCVUtils::fromPoint(landmarks[0])); |
| 91 | 102 | dst.file.set("Affine_1", OpenCVUtils::fromPoint(landmarks[1])); |
| 92 | - | |
| 93 | 103 | if (!twoPoints) dst.file.set("Affine_2", OpenCVUtils::fromPoint(landmarks[2])); |
| 94 | 104 | } |
| 95 | 105 | } |
| 96 | 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 | 38 | { |
| 39 | 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 | 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 | 241 | } |
| 242 | 242 | |
| 243 | 243 | virtual void close() = 0; |
| 244 | - virtual bool open(Template & output) = 0; | |
| 244 | + virtual bool open(Template & output, int start_index=0) = 0; | |
| 245 | 245 | virtual bool isOpen() = 0; |
| 246 | 246 | |
| 247 | 247 | virtual bool getNext(FrameData & input) = 0; |
| ... | ... | @@ -262,13 +262,13 @@ class VideoDataSource : public DataSource |
| 262 | 262 | public: |
| 263 | 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 | 267 | final_frame = -1; |
| 268 | 268 | last_issued = -2; |
| 269 | 269 | last_received = -3; |
| 270 | 270 | |
| 271 | - next_idx = 0; | |
| 271 | + next_idx = start_index; | |
| 272 | 272 | basis = input; |
| 273 | 273 | bool is_int = false; |
| 274 | 274 | int anInt = input.file.name.toInt(&is_int); |
| ... | ... | @@ -336,11 +336,11 @@ public: |
| 336 | 336 | } |
| 337 | 337 | bool data_ok; |
| 338 | 338 | |
| 339 | - bool open(Template &input) | |
| 339 | + bool open(Template &input, int start_index=0) | |
| 340 | 340 | { |
| 341 | 341 | basis = input; |
| 342 | 342 | current_idx = 0; |
| 343 | - next_sequence = 0; | |
| 343 | + next_sequence = start_index; | |
| 344 | 344 | final_frame = -1; |
| 345 | 345 | last_issued = -2; |
| 346 | 346 | last_received = -3; |
| ... | ... | @@ -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 | 419 | close(); |
| 412 | 420 | final_frame = -1; |
| 413 | 421 | last_issued = -2; |
| 414 | 422 | last_received = -3; |
| 423 | + next_frame = start_index; | |
| 415 | 424 | |
| 416 | 425 | // Input has no matrices? Its probably a video that hasn't been loaded yet |
| 417 | 426 | if (input.empty()) { |
| 418 | 427 | actualSource = new VideoDataSource(0); |
| 419 | - actualSource->open(input); | |
| 428 | + actualSource->open(input, next_frame); | |
| 420 | 429 | } |
| 421 | 430 | else { |
| 422 | 431 | // create frame dealer |
| 423 | 432 | actualSource = new TemplateDataSource(0); |
| 424 | - actualSource->open(input); | |
| 433 | + actualSource->open(input, next_frame); | |
| 425 | 434 | } |
| 426 | 435 | if (!isOpen()) { |
| 427 | 436 | delete actualSource; |
| ... | ... | @@ -434,10 +443,31 @@ public: |
| 434 | 443 | bool isOpen() { return !actualSource ? false : actualSource->isOpen(); } |
| 435 | 444 | |
| 436 | 445 | protected: |
| 446 | + int currentIdx; | |
| 447 | + int next_frame; | |
| 448 | + TemplateList templates; | |
| 437 | 449 | DataSource * actualSource; |
| 438 | 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 | 826 | // start processing |
| 797 | 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 | 829 | dst = src; |
| 803 | - bool res = readStage->dataSource.open(dst[0]); | |
| 830 | + | |
| 831 | + bool res = readStage->dataSource.open(dst); | |
| 804 | 832 | if (!res) return; |
| 805 | 833 | |
| 806 | 834 | QThreadPool::globalInstance()->releaseThread(); | ... | ... |
share/openbr/cmake/InstallDependencies.cmake
| ... | ... | @@ -10,7 +10,8 @@ function(install_opencv_library lib) |
| 10 | 10 | if(NOT MSVC) |
| 11 | 11 | set(BR_INSTALL_DEPENDENCIES_PREFIX "lib") |
| 12 | 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 | 15 | else() |
| 15 | 16 | set(OpenCV_LIB_DIR "/usr/local/lib") |
| 16 | 17 | install(FILES ${OpenCV_LIB_DIR}/lib${lib}.${OpenCV_VERSION_MAJOR}.${OpenCV_VERSION_MINOR}.${OpenCV_VERSION_PATCH}${CMAKE_SHARED_LIBRARY_SUFFIX} DESTINATION lib) | ... | ... |