graph_manager.cpp
Go to the documentation of this file.
00001 /*
00002  * Software License Agreement (BSD License)
00003  *
00004  *  Copyright (c) 2013, Intelligent Robotics Lab, DLUT.
00005  *  Author: Qinghua Li, Yan Zhuang, Fei Yan
00006  *
00007  *  All rights reserved.
00008  *
00009  *  Redistribution and use in source and binary forms, with or without
00010  *  modification, are permitted provided that the following conditions
00011  *  are met:
00012  *
00013  *   * Redistributions of source code must retain the above copyright
00014  *     notice, this list of conditions and the following disclaimer.
00015  *   * Redistributions in binary form must reproduce the above
00016  *     copyright notice, this list of conditions and the following
00017  *     disclaimer in the documentation and/or other materials provided
00018  *     with the distribution.
00019  *   * Neither the name of Intelligent Robotics Lab, DLUT. nor the names
00020  *     of its contributors may be used to endorse or promote products
00021  *     derived from this software without specific prior written permission.
00022  *
00023  *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
00024  *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
00025  *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
00026  *  FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
00027  *  COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
00028  *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
00029  *  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
00030  *  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
00031  *  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
00032  *  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
00033  *  ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
00034  *  POSSIBILITY OF SUCH DAMAGE.
00035  */
00036 
00037 #include "dlut_place_recognition/graph_manager.h"
00038 
00039 #define MIN_VALUE 0.0
00040 #define MIN_NUMBER 0  // The minimum requirement for matching pairs
00041 
00042 std::vector < std::vector < pcl::PointXYZ > >GraphManager::lib_cloud;
00043 std::vector < std::vector < pcl::PointXYZ > >GraphManager::query_cloud;
00044 
00045 
00046 GraphManager::GraphManager ()
00047   : cloud_width (361), cloud_height (361)
00048 {
00049 }
00050 
00051 GraphManager::~GraphManager ()
00052 {
00053 }
00054 
00055 
00056 void GraphManager::loadData ()
00057 {
00058   Q_EMIT reset();
00059   QString message;
00060   QString fileName;
00061   fileName = QFileDialog::getOpenFileName (0, tr ("Open"), ".", tr ("PCD Files (*.pcd);; TXT Files (*.txt)"));
00062   if (!fileName.isNull ())
00063   {
00064     std::string file_name = std::string ((const char*) fileName.toLocal8Bit ());
00065 
00066     pcl::PointCloud<pcl::PointXYZ>::Ptr cloud (new pcl::PointCloud<pcl::PointXYZ>);
00067     if (pcl::io::loadPCDFile<pcl::PointXYZ> (file_name, *cloud) == -1)  // Load the file
00068     {
00069       PCL_ERROR ("Error!\nCouldn't read PCD file.\n");
00070       switch (QMessageBox::critical (NULL, tr ("Critical"), "Load data failed!\n"
00071                                      "Would you like to reload data?",
00072                                      QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes))
00073       {
00074         case QMessageBox::Yes:
00075           loadData ();
00076           break;
00077 
00078         case QMessageBox::No:
00079           break;
00080       }
00081       return;
00082     }
00083 
00084     int points_number = cloud->width * cloud->height;
00085     Q_EMIT setGUIStatus(message.sprintf ("Loaded %d data points from PCD file Successfully", points_number));
00086 
00087     cloud_width = cloud->width;
00088     cloud_height = cloud->height;
00089 
00090     pcl::PointXYZ temp_point;
00091     std::vector < pcl::PointXYZ > temp_vector;
00092 
00093     // Push the raw point cloud data to the 2-D vector(query_cloud)
00094     for (size_t i = 0; i < cloud->points.size (); ++i)
00095     {
00096       temp_point.x = cloud->points[i].x;
00097       temp_point.y = cloud->points[i].y;
00098       temp_point.z = cloud->points[i].z;
00099       temp_vector.push_back (temp_point);
00100 
00101       if (!((i + 1) % cloud->width))
00102       {
00103         query_cloud.push_back (temp_vector);
00104         temp_vector.clear ();
00105       }
00106     }
00107   }
00108   else
00109   {
00110     switch (QMessageBox::critical (NULL, tr ("Critical"), "Failed!\n Please open file again!",
00111                                    QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes))
00112     {
00113       case QMessageBox::Yes:
00114         loadData ();
00115         break;
00116 
00117       case QMessageBox::No:
00118         break;
00119     }
00120   }
00121 }
00122 
00123 void GraphManager::showBAImage ()
00124 {
00125   if (query_cloud.size () == 0)
00126   {
00127     switch (QMessageBox::critical (NULL, tr ("Critical"), "Failed!\n"
00128                                    "NO available data, please load data at first!",
00129                                    QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes))
00130     {
00131       case QMessageBox::Yes:
00132         loadData ();
00133         showBAImage ();
00134         break;
00135 
00136       case QMessageBox::No:
00137         break;
00138     }
00139   }
00140   else
00141   {
00142     BearingAngleImage ba;
00143     BA_iplimage = ba.generateBAImage (query_cloud, cloud_width, cloud_height);
00144     QImage BA_qimage = ba.cvIplImage2QImage (ba.getChannelsImage (BA_iplimage));
00145     drawQImageText (BA_qimage, "Query scene BA image");
00146     Q_EMIT newQueryBAImage(BA_qimage);
00147   }
00148 }
00149 
00150 void GraphManager::extractFeatures ()
00151 {
00152   PlaceRecognition sr;
00153   GlobalFeature global_feature;
00154   if (sr.extractGlobalFeature (query_cloud, &global_feature)) // Extract global spatial features
00155   {
00156     BearingAngleImage ba;
00157     BA_iplimage = ba.generateBAImage (query_cloud, cloud_width, cloud_height);
00158     QImage BA_qimage = ba.cvIplImage2QImage (ba.getChannelsImage (BA_iplimage));
00159     cvReleaseImage (&ba.channels_image);
00160     drawQImageText (BA_qimage, "Query scene BA image");
00161     Q_EMIT setGUIStatus("Show Bearing-Angle image and Features image of Query scene");
00162     Q_EMIT newQueryBAImage(BA_qimage);
00163 
00164     CvMemStorage* storage = cvCreateMemStorage (0);
00165     sr.cvSURFInitialize ();
00166 
00167     CvSeq* model_descriptors = sr.cvSURFDescriptor (BA_iplimage, storage, 4., 1);
00168     int n = model_descriptors->total;  // Total number of features
00169 
00170     if (sr.drawSURFFeatures (BA_iplimage, model_descriptors, n)) // Mark features on the Bearing-Angle image
00171     {
00172       QImage features_qimage = ba.cvIplImage2QImage (ba.getChannelsImage (BA_iplimage));
00173       drawQImageText (features_qimage, "Query scene features image");
00174       Q_EMIT newFeaturesImage(features_qimage);
00175 
00176       QString message;
00177       QMessageBox::about (NULL, tr ("Tips"), message.sprintf ("The number of features is %d.\n"
00178                           "Please save these global spatial features and local SURF features to database files", n));
00179       while (true)
00180       {
00181         // Save features to database file
00182         QString fileName;
00183         fileName = QFileDialog::getSaveFileName (0, tr ("Save"), ".", tr ("TXT Files (*.txt)"));
00184         Q_EMIT setGUIStatus("Please input file name(*.txt) correctly");
00185         if (!fileName.isNull ())
00186         {
00187           QFile file (fileName);
00188           if (file.open (QIODevice::WriteOnly | QIODevice::Text))
00189           {
00190             QTextStream stream (&file);
00191             // Write the global spatial features to file
00192             stream << global_feature.area << "  " << global_feature.duty_cycle << "\n";
00193 
00194             // Write SURF features to file
00195             SURFDescriptor* tmp_feature;
00196             for (int i = 0; i < n; i++)
00197             {
00198               tmp_feature = (SURFDescriptor*) cvGetSeqElem (model_descriptors, i);
00199               tmp_feature->laser_neighbors.resize (NEIGHBOR);  // 24 neighborhoods of laser point
00200 
00201               // Get the corresponding position(row and column) of laser point
00202               int x = (int) tmp_feature->x;
00203               int y = (int) tmp_feature->y;
00204 
00205               if (x < 0)
00206               {
00207                 x = 0;
00208               }
00209               else if (x > cloud_width - 1)
00210               {
00211                 x = cloud_width - 1;
00212               }
00213 
00214               if (y < 0)
00215               {
00216                 y = 0;
00217               }
00218               else if (y > cloud_height - 1)
00219               {
00220                 y = cloud_height - 1;
00221               }
00222               x = cloud_width - 1 - x;
00223               y = cloud_height - 1 - y;
00224 
00225               tmp_feature->cor_point = query_cloud[y][x];  // laser data point
00226 
00227               stream << tmp_feature->x << " "
00228                 << tmp_feature->y << " "
00229                 << tmp_feature->laplacian << " "
00230                 << tmp_feature->s << " "
00231                 << tmp_feature->dir << " "
00232                 << tmp_feature->mod << " "
00233                 << tmp_feature->cor_point.x << " "
00234                 << tmp_feature->cor_point.y << " " << tmp_feature->cor_point.z << " ";
00235 
00236               for (int j = 0; j < 128; j++)
00237               {
00238                 stream << tmp_feature->vector[j] << " ";
00239               }
00240               stream << "\n";
00241             }
00242             file.close ();
00243           }
00244 
00245           Q_EMIT setGUIStatus("Save global spatial features and local SURF features to database files successfully");
00246           break;
00247         }
00248         else
00249         {
00250           switch (QMessageBox::critical (NULL, tr ("Critical"), "~Failed~\n Invalid file name, please input file name correctly!",
00251                                          QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes))
00252           {
00253             case QMessageBox::Yes:
00254               break;
00255 
00256             case QMessageBox::No:
00257               return;
00258           }
00259         }
00260       }  // End of while
00261     }    // if (sr.drawSURFFeatures (BA_iplimage, model_descriptors, n))
00262   }      // if (sr.extractGlobalFeature (query_cloud, &global_feature))
00263 }
00264 
00265 void GraphManager::placeRecognition ()
00266 {
00267   PlaceRecognition sr;
00268   GlobalFeature global_feature;
00269   if (sr.extractGlobalFeature (query_cloud, &global_feature)) // Extract global spatial features
00270   {
00271     BearingAngleImage ba;
00272     BA_iplimage = ba.generateBAImage (query_cloud, cloud_width, cloud_height);
00273     QImage BA_qimage = ba.cvIplImage2QImage (ba.getChannelsImage (BA_iplimage));
00274     cvReleaseImage (&ba.channels_image);
00275     drawQImageText (BA_qimage, "Query scene BA image");
00276     Q_EMIT setGUIStatus("Show Bearing-Angle image and Features image of Query scene");
00277     Q_EMIT newQueryBAImage(BA_qimage);
00278 
00279     CvMemStorage* storage = cvCreateMemStorage (0);
00280     sr.cvSURFInitialize ();
00281 
00282     CvSeq* model_descriptors = sr.cvSURFDescriptor (BA_iplimage, storage, 4., 1);
00283     int n = model_descriptors->total;  // Total number of features
00284 
00285     // Add the character of laser data
00286     for (int i = 0; i < n; i++)
00287     {
00288       SURFDescriptor* temp_feature = (SURFDescriptor*) cvGetSeqElem (model_descriptors, i);
00289       temp_feature->laser_neighbors.resize (NEIGHBOR);  // 24 neighborhoods of laser point
00290 
00291       // Get the corresponding position(row and column) of laser point
00292       int x = temp_feature->x;
00293       int y = temp_feature->y;
00294 
00295       if (x < 0)
00296       {
00297         x = 0;
00298       }
00299       else if (x > cloud_width - 1)
00300       {
00301         x = cloud_width - 1;
00302       }
00303 
00304       if (y < 0)
00305       {
00306         y = 0;
00307       }
00308       else if (y > cloud_height - 1)
00309       {
00310         y = cloud_height - 1;
00311       }
00312       x = cloud_width - 1 - x;
00313       y = cloud_height - 1 - y;
00314 
00315       temp_feature->cor_point = query_cloud[y][x];  // laser data point
00316     }
00317 
00318     if (sr.drawSURFFeatures (BA_iplimage, model_descriptors, n)) // Mark features on the Bearing-Angle image
00319     {
00320       QImage features_qimage = ba.cvIplImage2QImage (ba.getChannelsImage (BA_iplimage));
00321       drawQImageText (features_qimage, "Query scene features image");
00322       Q_EMIT newFeaturesImage(features_qimage);
00323 
00324       QString message;
00325       QMessageBox::about (NULL, tr ("Tips"), message.sprintf ("The number of features is %d.\n"
00326                                              "Please select database files what you want to load!", n));
00327       // Load the SURF features from database files
00328       sr.readFeaturesFromFile ();
00329 
00330       int file_num = sr.global_descriptors.size ();
00331       Q_EMIT setGUIStatus(message.sprintf ("Select %d database files totally", file_num));
00332 
00333       if (file_num > 0)
00334       {
00335         int check_number = 0;
00336         std::vector < bool > if_select (file_num, false);
00337 
00338         while (check_number < file_num)
00339         {
00340           // Locate the unchecked matching scene
00341           int uncheck = -1;
00342           do
00343           {
00344             uncheck++;
00345           }
00346           while (if_select[uncheck] == true && uncheck < file_num);
00347 
00348           int best = uncheck;
00349           double flag_dis = 0;
00350           // Select the closest scene with query one from unchecked matching database scenes
00351           for (int next = uncheck + 1; next < file_num; next++)
00352           {
00353             double area_distance1 = (sr.global_descriptors[next].area - global_feature.area) *
00354                                     (sr.global_descriptors[next].area - global_feature.area);
00355             double duty_cycle_distance1 = (sr.global_descriptors[next].duty_cycle - global_feature.duty_cycle) *
00356                                           (sr.global_descriptors[next].duty_cycle - global_feature.duty_cycle);
00357             double area_distance2 = (sr.global_descriptors[best].area - global_feature.area) *
00358                                     (sr.global_descriptors[best].area - global_feature.area);
00359             double duty_cycle_distance2 = (sr.global_descriptors[best].duty_cycle - global_feature.duty_cycle) *
00360                                           (sr.global_descriptors[best].duty_cycle - global_feature.duty_cycle);
00361 
00362             double distance1 = 0.8 * area_distance1;
00363             double distance2 = 0.8 * area_distance2;
00364 
00365             flag_dis = distance2;
00366             if (distance1 < distance2 && if_select[next] == false)
00367             {
00368               best = next;
00369               flag_dis = distance1;
00370             }
00371           }
00372 
00373           if_select[best] = true;
00374 
00375           // KD-Tree matching
00376           CvSeq* seq = sr.findSURFMatchPairs (model_descriptors, n, sr.surf_descriptors[best], storage);
00377 
00378           // Get the matching degree
00379           double match_degree = sr.getMatchDegree (seq);
00380           // The determined factor for whether query scene is one of database scenes
00381           if (sr.eff_match_pairs >= MIN_NUMBER && match_degree >= MIN_VALUE)
00382           {
00383             QString file_full_name = sr.files[best];
00384             QFileInfo info (file_full_name);
00385             QString file_name = info.baseName ();
00386             QString place_name = file_name;
00387 
00388             Q_EMIT setGUIStatus("Place Recognition is accomplished Successfully");
00389             QMessageBox::about (NULL, tr ("Tips"), message.sprintf ("Place recognition is accomplished successfully!\n\n"
00390                                 "The query place should be %s .\n"
00391                                 "    Matching degree: %lf \n"
00392                                 "    Effective matching pairs: %d",
00393                                 (const char*) file_name.toLocal8Bit (), match_degree, sr.eff_match_pairs));
00394 
00395             file_name = info.path () + "/" + info.baseName () + ".pcd";
00396             std::string lib_file_name = std::string ((const char*) file_name.toLocal8Bit ());
00397 
00398             pcl::PointCloud<pcl::PointXYZ>::Ptr cloud (new pcl::PointCloud<pcl::PointXYZ>);
00399             /* Load the corresponding scene in database */
00400             if (pcl::io::loadPCDFile<pcl::PointXYZ> (lib_file_name, *cloud) == -1)
00401             {
00402               PCL_ERROR ("Error!\nCouldn't read PCD file.\n");
00403               return;
00404             }
00405 
00406             int points_number = cloud->width * cloud->height;
00407             Q_EMIT setGUIStatus(message.sprintf ("Loaded %d data points from PCD file", points_number));
00408 
00409             int lib_cloud_width = cloud->width;
00410             int lib_cloud_height = cloud->height;
00411 
00412             lib_cloud.clear ();
00413             pcl::PointXYZ temp_point;
00414             std::vector < pcl::PointXYZ > temp_vector;
00415 
00416             // Push the raw point cloud data to the 2-D vector(lib_cloud)
00417             for (size_t i = 0; i < cloud->points.size (); ++i)
00418             {
00419               temp_point.x = cloud->points[i].x;
00420               temp_point.y = cloud->points[i].y;
00421               temp_point.z = cloud->points[i].z;
00422               temp_vector.push_back (temp_point);
00423 
00424               if (!((i + 1) % cloud->width))
00425               {
00426                 lib_cloud.push_back (temp_vector);
00427                 temp_vector.clear ();
00428               }
00429             }
00430 
00431             Q_EMIT setGUIStatus("Show 3D laser scanning data and Bearing-Angle image of " + place_name + " room");
00432 
00433             if (lib_cloud.size () > 0)
00434             {
00435               cvReleaseImage (&BA_iplimage);
00436               cvReleaseImage (&ba.channels_image);
00437               BA_iplimage = ba.generateBAImage (lib_cloud, lib_cloud_width, lib_cloud_height);
00438               QImage lib_BA_qimage = ba.cvIplImage2QImage (ba.getChannelsImage (BA_iplimage));
00439               drawQImageText (lib_BA_qimage, "Corresponding scene BA image");
00440               Q_EMIT newLibBAImage(lib_BA_qimage);
00441             }
00442             break;
00443           }
00444 
00445           check_number++;
00446           if (check_number == file_num)
00447           {
00448             switch (QMessageBox::question (NULL, tr ("Question"), "Sorry, there is not available matching place in the database!\n"
00449                                            "Would you like to add the query scene to database?",
00450                                            QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes))
00451             {
00452               case QMessageBox::Yes:
00453                 extractFeatures ();
00454                 break;
00455 
00456               case QMessageBox::No:
00457                 break;
00458             }
00459           }
00460         }  // End of while
00461       }    // if (file_num > 0)
00462     }      // if (sr.drawSURFFeatures(BA_iplimage, model_descriptors, n))
00463   }        // if (sr.extractGlobalFeature(query_cloud, &global_feature))
00464 }
00465 
00466 void GraphManager::drawQImageText (QImage & image, const QString & str)
00467 {
00468   // Construct QPainter for the QImage
00469   QPainter painter (&image);
00470   painter.setCompositionMode (QPainter::CompositionMode_SourceIn);
00471 
00472   // set the pen and font for painter
00473   painter.setPen (Qt::blue);
00474   painter.setFont (QFont ("Times", 16, QFont::Normal, true));
00475 
00476   painter.drawText (QPoint (5, 20), str);
00477 }


dlut_place_recognition
Author(s): Qinghua Li, Yan Zhuang, Fei Yan
autogenerated on Sun Oct 5 2014 23:29:57