00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
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)
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
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))
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;
00169
00170 if (sr.drawSURFFeatures (BA_iplimage, model_descriptors, n))
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
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
00192 stream << global_feature.area << " " << global_feature.duty_cycle << "\n";
00193
00194
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);
00200
00201
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];
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 }
00261 }
00262 }
00263 }
00264
00265 void GraphManager::placeRecognition ()
00266 {
00267 PlaceRecognition sr;
00268 GlobalFeature global_feature;
00269 if (sr.extractGlobalFeature (query_cloud, &global_feature))
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;
00284
00285
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);
00290
00291
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];
00316 }
00317
00318 if (sr.drawSURFFeatures (BA_iplimage, model_descriptors, n))
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
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
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
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
00376 CvSeq* seq = sr.findSURFMatchPairs (model_descriptors, n, sr.surf_descriptors[best], storage);
00377
00378
00379 double match_degree = sr.getMatchDegree (seq);
00380
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
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
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 }
00461 }
00462 }
00463 }
00464 }
00465
00466 void GraphManager::drawQImageText (QImage & image, const QString & str)
00467 {
00468
00469 QPainter painter (&image);
00470 painter.setCompositionMode (QPainter::CompositionMode_SourceIn);
00471
00472
00473 painter.setPen (Qt::blue);
00474 painter.setFont (QFont ("Times", 16, QFont::Normal, true));
00475
00476 painter.drawText (QPoint (5, 20), str);
00477 }