1 /* 2 * OctoMap - An Efficient Probabilistic 3D Mapping Framework Based on Octrees 3 * http://octomap.github.com/ 4 * 5 * Copyright (c) 2009-2013, K.M. Wurm and A. Hornung, University of Freiburg 6 * All rights reserved. 7 * License: New BSD 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions are met: 11 * 12 * * Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * * Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * * Neither the name of the University of Freiburg nor the names of its 18 * contributors may be used to endorse or promote products derived from 19 * this software without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 22 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 25 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 26 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 27 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 28 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 29 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 30 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 31 * POSSIBILITY OF SUCH DAMAGE. 32 */ 33 34 #include <iomanip> 35 #include <fstream> 36 #include <sstream> 37 #include <stdlib.h> 38 39 #include <octomap/math/Pose6D.h> 40 #include <octomap/ScanGraph.h> 41 42 namespace octomap { 43 44 ~ScanNode()45 ScanNode::~ScanNode(){ 46 if (scan != NULL){ 47 delete scan; 48 scan = NULL; 49 } 50 } 51 writeBinary(std::ostream & s) const52 std::ostream& ScanNode::writeBinary(std::ostream &s) const { 53 54 // file structure: pointcloud | pose | id 55 56 scan->writeBinary(s); 57 pose.writeBinary(s); 58 59 uint32_t uintId = static_cast<uint32_t>(id); 60 s.write((char*)&uintId, sizeof(uintId)); 61 62 return s; 63 } 64 readBinary(std::istream & s)65 std::istream& ScanNode::readBinary(std::istream &s) { 66 67 this->scan = new Pointcloud(); 68 this->scan->readBinary(s); 69 70 this->pose.readBinary(s); 71 72 uint32_t uintId; 73 s.read((char*)&uintId, sizeof(uintId)); 74 this->id = uintId; 75 76 return s; 77 } 78 79 writePoseASCII(std::ostream & s) const80 std::ostream& ScanNode::writePoseASCII(std::ostream &s) const { 81 s << " " << this->id; // export pose for human editor 82 s << " "; 83 this->pose.trans().write(s); 84 s << " "; 85 this->pose.rot().toEuler().write(s); 86 s << std::endl; 87 return s; 88 } 89 readPoseASCII(std::istream & s)90 std::istream& ScanNode::readPoseASCII(std::istream &s) { 91 unsigned int read_id; 92 s >> read_id; 93 if (read_id != this->id) 94 OCTOMAP_ERROR("ERROR while reading ScanNode pose from ASCII. id %d does not match real id %d.\n", read_id, this->id); 95 96 this->pose.trans().read(s); 97 98 // read rotation from euler angles 99 point3d rot; 100 rot.read(s); 101 this->pose.rot() = octomath::Quaternion(rot); 102 return s; 103 } 104 105 writeBinary(std::ostream & s) const106 std::ostream& ScanEdge::writeBinary(std::ostream &s) const { 107 108 // file structure: first_id | second_id | constraint | weight 109 110 s.write((char*)&first->id, sizeof(first->id)); 111 s.write((char*)&second->id, sizeof(second->id)); 112 constraint.writeBinary(s); 113 s.write((char*)&weight, sizeof(weight)); 114 return s; 115 } 116 readBinary(std::istream & s,ScanGraph & graph)117 std::istream& ScanEdge::readBinary(std::istream &s, ScanGraph& graph) { 118 unsigned int first_id, second_id; 119 s.read((char*)&first_id, sizeof(first_id)); 120 s.read((char*)&second_id, sizeof(second_id)); 121 122 this->first = graph.getNodeByID(first_id); 123 if (this->first == NULL) OCTOMAP_ERROR("ERROR while reading ScanEdge. first node not found.\n"); 124 this->second = graph.getNodeByID(second_id); 125 if (this->second == NULL) OCTOMAP_ERROR("ERROR while reading ScanEdge. second node not found.\n"); 126 127 this->constraint.readBinary(s); 128 s.read((char*)&weight, sizeof(weight)); 129 130 return s; 131 } 132 133 writeASCII(std::ostream & s) const134 std::ostream& ScanEdge::writeASCII(std::ostream &s) const { 135 136 // file structure: first_id | second_id | constraint | weight 137 138 s << " " << first->id << " " << second->id; 139 s << " "; 140 constraint.write(s); 141 s << " " << weight; 142 s << std::endl; 143 return s; 144 } 145 readASCII(std::istream & s,ScanGraph & graph)146 std::istream& ScanEdge::readASCII(std::istream &s, ScanGraph& graph) { 147 148 unsigned int first_id, second_id; 149 s >> first_id; 150 s >> second_id; 151 152 this->first = graph.getNodeByID(first_id); 153 if (this->first == NULL) OCTOMAP_ERROR("ERROR while reading ScanEdge. first node %d not found.\n", first_id); 154 this->second = graph.getNodeByID(second_id); 155 if (this->second == NULL) OCTOMAP_ERROR("ERROR while reading ScanEdge. second node %d not found.\n", second_id); 156 157 this->constraint.read(s); 158 s >> weight; 159 return s; 160 } 161 162 ~ScanGraph()163 ScanGraph::~ScanGraph() { 164 this->clear(); 165 } 166 clear()167 void ScanGraph::clear() { 168 for (unsigned int i=0; i<nodes.size(); i++) { 169 delete nodes[i]; 170 } 171 nodes.clear(); 172 for (unsigned int i=0; i<edges.size(); i++) { 173 delete edges[i]; 174 } 175 edges.clear(); 176 } 177 178 addNode(Pointcloud * scan,pose6d pose)179 ScanNode* ScanGraph::addNode(Pointcloud* scan, pose6d pose) { 180 if (scan != 0) { 181 nodes.push_back(new ScanNode(scan, pose, (unsigned int) nodes.size())); 182 return nodes.back(); 183 } 184 else { 185 OCTOMAP_ERROR("scan is invalid.\n"); 186 return NULL; 187 } 188 } 189 190 addEdge(ScanNode * first,ScanNode * second,pose6d constraint)191 ScanEdge* ScanGraph::addEdge(ScanNode* first, ScanNode* second, pose6d constraint) { 192 193 if ((first != 0) && (second != 0)) { 194 edges.push_back(new ScanEdge(first, second, constraint)); 195 // OCTOMAP_DEBUG("ScanGraph::AddEdge %d --> %d\n", first->id, second->id); 196 return edges.back(); 197 } 198 else { 199 OCTOMAP_ERROR("addEdge:: one or both nodes invalid.\n"); 200 return NULL; 201 } 202 } 203 204 addEdge(unsigned int first_id,unsigned int second_id)205 ScanEdge* ScanGraph::addEdge(unsigned int first_id, unsigned int second_id) { 206 207 if ( this->edgeExists(first_id, second_id)) { 208 OCTOMAP_ERROR("addEdge:: Edge exists!\n"); 209 return NULL; 210 } 211 212 ScanNode* first = getNodeByID(first_id); 213 ScanNode* second = getNodeByID(second_id); 214 215 if ((first != 0) && (second != 0)) { 216 pose6d constr = first->pose.inv() * second->pose; 217 return this->addEdge(first, second, constr); 218 } 219 else { 220 OCTOMAP_ERROR("addEdge:: one or both scans invalid.\n"); 221 return NULL; 222 } 223 } 224 225 connectPrevious()226 void ScanGraph::connectPrevious() { 227 if (nodes.size() >= 2) { 228 ScanNode* first = nodes[nodes.size()-2]; 229 ScanNode* second = nodes[nodes.size()-1]; 230 pose6d c = (first->pose).inv() * second->pose; 231 this->addEdge(first, second, c); 232 } 233 } 234 235 exportDot(std::string filename)236 void ScanGraph::exportDot(std::string filename) { 237 std::ofstream outfile (filename.c_str()); 238 outfile << "graph ScanGraph" << std::endl; 239 outfile << "{" << std::endl; 240 for (unsigned int i=0; i<edges.size(); i++) { 241 outfile << (edges[i]->first)->id 242 << " -- " 243 << (edges[i]->second)->id 244 << " [label=" 245 << std::fixed << std::setprecision(2) << edges[i]->constraint.transLength() 246 << "]" << std::endl; 247 } 248 outfile << "}" << std::endl; 249 outfile.close(); 250 } 251 getNodeByID(unsigned int id)252 ScanNode* ScanGraph::getNodeByID(unsigned int id) { 253 for (unsigned int i = 0; i < nodes.size(); i++) { 254 if (nodes[i]->id == id) return nodes[i]; 255 } 256 return NULL; 257 } 258 edgeExists(unsigned int first_id,unsigned int second_id)259 bool ScanGraph::edgeExists(unsigned int first_id, unsigned int second_id) { 260 261 for (unsigned int i=0; i<edges.size(); i++) { 262 if ( 263 (((edges[i]->first)->id == first_id) && ((edges[i]->second)->id == second_id)) 264 || 265 (((edges[i]->first)->id == second_id) && ((edges[i]->second)->id == first_id))) { 266 return true; 267 } 268 } 269 return false; 270 } 271 getNeighborIDs(unsigned int id)272 std::vector<unsigned int> ScanGraph::getNeighborIDs(unsigned int id) { 273 std::vector<unsigned int> res; 274 ScanNode* node = getNodeByID(id); 275 if (node) { 276 // check all nodes 277 for (unsigned int i = 0; i < nodes.size(); i++) { 278 if (node->id == nodes[i]->id) continue; 279 if (edgeExists(id, nodes[i]->id)) { 280 res.push_back(nodes[i]->id); 281 } 282 } 283 } 284 return res; 285 } 286 getOutEdges(ScanNode * node)287 std::vector<ScanEdge*> ScanGraph::getOutEdges(ScanNode* node) { 288 std::vector<ScanEdge*> res; 289 if (node) { 290 for (std::vector<ScanEdge*>::iterator it = edges.begin(); it != edges.end(); it++) { 291 if ((*it)->first == node) { 292 res.push_back(*it); 293 } 294 } 295 } 296 return res; 297 } 298 getInEdges(ScanNode * node)299 std::vector<ScanEdge*> ScanGraph::getInEdges(ScanNode* node) { 300 std::vector<ScanEdge*> res; 301 if (node) { 302 for (std::vector<ScanEdge*>::iterator it = edges.begin(); it != edges.end(); it++) { 303 if ((*it)->second == node) { 304 res.push_back(*it); 305 } 306 } 307 } 308 return res; 309 } 310 transformScans()311 void ScanGraph::transformScans() { 312 for(ScanGraph::iterator it=this->begin(); it != this->end(); it++) { 313 ((*it)->scan)->transformAbsolute((*it)->pose); 314 } 315 } 316 writeBinary(const std::string & filename) const317 bool ScanGraph::writeBinary(const std::string& filename) const { 318 std::ofstream binary_outfile( filename.c_str(), std::ios_base::binary); 319 if (!binary_outfile.is_open()){ 320 OCTOMAP_ERROR_STR("Filestream to "<< filename << " not open, nothing written."); 321 return false; 322 } 323 writeBinary(binary_outfile); 324 binary_outfile.close(); 325 return true; 326 } 327 writeBinary(std::ostream & s) const328 std::ostream& ScanGraph::writeBinary(std::ostream &s) const { 329 330 // file structure: n | node_1 | ... | node_n | m | edge_1 | ... | edge_m 331 332 // write nodes --------------------------------- 333 // note: size is always an unsigned int! 334 unsigned int graph_size = (unsigned int) this->size(); 335 if (graph_size) OCTOMAP_DEBUG("writing %u nodes to binary file...\n", graph_size); 336 s.write((char*)&graph_size, sizeof(graph_size)); 337 338 for (ScanGraph::const_iterator it = this->begin(); it != this->end(); it++) { 339 (*it)->writeBinary(s); 340 } 341 342 if (graph_size) OCTOMAP_DEBUG("done.\n"); 343 344 // write edges --------------------------------- 345 unsigned int num_edges = (unsigned int) this->edges.size(); 346 if (num_edges) OCTOMAP_DEBUG("writing %u edges to binary file...\n", num_edges); 347 s.write((char*)&num_edges, sizeof(num_edges)); 348 349 for (ScanGraph::const_edge_iterator it = this->edges_begin(); it != this->edges_end(); it++) { 350 (*it)->writeBinary(s); 351 } 352 353 if (num_edges) OCTOMAP_DEBUG(" done.\n"); 354 355 return s; 356 } 357 readBinary(const std::string & filename)358 bool ScanGraph::readBinary(const std::string& filename) { 359 std::ifstream binary_infile(filename.c_str(), std::ios_base::binary); 360 if (!binary_infile.is_open()){ 361 OCTOMAP_ERROR_STR("Filestream to "<< filename << " not open, nothing read."); 362 return false; 363 } 364 readBinary(binary_infile); 365 binary_infile.close(); 366 return true; 367 } 368 readBinary(std::ifstream & s)369 std::istream& ScanGraph::readBinary(std::ifstream &s) { 370 if (!s.is_open()){ 371 OCTOMAP_ERROR_STR("Could not read from input filestream in ScanGraph::readBinary"); 372 return s; 373 } else if (!s.good()){ 374 OCTOMAP_WARNING_STR("Input filestream not \"good\" in ScanGraph::readBinary"); 375 } 376 this->clear(); 377 378 // read nodes --------------------------------- 379 unsigned int graph_size = 0; 380 s.read((char*)&graph_size, sizeof(graph_size)); 381 if (graph_size) OCTOMAP_DEBUG("reading %d nodes from binary file...\n", graph_size); 382 383 if (graph_size > 0) { 384 this->nodes.reserve(graph_size); 385 386 for (unsigned int i=0; i<graph_size; i++) { 387 388 ScanNode* node = new ScanNode(); 389 node->readBinary(s); 390 if (!s.fail()) { 391 this->nodes.push_back(node); 392 } 393 else { 394 OCTOMAP_ERROR("ScanGraph::readBinary: ERROR.\n" ); 395 break; 396 } 397 } 398 } 399 if (graph_size) OCTOMAP_DEBUG("done.\n"); 400 401 // read edges --------------------------------- 402 unsigned int num_edges = 0; 403 s.read((char*)&num_edges, sizeof(num_edges)); 404 if (num_edges) OCTOMAP_DEBUG("reading %d edges from binary file...\n", num_edges); 405 406 if (num_edges > 0) { 407 this->edges.reserve(num_edges); 408 409 for (unsigned int i=0; i<num_edges; i++) { 410 411 ScanEdge* edge = new ScanEdge(); 412 edge->readBinary(s, *this); 413 if (!s.fail()) { 414 this->edges.push_back(edge); 415 } 416 else { 417 OCTOMAP_ERROR("ScanGraph::readBinary: ERROR.\n" ); 418 break; 419 } 420 } 421 } 422 if (num_edges) OCTOMAP_DEBUG("done.\n"); 423 return s; 424 } 425 readPlainASCII(const std::string & filename)426 void ScanGraph::readPlainASCII(const std::string& filename){ 427 std::ifstream infile(filename.c_str()); 428 if (!infile.is_open()){ 429 OCTOMAP_ERROR_STR("Filestream to "<< filename << " not open, nothing read."); 430 return; 431 } 432 readPlainASCII(infile); 433 infile.close(); 434 } 435 readPlainASCII(std::istream & s)436 std::istream& ScanGraph::readPlainASCII(std::istream& s){ 437 std::string currentLine; 438 ScanNode* currentNode = NULL; 439 while (true){ 440 getline(s, currentLine); 441 if (s.good() && !s.eof()){ 442 std::stringstream ss; 443 ss << currentLine; 444 // skip empty and comment lines: 445 if (currentLine.size() == 0 446 || (currentLine.compare(0,1, "#") == 0) 447 || (currentLine.compare(0,1, " ") == 0)){ 448 449 continue; 450 } else if(currentLine.compare(0,4,"NODE")==0){ 451 if (currentNode){ 452 this->nodes.push_back(currentNode); 453 this->connectPrevious(); 454 OCTOMAP_DEBUG_STR("ScanNode "<< currentNode->pose << " done, size: "<< currentNode->scan->size()); 455 } 456 457 currentNode = new ScanNode(); 458 currentNode->scan = new Pointcloud(); 459 460 float x, y, z, roll, pitch, yaw; 461 std::string tmp; 462 ss >> tmp >> x >> y >> z >> roll >> pitch >> yaw; 463 pose6d pose(x, y, z, roll, pitch, yaw); 464 //std::cout << "Pose "<< pose << " found.\n"; 465 currentNode->pose = pose; 466 } else{ 467 if (currentNode == NULL){ 468 // TODO: allow "simple" pc files by setting initial Scan Pose to (0,0,0) 469 OCTOMAP_ERROR_STR("Error parsing log file, no Scan to add point to!"); 470 break; 471 } 472 float x, y, z; 473 ss >> x >> y >> z; 474 475 //std::cout << "Point "<< x << "," <<y <<"," <<z << " found.\n"; 476 currentNode->scan->push_back(x,y,z); 477 } 478 } else{ 479 if (currentNode){ 480 this->nodes.push_back(currentNode); 481 this->connectPrevious(); 482 OCTOMAP_DEBUG_STR("Final ScanNode "<< currentNode->pose << " done, size: "<< currentNode->scan->size()); 483 } 484 break; 485 } 486 } 487 488 return s; 489 } 490 writeEdgesASCII(std::ostream & s) const491 std::ostream& ScanGraph::writeEdgesASCII(std::ostream &s) const { 492 493 // file structure: n | edge_1 | ... | edge_n 494 495 OCTOMAP_DEBUG_STR("Writing " << this->edges.size() << " edges to ASCII file..."); 496 497 s << " " << this->edges.size(); 498 s << std::endl; 499 500 for (ScanGraph::const_edge_iterator it = this->edges_begin(); it != this->edges_end(); it++) { 501 (*it)->writeASCII(s); 502 } 503 s << std::endl; 504 OCTOMAP_DEBUG_STR("Done."); 505 506 return s; 507 } 508 509 readEdgesASCII(std::istream & s)510 std::istream& ScanGraph::readEdgesASCII(std::istream &s) { 511 512 unsigned int num_edges = 0; 513 s >> num_edges; 514 OCTOMAP_DEBUG("Reading %d edges from ASCII file...\n", num_edges); 515 516 if (num_edges > 0) { 517 518 for (unsigned int i=0; i<this->edges.size(); i++) delete edges[i]; 519 this->edges.clear(); 520 521 this->edges.reserve(num_edges); 522 523 for (unsigned int i=0; i<num_edges; i++) { 524 525 ScanEdge* edge = new ScanEdge(); 526 edge->readASCII(s, *this); 527 if (!s.fail()) { 528 this->edges.push_back(edge); 529 } 530 else { 531 OCTOMAP_ERROR("ScanGraph::readBinary: ERROR.\n" ); 532 break; 533 } 534 } 535 } 536 537 OCTOMAP_DEBUG("done.\n"); 538 539 return s; 540 } 541 542 writeNodePosesASCII(std::ostream & s) const543 std::ostream& ScanGraph::writeNodePosesASCII(std::ostream &s) const { 544 545 OCTOMAP_DEBUG("Writing %lu node poses to ASCII file...\n", (unsigned long) this->size()); 546 547 for (ScanGraph::const_iterator it = this->begin(); it != this->end(); it++) { 548 (*it)->writePoseASCII(s); 549 } 550 s << std::endl; 551 OCTOMAP_DEBUG("done.\n"); 552 553 return s; 554 } 555 readNodePosesASCII(std::istream & s)556 std::istream& ScanGraph::readNodePosesASCII(std::istream &s) { 557 558 for (ScanGraph::const_iterator it = this->begin(); it != this->end(); it++) { 559 (*it)->readPoseASCII(s); 560 } 561 562 for (ScanGraph::edge_iterator it = this->edges_begin(); it != this->edges_end(); it++) { 563 ScanNode* first = (*it)->first; 564 ScanNode* second = (*it)->second; 565 (*it)->constraint = (first->pose).inv() * second->pose; 566 } 567 568 // constraints and nodes are inconsistent, rewire graph 569 // for (unsigned int i=0; i<this->edges.size(); i++) delete edges[i]; 570 // this->edges.clear(); 571 572 573 // ScanGraph::iterator first_it = this->begin(); 574 // ScanGraph::iterator second_it = first_it+1; 575 576 // for ( ; second_it != this->end(); first_it++, second_it++) { 577 // ScanNode* first = (*first_it); 578 // ScanNode* second = (*second_it); 579 // octomath::Pose6D c = (first->pose).inv() * second->pose; 580 // this->addEdge(first, second, c); 581 // } 582 583 584 return s; 585 } 586 587 cropEachScan(point3d lowerBound,point3d upperBound)588 void ScanGraph::cropEachScan(point3d lowerBound, point3d upperBound) { 589 590 for (ScanGraph::iterator it = this->begin(); it != this->end(); it++) { 591 ((*it)->scan)->crop(lowerBound, upperBound); 592 } 593 } 594 595 crop(point3d lowerBound,point3d upperBound)596 void ScanGraph::crop(point3d lowerBound, point3d upperBound) { 597 598 599 // for all node in graph... 600 for (ScanGraph::iterator it = this->begin(); it != this->end(); it++) { 601 pose6d scan_pose = (*it)->pose; 602 Pointcloud* pc = new Pointcloud((*it)->scan); 603 pc->transformAbsolute(scan_pose); 604 pc->crop(lowerBound, upperBound); 605 pc->transform(scan_pose.inv()); 606 delete (*it)->scan; 607 (*it)->scan = pc; 608 } 609 } 610 getNumPoints(unsigned int max_id) const611 size_t ScanGraph::getNumPoints(unsigned int max_id) const { 612 size_t retval = 0; 613 614 for (ScanGraph::const_iterator it = this->begin(); it != this->end(); it++) { 615 retval += (*it)->scan->size(); 616 if ((max_id > 0) && ((*it)->id == max_id)) break; 617 } 618 return retval; 619 } 620 621 622 } // end namespace 623 624 625 626