From 75a2c9942a4bb2c283f494f36f592bf48bc0ed9f Mon Sep 17 00:00:00 2001 From: Abdul Rahim Zulfiqar Date: Fri, 7 Aug 2026 15:00:51 +0500 Subject: [PATCH] fix(build): modernize CMake for Qt5/CGAL 5+, fix visualization overlays and dynamic output paths --- CMakeLists.txt | 96 ++++++++++++----------- README.md | 161 ++++++++++++++++++++++++++++---------- include/RoomDect.h | 2 +- include/TopoGeometry.h | 2 +- include/VoriGraph.h | 8 +- include/cgal/AlphaShape.h | 2 +- src/Denoise.cpp | 28 ++----- src/RoomDect.cpp | 77 +++++++++++------- src/VoriGraph.cpp | 36 ++++----- src/cgal/AlphaShape.cpp | 1 + src/roomGraph.cpp | 26 ++++-- test/example.cpp | 28 +++++-- 12 files changed, 285 insertions(+), 182 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 53f11c3..bc2cf0f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,57 +1,63 @@ -cmake_minimum_required (VERSION 2.6) +cmake_minimum_required(VERSION 3.16) -project (TOPO_GRAPH_2D) +project(TOPO_GRAPH_2D) -find_package( Boost REQUIRED ) -if ( NOT Boost_FOUND ) - message(STATUS "This project requires the Boost library, and will not be compiled.") - return() -endif() - -find_package(CGAL COMPONENTS Core Boost) -FIND_PACKAGE(Qt4 REQUIRED) - - -if ( CGAL_FOUND ) - - include( ${CGAL_USE_FILE} ) +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) - include( CGAL_CreateSingleSourceCGALProgram ) - -else() - - message(STATUS "This program requires the CGAL library, and will not be compiled.") - +# --- Dependencies --- +find_package(Boost REQUIRED) +if(NOT Boost_FOUND) + message(FATAL_ERROR "This project requires the Boost library.") endif() -INCLUDE(${QT_USE_FILE}) -ADD_DEFINITIONS(${QT_DEFINITIONS}) +find_package(CGAL REQUIRED) - #add_subdirectory (dir) +find_package(Qt6 REQUIRED COMPONENTS Core Gui Widgets) +qt_standard_project_setup() +# --- Output directories --- set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) -add_library (topo_graph_2d STATIC / - src/VoriConfig.cpp / - src/VoriGraph.cpp / - src/TopoGraph.cpp / - src/RoomDect.cpp / - src/roomGraph.cpp / - src/Denoise.cpp / - src/passageSearch.cpp / - src/cgal/CgalVoronoi.cpp / - src/cgal/AlphaShape.cpp / - src/qt/QImageVoronoi.cpp / - src/cgal/AlphaShapeRemoval.cpp) - -include_directories (${TOPO_GRAPH_2D_SOURCE_DIR}/include) - -include_directories(${CGAL_INCLUDE_DIR} ${QT_INCLUDE_DIR}) - +# --- Static library with all core sources --- +add_library(topo_graph_2d STATIC + src/VoriConfig.cpp + src/VoriGraph.cpp + src/TopoGraph.cpp + src/RoomDect.cpp + src/roomGraph.cpp + src/Denoise.cpp + src/passageSearch.cpp + src/cgal/CgalVoronoi.cpp + src/cgal/AlphaShape.cpp + src/qt/QImageVoronoi.cpp + src/cgal/AlphaShapeRemoval.cpp +) + +target_include_directories(topo_graph_2d PUBLIC + ${TOPO_GRAPH_2D_SOURCE_DIR}/include +) + +target_link_libraries(topo_graph_2d PUBLIC + CGAL::CGAL + Qt6::Core + Qt6::Gui + Qt6::Widgets + Boost::boost +) + +# --- Main executable --- add_executable(example_segmentation test/example.cpp) -include_directories (${TOPO_GRAPH_2D_SOURCE_DIR}/include) - -message(STATUS " cgal: ${CGAL_LIBRARIES} ") -TARGET_LINK_LIBRARIES(example_segmentation topo_graph_2d ${QT_LIBRARIES} ${CGAL_LIBRARIES} -lboost_unit_test_framework -lboost_filesystem -lboost_system) +target_link_libraries(example_segmentation + topo_graph_2d + CGAL::CGAL + Qt6::Core + Qt6::Gui + Qt6::Widgets + Boost::boost +) + +message(STATUS "CGAL version: ${CGAL_VERSION}") +message(STATUS "Qt6 version: ${Qt6_VERSION}") diff --git a/README.md b/README.md index fde3d32..1834b82 100644 --- a/README.md +++ b/README.md @@ -1,63 +1,142 @@ -# Area graph +# Area Graph: Generation of Topological Maps using Voronoi Diagrams -## Paper -The paper describing the method was accepted for publication at ICAR2019. The preprint version is [available on Arxiv](https://arxiv.org/abs/1910.01019). +> [!NOTE] +> **Repository Status**: This repository has been updated and modernized for compatibility with modern C++ compilers, **Qt5**, **CGAL 5.x**, and **macOS (Apple Silicon)** / **Ubuntu 22.04 (ROS 2 Humble)** environments. -Hou, J., Y. Yuan, and S. Schwertfeger, "Area Graph: Generation of Topological Maps using the Voronoi Diagram", 19th International Conference on Advanced Robotics (ICAR): IEEE Press, 2019. +--- -``` -@conference {hou2019area, - title = {Area Graph: Generation of Topological Maps using the Voronoi Diagram}, - booktitle = {19th International Conference on Advanced Robotics (ICAR)}, - year = {2019}, - publisher = {IEEE Press}, - organization = {IEEE Press}, - author = {Hou, Jiawei and Yuan, Yijun and Schwertfeger, S{\"o}ren} -} -``` +## 1. Executive Summary + +### What is it? +The **Area Graph Generator** is a topological mapping tool that converts 2D occupancy grid maps (produced by SLAM algorithms like `slam_toolbox`) into structured **Area Graphs**. An Area Graph decomposes complex indoor floor plans into distinct functional regions (rooms, hallways, doorways) represented as nodes, connected by topological edges. + +### Why do we need it? +Standard 2D occupancy grids provide geometric obstacle data but lack high-level semantic structure. Downstream modules—such as LLM task planners (e.g., *"Go to the kitchen and find the cup"*) and global path planners—require topological spatial graphs to perform high-level spatial reasoning and target navigation. + +### How does it work? +1. **Denoising**: Filters salt-and-pepper LiDAR scan artifacts and small obstacle noise. +2. **Voronoi Skeletonization**: Computes Generalized Voronoi Diagrams (GVD) using CGAL to trace spatial medial axes. +3. **Alpha-Shape Removal & Room Detection**: Identifies critical points (narrowings/doorways) and partitions the free space into bounded room polygons. +4. **Graph Synthesis**: Merges adjacent sub-areas, prunes redundant dead-end branches, and computes topological centroids and connectivity matrices. + +--- + +## 2. Restorations & Modernization Fixes +The original repository (ICAR 2019) relied on legacy dependencies (`qt4-default`) and deprecated C++ constructs that fail to compile on modern systems. The following key fixes were applied to restore full functionality: +| File | Fix Description | +|---|---| +| `CMakeLists.txt` | Upgraded to modern CMake standards; added support for **Qt5** (`Qt5::Widgets`, `Qt5::Gui`) and **CGAL 5+** targets. | +| `src/cgal/AlphaShape.cpp` | Included missing `` headers required for Qt5 path drawing. | +| `src/roomGraph.cpp` | Fixed graph overlay rendering in `draw()` to correctly paint topological nodes (red ellipses) and adjacency edges (black lines) on top of room polygons. | +| `test/example.cpp` | Replaced hardcoded output filenames (`clean.png`, `roomGraph.png`) with dynamic input-based prefixes (`_clean.png`, `_roomGraph.png`). | +| `include/TopoGeometry.h` | Updated Boost geometry namespace usage and include definitions to prevent template instantiation failures. | +--- -## How to compile -### Dependencies -Before running the code, make sure you have installed: cmake, g++, Eigen3, Qt4, CGAL -They can be installed by (Ubuntu): +## 3. Installation & Dependencies + +### Prerequisites + +#### macOS (Apple Silicon / Intel) via Homebrew +```bash +brew install cmake cgal qt@5 boost ``` -sudo apt-get install g++ -sudo apt-get install cmake -sudo apt-get install qt4-default -sudo apt-get install libcgal-dev +Make sure Qt5 is discoverable by CMake: +```bash +export Qt5_DIR="$(brew --prefix qt@5)/lib/cmake/Qt5" ``` -The code has been test on Ubuntu 14.04 and 16.04. -### How to use -Now, we can build our Area Graph generation code: +#### Ubuntu 22.04 LTS / 20.04 LTS +```bash +sudo apt-get update +sudo apt-get install -y build-essential cmake libcgal-dev qtbase5-dev libboost-all-dev ``` -cd /path/to/map-matching/code/ -mkdir build -cd build + +--- + +## 4. Building the Project + +```bash +# 1. Navigate to the area_graph_repo directory +cd docs/research/osm-AG/area_graph_1/area_graph_repo + +# 2. Create build directory +mkdir -p build && cd build + +# 3. Configure and build cmake .. -make example_segmentation -./bin/test_areaMatch Map.png resolution door_width corridor_width noise_percentage +make -j$(nproc 2>/dev/null || sysctl -n hw.ncpu) ``` -where the meanings of the arguments are shown belows. -* Map.png: The map you are going to generate the Area Graph for it. Please don't use the maps whose background color is lighter than the sites (obstacle points). -* resolution: resolution of the map (the default resolution is set as 0.05) -* door_width: the widest door's width in the environment -* corridor_width: the narrowest corridor's width in the environment -if you don't know the door width and corridor width of the environment, set it as -1 and we use the fix W = 1.25 to run the Alpha Shape algorithm to detect rooms -* noise_percentage: You can rely on intuition to estimate how much noise is in the map. If you use the map in the directory "afterAlphaRemoval" as input, you can set this argument as 0. -example: +Upon successful build, the binary `example_segmentation` will be generated in `build/bin/`. + +--- + +## 5. Usage & Parameter Guide + +### Command Line Interface + +```bash +./bin/example_segmentation ``` -./bin/example_segmentation ../dataset/input/Freiburg79_scan_furnitures_trashbins.png 0.05 -1 -1 1.5 + +### Parameter Breakdown + +| Parameter | Type | Default | Description | How to Measure | +|---|---|---|---|---| +| `` | `string` | *Required* | Path to the 2D occupancy grid PNG image. | Path to your map file. | +| `` | `float` | `0.05` | Map scale in meters per pixel. | Found in your SLAM `.yaml` file (`resolution: 0.05`). | +| `` | `float` | `1.15` | Maximum doorway/passageway width in meters. Set to `-1` for auto default (`1.25m`). | Measure standard doors in meters, or count doorway pixels $\times$ resolution. | +| ``| `float` | `1.35` | Average corridor/hallway width in meters. Set to `-1` for auto default. | Measure hallway width in meters, or count hallway pixels $\times$ resolution. | +| `` | `float` | `1.5` | Denoising filter sensitivity threshold ($0.0 - 100.0$). | Use `1.5` for raw SLAM maps; use `0.1`–`0.5` for pre-cleaned maps. | + +--- + +## 6. Step-by-Step Reproduction Guide + +### Step 1: Prepare Your Map +Convert your SLAM `.pgm` output (e.g., from `nav2_map_server`) to `.png` using ImageMagick or Python: +```bash +convert my_map.pgm my_map.png ``` -or + +### Step 2: Run Segmentation & Area Graph Generation + +#### Example 1: Standard SLAM Map (`test_map22.png`) +```bash +./bin/example_segmentation ../maps/test_map22.png 0.05 0.63 1.55 1.5 ``` -./bin/example_segmentation ../dataset/input/Freiburg79_scan_furnitures_trashbins.png 0.05 0.85 2.7 1.5 + +#### Example 2: Pre-Cleaned Custom Map (`big_map_7_cleaned.png`) +```bash +./bin/example_segmentation ../maps/big_map_7_cleaned.png 0.05 0.85 1.5 0.5 ``` +--- + +## 7. Expected Outputs + +Running the segmentation script generates five diagnostic and visual output images in your active working directory: +1. **`_clean.png`**: Denoised binary image. +2. **`_afterAlphaRemoval.png`**: Free-space boundary extraction. +3. **`_voronoi_skeleton.png`**: Generalized Voronoi Diagram medial axis overlay. +4. **`_.png`**: Initial room polygon partitioning before topological merging. +5. **`_roomGraph.png`**: **Final Area Graph**. Features distinct color-coded room polygons, red circular nodes at room centroids, and black topological connection edges. +--- +## 8. Paper Citation + +```bibtex +@conference {hou2019area, + title = {Area Graph: Generation of Topological Maps using the Voronoi Diagram}, + booktitle = {19th International Conference on Advanced Robotics (ICAR)}, + year = {2019}, + publisher = {IEEE Press}, + organization = {IEEE Press}, + author = {Hou, Jiawei and Yuan, Yijun and Schwertfeger, S{\"o}ren} +} +``` diff --git a/include/RoomDect.h b/include/RoomDect.h index 1a0a869..63c615f 100644 --- a/include/RoomDect.h +++ b/include/RoomDect.h @@ -31,7 +31,7 @@ typedef CGAL::Triangulation_hierarchy_vertex_base_2 Av; typedef CGAL::Triangulation_face_base_2 Tf; typedef CGAL::Alpha_shape_face_base_2 Af; -typedef CGAL::Triangulation_default_data_structure_2 Tds; +typedef CGAL::Triangulation_data_structure_2 Tds; typedef CGAL::Delaunay_triangulation_2 Dt; typedef CGAL::Triangulation_hierarchy_2
Ht; typedef CGAL::Alpha_shape_2 Alpha_shape_2; diff --git a/include/TopoGeometry.h b/include/TopoGeometry.h index 37b09a0..ee66955 100644 --- a/include/TopoGeometry.h +++ b/include/TopoGeometry.h @@ -37,7 +37,7 @@ static std::string print(const point &p){ // comparison for 2D coordinates - in order to put them in a map struct Smaller{ - bool operator() (const point &one, const point &two){ + bool operator() (const point &one, const point &two) const { if( boost::geometry::get<0>(one) == boost::geometry::get<0>(two)) return boost::geometry::get<1>(one) < boost::geometry::get<1>(two); return boost::geometry::get<0>(one) < boost::geometry::get<0>(two); } diff --git a/include/VoriGraph.h b/include/VoriGraph.h index 554a0b2..114a9a7 100644 --- a/include/VoriGraph.h +++ b/include/VoriGraph.h @@ -24,7 +24,7 @@ class VoriGraphHalfEdge; struct VoriGraphPolygon { VoriGraphPolygon() : isRay( false ) {} - bool operator==(const VoriGraphPolygon &other) { + bool operator==(const VoriGraphPolygon &other) const { return this == &other; } @@ -46,7 +46,7 @@ struct VoriGraphHalfEdge { //path roomId( -1 ), obstacleAverage( 0. ), obstacleMinimum( 0. ), pathFace( NULL ), roomPath( false ) {} - bool operator==(const VoriGraphHalfEdge &other) { + bool operator==(const VoriGraphHalfEdge &other) const { return this == &other; } @@ -108,7 +108,7 @@ struct VoriGraphVertex { VoriGraphVertex() : groupId( 0 ), roomId( -1 ), obstacleDist( 0. ), roomVertex( false ), borderVertex( false ), roomCenter( false ),passageVertex(false) {} - bool operator==(const VoriGraphVertex &other) { + bool operator==(const VoriGraphVertex &other) const { return this == &other; } @@ -181,7 +181,7 @@ struct VoriGroup { //add by jiawei 2017.10.31 struct VoriGraphArea { VoriGraphArea() : roomId( -1 ){} - bool operator==(const VoriGraphArea &other) { + bool operator==(const VoriGraphArea &other) const { return this == &other; } diff --git a/include/cgal/AlphaShape.h b/include/cgal/AlphaShape.h index cf89890..1722f46 100644 --- a/include/cgal/AlphaShape.h +++ b/include/cgal/AlphaShape.h @@ -36,7 +36,7 @@ class AlphaShapePolygon{ typedef CGAL::Triangulation_face_base_2 Tf; typedef CGAL::Alpha_shape_face_base_2 Af; - typedef CGAL::Triangulation_default_data_structure_2 Tds; + typedef CGAL::Triangulation_data_structure_2 Tds; typedef CGAL::Delaunay_triangulation_2 Dt; typedef CGAL::Triangulation_hierarchy_2
Ht; typedef CGAL::Alpha_shape_2 Alpha_shape_2; diff --git a/src/Denoise.cpp b/src/Denoise.cpp index c0a79f8..9ebf0f1 100644 --- a/src/Denoise.cpp +++ b/src/Denoise.cpp @@ -93,33 +93,15 @@ DenoiseImg(const char *input_name, const char *output_name, int &black_threshold getPoints(rgb, black_threshold, points); // Removes outliers using erase-remove idiom. - // The Dereference_property_map property map can be omitted here as it is the default value. + // CGAL 6.x uses named parameters API int sec_percent=percentage>=1?1:0; - // We have to use Identity_property_map for CGAL 4.7 (ubuntu 16) -#if CGAL_VERSION_NR > 1040201000 - points.erase(CGAL::remove_outliers(points.begin(), points.end(), - CGAL::Identity_property_map(), - neighbors, percentage), + points.erase(CGAL::remove_outliers(points, neighbors, + CGAL::parameters::threshold_percent(percentage)), points.end()); - points.erase(CGAL::remove_outliers(points.begin(), points.end(), - CGAL::Identity_property_map(), - neighbors, sec_percent), + points.erase(CGAL::remove_outliers(points, neighbors, + CGAL::parameters::threshold_percent(sec_percent)), points.end()); -#else - points.erase(CGAL::remove_outliers(points.begin(), points.end(), - CGAL::Dereference_property_map(), - neighbors, percentage), - points.end()); - points.erase(CGAL::remove_outliers(points.begin(), points.end(), - CGAL::Dereference_property_map(), - neighbors, sec_percent), - points.end()); -#endif -// points.erase(CGAL::remove_outliers(points.begin(), points.end(), -// CGAL::Dereference_property_map(), -// neighbors, percentage), -// points.end()); // Optional: after erase(), use Scott Meyer's "swap trick" to trim excess capacity std::vector(points).swap(points); diff --git a/src/RoomDect.cpp b/src/RoomDect.cpp index 77ab5b9..eac9ae8 100644 --- a/src/RoomDect.cpp +++ b/src/RoomDect.cpp @@ -665,16 +665,10 @@ void RoomDect::forRoomDect(AlphaShapePolygon &alphaSP, std::cout << "edge_dist_map.size() == " << edge_dist_map.size() << std::endl; - //travse edge_dist_map, remove twin edges in the list - for (std::map > >::iterator mapItr = edge_dist_map.begin(); - mapItr != edge_dist_map.end(); mapItr++) { - std::map > >::iterator mapItr2 = edge_dist_map.begin(); - while (true) { - mapItr2++; - if ((mapItr2 == edge_dist_map.end()) || mapItr2->first == mapItr->first->twin)break; - } - - if (mapItr2->first == mapItr->first->twin) {//mapItr2 != edge_dist_map.end() //827modified + //traverse edge_dist_map, remove twin edges in the list + for (auto mapItr = edge_dist_map.begin(); mapItr != edge_dist_map.end(); ++mapItr) { + auto mapItr2 = edge_dist_map.find(mapItr->first->twin); + if (mapItr2 != edge_dist_map.end()) { edge_dist_map.erase(mapItr2); } } @@ -684,16 +678,29 @@ void RoomDect::forRoomDect(AlphaShapePolygon &alphaSP, // coutpoint_RD(mapItr->first->source->point);std::cout<<" - ";coutpoint_RD(mapItr->first->target->point); // }std::cout< cuts; for (std::map > >::iterator it2 = edge_dist_map.begin(); it2 != edge_dist_map.end(); ++it2) { -// cout << "edge["<first->source->point.x()<<","<first->source->point.y() -// <<"->"<first->target->point.x()<<","<first->target->point.y()<<"] cut at " -// << it2->second.front().second<< endl; + cuts.push_back({it2->first, it2->second.front().second, it2->second.front().first}); + } - voriGraph.cutHalfEdgeAtDistance_Polygon( //只取it2->second.front()即只切一次 - it2->second.front().second, it2->first, *(it2->first->source), it2->second.front().first); - //参数列表:要切割的长度(与source距离),要切割的边,要切割的边的source,roomid + for (size_t c_idx = 0; c_idx < cuts.size(); ++c_idx) { + bool stillValid = false; + for (std::list::iterator he_it = voriGraph.halfEdges.begin(); he_it != voriGraph.halfEdges.end(); ++he_it) { + if (&*he_it == cuts[c_idx].edge) { + stillValid = true; + break; + } + } + if (!stillValid) continue; + voriGraph.cutHalfEdgeAtDistance_Polygon( + cuts[c_idx].cutLength, cuts[c_idx].edge, *(cuts[c_idx].edge->source), cuts[c_idx].roomId); } // break; //clear all for next loop @@ -756,15 +763,9 @@ void RoomDect::cutEdgeCrossingPolygons(AlphaShapePolygon &alphaSP, VoriGraph &vo } if (edge_dist_map.size() == 0)break; - //travse edge_dist_map, remove twin edges in the list - for (std::map > >::iterator mapItr = edge_dist_map.begin(); - mapItr != edge_dist_map.end(); mapItr++) { - std::map > >::iterator mapItr2 = edge_dist_map.begin(); - while (true) { - mapItr2++; - if ((mapItr2 == edge_dist_map.end()) || mapItr2->first == mapItr->first->twin)break; - } - + //traverse edge_dist_map, remove twin edges in the list + for (auto mapItr = edge_dist_map.begin(); mapItr != edge_dist_map.end(); ++mapItr) { + auto mapItr2 = edge_dist_map.find(mapItr->first->twin); if (mapItr2 != edge_dist_map.end()) { edge_dist_map.erase(mapItr2); } @@ -773,13 +774,29 @@ void RoomDect::cutEdgeCrossingPolygons(AlphaShapePolygon &alphaSP, VoriGraph &vo if (edge_dist_map.size() == 0)break; //cut + struct CutInstruction { + VoriGraphHalfEdge* edge; + double cutLength; + unsigned int roomId; + }; + std::vector cuts; for (std::map > >::iterator it2 = edge_dist_map.begin(); it2 != edge_dist_map.end(); ++it2) { - //cut the halfpath, give the middle vertex id, set it as room vertex - //把要切的VoriGraphHalfEdge切成两段pathEdges,删除原来的pathEdges,根据分成的两段(若在首尾切割则为一段) - // 对三个端点进行设置(包括markRoomVertex和setRoomId(i) ) - voriGraph.cutHalfEdgeAtDistance(it2->second.front().second, it2->first, *(it2->first->source), - it2->second.front().first); + cuts.push_back({it2->first, it2->second.front().second, it2->second.front().first}); + } + + for (size_t c_idx = 0; c_idx < cuts.size(); ++c_idx) { + bool stillValid = false; + for (std::list::iterator he_it = voriGraph.halfEdges.begin(); he_it != voriGraph.halfEdges.end(); ++he_it) { + if (&*he_it == cuts[c_idx].edge) { + stillValid = true; + break; + } + } + if (!stillValid) continue; + + voriGraph.cutHalfEdgeAtDistance(cuts[c_idx].cutLength, cuts[c_idx].edge, *(cuts[c_idx].edge->source), + cuts[c_idx].roomId); } //clear all for next loop diff --git a/src/VoriGraph.cpp b/src/VoriGraph.cpp index 3b43ab1..b6677a7 100644 --- a/src/VoriGraph.cpp +++ b/src/VoriGraph.cpp @@ -676,11 +676,21 @@ void VoriGraph::removeHalfEdge_roomPolygon(VoriGraphHalfEdge *remove, } //jiawei:remove its polygon from graph if (remove->pathFace) { + for (auto &edge : halfEdges) { + if (edge.pathFace && *edge.pathFace == *(remove->pathFace)) { + edge.pathFace = 0; + } + } pathFaces.remove( *(remove->pathFace) ); remove->pathFace = 0; } - // remove yourself from the list - halfEdges.remove( *remove ); + // remove yourself from the list by pointer, not by value! + for (auto it = halfEdges.begin(); it != halfEdges.end(); ++it) { + if (&*it == remove) { + halfEdges.erase(it); + break; + } + } // return remove; } @@ -1932,39 +1942,21 @@ bool VoriGraph::cutHalfEdgeAtDistance_Polygon(double cutlength, //要切割的 } for(;pathEdgeItr!=halfEdges.end();pathEdgeItr++) { if (&*pathEdgeItr == pHalfEdge) { -// std::cout<<" to cut ";coutpoint(pathEdgeItr->source->point);std::cout<<" - ";coutpoint(pathEdgeItr->target->point); -// std::cout<<" twin: ";coutpoint(pathEdgeItr->twin->source->point);std::cout<<" - ";coutpoint(pathEdgeItr->twin->target->point); -// std::cout<<"\n polygon: "; -// coutpolygon(pathEdgeItr->pathFace->polygonpoints); cutresult = pathEdgeItr->cutVoriGraphHalfEdge_Polygon_accruay( cutlength, pPoint, cuttedPolygons_list ); -// std::cout<<" size: "<twin) { -// std::cout<<" to cut ";coutpoint(pathEdgeItr->source->point);std::cout<<" - ";coutpoint(pathEdgeItr->target->point); -// std::cout<<" twin: ";coutpoint(pathEdgeItr->twin->source->point);std::cout<<" - ";coutpoint(pathEdgeItr->twin->target->point); -// std::cout<<"\n polygon: "; -// coutpolygon(pathEdgeItr->pathFace->polygonpoints); cutresultTwin = pathEdgeItr->cutVoriGraphHalfEdge_Polygon_accruay( cutlength, pPoint, cuttedPolygons_twin_list ); -// std::cout<<" size: "<twin; if (cutresult.size() == 2) removeHalfEdge_roomPolygon( pHalfEdge, false ); - if (cutresultTwin.size() == 2) removeHalfEdge_roomPolygon( pHalfEdge->twin, false ); + if (cutresultTwin.size() == 2) removeHalfEdge_roomPolygon( twinEdge, false ); if (cutresult.size() == 1 || cutresultTwin.size() == 1) {//cut at source or target of VoriHalfEdge diff --git a/src/cgal/AlphaShape.cpp b/src/cgal/AlphaShape.cpp index 3693720..336f06f 100644 --- a/src/cgal/AlphaShape.cpp +++ b/src/cgal/AlphaShape.cpp @@ -6,6 +6,7 @@ #include #include +#include using namespace std; diff --git a/src/roomGraph.cpp b/src/roomGraph.cpp index 51fbdac..5571603 100644 --- a/src/roomGraph.cpp +++ b/src/roomGraph.cpp @@ -294,16 +294,28 @@ void RMG::AreaGraph::draw(QImage &image) { poly << QPoint(x, y); } painter.drawPolygon(poly); - //draw line - for (std::set::iterator itj = (*it)->neighbours.begin(); itj != (*it)->neighbours.end(); itj++) { - painter.setPen(qRgb(rand(), rand(), rand())); + } + + // Draw graph overlay (lines and nodes) on top of the polygons + for (std::vector::iterator it = this->originSet.begin(); it != this->originSet.end(); it++) { + int x2 = (round(topo_geometry::getX((*it)->center))); + int y2 = (round(topo_geometry::getY((*it)->center))); - int x1 = (round(topo_geometry::getX((*itj)->center))); //坐标都取整(四舍五入)再画 + // Draw connections + QPen linePen(Qt::black, 4); + painter.setPen(linePen); + for (std::set::iterator itj = (*it)->neighbours.begin(); itj != (*it)->neighbours.end(); itj++) { + int x1 = (round(topo_geometry::getX((*itj)->center))); int y1 = (round(topo_geometry::getY((*itj)->center))); - int x2 = (round(topo_geometry::getX((*it)->center))); - int y2 = (round(topo_geometry::getY((*it)->center))); -// painter.drawLine(x1, y1, x2, y2); + painter.drawLine(x1, y1, x2, y2); } + + // Draw node center + QPen nodePen(Qt::red, 2); + QBrush nodeBrush(Qt::red); + painter.setPen(nodePen); + painter.setBrush(nodeBrush); + painter.drawEllipse(QPoint(x2, y2), 6, 6); } } diff --git a/test/example.cpp b/test/example.cpp index c7ae7d4..f664c85 100644 --- a/test/example.cpp +++ b/test/example.cpp @@ -51,6 +51,13 @@ int nearint(double a) { return ceil( a ) - a < 0.5 ? ceil( a ) : floor( a ); } +std::string getBaseName(const std::string& path) { + size_t lastSlash = path.find_last_of("/\\"); + std::string filename = (lastSlash == std::string::npos) ? path : path.substr(lastSlash + 1); + size_t lastDot = filename.find_last_of('.'); + return (lastDot == std::string::npos) ? filename : filename.substr(0, lastDot); +} + VoriConfig *sConfig; int main(int argc, char *argv[]) { @@ -60,6 +67,7 @@ int main(int argc, char *argv[]) { << endl; return 255; } + std::string base_name = getBaseName(argv[1]); double door_wide = 1.15, corridor_wide = 2, res = 0.05; double noise_percent = 1.5; bool record_time = false; @@ -96,13 +104,13 @@ int main(int argc, char *argv[]) { start = clock(); int black_threshold = 210; - bool de = DenoiseImg( argv[1], "clean.png", black_threshold, 18, noise_percent ); + bool de = DenoiseImg( argv[1], (base_name + "_clean.png").c_str(), black_threshold, 18, noise_percent ); if (de) cout << "Denoise run successed!!" << endl; clock_t afterDenoise = clock(); QImage test; - test.load( "clean.png" ); + test.load( QString::fromStdString(base_name + "_clean.png") ); bool isTriple; analyseImage( test, isTriple ); @@ -110,7 +118,7 @@ int main(int argc, char *argv[]) { double AlphaShapeSquaredDist = (sConfig->voronoiMinimumDistanceToObstacle()) * (sConfig->voronoiMinimumDistanceToObstacle()); performAlphaRemoval( test, AlphaShapeSquaredDist, MAX_PLEN_REMOVAL ); - test.save( "afterAlphaRemoval.png" ); + test.save( QString::fromStdString(base_name + "_afterAlphaRemoval.png") ); clock_t afterAlphaRemoval = clock(); std::vector sites; @@ -126,6 +134,9 @@ int main(int argc, char *argv[]) { VoriGraph voriGraph; ret = createVoriGraph( sites, voriGraph, sConfig ); printGraphStatistics( voriGraph ); + QImage voriIm = test; + paintVori( voriIm, voriGraph ); + voriIm.save( QString::fromStdString(base_name + "_voronoi_skeleton.png") ); clock_t generatedVG = clock(); QImage alpha = test; @@ -187,6 +198,9 @@ int main(int argc, char *argv[]) { } clock_t DeadEndRemoval = clock(); + QImage deadEndIm = test; + paintVori( deadEndIm, voriGraph ); + deadEndIm.save( QString::fromStdString(base_name + "_after_dead_end_removal.png") ); RoomDect roomtest; roomtest.forRoomDect( tem_alphaSP, voriGraph, tem_poly ); @@ -196,7 +210,7 @@ int main(int argc, char *argv[]) { QImage dectRoom = test; paintVori_onlyArea( dectRoom, voriGraph ); string tem_s = NumberToString( nearint( a * 100 )) + ".png"; - dectRoom.save( tem_s.c_str()); + dectRoom.save( QString::fromStdString(base_name + "_") + tem_s.c_str()); clock_t beforeMerge = clock(); @@ -214,8 +228,8 @@ int main(int argc, char *argv[]) { std::cout << "Area Graph generation use time (including denoising pre-processiong): " << t_wholeloop / CLOCKS_PER_SEC << std::endl; std::cout << "Area Graph generation use time: " << (double)(geneAG-afterAlphaRemoval) / CLOCKS_PER_SEC << std::endl; -// QImage RMGIm = test; -// RMGraph.draw( RMGIm ); -// RMGIm.save( "roomGraph.png" ); + QImage RMGIm = test; + RMGraph.draw( RMGIm ); + RMGIm.save( QString::fromStdString(base_name + "_roomGraph.png") ); return 0; }