From 82f953b911a284e5cdae0c00af65a482f71a35d6 Mon Sep 17 00:00:00 2001 From: matlabbe Date: Mon, 12 Jan 2015 01:19:09 +0000 Subject: [PATCH] Added tcpService tool to Add/Remove objects through TCP git-svn-id: http://find-object.googlecode.com/svn/trunk/find_object@420 620bd6b2-0a58-f614-fd9a-1bd335dccda9 --- app/main.cpp | 14 +++ include/find_object/FindObject.h | 8 +- include/find_object/MainWindow.h | 5 +- include/find_object/TcpServer.h | 17 +++ src/FindObject.cpp | 95 ++++++++++---- src/MainWindow.cpp | 130 +++++++++++++------ src/TcpServer.cpp | 98 ++++++++++++++- tools/CMakeLists.txt | 1 + tools/tcpService/CMakeLists.txt | 33 +++++ tools/tcpService/main.cpp | 208 +++++++++++++++++++++++++++++++ 10 files changed, 546 insertions(+), 63 deletions(-) create mode 100644 tools/tcpService/CMakeLists.txt create mode 100644 tools/tcpService/main.cpp diff --git a/app/main.cpp b/app/main.cpp index c006d953..27f84457 100644 --- a/app/main.cpp +++ b/app/main.cpp @@ -509,6 +509,10 @@ int main(int argc, char* argv[]) QObject::connect(&camera, SIGNAL(imageReceived(const cv::Mat &)), findObject, SLOT(detect(const cv::Mat &))); QObject::connect(&camera, SIGNAL(finished()), &app, SLOT(quit())); + //connect services + QObject::connect(&tcpServer, SIGNAL(addObject(const cv::Mat &, int, const QString &)), findObject, SLOT(addObjectAndUpdate(const cv::Mat &, int, const QString &))); + QObject::connect(&tcpServer, SIGNAL(removeObject(int)), findObject, SLOT(removeObjectAndUpdate(int))); + //use camera in settings setupQuitSignal(); @@ -521,6 +525,16 @@ int main(int argc, char* argv[]) if(running) { app.exec(); + + if(!sessionPath.isEmpty() && findObject->isSessionModified()) + { + UINFO("The session has been modified, updating the session file..."); + if(findObject->saveSession(sessionPath)) + { + UINFO("Session \"%s\" successfully saved (%d objects)!", + sessionPath.toStdString().c_str(), findObject->objects().size()); + } + } } // cleanup diff --git a/include/find_object/FindObject.h b/include/find_object/FindObject.h index 14f7e4c8..1df6aab2 100644 --- a/include/find_object/FindObject.h +++ b/include/find_object/FindObject.h @@ -66,7 +66,8 @@ public: virtual ~FindObject(); bool loadSession(const QString & path); - bool saveSession(const QString & path) const; + bool saveSession(const QString & path); + bool isSessionModified() const {return sessionModified_;} int loadObjects(const QString & dirPath); // call updateObjects() const ObjSignature * addObject(const QString & filePath); @@ -78,13 +79,15 @@ public: bool detect(const cv::Mat & image, find_object::DetectionInfo & info); void updateDetectorExtractor(); - void updateObjects(); + void updateObjects(const QList & ids = QList()); void updateVocabulary(); const QMap & objects() const {return objects_;} const Vocabulary * vocabulary() const {return vocabulary_;} public Q_SLOTS: + void addObjectAndUpdate(const cv::Mat & image, int id=0, const QString & filename = QString()); + void removeObjectAndUpdate(int id); void detect(const cv::Mat & image); // emit objectsFound() Q_SIGNALS: @@ -100,6 +103,7 @@ private: QMap dataRange_; // KeypointDetector * detector_; DescriptorExtractor * extractor_; + bool sessionModified_; }; } // namespace find_object diff --git a/include/find_object/MainWindow.h b/include/find_object/MainWindow.h index e20a8b9f..702632fe 100644 --- a/include/find_object/MainWindow.h +++ b/include/find_object/MainWindow.h @@ -90,12 +90,14 @@ private Q_SLOTS: void addObjectFromScene(); void addObjectsFromFiles(const QStringList & fileNames); void addObjectsFromFiles(); + void addObjectFromTcp(const cv::Mat & image, int id, const QString & filePath); void loadSceneFromFile(const QStringList & fileNames); void loadSceneFromFile(); void setupCameraFromVideoFile(); void setupCameraFromImagesDirectory(); void setupCameraFromTcpIp(); void removeObject(find_object::ObjWidget * object); + void removeObject(int id); void removeAllObjects(); void updateObjectsSize(); void updateMirrorView(); @@ -116,10 +118,11 @@ private: int loadObjects(const QString & dirPath); int saveObjects(const QString & dirPath); void setupTCPServer(); - bool addObjectFromFile(const QString & filePath); + int addObjectFromFile(const QString & filePath); void showObject(find_object::ObjWidget * obj); void updateObjectSize(find_object::ObjWidget * obj); void updateVocabulary(); + void updateObjects(const QList & ids); private: Ui_mainWindow * ui_; diff --git a/include/find_object/TcpServer.h b/include/find_object/TcpServer.h index 3476c874..96038889 100644 --- a/include/find_object/TcpServer.h +++ b/include/find_object/TcpServer.h @@ -31,6 +31,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "find_object/FindObjectExp.h" // DLL export/import defines #include "find_object/DetectionInfo.h" +#include #include @@ -42,6 +43,12 @@ class FINDOBJECT_EXP TcpServer : public QTcpServer { Q_OBJECT +public: + enum Service { + kAddObject, // id fileName imageSize image + kRemoveObject // id + }; + public: TcpServer(quint16 port = 0, QObject * parent = 0); @@ -53,6 +60,16 @@ public Q_SLOTS: private Q_SLOTS: void addClient(); + void readReceivedData(); + void displayError(QAbstractSocket::SocketError socketError); + void connectionLost(); + +Q_SIGNALS: + void addObject(const cv::Mat &, int, const QString &); + void removeObject(int); + +private: + QMap blockSizes_; }; } // namespace find_object diff --git a/src/FindObject.cpp b/src/FindObject.cpp index e17d9ca0..81d211cd 100644 --- a/src/FindObject.cpp +++ b/src/FindObject.cpp @@ -46,7 +46,8 @@ FindObject::FindObject(QObject * parent) : QObject(parent), vocabulary_(new Vocabulary()), detector_(Settings::createKeypointDetector()), - extractor_(Settings::createDescriptorExtractor()) + extractor_(Settings::createDescriptorExtractor()), + sessionModified_(false) { qRegisterMetaType("find_object::DetectionInfo"); UASSERT(detector_ != 0 && extractor_ != 0); @@ -101,7 +102,7 @@ bool FindObject::loadSession(const QString & path) // this will fill objectsDescriptors_ matrix updateVocabulary(); } - + sessionModified_ = false; return true; } else @@ -111,7 +112,7 @@ bool FindObject::loadSession(const QString & path) return false; } -bool FindObject::saveSession(const QString & path) const +bool FindObject::saveSession(const QString & path) { if(!path.isEmpty() && QFileInfo(path).suffix().compare("bin") == 0) { @@ -132,6 +133,7 @@ bool FindObject::saveSession(const QString & path) const } file.close(); + sessionModified_ = false; return true; } UERROR("Path \"%s\" not valid (should be *.bin)", path.toStdString().c_str()); @@ -243,6 +245,28 @@ void FindObject::removeAllObjects() clearVocabulary(); } +void FindObject::addObjectAndUpdate(const cv::Mat & image, int id, const QString & filename) +{ + const ObjSignature * s = this->addObject(image, id, filename); + if(s) + { + QList ids; + ids.push_back(s->id()); + updateObjects(ids); + updateVocabulary(); + } +} + +void FindObject::removeObjectAndUpdate(int id) +{ + if(objects_.contains(id)) + { + delete objects_.value(id); + objects_.remove(id); + } + updateVocabulary(); +} + void FindObject::updateDetectorExtractor() { delete detector_; @@ -561,39 +585,63 @@ private: int timeExtraction_; }; -void FindObject::updateObjects() +void FindObject::updateObjects(const QList & ids) { - if(objects_.size()) + QList objectsList; + if(ids.size()) { + for(int i=0; i objectsList = objects_.values(); - for(int i=0; i threads; - for(int k=i; kid(), objectsList.at(k)->image())); - threads.back()->start(); - } - - for(int j=0; jwait(); - - int id = threads[j]->objectId(); - - objects_.value(id)->setData(threads[j]->keypoints(), threads[j]->descriptors()); + QVector threads; + for(int k=i; kid(), objectsList.at(k)->image())); + threads.back()->start(); + } + + for(int j=0; jwait(); + + int id = threads[j]->objectId(); + + objects_.value(id)->setData(threads[j]->keypoints(), threads[j]->descriptors()); + } } + UINFO("Features extraction from %d objects... done! (%d ms)", objectsList.size(), time.elapsed()); } - UINFO("Features extraction from %d objects... done! (%d ms)", objects_.size(), time.elapsed()); } else { @@ -667,6 +715,7 @@ void FindObject::updateVocabulary() if(Settings::getGeneral_invertedSearch()) { + sessionModified_ = true; QTime time; time.start(); bool incremental = Settings::getGeneral_vocabularyIncremental(); diff --git a/src/MainWindow.cpp b/src/MainWindow.cpp index 9ccdd3cc..be1ce22f 100644 --- a/src/MainWindow.cpp +++ b/src/MainWindow.cpp @@ -311,6 +311,10 @@ void MainWindow::setupTCPServer() ui_->label_ipAddress->setText(tcpServer_->getHostAddress().toString()); ui_->label_port->setNum(tcpServer_->getPort()); UINFO("Detection sent on port: %d (IP=%s)", tcpServer_->getPort(), tcpServer_->getHostAddress().toString().toStdString().c_str()); + + //connect services + connect(tcpServer_, SIGNAL(addObject(const cv::Mat &, int, const QString &)), this, SLOT(addObjectFromTcp(const cv::Mat &, int, const QString &))); + connect(tcpServer_, SIGNAL(removeObject(int)), this, SLOT(removeObject(int))); } void MainWindow::setSourceImageText(const QString & text) @@ -459,7 +463,7 @@ bool MainWindow::saveSettings(const QString & path) const int MainWindow::loadObjects(const QString & dirPath) { - int loadedObjects = 0; + QList loadedObjects; QString formats = Settings::getGeneral_imageFormats().remove('*').remove('.'); UDirectory dir(dirPath.toStdString(), formats.toStdString()); if(dir.isValid()) @@ -467,17 +471,18 @@ int MainWindow::loadObjects(const QString & dirPath) const std::list & names = dir.getFileNames(); // sorted in natural order for(std::list::const_iterator iter=names.begin(); iter!=names.end(); ++iter) { - if(this->addObjectFromFile((dirPath.toStdString()+dir.separator()+*iter).c_str())) + int id = this->addObjectFromFile((dirPath.toStdString()+dir.separator()+*iter).c_str()); + if(id >= 0) { - ++loadedObjects; + loadedObjects.push_back(id); } } - if(loadedObjects) + if(loadedObjects.size()) { - this->updateObjects(); + this->updateObjects(loadedObjects); } } - return loadedObjects; + return loadedObjects.size(); } int MainWindow::saveObjects(const QString & dirPath) @@ -560,6 +565,18 @@ void MainWindow::removeObject(find_object::ObjWidget * object) } } +void MainWindow::removeObject(int id) +{ + if(objWidgets_.contains(id)) + { + removeObject(objWidgets_[id]); + } + else + { + UERROR("Remove object: Object %d not found!", id); + } +} + void MainWindow::removeAllObjects() { qDeleteAll(objWidgets_); @@ -677,12 +694,20 @@ void MainWindow::addObjectsFromFiles(const QStringList & fileNames) { if(fileNames.size()) { + QList ids; for(int i=0; iaddObjectFromFile(fileNames.at(i)); + int id = this->addObjectFromFile(fileNames.at(i)); + if(id >= 0) + { + ids.push_back(id); + } + } + if(ids.size()) + { + objectsModified_ = true; + updateObjects(ids); } - objectsModified_ = true; - updateObjects(); } } @@ -691,7 +716,7 @@ void MainWindow::addObjectsFromFiles() addObjectsFromFiles(QFileDialog::getOpenFileNames(this, tr("Add objects..."), Settings::workingDirectory(), tr("Image Files (%1)").arg(Settings::getGeneral_imageFormats()))); } -bool MainWindow::addObjectFromFile(const QString & filePath) +int MainWindow::addObjectFromFile(const QString & filePath) { const ObjSignature * s = findObject_->addObject(filePath); if(s) @@ -701,12 +726,36 @@ bool MainWindow::addObjectFromFile(const QString & filePath) ui_->actionSave_objects->setEnabled(true); ui_->actionSave_session->setEnabled(true); this->showObject(obj); - return true; + return s->id(); } else { QMessageBox::critical(this, tr("Error adding object"), tr("Failed to add object from \"%1\"").arg(filePath)); - return false; + return -1; + } +} + +void MainWindow::addObjectFromTcp(const cv::Mat & image, int id, const QString & filePath) +{ + if(objWidgets_.contains(id)) + { + UERROR("Add Object: Object %d is already added.", id); + } + const ObjSignature * s = findObject_->addObject(image, id, filePath); + if(s) + { + ObjWidget * obj = new ObjWidget(s->id(), std::vector(), cvtCvMat2QImage(s->image())); + objWidgets_.insert(obj->id(), obj); + ui_->actionSave_objects->setEnabled(true); + ui_->actionSave_session->setEnabled(true); + this->showObject(obj); + QList ids; + ids.push_back(obj->id()); + updateObjects(ids); + } + else + { + UERROR("Add Object: Error adding object %d.", id); } } @@ -883,30 +932,41 @@ void MainWindow::showObject(ObjWidget * obj) } } +void MainWindow::updateObjects(const QList & ids) +{ + if(ids.size()) + { + this->statusBar()->showMessage(tr("Updating %1 objects...").arg(ids.size())); + QApplication::processEvents(); + + findObject_->updateObjects(ids); + + updateVocabulary(); + + QList signatures = findObject_->objects().values(); + for(int i=0; iid())) + { + objWidgets_.value(signatures[i]->id())->setData(signatures[i]->keypoints(), cvtCvMat2QImage(signatures[i]->image())); + + //update object labels + QLabel * title = qFindChild(this, QString("%1title").arg(signatures[i]->id())); + title->setText(QString("%1 (%2)").arg(signatures[i]->id()).arg(QString::number(signatures[i]->keypoints().size()))); + } + } + + if(!camera_->isRunning() && !sceneImage_.empty()) + { + this->update(sceneImage_); + } + this->statusBar()->clearMessage(); + } +} + void MainWindow::updateObjects() { - this->statusBar()->showMessage(tr("Updating %1 objects...").arg(findObject_->objects().size())); - QApplication::processEvents(); - - findObject_->updateObjects(); - - updateVocabulary(); - - QList signatures = findObject_->objects().values(); - for(int i=0; iid())->setData(signatures[i]->keypoints(), cvtCvMat2QImage(signatures[i]->image())); - - //update object labels - QLabel * title = qFindChild(this, QString("%1title").arg(signatures[i]->id())); - title->setText(QString("%1 (%2)").arg(signatures[i]->id()).arg(QString::number(signatures[i]->keypoints().size()))); - } - - if(!camera_->isRunning() && !sceneImage_.empty()) - { - this->update(sceneImage_); - } - this->statusBar()->clearMessage(); + updateObjects(objWidgets_.keys()); } void MainWindow::updateVocabulary() @@ -1374,7 +1434,7 @@ void MainWindow::notifyParametersChanged(const QStringList & paramChanged) { if(detectorDescriptorParamsChanged) { - this->updateObjects(); + this->updateObjects(objWidgets_.keys()); } else if(nearestNeighborParamsChanged) { diff --git a/src/TcpServer.cpp b/src/TcpServer.cpp index 451d6d3e..dfc5a290 100644 --- a/src/TcpServer.cpp +++ b/src/TcpServer.cpp @@ -30,6 +30,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include #include +#include namespace find_object { @@ -98,8 +99,101 @@ void TcpServer::publishDetectionInfo(const DetectionInfo & info) void TcpServer::addClient() { - QTcpSocket * client = this->nextPendingConnection(); - connect(client, SIGNAL(disconnected()), client, SLOT(deleteLater())); + while(this->hasPendingConnections()) + { + QTcpSocket * client = this->nextPendingConnection(); + connect(client, SIGNAL(readyRead()), this, SLOT(readReceivedData())); + connect(client, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(displayError(QAbstractSocket::SocketError))); + connect(client, SIGNAL(disconnected()), this, SLOT(connectionLost())); + } +} + +void TcpServer::readReceivedData() +{ + QTcpSocket * client = (QTcpSocket*)sender(); + QDataStream in(client); + in.setVersion(QDataStream::Qt_4_0); + + if (blockSizes_.value(client->socketDescriptor(), 0) == 0) + { + if (client->bytesAvailable() < (int)sizeof(quint64)) + { + return; + } + + in >> blockSizes_[client->socketDescriptor()]; + } + + if (client->bytesAvailable() < (int)blockSizes_[client->socketDescriptor()]) + { + return; + } + + quint32 serviceType; + in >> serviceType; + + bool ok = true; + if(serviceType == kAddObject) + { + int id; + in >> id; + QString fileName; + in >> fileName; + quint64 imageSize; + in >> imageSize; + std::vector buf(imageSize); + in.readRawData((char*)buf.data(), imageSize); + cv::Mat image = cv::imdecode(buf, cv::IMREAD_UNCHANGED); + + UINFO("TCP service: Add %d \"%s\"", id, fileName.toStdString().c_str()); + Q_EMIT addObject(image, id, fileName); + } + else if(serviceType == kRemoveObject) + { + int id; + in >> id; + + UINFO("TCP service: Remove %d", id); + Q_EMIT removeObject(id); + } + else + { + UERROR("Unknown service type called %d", serviceType); + ok = false; + } + + blockSizes_.remove(client->socketDescriptor()); + client->write(QByteArray(ok?"1":"0")); // send acknowledge +} + +void TcpServer::displayError(QAbstractSocket::SocketError socketError) +{ + switch (socketError) + { + case QAbstractSocket::RemoteHostClosedError: + break; + case QAbstractSocket::HostNotFoundError: + UWARN("CameraTcp: Tcp error: The host was not found. Please " + "check the host name and port settings.\n"); + break; + case QAbstractSocket::ConnectionRefusedError: + UWARN("CameraTcp: The connection was refused by the peer. " + "Make sure your images server is running, " + "and check that the host name and port " + "settings are correct."); + break; + default: + //UERROR("The following error occurred: %s.", this->errorString().toStdString().c_str()); + break; + } +} + +void TcpServer::connectionLost() +{ + //printf("[WARNING] CameraTcp: Connection lost!\n"); + blockSizes_.remove(((QTcpSocket*)sender())->socketDescriptor()); + ((QTcpSocket*)sender())->close(); + sender()->deleteLater(); } } // namespace find_object diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt index 7e401f1c..7ca893a2 100644 --- a/tools/CMakeLists.txt +++ b/tools/CMakeLists.txt @@ -1,4 +1,5 @@ ADD_SUBDIRECTORY( tcpClient ) ADD_SUBDIRECTORY( tcpImagesServer ) ADD_SUBDIRECTORY( tcpRequest ) +ADD_SUBDIRECTORY( tcpService ) ADD_SUBDIRECTORY( similarity ) \ No newline at end of file diff --git a/tools/tcpService/CMakeLists.txt b/tools/tcpService/CMakeLists.txt new file mode 100644 index 00000000..221b6003 --- /dev/null +++ b/tools/tcpService/CMakeLists.txt @@ -0,0 +1,33 @@ + +SET(SRC_FILES + main.cpp +) + +SET(INCLUDE_DIRS + ${CMAKE_CURRENT_SOURCE_DIR}/../../include + ${CMAKE_CURRENT_SOURCE_DIR} + ${OpenCV_INCLUDE_DIRS} +) + +INCLUDE(${QT_USE_FILE}) + +SET(LIBRARIES + ${OpenCV_LIBS} + ${QT_LIBRARIES} +) + +# Make sure the compiler can find include files from our library. +INCLUDE_DIRECTORIES(${INCLUDE_DIRS}) + +# Add binary called "example" that is built from the source file "main.cpp". +# The extension is automatically found. +ADD_EXECUTABLE(tcpService ${SRC_FILES}) +TARGET_LINK_LIBRARIES(tcpService find_object ${LIBRARIES}) + +SET_TARGET_PROPERTIES( tcpService + PROPERTIES OUTPUT_NAME ${PROJECT_PREFIX}-tcpService) + +INSTALL(TARGETS tcpService + RUNTIME DESTINATION bin COMPONENT runtime + BUNDLE DESTINATION "${CMAKE_BUNDLE_LOCATION}" COMPONENT runtime) + diff --git a/tools/tcpService/main.cpp b/tools/tcpService/main.cpp new file mode 100644 index 00000000..8ef90197 --- /dev/null +++ b/tools/tcpService/main.cpp @@ -0,0 +1,208 @@ +/* +Copyright (c) 2011-2014, Mathieu Labbe - IntRoLab - Universite de Sherbrooke +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the Universite de Sherbrooke nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include + +void showUsage() +{ + printf("\ntcpService [options] port\n" + " Options:\n" + " --add \"image.png\" # Add object (file name + id). Set id=0 " + " will make the server generating an id.\n" + " --remove # Remove object by ID.\n" + " --host #.#.#.# Set host address.\n" + " --help Show this help.\n" + " Examples:\n" + " Add: $ tcpService --add image.png 1 --host 127.0.0.1 4000\n" + " Remove: $ tcpService --remove 1 --host 127.0.0.1 4000\n"); + exit(-1); +} + +int main(int argc, char * argv[]) +{ + QString ipAddress; + QString fileName; + int addId = 0; + int removeId = -1; + quint16 port = 0; + + for(int i=1; i buf; + cv::imencode(".png", image, buf); + + out << (quint32)find_object::TcpServer::kAddObject; + out << addId; + out << fileName; + quint64 imageSize = buf.size(); + out << imageSize; + out.writeRawData((char*)buf.data(), (int)buf.size()); + printf("Add object %d \"%s\"\n", addId, fileName.toStdString().c_str()); + } + else if(removeId) + { + out << (quint32)find_object::TcpServer::kRemoveObject; + out << removeId; + printf("Remove object %d\n", removeId); + } + + out.device()->seek(0); + out << (quint64)(block.size() - sizeof(quint64)); + + qint64 bytes = request.write(block); + printf("Service published (%d bytes)!\n", (int)bytes); + request.waitForReadyRead(); + + return 0; +} +