diff --git a/openbr/core/core.cpp b/openbr/core/core.cpp index a9eec23..70b2991 100644 --- a/openbr/core/core.cpp +++ b/openbr/core/core.cpp @@ -26,21 +26,20 @@ namespace br { struct AlgorithmCore { QSharedPointer transform; + QSharedPointer comparison; QSharedPointer distance; - QString galleryCompareString; - - QString transformString; - QString distanceString; + QSharedPointer progressCounter; AlgorithmCore(const QString &name) { this->name = name; init(name); + progressCounter = QSharedPointer(Transform::make("ProgressCounter", NULL)); } bool isClassifier() const { - return distance.isNull(); + return comparison.isNull(); } void train(const File &input, const QString &model) @@ -116,7 +115,9 @@ struct AlgorithmCore in >> name; init(Globals->abbreviations.contains(name) ? Globals->abbreviations[name] : name); transform->load(in); bool hasDistance; in >> hasDistance; - if (hasDistance) distance->load(in); + + if (hasDistance) + distance->load(in); } File getMemoryGallery(const File &file) const @@ -148,54 +149,37 @@ struct AlgorithmCore Gallery *temp = Gallery::make(input); qint64 total = temp->totalSize(); - QScopedPointer basePipe; - - QString pipeDesc = "GalleryOutput("+gallery.flat()+")+ProgressCounter("+QString::number(total)+")+Discard"; - - if (!multiProcess) { - basePipe.reset(Transform::make(pipeDesc,NULL)); - CompositeTransform *downcast = dynamic_cast(basePipe.data()); - - if (downcast == NULL) qFatal("downcast failed?"); + Transform * enroll = this->transform.data(); + if (multiProcess) + enroll = wrapTransform(enroll, "ProcessWrapper"); - downcast->transforms.prepend(this->transform.data()); - if (fileExclusion) { - Transform *temp = Transform::make("FileExclusion(" + gallery.flat() + ")", downcast); - downcast->transforms.prepend(temp); - } - - // call init on the pipe to collapse the algorithm (if its top level is a pipe) - downcast->init(); - } - else { - pipeDesc = "ProcessWrapper("+transformString+")+"+pipeDesc; - if (fileExclusion) - pipeDesc = "FileExclusion(" + gallery.flat() +")+" + pipeDesc; + QList stages; + stages.append(enroll); - basePipe.reset(Transform::make(pipeDesc,NULL)); - } + QString outputDesc; + if (fileExclusion) + outputDesc = "FileExclusion(" + gallery.flat() + ")+"; + outputDesc.append("GalleryOutput("+gallery.flat()+")"); + QScopedPointer outputTform(Transform::make(outputDesc, NULL)); + stages.append(outputTform.data()); + stages.append(progressCounter.data()); + QScopedPointer discard(Transform::make("Discard",NULL)); + stages.append(discard.data()); - // Next, we make a Stream (with placeholder transform) - QString streamDesc = "Stream(readMode=StreamGallery)"; - QScopedPointer baseStream(Transform::make(streamDesc, NULL)); - WrapperTransform *wrapper = dynamic_cast (baseStream.data()); + QScopedPointer pipeline(br::pipeTransforms(stages)); - // replace that placeholder with the pipe we built - wrapper->transform = basePipe.data(); - - // and get the final stream's stages by reinterpreting the pipe. Perfectly straightforward. - wrapper->init(); - - Globals->startTime.start(); - Globals->currentStep = 0; - Globals->totalSteps = total; + QScopedPointer stream(br::wrapTransform(pipeline.data(), "Stream(readMode=StreamGallery)")); TemplateList data, output; data.append(input); - wrapper->projectUpdate(data, output); + progressCounter->setPropertyRecursive("totalProgress", QString::number(total)); + stream->projectUpdate(data, output); files.append(output.files()); + if (multiProcess) + delete enroll; + return files; } @@ -389,12 +373,12 @@ struct AlgorithmCore // simple make sure the enrolled data is stored in a memGallery, but in multi-process mode we save the enrolled // data to disk (as a .gal file) so that each worker process can read it without re-doing enrollment. File colEnrolledGallery = colGallery; - QString targetExtension = multiProcess ? "gal" : "mem"; + QString targetExtension = "mem"; // If the column gallery is not already of the appropriate type, we need to do something if (colGallery.suffix() != targetExtension) { // Build the name of a gallery containing the enrolled data, of the appropriate type. - colEnrolledGallery = colGallery.baseName() + colGallery.hash() + (multiProcess ? ".gal" : ".mem"); + colEnrolledGallery = colGallery.baseName() + colGallery.hash() + '.' + targetExtension; // Check if we have to do real enrollment, and not just convert the gallery's type. if (!(QStringList() << "gal" << "template" << "mem").contains(colGallery.suffix())) @@ -435,37 +419,23 @@ struct AlgorithmCore // The actual comparison step is done by a GalleryCompare transform, which has a Distance, and a gallery as data. // Incoming templates are compared against the templates in the gallery, and the output is the resulting score // vector. + TemplateList tlist = TemplateList::fromGallery(colEnrolledGallery); + comparison->train(tlist); + comparison->setPropertyRecursive("galleryName",""); + QString compareRegionDesc; + QList enrollCompare; + enrollCompare.append(comparison.data()); + // if we have to enroll the row gallery, add that transform to the list + if (needEnrollRows) + enrollCompare.prepend(this->transform.data()); - if (this->galleryCompareString.isEmpty() ) - compareRegionDesc = "Pipe([GalleryCompare("+Globals->algorithm+",galleryName="+colEnrolledGallery.flat()+")])"; - else - compareRegionDesc = "Pipe(["+galleryCompareString+"(galleryName="+colEnrolledGallery.flat()+")])"; - - QScopedPointer compareRegion; - // If we need to enroll the row set, we add the current algorithm's enrollment transform before the - // GalleryCompare in a pipe. - if (needEnrollRows) { - if (!multiProcess) { - compareRegionDesc = compareRegionDesc; - compareRegion.reset(Transform::make(compareRegionDesc,NULL)); - CompositeTransform *downcast = dynamic_cast (compareRegion.data()); - if (downcast == NULL) - qFatal("Pipe downcast failed in compare"); - - downcast->transforms.prepend(this->transform.data()); - downcast->init(); - } - else { - compareRegionDesc = "ProcessWrapper(" + this->transformString + "+" + compareRegionDesc + ")"; - compareRegion.reset(Transform::make(compareRegionDesc, NULL)); - } - } - else { - if (multiProcess) - compareRegionDesc = "ProcessWrapper(" + compareRegionDesc + ")"; - compareRegion.reset(Transform::make(compareRegionDesc,NULL)); - } + Transform * compareRegionBase = pipeTransforms(enrollCompare); + // If in multi-process mode, wrap the enroll+compare structure in a ProcessWrapper. + if (multiProcess) + compareRegionBase = wrapTransform(compareRegionBase, "ProcessWrapper"); + + QScopedPointer compareRegion(compareRegionBase); // At this point, compareRegion is a transform, which optionally does enrollment, then compares the row // set against the column set. If in multi-process mode, the enrollment and comparison are wrapped in a @@ -473,47 +443,36 @@ struct AlgorithmCore // We also need to add Output and progress counting to the algorithm we are building, so we will assign them to // two stages of a pipe. - QString joinDesc = "Pipe()"; - QScopedPointer join(Transform::make(joinDesc, NULL)); + QList compareOutput; + compareOutput.append(compareRegion.data()); // The output transform takes the metadata memGalleries we set up previously as input, along with the // output specification we were passed. Gallery metadata is necessary for some Outputs to function correctly. QString outputString = output.flat().isEmpty() ? "Empty" : output.flat(); QString outputRegionDesc = "Output("+ outputString +"," + targetGallery.flat() +"," + queryGallery.flat() + ","+ QString::number(transposeMode ? 1 : 0) + ")"; - // The ProgressCounter transform will simply provide a display about the number of rows completed. - outputRegionDesc += "+ProgressCounter("+QString::number(rowSize)+")+Discard"; - QScopedPointer outputTform(Transform::make(outputRegionDesc, NULL)); + QScopedPointer outputTForm(Transform::make(outputRegionDesc,NULL)); + compareOutput.append(outputTForm.data()); - // Assign the comparison transform we previously built, and the output transform we just built to - // two stages of a pipe. - CompositeTransform *downcast = dynamic_cast (join.data()); - downcast->transforms.append(compareRegion.data()); - downcast->transforms.append(outputTform.data()); + // The ProgressCounter transform will simply provide a display about the number of rows completed. + compareOutput.append(progressCounter.data()); + QScopedPointer discard(Transform::make("Discard",NULL)); + compareOutput.append(discard.data()); // With this, we have set up a transform which (optionally) enrolls templates, compares them // against a gallery, and outputs them. - join->init(); + Transform * pipeline = br::pipeTransforms(compareOutput); // Now, we will give that base transform to a stream, which will incrementally read the row gallery // and pass the transforms it reads through the base algorithm. - QString streamDesc = "Stream(readMode=StreamGallery)"; - QScopedPointer streamBase(Transform::make(streamDesc, NULL)); - WrapperTransform *streamWrapper = dynamic_cast (streamBase.data()); - streamWrapper->transform = join.data(); - - // The transform we will use is now complete. - streamWrapper->init(); + QScopedPointer streamWrapper(br::wrapTransform(pipeline, "Stream(readMode=StreamGallery)")); // We set up a template containing the rowGallery we want to compare. TemplateList rowGalleryTemplate; rowGalleryTemplate.append(Template(rowGallery)); TemplateList outputGallery; - // Set up progress counting variables - Globals->currentStep = 0; - Globals->currentProgress = 0; - Globals->totalSteps = rowSize; - Globals->startTime.start(); + // initialize the progress counter + progressCounter->setPropertyRecursive("totalProgress", QString::number(rowSize)); // Do the actual comparisons streamWrapper->projectUpdate(rowGalleryTemplate, outputGallery); @@ -557,27 +516,22 @@ private: return; } + //! [Parsing the algorithm description] const bool compareTransform = description.contains('!'); QStringList words = QtUtils::parse(description, compareTransform ? '!' : ':'); if ((words.size() < 1) || (words.size() > 2)) qFatal("Invalid algorithm format."); - //! [Parsing the algorithm description] - transformString = words[0]; - //! [Creating the template generation and comparison methods] transform = QSharedPointer(Transform::make(words[0], NULL)); if (words.size() > 1) { if (!compareTransform) { distance = QSharedPointer(Distance::make(words[1], NULL)); - distanceString = words[1]; - galleryCompareString.clear(); - } - else { - galleryCompareString = words[1]; - distanceString.clear(); + comparison = QSharedPointer(Transform::make("GalleryCompare", NULL)); + comparison->setPropertyRecursive("distance", QVariant::fromValue(distance.data())); } - + else + comparison = QSharedPointer(Transform::make(words[1], NULL)); } //! [Creating the template generation and comparison methods] } diff --git a/openbr/openbr_plugin.cpp b/openbr/openbr_plugin.cpp index 0d84260..fb107c7 100644 --- a/openbr/openbr_plugin.cpp +++ b/openbr/openbr_plugin.cpp @@ -577,16 +577,31 @@ QStringList Object::parameters() const return parameters; } -QStringList Object::arguments() const +QStringList Object::arguments(bool expanded) { QStringList arguments; - for (int i=metaObject()->propertyOffset(); ipropertyCount(); i++) - arguments.append(argument(i)); + + for (int i=firstAvailablePropertyIdx; ipropertyCount(); i++) { + const char *name = metaObject()->property(i).name(); + + QVariant oldVal = property(name); + QVariant defaultVal = oldVal; + + if (metaObject()->property(i).isResettable()) { + metaObject()->property(i).reset(this); + defaultVal = property(name); + } + + if (defaultVal != oldVal) { + metaObject()->property(i).write(this, oldVal); + arguments.append(name + QString("=") + argument(i, expanded)); + } + } return arguments; } -QString Object::argument(int index) const +QString Object::argument(int index, bool expanded) const { if ((index < 0) || (index > metaObject()->propertyCount())) return ""; const QMetaProperty property = metaObject()->property(index); @@ -604,19 +619,19 @@ QString Object::argument(int index) const strings.append(QString::number(value)); } else if (type == "QList") { foreach (Transform *transform, variant.value< QList >()) - strings.append(transform->description()); + strings.append(transform->description(expanded)); } else if (type == "QList") { foreach (Distance *distance, variant.value< QList >()) - strings.append(distance->description()); + strings.append(distance->description(expanded)); } else { qFatal("Unrecognized type: %s", qPrintable(type)); } return "[" + strings.join(",") + "]"; } else if (type == "br::Transform*") { - return variant.value()->description(); + return variant.value()->description(expanded); } else if (type == "br::Distance*") { - return variant.value()->description(); + return variant.value()->description(expanded); } else if (type == "QStringList") { return "[" + variant.toStringList().join(",") + "]"; } @@ -624,13 +639,17 @@ QString Object::argument(int index) const return variant.toString(); } -QString Object::description() const +QString Object::description(bool expanded) { - QString argumentString = arguments().join(","); + (void) expanded; + QString argumentString = arguments(expanded).join(","); + if (argumentString.endsWith(",")) + argumentString.chop(1); + return objectName() + (argumentString.isEmpty() ? "" : ("(" + argumentString + ")")); } -void Object::store(QDataStream &stream) const +void Object::store(QDataStream &stream, bool force) const { // Start from 1 to skip QObject::objectName for (int i=1; ipropertyCount(); i++) { @@ -641,14 +660,14 @@ void Object::store(QDataStream &stream) const const QString type = property.typeName(); if (type == "QList") { foreach (Transform *transform, property.read(this).value< QList >()) - transform->store(stream); + transform->store(stream, force); } else if (type == "QList") { foreach (Distance *distance, property.read(this).value< QList >()) - distance->store(stream); + distance->store(stream, force); } else if (type == "br::Transform*") { - property.read(this).value()->store(stream); + property.read(this).value()->store(stream, force); } else if (type == "br::Distance*") { - property.read(this).value()->store(stream); + property.read(this).value()->store(stream, force); } else if (type == "bool") { stream << property.read(this).toBool(); } else if (type == "int") { @@ -1284,7 +1303,8 @@ Transform *Transform::make(QString str, QObject *parent) Transform *Transform::clone() const { - Transform *clone = Factory::make(file.flat()); + Transform * temp = (Transform *) this; + Transform *clone = Factory::make("."+temp->description(false)); return clone; } @@ -1453,3 +1473,17 @@ void br::applyAdditionalProperties(const File &temp, Transform *target) target->setPropertyRecursive(i.key(), i.value() ); } } + +Transform *br::wrapTransform(Transform *base, const QString &target) +{ + Transform *res = Transform::make(target, NULL); + res->setPropertyRecursive("transform", QVariant::fromValue(base)); + return res; +} + +Transform *br::pipeTransforms(QList &transforms) +{ + Transform *res = Transform::make("Pipe",NULL); + res->setPropertyRecursive("transforms", QVariant::fromValue(transforms)); + return res; +} diff --git a/openbr/openbr_plugin.h b/openbr/openbr_plugin.h index bc30f40..949db6d 100644 --- a/openbr/openbr_plugin.h +++ b/openbr/openbr_plugin.h @@ -589,13 +589,20 @@ public: File file; /*!< \brief The file used to construct the plugin. */ virtual void init() {} /*!< \brief Overload this function instead of the default constructor to initialize the derived class. It should be safe to call this function multiple times. */ - virtual void store(QDataStream &stream) const; /*!< \brief Serialize the object. */ + virtual void store(QDataStream &stream, bool force = false) const; /*!< \brief Serialize the object. */ virtual void load(QDataStream &stream); /*!< \brief Deserialize the object. Default implementation calls init() after deserialization. */ + virtual void serialize(QDataStream & stream, bool force) + { + stream << description(force); + store(stream, force); + } + QStringList parameters() const; /*!< \brief A string describing the parameters the object takes. */ - QStringList arguments() const; /*!< \brief A string describing the values the object has. */ - QString argument(int index) const; /*!< \brief A string value for the argument at the specified index. */ - QString description() const; /*!< \brief Returns a string description of the object. */ + QStringList arguments(bool expanded = false); /*!< \brief A string describing the values the object has. */ + QString argument(int index, bool expanded) const; /*!< \brief A string value for the argument at the specified index. */ + virtual QString description(bool expanded = false); /*!< \brief Returns a string description of the object. */ + void setProperty(const QString &name, QVariant value); /*!< \brief Overload of QObject::setProperty to handle OpenBR data types. */ virtual bool setPropertyRecursive(const QString &name, QVariant value); /*!< \brief Recursive version of setProperty, try to set the property on this object, or its children, returns true if successful. */ @@ -1273,6 +1280,15 @@ public: */ QList getChildren() const; + static Transform *deserialize(QDataStream &stream) + { + QString desc; + stream >> desc; + Transform *res = Transform::make(desc, NULL); + res->load(stream); + return res; + } + protected: Transform(bool independent = true, bool trainable = true); /*!< \brief Construct a transform. */ inline Transform *make(const QString &description) { return make(description, this); } /*!< \brief Make a subtransform. */ diff --git a/openbr/plugins/cascade.cpp b/openbr/plugins/cascade.cpp index d91ac89..e6fa835 100644 --- a/openbr/plugins/cascade.cpp +++ b/openbr/plugins/cascade.cpp @@ -415,7 +415,7 @@ class CascadeTransform : public MetaTransform } // TODO: Remove this code when ready to break binary compatibility - void store(QDataStream &stream) const + void store(QDataStream &stream, bool force) const { int size = 1; stream << size; diff --git a/openbr/plugins/cluster.cpp b/openbr/plugins/cluster.cpp index 24bea1f..cf47a70 100644 --- a/openbr/plugins/cluster.cpp +++ b/openbr/plugins/cluster.cpp @@ -69,7 +69,7 @@ class KMeansTransform : public Transform reindex(); } - void store(QDataStream &stream) const + void store(QDataStream &stream, bool force) const { stream << centers; } @@ -131,7 +131,7 @@ class KNNTransform : public Transform dst.file.set("Nearest", gallery[sortedScores[0].second].file.name); } - void store(QDataStream &stream) const + void store(QDataStream &stream, bool force) const { stream << gallery; } @@ -196,7 +196,7 @@ class RandomCentroidsTransform : public Transform reindex(); } - void store(QDataStream &stream) const + void store(QDataStream &stream, bool force) const { stream << centers; } diff --git a/openbr/plugins/distance.cpp b/openbr/plugins/distance.cpp index 34e91e8..a6ac657 100644 --- a/openbr/plugins/distance.cpp +++ b/openbr/plugins/distance.cpp @@ -253,11 +253,11 @@ private: } } - void store(QDataStream &stream) const + void store(QDataStream &stream, bool force) const { stream << distances.size(); foreach (Distance *distance, distances) - distance->store(stream); + distance->store(stream, force); } void load(QDataStream &stream) @@ -328,9 +328,9 @@ class NegativeLogPlusOneDistance : public Distance return -log(distance->compare(a,b)+1); } - void store(QDataStream &stream) const + void store(QDataStream &stream, bool force) const { - distance->store(stream); + distance->store(stream, force); } void load(QDataStream &stream) @@ -460,13 +460,12 @@ BR_REGISTER(Distance, SumDistance) class GalleryCompareTransform : public Transform { Q_OBJECT - Q_PROPERTY(QString distanceAlgorithm READ get_distanceAlgorithm WRITE set_distanceAlgorithm RESET reset_distanceAlgorithm STORED false) + Q_PROPERTY(br::Distance *distance READ get_distance WRITE set_distance RESET reset_distance STORED true) Q_PROPERTY(QString galleryName READ get_galleryName WRITE set_galleryName RESET reset_galleryName STORED false) - BR_PROPERTY(QString, distanceAlgorithm, "") + BR_PROPERTY(br::Distance*, distance, NULL) BR_PROPERTY(QString, galleryName, "") TemplateList gallery; - QSharedPointer distance; void project(const Template &src, Template &dst) const { @@ -480,16 +479,29 @@ class GalleryCompareTransform : public Transform void init() { - if (!galleryName.isEmpty()) { + if (!galleryName.isEmpty()) gallery = TemplateList::fromGallery(galleryName); - } - if (!distanceAlgorithm.isEmpty()) - { - distance = Distance::fromAlgorithm(distanceAlgorithm); - } } + + void train(const TemplateList & data) + { + gallery = data; + } + + void store(QDataStream &stream, bool force) const + { + br::Object::store(stream, force); + stream << gallery; + } + + void load(QDataStream &stream) + { + br::Object::load(stream); + stream >> gallery; + } + public: - GalleryCompareTransform() : Transform(false, false) {} + GalleryCompareTransform() : Transform(false, true) {} }; BR_REGISTER(Transform, GalleryCompareTransform) diff --git a/openbr/plugins/eigen3.cpp b/openbr/plugins/eigen3.cpp index 45fd671..f3caa60 100644 --- a/openbr/plugins/eigen3.cpp +++ b/openbr/plugins/eigen3.cpp @@ -119,7 +119,7 @@ private: outMap = eVecs.transpose() * (inMap - mean); } - void store(QDataStream &stream) const + void store(QDataStream &stream, bool force) const { stream << keep << drop << whiten << originalRows << mean << eVals << eVecs; } @@ -295,9 +295,9 @@ class DFFSTransform : public Transform dst.file.set("DFFS", sqrt(pca.residualReconstructionError((*cvtFloat)(src)))); } - void store(QDataStream &stream) const + void store(QDataStream &stream, bool force) const { - pca.store(stream); + pca.store(stream, force); } void load(QDataStream &stream) @@ -525,7 +525,7 @@ class LDATransform : public Transform dst.m().at(0,0) = dst.m().at(0,0) / stdDev; } - void store(QDataStream &stream) const + void store(QDataStream &stream, bool force) const { stream << pcaKeep; stream << directLDA; @@ -631,10 +631,10 @@ class SparseLDATransform : public Transform ldaSparse.project(Template(src.file, inSelect), dst); } - void store(QDataStream &stream) const + void store(QDataStream &stream, bool force) const { stream << pcaKeep; - stream << ldaSparse; + ldaSparse.store(stream, force); stream << dimsOut; stream << selections; } diff --git a/openbr/plugins/frames.cpp b/openbr/plugins/frames.cpp index fb7f507..58a3def 100644 --- a/openbr/plugins/frames.cpp +++ b/openbr/plugins/frames.cpp @@ -43,7 +43,7 @@ private: buffer.clear(); } - void store(QDataStream &stream) const + void store(QDataStream &stream, bool force) const { (void) stream; } diff --git a/openbr/plugins/independent.cpp b/openbr/plugins/independent.cpp index 2a627c1..3ac80ae 100644 --- a/openbr/plugins/independent.cpp +++ b/openbr/plugins/independent.cpp @@ -126,6 +126,10 @@ class IndependentTransform : public MetaTransform QList transforms; + QString description(bool expanded) + { + return transform->description(expanded); + } bool setPropertyRecursive(const QString &name, QVariant value) { @@ -226,12 +230,12 @@ class IndependentTransform : public MetaTransform } } - void store(QDataStream &stream) const + void store(QDataStream &stream, bool force) const { const int size = transforms.size(); stream << size; for (int i=0; istore(stream); + transforms[i]->store(stream, force); } void load(QDataStream &stream) @@ -292,10 +296,10 @@ class SingletonTransform : public MetaTransform transform->project(src, dst); } - void store(QDataStream &stream) const + void store(QDataStream &stream, bool force) const { if (transform->parent() == this) - transform->store(stream); + transform->store(stream, force); } void load(QDataStream &stream) diff --git a/openbr/plugins/integral.cpp b/openbr/plugins/integral.cpp index f6117a0..fe00573 100644 --- a/openbr/plugins/integral.cpp +++ b/openbr/plugins/integral.cpp @@ -251,12 +251,12 @@ class RecursiveIntegralSamplerTransform : public Transform } } - void store(QDataStream &stream) const + void store(QDataStream &stream, bool force) const { - transform->store(stream); + transform->store(stream, force); stream << (subTransform != NULL); if (subTransform != NULL) - subTransform->store(stream); + subTransform->store(stream, force); } void load(QDataStream &stream) diff --git a/openbr/plugins/landmarks.cpp b/openbr/plugins/landmarks.cpp index 993312d..caafc29 100644 --- a/openbr/plugins/landmarks.cpp +++ b/openbr/plugins/landmarks.cpp @@ -125,7 +125,7 @@ class ProcrustesTransform : public Transform } } - void store(QDataStream &stream) const + void store(QDataStream &stream, bool force) const { stream << meanShape; } @@ -299,7 +299,7 @@ class ProcrustesAlignTransform : public Transform dst.file.set("ProcrustesBound", QRectF(0, 0, width + 2 * padding, (qRound(width / aspectRatio) + 2 * padding))); } - void store(QDataStream &stream) const + void store(QDataStream &stream, bool force) const { stream << referenceShape; stream << minX; diff --git a/openbr/plugins/meta.cpp b/openbr/plugins/meta.cpp index dc2d6e9..79a3e11 100644 --- a/openbr/plugins/meta.cpp +++ b/openbr/plugins/meta.cpp @@ -482,9 +482,9 @@ BR_REGISTER(Transform, CacheTransform) class LoadStoreTransform : public MetaTransform { Q_OBJECT - Q_PROPERTY(QString description READ get_description WRITE set_description RESET reset_description STORED false) + Q_PROPERTY(QString description2 READ get_description2 WRITE set_description2 RESET reset_description2 STORED false) Q_PROPERTY(QString fileName READ get_fileName WRITE set_fileName RESET reset_fileName STORED false) - BR_PROPERTY(QString, description, "Identity") + BR_PROPERTY(QString, description2, "Identity") BR_PROPERTY(QString, fileName, QString()) Transform *transform; @@ -493,6 +493,13 @@ class LoadStoreTransform : public MetaTransform public: LoadStoreTransform() : transform(NULL) {} + QString description(bool expanded = false) + { + if (expanded) + return transform->description(expanded); + return br::Object::description(expanded); + } + bool setPropertyRecursive(const QString &name, QVariant value) { if (br::Object::setPropertyRecursive(name, value)) @@ -500,12 +507,22 @@ public: return transform->setPropertyRecursive(name, value); } private: + + virtual void store(QDataStream &stream, bool force = false) const + { + if (force) { + transform->store(stream, force); + } + + br::Object::store(stream, false); + } + void init() { if (transform != NULL) return; - if (fileName.isEmpty()) baseName = QRegExp("^[a-zA-Z0-9]+$").exactMatch(description) ? description : QtUtils::shortTextHash(description); + if (fileName.isEmpty()) baseName = QRegExp("^[a-zA-Z0-9]+$").exactMatch(description2) ? description2 : QtUtils::shortTextHash(description2); else baseName = fileName; - if (!tryLoad()) transform = make(description); + if (!tryLoad()) transform = make(description2); else trainable = false; } @@ -524,7 +541,7 @@ private: qDebug("Storing %s", qPrintable(baseName)); QByteArray byteArray; QDataStream stream(&byteArray, QFile::WriteOnly); - stream << description; + stream << description2; transform->store(stream); QtUtils::writeFile(baseName, byteArray, -1); } @@ -570,8 +587,8 @@ private: QByteArray data; QtUtils::readFile(file, data, true); QDataStream stream(&data, QFile::ReadOnly); - stream >> description; - transform = Transform::make(description); + stream >> description2; + transform = Transform::make(description2); transform->load(stream); return true; } diff --git a/openbr/plugins/misc.cpp b/openbr/plugins/misc.cpp index 35db597..dc7d76a 100644 --- a/openbr/plugins/misc.cpp +++ b/openbr/plugins/misc.cpp @@ -617,8 +617,8 @@ class ProgressCounterTransform : public TimeVaryingTransform { Q_OBJECT - Q_PROPERTY(int totalTemplates READ get_totalTemplates WRITE set_totalTemplates RESET reset_totalTemplates STORED false) - BR_PROPERTY(int, totalTemplates, 1) + Q_PROPERTY(int totalProgress READ get_totalProgress WRITE set_totalProgress RESET reset_totalProgress STORED false) + BR_PROPERTY(int, totalProgress, 1) void projectUpdate(const TemplateList &src, TemplateList &dst) { @@ -658,16 +658,20 @@ class ProgressCounterTransform : public TimeVaryingTransform (void) data; float p = br_progress(); qDebug("\r%05.2f%% ELAPSED=%s REMAINING=%s COUNT=%g", p*100, QtUtils::toTime(Globals->startTime.elapsed()/1000.0f).toStdString().c_str(), QtUtils::toTime(0).toStdString().c_str(), Globals->currentStep); + timer.start(); + Globals->startTime.start(); Globals->currentStep = 0; Globals->currentProgress = 0; - Globals->totalSteps = 0; + Globals->totalSteps = totalProgress; } void init() { timer.start(); + Globals->startTime.start(); Globals->currentStep = 0; Globals->currentProgress = 0; + Globals->totalSteps = totalProgress; } public: diff --git a/openbr/plugins/normalize.cpp b/openbr/plugins/normalize.cpp index 449ca61..80609b5 100644 --- a/openbr/plugins/normalize.cpp +++ b/openbr/plugins/normalize.cpp @@ -167,7 +167,7 @@ private: divide(dst, a, dst); } - void store(QDataStream &stream) const + void store(QDataStream &stream, bool force) const { stream << a << b; } @@ -256,7 +256,7 @@ class RowWiseMeanCenterTransform : public Transform dst = m; } - void store(QDataStream &stream) const + void store(QDataStream &stream, bool force) const { stream << mean; } diff --git a/openbr/plugins/openbr_internal.h b/openbr/plugins/openbr_internal.h index 2283760..999c05e 100644 --- a/openbr/plugins/openbr_internal.h +++ b/openbr/plugins/openbr_internal.h @@ -19,7 +19,7 @@ protected: private: Transform *clone() const { return const_cast(this); } void train(const TemplateList &data) { (void) data; } - void store(QDataStream &stream) const { (void) stream; } + void store(QDataStream &stream, bool force) const { (void) stream; (void) force; } void load(QDataStream &stream) { (void) stream; } }; @@ -212,6 +212,45 @@ public: } return false; } + + Transform * smartCopy(bool & newTransform) + { + if (!timeVarying()) { + newTransform = false; + return this; + } + newTransform = true; + + QString name = metaObject()->className(); + name.replace("Transform",""); + name += "(Identity"; + + QStringList arguments = this->arguments(); + if (!arguments.isEmpty()) { + name += ","; + name += this->arguments().join(","); + } + + name += ")"; + name.replace("br::",""); + + WrapperTransform *output = dynamic_cast(Transform::make(name, NULL)); + + if (output == NULL) + qFatal("Dynamic cast failed!"); + + bool newItem = false; + Transform * maybe_copy = transform->smartCopy(newItem); + if (newItem) + maybe_copy->setParent(output); + output->transform = maybe_copy; + + output->file = this->file; + output->init(); + + return output; + } + }; /*! @@ -388,6 +427,10 @@ public: void applyAdditionalProperties(const File &temp, Transform *target); +Transform *wrapTransform(Transform *base, const QString &target); + +Transform *pipeTransforms(QList &transforms); + } #endif // OPENBR_INTERNAL_H diff --git a/openbr/plugins/pp5.cpp b/openbr/plugins/pp5.cpp index e605839..c3e960c 100644 --- a/openbr/plugins/pp5.cpp +++ b/openbr/plugins/pp5.cpp @@ -450,6 +450,7 @@ class PP5GalleryTransform: public UntrainableMetaTransform ppr_gallery_type target; QList targetIDs; + TemplateList gallery; void project(const Template & src, Template & dst) const { @@ -495,11 +496,33 @@ class PP5GalleryTransform: public UntrainableMetaTransform void init() { - // set up the gallery - ppr_create_gallery(context, &target); - TemplateList templates = TemplateList::fromGallery(galleryName); - enroll(templates,&target, targetIDs); + if (!galleryName.isEmpty() || !gallery.isEmpty()) { + // set up the gallery + ppr_create_gallery(context, &target); + if (gallery.isEmpty() ) + gallery = TemplateList::fromGallery(galleryName); + enroll(gallery, &target, targetIDs); + } + } + + void train(const TemplateList & data) + { + gallery = data; } + + void store(QDataStream &stream, bool force) const + { + br::Object::store(stream, force); + stream << gallery; + } + + void load(QDataStream &stream) + { + br::Object::load(stream); + stream >> gallery; + init(); + } + }; BR_REGISTER(Transform, PP5GalleryTransform) diff --git a/openbr/plugins/process.cpp b/openbr/plugins/process.cpp index b8c519c..29f5969 100644 --- a/openbr/plugins/process.cpp +++ b/openbr/plugins/process.cpp @@ -305,9 +305,7 @@ public: while (!inbound || inbound->state() != QLocalSocket::ConnectedState) { bool res = receivedWait.wait(&receivedLock,30*1000); if (!res) - { qDebug() << key << " " << QThread::currentThread() << " waiting timed out, server thread is " << server.thread() << " base thread " << basis; - } } } @@ -338,6 +336,16 @@ public: return true; } + Transform * readTForm() + { + emit pulseReadSerialized(); + + QByteArray data = readArray; + QDataStream deserializer(data); + Transform * res = Transform::deserialize(deserializer); + return res; + } + template bool sendData(const T & output) { @@ -401,6 +409,8 @@ public: comm->connectToRemote(baseName+"_master"); comm->waitForInbound(); + + transform = comm->readTForm(); } void workerLoop() @@ -428,7 +438,7 @@ public: void WorkerProcess::mainLoop() { processInterface = new EnrollmentWorker(); - processInterface->transform = Transform::make(this->transform,NULL); + processInterface->transform = NULL; processInterface->connections(baseName); processInterface->workerLoop(); delete processInterface; @@ -478,63 +488,98 @@ protected slots: } }; +struct ProcessData +{ + CommunicationManager comm; + ProcessInterface proc; + bool initialized; + ProcessData() + { + initialized = false; + } + + ~ProcessData() + { + comm.sendSignal(CommunicationManager::SHOULD_END); + proc.endProcess(); + comm.shutdown(); + comm.shutDownThread(); + } +}; + + /*! * \ingroup transforms * \brief Interface to a separate process * \author Charles Otto \cite caotto */ -class ProcessWrapperTransform : public TimeVaryingTransform +class ProcessWrapperTransform : public WrapperTransform { Q_OBJECT - Q_PROPERTY(QString transform READ get_transform WRITE set_transform RESET reset_transform) - BR_PROPERTY(QString, transform, "") - QString baseKey; - ProcessInterface workerProcess; - CommunicationManager * comm; + Resource processes; + + Transform * smartCopy(bool & newTransform) + { + newTransform = false; + return this; + } - void projectUpdate(const TemplateList &src, TemplateList &dst) + void project(const TemplateList &src, TemplateList &dst) const { if (src.empty()) return; + + ProcessData *data = processes.acquire(); + if (!data->initialized) + activateProcess(data); - if (!processActive) - { - activateProcess(); - } - comm->sendSignal(CommunicationManager::INPUT_AVAILABLE); - - comm->sendData(src); + CommunicationManager * localComm = &(data->comm); + localComm->sendSignal(CommunicationManager::INPUT_AVAILABLE); - comm->readData(dst); + localComm->sendData(src); + localComm->readData(dst); + processes.release(data); } - void train(const TemplateList& data) { (void) data; } - // create the process void init() { processActive = false; + serialized.clear(); + if (transform) { + QDataStream out(&serialized, QFile::WriteOnly); + transform->serialize(out, true); + } } - void activateProcess() + QByteArray serialized; + void transmitTForm(CommunicationManager * localComm) const { - comm = new CommunicationManager(); - processActive = true; + if (serialized.isEmpty() ) + qFatal("Trying to transmit empty transform!"); + + localComm->writeArray = serialized; + emit localComm->pulseSendSerialized(); + } + void activateProcess(ProcessData * data) const + { + data->initialized = true; // generate a uuid for our local servers QUuid id = QUuid::createUuid(); - baseKey = id.toString(); + QString baseKey = id.toString(); QStringList argumentList; + // We serialize and transmit the transform directly, so algorithm doesn't matter. argumentList.append("-algorithm"); - argumentList.append(transform); + argumentList.append("Identity"); if (!Globals->path.isEmpty()) { argumentList.append("-path"); argumentList.append(Globals->path); @@ -544,36 +589,25 @@ class ProcessWrapperTransform : public TimeVaryingTransform argumentList.append("-slave"); argumentList.append(baseKey); - comm->key = "master_"+baseKey.mid(1,5); + data->comm.key = "master_"+baseKey.mid(1,5); - comm->startServer(baseKey+"_master"); - workerProcess.startProcess(argumentList); - comm->waitForInbound(); - comm->connectToRemote(baseKey+"_worker"); - } + data->comm.startServer(baseKey+"_master"); - bool timeVarying() const { - return false; + data->proc.startProcess(argumentList); + data->comm.waitForInbound(); + data->comm.connectToRemote(baseKey+"_worker"); + + transmitTForm(&(data->comm)); } - ~ProcessWrapperTransform() + bool timeVarying() const { - // end the process - if (this->processActive) { - comm->sendSignal(CommunicationManager::SHOULD_END); - - workerProcess.endProcess(); - processActive = false; - comm->shutdown(); - comm->shutDownThread(); - - delete comm; - } + return false; } public: bool processActive; - ProcessWrapperTransform() : TimeVaryingTransform(false,false) { processActive = false; } + ProcessWrapperTransform() : WrapperTransform(false) { processActive = false; } }; BR_REGISTER(Transform, ProcessWrapperTransform) diff --git a/openbr/plugins/quality.cpp b/openbr/plugins/quality.cpp index 0a02f40..5cbff2d 100644 --- a/openbr/plugins/quality.cpp +++ b/openbr/plugins/quality.cpp @@ -62,9 +62,9 @@ class ImpostorUniquenessMeasureTransform : public Transform dst.file.set("Impostor_Uniqueness_Measure_Bin", ium < mean-stddev ? 0 : (ium < mean+stddev ? 1 : 2)); } - void store(QDataStream &stream) const + void store(QDataStream &stream, bool force) const { - distance->store(stream); + distance->store(stream, force); stream << mean << stddev << impostors; } @@ -199,9 +199,9 @@ class MatchProbabilityDistance : public Distance return mp(score, gaussian); } - void store(QDataStream &stream) const + void store(QDataStream &stream, bool force) const { - distance->store(stream); + distance->store(stream, force); stream << mp; } @@ -264,9 +264,9 @@ class ZScoreDistance : public Distance return score; } - void store(QDataStream &stream) const + void store(QDataStream &stream, bool force) const { - distance->store(stream); + distance->store(stream, force); stream << min << max << mean << stddev; } @@ -335,11 +335,11 @@ class HeatMapDistance : public Distance } } - void store(QDataStream &stream) const + void store(QDataStream &stream, bool force) const { stream << distances.size(); foreach (Distance *distance, distances) - distance->store(stream); + distance->store(stream, force); } void load(QDataStream &stream) diff --git a/openbr/plugins/quantize.cpp b/openbr/plugins/quantize.cpp index 3091e09..bf7477c 100644 --- a/openbr/plugins/quantize.cpp +++ b/openbr/plugins/quantize.cpp @@ -99,7 +99,7 @@ class HistEqQuantizationTransform : public Transform } } - void store(QDataStream &stream) const + void store(QDataStream &stream, bool force) const { stream << thresholds; } @@ -174,7 +174,7 @@ class BayesianQuantizationDistance : public Distance return likelihood; } - void store(QDataStream &stream) const + void store(QDataStream &stream, bool force) const { stream << loglikelihoods; } @@ -527,7 +527,7 @@ private: dst.m().at(0,sizeof(quint16)+i) = getIndex(m.colRange(max(0, i*step-offset), (i+1)*step-offset), centers[i]); } - void store(QDataStream &stream) const + void store(QDataStream &stream, bool force) const { stream << index << centers << ProductQuantizationLUTs[index]; } diff --git a/openbr/plugins/quantize2.cpp b/openbr/plugins/quantize2.cpp index 8d05523..2ecea1c 100644 --- a/openbr/plugins/quantize2.cpp +++ b/openbr/plugins/quantize2.cpp @@ -104,7 +104,7 @@ class BayesianQuantizationTransform : public Transform } } - void store(QDataStream &stream) const + void store(QDataStream &stream, bool force) const { stream << thresholds; } diff --git a/openbr/plugins/random.cpp b/openbr/plugins/random.cpp index 970061a..1bc4841 100644 --- a/openbr/plugins/random.cpp +++ b/openbr/plugins/random.cpp @@ -78,7 +78,7 @@ class RndSubspaceTransform : public Transform remap(src, dst, map, Mat(), INTER_NEAREST); } - void store(QDataStream &stream) const + void store(QDataStream &stream, bool force) const { stream << fraction << weighted << map; } diff --git a/openbr/plugins/slidingwindow.cpp b/openbr/plugins/slidingwindow.cpp index 1ae5663..4dc89ef 100644 --- a/openbr/plugins/slidingwindow.cpp +++ b/openbr/plugins/slidingwindow.cpp @@ -79,9 +79,9 @@ private: } } - void store(QDataStream &stream) const + void store(QDataStream &stream, bool force) const { - transform->store(stream); + transform->store(stream, force); stream << windowHeight; } @@ -289,9 +289,9 @@ private: } } - void store(QDataStream &stream) const + void store(QDataStream &stream, bool force) const { - transform->store(stream); + transform->store(stream, force); stream << aspectRatio << windowHeight; } void load(QDataStream &stream) diff --git a/openbr/plugins/svm.cpp b/openbr/plugins/svm.cpp index c374586..79a6c5d 100644 --- a/openbr/plugins/svm.cpp +++ b/openbr/plugins/svm.cpp @@ -172,7 +172,7 @@ private: dst.file.set(outputVariable, reverseLookup[prediction]); } - void store(QDataStream &stream) const + void store(QDataStream &stream, bool force) const { storeSVM(svm, stream); stream << labelMap << reverseLookup; @@ -270,7 +270,7 @@ private: return svm.predict(delta.reshape(1, 1)); } - void store(QDataStream &stream) const + void store(QDataStream &stream, bool force) const { storeSVM(svm, stream); } diff --git a/openbr/plugins/validate.cpp b/openbr/plugins/validate.cpp index 75bca83..873c9ec 100644 --- a/openbr/plugins/validate.cpp +++ b/openbr/plugins/validate.cpp @@ -114,11 +114,11 @@ class CrossValidateTransform : public MetaTransform transforms[partition]->project(src, dst); } - void store(QDataStream &stream) const + void store(QDataStream &stream, bool force) const { stream << transforms.size(); foreach (Transform *transform, transforms) - transform->store(stream); + transform->store(stream, force); } void load(QDataStream &stream)