Commit e465e02e43c1610e59a1df09c4a6c5a5639402c2

Authored by Charles Otto
1 parent fe72ae12

API changes in support of adding a stream based processing model

Allow inherited properties to be set via a transforms argument list at the
command line, but exclude properties of classes that directly inherit from
br::Object (this excludes e.g. Transform's properties which should be set
during training). Properties are set in the same order that is currently used
(the order of their property declarations), but all superclass properties are
set before base class properties.

Changes to the Transform API: add a set of methods supporting non-const project
operations. Since overloading with a non-const version of the same function
name won't work, the methods are called projectUpdate, since they perform the
expected project operation, but also allow the transform to update its internal
state. This can be used to support tracking operations, as well as online
learning methods. All current classes can implement projectUpdate by calling
their const project method.

To handle variable input/output situations (e.g. a tracking function may only
wish to emit a single template per unique detected object), a "finalize" method
is also added to Transform, to be called to indicate that no further calls to
projectUpdate will be made, and allow the transform to emit any final templates
This is only relevant for non-const project since a time-invariant transform
has no way to manage an internal list of templates it is waiting to emit.

Add a "timeVarying" method to Transform, which returns a bool indicate whether
or not the transform is time-invariant (only projectUpdate should be called
for a time-varying transform).

Refactor Transforms that maintain a list of subclasses (pipe transform, expand
transform, fork transform, random transform) to inherit from a common subclass
(CompositeTransform). Time variance for composite transforms is set based on
their child transforms -- a composite transform with any time-varying children
is time-varying. Some code consolidation via the shared subclass.
sdk/openbr_plugin.cpp
@@ -494,7 +494,8 @@ TemplateList TemplateList::relabel(const TemplateList &tl) @@ -494,7 +494,8 @@ TemplateList TemplateList::relabel(const TemplateList &tl)
494 QStringList Object::parameters() const 494 QStringList Object::parameters() const
495 { 495 {
496 QStringList parameters; 496 QStringList parameters;
497 - for (int i=metaObject()->propertyOffset(); i<metaObject()->propertyCount(); i++) { 497 +
  498 + for (int i = first_available_property_idx; i < metaObject()->propertyCount();i++) {
498 QMetaProperty property = metaObject()->property(i); 499 QMetaProperty property = metaObject()->property(i);
499 if (property.isStored(this)) continue; 500 if (property.isStored(this)) continue;
500 parameters.append(QString("%1 %2 = %3").arg(property.typeName(), property.name(), property.read(this).toString())); 501 parameters.append(QString("%1 %2 = %3").arg(property.typeName(), property.name(), property.read(this).toString()));
@@ -713,18 +714,37 @@ void Object::init(const File &amp;file_) @@ -713,18 +714,37 @@ void Object::init(const File &amp;file_)
713 // Set name 714 // Set name
714 QString name = metaObject()->className(); 715 QString name = metaObject()->className();
715 if (name.startsWith("br::")) name = name.right(name.size()-4); 716 if (name.startsWith("br::")) name = name.right(name.size()-4);
716 - const QMetaObject *superClass = metaObject()->superClass(); 717 +
  718 + first_available_property_idx = metaObject()->propertyCount();
  719 +
  720 + const QMetaObject * baseClass = metaObject();
  721 + const QMetaObject * superClass = metaObject()->superClass();
  722 +
717 while (superClass != NULL) { 723 while (superClass != NULL) {
  724 + const QMetaObject * nextClass = superClass->superClass();
  725 +
  726 + // baseClass <- something <- br::Object
  727 + // baseClass is the highest class whose properties we can set via positional arguments
  728 + if (nextClass && !strcmp(nextClass->className(),"br::Object")) {
  729 + first_available_property_idx = baseClass->propertyOffset();
  730 + }
  731 +
718 QString superClassName = superClass->className(); 732 QString superClassName = superClass->className();
  733 +
  734 + // strip br:: prefix from superclass name
719 if (superClassName.startsWith("br::")) 735 if (superClassName.startsWith("br::"))
720 superClassName = superClassName.right(superClassName.size()-4); 736 superClassName = superClassName.right(superClassName.size()-4);
  737 +
  738 + // Strip superclass name from base class name (e.g. PipeTransform -> Pipe)
721 if (name.endsWith(superClassName)) 739 if (name.endsWith(superClassName))
722 name = name.left(name.size() - superClassName.size()); 740 name = name.left(name.size() - superClassName.size());
  741 + baseClass = superClass;
723 superClass = superClass->superClass(); 742 superClass = superClass->superClass();
  743 +
724 } 744 }
725 setObjectName(name); 745 setObjectName(name);
726 746
727 - // Set properties 747 + // Reset all properties
728 for (int i=0; i<metaObject()->propertyCount(); i++) { 748 for (int i=0; i<metaObject()->propertyCount(); i++) {
729 QMetaProperty property = metaObject()->property(i); 749 QMetaProperty property = metaObject()->property(i);
730 if (property.isResettable()) 750 if (property.isResettable())
@@ -734,8 +754,17 @@ void Object::init(const File &amp;file_) @@ -734,8 +754,17 @@ void Object::init(const File &amp;file_)
734 754
735 foreach (QString key, file.localKeys()) { 755 foreach (QString key, file.localKeys()) {
736 const QString value = file.value(key).toString(); 756 const QString value = file.value(key).toString();
737 - if (key.startsWith("_Arg"))  
738 - key = metaObject()->property(metaObject()->propertyOffset()+key.mid(4).toInt()).name(); 757 +
  758 + if (key.startsWith(("_Arg"))) {
  759 + int argument_number = key.mid(4).toInt();
  760 + int target_idx = argument_number + first_available_property_idx;
  761 +
  762 + if (target_idx >= metaObject()->propertyCount()) {
  763 + qWarning("too many arguments for transform, ignoring %s\n", qPrintable(value));
  764 + continue;
  765 + }
  766 + key = metaObject()->property(target_idx).name();
  767 + }
739 setProperty(key, value); 768 setProperty(key, value);
740 } 769 }
741 770
sdk/openbr_plugin.h
@@ -421,6 +421,9 @@ class BR_EXPORT Object : public QObject @@ -421,6 +421,9 @@ class BR_EXPORT Object : public QObject
421 { 421 {
422 Q_OBJECT 422 Q_OBJECT
423 423
  424 + // Index of the first property that can be set via command line arguments
  425 + int first_available_property_idx;
  426 +
424 public: 427 public:
425 File file; /*!< \brief The file used to construct the plugin. */ 428 File file; /*!< \brief The file used to construct the plugin. */
426 429
@@ -989,6 +992,48 @@ public: @@ -989,6 +992,48 @@ public:
989 virtual void backProject(const Template &dst, Template &src) const; /*!< \brief Invert the transform. */ 992 virtual void backProject(const Template &dst, Template &src) const; /*!< \brief Invert the transform. */
990 virtual void backProject(const TemplateList &dst, TemplateList &src) const; /*!< \brief Invert the transform. */ 993 virtual void backProject(const TemplateList &dst, TemplateList &src) const; /*!< \brief Invert the transform. */
991 994
  995 + /*!< \brief Apply the transform, may update the transform's internal state */
  996 + virtual void projectUpdate(const Template &src, Template &dst)
  997 + {
  998 + project(src, dst);
  999 + }
  1000 +
  1001 + /*!< \brief Apply the transform, may update the transform's internal state */
  1002 + virtual void projectUpdate(const TemplateList &src, TemplateList &dst)
  1003 + {
  1004 + project(src,dst);
  1005 + }
  1006 +
  1007 + // inplace project/update
  1008 + void projectUpdate(Template & src)
  1009 + {
  1010 + Template dst = src;
  1011 + projectUpdate(src, dst);
  1012 + src = dst;
  1013 + }
  1014 + void projectUpdate(TemplateList & src)
  1015 + {
  1016 + TemplateList dst = src;
  1017 + projectUpdate(src, dst);
  1018 + src = dst;
  1019 + }
  1020 +
  1021 + // Time-varying transforms may move away from a single input->single output model, and only emit
  1022 + // templates under some conditions (e.g. a tracking thing may emit a template for each detected
  1023 + // unique object), in this case finalize indicates that no further calls to project will be made
  1024 + // and the transform can emit a final set if templates if it wants. Time-invariant transforms
  1025 + // don't have to do anything.
  1026 + virtual void finalize(TemplateList & output)
  1027 + {
  1028 + output = TemplateList();
  1029 + }
  1030 +
  1031 + /*!
  1032 + * \brief Does the transform require the non-const version of project? Can vary for aggregation type transforms
  1033 + * (if their children are time varying, they are also time varying, otherwise probably not)
  1034 + */
  1035 + virtual bool timeVarying() const {return false;}
  1036 +
992 /*! 1037 /*!
993 * \brief Convenience function equivalent to project(). 1038 * \brief Convenience function equivalent to project().
994 */ 1039 */
@@ -1051,6 +1096,33 @@ inline QDataStream &amp;operator&gt;&gt;(QDataStream &amp;stream, Transform &amp;f) @@ -1051,6 +1096,33 @@ inline QDataStream &amp;operator&gt;&gt;(QDataStream &amp;stream, Transform &amp;f)
1051 return stream; 1096 return stream;
1052 } 1097 }
1053 1098
  1099 +
  1100 +/*!
  1101 + * \brief A br::Transform for which the results of project may change due to prior calls to project
  1102 + */
  1103 +class BR_EXPORT TimeVaryingTransform : public Transform
  1104 +{
  1105 + Q_OBJECT
  1106 +
  1107 + virtual bool timeVarying() const {return true;}
  1108 + virtual void project(const Template &src, Template &dst) const
  1109 + {
  1110 + qFatal("No const project defined for time-varying transform");
  1111 + // shut up unused param warning? probably a better way to handle this
  1112 + dst = src;
  1113 + }
  1114 + virtual void project(const TemplateList &src, TemplateList &dst) const
  1115 + {
  1116 + qFatal("No const project defined for time-varying transform");
  1117 + dst = src;
  1118 + }
  1119 +
  1120 +
  1121 +protected:
  1122 + TimeVaryingTransform(bool independent = true, bool trainable = true) : Transform(independent, trainable) {}
  1123 +};
  1124 +
  1125 +
1054 /*! 1126 /*!
1055 * \brief A br::Transform expecting multiple matrices per template. 1127 * \brief A br::Transform expecting multiple matrices per template.
1056 */ 1128 */
@@ -1063,6 +1135,137 @@ protected: @@ -1063,6 +1135,137 @@ protected:
1063 }; 1135 };
1064 1136
1065 /*! 1137 /*!
  1138 + * \brief A MetaTransform that aggregates some sub-transforms
  1139 + */
  1140 +class BR_EXPORT CompositeTransform : public TimeVaryingTransform
  1141 +{
  1142 + Q_OBJECT
  1143 +public:
  1144 + Q_PROPERTY(QList<br::Transform*> transforms READ get_transforms WRITE set_transforms RESET reset_transforms)
  1145 + BR_PROPERTY(QList<br::Transform*>, transforms, QList<br::Transform*>())
  1146 +
  1147 + virtual void train(const TemplateList &data);
  1148 +
  1149 + virtual void project(const Template &src, Template &dst) const
  1150 + {
  1151 + if (timeVarying()) qFatal("No const project defined for time-varying transform");
  1152 + _project(src, dst);
  1153 + }
  1154 +
  1155 + virtual void project(const TemplateList &src, TemplateList &dst) const
  1156 + {
  1157 + if (timeVarying()) qFatal("No const project defined for time-varying transform");
  1158 + _project(src, dst);
  1159 + }
  1160 +
  1161 + void backProject(const Template &dst, Template &src) const
  1162 + {
  1163 + // Backprojecting a time-varying transform is probably not going to work.
  1164 + if (timeVarying()) qFatal("No backProject defined for time-varying transform");
  1165 +
  1166 + src = dst;
  1167 + // Reverse order in which transforms are processed
  1168 + int length = transforms.length();
  1169 + for (int i=length-1; i>=0; i--) {
  1170 + Transform *f = transforms.at(i);
  1171 + try {
  1172 + src >> *f;
  1173 + } catch (...) {
  1174 + qWarning("Exception triggered when processing %s with transform %s", qPrintable(dst.file.flat()), qPrintable(f->objectName()));
  1175 + src = Template(src.file);
  1176 + src.file.setBool("FTE");
  1177 + }
  1178 + }
  1179 + }
  1180 +
  1181 + void projectUpdate(const Template &src, Template &dst)
  1182 + {
  1183 + dst = src;
  1184 + foreach (Transform *f, transforms) {
  1185 + try {
  1186 + f->projectUpdate(dst);
  1187 + } catch (...) {
  1188 + qWarning("Exception triggered when processing %s with transform %s", qPrintable(src.file.flat()), qPrintable(f->objectName()));
  1189 + dst = Template(src.file);
  1190 + dst.file.setBool("FTE");
  1191 + }
  1192 + }
  1193 + }
  1194 +
  1195 + // For time varying transforms, parallel execution over individual templates
  1196 + // won't work.
  1197 + void projectUpdate(const TemplateList & src, TemplateList & dst)
  1198 + {
  1199 + dst = src;
  1200 + foreach (Transform *f, transforms)
  1201 + {
  1202 + f->projectUpdate(dst);
  1203 + }
  1204 + }
  1205 +
  1206 + bool timeVarying() const
  1207 + {
  1208 + return time_varying;
  1209 + }
  1210 +
  1211 + void init()
  1212 + {
  1213 + time_varying = false;
  1214 + foreach(const br::Transform * transform, transforms)
  1215 + {
  1216 + if (transform->timeVarying()) {
  1217 + time_varying = true;
  1218 + break;
  1219 + }
  1220 + }
  1221 + }
  1222 +
  1223 + virtual void finalize(TemplateList & output)
  1224 + {
  1225 + output.clear();
  1226 + // For each transform,
  1227 + for (int i = 0; i < transforms.size(); i++)
  1228 + {
  1229 +
  1230 + // Collect any final templates
  1231 + TemplateList last_set;
  1232 + transforms[i]->finalize(last_set);
  1233 + if (last_set.empty())
  1234 + continue;
  1235 + // Push any templates received through the remaining transforms in the sequence
  1236 + for (int j = (i+1); j < transforms.size();j++)
  1237 + {
  1238 + transforms[j]->projectUpdate(last_set);
  1239 + }
  1240 + // append the result to the output set
  1241 + output.append(last_set);
  1242 + }
  1243 + }
  1244 +
  1245 +protected:
  1246 + bool time_varying;
  1247 + // Single template const project, default implementation for aggregate transforms--pass the template through each
  1248 + // sub-transform, one after the other
  1249 + virtual void _project(const Template & src, Template & dst) const
  1250 + {
  1251 + dst = src;
  1252 + foreach (const Transform *f, transforms) {
  1253 + try {
  1254 + dst >> *f;
  1255 + } catch (...) {
  1256 + qWarning("Exception triggered when processing %s with transform %s", qPrintable(src.file.flat()), qPrintable(f->objectName()));
  1257 + dst = Template(src.file);
  1258 + dst.file.setBool("FTE");
  1259 + }
  1260 + }
  1261 + }
  1262 + virtual void _project(const TemplateList & src, TemplateList & dst) const = 0;
  1263 +
  1264 + CompositeTransform() : TimeVaryingTransform(false) {}
  1265 +};
  1266 +
  1267 +
  1268 +/*!
1066 * \brief A br::Transform that does not require training data. 1269 * \brief A br::Transform that does not require training data.
1067 */ 1270 */
1068 class BR_EXPORT UntrainableTransform : public Transform 1271 class BR_EXPORT UntrainableTransform : public Transform
sdk/plugins/meta.cpp
@@ -85,6 +85,21 @@ static void incrementStep() @@ -85,6 +85,21 @@ static void incrementStep()
85 Globals->currentStep += 1.0 / pow(10.0, double(depth)); 85 Globals->currentStep += 1.0 / pow(10.0, double(depth));
86 } 86 }
87 87
  88 +// CompositeTransform::train placed here to pick up acquireStep and incrementStep
  89 +void CompositeTransform::train(const TemplateList &data)
  90 +{
  91 + acquireStep();
  92 +
  93 + TemplateList copy(data);
  94 + for (int i=0; i<transforms.size(); i++) {
  95 + transforms[i]->train(copy);
  96 + copy >> *transforms[i];
  97 + incrementStep();
  98 + }
  99 +
  100 + releaseStep();
  101 +}
  102 +
88 /*! 103 /*!
89 * \ingroup Transforms 104 * \ingroup Transforms
90 * \brief Transforms in series. 105 * \brief Transforms in series.
@@ -95,41 +110,15 @@ static void incrementStep() @@ -95,41 +110,15 @@ static void incrementStep()
95 * \see ExpandTransform 110 * \see ExpandTransform
96 * \see ForkTransform 111 * \see ForkTransform
97 */ 112 */
98 -class PipeTransform : public MetaTransform 113 +class PipeTransform : public CompositeTransform
99 { 114 {
100 Q_OBJECT 115 Q_OBJECT
101 - Q_PROPERTY(QList<br::Transform*> transforms READ get_transforms WRITE set_transforms RESET reset_transforms)  
102 - BR_PROPERTY(QList<br::Transform*>, transforms, QList<br::Transform*>())  
103 116
104 - void train(const TemplateList &data)  
105 - {  
106 - acquireStep();  
107 117
108 - TemplateList copy(data);  
109 - for (int i=0; i<transforms.size(); i++) {  
110 - transforms[i]->train(copy);  
111 - copy >> *transforms[i];  
112 - incrementStep();  
113 - }  
114 -  
115 - releaseStep();  
116 - }  
117 -  
118 - void project(const Template &src, Template &dst) const  
119 - {  
120 - dst = src;  
121 - foreach (const Transform *f, transforms) {  
122 - try {  
123 - dst >> *f;  
124 - } catch (...) {  
125 - qWarning("Exception triggered when processing %s with transform %s", qPrintable(src.file.flat()), qPrintable(f->objectName()));  
126 - dst = Template(src.file);  
127 - dst.file.setBool("FTE");  
128 - }  
129 - }  
130 - }  
131 -  
132 - void project(const TemplateList &src, TemplateList &dst) const 118 +protected:
  119 + // Template list project -- process templates in parallel through Transform::project
  120 + // or if parallelism is disabled, handle them sequentially
  121 + void _project(const TemplateList &src, TemplateList &dst) const
133 { 122 {
134 if (Globals->parallelism < 0) { 123 if (Globals->parallelism < 0) {
135 dst = src; 124 dst = src;
@@ -139,23 +128,6 @@ class PipeTransform : public MetaTransform @@ -139,23 +128,6 @@ class PipeTransform : public MetaTransform
139 Transform::project(src, dst); 128 Transform::project(src, dst);
140 } 129 }
141 } 130 }
142 -  
143 - void backProject(const Template &dst, Template &src) const  
144 - {  
145 - src = dst;  
146 - // Reverse order in which transforms are processed  
147 - int length = transforms.length();  
148 - for (int i=length-1; i>=0; i--) {  
149 - Transform *f = transforms.at(i);  
150 - try {  
151 - src >> *f;  
152 - } catch (...) {  
153 - qWarning("Exception triggered when processing %s with transform %s", qPrintable(dst.file.flat()), qPrintable(f->objectName()));  
154 - src = Template(src.file);  
155 - src.file.setBool("FTE");  
156 - }  
157 - }  
158 - }  
159 }; 131 };
160 132
161 BR_REGISTER(Transform, PipeTransform) 133 BR_REGISTER(Transform, PipeTransform)
@@ -170,11 +142,9 @@ BR_REGISTER(Transform, PipeTransform) @@ -170,11 +142,9 @@ BR_REGISTER(Transform, PipeTransform)
170 * 142 *
171 * \see PipeTransform 143 * \see PipeTransform
172 */ 144 */
173 -class ExpandTransform : public MetaTransform 145 +class ExpandTransform : public CompositeTransform
174 { 146 {
175 Q_OBJECT 147 Q_OBJECT
176 - Q_PROPERTY(QList<br::Transform*> transforms READ get_transforms WRITE set_transforms RESET reset_transforms)  
177 - BR_PROPERTY(QList<br::Transform*>, transforms, QList<br::Transform*>())  
178 148
179 void train(const TemplateList &data) 149 void train(const TemplateList &data)
180 { 150 {
@@ -191,21 +161,47 @@ class ExpandTransform : public MetaTransform @@ -191,21 +161,47 @@ class ExpandTransform : public MetaTransform
191 releaseStep(); 161 releaseStep();
192 } 162 }
193 163
194 - void project(const Template &src, Template &dst) const 164 + // same as _project, but calling projectUpdate on sub-transforms instead of using
  165 + // operator>>
  166 + void projectUpdate(const TemplateList &src, TemplateList &dst)
195 { 167 {
196 dst = src; 168 dst = src;
197 - foreach (const Transform *f, transforms) {  
198 - try {  
199 - dst >> *f;  
200 - } catch (...) {  
201 - qWarning("Exception triggered when processing %s with transform %s", qPrintable(src.file.flat()), qPrintable(f->objectName()));  
202 - dst = Template(src.file);  
203 - dst.file.setBool("FTE"); 169 + for (int i=0; i<transforms.size(); i++) {
  170 + transforms[i]->projectUpdate(dst);
  171 + dst = Expanded(dst);
  172 + }
  173 + }
  174 +
  175 + virtual void finalize(TemplateList & output)
  176 + {
  177 + output.clear();
  178 + // For each transform,
  179 + for (int i = 0; i < transforms.size(); i++)
  180 + {
  181 +
  182 + // Collect any final templates
  183 + TemplateList last_set;
  184 + transforms[i]->finalize(last_set);
  185 + last_set = Expanded(last_set);
  186 + if (last_set.empty())
  187 + continue;
  188 + // Push any templates received through the remaining transforms in the sequence
  189 + for (int j = (i+1); j < transforms.size();j++)
  190 + {
  191 + transforms[j]->projectUpdate(last_set);
  192 + last_set = Expanded(last_set);
204 } 193 }
  194 + // append the result to the output set
  195 + output.append(last_set);
205 } 196 }
206 } 197 }
207 198
208 - void project(const TemplateList &src, TemplateList &dst) const 199 +protected:
  200 +
  201 + // Template list project -- project through transforms sequentially,
  202 + // then expand the results, can't use Transform::Project(templateList) since
  203 + // we need to expand between tranforms
  204 + void _project(const TemplateList &src, TemplateList &dst) const
209 { 205 {
210 dst = src; 206 dst = src;
211 for (int i=0; i<transforms.size(); i++) { 207 for (int i=0; i<transforms.size(); i++) {
@@ -213,23 +209,6 @@ class ExpandTransform : public MetaTransform @@ -213,23 +209,6 @@ class ExpandTransform : public MetaTransform
213 dst = Expanded(dst); 209 dst = Expanded(dst);
214 } 210 }
215 } 211 }
216 -  
217 - void backProject(const Template &dst, Template &src) const  
218 - {  
219 - src = dst;  
220 - // Reverse order in which transforms are processed  
221 - int length = transforms.length();  
222 - for (int i=length-1; i>=0; i--) {  
223 - Transform *f = transforms.at(i);  
224 - try {  
225 - src >> *f;  
226 - } catch (...) {  
227 - qWarning("Exception triggered when processing %s with transform %s", qPrintable(dst.file.flat()), qPrintable(f->objectName()));  
228 - src = Template(src.file);  
229 - src.file.setBool("FTE");  
230 - }  
231 - }  
232 - }  
233 }; 212 };
234 213
235 BR_REGISTER(Transform, ExpandTransform) 214 BR_REGISTER(Transform, ExpandTransform)
@@ -243,11 +222,9 @@ BR_REGISTER(Transform, ExpandTransform) @@ -243,11 +222,9 @@ BR_REGISTER(Transform, ExpandTransform)
243 * 222 *
244 * \see PipeTransform 223 * \see PipeTransform
245 */ 224 */
246 -class ForkTransform : public MetaTransform 225 +class ForkTransform : public CompositeTransform
247 { 226 {
248 Q_OBJECT 227 Q_OBJECT
249 - Q_PROPERTY(QList<br::Transform*> transforms READ get_transforms WRITE set_transforms RESET reset_transforms)  
250 - BR_PROPERTY(QList<br::Transform*>, transforms, QList<br::Transform*>())  
251 228
252 void train(const TemplateList &data) 229 void train(const TemplateList &data)
253 { 230 {
@@ -259,8 +236,58 @@ class ForkTransform : public MetaTransform @@ -259,8 +236,58 @@ class ForkTransform : public MetaTransform
259 } 236 }
260 if (threaded) Globals->trackFutures(futures); 237 if (threaded) Globals->trackFutures(futures);
261 } 238 }
  239 + // The implementation of backProject in aggregate transform probably doesn't do anything useful here.
  240 + void backProject(const Template &dst, Template &src) const {Transform::backProject(dst, src);}
262 241
263 - void project(const Template &src, Template &dst) const 242 + // same as _project, but calls projectUpdate on sub-transforms
  243 + void projectupdate(const Template & src, Template & dst)
  244 + {
  245 + foreach (Transform *f, transforms) {
  246 + try {
  247 + Template res;
  248 + f->projectUpdate(src, res);
  249 + dst.merge(res);
  250 + } catch (...) {
  251 + qWarning("Exception triggered when processing %s with transform %s", qPrintable(src.file.flat()), qPrintable(f->objectName()));
  252 + dst = Template(src.file);
  253 + dst.file.setBool("FTE");
  254 + }
  255 + }
  256 + }
  257 +
  258 +
  259 + // this is probably going to go bad, fork transform probably won't work well in a variable
  260 + // input/output scenario
  261 + virtual void finalize(TemplateList & output)
  262 + {
  263 + output.clear();
  264 + // For each transform,
  265 + for (int i = 0; i < transforms.size(); i++)
  266 + {
  267 + // Collect any final templates
  268 + TemplateList last_set;
  269 + transforms[i]->finalize(last_set);
  270 + if (last_set.empty())
  271 + continue;
  272 +
  273 + if (output.empty()) output = last_set;
  274 + else
  275 + {
  276 + // is the number of templates received from this transform consistent with the number
  277 + // received previously? If not we can't do anything coherent here.
  278 + if (last_set.size() != output.size())
  279 + qFatal("mismatched template list sizes in ForkTransform");
  280 + for (int j = 0; j < output.size(); j++) {
  281 + output[j].append(last_set[j]);
  282 + }
  283 + }
  284 + }
  285 + }
  286 +
  287 +protected:
  288 +
  289 + // Apply each transform to src, concatenate the results
  290 + void _project(const Template &src, Template &dst) const
264 { 291 {
265 foreach (const Transform *f, transforms) { 292 foreach (const Transform *f, transforms) {
266 try { 293 try {
@@ -273,7 +300,7 @@ class ForkTransform : public MetaTransform @@ -273,7 +300,7 @@ class ForkTransform : public MetaTransform
273 } 300 }
274 } 301 }
275 302
276 - void project(const TemplateList &src, TemplateList &dst) const 303 + void _project(const TemplateList &src, TemplateList &dst) const
277 { 304 {
278 if (Globals->parallelism < 0) { 305 if (Globals->parallelism < 0) {
279 dst.reserve(src.size()); 306 dst.reserve(src.size());
@@ -288,6 +315,20 @@ class ForkTransform : public MetaTransform @@ -288,6 +315,20 @@ class ForkTransform : public MetaTransform
288 Transform::project(src, dst); 315 Transform::project(src, dst);
289 } 316 }
290 } 317 }
  318 +
  319 + void projectUpdate(const TemplateList & src, TemplateList & dst)
  320 + {
  321 + dst = src;
  322 + dst.reserve(src.size());
  323 + for (int i=0; i<src.size(); i++) dst.append(Template());
  324 + foreach (Transform *f, transforms) {
  325 + TemplateList m;
  326 + f->projectUpdate(src, m);
  327 + if (m.size() != dst.size()) qFatal("TemplateList is of an unexpected size.");
  328 + for (int i=0; i<src.size(); i++) dst[i].append(m[i]);
  329 + }
  330 + }
  331 +
291 }; 332 };
292 333
293 BR_REGISTER(Transform, ForkTransform) 334 BR_REGISTER(Transform, ForkTransform)
sdk/plugins/random.cpp
@@ -30,11 +30,9 @@ namespace br @@ -30,11 +30,9 @@ namespace br
30 * \brief Selects a random transform. 30 * \brief Selects a random transform.
31 * \author Josh Klontz \cite jklontz 31 * \author Josh Klontz \cite jklontz
32 */ 32 */
33 -class RndTransformTransform : public Transform 33 +class RndTransformTransform : public CompositeTransform
34 { 34 {
35 Q_OBJECT 35 Q_OBJECT
36 - Q_PROPERTY(QList<br::Transform*> transforms READ get_transforms WRITE set_transforms RESET reset_transforms STORED false)  
37 - BR_PROPERTY(QList<br::Transform*>, transforms, QList<br::Transform*>())  
38 36
39 int selectedIndex; 37 int selectedIndex;
40 Transform *selectedTransform; 38 Transform *selectedTransform;
@@ -46,11 +44,37 @@ class RndTransformTransform : public Transform @@ -46,11 +44,37 @@ class RndTransformTransform : public Transform
46 selectedTransform->train(data); 44 selectedTransform->train(data);
47 } 45 }
48 46
49 - void project(const Template &src, Template &dst) const 47 + bool timeVarying() const
  48 + {
  49 + // calling on selectedTransform assumes train has already been called. -cao
  50 + return selectedTransform->timeVarying();
  51 + }
  52 +
  53 + void _project(const Template &src, Template &dst) const
50 { 54 {
51 selectedTransform->project(src, dst); 55 selectedTransform->project(src, dst);
52 } 56 }
53 57
  58 + void _project(const TemplateList &src, TemplateList &dst) const
  59 + {
  60 + Transform::project(src, dst);
  61 + }
  62 +
  63 + void projectUpdate(const Template &src, Template &dst)
  64 + {
  65 + selectedTransform->projectUpdate(src,dst);
  66 + }
  67 +
  68 + void projectUpdate(const TemplateList & src, TemplateList & dst)
  69 + {
  70 + selectedTransform->projectUpdate(src, dst);
  71 + }
  72 +
  73 + void finalize(TemplateList & output)
  74 + {
  75 + selectedTransform->finalize(output);
  76 + }
  77 +
54 void store(QDataStream &stream) const 78 void store(QDataStream &stream) const
55 { 79 {
56 stream << selectedIndex << *selectedTransform; 80 stream << selectedIndex << *selectedTransform;