diff --git a/CHANGELOG.md b/CHANGELOG.md index 3ac895d..1939d61 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ * Refactored File class to improve point and rect storage (#22) * Added algorithm to show face detection results (#25) * Reorganized GUI code and include paths (#31) +* 'br -daemon' to listen for commands on stdin 0.2.0 - 2/23/13 =============== diff --git a/app/br/br.cpp b/app/br/br.cpp index 0f8ac51..2e26a3c 100644 --- a/app/br/br.cpp +++ b/app/br/br.cpp @@ -67,26 +67,23 @@ public: if (argc == 0) printf("%s\nTry running 'br -help'\n", br_about()); - bool shell = false; - while (shell || (argc > 0)) { + bool daemon = false; + while (daemon || (argc > 0)) { const char *fun; int parc; const char **parv; - if (shell) { - printf("> "); - br_read_line(&parc, &parv); - if (parc == 0) continue; - fun = parv[0]; - parc--; - parv = &parv[1]; - } else /* argc > 0 */ { - fun = &argv[0][1]; - parc = 0; while ((1+parc < argc) && (argv[1+parc][0] != '-')) parc++; - parv = (const char **)&argv[1]; - argc = argc - (1+parc); - argv = &argv[parc+1]; + if (argc == 0) { // daemon + br_read_stdin(&argc, &argv); + if (argc == 0) break; } + fun = argv[0]; + if (fun[0] == '-') fun++; + parc = 0; while ((parc+1 < argc) && (argv[parc+1][0] != '-')) parc++; + parv = (const char **)&argv[1]; + argc = argc - (parc+1); + argv = &argv[parc+1]; + // Core Tasks if (!strcmp(fun, "train")) { check(parc >= 1, "Insufficient parameter count for 'train'."); @@ -94,7 +91,7 @@ public: } else if (!strcmp(fun, "enroll")) { check(parc >= 1, "Insufficient parameter count for 'enroll'."); if (parc == 1) br_enroll(parv[0]); - else br_enroll_n(parc-1, parv, parv[parc-1]); + else br_enroll_n(parc-1, parv, parv[parc-1]); } else if (!strcmp(fun, "compare")) { check((parc >= 2) && (parc <= 3), "Incorrect parameter count for 'compare'."); br_compare(parv[0], parv[1], parc == 3 ? parv[2] : ""); @@ -162,12 +159,12 @@ public: } else if (!strcmp(fun, "version")) { check(parc == 0, "No parameters expected for 'version'."); printf("%s\n", br_version()); - } else if (!strcmp(fun, "shell")) { - check(parc == 0, "No parameters expected for 'shell'."); - shell = true; + } else if (!strcmp(fun, "daemon")) { + check(parc == 0, "No parameters expected for 'daemon'."); + daemon = true; } else if (!strcmp(fun, "exit")) { check(parc == 0, "No parameters expected for 'exit'."); - shell = false; + daemon = false; } else if (!strcmp(fun, "br")) { printf("That's me!\n"); } else if (parc <= 1) { diff --git a/openbr/core/core.cpp b/openbr/core/core.cpp index cb37038..50988a2 100644 --- a/openbr/core/core.cpp +++ b/openbr/core/core.cpp @@ -111,64 +111,68 @@ struct AlgorithmCore if (gallery.isNull()) gallery = getMemoryGallery(input); QScopedPointer g(Gallery::make(gallery)); - if (g.isNull()) return FileList(); - - if (gallery.contains("read") || gallery.contains("cache")) { - fileList = g->files(); - } - if (!fileList.isEmpty() && gallery.contains("cache")) - return fileList; - - const TemplateList i(TemplateList::fromGallery(input)); - if (i.isEmpty()) return fileList; // Nothing to enroll - - if (transform.isNull()) qFatal("Null transform."); - const int blocks = Globals->blocks(i.size()); - Globals->currentStep = 0; - Globals->totalSteps = i.size(); - Globals->startTime.start(); - - const bool noDuplicates = gallery.contains("noDuplicates"); - QStringList fileNames = noDuplicates ? fileList.names() : QStringList(); - const int subBlockSize = 4*std::max(1, Globals->parallelism); - const int numSubBlocks = ceil(1.0*Globals->blockSize/subBlockSize); - int totalCount = 0, failureCount = 0; - double totalBytes = 0; - for (int block=0; blockblockSize + subBlock*subBlockSize, subBlockSize); - if (data.isEmpty()) break; - if (noDuplicates) - for (int i=data.size()-1; i>=0; i--) - if (fileNames.contains(data[i].file.name)) - data.removeAt(i); - const int numFiles = data.size(); - - if (Globals->backProject) { - TemplateList backProjectedData; - transform->backProject(data, backProjectedData); - data = backProjectedData; - } else { - data >> *transform; + if (g.isNull()) qFatal("Null gallery!"); + + do { + fileList.clear(); + + if (gallery.contains("read") || gallery.contains("cache")) + fileList = g->files(); + + if (!fileList.isEmpty() && gallery.contains("cache")) + return fileList; + + const TemplateList i(TemplateList::fromGallery(input)); + if (i.isEmpty()) return fileList; // Nothing to enroll + + if (transform.isNull()) qFatal("Null transform."); + const int blocks = Globals->blocks(i.size()); + Globals->currentStep = 0; + Globals->totalSteps = i.size(); + Globals->startTime.start(); + + const bool noDuplicates = gallery.contains("noDuplicates"); + QStringList fileNames = noDuplicates ? fileList.names() : QStringList(); + const int subBlockSize = 4*std::max(1, Globals->parallelism); + const int numSubBlocks = ceil(1.0*Globals->blockSize/subBlockSize); + int totalCount = 0, failureCount = 0; + double totalBytes = 0; + for (int block=0; blockblockSize + subBlock*subBlockSize, subBlockSize); + if (data.isEmpty()) break; + if (noDuplicates) + for (int i=data.size()-1; i>=0; i--) + if (fileNames.contains(data[i].file.name)) + data.removeAt(i); + const int numFiles = data.size(); + + if (Globals->backProject) { + TemplateList backProjectedData; + transform->backProject(data, backProjectedData); + data = backProjectedData; + } else { + data >> *transform; + } + + g->writeBlock(data); + const FileList newFiles = data.files(); + fileList.append(newFiles); + + totalCount += newFiles.size(); + failureCount += newFiles.failures(); + totalBytes += data.bytes(); + Globals->currentStep += numFiles; + Globals->printStatus(); } - - g->writeBlock(data); - const FileList newFiles = data.files(); - fileList.append(newFiles); - - totalCount += newFiles.size(); - failureCount += newFiles.failures(); - totalBytes += data.bytes(); - Globals->currentStep += numFiles; - Globals->printStatus(); } - } - const float speed = 1000 * Globals->totalSteps / Globals->startTime.elapsed() / std::max(1, abs(Globals->parallelism)); - if (!Globals->quiet && (Globals->totalSteps > 1)) - fprintf(stderr, "\rSPEED=%.1e SIZE=%.4g FAILURES=%d/%d \n", - speed, totalBytes/totalCount, failureCount, totalCount); - Globals->totalSteps = 0; + const float speed = 1000 * Globals->totalSteps / Globals->startTime.elapsed() / std::max(1, abs(Globals->parallelism)); + if (!Globals->quiet && (Globals->totalSteps > 1)) + fprintf(stderr, "\rSPEED=%.1e SIZE=%.4g FAILURES=%d/%d \n", + speed, totalBytes/totalCount, failureCount, totalCount); + Globals->totalSteps = 0; + } while (input.getBool("infinite")); return fileList; } diff --git a/openbr/openbr.cpp b/openbr/openbr.cpp index b0318bf..c525631 100644 --- a/openbr/openbr.cpp +++ b/openbr/openbr.cpp @@ -182,10 +182,10 @@ float br_progress() return Globals->progress(); } -void br_read_line(int *argc, const char ***argv) +void br_read_stdin(int *argc, char ***argv) { static QList byteArrayList; - static QVector rawCharArrayList; + static QVector rawCharArrayList; byteArrayList.clear(); rawCharArrayList.clear(); foreach (const QString &string, QtUtils::parse(QTextStream(stdin).readLine(), ' ')) { diff --git a/openbr/openbr.h b/openbr/openbr.h index 627bc65..07cc848 100644 --- a/openbr/openbr.h +++ b/openbr/openbr.h @@ -297,15 +297,15 @@ BR_EXPORT bool br_plot_metadata(int num_files, const char *files[], const char * BR_EXPORT float br_progress(); /*! - * \brief Read and parse a line from the terminal. + * \brief Read and parse stdin. * - * Used by the \ref cli to implement \c -shell. + * Used by the \ref cli to implement \c -daemon. * Generally not useful otherwise. * \param[out] argc argument count * \param[out] argv argument list * \note \ref managed_return_value */ -BR_EXPORT void br_read_line(int *argc, const char ***argv); +BR_EXPORT void br_read_stdin(int *argc, char ***argv); /*! * \brief Converts a simmat to a new output format. diff --git a/openbr/openbr_plugin.cpp b/openbr/openbr_plugin.cpp index 5136e6e..916fe6b 100644 --- a/openbr/openbr_plugin.cpp +++ b/openbr/openbr_plugin.cpp @@ -131,7 +131,7 @@ void File::set(const QString &key, const QVariant &value) const QString valueString = value.toString(); /* We assume that if the value starts with '0' - then it was probably intended to to be a string UID + then it was probably intended to be a string UID and that it's numerical value is not relevant. */ if (value.canConvert(QVariant::Double) && (!valueString.startsWith('0') || (valueString == "0"))) diff --git a/openbr/plugins/format.cpp b/openbr/plugins/format.cpp index e94ec4a..348683b 100644 --- a/openbr/plugins/format.cpp +++ b/openbr/plugins/format.cpp @@ -492,6 +492,28 @@ BR_REGISTER(Format, matFormat) /*! * \ingroup formats + * \brief Returns an empty matrix. + * \author Josh Klontz \cite jklontz + */ +class nullFormat : public Format +{ + Q_OBJECT + + Template read() const + { + return Template(file, Mat()); + } + + void write(const Template &t) const + { + (void)t; + } +}; + +BR_REGISTER(Format, nullFormat) + +/*! + * \ingroup formats * \brief RAW format * * http://www.nist.gov/srd/nistsd27.cfm diff --git a/openbr/plugins/qtnetwork.cpp b/openbr/plugins/qtnetwork.cpp index b25a6c0..e29d81c 100644 --- a/openbr/plugins/qtnetwork.cpp +++ b/openbr/plugins/qtnetwork.cpp @@ -50,55 +50,119 @@ class urlFormat : public Format BR_REGISTER(Format, urlFormat) -class WebServices : public QTcpServer +/*! + * \ingroup galleries + * \brief Handle POST requests + * \author Josh Klontz \cite jklontz + */ +class postGallery : public Gallery { Q_OBJECT + class TcpServer : public QTcpServer + { + QList socketDescriptors; + QMutex socketDescriptorsLock; + + public: + QList availableSocketDescriptors() + { + QMutexLocker locker(&socketDescriptorsLock); + QList result(socketDescriptors); + socketDescriptors.clear(); + return result; + } + + void addPendingConnection(QTcpSocket *socket) + { + QTcpServer::addPendingConnection(socket); + } + + private: + void incomingConnection(qintptr socketDescriptor) + { + QMutexLocker locker(&socketDescriptorsLock); + socketDescriptors.append(socketDescriptor); + } + }; + public: - WebServices() + static QScopedPointer server; + + postGallery() { - connect(this, SIGNAL(newConnection()), this, SLOT(handleNewConnection())); + if (server.isNull()) { + server.reset(new TcpServer()); + server->listen(QHostAddress::Any, 8080); + server->moveToThread(QCoreApplication::instance()->thread()); + qDebug("Listening on %s:%d", qPrintable(server->serverAddress().toString()), server->serverPort()); + } } - void listen() +private: + TemplateList readBlock(bool *done) { - QTcpServer::listen(QHostAddress::Any, 8080); + *done = true; + TemplateList templates; + foreach (qintptr socketDescriptor, server->availableSocketDescriptors()) { + File f(".post"); + f.set("socketDescriptor", socketDescriptor); + templates.append(f); + } + if (templates.isEmpty()) + templates.append(File(".null")); + return templates; } -private slots: - void handleNewConnection() + void write(const Template &t) { - while (hasPendingConnections()) { - QTcpSocket *socket = QTcpServer::nextPendingConnection(); - connect(socket, SIGNAL(disconnected()), socket, SLOT(deleteLater())); - connect(socket, SIGNAL(readyRead()), this, SLOT(read())); -// socket->write("HTTP/1.1 200 OK\r\n" -// "Content-Type: text/html; charset=UTF-8\r\n\r\n" -// "Hello World!\r\n"); -// socket->disconnectFromHost(); - } + (void) t; + qFatal("Not supported!"); } +}; + +QScopedPointer postGallery::server; + +BR_REGISTER(Gallery, postGallery) - void read() +/*! + * \ingroup formats + * \brief Handle POST requests + * \author Josh Klontz \cite jklontz + */ +class postFormat : public Format +{ + Q_OBJECT + + Template read() const { - QTcpSocket *socket = dynamic_cast(QObject::sender()); - if (socket == NULL) return; + Template t(file); + qDebug() << t.file.flat(); + QTcpSocket *socket = new QTcpSocket(); + socket->setSocketDescriptor(file.get("socketDescriptor")); + + socket->write("HTTP/1.1 200 OK\r\n" + "Content-Type: text/html; charset=UTF-8\r\n\r\n" + "Hello World!\r\n"); + socket->waitForBytesWritten(); + + socket->waitForReadyRead(); QByteArray data = socket->readAll(); - qDebug() << data; + t.append(imdecode(Mat(1, data.size(), CV_8UC1, data.data()), 1)); + + socket->close(); + delete socket; + return t; + } + + void write(const Template &t) const + { + (void) t; + qFatal("Not supported!"); } }; -//static void web() -//{ -// static WebServices webServices; -// if (webServices.isListening()) -// return; - -// webServices.listen(); -// qDebug("Listening on %s:%d", qPrintable(webServices.serverAddress().toString()), webServices.serverPort()); -// while (true) -// QCoreApplication::processEvents(); -//} +BR_REGISTER(Format, postFormat) } // namespace br