Commit 6a6d404e40b9efd64ee8441997c0f773a9be132c
Merge branch 'master' of https://github.com/biometrics/openbr
Showing
8 changed files
with
200 additions
and
112 deletions
CHANGELOG.md
| ... | ... | @@ -8,6 +8,7 @@ |
| 8 | 8 | * Refactored File class to improve point and rect storage (#22) |
| 9 | 9 | * Added algorithm to show face detection results (#25) |
| 10 | 10 | * Reorganized GUI code and include paths (#31) |
| 11 | +* 'br -daemon' to listen for commands on stdin | |
| 11 | 12 | |
| 12 | 13 | 0.2.0 - 2/23/13 |
| 13 | 14 | =============== | ... | ... |
app/br/br.cpp
| ... | ... | @@ -67,26 +67,23 @@ public: |
| 67 | 67 | |
| 68 | 68 | if (argc == 0) printf("%s\nTry running 'br -help'\n", br_about()); |
| 69 | 69 | |
| 70 | - bool shell = false; | |
| 71 | - while (shell || (argc > 0)) { | |
| 70 | + bool daemon = false; | |
| 71 | + while (daemon || (argc > 0)) { | |
| 72 | 72 | const char *fun; |
| 73 | 73 | int parc; |
| 74 | 74 | const char **parv; |
| 75 | - if (shell) { | |
| 76 | - printf("> "); | |
| 77 | - br_read_line(&parc, &parv); | |
| 78 | - if (parc == 0) continue; | |
| 79 | - fun = parv[0]; | |
| 80 | - parc--; | |
| 81 | - parv = &parv[1]; | |
| 82 | - } else /* argc > 0 */ { | |
| 83 | - fun = &argv[0][1]; | |
| 84 | - parc = 0; while ((1+parc < argc) && (argv[1+parc][0] != '-')) parc++; | |
| 85 | - parv = (const char **)&argv[1]; | |
| 86 | - argc = argc - (1+parc); | |
| 87 | - argv = &argv[parc+1]; | |
| 75 | + if (argc == 0) { // daemon | |
| 76 | + br_read_stdin(&argc, &argv); | |
| 77 | + if (argc == 0) break; | |
| 88 | 78 | } |
| 89 | 79 | |
| 80 | + fun = argv[0]; | |
| 81 | + if (fun[0] == '-') fun++; | |
| 82 | + parc = 0; while ((parc+1 < argc) && (argv[parc+1][0] != '-')) parc++; | |
| 83 | + parv = (const char **)&argv[1]; | |
| 84 | + argc = argc - (parc+1); | |
| 85 | + argv = &argv[parc+1]; | |
| 86 | + | |
| 90 | 87 | // Core Tasks |
| 91 | 88 | if (!strcmp(fun, "train")) { |
| 92 | 89 | check(parc >= 1, "Insufficient parameter count for 'train'."); |
| ... | ... | @@ -94,7 +91,7 @@ public: |
| 94 | 91 | } else if (!strcmp(fun, "enroll")) { |
| 95 | 92 | check(parc >= 1, "Insufficient parameter count for 'enroll'."); |
| 96 | 93 | if (parc == 1) br_enroll(parv[0]); |
| 97 | - else br_enroll_n(parc-1, parv, parv[parc-1]); | |
| 94 | + else br_enroll_n(parc-1, parv, parv[parc-1]); | |
| 98 | 95 | } else if (!strcmp(fun, "compare")) { |
| 99 | 96 | check((parc >= 2) && (parc <= 3), "Incorrect parameter count for 'compare'."); |
| 100 | 97 | br_compare(parv[0], parv[1], parc == 3 ? parv[2] : ""); |
| ... | ... | @@ -162,12 +159,12 @@ public: |
| 162 | 159 | } else if (!strcmp(fun, "version")) { |
| 163 | 160 | check(parc == 0, "No parameters expected for 'version'."); |
| 164 | 161 | printf("%s\n", br_version()); |
| 165 | - } else if (!strcmp(fun, "shell")) { | |
| 166 | - check(parc == 0, "No parameters expected for 'shell'."); | |
| 167 | - shell = true; | |
| 162 | + } else if (!strcmp(fun, "daemon")) { | |
| 163 | + check(parc == 0, "No parameters expected for 'daemon'."); | |
| 164 | + daemon = true; | |
| 168 | 165 | } else if (!strcmp(fun, "exit")) { |
| 169 | 166 | check(parc == 0, "No parameters expected for 'exit'."); |
| 170 | - shell = false; | |
| 167 | + daemon = false; | |
| 171 | 168 | } else if (!strcmp(fun, "br")) { |
| 172 | 169 | printf("That's me!\n"); |
| 173 | 170 | } else if (parc <= 1) { | ... | ... |
openbr/core/core.cpp
| ... | ... | @@ -111,64 +111,68 @@ struct AlgorithmCore |
| 111 | 111 | if (gallery.isNull()) gallery = getMemoryGallery(input); |
| 112 | 112 | |
| 113 | 113 | QScopedPointer<Gallery> g(Gallery::make(gallery)); |
| 114 | - if (g.isNull()) return FileList(); | |
| 115 | - | |
| 116 | - if (gallery.contains("read") || gallery.contains("cache")) { | |
| 117 | - fileList = g->files(); | |
| 118 | - } | |
| 119 | - if (!fileList.isEmpty() && gallery.contains("cache")) | |
| 120 | - return fileList; | |
| 121 | - | |
| 122 | - const TemplateList i(TemplateList::fromGallery(input)); | |
| 123 | - if (i.isEmpty()) return fileList; // Nothing to enroll | |
| 124 | - | |
| 125 | - if (transform.isNull()) qFatal("Null transform."); | |
| 126 | - const int blocks = Globals->blocks(i.size()); | |
| 127 | - Globals->currentStep = 0; | |
| 128 | - Globals->totalSteps = i.size(); | |
| 129 | - Globals->startTime.start(); | |
| 130 | - | |
| 131 | - const bool noDuplicates = gallery.contains("noDuplicates"); | |
| 132 | - QStringList fileNames = noDuplicates ? fileList.names() : QStringList(); | |
| 133 | - const int subBlockSize = 4*std::max(1, Globals->parallelism); | |
| 134 | - const int numSubBlocks = ceil(1.0*Globals->blockSize/subBlockSize); | |
| 135 | - int totalCount = 0, failureCount = 0; | |
| 136 | - double totalBytes = 0; | |
| 137 | - for (int block=0; block<blocks; block++) { | |
| 138 | - for (int subBlock = 0; subBlock<numSubBlocks; subBlock++) { | |
| 139 | - TemplateList data = i.mid(block*Globals->blockSize + subBlock*subBlockSize, subBlockSize); | |
| 140 | - if (data.isEmpty()) break; | |
| 141 | - if (noDuplicates) | |
| 142 | - for (int i=data.size()-1; i>=0; i--) | |
| 143 | - if (fileNames.contains(data[i].file.name)) | |
| 144 | - data.removeAt(i); | |
| 145 | - const int numFiles = data.size(); | |
| 146 | - | |
| 147 | - if (Globals->backProject) { | |
| 148 | - TemplateList backProjectedData; | |
| 149 | - transform->backProject(data, backProjectedData); | |
| 150 | - data = backProjectedData; | |
| 151 | - } else { | |
| 152 | - data >> *transform; | |
| 114 | + if (g.isNull()) qFatal("Null gallery!"); | |
| 115 | + | |
| 116 | + do { | |
| 117 | + fileList.clear(); | |
| 118 | + | |
| 119 | + if (gallery.contains("read") || gallery.contains("cache")) | |
| 120 | + fileList = g->files(); | |
| 121 | + | |
| 122 | + if (!fileList.isEmpty() && gallery.contains("cache")) | |
| 123 | + return fileList; | |
| 124 | + | |
| 125 | + const TemplateList i(TemplateList::fromGallery(input)); | |
| 126 | + if (i.isEmpty()) return fileList; // Nothing to enroll | |
| 127 | + | |
| 128 | + if (transform.isNull()) qFatal("Null transform."); | |
| 129 | + const int blocks = Globals->blocks(i.size()); | |
| 130 | + Globals->currentStep = 0; | |
| 131 | + Globals->totalSteps = i.size(); | |
| 132 | + Globals->startTime.start(); | |
| 133 | + | |
| 134 | + const bool noDuplicates = gallery.contains("noDuplicates"); | |
| 135 | + QStringList fileNames = noDuplicates ? fileList.names() : QStringList(); | |
| 136 | + const int subBlockSize = 4*std::max(1, Globals->parallelism); | |
| 137 | + const int numSubBlocks = ceil(1.0*Globals->blockSize/subBlockSize); | |
| 138 | + int totalCount = 0, failureCount = 0; | |
| 139 | + double totalBytes = 0; | |
| 140 | + for (int block=0; block<blocks; block++) { | |
| 141 | + for (int subBlock = 0; subBlock<numSubBlocks; subBlock++) { | |
| 142 | + TemplateList data = i.mid(block*Globals->blockSize + subBlock*subBlockSize, subBlockSize); | |
| 143 | + if (data.isEmpty()) break; | |
| 144 | + if (noDuplicates) | |
| 145 | + for (int i=data.size()-1; i>=0; i--) | |
| 146 | + if (fileNames.contains(data[i].file.name)) | |
| 147 | + data.removeAt(i); | |
| 148 | + const int numFiles = data.size(); | |
| 149 | + | |
| 150 | + if (Globals->backProject) { | |
| 151 | + TemplateList backProjectedData; | |
| 152 | + transform->backProject(data, backProjectedData); | |
| 153 | + data = backProjectedData; | |
| 154 | + } else { | |
| 155 | + data >> *transform; | |
| 156 | + } | |
| 157 | + | |
| 158 | + g->writeBlock(data); | |
| 159 | + const FileList newFiles = data.files(); | |
| 160 | + fileList.append(newFiles); | |
| 161 | + | |
| 162 | + totalCount += newFiles.size(); | |
| 163 | + failureCount += newFiles.failures(); | |
| 164 | + totalBytes += data.bytes<double>(); | |
| 165 | + Globals->currentStep += numFiles; | |
| 166 | + Globals->printStatus(); | |
| 153 | 167 | } |
| 154 | - | |
| 155 | - g->writeBlock(data); | |
| 156 | - const FileList newFiles = data.files(); | |
| 157 | - fileList.append(newFiles); | |
| 158 | - | |
| 159 | - totalCount += newFiles.size(); | |
| 160 | - failureCount += newFiles.failures(); | |
| 161 | - totalBytes += data.bytes<double>(); | |
| 162 | - Globals->currentStep += numFiles; | |
| 163 | - Globals->printStatus(); | |
| 164 | 168 | } |
| 165 | - } | |
| 166 | 169 | |
| 167 | - const float speed = 1000 * Globals->totalSteps / Globals->startTime.elapsed() / std::max(1, abs(Globals->parallelism)); | |
| 168 | - if (!Globals->quiet && (Globals->totalSteps > 1)) | |
| 169 | - fprintf(stderr, "\rSPEED=%.1e SIZE=%.4g FAILURES=%d/%d \n", | |
| 170 | - speed, totalBytes/totalCount, failureCount, totalCount); | |
| 171 | - Globals->totalSteps = 0; | |
| 170 | + const float speed = 1000 * Globals->totalSteps / Globals->startTime.elapsed() / std::max(1, abs(Globals->parallelism)); | |
| 171 | + if (!Globals->quiet && (Globals->totalSteps > 1)) | |
| 172 | + fprintf(stderr, "\rSPEED=%.1e SIZE=%.4g FAILURES=%d/%d \n", | |
| 173 | + speed, totalBytes/totalCount, failureCount, totalCount); | |
| 174 | + Globals->totalSteps = 0; | |
| 175 | + } while (input.getBool("infinite")); | |
| 172 | 176 | |
| 173 | 177 | return fileList; |
| 174 | 178 | } | ... | ... |
openbr/openbr.cpp
| ... | ... | @@ -182,10 +182,10 @@ float br_progress() |
| 182 | 182 | return Globals->progress(); |
| 183 | 183 | } |
| 184 | 184 | |
| 185 | -void br_read_line(int *argc, const char ***argv) | |
| 185 | +void br_read_stdin(int *argc, char ***argv) | |
| 186 | 186 | { |
| 187 | 187 | static QList<QByteArray> byteArrayList; |
| 188 | - static QVector<const char*> rawCharArrayList; | |
| 188 | + static QVector<char*> rawCharArrayList; | |
| 189 | 189 | |
| 190 | 190 | byteArrayList.clear(); rawCharArrayList.clear(); |
| 191 | 191 | foreach (const QString &string, QtUtils::parse(QTextStream(stdin).readLine(), ' ')) { | ... | ... |
openbr/openbr.h
| ... | ... | @@ -297,15 +297,15 @@ BR_EXPORT bool br_plot_metadata(int num_files, const char *files[], const char * |
| 297 | 297 | BR_EXPORT float br_progress(); |
| 298 | 298 | |
| 299 | 299 | /*! |
| 300 | - * \brief Read and parse a line from the terminal. | |
| 300 | + * \brief Read and parse stdin. | |
| 301 | 301 | * |
| 302 | - * Used by the \ref cli to implement \c -shell. | |
| 302 | + * Used by the \ref cli to implement \c -daemon. | |
| 303 | 303 | * Generally not useful otherwise. |
| 304 | 304 | * \param[out] argc argument count |
| 305 | 305 | * \param[out] argv argument list |
| 306 | 306 | * \note \ref managed_return_value |
| 307 | 307 | */ |
| 308 | -BR_EXPORT void br_read_line(int *argc, const char ***argv); | |
| 308 | +BR_EXPORT void br_read_stdin(int *argc, char ***argv); | |
| 309 | 309 | |
| 310 | 310 | /*! |
| 311 | 311 | * \brief Converts a simmat to a new output format. | ... | ... |
openbr/openbr_plugin.cpp
| ... | ... | @@ -131,7 +131,7 @@ void File::set(const QString &key, const QVariant &value) |
| 131 | 131 | const QString valueString = value.toString(); |
| 132 | 132 | |
| 133 | 133 | /* We assume that if the value starts with '0' |
| 134 | - then it was probably intended to to be a string UID | |
| 134 | + then it was probably intended to be a string UID | |
| 135 | 135 | and that it's numerical value is not relevant. */ |
| 136 | 136 | if (value.canConvert(QVariant::Double) && |
| 137 | 137 | (!valueString.startsWith('0') || (valueString == "0"))) | ... | ... |
openbr/plugins/format.cpp
| ... | ... | @@ -492,6 +492,28 @@ BR_REGISTER(Format, matFormat) |
| 492 | 492 | |
| 493 | 493 | /*! |
| 494 | 494 | * \ingroup formats |
| 495 | + * \brief Returns an empty matrix. | |
| 496 | + * \author Josh Klontz \cite jklontz | |
| 497 | + */ | |
| 498 | +class nullFormat : public Format | |
| 499 | +{ | |
| 500 | + Q_OBJECT | |
| 501 | + | |
| 502 | + Template read() const | |
| 503 | + { | |
| 504 | + return Template(file, Mat()); | |
| 505 | + } | |
| 506 | + | |
| 507 | + void write(const Template &t) const | |
| 508 | + { | |
| 509 | + (void)t; | |
| 510 | + } | |
| 511 | +}; | |
| 512 | + | |
| 513 | +BR_REGISTER(Format, nullFormat) | |
| 514 | + | |
| 515 | +/*! | |
| 516 | + * \ingroup formats | |
| 495 | 517 | * \brief RAW format |
| 496 | 518 | * |
| 497 | 519 | * http://www.nist.gov/srd/nistsd27.cfm | ... | ... |
openbr/plugins/qtnetwork.cpp
| ... | ... | @@ -50,55 +50,119 @@ class urlFormat : public Format |
| 50 | 50 | |
| 51 | 51 | BR_REGISTER(Format, urlFormat) |
| 52 | 52 | |
| 53 | -class WebServices : public QTcpServer | |
| 53 | +/*! | |
| 54 | + * \ingroup galleries | |
| 55 | + * \brief Handle POST requests | |
| 56 | + * \author Josh Klontz \cite jklontz | |
| 57 | + */ | |
| 58 | +class postGallery : public Gallery | |
| 54 | 59 | { |
| 55 | 60 | Q_OBJECT |
| 56 | 61 | |
| 62 | + class TcpServer : public QTcpServer | |
| 63 | + { | |
| 64 | + QList<qintptr> socketDescriptors; | |
| 65 | + QMutex socketDescriptorsLock; | |
| 66 | + | |
| 67 | + public: | |
| 68 | + QList<qintptr> availableSocketDescriptors() | |
| 69 | + { | |
| 70 | + QMutexLocker locker(&socketDescriptorsLock); | |
| 71 | + QList<qintptr> result(socketDescriptors); | |
| 72 | + socketDescriptors.clear(); | |
| 73 | + return result; | |
| 74 | + } | |
| 75 | + | |
| 76 | + void addPendingConnection(QTcpSocket *socket) | |
| 77 | + { | |
| 78 | + QTcpServer::addPendingConnection(socket); | |
| 79 | + } | |
| 80 | + | |
| 81 | + private: | |
| 82 | + void incomingConnection(qintptr socketDescriptor) | |
| 83 | + { | |
| 84 | + QMutexLocker locker(&socketDescriptorsLock); | |
| 85 | + socketDescriptors.append(socketDescriptor); | |
| 86 | + } | |
| 87 | + }; | |
| 88 | + | |
| 57 | 89 | public: |
| 58 | - WebServices() | |
| 90 | + static QScopedPointer<TcpServer> server; | |
| 91 | + | |
| 92 | + postGallery() | |
| 59 | 93 | { |
| 60 | - connect(this, SIGNAL(newConnection()), this, SLOT(handleNewConnection())); | |
| 94 | + if (server.isNull()) { | |
| 95 | + server.reset(new TcpServer()); | |
| 96 | + server->listen(QHostAddress::Any, 8080); | |
| 97 | + server->moveToThread(QCoreApplication::instance()->thread()); | |
| 98 | + qDebug("Listening on %s:%d", qPrintable(server->serverAddress().toString()), server->serverPort()); | |
| 99 | + } | |
| 61 | 100 | } |
| 62 | 101 | |
| 63 | - void listen() | |
| 102 | +private: | |
| 103 | + TemplateList readBlock(bool *done) | |
| 64 | 104 | { |
| 65 | - QTcpServer::listen(QHostAddress::Any, 8080); | |
| 105 | + *done = true; | |
| 106 | + TemplateList templates; | |
| 107 | + foreach (qintptr socketDescriptor, server->availableSocketDescriptors()) { | |
| 108 | + File f(".post"); | |
| 109 | + f.set("socketDescriptor", socketDescriptor); | |
| 110 | + templates.append(f); | |
| 111 | + } | |
| 112 | + if (templates.isEmpty()) | |
| 113 | + templates.append(File(".null")); | |
| 114 | + return templates; | |
| 66 | 115 | } |
| 67 | 116 | |
| 68 | -private slots: | |
| 69 | - void handleNewConnection() | |
| 117 | + void write(const Template &t) | |
| 70 | 118 | { |
| 71 | - while (hasPendingConnections()) { | |
| 72 | - QTcpSocket *socket = QTcpServer::nextPendingConnection(); | |
| 73 | - connect(socket, SIGNAL(disconnected()), socket, SLOT(deleteLater())); | |
| 74 | - connect(socket, SIGNAL(readyRead()), this, SLOT(read())); | |
| 75 | -// socket->write("HTTP/1.1 200 OK\r\n" | |
| 76 | -// "Content-Type: text/html; charset=UTF-8\r\n\r\n" | |
| 77 | -// "Hello World!\r\n"); | |
| 78 | -// socket->disconnectFromHost(); | |
| 79 | - } | |
| 119 | + (void) t; | |
| 120 | + qFatal("Not supported!"); | |
| 80 | 121 | } |
| 122 | +}; | |
| 123 | + | |
| 124 | +QScopedPointer<postGallery::TcpServer> postGallery::server; | |
| 125 | + | |
| 126 | +BR_REGISTER(Gallery, postGallery) | |
| 81 | 127 | |
| 82 | - void read() | |
| 128 | +/*! | |
| 129 | + * \ingroup formats | |
| 130 | + * \brief Handle POST requests | |
| 131 | + * \author Josh Klontz \cite jklontz | |
| 132 | + */ | |
| 133 | +class postFormat : public Format | |
| 134 | +{ | |
| 135 | + Q_OBJECT | |
| 136 | + | |
| 137 | + Template read() const | |
| 83 | 138 | { |
| 84 | - QTcpSocket *socket = dynamic_cast<QTcpSocket*>(QObject::sender()); | |
| 85 | - if (socket == NULL) return; | |
| 139 | + Template t(file); | |
| 140 | + qDebug() << t.file.flat(); | |
| 141 | + QTcpSocket *socket = new QTcpSocket(); | |
| 142 | + socket->setSocketDescriptor(file.get<qintptr>("socketDescriptor")); | |
| 143 | + | |
| 144 | + socket->write("HTTP/1.1 200 OK\r\n" | |
| 145 | + "Content-Type: text/html; charset=UTF-8\r\n\r\n" | |
| 146 | + "Hello World!\r\n"); | |
| 147 | + socket->waitForBytesWritten(); | |
| 148 | + | |
| 149 | + socket->waitForReadyRead(); | |
| 86 | 150 | QByteArray data = socket->readAll(); |
| 87 | - qDebug() << data; | |
| 151 | + t.append(imdecode(Mat(1, data.size(), CV_8UC1, data.data()), 1)); | |
| 152 | + | |
| 153 | + socket->close(); | |
| 154 | + delete socket; | |
| 155 | + return t; | |
| 156 | + } | |
| 157 | + | |
| 158 | + void write(const Template &t) const | |
| 159 | + { | |
| 160 | + (void) t; | |
| 161 | + qFatal("Not supported!"); | |
| 88 | 162 | } |
| 89 | 163 | }; |
| 90 | 164 | |
| 91 | -//static void web() | |
| 92 | -//{ | |
| 93 | -// static WebServices webServices; | |
| 94 | -// if (webServices.isListening()) | |
| 95 | -// return; | |
| 96 | - | |
| 97 | -// webServices.listen(); | |
| 98 | -// qDebug("Listening on %s:%d", qPrintable(webServices.serverAddress().toString()), webServices.serverPort()); | |
| 99 | -// while (true) | |
| 100 | -// QCoreApplication::processEvents(); | |
| 101 | -//} | |
| 165 | +BR_REGISTER(Format, postFormat) | |
| 102 | 166 | |
| 103 | 167 | } // namespace br |
| 104 | 168 | ... | ... |