Commit c6a5dfecbf2a03795fc25f6c334202e285f6d385
1 parent
a2489d31
In enroll, apply Stream's staging logic to the base algorithm
When setting up the stream in enroll, use the Stream interface's staging logic on the input algorithm. Making this happen is slightly complex when we don't particularly have header files, but on the plus side algorithms incorporating something like Show (e.g. ShowFaceDetection) will not be used as a single threaded stage in the process.
Showing
4 changed files
with
76 additions
and
13 deletions
openbr/core/core.cpp
| ... | ... | @@ -131,18 +131,33 @@ struct AlgorithmCore |
| 131 | 131 | |
| 132 | 132 | TemplateList i(TemplateList::fromGallery(input)); |
| 133 | 133 | |
| 134 | - QString shellDescription = "DirectStream([Identity,ProgressCounter("+QString::number(i.length())+")+GalleryOutput("+gallery.flat()+")+Discard],readMode=DistributeFrames)"; | |
| 134 | + // Trust me, this makes complete sense. | |
| 135 | + // We're just going to make a pipe with a placeholder first transform | |
| 136 | + QString pipeDesc = "Identity+ProgressCounter("+QString::number(i.length())+")+GalleryOutput("+gallery.flat()+")+Discard"; | |
| 137 | + QScopedPointer<Transform> basePipe(Transform::make(pipeDesc,NULL)); | |
| 135 | 138 | |
| 136 | - // Make a stream with a placeholder first transform, and our progress counter/gallery output. | |
| 137 | - QScopedPointer<Transform> enrollJob(Transform::make(shellDescription, NULL)); | |
| 138 | - | |
| 139 | - CompositeTransform * downcast = dynamic_cast<CompositeTransform *>(enrollJob.data()); | |
| 139 | + CompositeTransform * downcast = dynamic_cast<CompositeTransform *>(basePipe.data()); | |
| 140 | 140 | if (downcast == NULL) |
| 141 | 141 | qFatal("downcast failed?"); |
| 142 | 142 | |
| 143 | + // replace that placeholder with the current algorithm | |
| 143 | 144 | downcast->transforms[0] = this->transform.data(); |
| 145 | + | |
| 146 | + // call init on the pipe to collapse the algorithm (if its top level is a pipe) | |
| 144 | 147 | downcast->init(); |
| 145 | - downcast->projectUpdate(i,i); | |
| 148 | + | |
| 149 | + // Next, we make a Stream (with placeholder transform) | |
| 150 | + QString streamDesc = "Stream(Identity, readMode=DistributeFrames)"; | |
| 151 | + QScopedPointer<Transform> baseStream(Transform::make(streamDesc, NULL)); | |
| 152 | + WrapperTransform * wrapper = dynamic_cast<WrapperTransform *> (baseStream.data()); | |
| 153 | + | |
| 154 | + // replace that placeholder with the pipe we built | |
| 155 | + wrapper->transform = downcast; | |
| 156 | + | |
| 157 | + // and get the final stream's stages by reinterpreting the pipe. Perfectly straightforward. | |
| 158 | + wrapper->init(); | |
| 159 | + | |
| 160 | + wrapper->projectUpdate(i,i); | |
| 146 | 161 | |
| 147 | 162 | return i.files(); |
| 148 | 163 | } | ... | ... |
openbr/plugins/algorithms.cpp
| ... | ... | @@ -38,7 +38,7 @@ class AlgorithmsInitializer : public Initializer |
| 38 | 38 | Globals->abbreviations.insert("MedianFace", "Open!Cascade(FrontalFace)+ASEFEyes+Affine(256,256,0.37,0.45)+Center(Median)"); |
| 39 | 39 | Globals->abbreviations.insert("BlurredFaceDetection", "Open+LimitSize(1024)+SkinMask/(Cvt(Gray)+GradientMask)+And+Morph(Erode,16)+LargestConvexArea"); |
| 40 | 40 | Globals->abbreviations.insert("DrawFaceDetection", "Open+Cascade(FrontalFace)!ASEFEyes+Draw"); |
| 41 | - Globals->abbreviations.insert("ShowFaceDetection", "DrawFaceDetection!Show[distribute=false]"); | |
| 41 | + Globals->abbreviations.insert("ShowFaceDetection", "DrawFaceDetection!Show"); | |
| 42 | 42 | Globals->abbreviations.insert("OpenBR", "FaceRecognition"); |
| 43 | 43 | Globals->abbreviations.insert("GenderEstimation", "GenderClassification"); |
| 44 | 44 | Globals->abbreviations.insert("AgeEstimation", "AgeRegression"); | ... | ... |
openbr/plugins/openbr_internal.h
| ... | ... | @@ -154,6 +154,53 @@ protected: |
| 154 | 154 | } |
| 155 | 155 | }; |
| 156 | 156 | |
| 157 | +/*! | |
| 158 | + * \brief Interface for transforms that act as decorators of another transform | |
| 159 | + */ | |
| 160 | +class BR_EXPORT WrapperTransform : public TimeVaryingTransform | |
| 161 | +{ | |
| 162 | + Q_OBJECT | |
| 163 | +public: | |
| 164 | + WrapperTransform(bool independent = true) : TimeVaryingTransform(independent) | |
| 165 | + { | |
| 166 | + } | |
| 167 | + | |
| 168 | + Q_PROPERTY(br::Transform *transform READ get_transform WRITE set_transform RESET reset_transform STORED false) | |
| 169 | + BR_PROPERTY(br::Transform *, transform, NULL) | |
| 170 | + | |
| 171 | + bool timeVarying() const { return transform->timeVarying(); } | |
| 172 | + | |
| 173 | + void project(const Template &src, Template &dst) const | |
| 174 | + { | |
| 175 | + transform->project(src,dst); | |
| 176 | + } | |
| 177 | + | |
| 178 | + void projectUpdate(const Template &src, Template &dst) | |
| 179 | + { | |
| 180 | + transform->projectUpdate(src,dst); | |
| 181 | + } | |
| 182 | + void projectUpdate(const TemplateList & src, TemplateList & dst) | |
| 183 | + { | |
| 184 | + transform->projectUpdate(src,dst); | |
| 185 | + } | |
| 186 | + | |
| 187 | + void train(const QList<TemplateList> & data) | |
| 188 | + { | |
| 189 | + transform->train(data); | |
| 190 | + } | |
| 191 | + | |
| 192 | + virtual void finalize(TemplateList & output) | |
| 193 | + { | |
| 194 | + transform->finalize(output); | |
| 195 | + } | |
| 196 | + | |
| 197 | + void init() | |
| 198 | + { | |
| 199 | + if (transform) | |
| 200 | + this->trainable = transform->trainable; | |
| 201 | + } | |
| 202 | + | |
| 203 | +}; | |
| 157 | 204 | |
| 158 | 205 | /*! |
| 159 | 206 | * \brief A MetaTransform that aggregates some sub-transforms | ... | ... |
openbr/plugins/stream.cpp
| ... | ... | @@ -1013,21 +1013,20 @@ QMutex DirectStreamTransform::poolsAccess; |
| 1013 | 1013 | |
| 1014 | 1014 | BR_REGISTER(Transform, DirectStreamTransform) |
| 1015 | 1015 | |
| 1016 | -; | |
| 1017 | - | |
| 1018 | -class StreamTransform : public TimeVaryingTransform | |
| 1016 | +class StreamTransform : public WrapperTransform | |
| 1019 | 1017 | { |
| 1020 | 1018 | Q_OBJECT |
| 1021 | 1019 | |
| 1022 | 1020 | public: |
| 1023 | - StreamTransform() : TimeVaryingTransform(false) | |
| 1021 | + StreamTransform() : WrapperTransform(false) | |
| 1024 | 1022 | { |
| 1025 | 1023 | } |
| 1026 | 1024 | |
| 1027 | - Q_PROPERTY(br::Transform *transform READ get_transform WRITE set_transform RESET reset_transform STORED false) | |
| 1028 | 1025 | Q_PROPERTY(int activeFrames READ get_activeFrames WRITE set_activeFrames RESET reset_activeFrames) |
| 1029 | - BR_PROPERTY(br::Transform *, transform, NULL) | |
| 1026 | + Q_PROPERTY(br::DirectStreamTransform::StreamModes readMode READ get_readMode WRITE set_readMode RESET reset_readMode) | |
| 1027 | + | |
| 1030 | 1028 | BR_PROPERTY(int, activeFrames, 100) |
| 1029 | + BR_PROPERTY(br::DirectStreamTransform::StreamModes, readMode, br::DirectStreamTransform::Auto) | |
| 1031 | 1030 | |
| 1032 | 1031 | bool timeVarying() const { return true; } |
| 1033 | 1032 | |
| ... | ... | @@ -1068,6 +1067,7 @@ public: |
| 1068 | 1067 | basis.setParent(this->parent()); |
| 1069 | 1068 | basis.transforms.clear(); |
| 1070 | 1069 | basis.activeFrames = this->activeFrames; |
| 1070 | + basis.readMode = this->readMode; | |
| 1071 | 1071 | |
| 1072 | 1072 | // We need at least a CompositeTransform * to acess transform's children. |
| 1073 | 1073 | CompositeTransform * downcast = dynamic_cast<CompositeTransform *> (transform); |
| ... | ... | @@ -1468,6 +1468,7 @@ void DirectStreamTransform::init() |
| 1468 | 1468 | |
| 1469 | 1469 | // Are our children time varying or not? This decides whether |
| 1470 | 1470 | // we run them in single threaded or multi threaded stages |
| 1471 | + stage_variance.clear(); | |
| 1471 | 1472 | stage_variance.reserve(transforms.size()); |
| 1472 | 1473 | foreach (const br::Transform *transform, transforms) { |
| 1473 | 1474 | stage_variance.append(transform->timeVarying()); | ... | ... |