diff --git a/openbr/core/core.cpp b/openbr/core/core.cpp index 03f9986..372723b 100644 --- a/openbr/core/core.cpp +++ b/openbr/core/core.cpp @@ -131,18 +131,33 @@ struct AlgorithmCore TemplateList i(TemplateList::fromGallery(input)); - QString shellDescription = "DirectStream([Identity,ProgressCounter("+QString::number(i.length())+")+GalleryOutput("+gallery.flat()+")+Discard],readMode=DistributeFrames)"; + // Trust me, this makes complete sense. + // We're just going to make a pipe with a placeholder first transform + QString pipeDesc = "Identity+ProgressCounter("+QString::number(i.length())+")+GalleryOutput("+gallery.flat()+")+Discard"; + QScopedPointer basePipe(Transform::make(pipeDesc,NULL)); - // Make a stream with a placeholder first transform, and our progress counter/gallery output. - QScopedPointer enrollJob(Transform::make(shellDescription, NULL)); - - CompositeTransform * downcast = dynamic_cast(enrollJob.data()); + CompositeTransform * downcast = dynamic_cast(basePipe.data()); if (downcast == NULL) qFatal("downcast failed?"); + // replace that placeholder with the current algorithm downcast->transforms[0] = this->transform.data(); + + // call init on the pipe to collapse the algorithm (if its top level is a pipe) downcast->init(); - downcast->projectUpdate(i,i); + + // Next, we make a Stream (with placeholder transform) + QString streamDesc = "Stream(Identity, readMode=DistributeFrames)"; + QScopedPointer baseStream(Transform::make(streamDesc, NULL)); + WrapperTransform * wrapper = dynamic_cast (baseStream.data()); + + // replace that placeholder with the pipe we built + wrapper->transform = downcast; + + // and get the final stream's stages by reinterpreting the pipe. Perfectly straightforward. + wrapper->init(); + + wrapper->projectUpdate(i,i); return i.files(); } diff --git a/openbr/plugins/algorithms.cpp b/openbr/plugins/algorithms.cpp index 2bf0895..1b4e0f4 100644 --- a/openbr/plugins/algorithms.cpp +++ b/openbr/plugins/algorithms.cpp @@ -38,7 +38,7 @@ class AlgorithmsInitializer : public Initializer Globals->abbreviations.insert("MedianFace", "Open!Cascade(FrontalFace)+ASEFEyes+Affine(256,256,0.37,0.45)+Center(Median)"); Globals->abbreviations.insert("BlurredFaceDetection", "Open+LimitSize(1024)+SkinMask/(Cvt(Gray)+GradientMask)+And+Morph(Erode,16)+LargestConvexArea"); Globals->abbreviations.insert("DrawFaceDetection", "Open+Cascade(FrontalFace)!ASEFEyes+Draw"); - Globals->abbreviations.insert("ShowFaceDetection", "DrawFaceDetection!Show[distribute=false]"); + Globals->abbreviations.insert("ShowFaceDetection", "DrawFaceDetection!Show"); Globals->abbreviations.insert("OpenBR", "FaceRecognition"); Globals->abbreviations.insert("GenderEstimation", "GenderClassification"); Globals->abbreviations.insert("AgeEstimation", "AgeRegression"); diff --git a/openbr/plugins/openbr_internal.h b/openbr/plugins/openbr_internal.h index e40a770..b56daad 100644 --- a/openbr/plugins/openbr_internal.h +++ b/openbr/plugins/openbr_internal.h @@ -154,6 +154,53 @@ protected: } }; +/*! + * \brief Interface for transforms that act as decorators of another transform + */ +class BR_EXPORT WrapperTransform : public TimeVaryingTransform +{ + Q_OBJECT +public: + WrapperTransform(bool independent = true) : TimeVaryingTransform(independent) + { + } + + Q_PROPERTY(br::Transform *transform READ get_transform WRITE set_transform RESET reset_transform STORED false) + BR_PROPERTY(br::Transform *, transform, NULL) + + bool timeVarying() const { return transform->timeVarying(); } + + void project(const Template &src, Template &dst) const + { + transform->project(src,dst); + } + + void projectUpdate(const Template &src, Template &dst) + { + transform->projectUpdate(src,dst); + } + void projectUpdate(const TemplateList & src, TemplateList & dst) + { + transform->projectUpdate(src,dst); + } + + void train(const QList & data) + { + transform->train(data); + } + + virtual void finalize(TemplateList & output) + { + transform->finalize(output); + } + + void init() + { + if (transform) + this->trainable = transform->trainable; + } + +}; /*! * \brief A MetaTransform that aggregates some sub-transforms diff --git a/openbr/plugins/stream.cpp b/openbr/plugins/stream.cpp index a84167e..982d061 100644 --- a/openbr/plugins/stream.cpp +++ b/openbr/plugins/stream.cpp @@ -1013,21 +1013,20 @@ QMutex DirectStreamTransform::poolsAccess; BR_REGISTER(Transform, DirectStreamTransform) -; - -class StreamTransform : public TimeVaryingTransform +class StreamTransform : public WrapperTransform { Q_OBJECT public: - StreamTransform() : TimeVaryingTransform(false) + StreamTransform() : WrapperTransform(false) { } - Q_PROPERTY(br::Transform *transform READ get_transform WRITE set_transform RESET reset_transform STORED false) Q_PROPERTY(int activeFrames READ get_activeFrames WRITE set_activeFrames RESET reset_activeFrames) - BR_PROPERTY(br::Transform *, transform, NULL) + Q_PROPERTY(br::DirectStreamTransform::StreamModes readMode READ get_readMode WRITE set_readMode RESET reset_readMode) + BR_PROPERTY(int, activeFrames, 100) + BR_PROPERTY(br::DirectStreamTransform::StreamModes, readMode, br::DirectStreamTransform::Auto) bool timeVarying() const { return true; } @@ -1068,6 +1067,7 @@ public: basis.setParent(this->parent()); basis.transforms.clear(); basis.activeFrames = this->activeFrames; + basis.readMode = this->readMode; // We need at least a CompositeTransform * to acess transform's children. CompositeTransform * downcast = dynamic_cast (transform); @@ -1468,6 +1468,7 @@ void DirectStreamTransform::init() // Are our children time varying or not? This decides whether // we run them in single threaded or multi threaded stages + stage_variance.clear(); stage_variance.reserve(transforms.size()); foreach (const br::Transform *transform, transforms) { stage_variance.append(transform->timeVarying());