Commit caa661c922a52438647cc8d8f1e85a05c8384d03
1 parent
4887d291
Add a simplified interface for Stream
Now, Stream takes a single transform argument. The old interface remains, renamed to DirectStream. If that transform is a pipe, Stream will split its child transforms appropriately, and set up a DirectStream. Adjacent non-timeVarying transforms are grouped in pipes, time varying transforms all get separate stages. The default behavior isn't necessarily ideal if e.g. a lot of light time varying stages are present, in that case its possible the additional synchronization costs will outweight gains from paraellelism (due to having more separate single thread stages). In these cases DirectStream may be preferable, however current algorithms seem largely unaffected.
Showing
2 changed files
with
157 additions
and
11 deletions
openbr/plugins/algorithms.cpp
| ... | ... | @@ -46,12 +46,13 @@ class AlgorithmsInitializer : public Initializer |
| 46 | 46 | Globals->abbreviations.insert("CropFace", "Open+Cvt(Gray)+Cascade(FrontalFace)+ASEFEyes+Affine(128,128,0.25,0.35)"); |
| 47 | 47 | |
| 48 | 48 | // Video |
| 49 | - Globals->abbreviations.insert("DisplayVideo", "Stream([FPSLimit(30)+Show(false,[FrameNumber])+Discard])"); | |
| 50 | - Globals->abbreviations.insert("PerFrameDetection", "Stream([SaveMat(original)+Cvt(Gray)+Cascade(FrontalFace)+ASEFEyes+RestoreMat(original)+Draw(inPlace=true),Show(false,[FrameNumber])+Discard])"); | |
| 51 | - Globals->abbreviations.insert("AgeGenderDemo", "Stream([SaveMat(original)+Cvt(Gray)+Cascade(FrontalFace)+Expand+<FaceClassificationRegistration>+<FaceClassificationExtraction>+<AgeRegressor>/<GenderClassifier>+Discard+RestoreMat(original)+Draw(inPlace=true)+DrawPropertiesPoint([Age,Gender],Affine_0,inPlace=true)+SaveMat(original)+Discard+Contract,RestoreMat(original)+FPSCalc+Show(false,[AvgFPS,Age,Gender])+Discard])"); | |
| 52 | - Globals->abbreviations.insert("HOG", "Stream([DropFrames(5)+Cvt(Gray)+KeyPointDetector(SIFT)+ROI+Expand+Resize(32,32)+Gradient+RectRegions+Bin(0,360,8)+Hist(8)+Cat])+Contract+CatRows+KMeans(500)+Hist(500)+SVM"); | |
| 53 | - Globals->abbreviations.insert("HOF", "Stream([DropFrames(5)+KeyPointDetector(SIFT),AggregateFrames(2),OpticalFlow+ROI+Expand+Resize(32,32)+Gradient+RectRegions+Bin(0,360,8)+Hist(8)+Cat])+Contract+CatRows+KMeans(500)+Hist(500)"); | |
| 54 | - Globals->abbreviations.insert("HOGHOF", "Stream([DropFrames(5)+Cvt(Gray),KeyPointDetector(SIFT),AggregateFrames(2),(OpticalFlow+Gradient+Bin(0,360,8)+ROI+Hist(8))/(First+Gradient+Bin(0,360,8)+ROI+Hist(8)),CatCols])+Contract+CatRows+KMeans(500)+Hist(500)"); | |
| 49 | + Globals->abbreviations.insert("DisplayVideo", "Stream(FPSLimit(30)+Show(false,[FrameNumber])+Discard)"); | |
| 50 | + Globals->abbreviations.insert("PerFrameDetection", "Stream(SaveMat(original)+Cvt(Gray)+Cascade(FrontalFace)+ASEFEyes+RestoreMat(original)+Draw(inPlace=true)+Show(false,[FrameNumber])+Discard)"); | |
| 51 | + Globals->abbreviations.insert("AgeGenderDemo", "Stream(SaveMat(original)+Cvt(Gray)+Cascade(FrontalFace)+Expand+<FaceClassificationRegistration>+<FaceClassificationExtraction>+<AgeRegressor>/<GenderClassifier>+Discard+RestoreMat(original)+Draw(inPlace=true)+DrawPropertiesPoint([Age,Gender],Affine_0,inPlace=true)+SaveMat(original)+Discard+Contract+RestoreMat(original)+FPSCalc+Show(false,[AvgFPS,Age,Gender])+Discard)"); | |
| 52 | + | |
| 53 | + Globals->abbreviations.insert("HOG", "Stream(DropFrames(5)+Cvt(Gray)+KeyPointDetector(SIFT)+ROI+Expand+Resize(32,32)+Gradient+RectRegions+Bin(0,360,8)+Hist(8)+Cat)+Contract+CatRows+KMeans(500)+Hist(500)+SVM"); | |
| 54 | + Globals->abbreviations.insert("HOF", "Stream(DropFrames(5)+KeyPointDetector(SIFT)+AggregateFrames(2)+OpticalFlow+ROI+Expand+Resize(32,32)+Gradient+RectRegions+Bin(0,360,8)+Hist(8)+Cat)+Contract+CatRows+KMeans(500)+Hist(500)"); | |
| 55 | + Globals->abbreviations.insert("HOGHOF", "Stream(DropFrames(5)+Cvt(Gray),KeyPointDetector(SIFT)+AggregateFrames(2)+(OpticalFlow+Gradient+Bin(0,360,8)+ROI+Hist(8))/(First+Gradient+Bin(0,360,8)+ROI+Hist(8))+CatCols)+Contract+CatRows+KMeans(500)+Hist(500)"); | |
| 55 | 56 | |
| 56 | 57 | // Generic Image Processing |
| 57 | 58 | Globals->abbreviations.insert("SIFT", "Open+KeyPointDetector(SIFT)+KeyPointDescriptor(SIFT):KeyPointMatcher(BruteForce)"); | ... | ... |
openbr/plugins/stream.cpp
| ... | ... | @@ -649,7 +649,7 @@ public: |
| 649 | 649 | class ProcessingStage |
| 650 | 650 | { |
| 651 | 651 | public: |
| 652 | - friend class StreamTransform; | |
| 652 | + friend class DirectStreamTransform; | |
| 653 | 653 | public: |
| 654 | 654 | ProcessingStage(int nThreads = 1) |
| 655 | 655 | { |
| ... | ... | @@ -1015,13 +1015,16 @@ public: |
| 1015 | 1015 | }; |
| 1016 | 1016 | |
| 1017 | 1017 | |
| 1018 | -class StreamTransform : public CompositeTransform | |
| 1018 | + | |
| 1019 | +class DirectStreamTransform : public CompositeTransform | |
| 1019 | 1020 | { |
| 1020 | 1021 | Q_OBJECT |
| 1021 | 1022 | public: |
| 1022 | 1023 | Q_PROPERTY(int activeFrames READ get_activeFrames WRITE set_activeFrames RESET reset_activeFrames) |
| 1023 | 1024 | BR_PROPERTY(int, activeFrames, 100) |
| 1024 | 1025 | |
| 1026 | + friend class StreamTransfrom; | |
| 1027 | + | |
| 1025 | 1028 | void train(const TemplateList & data) |
| 1026 | 1029 | { |
| 1027 | 1030 | if (!trainable) { |
| ... | ... | @@ -1115,6 +1118,10 @@ public: |
| 1115 | 1118 | { |
| 1116 | 1119 | if (transforms.isEmpty()) return; |
| 1117 | 1120 | |
| 1121 | + for (int i=0; i < processingStages.size();i++) | |
| 1122 | + delete processingStages[i]; | |
| 1123 | + processingStages.clear(); | |
| 1124 | + | |
| 1118 | 1125 | // call CompositeTransform::init so that trainable is set |
| 1119 | 1126 | // correctly. |
| 1120 | 1127 | CompositeTransform::init(); |
| ... | ... | @@ -1190,12 +1197,13 @@ public: |
| 1190 | 1197 | collectionStage->nextStage = readStage; |
| 1191 | 1198 | } |
| 1192 | 1199 | |
| 1193 | - ~StreamTransform() | |
| 1200 | + ~DirectStreamTransform() | |
| 1194 | 1201 | { |
| 1195 | 1202 | // Delete all the stages |
| 1196 | 1203 | for (int i = 0; i < processingStages.size(); i++) { |
| 1197 | 1204 | delete processingStages[i]; |
| 1198 | 1205 | } |
| 1206 | + processingStages.clear(); | |
| 1199 | 1207 | } |
| 1200 | 1208 | |
| 1201 | 1209 | protected: |
| ... | ... | @@ -1237,12 +1245,149 @@ protected: |
| 1237 | 1245 | } |
| 1238 | 1246 | }; |
| 1239 | 1247 | |
| 1240 | -QHash<QObject *, QThreadPool *> StreamTransform::pools; | |
| 1241 | -QMutex StreamTransform::poolsAccess; | |
| 1248 | +QHash<QObject *, QThreadPool *> DirectStreamTransform::pools; | |
| 1249 | +QMutex DirectStreamTransform::poolsAccess; | |
| 1250 | + | |
| 1251 | +BR_REGISTER(Transform, DirectStreamTransform) | |
| 1252 | + | |
| 1253 | +; | |
| 1254 | + | |
| 1255 | +class StreamTransform : public TimeVaryingTransform | |
| 1256 | +{ | |
| 1257 | + Q_OBJECT | |
| 1258 | + | |
| 1259 | +public: | |
| 1260 | + StreamTransform() : TimeVaryingTransform(false) | |
| 1261 | + { | |
| 1262 | + } | |
| 1263 | + | |
| 1264 | + Q_PROPERTY(br::Transform *transform READ get_transform WRITE set_transform STORED false) | |
| 1265 | + Q_PROPERTY(int activeFrames READ get_activeFrames WRITE set_activeFrames RESET reset_activeFrames) | |
| 1266 | + BR_PROPERTY(br::Transform *, transform, NULL) | |
| 1267 | + BR_PROPERTY(int, activeFrames, 100) | |
| 1268 | + | |
| 1269 | + bool timeVarying() const { return true; } | |
| 1270 | + | |
| 1271 | + void project(const Template &src, Template &dst) const | |
| 1272 | + { | |
| 1273 | + basis.project(src,dst); | |
| 1274 | + } | |
| 1275 | + | |
| 1276 | + void projectUpdate(const Template &src, Template &dst) | |
| 1277 | + { | |
| 1278 | + basis.projectUpdate(src,dst); | |
| 1279 | + } | |
| 1280 | + void projectUpdate(const TemplateList & src, TemplateList & dst) | |
| 1281 | + { | |
| 1282 | + basis.projectUpdate(src,dst); | |
| 1283 | + } | |
| 1284 | + | |
| 1285 | + | |
| 1286 | + void train(const TemplateList & data) | |
| 1287 | + { | |
| 1288 | + basis.train(data); | |
| 1289 | + } | |
| 1290 | + | |
| 1291 | + virtual void finalize(TemplateList & output) | |
| 1292 | + { | |
| 1293 | + (void) output; | |
| 1294 | + // Nothing in particular to do here, stream calls finalize | |
| 1295 | + // on all child transforms as part of projectUpdate | |
| 1296 | + } | |
| 1297 | + | |
| 1298 | + // reinterpret transform, set up the actual stream. We can only reinterpret pipes | |
| 1299 | + void init() | |
| 1300 | + { | |
| 1301 | + if (!transform) | |
| 1302 | + return; | |
| 1303 | + | |
| 1304 | + basis.setParent(this->parent()); | |
| 1305 | + basis.transforms.clear(); | |
| 1306 | + basis.activeFrames = this->activeFrames; | |
| 1307 | + | |
| 1308 | + // We need at least a CompositeTransform * to acess transform's children. | |
| 1309 | + CompositeTransform * downcast = dynamic_cast<CompositeTransform *> (transform); | |
| 1310 | + | |
| 1311 | + // If this isn't even a composite transform, or it's not a pipe, just set up | |
| 1312 | + // basis with 1 stage. | |
| 1313 | + if (!downcast || QString(transform->metaObject()->className()) != "br::PipeTransform") | |
| 1314 | + { | |
| 1315 | + basis.transforms.append(transform); | |
| 1316 | + basis.init(); | |
| 1317 | + return; | |
| 1318 | + } | |
| 1319 | + if (downcast->transforms.empty()) | |
| 1320 | + { | |
| 1321 | + qWarning("Trying to set up empty stream"); | |
| 1322 | + basis.init(); | |
| 1323 | + return; | |
| 1324 | + } | |
| 1325 | + | |
| 1326 | + // OK now we will regroup downcast's children | |
| 1327 | + QList<QList<Transform *> > sets; | |
| 1328 | + sets.append(QList<Transform *> ()); | |
| 1329 | + sets.last().append(downcast->transforms[0]); | |
| 1330 | + if (downcast->transforms[0]->timeVarying()) | |
| 1331 | + sets.append(QList<Transform *> ()); | |
| 1332 | + | |
| 1333 | + for (int i=1;i < downcast->transforms.size(); i++) { | |
| 1334 | + // If this is time varying it becomse its own stage | |
| 1335 | + if (downcast->transforms[i]->timeVarying()) { | |
| 1336 | + // If a set was already active, we add another one | |
| 1337 | + if (!sets.last().empty()) { | |
| 1338 | + sets.append(QList<Transform *>()); | |
| 1339 | + } | |
| 1340 | + // add the item | |
| 1341 | + sets.last().append(downcast->transforms[i]); | |
| 1342 | + // Add another set to indicate separation. | |
| 1343 | + sets.append(QList<Transform *>()); | |
| 1344 | + } | |
| 1345 | + // otherwise, we can combine non time-varying stages | |
| 1346 | + else { | |
| 1347 | + sets.last().append(downcast->transforms[i]); | |
| 1348 | + } | |
| 1349 | + | |
| 1350 | + } | |
| 1351 | + if (sets.last().empty()) | |
| 1352 | + sets.removeLast(); | |
| 1353 | + | |
| 1354 | + QList<Transform *> transform_set; | |
| 1355 | + transform_set.reserve(sets.size()); | |
| 1356 | + for (int i=0; i < sets.size(); i++) { | |
| 1357 | + // If this is a single transform set, we add that to the list | |
| 1358 | + if (sets[i].size() == 1 ) { | |
| 1359 | + transform_set.append(sets[i].at(0)); | |
| 1360 | + } | |
| 1361 | + //otherwise we build a pipe | |
| 1362 | + else { | |
| 1363 | + CompositeTransform * pipe = dynamic_cast<CompositeTransform *>(Transform::make("Pipe([])", this)); | |
| 1364 | + pipe->transforms = sets[i]; | |
| 1365 | + pipe->init(); | |
| 1366 | + transform_set.append(pipe); | |
| 1367 | + } | |
| 1368 | + } | |
| 1369 | + | |
| 1370 | + basis.transforms = transform_set; | |
| 1371 | + basis.init(); | |
| 1372 | + } | |
| 1373 | + | |
| 1374 | + Transform * smartCopy() | |
| 1375 | + { | |
| 1376 | + // We just want the DirectStream to begin with, so just return a copy of that. | |
| 1377 | + DirectStreamTransform * res = (DirectStreamTransform *) basis.smartCopy(); | |
| 1378 | + res->activeFrames = this->activeFrames; | |
| 1379 | + return res; | |
| 1380 | + } | |
| 1381 | + | |
| 1382 | + | |
| 1383 | +private: | |
| 1384 | + DirectStreamTransform basis; | |
| 1385 | +}; | |
| 1242 | 1386 | |
| 1243 | 1387 | BR_REGISTER(Transform, StreamTransform) |
| 1244 | 1388 | |
| 1245 | 1389 | |
| 1390 | + | |
| 1246 | 1391 | } // namespace br |
| 1247 | 1392 | |
| 1248 | 1393 | #include "stream.moc" | ... | ... |