00001 /*!@file SIFT/VisualObjectDBQt.qt.C Qt GUI around VisualObjectDB */ 00002 00003 // //////////////////////////////////////////////////////////////////// // 00004 // The iLab Neuromorphic Vision C++ Toolkit - Copyright (C) 2000-2005 // 00005 // by the University of Southern California (USC) and the iLab at USC. // 00006 // See http://iLab.usc.edu for information about this project. // 00007 // //////////////////////////////////////////////////////////////////// // 00008 // Major portions of the iLab Neuromorphic Vision Toolkit are protected // 00009 // under the U.S. patent ``Computation of Intrinsic Perceptual Saliency // 00010 // in Visual Environments, and Applications'' by Christof Koch and // 00011 // Laurent Itti, California Institute of Technology, 2001 (patent // 00012 // pending; application number 09/912,225 filed July 23, 2001; see // 00013 // http://pair.uspto.gov/cgi-bin/final/home.pl for current status). // 00014 // //////////////////////////////////////////////////////////////////// // 00015 // This file is part of the iLab Neuromorphic Vision C++ Toolkit. // 00016 // // 00017 // The iLab Neuromorphic Vision C++ Toolkit is free software; you can // 00018 // redistribute it and/or modify it under the terms of the GNU General // 00019 // Public License as published by the Free Software Foundation; either // 00020 // version 2 of the License, or (at your option) any later version. // 00021 // // 00022 // The iLab Neuromorphic Vision C++ Toolkit is distributed in the hope // 00023 // that it will be useful, but WITHOUT ANY WARRANTY; without even the // 00024 // implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // 00025 // PURPOSE. See the GNU General Public License for more details. // 00026 // // 00027 // You should have received a copy of the GNU General Public License // 00028 // along with the iLab Neuromorphic Vision C++ Toolkit; if not, write // 00029 // to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, // 00030 // Boston, MA 02111-1307 USA. // 00031 // //////////////////////////////////////////////////////////////////// // 00032 // 00033 // Primary maintainer for this file: Laurent Itti 00034 // $HeadURL: svn://isvn.usc.edu/software/invt/trunk/saliency/src/Apps/BorderWatch/BorderWatchQt.qt.C $ 00035 // $Id: BorderWatchQt.qt.C 13059 2010-03-26 08:14:32Z itti $ 00036 // 00037 00038 #include "SIFT/VisualObjectDBQt.qt.H" 00039 00040 #include <QtCore/QTimer> 00041 #include <QtGui/QLabel> 00042 #include <QtGui/QVBoxLayout> 00043 #include <QtGui/QHBoxLayout> 00044 #include <QtGui/QListWidget> 00045 #include <QtGui/QSplitter> 00046 #include <QtGui/QFrame> 00047 #include <QtGui/QAction> 00048 #include <QtGui/QMenu> 00049 #include <QtGui/QMenuBar> 00050 #include <QtGui/QFileDialog> 00051 #include <QtGui/QStatusBar> 00052 #include <QtGui/QMessageBox> 00053 00054 #include "QtUtil/ImageConvert4.H" 00055 #include "Raster/Raster.H" 00056 #include "Util/FileUtil.H" 00057 #include "Util/log.H" 00058 #include "Util/sformat.H" 00059 #include "Util/StringUtil.H" 00060 00061 #include <dirent.h> 00062 00063 // ###################################################################### 00064 VisualObjectDBQt::VisualObjectDBQt(QWidget* parent) : 00065 QMainWindow(parent), itsVDBchanged(false) 00066 { 00067 QAction *loadaction = new QAction("&Load VisualObjectDB", this); 00068 QAction *saveaction = new QAction("&Save VisualObjectDB", this); 00069 QAction *quitaction = new QAction("&Quit", this); 00070 00071 QMenu *file = menuBar()->addMenu("&File"); 00072 file->addAction(loadaction); connect(loadaction, SIGNAL(triggered()), this, SLOT(load())); 00073 file->addAction(saveaction); connect(saveaction, SIGNAL(triggered()), this, SLOT(save())); 00074 file->addAction(quitaction); connect(quitaction, SIGNAL(triggered()), this, SLOT(close())); 00075 00076 QAction *importaction = new QAction("I&mport Image", this); 00077 QAction *rimportaction = new QAction("&Recursive Import Images", this); 00078 00079 QMenu *import = menuBar()->addMenu("&Import"); 00080 import->addAction(importaction); connect(importaction, SIGNAL(triggered()), this, SLOT(import())); 00081 import->addAction(rimportaction); connect(rimportaction, SIGNAL(triggered()), this, SLOT(rimport())); 00082 00083 QAction *matchaction = new QAction("&Match against Image", this); 00084 00085 QMenu *process = menuBar()->addMenu("&Processing"); 00086 process->addAction(matchaction); connect(matchaction, SIGNAL(triggered()), this, SLOT(match())); 00087 00088 QVBoxLayout *main = new QVBoxLayout(); 00089 main->setSpacing(4); 00090 main->setMargin(2); 00091 00092 QSplitter* splitter = new QSplitter(Qt::Horizontal, this); 00093 splitter->setChildrenCollapsible(false); 00094 00095 itsListWidget = new QListWidget(this); 00096 itsListWidget->setMinimumWidth(180); 00097 itsListWidget->setSortingEnabled(true); // sort list entries 00098 00099 splitter->addWidget(itsListWidget); 00100 connect(itsListWidget, SIGNAL(currentRowChanged(int)), this, SLOT(listChanged(int))); 00101 00102 itsFrameWidget = new QLabel(this); 00103 splitter->addWidget(itsFrameWidget); 00104 00105 splitter->setStretchFactor(0, 1); 00106 splitter->setStretchFactor(1, 7); // preferentially stretch the image pane over the text list pane 00107 00108 splitter->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); 00109 main->addWidget(splitter); 00110 00111 QWidget *centralWidget = new QWidget; 00112 setCentralWidget(centralWidget); 00113 centralWidget->setLayout(main); 00114 00115 statusBar(); // initialize the statusbar 00116 statusBar()->showMessage("Status: Idle. Use the File menu to load a VisualObjectDB."); 00117 } 00118 00119 // ###################################################################### 00120 VisualObjectDBQt::~VisualObjectDBQt() 00121 { } 00122 00123 // ###################################################################### 00124 bool VisualObjectDBQt::load() 00125 { 00126 asksave(); 00127 00128 QString fileName = QFileDialog::getOpenFileName(this, tr("Select VisualObjectDB File to Load"), 00129 QString(""), tr("VisualObjectDB Files (*.vdb)")); 00130 statusBar()->showMessage(sformat("Loading: %s ...", fileName.toLatin1().data()).c_str()); repaint(); 00131 LINFO("Loading: %s", fileName.toLatin1().data()); 00132 00133 if (itsVDB.loadFrom(fileName.toStdString(), false)) { 00134 itsFileName = fileName.toStdString(); 00135 LINFO("Loaded VisualObjectDB with %u objects.", itsVDB.numObjects()); 00136 } else { 00137 itsFileName = ""; 00138 LINFO("Error loading VisualObjectDB"); 00139 QMessageBox::warning(this, tr("VisualObjectDBQt"), tr("Error loading VisualObjectDB"), 00140 QMessageBox::Ok | QMessageBox::Default); 00141 return false; 00142 } 00143 00144 // refresh our list: 00145 refreshList(); 00146 00147 return true; 00148 } 00149 00150 // ###################################################################### 00151 void VisualObjectDBQt::refreshList() 00152 { 00153 itsListWidget->clear(); 00154 for (uint i = 0; i < itsVDB.numObjects(); ++i) itsListWidget->addItem(itsVDB.getObject(i)->getName().c_str()); 00155 if (itsVDB.numObjects() > 0) itsListWidget->setCurrentRow(0); 00156 } 00157 00158 // ###################################################################### 00159 bool VisualObjectDBQt::save() 00160 { 00161 if (itsFileName.empty()) { 00162 QString fileName = QFileDialog::getSaveFileName(this, tr("Select VisualObjectDB File to Save"), 00163 QString(""), tr("VisualObjectDB Files (*.vdb)")); 00164 itsFileName = fileName.toStdString(); 00165 if (itsFileName.empty()) return false; 00166 } 00167 00168 if (itsVDB.saveTo(itsFileName)) { 00169 LINFO("Saved VisualObjectDB with %u objects.", itsVDB.numObjects()); 00170 itsVDBchanged = false; 00171 return true; 00172 } 00173 00174 QMessageBox::warning(this, tr("VisualObjectDBQt"), tr("Error saving VisualObjectDB"), 00175 QMessageBox::Ok | QMessageBox::Default); 00176 return false; 00177 } 00178 00179 // ###################################################################### 00180 bool VisualObjectDBQt::import() 00181 { 00182 QString fileName = QFileDialog::getOpenFileName(this, tr("Select Image File to Load"), 00183 QString(""), tr("Image Files (*.png *.pnm *.ppm *.jpg)")); 00184 itsVDBchanged = importcore(fileName.toLatin1().data()); 00185 00186 refreshList(); 00187 00188 return itsVDBchanged; 00189 } 00190 00191 // ###################################################################### 00192 bool VisualObjectDBQt::importcore(const char *fil) 00193 { 00194 statusBar()->showMessage(sformat("Importing image: %s", fil).c_str()); repaint(); 00195 00196 QFileInfo fi(fil); 00197 std::string path = fi.canonicalPath().toStdString(); 00198 std::string name = fi.completeBaseName().toStdString(); 00199 std::vector<std::string> tokens; split(path, "/", std::back_inserter(tokens)); 00200 if (tokens.size() >= 2) name = tokens[tokens.size()-2] + ':' + tokens[tokens.size()-1] + ':' + name; 00201 00202 Image< PixRGB<byte> > img = Raster::ReadRGB(fil); 00203 00204 rutz::shared_ptr<VisualObject> obj(new VisualObject(name, fil, img)); 00205 if (itsVDB.addObject(obj)) { 00206 LINFO("Added VisualObject '%s'", name.c_str()); 00207 return true; 00208 } 00209 00210 QMessageBox::warning(this, tr("VisualObjectDBQt"), tr("Error adding VisualObject"), 00211 QMessageBox::Ok | QMessageBox::Default); 00212 return false; 00213 } 00214 00215 // ###################################################################### 00216 bool VisualObjectDBQt::rimport() 00217 { 00218 QString fileName = QFileDialog::getExistingDirectory(this, tr("Select Directory to Load")); 00219 statusBar()->showMessage(sformat("Recursively importing images from: %s", fileName.toLatin1().data()).c_str()); 00220 repaint(); 00221 00222 itsVDBchanged = rimportcore(fileName.toLatin1().data()); 00223 00224 refreshList(); 00225 return itsVDBchanged; 00226 } 00227 00228 // ###################################################################### 00229 bool VisualObjectDBQt::rimportcore(const char *dir) 00230 { 00231 LINFO("Considering: %s", dir); 00232 00233 bool ret = true; 00234 DIR *dp = opendir(dir); dirent *dirp; 00235 while( (dirp = readdir(dp)) ) { 00236 if (dirp->d_name[0] != '.') { 00237 std::string fil = sformat("%s/%s", dir, dirp->d_name); 00238 if (isDirectory(dirp)) ret &= rimportcore(fil.c_str()); else ret &= importcore(fil.c_str()); 00239 } 00240 } 00241 closedir(dp); 00242 00243 return ret; 00244 } 00245 00246 // ###################################################################### 00247 void VisualObjectDBQt::listChanged(const int idx) 00248 { 00249 if (itsListWidget->count() && idx >= 0) 00250 { 00251 const QListWidgetItem *item = itsListWidget->item(idx); 00252 std::string oname = item->text().toStdString(); 00253 00254 rutz::shared_ptr<VisualObject> obj = itsVDB.getObject(oname); 00255 if (obj.is_invalid()) LFATAL("Selected a non-existent object!"); 00256 00257 const Image<PixRGB<byte> > &img = obj->getKeypointImage(1.0F); //getImage(); 00258 00259 // display the image in our widget: 00260 QPixmap pixmap = convertToQPixmap4(img); 00261 itsFrameWidget->setPixmap(pixmap); 00262 00263 statusBar()->showMessage(sformat("%u keypoints, %u features -- %s", obj->numKeypoints(), obj->numFeatures(), 00264 obj->getImageFname().c_str()).c_str()); 00265 } 00266 } 00267 00268 // ###################################################################### 00269 bool VisualObjectDBQt::match() 00270 { 00271 QString fileName = QFileDialog::getOpenFileName(this, tr("Select Image File to Load"), 00272 QString(""), tr("Image Files (*.png *.pnm *.ppm *.jpg)")); 00273 00274 statusBar()->showMessage(sformat("Matching image: %s", fileName.toLatin1().data()).c_str()); repaint(); 00275 00276 Image< PixRGB<byte> > img = Raster::ReadRGB(fileName.toLatin1().data()); 00277 rutz::shared_ptr<VisualObject> obj(new VisualObject("Test Object", fileName.toLatin1().data(), img)); 00278 00279 std::vector< rutz::shared_ptr<VisualObjectMatch> > matches; 00280 if (itsVDB.getObjectMatchesParallel(obj, matches, 50 /* numthreads */, 0.05F, 0.95F, 0.5F, 5U, 9U, false)) { 00281 std::string msg = "Object matches found:\n\n"; 00282 00283 size_t bestidx = 0; float bestscore = -1.0F; 00284 for (size_t i = 0; i < matches.size(); ++i) { 00285 rutz::shared_ptr<VisualObject> ref = matches[i]->getVoTest(); 00286 LINFO("Matched %s, score = %f", ref->getName().c_str(), matches[i]->getScore()); 00287 msg += sformat("%s: score = %f\n", ref->getName().c_str(), matches[i]->getScore()); 00288 00289 if (matches[i]->getScore() > bestscore) { bestscore = matches[i]->getScore(); bestidx = i; } 00290 } 00291 00292 msg += sformat("\nShowing image for match %"ZU, bestidx); 00293 QMessageBox mbox(this); 00294 mbox.setInformativeText(msg.c_str()); 00295 mbox.addButton(QMessageBox::Ok); 00296 QPixmap pixmap = convertToQPixmap4(matches[bestidx]->getMatchImage(0.5F)); 00297 mbox.setIconPixmap(pixmap); 00298 mbox.exec(); 00299 return true; 00300 } else { 00301 LINFO("No good object match found."); 00302 QMessageBox::information(this, tr("VisualObjectDBQt"), tr("No good object match found!"), 00303 QMessageBox::Ok | QMessageBox::Default); 00304 return false; 00305 } 00306 } 00307 00308 // ###################################################################### 00309 void VisualObjectDBQt::asksave() 00310 { 00311 if (itsVDBchanged) { 00312 int r = QMessageBox::warning(this, tr("VisualObjectDBQt"), 00313 tr("The VisualObjectDB has been modified.\n" 00314 "Do you want to save your changes?"), 00315 QMessageBox::Yes | QMessageBox::Default, 00316 QMessageBox::No | QMessageBox::Escape); 00317 if (r == QMessageBox::Yes) itsVDBchanged = save(); 00318 else if (r == QMessageBox::No) itsVDBchanged = false; 00319 } 00320 } 00321 00322 // ###################################################################### 00323 void VisualObjectDBQt::closeEvent(QCloseEvent *event) 00324 { 00325 asksave(); // do an event->accept() or event->ignore() 00326 close(); 00327 } 00328 00329 // ###################################################################### 00330 /* So things look consistent in everyone's emacs... */ 00331 /* Local Variables: */ 00332 /* mode: c++ */ 00333 /* indent-tabs-mode: nil */ 00334 /* End: */