Commit 27a5fc9bd8a19f313d9eaca35c1fdefa0ee6117e
Merge branch 'master' of https://github.com/biometrics/openbr into streamlining
Conflicts: openbr/core/core.cpp
Showing
16 changed files
with
434 additions
and
96 deletions
app/br/br.cpp
| @@ -171,7 +171,11 @@ public: | @@ -171,7 +171,11 @@ public: | ||
| 171 | check(parc == 1, "Incorrect parameter count for 'daemon'."); | 171 | check(parc == 1, "Incorrect parameter count for 'daemon'."); |
| 172 | daemon = true; | 172 | daemon = true; |
| 173 | daemon_pipe = parv[0]; | 173 | daemon_pipe = parv[0]; |
| 174 | - } else if (!strcmp(fun, "exit")) { | 174 | + } else if (!strcmp(fun,"slave")) { |
| 175 | + check(parc == 1, "Incorrect parameter count for 'slave'"); | ||
| 176 | + br_slave_process(parv[0]); | ||
| 177 | + } | ||
| 178 | + else if (!strcmp(fun, "exit")) { | ||
| 175 | check(parc == 0, "No parameters expected for 'exit'."); | 179 | check(parc == 0, "No parameters expected for 'exit'."); |
| 176 | daemon = false; | 180 | daemon = false; |
| 177 | } else if (!strcmp(fun, "getHeader")) { | 181 | } else if (!strcmp(fun, "getHeader")) { |
openbr/core/core.cpp
| @@ -138,8 +138,6 @@ struct AlgorithmCore | @@ -138,8 +138,6 @@ struct AlgorithmCore | ||
| 138 | 138 | ||
| 139 | downcast->transforms[0] = this->transform.data(); | 139 | downcast->transforms[0] = this->transform.data(); |
| 140 | downcast->init(); | 140 | downcast->init(); |
| 141 | - | ||
| 142 | - | ||
| 143 | downcast->projectUpdate(i,i); | 141 | downcast->projectUpdate(i,i); |
| 144 | 142 | ||
| 145 | return i.files(); | 143 | return i.files(); |
openbr/core/qtutils.cpp
| @@ -399,33 +399,31 @@ void showFile(const QString &file) | @@ -399,33 +399,31 @@ void showFile(const QString &file) | ||
| 399 | 399 | ||
| 400 | QString toString(const QVariant &variant) | 400 | QString toString(const QVariant &variant) |
| 401 | { | 401 | { |
| 402 | - if (variant.canConvert(QVariant::String)) | ||
| 403 | - return variant.toString(); | ||
| 404 | - else if(variant.canConvert(QVariant::PointF)) { | ||
| 405 | - QPointF pt = qvariant_cast<QPointF>(variant); | ||
| 406 | - return QString("(%1,%2)").arg(QString::number(pt.x()), | ||
| 407 | - QString::number(pt.y())); | ||
| 408 | - } | ||
| 409 | - else if (variant.canConvert(QVariant::RectF)) { | 402 | + if (variant.canConvert(QVariant::List)) return toString(qvariant_cast<QVariantList>(variant)); |
| 403 | + else if (variant.canConvert(QVariant::String)) return variant.toString(); | ||
| 404 | + else if (variant.canConvert(QVariant::PointF)) { | ||
| 405 | + QPointF point = qvariant_cast<QPointF>(variant); | ||
| 406 | + return QString("(%1,%2)").arg(QString::number(point.x()),QString::number(point.y())); | ||
| 407 | + } else if (variant.canConvert(QVariant::RectF)) { | ||
| 410 | QRectF rect = qvariant_cast<QRectF>(variant); | 408 | QRectF rect = qvariant_cast<QRectF>(variant); |
| 411 | return QString("(%1,%2,%3,%4)").arg(QString::number(rect.x()), | 409 | return QString("(%1,%2,%3,%4)").arg(QString::number(rect.x()), |
| 412 | QString::number(rect.y()), | 410 | QString::number(rect.y()), |
| 413 | QString::number(rect.width()), | 411 | QString::number(rect.width()), |
| 414 | QString::number(rect.height())); | 412 | QString::number(rect.height())); |
| 415 | } | 413 | } |
| 416 | - else if (variant.canConvert(QVariant::List)) { | ||
| 417 | - QString ret = QString("["); | ||
| 418 | - bool first = true; | ||
| 419 | - foreach (const QVariant &i, variant.toList()) { | ||
| 420 | - if (!first) | ||
| 421 | - ret += ","; | ||
| 422 | - else | ||
| 423 | - first = false; | ||
| 424 | - ret += toString(i); | ||
| 425 | - } | ||
| 426 | - ret += "]"; | ||
| 427 | - return ret; | ||
| 428 | - } | 414 | + |
| 415 | + return QString(); | ||
| 416 | +} | ||
| 417 | + | ||
| 418 | +QString toString(const QVariantList &variantList) | ||
| 419 | +{ | ||
| 420 | + QStringList variants; | ||
| 421 | + | ||
| 422 | + foreach(const QVariant &variant, variantList) | ||
| 423 | + variants.append(toString(variant)); | ||
| 424 | + | ||
| 425 | + if (!variants.isEmpty()) return "[" + variants.join(", ") + "]"; | ||
| 426 | + | ||
| 429 | return QString(); | 427 | return QString(); |
| 430 | } | 428 | } |
| 431 | 429 |
openbr/core/qtutils.h
| @@ -74,6 +74,7 @@ namespace QtUtils | @@ -74,6 +74,7 @@ namespace QtUtils | ||
| 74 | 74 | ||
| 75 | /**** Variant Utilities ****/ | 75 | /**** Variant Utilities ****/ |
| 76 | QString toString(const QVariant &variant); | 76 | QString toString(const QVariant &variant); |
| 77 | + QString toString(const QVariantList &variantList); | ||
| 77 | 78 | ||
| 78 | template <typename T> | 79 | template <typename T> |
| 79 | QVariantList toVariantList(const QList<T> &list) | 80 | QVariantList toVariantList(const QList<T> &list) |
openbr/openbr.cpp
| @@ -22,6 +22,7 @@ | @@ -22,6 +22,7 @@ | ||
| 22 | #include "core/fuse.h" | 22 | #include "core/fuse.h" |
| 23 | #include "core/plot.h" | 23 | #include "core/plot.h" |
| 24 | #include "core/qtutils.h" | 24 | #include "core/qtutils.h" |
| 25 | +#include "plugins/openbr_internal.h" | ||
| 25 | 26 | ||
| 26 | using namespace br; | 27 | using namespace br; |
| 27 | 28 | ||
| @@ -279,3 +280,11 @@ const char *br_version() | @@ -279,3 +280,11 @@ const char *br_version() | ||
| 279 | static QByteArray version = Context::version().toLocal8Bit(); | 280 | static QByteArray version = Context::version().toLocal8Bit(); |
| 280 | return version.data(); | 281 | return version.data(); |
| 281 | } | 282 | } |
| 283 | + | ||
| 284 | +void br_slave_process(const char * baseName) | ||
| 285 | +{ | ||
| 286 | + WorkerProcess worker; | ||
| 287 | + worker.transform = Globals->algorithm; | ||
| 288 | + worker.baseName = baseName; | ||
| 289 | + worker.mainLoop(); | ||
| 290 | +} |
openbr/openbr.h
| @@ -408,6 +408,12 @@ BR_EXPORT void br_train_n(int num_inputs, const char *inputs[], const char *mode | @@ -408,6 +408,12 @@ BR_EXPORT void br_train_n(int num_inputs, const char *inputs[], const char *mode | ||
| 408 | */ | 408 | */ |
| 409 | BR_EXPORT const char *br_version(); | 409 | BR_EXPORT const char *br_version(); |
| 410 | 410 | ||
| 411 | + | ||
| 412 | +/*! | ||
| 413 | + * \brief For internal use via ProcessWrapperTransform | ||
| 414 | + */ | ||
| 415 | +BR_EXPORT void br_slave_process(const char * baseKey); | ||
| 416 | + | ||
| 411 | /*! @}*/ | 417 | /*! @}*/ |
| 412 | 418 | ||
| 413 | #ifdef __cplusplus | 419 | #ifdef __cplusplus |
openbr/openbr_plugin.cpp
| @@ -17,6 +17,7 @@ | @@ -17,6 +17,7 @@ | ||
| 17 | #include <QCoreApplication> | 17 | #include <QCoreApplication> |
| 18 | #include <QCryptographicHash> | 18 | #include <QCryptographicHash> |
| 19 | #include <QFutureSynchronizer> | 19 | #include <QFutureSynchronizer> |
| 20 | +#include <QLocalSocket> | ||
| 20 | #include <QMetaProperty> | 21 | #include <QMetaProperty> |
| 21 | #include <QPointF> | 22 | #include <QPointF> |
| 22 | #include <QProcess> | 23 | #include <QProcess> |
| @@ -58,16 +59,7 @@ QString File::flat() const | @@ -58,16 +59,7 @@ QString File::flat() const | ||
| 58 | foreach (const QString &key, keys) { | 59 | foreach (const QString &key, keys) { |
| 59 | const QVariant value = this->value(key); | 60 | const QVariant value = this->value(key); |
| 60 | if (value.isNull()) values.append(key); | 61 | if (value.isNull()) values.append(key); |
| 61 | - else { | ||
| 62 | - if (QString(value.typeName()) == "QVariantList") { | ||
| 63 | - QStringList variants; | ||
| 64 | - foreach(const QVariant &variant, qvariant_cast<QVariantList>(value)) { | ||
| 65 | - variants.append(QtUtils::toString(variant)); | ||
| 66 | - } | ||
| 67 | - if (!variants.isEmpty()) values.append(key + "=[" + variants.join(", ") + "]"); | ||
| 68 | - } | ||
| 69 | - else values.append(key + "=" + QtUtils::toString(value)); | ||
| 70 | - } | 62 | + else values.append(key + "=" + QtUtils::toString(value)); |
| 71 | } | 63 | } |
| 72 | 64 | ||
| 73 | QString flat = name; | 65 | QString flat = name; |
| @@ -942,6 +934,8 @@ void br::Context::initialize(int &argc, char *argv[], QString sdkPath, bool use_ | @@ -942,6 +934,8 @@ void br::Context::initialize(int &argc, char *argv[], QString sdkPath, bool use_ | ||
| 942 | qRegisterMetaType< QList<float> >(); | 934 | qRegisterMetaType< QList<float> >(); |
| 943 | qRegisterMetaType< QList<br::Transform*> >(); | 935 | qRegisterMetaType< QList<br::Transform*> >(); |
| 944 | qRegisterMetaType< QList<br::Distance*> >(); | 936 | qRegisterMetaType< QList<br::Distance*> >(); |
| 937 | + qRegisterMetaType< QAbstractSocket::SocketState> (); | ||
| 938 | + | ||
| 945 | 939 | ||
| 946 | Globals = new Context(); | 940 | Globals = new Context(); |
| 947 | Globals->init(File()); | 941 | Globals->init(File()); |
openbr/plugins/eigen3.cpp
| @@ -493,7 +493,7 @@ class LDATransform : public Transform | @@ -493,7 +493,7 @@ class LDATransform : public Transform | ||
| 493 | outMap = projection.transpose() * (inMap - mean); | 493 | outMap = projection.transpose() * (inMap - mean); |
| 494 | 494 | ||
| 495 | if (isBinary) { | 495 | if (isBinary) { |
| 496 | - dst.file.set("conf",dst.m().at<float>(0,0)); | 496 | + dst.file.set("Confidence",dst.m().at<float>(0,0)); |
| 497 | } | 497 | } |
| 498 | } | 498 | } |
| 499 | 499 |
openbr/plugins/openbr_internal.h
| @@ -240,6 +240,18 @@ protected: | @@ -240,6 +240,18 @@ protected: | ||
| 240 | CompositeTransform() : TimeVaryingTransform(false) {} | 240 | CompositeTransform() : TimeVaryingTransform(false) {} |
| 241 | }; | 241 | }; |
| 242 | 242 | ||
| 243 | +class EnrollmentWorker; | ||
| 244 | + | ||
| 245 | +// Implemented in plugins/process.cpp | ||
| 246 | +struct WorkerProcess | ||
| 247 | +{ | ||
| 248 | + QString transform; | ||
| 249 | + QString baseName; | ||
| 250 | + EnrollmentWorker * processInterface; | ||
| 251 | + | ||
| 252 | + void mainLoop(); | ||
| 253 | +}; | ||
| 254 | + | ||
| 243 | } | 255 | } |
| 244 | 256 | ||
| 245 | #endif // OPENBR_INTERNAL_H | 257 | #endif // OPENBR_INTERNAL_H |
openbr/plugins/process.cpp
0 โ 100644
| 1 | + | ||
| 2 | +#include <QBuffer> | ||
| 3 | +#include <QLocalServer> | ||
| 4 | +#include <QLocalSocket> | ||
| 5 | +#include <QProcess> | ||
| 6 | +#include <QUuid> | ||
| 7 | + | ||
| 8 | +#include <iostream> | ||
| 9 | +#include <fstream> | ||
| 10 | + | ||
| 11 | +#include "openbr_internal.h" | ||
| 12 | +#include "openbr/core/opencvutils.h" | ||
| 13 | + | ||
| 14 | +using namespace cv; | ||
| 15 | + | ||
| 16 | +namespace br | ||
| 17 | +{ | ||
| 18 | + | ||
| 19 | +enum SignalType | ||
| 20 | +{ | ||
| 21 | + INPUT_AVAILABLE, | ||
| 22 | + OUTPUT_AVAILABLE, | ||
| 23 | + SHOULD_END | ||
| 24 | +}; | ||
| 25 | + | ||
| 26 | +class EnrollmentWorker | ||
| 27 | +{ | ||
| 28 | +public: | ||
| 29 | + QLocalServer inbound; | ||
| 30 | + QLocalSocket outbound; | ||
| 31 | + QLocalSocket * receiver; | ||
| 32 | + | ||
| 33 | + ~EnrollmentWorker() | ||
| 34 | + { | ||
| 35 | + delete transform; | ||
| 36 | + } | ||
| 37 | + | ||
| 38 | + br::Transform * transform; | ||
| 39 | + | ||
| 40 | + void connections(const QString & baseName) | ||
| 41 | + { | ||
| 42 | + inbound.listen(baseName+"_worker"); | ||
| 43 | + outbound.connectToServer(baseName+"_master"); | ||
| 44 | + inbound.waitForNewConnection(-1); | ||
| 45 | + receiver = inbound.nextPendingConnection(); | ||
| 46 | + outbound.waitForConnected(-1); | ||
| 47 | + } | ||
| 48 | + | ||
| 49 | + void workerLoop() | ||
| 50 | + { | ||
| 51 | + SignalType signal; | ||
| 52 | + | ||
| 53 | + forever | ||
| 54 | + { | ||
| 55 | + while (receiver->bytesAvailable() < qint64(sizeof(signal))) { | ||
| 56 | + receiver->waitForReadyRead(-1); | ||
| 57 | + } | ||
| 58 | + receiver->read((char *) &signal, sizeof(signal)); | ||
| 59 | + | ||
| 60 | + if (signal == SHOULD_END) { | ||
| 61 | + outbound.close(); | ||
| 62 | + inbound.close(); | ||
| 63 | + break; | ||
| 64 | + } | ||
| 65 | + | ||
| 66 | + qint64 inBufferSize; | ||
| 67 | + while (receiver->bytesAvailable() < qint64(sizeof(inBufferSize))) { | ||
| 68 | + receiver->waitForReadyRead(-1); | ||
| 69 | + } | ||
| 70 | + receiver->read((char *) &inBufferSize, sizeof(inBufferSize)); | ||
| 71 | + | ||
| 72 | + QByteArray inArray(inBufferSize,'0'); | ||
| 73 | + | ||
| 74 | + qint64 arrayPosition = 0; | ||
| 75 | + while (arrayPosition < inBufferSize) { | ||
| 76 | + if (!receiver->bytesAvailable()) | ||
| 77 | + receiver->waitForReadyRead(-1); | ||
| 78 | + arrayPosition += receiver->read(inArray.data()+arrayPosition, receiver->bytesAvailable()); | ||
| 79 | + } | ||
| 80 | + | ||
| 81 | + TemplateList inList; | ||
| 82 | + TemplateList outList; | ||
| 83 | + // deserialize the template list | ||
| 84 | + QDataStream deserializer(inArray); | ||
| 85 | + deserializer >> inList; | ||
| 86 | + | ||
| 87 | + // and project it | ||
| 88 | + transform->projectUpdate(inList,outList); | ||
| 89 | + | ||
| 90 | + // serialize the output list | ||
| 91 | + QBuffer outBuff; | ||
| 92 | + outBuff.open(QBuffer::ReadWrite); | ||
| 93 | + QDataStream serializer(&outBuff); | ||
| 94 | + serializer << outList; | ||
| 95 | + | ||
| 96 | + // send the size of the buffer | ||
| 97 | + //qint64 bufferSize = outBuff.size(); | ||
| 98 | + qint64 bufferSize = outBuff.data().size(); | ||
| 99 | + outbound.write((char *) &bufferSize, sizeof(bufferSize)); | ||
| 100 | + | ||
| 101 | + outbound.write(outBuff.data().data(), bufferSize); | ||
| 102 | + while (outbound.bytesToWrite() > 0) { | ||
| 103 | + outbound.waitForBytesWritten(-1); | ||
| 104 | + } | ||
| 105 | + } | ||
| 106 | + } | ||
| 107 | +}; | ||
| 108 | + | ||
| 109 | +void WorkerProcess::mainLoop() | ||
| 110 | +{ | ||
| 111 | + processInterface = new EnrollmentWorker(); | ||
| 112 | + processInterface->transform = Transform::make(this->transform,NULL); | ||
| 113 | + processInterface->connections(baseName); | ||
| 114 | + processInterface->workerLoop(); | ||
| 115 | + delete processInterface; | ||
| 116 | +} | ||
| 117 | + | ||
| 118 | +/*! | ||
| 119 | + * \ingroup transforms | ||
| 120 | + * \brief Interface to a separate process | ||
| 121 | + * \author Charles Otto \cite caotto | ||
| 122 | + */ | ||
| 123 | +class ProcessWrapperTransform : public TimeVaryingTransform | ||
| 124 | +{ | ||
| 125 | + Q_OBJECT | ||
| 126 | + | ||
| 127 | + Q_PROPERTY(QString transform READ get_transform WRITE set_transform RESET reset_transform) | ||
| 128 | + BR_PROPERTY(QString, transform, "") | ||
| 129 | + | ||
| 130 | + QString baseKey; | ||
| 131 | + QProcess workerProcess; | ||
| 132 | + | ||
| 133 | + QLocalServer inbound; | ||
| 134 | + QLocalSocket outbound; | ||
| 135 | + QLocalSocket * receiver; | ||
| 136 | + | ||
| 137 | + void projectUpdate(const TemplateList &src, TemplateList &dst) | ||
| 138 | + { | ||
| 139 | + if (!processActive) | ||
| 140 | + { | ||
| 141 | + activateProcess(); | ||
| 142 | + } | ||
| 143 | + | ||
| 144 | + SignalType signal = INPUT_AVAILABLE; | ||
| 145 | + outbound.write((char *) &signal, sizeof(SignalType)); | ||
| 146 | + | ||
| 147 | + QBuffer inBuffer; | ||
| 148 | + inBuffer.open(QBuffer::ReadWrite); | ||
| 149 | + QDataStream serializer(&inBuffer); | ||
| 150 | + serializer << src; | ||
| 151 | + | ||
| 152 | + qint64 in_size = inBuffer.size(); | ||
| 153 | + outbound.write((char *) &in_size, sizeof(in_size)); | ||
| 154 | + | ||
| 155 | + outbound.write(inBuffer.data(), in_size); | ||
| 156 | + | ||
| 157 | + while (outbound.bytesToWrite() > 0) { | ||
| 158 | + outbound.waitForBytesWritten(-1); | ||
| 159 | + } | ||
| 160 | + | ||
| 161 | + qint64 out_size; | ||
| 162 | + | ||
| 163 | + // read the size | ||
| 164 | + receiver->waitForReadyRead(-1); | ||
| 165 | + receiver->read((char *) &out_size, sizeof(out_size)); | ||
| 166 | + QByteArray outBuffer(out_size,'0'); | ||
| 167 | + | ||
| 168 | + // read the (serialized) output templatelist | ||
| 169 | + qint64 arrayPosition = 0; | ||
| 170 | + while (arrayPosition < out_size) { | ||
| 171 | + if (!receiver->bytesAvailable()) | ||
| 172 | + receiver->waitForReadyRead(-1); | ||
| 173 | + arrayPosition += receiver->read(outBuffer.data()+arrayPosition, receiver->bytesAvailable()); | ||
| 174 | + } | ||
| 175 | + // and deserialize it. | ||
| 176 | + QDataStream deserialize(outBuffer); | ||
| 177 | + deserialize >> dst; | ||
| 178 | + } | ||
| 179 | + | ||
| 180 | + | ||
| 181 | + void train(const TemplateList& data) | ||
| 182 | + { | ||
| 183 | + (void) data; | ||
| 184 | + } | ||
| 185 | + | ||
| 186 | + // create the process | ||
| 187 | + void init() | ||
| 188 | + { | ||
| 189 | + processActive = false; | ||
| 190 | + } | ||
| 191 | + | ||
| 192 | + void activateProcess() | ||
| 193 | + { | ||
| 194 | + processActive = true; | ||
| 195 | + | ||
| 196 | + // generate a uuid for our local servers | ||
| 197 | + QUuid id = QUuid::createUuid(); | ||
| 198 | + baseKey = id.toString(); | ||
| 199 | + | ||
| 200 | + QStringList argumentList; | ||
| 201 | + argumentList.append("-useGui"); | ||
| 202 | + argumentList.append("0"); | ||
| 203 | + argumentList.append("-algorithm"); | ||
| 204 | + argumentList.append(transform); | ||
| 205 | + argumentList.append("-path"); | ||
| 206 | + argumentList.append(Globals->path); | ||
| 207 | + argumentList.append("-parallelism"); | ||
| 208 | + argumentList.append(QString::number(0)); | ||
| 209 | + argumentList.append("-slave"); | ||
| 210 | + argumentList.append(baseKey); | ||
| 211 | + | ||
| 212 | + // start listening | ||
| 213 | + inbound.listen(baseKey+"_master"); | ||
| 214 | + | ||
| 215 | + workerProcess.setProcessChannelMode(QProcess::ForwardedChannels); | ||
| 216 | + workerProcess.start("br", argumentList); | ||
| 217 | + workerProcess.waitForStarted(-1); | ||
| 218 | + | ||
| 219 | + // blocking wait for the connection from the worker process | ||
| 220 | + inbound.waitForNewConnection(-1); | ||
| 221 | + receiver = inbound.nextPendingConnection(); | ||
| 222 | + | ||
| 223 | + // Now, create our connection to the worker process. | ||
| 224 | + outbound.connectToServer(baseKey+"_worker"); | ||
| 225 | + outbound.waitForConnected(-1); | ||
| 226 | + } | ||
| 227 | + | ||
| 228 | + bool timeVarying() const { | ||
| 229 | + return false; | ||
| 230 | + } | ||
| 231 | + | ||
| 232 | + ~ProcessWrapperTransform() | ||
| 233 | + { | ||
| 234 | + // end the process | ||
| 235 | + if (this->processActive) { | ||
| 236 | + | ||
| 237 | + SignalType signal = SHOULD_END; | ||
| 238 | + outbound.write((char *) &signal, sizeof(SignalType)); | ||
| 239 | + outbound.waitForBytesWritten(-1); | ||
| 240 | + outbound.close(); | ||
| 241 | + | ||
| 242 | + workerProcess.waitForFinished(-1); | ||
| 243 | + inbound.close(); | ||
| 244 | + processActive = false; | ||
| 245 | + } | ||
| 246 | + } | ||
| 247 | + | ||
| 248 | +public: | ||
| 249 | + bool processActive; | ||
| 250 | + ProcessWrapperTransform() : TimeVaryingTransform(false,false) { processActive = false; } | ||
| 251 | +}; | ||
| 252 | + | ||
| 253 | +BR_REGISTER(Transform, ProcessWrapperTransform) | ||
| 254 | + | ||
| 255 | +} | ||
| 256 | + | ||
| 257 | +#include "process.moc" |
openbr/plugins/regions.cpp
| @@ -345,9 +345,12 @@ class RectFromPointsTransform : public UntrainableTransform | @@ -345,9 +345,12 @@ class RectFromPointsTransform : public UntrainableTransform | ||
| 345 | 345 | ||
| 346 | dst.file.setPoints(points); | 346 | dst.file.setPoints(points); |
| 347 | 347 | ||
| 348 | - 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))); | 348 | + const int x = std::max(0.0, minX - deltaWidth/2.0); |
| 349 | + const int y = std::max(0.0, minY - deltaHeight/2.0); | ||
| 350 | + | ||
| 351 | + if (crop) dst.m() = src.m()(Rect(x, y, std::min((double)src.m().cols-x, width), std::min((double)src.m().rows-y, height))); | ||
| 349 | else { | 352 | else { |
| 350 | - 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))); | 353 | + dst.file.appendRect(QRectF(x, y, std::min((double)src.m().cols-x, width), std::min((double)src.m().rows-y, height))); |
| 351 | dst.m() = src.m(); | 354 | dst.m() = src.m(); |
| 352 | } | 355 | } |
| 353 | } | 356 | } |
openbr/plugins/slidingwindow.cpp
| @@ -8,12 +8,28 @@ | @@ -8,12 +8,28 @@ | ||
| 8 | 8 | ||
| 9 | using namespace cv; | 9 | using namespace cv; |
| 10 | 10 | ||
| 11 | -// Because MSVC doesn't provide a round() function in math.h | ||
| 12 | -static int round(float x) { return (floor(x + 0.5)); } | ||
| 13 | - | ||
| 14 | namespace br | 11 | namespace br |
| 15 | { | 12 | { |
| 16 | 13 | ||
| 14 | +// Find avg aspect ratio | ||
| 15 | +static float getAspectRatio(const TemplateList &data) | ||
| 16 | +{ | ||
| 17 | + double tempRatio = 0; | ||
| 18 | + int ratioCnt = 0; | ||
| 19 | + | ||
| 20 | + foreach (const Template &tmpl, data) { | ||
| 21 | + QList<Rect> posRects = OpenCVUtils::toRects(tmpl.file.rects()); | ||
| 22 | + foreach (const Rect &posRect, posRects) { | ||
| 23 | + if (posRect.x + posRect.width >= tmpl.m().cols || posRect.y + posRect.height >= tmpl.m().rows || posRect.x < 0 || posRect.y < 0) { | ||
| 24 | + continue; | ||
| 25 | + } | ||
| 26 | + tempRatio += (float)posRect.width / (float)posRect.height; | ||
| 27 | + ratioCnt += 1; | ||
| 28 | + } | ||
| 29 | + } | ||
| 30 | + return tempRatio / (double)ratioCnt; | ||
| 31 | +} | ||
| 32 | + | ||
| 17 | /*! | 33 | /*! |
| 18 | * \ingroup transforms | 34 | * \ingroup transforms |
| 19 | * \brief Applies a transform to a sliding window. | 35 | * \brief Applies a transform to a sliding window. |
| @@ -25,23 +41,19 @@ class SlidingWindowTransform : public Transform | @@ -25,23 +41,19 @@ class SlidingWindowTransform : public Transform | ||
| 25 | Q_OBJECT | 41 | Q_OBJECT |
| 26 | Q_PROPERTY(br::Transform *transform READ get_transform WRITE set_transform RESET reset_transform STORED false) | 42 | Q_PROPERTY(br::Transform *transform READ get_transform WRITE set_transform RESET reset_transform STORED false) |
| 27 | Q_PROPERTY(int minSize READ get_minSize WRITE set_minSize RESET reset_minSize STORED false) | 43 | Q_PROPERTY(int minSize READ get_minSize WRITE set_minSize RESET reset_minSize STORED false) |
| 28 | - Q_PROPERTY(double scaleFactor READ get_scaleFactor WRITE set_scaleFactor RESET reset_scaleFactor STORED false) | ||
| 29 | Q_PROPERTY(int stepSize READ get_stepSize WRITE set_stepSize RESET reset_stepSize STORED false) | 44 | Q_PROPERTY(int stepSize READ get_stepSize WRITE set_stepSize RESET reset_stepSize STORED false) |
| 30 | - Q_PROPERTY(bool takeLargestScale READ get_takeLargestScale WRITE set_takeLargestScale RESET reset_takeLargestScale STORED false) | 45 | + Q_PROPERTY(bool takeFirst READ get_takeFirst WRITE set_takeFirst RESET reset_takeFirst STORED false) |
| 31 | Q_PROPERTY(bool negSamples READ get_negSamples WRITE set_negSamples RESET reset_negSamples STORED false) | 46 | Q_PROPERTY(bool negSamples READ get_negSamples WRITE set_negSamples RESET reset_negSamples STORED false) |
| 32 | Q_PROPERTY(int negToPosRatio READ get_negToPosRatio WRITE set_negToPosRatio RESET reset_negToPosRatio STORED false) | 47 | Q_PROPERTY(int negToPosRatio READ get_negToPosRatio WRITE set_negToPosRatio RESET reset_negToPosRatio STORED false) |
| 33 | Q_PROPERTY(double maxOverlap READ get_maxOverlap WRITE set_maxOverlap RESET reset_maxOverlap STORED false) | 48 | Q_PROPERTY(double maxOverlap READ get_maxOverlap WRITE set_maxOverlap RESET reset_maxOverlap STORED false) |
| 34 | - Q_PROPERTY(float aspectRatio READ get_aspectRatio WRITE set_aspectRatio RESET reset_aspectRatio STORED true) | ||
| 35 | Q_PROPERTY(int windowWidth READ get_windowWidth WRITE set_windowWidth RESET reset_windowWidth STORED false) | 49 | Q_PROPERTY(int windowWidth READ get_windowWidth WRITE set_windowWidth RESET reset_windowWidth STORED false) |
| 36 | BR_PROPERTY(br::Transform *, transform, NULL) | 50 | BR_PROPERTY(br::Transform *, transform, NULL) |
| 37 | BR_PROPERTY(int, minSize, 8) | 51 | BR_PROPERTY(int, minSize, 8) |
| 38 | - BR_PROPERTY(double, scaleFactor, 0.75) | ||
| 39 | BR_PROPERTY(int, stepSize, 1) | 52 | BR_PROPERTY(int, stepSize, 1) |
| 40 | - BR_PROPERTY(bool, takeLargestScale, true) | 53 | + BR_PROPERTY(bool, takeFirst, false) |
| 41 | BR_PROPERTY(bool, negSamples, true) | 54 | BR_PROPERTY(bool, negSamples, true) |
| 42 | BR_PROPERTY(int, negToPosRatio, 1) | 55 | BR_PROPERTY(int, negToPosRatio, 1) |
| 43 | BR_PROPERTY(double, maxOverlap, 0) | 56 | BR_PROPERTY(double, maxOverlap, 0) |
| 44 | - BR_PROPERTY(float, aspectRatio, 1) | ||
| 45 | BR_PROPERTY(int, windowWidth, 24) | 57 | BR_PROPERTY(int, windowWidth, 24) |
| 46 | 58 | ||
| 47 | public: | 59 | public: |
| @@ -50,24 +62,14 @@ private: | @@ -50,24 +62,14 @@ private: | ||
| 50 | 62 | ||
| 51 | void train(const TemplateList &data) | 63 | void train(const TemplateList &data) |
| 52 | { | 64 | { |
| 65 | + // only calculate if the work hasn't been done | ||
| 66 | + aspectRatio = data.first().file.get<float>("aspectRatio", -1); | ||
| 67 | + if (aspectRatio == -1) | ||
| 68 | + aspectRatio = getAspectRatio(data); | ||
| 69 | + | ||
| 53 | if (transform->trainable) { | 70 | if (transform->trainable) { |
| 54 | - double tempRatio = 0; | ||
| 55 | - int ratioCnt = 0; | ||
| 56 | TemplateList full; | 71 | TemplateList full; |
| 57 | 72 | ||
| 58 | - //First find avg aspect ratio | ||
| 59 | - foreach (const Template &tmpl, data) { | ||
| 60 | - QList<Rect> posRects = OpenCVUtils::toRects(tmpl.file.rects()); | ||
| 61 | - foreach (const Rect &posRect, posRects) { | ||
| 62 | - if (posRect.x + posRect.width >= tmpl.m().cols || posRect.y + posRect.height >= tmpl.m().rows || posRect.x < 0 || posRect.y < 0) { | ||
| 63 | - continue; | ||
| 64 | - } | ||
| 65 | - tempRatio += (float)posRect.width / (float)posRect.height; | ||
| 66 | - ratioCnt += 1; | ||
| 67 | - } | ||
| 68 | - } | ||
| 69 | - aspectRatio = tempRatio / (double)ratioCnt; | ||
| 70 | - | ||
| 71 | foreach (const Template &tmpl, data) { | 73 | foreach (const Template &tmpl, data) { |
| 72 | QList<Rect> posRects = OpenCVUtils::toRects(tmpl.file.rects()); | 74 | QList<Rect> posRects = OpenCVUtils::toRects(tmpl.file.rects()); |
| 73 | QList<Rect> negRects; | 75 | QList<Rect> negRects; |
| @@ -83,7 +85,7 @@ private: | @@ -83,7 +85,7 @@ private: | ||
| 83 | } | 85 | } |
| 84 | 86 | ||
| 85 | Mat scaledImg; | 87 | Mat scaledImg; |
| 86 | - resize(Mat(tmpl, posRect), scaledImg, Size(windowWidth,round(windowWidth / aspectRatio))); | 88 | + resize(Mat(tmpl, posRect), scaledImg, Size(windowWidth,qRound(windowWidth / aspectRatio))); |
| 87 | Template pos(tmpl.file, scaledImg); | 89 | Template pos(tmpl.file, scaledImg); |
| 88 | full += pos; | 90 | full += pos; |
| 89 | 91 | ||
| @@ -132,43 +134,94 @@ private: | @@ -132,43 +134,94 @@ private: | ||
| 132 | if (src.file.getBool("Train", false)) return; | 134 | if (src.file.getBool("Train", false)) return; |
| 133 | 135 | ||
| 134 | dst.file.clearRects(); | 136 | dst.file.clearRects(); |
| 137 | + int windowHeight = (int) qRound((float) windowWidth / aspectRatio); | ||
| 138 | + int scale = src.file.get<float>("scale", 1); | ||
| 139 | + | ||
| 140 | + for (double y = 0; y + windowHeight < src.m().rows; y += stepSize) { | ||
| 141 | + for (double x = 0; x + windowWidth < src.m().cols; x += stepSize) { | ||
| 142 | + Rect window(x, y, windowWidth, windowHeight); | ||
| 143 | + Template windowMat(src.file, Mat(src, window)); | ||
| 144 | + Template detect; | ||
| 145 | + transform->project(windowMat, detect); | ||
| 146 | + float conf = detect.file.get<float>("Confidence"); | ||
| 147 | + | ||
| 148 | + // the result will be in the Label | ||
| 149 | + if (conf > 0) { | ||
| 150 | + dst.file.appendRect(QRectF((float) x * scale, (float) y * scale, (float) windowWidth * scale, (float) windowHeight * scale)); | ||
| 151 | + QList<float> confidences = dst.file.getList<float>("Confidences", QList<float>()); | ||
| 152 | + confidences.append(conf); | ||
| 153 | + dst.file.setList<float>("Confidences", confidences); | ||
| 154 | + if (takeFirst) | ||
| 155 | + return; | ||
| 156 | + } | ||
| 157 | + } | ||
| 158 | + } | ||
| 159 | + } | ||
| 160 | + | ||
| 161 | + float aspectRatio; | ||
| 162 | +}; | ||
| 163 | + | ||
| 164 | +BR_REGISTER(Transform, SlidingWindowTransform) | ||
| 165 | + | ||
| 166 | +/*! | ||
| 167 | + * \ingroup transforms | ||
| 168 | + * \brief . | ||
| 169 | + * \author Austin Blanton \cite imaus10 | ||
| 170 | + */ | ||
| 171 | +class BuildScalesTransform : public Transform | ||
| 172 | +{ | ||
| 173 | + Q_OBJECT | ||
| 174 | + Q_PROPERTY(br::Transform *transform READ get_transform WRITE set_transform RESET reset_transform STORED false) | ||
| 175 | + Q_PROPERTY(double scaleFactor READ get_scaleFactor WRITE set_scaleFactor RESET reset_scaleFactor STORED false) | ||
| 176 | + Q_PROPERTY(bool takeLargestScale READ get_takeLargestScale WRITE set_takeLargestScale RESET reset_takeLargestScale STORED false) | ||
| 177 | + Q_PROPERTY(int windowWidth READ get_windowWidth WRITE set_windowWidth RESET reset_windowWidth STORED false) | ||
| 178 | + BR_PROPERTY(br::Transform *, transform, NULL) | ||
| 179 | + BR_PROPERTY(double, scaleFactor, 0.75) | ||
| 180 | + BR_PROPERTY(bool, takeLargestScale, false) | ||
| 181 | + BR_PROPERTY(int, windowWidth, 24) | ||
| 182 | + | ||
| 183 | +public: | ||
| 184 | + BuildScalesTransform() : Transform(false, true) {} | ||
| 185 | +private: | ||
| 186 | + | ||
| 187 | + void train(const TemplateList &data) | ||
| 188 | + { | ||
| 189 | + aspectRatio = getAspectRatio(data); | ||
| 190 | + // have to make a copy b/c data is const | ||
| 191 | + TemplateList cp = data; | ||
| 192 | + cp.first().file.set("aspectRatio", aspectRatio); | ||
| 193 | + if (transform->trainable) | ||
| 194 | + transform->train(cp); | ||
| 195 | + } | ||
| 196 | + | ||
| 197 | + void project(const Template &src, Template &dst) const | ||
| 198 | + { | ||
| 199 | + dst = src; | ||
| 200 | + // do not scale images during training | ||
| 201 | + if (src.file.getBool("Train", false)) return; | ||
| 202 | + | ||
| 135 | int rows = src.m().rows; | 203 | int rows = src.m().rows; |
| 136 | int cols = src.m().cols; | 204 | int cols = src.m().cols; |
| 137 | - int windowHeight = (int) round((float) windowWidth / aspectRatio); | 205 | + int windowHeight = (int) qRound((float) windowWidth / aspectRatio); |
| 138 | float startScale; | 206 | float startScale; |
| 139 | if ((cols / rows) > aspectRatio) | 207 | if ((cols / rows) > aspectRatio) |
| 140 | - startScale = round((float) rows / (float) windowHeight); | 208 | + startScale = qRound((float) rows / (float) windowHeight); |
| 141 | else | 209 | else |
| 142 | - startScale = round((float) cols / (float) windowWidth); | ||
| 143 | - | 210 | + startScale = qRound((float) cols / (float) windowWidth); |
| 144 | for (float scale = startScale; scale >= 1.0; scale -= (1.0 - scaleFactor)) { | 211 | for (float scale = startScale; scale >= 1.0; scale -= (1.0 - scaleFactor)) { |
| 145 | - Mat scaleImg; | ||
| 146 | - resize(src, scaleImg, Size(round(cols / scale), round(rows / scale))); | ||
| 147 | - | ||
| 148 | - for (double y = 0; y + windowHeight < scaleImg.rows; y += stepSize) { | ||
| 149 | - for (double x = 0; x + windowWidth < scaleImg.cols; x += stepSize) { | ||
| 150 | - Rect window(x, y, windowWidth, windowHeight); | ||
| 151 | - Template windowMat(src.file, Mat(scaleImg, window)); | ||
| 152 | - Template detect; | ||
| 153 | - transform->project(windowMat, detect); | ||
| 154 | - float conf = detect.file.get<float>("conf"); | ||
| 155 | - | ||
| 156 | - // the result will be in the Label | ||
| 157 | - if (conf > 0) { | ||
| 158 | - dst.file.appendRect(QRectF((float) x * scale, (float) y * scale, (float) windowWidth * scale, (float) windowHeight * scale)); | ||
| 159 | - QList<float> confidences = dst.file.getList<float>("Confidences", QList<float>()); | ||
| 160 | - confidences.append(conf); | ||
| 161 | - dst.file.setList<float>("Confidences", confidences); | ||
| 162 | - if (takeLargestScale) | ||
| 163 | - return; | ||
| 164 | - } | ||
| 165 | - } | ||
| 166 | - } | 212 | + Template scaleImg(src.file, Mat()); |
| 213 | + scaleImg.file.set("scale", scale); | ||
| 214 | + resize(src, scaleImg, Size(qRound(cols / scale), qRound(rows / scale))); | ||
| 215 | + transform->project(scaleImg, dst); | ||
| 216 | + if (takeLargestScale && !dst.file.rects().empty()) | ||
| 217 | + return; | ||
| 167 | } | 218 | } |
| 168 | } | 219 | } |
| 220 | + | ||
| 221 | + float aspectRatio; | ||
| 169 | }; | 222 | }; |
| 170 | 223 | ||
| 171 | -BR_REGISTER(Transform, SlidingWindowTransform) | 224 | +BR_REGISTER(Transform, BuildScalesTransform) |
| 172 | 225 | ||
| 173 | /*! | 226 | /*! |
| 174 | * \ingroup transforms | 227 | * \ingroup transforms |
openbr/plugins/stasm4.cpp
| @@ -132,13 +132,16 @@ class StasmTransform : public UntrainableTransform | @@ -132,13 +132,16 @@ class StasmTransform : public UntrainableTransform | ||
| 132 | 132 | ||
| 133 | if (!foundFace) { | 133 | if (!foundFace) { |
| 134 | qWarning("No face found in %s.", qPrintable(src.file.fileName())); | 134 | qWarning("No face found in %s.", qPrintable(src.file.fileName())); |
| 135 | + dst.file.set("FTE",true); | ||
| 135 | } else { | 136 | } else { |
| 137 | + QList<QPointF> points; | ||
| 136 | for (int i = 0; i < nLandmarks; i++) { | 138 | for (int i = 0; i < nLandmarks; i++) { |
| 137 | QPointF point(landmarks[2 * i], landmarks[2 * i + 1]); | 139 | QPointF point(landmarks[2 * i], landmarks[2 * i + 1]); |
| 138 | - dst.file.appendPoint(point); | ||
| 139 | - if (i == 38) dst.file.set("StasmRightEye",point); | ||
| 140 | - else if (i == 39) dst.file.set("StasmLeftEye", point); | 140 | + points.append(point); |
| 141 | } | 141 | } |
| 142 | + dst.file.set("StasmRightEye", points[38]); | ||
| 143 | + dst.file.set("StasmLeftEye", points[39]); | ||
| 144 | + dst.file.appendPoints(points); | ||
| 142 | } | 145 | } |
| 143 | } | 146 | } |
| 144 | }; | 147 | }; |
openbr/plugins/svm.cpp
| @@ -157,7 +157,7 @@ private: | @@ -157,7 +157,7 @@ private: | ||
| 157 | dst = src; | 157 | dst = src; |
| 158 | float prediction = svm.predict(src.m().reshape(1, 1), returnDFVal); | 158 | float prediction = svm.predict(src.m().reshape(1, 1), returnDFVal); |
| 159 | if (returnDFVal) { | 159 | if (returnDFVal) { |
| 160 | - dst.file.set("conf", prediction); | 160 | + dst.file.set("Confidence", prediction); |
| 161 | // positive values ==> first class | 161 | // positive values ==> first class |
| 162 | // negative values ==> second class | 162 | // negative values ==> second class |
| 163 | prediction = prediction > 0 ? 0 : 1; | 163 | prediction = prediction > 0 ? 0 : 1; |
openbr/plugins/template.cpp
| @@ -9,7 +9,7 @@ namespace br | @@ -9,7 +9,7 @@ namespace br | ||
| 9 | * \brief Retains only the values for the keys listed, to reduce template size | 9 | * \brief Retains only the values for the keys listed, to reduce template size |
| 10 | * \author Scott Klum \cite sklum | 10 | * \author Scott Klum \cite sklum |
| 11 | */ | 11 | */ |
| 12 | -class RetainTransform : public UntrainableTransform | 12 | +class KeepMetadataTransform : public UntrainableTransform |
| 13 | { | 13 | { |
| 14 | Q_OBJECT | 14 | Q_OBJECT |
| 15 | Q_PROPERTY(QStringList keys READ get_keys WRITE set_keys RESET reset_keys STORED false) | 15 | Q_PROPERTY(QStringList keys READ get_keys WRITE set_keys RESET reset_keys STORED false) |
| @@ -24,7 +24,7 @@ class RetainTransform : public UntrainableTransform | @@ -24,7 +24,7 @@ class RetainTransform : public UntrainableTransform | ||
| 24 | } | 24 | } |
| 25 | }; | 25 | }; |
| 26 | 26 | ||
| 27 | -BR_REGISTER(Transform, RetainTransform) | 27 | +BR_REGISTER(Transform, KeepMetadataTransform) |
| 28 | 28 | ||
| 29 | /*! | 29 | /*! |
| 30 | * \ingroup transforms | 30 | * \ingroup transforms |
openbr/plugins/validate.cpp
| @@ -255,7 +255,7 @@ class RejectDistance : public Distance | @@ -255,7 +255,7 @@ class RejectDistance : public Distance | ||
| 255 | { | 255 | { |
| 256 | // We don't look at the query | 256 | // We don't look at the query |
| 257 | (void) b; | 257 | (void) b; |
| 258 | - | 258 | + |
| 259 | foreach (const QString &key, keys) | 259 | foreach (const QString &key, keys) |
| 260 | if ((rejectIfContains && a.file.contains(key)) || (!rejectIfContains && !a.file.contains(key))) | 260 | if ((rejectIfContains && a.file.contains(key)) || (!rejectIfContains && !a.file.contains(key))) |
| 261 | return -std::numeric_limits<float>::max(); | 261 | return -std::numeric_limits<float>::max(); |