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 494 QStringList Object::parameters() const
495 495 {
496 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 499 QMetaProperty property = metaObject()->property(i);
499 500 if (property.isStored(this)) continue;
500 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 714 // Set name
714 715 QString name = metaObject()->className();
715 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 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 732 QString superClassName = superClass->className();
  733 +
  734 + // strip br:: prefix from superclass name
719 735 if (superClassName.startsWith("br::"))
720 736 superClassName = superClassName.right(superClassName.size()-4);
  737 +
  738 + // Strip superclass name from base class name (e.g. PipeTransform -> Pipe)
721 739 if (name.endsWith(superClassName))
722 740 name = name.left(name.size() - superClassName.size());
  741 + baseClass = superClass;
723 742 superClass = superClass->superClass();
  743 +
724 744 }
725 745 setObjectName(name);
726 746  
727   - // Set properties
  747 + // Reset all properties
728 748 for (int i=0; i<metaObject()->propertyCount(); i++) {
729 749 QMetaProperty property = metaObject()->property(i);
730 750 if (property.isResettable())
... ... @@ -734,8 +754,17 @@ void Object::init(const File &amp;file_)
734 754  
735 755 foreach (QString key, file.localKeys()) {
736 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 768 setProperty(key, value);
740 769 }
741 770  
... ...
sdk/openbr_plugin.h
... ... @@ -421,6 +421,9 @@ class BR_EXPORT Object : public QObject
421 421 {
422 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 427 public:
425 428 File file; /*!< \brief The file used to construct the plugin. */
426 429  
... ... @@ -989,6 +992,48 @@ public:
989 992 virtual void backProject(const Template &dst, Template &src) const; /*!< \brief Invert the transform. */
990 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 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 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 1127 * \brief A br::Transform expecting multiple matrices per template.
1056 1128 */
... ... @@ -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 1269 * \brief A br::Transform that does not require training data.
1067 1270 */
1068 1271 class BR_EXPORT UntrainableTransform : public Transform
... ...
sdk/plugins/meta.cpp
... ... @@ -85,6 +85,21 @@ static void incrementStep()
85 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 104 * \ingroup Transforms
90 105 * \brief Transforms in series.
... ... @@ -95,41 +110,15 @@ static void incrementStep()
95 110 * \see ExpandTransform
96 111 * \see ForkTransform
97 112 */
98   -class PipeTransform : public MetaTransform
  113 +class PipeTransform : public CompositeTransform
99 114 {
100 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 123 if (Globals->parallelism < 0) {
135 124 dst = src;
... ... @@ -139,23 +128,6 @@ class PipeTransform : public MetaTransform
139 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 133 BR_REGISTER(Transform, PipeTransform)
... ... @@ -170,11 +142,9 @@ BR_REGISTER(Transform, PipeTransform)
170 142 *
171 143 * \see PipeTransform
172 144 */
173   -class ExpandTransform : public MetaTransform
  145 +class ExpandTransform : public CompositeTransform
174 146 {
175 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 149 void train(const TemplateList &data)
180 150 {
... ... @@ -191,21 +161,47 @@ class ExpandTransform : public MetaTransform
191 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 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 206 dst = src;
211 207 for (int i=0; i<transforms.size(); i++) {
... ... @@ -213,23 +209,6 @@ class ExpandTransform : public MetaTransform
213 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 214 BR_REGISTER(Transform, ExpandTransform)
... ... @@ -243,11 +222,9 @@ BR_REGISTER(Transform, ExpandTransform)
243 222 *
244 223 * \see PipeTransform
245 224 */
246   -class ForkTransform : public MetaTransform
  225 +class ForkTransform : public CompositeTransform
247 226 {
248 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 229 void train(const TemplateList &data)
253 230 {
... ... @@ -259,8 +236,58 @@ class ForkTransform : public MetaTransform
259 236 }
260 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 292 foreach (const Transform *f, transforms) {
266 293 try {
... ... @@ -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 305 if (Globals->parallelism < 0) {
279 306 dst.reserve(src.size());
... ... @@ -288,6 +315,20 @@ class ForkTransform : public MetaTransform
288 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 334 BR_REGISTER(Transform, ForkTransform)
... ...
sdk/plugins/random.cpp
... ... @@ -30,11 +30,9 @@ namespace br
30 30 * \brief Selects a random transform.
31 31 * \author Josh Klontz \cite jklontz
32 32 */
33   -class RndTransformTransform : public Transform
  33 +class RndTransformTransform : public CompositeTransform
34 34 {
35 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 37 int selectedIndex;
40 38 Transform *selectedTransform;
... ... @@ -46,11 +44,37 @@ class RndTransformTransform : public Transform
46 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 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 78 void store(QDataStream &stream) const
55 79 {
56 80 stream << selectedIndex << *selectedTransform;
... ...