Commit 8f3fa09b8f029a6a9490c7bd5cf421882054eb12
1 parent
9a77f8f5
refactored out independent transform to its own file
Showing
3 changed files
with
218 additions
and
161 deletions
openbr/openbr_plugin.cpp
| ... | ... | @@ -679,12 +679,17 @@ void Object::setProperty(const QString &name, const QString &value) |
| 679 | 679 | variant = value; |
| 680 | 680 | } |
| 681 | 681 | |
| 682 | - if (!QObject::setProperty(qPrintable(name), variant) && !type.isEmpty()) | |
| 683 | - qFatal("Failed to set %s::%s to: %s %s", | |
| 684 | - metaObject()->className(), qPrintable(name), qPrintable(value), qPrintable(type)); | |
| 682 | + setProperty(name, variant, !type.isEmpty()); | |
| 685 | 683 | } |
| 686 | 684 | |
| 687 | -QStringList br::Object::parse(const QString &string, char split) | |
| 685 | +void Object::setProperty(const QString &name, const QVariant &value, bool failOnError) | |
| 686 | +{ | |
| 687 | + if (!QObject::setProperty(qPrintable(name), value) && failOnError) | |
| 688 | + qFatal("Failed to set %s::%s to: %s", | |
| 689 | + metaObject()->className(), qPrintable(name), qPrintable(value.toString())); | |
| 690 | +} | |
| 691 | + | |
| 692 | +QStringList Object::parse(const QString &string, char split) | |
| 688 | 693 | { |
| 689 | 694 | return QtUtils::parse(string, split); |
| 690 | 695 | } |
| ... | ... | @@ -736,19 +741,21 @@ void Object::init(const File &file_) |
| 736 | 741 | } |
| 737 | 742 | |
| 738 | 743 | foreach (QString key, file.localKeys()) { |
| 739 | - const QString value = file.value(key).toString(); | |
| 744 | + const QVariant value = file.value(key); | |
| 745 | + const QString valueString = value.toString(); | |
| 740 | 746 | |
| 741 | 747 | if (key.startsWith(("_Arg"))) { |
| 742 | - int argument_number = key.mid(4).toInt(); | |
| 743 | - int target_idx = argument_number + firstAvailablePropertyIdx; | |
| 744 | - | |
| 745 | - if (target_idx >= metaObject()->propertyCount()) { | |
| 746 | - qWarning("too many arguments for transform, ignoring %s\n", qPrintable(value)); | |
| 748 | + int argumentNumber = key.mid(4).toInt(); | |
| 749 | + int targetIdx = argumentNumber + firstAvailablePropertyIdx; | |
| 750 | + if (targetIdx >= metaObject()->propertyCount()) { | |
| 751 | + qWarning("too many arguments for transform %s, ignoring %s", qPrintable(objectName()), qPrintable(valueString)); | |
| 747 | 752 | continue; |
| 748 | 753 | } |
| 749 | - key = metaObject()->property(target_idx).name(); | |
| 754 | + key = metaObject()->property(targetIdx).name(); | |
| 750 | 755 | } |
| 751 | - setProperty(key, value); | |
| 756 | + | |
| 757 | + if (valueString.isEmpty()) setProperty(key, value); // Set the property directly | |
| 758 | + else setProperty(key, valueString); // Parse the value first | |
| 752 | 759 | } |
| 753 | 760 | |
| 754 | 761 | init(); |
| ... | ... | @@ -1083,153 +1090,6 @@ Gallery *Gallery::make(const File &file) |
| 1083 | 1090 | return gallery; |
| 1084 | 1091 | } |
| 1085 | 1092 | |
| 1086 | -static TemplateList Downsample(const TemplateList &templates, const Transform *transform) | |
| 1087 | -{ | |
| 1088 | - // Return early when no downsampling is required | |
| 1089 | - if ((transform->classes == std::numeric_limits<int>::max()) && | |
| 1090 | - (transform->instances == std::numeric_limits<int>::max()) && | |
| 1091 | - (transform->fraction >= 1)) | |
| 1092 | - return templates; | |
| 1093 | - | |
| 1094 | - const bool atLeast = transform->instances < 0; | |
| 1095 | - const int instances = abs(transform->instances); | |
| 1096 | - | |
| 1097 | - QList<int> allLabels = templates.labels<int>(); | |
| 1098 | - QList<int> uniqueLabels = allLabels.toSet().toList(); | |
| 1099 | - qSort(uniqueLabels); | |
| 1100 | - | |
| 1101 | - QMap<int,int> counts = templates.labelCounts(instances != std::numeric_limits<int>::max()); | |
| 1102 | - if ((instances != std::numeric_limits<int>::max()) && (transform->classes != std::numeric_limits<int>::max())) | |
| 1103 | - foreach (int label, counts.keys()) | |
| 1104 | - if (counts[label] < instances) | |
| 1105 | - counts.remove(label); | |
| 1106 | - uniqueLabels = counts.keys(); | |
| 1107 | - if ((transform->classes != std::numeric_limits<int>::max()) && (uniqueLabels.size() < transform->classes)) | |
| 1108 | - qWarning("Downsample requested %d classes but only %d are available.", transform->classes, uniqueLabels.size()); | |
| 1109 | - | |
| 1110 | - Common::seedRNG(); | |
| 1111 | - QList<int> selectedLabels = uniqueLabels; | |
| 1112 | - if (transform->classes < uniqueLabels.size()) { | |
| 1113 | - std::random_shuffle(selectedLabels.begin(), selectedLabels.end()); | |
| 1114 | - selectedLabels = selectedLabels.mid(0, transform->classes); | |
| 1115 | - } | |
| 1116 | - | |
| 1117 | - TemplateList downsample; | |
| 1118 | - for (int i=0; i<selectedLabels.size(); i++) { | |
| 1119 | - const int selectedLabel = selectedLabels[i]; | |
| 1120 | - QList<int> indices; | |
| 1121 | - for (int j=0; j<allLabels.size(); j++) | |
| 1122 | - if ((allLabels[j] == selectedLabel) && (!templates.value(j).file.get<bool>("FTE", false))) | |
| 1123 | - indices.append(j); | |
| 1124 | - | |
| 1125 | - std::random_shuffle(indices.begin(), indices.end()); | |
| 1126 | - const int max = atLeast ? indices.size() : std::min(indices.size(), instances); | |
| 1127 | - for (int j=0; j<max; j++) | |
| 1128 | - downsample.append(templates.value(indices[j])); | |
| 1129 | - } | |
| 1130 | - | |
| 1131 | - if (transform->fraction < 1) { | |
| 1132 | - std::random_shuffle(downsample.begin(), downsample.end()); | |
| 1133 | - downsample = downsample.mid(0, downsample.size()*transform->fraction); | |
| 1134 | - } | |
| 1135 | - | |
| 1136 | - return downsample; | |
| 1137 | -} | |
| 1138 | - | |
| 1139 | -/*! | |
| 1140 | - * \ingroup transforms | |
| 1141 | - * \brief Clones the transform so that it can be applied independently. | |
| 1142 | - * | |
| 1143 | - * \em Independent transforms expect single-matrix templates. | |
| 1144 | - */ | |
| 1145 | -class Independent : public MetaTransform | |
| 1146 | -{ | |
| 1147 | - Q_PROPERTY(QList<Transform*> transforms READ get_transforms WRITE set_transforms STORED false) | |
| 1148 | - BR_PROPERTY(QList<Transform*>, transforms, QList<Transform*>()) | |
| 1149 | - | |
| 1150 | - public: | |
| 1151 | - /*! | |
| 1152 | - * \brief Independent | |
| 1153 | - * \param transform | |
| 1154 | - */ | |
| 1155 | - Independent(Transform *transform) | |
| 1156 | - { | |
| 1157 | - transform->setParent(this); | |
| 1158 | - transforms.append(transform); | |
| 1159 | - file = transform->file; | |
| 1160 | - trainable = transform->trainable; | |
| 1161 | - setObjectName(transforms.first()->objectName()); | |
| 1162 | - } | |
| 1163 | - | |
| 1164 | -private: | |
| 1165 | - Transform *clone() const | |
| 1166 | - { | |
| 1167 | - return new Independent(transforms.first()->clone()); | |
| 1168 | - } | |
| 1169 | - | |
| 1170 | - static void _train(Transform *transform, const TemplateList *data) | |
| 1171 | - { | |
| 1172 | - transform->train(*data); | |
| 1173 | - } | |
| 1174 | - | |
| 1175 | - void train(const TemplateList &data) | |
| 1176 | - { | |
| 1177 | - // Don't bother if the transform is untrainable | |
| 1178 | - if (!trainable) return; | |
| 1179 | - | |
| 1180 | - QList<TemplateList> templatesList; | |
| 1181 | - foreach (const Template &t, data) { | |
| 1182 | - if ((templatesList.size() != t.size()) && !templatesList.isEmpty()) | |
| 1183 | - qWarning("Independent::train template %s of size %d differs from expected size %d.", qPrintable(t.file.name), t.size(), templatesList.size()); | |
| 1184 | - while (templatesList.size() < t.size()) | |
| 1185 | - templatesList.append(TemplateList()); | |
| 1186 | - for (int i=0; i<t.size(); i++) | |
| 1187 | - templatesList[i].append(Template(t.file, t[i])); | |
| 1188 | - } | |
| 1189 | - | |
| 1190 | - while (transforms.size() < templatesList.size()) | |
| 1191 | - transforms.append(transforms.first()->clone()); | |
| 1192 | - | |
| 1193 | - for (int i=0; i<templatesList.size(); i++) | |
| 1194 | - templatesList[i] = Downsample(templatesList[i], transforms[i]); | |
| 1195 | - | |
| 1196 | - QFutureSynchronizer<void> futures; | |
| 1197 | - for (int i=0; i<templatesList.size(); i++) | |
| 1198 | - futures.addFuture(QtConcurrent::run(_train, transforms[i], &templatesList[i])); | |
| 1199 | - futures.waitForFinished(); | |
| 1200 | - } | |
| 1201 | - | |
| 1202 | - void project(const Template &src, Template &dst) const | |
| 1203 | - { | |
| 1204 | - dst.file = src.file; | |
| 1205 | - QList<Mat> mats; | |
| 1206 | - for (int i=0; i<src.size(); i++) { | |
| 1207 | - transforms[i%transforms.size()]->project(Template(src.file, src[i]), dst); | |
| 1208 | - mats.append(dst); | |
| 1209 | - dst.clear(); | |
| 1210 | - } | |
| 1211 | - dst.append(mats); | |
| 1212 | - } | |
| 1213 | - | |
| 1214 | - void store(QDataStream &stream) const | |
| 1215 | - { | |
| 1216 | - const int size = transforms.size(); | |
| 1217 | - stream << size; | |
| 1218 | - for (int i=0; i<size; i++) | |
| 1219 | - transforms[i]->store(stream); | |
| 1220 | - } | |
| 1221 | - | |
| 1222 | - void load(QDataStream &stream) | |
| 1223 | - { | |
| 1224 | - int size; | |
| 1225 | - stream >> size; | |
| 1226 | - while (transforms.size() < size) | |
| 1227 | - transforms.append(transforms.first()->clone()); | |
| 1228 | - for (int i=0; i<size; i++) | |
| 1229 | - transforms[i]->load(stream); | |
| 1230 | - } | |
| 1231 | -}; | |
| 1232 | - | |
| 1233 | 1093 | /* Transform - public methods */ |
| 1234 | 1094 | Transform::Transform(bool _independent, bool _trainable) |
| 1235 | 1095 | { |
| ... | ... | @@ -1277,8 +1137,17 @@ Transform *Transform::make(QString str, QObject *parent) |
| 1277 | 1137 | File f = "." + str; |
| 1278 | 1138 | Transform *transform = Factory<Transform>::make(f); |
| 1279 | 1139 | |
| 1280 | - if (transform->independent) | |
| 1281 | - transform = new Independent(transform); | |
| 1140 | + if (transform->independent) { | |
| 1141 | +// Transform *independentTransform = Factory<Transform>::make(".Independent"); | |
| 1142 | +// static_cast<QObject*>(independentTransform)->setProperty("transform", qVariantFromValue<void*>(transform)); | |
| 1143 | +// independentTransform->init(); | |
| 1144 | +// transform = independentTransform; | |
| 1145 | + | |
| 1146 | + File independent(".Independent"); | |
| 1147 | + independent.set("transform", qVariantFromValue<void*>(transform)); | |
| 1148 | + transform = Factory<Transform>::make(independent); | |
| 1149 | + } | |
| 1150 | + | |
| 1282 | 1151 | transform->setParent(parent); |
| 1283 | 1152 | return transform; |
| 1284 | 1153 | } | ... | ... |
openbr/openbr_plugin.h
| ... | ... | @@ -497,6 +497,7 @@ public: |
| 497 | 497 | QString argument(int index) const; /*!< \brief A string value for the argument at the specified index. */ |
| 498 | 498 | QString description() const; /*!< \brief Returns a string description of the object. */ |
| 499 | 499 | void setProperty(const QString &name, const QString &value); /*!< \brief Overload of QObject::setProperty to handle OpenBR data types. */ |
| 500 | + void setProperty(const QString &name, const QVariant &value, bool failOnError = false); /*!< \brief Overload of QObject::setProperty to handle OpenBR data types. */ | |
| 500 | 501 | static QStringList parse(const QString &string, char split = ','); /*!< \brief Splits the string while respecting lexical scoping of <tt>()</tt>, <tt>[]</tt>, <tt>\<\></tt>, and <tt>{}</tt>. */ |
| 501 | 502 | |
| 502 | 503 | private: | ... | ... |
openbr/plugins/independent.cpp
0 โ 100644
| 1 | +#include <QFutureSynchronizer> | |
| 2 | +#include <QtConcurrentRun> | |
| 3 | + | |
| 4 | +#include "openbr_internal.h" | |
| 5 | +#include "openbr/core/common.h" | |
| 6 | + | |
| 7 | +using namespace cv; | |
| 8 | + | |
| 9 | +namespace br | |
| 10 | +{ | |
| 11 | + | |
| 12 | +static TemplateList Downsample(const TemplateList &templates, const Transform *transform) | |
| 13 | +{ | |
| 14 | + // Return early when no downsampling is required | |
| 15 | + if ((transform->classes == std::numeric_limits<int>::max()) && | |
| 16 | + (transform->instances == std::numeric_limits<int>::max()) && | |
| 17 | + (transform->fraction >= 1)) | |
| 18 | + return templates; | |
| 19 | + | |
| 20 | + const bool atLeast = transform->instances < 0; | |
| 21 | + const int instances = abs(transform->instances); | |
| 22 | + | |
| 23 | + QList<int> allLabels = templates.labels<int>(); | |
| 24 | + QList<int> uniqueLabels = allLabels.toSet().toList(); | |
| 25 | + qSort(uniqueLabels); | |
| 26 | + | |
| 27 | + QMap<int,int> counts = templates.labelCounts(instances != std::numeric_limits<int>::max()); | |
| 28 | + if ((instances != std::numeric_limits<int>::max()) && (transform->classes != std::numeric_limits<int>::max())) | |
| 29 | + foreach (int label, counts.keys()) | |
| 30 | + if (counts[label] < instances) | |
| 31 | + counts.remove(label); | |
| 32 | + uniqueLabels = counts.keys(); | |
| 33 | + if ((transform->classes != std::numeric_limits<int>::max()) && (uniqueLabels.size() < transform->classes)) | |
| 34 | + qWarning("Downsample requested %d classes but only %d are available.", transform->classes, uniqueLabels.size()); | |
| 35 | + | |
| 36 | + Common::seedRNG(); | |
| 37 | + QList<int> selectedLabels = uniqueLabels; | |
| 38 | + if (transform->classes < uniqueLabels.size()) { | |
| 39 | + std::random_shuffle(selectedLabels.begin(), selectedLabels.end()); | |
| 40 | + selectedLabels = selectedLabels.mid(0, transform->classes); | |
| 41 | + } | |
| 42 | + | |
| 43 | + TemplateList downsample; | |
| 44 | + for (int i=0; i<selectedLabels.size(); i++) { | |
| 45 | + const int selectedLabel = selectedLabels[i]; | |
| 46 | + QList<int> indices; | |
| 47 | + for (int j=0; j<allLabels.size(); j++) | |
| 48 | + if ((allLabels[j] == selectedLabel) && (!templates.value(j).file.get<bool>("FTE", false))) | |
| 49 | + indices.append(j); | |
| 50 | + | |
| 51 | + std::random_shuffle(indices.begin(), indices.end()); | |
| 52 | + const int max = atLeast ? indices.size() : std::min(indices.size(), instances); | |
| 53 | + for (int j=0; j<max; j++) | |
| 54 | + downsample.append(templates.value(indices[j])); | |
| 55 | + } | |
| 56 | + | |
| 57 | + if (transform->fraction < 1) { | |
| 58 | + std::random_shuffle(downsample.begin(), downsample.end()); | |
| 59 | + downsample = downsample.mid(0, downsample.size()*transform->fraction); | |
| 60 | + } | |
| 61 | + | |
| 62 | + return downsample; | |
| 63 | +} | |
| 64 | + | |
| 65 | +/*! | |
| 66 | + * \ingroup transforms | |
| 67 | + * \brief Clones the transform so that it can be applied independently. | |
| 68 | + * \author Josh Klontz \cite jklontz | |
| 69 | + * \em Independent transforms expect single-matrix templates. | |
| 70 | + */ | |
| 71 | +class IndependentTransform : public MetaTransform | |
| 72 | +{ | |
| 73 | + Q_OBJECT | |
| 74 | + Q_PROPERTY(br::Transform* transform READ get_transform WRITE set_transform STORED false) | |
| 75 | + BR_PROPERTY(br::Transform*, transform, NULL) | |
| 76 | + | |
| 77 | + QList<Transform*> transforms; | |
| 78 | + | |
| 79 | + void init() | |
| 80 | + { | |
| 81 | + transforms.clear(); | |
| 82 | + if (transform == NULL) | |
| 83 | + return; | |
| 84 | + | |
| 85 | + transform->setParent(this); | |
| 86 | + transforms.append(transform); | |
| 87 | + file = transform->file; | |
| 88 | + trainable = transform->trainable; | |
| 89 | + setObjectName(transform->objectName()); | |
| 90 | + } | |
| 91 | + | |
| 92 | + Transform *clone() const | |
| 93 | + { | |
| 94 | + IndependentTransform *independentTransform = new IndependentTransform(); | |
| 95 | + independentTransform->transform = transform->clone(); | |
| 96 | + independentTransform->init(); | |
| 97 | + return independentTransform; | |
| 98 | + } | |
| 99 | + | |
| 100 | + static void _train(Transform *transform, const TemplateList *data) | |
| 101 | + { | |
| 102 | + transform->train(*data); | |
| 103 | + } | |
| 104 | + | |
| 105 | + void train(const TemplateList &data) | |
| 106 | + { | |
| 107 | + // Don't bother if the transform is untrainable | |
| 108 | + if (!trainable) return; | |
| 109 | + | |
| 110 | + QList<TemplateList> templatesList; | |
| 111 | + foreach (const Template &t, data) { | |
| 112 | + if ((templatesList.size() != t.size()) && !templatesList.isEmpty()) | |
| 113 | + qWarning("Independent::train template %s of size %d differs from expected size %d.", qPrintable(t.file.name), t.size(), templatesList.size()); | |
| 114 | + while (templatesList.size() < t.size()) | |
| 115 | + templatesList.append(TemplateList()); | |
| 116 | + for (int i=0; i<t.size(); i++) | |
| 117 | + templatesList[i].append(Template(t.file, t[i])); | |
| 118 | + } | |
| 119 | + | |
| 120 | + while (transforms.size() < templatesList.size()) | |
| 121 | + transforms.append(transform->clone()); | |
| 122 | + | |
| 123 | + for (int i=0; i<templatesList.size(); i++) | |
| 124 | + templatesList[i] = Downsample(templatesList[i], transforms[i]); | |
| 125 | + | |
| 126 | + QFutureSynchronizer<void> futures; | |
| 127 | + for (int i=0; i<templatesList.size(); i++) | |
| 128 | + futures.addFuture(QtConcurrent::run(_train, transforms[i], &templatesList[i])); | |
| 129 | + futures.waitForFinished(); | |
| 130 | + } | |
| 131 | + | |
| 132 | + void project(const Template &src, Template &dst) const | |
| 133 | + { | |
| 134 | + dst.file = src.file; | |
| 135 | + QList<Mat> mats; | |
| 136 | + for (int i=0; i<src.size(); i++) { | |
| 137 | + transforms[i%transforms.size()]->project(Template(src.file, src[i]), dst); | |
| 138 | + mats.append(dst); | |
| 139 | + dst.clear(); | |
| 140 | + } | |
| 141 | + dst.append(mats); | |
| 142 | + } | |
| 143 | + | |
| 144 | + void store(QDataStream &stream) const | |
| 145 | + { | |
| 146 | + const int size = transforms.size(); | |
| 147 | + stream << size; | |
| 148 | + for (int i=0; i<size; i++) | |
| 149 | + transforms[i]->store(stream); | |
| 150 | + } | |
| 151 | + | |
| 152 | + void load(QDataStream &stream) | |
| 153 | + { | |
| 154 | + int size; | |
| 155 | + stream >> size; | |
| 156 | + while (transforms.size() < size) | |
| 157 | + transforms.append(transform->clone()); | |
| 158 | + for (int i=0; i<size; i++) | |
| 159 | + transforms[i]->load(stream); | |
| 160 | + } | |
| 161 | +}; | |
| 162 | + | |
| 163 | +BR_REGISTER(Transform, IndependentTransform) | |
| 164 | + | |
| 165 | +// File independent(".Independent"); | |
| 166 | +// independent.set("transform", qVariantFromValue<void*>(transform)); | |
| 167 | +// transform = Factory<Transform>::make(independent); | |
| 168 | + | |
| 169 | +// void setProperty(const QString &name, const QVariant &value, bool errorOnFailure = false); /*!< \brief Overload of QObject::setProperty to handle OpenBR data types. */ | |
| 170 | + | |
| 171 | +//if (key.startsWith(("_Arg"))) { | |
| 172 | +// const int argumentNumber = key.mid(4).toInt(); | |
| 173 | +// const int targetIdx = argumentNumber + firstAvailablePropertyIdx; | |
| 174 | +// if (targetIdx >= metaObject()->propertyCount()) { | |
| 175 | +// qWarning("Too many arguments for transform %s, ignoring %s", qPrintable(objectName()), qPrintable(key)); | |
| 176 | +// continue; | |
| 177 | +// } | |
| 178 | +// key = metaObject()->property(targetIdx).name(); | |
| 179 | +//} | |
| 180 | + | |
| 181 | +//const QVariant value = file.value(key); | |
| 182 | +//if (value.canConvert<QString>()) setProperty(key, value.toString()); // Parse the value first | |
| 183 | +//else setProperty(key, value); // Set the property directly | |
| 184 | + | |
| 185 | +} // namespace br | |
| 186 | + | |
| 187 | +#include "independent.moc" | ... | ... |