diff --git a/app/br/br.cpp b/app/br/br.cpp index 53040f8..83640e9 100644 --- a/app/br/br.cpp +++ b/app/br/br.cpp @@ -171,7 +171,11 @@ public: check(parc == 1, "Incorrect parameter count for 'daemon'."); daemon = true; daemon_pipe = parv[0]; - } else if (!strcmp(fun, "exit")) { + } else if (!strcmp(fun,"slave")) { + check(parc == 1, "Incorrect parameter count for 'slave'"); + br_slave_process(parv[0]); + } + else if (!strcmp(fun, "exit")) { check(parc == 0, "No parameters expected for 'exit'."); daemon = false; } else if (!strcmp(fun, "getHeader")) { diff --git a/openbr/core/core.cpp b/openbr/core/core.cpp index 370cedf..e2506e4 100644 --- a/openbr/core/core.cpp +++ b/openbr/core/core.cpp @@ -138,8 +138,6 @@ struct AlgorithmCore downcast->transforms[0] = this->transform.data(); downcast->init(); - - downcast->projectUpdate(i,i); return i.files(); diff --git a/openbr/core/qtutils.cpp b/openbr/core/qtutils.cpp index 1be8b99..0d5b511 100644 --- a/openbr/core/qtutils.cpp +++ b/openbr/core/qtutils.cpp @@ -399,33 +399,31 @@ void showFile(const QString &file) QString toString(const QVariant &variant) { - if (variant.canConvert(QVariant::String)) - return variant.toString(); - else if(variant.canConvert(QVariant::PointF)) { - QPointF pt = qvariant_cast(variant); - return QString("(%1,%2)").arg(QString::number(pt.x()), - QString::number(pt.y())); - } - else if (variant.canConvert(QVariant::RectF)) { + if (variant.canConvert(QVariant::List)) return toString(qvariant_cast(variant)); + else if (variant.canConvert(QVariant::String)) return variant.toString(); + else if (variant.canConvert(QVariant::PointF)) { + QPointF point = qvariant_cast(variant); + return QString("(%1,%2)").arg(QString::number(point.x()),QString::number(point.y())); + } else if (variant.canConvert(QVariant::RectF)) { QRectF rect = qvariant_cast(variant); return QString("(%1,%2,%3,%4)").arg(QString::number(rect.x()), QString::number(rect.y()), QString::number(rect.width()), QString::number(rect.height())); } - else if (variant.canConvert(QVariant::List)) { - QString ret = QString("["); - bool first = true; - foreach (const QVariant &i, variant.toList()) { - if (!first) - ret += ","; - else - first = false; - ret += toString(i); - } - ret += "]"; - return ret; - } + + return QString(); +} + +QString toString(const QVariantList &variantList) +{ + QStringList variants; + + foreach(const QVariant &variant, variantList) + variants.append(toString(variant)); + + if (!variants.isEmpty()) return "[" + variants.join(", ") + "]"; + return QString(); } diff --git a/openbr/core/qtutils.h b/openbr/core/qtutils.h index 649f771..256b968 100644 --- a/openbr/core/qtutils.h +++ b/openbr/core/qtutils.h @@ -74,6 +74,7 @@ namespace QtUtils /**** Variant Utilities ****/ QString toString(const QVariant &variant); + QString toString(const QVariantList &variantList); template QVariantList toVariantList(const QList &list) diff --git a/openbr/openbr.cpp b/openbr/openbr.cpp index 0950fa4..008a4b1 100644 --- a/openbr/openbr.cpp +++ b/openbr/openbr.cpp @@ -22,6 +22,7 @@ #include "core/fuse.h" #include "core/plot.h" #include "core/qtutils.h" +#include "plugins/openbr_internal.h" using namespace br; @@ -279,3 +280,11 @@ const char *br_version() static QByteArray version = Context::version().toLocal8Bit(); return version.data(); } + +void br_slave_process(const char * baseName) +{ + WorkerProcess worker; + worker.transform = Globals->algorithm; + worker.baseName = baseName; + worker.mainLoop(); +} diff --git a/openbr/openbr.h b/openbr/openbr.h index 9a115ce..93d0d8f 100644 --- a/openbr/openbr.h +++ b/openbr/openbr.h @@ -408,6 +408,12 @@ BR_EXPORT void br_train_n(int num_inputs, const char *inputs[], const char *mode */ BR_EXPORT const char *br_version(); + +/*! + * \brief For internal use via ProcessWrapperTransform + */ +BR_EXPORT void br_slave_process(const char * baseKey); + /*! @}*/ #ifdef __cplusplus diff --git a/openbr/openbr_plugin.cpp b/openbr/openbr_plugin.cpp index d5ac481..1013062 100644 --- a/openbr/openbr_plugin.cpp +++ b/openbr/openbr_plugin.cpp @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -58,16 +59,7 @@ QString File::flat() const foreach (const QString &key, keys) { const QVariant value = this->value(key); if (value.isNull()) values.append(key); - else { - if (QString(value.typeName()) == "QVariantList") { - QStringList variants; - foreach(const QVariant &variant, qvariant_cast(value)) { - variants.append(QtUtils::toString(variant)); - } - if (!variants.isEmpty()) values.append(key + "=[" + variants.join(", ") + "]"); - } - else values.append(key + "=" + QtUtils::toString(value)); - } + else values.append(key + "=" + QtUtils::toString(value)); } QString flat = name; @@ -942,6 +934,8 @@ void br::Context::initialize(int &argc, char *argv[], QString sdkPath, bool use_ qRegisterMetaType< QList >(); qRegisterMetaType< QList >(); qRegisterMetaType< QList >(); + qRegisterMetaType< QAbstractSocket::SocketState> (); + Globals = new Context(); Globals->init(File()); diff --git a/openbr/plugins/eigen3.cpp b/openbr/plugins/eigen3.cpp index 1b71955..3507b91 100644 --- a/openbr/plugins/eigen3.cpp +++ b/openbr/plugins/eigen3.cpp @@ -493,7 +493,7 @@ class LDATransform : public Transform outMap = projection.transpose() * (inMap - mean); if (isBinary) { - dst.file.set("conf",dst.m().at(0,0)); + dst.file.set("Confidence",dst.m().at(0,0)); } } diff --git a/openbr/plugins/openbr_internal.h b/openbr/plugins/openbr_internal.h index 3348b8c..e40a770 100644 --- a/openbr/plugins/openbr_internal.h +++ b/openbr/plugins/openbr_internal.h @@ -240,6 +240,18 @@ protected: CompositeTransform() : TimeVaryingTransform(false) {} }; +class EnrollmentWorker; + +// Implemented in plugins/process.cpp +struct WorkerProcess +{ + QString transform; + QString baseName; + EnrollmentWorker * processInterface; + + void mainLoop(); +}; + } #endif // OPENBR_INTERNAL_H diff --git a/openbr/plugins/process.cpp b/openbr/plugins/process.cpp new file mode 100644 index 0000000..af9c63c --- /dev/null +++ b/openbr/plugins/process.cpp @@ -0,0 +1,257 @@ + +#include +#include +#include +#include +#include + +#include +#include + +#include "openbr_internal.h" +#include "openbr/core/opencvutils.h" + +using namespace cv; + +namespace br +{ + +enum SignalType +{ + INPUT_AVAILABLE, + OUTPUT_AVAILABLE, + SHOULD_END +}; + +class EnrollmentWorker +{ +public: + QLocalServer inbound; + QLocalSocket outbound; + QLocalSocket * receiver; + + ~EnrollmentWorker() + { + delete transform; + } + + br::Transform * transform; + + void connections(const QString & baseName) + { + inbound.listen(baseName+"_worker"); + outbound.connectToServer(baseName+"_master"); + inbound.waitForNewConnection(-1); + receiver = inbound.nextPendingConnection(); + outbound.waitForConnected(-1); + } + + void workerLoop() + { + SignalType signal; + + forever + { + while (receiver->bytesAvailable() < qint64(sizeof(signal))) { + receiver->waitForReadyRead(-1); + } + receiver->read((char *) &signal, sizeof(signal)); + + if (signal == SHOULD_END) { + outbound.close(); + inbound.close(); + break; + } + + qint64 inBufferSize; + while (receiver->bytesAvailable() < qint64(sizeof(inBufferSize))) { + receiver->waitForReadyRead(-1); + } + receiver->read((char *) &inBufferSize, sizeof(inBufferSize)); + + QByteArray inArray(inBufferSize,'0'); + + qint64 arrayPosition = 0; + while (arrayPosition < inBufferSize) { + if (!receiver->bytesAvailable()) + receiver->waitForReadyRead(-1); + arrayPosition += receiver->read(inArray.data()+arrayPosition, receiver->bytesAvailable()); + } + + TemplateList inList; + TemplateList outList; + // deserialize the template list + QDataStream deserializer(inArray); + deserializer >> inList; + + // and project it + transform->projectUpdate(inList,outList); + + // serialize the output list + QBuffer outBuff; + outBuff.open(QBuffer::ReadWrite); + QDataStream serializer(&outBuff); + serializer << outList; + + // send the size of the buffer + //qint64 bufferSize = outBuff.size(); + qint64 bufferSize = outBuff.data().size(); + outbound.write((char *) &bufferSize, sizeof(bufferSize)); + + outbound.write(outBuff.data().data(), bufferSize); + while (outbound.bytesToWrite() > 0) { + outbound.waitForBytesWritten(-1); + } + } + } +}; + +void WorkerProcess::mainLoop() +{ + processInterface = new EnrollmentWorker(); + processInterface->transform = Transform::make(this->transform,NULL); + processInterface->connections(baseName); + processInterface->workerLoop(); + delete processInterface; +} + +/*! + * \ingroup transforms + * \brief Interface to a separate process + * \author Charles Otto \cite caotto + */ +class ProcessWrapperTransform : public TimeVaryingTransform +{ + Q_OBJECT + + Q_PROPERTY(QString transform READ get_transform WRITE set_transform RESET reset_transform) + BR_PROPERTY(QString, transform, "") + + QString baseKey; + QProcess workerProcess; + + QLocalServer inbound; + QLocalSocket outbound; + QLocalSocket * receiver; + + void projectUpdate(const TemplateList &src, TemplateList &dst) + { + if (!processActive) + { + activateProcess(); + } + + SignalType signal = INPUT_AVAILABLE; + outbound.write((char *) &signal, sizeof(SignalType)); + + QBuffer inBuffer; + inBuffer.open(QBuffer::ReadWrite); + QDataStream serializer(&inBuffer); + serializer << src; + + qint64 in_size = inBuffer.size(); + outbound.write((char *) &in_size, sizeof(in_size)); + + outbound.write(inBuffer.data(), in_size); + + while (outbound.bytesToWrite() > 0) { + outbound.waitForBytesWritten(-1); + } + + qint64 out_size; + + // read the size + receiver->waitForReadyRead(-1); + receiver->read((char *) &out_size, sizeof(out_size)); + QByteArray outBuffer(out_size,'0'); + + // read the (serialized) output templatelist + qint64 arrayPosition = 0; + while (arrayPosition < out_size) { + if (!receiver->bytesAvailable()) + receiver->waitForReadyRead(-1); + arrayPosition += receiver->read(outBuffer.data()+arrayPosition, receiver->bytesAvailable()); + } + // and deserialize it. + QDataStream deserialize(outBuffer); + deserialize >> dst; + } + + + void train(const TemplateList& data) + { + (void) data; + } + + // create the process + void init() + { + processActive = false; + } + + void activateProcess() + { + processActive = true; + + // generate a uuid for our local servers + QUuid id = QUuid::createUuid(); + baseKey = id.toString(); + + QStringList argumentList; + argumentList.append("-useGui"); + argumentList.append("0"); + argumentList.append("-algorithm"); + argumentList.append(transform); + argumentList.append("-path"); + argumentList.append(Globals->path); + argumentList.append("-parallelism"); + argumentList.append(QString::number(0)); + argumentList.append("-slave"); + argumentList.append(baseKey); + + // start listening + inbound.listen(baseKey+"_master"); + + workerProcess.setProcessChannelMode(QProcess::ForwardedChannels); + workerProcess.start("br", argumentList); + workerProcess.waitForStarted(-1); + + // blocking wait for the connection from the worker process + inbound.waitForNewConnection(-1); + receiver = inbound.nextPendingConnection(); + + // Now, create our connection to the worker process. + outbound.connectToServer(baseKey+"_worker"); + outbound.waitForConnected(-1); + } + + bool timeVarying() const { + return false; + } + + ~ProcessWrapperTransform() + { + // end the process + if (this->processActive) { + + SignalType signal = SHOULD_END; + outbound.write((char *) &signal, sizeof(SignalType)); + outbound.waitForBytesWritten(-1); + outbound.close(); + + workerProcess.waitForFinished(-1); + inbound.close(); + processActive = false; + } + } + +public: + bool processActive; + ProcessWrapperTransform() : TimeVaryingTransform(false,false) { processActive = false; } +}; + +BR_REGISTER(Transform, ProcessWrapperTransform) + +} + +#include "process.moc" diff --git a/openbr/plugins/regions.cpp b/openbr/plugins/regions.cpp index 317eb54..9e9e92a 100644 --- a/openbr/plugins/regions.cpp +++ b/openbr/plugins/regions.cpp @@ -345,9 +345,12 @@ class RectFromPointsTransform : public UntrainableTransform dst.file.setPoints(points); - 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))); + const int x = std::max(0.0, minX - deltaWidth/2.0); + const int y = std::max(0.0, minY - deltaHeight/2.0); + + 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))); else { - 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))); + dst.file.appendRect(QRectF(x, y, std::min((double)src.m().cols-x, width), std::min((double)src.m().rows-y, height))); dst.m() = src.m(); } } diff --git a/openbr/plugins/slidingwindow.cpp b/openbr/plugins/slidingwindow.cpp index 8bb4c83..ff36aed 100644 --- a/openbr/plugins/slidingwindow.cpp +++ b/openbr/plugins/slidingwindow.cpp @@ -8,12 +8,28 @@ using namespace cv; -// Because MSVC doesn't provide a round() function in math.h -static int round(float x) { return (floor(x + 0.5)); } - namespace br { +// Find avg aspect ratio +static float getAspectRatio(const TemplateList &data) +{ + double tempRatio = 0; + int ratioCnt = 0; + + foreach (const Template &tmpl, data) { + QList posRects = OpenCVUtils::toRects(tmpl.file.rects()); + foreach (const Rect &posRect, posRects) { + if (posRect.x + posRect.width >= tmpl.m().cols || posRect.y + posRect.height >= tmpl.m().rows || posRect.x < 0 || posRect.y < 0) { + continue; + } + tempRatio += (float)posRect.width / (float)posRect.height; + ratioCnt += 1; + } + } + return tempRatio / (double)ratioCnt; +} + /*! * \ingroup transforms * \brief Applies a transform to a sliding window. @@ -25,23 +41,19 @@ class SlidingWindowTransform : public Transform Q_OBJECT Q_PROPERTY(br::Transform *transform READ get_transform WRITE set_transform RESET reset_transform STORED false) Q_PROPERTY(int minSize READ get_minSize WRITE set_minSize RESET reset_minSize STORED false) - Q_PROPERTY(double scaleFactor READ get_scaleFactor WRITE set_scaleFactor RESET reset_scaleFactor STORED false) Q_PROPERTY(int stepSize READ get_stepSize WRITE set_stepSize RESET reset_stepSize STORED false) - Q_PROPERTY(bool takeLargestScale READ get_takeLargestScale WRITE set_takeLargestScale RESET reset_takeLargestScale STORED false) + Q_PROPERTY(bool takeFirst READ get_takeFirst WRITE set_takeFirst RESET reset_takeFirst STORED false) Q_PROPERTY(bool negSamples READ get_negSamples WRITE set_negSamples RESET reset_negSamples STORED false) Q_PROPERTY(int negToPosRatio READ get_negToPosRatio WRITE set_negToPosRatio RESET reset_negToPosRatio STORED false) Q_PROPERTY(double maxOverlap READ get_maxOverlap WRITE set_maxOverlap RESET reset_maxOverlap STORED false) - Q_PROPERTY(float aspectRatio READ get_aspectRatio WRITE set_aspectRatio RESET reset_aspectRatio STORED true) Q_PROPERTY(int windowWidth READ get_windowWidth WRITE set_windowWidth RESET reset_windowWidth STORED false) BR_PROPERTY(br::Transform *, transform, NULL) BR_PROPERTY(int, minSize, 8) - BR_PROPERTY(double, scaleFactor, 0.75) BR_PROPERTY(int, stepSize, 1) - BR_PROPERTY(bool, takeLargestScale, true) + BR_PROPERTY(bool, takeFirst, false) BR_PROPERTY(bool, negSamples, true) BR_PROPERTY(int, negToPosRatio, 1) BR_PROPERTY(double, maxOverlap, 0) - BR_PROPERTY(float, aspectRatio, 1) BR_PROPERTY(int, windowWidth, 24) public: @@ -50,24 +62,14 @@ private: void train(const TemplateList &data) { + // only calculate if the work hasn't been done + aspectRatio = data.first().file.get("aspectRatio", -1); + if (aspectRatio == -1) + aspectRatio = getAspectRatio(data); + if (transform->trainable) { - double tempRatio = 0; - int ratioCnt = 0; TemplateList full; - //First find avg aspect ratio - foreach (const Template &tmpl, data) { - QList posRects = OpenCVUtils::toRects(tmpl.file.rects()); - foreach (const Rect &posRect, posRects) { - if (posRect.x + posRect.width >= tmpl.m().cols || posRect.y + posRect.height >= tmpl.m().rows || posRect.x < 0 || posRect.y < 0) { - continue; - } - tempRatio += (float)posRect.width / (float)posRect.height; - ratioCnt += 1; - } - } - aspectRatio = tempRatio / (double)ratioCnt; - foreach (const Template &tmpl, data) { QList posRects = OpenCVUtils::toRects(tmpl.file.rects()); QList negRects; @@ -83,7 +85,7 @@ private: } Mat scaledImg; - resize(Mat(tmpl, posRect), scaledImg, Size(windowWidth,round(windowWidth / aspectRatio))); + resize(Mat(tmpl, posRect), scaledImg, Size(windowWidth,qRound(windowWidth / aspectRatio))); Template pos(tmpl.file, scaledImg); full += pos; @@ -132,43 +134,94 @@ private: if (src.file.getBool("Train", false)) return; dst.file.clearRects(); + int windowHeight = (int) qRound((float) windowWidth / aspectRatio); + int scale = src.file.get("scale", 1); + + for (double y = 0; y + windowHeight < src.m().rows; y += stepSize) { + for (double x = 0; x + windowWidth < src.m().cols; x += stepSize) { + Rect window(x, y, windowWidth, windowHeight); + Template windowMat(src.file, Mat(src, window)); + Template detect; + transform->project(windowMat, detect); + float conf = detect.file.get("Confidence"); + + // the result will be in the Label + if (conf > 0) { + dst.file.appendRect(QRectF((float) x * scale, (float) y * scale, (float) windowWidth * scale, (float) windowHeight * scale)); + QList confidences = dst.file.getList("Confidences", QList()); + confidences.append(conf); + dst.file.setList("Confidences", confidences); + if (takeFirst) + return; + } + } + } + } + + float aspectRatio; +}; + +BR_REGISTER(Transform, SlidingWindowTransform) + +/*! + * \ingroup transforms + * \brief . + * \author Austin Blanton \cite imaus10 + */ +class BuildScalesTransform : public Transform +{ + Q_OBJECT + Q_PROPERTY(br::Transform *transform READ get_transform WRITE set_transform RESET reset_transform STORED false) + Q_PROPERTY(double scaleFactor READ get_scaleFactor WRITE set_scaleFactor RESET reset_scaleFactor STORED false) + Q_PROPERTY(bool takeLargestScale READ get_takeLargestScale WRITE set_takeLargestScale RESET reset_takeLargestScale STORED false) + Q_PROPERTY(int windowWidth READ get_windowWidth WRITE set_windowWidth RESET reset_windowWidth STORED false) + BR_PROPERTY(br::Transform *, transform, NULL) + BR_PROPERTY(double, scaleFactor, 0.75) + BR_PROPERTY(bool, takeLargestScale, false) + BR_PROPERTY(int, windowWidth, 24) + +public: + BuildScalesTransform() : Transform(false, true) {} +private: + + void train(const TemplateList &data) + { + aspectRatio = getAspectRatio(data); + // have to make a copy b/c data is const + TemplateList cp = data; + cp.first().file.set("aspectRatio", aspectRatio); + if (transform->trainable) + transform->train(cp); + } + + void project(const Template &src, Template &dst) const + { + dst = src; + // do not scale images during training + if (src.file.getBool("Train", false)) return; + int rows = src.m().rows; int cols = src.m().cols; - int windowHeight = (int) round((float) windowWidth / aspectRatio); + int windowHeight = (int) qRound((float) windowWidth / aspectRatio); float startScale; if ((cols / rows) > aspectRatio) - startScale = round((float) rows / (float) windowHeight); + startScale = qRound((float) rows / (float) windowHeight); else - startScale = round((float) cols / (float) windowWidth); - + startScale = qRound((float) cols / (float) windowWidth); for (float scale = startScale; scale >= 1.0; scale -= (1.0 - scaleFactor)) { - Mat scaleImg; - resize(src, scaleImg, Size(round(cols / scale), round(rows / scale))); - - for (double y = 0; y + windowHeight < scaleImg.rows; y += stepSize) { - for (double x = 0; x + windowWidth < scaleImg.cols; x += stepSize) { - Rect window(x, y, windowWidth, windowHeight); - Template windowMat(src.file, Mat(scaleImg, window)); - Template detect; - transform->project(windowMat, detect); - float conf = detect.file.get("conf"); - - // the result will be in the Label - if (conf > 0) { - dst.file.appendRect(QRectF((float) x * scale, (float) y * scale, (float) windowWidth * scale, (float) windowHeight * scale)); - QList confidences = dst.file.getList("Confidences", QList()); - confidences.append(conf); - dst.file.setList("Confidences", confidences); - if (takeLargestScale) - return; - } - } - } + Template scaleImg(src.file, Mat()); + scaleImg.file.set("scale", scale); + resize(src, scaleImg, Size(qRound(cols / scale), qRound(rows / scale))); + transform->project(scaleImg, dst); + if (takeLargestScale && !dst.file.rects().empty()) + return; } } + + float aspectRatio; }; -BR_REGISTER(Transform, SlidingWindowTransform) +BR_REGISTER(Transform, BuildScalesTransform) /*! * \ingroup transforms diff --git a/openbr/plugins/stasm4.cpp b/openbr/plugins/stasm4.cpp index acb7911..4b2edc5 100644 --- a/openbr/plugins/stasm4.cpp +++ b/openbr/plugins/stasm4.cpp @@ -132,13 +132,16 @@ class StasmTransform : public UntrainableTransform if (!foundFace) { qWarning("No face found in %s.", qPrintable(src.file.fileName())); + dst.file.set("FTE",true); } else { + QList points; for (int i = 0; i < nLandmarks; i++) { QPointF point(landmarks[2 * i], landmarks[2 * i + 1]); - dst.file.appendPoint(point); - if (i == 38) dst.file.set("StasmRightEye",point); - else if (i == 39) dst.file.set("StasmLeftEye", point); + points.append(point); } + dst.file.set("StasmRightEye", points[38]); + dst.file.set("StasmLeftEye", points[39]); + dst.file.appendPoints(points); } } }; diff --git a/openbr/plugins/svm.cpp b/openbr/plugins/svm.cpp index 776426e..eda6814 100644 --- a/openbr/plugins/svm.cpp +++ b/openbr/plugins/svm.cpp @@ -157,7 +157,7 @@ private: dst = src; float prediction = svm.predict(src.m().reshape(1, 1), returnDFVal); if (returnDFVal) { - dst.file.set("conf", prediction); + dst.file.set("Confidence", prediction); // positive values ==> first class // negative values ==> second class prediction = prediction > 0 ? 0 : 1; diff --git a/openbr/plugins/template.cpp b/openbr/plugins/template.cpp index cd68a32..5d92eba 100644 --- a/openbr/plugins/template.cpp +++ b/openbr/plugins/template.cpp @@ -9,7 +9,7 @@ namespace br * \brief Retains only the values for the keys listed, to reduce template size * \author Scott Klum \cite sklum */ -class RetainTransform : public UntrainableTransform +class KeepMetadataTransform : public UntrainableTransform { Q_OBJECT Q_PROPERTY(QStringList keys READ get_keys WRITE set_keys RESET reset_keys STORED false) @@ -24,7 +24,7 @@ class RetainTransform : public UntrainableTransform } }; -BR_REGISTER(Transform, RetainTransform) +BR_REGISTER(Transform, KeepMetadataTransform) /*! * \ingroup transforms diff --git a/openbr/plugins/validate.cpp b/openbr/plugins/validate.cpp index 0774565..280b969 100644 --- a/openbr/plugins/validate.cpp +++ b/openbr/plugins/validate.cpp @@ -255,7 +255,7 @@ class RejectDistance : public Distance { // We don't look at the query (void) b; - + foreach (const QString &key, keys) if ((rejectIfContains && a.file.contains(key)) || (!rejectIfContains && !a.file.contains(key))) return -std::numeric_limits::max();