Commit e6ad9c2274ce1248cd833074296cfeea5200afb3
Merge branch 'master' of https://github.com/biometrics/openbr
Showing
10 changed files
with
250 additions
and
15 deletions
app/br/br.cpp
| @@ -167,6 +167,8 @@ public: | @@ -167,6 +167,8 @@ public: | ||
| 167 | else if (!strcmp(fun, "help")) { | 167 | else if (!strcmp(fun, "help")) { |
| 168 | check(parc == 0, "No parameters expected for 'help'."); | 168 | check(parc == 0, "No parameters expected for 'help'."); |
| 169 | help(); | 169 | help(); |
| 170 | + } else if (!strcmp(fun, "gui")) { | ||
| 171 | + // Do nothing because we checked for this flag prior to initialization | ||
| 170 | } else if (!strcmp(fun, "objects")) { | 172 | } else if (!strcmp(fun, "objects")) { |
| 171 | check(parc <= 2, "Incorrect parameter count for 'objects'."); | 173 | check(parc <= 2, "Incorrect parameter count for 'objects'."); |
| 172 | printf("%s\n", br_objects(parc >= 1 ? parv[0] : ".*", parc >= 2 ? parv[1] : ".*")); | 174 | printf("%s\n", br_objects(parc >= 1 ? parv[0] : ".*", parc >= 2 ? parv[1] : ".*")); |
| @@ -180,11 +182,10 @@ public: | @@ -180,11 +182,10 @@ public: | ||
| 180 | check(parc == 1, "Incorrect parameter count for 'daemon'."); | 182 | check(parc == 1, "Incorrect parameter count for 'daemon'."); |
| 181 | daemon = true; | 183 | daemon = true; |
| 182 | daemon_pipe = parv[0]; | 184 | daemon_pipe = parv[0]; |
| 183 | - } else if (!strcmp(fun,"slave")) { | 185 | + } else if (!strcmp(fun, "slave")) { |
| 184 | check(parc == 1, "Incorrect parameter count for 'slave'"); | 186 | check(parc == 1, "Incorrect parameter count for 'slave'"); |
| 185 | br_slave_process(parv[0]); | 187 | br_slave_process(parv[0]); |
| 186 | - } | ||
| 187 | - else if (!strcmp(fun, "exit")) { | 188 | + } else if (!strcmp(fun, "exit")) { |
| 188 | check(parc == 0, "No parameters expected for 'exit'."); | 189 | check(parc == 0, "No parameters expected for 'exit'."); |
| 189 | daemon = false; | 190 | daemon = false; |
| 190 | } else if (!strcmp(fun, "getHeader")) { | 191 | } else if (!strcmp(fun, "getHeader")) { |
| @@ -248,6 +249,7 @@ private: | @@ -248,6 +249,7 @@ private: | ||
| 248 | "\n" | 249 | "\n" |
| 249 | "==== Miscellaneous ====\n" | 250 | "==== Miscellaneous ====\n" |
| 250 | "-help\n" | 251 | "-help\n" |
| 252 | + "-gui\n" | ||
| 251 | "-objects [abstraction [implementation]]\n" | 253 | "-objects [abstraction [implementation]]\n" |
| 252 | "-about\n" | 254 | "-about\n" |
| 253 | "-version\n" | 255 | "-version\n" |
| @@ -258,7 +260,7 @@ private: | @@ -258,7 +260,7 @@ private: | ||
| 258 | 260 | ||
| 259 | int main(int argc, char *argv[]) | 261 | int main(int argc, char *argv[]) |
| 260 | { | 262 | { |
| 261 | - br_initialize(argc, argv); | 263 | + br_initialize(argc, argv, "", argc >= 2 && !strcmp(argv[1], "-gui")); |
| 262 | 264 | ||
| 263 | FakeMain *fakeMain = new FakeMain(argc, argv); | 265 | FakeMain *fakeMain = new FakeMain(argc, argv); |
| 264 | QThreadPool::globalInstance()->start(fakeMain); | 266 | QThreadPool::globalInstance()->start(fakeMain); |
openbr/core/core.cpp
| @@ -65,6 +65,9 @@ struct AlgorithmCore | @@ -65,6 +65,9 @@ struct AlgorithmCore | ||
| 65 | downcast->train(data); | 65 | downcast->train(data); |
| 66 | 66 | ||
| 67 | if (!distance.isNull()) { | 67 | if (!distance.isNull()) { |
| 68 | + if (Globals->crossValidate > 0) | ||
| 69 | + for (int i=data.size()-1; i>=0; i--) if (data[i].file.get<bool>("allPartitions",false)) data.removeAt(i); | ||
| 70 | + | ||
| 68 | qDebug("Projecting Enrollment"); | 71 | qDebug("Projecting Enrollment"); |
| 69 | downcast->projectUpdate(data,data); | 72 | downcast->projectUpdate(data,data); |
| 70 | 73 |
openbr/janus.cpp
| @@ -13,7 +13,7 @@ static QSharedPointer<Distance> distance; | @@ -13,7 +13,7 @@ static QSharedPointer<Distance> distance; | ||
| 13 | 13 | ||
| 14 | size_t janus_max_template_size() | 14 | size_t janus_max_template_size() |
| 15 | { | 15 | { |
| 16 | - return JANUS_MAX_TEMPLATE_SIZE_LIMIT; | 16 | + return 33554432; // 32 MB |
| 17 | } | 17 | } |
| 18 | 18 | ||
| 19 | janus_error janus_initialize(const char *sdk_path, const char *model_file) | 19 | janus_error janus_initialize(const char *sdk_path, const char *model_file) |
openbr/openbr.cpp
| @@ -122,9 +122,9 @@ void br_fuse(int num_input_simmats, const char *input_simmats[], | @@ -122,9 +122,9 @@ void br_fuse(int num_input_simmats, const char *input_simmats[], | ||
| 122 | Fuse(QtUtils::toStringList(num_input_simmats, input_simmats), normalization, fusion, output_simmat); | 122 | Fuse(QtUtils::toStringList(num_input_simmats, input_simmats), normalization, fusion, output_simmat); |
| 123 | } | 123 | } |
| 124 | 124 | ||
| 125 | -void br_initialize(int &argc, char *argv[], const char *sdk_path) | 125 | +void br_initialize(int &argc, char *argv[], const char *sdk_path, bool use_gui) |
| 126 | { | 126 | { |
| 127 | - Context::initialize(argc, argv, sdk_path); | 127 | + Context::initialize(argc, argv, sdk_path, use_gui); |
| 128 | } | 128 | } |
| 129 | 129 | ||
| 130 | void br_initialize_default() | 130 | void br_initialize_default() |
| @@ -382,10 +382,9 @@ bool br_img_is_empty(br_template tmpl) | @@ -382,10 +382,9 @@ bool br_img_is_empty(br_template tmpl) | ||
| 382 | 382 | ||
| 383 | const char* br_get_filename(br_template tmpl) | 383 | const char* br_get_filename(br_template tmpl) |
| 384 | { | 384 | { |
| 385 | - Template *t = reinterpret_cast<Template*>(tmpl); | ||
| 386 | - QByteArray s = t->file.name.toLocal8Bit(); | ||
| 387 | - char *buffer = s.data(); | ||
| 388 | - return buffer; | 385 | + static QByteArray buffer; |
| 386 | + buffer = reinterpret_cast<Template*>(tmpl)->file.name.toLocal8Bit(); | ||
| 387 | + return buffer.data(); | ||
| 389 | } | 388 | } |
| 390 | 389 | ||
| 391 | void br_set_filename(br_template tmpl, const char *filename) | 390 | void br_set_filename(br_template tmpl, const char *filename) |
openbr/openbr.h
| @@ -224,7 +224,7 @@ BR_EXPORT void br_fuse(int num_input_simmats, const char *input_simmats[], | @@ -224,7 +224,7 @@ BR_EXPORT void br_fuse(int num_input_simmats, const char *input_simmats[], | ||
| 224 | * \brief Wraps br::Context::initialize() | 224 | * \brief Wraps br::Context::initialize() |
| 225 | * \see br_finalize | 225 | * \see br_finalize |
| 226 | */ | 226 | */ |
| 227 | -BR_EXPORT void br_initialize(int &argc, char *argv[], const char *sdk_path = ""); | 227 | +BR_EXPORT void br_initialize(int &argc, char *argv[], const char *sdk_path = "", bool use_gui = false); |
| 228 | /*! | 228 | /*! |
| 229 | * \brief Wraps br::Context::initialize() with default arguments. | 229 | * \brief Wraps br::Context::initialize() with default arguments. |
| 230 | * \see br_finalize | 230 | * \see br_finalize |
openbr/openbr_plugin.cpp
| @@ -899,8 +899,12 @@ void br::Context::initialize(int &argc, char *argv[], QString sdkPath, bool useG | @@ -899,8 +899,12 @@ void br::Context::initialize(int &argc, char *argv[], QString sdkPath, bool useG | ||
| 899 | { | 899 | { |
| 900 | qInstallMessageHandler(messageHandler); | 900 | qInstallMessageHandler(messageHandler); |
| 901 | 901 | ||
| 902 | + QString sep; | ||
| 902 | #ifndef _WIN32 | 903 | #ifndef _WIN32 |
| 903 | useGui = useGui && (getenv("DISPLAY") != NULL); | 904 | useGui = useGui && (getenv("DISPLAY") != NULL); |
| 905 | + sep = ":"; | ||
| 906 | +#else | ||
| 907 | + sep = ";"; | ||
| 904 | #endif // not _WIN32 | 908 | #endif // not _WIN32 |
| 905 | 909 | ||
| 906 | // We take in argc as a reference due to: | 910 | // We take in argc as a reference due to: |
| @@ -944,6 +948,7 @@ void br::Context::initialize(int &argc, char *argv[], QString sdkPath, bool useG | @@ -944,6 +948,7 @@ void br::Context::initialize(int &argc, char *argv[], QString sdkPath, bool useG | ||
| 944 | // Search for SDK | 948 | // Search for SDK |
| 945 | if (sdkPath.isEmpty()) { | 949 | if (sdkPath.isEmpty()) { |
| 946 | QStringList checkPaths; checkPaths << QDir::currentPath() << QCoreApplication::applicationDirPath(); | 950 | QStringList checkPaths; checkPaths << QDir::currentPath() << QCoreApplication::applicationDirPath(); |
| 951 | + checkPaths << QString(getenv("PATH")).split(sep, QString::SkipEmptyParts); | ||
| 947 | 952 | ||
| 948 | bool foundSDK = false; | 953 | bool foundSDK = false; |
| 949 | foreach (const QString &path, checkPaths) { | 954 | foreach (const QString &path, checkPaths) { |
openbr/plugins/gui.cpp
| 1 | #include <QApplication> | 1 | #include <QApplication> |
| 2 | #include <QLabel> | 2 | #include <QLabel> |
| 3 | #include <QElapsedTimer> | 3 | #include <QElapsedTimer> |
| 4 | +#include <QInputDialog> | ||
| 4 | #include <QWaitCondition> | 5 | #include <QWaitCondition> |
| 5 | #include <QMutex> | 6 | #include <QMutex> |
| 6 | #include <QMouseEvent> | 7 | #include <QMouseEvent> |
| @@ -182,6 +183,149 @@ public slots: | @@ -182,6 +183,149 @@ public slots: | ||
| 182 | } | 183 | } |
| 183 | }; | 184 | }; |
| 184 | 185 | ||
| 186 | +class RectMarkingWindow : public DisplayWindow | ||
| 187 | +{ | ||
| 188 | +public: | ||
| 189 | + RectMarkingWindow() : DisplayWindow() | ||
| 190 | + { | ||
| 191 | + drawingRect = false; | ||
| 192 | + } | ||
| 193 | + | ||
| 194 | + bool drawingRect; | ||
| 195 | + QVector<QRectF> rects; | ||
| 196 | + QList<QString> rectLabels; | ||
| 197 | + | ||
| 198 | + QPointF rectOrigin; | ||
| 199 | + QPointF currentEnd; | ||
| 200 | + QRectF currentRect; | ||
| 201 | + bool disableAccept; | ||
| 202 | + | ||
| 203 | + bool eventFilter(QObject *obj, QEvent *event) | ||
| 204 | + { | ||
| 205 | + if (disableAccept) | ||
| 206 | + return QObject::eventFilter(obj, event); | ||
| 207 | + | ||
| 208 | + | ||
| 209 | + if (event->type() == QEvent::MouseButtonPress || event->type() == QEvent::MouseMove) | ||
| 210 | + { | ||
| 211 | + event->accept(); | ||
| 212 | + | ||
| 213 | + QMouseEvent *mouseEvent = (QMouseEvent*)event; | ||
| 214 | + | ||
| 215 | + if (event->type() == QEvent::MouseButtonPress) | ||
| 216 | + { | ||
| 217 | + | ||
| 218 | + if (mouseEvent->button() == Qt::LeftButton) { | ||
| 219 | + if (!drawingRect) | ||
| 220 | + { | ||
| 221 | + drawingRect = true; | ||
| 222 | + rectOrigin = mouseEvent->pos(); | ||
| 223 | + return true; | ||
| 224 | + } | ||
| 225 | + else | ||
| 226 | + { | ||
| 227 | + drawingRect = false; | ||
| 228 | + | ||
| 229 | + rects.append(QRectF(rectOrigin, mouseEvent->pos())); | ||
| 230 | + rects.last() = rects.last().normalized(); | ||
| 231 | + // If no labels were provided, we store everything as anonymous rectangles | ||
| 232 | + if (promptKeys.empty()) | ||
| 233 | + rectLabels.append("rects"); | ||
| 234 | + // otherwise, prompt the user to select a label | ||
| 235 | + else | ||
| 236 | + { | ||
| 237 | + // get a label from the user | ||
| 238 | + bool ok = false; | ||
| 239 | + | ||
| 240 | + // Don't intercept events while the sub-dialog is up (if we take the events, then it will not work correctly) | ||
| 241 | + disableAccept = true; | ||
| 242 | + QString res = QInputDialog::getItem(this, "Select a label", "", promptKeys, next_idx, false, &ok); | ||
| 243 | + | ||
| 244 | + disableAccept = false; | ||
| 245 | + if (ok) { | ||
| 246 | + rectLabels.append(res); | ||
| 247 | + for (int i=0; i < promptKeys.size(); i++) | ||
| 248 | + { | ||
| 249 | + if (res == promptKeys[i]) { | ||
| 250 | + next_idx = (i + 1) % promptKeys.size(); | ||
| 251 | + break; | ||
| 252 | + } | ||
| 253 | + } | ||
| 254 | + } | ||
| 255 | + else { | ||
| 256 | + rects.removeLast(); | ||
| 257 | + } | ||
| 258 | + } | ||
| 259 | + } | ||
| 260 | + } | ||
| 261 | + // rclick -- reset state if drawing, remove last rect if done | ||
| 262 | + else if (mouseEvent->button() == Qt::RightButton && (!rects.isEmpty() || drawingRect)) | ||
| 263 | + { | ||
| 264 | + if(drawingRect) | ||
| 265 | + drawingRect = false; | ||
| 266 | + else | ||
| 267 | + { | ||
| 268 | + rects.removeLast(); | ||
| 269 | + rectLabels.removeLast(); | ||
| 270 | + } | ||
| 271 | + } | ||
| 272 | + } | ||
| 273 | + else | ||
| 274 | + currentEnd = mouseEvent->pos(); | ||
| 275 | + QPixmap pixmapBuffer = pixmap; | ||
| 276 | + | ||
| 277 | + QPainter painter(&pixmapBuffer); | ||
| 278 | + painter.setPen(Qt::red); | ||
| 279 | + | ||
| 280 | + painter.drawRects(rects); | ||
| 281 | + | ||
| 282 | + if (drawingRect) | ||
| 283 | + { | ||
| 284 | + currentRect = QRectF(rectOrigin, currentEnd); | ||
| 285 | + painter.setPen(Qt::green); | ||
| 286 | + painter.drawRect(currentRect); | ||
| 287 | + } | ||
| 288 | + | ||
| 289 | + setPixmap(pixmapBuffer); | ||
| 290 | + | ||
| 291 | + return true; | ||
| 292 | + } else { | ||
| 293 | + if (event->type() == QEvent::KeyPress) | ||
| 294 | + { | ||
| 295 | + QKeyEvent * kevent = (QKeyEvent *) event; | ||
| 296 | + if (kevent->key() == Qt::Key_Enter || kevent->key() == Qt::Key_Return) { | ||
| 297 | + event->accept(); | ||
| 298 | + return true; | ||
| 299 | + } | ||
| 300 | + } | ||
| 301 | + return DisplayWindow::eventFilter(obj, event); | ||
| 302 | + } | ||
| 303 | + } | ||
| 304 | + | ||
| 305 | + | ||
| 306 | + QList<QPointF> waitForKey() | ||
| 307 | + { | ||
| 308 | + | ||
| 309 | + rects.clear(); | ||
| 310 | + drawingRect = false; | ||
| 311 | + disableAccept = false; | ||
| 312 | + next_idx = 0; | ||
| 313 | + DisplayWindow::waitForKey(); | ||
| 314 | + | ||
| 315 | + return QList<QPointF>(); | ||
| 316 | + } | ||
| 317 | + | ||
| 318 | + void setKeys(const QStringList & keys) | ||
| 319 | + { | ||
| 320 | + promptKeys = keys; | ||
| 321 | + } | ||
| 322 | + | ||
| 323 | + int next_idx; | ||
| 324 | +private: | ||
| 325 | + QStringList promptKeys; | ||
| 326 | + | ||
| 327 | +}; | ||
| 328 | + | ||
| 185 | class PointMarkingWindow : public DisplayWindow | 329 | class PointMarkingWindow : public DisplayWindow |
| 186 | { | 330 | { |
| 187 | bool eventFilter(QObject *obj, QEvent *event) | 331 | bool eventFilter(QObject *obj, QEvent *event) |
| @@ -556,6 +700,87 @@ BR_REGISTER(Transform, ManualTransform) | @@ -556,6 +700,87 @@ BR_REGISTER(Transform, ManualTransform) | ||
| 556 | 700 | ||
| 557 | /*! | 701 | /*! |
| 558 | * \ingroup transforms | 702 | * \ingroup transforms |
| 703 | + * \brief Manual select rectangular regions on an image. | ||
| 704 | + * Stores marked rectangles as anonymous rectangles, or if a set of labels is provided, prompt the user | ||
| 705 | + * to select one of those labels after drawing each rectangle. | ||
| 706 | + * \author Charles Otto \cite caotto | ||
| 707 | + */ | ||
| 708 | +class ManualRectsTransform : public ShowTransform | ||
| 709 | +{ | ||
| 710 | + Q_OBJECT | ||
| 711 | + | ||
| 712 | +public: | ||
| 713 | + | ||
| 714 | + Q_PROPERTY(QStringList labels READ get_labels WRITE set_labels RESET reset_labels STORED false) | ||
| 715 | + BR_PROPERTY(QStringList, labels, QStringList()) | ||
| 716 | + | ||
| 717 | + void projectUpdate(const TemplateList &src, TemplateList &dst) | ||
| 718 | + { | ||
| 719 | + | ||
| 720 | + dst = src; | ||
| 721 | + | ||
| 722 | + if (src.empty()) | ||
| 723 | + return; | ||
| 724 | + | ||
| 725 | + for (int i = 0; i < dst.size(); i++) { | ||
| 726 | + foreach(const cv::Mat &m, dst[i]) { | ||
| 727 | + qImageBuffer = toQImage(m); | ||
| 728 | + displayBuffer->convertFromImage(qImageBuffer); | ||
| 729 | + | ||
| 730 | + emit updateImage(displayBuffer->copy(displayBuffer->rect())); | ||
| 731 | + | ||
| 732 | + // Blocking wait for a key-press | ||
| 733 | + if (this->waitInput) { | ||
| 734 | + window->waitForKey(); | ||
| 735 | + QVector<QRectF> rectSet = trueWindow->rects; | ||
| 736 | + QList<QString> labelSet= trueWindow->rectLabels; | ||
| 737 | + | ||
| 738 | + for (int idx = 0; idx < rectSet.size(); idx++) | ||
| 739 | + { | ||
| 740 | + if (dst[i].file.contains(labelSet[idx])) | ||
| 741 | + { | ||
| 742 | + QVariant currentProp = dst[i].file.value(labelSet[idx]); | ||
| 743 | + QList<QVariant> currentPropList; | ||
| 744 | + | ||
| 745 | + if (currentProp.canConvert<QList<QVariant> >() ) | ||
| 746 | + { | ||
| 747 | + currentPropList = currentProp.toList(); | ||
| 748 | + } | ||
| 749 | + else if (currentProp.canConvert<QRectF>()) | ||
| 750 | + { | ||
| 751 | + currentPropList.append(currentProp); | ||
| 752 | + } | ||
| 753 | + else | ||
| 754 | + { | ||
| 755 | + qFatal("Unknown type of property"); | ||
| 756 | + } | ||
| 757 | + | ||
| 758 | + currentPropList.append(rectSet[idx]); | ||
| 759 | + dst[i].file.set(labelSet[idx], QVariant::fromValue(currentPropList)); | ||
| 760 | + } | ||
| 761 | + else | ||
| 762 | + { | ||
| 763 | + dst[i].file.set(labelSet[idx], rectSet[idx]); | ||
| 764 | + } | ||
| 765 | + } | ||
| 766 | + } | ||
| 767 | + | ||
| 768 | + } | ||
| 769 | + } | ||
| 770 | + } | ||
| 771 | + RectMarkingWindow * trueWindow; | ||
| 772 | + void init() | ||
| 773 | + { | ||
| 774 | + initActual<RectMarkingWindow>(); | ||
| 775 | + trueWindow = dynamic_cast<RectMarkingWindow *> (this->window); | ||
| 776 | + trueWindow->setKeys(this->keys); | ||
| 777 | + } | ||
| 778 | +}; | ||
| 779 | + | ||
| 780 | +BR_REGISTER(Transform, ManualRectsTransform) | ||
| 781 | + | ||
| 782 | +/*! | ||
| 783 | + * \ingroup transforms | ||
| 559 | * \brief Elicits metadata for templates in a pretty GUI | 784 | * \brief Elicits metadata for templates in a pretty GUI |
| 560 | * \author Scott Klum \cite sklum | 785 | * \author Scott Klum \cite sklum |
| 561 | */ | 786 | */ |
openbr/plugins/output.cpp
| @@ -268,7 +268,7 @@ class rrOutput : public MatrixOutput | @@ -268,7 +268,7 @@ class rrOutput : public MatrixOutput | ||
| 268 | 268 | ||
| 269 | for (int i=0; i<queryFiles.size(); i++) { | 269 | for (int i=0; i<queryFiles.size(); i++) { |
| 270 | QStringList files; | 270 | QStringList files; |
| 271 | - if (simple) files.append(queryFiles[i]); | 271 | + if (simple) files.append(queryFiles[i].fileName()); |
| 272 | 272 | ||
| 273 | typedef QPair<float,int> Pair; | 273 | typedef QPair<float,int> Pair; |
| 274 | foreach (const Pair &pair, Common::Sort(OpenCVUtils::matrixToVector<float>(data.row(i)), true, limit)) { | 274 | foreach (const Pair &pair, Common::Sort(OpenCVUtils::matrixToVector<float>(data.row(i)), true, limit)) { |
| @@ -276,7 +276,7 @@ class rrOutput : public MatrixOutput | @@ -276,7 +276,7 @@ class rrOutput : public MatrixOutput | ||
| 276 | if (pair.first < threshold) break; | 276 | if (pair.first < threshold) break; |
| 277 | File target = targetFiles[pair.second]; | 277 | File target = targetFiles[pair.second]; |
| 278 | target.set("Score", QString::number(pair.first)); | 278 | target.set("Score", QString::number(pair.first)); |
| 279 | - if (simple) files.append(target.baseName() + " " + QString::number(pair.first)); | 279 | + if (simple) files.append(target.fileName() + " " + QString::number(pair.first)); |
| 280 | else files.append(target.flat()); | 280 | else files.append(target.flat()); |
| 281 | } | 281 | } |
| 282 | } | 282 | } |
openbr/plugins/validate.cpp
| @@ -108,6 +108,7 @@ class CrossValidateTransform : public MetaTransform | @@ -108,6 +108,7 @@ class CrossValidateTransform : public MetaTransform | ||
| 108 | // If we want to duplicate templates but use the same training data | 108 | // If we want to duplicate templates but use the same training data |
| 109 | // for all partitions (i.e. transforms.size() == 1), we need to | 109 | // for all partitions (i.e. transforms.size() == 1), we need to |
| 110 | // restrict the partition | 110 | // restrict the partition |
| 111 | + | ||
| 111 | int partition = src.file.get<int>("Partition", 0); | 112 | int partition = src.file.get<int>("Partition", 0); |
| 112 | partition = (partition >= transforms.size()) ? 0 : partition; | 113 | partition = (partition >= transforms.size()) ? 0 : partition; |
| 113 | transforms[partition]->project(src, dst); | 114 | transforms[partition]->project(src, dst); |