├── .gitignore ├── CMakeLists.txt ├── KittiConfig.cpp ├── KittiConfig.h ├── KittiDataset.cpp ├── KittiDataset.h ├── QtKittiVisualizer.cpp ├── QtKittiVisualizer.h ├── QtKittiVisualizer.ui ├── README.md ├── cmake └── modules │ ├── FindEigen.cmake │ └── FindFLANN.cmake ├── documentation ├── build-instructions │ └── ubuntu-20.04.md └── screenshots │ └── qt-kitti-visualizer.png ├── kitti-devkit-raw ├── readme.txt └── tracklets.h ├── main.cpp └── patches └── pcl-1.12.1 └── link-required-vtk-libraries-for-libpcl_io.patch /.gitignore: -------------------------------------------------------------------------------- 1 | CMakeLists.txt.user 2 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.16) 2 | 3 | project(QtKittiVisualizer) 4 | 5 | # Make sure PCL finds the installed Eigen3 and FLANN: 6 | # FindEigen.cmake and FindFLANN.cmake 7 | # were just copied from pcl-1.12.1/cmake/Modules 8 | set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/modules) 9 | 10 | # You can alternatively include the modules from your PCL source directly: 11 | #set(CMAKE_MODULE_PATH $ENV{HOME}/pcl-1.12.1/cmake/Modules) 12 | 13 | #message(STATUS "CMAKE_MODULE_PATH: " ${CMAKE_MODULE_PATH}) 14 | 15 | 16 | # VTK 17 | # === 18 | 19 | # Hint were to find the VTK source code. 20 | if (NOT DEFINED VTK_DIR) 21 | set(VTK_DIR $ENV{HOME}/vtk-9.1.0-build/) 22 | endif() 23 | 24 | find_package(VTK 9.1.0 REQUIRED COMPONENTS 25 | CommonCore 26 | FiltersSources 27 | GUISupportQt 28 | InteractionStyle 29 | RenderingCore 30 | RenderingOpenGL2 31 | ) 32 | 33 | message(STATUS "VTK_QT_VERSION: " ${VTK_QT_VERSION}) 34 | 35 | 36 | # Qt 37 | # == 38 | 39 | find_package(Qt5Widgets REQUIRED COMPONENTS 40 | Core 41 | Gui 42 | REQUIRED 43 | ) 44 | 45 | 46 | # Boost 47 | # ===== 48 | 49 | find_package(Boost 1.71 COMPONENTS 50 | program_options 51 | filesystem 52 | REQUIRED 53 | ) 54 | message(STATUS "Boost_VERSION: " ${Boost_VERSION}) 55 | set(Boost_COMPONENTS_INCLUDE_DIRS ${Boost_INCLUDE_DIRS}) 56 | set(Boost_COMPONENTS_LIBRARY_DIRS ${Boost_LIBRARY_DIRS}) 57 | set(Boost_COMPONENTS_LIBRARIES ${Boost_LIBRARIES}) 58 | include_directories(${Boost_COMPONENTS_INCLUDE_DIRS}) 59 | link_directories(${Boost_COMPONENTS_LIBRARY_DIRS}) 60 | add_definitions(${Boost_COMPONENTS_DEFINITIONS}) 61 | 62 | 63 | # PCL 64 | # === 65 | 66 | # Hint were to find the PCL source code. 67 | if (NOT DEFINED PCL_DIR) 68 | set(PCL_DIR $ENV{HOME}/pcl-1.12.1-build/) 69 | endif() 70 | 71 | find_package(PCL 1.12.1 REQUIRED) 72 | message(STATUS "PCL_VERSION: " ${PCL_VERSION}) 73 | include_directories(${PCL_INCLUDE_DIRS}) 74 | link_directories(${PCL_LIBRARY_DIRS}) 75 | add_definitions(${PCL_DEFINITIONS}) 76 | 77 | 78 | # Target: qt-kitti-visualizer 79 | # =========================== 80 | 81 | set(PROJECT_BINARY_NAME qt-kitti-visualizer) 82 | 83 | include_directories(${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_SOURCE_DIR}) 84 | 85 | set(CPP_FILES 86 | KittiConfig.cpp 87 | KittiDataset.cpp 88 | main.cpp 89 | QtKittiVisualizer.cpp 90 | ) 91 | 92 | add_executable(${PROJECT_BINARY_NAME} 93 | ${CPP_FILES} 94 | ) 95 | 96 | target_link_libraries(${PROJECT_BINARY_NAME} PRIVATE 97 | ${PCL_LIBRARIES} 98 | Qt5::Widgets 99 | VTK::CommonCore 100 | VTK::FiltersSources 101 | VTK::GUISupportQt 102 | VTK::InteractionStyle 103 | VTK::RenderingCore 104 | VTK::RenderingOpenGL2 105 | ${Boost_COMPONENTS_LIBRARIES} 106 | ) 107 | 108 | set_target_properties(${PROJECT_BINARY_NAME} PROPERTIES 109 | AUTOMOC ON 110 | AUTOUIC ON 111 | ) 112 | -------------------------------------------------------------------------------- /KittiConfig.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright 2016 Mark Muth 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | 17 | */ 18 | 19 | #include "KittiConfig.h" 20 | 21 | #include 22 | #include 23 | #include 24 | 25 | #include 26 | #include 27 | 28 | std::string KittiConfig::data_directory = "../KittiData"; 29 | std::string KittiConfig::raw_data_directory = "raw"; 30 | std::string KittiConfig::dataset_folder_template = "%|04|_sync"; 31 | std::string KittiConfig::point_cloud_directory = "velodyne_points/data"; 32 | std::string KittiConfig::point_cloud_file_template = "%|010|.bin"; 33 | std::string KittiConfig::tracklets_directory = "."; 34 | std::string KittiConfig::tracklets_file_name = "tracklet_labels.xml"; 35 | 36 | const std::vector KittiConfig::availableDatasets = KittiConfig::initAvailableDatasets(); 37 | 38 | boost::filesystem::path KittiConfig::getPointCloudPath(int dataset) 39 | { 40 | return boost::filesystem::path(data_directory) 41 | / raw_data_directory 42 | / (boost::format(dataset_folder_template) % dataset).str() 43 | / point_cloud_directory 44 | ; 45 | } 46 | 47 | boost::filesystem::path KittiConfig::getPointCloudPath(int dataset, int frameId) 48 | { 49 | return getPointCloudPath(dataset) 50 | / (boost::format(point_cloud_file_template) % frameId).str() 51 | ; 52 | } 53 | 54 | boost::filesystem::path KittiConfig::getTrackletsPath(int dataset) 55 | { 56 | return boost::filesystem::path(data_directory) 57 | / raw_data_directory 58 | / (boost::format(dataset_folder_template) % dataset).str() 59 | / tracklets_directory 60 | / tracklets_file_name 61 | ; 62 | } 63 | 64 | int KittiConfig::getDatasetNumber(int index) 65 | { 66 | if (index >= 0 && index < availableDatasets.size()) 67 | { 68 | return availableDatasets.at(index); 69 | } 70 | std::cerr << "No such data set index: " << index << std::endl; 71 | return 0; 72 | } 73 | 74 | int KittiConfig::getDatasetIndex(int number) 75 | { 76 | for (int i = 0; i < availableDatasets.size(); ++i) 77 | { 78 | if (availableDatasets.at(i) == number) 79 | { 80 | return i; 81 | } 82 | } 83 | std::cerr << "No such data set number: " << number << std::endl; 84 | return 0; 85 | } 86 | 87 | std::vector KittiConfig::initAvailableDatasets() 88 | { 89 | std::vector datasets; 90 | datasets.push_back(1); 91 | datasets.push_back(2); 92 | datasets.push_back(5); 93 | datasets.push_back(9); 94 | datasets.push_back(11); 95 | datasets.push_back(13); 96 | datasets.push_back(14); 97 | datasets.push_back(17); 98 | datasets.push_back(18); 99 | datasets.push_back(48); 100 | datasets.push_back(51); 101 | datasets.push_back(56); 102 | datasets.push_back(57); 103 | datasets.push_back(59); 104 | datasets.push_back(60); 105 | datasets.push_back(84); 106 | datasets.push_back(91); 107 | datasets.push_back(93); 108 | return datasets; 109 | } 110 | -------------------------------------------------------------------------------- /KittiConfig.h: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright 2016 Mark Muth 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | 17 | */ 18 | 19 | #ifndef KITTICONFIG_H 20 | #define KITTICONFIG_H 21 | 22 | #include 23 | #include 24 | 25 | #include 26 | 27 | /** 28 | * @brief The KittiConfig class 29 | * 30 | * Define where the KITTI data sets are stored. Templated strings provide for a 31 | * high flexibility in storing and accessing your files. 32 | * 33 | * The predefined values assume the following filesystem hierarchy: 34 | * 35 | * /QtKittiVisualizer (source) 36 | * /QtKittiVisualizer-build (working directory) 37 | * /KittiData 38 | * /raw 39 | * /%|04|_sync (datasets, e.g. 0001_sync) 40 | * /velodyne_points 41 | * /data 42 | * /%|010|.bin (point clouds, e.g. 0000000000.bin) 43 | * /tracklet_labels.xml (tracklets) 44 | * 45 | * You can change the predefined values to your needs in KittiConfig.cpp. 46 | */ 47 | class KittiConfig 48 | { 49 | 50 | public: 51 | 52 | static boost::filesystem::path getPointCloudPath(int dataset); 53 | static boost::filesystem::path getPointCloudPath(int dataset,int frameId); 54 | static boost::filesystem::path getTrackletsPath(int dataset); 55 | 56 | /** Contains the numbers of data sets available from your data set folder */ 57 | static const std::vector availableDatasets; 58 | static int getDatasetNumber(int index); 59 | static int getDatasetIndex(int number); 60 | 61 | private: 62 | 63 | 64 | // Following variables describe the filesystem hierarchy of the KITTI dataset 65 | static std::string data_directory; 66 | static std::string raw_data_directory; 67 | static std::string dataset_folder_template; 68 | static std::string point_cloud_directory; 69 | static std::string point_cloud_file_template; 70 | static std::string tracklets_directory; 71 | static std::string tracklets_file_name; 72 | 73 | static std::vector initAvailableDatasets(); 74 | }; 75 | 76 | #endif // KITTICONFIG_H 77 | -------------------------------------------------------------------------------- /KittiDataset.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright 2016 Mark Muth 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | 17 | */ 18 | 19 | #include "KittiDataset.h" 20 | 21 | #include 22 | #include 23 | 24 | #include 25 | #include 26 | #include 27 | 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | #include 34 | 35 | KittiDataset::KittiDataset(int dataset) : 36 | _dataset(dataset), 37 | _number_of_frames(0) 38 | { 39 | if (!boost::filesystem::exists(KittiConfig::getPointCloudPath(_dataset))) 40 | { 41 | std::cerr << "Error in KittiDataset: Data set path " 42 | << (KittiConfig::getPointCloudPath(_dataset)).string() 43 | << " does not exist!" << std::endl; 44 | return; 45 | } 46 | if (!boost::filesystem::exists(KittiConfig::getPointCloudPath(_dataset, 0))) 47 | { 48 | std::cerr << "Error in KittiDataset: No point cloud was found at " 49 | << (KittiConfig::getPointCloudPath(_dataset, 0)).string() 50 | << std::endl; 51 | return; 52 | } 53 | if (!boost::filesystem::exists(KittiConfig::getTrackletsPath(_dataset))) 54 | { 55 | std::cerr << "Error in KittiDataset: No tracklets were found at " 56 | << (KittiConfig::getTrackletsPath(_dataset)).string() 57 | << std::endl; 58 | return; 59 | } 60 | 61 | initNumberOfFrames(); 62 | initTracklets(); 63 | } 64 | 65 | int KittiDataset::getNumberOfFrames() 66 | { 67 | return _number_of_frames; 68 | } 69 | 70 | KittiPointCloud::Ptr KittiDataset::getPointCloud(int frameId) 71 | { 72 | KittiPointCloud::Ptr cloud(new KittiPointCloud); 73 | std::fstream file(KittiConfig::getPointCloudPath(_dataset, frameId).c_str(), std::ios::in | std::ios::binary); 74 | if(file.good()){ 75 | file.seekg(0, std::ios::beg); 76 | int i; 77 | for (i = 0; file.good() && !file.eof(); i++) { 78 | KittiPoint point; 79 | file.read((char *) &point.x, 3*sizeof(float)); 80 | file.read((char *) &point.intensity, sizeof(float)); 81 | cloud->push_back(point); 82 | } 83 | file.close(); 84 | } 85 | return cloud; 86 | } 87 | 88 | KittiPointCloud::Ptr KittiDataset::getTrackletPointCloud(KittiPointCloud::Ptr& pointCloud, const KittiTracklet& tracklet, int frameId) 89 | { 90 | int pose_number = frameId - tracklet.first_frame; 91 | Tracklets::tPose tpose = tracklet.poses.at(pose_number); 92 | 93 | Eigen::Vector4f minPoint(-tracklet.l / 2.0f, -tracklet.w / 2.0, -tracklet.h / 2.0, 1.0f); 94 | Eigen::Vector4f maxPoint( tracklet.l / 2.0f, tracklet.w / 2.0, tracklet.h / 2.0, 1.0f); 95 | Eigen::Vector3f boxTranslation((float) tpose.tx, (float) tpose.ty, (float) tpose.tz + tracklet.h / 2.0f); 96 | Eigen::Vector3f boxRotation((float) tpose.rx, (float) tpose.ry, (float) tpose.rz); 97 | 98 | KittiPointCloud::Ptr trackletPointCloud(new KittiPointCloud()); 99 | pcl::CropBox cropFilter; 100 | cropFilter.setInputCloud(pointCloud); 101 | cropFilter.setMin(minPoint); 102 | cropFilter.setMax(maxPoint); 103 | cropFilter.setTranslation(boxTranslation); 104 | cropFilter.setRotation(boxRotation); 105 | cropFilter.filter(*trackletPointCloud); 106 | 107 | return trackletPointCloud; 108 | } 109 | 110 | Tracklets& KittiDataset::getTracklets() 111 | { 112 | return _tracklets; 113 | } 114 | 115 | int KittiDataset::getLabel(const char* labelString) 116 | { 117 | if (strcmp(labelString, "Car") == 0) 118 | { 119 | return 0; 120 | } 121 | if (strcmp(labelString, "Van") == 0) 122 | { 123 | return 1; 124 | } 125 | if (strcmp(labelString, "Truck") == 0) 126 | { 127 | return 2; 128 | } 129 | if (strcmp(labelString, "Pedestrian") == 0) 130 | { 131 | return 3; 132 | } 133 | if (strcmp(labelString, "Person (sitting)") == 0) 134 | { 135 | return 4; 136 | } 137 | if (strcmp(labelString, "Cyclist") == 0) 138 | { 139 | return 5; 140 | } 141 | if (strcmp(labelString, "Tram") == 0) 142 | { 143 | return 6; 144 | } 145 | if (strcmp(labelString, "Misc") == 0) 146 | { 147 | return 7; 148 | } 149 | std::cerr << "Error in KittiDataset:getLabel(): Not a valid label string: " 150 | << labelString << std::endl; 151 | return -1; 152 | } 153 | 154 | void KittiDataset::getColor(const char* labelString, int& r, int& g, int& b) 155 | { 156 | if (strcmp(labelString, "Car") == 0) 157 | { 158 | r = 255; g = 0; b = 0; 159 | return; 160 | } 161 | if (strcmp(labelString, "Van") == 0) 162 | { 163 | r = 191; g = 0; b = 0; 164 | return; 165 | } 166 | if (strcmp(labelString, "Truck") == 0) 167 | { 168 | r = 127; g = 0; b = 0; 169 | return; 170 | } 171 | if (strcmp(labelString, "Pedestrian") == 0) 172 | { 173 | r = 0; g = 255; b = 0; 174 | return; 175 | } 176 | if (strcmp(labelString, "Person (sitting)") == 0) 177 | { 178 | r = 0; g = 128; b = 0; 179 | return; 180 | } 181 | if (strcmp(labelString, "Cyclist") == 0) 182 | { 183 | r = 0; g = 0; b = 255; 184 | return; 185 | } 186 | if (strcmp(labelString, "Tram") == 0) 187 | { 188 | r = 0; g = 255; b = 255; 189 | return; 190 | } 191 | if (strcmp(labelString, "Misc") == 0) 192 | { 193 | r = 255; g = 255; b = 0; 194 | return; 195 | } 196 | std::cerr << "Error in KittiDataset:getColor(): Not a valid label string: " 197 | << labelString << std::endl; 198 | r = 128; g = 128; b = 128; 199 | } 200 | 201 | void KittiDataset::getColor(int label, int& r, int& g, int& b) 202 | { 203 | if (label == 0) 204 | { 205 | r = 255; g = 0; b = 0; 206 | return; 207 | } 208 | if (label == 1) 209 | { 210 | r = 191; g = 0; b = 0; 211 | return; 212 | } 213 | if (label == 2) 214 | { 215 | r = 127; g = 0; b = 0; 216 | return; 217 | } 218 | if (label == 3) 219 | { 220 | r = 0; g = 255; b = 0; 221 | return; 222 | } 223 | if (label == 4) 224 | { 225 | r = 0; g = 128; b = 0; 226 | return; 227 | } 228 | if (label == 5) 229 | { 230 | r = 0; g = 0; b = 255; 231 | return; 232 | } 233 | if (label == 6) 234 | { 235 | r = 0; g = 255; b = 255; 236 | return; 237 | } 238 | if (label == 7) 239 | { 240 | r = 255; g = 255; b = 0; 241 | return; 242 | } 243 | std::cerr << "Error in KittiDataset:getColor(): Not a valid label: " 244 | << label << std::endl; 245 | r = 128; g = 128; b = 128; 246 | } 247 | 248 | std::string KittiDataset::getLabelString(int label) 249 | { 250 | if (label == 0) 251 | { 252 | return "Car"; 253 | } 254 | if (label == 1) 255 | { 256 | return "Van"; 257 | } 258 | if (label == 2) 259 | { 260 | return "Truck"; 261 | } 262 | if (label == 3) 263 | { 264 | return "Pedestrian"; 265 | } 266 | if (label == 4) 267 | { 268 | return "Person (sitting)"; 269 | } 270 | if (label == 5) 271 | { 272 | return "Cyclist"; 273 | } 274 | if (label == 6) 275 | { 276 | return "Tram"; 277 | } 278 | if (label == 7) 279 | { 280 | return "Misc"; 281 | } 282 | std::cerr << "Error in KittiDataset:getLabelString(): Not a valid label: " 283 | << label << std::endl; 284 | return "Unknown"; 285 | } 286 | 287 | void KittiDataset::initNumberOfFrames() 288 | { 289 | boost::filesystem::directory_iterator dit(KittiConfig::getPointCloudPath(_dataset)); 290 | boost::filesystem::directory_iterator eit; 291 | 292 | while(dit != eit) 293 | { 294 | if(boost::filesystem::is_regular_file(*dit) && dit->path().extension() == ".bin") 295 | { 296 | _number_of_frames++; 297 | } 298 | ++dit; 299 | } 300 | } 301 | 302 | void KittiDataset::initTracklets() 303 | { 304 | boost::filesystem::path trackletsPath = KittiConfig::getTrackletsPath(_dataset); 305 | _tracklets.loadFromFile(trackletsPath.string()); 306 | } 307 | 308 | //#undef DEBUG_OUTPUT_ENABLED 309 | -------------------------------------------------------------------------------- /KittiDataset.h: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright 2016 Mark Muth 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | 17 | */ 18 | 19 | #ifndef KITTIDATASET_H 20 | #define KITTIDATASET_H 21 | 22 | #include 23 | 24 | #include 25 | #include 26 | 27 | #include 28 | #include 29 | 30 | #include "KittiConfig.h" 31 | 32 | #include "kitti-devkit-raw/tracklets.h" 33 | 34 | typedef pcl::PointXYZI KittiPoint; 35 | typedef pcl::PointCloud KittiPointCloud; 36 | typedef Tracklets::tTracklet KittiTracklet; 37 | 38 | class KittiDataset 39 | { 40 | 41 | public: 42 | 43 | KittiDataset(); 44 | KittiDataset(int dataset); 45 | int getNumberOfFrames(); 46 | KittiPointCloud::Ptr getPointCloud(int frameId); 47 | KittiPointCloud::Ptr getTrackletPointCloud(KittiPointCloud::Ptr& pointCloud, const KittiTracklet& tracklet, int frameId); 48 | Tracklets& getTracklets(); 49 | 50 | static int getLabel(const char* labelString); 51 | static void getColor(const char* labelString, int& r, int& g, int& b); 52 | static void getColor(int label, int& r, int& g, int& b); 53 | static std::string getLabelString(int label); 54 | 55 | private: 56 | 57 | int _dataset; 58 | int _number_of_frames; 59 | /** Counts the number of files with an ".bin" extension in the point cloud path */ 60 | void initNumberOfFrames(); 61 | 62 | Tracklets _tracklets; 63 | void initTracklets(); 64 | }; 65 | 66 | #endif // KITTIDATASET_H 67 | -------------------------------------------------------------------------------- /QtKittiVisualizer.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright 2016 Mark Muth 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | 17 | */ 18 | 19 | #include "QtKittiVisualizer.h" 20 | #include "ui_QtKittiVisualizer.h" 21 | 22 | #include 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | #include 31 | #include 32 | 33 | #include 34 | 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | 42 | #include 43 | #include 44 | #include 45 | 46 | #include 47 | #include 48 | 49 | #include 50 | 51 | typedef pcl::visualization::PointCloudColorHandlerCustom KittiPointCloudColorHandlerCustom; 52 | 53 | KittiVisualizerQt::KittiVisualizerQt(QWidget *parent, int argc, char** argv) 54 | : QMainWindow(parent) 55 | , ui(new Ui::KittiVisualizerQt) 56 | , dataset_index(0) 57 | , frame_index(0) 58 | , tracklet_index(0) 59 | , pclVisualizer(nullptr) 60 | , pointCloudVisible(true) 61 | , trackletBoundingBoxesVisible(true) 62 | , trackletPointsVisible(true) 63 | , trackletInCenterVisible(true) 64 | { 65 | int invalidOptions = parseCommandLineOptions(argc, argv); 66 | if (invalidOptions) 67 | { 68 | exit(invalidOptions); 69 | } 70 | 71 | // Set up user interface 72 | ui->setupUi(this); 73 | 74 | vtkNew renderWindow; 75 | vtkNew renderer; 76 | renderWindow->AddRenderer(renderer); 77 | 78 | pclVisualizer.reset( 79 | new pcl::visualization::PCLVisualizer( 80 | renderer, renderWindow, 81 | "pclVisualizer", false)); 82 | 83 | ui->qvtkWidget_pclViewer->setRenderWindow( 84 | pclVisualizer->getRenderWindow()); 85 | pclVisualizer->setupInteractor( 86 | ui->qvtkWidget_pclViewer->GetInteractor(), 87 | ui->qvtkWidget_pclViewer->GetRenderWindow()); 88 | pclVisualizer->setBackgroundColor(0, 0, 0); 89 | pclVisualizer->addCoordinateSystem(1.0); 90 | pclVisualizer->registerKeyboardCallback( 91 | &KittiVisualizerQt::keyboardEventOccurred, *this, 0); 92 | this->setWindowTitle("Qt KITTI Visualizer"); 93 | ui->qvtkWidget_pclViewer->update(); 94 | 95 | // Init the viewer with the first point cloud and corresponding tracklets 96 | dataset = new KittiDataset(KittiConfig::availableDatasets.at(dataset_index)); 97 | loadPointCloud(); 98 | if (pointCloudVisible) 99 | showPointCloud(); 100 | loadAvailableTracklets(); 101 | if (trackletBoundingBoxesVisible) 102 | showTrackletBoxes(); 103 | loadTrackletPoints(); 104 | if (trackletPointsVisible) 105 | showTrackletPoints(); 106 | if (trackletInCenterVisible) 107 | showTrackletInCenter(); 108 | 109 | ui->slider_dataSet->setRange(0, KittiConfig::availableDatasets.size() - 1); 110 | ui->slider_dataSet->setValue(dataset_index); 111 | ui->slider_frame->setRange(0, dataset->getNumberOfFrames() - 1); 112 | ui->slider_frame->setValue(frame_index); 113 | if (availableTracklets.size() != 0) 114 | ui->slider_tracklet->setRange(0, availableTracklets.size() - 1); 115 | else 116 | ui->slider_tracklet->setRange(0, 0); 117 | ui->slider_tracklet->setValue(tracklet_index); 118 | 119 | updateDatasetLabel(); 120 | updateFrameLabel(); 121 | updateTrackletLabel(); 122 | 123 | // Connect signals and slots 124 | connect(ui->slider_dataSet, SIGNAL (valueChanged(int)), this, SLOT (newDatasetRequested(int))); 125 | connect(ui->slider_frame, SIGNAL (valueChanged(int)), this, SLOT (newFrameRequested(int))); 126 | connect(ui->slider_tracklet, SIGNAL (valueChanged(int)), this, SLOT (newTrackletRequested(int))); 127 | connect(ui->checkBox_showFramePointCloud, SIGNAL (toggled(bool)), this, SLOT (showFramePointCloudToggled(bool))); 128 | connect(ui->checkBox_showTrackletBoundingBoxes, SIGNAL (toggled(bool)), this, SLOT (showTrackletBoundingBoxesToggled(bool))); 129 | connect(ui->checkBox_showTrackletPointClouds, SIGNAL (toggled(bool)), this, SLOT (showTrackletPointCloudsToggled(bool))); 130 | connect(ui->checkBox_showTrackletInCenter, SIGNAL (toggled(bool)), this, SLOT (showTrackletInCenterToggled(bool))); 131 | } 132 | 133 | KittiVisualizerQt::~KittiVisualizerQt() 134 | { 135 | delete dataset; 136 | delete ui; 137 | } 138 | 139 | int KittiVisualizerQt::parseCommandLineOptions(int argc, char** argv) 140 | { 141 | // Declare the supported options. 142 | boost::program_options::options_description desc("Program options"); 143 | desc.add_options() 144 | ("help", "Produce this help message.") 145 | ("dataset", boost::program_options::value(), "Set the number of the KITTI data set to be used.") 146 | ; 147 | 148 | boost::program_options::variables_map vm; 149 | boost::program_options::store(boost::program_options::parse_command_line(argc, argv, desc), vm); 150 | boost::program_options::notify(vm); 151 | 152 | if (vm.count("help")) { 153 | std::cout << desc << std::endl; 154 | return 1; 155 | } 156 | 157 | if (vm.count("dataset")) { 158 | dataset_index = vm["dataset"].as(); 159 | std::cout << "Using data set " << dataset_index << "." << std::endl; 160 | dataset_index = KittiConfig::getDatasetIndex(dataset_index); 161 | } else { 162 | dataset_index = 0; 163 | std::cout << "Data set was not specified." << std::endl; 164 | std::cout << "Using data set " << KittiConfig::getDatasetNumber(dataset_index) << "." << std::endl; 165 | } 166 | return 0; 167 | } 168 | 169 | bool KittiVisualizerQt::loadNextFrame() 170 | { 171 | newFrameRequested(frame_index + 1); 172 | return false; 173 | } 174 | 175 | bool KittiVisualizerQt::loadPreviousFrame() 176 | { 177 | newFrameRequested(frame_index - 1); 178 | return false; 179 | } 180 | 181 | void KittiVisualizerQt::getTrackletColor(const KittiTracklet& tracklet, int &r, int& g, int& b) 182 | { 183 | KittiDataset::getColor(tracklet.objectType.c_str(), r, g, b); 184 | } 185 | 186 | void KittiVisualizerQt::showFramePointCloudToggled(bool value) 187 | { 188 | pointCloudVisible = value; 189 | if (pointCloudVisible) 190 | { 191 | showPointCloud(); 192 | } 193 | else 194 | { 195 | hidePointCloud(); 196 | } 197 | ui->qvtkWidget_pclViewer->update(); 198 | } 199 | 200 | void KittiVisualizerQt::newDatasetRequested(int value) 201 | { 202 | if (dataset_index == value) 203 | return; 204 | 205 | if (trackletInCenterVisible) 206 | hideTrackletInCenter(); 207 | if (trackletPointsVisible) 208 | hideTrackletPoints(); 209 | clearTrackletPoints(); 210 | if (trackletBoundingBoxesVisible) 211 | hideTrackletBoxes(); 212 | clearAvailableTracklets(); 213 | if (pointCloudVisible) 214 | hidePointCloud(); 215 | 216 | dataset_index = value; 217 | if (dataset_index >= KittiConfig::availableDatasets.size()) 218 | dataset_index = KittiConfig::availableDatasets.size() - 1; 219 | if (dataset_index < 0) 220 | dataset_index = 0; 221 | 222 | delete dataset; 223 | dataset = new KittiDataset(KittiConfig::availableDatasets.at(dataset_index)); 224 | 225 | if (frame_index >= dataset->getNumberOfFrames()) 226 | frame_index = dataset->getNumberOfFrames() - 1; 227 | 228 | loadPointCloud(); 229 | if (pointCloudVisible) 230 | showPointCloud(); 231 | loadAvailableTracklets(); 232 | if (trackletBoundingBoxesVisible) 233 | showTrackletBoxes(); 234 | loadTrackletPoints(); 235 | if (trackletPointsVisible) 236 | showTrackletPoints(); 237 | if (tracklet_index >= availableTracklets.size()) 238 | tracklet_index = availableTracklets.size() - 1; 239 | if (tracklet_index < 0) 240 | tracklet_index = 0; 241 | if (trackletInCenterVisible) 242 | showTrackletInCenter(); 243 | 244 | ui->slider_frame->setRange(0, dataset->getNumberOfFrames() - 1); 245 | ui->slider_frame->setValue(frame_index); 246 | if (availableTracklets.size() != 0) 247 | ui->slider_tracklet->setRange(0, availableTracklets.size() - 1); 248 | else 249 | ui->slider_tracklet->setRange(0, 0); 250 | ui->slider_tracklet->setValue(tracklet_index); 251 | 252 | updateDatasetLabel(); 253 | updateFrameLabel(); 254 | updateTrackletLabel(); 255 | ui->qvtkWidget_pclViewer->update(); 256 | } 257 | 258 | void KittiVisualizerQt::newFrameRequested(int value) 259 | { 260 | if (frame_index == value) 261 | return; 262 | 263 | if (trackletInCenterVisible) 264 | hideTrackletInCenter(); 265 | if (trackletPointsVisible) 266 | hideTrackletPoints(); 267 | clearTrackletPoints(); 268 | if (trackletBoundingBoxesVisible) 269 | hideTrackletBoxes(); 270 | clearAvailableTracklets(); 271 | if (pointCloudVisible) 272 | hidePointCloud(); 273 | 274 | frame_index = value; 275 | if (frame_index >= dataset->getNumberOfFrames()) 276 | frame_index = dataset->getNumberOfFrames() - 1; 277 | if (frame_index < 0) 278 | frame_index = 0; 279 | 280 | loadPointCloud(); 281 | if (pointCloudVisible) 282 | showPointCloud(); 283 | loadAvailableTracklets(); 284 | if (trackletBoundingBoxesVisible) 285 | showTrackletBoxes(); 286 | loadTrackletPoints(); 287 | if (trackletPointsVisible) 288 | showTrackletPoints(); 289 | if (tracklet_index >= availableTracklets.size()) 290 | tracklet_index = availableTracklets.size() - 1; 291 | if (tracklet_index < 0) 292 | tracklet_index = 0; 293 | if (trackletInCenterVisible) 294 | showTrackletInCenter(); 295 | 296 | if (availableTracklets.size() != 0) 297 | ui->slider_tracklet->setRange(0, availableTracklets.size() - 1); 298 | else 299 | ui->slider_tracklet->setRange(0, 0); 300 | ui->slider_tracklet->setValue(tracklet_index); 301 | 302 | updateFrameLabel(); 303 | updateTrackletLabel(); 304 | ui->qvtkWidget_pclViewer->update(); 305 | } 306 | 307 | void KittiVisualizerQt::newTrackletRequested(int value) 308 | { 309 | if (tracklet_index == value) 310 | return; 311 | 312 | if (trackletInCenterVisible) 313 | hideTrackletInCenter(); 314 | 315 | tracklet_index = value; 316 | if (tracklet_index >= availableTracklets.size()) 317 | tracklet_index = availableTracklets.size() - 1; 318 | if (tracklet_index < 0) 319 | tracklet_index = 0; 320 | if (trackletInCenterVisible) 321 | showTrackletInCenter(); 322 | 323 | updateTrackletLabel(); 324 | ui->qvtkWidget_pclViewer->update(); 325 | } 326 | 327 | void KittiVisualizerQt::showTrackletBoundingBoxesToggled(bool value) 328 | { 329 | trackletBoundingBoxesVisible = value; 330 | if (trackletBoundingBoxesVisible) 331 | { 332 | showTrackletBoxes(); 333 | } 334 | else 335 | { 336 | hideTrackletBoxes(); 337 | } 338 | ui->qvtkWidget_pclViewer->update(); 339 | } 340 | 341 | void KittiVisualizerQt::showTrackletPointCloudsToggled(bool value) 342 | { 343 | trackletPointsVisible = value; 344 | if (trackletPointsVisible) 345 | { 346 | showTrackletPoints(); 347 | } 348 | else 349 | { 350 | hideTrackletPoints(); 351 | } 352 | ui->qvtkWidget_pclViewer->update(); 353 | } 354 | 355 | void KittiVisualizerQt::showTrackletInCenterToggled(bool value) 356 | { 357 | trackletInCenterVisible = value; 358 | if (trackletInCenterVisible) 359 | { 360 | showTrackletInCenter(); 361 | } 362 | else 363 | { 364 | hideTrackletInCenter(); 365 | } 366 | ui->qvtkWidget_pclViewer->update(); 367 | } 368 | 369 | void KittiVisualizerQt::loadPointCloud() 370 | { 371 | pointCloud = dataset->getPointCloud(frame_index); 372 | } 373 | 374 | void KittiVisualizerQt::showPointCloud() 375 | { 376 | KittiPointCloudColorHandlerCustom colorHandler(pointCloud, 255, 255, 255); 377 | pclVisualizer->addPointCloud(pointCloud, colorHandler, "point_cloud"); 378 | } 379 | 380 | void KittiVisualizerQt::hidePointCloud() 381 | { 382 | pclVisualizer->removePointCloud("point_cloud"); 383 | } 384 | 385 | void KittiVisualizerQt::loadAvailableTracklets() 386 | { 387 | Tracklets& tracklets = dataset->getTracklets(); 388 | int tracklet_id; 389 | int number_of_tracklets = tracklets.numberOfTracklets(); 390 | for (tracklet_id = 0; tracklet_id < number_of_tracklets; tracklet_id++) 391 | { 392 | KittiTracklet* tracklet = tracklets.getTracklet(tracklet_id); 393 | if (tracklet->first_frame <= frame_index && tracklet->lastFrame() >= frame_index) 394 | { 395 | availableTracklets.push_back(*tracklet); 396 | } 397 | } 398 | } 399 | 400 | void KittiVisualizerQt::clearAvailableTracklets() 401 | { 402 | availableTracklets.clear(); 403 | } 404 | 405 | void KittiVisualizerQt::updateDatasetLabel() 406 | { 407 | std::stringstream text; 408 | text << "Data set: " 409 | << dataset_index + 1 << " of " << KittiConfig::availableDatasets.size() 410 | << " [" << KittiConfig::getDatasetNumber(dataset_index) << "]" 411 | << std::endl; 412 | ui->label_dataSet->setText(text.str().c_str()); 413 | } 414 | 415 | void KittiVisualizerQt::updateFrameLabel() 416 | { 417 | std::stringstream text; 418 | text << "Frame: " 419 | << frame_index + 1 << " of " << dataset->getNumberOfFrames() 420 | << std::endl; 421 | ui->label_frame->setText(text.str().c_str()); 422 | 423 | } 424 | 425 | void KittiVisualizerQt::updateTrackletLabel() 426 | { 427 | if (availableTracklets.size()) 428 | { 429 | std::stringstream text; 430 | KittiTracklet tracklet = availableTracklets.at(tracklet_index); 431 | text << "Tracklet: " 432 | << tracklet_index + 1 << " of " << availableTracklets.size() 433 | << " (\"" << tracklet.objectType 434 | << "\", " << croppedTrackletPointClouds.at(tracklet_index).get()->size() 435 | << " points)" 436 | << std::endl; 437 | ui->label_tracklet->setText(text.str().c_str()); 438 | } 439 | else 440 | { 441 | std::stringstream text; 442 | text << "Tracklet: " 443 | << "0 of 0" 444 | << std::endl; 445 | ui->label_tracklet->setText(text.str().c_str()); 446 | } 447 | } 448 | 449 | void KittiVisualizerQt::showTrackletBoxes() 450 | { 451 | double boxHeight = 0.0d; 452 | double boxWidth = 0.0d; 453 | double boxLength = 0.0d; 454 | int pose_number = 0; 455 | 456 | for (int i = 0; i < availableTracklets.size(); ++i) 457 | { 458 | // Create the bounding box 459 | const KittiTracklet& tracklet = availableTracklets.at(i); 460 | 461 | boxHeight = tracklet.h; 462 | boxWidth = tracklet.w; 463 | boxLength = tracklet.l; 464 | pose_number = frame_index - tracklet.first_frame; 465 | const Tracklets::tPose& tpose = tracklet.poses.at(pose_number); 466 | Eigen::Vector3f boxTranslation; 467 | boxTranslation[0] = (float) tpose.tx; 468 | boxTranslation[1] = (float) tpose.ty; 469 | boxTranslation[2] = (float) tpose.tz + (float) boxHeight / 2.0f; 470 | Eigen::Quaternionf boxRotation = Eigen::Quaternionf(Eigen::AngleAxisf((float) tpose.rz, Eigen::Vector3f::UnitZ())); 471 | 472 | // Add the bounding box to the visualizer 473 | std::string viewer_id = "tracklet_box_" + i; 474 | pclVisualizer->addCube(boxTranslation, boxRotation, boxLength, boxWidth, boxHeight, viewer_id); 475 | } 476 | } 477 | 478 | void KittiVisualizerQt::hideTrackletBoxes() 479 | { 480 | for (int i = 0; i < availableTracklets.size(); ++i) 481 | { 482 | std::string viewer_id = "tracklet_box_" + i; 483 | pclVisualizer->removeShape(viewer_id); 484 | } 485 | } 486 | 487 | void KittiVisualizerQt::loadTrackletPoints() 488 | { 489 | for (int i = 0; i < availableTracklets.size(); ++i) 490 | { 491 | // Create the tracklet point cloud 492 | const KittiTracklet& tracklet = availableTracklets.at(i); 493 | pcl::PointCloud::Ptr trackletPointCloud = dataset->getTrackletPointCloud(pointCloud, tracklet, frame_index); 494 | pcl::PointCloud::Ptr trackletPointCloudTransformed(new pcl::PointCloud); 495 | 496 | Eigen::Vector3f transformOffset; 497 | transformOffset[0] = 0.0f; 498 | transformOffset[1] = 0.0f; 499 | transformOffset[2] = 6.0f; 500 | pcl::transformPointCloud(*trackletPointCloud, *trackletPointCloudTransformed, transformOffset, Eigen::Quaternionf::Identity()); 501 | 502 | // Store the tracklet point cloud 503 | croppedTrackletPointClouds.push_back(trackletPointCloudTransformed); 504 | } 505 | } 506 | 507 | void KittiVisualizerQt::showTrackletPoints() 508 | { 509 | for (int i = 0; i < availableTracklets.size(); ++i) 510 | { 511 | // Create a color handler for the tracklet point cloud 512 | const KittiTracklet& tracklet = availableTracklets.at(i); 513 | int r, g, b; 514 | getTrackletColor(tracklet, r, g, b); 515 | KittiPointCloudColorHandlerCustom colorHandler(croppedTrackletPointClouds.at(i), r, g, b); 516 | 517 | // Add tracklet point cloud to the visualizer 518 | std::string viewer_id = "cropped_tracklet_" + i; 519 | pclVisualizer->addPointCloud(croppedTrackletPointClouds.at(i), colorHandler, viewer_id); 520 | } 521 | } 522 | 523 | void KittiVisualizerQt::hideTrackletPoints() 524 | { 525 | for (int i = 0; i < availableTracklets.size(); ++i) 526 | { 527 | std::string viewer_id = "cropped_tracklet_" + i; 528 | pclVisualizer->removeShape(viewer_id); 529 | } 530 | } 531 | 532 | void KittiVisualizerQt::clearTrackletPoints() 533 | { 534 | croppedTrackletPointClouds.clear(); 535 | } 536 | 537 | void KittiVisualizerQt::showTrackletInCenter() 538 | { 539 | if (availableTracklets.size()) 540 | { 541 | // Create the centered tracklet point cloud 542 | const KittiTracklet& tracklet = availableTracklets.at(tracklet_index); 543 | pcl::PointCloud::Ptr cloudOut = dataset->getTrackletPointCloud(pointCloud, tracklet, frame_index); 544 | pcl::PointCloud::Ptr cloudOutTransformed(new pcl::PointCloud); 545 | 546 | int pose_number = frame_index - tracklet.first_frame; 547 | Tracklets::tPose tpose = tracklet.poses.at(pose_number); 548 | 549 | Eigen::Vector3f transformOffset((float) -(tpose.tx), (float) -(tpose.ty), (float) -(tpose.tz + tracklet.h / 2.0)); 550 | 551 | Eigen::AngleAxisf angleAxisZ(-tpose.rz, Eigen::Vector3f::UnitZ()); 552 | Eigen::Quaternionf transformRotation(angleAxisZ); 553 | pcl::transformPointCloud(*cloudOut, *cloudOutTransformed, transformOffset, Eigen::Quaternionf::Identity()); 554 | pcl::transformPointCloud(*cloudOutTransformed, *cloudOut, Eigen::Vector3f::Zero(), transformRotation); 555 | 556 | // Create color handler for the centered tracklet point cloud 557 | KittiPointCloudColorHandlerCustom colorHandler(cloudOut, 0, 255, 0); 558 | 559 | // Add the centered tracklet point cloud to the visualizer 560 | pclVisualizer->addPointCloud(cloudOut, colorHandler, "centered_tracklet"); 561 | } 562 | } 563 | 564 | void KittiVisualizerQt::hideTrackletInCenter() 565 | { 566 | if (availableTracklets.size()) 567 | { 568 | pclVisualizer->removeShape("centered_tracklet"); 569 | } 570 | } 571 | 572 | void KittiVisualizerQt::setFrameNumber(int frameNumber) 573 | { 574 | frame_index = frameNumber; 575 | } 576 | 577 | void KittiVisualizerQt::keyboardEventOccurred (const pcl::visualization::KeyboardEvent &event, 578 | void* viewer_void) 579 | { 580 | if (event.getKeyCode() == 0 && event.keyDown()) 581 | { 582 | if (event.getKeySym() == "Left") 583 | { 584 | loadPreviousFrame(); 585 | } 586 | else if (event.getKeySym() == "Right") 587 | { 588 | loadNextFrame(); 589 | } 590 | } 591 | } 592 | -------------------------------------------------------------------------------- /QtKittiVisualizer.h: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright 2016 Mark Muth 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | 17 | */ 18 | 19 | #ifndef QT_KITTI_VISUALIZER_H 20 | #define QT_KITTI_VISUALIZER_H 21 | 22 | #include 23 | // Qt 24 | #include 25 | #include 26 | 27 | // Boost 28 | #include 29 | 30 | // PCL 31 | #include 32 | #include 33 | 34 | // VTK 35 | #include 36 | 37 | #include "KittiDataset.h" 38 | 39 | #include 40 | 41 | namespace Ui 42 | { 43 | class KittiVisualizerQt; 44 | } 45 | 46 | class KittiVisualizerQt : public QMainWindow 47 | { 48 | Q_OBJECT 49 | 50 | public: 51 | 52 | KittiVisualizerQt(QWidget *parent, int argc, char** argv); 53 | virtual ~KittiVisualizerQt(); 54 | 55 | bool loadDataset(); 56 | bool loadNextFrame(); 57 | bool loadPreviousFrame(); 58 | 59 | void getTrackletColor(const KittiTracklet& tracklet, int &r, int& g, int& b); 60 | 61 | public slots: 62 | 63 | void newDatasetRequested(int value); 64 | void newFrameRequested(int value); 65 | void newTrackletRequested(int value); 66 | 67 | void showFramePointCloudToggled(bool value); 68 | void showTrackletBoundingBoxesToggled(bool value); 69 | void showTrackletPointCloudsToggled(bool value); 70 | void showTrackletInCenterToggled(bool value); 71 | 72 | private: 73 | 74 | int parseCommandLineOptions(int argc, char** argv); 75 | 76 | int dataset_index; 77 | KittiDataset* dataset; 78 | 79 | int frame_index; 80 | 81 | int tracklet_index; 82 | 83 | pcl::visualization::PCLVisualizer::Ptr pclVisualizer; 84 | 85 | void loadAvailableTracklets(); 86 | void clearAvailableTracklets(); 87 | std::vector availableTracklets; 88 | 89 | void updateDatasetLabel(); 90 | void updateFrameLabel(); 91 | void updateTrackletLabel(); 92 | 93 | void loadPointCloud(); 94 | void showPointCloud(); 95 | void hidePointCloud(); 96 | bool pointCloudVisible; 97 | KittiPointCloud::Ptr pointCloud; 98 | 99 | void showTrackletBoxes(); 100 | void hideTrackletBoxes(); 101 | bool trackletBoundingBoxesVisible; 102 | 103 | void loadTrackletPoints(); 104 | void showTrackletPoints(); 105 | void hideTrackletPoints(); 106 | void clearTrackletPoints(); 107 | bool trackletPointsVisible; 108 | std::vector croppedTrackletPointClouds; 109 | 110 | void showTrackletInCenter(); 111 | void hideTrackletInCenter(); 112 | bool trackletInCenterVisible; 113 | 114 | void setFrameNumber(int frameNumber); 115 | 116 | void keyboardEventOccurred (const pcl::visualization::KeyboardEvent &event, 117 | void* viewer_void); 118 | 119 | Ui::KittiVisualizerQt *ui; 120 | }; 121 | 122 | #endif // QT_KITTI_VISUALIZER_H 123 | -------------------------------------------------------------------------------- /QtKittiVisualizer.ui: -------------------------------------------------------------------------------- 1 | 2 | 19 | 20 | KittiVisualizerQt 21 | 22 | 23 | 24 | 0 25 | 0 26 | 989 27 | 503 28 | 29 | 30 | 31 | 32 | 0 33 | 0 34 | 35 | 36 | 37 | 38 | 5000 39 | 5000 40 | 41 | 42 | 43 | PCLViewer 44 | 45 | 46 | 47 | 48 | 49 | 50 | Qt::Horizontal 51 | 52 | 53 | 54 | Qt::Vertical 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | Data set: 64 | 65 | 66 | 67 | 68 | 69 | 70 | Qt::Horizontal 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | Frame: 82 | 83 | 84 | 85 | 86 | 87 | 88 | Qt::Horizontal 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | Tracklet: 100 | 101 | 102 | 103 | 104 | 105 | 106 | Qt::Horizontal 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | true 120 | 121 | 122 | Show frame point cloud 123 | 124 | 125 | true 126 | 127 | 128 | 129 | 130 | 131 | 132 | Show tracklet bounding boxes 133 | 134 | 135 | true 136 | 137 | 138 | 139 | 140 | 141 | 142 | Show tracklet point clouds 143 | 144 | 145 | true 146 | 147 | 148 | 149 | 150 | 151 | 152 | Show tracklet in center 153 | 154 | 155 | true 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 0 166 | 0 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 0 179 | 0 180 | 989 181 | 25 182 | 183 | 184 | 185 | 186 | File 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | Exit 195 | 196 | 197 | Ctrl+Q 198 | 199 | 200 | 201 | 202 | 203 | QVTKOpenGLStereoWidget 204 | QWidget 205 |
QVTKOpenGLStereoWidget.h
206 | 1 207 |
208 |
209 | 210 | slider_dataSet 211 | slider_frame 212 | slider_tracklet 213 | checkBox_showFramePointCloud 214 | checkBox_showTrackletBoundingBoxes 215 | checkBox_showTrackletPointClouds 216 | checkBox_showTrackletInCenter 217 | 218 | 219 | 220 |
221 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | QtKittiVisualizer 2 | ================= 3 | 4 | Description 5 | ----------- 6 | 7 | The **QtKittiVisualizer** allows viewing the 3D point clouds and bounding boxes of the [raw KITTI data sets](http://www.cvlibs.net/datasets/kitti/raw_data.php). It is a tool based on *Qt*, the *PCL* and the *VTK*. 8 | 9 | It includes the *C++* part of the [raw data development kit](http://kitti.is.tue.mpg.de/kitti/devkit_raw_data.zip) provided on the [official KITTI website](http://www.cvlibs.net/datasets/kitti/). 10 | 11 | Tested Versions 12 | --------------- 13 | 14 | Ubuntu 20.04 with 15 | - CMake (3.16.3) 16 | - Qt (5.12.8) 17 | - VTK (9.1.0) 18 | - PCL (1.12.1) 19 | 20 | Build Instructions 21 | ------------------ 22 | 23 | Can now be found in the `documentation/build-instructions` directory. 24 | 25 | Screenshot 26 | ---------- 27 | 28 | A screen shot of the current version can be found in the `documentation/screenshots` directory. 29 | 30 | License 31 | ------- 32 | 33 | The new parts of the software are licensed under the [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0). -------------------------------------------------------------------------------- /cmake/modules/FindEigen.cmake: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Find Eigen3 3 | # 4 | # This sets the following variables: 5 | # EIGEN_FOUND - True if Eigen was found. 6 | # EIGEN_INCLUDE_DIRS - Directories containing the Eigen include files. 7 | # EIGEN_DEFINITIONS - Compiler flags for Eigen. 8 | # EIGEN_VERSION - Package version 9 | 10 | find_package(PkgConfig QUIET) 11 | pkg_check_modules(PC_EIGEN eigen3) 12 | set(EIGEN_DEFINITIONS ${PC_EIGEN_CFLAGS_OTHER}) 13 | 14 | find_path(EIGEN_INCLUDE_DIR Eigen/Core 15 | HINTS "${EIGEN_ROOT}" "$ENV{EIGEN_ROOT}" ${PC_EIGEN_INCLUDEDIR} ${PC_EIGEN_INCLUDE_DIRS} 16 | PATHS "$ENV{PROGRAMFILES}/Eigen" "$ENV{PROGRAMW6432}/Eigen" 17 | "$ENV{PROGRAMFILES}/Eigen3" "$ENV{PROGRAMW6432}/Eigen3" 18 | PATH_SUFFIXES eigen3 include/eigen3 include) 19 | 20 | if(EIGEN_INCLUDE_DIR) 21 | file(READ "${EIGEN_INCLUDE_DIR}/Eigen/src/Core/util/Macros.h" _eigen_version_header) 22 | 23 | string(REGEX MATCH "define[ \t]+EIGEN_WORLD_VERSION[ \t]+([0-9]+)" _eigen_world_version_match "${_eigen_version_header}") 24 | set(EIGEN_WORLD_VERSION "${CMAKE_MATCH_1}") 25 | string(REGEX MATCH "define[ \t]+EIGEN_MAJOR_VERSION[ \t]+([0-9]+)" _eigen_major_version_match "${_eigen_version_header}") 26 | set(EIGEN_MAJOR_VERSION "${CMAKE_MATCH_1}") 27 | string(REGEX MATCH "define[ \t]+EIGEN_MINOR_VERSION[ \t]+([0-9]+)" _eigen_minor_version_match "${_eigen_version_header}") 28 | set(EIGEN_MINOR_VERSION "${CMAKE_MATCH_1}") 29 | set(EIGEN_VERSION ${EIGEN_WORLD_VERSION}.${EIGEN_MAJOR_VERSION}.${EIGEN_MINOR_VERSION}) 30 | endif() 31 | 32 | set(EIGEN_INCLUDE_DIRS ${EIGEN_INCLUDE_DIR}) 33 | 34 | include(FindPackageHandleStandardArgs) 35 | find_package_handle_standard_args(Eigen DEFAULT_MSG EIGEN_INCLUDE_DIR) 36 | 37 | mark_as_advanced(EIGEN_INCLUDE_DIR) 38 | 39 | if(EIGEN_FOUND) 40 | message(STATUS "Eigen found (include: ${EIGEN_INCLUDE_DIRS}, version: ${EIGEN_VERSION})") 41 | endif() 42 | -------------------------------------------------------------------------------- /cmake/modules/FindFLANN.cmake: -------------------------------------------------------------------------------- 1 | #.rst: 2 | # FindFLANN 3 | # -------- 4 | # 5 | # Try to find FLANN library and headers. This module supports both old released versions 6 | # of FLANN ≤ 1.9.1 and newer development versions that ship with a modern config file. 7 | # 8 | # IMPORTED Targets 9 | # ^^^^^^^^^^^^^^^^ 10 | # 11 | # This module defines the :prop_tgt:`IMPORTED` targets: 12 | # 13 | # ``FLANN::FLANN`` 14 | # Defined if the system has FLANN. 15 | # 16 | # Result Variables 17 | # ^^^^^^^^^^^^^^^^ 18 | # 19 | # This module sets the following variables: 20 | # 21 | # :: 22 | # 23 | # FLANN_FOUND True in case FLANN is found, otherwise false 24 | # FLANN_ROOT Path to the root of found FLANN installation 25 | # 26 | # Example usage 27 | # ^^^^^^^^^^^^^ 28 | # 29 | # :: 30 | # 31 | # find_package(FLANN REQUIRED) 32 | # 33 | # add_executable(foo foo.cc) 34 | # target_link_libraries(foo FLANN::FLANN) 35 | # 36 | 37 | # Early return if FLANN target is already defined. This makes it safe to run 38 | # this script multiple times. 39 | if(TARGET FLANN::FLANN) 40 | return() 41 | endif() 42 | 43 | # First try to locate FLANN using modern config 44 | find_package(flann NO_MODULE ${FLANN_FIND_VERSION} QUIET) 45 | 46 | if(flann_FOUND) 47 | unset(flann_FOUND) 48 | set(FLANN_FOUND ON) 49 | 50 | # Create interface library that effectively becomes an alias for the appropriate (static/dynamic) imported FLANN target 51 | add_library(FLANN::FLANN INTERFACE IMPORTED) 52 | 53 | if(TARGET flann::flann_cpp_s AND TARGET flann::flann_cpp) 54 | if(PCL_FLANN_REQUIRED_TYPE MATCHES "SHARED") 55 | set_property(TARGET FLANN::FLANN APPEND PROPERTY INTERFACE_LINK_LIBRARIES flann::flann_cpp) 56 | set(FLANN_LIBRARY_TYPE SHARED) 57 | elseif(PCL_FLANN_REQUIRED_TYPE MATCHES "STATIC") 58 | set_property(TARGET FLANN::FLANN APPEND PROPERTY INTERFACE_LINK_LIBRARIES flann::flann_cpp_s) 59 | set(FLANN_LIBRARY_TYPE STATIC) 60 | else() 61 | if(PCL_SHARED_LIBS) 62 | set_property(TARGET FLANN::FLANN APPEND PROPERTY INTERFACE_LINK_LIBRARIES flann::flann_cpp) 63 | set(FLANN_LIBRARY_TYPE SHARED) 64 | else() 65 | set_property(TARGET FLANN::FLANN APPEND PROPERTY INTERFACE_LINK_LIBRARIES flann::flann_cpp_s) 66 | set(FLANN_LIBRARY_TYPE STATIC) 67 | endif() 68 | endif() 69 | elseif(TARGET flann::flann_cpp_s) 70 | set_property(TARGET FLANN::FLANN APPEND PROPERTY INTERFACE_LINK_LIBRARIES flann::flann_cpp_s) 71 | set(FLANN_LIBRARY_TYPE STATIC) 72 | else() 73 | set_property(TARGET FLANN::FLANN APPEND PROPERTY INTERFACE_LINK_LIBRARIES flann::flann_cpp) 74 | set(FLANN_LIBRARY_TYPE SHARED) 75 | endif() 76 | 77 | # Determine FLANN installation root based on the path to the processed Config file 78 | get_filename_component(_config_dir "${flann_CONFIG}" DIRECTORY) 79 | get_filename_component(FLANN_ROOT "${_config_dir}/../../.." ABSOLUTE) 80 | unset(_config_dir) 81 | message(STATUS "Found flann version ${flann_VERSION}") 82 | return() 83 | endif() 84 | 85 | # Second try to locate FLANN using pkgconfig 86 | find_package(PkgConfig QUIET) 87 | if(FLANN_FIND_VERSION) 88 | pkg_check_modules(PC_FLANN flann>=${FLANN_FIND_VERSION}) 89 | else() 90 | pkg_check_modules(PC_FLANN flann) 91 | endif() 92 | 93 | find_path(FLANN_INCLUDE_DIR 94 | NAMES 95 | flann/flann.hpp 96 | HINTS 97 | ${PC_FLANN_INCLUDE_DIRS} 98 | ${FLANN_ROOT} 99 | $ENV{FLANN_ROOT} 100 | PATHS 101 | $ENV{PROGRAMFILES}/Flann 102 | $ENV{PROGRAMW6432}/Flann 103 | PATH_SUFFIXES 104 | include 105 | ) 106 | 107 | find_library(FLANN_LIBRARY_SHARED 108 | NAMES 109 | flann_cpp 110 | HINTS 111 | ${PC_FLANN_LIBRARY_DIRS} 112 | ${FLANN_ROOT} 113 | $ENV{FLANN_ROOT} 114 | PATHS 115 | $ENV{PROGRAMFILES}/Flann 116 | $ENV{PROGRAMW6432}/Flann 117 | PATH_SUFFIXES 118 | lib 119 | ) 120 | 121 | find_library(FLANN_LIBRARY_DEBUG_SHARED 122 | NAMES 123 | flann_cpp-gd flann_cppd 124 | HINTS 125 | ${PC_FLANN_LIBRARY_DIRS} 126 | ${FLANN_ROOT} 127 | $ENV{FLANN_ROOT} 128 | PATHS 129 | $ENV{PROGRAMFILES}/Flann 130 | $ENV{PROGRAMW6432}/Flann 131 | PATH_SUFFIXES 132 | lib 133 | ) 134 | 135 | find_library(FLANN_LIBRARY_STATIC 136 | NAMES 137 | flann_cpp_s 138 | HINTS 139 | ${PC_FLANN_LIBRARY_DIRS} 140 | ${FLANN_ROOT} 141 | $ENV{FLANN_ROOT} 142 | PATHS 143 | $ENV{PROGRAMFILES}/Flann 144 | $ENV{PROGRAMW6432}/Flann 145 | PATH_SUFFIXES 146 | lib 147 | ) 148 | 149 | find_library(FLANN_LIBRARY_DEBUG_STATIC 150 | NAMES 151 | flann_cpp_s-gd flann_cpp_sd 152 | HINTS 153 | ${PC_FLANN_LIBRARY_DIRS} 154 | ${FLANN_ROOT} 155 | $ENV{FLANN_ROOT} 156 | PATHS 157 | $ENV{PROGRAMFILES}/Flann 158 | $ENV{PROGRAMW6432}/Flann 159 | PATH_SUFFIXES 160 | lib 161 | ) 162 | 163 | if(FLANN_LIBRARY_SHARED AND FLANN_LIBRARY_STATIC) 164 | if(PCL_FLANN_REQUIRED_TYPE MATCHES "SHARED") 165 | set(FLANN_LIBRARY_TYPE SHARED) 166 | set(FLANN_LIBRARY ${FLANN_LIBRARY_SHARED}) 167 | elseif(PCL_FLANN_REQUIRED_TYPE MATCHES "STATIC") 168 | set(FLANN_LIBRARY_TYPE STATIC) 169 | set(FLANN_LIBRARY ${FLANN_LIBRARY_STATIC}) 170 | else() 171 | if(PCL_SHARED_LIBS) 172 | set(FLANN_LIBRARY_TYPE SHARED) 173 | set(FLANN_LIBRARY ${FLANN_LIBRARY_SHARED}) 174 | else() 175 | set(FLANN_LIBRARY_TYPE STATIC) 176 | set(FLANN_LIBRARY ${FLANN_LIBRARY_STATIC}) 177 | endif() 178 | endif() 179 | elseif(FLANN_LIBRARY_STATIC) 180 | set(FLANN_LIBRARY_TYPE STATIC) 181 | set(FLANN_LIBRARY ${FLANN_LIBRARY_STATIC}) 182 | elseif(FLANN_LIBRARY_SHARED) 183 | set(FLANN_LIBRARY_TYPE SHARED) 184 | set(FLANN_LIBRARY ${FLANN_LIBRARY_SHARED}) 185 | endif() 186 | 187 | include(FindPackageHandleStandardArgs) 188 | find_package_handle_standard_args( 189 | FLANN DEFAULT_MSG 190 | FLANN_LIBRARY FLANN_INCLUDE_DIR 191 | ) 192 | 193 | if(FLANN_FOUND) 194 | add_library(FLANN::FLANN ${FLANN_LIBRARY_TYPE} IMPORTED) 195 | set_target_properties(FLANN::FLANN PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${FLANN_INCLUDE_DIR}") 196 | set_target_properties(FLANN::FLANN PROPERTIES INTERFACE_COMPILE_DEFINITIONS "${PC_FLANN_CFLAGS_OTHER}") 197 | set_property(TARGET FLANN::FLANN APPEND PROPERTY IMPORTED_CONFIGURATIONS "RELEASE") 198 | set_target_properties(FLANN::FLANN PROPERTIES IMPORTED_LINK_INTERFACE_LANGUAGES_RELEASE "CXX") 199 | if(WIN32 AND (NOT FLANN_LIBRARY_TYPE MATCHES "STATIC")) 200 | set_target_properties(FLANN::FLANN PROPERTIES IMPORTED_IMPLIB_RELEASE "${FLANN_LIBRARY}") 201 | else() 202 | set_target_properties(FLANN::FLANN PROPERTIES IMPORTED_LOCATION_RELEASE "${FLANN_LIBRARY}") 203 | endif() 204 | if(FLANN_LIBRARY_DEBUG) 205 | set_property(TARGET FLANN::FLANN APPEND PROPERTY IMPORTED_CONFIGURATIONS "DEBUG") 206 | set_target_properties(FLANN::FLANN PROPERTIES IMPORTED_LINK_INTERFACE_LANGUAGES_DEBUG "CXX") 207 | if(WIN32 AND (NOT FLANN_LIBRARY_TYPE MATCHES "STATIC")) 208 | set_target_properties(FLANN::FLANN PROPERTIES IMPORTED_IMPLIB_DEBUG "${FLANN_LIBRARY_DEBUG}") 209 | else() 210 | set_target_properties(FLANN::FLANN PROPERTIES IMPORTED_LOCATION_DEBUG "${FLANN_LIBRARY_DEBUG}") 211 | endif() 212 | endif() 213 | # Pkgconfig may specify additional link libraries besides from FLANN itself 214 | # in PC_FLANN_LIBRARIES, add them to the target link interface. 215 | foreach(_library ${PC_FLANN_LIBRARIES}) 216 | if(NOT _library MATCHES "flann") 217 | set_property(TARGET FLANN::FLANN APPEND PROPERTY INTERFACE_LINK_LIBRARIES "${_library}") 218 | endif() 219 | endforeach() 220 | get_filename_component(FLANN_ROOT "${FLANN_INCLUDE_DIR}" PATH) 221 | message(STATUS "FLANN found (include: ${FLANN_INCLUDE_DIR}, lib: ${FLANN_LIBRARY})") 222 | endif() 223 | -------------------------------------------------------------------------------- /documentation/build-instructions/ubuntu-20.04.md: -------------------------------------------------------------------------------- 1 | It turns out that it is incredibly difficult to find versions of Qt, VTK and PCL that work well together these days. 2 | 3 | On Ubuntu 20.04 I managed to get QtKittiVisualizer running with the default Qt version of the OS. 4 | 5 | However I had to compile VTK manually and even had to patch PCL before compiling. 6 | 7 | In order to save you some time and trouble, here is what you need to do in order to produce a successful build on Ubunut 20.04. 8 | 9 | Install `cmake` 10 | =============== 11 | 12 | `sudo apt install cmake` 13 | 14 | And checked the version: 15 | 16 | `cmake --version` 17 | 18 | `cmake version 3.16.3` 19 | 20 | 21 | Install Qt Creator to have an IDE and Qt available. 22 | =================================================== 23 | 24 | `sudo apt install qtcreator` 25 | 26 | Check Qt version: 27 | 28 | `qtdiag` 29 | 30 | `Qt 5.12.8 (x86_64-little_endian-lp64 shared (dynamic) release build; by GCC 9.3.0) on "xcb" 31 | OS: Ubuntu 20.04.4 LTS [linux version 5.13.0-51-generic]` 32 | 33 | 34 | Then build the required libraries from scratch: 35 | 36 | 37 | Build VTK 38 | ========= 39 | 40 | `sudo apt install build-essential cmake mesa-common-dev mesa-utils freeglut3-dev cmake-curses-gui qtdeclarative5-dev qtquickcontrols2-5-dev` 41 | 42 | Seems like ninja is not available on Ubuntu 20.04 but recommended for install in the VTK docs. 43 | 44 | `cd` 45 | 46 | `git clone -b v9.1.0 --depth 1 https://gitlab.kitware.com/vtk/vtk vtk-9.1.0` 47 | 48 | `mkdir vtk-9.1.0-build && cd $_` 49 | 50 | `ccmake ../vtk-9.1.0` 51 | 52 | Set VTK_GROUP_ENABLE_Qt to YES and exit ccmake by pressing e 53 | 54 | `cmake ../vtk-9.1.0` 55 | 56 | `make all` 57 | 58 | Note: 59 | QVTKOpenGLWidget was renamed to QVTKOpenGLStereoWidget in this version of VTK. 60 | 61 | 62 | Build PCL 63 | ========= 64 | 65 | `sudo apt install libbenchmark-dev` 66 | 67 | Here I also needed to install several other packages like libboost etc. I don't have the exact names since the PC crashed one time compiling the PCL, which takes an incredible amount of RAM and time. 68 | 69 | `cd` 70 | 71 | `git clone -b pcl-1.12.1 --depth 1 https://github.com/PointCloudLibrary/pcl pcl-1.12.1` 72 | 73 | `mkdir pcl-1.12.1-build && cd $_` 74 | 75 | `cmake ../pcl-1.12.1 -DVTK_DIR=$HOME/vtk-9.1.0-build` 76 | 77 | Apply the `link-required-vtk-libraries-for-libpcl_io.patch` for pcl from the QtKittiVisualizer source code. The patch is located in the `patches/pcl-1.12.1` directory. 78 | 79 | It adds VTK::FiltersCore and VTK::FiltersGeneral to line 350 of io/CMakeLists 80 | 81 | `make all` 82 | 83 | Build QtKittiVisualizer 84 | ======================= 85 | 86 | `cd` 87 | 88 | `git clone https://github.com/MarkMuth/QtKittiVisualizer` 89 | 90 | `mkdir QtKittiVisualizer-build && cd $_` 91 | 92 | `cmake ../QtKittiVisualizer` 93 | 94 | `make all` 95 | 96 | Now you should have the `qt-kitti-visualizer` binary in your home folder. Make sure to download at least one dataset and extract the data to the right location. Please refer to `KittiConfig.cpp` to see were the datasets should be located or change that file as needed and recompile. 97 | 98 | Then start QtKittiVisualizer: 99 | 100 | ./qt-kitti-visulizer --dataset=5 101 | 102 | The visualization might be hidden. Try to drag the splitter from the very right side of the window to see the render window. 103 | -------------------------------------------------------------------------------- /documentation/screenshots/qt-kitti-visualizer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MarkMuth/QtKittiVisualizer/3fb041d835951ced924c8352220ee4b55f866889/documentation/screenshots/qt-kitti-visualizer.png -------------------------------------------------------------------------------- /kitti-devkit-raw/readme.txt: -------------------------------------------------------------------------------- 1 | ########################################################################### 2 | # THE KITTI VISION BENCHMARK SUITE: RAW DATA RECORDINGS # 3 | # Andreas Geiger Philip Lenz Raquel Urtasun # 4 | # Karlsruhe Institute of Technology # 5 | # Toyota Technological Institute at Chicago # 6 | # www.cvlibs.net # 7 | ########################################################################### 8 | 9 | This file gives more information about the KITTI raw data recordings. 10 | 11 | General information about streams and timestamps 12 | ================================================ 13 | 14 | Each sensor stream is stored in a single folder. The main folder contains 15 | meta information and a timestamp file, listing the timestamp of each frame 16 | of the sequence to nanosecond precision. Numbers in the data stream correspond 17 | to each numbers in each other data stream and to line numbers in the 18 | timestamp file (0-based index), as all data has been synchronized. All 19 | cameras have been triggered directly by the Velodyne laser scanner, while 20 | from the GPS/IMU system (recording at 100 Hz), we have taken the data 21 | information closest to the respective reference frame. For all sequences 22 | 'image_00' has been used as the synchronization reference stream. 23 | 24 | Rectified color + grayscale stereo sequences 25 | ============================================ 26 | 27 | Our vehicle has been equipped with four cameras: 1 color camera stereo pair 28 | and 1 grayscale camera stereo pair. The color and grayscale cameras are 29 | mounted close to each other (~6 cm), the baseline of both stereo rigs is 30 | approximately 54 cm. We have chosen this setup such that for the left and 31 | right camera we can provide both color and grayscale information. While the 32 | color cameras (obviously) come with color information, the grayscale camera 33 | images have higher contrast and a little bit less noise. 34 | 35 | All cameras are synchronized at about 10 Hz with respect to the Velodyne 36 | laser scanner. The trigger is mounted such that camera images coincide 37 | roughly with the Velodyne lasers facing forward (in driving direction). 38 | 39 | All camera images are provided as lossless compressed and rectified png 40 | sequences. The native image resolution is 1382x512 pixels and a little bit 41 | less after rectification, for details see the calibration section below. 42 | The opening angle of the cameras (left-right) is approximately 90 degrees. 43 | 44 | The camera images are stored in the following directories: 45 | 46 | - 'image_00': left rectified grayscale image sequence 47 | - 'image_01': right rectified grayscale image sequence 48 | - 'image_02': left rectified color image sequence 49 | - 'image_03': right rectified color image sequence 50 | 51 | Velodyne 3D laser scan data 52 | =========================== 53 | 54 | The velodyne point clouds are stored in the folder 'velodyne_points'. To 55 | save space, all scans have been stored as Nx4 float matrix into a binary 56 | file using the following code: 57 | 58 | stream = fopen (dst_file.c_str(),"wb"); 59 | fwrite(data,sizeof(float),4*num,stream); 60 | fclose(stream); 61 | 62 | Here, data contains 4*num values, where the first 3 values correspond to 63 | x,y and z, and the last value is the reflectance information. All scans 64 | are stored row-aligned, meaning that the first 4 values correspond to the 65 | first measurement. Since each scan might potentially have a different 66 | number of points, this must be determined from the file size when reading 67 | the file, where 1e6 is a good enough upper bound on the number of values: 68 | 69 | // allocate 4 MB buffer (only ~130*4*4 KB are needed) 70 | int32_t num = 1000000; 71 | float *data = (float*)malloc(num*sizeof(float)); 72 | 73 | // pointers 74 | float *px = data+0; 75 | float *py = data+1; 76 | float *pz = data+2; 77 | float *pr = data+3; 78 | 79 | // load point cloud 80 | FILE *stream; 81 | stream = fopen (currFilenameBinary.c_str(),"rb"); 82 | num = fread(data,sizeof(float),num,stream)/4; 83 | for (int32_t i=0; i image plane 226 | - R_rect_00 (4x4): cam 0 coordinates -> rectified cam 0 coord. 227 | - (R|T)_velo_to_cam (4x4): velodyne coordinates -> cam 0 coordinates 228 | - (R|T)_imu_to_velo (4x4): imu coordinates -> velodyne coordinates 229 | 230 | Note that the (4x4) matrices above are padded with zeros and: 231 | R_rect_00(4,4) = (R|T)_velo_to_cam(4,4) = (R|T)_imu_to_velo(4,4) = 1. 232 | 233 | Tracklet Labels 234 | =============== 235 | 236 | Tracklet labels are stored in XML and can be read / written using the 237 | C++/MATLAB source code provided with this development kit. For compiling 238 | the code you will need to have a recent version of the boost libraries 239 | installed. 240 | 241 | Each tracklet is stored as a 3D bounding box of given height, width and 242 | length, spanning multiple frames. For each frame we have labeled 3D location 243 | and rotation in bird's eye view. Additionally, occlusion / truncation 244 | information is provided in the form of averaged Mechanical Turk label 245 | outputs. All tracklets are represented in Velodyne coordinates. 246 | 247 | Object categories are classified as following: 248 | 249 | - 'Car' 250 | - 'Van' 251 | - 'Truck' 252 | - 'Pedestrian' 253 | - 'Person (sitting)' 254 | - 'Cyclist' 255 | - 'Tram' 256 | - 'Misc' 257 | 258 | Here, 'Misc' denotes all other categories, e.g., 'Trailers' or 'Segways'. 259 | 260 | Reading the Tracklet Label XML Files 261 | ==================================== 262 | 263 | This toolkit provides the header 'cpp/tracklets.h', which can be used to 264 | parse a tracklet XML file into the corresponding data structures. Its usage 265 | is quite simple, you can directly include the header file into your code 266 | as follows: 267 | 268 | #include "tracklets.h" 269 | Tracklets *tracklets = new Tracklets(); 270 | if (!tracklets->loadFromFile(filename.xml)) 271 | 272 | 273 | delete tracklets; 274 | 275 | In order to compile this code you will need to have a recent version of the 276 | boost libraries installed and you need to link against 277 | 'libboost_serialization'. 278 | 279 | 'matlab/readTrackletsMex.cpp' is a MATLAB wrapper for 'cpp/tracklets.h'. 280 | It can be build using make.m. Again you need to link against 281 | 'libboost_serialization', which might be problematic on newer MATLAB 282 | versions due to MATLAB's internal definitions of libstdc, etc. The latest 283 | version which we know of which works on Linux is 2008b. This is because 284 | MATLAB has changed its pointer representation. 285 | 286 | Of course you can also directly parse the XML file using your preferred 287 | XML parser. If you need to create another useful wrapper for the header file 288 | (e.g., for Python) we would be more than happy if you could share it with us). 289 | 290 | Demo Utility for projecting Tracklets into Images 291 | ================================================= 292 | 293 | In 'matlab/run_demoTracklets.m' you find a demonstration script that reads 294 | tracklets and projects them as 2D/3D bounding boxes into the images. You 295 | will need to compile the MATLAB wrapper above in order to read the tracklets. 296 | For further instructions, please have a look at the comments in the 297 | respective MATLAB scripts and functions. 298 | -------------------------------------------------------------------------------- /kitti-devkit-raw/tracklets.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | class Tracklets { 15 | 16 | public: 17 | 18 | // pose states 19 | enum POSE_STATES { 20 | UNSET = 0, 21 | INTERP = 1, 22 | LABELED = 2 23 | }; 24 | 25 | // occlusion states 26 | enum OCCLUSION_STATES { 27 | OCCLUSION_UNSET = -1, 28 | VISIBLE = 0, 29 | PARTLY = 1, 30 | FULLY = 2 31 | }; 32 | 33 | // occlusion states 34 | enum TRUNCATION_STATES { 35 | TRUNCATION_UNSET = -1, 36 | IN_IMAGE = 0, 37 | TRUNCATED = 1, 38 | OUT_IMAGE = 2, 39 | BEHIND_IMAGE = 99 40 | }; 41 | 42 | // constructor / deconstructor 43 | Tracklets () {} 44 | ~Tracklets () {} 45 | 46 | // pose of tracklet at specific frame 47 | struct tPose{ 48 | 49 | double tx,ty,tz; // translation wrt. Velodyne coordinates 50 | double rx,ry,rz; // rotation wrt. Velodyne coordinates 51 | POSE_STATES state; // pose state 52 | OCCLUSION_STATES occlusion; // occusion state 53 | bool occlusion_kf; // is this an occlusion keyframe 54 | TRUNCATION_STATES truncation; // truncation state 55 | float amt_occlusion; // Mechanical Turk occlusion label 56 | float amt_border_l; // Mechanical Turk left boundary label (relative) 57 | float amt_border_r; // Mechanical Turk right boundary label (relative) 58 | int amt_occlusion_kf; // Mechanical Turk occlusion keyframe 59 | int amt_border_kf; // Mechanical Turk border keyframe 60 | 61 | tPose() {} 62 | tPose(double tx, double ty, double tz, double rx, double ry, double rz, 63 | POSE_STATES state, OCCLUSION_STATES occlusion, TRUNCATION_STATES truncation) 64 | : tx(tx), ty(ty), tz(tz), rx(rx), ry(ry), rz(rz), state(state), 65 | occlusion(occlusion), occlusion_kf(false), truncation(truncation)/*, geometry()*/ {} 66 | 67 | template void load(Archive &ar, const unsigned int version){ 68 | ar & BOOST_SERIALIZATION_NVP(tx) 69 | & BOOST_SERIALIZATION_NVP(ty) 70 | & BOOST_SERIALIZATION_NVP(tz) 71 | & BOOST_SERIALIZATION_NVP(rx) 72 | & BOOST_SERIALIZATION_NVP(ry) 73 | & BOOST_SERIALIZATION_NVP(rz) 74 | & BOOST_SERIALIZATION_NVP(state); 75 | 76 | // version 1 77 | if (version > 0){ 78 | ar & BOOST_SERIALIZATION_NVP(occlusion) 79 | & BOOST_SERIALIZATION_NVP(occlusion_kf) 80 | & BOOST_SERIALIZATION_NVP(truncation); 81 | 82 | // default values 83 | } else { 84 | occlusion = OCCLUSION_UNSET; 85 | occlusion_kf = false; 86 | truncation = IN_IMAGE; 87 | } 88 | 89 | // version 2 90 | if(version > 1){ 91 | ar & BOOST_SERIALIZATION_NVP(amt_occlusion) 92 | & BOOST_SERIALIZATION_NVP(amt_occlusion_kf) 93 | & BOOST_SERIALIZATION_NVP(amt_border_l) 94 | & BOOST_SERIALIZATION_NVP(amt_border_r) 95 | & BOOST_SERIALIZATION_NVP(amt_border_kf); 96 | 97 | // default values 98 | } else{ 99 | amt_occlusion = -1; 100 | amt_occlusion_kf = -1; 101 | amt_border_l = -1; 102 | amt_border_r = -1; 103 | amt_border_kf = -1; 104 | } 105 | } 106 | 107 | template void save(Archive &ar, const unsigned int) const{ 108 | ar & BOOST_SERIALIZATION_NVP(tx) 109 | & BOOST_SERIALIZATION_NVP(ty) 110 | & BOOST_SERIALIZATION_NVP(tz) 111 | & BOOST_SERIALIZATION_NVP(rx) 112 | & BOOST_SERIALIZATION_NVP(ry) 113 | & BOOST_SERIALIZATION_NVP(rz) 114 | & BOOST_SERIALIZATION_NVP(state) 115 | & BOOST_SERIALIZATION_NVP(occlusion) 116 | & BOOST_SERIALIZATION_NVP(occlusion_kf) 117 | & BOOST_SERIALIZATION_NVP(truncation) 118 | & BOOST_SERIALIZATION_NVP(amt_occlusion) 119 | & BOOST_SERIALIZATION_NVP(amt_occlusion_kf) 120 | & BOOST_SERIALIZATION_NVP(amt_border_l) 121 | & BOOST_SERIALIZATION_NVP(amt_border_r) 122 | & BOOST_SERIALIZATION_NVP(amt_border_kf); 123 | } 124 | 125 | // for versioning 126 | BOOST_SERIALIZATION_SPLIT_MEMBER() 127 | }; 128 | 129 | // tracklet with meta information and vector of poses 130 | struct tTracklet { 131 | 132 | std::string objectType; // object type: 'Car', 'Pedestrian', etc. 133 | float h,w,l; // height, width, length of bounding box 134 | int first_frame; // number of first frame of tracklet 135 | std::vector poses; // poses of this tracklet 136 | int finished; // is this tracklet fully labeled? 137 | 138 | tTracklet(){} 139 | tTracklet(std::string objectType, float h, float w, float l, int first_frame, std::vector poses, int finished) 140 | : objectType(objectType), h(h), w(w), l(l), first_frame(first_frame), poses(poses), finished(finished) {} 141 | 142 | template void serialize(Archive &ar, const unsigned int version){ 143 | ar & BOOST_SERIALIZATION_NVP(objectType) 144 | & BOOST_SERIALIZATION_NVP(h) 145 | & BOOST_SERIALIZATION_NVP(w) 146 | & BOOST_SERIALIZATION_NVP(l) 147 | & BOOST_SERIALIZATION_NVP(first_frame) 148 | & BOOST_SERIALIZATION_NVP(poses); 149 | if(version > 0) ar & BOOST_SERIALIZATION_NVP(finished); 150 | else finished = 0; 151 | } 152 | 153 | // return last frame index of tracklet wrt. to sequence 154 | int lastFrame() { 155 | return first_frame+poses.size()-1; 156 | } 157 | }; 158 | 159 | // total number of tracklets 160 | int numberOfTracklets() { 161 | return tracklets.size(); 162 | } 163 | 164 | // get tracklet with given id, ranging [0..#tracklets-1] 165 | tTracklet* getTracklet (int tracklet_id) { 166 | return &tracklets[tracklet_id]; 167 | } 168 | 169 | // push back a tracklet 170 | void addTracklet (tTracklet tracklet) { 171 | tracklets.push_back(tracklet); 172 | } 173 | 174 | // returns pose pointer, if requested tracklet is active at given frame 175 | bool getPose (int tracklet_id, int frame_number,tPose* &pose) { 176 | if (!isActive(tracklet_id,frame_number)) { 177 | return false; 178 | } else { 179 | int pose_idx = frame_number-tracklets[tracklet_id].first_frame; 180 | pose = &(tracklets[tracklet_id].poses[pose_idx]); 181 | return true; 182 | } 183 | } 184 | 185 | // checks if tracklet wit given id exists at given frame 186 | bool isActive (int tracklet_id, int frame_number) { 187 | if (tracklet_id<0 || tracklet_id>=(int)tracklets.size()) 188 | return false; 189 | int pose_idx = frame_number-tracklets[tracklet_id].first_frame; 190 | if (pose_idx<0 || pose_idx>=(int)tracklets[tracklet_id].poses.size()) 191 | return false; 192 | return true; 193 | } 194 | 195 | // load tracklets from xml file 196 | bool loadFromFile (std::string filename) { 197 | try { 198 | std::ifstream ifs(filename.c_str()); 199 | boost::archive::xml_iarchive ia(ifs); 200 | ia >> boost::serialization::make_nvp("tracklets", tracklets); 201 | return true; 202 | } catch (...) { 203 | return false; 204 | } 205 | } 206 | 207 | // save tracklets to xml file, try several times 208 | bool saveToFile (std::string filename) { 209 | for (int i=0; i<10; i++) { 210 | try { 211 | std::ofstream ofs(filename.c_str()); 212 | boost::archive::xml_oarchive oa(ofs); 213 | oa << boost::serialization::make_nvp("tracklets", tracklets); 214 | return true; 215 | } catch(...) { 216 | usleep(200000); 217 | } 218 | } 219 | return false; 220 | } 221 | 222 | private: 223 | 224 | std::vector tracklets; 225 | 226 | }; 227 | 228 | // set the version of the stored tracklet data 229 | BOOST_CLASS_VERSION(Tracklets::tTracklet, 1) 230 | BOOST_CLASS_VERSION(Tracklets::tPose, 2) 231 | 232 | -------------------------------------------------------------------------------- /main.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright 2016 Mark Muth 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | 17 | */ 18 | 19 | #include "QtKittiVisualizer.h" 20 | 21 | #include 22 | 23 | int main (int argc, char *argv[]) 24 | { 25 | QApplication a (argc, argv); 26 | KittiVisualizerQt w(NULL, argc, argv); 27 | w.show (); 28 | return a.exec (); 29 | } 30 | -------------------------------------------------------------------------------- /patches/pcl-1.12.1/link-required-vtk-libraries-for-libpcl_io.patch: -------------------------------------------------------------------------------- 1 | diff --git a/io/CMakeLists.txt b/io/CMakeLists.txt 2 | index 163639b..c249aee 100644 3 | --- a/io/CMakeLists.txt 4 | +++ b/io/CMakeLists.txt 5 | @@ -347,6 +347,8 @@ target_link_libraries("${LIB_NAME}" Boost::boost Boost::filesystem Boost::iostre 6 | if(VTK_FOUND) 7 | if(${VTK_VERSION} VERSION_GREATER_EQUAL 9.0) 8 | target_link_libraries("${LIB_NAME}" 9 | + VTK::FiltersCore 10 | + VTK::FiltersGeneral 11 | VTK::IOImage 12 | VTK::IOGeometry 13 | VTK::IOPLY) 14 | --------------------------------------------------------------------------------