Browse Source

提交scripts

master
hesuicong 5 months ago
parent
commit
07dabbd186
  1. 14
      apps/CMakeLists.txt
  2. 13
      apps/DensifyPointCloud/CMakeLists.txt
  3. 433
      apps/DensifyPointCloud/DensifyPointCloud.cpp
  4. 13
      apps/InterfaceCOLMAP/CMakeLists.txt
  5. 1493
      apps/InterfaceCOLMAP/InterfaceCOLMAP.cpp
  6. 167
      apps/InterfaceCOLMAP/endian.h
  7. 13
      apps/InterfaceCOLMAPTest/CMakeLists.txt
  8. 1493
      apps/InterfaceCOLMAPTest/InterfaceCOLMAPTest.cpp
  9. 167
      apps/InterfaceCOLMAPTest/endian.h
  10. 13
      apps/InterfaceMVSNet/CMakeLists.txt
  11. 706
      apps/InterfaceMVSNet/InterfaceMVSNet.cpp
  12. 13
      apps/InterfaceMetashape/CMakeLists.txt
  13. 887
      apps/InterfaceMetashape/InterfaceMetashape.cpp
  14. 23
      apps/InterfaceOpenMVG/CMakeLists.txt
  15. 755
      apps/InterfaceOpenMVG/InterfaceOpenMVG.cpp
  16. 13
      apps/InterfacePolycam/CMakeLists.txt
  17. 362
      apps/InterfacePolycam/InterfacePolycam.cpp
  18. 13
      apps/InterfaceVisualSFM/CMakeLists.txt
  19. 383
      apps/InterfaceVisualSFM/DataInterface.h
  20. 607
      apps/InterfaceVisualSFM/InterfaceVisualSFM.cpp
  21. 756
      apps/InterfaceVisualSFM/Util.h
  22. 13
      apps/ReconstructMesh/CMakeLists.txt
  23. 498
      apps/ReconstructMesh/ReconstructMesh.cpp
  24. 13
      apps/RefineMesh/CMakeLists.txt
  25. 266
      apps/RefineMesh/RefineMesh.cpp
  26. 15
      apps/Tests/CMakeLists.txt
  27. 136
      apps/Tests/Tests.cpp
  28. BIN
      apps/Tests/data/images/00000.jpg
  29. BIN
      apps/Tests/data/images/00001.jpg
  30. BIN
      apps/Tests/data/images/00002.jpg
  31. BIN
      apps/Tests/data/images/00003.jpg
  32. BIN
      apps/Tests/data/scene.mvs
  33. 13
      apps/TextureMesh/CMakeLists.txt
  34. 1168
      apps/TextureMesh/TextureMesh.cpp
  35. 13
      apps/TransformScene/CMakeLists.txt
  36. 329
      apps/TransformScene/TransformScene.cpp
  37. 47
      apps/Viewer/CMakeLists.txt
  38. 180
      apps/Viewer/Camera.cpp
  39. 87
      apps/Viewer/Camera.h
  40. 36
      apps/Viewer/Common.cpp
  41. 104
      apps/Viewer/Common.h
  42. 124
      apps/Viewer/Image.cpp
  43. 97
      apps/Viewer/Image.h
  44. 823
      apps/Viewer/Scene.cpp
  45. 111
      apps/Viewer/Scene.h
  46. 226
      apps/Viewer/Viewer.cpp
  47. 493
      apps/Viewer/Window.cpp
  48. 167
      apps/Viewer/Window.h
  49. 8
      libs/CMakeLists.txt
  50. 121
      libs/Common/AABB.h
  51. 419
      libs/Common/AABB.inl
  52. 668
      libs/Common/AutoEstimator.h
  53. 275
      libs/Common/AutoPtr.h
  54. 25
      libs/Common/CMakeLists.txt
  55. 47
      libs/Common/Common.cpp
  56. 308
      libs/Common/Common.h
  57. 281
      libs/Common/Config.h
  58. 153
      libs/Common/ConfigTable.cpp
  59. 82
      libs/Common/ConfigTable.h
  60. 264
      libs/Common/CriticalSection.h
  61. 101
      libs/Common/EventQueue.cpp
  62. 90
      libs/Common/EventQueue.h
  63. 2105
      libs/Common/FastDelegate.h
  64. 240
      libs/Common/FastDelegateBind.h
  65. 379
      libs/Common/FastDelegateCPP11.h
  66. 686
      libs/Common/File.h
  67. 612
      libs/Common/Filters.h
  68. 457
      libs/Common/HTMLDoc.h
  69. 159
      libs/Common/HalfFloat.h
  70. 303
      libs/Common/Hash.h
  71. 95
      libs/Common/Line.h
  72. 215
      libs/Common/Line.inl
  73. 111
      libs/Common/LinkLib.h
  74. 1704
      libs/Common/List.h
  75. 453
      libs/Common/Log.cpp
  76. 211
      libs/Common/Log.h
  77. 182
      libs/Common/MemFile.h
  78. 127
      libs/Common/OBB.h
  79. 401
      libs/Common/OBB.inl
  80. 237
      libs/Common/Octree.h
  81. 851
      libs/Common/Octree.inl
  82. 161
      libs/Common/Plane.h
  83. 623
      libs/Common/Plane.inl
  84. 296
      libs/Common/Queue.h
  85. 164
      libs/Common/Random.h
  86. 226
      libs/Common/Ray.h
  87. 951
      libs/Common/Ray.inl
  88. 595
      libs/Common/Rotation.h
  89. 1563
      libs/Common/Rotation.inl
  90. 419
      libs/Common/SML.cpp
  91. 116
      libs/Common/SML.h
  92. 274
      libs/Common/Sampler.inl
  93. 170
      libs/Common/Semaphore.h
  94. 209
      libs/Common/SharedPtr.h
  95. 56
      libs/Common/Sphere.h
  96. 80
      libs/Common/Sphere.inl
  97. 244
      libs/Common/Streams.h
  98. 239
      libs/Common/Strings.h
  99. 435
      libs/Common/Thread.h
  100. 165
      libs/Common/Timer.cpp
  101. Some files were not shown because too many files have changed in this diff Show More

14
apps/CMakeLists.txt

@ -0,0 +1,14 @@ @@ -0,0 +1,14 @@
# Add applications
ADD_SUBDIRECTORY(InterfaceCOLMAP)
ADD_SUBDIRECTORY(InterfaceMetashape)
ADD_SUBDIRECTORY(InterfaceMVSNet)
ADD_SUBDIRECTORY(InterfacePolycam)
ADD_SUBDIRECTORY(DensifyPointCloud)
ADD_SUBDIRECTORY(ReconstructMesh)
ADD_SUBDIRECTORY(RefineMesh)
ADD_SUBDIRECTORY(TextureMesh)
ADD_SUBDIRECTORY(TransformScene)
ADD_SUBDIRECTORY(Viewer)
if(OpenMVS_ENABLE_TESTS)
ADD_SUBDIRECTORY(Tests)
endif()

13
apps/DensifyPointCloud/CMakeLists.txt

@ -0,0 +1,13 @@ @@ -0,0 +1,13 @@
if(MSVC)
FILE(GLOB LIBRARY_FILES_C "*.cpp" "*.rc")
else()
FILE(GLOB LIBRARY_FILES_C "*.cpp")
endif()
FILE(GLOB LIBRARY_FILES_H "*.h" "*.inl")
cxx_executable_with_flags(DensifyPointCloud "Apps" "${cxx_default}" "MVS;${OpenMVS_EXTRA_LIBS}" ${LIBRARY_FILES_C} ${LIBRARY_FILES_H})
# Install
INSTALL(TARGETS DensifyPointCloud
EXPORT OpenMVSTargets
RUNTIME DESTINATION "${INSTALL_BIN_DIR}" COMPONENT bin)

433
apps/DensifyPointCloud/DensifyPointCloud.cpp

@ -0,0 +1,433 @@ @@ -0,0 +1,433 @@
/*
* DensifyPointCloud.cpp
*
* Copyright (c) 2014-2015 SEACAVE
*
* Author(s):
*
* cDc <cdc.seacave@gmail.com>
*
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*
* Additional Terms:
*
* You are required to preserve legal notices and author attributions in
* that material or in the Appropriate Legal Notices displayed by works
* containing it.
*/
#include "../../libs/MVS/Common.h"
#include "../../libs/MVS/Scene.h"
#include <boost/program_options.hpp>
using namespace MVS;
// D E F I N E S ///////////////////////////////////////////////////
#define APPNAME _T("DensifyPointCloud")
// S T R U C T S ///////////////////////////////////////////////////
namespace {
namespace OPT {
String strInputFileName;
String strPointCloudFileName;
String strOutputFileName;
String strViewNeighborsFileName;
String strOutputViewNeighborsFileName;
String strMeshFileName;
String strExportROIFileName;
String strImportROIFileName;
String strDenseConfigFileName;
String strExportDepthMapsName;
String strMaskPath;
float fMaxSubsceneArea;
float fSampleMesh;
float fBorderROI;
bool bCrop2ROI;
int nEstimateROI;
int nTowerMode;
int nFusionMode;
float fEstimateScale;
int thFilterPointCloud;
int nExportNumViews;
int nArchiveType;
int nProcessPriority;
unsigned nMaxThreads;
String strConfigFileName;
boost::program_options::variables_map vm;
} // namespace OPT
class Application {
public:
Application() {}
~Application() { Finalize(); }
bool Initialize(size_t argc, LPCTSTR* argv);
void Finalize();
}; // Application
// initialize and parse the command line parameters
bool Application::Initialize(size_t argc, LPCTSTR* argv)
{
// initialize log and console
OPEN_LOG();
OPEN_LOGCONSOLE();
// group of options allowed only on command line
boost::program_options::options_description generic("Generic options");
generic.add_options()
("help,h", "produce this help message")
("working-folder,w", boost::program_options::value<std::string>(&WORKING_FOLDER), "working directory (default current directory)")
("config-file,c", boost::program_options::value<std::string>(&OPT::strConfigFileName)->default_value(APPNAME _T(".cfg")), "file name containing program options")
("archive-type", boost::program_options::value(&OPT::nArchiveType)->default_value(ARCHIVE_MVS), "project archive type: -1-interface, 0-text, 1-binary, 2-compressed binary")
("process-priority", boost::program_options::value(&OPT::nProcessPriority)->default_value(-1), "process priority (below normal by default)")
("max-threads", boost::program_options::value(&OPT::nMaxThreads)->default_value(0), "maximum number of threads (0 for using all available cores)")
#if TD_VERBOSE != TD_VERBOSE_OFF
("verbosity,v", boost::program_options::value(&g_nVerbosityLevel)->default_value(
#if TD_VERBOSE == TD_VERBOSE_DEBUG
3
#else
2
#endif
), "verbosity level")
#endif
#ifdef _USE_CUDA
("cuda-device", boost::program_options::value(&CUDA::desiredDeviceID)->default_value(-1), "CUDA device number to be used for depth-map estimation (-2 - CPU processing, -1 - best GPU, >=0 - device index)")
#endif
;
// group of options allowed both on command line and in config file
#ifdef _USE_CUDA
const unsigned nNumViewsDefault(8);
const unsigned numIters(4);
#else
const unsigned nNumViewsDefault(5);
const unsigned numIters(3);
#endif
unsigned nResolutionLevel;
unsigned nMaxResolution;
unsigned nMinResolution;
unsigned nNumViews;
unsigned nMinViewsFuse;
unsigned nSubResolutionLevels;
unsigned nEstimationIters;
unsigned nEstimationGeometricIters;
unsigned nEstimateColors;
unsigned nEstimateNormals;
unsigned nOptimize;
int nIgnoreMaskLabel;
bool bRemoveDmaps;
boost::program_options::options_description config("Densify options");
config.add_options()
("input-file,i", boost::program_options::value<std::string>(&OPT::strInputFileName), "input filename containing camera poses and image list")
("pointcloud-file,p", boost::program_options::value<std::string>(&OPT::strPointCloudFileName), "sparse point-cloud with views file name to densify (overwrite existing point-cloud)")
("output-file,o", boost::program_options::value<std::string>(&OPT::strOutputFileName), "output filename for storing the dense point-cloud (optional)")
("view-neighbors-file", boost::program_options::value<std::string>(&OPT::strViewNeighborsFileName), "input filename containing the list of views and their neighbors (optional)")
("output-view-neighbors-file", boost::program_options::value<std::string>(&OPT::strOutputViewNeighborsFileName), "output filename containing the generated list of views and their neighbors")
("resolution-level", boost::program_options::value(&nResolutionLevel)->default_value(1), "how many times to scale down the images before point cloud computation")
("max-resolution", boost::program_options::value(&nMaxResolution)->default_value(2560), "do not scale images higher than this resolution")
("min-resolution", boost::program_options::value(&nMinResolution)->default_value(640), "do not scale images lower than this resolution")
("sub-resolution-levels", boost::program_options::value(&nSubResolutionLevels)->default_value(2), "number of patch-match sub-resolution iterations (0 - disabled)")
("number-views", boost::program_options::value(&nNumViews)->default_value(nNumViewsDefault), "number of views used for depth-map estimation (0 - all neighbor views available)")
("number-views-fuse", boost::program_options::value(&nMinViewsFuse)->default_value(3), "minimum number of images that agrees with an estimate during fusion in order to consider it inlier (<2 - only merge depth-maps)")
("ignore-mask-label", boost::program_options::value(&nIgnoreMaskLabel)->default_value(-1), "label value to ignore in the image mask, stored in the MVS scene or next to each image with '.mask.png' extension (<0 - disabled)")
("mask-path", boost::program_options::value<std::string>(&OPT::strMaskPath), "path to folder containing mask images with '.mask.png' extension")
("iters", boost::program_options::value(&nEstimationIters)->default_value(numIters), "number of patch-match iterations")
("geometric-iters", boost::program_options::value(&nEstimationGeometricIters)->default_value(2), "number of geometric consistent patch-match iterations (0 - disabled)")
("estimate-colors", boost::program_options::value(&nEstimateColors)->default_value(2), "estimate the colors for the dense point-cloud (0 - disabled, 1 - final, 2 - estimate)")
("estimate-normals", boost::program_options::value(&nEstimateNormals)->default_value(2), "estimate the normals for the dense point-cloud (0 - disabled, 1 - final, 2 - estimate)")
("estimate-scale", boost::program_options::value(&OPT::fEstimateScale)->default_value(0.f), "estimate the point-scale for the dense point-cloud (scale multiplier, 0 - disabled)")
("sub-scene-area", boost::program_options::value(&OPT::fMaxSubsceneArea)->default_value(0.f), "split the scene in sub-scenes such that each sub-scene surface does not exceed the given maximum sampling area (0 - disabled)")
("sample-mesh", boost::program_options::value(&OPT::fSampleMesh)->default_value(0.f), "uniformly samples points on a mesh (0 - disabled, <0 - number of points, >0 - sample density per square unit)")
("fusion-mode", boost::program_options::value(&OPT::nFusionMode)->default_value(0), "depth-maps fusion mode (-2 - fuse disparity-maps, -1 - export disparity-maps only, 0 - depth-maps & fusion, 1 - export depth-maps only)")
("postprocess-dmaps", boost::program_options::value(&nOptimize)->default_value(7), "flags used to filter the depth-maps after estimation (0 - disabled, 1 - remove-speckles, 2 - fill-gaps, 4 - adjust-filter)")
("filter-point-cloud", boost::program_options::value(&OPT::thFilterPointCloud)->default_value(0), "filter dense point-cloud based on visibility (0 - disabled)")
("export-number-views", boost::program_options::value(&OPT::nExportNumViews)->default_value(0), "export points with >= number of views (0 - disabled, <0 - save MVS project too)")
("roi-border", boost::program_options::value(&OPT::fBorderROI)->default_value(0), "add a border to the region-of-interest when cropping the scene (0 - disabled, >0 - percentage, <0 - absolute)")
("estimate-roi", boost::program_options::value(&OPT::nEstimateROI)->default_value(2), "estimate and set region-of-interest (0 - disabled, 1 - enabled, 2 - adaptive)")
("crop-to-roi", boost::program_options::value(&OPT::bCrop2ROI)->default_value(true), "crop scene using the region-of-interest")
("remove-dmaps", boost::program_options::value(&bRemoveDmaps)->default_value(false), "remove depth-maps after fusion")
("tower-mode", boost::program_options::value(&OPT::nTowerMode)->default_value(4), "add a cylinder of points in the center of ROI; scene assume to be Z-up oriented (0 - disabled, 1 - replace, 2 - append, 3 - select neighbors, 4 - select neighbors & append, <0 - force tower mode)")
;
// hidden options, allowed both on command line and
// in config file, but will not be shown to the user
boost::program_options::options_description hidden("Hidden options");
hidden.add_options()
("mesh-file", boost::program_options::value<std::string>(&OPT::strMeshFileName), "mesh file name used for image pair overlap estimation")
("export-roi-file", boost::program_options::value<std::string>(&OPT::strExportROIFileName), "ROI file name to be exported form the scene")
("import-roi-file", boost::program_options::value<std::string>(&OPT::strImportROIFileName), "ROI file name to be imported into the scene")
("dense-config-file", boost::program_options::value<std::string>(&OPT::strDenseConfigFileName), "optional configuration file for the densifier (overwritten by the command line options)")
("export-depth-maps-name", boost::program_options::value<std::string>(&OPT::strExportDepthMapsName), "render given mesh and save the depth-map for every image to this file name base (empty - disabled)")
;
boost::program_options::options_description cmdline_options;
cmdline_options.add(generic).add(config).add(hidden);
boost::program_options::options_description config_file_options;
config_file_options.add(config).add(hidden);
boost::program_options::positional_options_description p;
p.add("input-file", -1);
try {
// parse command line options
boost::program_options::store(boost::program_options::command_line_parser((int)argc, argv).options(cmdline_options).positional(p).run(), OPT::vm);
boost::program_options::notify(OPT::vm);
INIT_WORKING_FOLDER;
// parse configuration file
std::ifstream ifs(MAKE_PATH_SAFE(OPT::strConfigFileName));
if (ifs) {
boost::program_options::store(parse_config_file(ifs, config_file_options), OPT::vm);
boost::program_options::notify(OPT::vm);
}
}
catch (const std::exception& e) {
LOG(e.what());
return false;
}
// initialize the log file
OPEN_LOGFILE(MAKE_PATH(APPNAME _T("-")+Util::getUniqueName(0)+_T(".log")).c_str());
// print application details: version and command line
Util::LogBuild();
LOG(_T("Command line: ") APPNAME _T("%s"), Util::CommandLineToString(argc, argv).c_str());
// validate input
Util::ensureValidPath(OPT::strInputFileName);
if (OPT::vm.count("help") || OPT::strInputFileName.empty()) {
boost::program_options::options_description visible("Available options");
visible.add(generic).add(config);
GET_LOG() << visible;
}
if (OPT::strInputFileName.empty())
return false;
// initialize optional options
Util::ensureValidPath(OPT::strPointCloudFileName);
Util::ensureValidPath(OPT::strOutputFileName);
Util::ensureValidPath(OPT::strViewNeighborsFileName);
Util::ensureValidPath(OPT::strOutputViewNeighborsFileName);
Util::ensureValidPath(OPT::strMeshFileName);
Util::ensureValidPath(OPT::strExportROIFileName);
Util::ensureValidPath(OPT::strImportROIFileName);
if (OPT::strOutputFileName.empty())
OPT::strOutputFileName = Util::getFileFullName(OPT::strInputFileName) + _T("_dense.mvs");
// init dense options
if (!OPT::strDenseConfigFileName.empty())
OPT::strDenseConfigFileName = MAKE_PATH_SAFE(OPT::strDenseConfigFileName);
OPTDENSE::init();
const bool bValidConfig(OPTDENSE::oConfig.Load(OPT::strDenseConfigFileName));
OPTDENSE::update();
OPTDENSE::nResolutionLevel = nResolutionLevel;
OPTDENSE::nMaxResolution = nMaxResolution;
OPTDENSE::nMinResolution = nMinResolution;
OPTDENSE::nSubResolutionLevels = nSubResolutionLevels;
OPTDENSE::nNumViews = nNumViews;
OPTDENSE::nMinViewsFuse = nMinViewsFuse;
OPTDENSE::nEstimationIters = nEstimationIters;
OPTDENSE::nEstimationGeometricIters = nEstimationGeometricIters;
OPTDENSE::nEstimateColors = nEstimateColors;
OPTDENSE::nEstimateNormals = nEstimateNormals;
OPTDENSE::nOptimize = nOptimize;
OPTDENSE::nIgnoreMaskLabel = nIgnoreMaskLabel;
OPTDENSE::bRemoveDmaps = bRemoveDmaps;
if (!bValidConfig && !OPT::strDenseConfigFileName.empty())
OPTDENSE::oConfig.Save(OPT::strDenseConfigFileName);
MVS::Initialize(APPNAME, OPT::nMaxThreads, OPT::nProcessPriority);
return true;
}
// finalize application instance
void Application::Finalize()
{
MVS::Finalize();
CLOSE_LOGFILE();
CLOSE_LOGCONSOLE();
CLOSE_LOG();
}
} // unnamed namespace
int main(int argc, LPCTSTR* argv)
{
#ifdef _DEBUGINFO
// set _crtBreakAlloc index to stop in <dbgheap.c> at allocation
_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);// | _CRTDBG_CHECK_ALWAYS_DF);
#endif
Application application;
if (!application.Initialize(argc, argv))
return EXIT_FAILURE;
Scene scene(OPT::nMaxThreads);
if (OPT::fSampleMesh != 0) {
// sample input mesh and export the obtained point-cloud
if (!scene.Load(MAKE_PATH_SAFE(OPT::strInputFileName), true) || scene.mesh.IsEmpty())
return EXIT_FAILURE;
TD_TIMER_START();
PointCloud pointcloud;
if (OPT::fSampleMesh > 0)
scene.mesh.SamplePoints(OPT::fSampleMesh, 0, pointcloud);
else
scene.mesh.SamplePoints(ROUND2INT<unsigned>(-OPT::fSampleMesh), pointcloud);
VERBOSE("Sample mesh completed: %u points (%s)", pointcloud.GetSize(), TD_TIMER_GET_FMT().c_str());
pointcloud.Save(MAKE_PATH_SAFE(Util::getFileFullName(OPT::strOutputFileName))+_T(".ply"));
return EXIT_SUCCESS;
}
// load and estimate a dense point-cloud
const Scene::SCENE_TYPE sceneType(scene.Load(MAKE_PATH_SAFE(OPT::strInputFileName)));
if (sceneType == Scene::SCENE_NA)
return EXIT_FAILURE;
if (!OPT::strPointCloudFileName.empty() && !scene.pointcloud.Load(MAKE_PATH_SAFE(OPT::strPointCloudFileName))) {
VERBOSE("error: cannot load point-cloud file");
return EXIT_FAILURE;
}
if (!OPT::strMaskPath.empty()) {
Util::ensureValidFolderPath(OPT::strMaskPath);
for (Image& image : scene.images) {
if (!image.maskName.empty()) {
VERBOSE("error: Image %s has non-empty maskName %s", image.name.c_str(), image.maskName.c_str());
return EXIT_FAILURE;
}
image.maskName = OPT::strMaskPath + Util::getFileName(image.name) + ".mask.png";
if (!File::access(image.maskName)) {
VERBOSE("error: Mask image %s not found", image.maskName.c_str());
return EXIT_FAILURE;
}
}
}
if (!OPT::strImportROIFileName.empty()) {
std::ifstream fs(MAKE_PATH_SAFE(OPT::strImportROIFileName));
if (!fs)
return EXIT_FAILURE;
fs >> scene.obb;
scene.Save(MAKE_PATH_SAFE(Util::getFileFullName(OPT::strOutputFileName))+_T(".mvs"), (ARCHIVE_TYPE)OPT::nArchiveType);
return EXIT_SUCCESS;
}
if (!scene.IsBounded())
scene.EstimateROI(OPT::nEstimateROI, 1.1f);
if (!OPT::strExportROIFileName.empty() && scene.IsBounded()) {
std::ofstream fs(MAKE_PATH_SAFE(OPT::strExportROIFileName));
if (!fs)
return EXIT_FAILURE;
fs << scene.obb;
return EXIT_SUCCESS;
}
if (OPT::nTowerMode!=0)
scene.InitTowerScene(OPT::nTowerMode);
if (!OPT::strMeshFileName.empty())
scene.mesh.Load(MAKE_PATH_SAFE(OPT::strMeshFileName));
if (!OPT::strViewNeighborsFileName.empty())
scene.LoadViewNeighbors(MAKE_PATH_SAFE(OPT::strViewNeighborsFileName));
if (!OPT::strOutputViewNeighborsFileName.empty()) {
if (!scene.ImagesHaveNeighbors()) {
VERBOSE("error: neighbor views not computed yet");
return EXIT_FAILURE;
}
scene.SaveViewNeighbors(MAKE_PATH_SAFE(OPT::strOutputViewNeighborsFileName));
return EXIT_SUCCESS;
}
if (!OPT::strExportDepthMapsName.empty() && !scene.mesh.IsEmpty()) {
// project mesh onto each image and save the resulted depth-maps
TD_TIMER_START();
if (!scene.ExportMeshToDepthMaps(MAKE_PATH_SAFE(OPT::strExportDepthMapsName)))
return EXIT_FAILURE;
VERBOSE("Mesh projection completed: %u depth-maps (%s)", scene.images.size(), TD_TIMER_GET_FMT().c_str());
return EXIT_SUCCESS;
}
if (OPT::fMaxSubsceneArea > 0) {
// split the scene in sub-scenes by maximum sampling area
Scene::ImagesChunkArr chunks;
scene.Split(chunks, OPT::fMaxSubsceneArea);
scene.ExportChunks(chunks, GET_PATH_FULL(OPT::strOutputFileName), (ARCHIVE_TYPE)OPT::nArchiveType);
return EXIT_SUCCESS;
}
if (OPT::thFilterPointCloud < 0) {
// filter point-cloud based on camera-point visibility intersections
scene.PointCloudFilter(OPT::thFilterPointCloud);
const String baseFileName(MAKE_PATH_SAFE(Util::getFileFullName(OPT::strOutputFileName))+_T("_filtered"));
scene.Save(baseFileName+_T(".mvs"), (ARCHIVE_TYPE)OPT::nArchiveType);
scene.pointcloud.Save(baseFileName+_T(".ply"));
return EXIT_SUCCESS;
}
if (OPT::nExportNumViews && scene.pointcloud.IsValid()) {
// export point-cloud containing only points with N+ views
const String baseFileName(MAKE_PATH_SAFE(Util::getFileFullName(OPT::strOutputFileName))+
String::FormatString(_T("_%dviews"), ABS(OPT::nExportNumViews)));
if (OPT::nExportNumViews > 0) {
// export point-cloud containing only points with N+ views
scene.pointcloud.SaveNViews(baseFileName+_T(".ply"), (IIndex)OPT::nExportNumViews);
} else {
// save scene and export point-cloud containing only points with N+ views
scene.pointcloud.RemoveMinViews((IIndex)-OPT::nExportNumViews);
scene.Save(baseFileName+_T(".mvs"), (ARCHIVE_TYPE)OPT::nArchiveType);
scene.pointcloud.Save(baseFileName+_T(".ply"));
}
return EXIT_SUCCESS;
}
if (OPT::fEstimateScale > 0 && !scene.pointcloud.IsEmpty() && !scene.images.empty()) {
// simply export existing point-cloud with scale
if (scene.pointcloud.normals.empty()) {
if (!scene.pointcloud.IsValid()) {
VERBOSE("error: can not estimate normals as the point-cloud is not valid");
return EXIT_FAILURE;
}
EstimatePointNormals(scene.images, scene.pointcloud);
}
const String baseFileName(MAKE_PATH_SAFE(Util::getFileFullName(OPT::strOutputFileName)));
scene.pointcloud.SaveWithScale(baseFileName+_T("_scale.ply"), scene.images, OPT::fEstimateScale);
return EXIT_SUCCESS;
}
PointCloud sparsePointCloud;
if ((ARCHIVE_TYPE)OPT::nArchiveType != ARCHIVE_MVS || sceneType == Scene::SCENE_INTERFACE) {
#if TD_VERBOSE != TD_VERBOSE_OFF
if (VERBOSITY_LEVEL > 1 && !scene.pointcloud.IsEmpty())
scene.pointcloud.PrintStatistics(scene.images.data(), &scene.obb);
#endif
if ((ARCHIVE_TYPE)OPT::nArchiveType == ARCHIVE_MVS)
sparsePointCloud = scene.pointcloud;
TD_TIMER_START();
if (!scene.DenseReconstruction(OPT::nFusionMode, OPT::bCrop2ROI, OPT::fBorderROI)) {
if (ABS(OPT::nFusionMode) != 1)
return EXIT_FAILURE;
VERBOSE("Depth-maps estimated (%s)", TD_TIMER_GET_FMT().c_str());
return EXIT_SUCCESS;
}
VERBOSE("Densifying point-cloud completed: %u points (%s)", scene.pointcloud.GetSize(), TD_TIMER_GET_FMT().c_str());
}
// save the final point-cloud
const String baseFileName(MAKE_PATH_SAFE(Util::getFileFullName(OPT::strOutputFileName)));
scene.pointcloud.Save(baseFileName+_T(".ply"), (ARCHIVE_TYPE)OPT::nArchiveType == ARCHIVE_MVS);
#if TD_VERBOSE != TD_VERBOSE_OFF
if (VERBOSITY_LEVEL > 2)
scene.ExportCamerasMLP(baseFileName+_T(".mlp"), baseFileName+_T(".ply"));
#endif
if ((ARCHIVE_TYPE)OPT::nArchiveType == ARCHIVE_MVS)
scene.pointcloud.Swap(sparsePointCloud);
scene.Save(baseFileName+_T(".mvs"), (ARCHIVE_TYPE)OPT::nArchiveType);
return EXIT_SUCCESS;
}
/*----------------------------------------------------------------*/

13
apps/InterfaceCOLMAP/CMakeLists.txt

@ -0,0 +1,13 @@ @@ -0,0 +1,13 @@
if(MSVC)
FILE(GLOB LIBRARY_FILES_C "*.cpp" "*.rc")
else()
FILE(GLOB LIBRARY_FILES_C "*.cpp")
endif()
FILE(GLOB LIBRARY_FILES_H "*.h" "*.inl")
cxx_executable_with_flags(InterfaceCOLMAP "Apps" "${cxx_default}" "MVS;${OpenMVS_EXTRA_LIBS}" ${LIBRARY_FILES_C} ${LIBRARY_FILES_H})
# Install
INSTALL(TARGETS InterfaceCOLMAP
EXPORT OpenMVSTargets
RUNTIME DESTINATION "${INSTALL_BIN_DIR}" COMPONENT bin)

1493
apps/InterfaceCOLMAP/InterfaceCOLMAP.cpp

File diff suppressed because it is too large Load Diff

167
apps/InterfaceCOLMAP/endian.h

@ -0,0 +1,167 @@ @@ -0,0 +1,167 @@
// Copyright (c) 2018, ETH Zurich and UNC Chapel Hill.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//
// * Neither the name of ETH Zurich and UNC Chapel Hill nor the names of
// its contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
//
// Author: Johannes L. Schoenberger (jsch-at-demuc-dot-de)
#ifndef COLMAP_SRC_UTIL_ENDIAN_H_
#define COLMAP_SRC_UTIL_ENDIAN_H_
#include <algorithm>
#include <vector>
#include <iostream>
namespace colmap {
// Reverse the order of each byte.
template <typename T>
T ReverseBytes(const T& data);
// Check the order in which bytes are stored in computer memory.
bool IsLittleEndian();
bool IsBigEndian();
// Convert data between endianness and the native format. Note that, for float
// and double types, these functions are only valid if the format is IEEE-754.
// This is the case for pretty much most processors.
template <typename T>
T LittleEndianToNative(const T x);
template <typename T>
T BigEndianToNative(const T x);
template <typename T>
T NativeToLittleEndian(const T x);
template <typename T>
T NativeToBigEndian(const T x);
// Read data in little endian format for cross-platform support.
template <typename T>
T ReadBinaryLittleEndian(std::istream* stream);
template <typename T>
void ReadBinaryLittleEndian(std::istream* stream, std::vector<T>* data);
// Write data in little endian format for cross-platform support.
template <typename T>
void WriteBinaryLittleEndian(std::ostream* stream, const T& data);
template <typename T>
void WriteBinaryLittleEndian(std::ostream* stream, const std::vector<T>& data);
////////////////////////////////////////////////////////////////////////////////
// Implementation
////////////////////////////////////////////////////////////////////////////////
template <typename T>
T ReverseBytes(const T& data) {
T data_reversed = data;
std::reverse(reinterpret_cast<char*>(&data_reversed),
reinterpret_cast<char*>(&data_reversed) + sizeof(T));
return data_reversed;
}
inline bool IsLittleEndian() {
#ifdef BOOST_BIG_ENDIAN
return false;
#else
return true;
#endif
}
inline bool IsBigEndian() {
#ifdef BOOST_BIG_ENDIAN
return true;
#else
return false;
#endif
}
template <typename T>
T LittleEndianToNative(const T x) {
if (IsLittleEndian()) {
return x;
} else {
return ReverseBytes(x);
}
}
template <typename T>
T BigEndianToNative(const T x) {
if (IsBigEndian()) {
return x;
} else {
return ReverseBytes(x);
}
}
template <typename T>
T NativeToLittleEndian(const T x) {
if (IsLittleEndian()) {
return x;
} else {
return ReverseBytes(x);
}
}
template <typename T>
T NativeToBigEndian(const T x) {
if (IsBigEndian()) {
return x;
} else {
return ReverseBytes(x);
}
}
template <typename T>
T ReadBinaryLittleEndian(std::istream* stream) {
T data_little_endian;
stream->read(reinterpret_cast<char*>(&data_little_endian), sizeof(T));
return LittleEndianToNative(data_little_endian);
}
template <typename T>
void ReadBinaryLittleEndian(std::istream* stream, std::vector<T>* data) {
for (size_t i = 0; i < data->size(); ++i) {
(*data)[i] = ReadBinaryLittleEndian<T>(stream);
}
}
template <typename T>
void WriteBinaryLittleEndian(std::ostream* stream, const T& data) {
const T data_little_endian = NativeToLittleEndian(data);
stream->write(reinterpret_cast<const char*>(&data_little_endian), sizeof(T));
}
template <typename T>
void WriteBinaryLittleEndian(std::ostream* stream, const std::vector<T>& data) {
for (const auto& elem : data) {
WriteBinaryLittleEndian<T>(stream, elem);
}
}
} // namespace colmap
#endif // COLMAP_SRC_UTIL_ENDIAN_H_

13
apps/InterfaceCOLMAPTest/CMakeLists.txt

@ -0,0 +1,13 @@ @@ -0,0 +1,13 @@
if(MSVC)
FILE(GLOB LIBRARY_FILES_C "*.cpp" "*.rc")
else()
FILE(GLOB LIBRARY_FILES_C "*.cpp")
endif()
FILE(GLOB LIBRARY_FILES_H "*.h" "*.inl")
cxx_executable_with_flags(InterfaceCOLMAP "Apps" "${cxx_default}" "MVS;${OpenMVS_EXTRA_LIBS}" ${LIBRARY_FILES_C} ${LIBRARY_FILES_H})
# Install
INSTALL(TARGETS InterfaceCOLMAP
EXPORT OpenMVSTargets
RUNTIME DESTINATION "${INSTALL_BIN_DIR}" COMPONENT bin)

1493
apps/InterfaceCOLMAPTest/InterfaceCOLMAPTest.cpp

File diff suppressed because it is too large Load Diff

167
apps/InterfaceCOLMAPTest/endian.h

@ -0,0 +1,167 @@ @@ -0,0 +1,167 @@
// Copyright (c) 2018, ETH Zurich and UNC Chapel Hill.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//
// * Neither the name of ETH Zurich and UNC Chapel Hill nor the names of
// its contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
//
// Author: Johannes L. Schoenberger (jsch-at-demuc-dot-de)
#ifndef COLMAP_SRC_UTIL_ENDIAN_H_
#define COLMAP_SRC_UTIL_ENDIAN_H_
#include <algorithm>
#include <vector>
#include <iostream>
namespace colmap {
// Reverse the order of each byte.
template <typename T>
T ReverseBytes(const T& data);
// Check the order in which bytes are stored in computer memory.
bool IsLittleEndian();
bool IsBigEndian();
// Convert data between endianness and the native format. Note that, for float
// and double types, these functions are only valid if the format is IEEE-754.
// This is the case for pretty much most processors.
template <typename T>
T LittleEndianToNative(const T x);
template <typename T>
T BigEndianToNative(const T x);
template <typename T>
T NativeToLittleEndian(const T x);
template <typename T>
T NativeToBigEndian(const T x);
// Read data in little endian format for cross-platform support.
template <typename T>
T ReadBinaryLittleEndian(std::istream* stream);
template <typename T>
void ReadBinaryLittleEndian(std::istream* stream, std::vector<T>* data);
// Write data in little endian format for cross-platform support.
template <typename T>
void WriteBinaryLittleEndian(std::ostream* stream, const T& data);
template <typename T>
void WriteBinaryLittleEndian(std::ostream* stream, const std::vector<T>& data);
////////////////////////////////////////////////////////////////////////////////
// Implementation
////////////////////////////////////////////////////////////////////////////////
template <typename T>
T ReverseBytes(const T& data) {
T data_reversed = data;
std::reverse(reinterpret_cast<char*>(&data_reversed),
reinterpret_cast<char*>(&data_reversed) + sizeof(T));
return data_reversed;
}
inline bool IsLittleEndian() {
#ifdef BOOST_BIG_ENDIAN
return false;
#else
return true;
#endif
}
inline bool IsBigEndian() {
#ifdef BOOST_BIG_ENDIAN
return true;
#else
return false;
#endif
}
template <typename T>
T LittleEndianToNative(const T x) {
if (IsLittleEndian()) {
return x;
} else {
return ReverseBytes(x);
}
}
template <typename T>
T BigEndianToNative(const T x) {
if (IsBigEndian()) {
return x;
} else {
return ReverseBytes(x);
}
}
template <typename T>
T NativeToLittleEndian(const T x) {
if (IsLittleEndian()) {
return x;
} else {
return ReverseBytes(x);
}
}
template <typename T>
T NativeToBigEndian(const T x) {
if (IsBigEndian()) {
return x;
} else {
return ReverseBytes(x);
}
}
template <typename T>
T ReadBinaryLittleEndian(std::istream* stream) {
T data_little_endian;
stream->read(reinterpret_cast<char*>(&data_little_endian), sizeof(T));
return LittleEndianToNative(data_little_endian);
}
template <typename T>
void ReadBinaryLittleEndian(std::istream* stream, std::vector<T>* data) {
for (size_t i = 0; i < data->size(); ++i) {
(*data)[i] = ReadBinaryLittleEndian<T>(stream);
}
}
template <typename T>
void WriteBinaryLittleEndian(std::ostream* stream, const T& data) {
const T data_little_endian = NativeToLittleEndian(data);
stream->write(reinterpret_cast<const char*>(&data_little_endian), sizeof(T));
}
template <typename T>
void WriteBinaryLittleEndian(std::ostream* stream, const std::vector<T>& data) {
for (const auto& elem : data) {
WriteBinaryLittleEndian<T>(stream, elem);
}
}
} // namespace colmap
#endif // COLMAP_SRC_UTIL_ENDIAN_H_

13
apps/InterfaceMVSNet/CMakeLists.txt

@ -0,0 +1,13 @@ @@ -0,0 +1,13 @@
if(MSVC)
FILE(GLOB LIBRARY_FILES_C "*.cpp" "*.rc")
else()
FILE(GLOB LIBRARY_FILES_C "*.cpp")
endif()
FILE(GLOB LIBRARY_FILES_H "*.h" "*.inl")
cxx_executable_with_flags(InterfaceMVSNet "Apps" "${cxx_default}" "MVS;${OpenMVS_EXTRA_LIBS}" ${LIBRARY_FILES_C} ${LIBRARY_FILES_H})
# Install
INSTALL(TARGETS InterfaceMVSNet
EXPORT OpenMVSTargets
RUNTIME DESTINATION "${INSTALL_BIN_DIR}" COMPONENT bin)

706
apps/InterfaceMVSNet/InterfaceMVSNet.cpp

@ -0,0 +1,706 @@ @@ -0,0 +1,706 @@
/*
* InterfaceMVSNet.cpp
*
* Copyright (c) 2014-2021 SEACAVE
*
* Author(s):
*
* cDc <cdc.seacave@gmail.com>
*
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*
* Additional Terms:
*
* You are required to preserve legal notices and author attributions in
* that material or in the Appropriate Legal Notices displayed by works
* containing it.
*/
#include "../../libs/MVS/Common.h"
#include "../../libs/MVS/Scene.h"
#define JSON_NOEXCEPTION
#include "../../libs/IO/json.hpp"
#include <boost/program_options.hpp>
using namespace MVS;
// D E F I N E S ///////////////////////////////////////////////////
// uncomment to enable multi-threading based on OpenMP
#ifdef _USE_OPENMP
#define MVSNET_USE_OPENMP
#endif
#define APPNAME _T("InterfaceMVSNet")
#define MVS_FILE_EXTENSION _T(".mvs")
#define MVSNET_IMAGES_FOLDER _T("images")
#define MVSNET_CAMERAS_FOLDER _T("cams")
#define MVSNET_IMAGES_EXT _T(".jpg")
#define MVSNET_CAMERAS_NAME _T("_cam.txt")
#define RTMV_CAMERAS_EXT _T(".json")
#define NERFSTUDIO_TRANSFORMS _T("transforms.json")
// S T R U C T S ///////////////////////////////////////////////////
namespace {
namespace OPT {
String strInputFileName;
String strOutputFileName;
unsigned nArchiveType;
int nProcessPriority;
unsigned nMaxThreads;
String strConfigFileName;
boost::program_options::variables_map vm;
} // namespace OPT
class Application {
public:
Application() {}
~Application() { Finalize(); }
bool Initialize(size_t argc, LPCTSTR* argv);
void Finalize();
}; // Application
// initialize and parse the command line parameters
bool Application::Initialize(size_t argc, LPCTSTR* argv)
{
// initialize log and console
OPEN_LOG();
OPEN_LOGCONSOLE();
// group of options allowed only on command line
boost::program_options::options_description generic("Generic options");
generic.add_options()
("help,h", "produce this help message")
("working-folder,w", boost::program_options::value<std::string>(&WORKING_FOLDER), "working directory (default current directory)")
("config-file,c", boost::program_options::value<std::string>(&OPT::strConfigFileName)->default_value(APPNAME _T(".cfg")), "file name containing program options")
("archive-type", boost::program_options::value(&OPT::nArchiveType)->default_value(ARCHIVE_MVS), "project archive type: -1-interface, 0-text, 1-binary, 2-compressed binary")
("process-priority", boost::program_options::value(&OPT::nProcessPriority)->default_value(-1), "process priority (below normal by default)")
("max-threads", boost::program_options::value(&OPT::nMaxThreads)->default_value(0), "maximum number of threads (0 for using all available cores)")
#if TD_VERBOSE != TD_VERBOSE_OFF
("verbosity,v", boost::program_options::value(&g_nVerbosityLevel)->default_value(
#if TD_VERBOSE == TD_VERBOSE_DEBUG
3
#else
2
#endif
), "verbosity level")
#endif
;
// group of options allowed both on command line and in config file
boost::program_options::options_description config("Main options");
config.add_options()
("input-file,i", boost::program_options::value<std::string>(&OPT::strInputFileName), "input filename containing camera poses and image list")
("output-file,o", boost::program_options::value<std::string>(&OPT::strOutputFileName), "output filename for storing the mesh")
;
boost::program_options::options_description cmdline_options;
cmdline_options.add(generic).add(config);
boost::program_options::options_description config_file_options;
config_file_options.add(config);
boost::program_options::positional_options_description p;
p.add("input-file", -1);
try {
// parse command line options
boost::program_options::store(boost::program_options::command_line_parser((int)argc, argv).options(cmdline_options).positional(p).run(), OPT::vm);
boost::program_options::notify(OPT::vm);
INIT_WORKING_FOLDER;
// parse configuration file
std::ifstream ifs(MAKE_PATH_SAFE(OPT::strConfigFileName));
if (ifs) {
boost::program_options::store(parse_config_file(ifs, config_file_options), OPT::vm);
boost::program_options::notify(OPT::vm);
}
}
catch (const std::exception& e) {
LOG(e.what());
return false;
}
// initialize the log file
OPEN_LOGFILE(MAKE_PATH(APPNAME _T("-") + Util::getUniqueName(0) + _T(".log")).c_str());
// print application details: version and command line
Util::LogBuild();
LOG(_T("Command line: ") APPNAME _T("%s"), Util::CommandLineToString(argc, argv).c_str());
// validate input
Util::ensureValidPath(OPT::strInputFileName);
const String strInputFileNameExt(Util::getFileExt(OPT::strInputFileName).ToLower());
const bool bInvalidCommand(OPT::strInputFileName.empty());
if (OPT::vm.count("help") || bInvalidCommand) {
boost::program_options::options_description visible("Available options");
visible.add(generic).add(config);
GET_LOG() << visible;
}
if (bInvalidCommand)
return false;
// initialize optional options
Util::ensureValidPath(OPT::strOutputFileName);
if (OPT::strOutputFileName.empty())
OPT::strOutputFileName = Util::getFileName(OPT::strInputFileName) + "scene" MVS_FILE_EXTENSION;
MVS::Initialize(APPNAME, OPT::nMaxThreads, OPT::nProcessPriority);
return true;
}
// finalize application instance
void Application::Finalize()
{
MVS::Finalize();
CLOSE_LOGFILE();
CLOSE_LOGCONSOLE();
CLOSE_LOG();
}
void ImageListParse(const LPSTR* argv, Point3& C)
{
// read position vector
C.x = String::FromString<REAL>(argv[0]);
C.y = String::FromString<REAL>(argv[1]);
C.z = String::FromString<REAL>(argv[2]);
}
void ImageListParse(const LPSTR* argv, Matrix3x3& R)
{
// read rotation matrix
R(0, 0) = String::FromString<REAL>(argv[0]);
R(0, 1) = String::FromString<REAL>(argv[1]);
R(0, 2) = String::FromString<REAL>(argv[2]);
R(1, 0) = String::FromString<REAL>(argv[3]);
R(1, 1) = String::FromString<REAL>(argv[4]);
R(1, 2) = String::FromString<REAL>(argv[5]);
R(2, 0) = String::FromString<REAL>(argv[6]);
R(2, 1) = String::FromString<REAL>(argv[7]);
R(2, 2) = String::FromString<REAL>(argv[8]);
}
void ImageListParse(const LPSTR* argv, Matrix3x4& P)
{
// read projection matrix
P(0, 0) = String::FromString<REAL>(argv[0]);
P(0, 1) = String::FromString<REAL>(argv[1]);
P(0, 2) = String::FromString<REAL>(argv[2]);
P(0, 3) = String::FromString<REAL>(argv[3]);
P(1, 0) = String::FromString<REAL>(argv[4]);
P(1, 1) = String::FromString<REAL>(argv[5]);
P(1, 2) = String::FromString<REAL>(argv[6]);
P(1, 3) = String::FromString<REAL>(argv[7]);
P(2, 0) = String::FromString<REAL>(argv[8]);
P(2, 1) = String::FromString<REAL>(argv[9]);
P(2, 2) = String::FromString<REAL>(argv[10]);
P(2, 3) = String::FromString<REAL>(argv[11]);
}
// convert a range-map to depth-map
void RangeToDepthMap(const Image32F& rangeMap, const Camera& camera, DepthMap& depthMap)
{
depthMap.create(rangeMap.size());
for (int y = 0; y < depthMap.rows; ++y) {
const float* const rangeRow = rangeMap.ptr<float>(y);
float* const depthRow = depthMap.ptr<float>(y);
for (int x = 0; x < depthMap.cols; ++x) {
const float range = rangeRow[x];
depthRow[x] = (Depth)(range <= 0 ? 0 : normalized(camera.TransformPointI2C(Point2(x,y))).z*range);
}
}
}
// parse scene stored in MVSNet format composed of undistorted images and camera poses
// for example see GigaVision benchmark (http://gigamvs.net):
// |--sceneX
// |--images
// |--xxx.jpg
// |--xxx.jpg
// ....
// |--cams
// |--xxx_cam.txt
// |--xxx_cam.txt
// ....
// |--render_cams
// |--xxx_cam.txt
// |--xxx_cam.txt
// ....
//
// where the camera parameter of one image stored in a cam.txt file contains the camera
// extrinsic E = [R|t], intrinsic K and the depth range:
// extrinsic
// E00 E01 E02 E03
// E10 E11 E12 E13
// E20 E21 E22 E23
// E30 E31 E32 E33
//
// intrinsic
// K00 K01 K02
// K10 K11 K12
// K20 K21 K22
//
// DEPTH_MIN DEPTH_INTERVAL (DEPTH_NUM DEPTH_MAX)
bool ParseSceneMVSNet(Scene& scene, const String& strPath)
{
#if defined(_SUPPORT_CPP17) && (!defined(__GNUC__) || (__GNUC__ > 7))
IIndex prevPlatformID = NO_ID;
for (const std::filesystem::directory_entry& entry : std::filesystem::directory_iterator((strPath + MVSNET_IMAGES_FOLDER).c_str())) {
if (entry.path().extension() != MVSNET_IMAGES_EXT)
continue;
// parse camera
const std::string strCamFileName(strPath + MVSNET_CAMERAS_FOLDER PATH_SEPARATOR_STR + entry.path().stem().string().c_str() + MVSNET_CAMERAS_NAME);
std::ifstream fcam(strCamFileName);
if (!fcam)
continue;
String line;
do {
std::getline(fcam, line);
} while (line != "extrinsic" && fcam.good());
String strP;
for (int i = 0; i < 3; ++i) {
std::getline(fcam, line);
strP += ' ' + line;
}
if (!fcam.good())
continue;
size_t argc;
CAutoPtrArr<LPSTR> argv;
argv = Util::CommandLineToArgvA(strP, argc);
if (argc != 12)
continue;
Matrix3x4 P;
ImageListParse(argv, P);
do {
std::getline(fcam, line);
} while (line != "intrinsic" && fcam.good());
strP.clear();
for (int i = 0; i < 3; ++i) {
std::getline(fcam, line);
strP += ' ' + line;
}
if (!fcam.good())
continue;
argv = Util::CommandLineToArgvA(strP, argc);
if (argc != 9)
continue;
Matrix3x3 K;
ImageListParse(argv, K);
// setup camera
IIndex platformID;
if (prevPlatformID == NO_ID || !K.IsEqual(scene.platforms[prevPlatformID].cameras[0].K, 1e-3)) {
prevPlatformID = platformID = scene.platforms.size();
Platform& platform = scene.platforms.emplace_back();
Platform::Camera& camera = platform.cameras.emplace_back();
camera.K = K;
camera.R = RMatrix::IDENTITY;
camera.C = CMatrix::ZERO;
} else {
platformID = prevPlatformID;
}
Platform& platform = scene.platforms[platformID];
// setup image
const IIndex ID = scene.images.size();
Image& imageData = scene.images.emplace_back();
imageData.platformID = platformID;
imageData.cameraID = 0; // only one camera per platform supported by this format
imageData.poseID = NO_ID;
imageData.ID = ID;
imageData.name = entry.path().string();
Util::ensureUnifySlash(imageData.name);
imageData.name = MAKE_PATH_FULL(strPath, imageData.name);
// set image resolution
IMAGEPTR pimage = Image::ReadImageHeader(imageData.name);
imageData.width = pimage->GetWidth();
imageData.height = pimage->GetHeight();
imageData.scale = 1;
// set camera pose
imageData.poseID = platform.poses.size();
Platform::Pose& pose = platform.poses.emplace_back();
DecomposeProjectionMatrix(P, pose.R, pose.C);
imageData.camera = platform.GetCamera(imageData.cameraID, imageData.poseID);
}
if (scene.images.size() < 2)
return false;
scene.nCalibratedImages = (unsigned)scene.images.size();
return true;
#else
VERBOSE("error: C++17 is required to parse MVSNet format");
return false;
#endif // _SUPPORT_CPP17
}
// RTMV scene format: http://www.cs.umd.edu/~mmeshry/projects/rtmv
// |--sceneX
// |--images
// |--xxx.jpg
// |--xxx.jpg
// ....
// |--outputs (optional)
// |--depthxxxx.exr
// |--normalxxxx.exr
// ....
// |--transforms.json
bool ParseSceneNerfstudio(Scene& scene, const String& strPath)
{
const nlohmann::json data = nlohmann::json::parse(std::ifstream(strPath + NERFSTUDIO_TRANSFORMS));
if (data.empty())
return false;
// parse camera
const cv::Size resolution(data["w"].get<uint32_t>(), data["h"].get<uint32_t>());
const IIndex platformID = scene.platforms.size();
Platform& platform = scene.platforms.emplace_back();
Platform::Camera& camera = platform.cameras.emplace_back();
camera.K = KMatrix::IDENTITY;
camera.R = RMatrix::IDENTITY;
camera.C = CMatrix::ZERO;
camera.K(0,0) = data["fl_x"].get<REAL>();
camera.K(1,1) = data["fl_y"].get<REAL>();
camera.K(0,2) = data["cx"].get<REAL>();
camera.K(1,2) = data["cy"].get<REAL>();
const String cameraModel = data["camera_model"].get<std::string>();
if (cameraModel == "SIMPLE_PINHOLE") {
} else
// check ZERO radial distortion for all "PERSPECTIVE" type cameras
if (cameraModel == "PINHOLE" || cameraModel == "SIMPLE_RADIAL" || cameraModel == "RADIAL" || cameraModel == "OPENCV") {
const REAL k1 = data["k1"].get<REAL>();
const REAL k2 = data["k2"].get<REAL>();
const REAL p1 = data["p1"].get<REAL>();
const REAL p2 = data["p2"].get<REAL>();
if (k1 != 0 || k2 != 0 || p1 != 0 || p2 != 0) {
VERBOSE("error: radial distortion not supported");
return false;
}
} else {
VERBOSE("error: camera model not supported");
return false;
}
// parse images
const nlohmann::json& frames = data["frames"];
for (const nlohmann::json& frame: frames) {
// set image
// frames expected to be ordered in JSON
const IIndex imageID = scene.images.size();
const String strFileName(strPath + frame["file_path"].get<std::string>().c_str());
Image& imageData = scene.images.emplace_back();
imageData.platformID = platformID;
imageData.cameraID = 0; // only one camera per platform supported by this format
imageData.poseID = NO_ID;
imageData.ID = imageID;
imageData.name = strFileName;
ASSERT(Util::isFullPath(imageData.name));
// set image resolution
imageData.width = resolution.width;
imageData.height = resolution.height;
imageData.scale = 1;
// load camera pose
imageData.poseID = platform.poses.size();
Platform::Pose& pose = platform.poses.emplace_back();
const auto Ps = frame["transform_matrix"].get<std::vector<std::vector<double>>>();
Eigen::Matrix4d P{
{Ps[0][0], Ps[0][1], Ps[0][2], Ps[0][3]},
{Ps[1][0], Ps[1][1], Ps[1][2], Ps[1][3]},
{Ps[2][0], Ps[2][1], Ps[2][2], Ps[2][3]},
{Ps[3][0], Ps[3][1], Ps[3][2], Ps[3][3]}
};
// revert nerfstudio conversion:
// convert from COLMAP's camera coordinate system (OpenCV) to ours (OpenGL)
// c2w[0:3, 1:3] *= -1
// c2w = c2w[np.array([1, 0, 2, 3]), :]
// c2w[2, :] *= -1
P.row(2) *= -1;
P.row(0).swap(P.row(1));
P.col(2) *= -1;
P.col(1) *= -1;
// set camera pose
pose.R = P.topLeftCorner<3, 3>().transpose().eval();
pose.R.EnforceOrthogonality();
pose.C = P.topRightCorner<3, 1>().eval();
imageData.camera = platform.GetCamera(imageData.cameraID, imageData.poseID);
// try reading depth-map and normal-map
DepthMap depthMap; {
const String depthPath(strPath + String::FormatString("outputs/depth%04u.exr", imageID));
const Image32F rangeMap = cv::imread(depthPath, cv::IMREAD_UNCHANGED);
if (rangeMap.empty()) {
VERBOSE("Unable to load depthmap %s.", depthPath.c_str());
continue;
}
RangeToDepthMap(rangeMap, imageData.camera, depthMap);
}
NormalMap normalMap; {
const String normalPath(strPath + String::FormatString("outputs/normal%04u.exr", imageID));
normalMap = cv::imread(normalPath, cv::IMREAD_UNCHANGED);
if (normalMap.empty()) {
VERBOSE("Unable to load normalMap %s.", normalPath.c_str());
continue;
}
}
const ConfidenceMap confMap;
const ViewsMap viewsMap;
const IIndexArr IDs = {imageID};
double dMin, dMax;
cv::minMaxIdx(depthMap, &dMin, &dMax, NULL, NULL, depthMap > 0);
const String dmapPath(strPath + String::FormatString("depth%04u.dmap", imageID));
if (!ExportDepthDataRaw(dmapPath,
imageData.name, IDs, resolution,
camera.K, pose.R, pose.C,
(float)dMin, (float)dMax,
depthMap, normalMap, confMap, viewsMap))
{
VERBOSE("Unable to save dmap: %s", dmapPath.c_str());
continue;
}
}
if (scene.images.size() < 2)
return false;
scene.nCalibratedImages = (unsigned)scene.images.size();
return true;
}
// RTMV scene format: http://www.cs.umd.edu/~mmeshry/projects/rtmv
// |--sceneX
// |--xxx.exr
// |--xxx.seg.exr
// |--xxx.depth.exr
// |--xxx.json
// ....
bool ParseSceneRTMV(Scene& scene, const String& strPath)
{
const String strImagePath(strPath + "images/");
Util::ensureFolder(strImagePath);
std::vector<String> strImageNames;
#if defined(_SUPPORT_CPP17) && (!defined(__GNUC__) || (__GNUC__ > 7))
for (const std::filesystem::directory_entry& entry : std::filesystem::directory_iterator(strPath.c_str())) {
if (entry.path().extension() != RTMV_CAMERAS_EXT)
continue;
strImageNames.emplace_back(entry.path().stem().string());
}
#else
VERBOSE("error: C++17 is required to parse RTMV format");
return false;
#endif // _SUPPORT_CPP17
IIndex prevPlatformID = NO_ID;
scene.images.resize((IIndex)strImageNames.size());
scene.platforms.reserve((IIndex)strImageNames.size());
#ifdef MVSNET_USE_OPENMP
#pragma omp parallel for schedule(dynamic)
for (int_t i=0; i<(int_t)strImageNames.size(); ++i) {
#else
FOREACH(i, strImageNames) {
#endif
const IIndex imageID((IIndex)i);
const String& strImageName(strImageNames[imageID]);
// parse camera
const String strFileName(strPath + strImageName);
const nlohmann::json dataCamera = nlohmann::json::parse(std::ifstream(strFileName+RTMV_CAMERAS_EXT));
if (dataCamera.empty())
continue;
const nlohmann::json& data = dataCamera["camera_data"];
const cv::Size resolution(data["width"].get<uint32_t>(), data["height"].get<uint32_t>());
// set platform
Matrix3x3 K = Matrix3x3::IDENTITY;
K(0,0) = data["intrinsics"]["fx"].get<REAL>();
K(1,1) = data["intrinsics"]["fy"].get<REAL>();
K(0,2) = data["intrinsics"]["cx"].get<REAL>();
K(1,2) = data["intrinsics"]["cy"].get<REAL>();
IIndex platformID;
if (prevPlatformID == NO_ID || !K.IsEqual(scene.platforms[prevPlatformID].cameras[0].K, 1e-3)) {
#ifdef MVSNET_USE_OPENMP
#pragma omp critical
#endif
{
prevPlatformID = platformID = scene.platforms.size();
Platform& platform = scene.platforms.emplace_back();
Platform::Camera& camera = platform.cameras.emplace_back();
platform.poses.reserve((IIndex)strImageNames.size());
camera.K = K;
camera.R = RMatrix::IDENTITY;
camera.C = CMatrix::ZERO;
}
} else {
platformID = prevPlatformID;
}
Platform& platform = scene.platforms[platformID];
// set image
Image& imageData = scene.images[imageID];
imageData.platformID = platformID;
imageData.cameraID = 0; // only one camera per platform supported by this format
imageData.poseID = NO_ID;
imageData.ID = imageID;
imageData.name = strImagePath+strImageName+".jpg";
ASSERT(Util::isFullPath(imageData.name));
{
cv::Mat image = cv::imread(strFileName+".exr", cv::IMREAD_UNCHANGED);
ASSERT(image.type() == CV_32FC4);
std::vector<cv::Mat> channels;
cv::split(image, channels);
cv::merge(std::vector<cv::Mat>{channels[0], channels[1], channels[2]}, image);
image.convertTo(imageData.image, CV_8UC3, 255);
}
ASSERT(resolution == imageData.image.size());
if (imageData.image.empty()) {
VERBOSE("Unable to load image %s.", (strFileName+".exr").c_str());
continue;
}
cv::imwrite(imageData.name, imageData.image);
imageData.ReleaseImage();
// set image resolution
imageData.width = resolution.width;
imageData.height = resolution.height;
imageData.scale = 1;
// load camera pose
#ifdef MVSNET_USE_OPENMP
#pragma omp critical
#endif
{
imageData.poseID = platform.poses.size();
platform.poses.emplace_back();
}
Platform::Pose& pose = platform.poses[imageData.poseID];
const auto Ps = data["cam2world"].get<std::vector<std::vector<double>>>();
Eigen::Matrix4d P{
{Ps[0][0], Ps[1][0], Ps[2][0], Ps[3][0]},
{Ps[0][1], Ps[1][1], Ps[2][1], Ps[3][1]},
{Ps[0][2], Ps[1][2], Ps[2][2], Ps[3][2]},
{Ps[0][3], Ps[1][3], Ps[2][3], Ps[3][3]}
};
// apply the same transforms as nerfstudio converter
P.row(2) *= -1;
P.row(0).swap(P.row(1));
P.col(2) *= -1;
P.col(1) *= -1;
// set camera pose
pose.R = P.topLeftCorner<3, 3>().transpose().eval();
pose.R.EnforceOrthogonality();
pose.C = P.topRightCorner<3, 1>().eval();
imageData.camera = platform.GetCamera(imageData.cameraID, imageData.poseID);
// try reading the segmentation mask
{
cv::Mat imgMask = cv::imread(strFileName+".seg.exr", cv::IMREAD_UNCHANGED);
if (imgMask.empty()) {
VERBOSE("Unable to load segmentation mask %s.", (strFileName+".seg.exr").c_str());
continue;
}
ASSERT(imgMask.type() == CV_32FC4);
ASSERT(resolution == imgMask.size());
std::vector<cv::Mat> channels;
cv::split(imgMask, channels);
channels[0].convertTo(imgMask, CV_16U);
imageData.maskName = strImagePath+strImageName+".mask.png";
cv::imwrite(imageData.maskName, imgMask);
}
// try reading the depth-map
DepthMap depthMap; {
const cv::Mat imgDepthMap = cv::imread(strFileName+".depth.exr", cv::IMREAD_UNCHANGED);
if (imgDepthMap.empty()) {
VERBOSE("Unable to load depthmap %s.", (strFileName+".depth.exr").c_str());
continue;
}
ASSERT(imgDepthMap.type() == CV_32FC4);
ASSERT(resolution == imgDepthMap.size());
std::vector<cv::Mat> channels;
cv::split(imgDepthMap, channels);
RangeToDepthMap(channels[0], imageData.camera, depthMap);
}
const NormalMap normalMap;
const ConfidenceMap confMap;
const ViewsMap viewsMap;
const IIndexArr IDs = {imageID};
double dMin, dMax;
cv::minMaxIdx(depthMap, &dMin, &dMax, NULL, NULL, depthMap > 0);
const String dmapPath(strPath + String::FormatString("depth%04u.dmap", imageID));
if (!ExportDepthDataRaw(dmapPath,
imageData.name, IDs, resolution,
K, pose.R, pose.C,
(float)dMin, (float)dMax,
depthMap, normalMap, confMap, viewsMap))
{
VERBOSE("Unable to save dmap: %s", dmapPath.c_str());
continue;
}
}
if (scene.images.size() < 2)
return false;
scene.nCalibratedImages = (unsigned)scene.images.size();
return true;
}
bool ParseScene(Scene& scene)
{
#if defined(_SUPPORT_CPP17) && (!defined(__GNUC__) || (__GNUC__ > 7))
String strPath(MAKE_PATH_FULL(WORKING_FOLDER_FULL, OPT::strInputFileName));
Util::ensureValidFolderPath(strPath);
const std::filesystem::path path(static_cast<const std::string&>(strPath));
enum Type {
MVSNet = 0,
NERFSTUDIO,
RTMV,
} sceneType = MVSNet;
for (const std::filesystem::directory_entry& entry : std::filesystem::directory_iterator(path)) {
if (entry.path().extension() == RTMV_CAMERAS_EXT) {
if (entry.path().filename() == NERFSTUDIO_TRANSFORMS) {
sceneType = NERFSTUDIO;
break;
}
sceneType = RTMV;
}
}
switch (sceneType) {
case NERFSTUDIO: return ParseSceneNerfstudio(scene, strPath);
case RTMV: return ParseSceneRTMV(scene, strPath);
default: return ParseSceneMVSNet(scene, strPath);
}
#else
VERBOSE("error: C++17 is required to parse MVSNet format");
return false;
#endif // _SUPPORT_CPP17
}
} // unnamed namespace
int main(int argc, LPCTSTR* argv)
{
#ifdef _DEBUGINFO
// set _crtBreakAlloc index to stop in <dbgheap.c> at allocation
_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);// | _CRTDBG_CHECK_ALWAYS_DF);
#endif
Application application;
if (!application.Initialize(argc, argv))
return EXIT_FAILURE;
TD_TIMER_START();
Scene scene(OPT::nMaxThreads);
// convert data from MVSNet format to OpenMVS
if (!ParseScene(scene))
return EXIT_FAILURE;
// write OpenMVS input data
scene.Save(MAKE_PATH_SAFE(OPT::strOutputFileName), (ARCHIVE_TYPE)OPT::nArchiveType);
VERBOSE("Imported data: %u platforms, %u images, %u vertices (%s)",
scene.platforms.size(), scene.images.size(), scene.pointcloud.GetSize(),
TD_TIMER_GET_FMT().c_str());
return EXIT_SUCCESS;
}
/*----------------------------------------------------------------*/

13
apps/InterfaceMetashape/CMakeLists.txt

@ -0,0 +1,13 @@ @@ -0,0 +1,13 @@
if(MSVC)
FILE(GLOB LIBRARY_FILES_C "*.cpp" "*.rc")
else()
FILE(GLOB LIBRARY_FILES_C "*.cpp")
endif()
FILE(GLOB LIBRARY_FILES_H "*.h" "*.inl")
cxx_executable_with_flags(InterfaceMetashape "Apps" "${cxx_default}" "MVS;${OpenMVS_EXTRA_LIBS}" ${LIBRARY_FILES_C} ${LIBRARY_FILES_H})
# Install
INSTALL(TARGETS InterfaceMetashape
EXPORT OpenMVSTargets
RUNTIME DESTINATION "${INSTALL_BIN_DIR}" COMPONENT bin)

887
apps/InterfaceMetashape/InterfaceMetashape.cpp

@ -0,0 +1,887 @@ @@ -0,0 +1,887 @@
/*
* InterfaceMetashape.cpp
*
* Copyright (c) 2014-2021 SEACAVE
*
* Author(s):
*
* cDc <cdc.seacave@gmail.com>
*
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*
* Additional Terms:
*
* You are required to preserve legal notices and author attributions in
* that material or in the Appropriate Legal Notices displayed by works
* containing it.
*/
#include "../../libs/MVS/Common.h"
#include "../../libs/MVS/Scene.h"
#include "../../libs/IO/TinyXML2.h"
#include <boost/program_options.hpp>
using namespace MVS;
// D E F I N E S ///////////////////////////////////////////////////
#define APPNAME _T("InterfaceMetashape") // previously PhotoScan
#define MVS_FILE_EXTENSION _T(".mvs")
#define XML_EXT _T(".xml")
#define PLY_EXT _T(".ply")
// S T R U C T S ///////////////////////////////////////////////////
namespace {
namespace OPT {
String strInputFileName;
String strPointsFileName;
String strOutputFileName;
String strOutputImageFolder;
unsigned nArchiveType;
int nProcessPriority;
unsigned nMaxThreads;
String strConfigFileName;
boost::program_options::variables_map vm;
} // namespace OPT
class Application {
public:
Application() {}
~Application() { Finalize(); }
bool Initialize(size_t argc, LPCTSTR* argv);
void Finalize();
}; // Application
// initialize and parse the command line parameters
bool Application::Initialize(size_t argc, LPCTSTR* argv)
{
// initialize log and console
OPEN_LOG();
OPEN_LOGCONSOLE();
// group of options allowed only on command line
boost::program_options::options_description generic("Generic options");
generic.add_options()
("help,h", "imports SfM scene stored either in Metashape Agisoft/BlocksExchange or ContextCapture BlocksExchange XML format")
("working-folder,w", boost::program_options::value<std::string>(&WORKING_FOLDER), "working directory (default current directory)")
("config-file,c", boost::program_options::value<std::string>(&OPT::strConfigFileName)->default_value(APPNAME _T(".cfg")), "file name containing program options")
("archive-type", boost::program_options::value(&OPT::nArchiveType)->default_value(ARCHIVE_MVS), "project archive type: -1-interface, 0-text, 1-binary, 2-compressed binary")
("process-priority", boost::program_options::value(&OPT::nProcessPriority)->default_value(-1), "process priority (below normal by default)")
("max-threads", boost::program_options::value(&OPT::nMaxThreads)->default_value(0), "maximum number of threads (0 for using all available cores)")
#if TD_VERBOSE != TD_VERBOSE_OFF
("verbosity,v", boost::program_options::value(&g_nVerbosityLevel)->default_value(
#if TD_VERBOSE == TD_VERBOSE_DEBUG
3
#else
2
#endif
), "verbosity level")
#endif
;
// group of options allowed both on command line and in config file
boost::program_options::options_description config("Main options");
config.add_options()
("input-file,i", boost::program_options::value<std::string>(&OPT::strInputFileName), "input filename containing camera poses and image list")
("points-file,p", boost::program_options::value<std::string>(&OPT::strPointsFileName), "input filename containing the 3D points")
("output-file,o", boost::program_options::value<std::string>(&OPT::strOutputFileName), "output filename for storing the scene")
("output-image-folder", boost::program_options::value<std::string>(&OPT::strOutputImageFolder)->default_value("undistorted_images"), "output folder to store undistorted images")
;
boost::program_options::options_description cmdline_options;
cmdline_options.add(generic).add(config);
boost::program_options::options_description config_file_options;
config_file_options.add(config);
boost::program_options::positional_options_description p;
p.add("input-file", -1);
try {
// parse command line options
boost::program_options::store(boost::program_options::command_line_parser((int)argc, argv).options(cmdline_options).positional(p).run(), OPT::vm);
boost::program_options::notify(OPT::vm);
INIT_WORKING_FOLDER;
// parse configuration file
std::ifstream ifs(MAKE_PATH_SAFE(OPT::strConfigFileName));
if (ifs) {
boost::program_options::store(parse_config_file(ifs, config_file_options), OPT::vm);
boost::program_options::notify(OPT::vm);
}
}
catch (const std::exception& e) {
LOG(e.what());
return false;
}
// initialize the log file
OPEN_LOGFILE(MAKE_PATH(APPNAME _T("-")+Util::getUniqueName(0)+_T(".log")).c_str());
// print application details: version and command line
Util::LogBuild();
LOG(_T("Command line: ") APPNAME _T("%s"), Util::CommandLineToString(argc, argv).c_str());
// validate input
Util::ensureValidPath(OPT::strPointsFileName);
Util::ensureValidPath(OPT::strInputFileName);
Util::ensureValidFolderPath(OPT::strOutputImageFolder);
const bool bInvalidCommand(OPT::strInputFileName.empty());
if (OPT::vm.count("help") || bInvalidCommand) {
boost::program_options::options_description visible("Available options");
visible.add(generic).add(config);
GET_LOG() << visible;
}
if (bInvalidCommand)
return false;
// initialize optional options
Util::ensureValidPath(OPT::strOutputFileName);
if (OPT::strOutputFileName.empty())
OPT::strOutputFileName = Util::getFileName(OPT::strInputFileName) + MVS_FILE_EXTENSION;
MVS::Initialize(APPNAME, OPT::nMaxThreads, OPT::nProcessPriority);
return true;
}
// finalize application instance
void Application::Finalize()
{
MVS::Finalize();
CLOSE_LOGFILE();
CLOSE_LOGCONSOLE();
CLOSE_LOG();
}
struct DistCoeff {
union {
REAL coeff[8];
struct {
REAL k1, k2, p1, p2, k3, k4, k5, k6;
};
};
DistCoeff() : k1(0), k2(0), p1(0), p2(0), k3(0), k4(0), k5(0), k6(0) {}
bool HasDistortion() const { return k1 != 0 || k2 != 0 || k3 != 0 || k4 != 0 || k5 != 0 || k6 != 0; }
};
typedef cList<DistCoeff> DistCoeffs;
typedef cList<DistCoeffs> PlatformDistCoeffs;
void ImageListParseC(const LPSTR* argv, Point3& C)
{
// read position vector
C.x = String::FromString<REAL>(argv[0]);
C.y = String::FromString<REAL>(argv[1]);
C.z = String::FromString<REAL>(argv[2]);
}
void ImageListParseR(const LPSTR* argv, Matrix3x3& R)
{
// read rotation matrix
R(0, 0) = String::FromString<REAL>(argv[0]);
R(0, 1) = String::FromString<REAL>(argv[1]);
R(0, 2) = String::FromString<REAL>(argv[2]);
R(1, 0) = String::FromString<REAL>(argv[3]);
R(1, 1) = String::FromString<REAL>(argv[4]);
R(1, 2) = String::FromString<REAL>(argv[5]);
R(2, 0) = String::FromString<REAL>(argv[6]);
R(2, 1) = String::FromString<REAL>(argv[7]);
R(2, 2) = String::FromString<REAL>(argv[8]);
}
void ImageListParseP(const LPSTR* argv, Matrix3x4& P)
{
// read projection matrix
P(0, 0) = String::FromString<REAL>(argv[0]);
P(0, 1) = String::FromString<REAL>(argv[1]);
P(0, 2) = String::FromString<REAL>(argv[2]);
P(0, 3) = String::FromString<REAL>(argv[3]);
P(1, 0) = String::FromString<REAL>(argv[4]);
P(1, 1) = String::FromString<REAL>(argv[5]);
P(1, 2) = String::FromString<REAL>(argv[6]);
P(1, 3) = String::FromString<REAL>(argv[7]);
P(2, 0) = String::FromString<REAL>(argv[8]);
P(2, 1) = String::FromString<REAL>(argv[9]);
P(2, 2) = String::FromString<REAL>(argv[10]);
P(2, 3) = String::FromString<REAL>(argv[11]);
}
// parse images list containing calibration and pose information
// and load the corresponding 3D point-cloud
bool ParseImageListXML(tinyxml2::XMLDocument& doc, Scene& scene, PlatformDistCoeffs& pltDistCoeffs, size_t& nCameras, size_t& nPoses)
{
String strInputFileName(MAKE_PATH_FULL(WORKING_FOLDER_FULL, OPT::strInputFileName));
Util::ensureValidPath(strInputFileName);
tinyxml2::XMLElement* elem;
if (doc.ErrorID() != tinyxml2::XML_SUCCESS)
goto InvalidDocument;
{
tinyxml2::XMLElement* document = doc.FirstChildElement(_T("document"))->FirstChildElement(_T("chunk"));
if (document == NULL)
goto InvalidDocument;
{
bool bMetashapeFile(false);
CLISTDEF0(cv::Size) resolutions;
std::unordered_map<IIndex,IIndex> mapPlatformID;
std::unordered_map<IIndex,IIndex> mapImageID;
// parse platform and camera models
{
tinyxml2::XMLElement* sensors = document->FirstChildElement(_T("sensors"));
if (sensors == NULL)
goto InvalidDocument;
{
for (tinyxml2::XMLElement* sensor=sensors->FirstChildElement(); sensor!=NULL; sensor=sensor->NextSiblingElement()) {
unsigned ID;
if (0 != _tcsicmp(sensor->Value(), _T("sensor")) || sensor->QueryUnsignedAttribute(_T("id"), &ID) != tinyxml2::XML_SUCCESS)
goto InvalidDocument;
{
// add new camera
enum CameraModel {METASHAPE=0, VSFM};
int model(METASHAPE);
sensor->QueryIntAttribute(_T("model"), &model);
mapPlatformID.emplace(ID, scene.platforms.size());
Platform& platform = scene.platforms.AddEmpty();
LPCTSTR name;
if ((name=sensor->Attribute(_T("label"))) != NULL)
platform.name = name;
// parse intrinsics
tinyxml2::XMLElement* calibration = sensor->FirstChildElement(_T("calibration"));
if (calibration == NULL)
goto InvalidDocument;
{
if ((elem=calibration->FirstChildElement(_T("resolution"))) != NULL) {
resolutions.emplace_back(
elem->UnsignedAttribute(_T("width")),
elem->UnsignedAttribute(_T("height"))
);
ASSERT(model == METASHAPE);
bMetashapeFile = true;
}
Platform::Camera& camera = platform.cameras.AddEmpty();
camera.K = KMatrix::IDENTITY;
camera.R = RMatrix::IDENTITY;
camera.C = CMatrix::ZERO;
DistCoeff& dc = pltDistCoeffs.AddEmpty().AddEmpty();
for (elem=calibration->FirstChildElement(); elem!=NULL; elem=elem->NextSiblingElement()) {
if (0 == _tcsicmp(elem->Value(), _T("f"))) {
camera.K(0,0) = camera.K(1,1) = String::FromString<REAL>(elem->GetText());
} else
if (0 == _tcsicmp(elem->Value(), _T("fx"))) {
elem->QueryDoubleText(&camera.K(0,0));
} else
if (0 == _tcsicmp(elem->Value(), _T("fy"))) {
elem->QueryDoubleText(&camera.K(1,1));
} else
if (0 == _tcsicmp(elem->Value(), _T("cx"))) {
elem->QueryDoubleText(&camera.K(0,2));
} else
if (0 == _tcsicmp(elem->Value(), _T("cy"))) {
elem->QueryDoubleText(&camera.K(1,2));
} else
if (0 == _tcsicmp(elem->Value(), _T("k1"))) {
elem->QueryDoubleText(&dc.k1);
} else
if (0 == _tcsicmp(elem->Value(), _T("k2"))) {
elem->QueryDoubleText(&dc.k2);
} else
if (0 == _tcsicmp(elem->Value(), _T("k3"))) {
elem->QueryDoubleText(&dc.k3);
} else
if (0 == _tcsicmp(elem->Value(), _T("p1"))) {
elem->QueryDoubleText(&dc.p1);
} else
if (0 == _tcsicmp(elem->Value(), _T("p2"))) {
elem->QueryDoubleText(&dc.p2);
} else
if (0 == _tcsicmp(elem->Value(), _T("k4"))) {
elem->QueryDoubleText(&dc.k4);
} else
if (0 == _tcsicmp(elem->Value(), _T("k5"))) {
elem->QueryDoubleText(&dc.k5);
} else
if (0 == _tcsicmp(elem->Value(), _T("k6"))) {
elem->QueryDoubleText(&dc.k6);
}
}
if (bMetashapeFile) {
const cv::Size& resolution = resolutions.back();
camera.K(0,2) += resolution.width*REAL(0.5);
camera.K(1,2) += resolution.height*REAL(0.5);
camera.K = camera.GetScaledK(REAL(1)/Camera::GetNormalizationScale(resolution.width, resolution.height));
std::swap(dc.p1, dc.p2);
}
++nCameras;
}
}
}
}
}
// parse poses
{
tinyxml2::XMLElement* cameras = document->FirstChildElement(_T("cameras"));
if (cameras == NULL)
goto InvalidDocument;
{
PMatrix P;
size_t argc;
const String strPath(Util::getFilePath(strInputFileName));
for (tinyxml2::XMLElement* camera=cameras->FirstChildElement(); camera!=NULL; camera=camera->NextSiblingElement()) {
unsigned ID;
if (0 != _tcsicmp(camera->Value(), _T("camera")) || camera->QueryUnsignedAttribute(_T("id"), &ID) != tinyxml2::XML_SUCCESS)
goto InvalidDocument;
{
// add new image
mapImageID.emplace(ID, scene.images.size());
Image& imageData = scene.images.AddEmpty();
LPCTSTR name;
if ((name=camera->Attribute(_T("type"))) != NULL && _tcsicmp(name, _T("frame")) != 0) {
DEBUG_EXTRA("warning: unsupported camera calibration '%s'", name);
continue;
}
if ((name=camera->Attribute(_T("label"))) != NULL)
imageData.name = name;
Util::ensureUnifySlash(imageData.name);
if (Util::getFileExt(imageData.name).empty())
imageData.name += _T(".jpg");
imageData.name = MAKE_PATH_FULL(strPath, imageData.name);
imageData.platformID = mapPlatformID.at(camera->UnsignedAttribute(_T("sensor_id")));
imageData.cameraID = 0; // only one camera per platform supported by this format
imageData.ID = mapImageID.at(ID);
const cv::Size& resolution = resolutions[imageData.platformID];
imageData.width = resolution.width;
imageData.height = resolution.height;
imageData.scale = 1;
if (!bMetashapeFile && !camera->BoolAttribute(_T("enabled"))) {
imageData.poseID = NO_ID;
DEBUG_EXTRA("warning: uncalibrated image '%s'", name);
continue;
}
// set pose
CAutoPtrArr<LPSTR> argv;
if ((elem=camera->FirstChildElement(_T("transform"))) == NULL ||
(argv=Util::CommandLineToArgvA(elem->GetText(), argc)) == NULL ||
(argc != (bMetashapeFile ? 16 : 12)))
{
VERBOSE("Invalid image list camera: %u", ID);
continue;
}
Platform& platform = scene.platforms[imageData.platformID];
imageData.poseID = platform.poses.size();
Platform::Pose& pose = platform.poses.AddEmpty();
ImageListParseP(argv, P);
DecomposeProjectionMatrix(P, pose.R, pose.C);
if (bMetashapeFile) {
pose.C = pose.R*(-pose.C);
pose.R = pose.R.t();
}
imageData.camera = platform.GetCamera(imageData.cameraID, imageData.poseID);
++nPoses;
}
}
scene.nCalibratedImages = (unsigned)nPoses;
}
// parse bounding-box
{
tinyxml2::XMLElement* region = document->FirstChildElement(_T("region"));
if (region == NULL)
goto InvalidDocument;
{
size_t argc;
CAutoPtrArr<LPSTR> argv;
Point3 C, E; Matrix3x3 R;
if ((elem=region->FirstChildElement(_T("center"))) == NULL ||
(argv=Util::CommandLineToArgvA(elem->GetText(), argc)) == NULL ||
argc != 3)
{
VERBOSE("Invalid image list region: %s", elem->GetText());
goto InvalidDocument;
}
ImageListParseC(argv, C);
if ((elem=region->FirstChildElement(_T("size"))) == NULL ||
(argv=Util::CommandLineToArgvA(elem->GetText(), argc)) == NULL ||
argc != 3)
{
VERBOSE("Invalid image list region: %s", elem->GetText());
goto InvalidDocument;
}
ImageListParseC(argv, E);
E *= REAL(0.5);
if ((elem=region->FirstChildElement(_T("R"))) == NULL ||
(argv=Util::CommandLineToArgvA(elem->GetText(), argc)) == NULL ||
argc != 9)
{
VERBOSE("Invalid image list region: %s", elem->GetText());
goto InvalidDocument;
}
ImageListParseR(argv, R);
scene.obb.m_rot = Cast<float>(R);
scene.obb.m_pos = Cast<float>(C);
scene.obb.m_ext = Cast<float>(E);
}
}
}
}
}
return true;
InvalidDocument:
VERBOSE("Invalid camera list");
return false;
}
// parse scene stored in ContextCapture BlocksExchange format containing cameras, images and sparse point-cloud
bool ParseBlocksExchangeXML(tinyxml2::XMLDocument& doc, Scene& scene, PlatformDistCoeffs& pltDistCoeffs, size_t& nCameras, size_t& nPoses) {
String strInputFileName(MAKE_PATH_FULL(WORKING_FOLDER_FULL, OPT::strInputFileName));
Util::ensureValidPath(strInputFileName);
const tinyxml2::XMLElement* blocksExchange;
const tinyxml2::XMLElement* document;
const tinyxml2::XMLElement* photogroups;
if (doc.ErrorID() != tinyxml2::XML_SUCCESS ||
(blocksExchange=doc.FirstChildElement("BlocksExchange")) == NULL ||
(document=blocksExchange->FirstChildElement("Block")) == NULL ||
(photogroups=document->FirstChildElement("Photogroups")) == NULL) {
VERBOSE("error: invalid scene file");
return false;
}
CLISTDEF0(cv::Size) resolutions;
std::unordered_map<IIndex,IIndex> mapImageID;
const String strPath(Util::getFilePath(strInputFileName));
const tinyxml2::XMLElement* elem;
for (const tinyxml2::XMLElement* photogroup=photogroups->FirstChildElement(); photogroup!=NULL; photogroup=photogroup->NextSiblingElement()) {
if ((elem=photogroup->FirstChildElement("CameraModelType")) == NULL ||
std::strcmp(elem->GetText(), "Perspective") != 0)
continue;
if ((elem=photogroup->FirstChildElement("ImageDimensions")) == NULL)
continue;
const IIndex platformID = scene.platforms.size();
Platform& platform = scene.platforms.AddEmpty();
platform.name = photogroup->FirstChildElement("Name")->GetText();
resolutions.emplace_back(
elem->FirstChildElement("Width")->UnsignedText(),
elem->FirstChildElement("Height")->UnsignedText()
);
// parse camera
Platform::Camera& camera = platform.cameras.AddEmpty();
camera.K = KMatrix::IDENTITY;
camera.R = RMatrix::IDENTITY;
camera.C = CMatrix::ZERO;
const float resolutionScale = Camera::GetNormalizationScale(resolutions.back().width, resolutions.back().height);
if ((elem=photogroup->FirstChildElement("FocalLengthPixels")) != NULL) {
camera.K(0,0) = camera.K(1,1) = photogroup->FirstChildElement("FocalLengthPixels")->DoubleText();
} else {
camera.K(0,0) = camera.K(1,1) = photogroup->FirstChildElement("FocalLength")->DoubleText() * resolutionScale / photogroup->FirstChildElement("SensorSize")->DoubleText();
}
if ((elem=photogroup->FirstChildElement("PrincipalPoint")) != NULL) {
camera.K(0,2) = elem->FirstChildElement("x")->DoubleText();
camera.K(1,2) = elem->FirstChildElement("y")->DoubleText();
} else {
camera.K(0,2) = resolutions.back().width*REAL(0.5);
camera.K(1,2) = resolutions.back().height*REAL(0.5);
}
if ((elem=photogroup->FirstChildElement("AspectRatio")) != NULL)
camera.K(1,1) *= elem->DoubleText();
if ((elem=photogroup->FirstChildElement("Skew")) != NULL)
camera.K(0,1) = elem->DoubleText();
camera.K = camera.GetScaledK(REAL(1)/resolutionScale);
// parse distortion parameters
DistCoeff& dc = pltDistCoeffs.AddEmpty().AddEmpty(); {
const tinyxml2::XMLElement* distortion=photogroup->FirstChildElement("Distortion");
if (distortion) {
if ((elem=distortion->FirstChildElement("K1")) != NULL)
dc.k1 = elem->DoubleText();
if ((elem=distortion->FirstChildElement("K2")) != NULL)
dc.k2 = elem->DoubleText();
if ((elem=distortion->FirstChildElement("K3")) != NULL)
dc.k3 = elem->DoubleText();
if ((elem=distortion->FirstChildElement("P1")) != NULL)
dc.p2 = elem->DoubleText();
if ((elem=distortion->FirstChildElement("P2")) != NULL)
dc.p1 = elem->DoubleText();
}
}
++nCameras;
for (const tinyxml2::XMLElement* photo=photogroup->FirstChildElement("Photo"); photo!=NULL; photo=photo->NextSiblingElement()) {
const IIndex idxImage = scene.images.size();
Image& imageData = scene.images.AddEmpty();
imageData.platformID = platformID;
imageData.cameraID = 0; // only one camera per platform supported by this format
imageData.poseID = NO_ID;
imageData.ID = photo->FirstChildElement("Id")->UnsignedText();
imageData.name = photo->FirstChildElement("ImagePath")->GetText();
Util::ensureUnifySlash(imageData.name);
imageData.name = MAKE_PATH_FULL(strPath, imageData.name);
mapImageID.emplace(imageData.ID, idxImage);
// set image resolution
const cv::Size& resolution = resolutions[imageData.platformID];
imageData.width = resolution.width;
imageData.height = resolution.height;
imageData.scale = 1;
// set camera pose
const tinyxml2::XMLElement* photoPose = photo->FirstChildElement("Pose");
if (photoPose == NULL)
continue;
if ((elem=photoPose->FirstChildElement("Rotation")) == NULL)
continue;
imageData.poseID = platform.poses.size();
Platform::Pose& pose = platform.poses.AddEmpty();
pose.R = Matrix3x3(
elem->FirstChildElement("M_00")->DoubleText(),
elem->FirstChildElement("M_01")->DoubleText(),
elem->FirstChildElement("M_02")->DoubleText(),
elem->FirstChildElement("M_10")->DoubleText(),
elem->FirstChildElement("M_11")->DoubleText(),
elem->FirstChildElement("M_12")->DoubleText(),
elem->FirstChildElement("M_20")->DoubleText(),
elem->FirstChildElement("M_21")->DoubleText(),
elem->FirstChildElement("M_22")->DoubleText());
if ((elem=photoPose->FirstChildElement("Center")) == NULL)
continue;
pose.C = Point3(
elem->FirstChildElement("x")->DoubleText(),
elem->FirstChildElement("y")->DoubleText(),
elem->FirstChildElement("z")->DoubleText());
imageData.camera = platform.GetCamera(imageData.cameraID, imageData.poseID);
// set depth stats
if ((elem=photo->FirstChildElement("MedianDepth")) != NULL)
imageData.avgDepth = (float)elem->DoubleText();
else if (photo->FirstChildElement("NearDepth") != NULL && photo->FirstChildElement("FarDepth") != NULL)
imageData.avgDepth = (float)((photo->FirstChildElement("NearDepth")->DoubleText() + photo->FirstChildElement("FarDepth")->DoubleText())/2);
else
imageData.avgDepth = 0;
++nPoses;
}
}
if (scene.images.size() < 2)
return false;
scene.nCalibratedImages = (unsigned)nPoses;
// transform poses to a local coordinate system
const bool bLocalCoords(document->FirstChildElement("SRSId") == NULL ||
((elem=blocksExchange->FirstChildElement("SpatialReferenceSystems")) != NULL && (elem=elem->FirstChildElement("SRS")) != NULL && (elem=elem->FirstChildElement("Name")) != NULL && _tcsncmp(elem->GetText(), "Local Coordinates", 17) == 0));
Point3 center = Point3::ZERO;
if (!bLocalCoords) {
for (const Image& imageData : scene.images)
center += imageData.camera.C;
center /= scene.images.size();
for (Platform& platform : scene.platforms)
for (Platform::Pose& pose : platform.poses)
pose.C -= center;
}
// try to read also the sparse point-cloud
const tinyxml2::XMLElement* tiepoints = document->FirstChildElement("TiePoints");
if (tiepoints == NULL)
return true;
for (const tinyxml2::XMLElement* tiepoint=tiepoints->FirstChildElement(); tiepoint!=NULL; tiepoint=tiepoint->NextSiblingElement()) {
if ((elem=tiepoint->FirstChildElement("Position")) == NULL)
continue;
scene.pointcloud.points.emplace_back(
(float)elem->FirstChildElement("x")->DoubleText(),
(float)elem->FirstChildElement("y")->DoubleText(),
(float)elem->FirstChildElement("z")->DoubleText());
if (!bLocalCoords)
scene.pointcloud.points.back() -= Cast<float>(center);
if ((elem=tiepoint->FirstChildElement("Color")) != NULL)
scene.pointcloud.colors.emplace_back(
(uint8_t)CLAMP(elem->FirstChildElement("Red")->DoubleText()*255, 0.0, 255.0),
(uint8_t)CLAMP(elem->FirstChildElement("Green")->DoubleText()*255, 0.0, 255.0),
(uint8_t)CLAMP(elem->FirstChildElement("Blue")->DoubleText()*255, 0.0, 255.0));
PointCloud::ViewArr views;
for (const tinyxml2::XMLElement* view=tiepoint->FirstChildElement("Measurement"); view!=NULL; view=view->NextSiblingElement())
views.emplace_back(mapImageID.at(view->FirstChildElement("PhotoId")->UnsignedText()));
scene.pointcloud.pointViews.emplace_back(std::move(views));
}
return true;
}
// parse scene stored either in Metashape images list format or ContextCapture BlocksExchange format
bool ParseSceneXML(Scene& scene, PlatformDistCoeffs& pltDistCoeffs, size_t& nCameras, size_t& nPoses)
{
// parse XML file
const String strInputFileName(MAKE_PATH_SAFE(OPT::strInputFileName));
tinyxml2::XMLDocument doc; {
ISTREAMPTR pStream(new File(strInputFileName, File::READ, File::OPEN));
if (!((File*)(ISTREAM*)pStream)->isOpen()) {
VERBOSE("error: failed opening the input scene file");
return false;
}
const size_t nLen(pStream->getSize());
String str; str.resize(nLen);
pStream->read(&str[0], nLen);
doc.Parse(str.c_str(), nLen);
}
if (doc.ErrorID() != tinyxml2::XML_SUCCESS) {
VERBOSE("error: invalid XML file");
return false;
}
// parse scene
if (doc.FirstChildElement("BlocksExchange") == NULL)
return ParseImageListXML(doc, scene, pltDistCoeffs, nCameras, nPoses);
return ParseBlocksExchangeXML(doc, scene, pltDistCoeffs, nCameras, nPoses);
}
// undistort image using Brown's model
bool UndistortBrown(Image& imageData, uint32_t ID, const DistCoeff& dc, const String& pathData)
{
// do we need to undistort?
if (!dc.HasDistortion())
return true;
// load image pixels
if (!imageData.ReloadImage())
return false;
// initialize intrinsics
const cv::Vec<double,8>& distCoeffs = *reinterpret_cast<const cv::Vec<REAL,8>*>(dc.coeff);
const KMatrix prevK(imageData.camera.GetK<REAL>(imageData.width, imageData.height));
#if 1
const KMatrix& K(prevK);
#else
const KMatrix K(cv::getOptimalNewCameraMatrix(prevK, distCoeffs, imageData.size(), 0.0, cv::Size(), NULL, true));
ASSERT(K(0,2) == Camera::ComposeK(prevK(0,0), prevK(1,1), imageData.width(), imageData.height())(0,2));
ASSERT(K(1,2) == Camera::ComposeK(prevK(0,0), prevK(1,1), imageData.width(), imageData.height())(1,2));
if (K.IsEqual(prevK)) {
int i(0);
while (distCoeffs(i++) == 0.0) {
if (i == 8)
return true; // nothing to do
}
}
#endif
// undistort image
Image8U3 imgUndist;
cv::undistort(imageData.image, imgUndist, prevK, distCoeffs, K);
imageData.ReleaseImage();
// save undistorted image
imageData.image = imgUndist;
imageData.name = pathData + String::FormatString(_T("%05u.jpg"), ID);
Util::ensureFolder(imageData.name);
return imageData.image.Save(imageData.name);
}
// project all points in this image and keep those looking at the camera and are most in front
void AssignPoints(const Image& imageData, uint32_t ID, PointCloud& pointcloud)
{
ASSERT(pointcloud.IsValid());
const int CHalfSize(1);
const int FHalfSize(5);
const Depth thCloseDepth(0.1f);
// sort points by depth
IndexScoreArr points(0, pointcloud.points.size());
FOREACH(p, pointcloud.points) {
const PointCloud::Point& X(pointcloud.points[p]);
const float d((float)imageData.camera.PointDepth(X));
if (d <= 0)
continue;
points.emplace_back((uint32_t)p, d);
}
points.Sort();
// project all points to this view
DepthMap depthMap(imageData.GetSize());
TImage<cuint32_t> pointMap(imageData.GetSize());
depthMap.fill(FLT_MAX);
pointMap.memset((uint8_t)NO_ID);
RFOREACHPTR(pPD, points) {
const Point3 X(pointcloud.points[pPD->idx]);
const Point3f Xc(imageData.camera.TransformPointW2C(X));
// (also the view to point vector cause the face is in camera view space)
// point skip already in the previous step if the (cos) angle between
// the view to point vector and the view direction is negative
ASSERT(Xc.z > 0);
// skip point if the (cos) angle between
// its normal and the point to view vector is negative
if (!pointcloud.normals.empty() && Xc.dot(pointcloud.normals[pPD->idx]) > 0)
continue;
const Point2f x(imageData.camera.TransformPointC2I(Xc));
const ImageRef ir(ROUND2INT(x));
if (!depthMap.isInside(ir))
continue;
// skip point if the there is a very near by point closer
for (int i=-CHalfSize; i<=CHalfSize; ++i) {
const int rw(ir.y+i);
for (int j=-CHalfSize; j<=CHalfSize; ++j) {
const int cw(ir.x+j);
if (!depthMap.isInside(ImageRef(cw,rw)))
continue;
if (depthMap(rw,cw) < Xc.z)
goto NEXT_POINT;
}
}
// skip the point if there is a near by point much closer
for (int i=-FHalfSize; i<=FHalfSize; ++i) {
const int rw(ir.y+i);
for (int j=-FHalfSize; j<=FHalfSize; ++j) {
const int cw(ir.x+j);
if (!depthMap.isInside(ImageRef(cw,rw)))
continue;
const Depth depth(depthMap(rw,cw));
if (depth < Xc.z && !IsDepthSimilar(depth, Xc.z, thCloseDepth))
goto NEXT_POINT;
}
}
// store this point
depthMap(ir) = Xc.z;
pointMap(ir) = pPD->idx;
NEXT_POINT:;
}
// add all points viewed by this camera
const int HalfSize(1);
const int RowsEnd(pointMap.rows-HalfSize);
const int ColsEnd(pointMap.cols-HalfSize);
unsigned nNumPoints(0);
#ifdef _USE_OPENMP
#pragma omp critical
#endif
for (int r=HalfSize; r<RowsEnd; ++r) {
for (int c=HalfSize; c<ColsEnd; ++c) {
const uint32_t idx(pointMap(r,c));
if (idx == NO_ID)
continue;
#ifdef _USE_OPENMP
pointcloud.pointViews[idx].InsertSort(ID);
#else
pointcloud.pointViews[idx].Insert(ID);
ASSERT(pointcloud.pointViews[idx].IsSorted());
#endif
++nNumPoints;
}
}
DEBUG_ULTIMATE("\tview %3u sees %u points", ID, nNumPoints);
}
} // unnamed namespace
int main(int argc, LPCTSTR* argv)
{
#ifdef _DEBUGINFO
// set _crtBreakAlloc index to stop in <dbgheap.c> at allocation
_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);// | _CRTDBG_CHECK_ALWAYS_DF);
#endif
Application application;
if (!application.Initialize(argc, argv))
return EXIT_FAILURE;
TD_TIMER_START();
Scene scene(OPT::nMaxThreads);
// convert data from Metashape format to OpenMVS
PlatformDistCoeffs pltDistCoeffs;
size_t nCameras(0), nPoses(0);
if (!ParseSceneXML(scene, pltDistCoeffs, nCameras, nPoses))
return EXIT_FAILURE;
// read the 3D point-cloud if available
if (!OPT::strPointsFileName.empty() && !scene.pointcloud.Load(MAKE_PATH_SAFE(OPT::strPointsFileName)))
return EXIT_FAILURE;
const bool bAssignPoints(!scene.pointcloud.IsEmpty() && !scene.pointcloud.IsValid());
if (bAssignPoints)
scene.pointcloud.pointViews.resize(scene.pointcloud.GetSize());
// undistort images
const String pathData(MAKE_PATH_FULL(WORKING_FOLDER_FULL, OPT::strOutputImageFolder));
Util::Progress progress(_T("Processed images"), scene.images.size());
GET_LOGCONSOLE().Pause();
#ifdef _USE_OPENMP
bool bAbort(false);
#pragma omp parallel for shared(bAbort) schedule(dynamic)
for (int ID=0; ID<(int)scene.images.size(); ++ID) {
#pragma omp flush (bAbort)
if (bAbort)
continue;
#else
FOREACH(ID, scene.images) {
#endif
++progress;
Image& imageData = scene.images[ID];
if (!imageData.IsValid())
continue;
if (!UndistortBrown(imageData, ID, pltDistCoeffs[imageData.platformID][imageData.cameraID], pathData)) {
#ifdef _USE_OPENMP
bAbort = true;
#pragma omp flush (bAbort)
continue;
#else
return EXIT_FAILURE;
#endif
}
imageData.UpdateCamera(scene.platforms);
if (bAssignPoints)
AssignPoints(imageData, ID, scene.pointcloud);
}
GET_LOGCONSOLE().Play();
#ifdef _USE_OPENMP
if (bAbort)
return EXIT_FAILURE;
#endif
progress.close();
if (scene.pointcloud.IsValid()) {
// filter invalid points
RFOREACH(i, scene.pointcloud.points)
if (scene.pointcloud.pointViews[i].size() < 2)
scene.pointcloud.RemovePoint(i);
// compute average scene depth per image
if (!std::any_of(scene.images.begin(), scene.images.end(), [](const Image& imageData) { return imageData.avgDepth > 0; })) {
std::vector<float> avgDepths(scene.images.size(), 0.f);
std::vector<uint32_t> numDepths(scene.images.size(), 0u);
FOREACH(idxPoint, scene.pointcloud.points) {
const Point3 X(scene.pointcloud.points[idxPoint]);
for (const PointCloud::View& idxImage: scene.pointcloud.pointViews[idxPoint]) {
const Image& imageData = scene.images[idxImage];
const float depth((float)imageData.camera.PointDepth(X));
if (depth > 0) {
avgDepths[idxImage] += depth;
++numDepths[idxImage];
}
}
}
FOREACH(idxImage, scene.images) {
Image& imageData = scene.images[idxImage];
if (numDepths[idxImage] > 0)
imageData.avgDepth = avgDepths[idxImage] / numDepths[idxImage];
}
}
}
// print average scene depth per image stats
MeanStdMinMax<float,double> acc;
for (const Image& imageData: scene.images)
if (imageData.avgDepth > 0)
acc.Update(imageData.avgDepth);
// write OpenMVS input data
scene.Save(MAKE_PATH_SAFE(OPT::strOutputFileName), (ARCHIVE_TYPE)OPT::nArchiveType);
VERBOSE("Exported data: %u platforms, %u cameras, %u poses, %u images, %u vertices, %g min / %g mean (%g std) / %g max average scene depth per image (%s)",
scene.platforms.size(), nCameras, nPoses, scene.images.size(), scene.pointcloud.GetSize(),
acc.minVal, acc.GetMean(), acc.GetStdDev(), acc.maxVal,
TD_TIMER_GET_FMT().c_str());
return EXIT_SUCCESS;
}
/*----------------------------------------------------------------*/

23
apps/InterfaceOpenMVG/CMakeLists.txt

@ -0,0 +1,23 @@ @@ -0,0 +1,23 @@
FIND_PACKAGE(OpenMVG QUIET)
IF (OPENMVG_FOUND)
INCLUDE_DIRECTORIES(${OPENMVG_INCLUDE_DIRS})
add_definitions(-D_USE_OPENMVG)
set(LIBS_DEPEND "MVS;${OPENMVG_LIBRARIES}")
ELSE()
set(LIBS_DEPEND "MVS")
MESSAGE("OPENMVG_NOT FOUND : OpenMVG importer with JSON support will not be build")
ENDIF()
if(MSVC)
FILE(GLOB LIBRARY_FILES_C "*.cpp" "*.rc")
else()
FILE(GLOB LIBRARY_FILES_C "*.cpp")
endif()
FILE(GLOB LIBRARY_FILES_H "*.h" "*.inl")
cxx_executable_with_flags(InterfaceOpenMVG "Apps" "${cxx_default}" "${LIBS_DEPEND};${OpenMVS_EXTRA_LIBS}" ${LIBRARY_FILES_C} ${LIBRARY_FILES_H})
# Install
INSTALL(TARGETS InterfaceOpenMVG
EXPORT OpenMVSTargets
RUNTIME DESTINATION "${INSTALL_BIN_DIR}" COMPONENT bin)

755
apps/InterfaceOpenMVG/InterfaceOpenMVG.cpp

@ -0,0 +1,755 @@ @@ -0,0 +1,755 @@
/*
* InterfaceOpenMVG.cpp
*
* Copyright (c) 2014-2015 SEACAVE
*
* Author(s):
*
* cDc <cdc.seacave@gmail.com>
* Pierre MOULON <p.moulon@foxel.ch>
*
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*
* Additional Terms:
*
* You are required to preserve legal notices and author attributions in
* that material or in the Appropriate Legal Notices displayed by works
* containing it.
*/
#include "../../libs/MVS/Common.h"
#include "../../libs/MVS/Scene.h"
#include <boost/program_options.hpp>
#ifdef _USE_OPENMVG
#undef D2R
#undef R2D
#include <openMVG/sfm/sfm_data.hpp>
#include <openMVG/sfm/sfm_data_io.hpp>
#include <openMVG/image/image.hpp>
#endif
// D E F I N E S ///////////////////////////////////////////////////
#define APPNAME _T("InterfaceOpenMVG")
#define MVS_EXT _T(".mvs")
#define MVG_EXT _T(".baf")
#define MVG2_EXT _T(".json")
#define MVG3_EXT _T(".bin")
// S T R U C T S ///////////////////////////////////////////////////
namespace {
namespace openMVS {
namespace MVS_IO {
typedef REAL RealT;
typedef Eigen::Matrix<RealT,3,3,Eigen::RowMajor> Mat33;
typedef Eigen::Matrix<RealT,3,1> Vec3;
// Structure to model the pinhole camera projection model
struct Camera
{
Mat33 K; // camera's normalized intrinsics matrix
};
typedef std::vector<Camera> vec_Camera;
// structure describing a pose along the trajectory of a platform
struct Pose {
Mat33 R; // pose's rotation matrix
Vec3 C; // pose's translation vector
};
typedef std::vector<Pose> vec_Pose;
// structure describing an image
struct Image {
uint32_t id_camera; // ID of the associated camera on the associated platform
uint32_t id_pose; // ID of the pose of the associated platform
std::string name; // image file name
};
typedef std::vector<Image> vec_Image;
// structure describing a 3D point
struct Vertex {
typedef std::vector<uint32_t> vec_View;
Vec3 X; // 3D point position
vec_View views; // view visibility for this 3D feature
};
typedef std::vector<Vertex> vec_Vertex;
struct SfM_Scene
{
vec_Pose poses; // array of poses
vec_Camera cameras; // array of cameras
vec_Image images; // array of images
vec_Vertex vertices; // array of reconstructed 3D points
};
bool ImportScene(const std::string& sList_filename, const std::string& sBaf_filename, SfM_Scene& sceneBAF)
{
LOG_OUT() << "Reading:\n"
<< sList_filename << "\n"
<< sBaf_filename << std::endl;
// Read view list file (view filename, id_intrinsic, id_pose)
// Must be read first, since it allow to establish the link between the ViewId and the camera/poses ids.
std::map< std::pair<uint32_t, uint32_t>, uint32_t > map_cam_pose_toViewId;
{
std::ifstream file(sList_filename.c_str());
if (!file.good()) {
VERBOSE("error: unable to open file '%s'", sList_filename.c_str());
return false;
}
Image image;
uint32_t count = 0;
while (file >> image.name >> image.id_camera >> image.id_pose) {
sceneBAF.images.push_back(image);
map_cam_pose_toViewId[std::make_pair(image.id_camera, image.id_pose)] = count++;
LOG_OUT() << image.name << ' ' << image.id_camera << ' ' << image.id_pose << std::endl;
}
}
// Read BAF file
{
std::ifstream file(sBaf_filename.c_str());
if (!file.good()) {
VERBOSE("error: unable to open file '%s'", sBaf_filename.c_str());
return false;
}
uint32_t num_intrinsics, num_poses, num_points;
// Read header
file >> num_intrinsics;
file >> num_poses;
file >> num_points;
LOG_OUT() << "Reading BAF file with:\n"
<< " num_intrinsics: " << num_intrinsics << "\n"
<< " num_poses: " << num_poses << "\n"
<< " num_points: " << num_points << "\n";
// Read the intrinsics (only support reading Pinhole Radial 3).
{
for (uint32_t i = 0; i < num_intrinsics; ++i) {
double focal, ppx, ppy, k1, k2, k3;
file >> focal >> ppx >> ppy >> k1 >> k2 >> k3;
Camera cam;
cam.K <<
focal, 0, ppx,
0, focal, ppy,
0, 0, 1;
LOG_OUT() << "\n" << cam.K << std::endl;
sceneBAF.cameras.push_back(cam);
}
}
// Read poses
{
for (uint32_t i = 0; i < num_poses; ++i) {
Pose pose;
for (int r = 0; r < 3; ++r) {
for (int c = 0; c < 3; ++c) {
file >> pose.R(r,c);
}
}
file >> pose.C[0] >> pose.C[1] >> pose.C[2];
#ifndef _RELEASE
LOG_OUT() << "\n" << pose.R << "\n\n" << pose.C.transpose() << std::endl;
#endif
sceneBAF.poses.push_back(pose);
}
}
// Read structure and visibility
{
#ifdef _RELEASE
Util::Progress progress(_T("Processed points"), num_points);
#endif
for (uint32_t i = 0; i < num_points; ++i) {
Vertex vertex;
file >> vertex.X[0] >> vertex.X[1] >> vertex.X[2];
uint32_t num_observations_for_point = 0;
file >> num_observations_for_point;
for (uint32_t j = 0; j < num_observations_for_point; ++j) {
uint32_t id_intrinsics, id_pose;
double x, y;
file >> id_intrinsics >> id_pose >> x >> y;
#ifndef _RELEASE
LOG_OUT() << "observation:"
<< " " << id_intrinsics
<< " " << id_pose
<< " " << x << " " << y << std::endl;
#endif
const auto itIntrPose(map_cam_pose_toViewId.find(std::make_pair(id_intrinsics, id_pose)));
if (itIntrPose == map_cam_pose_toViewId.end()) {
LOG_OUT() << "error: intrinsics-pose pair not existing" << std::endl;
continue;
}
const uint32_t id_view(itIntrPose->second);
vertex.views.push_back(id_view);
}
sceneBAF.vertices.push_back(vertex);
#ifdef _RELEASE
progress.display(i);
#endif
}
#ifdef _RELEASE
progress.close();
#endif
}
}
return true;
}
bool ExportScene(const std::string& sList_filename, const std::string& sBaf_filename, const SfM_Scene& sceneBAF)
{
LOG_OUT() << "Writing:\n"
<< sList_filename << "\n"
<< sBaf_filename << std::endl;
// Write view list file (view filename, id_intrinsic, id_pose)
{
std::ofstream file(sList_filename.c_str());
if (!file.good()) {
VERBOSE("error: unable to open file '%s'", sList_filename.c_str());
return false;
}
for (uint32_t i=0; i<sceneBAF.images.size(); ++i) {
const Image& image = sceneBAF.images[i];
file << image.name << ' ' << image.id_camera << ' ' << image.id_pose << std::endl;
LOG_OUT() << image.name << ' ' << image.id_camera << ' ' << image.id_pose << std::endl;
}
}
// Write BAF file
{
std::ofstream file(sBaf_filename.c_str());
if (!file.good()) {
VERBOSE("error: unable to open file '%s'", sBaf_filename.c_str());
return false;
}
const uint32_t num_intrinsics = (uint32_t)sceneBAF.cameras.size();
const uint32_t num_poses = (uint32_t)sceneBAF.poses.size();
const uint32_t num_points = (uint32_t)sceneBAF.vertices.size();
LOG_OUT() << "Writing BAF file with:\n"
<< " num_intrinsics: " << num_intrinsics << "\n"
<< " num_poses: " << num_poses << "\n"
<< " num_points: " << num_points << "\n";
// Write header
file << num_intrinsics << std::endl;
file << num_poses << std::endl;
file << num_points << std::endl;
// Write the intrinsics (only support writing Pinhole Radial 3).
{
for (uint32_t i = 0; i < num_intrinsics; ++i) {
const Camera& cam = sceneBAF.cameras[i];
file << cam.K(0,0) << ' ' << cam.K(0,2) << ' ' << cam.K(1,2) << ' ' << 0 << ' ' << 0 << ' ' << 0 << std::endl;
LOG_OUT() << "\n" << cam.K << std::endl;
}
}
// Write poses
{
for (uint32_t i = 0; i < num_poses; ++i) {
const Pose& pose = sceneBAF.poses[i];
for (int r = 0; r < 3; ++r) {
for (int c = 0; c < 3; ++c) {
file << pose.R(r,c) << ' ';
}
}
file << pose.C[0] << ' ' << pose.C[1] << ' ' << pose.C[2] << std::endl;
#ifndef _RELEASE
LOG_OUT() << "\n" << pose.R << "\n\n" << pose.C.transpose() << std::endl;
#endif
}
}
// Write structure and visibility
{
#ifdef _RELEASE
Util::Progress progress(_T("Processed points"), num_points);
#endif
for (uint32_t i = 0; i < num_points; ++i) {
const Vertex& vertex = sceneBAF.vertices[i];
file << vertex.X[0] << ' ' << vertex.X[1] << ' ' << vertex.X[2] << std::endl;
const uint32_t num_observations_for_point = (uint32_t)vertex.views.size();
file << num_observations_for_point << std::endl;
for (uint32_t j = 0; j < num_observations_for_point; ++j) {
const uint32_t id_view = vertex.views[j];
const Image& image = sceneBAF.images[id_view];
file << image.id_camera << ' ' << image.id_pose << ' ' << 0 << ' ' << 0 << std::endl;
#ifndef _RELEASE
LOG_OUT() << "observation:"
<< " " << image.id_camera
<< " " << image.id_pose << std::endl;
#endif
}
#ifdef _RELEASE
progress.display(i);
#endif
}
#ifdef _RELEASE
progress.close();
#endif
}
}
return true;
}
} // MVS_IO
} // openMVS
namespace OPT {
#ifdef _USE_OPENMVG
bool bOpenMVGjson; // new import format
#endif
bool bOpenMVS2OpenMVG; // conversion direction
bool bNormalizeIntrinsics;
String strListFileName;
String strInputFileName;
String strOutputFileName;
String strOutputImageFolder;
unsigned nArchiveType;
int nProcessPriority;
unsigned nMaxThreads;
String strConfigFileName;
boost::program_options::variables_map vm;
} // namespace OPT
class Application {
public:
Application() {}
~Application() { Finalize(); }
bool Initialize(size_t argc, LPCTSTR* argv);
void Finalize();
}; // Application
// initialize and parse the command line parameters
bool Application::Initialize(size_t argc, LPCTSTR* argv)
{
// initialize log and console
OPEN_LOG();
OPEN_LOGCONSOLE();
// group of options allowed only on command line
boost::program_options::options_description generic("Generic options");
generic.add_options()
("help,h", "produce this help message")
("working-folder,w", boost::program_options::value<std::string>(&WORKING_FOLDER), "working directory (default current directory)")
("config-file,c", boost::program_options::value<std::string>(&OPT::strConfigFileName)->default_value(APPNAME _T(".cfg")), "file name containing program options")
("archive-type", boost::program_options::value(&OPT::nArchiveType)->default_value(ARCHIVE_DEFAULT), "project archive type: 0-text, 1-binary, 2-compressed binary")
("process-priority", boost::program_options::value(&OPT::nProcessPriority)->default_value(-1), "process priority (below normal by default)")
("max-threads", boost::program_options::value(&OPT::nMaxThreads)->default_value(0), "maximum number of threads (0 for using all available cores)")
#if TD_VERBOSE != TD_VERBOSE_OFF
("verbosity,v", boost::program_options::value(&g_nVerbosityLevel)->default_value(
#if TD_VERBOSE == TD_VERBOSE_DEBUG
3
#else
2
#endif
), "verbosity level")
#endif
;
// group of options allowed both on command line and in config file
boost::program_options::options_description config("Main options");
config.add_options()
("images-list-file,l", boost::program_options::value<std::string>(&OPT::strListFileName), "input filename containing image list")
("input-file,i", boost::program_options::value<std::string>(&OPT::strInputFileName), "input filename containing camera poses and image list")
("output-file,o", boost::program_options::value<std::string>(&OPT::strOutputFileName), "output filename for storing the mesh")
("output-image-folder", boost::program_options::value<std::string>(&OPT::strOutputImageFolder)->default_value("undistorted_images"), "output folder to store undistorted images")
("normalize,f", boost::program_options::value(&OPT::bNormalizeIntrinsics)->default_value(true), "normalize intrinsics while exporting to OpenMVS format")
;
boost::program_options::options_description cmdline_options;
cmdline_options.add(generic).add(config);
boost::program_options::options_description config_file_options;
config_file_options.add(config);
boost::program_options::positional_options_description p;
p.add("input-file", -1);
try {
// parse command line options
boost::program_options::store(boost::program_options::command_line_parser((int)argc, argv).options(cmdline_options).positional(p).run(), OPT::vm);
boost::program_options::notify(OPT::vm);
INIT_WORKING_FOLDER;
// parse configuration file
std::ifstream ifs(MAKE_PATH_SAFE(OPT::strConfigFileName));
if (ifs) {
boost::program_options::store(parse_config_file(ifs, config_file_options), OPT::vm);
boost::program_options::notify(OPT::vm);
}
}
catch (const std::exception& e) {
LOG(e.what());
return false;
}
// initialize the log file
OPEN_LOGFILE(MAKE_PATH(APPNAME _T("-")+Util::getUniqueName(0)+_T(".log")).c_str());
// print application details: version and command line
Util::LogBuild();
LOG(_T("Command line: ") APPNAME _T("%s"), Util::CommandLineToString(argc, argv).c_str());
// validate input
Util::ensureValidPath(OPT::strListFileName);
Util::ensureUnifySlash(OPT::strListFileName);
Util::ensureValidPath(OPT::strInputFileName);
Util::ensureUnifySlash(OPT::strInputFileName);
Util::ensureUnifySlash(OPT::strOutputImageFolder);
Util::ensureFolderSlash(OPT::strOutputImageFolder);
const String strInputFileNameExt(Util::getFileExt(OPT::strInputFileName).ToLower());
OPT::bOpenMVS2OpenMVG = (strInputFileNameExt == MVS_FILE_EXTENSION);
#ifdef _USE_OPENMVG
OPT::bOpenMVGjson = (strInputFileNameExt == MVG2_EXT || strInputFileNameExt == MVG3_EXT);
const bool bInvalidCommand(OPT::strInputFileName.IsEmpty() || (OPT::strListFileName.IsEmpty() && !OPT::bOpenMVGjson && !OPT::bOpenMVS2OpenMVG));
#else
const bool bInvalidCommand(OPT::strInputFileName.IsEmpty() || (OPT::strListFileName.IsEmpty() && !OPT::bOpenMVS2OpenMVG));
#endif
if (OPT::vm.count("help") || bInvalidCommand) {
boost::program_options::options_description visible("Available options");
visible.add(generic).add(config);
GET_LOG() << visible;
#ifndef _USE_OPENMVG
GET_LOG() << "\nWARNING: Only " MVG_EXT " files supported! In order to import " MVG2_EXT " or " MVG3_EXT " files use the converter included in OpenMVG, or link OpenMVS to the latest version of OpenMVG during compile time.\n";
#endif
}
if (bInvalidCommand)
return false;
// initialize optional options
Util::ensureValidPath(OPT::strOutputFileName);
Util::ensureUnifySlash(OPT::strOutputFileName);
if (OPT::bOpenMVS2OpenMVG) {
if (OPT::strOutputFileName.IsEmpty())
OPT::strOutputFileName = Util::getFileFullName(OPT::strInputFileName);
} else {
if (OPT::strOutputFileName.IsEmpty())
OPT::strOutputFileName = Util::getFileFullName(OPT::strInputFileName) + MVS_FILE_EXTENSION;
}
MVS::Initialize(APPNAME, OPT::nMaxThreads, OPT::nProcessPriority);
return true;
}
// finalize application instance
void Application::Finalize()
{
MVS::Finalize();
CLOSE_LOGFILE();
CLOSE_LOGCONSOLE();
CLOSE_LOG();
}
} // unnamed namespace
int main(int argc, LPCTSTR* argv)
{
#ifdef _DEBUGINFO
// set _crtBreakAlloc index to stop in <dbgheap.c> at allocation
_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);// | _CRTDBG_CHECK_ALWAYS_DF);
#endif
Application application;
if (!application.Initialize(argc, argv))
return EXIT_FAILURE;
TD_TIMER_START();
if (OPT::bOpenMVS2OpenMVG) {
// read OpenMVS input data
MVS::Scene scene(OPT::nMaxThreads);
if (!scene.Load(MAKE_PATH_SAFE(OPT::strInputFileName)))
return EXIT_FAILURE;
// convert data from OpenMVS to OpenMVG
openMVS::MVS_IO::SfM_Scene sceneBAF;
FOREACH(p, scene.platforms) {
const MVS::Platform& platform = scene.platforms[p];
if (platform.cameras.GetSize() != 1) {
LOG("error: unsupported scene structure");
return EXIT_FAILURE;
}
const MVS::Platform::Camera& camera = platform.cameras[0];
openMVS::MVS_IO::Camera cameraBAF;
cameraBAF.K = camera.K;
sceneBAF.cameras.push_back(cameraBAF);
}
FOREACH(i, scene.images) {
const MVS::Image& image = scene.images[i];
const MVS::Platform& platform = scene.platforms[image.platformID];
const MVS::Platform::Pose& pose = platform.poses[image.poseID];
openMVS::MVS_IO::Image imageBAF;
imageBAF.name = image.name;
imageBAF.name = MAKE_PATH_REL(WORKING_FOLDER_FULL, imageBAF.name);
imageBAF.id_camera = image.platformID;
imageBAF.id_pose = (uint32_t)sceneBAF.poses.size();
sceneBAF.images.push_back(imageBAF);
openMVS::MVS_IO::Pose poseBAF;
poseBAF.R = pose.R;
poseBAF.C = pose.C;
sceneBAF.poses.push_back(poseBAF);
}
sceneBAF.vertices.reserve(scene.pointcloud.points.GetSize());
FOREACH(p, scene.pointcloud.points) {
const MVS::PointCloud::Point& point = scene.pointcloud.points[p];
openMVS::MVS_IO::Vertex vertexBAF;
vertexBAF.X = ((const MVS::PointCloud::Point::EVec)point).cast<REAL>();
const MVS::PointCloud::ViewArr& views = scene.pointcloud.pointViews[p];
FOREACH(v, views) {
unsigned viewBAF = views[(uint32_t)v];
vertexBAF.views.push_back(viewBAF);
}
sceneBAF.vertices.push_back(vertexBAF);
}
// write OpenMVG input data
const String strOutputFileNameMVG(OPT::strOutputFileName + MVG_EXT);
openMVS::MVS_IO::ExportScene(MAKE_PATH_SAFE(OPT::strListFileName), MAKE_PATH_SAFE(strOutputFileNameMVG), sceneBAF);
VERBOSE("Input data exported: %u cameras & %u poses & %u images & %u vertices (%s)", sceneBAF.cameras.size(), sceneBAF.poses.size(), sceneBAF.images.size(), sceneBAF.vertices.size(), TD_TIMER_GET_FMT().c_str());
} else {
// convert data from OpenMVG to OpenMVS
MVS::Scene scene(OPT::nMaxThreads);
size_t nCameras(0), nPoses(0);
#ifdef _USE_OPENMVG
if (OPT::bOpenMVGjson) {
// read OpenMVG input data from a JSON file
using namespace openMVG::sfm;
using namespace openMVG::cameras;
SfM_Data sfm_data;
const String strSfM_Data_Filename(MAKE_PATH_SAFE(OPT::strInputFileName));
if (!Load(sfm_data, strSfM_Data_Filename, ESfM_Data(ALL))) {
VERBOSE("error: the input SfM_Data file '%s' cannot be read", strSfM_Data_Filename.c_str());
return EXIT_FAILURE;
}
VERBOSE("Imported data: %u cameras, %u poses, %u images, %u vertices",
sfm_data.GetIntrinsics().size(),
sfm_data.GetPoses().size(),
sfm_data.GetViews().size(),
sfm_data.GetLandmarks().size());
// OpenMVG can have not contiguous index, use a map to create the required OpenMVS contiguous ID index
std::map<openMVG::IndexT, uint32_t> map_intrinsic, map_view;
// define a platform with all the intrinsic group
nCameras = sfm_data.GetIntrinsics().size();
for (const auto& intrinsic: sfm_data.GetIntrinsics()) {
if (isPinhole(intrinsic.second.get()->getType())) {
const Pinhole_Intrinsic * cam = dynamic_cast<const Pinhole_Intrinsic*>(intrinsic.second.get());
if (map_intrinsic.count(intrinsic.first) == 0)
map_intrinsic.insert(std::make_pair(intrinsic.first, scene.platforms.GetSize()));
MVS::Platform& platform = scene.platforms.AddEmpty();
// add the camera
MVS::Platform::Camera& camera = platform.cameras.AddEmpty();
camera.K = cam->K();
// sub-pose
camera.R = RMatrix::IDENTITY;
camera.C = CMatrix::ZERO;
}
}
// define images & poses
const uint32_t nViews((uint32_t)sfm_data.GetViews().size());
scene.images.Reserve(nViews);
scene.nCalibratedImages = 0;
Util::Progress progress(_T("Processed images"), nViews);
GET_LOGCONSOLE().Pause();
#ifdef _USE_OPENMP
const std::vector<Views::value_type> views(sfm_data.GetViews().cbegin(), sfm_data.GetViews().cend());
#pragma omp parallel for schedule(dynamic)
for (int i=0; i<(int)nViews; ++i) {
const Views::value_type& view = views[i];
#pragma omp critical
map_view[view.first] = scene.images.GetSize();
#else
for (const auto& view : sfm_data.GetViews()) {
map_view[view.first] = scene.images.GetSize();
#endif
MVS::Image& image = scene.images.AddEmpty();
image.name = view.second->s_Img_path;
Util::ensureUnifySlash(image.name);
Util::strTrim(image.name, PATH_SEPARATOR_STR);
String pathRoot(sfm_data.s_root_path); Util::ensureFolderSlash(pathRoot);
const String srcImage(MAKE_PATH_FULL(WORKING_FOLDER_FULL, pathRoot+image.name));
image.name = MAKE_PATH_FULL(WORKING_FOLDER_FULL, OPT::strOutputImageFolder+image.name);
Util::ensureDirectory(image.name);
image.ID = static_cast<MVS::IIndex>(view.first);
image.platformID = map_intrinsic.at(view.second->id_intrinsic);
MVS::Platform& platform = scene.platforms[image.platformID];
image.cameraID = 0;
if (sfm_data.IsPoseAndIntrinsicDefined(view.second.get()) &&
File::access(srcImage)) {
MVS::Platform::Pose* pPose;
#ifdef _USE_OPENMP
#pragma omp critical
#endif
{
image.poseID = platform.poses.GetSize();
pPose = &platform.poses.AddEmpty();
++scene.nCalibratedImages;
}
const openMVG::geometry::Pose3 poseMVG(sfm_data.GetPoseOrDie(view.second.get()));
pPose->R = poseMVG.rotation();
pPose->C = poseMVG.center();
// export undistorted images
const openMVG::cameras::IntrinsicBase * cam = sfm_data.GetIntrinsics().at(view.second->id_intrinsic).get();
if (cam->have_disto()) {
// undistort and save the image
openMVG::image::Image<openMVG::image::RGBColor> imageRGB, imageRGB_ud;
openMVG::image::ReadImage(srcImage, &imageRGB);
openMVG::cameras::UndistortImage(imageRGB, cam, imageRGB_ud, openMVG::image::BLACK);
openMVG::image::WriteImage(image.name, imageRGB_ud);
} else {
// no distortion, copy the image
File::copyFile(srcImage, image.name);
}
++nPoses;
} else {
// image have not valid pose, so set an undefined pose
image.poseID = NO_ID;
// just copy the image
File::copyFile(srcImage, image.name);
DEBUG_EXTRA("warning: uncalibrated image '%s'", view.second->s_Img_path.c_str());
}
++progress;
}
GET_LOGCONSOLE().Play();
progress.close();
// define structure
scene.pointcloud.points.Reserve(sfm_data.GetLandmarks().size());
scene.pointcloud.pointViews.Reserve(sfm_data.GetLandmarks().size());
for (const auto& vertex: sfm_data.GetLandmarks()) {
const Landmark & landmark = vertex.second;
MVS::PointCloud::ViewArr& views = scene.pointcloud.pointViews.AddEmpty();
for (const auto& observation: landmark.obs) {
const auto it(map_view.find(observation.first));
if (it != map_view.end())
views.InsertSort(it->second);
}
if (views.GetSize() < 2) {
scene.pointcloud.pointViews.RemoveLast();
continue;
}
MVS::PointCloud::Point& point = scene.pointcloud.points.AddEmpty();
point = landmark.X.cast<float>();
}
} else
#endif
{
// read OpenMVG input data from BAF file
openMVS::MVS_IO::SfM_Scene sceneBAF;
if (!openMVS::MVS_IO::ImportScene(MAKE_PATH_SAFE(OPT::strListFileName), MAKE_PATH_SAFE(OPT::strInputFileName), sceneBAF))
return EXIT_FAILURE;
// convert data from OpenMVG to OpenMVS
nCameras = sceneBAF.cameras.size();
scene.platforms.Reserve((uint32_t)nCameras);
for (const auto& cameraBAF: sceneBAF.cameras) {
MVS::Platform& platform = scene.platforms.AddEmpty();
MVS::Platform::Camera& camera = platform.cameras.AddEmpty();
camera.K = cameraBAF.K;
camera.R = RMatrix::IDENTITY;
camera.C = CMatrix::ZERO;
}
nPoses = sceneBAF.images.size();
scene.images.Reserve((uint32_t)nPoses);
for (const auto& imageBAF: sceneBAF.images) {
openMVS::MVS_IO::Pose& poseBAF = sceneBAF.poses[imageBAF.id_pose];
MVS::Image& image = scene.images.AddEmpty();
image.name = imageBAF.name;
Util::ensureUnifySlash(image.name);
image.name = MAKE_PATH_FULL(WORKING_FOLDER_FULL, image.name);
image.ID = imageBAF.id_camera;
image.platformID = imageBAF.id_camera;
MVS::Platform& platform = scene.platforms[image.platformID];
image.cameraID = 0;
image.poseID = platform.poses.GetSize();
MVS::Platform::Pose& pose = platform.poses.AddEmpty();
pose.R = poseBAF.R;
pose.C = poseBAF.C;
}
scene.pointcloud.points.Reserve(sceneBAF.vertices.size());
scene.pointcloud.pointViews.Reserve(sceneBAF.vertices.size());
for (const auto& vertexBAF: sceneBAF.vertices) {
MVS::PointCloud::Point& point = scene.pointcloud.points.AddEmpty();
point = vertexBAF.X.cast<float>();
MVS::PointCloud::ViewArr& views = scene.pointcloud.pointViews.AddEmpty();
for (const auto& viewBAF: vertexBAF.views)
views.InsertSort(viewBAF);
}
}
// read images meta-data
FOREACHPTR(pImage, scene.images) {
if (!pImage->ReloadImage(0, false))
LOG("error: can not read image %s", pImage->name.c_str());
}
if (OPT::bNormalizeIntrinsics) {
// normalize camera intrinsics
FOREACH(p, scene.platforms) {
MVS::Platform& platform = scene.platforms[p];
FOREACH(c, platform.cameras) {
MVS::Platform::Camera& camera = platform.cameras[c];
// find one image using this camera
MVS::Image* pImage(NULL);
FOREACHPTR(pImg, scene.images) {
if (pImg->platformID == p && pImg->cameraID == c && pImg->poseID != NO_ID) {
pImage = pImg;
break;
}
}
if (pImage == NULL) {
LOG("error: no image using camera %u of platform %u", c, p);
continue;
}
const REAL fScale(REAL(1)/MVS::Camera::GetNormalizationScale(pImage->width, pImage->height));
camera.K(0,0) *= fScale;
camera.K(1,1) *= fScale;
camera.K(0,2) *= fScale;
camera.K(1,2) *= fScale;
}
}
}
// write OpenMVS input data
scene.Save(MAKE_PATH_SAFE(OPT::strOutputFileName), (ARCHIVE_TYPE)OPT::nArchiveType);
VERBOSE("Exported data: %u platforms, %u cameras, %u poses, %u images, %u vertices (%s)",
scene.platforms.GetSize(), nCameras, nPoses, scene.images.GetSize(), scene.pointcloud.GetSize(),
TD_TIMER_GET_FMT().c_str());
}
return EXIT_SUCCESS;
}
/*----------------------------------------------------------------*/

13
apps/InterfacePolycam/CMakeLists.txt

@ -0,0 +1,13 @@ @@ -0,0 +1,13 @@
if(MSVC)
FILE(GLOB LIBRARY_FILES_C "*.cpp" "*.rc")
else()
FILE(GLOB LIBRARY_FILES_C "*.cpp")
endif()
FILE(GLOB LIBRARY_FILES_H "*.h" "*.inl")
cxx_executable_with_flags(InterfacePolycam "Apps" "${cxx_default}" "MVS;${OpenMVS_EXTRA_LIBS}" ${LIBRARY_FILES_C} ${LIBRARY_FILES_H})
# Install
INSTALL(TARGETS InterfacePolycam
EXPORT OpenMVSTargets
RUNTIME DESTINATION "${INSTALL_BIN_DIR}" COMPONENT bin)

362
apps/InterfacePolycam/InterfacePolycam.cpp

@ -0,0 +1,362 @@ @@ -0,0 +1,362 @@
/*
* InterfacePolycam.cpp
*
* Copyright (c) 2014-2023 SEACAVE
*
* Author(s):
*
* cDc <cdc.seacave@gmail.com>
*
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*
* Additional Terms:
*
* You are required to preserve legal notices and author attributions in
* that material or in the Appropriate Legal Notices displayed by works
* containing it.
*/
#include "../../libs/MVS/Common.h"
#include "../../libs/MVS/Scene.h"
#define JSON_NOEXCEPTION
#include "../../libs/IO/json.hpp"
#include <boost/program_options.hpp>
using namespace MVS;
// D E F I N E S ///////////////////////////////////////////////////
#define APPNAME _T("InterfacePolycam")
#define MVS_FILE_EXTENSION _T(".mvs")
#define JSON_EXT _T(".json")
#define DEPTH_EXT _T(".png")
// S T R U C T S ///////////////////////////////////////////////////
namespace {
namespace OPT {
String strInputFileName;
String strOutputFileName;
unsigned nArchiveType;
int nProcessPriority;
unsigned nMaxThreads;
String strConfigFileName;
boost::program_options::variables_map vm;
} // namespace OPT
class Application {
public:
Application() {}
~Application() { Finalize(); }
bool Initialize(size_t argc, LPCTSTR* argv);
void Finalize();
}; // Application
// initialize and parse the command line parameters
bool Application::Initialize(size_t argc, LPCTSTR* argv)
{
// initialize log and console
OPEN_LOG();
OPEN_LOGCONSOLE();
// group of options allowed only on command line
boost::program_options::options_description generic("Generic options");
generic.add_options()
("help,h", "imports SfM scene stored Polycam format")
("working-folder,w", boost::program_options::value<std::string>(&WORKING_FOLDER), "working directory (default current directory)")
("config-file,c", boost::program_options::value<std::string>(&OPT::strConfigFileName)->default_value(APPNAME _T(".cfg")), "file name containing program options")
("archive-type", boost::program_options::value(&OPT::nArchiveType)->default_value(ARCHIVE_MVS), "project archive type: -1-interface, 0-text, 1-binary, 2-compressed binary")
("process-priority", boost::program_options::value(&OPT::nProcessPriority)->default_value(-1), "process priority (below normal by default)")
("max-threads", boost::program_options::value(&OPT::nMaxThreads)->default_value(0), "maximum number of threads (0 for using all available cores)")
#if TD_VERBOSE != TD_VERBOSE_OFF
("verbosity,v", boost::program_options::value(&g_nVerbosityLevel)->default_value(
#if TD_VERBOSE == TD_VERBOSE_DEBUG
3
#else
2
#endif
), "verbosity level")
#endif
;
// group of options allowed both on command line and in config file
boost::program_options::options_description config("Main options");
config.add_options()
("input-file,i", boost::program_options::value<std::string>(&OPT::strInputFileName), "input folder containing Polycam camera poses, images and depth-maps")
("output-file,o", boost::program_options::value<std::string>(&OPT::strOutputFileName), "output filename for storing the scene")
;
boost::program_options::options_description cmdline_options;
cmdline_options.add(generic).add(config);
boost::program_options::options_description config_file_options;
config_file_options.add(config);
boost::program_options::positional_options_description p;
p.add("input-file", -1);
try {
// parse command line options
boost::program_options::store(boost::program_options::command_line_parser((int)argc, argv).options(cmdline_options).positional(p).run(), OPT::vm);
boost::program_options::notify(OPT::vm);
INIT_WORKING_FOLDER;
// parse configuration file
std::ifstream ifs(MAKE_PATH_SAFE(OPT::strConfigFileName));
if (ifs) {
boost::program_options::store(parse_config_file(ifs, config_file_options), OPT::vm);
boost::program_options::notify(OPT::vm);
}
}
catch (const std::exception& e) {
LOG(e.what());
return false;
}
// initialize the log file
OPEN_LOGFILE(MAKE_PATH(APPNAME _T("-")+Util::getUniqueName(0)+_T(".log")).c_str());
// print application details: version and command line
Util::LogBuild();
LOG(_T("Command line: ") APPNAME _T("%s"), Util::CommandLineToString(argc, argv).c_str());
// validate input
Util::ensureValidFolderPath(OPT::strInputFileName);
const bool bInvalidCommand(OPT::strInputFileName.empty());
if (OPT::vm.count("help") || bInvalidCommand) {
boost::program_options::options_description visible("Available options");
visible.add(generic).add(config);
GET_LOG() << visible;
}
if (bInvalidCommand)
return false;
// initialize optional options
Util::ensureValidFolderPath(OPT::strOutputFileName);
if (OPT::strOutputFileName.empty())
OPT::strOutputFileName = "scene" MVS_FILE_EXTENSION;
MVS::Initialize(APPNAME, OPT::nMaxThreads, OPT::nProcessPriority);
return true;
}
// finalize application instance
void Application::Finalize()
{
MVS::Finalize();
CLOSE_LOGFILE();
CLOSE_LOGCONSOLE();
CLOSE_LOG();
}
// parse image containing calibration, pose, and depth-map information
bool ParseImage(Scene& scene, const String& imagePath, const String& cameraPath, const String& depthPath,
const std::unordered_map<String, IIndex>& mapImageName)
{
nlohmann::json data = nlohmann::json::parse(std::ifstream(cameraPath));
if (data.empty())
return false;
const cv::Size resolution(data["width"].get<uint32_t>(), data["height"].get<uint32_t>());
// set platform
const IIndex platformID = scene.platforms.size();
Platform& platform = scene.platforms.AddEmpty();
Platform::Camera& camera = platform.cameras.AddEmpty();
camera.K = KMatrix::IDENTITY;
camera.R = RMatrix::IDENTITY;
camera.C = CMatrix::ZERO;
camera.K(0,0) = data["fx"].get<float>();
camera.K(1,1) = data["fy"].get<float>();
camera.K(0,2) = data["cx"].get<float>();
camera.K(1,2) = data["cy"].get<float>();
// set image
const IIndex imageID = scene.images.size();
Image& imageData = scene.images.AddEmpty();
imageData.platformID = platformID;
imageData.cameraID = 0; // only one camera per platform supported by this format
imageData.poseID = NO_ID;
imageData.ID = imageID;
imageData.name = imagePath;
ASSERT(Util::isFullPath(imageData.name));
// set image resolution
imageData.width = resolution.width;
imageData.height = resolution.height;
imageData.scale = 1;
// set camera pose
imageData.poseID = platform.poses.size();
Platform::Pose& pose = platform.poses.AddEmpty();
const Eigen::Matrix3d R_session_arkitcam{
{data["t_00"].get<double>(), data["t_01"].get<double>(), data["t_02"].get<double>()},
{data["t_10"].get<double>(), data["t_11"].get<double>(), data["t_12"].get<double>()},
{data["t_20"].get<double>(), data["t_21"].get<double>(), data["t_22"].get<double>()}
};
const Eigen::Vector3d t_session_arkitcam{
data["t_03"].get<double>(),
data["t_13"].get<double>(),
data["t_23"].get<double>()
};
const Eigen::Affine3d T_session_arkitcam{
Eigen::Affine3d(Eigen::Translation3d(t_session_arkitcam)) * Eigen::Affine3d(Eigen::AngleAxisd(R_session_arkitcam))
};
const Eigen::Affine3d T_cam_arkitcam{
Eigen::AngleAxisd(M_PI, Eigen::Vector3d::UnitX())
};
const Eigen::Matrix4d P{
(T_cam_arkitcam * T_session_arkitcam.inverse()).matrix()
};
pose.R = P.topLeftCorner<3, 3>().eval();
pose.R.EnforceOrthogonality();
const Point3d t = P.topRightCorner<3, 1>().eval();
pose.C = pose.R.t() * (-t);
imageData.camera = platform.GetCamera(imageData.cameraID, imageData.poseID);
// set image neighbors if available
nlohmann::json::const_iterator itNeighbors = data.find("neighbors");
if (itNeighbors != data.end()) {
const std::vector<uint64_t> neighborTimestamps = itNeighbors->get<std::vector<uint64_t>>();
for (uint64_t timestamp: neighborTimestamps) {
const String neighborName = std::to_string(timestamp);
const IIndex neighborID = mapImageName.at(neighborName);
if (neighborID != imageData.ID)
imageData.neighbors.emplace_back(ViewScore{neighborID, 0, 1.f, FD2R(15.f), 0.5f, 3.f});
}
}
// load and convert depth-map
if (!depthPath.empty()) {
DepthMap depthMap; {
constexpr double depthScale{1000.0};
const cv::Mat imgDepthMap = cv::imread(depthPath, cv::IMREAD_ANYDEPTH);
if (imgDepthMap.empty())
return false;
imgDepthMap.convertTo(depthMap, CV_32FC1, 1.0/depthScale);
}
IIndexArr IDs = {imageData.ID};
IDs.JoinFunctor(imageData.neighbors.size(), [&imageData](IIndex i) {
return imageData.neighbors[i].ID;
});
double dMin, dMax;
cv::minMaxIdx(depthMap, &dMin, &dMax, NULL, NULL, depthMap > 0);
const NormalMap normalMap;
const ConfidenceMap confMap;
const ViewsMap viewsMap;
if (!ExportDepthDataRaw(MAKE_PATH(String::FormatString("depth%04u.dmap", imageData.ID)),
imageData.name, IDs, resolution,
camera.K, pose.R, pose.C,
(float)dMin, (float)dMax,
depthMap, normalMap, confMap, viewsMap))
return false;
}
return true;
}
// parse scene stored in Polycam format
bool ParseScene(Scene& scene, const String& scenePath)
{
#if defined(_SUPPORT_CPP17) && (!defined(__GNUC__) || (__GNUC__ > 7))
size_t numCorrectedFolders(0), numCorrectedDepthFolders(0), numFolders(0), numDepthFolders(0);
for (const auto& file: std::filesystem::directory_iterator(scenePath.c_str())) {
if (file.path().stem() == "corrected_cameras" ||
file.path().stem() == "corrected_images")
++numCorrectedFolders;
else if (file.path().stem() == "corrected_depth")
++numCorrectedDepthFolders;
else if (file.path().stem() == "cameras" ||
file.path().stem() == "images")
++numFolders;
else if (file.path().stem() == "depth")
++numDepthFolders;
}
if (numFolders != 2) {
VERBOSE("Invalid scene folder");
return false;
}
if (numCorrectedFolders == 2) {
// corrected data
CLISTDEFIDX(String, IIndex) imagePaths;
for (const auto& file: std::filesystem::directory_iterator((scenePath + "corrected_images").c_str()))
imagePaths.emplace_back(file.path().string());
VERBOSE("Parsing corrected data: %u...", imagePaths.size());
std::unordered_map<String, IIndex> mapImageName;
mapImageName.reserve(imagePaths.size());
for (String& imagePath: imagePaths) {
Util::ensureValidPath(imagePath);
mapImageName.emplace(Util::getFileName(imagePath), static_cast<IIndex>(mapImageName.size()));
}
for (const String& imagePath: imagePaths) {
const String imageName = Util::getFileName(imagePath);
const String cameraPath(scenePath + "corrected_cameras" + PATH_SEPARATOR_STR + imageName + JSON_EXT);
const String depthPath(numCorrectedDepthFolders ? scenePath + "corrected_depth" + PATH_SEPARATOR_STR + imageName + DEPTH_EXT : String());
if (!ParseImage(scene, imagePath, cameraPath, depthPath, mapImageName))
return false;
}
} else {
// raw data
CLISTDEFIDX(String, IIndex) imagePaths;
for (const auto& file: std::filesystem::directory_iterator((scenePath + "images").c_str()))
imagePaths.emplace_back(file.path().string());
VERBOSE("Parsing raw data: %u...", imagePaths.size());
std::unordered_map<String, IIndex> mapImageName;
mapImageName.reserve(imagePaths.size());
for (String& imagePath: imagePaths) {
Util::ensureValidPath(imagePath);
mapImageName.emplace(Util::getFileName(imagePath), static_cast<IIndex>(mapImageName.size()));
}
for (const String& imagePath: imagePaths) {
const String imageName = Util::getFileName(imagePath);
const String cameraPath(scenePath + "cameras" + PATH_SEPARATOR_STR + imageName + JSON_EXT);
const String depthPath(numDepthFolders ? scenePath + "depth" + PATH_SEPARATOR_STR + imageName + DEPTH_EXT : String());
if (!ParseImage(scene, imagePath, cameraPath, depthPath, mapImageName))
return false;
}
}
return true;
#else
return false;
#endif
}
} // unnamed namespace
int main(int argc, LPCTSTR* argv)
{
#ifdef _DEBUGINFO
// set _crtBreakAlloc index to stop in <dbgheap.c> at allocation
_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);// | _CRTDBG_CHECK_ALWAYS_DF);
#endif
Application application;
if (!application.Initialize(argc, argv))
return EXIT_FAILURE;
TD_TIMER_START();
Scene scene(OPT::nMaxThreads);
// convert data from Polycam format to OpenMVS
if (!ParseScene(scene, MAKE_PATH_FULL(WORKING_FOLDER_FULL, OPT::strInputFileName)))
return EXIT_FAILURE;
// write OpenMVS input data
scene.Save(MAKE_PATH_SAFE(OPT::strOutputFileName), (ARCHIVE_TYPE)OPT::nArchiveType);
VERBOSE("Exported data: %u platforms, %u cameras, %u poses, %u images (%s)",
scene.platforms.size(), scene.images.size(), scene.images.size(), scene.images.size(),
TD_TIMER_GET_FMT().c_str());
return EXIT_SUCCESS;
}
/*----------------------------------------------------------------*/

13
apps/InterfaceVisualSFM/CMakeLists.txt

@ -0,0 +1,13 @@ @@ -0,0 +1,13 @@
if(MSVC)
FILE(GLOB LIBRARY_FILES_C "*.cpp" "*.rc")
else()
FILE(GLOB LIBRARY_FILES_C "*.cpp")
endif()
FILE(GLOB LIBRARY_FILES_H "*.h" "*.inl")
cxx_executable_with_flags(InterfaceVisualSFM "Apps" "${cxx_default}" "MVS;${OpenMVS_EXTRA_LIBS}" ${LIBRARY_FILES_C} ${LIBRARY_FILES_H})
# Install
INSTALL(TARGETS InterfaceVisualSFM
EXPORT OpenMVSTargets
RUNTIME DESTINATION "${INSTALL_BIN_DIR}" COMPONENT bin)

383
apps/InterfaceVisualSFM/DataInterface.h

@ -0,0 +1,383 @@ @@ -0,0 +1,383 @@
////////////////////////////////////////////////////////////////////////////
// File: DataInterface.h
// Author: Changchang Wu (ccwu@cs.washington.edu)
// Description : data interface, the data format been uploaded to GPU
//
// Copyright (c) 2011 Changchang Wu (ccwu@cs.washington.edu)
// and the University of Washington at Seattle
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public
// License as published by the Free Software Foundation; either
// Version 3 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
////////////////////////////////////////////////////////////////////////////////
#ifndef DATA_INTERFACE_GPU_H
#define DATA_INTERFACE_GPU_H
namespace PBA {
// ----------------------------WARNING------------------------------
// -----------------------------------------------------------------
// ROTATION CONVERSION:
// The internal rotation representation is 3x3 float matrix. Reading
// back the rotations as quaternion or Rodrigues's representation will
// cause inaccuracy, IF you have wrongly reconstructed cameras with
// a very very large focal length (typically also very far away).
// In this case, any small change in the rotation matrix, will cause
// a large reprojection error.
//
// ---------------------------------------------------------------------
// RADIAL distortion is NOT enabled by default, use parameter "-md", -pd"
// or set ConfigBA::__use_radial_distortion to 1 or -1 to enable it.
// ---------------------------------------------------------------------------
//transfer data type with 4-float alignment
#define CameraT CameraT_
#define Point3D Point3D_
template<class FT>
struct CameraT_
{
typedef FT float_t;
//////////////////////////////////////////////////////
float_t f; // single focal length, K = [f, 0, 0; 0 f 0; 0 0 1]
float_t t[3]; // T in P = K[R T], T = - RC
float_t m[3][3]; // R in P = K[R T].
float_t radial; // WARNING: BE careful with the radial distortion model.
float_t distortion_type;
float_t constant_camera;
//////////////////////////////////////////////////////////
CameraT_() { radial = 0; distortion_type = 0; constant_camera = 0; }
//////////////////////////////////////////////
template <class CameraX> void SetCameraT(const CameraX & cam)
{
f = (float_t)cam.f;
t[0] = (float_t)cam.t[0]; t[1] = (float_t)cam.t[1]; t[2] = (float_t)cam.t[2];
for(int i = 0; i < 3; ++i) for(int j = 0; j < 3; ++j) m[i][j] = (float_t)cam.m[i][j];
radial = (float_t)cam.radial;
distortion_type = (float_t)cam.distortion_type;
constant_camera = (float_t)cam.constant_camera;
}
//////////////////////////////////////////
enum {
CAMERA_VARIABLE = 0,
CAMERA_FIXEDINTRINSIC = (1<<0),
CAMERA_FIXEDEXTRINSIC = (1<<1),
};
void SetVariableCamera() {(int&)constant_camera = CAMERA_VARIABLE;}
void SetFixedIntrinsic() {(int&)constant_camera = CAMERA_FIXEDINTRINSIC;}
void SetFixedExtrinsic() {(int&)constant_camera = CAMERA_FIXEDEXTRINSIC;}
void SetConstantCamera() {(int&)constant_camera = CAMERA_FIXEDINTRINSIC|CAMERA_FIXEDEXTRINSIC;}
//////////////////////////////////////
template <class Float> void SetFocalLength(Float F){ f = (float_t) F; }
float_t GetFocalLength() const{return f;}
template <class Float> void SetMeasurementDistortion(Float r) {radial = (float_t) r; distortion_type = -1;}
float_t GetMeasurementDistortion() const {return distortion_type == -1 ? radial : 0; }
//normalize radial distortion that applies to angle will be (radial * f * f);
template <class Float> void SetNormalizedMeasurementDistortion(Float r) {SetMeasurementDistortion(r / (f * f)); }
float_t GetNormalizedMeasurementDistortion() const{return GetMeasurementDistortion() * (f * f); }
//use projection distortion
template <class Float> void SetProjectionDistortion(Float r) {radial = float_t(r); distortion_type = 1; }
template <class Float> void SetProjectionDistortion(const Float* r) {SetProjectionDistortion(r[0]); }
float_t GetProjectionDistortion() {return distortion_type == 1 ? radial : 0; }
template <class Float> void SetRodriguesRotation(const Float r[3])
{
double a = sqrt(r[0]*r[0]+r[1]*r[1]+r[2]*r[2]);
double ct = a==0.0?0.5:(1.0-cos(a))/a/a;
double st = a==0.0?1:sin(a)/a;
m[0][0]=float_t(1.0 - (r[1]*r[1] + r[2]*r[2])*ct);
m[0][1]=float_t(r[0]*r[1]*ct - r[2]*st);
m[0][2]=float_t(r[2]*r[0]*ct + r[1]*st);
m[1][0]=float_t(r[0]*r[1]*ct + r[2]*st);
m[1][1]=float_t(1.0 - (r[2]*r[2] + r[0]*r[0])*ct);
m[1][2]=float_t(r[1]*r[2]*ct - r[0]*st);
m[2][0]=float_t(r[2]*r[0]*ct - r[1]*st);
m[2][1]=float_t(r[1]*r[2]*ct + r[0]*st);
m[2][2]=float_t(1.0 - (r[0]*r[0] + r[1]*r[1])*ct );
}
template <class Float> void GetRodriguesRotation(Float r[3]) const
{
double a = (m[0][0]+m[1][1]+m[2][2]-1.0)/2.0;
const double epsilon = 0.01;
if( fabs(m[0][1] - m[1][0]) < epsilon &&
fabs(m[1][2] - m[2][1]) < epsilon &&
fabs(m[0][2] - m[2][0]) < epsilon )
{
if( fabs(m[0][1] + m[1][0]) < 0.1 &&
fabs(m[1][2] + m[2][1]) < 0.1 &&
fabs(m[0][2] + m[2][0]) < 0.1 && a > 0.9)
{
r[0] = 0;
r[1] = 0;
r[2] = 0;
}
else
{
const Float ha = Float(sqrt(0.5) * 3.14159265358979323846);
double xx = (m[0][0]+1.0)/2.0;
double yy = (m[1][1]+1.0)/2.0;
double zz = (m[2][2]+1.0)/2.0;
double xy = (m[0][1]+m[1][0])/4.0;
double xz = (m[0][2]+m[2][0])/4.0;
double yz = (m[1][2]+m[2][1])/4.0;
if ((xx > yy) && (xx > zz))
{
if (xx< epsilon)
{
r[0] = 0; r[1] = r[2] = ha;
} else
{
double t = sqrt(xx) ;
r[0] = Float(t * 3.14159265358979323846);
r[1] = Float(xy/t * 3.14159265358979323846);
r[2] = Float(xz/t * 3.14159265358979323846);
}
} else if (yy > zz)
{
if (yy< epsilon)
{
r[0] = r[2] = ha; r[1] = 0;
} else
{
double t = sqrt(yy);
r[0] = Float(xy/t* 3.14159265358979323846);
r[1] = Float( t * 3.14159265358979323846);
r[2] = Float(yz/t* 3.14159265358979323846);
}
} else
{
if (zz< epsilon)
{
r[0] = r[1] = ha; r[2] = 0;
} else
{
double t = sqrt(zz);
r[0] = Float(xz/ t* 3.14159265358979323846);
r[1] = Float(yz/ t* 3.14159265358979323846);
r[2] = Float( t * 3.14159265358979323846);
}
}
}
}
else
{
a = acos(a);
double b = 0.5*a/sin(a);
r[0] = Float(b*(m[2][1]-m[1][2]));
r[1] = Float(b*(m[0][2]-m[2][0]));
r[2] = Float(b*(m[1][0]-m[0][1]));
}
}
////////////////////////
template <class Float> void SetQuaternionRotation(const Float q[4])
{
double qq = sqrt(q[0]*q[0]+q[1]*q[1]+q[2]*q[2]+q[3]*q[3]);
double qw, qx, qy, qz;
if(qq>0)
{
qw=q[0]/qq;
qx=q[1]/qq;
qy=q[2]/qq;
qz=q[3]/qq;
}else
{
qw = 1;
qx = qy = qz = 0;
}
m[0][0]=float_t(qw*qw + qx*qx- qz*qz- qy*qy );
m[0][1]=float_t(2*qx*qy -2*qz*qw );
m[0][2]=float_t(2*qy*qw + 2*qz*qx);
m[1][0]=float_t(2*qx*qy+ 2*qw*qz);
m[1][1]=float_t(qy*qy+ qw*qw - qz*qz- qx*qx);
m[1][2]=float_t(2*qz*qy- 2*qx*qw);
m[2][0]=float_t(2*qx*qz- 2*qy*qw);
m[2][1]=float_t(2*qy*qz + 2*qw*qx );
m[2][2]=float_t(qz*qz+ qw*qw- qy*qy- qx*qx);
}
template <class Float> void GetQuaternionRotation(Float q[4]) const
{
q[0]= 1 + m[0][0] + m[1][1] + m[2][2];
if(q[0]>0.000000001)
{
q[0] = sqrt(q[0])/2.0;
q[1]= (m[2][1] - m[1][2])/( 4.0 *q[0]);
q[2]= (m[0][2] - m[2][0])/( 4.0 *q[0]);
q[3]= (m[1][0] - m[0][1])/( 4.0 *q[0]);
}else
{
double s;
if ( m[0][0] > m[1][1] && m[0][0] > m[2][2] )
{
s = 2.0 * sqrt( 1.0 + m[0][0] - m[1][1] - m[2][2]);
q[1] = 0.25 * s;
q[2] = (m[0][1] + m[1][0] ) / s;
q[3] = (m[0][2] + m[2][0] ) / s;
q[0] = (m[1][2] - m[2][1] ) / s;
} else if (m[1][1] > m[2][2])
{
s = 2.0 * sqrt( 1.0 + m[1][1] - m[0][0] - m[2][2]);
q[1] = (m[0][1] + m[1][0] ) / s;
q[2] = 0.25 * s;
q[3] = (m[1][2] + m[2][1] ) / s;
q[0] = (m[0][2] - m[2][0] ) / s;
} else
{
s = 2.0 * sqrt( 1.0 + m[2][2] - m[0][0] - m[1][1]);
q[1] = (m[0][2] + m[2][0] ) / s;
q[2] = (m[1][2] + m[2][1] ) / s;
q[3] = 0.25f * s;
q[0] = (m[0][1] - m[1][0] ) / s;
}
}
}
////////////////////////////////////////////////
template <class Float> void SetMatrixRotation(const Float * r)
{
for(int i = 0; i < 9; ++i) m[0][i] = float_t(r[i]);
}
template <class Float> void GetMatrixRotation(Float * r) const
{
for(int i = 0; i < 9; ++i) r[i] = Float(m[0][i]);
}
float GetRotationMatrixDeterminant()const
{
return m[0][0]*m[1][1]*m[2][2] +
m[0][1]*m[1][2]*m[2][0] +
m[0][2]*m[1][0]*m[2][1] -
m[0][2]*m[1][1]*m[2][0] -
m[0][1]*m[1][0]*m[2][2] -
m[0][0]*m[1][2]*m[2][1];
}
///////////////////////////////////////
template <class Float> void SetTranslation(const Float T[3])
{
t[0] = (float_t)T[0];
t[1] = (float_t)T[1];
t[2] = (float_t)T[2];
}
template <class Float> void GetTranslation(Float T[3]) const
{
T[0] = (Float)t[0];
T[1] = (Float)t[1];
T[2] = (Float)t[2];
}
/////////////////////////////////////////////
template <class Float> void SetCameraCenterAfterRotation(const Float c[3])
{
//t = - R * C
for(int j = 0; j < 3; ++j) t[j] = -float_t(double(m[j][0])*double(c[0]) + double(m[j][1])*double(c[1]) + double(m[j][2])*double(c[2]));
}
template <class Float> void GetCameraCenter(Float c[3]) const
{
//C = - R' * t
for(int j = 0; j < 3; ++j) c[j] = -Float(double(m[0][j])*double(t[0]) + double(m[1][j])*double(t[1]) + double(m[2][j])*double(t[2]));
}
////////////////////////////////////////////
template <class Float> void SetInvertedRT(const Float e[3], const Float T[3])
{
SetRodriguesRotation(e);
for(int i = 3; i < 9; ++i) m[0][i] = - m[0][i];
SetTranslation(T); t[1] = - t[1]; t[2] = -t[2];
}
template <class Float> void GetInvertedRT (Float e[3], Float T[3]) const
{
CameraT ci; ci.SetMatrixRotation(m[0]);
for(int i = 3; i < 9; ++i) ci.m[0][i] = - ci.m[0][i];
//for(int i = 1; i < 3; ++i) for(int j = 0; j < 3; ++j) ci.m[i][j] = - ci.m[i][j];
ci.GetRodriguesRotation(e);
GetTranslation(T); T[1] = - T[1]; T[2] = -T[2];
}
template <class Float> void SetInvertedR9T(const Float e[9], const Float T[3])
{
//for(int i = 0; i < 9; ++i) m[0][i] = (i < 3 ? e[i] : - e[i]);
//SetTranslation(T); t[1] = - t[1]; t[2] = -t[2];
m[0][0] = e[0]; m[0][1] = e[1]; m[0][2] = e[2];
m[1][0] = -e[3]; m[1][1] = -e[4]; m[1][2] = -e[5];
m[2][0] = -e[6]; m[2][1] = -e[7]; m[2][2] = -e[8];
t[0] = T[0]; t[1] = -T[1]; t[2] = -T[2];
}
template<class Float> void GetInvertedR9T(Float e[9], Float T[3]) const
{
e[0] = m[0][0]; e[1] = m[0][1]; e[2] = m[0][2];
e[3] = - m[1][0]; e[4] = -m[1][1]; e[5] = -m[1][2];
e[6] = -m[2][0]; e[7] = -m[2][1]; e[8] = -m[2][2] ;
T[0] = t[0]; T[1] = -t[1]; T[2] = -t[2];
}
};
template<class FT>
struct Point3D
{
typedef FT float_t;
float_t xyz[3]; //3D point location
float_t reserved; //alignment
////////////////////////////////
template <class Float> void SetPoint(Float x, Float y, Float z)
{
xyz[0] = (float_t) x;
xyz[1] = (float_t) y;
xyz[2] = (float_t) z;
reserved = 0;
}
template <class Float> void SetPoint(const Float * p)
{
xyz[0] = (float_t) p[0];
xyz[1] = (float_t) p[1];
xyz[2] = (float_t) p[2];
reserved = 0;
}
template <class Float> void GetPoint(Float* p) const
{
p[0] = (Float) xyz[0];
p[1] = (Float) xyz[1];
p[2] = (Float) xyz[2];
}
template <class Float> void GetPoint(Float&x, Float&y, Float&z) const
{
x = (Float) xyz[0];
y = (Float) xyz[1];
z = (Float) xyz[2];
}
};
#undef CameraT
#undef Point3D
typedef CameraT_<float> CameraT;
typedef CameraT_<double> CameraD;
typedef Point3D_<float> Point3D;
typedef Point3D_<double> Point3B;
struct Point2D
{
float x, y;
////////////////////////////////////////////////////////
Point2D(){}
template <class Float> Point2D(Float X, Float Y) {SetPoint2D(X, Y); }
template <class Float> void SetPoint2D(Float X, Float Y) { x = (float) X; y = (float) Y; }
template <class Float> void GetPoint2D(Float&X, Float&Y) const { X = (Float) x; Y = (Float) y; }
};
} // namespace PBA
#endif

607
apps/InterfaceVisualSFM/InterfaceVisualSFM.cpp

@ -0,0 +1,607 @@ @@ -0,0 +1,607 @@
/*
* InterfaceVisualSFM.cpp
*
* Copyright (c) 2014-2015 SEACAVE
*
* Author(s):
*
* cDc <cdc.seacave@gmail.com>
*
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*
* Additional Terms:
*
* You are required to preserve legal notices and author attributions in
* that material or in the Appropriate Legal Notices displayed by works
* containing it.
*/
#include "../../libs/MVS/Common.h"
#include "../../libs/MVS/Scene.h"
#define LOG_OUT() GET_LOG()
#define LOG_ERR() GET_LOG()
#include "Util.h"
#include <boost/program_options.hpp>
// D E F I N E S ///////////////////////////////////////////////////
#define APPNAME _T("InterfaceVisualSFM")
#define MVS_EXT _T(".mvs")
#define VSFM_EXT _T(".nvm")
#define BUNDLE_EXT _T(".out")
#define CMPMVS_EXT _T(".lst")
// S T R U C T S ///////////////////////////////////////////////////
namespace {
namespace OPT {
String strInputFileName;
String strOutputFileName;
String strOutputImageFolder;
bool IsFromOpenMVS; // conversion direction
unsigned nArchiveType;
int nProcessPriority;
unsigned nMaxThreads;
String strConfigFileName;
boost::program_options::variables_map vm;
} // namespace OPT
class Application {
public:
Application() {}
~Application() { Finalize(); }
bool Initialize(size_t argc, LPCTSTR* argv);
void Finalize();
}; // Application
// initialize and parse the command line parameters
bool Application::Initialize(size_t argc, LPCTSTR* argv)
{
// initialize log and console
OPEN_LOG();
OPEN_LOGCONSOLE();
// group of options allowed only on command line
boost::program_options::options_description generic("Generic options");
generic.add_options()
("help,h", "produce this help message")
("working-folder,w", boost::program_options::value<std::string>(&WORKING_FOLDER), "working directory (default current directory)")
("config-file,c", boost::program_options::value<std::string>(&OPT::strConfigFileName)->default_value(APPNAME _T(".cfg")), "file name containing program options")
("archive-type", boost::program_options::value(&OPT::nArchiveType)->default_value(ARCHIVE_DEFAULT), "project archive type: 0-text, 1-binary, 2-compressed binary")
("process-priority", boost::program_options::value(&OPT::nProcessPriority)->default_value(-1), "process priority (below normal by default)")
("max-threads", boost::program_options::value(&OPT::nMaxThreads)->default_value(0), "maximum number of threads (0 for using all available cores)")
#if TD_VERBOSE != TD_VERBOSE_OFF
("verbosity,v", boost::program_options::value(&g_nVerbosityLevel)->default_value(
#if TD_VERBOSE == TD_VERBOSE_DEBUG
3
#else
2
#endif
), "verbosity level")
#endif
;
// group of options allowed both on command line and in config file
boost::program_options::options_description config("Main options");
config.add_options()
("input-file,i", boost::program_options::value<std::string>(&OPT::strInputFileName), "input filename containing camera poses and image list (NVM, undistorted OUT + image_list.TXT, LST)")
("output-file,o", boost::program_options::value<std::string>(&OPT::strOutputFileName), "output filename for storing the mesh")
("output-image-folder", boost::program_options::value<std::string>(&OPT::strOutputImageFolder)->default_value("undistorted_images"), "output folder to store undistorted images")
;
boost::program_options::options_description cmdline_options;
cmdline_options.add(generic).add(config);
boost::program_options::options_description config_file_options;
config_file_options.add(config);
boost::program_options::positional_options_description p;
p.add("input-file", -1);
try {
// parse command line options
boost::program_options::store(boost::program_options::command_line_parser((int)argc, argv).options(cmdline_options).positional(p).run(), OPT::vm);
boost::program_options::notify(OPT::vm);
INIT_WORKING_FOLDER;
// parse configuration file
std::ifstream ifs(MAKE_PATH_SAFE(OPT::strConfigFileName));
if (ifs) {
boost::program_options::store(parse_config_file(ifs, config_file_options), OPT::vm);
boost::program_options::notify(OPT::vm);
}
}
catch (const std::exception& e) {
LOG(e.what());
return false;
}
// initialize the log file
OPEN_LOGFILE(MAKE_PATH(APPNAME _T("-")+Util::getUniqueName(0)+_T(".log")).c_str());
// print application details: version and command line
Util::LogBuild();
LOG(_T("Command line: ") APPNAME _T("%s"), Util::CommandLineToString(argc, argv).c_str());
// validate input
Util::ensureValidPath(OPT::strInputFileName);
Util::ensureUnifySlash(OPT::strInputFileName);
if (OPT::vm.count("help") || OPT::strInputFileName.IsEmpty()) {
boost::program_options::options_description visible("Available options");
visible.add(generic).add(config);
GET_LOG() << visible;
}
if (OPT::strInputFileName.IsEmpty())
return false;
// initialize optional options
if (OPT::strInputFileName.IsEmpty())
return false;
Util::ensureValidPath(OPT::strOutputFileName);
Util::ensureUnifySlash(OPT::strOutputFileName);
Util::ensureUnifySlash(OPT::strOutputImageFolder);
Util::ensureFolderSlash(OPT::strOutputImageFolder);
const String strInputFileNameExt(Util::getFileExt(OPT::strInputFileName).ToLower());
OPT::IsFromOpenMVS = (strInputFileNameExt == MVS_FILE_EXTENSION);
if (OPT::IsFromOpenMVS) {
if (OPT::strOutputFileName.empty())
OPT::strOutputFileName = Util::getFilePath(OPT::strInputFileName);
} else {
if (OPT::strOutputFileName.empty())
OPT::strOutputFileName = Util::getFilePath(OPT::strInputFileName) + _T("scene") MVS_FILE_EXTENSION;
else
OPT::strOutputImageFolder = Util::getRelativePath(Util::getFilePath(OPT::strOutputFileName), Util::getFilePath(OPT::strInputFileName)+OPT::strOutputImageFolder);
}
MVS::Initialize(APPNAME, OPT::nMaxThreads, OPT::nProcessPriority);
return true;
}
// finalize application instance
void Application::Finalize()
{
MVS::Finalize();
CLOSE_LOGFILE();
CLOSE_LOGCONSOLE();
CLOSE_LOG();
}
} // unnamed namespace
#define PBA_PRECISION float
namespace PBA {
template<class FT> struct CameraT_;
typedef CameraT_<PBA_PRECISION> Camera;
template<class FT> struct Point3D_;
typedef Point3D_<PBA_PRECISION> Point3D;
} // namespace PBA
namespace MVS {
// given an undistorted pixel coordinate and one radial-undistortion parameter,
// compute the corresponding distorted coordinate
template<typename TYPE>
inline TPoint2<TYPE> DistortPointR1(const TPoint2<TYPE>& pt, const REAL& k1) {
if (k1 == 0)
return pt;
const REAL y(pt.y == 0 ? REAL(1.e-12) : REAL(pt.y));
const REAL t2(y*y);
const REAL t3(t2*t2*t2);
const REAL t4(pt.x*pt.x);
const REAL t7(k1*(t2+t4));
const REAL t9(1.0/t7);
const REAL t10(t2*t9*y*0.5);
const REAL t11(t3*t9*t9*(0.25+t9/27.0));
#ifndef _RELEASE
TPoint2<TYPE> upt;
#endif
if (k1 > 0) {
const REAL t17(CBRT(t10+SQRT(t11)));
const REAL t18(t17-t2*t9/(t17*3));
#ifndef _RELEASE
upt =
#else
return
#endif
TPoint2<TYPE>(TYPE(t18*pt.x/y), TYPE(t18));
} else {
ASSERT(t11 <= 0);
const std::complex<REAL> t16(t10, SQRT(-t11));
const std::complex<REAL> t17(pow(t16, 1.0/3.0));
const std::complex<REAL> t14((t2*t9)/(t17*3.0));
const std::complex<REAL> t18((t17+t14)*std::complex<REAL>(0.0,SQRT_3));
const std::complex<REAL> t19(0.5*(t14-t17-t18));
#ifndef _RELEASE
upt =
#else
return
#endif
TPoint2<TYPE>(TYPE(t19.real()*pt.x/y), TYPE(t19.real()));
}
#ifndef _RELEASE
ASSERT(ABS(TYPE((1.0+k1*(upt.x*upt.x+upt.y*upt.y))*upt.x) - pt.x) < TYPE(0.001));
ASSERT(ABS(TYPE((1.0+k1*(upt.x*upt.x+upt.y*upt.y))*upt.y) - pt.y) < TYPE(0.001));
return upt;
#endif
}
void UndistortImage(const Camera& camera, const REAL& k1, const Image8U3 imgIn, Image8U3& imgOut)
{
// allocate the undistorted image
if (imgOut.data == imgIn.data ||
imgOut.cols != imgIn.cols ||
imgOut.rows != imgIn.rows ||
imgOut.type() != imgIn.type())
imgOut = Image8U3(imgIn.rows, imgIn.cols);
// compute each pixel
const int w = imgIn.cols;
const int h = imgIn.rows;
const Matrix3x3f K(camera.K);
const Matrix3x3f invK(camera.GetInvK());
ASSERT(ISEQUAL(K(0,2),0.5f*(w-1)) && ISEQUAL(K(1,2),0.5f*(h-1)));
typedef Sampler::Cubic<float> Sampler;
const Sampler sampler;
Point2f pt;
for (int v=0; v<h; ++v) {
for (int u=0; u<w; ++u) {
// compute corresponding coordinates in the distorted image
pt.x = (float)u; pt.y = (float)v;
NormalizeProjection(invK.val, pt.ptr(), pt.ptr());
pt = DistortPointR1(pt, k1);
NormalizeProjection(K.val, pt.ptr(), pt.ptr());
// if coordinates in range
Pixel8U& col = imgOut(v,u);
if (imgIn.isInside(pt)) {
// get pixel color
col = imgIn.sample<Sampler,Pixel32F>(sampler, pt).cast<uint8_t>();
} else {
// set to black
col = Pixel8U::BLACK;
}
}
}
}
} // namespace MVS
bool ExportSceneVSFM()
{
TD_TIMER_START();
// read MVS input data
MVS::Scene scene(OPT::nMaxThreads);
if (!scene.Load(MAKE_PATH_SAFE(OPT::strInputFileName)))
return false;
// convert and write data from OpenMVS to VisualSFM
std::vector<PBA::Camera> cameras;
std::vector<PBA::Point3D> vertices;
std::vector<PBA::Point2D> measurements; // the array of 2D projections (only inliers)
std::vector<int> correspondingPoint; // 3D point index corresponding to each 2D projection
std::vector<int> correspondingView; // and camera index
std::vector<std::string> names;
std::vector<int> ptc;
cameras.reserve(scene.images.size());
names.reserve(scene.images.size());
MVS::IIndexArr mapIdx(scene.images.size());
bool bFocalWarning(false), bPrincipalpointWarning(false);
FOREACH(idx, scene.images) {
const MVS::Image& image = scene.images[idx];
if (!image.IsValid()) {
mapIdx[idx] = NO_ID;
continue;
}
if (!bFocalWarning && !ISEQUAL(image.camera.K(0, 0), image.camera.K(1, 1))) {
DEBUG("warning: fx != fy and NVM format does not support it");
bFocalWarning = true;
}
if (!bPrincipalpointWarning && (!ISEQUAL(REAL(image.width-1)*0.5, image.camera.K(0, 2)) || !ISEQUAL(REAL(image.height-1)*0.5, image.camera.K(1, 2)))) {
DEBUG("warning: cx, cy are not the image center and NVM format does not support it");
bPrincipalpointWarning = true;
}
PBA::Camera cameraNVM;
cameraNVM.SetFocalLength((image.camera.K(0, 0) + image.camera.K(1, 1)) * 0.5);
cameraNVM.SetMatrixRotation(image.camera.R.val);
cameraNVM.SetCameraCenterAfterRotation(image.camera.C.ptr());
mapIdx[idx] = static_cast<MVS::IIndex>(cameras.size());
cameras.emplace_back(cameraNVM);
names.emplace_back(MAKE_PATH_REL(WORKING_FOLDER_FULL, image.name));
}
vertices.reserve(scene.pointcloud.points.size());
measurements.reserve(scene.pointcloud.pointViews.size());
correspondingPoint.reserve(scene.pointcloud.pointViews.size());
correspondingView.reserve(scene.pointcloud.pointViews.size());
FOREACH(idx, scene.pointcloud.points) {
const MVS::PointCloud::Point& X = scene.pointcloud.points[idx];
const MVS::PointCloud::ViewArr& views = scene.pointcloud.pointViews[idx];
const size_t prevMeasurements(measurements.size());
for (MVS::IIndex idxView: views) {
const MVS::Image& image = scene.images[idxView];
const Point2f pt(image.camera.TransformPointW2I(Cast<REAL>(X)));
if (pt.x < 0 || pt.y < 0 || pt.x > image.width-1 || pt.y > image.height-1)
continue;
measurements.emplace_back(pt.x, pt.y);
correspondingView.emplace_back(static_cast<int>(mapIdx[idxView]));
correspondingPoint.emplace_back(static_cast<int>(vertices.size()));
}
if (prevMeasurements < measurements.size())
vertices.emplace_back(PBA::Point3D{X.x, X.y, X.z});
}
if (!scene.pointcloud.colors.empty()) {
ptc.reserve(scene.pointcloud.colors.size()*3);
FOREACH(idx, scene.pointcloud.points) {
const MVS::PointCloud::Color& c = scene.pointcloud.colors[idx];
ptc.emplace_back(c.r);
ptc.emplace_back(c.g);
ptc.emplace_back(c.b);
}
}
PBA::SaveModelFile(MAKE_PATH_SAFE(OPT::strOutputFileName), cameras, vertices, measurements, correspondingPoint, correspondingView, names, ptc);
VERBOSE("Input data exported: %u images & %u points (%s)", scene.images.size(), scene.pointcloud.GetSize(), TD_TIMER_GET_FMT().c_str());
return true;
}
bool ImportSceneVSFM()
{
TD_TIMER_START();
// read VisualSFM input data
std::vector<PBA::Camera> cameras;
std::vector<PBA::Point3D> vertices;
std::vector<PBA::Point2D> measurements; // the array of 2D projections (only inliers)
std::vector<int> correspondingPoint; // 3D point index corresponding to each 2D projection
std::vector<int> correspondingView; // and camera index
std::vector<std::string> names;
std::vector<int> ptc;
if (!PBA::LoadModelFile(MAKE_PATH_SAFE(OPT::strInputFileName), cameras, vertices, measurements, correspondingPoint, correspondingView, names, ptc))
return false;
// convert data from VisualSFM to OpenMVS
MVS::Scene scene(OPT::nMaxThreads);
scene.platforms.Reserve((uint32_t)cameras.size());
scene.images.Reserve((MVS::IIndex)cameras.size());
scene.nCalibratedImages = 0;
for (size_t idx=0; idx<cameras.size(); ++idx) {
MVS::Image& image = scene.images.AddEmpty();
image.name = names[idx];
Util::ensureUnifySlash(image.name);
image.name = MAKE_PATH_FULL(WORKING_FOLDER_FULL, image.name);
if (!image.ReloadImage(0, false)) {
LOG("error: can not read image %s", image.name.c_str());
return false;
}
// set camera
image.platformID = scene.platforms.GetSize();
MVS::Platform& platform = scene.platforms.AddEmpty();
MVS::Platform::Camera& camera = platform.cameras.AddEmpty();
image.cameraID = 0;
image.ID = static_cast<MVS::IIndex>(idx);
const PBA::Camera& cameraNVM = cameras[idx];
camera.K = MVS::Platform::Camera::ComposeK<REAL,REAL>(cameraNVM.GetFocalLength(), cameraNVM.GetFocalLength(), image.width, image.height);
camera.R = RMatrix::IDENTITY;
camera.C = CMatrix::ZERO;
// normalize camera intrinsics
camera.K = camera.GetScaledK(REAL(1)/MVS::Camera::GetNormalizationScale(image.width, image.height));
// set pose
image.poseID = platform.poses.GetSize();
MVS::Platform::Pose& pose = platform.poses.AddEmpty();
cameraNVM.GetMatrixRotation(pose.R.val);
cameraNVM.GetCameraCenter(pose.C.ptr());
image.UpdateCamera(scene.platforms);
++scene.nCalibratedImages;
}
scene.pointcloud.points.Reserve(vertices.size());
for (size_t idx=0; idx<vertices.size(); ++idx) {
const PBA::Point3D& X = vertices[idx];
scene.pointcloud.points.AddConstruct(X.xyz[0], X.xyz[1], X.xyz[2]);
}
scene.pointcloud.pointViews.Resize(vertices.size());
for (size_t idx=0; idx<measurements.size(); ++idx) {
MVS::PointCloud::ViewArr& views = scene.pointcloud.pointViews[correspondingPoint[idx]];
views.InsertSort(correspondingView[idx]);
}
if (ptc.size() == vertices.size()*3) {
scene.pointcloud.colors.Reserve(ptc.size());
for (size_t idx=0; idx<ptc.size(); idx+=3)
scene.pointcloud.colors.AddConstruct((uint8_t)ptc[idx+0], (uint8_t)ptc[idx+1], (uint8_t)ptc[idx+2]);
}
// undistort images
const String pathData(MAKE_PATH_FULL(WORKING_FOLDER_FULL, OPT::strOutputImageFolder));
Util::Progress progress(_T("Processed images"), scene.images.GetSize());
#ifdef _USE_OPENMP
bool bAbort(false);
#pragma omp parallel for shared(bAbort) schedule(dynamic)
for (int i=0; i<(int)scene.images.GetSize(); ++i) {
#pragma omp flush (bAbort)
if (bAbort)
continue;
#else
FOREACH(i, scene.images) {
#endif
++progress;
MVS::Image& imageData = scene.images[i];
const PBA::Camera& cameraNVM = cameras[i];
if (cameraNVM.GetMeasurementDistortion() == 0)
continue;
if (!imageData.ReloadImage()) {
#ifdef _USE_OPENMP
bAbort = true;
#pragma omp flush (bAbort)
continue;
#else
return false;
#endif
}
MVS::UndistortImage(imageData.camera, cameraNVM.GetNormalizedMeasurementDistortion(), imageData.image, imageData.image);
const String name(pathData + String::FormatString(_T("%05u.png"), i));
Util::ensureFolder(name);
if (!imageData.image.Save(name)) {
#ifdef _USE_OPENMP
bAbort = true;
#pragma omp flush (bAbort)
continue;
#else
return false;
#endif
}
imageData.ReleaseImage();
}
#ifdef _USE_OPENMP
if (bAbort)
return false;
#endif
progress.close();
VERBOSE("Input data imported: %u cameras, %u poses, %u images, %u points (%s)", cameras.size(), cameras.size(), cameras.size(), vertices.size(), TD_TIMER_GET_FMT().c_str());
// write OpenMVS input data
return scene.SaveInterface(MAKE_PATH_SAFE(OPT::strOutputFileName));
}
template <typename T>
void _ImageListParseP(const LPSTR* argv, TMatrix<T,3,4>& P)
{
// read projection matrix
P(0,0) = String::FromString<T>(argv[0]);
P(0,1) = String::FromString<T>(argv[1]);
P(0,2) = String::FromString<T>(argv[2]);
P(0,3) = String::FromString<T>(argv[3]);
P(1,0) = String::FromString<T>(argv[4]);
P(1,1) = String::FromString<T>(argv[5]);
P(1,2) = String::FromString<T>(argv[6]);
P(1,3) = String::FromString<T>(argv[7]);
P(2,0) = String::FromString<T>(argv[8]);
P(2,1) = String::FromString<T>(argv[9]);
P(2,2) = String::FromString<T>(argv[10]);
P(2,3) = String::FromString<T>(argv[11]);
}
int ImportSceneCMPMVS()
{
TD_TIMER_START();
MVS::Scene scene(OPT::nMaxThreads);
// read CmpMVS input data as a list of images and their projection matrices
std::ifstream iFilein(MAKE_PATH_SAFE(OPT::strInputFileName));
if (!iFilein.is_open())
return false;
while (iFilein.good()) {
String strImageName;
std::getline(iFilein, strImageName);
if (strImageName.empty())
continue;
if (!File::access(MAKE_PATH_SAFE(strImageName)))
return false;
const String strImageNameP(Util::getFileFullName(strImageName)+"_P.txt");
std::ifstream iFileP(MAKE_PATH_SAFE(strImageNameP));
if (!iFileP.is_open())
return false;
String strP; int numLines(0);
while (iFileP.good()) {
String line;
std::getline(iFileP, line);
if (strImageName.empty())
break;
if (strP.empty())
strP = line;
else
strP += _T(' ') + line;
++numLines;
}
if (numLines != 3)
return false;
PMatrix P;
size_t argc;
CAutoPtrArr<LPSTR> argv(Util::CommandLineToArgvA(strP, argc));
if (argc != 12)
return false;
_ImageListParseP(argv, P);
KMatrix K; RMatrix R; CMatrix C;
MVS::DecomposeProjectionMatrix(P, K, R, C);
// set image
MVS::Image& image = scene.images.AddEmpty();
image.name = strImageName;
Util::ensureUnifySlash(image.name);
image.name = MAKE_PATH_FULL(WORKING_FOLDER_FULL, image.name);
if (!image.ReloadImage(0, false)) {
LOG("error: can not read image %s", image.name.c_str());
return false;
}
// set camera
image.platformID = scene.platforms.GetSize();
MVS::Platform& platform = scene.platforms.AddEmpty();
MVS::Platform::Camera& camera = platform.cameras.AddEmpty();
image.cameraID = 0;
camera.K = K;
camera.R = RMatrix::IDENTITY;
camera.C = CMatrix::ZERO;
// normalize camera intrinsics
camera.K = camera.GetScaledK(REAL(1)/MVS::Camera::GetNormalizationScale(image.width, image.height));
// set pose
image.poseID = platform.poses.GetSize();
MVS::Platform::Pose& pose = platform.poses.AddEmpty();
pose.R = R;
pose.C = C;
image.UpdateCamera(scene.platforms);
++scene.nCalibratedImages;
}
VERBOSE("Input data imported: %u images (%s)", scene.images.size(), TD_TIMER_GET_FMT().c_str());
// write OpenMVS input data
return scene.SaveInterface(MAKE_PATH_SAFE(OPT::strOutputFileName));
}
int main(int argc, LPCTSTR* argv)
{
#ifdef _DEBUGINFO
// set _crtBreakAlloc index to stop in <dbgheap.c> at allocation
_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);// | _CRTDBG_CHECK_ALWAYS_DF);
#endif
Application application;
if (!application.Initialize(argc, argv))
return EXIT_FAILURE;
if (OPT::IsFromOpenMVS) {
ExportSceneVSFM();
} else {
const String strInputFileNameExt(Util::getFileExt(OPT::strInputFileName).ToLower());
if (strInputFileNameExt == VSFM_EXT || strInputFileNameExt == BUNDLE_EXT) {
if (!ImportSceneVSFM())
return EXIT_FAILURE;
} else
if (strInputFileNameExt == CMPMVS_EXT) {
if (!ImportSceneCMPMVS())
return EXIT_FAILURE;
}
}
return EXIT_SUCCESS;
}
/*----------------------------------------------------------------*/

756
apps/InterfaceVisualSFM/Util.h

@ -0,0 +1,756 @@ @@ -0,0 +1,756 @@
////////////////////////////////////////////////////////////////////////////
// File: util.h
// Author: Changchang Wu (ccwu@cs.washington.edu)
// Description : some utility functions for reading/writing SfM data
//
// Copyright (c) 2011 Changchang Wu (ccwu@cs.washington.edu)
// and the University of Washington at Seattle
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public
// License as published by the Free Software Foundation; either
// Version 3 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
////////////////////////////////////////////////////////////////////////////////
#include <iostream>
#include <fstream>
#include <vector>
#include <string>
#include <math.h>
#include <time.h>
#include <iomanip>
#include <algorithm>
#include "DataInterface.h"
namespace PBA {
//File loader supports .nvm format and bundler format
bool LoadModelFile(const char* name, std::vector<CameraT>& camera_data, std::vector<Point3D>& point_data,
std::vector<Point2D>& measurements, std::vector<int>& ptidx, std::vector<int>& camidx,
std::vector<std::string>& names, std::vector<int>& ptc);
void SaveNVM(const char* filename, std::vector<CameraT>& camera_data, std::vector<Point3D>& point_data,
std::vector<Point2D>& measurements, std::vector<int>& ptidx, std::vector<int>& camidx,
std::vector<std::string>& names, std::vector<int>& ptc);
void SaveBundlerModel(const char* filename, std::vector<CameraT>& camera_data, std::vector<Point3D>& point_data,
std::vector<Point2D>& measurements, std::vector<int>& ptidx, std::vector<int>& camidx);
//////////////////////////////////////////////////////////////////
void AddNoise(std::vector<CameraT>& camera_data, std::vector<Point3D>& point_data, float percent);
void AddStableNoise(std::vector<CameraT>& camera_data, std::vector<Point3D>& point_data,
const std::vector<int>& ptidx, const std::vector<int>& camidx, float percent);
bool RemoveInvisiblePoints( std::vector<CameraT>& camera_data, std::vector<Point3D>& point_data,
std::vector<int>& ptidx, std::vector<int>& camidx,
std::vector<Point2D>& measurements, std::vector<std::string>& names, std::vector<int>& ptc);
/////////////////////////////////////////////////////////////////////////////
bool LoadNVM(std::ifstream& in, std::vector<CameraT>& camera_data, std::vector<Point3D>& point_data,
std::vector<Point2D>& measurements, std::vector<int>& ptidx, std::vector<int>& camidx,
std::vector<std::string>& names, std::vector<int>& ptc)
{
int rotation_parameter_num = 4;
bool format_r9t = false;
std::string token;
if(in.peek() == 'N')
{
in >> token; //file header
if(strstr(token.c_str(), "R9T"))
{
rotation_parameter_num = 9; //rotation as 3x3 matrix
format_r9t = true;
}
}
double fxFixed, fyFixed, cxFixed, cyFixed, k1(0);
int ncam = 0, npoint = 0, nproj = 0;
in >> token;
if (token == "FixedK") {
// read fixed intrinsics
std::getline(in, token);
sscanf(token.c_str(), "%lf %lf %lf %lf %lf", &fxFixed, &cxFixed, &fyFixed, &cyFixed, &k1);
// read # of cameras
in >> ncam;
} else {
// read # of cameras
ncam = atoi(token.c_str());
}
if(ncam <= 1) return false;
//read the camera parameters
camera_data.resize(ncam); // allocate the camera data
names.resize(ncam);
for(int i = 0; i < ncam; ++i)
{
double f, q[9], c[3], d[2];
in >> token >> f ;
for(int j = 0; j < rotation_parameter_num; ++j) in >> q[j];
in >> c[0] >> c[1] >> c[2] >> d[0] >> d[1];
camera_data[i].SetFocalLength(f);
if(format_r9t)
{
camera_data[i].SetMatrixRotation(q);
camera_data[i].SetTranslation(c);
}
else
{
//older format for compatibility
camera_data[i].SetQuaternionRotation(q); //quaternion from the file
camera_data[i].SetCameraCenterAfterRotation(c); //camera center from the file
}
camera_data[i].SetNormalizedMeasurementDistortion(k1!=0 ? k1 : d[0]);
names[i] = token;
}
//////////////////////////////////////
in >> npoint; if(npoint <= 0) return false;
//read image projections and 3D points.
point_data.resize(npoint);
for(int i = 0; i < npoint; ++i)
{
float pt[3]; int cc[3], npj;
in >> pt[0] >> pt[1] >> pt[2]
>> cc[0] >> cc[1] >> cc[2] >> npj;
for(int j = 0; j < npj; ++j)
{
int cidx, fidx; float imx, imy;
in >> cidx >> fidx >> imx >> imy;
camidx.push_back(cidx); //camera index
ptidx.push_back(i); //point index
//add a measurement to the vector
measurements.push_back(Point2D(imx, imy));
nproj ++;
}
point_data[i].SetPoint(pt);
ptc.insert(ptc.end(), cc, cc + 3);
}
///////////////////////////////////////////////////////////////////////////////
LOG_OUT() << ncam << " cameras; " << npoint << " 3D points; " << nproj << " projections\n";
return true;
}
void SaveNVM(const char* filename, std::vector<CameraT>& camera_data, std::vector<Point3D>& point_data,
std::vector<Point2D>& measurements, std::vector<int>& ptidx, std::vector<int>& camidx,
std::vector<std::string>& names, std::vector<int>& ptc)
{
LOG_OUT() << "Saving model to " << filename << "...\n";
std::ofstream out(filename);
out << "NVM_V3_R9T\n" << camera_data.size() << '\n' << std::setprecision(12);
if(names.size() < camera_data.size()) names.resize(camera_data.size(),std::string("unknown"));
if(ptc.size() < 3 * point_data.size()) ptc.resize(point_data.size() * 3, 0);
////////////////////////////////////
for(size_t i = 0; i < camera_data.size(); ++i)
{
CameraT& cam = camera_data[i];
out << names[i] << ' ' << cam.GetFocalLength() << ' ';
for(int j = 0; j < 9; ++j) out << cam.m[0][j] << ' ';
out << cam.t[0] << ' ' << cam.t[1] << ' ' << cam.t[2] << ' '
<< cam.GetNormalizedMeasurementDistortion() << " 0\n";
}
out << point_data.size() << '\n';
for(size_t i = 0, j = 0; i < point_data.size(); ++i)
{
Point3D& pt = point_data[i];
int * pc = &ptc[i * 3];
out << pt.xyz[0] << ' ' << pt.xyz[1] << ' ' << pt.xyz[2] << ' '
<< pc[0] << ' ' << pc[1] << ' ' << pc[2] << ' ';
size_t je = j;
while(je < ptidx.size() && ptidx[je] == (int) i) je++;
out << (je - j) << ' ';
for(; j < je; ++j) out << camidx[j] << ' ' << " 0 " << measurements[j].x << ' ' << measurements[j].y << ' ';
out << '\n';
}
}
bool LoadBundlerOut(const char* name, std::ifstream& in, std::vector<CameraT>& camera_data, std::vector<Point3D>& point_data,
std::vector<Point2D>& measurements, std::vector<int>& ptidx, std::vector<int>& camidx,
std::vector<std::string>& names, std::vector<int>& ptc)
{
int rotation_parameter_num = 9;
std::string token;
while(in.peek() == '#') std::getline(in, token);
char listpath[1024], filepath[1024];
strcpy(listpath, name);
char* ext = strstr(listpath, ".out");
strcpy(ext, "-list.txt\0");
///////////////////////////////////
std::ifstream listin(listpath);
if(!listin.is_open())
{
listin.close(); listin.clear();
strcpy(ext, ".txt\0");
listin.open(listpath);
}
if(!listin.is_open())
{
listin.close(); listin.clear();
char * slash = strrchr(listpath, '/');
if(slash == NULL) slash = strrchr(listpath, '\\');
slash = slash ? slash + 1 : listpath;
strcpy(slash, "image_list.txt");
listin.open(listpath);
}
if(listin) LOG_OUT() << "Using image list: " << listpath << '\n';
// read # of cameras
int ncam = 0, npoint = 0, nproj = 0;
in >> ncam >> npoint;
if(ncam <= 1 || npoint <= 1) return false;
LOG_OUT() << ncam << " cameras; " << npoint << " 3D points;\n";
//read the camera parameters
camera_data.resize(ncam); // allocate the camera data
names.resize(ncam);
bool det_checked = false;
for(int i = 0; i < ncam; ++i)
{
float f, q[9], c[3], d[2];
in >> f >> d[0] >> d[1];
for(int j = 0; j < rotation_parameter_num; ++j) in >> q[j];
in >> c[0] >> c[1] >> c[2];
camera_data[i].SetFocalLength(f);
camera_data[i].SetInvertedR9T(q, c);
camera_data[i].SetProjectionDistortion(d[0]);
if(listin >> filepath && f != 0)
{
names[i] = filepath;
std::getline(listin, token);
if(!det_checked)
{
float det = camera_data[i].GetRotationMatrixDeterminant();
LOG_OUT() << "Check rotation matrix: " << det << '\n';
det_checked = true;
}
}else
{
names[i] = "unknown";
}
}
//read image projections and 3D points.
point_data.resize(npoint);
for(int i = 0; i < npoint; ++i)
{
float pt[3]; int cc[3], npj;
in >> pt[0] >> pt[1] >> pt[2]
>> cc[0] >> cc[1] >> cc[2] >> npj;
for(int j = 0; j < npj; ++j)
{
int cidx, fidx; float imx, imy;
in >> cidx >> fidx >> imx >> imy;
camidx.push_back(cidx); //camera index
ptidx.push_back(i); //point index
//add a measurement to the vector
measurements.push_back(Point2D(imx, -imy));
nproj ++;
}
point_data[i].SetPoint(pt[0], pt[1], pt[2]);
ptc.insert(ptc.end(), cc, cc + 3);
}
///////////////////////////////////////////////////////////////////////////////
LOG_OUT() << ncam << " cameras; " << npoint << " 3D points; " << nproj << " projections\n";
return true;
}
void SaveBundlerOut(const char* filename, std::vector<CameraT>& camera_data, std::vector<Point3D>& point_data,
std::vector<Point2D>& measurements, std::vector<int>& ptidx, std::vector<int>& camidx,
std::vector<std::string>& names, std::vector<int>& ptc)
{
char listpath[1024]; strcpy(listpath, filename);
char* ext = strstr(listpath, ".out"); if(ext == NULL) return;
strcpy(ext, "-list.txt\0");
std::ofstream out(filename);
out << "# Bundle file v0.3\n";
out << std::setprecision(12); //need enough precision
out << camera_data.size() << " " << point_data.size() << '\n';
//save camera data
for(size_t i = 0; i < camera_data.size(); ++i)
{
float q[9], c[3];
CameraT& ci = camera_data[i];
out << ci.GetFocalLength() << ' ' << ci.GetProjectionDistortion() << " 0\n";
ci.GetInvertedR9T(q, c);
for(int j = 0; j < 9; ++j) out << q[j] << (((j % 3) == 2)? '\n' : ' ');
out << c[0] << ' ' << c[1] << ' ' << c[2] << '\n';
}
///
for(size_t i = 0, j = 0; i < point_data.size(); ++i)
{
int npj = 0, *ci = &ptc[i * 3]; Point3D& pt = point_data[i];
while(j + npj < point_data.size() && ptidx[j + npj] == ptidx[j]) npj++;
///////////////////////////
out << pt.xyz[0] << ' ' << pt.xyz[1] << ' ' << pt.xyz[2] << '\n';
out << ci[0] << ' ' << ci[1] << ' ' << ci[2] << '\n';
out << npj << ' ';
for(int k = 0; k < npj; ++k) out << camidx[j + k] << " 0 "
<< measurements[j + k].x << ' ' << -measurements[j + k].y << '\n';
out << '\n'; j += npj;
}
std::ofstream listout(listpath);
for(size_t i = 0; i < names.size(); ++i) listout << names[i] << '\n';
}
template<class CameraT, class Point3D>
bool LoadBundlerModel(std::ifstream& in, std::vector<CameraT>& camera_data, std::vector<Point3D>& point_data,
std::vector<Point2D>& measurements, std::vector<int>& ptidx, std::vector<int>& camidx)
{
// read bundle data from a file
size_t ncam = 0, npt = 0, nproj = 0;
if(!(in >> ncam >> npt >> nproj)) return false;
///////////////////////////////////////////////////////////////////////////////
LOG_OUT() << ncam << " cameras; " << npt << " 3D points; " << nproj << " projections\n";
camera_data.resize(ncam);
point_data.resize(npt);
measurements.resize(nproj);
camidx.resize(nproj);
ptidx.resize(nproj);
for(size_t i = 0; i < nproj; ++i)
{
double x, y; int cidx, pidx;
in >> cidx >> pidx >> x >> y;
if(((size_t) pidx) == npt && camidx.size() > i)
{
camidx.resize(i);
ptidx.resize(i);
measurements.resize(i);
LOG_OUT() << "Truncate measurements to " << i << '\n';
}else if(((size_t) pidx) >= npt)
{
continue;
}else
{
camidx[i] = cidx; ptidx[i] = pidx;
measurements[i].SetPoint2D(x, -y);
}
}
for(size_t i = 0; i < ncam; ++i)
{
double p[9];
for(int j = 0; j < 9; ++j) in >> p[j];
CameraT& cam = camera_data[i];
cam.SetFocalLength(p[6]);
cam.SetInvertedRT(p, p + 3);
cam.SetProjectionDistortion(p[7]);
}
for(size_t i = 0; i < npt; ++i)
{
double pt[3];
in >> pt[0] >> pt[1] >> pt[2];
point_data[i].SetPoint(pt);
}
return true;
}
void SaveBundlerModel(const char* filename, std::vector<CameraT>& camera_data, std::vector<Point3D>& point_data,
std::vector<Point2D>& measurements, std::vector<int>& ptidx, std::vector<int>& camidx)
{
LOG_OUT() << "Saving model to " << filename << "...\n";
std::ofstream out(filename);
out << std::setprecision(12); //need enough precision
out << camera_data.size() << ' ' << point_data.size() << ' ' << measurements.size() << '\n';
for(size_t i = 0; i < measurements.size(); ++i)
{
out << camidx[i] << ' ' << ptidx[i] << ' ' << measurements[i].x << ' ' << -measurements[i].y << '\n';
}
for(size_t i = 0; i < camera_data.size(); ++i)
{
CameraT& cam = camera_data[i];
double r[3], t[3]; cam.GetInvertedRT(r, t);
out << r[0] << ' ' << r[1] << ' ' << r[2] << ' '
<< t[0] << ' ' << t[1] << ' ' << t[2] << ' ' << cam.f
<< ' ' << cam.GetProjectionDistortion() << " 0\n";
}
for(size_t i = 0; i < point_data.size(); ++i)
{
Point3D& pt = point_data[i];
out << pt.xyz[0] << ' ' << pt.xyz[1] << ' ' << pt.xyz[2] << '\n';
}
}
bool LoadModelFile(const char* name, std::vector<CameraT>& camera_data, std::vector<Point3D>& point_data,
std::vector<Point2D>& measurements, std::vector<int>& ptidx, std::vector<int>& camidx,
std::vector<std::string>& names, std::vector<int>& ptc)
{
if(name == NULL)return false;
std::ifstream in(name);
LOG_OUT() << "Loading cameras/points: " << name <<"\n" ;
if(!in.is_open()) return false;
if(strstr(name, ".nvm"))return LoadNVM(in, camera_data, point_data, measurements, ptidx, camidx, names, ptc);
else if(strstr(name, ".out")) return LoadBundlerOut(name, in, camera_data, point_data, measurements, ptidx, camidx, names, ptc);
else return LoadBundlerModel(in, camera_data, point_data, measurements, ptidx, camidx);
}
float random_ratio(float percent)
{
return (rand() % 101 - 50) * 0.02f * percent + 1.0f;
}
void AddNoise(std::vector<CameraT>& camera_data, std::vector<Point3D>& point_data, float percent)
{
std::srand((unsigned int) time(NULL));
for(size_t i = 0; i < camera_data.size(); ++i)
{
camera_data[i].f *= random_ratio(percent);
camera_data[i].t[0] *= random_ratio(percent);
camera_data[i].t[1] *= random_ratio(percent);
camera_data[i].t[2] *= random_ratio(percent);
double e[3];
camera_data[i].GetRodriguesRotation(e);
e[0] *= random_ratio(percent);
e[1] *= random_ratio(percent);
e[2] *= random_ratio(percent);
camera_data[i].SetRodriguesRotation(e);
}
for(size_t i = 0; i < point_data.size(); ++i)
{
point_data[i].xyz[0] *= random_ratio(percent);
point_data[i].xyz[1] *= random_ratio(percent);
point_data[i].xyz[2] *= random_ratio(percent);
}
}
void AddStableNoise(std::vector<CameraT>& camera_data, std::vector<Point3D>& point_data,
const std::vector<int>& ptidx, const std::vector<int>& camidx, float percent)
{
///
std::srand((unsigned int) time(NULL));
//do not modify the visibility status..
std::vector<float> zz0(ptidx.size());
std::vector<CameraT> backup = camera_data;
std::vector<float> vx(point_data.size()), vy(point_data.size()), vz(point_data.size());
for(size_t i = 0; i < point_data.size(); ++i)
{
Point3D& pt = point_data[i];
vx[i] = pt.xyz[0];
vy[i] = pt.xyz[1];
vz[i] = pt.xyz[2];
}
//find out the median location of all the 3D points.
size_t median_idx = point_data.size() / 2;
std::nth_element(vx.begin(), vx.begin() + median_idx, vx.end());
std::nth_element(vy.begin(), vy.begin() + median_idx, vy.end());
std::nth_element(vz.begin(), vz.begin() + median_idx, vz.end());
float cx = vx[median_idx], cy = vy[median_idx], cz = vz[median_idx];
for(size_t i = 0; i < ptidx.size(); ++i)
{
CameraT& cam = camera_data[camidx[i]];
Point3D& pt = point_data[ptidx[i]];
zz0[i] = cam.m[2][0] * pt.xyz[0] + cam.m[2][1] * pt.xyz[1] + cam.m[2][2] * pt.xyz[2] + cam.t[2];
}
std::vector<float> z2 = zz0; median_idx = ptidx.size() / 2;
std::nth_element(z2.begin(), z2.begin() + median_idx, z2.end());
float mz = z2[median_idx]; // median depth
float dist_noise_base = mz * 0.2f;
/////////////////////////////////////////////////
//modify points first..
for(size_t i = 0; i < point_data.size(); ++i)
{
Point3D& pt = point_data[i];
pt.xyz[0] = pt.xyz[0] - cx + dist_noise_base * random_ratio(percent);
pt.xyz[1] = pt.xyz[1] - cy + dist_noise_base * random_ratio(percent);
pt.xyz[2] = pt.xyz[2] - cz + dist_noise_base * random_ratio(percent);
}
std::vector<bool> need_modification(camera_data.size(), true);
int invalid_count = 0, modify_iteration = 1;
do
{
if(invalid_count) LOG_OUT() << "NOTE" << std::setw(2) << modify_iteration
<< ": modify " << invalid_count << " camera to fix visibility\n";
//////////////////////////////////////////////////////
for(size_t i = 0; i < camera_data.size(); ++i)
{
if(!need_modification[i])continue;
CameraT & cam = camera_data[i];
double e[3], c[3]; cam = backup[i];
cam.f *= random_ratio(percent);
///////////////////////////////////////////////////////////
cam.GetCameraCenter(c);
c[0] = c[0] - cx + dist_noise_base * random_ratio(percent);
c[1] = c[1] - cy + dist_noise_base * random_ratio(percent);
c[2] = c[2] - cz + dist_noise_base * random_ratio(percent);
///////////////////////////////////////////////////////////
cam.GetRodriguesRotation(e);
e[0] *= random_ratio(percent);
e[1] *= random_ratio(percent);
e[2] *= random_ratio(percent);
///////////////////////////////////////////////////////////
cam.SetRodriguesRotation(e);
cam.SetCameraCenterAfterRotation(c);
}
std::vector<bool> invalidc(camera_data.size(), false);
invalid_count = 0;
for(size_t i = 0; i < ptidx.size(); ++i)
{
int cid = camidx[i];
if(need_modification[cid] ==false) continue;
if(invalidc[cid])continue;
CameraT& cam = camera_data[cid];
Point3D& pt = point_data[ptidx[i]];
float z = cam.m[2][0] * pt.xyz[0] + cam.m[2][1] * pt.xyz[1] + cam.m[2][2] * pt.xyz[2] + cam.t[2];
if (z * zz0[i] > 0)continue;
if (zz0[i] == 0 && z > 0) continue;
invalid_count++;
invalidc[cid] = true;
}
need_modification = invalidc;
modify_iteration++;
}while(invalid_count && modify_iteration < 20);
}
void ExamineVisiblity(const char* input_filename )
{
//////////////
std::vector<CameraD> camera_data;
std::vector<Point3B> point_data;
std::vector<int> ptidx, camidx;
std::vector<Point2D> measurements;
std::ifstream in (input_filename);
LoadBundlerModel(in, camera_data, point_data, measurements, ptidx, camidx);
////////////////
int count = 0; double d1 = 100, d2 = 100;
LOG_OUT() << "checking visibility...\n";
std::vector<double> zz(ptidx.size());
for(size_t i = 0; i < ptidx.size(); ++i)
{
CameraD& cam = camera_data[camidx[i]];
Point3B& pt = point_data[ptidx[i]];
double dz = cam.m[2][0] * pt.xyz[0] + cam.m[2][1] * pt.xyz[1] + cam.m[2][2] * pt.xyz[2] + cam.t[2];
//double dx = cam.m[0][0] * pt.xyz[0] + cam.m[0][1] * pt.xyz[1] + cam.m[0][2] * pt.xyz[2] + cam.t[0];
//double dy = cam.m[1][0] * pt.xyz[0] + cam.m[1][1] * pt.xyz[1] + cam.m[1][2] * pt.xyz[2] + cam.t[1];
////////////////////////////////////////
float c[3]; cam.GetCameraCenter(c);
CameraT camt; camt.SetCameraT(cam);
Point3D ptt; ptt.SetPoint(pt.xyz);
double fz = camt.m[2][0] * ptt.xyz[0] + camt.m[2][1] * ptt.xyz[1] + camt.m[2][2] * ptt.xyz[2] + camt.t[2];
double fz2 = camt.m[2][0] * (ptt.xyz[0] - c[0]) + camt.m[2][1] * (ptt.xyz[1] - c[1])
+ camt.m[2][2] * (ptt.xyz[2] - c[2]);
//if(dz == 0 && fz == 0) continue;
if(dz * fz <= 0 || fz == 0)
{
LOG_OUT() << "cam " << camidx[i] //<<// "; dx = " << dx << "; dy = " << dy
<< "; double: " << dz << "; float " << fz << "; float2 " << fz2 << "\n";
//LOG_OUT() << cam.m[2][0] << " "<<cam.m[2][1]<< " " << cam.m[2][2] << " "<<cam.t[2] << "\n";
//LOG_OUT() << camt.m[2][0] << " "<<camt.m[2][1]<< " " << camt.m[2][2] << " "<<camt.t[2] << "\n";
//LOG_OUT() << cam.m[2][0] - camt.m[2][0] << " " <<cam.m[2][1] - camt.m[2][1]<< " "
// << cam.m[2][2] - camt.m[2][2] << " " <<cam.t[2] - camt.t[2]<< "\n";
}
zz[i] = dz;
d1 = std::min(fabs(dz), d1);
d2 = std::min(fabs(fz), d2);
}
LOG_OUT() << count << " points moved to wrong side "
<< d1 << ", " << d2 <<"\n";
}
bool RemoveInvisiblePoints( std::vector<CameraT>& camera_data, std::vector<Point3D>& point_data,
std::vector<int>& ptidx, std::vector<int>& camidx,
std::vector<Point2D>& measurements, std::vector<std::string>& names, std::vector<int>& ptc)
{
std::vector<float> zz(ptidx.size());
for(size_t i = 0; i < ptidx.size(); ++i)
{
CameraT& cam = camera_data[camidx[i]];
Point3D& pt = point_data[ptidx[i]];
zz[i] = cam.m[2][0] * pt.xyz[0] + cam.m[2][1] * pt.xyz[1] + cam.m[2][2] * pt.xyz[2] + cam.t[2];
}
size_t median_idx = ptidx.size() / 2;
std::nth_element(zz.begin(), zz.begin() + median_idx, zz.end());
float dist_threshold = zz[median_idx] * 0.001f;
//keep removing 3D points. until all of them are infront of the cameras..
std::vector<bool> pmask(point_data.size(), true);
int points_removed = 0;
for(size_t i = 0; i < ptidx.size(); ++i)
{
int cid = camidx[i], pid = ptidx[i];
if(!pmask[pid])continue;
CameraT& cam = camera_data[cid];
Point3D& pt = point_data[pid];
bool visible = (cam.m[2][0] * pt.xyz[0] + cam.m[2][1] * pt.xyz[1] + cam.m[2][2] * pt.xyz[2] + cam.t[2] > dist_threshold);
pmask[pid] = visible; //this point should be removed
if(!visible) points_removed++;
}
if(points_removed == 0) return false;
std::vector<int> cv(camera_data.size(), 0);
//should any cameras be removed ?
int min_observation = 20; //cameras should see at least 20 points
do
{
//count visible points for each camera
std::fill(cv.begin(), cv.end(), 0);
for(size_t i = 0; i < ptidx.size(); ++i)
{
int cid = camidx[i], pid = ptidx[i];
if(pmask[pid]) cv[cid]++;
}
//check if any more points should be removed
std::vector<int> pv(point_data.size(), 0);
for(size_t i = 0; i < ptidx.size(); ++i)
{
int cid = camidx[i], pid = ptidx[i];
if(!pmask[pid]) continue; //point already removed
if(cv[cid] < min_observation) //this camera shall be removed.
{
///
}else
{
pv[pid]++;
}
}
points_removed = 0;
for(size_t i = 0; i < point_data.size(); ++i)
{
if(pmask[i] == false) continue;
if(pv[i] >= 2) continue;
pmask[i] = false;
points_removed++;
}
}while(points_removed > 0);
////////////////////////////////////
std::vector<bool> cmask(camera_data.size(), true);
for(size_t i = 0; i < camera_data.size(); ++i) cmask[i] = cv[i] >= min_observation;
////////////////////////////////////////////////////////
std::vector<int> cidx(camera_data.size());
std::vector<int> pidx(point_data.size());
///modified model.
std::vector<CameraT> camera_data2;
std::vector<Point3D> point_data2;
std::vector<int> ptidx2;
std::vector<int> camidx2;
std::vector<Point2D> measurements2;
std::vector<std::string> names2;
std::vector<int> ptc2;
//
if(names.size() < camera_data.size()) names.resize(camera_data.size(),std::string("unknown"));
if(ptc.size() < 3 * point_data.size()) ptc.resize(point_data.size() * 3, 0);
//////////////////////////////
int new_camera_count = 0, new_point_count = 0;
for(size_t i = 0; i < camera_data.size(); ++i)
{
if(!cmask[i])continue;
camera_data2.push_back(camera_data[i]);
names2.push_back(names[i]);
cidx[i] = new_camera_count++;
}
for(size_t i = 0; i < point_data.size(); ++i)
{
if(!pmask[i])continue;
point_data2.push_back(point_data[i]);
ptc.push_back(ptc[i]);
pidx[i] = new_point_count++;
}
int new_observation_count = 0;
for(size_t i = 0; i < ptidx.size(); ++i)
{
int pid = ptidx[i], cid = camidx[i];
if(!pmask[pid] || ! cmask[cid]) continue;
ptidx2.push_back(pidx[pid]);
camidx2.push_back(cidx[cid]);
measurements2.push_back(measurements[i]);
new_observation_count++;
}
LOG_OUT() << "NOTE: removing " << (camera_data.size() - new_camera_count) << " cameras; "<< (point_data.size() - new_point_count)
<< " 3D Points; " << (measurements.size() - new_observation_count) << " Observations;\n";
camera_data2.swap(camera_data); names2.swap(names);
point_data2.swap(point_data); ptc2.swap(ptc);
ptidx2.swap(ptidx); camidx2.swap(camidx);
measurements2.swap(measurements);
return true;
}
void SaveModelFile(const char* outpath, std::vector<CameraT>& camera_data, std::vector<Point3D>& point_data,
std::vector<Point2D>& measurements, std::vector<int>& ptidx, std::vector<int>& camidx,
std::vector<std::string>& names, std::vector<int>& ptc)
{
if(outpath == NULL) return;
if(strstr(outpath, ".nvm"))
SaveNVM(outpath, camera_data, point_data, measurements, ptidx, camidx, names, ptc);
else if(strstr(outpath, ".out"))
SaveBundlerOut(outpath, camera_data, point_data, measurements, ptidx, camidx, names, ptc);
else
SaveBundlerModel(outpath, camera_data, point_data, measurements, ptidx, camidx);
}
} // namespace PBA

13
apps/ReconstructMesh/CMakeLists.txt

@ -0,0 +1,13 @@ @@ -0,0 +1,13 @@
if(MSVC)
FILE(GLOB LIBRARY_FILES_C "*.cpp" "*.rc")
else()
FILE(GLOB LIBRARY_FILES_C "*.cpp")
endif()
FILE(GLOB LIBRARY_FILES_H "*.h" "*.inl")
cxx_executable_with_flags(ReconstructMesh "Apps" "${cxx_default}" "MVS;${OpenMVS_EXTRA_LIBS}" ${LIBRARY_FILES_C} ${LIBRARY_FILES_H})
# Install
INSTALL(TARGETS ReconstructMesh
EXPORT OpenMVSTargets
RUNTIME DESTINATION "${INSTALL_BIN_DIR}" COMPONENT bin)

498
apps/ReconstructMesh/ReconstructMesh.cpp

@ -0,0 +1,498 @@ @@ -0,0 +1,498 @@
/*
* ReconstructMesh.cpp
*
* Copyright (c) 2014-2015 SEACAVE
*
* Author(s):
*
* cDc <cdc.seacave@gmail.com>
*
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*
* Additional Terms:
*
* You are required to preserve legal notices and author attributions in
* that material or in the Appropriate Legal Notices displayed by works
* containing it.
*/
#include "../../libs/MVS/Common.h"
#include "../../libs/MVS/Scene.h"
#include <boost/program_options.hpp>
using namespace MVS;
// D E F I N E S ///////////////////////////////////////////////////
#define APPNAME _T("ReconstructMesh")
// uncomment to enable multi-threading based on OpenMP
#ifdef _USE_OPENMP
#define RECMESH_USE_OPENMP
#endif
// S T R U C T S ///////////////////////////////////////////////////
namespace {
namespace OPT {
String strInputFileName;
String strPointCloudFileName;
String strOutputFileName;
String strMeshFileName;
String strImportROIFileName;
String strImagePointsFileName;
bool bMeshExport;
float fDistInsert;
bool bUseOnlyROI;
bool bUseConstantWeight;
bool bUseFreeSpaceSupport;
float fThicknessFactor;
float fQualityFactor;
float fDecimateMesh;
unsigned nTargetFaceNum;
float fRemoveSpurious;
bool bRemoveSpikes;
unsigned nCloseHoles;
unsigned nSmoothMesh;
float fEdgeLength;
bool bCrop2ROI;
float fBorderROI;
float fSplitMaxArea;
unsigned nArchiveType;
int nProcessPriority;
unsigned nMaxThreads;
String strExportType;
String strConfigFileName;
boost::program_options::variables_map vm;
} // namespace OPT
class Application {
public:
Application() {}
~Application() { Finalize(); }
bool Initialize(size_t argc, LPCTSTR* argv);
void Finalize();
}; // Application
// initialize and parse the command line parameters
bool Application::Initialize(size_t argc, LPCTSTR* argv)
{
// initialize log and console
OPEN_LOG();
OPEN_LOGCONSOLE();
// group of options allowed only on command line
boost::program_options::options_description generic("Generic options");
generic.add_options()
("help,h", "produce this help message")
("working-folder,w", boost::program_options::value<std::string>(&WORKING_FOLDER), "working directory (default current directory)")
("config-file,c", boost::program_options::value<std::string>(&OPT::strConfigFileName)->default_value(APPNAME _T(".cfg")), "file name containing program options")
("export-type", boost::program_options::value<std::string>(&OPT::strExportType)->default_value(_T("ply")), "file type used to export the 3D scene (ply or obj)")
("archive-type", boost::program_options::value(&OPT::nArchiveType)->default_value(ARCHIVE_MVS), "project archive type: -1-interface, 0-text, 1-binary, 2-compressed binary")
("process-priority", boost::program_options::value(&OPT::nProcessPriority)->default_value(-1), "process priority (below normal by default)")
("max-threads", boost::program_options::value(&OPT::nMaxThreads)->default_value(0), "maximum number of threads (0 for using all available cores)")
#if TD_VERBOSE != TD_VERBOSE_OFF
("verbosity,v", boost::program_options::value(&g_nVerbosityLevel)->default_value(
#if TD_VERBOSE == TD_VERBOSE_DEBUG
3
#else
2
#endif
), "verbosity level")
#endif
#ifdef _USE_CUDA
("cuda-device", boost::program_options::value(&CUDA::desiredDeviceID)->default_value(-1), "CUDA device number to be used to reconstruct the mesh (-2 - CPU processing, -1 - best GPU, >=0 - device index)")
#endif
;
// group of options allowed both on command line and in config file
boost::program_options::options_description config_main("Reconstruct options");
config_main.add_options()
("input-file,i", boost::program_options::value<std::string>(&OPT::strInputFileName), "input filename containing camera poses and image list")
("pointcloud-file,p", boost::program_options::value<std::string>(&OPT::strPointCloudFileName), "dense point-cloud with views file name to reconstruct (overwrite existing point-cloud)")
("output-file,o", boost::program_options::value<std::string>(&OPT::strOutputFileName), "output filename for storing the mesh")
("min-point-distance,d", boost::program_options::value(&OPT::fDistInsert)->default_value(2.5f), "minimum distance in pixels between the projection of two 3D points to consider them different while triangulating (0 - disabled)")
("integrate-only-roi", boost::program_options::value(&OPT::bUseOnlyROI)->default_value(false), "use only the points inside the ROI")
("constant-weight", boost::program_options::value(&OPT::bUseConstantWeight)->default_value(true), "considers all view weights 1 instead of the available weight")
("free-space-support,f", boost::program_options::value(&OPT::bUseFreeSpaceSupport)->default_value(false), "exploits the free-space support in order to reconstruct weakly-represented surfaces")
("thickness-factor", boost::program_options::value(&OPT::fThicknessFactor)->default_value(1.f), "multiplier adjusting the minimum thickness considered during visibility weighting")
("quality-factor", boost::program_options::value(&OPT::fQualityFactor)->default_value(1.f), "multiplier adjusting the quality weight considered during graph-cut")
;
boost::program_options::options_description config_clean("Clean options");
config_clean.add_options()
("decimate", boost::program_options::value(&OPT::fDecimateMesh)->default_value(1.f), "decimation factor in range (0..1] to be applied to the reconstructed surface (1 - disabled)")
("target-face-num", boost::program_options::value(&OPT::nTargetFaceNum)->default_value(0), "target number of faces to be applied to the reconstructed surface. (0 - disabled)")
("remove-spurious", boost::program_options::value(&OPT::fRemoveSpurious)->default_value(20.f), "spurious factor for removing faces with too long edges or isolated components (0 - disabled)")
("remove-spikes", boost::program_options::value(&OPT::bRemoveSpikes)->default_value(true), "flag controlling the removal of spike faces")
("close-holes", boost::program_options::value(&OPT::nCloseHoles)->default_value(30), "try to close small holes in the reconstructed surface (0 - disabled)")
("smooth", boost::program_options::value(&OPT::nSmoothMesh)->default_value(2), "number of iterations to smooth the reconstructed surface (0 - disabled)")
("edge-length", boost::program_options::value(&OPT::fEdgeLength)->default_value(0.f), "remesh such that the average edge length is this size (0 - disabled)")
("roi-border", boost::program_options::value(&OPT::fBorderROI)->default_value(0), "add a border to the region-of-interest when cropping the scene (0 - disabled, >0 - percentage, <0 - absolute)")
("crop-to-roi", boost::program_options::value(&OPT::bCrop2ROI)->default_value(true), "crop scene using the region-of-interest")
;
// hidden options, allowed both on command line and
// in config file, but will not be shown to the user
boost::program_options::options_description hidden("Hidden options");
hidden.add_options()
("mesh-file", boost::program_options::value<std::string>(&OPT::strMeshFileName), "mesh file name to clean (skips the reconstruction step)")
("mesh-export", boost::program_options::value(&OPT::bMeshExport)->default_value(false), "just export the mesh contained in loaded project")
("split-max-area", boost::program_options::value(&OPT::fSplitMaxArea)->default_value(0.f), "maximum surface area that a sub-mesh can contain (0 - disabled)")
("import-roi-file", boost::program_options::value<std::string>(&OPT::strImportROIFileName), "ROI file name to be imported into the scene")
("image-points-file", boost::program_options::value<std::string>(&OPT::strImagePointsFileName), "input filename containing the list of points from an image to project on the mesh (optional)")
;
boost::program_options::options_description cmdline_options;
cmdline_options.add(generic).add(config_main).add(config_clean).add(hidden);
boost::program_options::options_description config_file_options;
config_file_options.add(config_main).add(config_clean).add(hidden);
boost::program_options::positional_options_description p;
p.add("input-file", -1);
try {
// parse command line options
boost::program_options::store(boost::program_options::command_line_parser((int)argc, argv).options(cmdline_options).positional(p).run(), OPT::vm);
boost::program_options::notify(OPT::vm);
INIT_WORKING_FOLDER;
// parse configuration file
std::ifstream ifs(MAKE_PATH_SAFE(OPT::strConfigFileName));
if (ifs) {
boost::program_options::store(parse_config_file(ifs, config_file_options), OPT::vm);
boost::program_options::notify(OPT::vm);
}
}
catch (const std::exception& e) {
LOG(e.what());
return false;
}
// initialize the log file
OPEN_LOGFILE(MAKE_PATH(APPNAME _T("-")+Util::getUniqueName(0)+_T(".log")).c_str());
// print application details: version and command line
Util::LogBuild();
LOG(_T("Command line: ") APPNAME _T("%s"), Util::CommandLineToString(argc, argv).c_str());
// validate input
Util::ensureValidPath(OPT::strInputFileName);
if (OPT::vm.count("help") || OPT::strInputFileName.empty()) {
boost::program_options::options_description visible("Available options");
visible.add(generic).add(config_main).add(config_clean);
GET_LOG() << visible;
}
if (OPT::strInputFileName.empty())
return false;
OPT::strExportType = OPT::strExportType.ToLower() == _T("obj") ? _T(".obj") : _T(".ply");
// initialize optional options
Util::ensureValidPath(OPT::strPointCloudFileName);
Util::ensureValidPath(OPT::strOutputFileName);
Util::ensureValidPath(OPT::strImportROIFileName);
Util::ensureValidPath(OPT::strImagePointsFileName);
Util::ensureValidPath(OPT::strMeshFileName);
if (OPT::strPointCloudFileName.empty() && (ARCHIVE_TYPE)OPT::nArchiveType == ARCHIVE_MVS)
OPT::strPointCloudFileName = Util::getFileFullName(OPT::strInputFileName) + _T(".ply");
if (OPT::strOutputFileName.empty())
OPT::strOutputFileName = Util::getFileFullName(OPT::strInputFileName) + _T("_mesh.mvs");
MVS::Initialize(APPNAME, OPT::nMaxThreads, OPT::nProcessPriority);
return true;
}
// finalize application instance
void Application::Finalize()
{
MVS::Finalize();
CLOSE_LOGFILE();
CLOSE_LOGCONSOLE();
CLOSE_LOG();
}
} // unnamed namespace
// export 3D coordinates corresponding to 2D coordinates provided by inputFileName:
// parse image point list; first line is the name of the image to project,
// each consequent line store the xy coordinates to project:
// <image-name> <number-of-points>
// <x-coord1> <y-coord1>
// <x-coord2> <y-coord2>
// ...
//
// for example:
// N01.JPG 3
// 3090 2680
// 3600 2100
// 3640 2190
bool Export3DProjections(Scene& scene, const String& inputFileName) {
SML smlPointList(_T("ImagePoints"));
smlPointList.Load(inputFileName);
ASSERT(smlPointList.GetArrChildren().size() <= 1);
IDX idx(0);
// read image name
size_t argc;
CAutoPtrArr<LPSTR> argv;
while (true) {
argv = Util::CommandLineToArgvA(smlPointList.GetValue(idx).val, argc);
if (argc > 0 && argv[0][0] != _T('#'))
break;
if (++idx == smlPointList.size())
return false;
}
if (argc < 2)
return false;
String imgName(argv[0]);
IIndex imgID(NO_ID);
for (const Image& imageData : scene.images) {
if (!imageData.IsValid())
continue;
if (imageData.name.substr(imageData.name.size() - imgName.size()) == imgName) {
imgID = imageData.ID;
break;
}
}
if (imgID == NO_ID) {
VERBOSE("Unable to find image named: %s", imgName.c_str());
return false;
}
// read image points
std::vector<Point2f> imagePoints;
while (++idx != smlPointList.size()) {
// parse image element
const String& line(smlPointList.GetValue(idx).val);
argv = Util::CommandLineToArgvA(line, argc);
if (argc > 0 && argv[0][0] == _T('#'))
continue;
if (argc < 2) {
VERBOSE("Invalid image coordinates: %s", line.c_str());
continue;
}
const Point2f pt(
String::FromString<float>(argv[0], -1),
String::FromString<float>(argv[1], -1));
if (pt.x > 0 && pt.y > 0)
imagePoints.emplace_back(pt);
}
if (imagePoints.empty()) {
VERBOSE("Unable to read image points from: %s", imgName.c_str());
return false;
}
// prepare output file
String outFileName(Util::insertBeforeFileExt(inputFileName, "_3D"));
File oStream(outFileName, File::WRITE, File::CREATE | File::TRUNCATE);
if (!oStream.isOpen()) {
VERBOSE("Unable to open output file: %s", outFileName.c_str());
return false;
}
// print image name
oStream.print("%s %u\n", imgName.c_str(), imagePoints.size());
// init mesh octree
const Mesh::Octree octree(scene.mesh.vertices, [](Mesh::Octree::IDX_TYPE size, Mesh::Octree::Type /*radius*/) {
return size > 256;
});
scene.mesh.ListIncidenteFaces();
// save 3D coord in the output file
const Image& imgToExport = scene.images[imgID];
for (const Point2f& pt : imagePoints) {
// define ray from camera center to each x,y image coord
const Ray3 ray(imgToExport.camera.C, normalized(imgToExport.camera.RayPoint<REAL>(pt)));
// find ray intersection with the mesh
const IntersectRayMesh intRay(octree, ray, scene.mesh);
if (intRay.pick.IsValid()) {
const Point3d ptHit(ray.GetPoint(intRay.pick.dist));
oStream.print("%.7f %.7f %.7f\n", ptHit.x, ptHit.y, ptHit.z);
} else
oStream.print("NA\n");
}
return true;
}
int main(int argc, LPCTSTR* argv)
{
#ifdef _DEBUGINFO
// set _crtBreakAlloc index to stop in <dbgheap.c> at allocation
_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);// | _CRTDBG_CHECK_ALWAYS_DF);
#endif
Application application;
if (!application.Initialize(argc, argv))
return EXIT_FAILURE;
Scene scene(OPT::nMaxThreads);
// load project
const Scene::SCENE_TYPE sceneType(scene.Load(MAKE_PATH_SAFE(OPT::strInputFileName),
OPT::fSplitMaxArea > 0 || OPT::fDecimateMesh < 1 || OPT::nTargetFaceNum > 0 || !OPT::strImportROIFileName.empty()));
if (sceneType == Scene::SCENE_NA)
return EXIT_FAILURE;
if (!OPT::strPointCloudFileName.empty() && (File::isFile(MAKE_PATH_SAFE(OPT::strPointCloudFileName)) ?
!scene.pointcloud.Load(MAKE_PATH_SAFE(OPT::strPointCloudFileName)) :
!scene.pointcloud.IsValid())) {
VERBOSE("error: cannot load point-cloud file");
return EXIT_FAILURE;
}
if (!OPT::strMeshFileName.empty() && !scene.mesh.Load(MAKE_PATH_SAFE(OPT::strMeshFileName))) {
VERBOSE("error: cannot load mesh file");
return EXIT_FAILURE;
}
const String baseFileName(MAKE_PATH_SAFE(Util::getFileFullName(OPT::strOutputFileName)));
if (OPT::fSplitMaxArea > 0) {
// split mesh using max-area constraint
Mesh::FacesChunkArr chunks;
if (scene.mesh.Split(chunks, OPT::fSplitMaxArea))
scene.mesh.Save(chunks, baseFileName);
return EXIT_SUCCESS;
}
if (!OPT::strImportROIFileName.empty()) {
std::ifstream fs(MAKE_PATH_SAFE(OPT::strImportROIFileName));
if (!fs)
return EXIT_FAILURE;
fs >> scene.obb;
if (OPT::bCrop2ROI && !scene.mesh.IsEmpty() && !scene.IsValid()) {
TD_TIMER_START();
const size_t numVertices = scene.mesh.vertices.size();
const size_t numFaces = scene.mesh.faces.size();
scene.mesh.RemoveFacesOutside(scene.obb);
VERBOSE("Mesh trimmed to ROI: %u vertices and %u faces removed (%s)",
numVertices-scene.mesh.vertices.size(), numFaces-scene.mesh.faces.size(), TD_TIMER_GET_FMT().c_str());
scene.mesh.Save(baseFileName+OPT::strExportType);
return EXIT_SUCCESS;
}
}
if (!OPT::strImagePointsFileName.empty() && !scene.mesh.IsEmpty()) {
Export3DProjections(scene, MAKE_PATH_SAFE(OPT::strImagePointsFileName));
return EXIT_SUCCESS;
}
if (OPT::bMeshExport) {
// check there is a mesh to export
if (scene.mesh.IsEmpty())
return EXIT_FAILURE;
// save mesh
const String fileName(MAKE_PATH_SAFE(OPT::strOutputFileName));
scene.mesh.Save(fileName);
#if TD_VERBOSE != TD_VERBOSE_OFF
if (VERBOSITY_LEVEL > 2)
scene.ExportCamerasMLP(baseFileName+_T(".mlp"), fileName);
#endif
} else {
const OBB3f initialOBB(scene.obb);
if (OPT::fBorderROI > 0)
scene.obb.EnlargePercent(OPT::fBorderROI);
else if (OPT::fBorderROI < 0)
scene.obb.Enlarge(-OPT::fBorderROI);
if (OPT::strMeshFileName.empty() && scene.mesh.IsEmpty()) {
// reset image resolution to the original size and
// make sure the image neighbors are initialized before deleting the point-cloud
#ifdef RECMESH_USE_OPENMP
bool bAbort(false);
#pragma omp parallel for
for (int_t idx=0; idx<(int_t)scene.images.size(); ++idx) {
#pragma omp flush (bAbort)
if (bAbort)
continue;
const uint32_t idxImage((uint32_t)idx);
#else
FOREACH(idxImage, scene.images) {
#endif
Image& imageData = scene.images[idxImage];
if (!imageData.IsValid())
continue;
// reset image resolution
if (!imageData.ReloadImage(0, false)) {
#ifdef RECMESH_USE_OPENMP
bAbort = true;
#pragma omp flush (bAbort)
continue;
#else
return EXIT_FAILURE;
#endif
}
imageData.UpdateCamera(scene.platforms);
// select neighbor views
if (imageData.neighbors.empty()) {
IndexArr points;
scene.SelectNeighborViews(idxImage, points);
}
}
#ifdef RECMESH_USE_OPENMP
if (bAbort)
return EXIT_FAILURE;
#endif
// reconstruct a coarse mesh from the given point-cloud
TD_TIMER_START();
if (OPT::bUseConstantWeight)
scene.pointcloud.pointWeights.Release();
if (!scene.ReconstructMesh(OPT::fDistInsert, OPT::bUseFreeSpaceSupport, OPT::bUseOnlyROI, 4, OPT::fThicknessFactor, OPT::fQualityFactor))
return EXIT_FAILURE;
VERBOSE("Mesh reconstruction completed: %u vertices, %u faces (%s)", scene.mesh.vertices.GetSize(), scene.mesh.faces.GetSize(), TD_TIMER_GET_FMT().c_str());
#if TD_VERBOSE != TD_VERBOSE_OFF
if (VERBOSITY_LEVEL > 2) {
// dump raw mesh
scene.mesh.Save(baseFileName+_T("_raw")+OPT::strExportType);
}
#endif
} else if (!OPT::strMeshFileName.empty()) {
// load existing mesh to clean
scene.mesh.Load(MAKE_PATH_SAFE(OPT::strMeshFileName));
}
// clean the mesh
if (OPT::bCrop2ROI && scene.IsBounded()) {
TD_TIMER_START();
const size_t numVertices = scene.mesh.vertices.size();
const size_t numFaces = scene.mesh.faces.size();
scene.mesh.RemoveFacesOutside(scene.obb);
VERBOSE("Mesh trimmed to ROI: %u vertices and %u faces removed (%s)",
numVertices-scene.mesh.vertices.size(), numFaces-scene.mesh.faces.size(), TD_TIMER_GET_FMT().c_str());
}
const float fDecimate(OPT::nTargetFaceNum ? static_cast<float>(OPT::nTargetFaceNum) / scene.mesh.faces.size() : OPT::fDecimateMesh);
scene.mesh.Clean(fDecimate, OPT::fRemoveSpurious, OPT::bRemoveSpikes, OPT::nCloseHoles, OPT::nSmoothMesh, OPT::fEdgeLength, false);
scene.mesh.Clean(1.f, 0.f, OPT::bRemoveSpikes, OPT::nCloseHoles, 0u, 0.f, false); // extra cleaning trying to close more holes
scene.mesh.Clean(1.f, 0.f, false, 0u, 0u, 0.f, true); // extra cleaning to remove non-manifold problems created by closing holes
scene.obb = initialOBB;
// save the final mesh
scene.mesh.Save(baseFileName+OPT::strExportType);
#if TD_VERBOSE != TD_VERBOSE_OFF
if (VERBOSITY_LEVEL > 2)
scene.ExportCamerasMLP(baseFileName+_T(".mlp"), baseFileName+OPT::strExportType);
#endif
if ((ARCHIVE_TYPE)OPT::nArchiveType != ARCHIVE_MVS || sceneType != Scene::SCENE_INTERFACE)
scene.Save(baseFileName+_T(".mvs"), (ARCHIVE_TYPE)OPT::nArchiveType);
}
if (!OPT::strImagePointsFileName.empty()) {
Export3DProjections(scene, MAKE_PATH_SAFE(OPT::strImagePointsFileName));
return EXIT_SUCCESS;
}
return EXIT_SUCCESS;
}
/*----------------------------------------------------------------*/

13
apps/RefineMesh/CMakeLists.txt

@ -0,0 +1,13 @@ @@ -0,0 +1,13 @@
if(MSVC)
FILE(GLOB LIBRARY_FILES_C "*.cpp" "*.rc")
else()
FILE(GLOB LIBRARY_FILES_C "*.cpp")
endif()
FILE(GLOB LIBRARY_FILES_H "*.h" "*.inl")
cxx_executable_with_flags(RefineMesh "Apps" "${cxx_default}" "MVS;${OpenMVS_EXTRA_LIBS}" ${LIBRARY_FILES_C} ${LIBRARY_FILES_H})
# Install
INSTALL(TARGETS RefineMesh
EXPORT OpenMVSTargets
RUNTIME DESTINATION "${INSTALL_BIN_DIR}" COMPONENT bin)

266
apps/RefineMesh/RefineMesh.cpp

@ -0,0 +1,266 @@ @@ -0,0 +1,266 @@
/*
* RefineMesh.cpp
*
* Copyright (c) 2014-2015 SEACAVE
*
* Author(s):
*
* cDc <cdc.seacave@gmail.com>
*
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*
* Additional Terms:
*
* You are required to preserve legal notices and author attributions in
* that material or in the Appropriate Legal Notices displayed by works
* containing it.
*/
#include "../../libs/MVS/Common.h"
#include "../../libs/MVS/Scene.h"
#include <boost/program_options.hpp>
using namespace MVS;
// D E F I N E S ///////////////////////////////////////////////////
#define APPNAME _T("RefineMesh")
// S T R U C T S ///////////////////////////////////////////////////
namespace {
namespace OPT {
String strInputFileName;
String strMeshFileName;
String strOutputFileName;
unsigned nResolutionLevel;
unsigned nMinResolution;
unsigned nMaxViews;
float fDecimateMesh;
unsigned nCloseHoles;
unsigned nEnsureEdgeSize;
unsigned nScales;
float fScaleStep;
unsigned nReduceMemory;
unsigned nAlternatePair;
float fRegularityWeight;
float fRatioRigidityElasticity;
unsigned nMaxFaceArea;
float fPlanarVertexRatio;
float fGradientStep;
unsigned nArchiveType;
int nProcessPriority;
unsigned nMaxThreads;
String strExportType;
String strConfigFileName;
boost::program_options::variables_map vm;
} // namespace OPT
class Application {
public:
Application() {}
~Application() { Finalize(); }
bool Initialize(size_t argc, LPCTSTR* argv);
void Finalize();
}; // Application
// initialize and parse the command line parameters
bool Application::Initialize(size_t argc, LPCTSTR* argv)
{
// initialize log and console
OPEN_LOG();
OPEN_LOGCONSOLE();
// group of options allowed only on command line
boost::program_options::options_description generic("Generic options");
generic.add_options()
("help,h", "produce this help message")
("working-folder,w", boost::program_options::value<std::string>(&WORKING_FOLDER), "working directory (default current directory)")
("config-file,c", boost::program_options::value<std::string>(&OPT::strConfigFileName)->default_value(APPNAME _T(".cfg")), "file name containing program options")
("export-type", boost::program_options::value<std::string>(&OPT::strExportType)->default_value(_T("ply")), "file type used to export the 3D scene (ply or obj)")
("archive-type", boost::program_options::value(&OPT::nArchiveType)->default_value(ARCHIVE_MVS), "project archive type: -1-interface, 0-text, 1-binary, 2-compressed binary")
("process-priority", boost::program_options::value(&OPT::nProcessPriority)->default_value(-1), "process priority (below normal by default)")
("max-threads", boost::program_options::value(&OPT::nMaxThreads)->default_value(0), "maximum number of threads (0 for using all available cores)")
#if TD_VERBOSE != TD_VERBOSE_OFF
("verbosity,v", boost::program_options::value(&g_nVerbosityLevel)->default_value(
#if TD_VERBOSE == TD_VERBOSE_DEBUG
3
#else
2
#endif
), "verbosity level")
#endif
#ifdef _USE_CUDA
("cuda-device", boost::program_options::value(&CUDA::desiredDeviceID)->default_value(-2), "CUDA device number to be used for mesh refinement (-2 - CPU processing, -1 - best GPU, >=0 - device index)")
#endif
;
// group of options allowed both on command line and in config file
boost::program_options::options_description config("Refine options");
config.add_options()
("input-file,i", boost::program_options::value<std::string>(&OPT::strInputFileName), "input filename containing camera poses and image list")
("mesh-file,m", boost::program_options::value<std::string>(&OPT::strMeshFileName), "mesh file name to refine (overwrite existing mesh)")
("output-file,o", boost::program_options::value<std::string>(&OPT::strOutputFileName), "output filename for storing the mesh")
("resolution-level", boost::program_options::value(&OPT::nResolutionLevel)->default_value(0), "how many times to scale down the images before mesh refinement")
("min-resolution", boost::program_options::value(&OPT::nMinResolution)->default_value(640), "do not scale images lower than this resolution")
("max-views", boost::program_options::value(&OPT::nMaxViews)->default_value(8), "maximum number of neighbor images used to refine the mesh")
("decimate", boost::program_options::value(&OPT::fDecimateMesh)->default_value(0.f), "decimation factor in range [0..1] to be applied to the input surface before refinement (0 - auto, 1 - disabled)")
("close-holes", boost::program_options::value(&OPT::nCloseHoles)->default_value(30), "try to close small holes in the input surface (0 - disabled)")
("ensure-edge-size", boost::program_options::value(&OPT::nEnsureEdgeSize)->default_value(1), "ensure edge size and improve vertex valence of the input surface (0 - disabled, 1 - auto, 2 - force)")
("max-face-area", boost::program_options::value(&OPT::nMaxFaceArea)->default_value(32), "maximum face area projected in any pair of images that is not subdivided (0 - disabled)")
("scales", boost::program_options::value(&OPT::nScales)->default_value(2), "how many iterations to run mesh optimization on multi-scale images")
("scale-step", boost::program_options::value(&OPT::fScaleStep)->default_value(0.5f), "image scale factor used at each mesh optimization step")
("alternate-pair", boost::program_options::value(&OPT::nAlternatePair)->default_value(0), "refine mesh using an image pair alternatively as reference (0 - both, 1 - alternate, 2 - only left, 3 - only right)")
("regularity-weight", boost::program_options::value(&OPT::fRegularityWeight)->default_value(0.2f), "scalar regularity weight to balance between photo-consistency and regularization terms during mesh optimization")
("rigidity-elasticity-ratio", boost::program_options::value(&OPT::fRatioRigidityElasticity)->default_value(0.9f), "scalar ratio used to compute the regularity gradient as a combination of rigidity and elasticity")
("gradient-step", boost::program_options::value(&OPT::fGradientStep)->default_value(45.05f), "gradient step to be used instead (0 - auto)")
("planar-vertex-ratio", boost::program_options::value(&OPT::fPlanarVertexRatio)->default_value(0.f), "threshold used to remove vertices on planar patches (0 - disabled)")
("reduce-memory", boost::program_options::value(&OPT::nReduceMemory)->default_value(1), "recompute some data in order to reduce memory requirements")
;
boost::program_options::options_description cmdline_options;
cmdline_options.add(generic).add(config);
boost::program_options::options_description config_file_options;
config_file_options.add(config);
boost::program_options::positional_options_description p;
p.add("input-file", -1);
try {
// parse command line options
boost::program_options::store(boost::program_options::command_line_parser((int)argc, argv).options(cmdline_options).positional(p).run(), OPT::vm);
boost::program_options::notify(OPT::vm);
INIT_WORKING_FOLDER;
// parse configuration file
std::ifstream ifs(MAKE_PATH_SAFE(OPT::strConfigFileName));
if (ifs) {
boost::program_options::store(parse_config_file(ifs, config_file_options), OPT::vm);
boost::program_options::notify(OPT::vm);
}
}
catch (const std::exception& e) {
LOG(e.what());
return false;
}
// initialize the log file
OPEN_LOGFILE(MAKE_PATH(APPNAME _T("-")+Util::getUniqueName(0)+_T(".log")).c_str());
// print application details: version and command line
Util::LogBuild();
LOG(_T("Command line: ") APPNAME _T("%s"), Util::CommandLineToString(argc, argv).c_str());
// validate input
Util::ensureValidPath(OPT::strInputFileName);
if (OPT::vm.count("help") || OPT::strInputFileName.empty()) {
boost::program_options::options_description visible("Available options");
visible.add(generic).add(config);
GET_LOG() << visible;
}
if (OPT::strInputFileName.empty())
return false;
OPT::strExportType = OPT::strExportType.ToLower() == _T("obj") ? _T(".obj") : _T(".ply");
// initialize optional options
Util::ensureValidPath(OPT::strMeshFileName);
Util::ensureValidPath(OPT::strOutputFileName);
if (OPT::strMeshFileName.empty() && (ARCHIVE_TYPE)OPT::nArchiveType == ARCHIVE_MVS)
OPT::strMeshFileName = Util::getFileFullName(OPT::strInputFileName) + _T(".ply");
if (OPT::strOutputFileName.empty())
OPT::strOutputFileName = Util::getFileFullName(OPT::strInputFileName) + _T("_refine.mvs");
MVS::Initialize(APPNAME, OPT::nMaxThreads, OPT::nProcessPriority);
return true;
}
// finalize application instance
void Application::Finalize()
{
MVS::Finalize();
CLOSE_LOGFILE();
CLOSE_LOGCONSOLE();
CLOSE_LOG();
}
} // unnamed namespace
int main(int argc, LPCTSTR* argv)
{
#ifdef _DEBUGINFO
// set _crtBreakAlloc index to stop in <dbgheap.c> at allocation
_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);// | _CRTDBG_CHECK_ALWAYS_DF);
#endif
Application application;
if (!application.Initialize(argc, argv))
return EXIT_FAILURE;
Scene scene(OPT::nMaxThreads);
// load and refine the coarse mesh
const Scene::SCENE_TYPE sceneType(scene.Load(MAKE_PATH_SAFE(OPT::strInputFileName)));
if (sceneType == Scene::SCENE_NA)
return EXIT_FAILURE;
if (!OPT::strMeshFileName.empty() && !scene.mesh.Load(MAKE_PATH_SAFE(OPT::strMeshFileName))) {
VERBOSE("error: cannot load mesh file");
return EXIT_FAILURE;
}
if (scene.mesh.IsEmpty()) {
VERBOSE("error: empty initial mesh");
return EXIT_FAILURE;
}
TD_TIMER_START();
#ifdef _USE_CUDA
if (CUDA::desiredDeviceID < -1 ||
!scene.RefineMeshCUDA(OPT::nResolutionLevel, OPT::nMinResolution, OPT::nMaxViews,
OPT::fDecimateMesh, OPT::nCloseHoles, OPT::nEnsureEdgeSize,
OPT::nMaxFaceArea,
OPT::nScales, OPT::fScaleStep,
OPT::nAlternatePair>10 ? OPT::nAlternatePair%10 : 0,
OPT::fRegularityWeight,
OPT::fRatioRigidityElasticity,
OPT::fGradientStep))
#endif
if (!scene.RefineMesh(OPT::nResolutionLevel, OPT::nMinResolution, OPT::nMaxViews,
OPT::fDecimateMesh, OPT::nCloseHoles, OPT::nEnsureEdgeSize,
OPT::nMaxFaceArea,
OPT::nScales, OPT::fScaleStep,
OPT::nAlternatePair,
OPT::fRegularityWeight,
OPT::fRatioRigidityElasticity,
OPT::fGradientStep,
OPT::fPlanarVertexRatio,
OPT::nReduceMemory))
return EXIT_FAILURE;
VERBOSE("Mesh refinement completed: %u vertices, %u faces (%s)", scene.mesh.vertices.GetSize(), scene.mesh.faces.GetSize(), TD_TIMER_GET_FMT().c_str());
// save the final mesh
const String baseFileName(MAKE_PATH_SAFE(Util::getFileFullName(OPT::strOutputFileName)));
scene.mesh.Save(baseFileName+OPT::strExportType);
#if TD_VERBOSE != TD_VERBOSE_OFF
if (VERBOSITY_LEVEL > 2)
scene.ExportCamerasMLP(baseFileName+_T(".mlp"), baseFileName+OPT::strExportType);
#endif
if ((ARCHIVE_TYPE)OPT::nArchiveType != ARCHIVE_MVS || sceneType != Scene::SCENE_INTERFACE)
scene.Save(baseFileName+_T(".mvs"), (ARCHIVE_TYPE)OPT::nArchiveType);
return EXIT_SUCCESS;
}
/*----------------------------------------------------------------*/

15
apps/Tests/CMakeLists.txt

@ -0,0 +1,15 @@ @@ -0,0 +1,15 @@
if(MSVC)
FILE(GLOB LIBRARY_FILES_C "*.cpp" "*.rc")
else()
FILE(GLOB LIBRARY_FILES_C "*.cpp")
endif()
FILE(GLOB LIBRARY_FILES_H "*.h" "*.inl")
ADD_DEFINITIONS(-D_DATA_PATH="${CMAKE_CURRENT_SOURCE_DIR}/data/")
cxx_executable_with_flags(Tests "Apps" "${cxx_default}" "MVS;${OpenMVS_EXTRA_LIBS}" ${LIBRARY_FILES_C} ${LIBRARY_FILES_H})
# Install
INSTALL(TARGETS Tests
EXPORT OpenMVSTargets
RUNTIME DESTINATION "${INSTALL_BIN_DIR}" COMPONENT bin)

136
apps/Tests/Tests.cpp

@ -0,0 +1,136 @@ @@ -0,0 +1,136 @@
/*
* Tests.cpp
*
* Copyright (c) 2014-2021 SEACAVE
*
* Author(s):
*
* cDc <cdc.seacave@gmail.com>
*
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*
* Additional Terms:
*
* You are required to preserve legal notices and author attributions in
* that material or in the Appropriate Legal Notices displayed by works
* containing it.
*/
#include "../../libs/MVS/Common.h"
#include "../../libs/MVS/Scene.h"
using namespace MVS;
// D E F I N E S ///////////////////////////////////////////////////
#define APPNAME _T("Tests")
// S T R U C T S ///////////////////////////////////////////////////
// test various algorithms independently
bool UnitTests()
{
TD_TIMER_START();
if (!SEACAVE::cListTest<true>(100)) {
VERBOSE("ERROR: cListTest failed!");
return false;
}
if (!SEACAVE::OctreeTest<double,2>(100)) {
VERBOSE("ERROR: OctreeTest<double,2> failed!");
return false;
}
if (!SEACAVE::OctreeTest<float,3>(100)) {
VERBOSE("ERROR: OctreeTest<float,3> failed!");
return false;
}
if (!SEACAVE::TestRayTriangleIntersection<float>(1000)) {
VERBOSE("ERROR: TestRayTriangleIntersection<float> failed!");
return false;
}
if (!SEACAVE::TestRayTriangleIntersection<double>(1000)) {
VERBOSE("ERROR: TestRayTriangleIntersection<double> failed!");
return false;
}
VERBOSE("All unit tests passed (%s)", TD_TIMER_GET_FMT().c_str());
return true;
}
// test MVS stages on a small sample dataset
bool PipelineTest(bool verbose=false)
{
TD_TIMER_START();
Scene scene;
if (!scene.Load(MAKE_PATH("scene.mvs"))) {
VERBOSE("ERROR: TestDataset failed loading the scene!");
return false;
}
OPTDENSE::init();
OPTDENSE::bRemoveDmaps = true;
if (!scene.DenseReconstruction() || scene.pointcloud.GetSize() < 200000u) {
VERBOSE("ERROR: TestDataset failed estimating dense point cloud!");
return false;
}
if (verbose)
scene.pointcloud.Save(MAKE_PATH("scene_dense.ply"));
if (!scene.ReconstructMesh() || scene.mesh.faces.size() < 75000u) {
VERBOSE("ERROR: TestDataset failed reconstructing the mesh!");
return false;
}
if (verbose)
scene.mesh.Save(MAKE_PATH("scene_dense_mesh.ply"));
constexpr float decimate = 0.5f;
scene.mesh.Clean(decimate);
if (!ISINSIDE(scene.mesh.faces.size(), 35000u, 45000u)) {
VERBOSE("ERROR: TestDataset failed cleaning the mesh!");
return false;
}
#ifdef _USE_OPENMP
TestMeshProjectionMT(scene.mesh, scene.images[1]);
#endif
if (!scene.TextureMesh(0, 0) || !scene.mesh.HasTexture()) {
VERBOSE("ERROR: TestDataset failed texturing the mesh!");
return false;
}
if (verbose)
scene.mesh.Save(MAKE_PATH("scene_dense_mesh_texture.ply"));
VERBOSE("All pipeline stages passed (%s)", TD_TIMER_GET_FMT().c_str());
return true;
}
// test OpenMVS functionality
int main(int argc, LPCTSTR* argv)
{
OPEN_LOG();
OPEN_LOGCONSOLE();
MVS::Initialize(APPNAME);
WORKING_FOLDER = _DATA_PATH;
INIT_WORKING_FOLDER;
if (argc < 2 || std::atoi(argv[1]) == 0) {
if (!UnitTests())
return EXIT_FAILURE;
} else {
if (!PipelineTest())
return EXIT_FAILURE;
}
MVS::Finalize();
CLOSE_LOGCONSOLE();
CLOSE_LOG();
return EXIT_SUCCESS;
}
/*----------------------------------------------------------------*/

BIN
apps/Tests/data/images/00000.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 280 KiB

BIN
apps/Tests/data/images/00001.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 296 KiB

BIN
apps/Tests/data/images/00002.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 289 KiB

BIN
apps/Tests/data/images/00003.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 290 KiB

BIN
apps/Tests/data/scene.mvs

Binary file not shown.

13
apps/TextureMesh/CMakeLists.txt

@ -0,0 +1,13 @@ @@ -0,0 +1,13 @@
if(MSVC)
FILE(GLOB LIBRARY_FILES_C "*.cpp" "*.rc")
else()
FILE(GLOB LIBRARY_FILES_C "*.cpp")
endif()
FILE(GLOB LIBRARY_FILES_H "*.h" "*.inl")
cxx_executable_with_flags(TextureMesh "Apps" "${cxx_default}" "MVS;${OpenMVS_EXTRA_LIBS}" ${LIBRARY_FILES_C} ${LIBRARY_FILES_H})
# Install
INSTALL(TARGETS TextureMesh
EXPORT OpenMVSTargets
RUNTIME DESTINATION "${INSTALL_BIN_DIR}" COMPONENT bin)

1168
apps/TextureMesh/TextureMesh.cpp

File diff suppressed because it is too large Load Diff

13
apps/TransformScene/CMakeLists.txt

@ -0,0 +1,13 @@ @@ -0,0 +1,13 @@
if(MSVC)
FILE(GLOB LIBRARY_FILES_C "*.cpp" "*.rc")
else()
FILE(GLOB LIBRARY_FILES_C "*.cpp")
endif()
FILE(GLOB LIBRARY_FILES_H "*.h" "*.inl")
cxx_executable_with_flags(TransformScene "Apps" "${cxx_default}" "MVS;${OpenMVS_EXTRA_LIBS}" ${LIBRARY_FILES_C} ${LIBRARY_FILES_H})
# Install
INSTALL(TARGETS TransformScene
EXPORT OpenMVSTargets
RUNTIME DESTINATION "${INSTALL_BIN_DIR}" COMPONENT bin)

329
apps/TransformScene/TransformScene.cpp

@ -0,0 +1,329 @@ @@ -0,0 +1,329 @@
/*
* TransformScene.cpp
*
* Copyright (c) 2014-2021 SEACAVE
*
* Author(s):
*
* cDc <cdc.seacave@gmail.com>
*
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*
* Additional Terms:
*
* You are required to preserve legal notices and author attributions in
* that material or in the Appropriate Legal Notices displayed by works
* containing it.
*/
#include "../../libs/MVS/Common.h"
#include "../../libs/MVS/Scene.h"
#include <boost/program_options.hpp>
using namespace MVS;
// D E F I N E S ///////////////////////////////////////////////////
#define APPNAME _T("TransformScene")
#define MVS_FILE_EXTENSION _T(".mvs")
// S T R U C T S ///////////////////////////////////////////////////
namespace {
namespace OPT {
String strInputFileName;
String strPointCloudFileName;
String strMeshFileName;
String strOutputFileName;
String strAlignFileName;
String strTransformFileName;
String strTransferTextureFileName;
String strIndicesFileName;
bool bComputeVolume;
float fPlaneThreshold;
float fSampleMesh;
unsigned nMaxResolution;
unsigned nUpAxis;
unsigned nArchiveType;
int nProcessPriority;
unsigned nMaxThreads;
String strExportType;
String strConfigFileName;
boost::program_options::variables_map vm;
} // namespace OPT
class Application {
public:
Application() {}
~Application() { Finalize(); }
bool Initialize(size_t argc, LPCTSTR* argv);
void Finalize();
}; // Application
// initialize and parse the command line parameters
bool Application::Initialize(size_t argc, LPCTSTR* argv)
{
// initialize log and console
OPEN_LOG();
OPEN_LOGCONSOLE();
// group of options allowed only on command line
boost::program_options::options_description generic("Generic options");
generic.add_options()
("help,h", "produce this help message")
("working-folder,w", boost::program_options::value<std::string>(&WORKING_FOLDER), "working directory (default current directory)")
("config-file,c", boost::program_options::value<std::string>(&OPT::strConfigFileName)->default_value(APPNAME _T(".cfg")), "file name containing program options")
("export-type", boost::program_options::value<std::string>(&OPT::strExportType)->default_value(_T("ply")), "file type used to export the 3D scene (ply, obj, glb or gltf)")
("archive-type", boost::program_options::value(&OPT::nArchiveType)->default_value(ARCHIVE_MVS), "project archive type: -1-interface, 0-text, 1-binary, 2-compressed binary")
("process-priority", boost::program_options::value(&OPT::nProcessPriority)->default_value(-1), "process priority (below normal by default)")
("max-threads", boost::program_options::value(&OPT::nMaxThreads)->default_value(0), "maximum number of threads (0 for using all available cores)")
#if TD_VERBOSE != TD_VERBOSE_OFF
("verbosity,v", boost::program_options::value(&g_nVerbosityLevel)->default_value(
#if TD_VERBOSE == TD_VERBOSE_DEBUG
3
#else
2
#endif
), "verbosity level")
#endif
;
// group of options allowed both on command line and in config file
boost::program_options::options_description config("Main options");
config.add_options()
("input-file,i", boost::program_options::value<std::string>(&OPT::strInputFileName), "input scene filename")
("pointcloud-file,p", boost::program_options::value<std::string>(&OPT::strPointCloudFileName), "dense point-cloud with views file name to transform (overwrite existing point-cloud)")
("mesh-file,m", boost::program_options::value<std::string>(&OPT::strMeshFileName), "mesh file name to transform (overwrite existing mesh)")
("output-file,o", boost::program_options::value<std::string>(&OPT::strOutputFileName), "output filename for storing the scene")
("align-file,a", boost::program_options::value<std::string>(&OPT::strAlignFileName), "input scene filename to which the scene will be cameras aligned")
("transform-file,t", boost::program_options::value<std::string>(&OPT::strTransformFileName), "input transform filename by which the scene will transformed")
("transfer-texture-file", boost::program_options::value<std::string>(&OPT::strTransferTextureFileName), "input mesh filename to which the texture of the scene's mesh will be transfered to (the two meshes should be aligned and the new mesh to have UV-map)")
("indices-file", boost::program_options::value<std::string>(&OPT::strIndicesFileName), "input indices filename to be used with ex. texture transfer to select a subset of the scene's mesh")
("compute-volume", boost::program_options::value(&OPT::bComputeVolume)->default_value(false), "compute the volume of the given watertight mesh, or else try to estimate the ground plane and assume the mesh is bounded by it")
("plane-threshold", boost::program_options::value(&OPT::fPlaneThreshold)->default_value(0.f), "threshold used to estimate the ground plane (<0 - disabled, 0 - auto, >0 - desired threshold)")
("sample-mesh", boost::program_options::value(&OPT::fSampleMesh)->default_value(-300000.f), "uniformly samples points on a mesh (0 - disabled, <0 - number of points, >0 - sample density per square unit)")
("max-resolution", boost::program_options::value(&OPT::nMaxResolution)->default_value(0), "make sure image resolution are not not larger than this (0 - disabled)")
("up-axis", boost::program_options::value(&OPT::nUpAxis)->default_value(2), "scene axis considered to point upwards (0 - x, 1 - y, 2 - z)")
;
boost::program_options::options_description cmdline_options;
cmdline_options.add(generic).add(config);
boost::program_options::options_description config_file_options;
config_file_options.add(config);
boost::program_options::positional_options_description p;
p.add("input-file", -1);
try {
// parse command line options
boost::program_options::store(boost::program_options::command_line_parser((int)argc, argv).options(cmdline_options).positional(p).run(), OPT::vm);
boost::program_options::notify(OPT::vm);
INIT_WORKING_FOLDER;
// parse configuration file
std::ifstream ifs(MAKE_PATH_SAFE(OPT::strConfigFileName));
if (ifs) {
boost::program_options::store(parse_config_file(ifs, config_file_options), OPT::vm);
boost::program_options::notify(OPT::vm);
}
}
catch (const std::exception& e) {
LOG(e.what());
return false;
}
// initialize the log file
OPEN_LOGFILE(MAKE_PATH(APPNAME _T("-") + Util::getUniqueName(0) + _T(".log")).c_str());
// print application details: version and command line
Util::LogBuild();
LOG(_T("Command line: ") APPNAME _T("%s"), Util::CommandLineToString(argc, argv).c_str());
// validate input
Util::ensureValidPath(OPT::strInputFileName);
Util::ensureValidPath(OPT::strAlignFileName);
Util::ensureValidPath(OPT::strTransformFileName);
Util::ensureValidPath(OPT::strTransferTextureFileName);
Util::ensureValidPath(OPT::strIndicesFileName);
const String strInputFileNameExt(Util::getFileExt(OPT::strInputFileName).ToLower());
const bool bInvalidCommand(OPT::strInputFileName.empty() ||
(OPT::strAlignFileName.empty() && OPT::strTransformFileName.empty() && OPT::strTransferTextureFileName.empty() && !OPT::bComputeVolume));
if (OPT::vm.count("help") || bInvalidCommand) {
boost::program_options::options_description visible("Available options");
visible.add(generic).add(config);
GET_LOG() << visible;
}
if (bInvalidCommand)
return false;
OPT::strExportType = OPT::strExportType.ToLower();
if (OPT::strExportType == _T("obj"))
OPT::strExportType = _T(".obj");
else
if (OPT::strExportType == _T("glb"))
OPT::strExportType = _T(".glb");
else
if (OPT::strExportType == _T("gltf"))
OPT::strExportType = _T(".gltf");
else
OPT::strExportType = _T(".ply");
// initialize optional options
Util::ensureValidPath(OPT::strPointCloudFileName);
Util::ensureValidPath(OPT::strMeshFileName);
Util::ensureValidPath(OPT::strOutputFileName);
if (OPT::strMeshFileName.empty() && (ARCHIVE_TYPE)OPT::nArchiveType == ARCHIVE_MVS && strInputFileNameExt == MVS_FILE_EXTENSION)
OPT::strMeshFileName = Util::getFileFullName(OPT::strInputFileName) + _T(".ply");
if (OPT::strOutputFileName.empty())
OPT::strOutputFileName = Util::getFileName(OPT::strInputFileName) + _T("_transformed") MVS_FILE_EXTENSION;
MVS::Initialize(APPNAME, OPT::nMaxThreads, OPT::nProcessPriority);
return true;
}
// finalize application instance
void Application::Finalize()
{
MVS::Finalize();
CLOSE_LOGFILE();
CLOSE_LOGCONSOLE();
CLOSE_LOG();
}
} // unnamed namespace
int main(int argc, LPCTSTR* argv)
{
#ifdef _DEBUGINFO
// set _crtBreakAlloc index to stop in <dbgheap.c> at allocation
_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);// | _CRTDBG_CHECK_ALWAYS_DF);
#endif
Application application;
if (!application.Initialize(argc, argv))
return EXIT_FAILURE;
TD_TIMER_START();
Scene scene(OPT::nMaxThreads);
// load given scene
const Scene::SCENE_TYPE sceneType(scene.Load(MAKE_PATH_SAFE(OPT::strInputFileName),
!OPT::strTransformFileName.empty() || !OPT::strTransferTextureFileName.empty() || OPT::bComputeVolume));
if (sceneType == Scene::SCENE_NA)
return EXIT_FAILURE;
if (!OPT::strPointCloudFileName.empty() && !scene.pointcloud.Load(MAKE_PATH_SAFE(OPT::strPointCloudFileName))) {
VERBOSE("error: cannot load point-cloud file");
return EXIT_FAILURE;
}
if (!OPT::strMeshFileName.empty() && !scene.mesh.Load(MAKE_PATH_SAFE(OPT::strMeshFileName))) {
VERBOSE("error: cannot load mesh file");
return EXIT_FAILURE;
}
const String baseFileName(MAKE_PATH_SAFE(Util::getFileFullName(OPT::strOutputFileName)));
if (!OPT::strAlignFileName.empty()) {
// transform this scene such that it best aligns with the given scene based on the camera positions
Scene sceneRef(OPT::nMaxThreads);
if (!sceneRef.Load(MAKE_PATH_SAFE(OPT::strAlignFileName)))
return EXIT_FAILURE;
if (!scene.AlignTo(sceneRef))
return EXIT_FAILURE;
VERBOSE("Scene aligned to the given reference scene (%s)", TD_TIMER_GET_FMT().c_str());
}
if (!OPT::strTransformFileName.empty()) {
// transform this scene by the given transform matrix
std::ifstream file(MAKE_PATH_SAFE(OPT::strTransformFileName));
std::string value;
std::vector<double> transformValues;
while (file >> value) {
double v;
try {
v = std::stod(value);
}
catch (...) {
continue;
}
transformValues.push_back(v);
}
if (transformValues.size() != 12 &&
(transformValues.size() != 16 || transformValues[12] != 0 || transformValues[13] != 0 || transformValues[14] != 0 || transformValues[15] != 1)) {
VERBOSE("error: invalid transform");
return EXIT_FAILURE;
}
Matrix3x4 transform;
for (unsigned i=0; i<12; ++i)
transform[i] = transformValues[i];
scene.Transform(transform);
VERBOSE("Scene transformed by the given transformation matrix (%s)", TD_TIMER_GET_FMT().c_str());
}
if (!OPT::strTransferTextureFileName.empty()) {
// transfer the texture of the scene's mesh to the new mesh;
// the two meshes should be aligned and the new mesh to have UV-coordinates
Mesh newMesh;
if (!newMesh.Load(MAKE_PATH_SAFE(OPT::strTransferTextureFileName)))
return EXIT_FAILURE;
Mesh::FaceIdxArr faceSubsetIndices;
if (!OPT::strIndicesFileName.empty()) {
std::ifstream in(OPT::strIndicesFileName.c_str());
while (true) {
String index;
in >> index;
if (!in.good())
break;
faceSubsetIndices.emplace_back(index.From<Mesh::FIndex>());
}
}
if (!scene.mesh.TransferTexture(newMesh, faceSubsetIndices))
return EXIT_FAILURE;
newMesh.Save(baseFileName + OPT::strExportType);
VERBOSE("Texture transfered (%s)", TD_TIMER_GET_FMT().c_str());
return EXIT_SUCCESS;
}
if (OPT::nMaxResolution > 0) {
// scale scene images
const String folderName(Util::getFilePath(MAKE_PATH_FULL(WORKING_FOLDER_FULL, OPT::strInputFileName)) + String::FormatString("images%u" PATH_SEPARATOR_STR, OPT::nMaxResolution));
if (!scene.ScaleImages(OPT::nMaxResolution, 0, folderName)) {
DEBUG("error: can not scale scene images to '%s'", folderName.c_str());
return EXIT_FAILURE;
}
}
if (OPT::bComputeVolume && !scene.mesh.IsEmpty()) {
// compute the mesh volume
const REAL volume(scene.ComputeLeveledVolume(OPT::fPlaneThreshold, OPT::fSampleMesh, OPT::nUpAxis));
VERBOSE("Mesh volume: %g (%s)", volume, TD_TIMER_GET_FMT().c_str());
}
// write transformed scene
if (scene.IsValid())
scene.Save(MAKE_PATH_SAFE(OPT::strOutputFileName), (ARCHIVE_TYPE)OPT::nArchiveType);
if (!scene.IsValid() || (ARCHIVE_TYPE)OPT::nArchiveType == ARCHIVE_MVS) {
if (!scene.pointcloud.IsEmpty())
scene.pointcloud.Save(baseFileName + _T(".ply"), (ARCHIVE_TYPE)OPT::nArchiveType == ARCHIVE_MVS);
if (!scene.mesh.IsEmpty())
scene.mesh.Save(baseFileName + OPT::strExportType);
}
return EXIT_SUCCESS;
}
/*----------------------------------------------------------------*/

47
apps/Viewer/CMakeLists.txt

@ -0,0 +1,47 @@ @@ -0,0 +1,47 @@
if((NOT OpenMVS_USE_OPENGL) OR (NOT _USE_OPENGL))
RETURN()
endif()
if(NOT VIEWER_NAME)
set(VIEWER_NAME "Viewer")
endif()
# Find required packages
FIND_PACKAGE(GLEW QUIET)
if(GLEW_FOUND)
INCLUDE_DIRECTORIES(${GLEW_INCLUDE_DIRS})
ADD_DEFINITIONS(${GLEW_DEFINITIONS})
MESSAGE(STATUS "GLEW ${GLEW_VERSION} found (include: ${GLEW_INCLUDE_DIRS})")
else()
MESSAGE("-- Can't find GLEW. Continuing without it.")
RETURN()
endif()
FIND_PACKAGE(glfw3 QUIET)
if(glfw3_FOUND)
INCLUDE_DIRECTORIES(${glfw3_INCLUDE_DIRS})
ADD_DEFINITIONS(${glfw3_DEFINITIONS})
MESSAGE(STATUS "GLFW3 ${glfw3_VERSION} found (include: ${glfw3_INCLUDE_DIRS})")
else()
MESSAGE("-- Can't find GLFW3. Continuing without it.")
RETURN()
endif()
# List sources files
if(MSVC)
FILE(GLOB LIBRARY_FILES_C "*.cpp" "*.rc")
else()
FILE(GLOB LIBRARY_FILES_C "*.cpp")
endif()
FILE(GLOB LIBRARY_FILES_H "*.h" "*.inl")
cxx_executable_with_flags(${VIEWER_NAME} "Apps" "${cxx_default}" "MVS;${OPENGL_LIBRARIES};${GLEW_LIBRARY};${GLFW_STATIC_LIBRARIES};GLEW::GLEW;${glfw3_LIBRARY};${GLFW3_LIBRARY};glfw;${OpenMVS_EXTRA_LIBS}" ${LIBRARY_FILES_C} ${LIBRARY_FILES_H})
# Manually set Common.h as the precompiled header
IF(CMAKE_VERSION VERSION_GREATER_EQUAL 3.16.0)
TARGET_PRECOMPILE_HEADERS(${VIEWER_NAME} PRIVATE "Common.h")
endif()
# Install
INSTALL(TARGETS ${VIEWER_NAME}
EXPORT OpenMVSTargets
RUNTIME DESTINATION "${INSTALL_BIN_DIR}" COMPONENT bin)

180
apps/Viewer/Camera.cpp

@ -0,0 +1,180 @@ @@ -0,0 +1,180 @@
/*
* Camera.cpp
*
* Copyright (c) 2014-2015 SEACAVE
*
* Author(s):
*
* cDc <cdc.seacave@gmail.com>
*
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*
* Additional Terms:
*
* You are required to preserve legal notices and author attributions in
* that material or in the Appropriate Legal Notices displayed by works
* containing it.
*/
#include "Common.h"
#include "Camera.h"
using namespace VIEWER;
// D E F I N E S ///////////////////////////////////////////////////
// S T R U C T S ///////////////////////////////////////////////////
Camera::Camera(const AABB3d& _box, const Point3d& _center, float _scaleF, float _fov)
:
boxScene(_box),
centerScene(_center),
rotation(Eigen::Quaterniond::Identity()),
center(Eigen::Vector3d::Zero()),
dist(0), radius(100),
fovDef(_fov), scaleFDef(_scaleF),
prevCamID(NO_ID), currentCamID(NO_ID), maxCamID(0)
{
Reset();
}
void Camera::Reset()
{
if (boxScene.IsEmpty()) {
center = Point3d::ZERO;
radius = 1;
} else {
center = centerScene;
radius = boxScene.GetSize().norm()*0.5;
}
rotation = Eigen::Quaterniond::Identity();
scaleF = scaleFDef;
prevCamID = currentCamID = NO_ID;
fov = fovDef;
dist = radius*0.5 / SIN(D2R((double)fov));
if (size.area())
Resize(size);
}
void Camera::Resize(const cv::Size& _size)
{
ASSERT(MINF(_size.width, _size.height) > 0);
size = _size;
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
const GLfloat zNear = 1e-3f;
const GLfloat zFar = (float)boxScene.GetSize().norm()*10;
const GLfloat aspect = float(size.width)/float(size.height);
if (fov == 5.f) {
// orthographic projection
const GLfloat fH = (float)boxScene.GetSize().norm()*0.5f;
const GLfloat fW = fH * aspect;
glOrtho(-fW, fW, -fH, fH, zNear, zFar);
} else {
// perspective projection
const GLfloat fH = TAN(FD2R(fov)) * zNear;
const GLfloat fW = fH * aspect;
glFrustum(-fW, fW, -fH, fH, zNear, zFar);
}
}
void Camera::SetFOV(float _fov)
{
fov = MAXF(_fov, 5.f);
Resize(size);
}
Eigen::Vector3d Camera::GetPosition() const
{
const Eigen::Matrix3d R(GetRotation());
return center + R.col(2) * dist;
}
Eigen::Matrix3d Camera::GetRotation() const
{
return rotation.toRotationMatrix();
}
Eigen::Matrix4d Camera::GetLookAt() const
{
const Eigen::Matrix3d R(GetRotation());
const Eigen::Vector3d eye(center + R.col(2) * dist);
const Eigen::Vector3d up(R.col(1));
const Eigen::Vector3d n((center-eye).normalized());
const Eigen::Vector3d s(n.cross(up));
const Eigen::Vector3d v(s.cross(n));
Eigen::Matrix4d m; m <<
s(0), s(1), s(2), -eye.dot(s),
v(0), v(1), v(2), -eye.dot(v),
-n(0), -n(1), -n(2), eye.dot(n),
0.0, 0.0, 0.0, 1.0;
return m;
}
void Camera::GetLookAt(Eigen::Vector3d& _eye, Eigen::Vector3d& _center, Eigen::Vector3d& _up) const
{
const Eigen::Matrix3d R(GetRotation());
_eye = center + R.col(2) * dist;
_center = center;
_up = R.col(1);
}
void Camera::Rotate(const Eigen::Vector2d& pos, const Eigen::Vector2d& prevPos)
{
if (pos.isApprox(prevPos, ZEROTOLERANCE<double>()))
return;
Eigen::Vector3d oldp(prevPos.x(), prevPos.y(), 0);
Eigen::Vector3d newp(pos.x(), pos.y(), 0);
const double radiusSphere(0.9);
ProjectOnSphere(radiusSphere, oldp);
ProjectOnSphere(radiusSphere, newp);
rotation *= Eigen::Quaterniond().setFromTwoVectors(newp, oldp);
// disable camera view mode
prevCamID = currentCamID;
}
void Camera::Translate(const Eigen::Vector2d& pos, const Eigen::Vector2d& prevPos)
{
if (pos.isApprox(prevPos, ZEROTOLERANCE<double>()))
return;
Eigen::Matrix<double,4,4,Eigen::ColMajor> P, V;
glGetDoublev(GL_MODELVIEW_MATRIX, V.data());
glGetDoublev(GL_PROJECTION_MATRIX, P.data());
Eigen::Vector3d centerScreen((P*V*center.homogeneous().eval()).hnormalized());
centerScreen.head<2>() += prevPos - pos;
center = (V.inverse()*P.inverse()*centerScreen.homogeneous().eval()).hnormalized();
// disable camera view mode
prevCamID = currentCamID;
}
void Camera::ProjectOnSphere(double radius, Eigen::Vector3d& p) const
{
p.z() = 0;
const double d = p.x()* p.x()+ p.y() * p.y();
const double r = radius * radius;
if (d < r) p.z() = SQRT(r - d);
else p *= radius / p.norm();
}
/*----------------------------------------------------------------*/

87
apps/Viewer/Camera.h

@ -0,0 +1,87 @@ @@ -0,0 +1,87 @@
/*
* Camera.h
*
* Copyright (c) 2014-2015 SEACAVE
*
* Author(s):
*
* cDc <cdc.seacave@gmail.com>
*
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*
* Additional Terms:
*
* You are required to preserve legal notices and author attributions in
* that material or in the Appropriate Legal Notices displayed by works
* containing it.
*/
#ifndef _VIEWER_CAMERA_H_
#define _VIEWER_CAMERA_H_
// I N C L U D E S /////////////////////////////////////////////////
// D E F I N E S ///////////////////////////////////////////////////
// S T R U C T S ///////////////////////////////////////////////////
namespace VIEWER {
class Camera
{
public:
EIGEN_MAKE_ALIGNED_OPERATOR_NEW
cv::Size size;
AABB3d boxScene;
Eigen::Vector3d centerScene;
Eigen::Quaterniond rotation;
Eigen::Vector3d center;
double dist, radius;
float fov, fovDef;
float scaleF, scaleFDef;
MVS::IIndex prevCamID, currentCamID, maxCamID;
public:
Camera(const AABB3d& _box=AABB3d(true), const Point3d& _center=Point3d::ZERO, float _scaleF=1, float _fov=40);
void Reset();
void Resize(const cv::Size&);
void SetFOV(float _fov);
const cv::Size& GetSize() const { return size; }
Eigen::Vector3d GetPosition() const;
Eigen::Matrix3d GetRotation() const;
Eigen::Matrix4d GetLookAt() const;
void GetLookAt(Eigen::Vector3d& eye, Eigen::Vector3d& center, Eigen::Vector3d& up) const;
void Rotate(const Eigen::Vector2d& pos, const Eigen::Vector2d& prevPos);
void Translate(const Eigen::Vector2d& pos, const Eigen::Vector2d& prevPos);
bool IsCameraViewMode() const { return prevCamID != currentCamID && currentCamID != NO_ID; }
protected:
void ProjectOnSphere(double radius, Eigen::Vector3d& p) const;
};
/*----------------------------------------------------------------*/
} // namespace VIEWER
#endif // _VIEWER_CAMERA_H_

36
apps/Viewer/Common.cpp

@ -0,0 +1,36 @@ @@ -0,0 +1,36 @@
/*
* Common.cpp
*
* Copyright (c) 2014-2015 SEACAVE
*
* Author(s):
*
* cDc <cdc.seacave@gmail.com>
*
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*
* Additional Terms:
*
* You are required to preserve legal notices and author attributions in
* that material or in the Appropriate Legal Notices displayed by works
* containing it.
*/
// Source file that includes just the standard includes
// Common.pch will be the pre-compiled header
// Common.obj will contain the pre-compiled type information
#include "Common.h"

104
apps/Viewer/Common.h

@ -0,0 +1,104 @@ @@ -0,0 +1,104 @@
/*
* Common.h
*
* Copyright (c) 2014-2015 SEACAVE
*
* Author(s):
*
* cDc <cdc.seacave@gmail.com>
*
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*
* Additional Terms:
*
* You are required to preserve legal notices and author attributions in
* that material or in the Appropriate Legal Notices displayed by works
* containing it.
*/
#ifndef _VIEWER_COMMON_H_
#define _VIEWER_COMMON_H_
// I N C L U D E S /////////////////////////////////////////////////
#include <GL/glew.h>
#include "../../libs/MVS/Common.h"
#include "../../libs/MVS/Scene.h"
#if defined(_MSC_VER)
#include <gl/GLU.h>
#elif defined(__APPLE__)
#include <OpenGL/glu.h>
#else
#include <GL/glu.h>
#endif
#include <GLFW/glfw3.h>
// D E F I N E S ///////////////////////////////////////////////////
// P R O T O T Y P E S /////////////////////////////////////////////
using namespace SEACAVE;
namespace VIEWER {
// the conversion matrix from OpenGL default coordinate system
// to the camera coordinate system (NADIR orientation):
// [ 1 0 0 0] * [ x ] = [ x ]
// 0 -1 0 0 y -y
// 0 0 -1 0 z -z
// 0 0 0 1 1 1
static const Eigen::Matrix4d gs_convert = [] {
Eigen::Matrix4d tmp; tmp <<
1, 0, 0, 0,
0, -1, 0, 0,
0, 0, -1, 0,
0, 0, 0, 1;
return tmp;
}();
/// given rotation matrix R and translation vector t,
/// column-major matrix m is equal to:
/// [ R11 R12 R13 t.x ]
/// | R21 R22 R23 t.y |
/// | R31 R32 R33 t.z |
/// [ 0.0 0.0 0.0 1.0 ]
//
// World to Local
inline Eigen::Matrix4d TransW2L(const Eigen::Matrix3d& R, const Eigen::Vector3d& t)
{
Eigen::Matrix4d m(Eigen::Matrix4d::Identity());
m.block(0,0,3,3) = R;
m.block(0,3,3,1) = t;
return m;
}
// Local to World
// same as above, but with the inverse of the two
inline Eigen::Matrix4d TransL2W(const Eigen::Matrix3d& R, const Eigen::Vector3d& t)
{
Eigen::Matrix4d m(Eigen::Matrix4d::Identity());
m.block(0,0,3,3) = R.transpose();
m.block(0,3,3,1) = -t;
return m;
}
/*----------------------------------------------------------------*/
} // namespace MVS
#endif // _VIEWER_COMMON_H_

124
apps/Viewer/Image.cpp

@ -0,0 +1,124 @@ @@ -0,0 +1,124 @@
/*
* Image.cpp
*
* Copyright (c) 2014-2015 SEACAVE
*
* Author(s):
*
* cDc <cdc.seacave@gmail.com>
*
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*
* Additional Terms:
*
* You are required to preserve legal notices and author attributions in
* that material or in the Appropriate Legal Notices displayed by works
* containing it.
*/
#include "Common.h"
#include "Image.h"
using namespace VIEWER;
// D E F I N E S ///////////////////////////////////////////////////
// S T R U C T S ///////////////////////////////////////////////////
Image::Image(MVS::IIndex _idx)
:
idx(_idx),
texture(0)
{
}
Image::~Image()
{
Release();
}
void Image::Release()
{
if (IsValid()) {
glDeleteTextures(1, &texture);
texture = 0;
}
ReleaseImage();
}
void Image::ReleaseImage()
{
if (IsImageValid()) {
cv::Mat* const p(pImage);
Thread::safeExchange(pImage.ptr, (int_t)IMG_NULL);
delete p;
}
}
void Image::SetImageLoading()
{
ASSERT(IsImageEmpty());
Thread::safeExchange(pImage.ptr, (int_t)IMG_LOADING);
}
void Image::AssignImage(cv::InputArray img)
{
ASSERT(IsImageLoading());
ImagePtrInt pImg(new cv::Mat(img.getMat()));
if (pImg.pImage->cols%4 != 0) {
// make sure the width is multiple of 4 (seems to be an OpenGL limitation)
cv::resize(*pImg.pImage, *pImg.pImage, cv::Size((pImg.pImage->cols/4)*4, pImg.pImage->rows), 0, 0, cv::INTER_AREA);
}
Thread::safeExchange(pImage.ptr, pImg.ptr);
}
bool Image::TransferImage()
{
if (!IsImageValid())
return false;
SetImage(*pImage);
glfwPostEmptyEvent();
ReleaseImage();
return true;
}
void Image::SetImage(cv::InputArray img)
{
cv::Mat image(img.getMat());
glEnable(GL_TEXTURE_2D);
// create texture
glGenTextures(1, &texture);
// select our current texture
glBindTexture(GL_TEXTURE_2D, texture);
// load texture
width = image.cols;
height = image.rows;
ASSERT(image.channels() == 1 || image.channels() == 3);
ASSERT(image.isContinuous());
glTexImage2D(GL_TEXTURE_2D,
0, image.channels(),
width, height,
0, (image.channels() == 1) ? GL_LUMINANCE : GL_BGR,
GL_UNSIGNED_BYTE, image.ptr<uint8_t>());
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
}
void Image::GenerateMipmap() const {
glBindTexture(GL_TEXTURE_2D, texture);
glGenerateMipmap(GL_TEXTURE_2D);
}
void Image::Bind() const {
glBindTexture(GL_TEXTURE_2D, texture);
}
/*----------------------------------------------------------------*/

97
apps/Viewer/Image.h

@ -0,0 +1,97 @@ @@ -0,0 +1,97 @@
/*
* Image.h
*
* Copyright (c) 2014-2015 SEACAVE
*
* Author(s):
*
* cDc <cdc.seacave@gmail.com>
*
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*
* Additional Terms:
*
* You are required to preserve legal notices and author attributions in
* that material or in the Appropriate Legal Notices displayed by works
* containing it.
*/
#ifndef _VIEWER_IMAGE_H_
#define _VIEWER_IMAGE_H_
// I N C L U D E S /////////////////////////////////////////////////
// D E F I N E S ///////////////////////////////////////////////////
// S T R U C T S ///////////////////////////////////////////////////
namespace VIEWER {
class Image
{
public:
typedef CLISTDEFIDX(Image,uint32_t) ImageArr;
enum {
IMG_NULL = 0,
IMG_LOADING,
IMG_VALID
};
union ImagePtrInt {
cv::Mat* pImage;
int_t ptr;
inline ImagePtrInt() : ptr(IMG_NULL) {}
inline ImagePtrInt(cv::Mat* p) : pImage(p) {}
inline operator cv::Mat* () const { return pImage; }
inline operator cv::Mat*& () { return pImage; }
};
public:
MVS::IIndex idx; // image index in the current scene
int width, height;
GLuint texture;
double opacity;
ImagePtrInt pImage;
public:
Image(MVS::IIndex = NO_ID);
~Image();
void Release();
void ReleaseImage();
inline bool IsValid() const { return texture > 0; }
inline bool IsImageEmpty() const { return pImage.ptr == IMG_NULL; }
inline bool IsImageLoading() const { return pImage.ptr == IMG_LOADING; }
inline bool IsImageValid() const { return pImage.ptr >= IMG_VALID; }
void SetImageLoading();
void AssignImage(cv::InputArray);
bool TransferImage();
void SetImage(cv::InputArray);
void GenerateMipmap() const;
void Bind() const;
protected:
};
typedef Image::ImageArr ImageArr;
/*----------------------------------------------------------------*/
} // namespace VIEWER
#endif // _VIEWER_IMAGE_H_

823
apps/Viewer/Scene.cpp

@ -0,0 +1,823 @@ @@ -0,0 +1,823 @@
/*
* Scene.cpp
*
* Copyright (c) 2014-2015 SEACAVE
*
* Author(s):
*
* cDc <cdc.seacave@gmail.com>
*
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*
* Additional Terms:
*
* You are required to preserve legal notices and author attributions in
* that material or in the Appropriate Legal Notices displayed by works
* containing it.
*/
#include "Common.h"
#include "Scene.h"
using namespace VIEWER;
// D E F I N E S ///////////////////////////////////////////////////
#define IMAGE_MAX_RESOLUTION 1024
// S T R U C T S ///////////////////////////////////////////////////
enum EVENT_TYPE {
EVT_JOB = 0,
EVT_CLOSE,
};
class EVTClose : public Event
{
public:
EVTClose() : Event(EVT_CLOSE) {}
};
class EVTLoadImage : public Event
{
public:
Scene* pScene;
MVS::IIndex idx;
unsigned nMaxResolution;
bool Run(void*) {
Image& image = pScene->images[idx];
ASSERT(image.idx != NO_ID);
MVS::Image& imageData = pScene->scene.images[image.idx];
ASSERT(imageData.IsValid());
if (imageData.image.empty() && !imageData.ReloadImage(nMaxResolution))
return false;
imageData.UpdateCamera(pScene->scene.platforms);
image.AssignImage(imageData.image);
imageData.ReleaseImage();
glfwPostEmptyEvent();
return true;
}
EVTLoadImage(Scene* _pScene, MVS::IIndex _idx, unsigned _nMaxResolution=0)
: Event(EVT_JOB), pScene(_pScene), idx(_idx), nMaxResolution(_nMaxResolution) {}
};
class EVTComputeOctree : public Event
{
public:
Scene* pScene;
bool Run(void*) {
MVS::Scene& scene = pScene->scene;
if (!scene.mesh.IsEmpty()) {
Scene::OctreeMesh octMesh(scene.mesh.vertices, [](Scene::OctreeMesh::IDX_TYPE size, Scene::OctreeMesh::Type /*radius*/) {
return size > 256;
});
scene.mesh.ListIncidenteFaces();
pScene->octMesh.Swap(octMesh);
} else
if (!scene.pointcloud.IsEmpty()) {
Scene::OctreePoints octPoints(scene.pointcloud.points, [](Scene::OctreePoints::IDX_TYPE size, Scene::OctreePoints::Type /*radius*/) {
return size > 512;
});
pScene->octPoints.Swap(octPoints);
}
return true;
}
EVTComputeOctree(Scene* _pScene)
: Event(EVT_JOB), pScene(_pScene) {}
};
void* Scene::ThreadWorker(void*) {
while (true) {
CAutoPtr<Event> evt(events.GetEvent());
switch (evt->GetID()) {
case EVT_JOB:
evt->Run();
break;
case EVT_CLOSE:
return NULL;
default:
ASSERT("Should not happen!" == NULL);
}
}
return NULL;
}
/*----------------------------------------------------------------*/
// S T R U C T S ///////////////////////////////////////////////////
SEACAVE::EventQueue Scene::events;
SEACAVE::Thread Scene::thread;
Scene::Scene(ARCHIVE_TYPE _nArchiveType)
:
nArchiveType(_nArchiveType),
listPointCloud(0)
{
}
Scene::~Scene()
{
Release();
}
void Scene::Empty()
{
ReleasePointCloud();
ReleaseMesh();
obbPoints.Release();
if (window.IsValid()) {
window.ReleaseClbk();
window.Reset();
window.SetName(_T("(empty)"));
}
textures.Release();
images.Release();
scene.Release();
sceneName.clear();
geometryName.clear();
}
void Scene::Release()
{
if (window.IsValid())
window.SetVisible(false);
if (!thread.isRunning()) {
events.AddEvent(new EVTClose());
thread.join();
}
Empty();
window.Release();
glfwTerminate();
}
void Scene::ReleasePointCloud()
{
if (listPointCloud) {
glDeleteLists(listPointCloud, 1);
listPointCloud = 0;
}
}
void Scene::ReleaseMesh()
{
if (!listMeshes.empty()) {
for (GLuint listMesh: listMeshes)
glDeleteLists(listMesh, 1);
listMeshes.Release();
}
}
bool Scene::Init(const cv::Size& size, LPCTSTR windowName, LPCTSTR fileName, LPCTSTR geometryFileName)
{
ASSERT(scene.IsEmpty());
// init window
if (glfwInit() == GL_FALSE)
return false;
if (!window.Init(size, windowName))
return false;
if (glewInit() != GLEW_OK)
return false;
name = windowName;
window.clbkOpenScene = DELEGATEBINDCLASS(Window::ClbkOpenScene, &Scene::Open, this);
// init OpenGL
glPolygonMode(GL_FRONT, GL_FILL);
glEnable(GL_DEPTH_TEST);
glClearColor(0.f, 0.5f, 0.9f, 1.f);
static const float light0_ambient[] = {0.1f, 0.1f, 0.1f, 1.0f};
static const float light0_diffuse[] = {1.0f, 1.0f, 1.0f, 1.0f};
static const float light0_position[] = {0.0f, 0.0f, 1000.0f, 0.0f};
static const float light0_specular[] = {0.4f, 0.4f, 0.4f, 1.0f};
glLightfv(GL_LIGHT0, GL_AMBIENT, light0_ambient);
glLightfv(GL_LIGHT0, GL_DIFFUSE, light0_diffuse);
glLightfv(GL_LIGHT0, GL_SPECULAR, light0_specular);
glLightfv(GL_LIGHT0, GL_POSITION, light0_position);
glLightModelf(GL_LIGHT_MODEL_TWO_SIDE, GL_TRUE);
glEnable(GL_LIGHT0);
glDisable(GL_LIGHTING);
// init working thread
thread.start(ThreadWorker);
// open scene or init empty scene
window.SetCamera(Camera());
if (fileName != NULL)
Open(fileName, geometryFileName);
window.SetVisible(true);
return true;
}
bool Scene::Open(LPCTSTR fileName, LPCTSTR geometryFileName)
{
ASSERT(fileName);
DEBUG_EXTRA("Loading: '%s'", Util::getFileNameExt(fileName).c_str());
Empty();
sceneName = fileName;
// load the scene
WORKING_FOLDER = Util::getFilePath(fileName);
INIT_WORKING_FOLDER;
if (!scene.Load(fileName, true))
return false;
if (geometryFileName) {
// try to load given mesh
MVS::Mesh mesh;
MVS::PointCloud pointcloud;
if (mesh.Load(geometryFileName)) {
scene.mesh.Swap(mesh);
geometryName = geometryFileName;
geometryMesh = true;
} else
// try to load as a point-cloud
if (pointcloud.Load(geometryFileName)) {
scene.pointcloud.Swap(pointcloud);
geometryName = geometryFileName;
geometryMesh = false;
}
}
if (!scene.pointcloud.IsEmpty())
scene.pointcloud.PrintStatistics(scene.images.data(), &scene.obb);
#if 1
// create octree structure used to accelerate selection functionality
if (!scene.IsEmpty())
events.AddEvent(new EVTComputeOctree(this));
#endif
// init scene
AABB3d bounds(true);
Point3d center(Point3d::INF);
if (scene.IsBounded()) {
bounds = AABB3d(scene.obb.GetAABB());
center = bounds.GetCenter();
} else {
if (!scene.pointcloud.IsEmpty()) {
bounds = scene.pointcloud.GetAABB(MINF(3u,scene.nCalibratedImages));
if (bounds.IsEmpty())
bounds = scene.pointcloud.GetAABB();
center = scene.pointcloud.GetCenter();
}
if (!scene.mesh.IsEmpty()) {
scene.mesh.ComputeNormalFaces();
bounds.Insert(scene.mesh.GetAABB());
center = scene.mesh.GetCenter();
}
}
// init images
AABB3d imageBounds(true);
images.Reserve(scene.images.size());
FOREACH(idxImage, scene.images) {
const MVS::Image& imageData = scene.images[idxImage];
if (!imageData.IsValid())
continue;
images.emplace_back(idxImage);
imageBounds.InsertFull(imageData.camera.C);
}
if (imageBounds.IsEmpty())
imageBounds.Enlarge(0.5);
if (bounds.IsEmpty())
bounds = imageBounds;
// init and load texture
if (scene.mesh.HasTexture()) {
FOREACH(i, scene.mesh.texturesDiffuse) {
Image& image = textures.emplace_back();
ASSERT(image.idx == NO_ID);
#if 0
Image8U3& textureDiffuse = scene.mesh.texturesDiffuse[i];
cv::flip(textureDiffuse, textureDiffuse, 0);
image.SetImage(textureDiffuse);
textureDiffuse.release();
#else // preserve texture, used only to be able to export the mesh
Image8U3 textureDiffuse;
cv::flip(scene.mesh.texturesDiffuse[i], textureDiffuse, 0);
image.SetImage(textureDiffuse);
#endif
image.GenerateMipmap();
}
}
// init display lists
// compile point-cloud
CompilePointCloud();
// compile mesh
CompileMesh();
// compile bounding-box
CompileBounds();
// init camera
window.SetCamera(Camera(bounds,
center == Point3d::INF ? Point3d(bounds.GetCenter()) : center,
images.size()<2?1.f:(float)imageBounds.EnlargePercent(REAL(1)/images.size()).GetSize().norm()));
window.camera.maxCamID = images.size();
window.SetName(String::FormatString((name + _T(": %s")).c_str(), Util::getFileName(fileName).c_str()));
window.clbkSaveScene = DELEGATEBINDCLASS(Window::ClbkSaveScene, &Scene::Save, this);
window.clbkExportScene = DELEGATEBINDCLASS(Window::ClbkExportScene, &Scene::Export, this);
window.clbkCenterScene = DELEGATEBINDCLASS(Window::ClbkCenterScene, &Scene::Center, this);
window.clbkCompilePointCloud = DELEGATEBINDCLASS(Window::ClbkCompilePointCloud, &Scene::CompilePointCloud, this);
window.clbkCompileMesh = DELEGATEBINDCLASS(Window::ClbkCompileMesh, &Scene::CompileMesh, this);
window.clbkTogleSceneBox = DELEGATEBINDCLASS(Window::ClbkTogleSceneBox, &Scene::TogleSceneBox, this);
window.clbkCropToBounds = DELEGATEBINDCLASS(Window::ClbkCropToBounds, &Scene::CropToBounds, this);
if (scene.IsBounded())
window.clbkCompileBounds = DELEGATEBINDCLASS(Window::ClbkCompileBounds, &Scene::CompileBounds, this);
if (!scene.IsEmpty())
window.clbkRayScene = DELEGATEBINDCLASS(Window::ClbkRayScene, &Scene::CastRay, this);
window.Reset(!scene.pointcloud.IsEmpty()&&!scene.mesh.IsEmpty()?Window::SPR_NONE:Window::SPR_ALL,
MINF(2u,images.size()));
return true;
}
// export the scene
bool Scene::Save(LPCTSTR _fileName, bool bRescaleImages)
{
if (!IsOpen())
return false;
REAL imageScale = 0;
if (bRescaleImages) {
window.SetVisible(false);
std::cout << "Enter image resolution scale: ";
String strScale;
std::cin >> strScale;
window.SetVisible(true);
imageScale = strScale.From<REAL>(0);
}
const String fileName(_fileName != NULL ? String(_fileName) : Util::insertBeforeFileExt(sceneName, _T("_new")));
MVS::Mesh mesh;
if (!scene.mesh.IsEmpty() && !geometryName.empty() && geometryMesh)
mesh.Swap(scene.mesh);
MVS::PointCloud pointcloud;
if (!scene.pointcloud.IsEmpty() && !geometryName.empty() && !geometryMesh)
pointcloud.Swap(scene.pointcloud);
if (imageScale > 0 && imageScale < 1) {
// scale and save images
const String folderName(Util::getFilePath(MAKE_PATH_FULL(WORKING_FOLDER_FULL, fileName)) + String::FormatString("images%d" PATH_SEPARATOR_STR, ROUND2INT(imageScale*100)));
if (!scene.ScaleImages(0, imageScale, folderName)) {
DEBUG("error: can not scale scene images to '%s'", folderName.c_str());
return false;
}
}
if (!scene.Save(fileName, nArchiveType)) {
DEBUG("error: can not save scene to '%s'", fileName.c_str());
return false;
}
if (!mesh.IsEmpty())
scene.mesh.Swap(mesh);
if (!pointcloud.IsEmpty())
scene.pointcloud.Swap(pointcloud);
sceneName = fileName;
return true;
}
// export the scene
bool Scene::Export(LPCTSTR _fileName, LPCTSTR exportType) const
{
if (!IsOpen())
return false;
ASSERT(!sceneName.IsEmpty());
String lastFileName;
const String fileName(_fileName != NULL ? String(_fileName) : sceneName);
const String baseFileName(Util::getFileFullName(fileName));
const bool bPoints(scene.pointcloud.Save(lastFileName=(baseFileName+_T("_pointcloud.ply")), nArchiveType==ARCHIVE_MVS));
const bool bMesh(scene.mesh.Save(lastFileName=(baseFileName+_T("_mesh")+(exportType?exportType:(Util::getFileExt(fileName)==_T(".obj")?_T(".obj"):_T(".ply")))), cList<String>(), true));
#if TD_VERBOSE != TD_VERBOSE_OFF
if (VERBOSITY_LEVEL > 2 && (bPoints || bMesh))
scene.ExportCamerasMLP(Util::getFileFullName(lastFileName)+_T(".mlp"), lastFileName);
#endif
AABB3f aabb(true);
if (scene.IsBounded()) {
std::ofstream fs(baseFileName+_T("_roi.txt"));
if (fs)
fs << scene.obb;
aabb = scene.obb.GetAABB();
} else
if (!scene.pointcloud.IsEmpty()) {
aabb = scene.pointcloud.GetAABB();
} else
if (!scene.mesh.IsEmpty()) {
aabb = scene.mesh.GetAABB();
}
if (!aabb.IsEmpty()) {
std::ofstream fs(baseFileName+_T("_roi_box.txt"));
if (fs)
fs << aabb;
}
return bPoints || bMesh;
}
void Scene::CompilePointCloud()
{
if (scene.pointcloud.IsEmpty())
return;
ReleasePointCloud();
listPointCloud = glGenLists(1);
glNewList(listPointCloud, GL_COMPILE);
ASSERT((window.sparseType&(Window::SPR_POINTS|Window::SPR_LINES)) != 0);
// compile point-cloud
if ((window.sparseType&Window::SPR_POINTS) != 0) {
ASSERT_ARE_SAME_TYPE(float, MVS::PointCloud::Point::Type);
glBegin(GL_POINTS);
glColor3f(1.f,1.f,1.f);
FOREACH(i, scene.pointcloud.points) {
if (!scene.pointcloud.pointViews.empty() &&
scene.pointcloud.pointViews[i].size() < window.minViews)
continue;
if (!scene.pointcloud.colors.empty()) {
const MVS::PointCloud::Color& c = scene.pointcloud.colors[i];
glColor3ub(c.r,c.g,c.b);
}
const MVS::PointCloud::Point& X = scene.pointcloud.points[i];
glVertex3fv(X.ptr());
}
glEnd();
}
glEndList();
}
void Scene::CompileMesh()
{
if (scene.mesh.IsEmpty())
return;
ReleaseMesh();
if (scene.mesh.faceNormals.empty())
scene.mesh.ComputeNormalFaces();
// translate, normalize and flip Y axis of the texture coordinates
MVS::Mesh::TexCoordArr normFaceTexcoords;
if (scene.mesh.HasTexture() && window.bRenderTexture)
scene.mesh.FaceTexcoordsNormalize(normFaceTexcoords, true);
MVS::Mesh::TexIndex texIdx(0);
do {
GLuint& listMesh = listMeshes.emplace_back(glGenLists(1));
listMesh = glGenLists(1);
glNewList(listMesh, GL_COMPILE);
// compile mesh
ASSERT_ARE_SAME_TYPE(float, MVS::Mesh::Vertex::Type);
ASSERT_ARE_SAME_TYPE(float, MVS::Mesh::Normal::Type);
ASSERT_ARE_SAME_TYPE(float, MVS::Mesh::TexCoord::Type);
glColor3f(1.f, 1.f, 1.f);
glBegin(GL_TRIANGLES);
FOREACH(idxFace, scene.mesh.faces) {
if (!scene.mesh.faceTexindices.empty() && scene.mesh.faceTexindices[idxFace] != texIdx)
continue;
const MVS::Mesh::Face& face = scene.mesh.faces[idxFace];
const MVS::Mesh::Normal& n = scene.mesh.faceNormals[idxFace];
glNormal3fv(n.ptr());
for (int j = 0; j < 3; ++j) {
if (!normFaceTexcoords.empty()) {
const MVS::Mesh::TexCoord& t = normFaceTexcoords[idxFace*3 + j];
glTexCoord2fv(t.ptr());
}
const MVS::Mesh::Vertex& p = scene.mesh.vertices[face[j]];
glVertex3fv(p.ptr());
}
}
glEnd();
glEndList();
} while (++texIdx < scene.mesh.texturesDiffuse.size());
}
void Scene::CompileBounds()
{
obbPoints.Release();
if (!scene.IsBounded()) {
window.bRenderBounds = false;
return;
}
window.bRenderBounds = !window.bRenderBounds;
if (window.bRenderBounds) {
static const uint8_t indices[12*2] = {
0,2, 2,3, 3,1, 1,0,
0,6, 2,4, 3,5, 1,7,
6,4, 4,5, 5,7, 7,6
};
OBB3f::POINT corners[OBB3f::numCorners];
scene.obb.GetCorners(corners);
for (int i=0; i<12; ++i) {
obbPoints.emplace_back(corners[indices[i*2+0]]);
obbPoints.emplace_back(corners[indices[i*2+1]]);
}
}
}
void Scene::CropToBounds()
{
if (!IsOpen())
return;
if (!scene.IsBounded())
return;
scene.pointcloud.RemovePointsOutside(scene.obb);
scene.mesh.RemoveFacesOutside(scene.obb);
Center();
}
void Scene::Draw()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glPointSize(window.pointSize);
// render point-cloud
if (listPointCloud) {
glDisable(GL_TEXTURE_2D);
glCallList(listPointCloud);
}
// render mesh
if (!listMeshes.empty()) {
glEnable(GL_DEPTH_TEST);
glEnable(GL_CULL_FACE);
if (!scene.mesh.faceTexcoords.empty() && window.bRenderTexture) {
glEnable(GL_TEXTURE_2D);
FOREACH(i, listMeshes) {
textures[i].Bind();
glCallList(listMeshes[i]);
}
glDisable(GL_TEXTURE_2D);
} else {
glEnable(GL_LIGHTING);
for (GLuint listMesh: listMeshes)
glCallList(listMesh);
glDisable(GL_LIGHTING);
}
}
// render cameras
if (window.bRenderCameras) {
glDisable(GL_CULL_FACE);
const Point3* ptrPrevC(NULL);
FOREACH(idx, images) {
Image& image = images[idx];
const MVS::Image& imageData = scene.images[image.idx];
const MVS::Camera& camera = imageData.camera;
// cache image corner coordinates
const double scaleFocal(window.camera.scaleF);
const Point2d pp(camera.GetPrincipalPoint());
const double focal(camera.GetFocalLength()/scaleFocal);
const double cx(-pp.x/focal);
const double cy(-pp.y/focal);
const double px((double)imageData.width/focal+cx);
const double py((double)imageData.height/focal+cy);
const Point3d ic1(cx, cy, scaleFocal);
const Point3d ic2(cx, py, scaleFocal);
const Point3d ic3(px, py, scaleFocal);
const Point3d ic4(px, cy, scaleFocal);
// change coordinates system to the camera space
glPushMatrix();
glMultMatrixd((GLdouble*)TransL2W((const Matrix3x3::EMat)camera.R, -(const Point3::EVec)camera.C).data());
glPointSize(window.pointSize+1.f);
glDisable(GL_TEXTURE_2D);
// draw camera position and image center
glBegin(GL_POINTS);
glColor3f(1,0,0); glVertex3f(0,0,0); // camera position
glColor3f(0,1,0); glVertex3f(0,0,(float)scaleFocal); // image center
glColor3f(0,0,1); glVertex3d((0.5*imageData.width-pp.x)/focal, cy, scaleFocal); // image up
glEnd();
// draw image thumbnail
const bool bSelectedImage(idx == window.camera.currentCamID);
if (bSelectedImage) {
if (image.IsValid()) {
// render image
glEnable(GL_TEXTURE_2D);
image.Bind();
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glEnable(GL_BLEND);
glDisable(GL_DEPTH_TEST);
glColor4f(1,1,1,window.cameraBlend);
glBegin(GL_QUADS);
glTexCoord2d(0,0); glVertex3dv(ic1.ptr());
glTexCoord2d(0,1); glVertex3dv(ic2.ptr());
glTexCoord2d(1,1); glVertex3dv(ic3.ptr());
glTexCoord2d(1,0); glVertex3dv(ic4.ptr());
glEnd();
glDisable(GL_TEXTURE_2D);
glDisable(GL_BLEND);
glEnable(GL_DEPTH_TEST);
} else {
// start and wait to load the image
if (image.IsImageEmpty()) {
// start loading
image.SetImageLoading();
events.AddEvent(new EVTLoadImage(this, idx, IMAGE_MAX_RESOLUTION));
} else {
// check if the image is available and set it
image.TransferImage();
}
}
}
// draw camera frame
glColor3f(bSelectedImage ? 0.f : 1.f, 1.f, 0.f);
glBegin(GL_LINES);
glVertex3d(0,0,0); glVertex3dv(ic1.ptr());
glVertex3d(0,0,0); glVertex3dv(ic2.ptr());
glVertex3d(0,0,0); glVertex3dv(ic3.ptr());
glVertex3d(0,0,0); glVertex3dv(ic4.ptr());
glVertex3dv(ic1.ptr()); glVertex3dv(ic2.ptr());
glVertex3dv(ic2.ptr()); glVertex3dv(ic3.ptr());
glVertex3dv(ic3.ptr()); glVertex3dv(ic4.ptr());
glVertex3dv(ic4.ptr()); glVertex3dv(ic1.ptr());
glEnd();
// restore coordinate system
glPopMatrix();
// render image visibility info
if (window.bRenderImageVisibility && idx != NO_ID && idx==window.camera.currentCamID) {
if (scene.pointcloud.IsValid()) {
const Image& image = images[idx];
glPointSize(window.pointSize*1.1f);
glDisable(GL_DEPTH_TEST);
glBegin(GL_POINTS);
glColor3f(1.f,0.f,0.f);
FOREACH(i, scene.pointcloud.points) {
ASSERT(!scene.pointcloud.pointViews[i].empty());
if (scene.pointcloud.pointViews[i].size() < window.minViews)
continue;
if (scene.pointcloud.pointViews[i].FindFirst(image.idx) == MVS::PointCloud::ViewArr::NO_INDEX)
continue;
glVertex3fv(scene.pointcloud.points[i].ptr());
}
glEnd();
glEnable(GL_DEPTH_TEST);
glPointSize(window.pointSize);
}
}
// render camera trajectory
if (window.bRenderCameraTrajectory && ptrPrevC) {
glBegin(GL_LINES);
glColor3f(1.f,0.5f,0.f);
glVertex3dv(ptrPrevC->ptr());
glVertex3dv(camera.C.ptr());
glEnd();
}
ptrPrevC = &camera.C;
}
}
// render selection
if (window.selectionType != Window::SEL_NA) {
glPointSize(window.pointSize+4);
glDisable(GL_DEPTH_TEST);
glBegin(GL_POINTS);
glColor3f(1,0,0); glVertex3fv(window.selectionPoints[0].ptr());
if (window.selectionType == Window::SEL_TRIANGLE) {
glColor3f(0,1,0); glVertex3fv(window.selectionPoints[1].ptr());
glColor3f(0,0,1); glVertex3fv(window.selectionPoints[2].ptr());
}
glEnd();
if (window.bRenderViews && window.selectionType == Window::SEL_POINT) {
if (!scene.pointcloud.pointViews.empty()) {
glBegin(GL_LINES);
const MVS::PointCloud::ViewArr& views = scene.pointcloud.pointViews[(MVS::PointCloud::Index)window.selectionIdx];
ASSERT(!views.empty());
for (MVS::PointCloud::View idxImage: views) {
const MVS::Image& imageData = scene.images[idxImage];
glVertex3dv(imageData.camera.C.ptr());
glVertex3fv(window.selectionPoints[0].ptr());
}
glEnd();
}
}
glEnable(GL_DEPTH_TEST);
glPointSize(window.pointSize);
}
// render oriented-bounding-box
if (!obbPoints.empty()) {
glDepthMask(GL_FALSE);
glBegin(GL_LINES);
glColor3f(0.5f,0.1f,0.8f);
for (IDX i=0; i<obbPoints.size(); i+=2) {
glVertex3fv(obbPoints[i+0].ptr());
glVertex3fv(obbPoints[i+1].ptr());
}
glEnd();
glDepthMask(GL_TRUE);
}
glfwSwapBuffers(window.GetWindow());
}
void Scene::Loop()
{
while (!glfwWindowShouldClose(window.GetWindow())) {
window.UpdateView(images, scene.images);
Draw();
glfwWaitEvents();
}
}
void Scene::Center()
{
if (!IsOpen())
return;
scene.Center();
CompilePointCloud();
CompileMesh();
if (scene.IsBounded()) {
window.bRenderBounds = false;
CompileBounds();
}
events.AddEvent(new EVTComputeOctree(this));
}
void Scene::TogleSceneBox()
{
if (!IsOpen())
return;
const auto EnlargeAABB = [](AABB3f aabb) {
return aabb.Enlarge(aabb.GetSize().maxCoeff()*0.03f);
};
if (scene.IsBounded())
scene.obb = OBB3f(true);
else if (!scene.mesh.IsEmpty())
scene.obb.Set(EnlargeAABB(scene.mesh.GetAABB()));
else if (!scene.pointcloud.IsEmpty())
scene.obb.Set(EnlargeAABB(scene.pointcloud.GetAABB(window.minViews)));
CompileBounds();
}
void Scene::CastRay(const Ray3& ray, int action)
{
if (!IsOctreeValid())
return;
const double timeClick(0.2);
const double timeDblClick(0.3);
const double now(glfwGetTime());
switch (action) {
case GLFW_PRESS: {
// remember when the click action started
window.selectionTimeClick = now;
break; }
case GLFW_RELEASE: {
if (now-window.selectionTimeClick > timeClick) {
// this is a long click, ignore it
break;
} else
if (window.selectionType != Window::SEL_NA &&
now-window.selectionTime < timeDblClick) {
// this is a double click, center scene at the selected point
window.CenterCamera(window.selectionPoints[3]);
window.selectionTime = now;
} else
if (!octMesh.IsEmpty()) {
// find ray intersection with the mesh
const MVS::IntersectRayMesh intRay(octMesh, ray, scene.mesh);
if (intRay.pick.IsValid()) {
const MVS::Mesh::Face& face = scene.mesh.faces[(MVS::Mesh::FIndex)intRay.pick.idx];
window.selectionPoints[0] = scene.mesh.vertices[face[0]];
window.selectionPoints[1] = scene.mesh.vertices[face[1]];
window.selectionPoints[2] = scene.mesh.vertices[face[2]];
window.selectionPoints[3] = ray.GetPoint(intRay.pick.dist).cast<float>();
window.selectionType = Window::SEL_TRIANGLE;
window.selectionTime = now;
window.selectionIdx = intRay.pick.idx;
DEBUG("Face selected:\n\tindex: %u\n\tvertex 1: %u (%g %g %g)\n\tvertex 2: %u (%g %g %g)\n\tvertex 3: %u (%g %g %g)",
intRay.pick.idx,
face[0], window.selectionPoints[0].x, window.selectionPoints[0].y, window.selectionPoints[0].z,
face[1], window.selectionPoints[1].x, window.selectionPoints[1].y, window.selectionPoints[1].z,
face[2], window.selectionPoints[2].x, window.selectionPoints[2].y, window.selectionPoints[2].z
);
} else {
window.selectionType = Window::SEL_NA;
}
} else
if (!octPoints.IsEmpty()) {
// find ray intersection with the points
const MVS::IntersectRayPoints intRay(octPoints, ray, scene.pointcloud, window.minViews);
if (intRay.pick.IsValid()) {
window.selectionPoints[0] = window.selectionPoints[3] = scene.pointcloud.points[intRay.pick.idx];
window.selectionType = Window::SEL_POINT;
window.selectionTime = now;
window.selectionIdx = intRay.pick.idx;
DEBUG("Point selected:\n\tindex: %u (%g %g %g)%s",
intRay.pick.idx,
window.selectionPoints[0].x, window.selectionPoints[0].y, window.selectionPoints[0].z,
[&]() {
if (scene.pointcloud.pointViews.empty())
return String();
const MVS::PointCloud::ViewArr& views = scene.pointcloud.pointViews[intRay.pick.idx];
ASSERT(!views.empty());
String strViews(String::FormatString("\n\tviews: %u", views.size()));
for (MVS::PointCloud::View idxImage: views) {
const MVS::Image& imageData = scene.images[idxImage];
const Point2 x(imageData.camera.TransformPointW2I(Cast<REAL>(window.selectionPoints[0])));
strViews += String::FormatString("\n\t\t%s (%.2f %.2f)", Util::getFileNameExt(imageData.name).c_str(), x.x, x.y);
}
return strViews;
}().c_str()
);
} else {
window.selectionType = Window::SEL_NA;
}
}
break; }
}
}
/*----------------------------------------------------------------*/

111
apps/Viewer/Scene.h

@ -0,0 +1,111 @@ @@ -0,0 +1,111 @@
/*
* Scene.h
*
* Copyright (c) 2014-2015 SEACAVE
*
* Author(s):
*
* cDc <cdc.seacave@gmail.com>
*
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*
* Additional Terms:
*
* You are required to preserve legal notices and author attributions in
* that material or in the Appropriate Legal Notices displayed by works
* containing it.
*/
#ifndef _VIEWER_SCENE_H_
#define _VIEWER_SCENE_H_
// I N C L U D E S /////////////////////////////////////////////////
#include "Window.h"
// D E F I N E S ///////////////////////////////////////////////////
// S T R U C T S ///////////////////////////////////////////////////
namespace VIEWER {
class Scene
{
public:
typedef MVS::PointCloud::Octree OctreePoints;
typedef MVS::Mesh::Octree OctreeMesh;
public:
ARCHIVE_TYPE nArchiveType;
String name;
String sceneName;
String geometryName;
bool geometryMesh;
MVS::Scene scene;
Window window;
ImageArr images; // scene photos
ImageArr textures; // mesh textures
OctreePoints octPoints;
OctreeMesh octMesh;
Point3fArr obbPoints;
GLuint listPointCloud;
CLISTDEF0IDX(GLuint,MVS::Mesh::TexIndex) listMeshes;
// multi-threading
static SEACAVE::EventQueue events; // internal events queue (processed by the working threads)
static SEACAVE::Thread thread; // worker thread
public:
explicit Scene(ARCHIVE_TYPE _nArchiveType = ARCHIVE_MVS);
~Scene();
void Empty();
void Release();
void ReleasePointCloud();
void ReleaseMesh();
inline bool IsValid() const { return window.IsValid(); }
inline bool IsOpen() const { return IsValid() && !scene.IsEmpty(); }
inline bool IsOctreeValid() const { return !octPoints.IsEmpty() || !octMesh.IsEmpty(); }
bool Init(const cv::Size&, LPCTSTR windowName, LPCTSTR fileName=NULL, LPCTSTR geometryFileName=NULL);
bool Open(LPCTSTR fileName, LPCTSTR geometryFileName=NULL);
bool Save(LPCTSTR fileName=NULL, bool bRescaleImages=false);
bool Export(LPCTSTR fileName, LPCTSTR exportType=NULL) const;
void CompilePointCloud();
void CompileMesh();
void CompileBounds();
void CropToBounds();
void Draw();
void Loop();
void Center();
void TogleSceneBox();
void CastRay(const Ray3&, int);
protected:
static void* ThreadWorker(void*);
};
/*----------------------------------------------------------------*/
} // namespace VIEWER
#endif // _VIEWER_SCENE_H_

226
apps/Viewer/Viewer.cpp

@ -0,0 +1,226 @@ @@ -0,0 +1,226 @@
/*
* Viewer.cpp
*
* Copyright (c) 2014-2015 SEACAVE
*
* Author(s):
*
* cDc <cdc.seacave@gmail.com>
*
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*
* Additional Terms:
*
* You are required to preserve legal notices and author attributions in
* that material or in the Appropriate Legal Notices displayed by works
* containing it.
*/
#include "Common.h"
#include <boost/program_options.hpp>
#include "Scene.h"
using namespace VIEWER;
// D E F I N E S ///////////////////////////////////////////////////
#define APPNAME _T("Viewer")
// S T R U C T S ///////////////////////////////////////////////////
namespace {
namespace OPT {
String strInputFileName;
String strGeometryFileName;
String strOutputFileName;
unsigned nArchiveType;
int nProcessPriority;
unsigned nMaxThreads;
unsigned nMaxMemory;
String strExportType;
String strConfigFileName;
#if TD_VERBOSE != TD_VERBOSE_OFF
bool bLogFile;
#endif
boost::program_options::variables_map vm;
} // namespace OPT
class Application {
public:
Application() {}
~Application() { Finalize(); }
bool Initialize(size_t argc, LPCTSTR* argv);
void Finalize();
}; // Application
// initialize and parse the command line parameters
bool Application::Initialize(size_t argc, LPCTSTR* argv)
{
// initialize log and console
OPEN_LOG();
OPEN_LOGCONSOLE();
// group of options allowed only on command line
boost::program_options::options_description generic("Generic options");
generic.add_options()
("help,h", "produce this help message")
("working-folder,w", boost::program_options::value<std::string>(&WORKING_FOLDER), "working directory (default current directory)")
("config-file,c", boost::program_options::value<std::string>(&OPT::strConfigFileName)->default_value(APPNAME _T(".cfg")), "file name containing program options")
("export-type", boost::program_options::value<std::string>(&OPT::strExportType), "file type used to export the 3D scene (ply or obj)")
("archive-type", boost::program_options::value(&OPT::nArchiveType)->default_value(ARCHIVE_MVS), "project archive type: -1-interface, 0-text, 1-binary, 2-compressed binary")
("process-priority", boost::program_options::value(&OPT::nProcessPriority)->default_value(0), "process priority (normal by default)")
("max-threads", boost::program_options::value(&OPT::nMaxThreads)->default_value(0), "maximum number of threads that this process should use (0 - use all available cores)")
("max-memory", boost::program_options::value(&OPT::nMaxMemory)->default_value(0), "maximum amount of memory in MB that this process should use (0 - use all available memory)")
#if TD_VERBOSE != TD_VERBOSE_OFF
("log-file", boost::program_options::value(&OPT::bLogFile)->default_value(false), "dump log to a file")
("verbosity,v", boost::program_options::value(&g_nVerbosityLevel)->default_value(
#if TD_VERBOSE == TD_VERBOSE_DEBUG
3
#else
2
#endif
), "verbosity level")
#endif
;
// group of options allowed both on command line and in config file
boost::program_options::options_description config("Viewer options");
config.add_options()
("input-file,i", boost::program_options::value<std::string>(&OPT::strInputFileName), "input project filename containing camera poses and scene (point-cloud/mesh)")
("geometry-file,g", boost::program_options::value<std::string>(&OPT::strGeometryFileName), "mesh or point-cloud with views file name (overwrite existing geometry)")
("output-file,o", boost::program_options::value<std::string>(&OPT::strOutputFileName), "output filename for storing the mesh")
;
boost::program_options::options_description cmdline_options;
cmdline_options.add(generic).add(config);
boost::program_options::options_description config_file_options;
config_file_options.add(config);
boost::program_options::positional_options_description p;
p.add("input-file", -1);
try {
// parse command line options
boost::program_options::store(boost::program_options::command_line_parser((int)argc, argv).options(cmdline_options).positional(p).run(), OPT::vm);
boost::program_options::notify(OPT::vm);
INIT_WORKING_FOLDER;
// parse configuration file
std::ifstream ifs(MAKE_PATH_SAFE(OPT::strConfigFileName));
if (ifs) {
boost::program_options::store(parse_config_file(ifs, config_file_options), OPT::vm);
boost::program_options::notify(OPT::vm);
}
}
catch (const std::exception& e) {
LOG(e.what());
return false;
}
#if TD_VERBOSE != TD_VERBOSE_OFF
// initialize the log file
if (OPT::bLogFile)
OPEN_LOGFILE((MAKE_PATH(APPNAME _T("-")+Util::getUniqueName(0)+_T(".log"))).c_str());
#endif
// print application details: version and command line
Util::LogBuild();
LOG(_T("Command line: ") APPNAME _T("%s"), Util::CommandLineToString(argc, argv).c_str());
// validate input
Util::ensureValidPath(OPT::strInputFileName);
if (OPT::vm.count("help")) {
boost::program_options::options_description visible("Available options");
visible.add(generic).add(config);
GET_LOG() << _T("\n"
"Visualize any know point-cloud/mesh formats or MVS projects. Supply files through command line or Drag&Drop.\n"
"Keys:\n"
"\tE: export scene\n"
"\tR: reset scene\n"
"\tB: render bounds\n"
"\tB + Shift: togle bounds\n"
"\tC: render cameras\n"
"\tC + Shift: render camera trajectory\n"
"\tC + Ctrl: center scene\n"
"\tLeft/Right: select next camera to view the scene\n"
"\tS: save scene\n"
"\tS + Shift: rescale images and save scene\n"
"\tT: render mesh texture\n"
"\tW: render wire-frame mesh\n"
"\tV: render view rays to the selected point\n"
"\tV + Shift: render points seen by the current view\n"
"\tUp/Down: adjust point size\n"
"\tUp/Down + Shift: adjust minimum number of views accepted when displaying a point or line\n"
"\t+/-: adjust camera thumbnail transparency\n"
"\t+/- + Shift: adjust camera cones' length\n"
"\n")
<< visible;
}
if (!OPT::strExportType.empty())
OPT::strExportType = OPT::strExportType.ToLower() == _T("obj") ? _T(".obj") : _T(".ply");
// initialize optional options
Util::ensureValidPath(OPT::strGeometryFileName);
Util::ensureValidPath(OPT::strOutputFileName);
MVS::Initialize(APPNAME, OPT::nMaxThreads, OPT::nProcessPriority);
return true;
}
// finalize application instance
void Application::Finalize()
{
MVS::Finalize();
if (OPT::bLogFile)
CLOSE_LOGFILE();
CLOSE_LOGCONSOLE();
CLOSE_LOG();
}
} // unnamed namespace
int main(int argc, LPCTSTR* argv)
{
#ifdef _DEBUGINFO
// set _crtBreakAlloc index to stop in <dbgheap.c> at allocation
_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);// | _CRTDBG_CHECK_ALWAYS_DF);
#endif
Application application;
if (!application.Initialize(argc, argv))
return EXIT_FAILURE;
// create viewer
Scene viewer;
if (!viewer.Init(cv::Size(1280, 720), APPNAME,
OPT::strInputFileName.empty() ? NULL : MAKE_PATH_SAFE(OPT::strInputFileName).c_str(),
OPT::strGeometryFileName.empty() ? NULL : MAKE_PATH_SAFE(OPT::strGeometryFileName).c_str()))
return EXIT_FAILURE;
if (viewer.IsOpen() && !OPT::strOutputFileName.empty()) {
// export the scene
viewer.Export(MAKE_PATH_SAFE(OPT::strOutputFileName), OPT::strExportType.empty()?LPCTSTR(NULL):OPT::strExportType.c_str());
}
// enter viewer loop
viewer.Loop();
return EXIT_SUCCESS;
}
/*----------------------------------------------------------------*/

493
apps/Viewer/Window.cpp

@ -0,0 +1,493 @@ @@ -0,0 +1,493 @@
/*
* Window.cpp
*
* Copyright (c) 2014-2015 SEACAVE
*
* Author(s):
*
* cDc <cdc.seacave@gmail.com>
*
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*
* Additional Terms:
*
* You are required to preserve legal notices and author attributions in
* that material or in the Appropriate Legal Notices displayed by works
* containing it.
*/
#include "Common.h"
#include "Window.h"
using namespace VIEWER;
// D E F I N E S ///////////////////////////////////////////////////
// S T R U C T S ///////////////////////////////////////////////////
Window::WindowsMap Window::g_mapWindows;
Window::Window()
:
window(NULL),
pos(Eigen::Vector2d::Zero()),
prevPos(Eigen::Vector2d::Zero())
{
}
Window::~Window()
{
Release();
}
void Window::Release()
{
if (IsValid()) {
#ifdef _USE_NUKLEAR
nk_glfw3_shutdown();
#endif
glfwDestroyWindow(window);
window = NULL;
}
clbkOpenScene.reset();
ReleaseClbk();
}
void Window::ReleaseClbk()
{
clbkSaveScene.reset();
clbkExportScene.reset();
clbkCenterScene.reset();
clbkRayScene.reset();
clbkCompilePointCloud.reset();
clbkCompileMesh.reset();
clbkCompileBounds.reset();
clbkTogleSceneBox.reset();
clbkCropToBounds.reset();
}
bool Window::Init(const cv::Size& _size, LPCTSTR name)
{
sizeScale = 1;
size = _size;
glfwDefaultWindowHints();
glfwWindowHint(GLFW_VISIBLE, 0);
window = glfwCreateWindow(size.width, size.height, name, NULL, NULL);
if (!window)
return false;
glfwMakeContextCurrent(window);
glfwSetFramebufferSizeCallback(window, Window::Resize);
glfwSetKeyCallback(window, Window::Key);
glfwSetMouseButtonCallback(window, Window::MouseButton);
glfwSetCursorPosCallback(window, Window::MouseMove);
glfwSetScrollCallback(window, Window::Scroll);
glfwSetDropCallback(window, Window::Drop);
g_mapWindows[window] = this;
Reset();
return true;
}
void Window::SetCamera(const Camera& cam)
{
camera = cam;
cv::Size _size;
glfwGetFramebufferSize(window, &_size.width, &_size.height);
Resize(_size);
}
void Window::SetName(LPCTSTR name)
{
glfwSetWindowTitle(window, name);
}
void Window::SetVisible(bool v)
{
if (v)
glfwShowWindow(window);
else
glfwHideWindow(window);
}
bool Window::IsVisible() const
{
return glfwGetWindowAttrib(window, GLFW_VISIBLE) != 0;
}
void Window::Reset(SPARSE _sparseType, unsigned _minViews)
{
camera.Reset();
inputType = INP_NA;
sparseType = _sparseType;
minViews = _minViews;
pointSize = 2.f;
cameraBlend = 0.5f;
bRenderCameras = true;
bRenderCameraTrajectory = true;
bRenderImageVisibility = false;
bRenderViews = true;
bRenderSolid = true;
bRenderTexture = true;
bRenderBounds = false;
selectionType = SEL_NA;
selectionIdx = NO_IDX;
if (clbkCompilePointCloud != NULL)
clbkCompilePointCloud();
if (clbkCompileMesh != NULL)
clbkCompileMesh();
glfwPostEmptyEvent();
}
void Window::CenterCamera(const Point3& pos)
{
camera.center = pos;
camera.dist *= 0.7;
}
void Window::UpdateView(const ImageArr& images, const MVS::ImageArr& sceneImagesMVS)
{
if (camera.IsCameraViewMode()) {
// enable camera view mode and apply current camera transform
const Image& image = images[camera.currentCamID];
const MVS::Camera& camera = sceneImagesMVS[image.idx].camera;
UpdateView((const Matrix3x3::EMat)camera.R, camera.GetT());
} else {
// apply view point transform
glMatrixMode(GL_MODELVIEW);
const Eigen::Matrix4d trans(camera.GetLookAt());
glLoadMatrixd((GLdouble*)trans.data());
}
}
void Window::UpdateView(const Eigen::Matrix3d& R, const Eigen::Vector3d& t)
{
glMatrixMode(GL_MODELVIEW);
transform = gs_convert * TransW2L(R, t);
glLoadMatrixd((GLdouble*)transform.data());
}
void Window::UpdateMousePosition(double xpos, double ypos)
{
prevPos = pos;
pos.x() = xpos;
pos.y() = ypos;
// normalize position to [-1:1] range
const int w(camera.size.width);
const int h(camera.size.height);
pos.x() = (2.0 * pos.x() - w) / w;
pos.y() = (h - 2.0 * pos.y()) / h;
}
void Window::GetFrame(Image8U3& image) const
{
image.create(GetSize());
glReadPixels(0, 0, image.width(), image.height(), GL_BGR_EXT, GL_UNSIGNED_BYTE, image.ptr());
cv::flip(image, image, 0);
}
cv::Size Window::GetSize() const
{
cv::Size _size;
glfwGetWindowSize(window, &_size.width, &_size.height);
return _size;
}
void Window::Resize(const cv::Size& _size)
{
// detect scaled window
sizeScale = (double)GetSize().width/_size.width;
size = _size;
// update resolution
glfwMakeContextCurrent(window);
glViewport(0, 0, size.width, size.height);
camera.Resize(cv::Size(ROUND2INT(size.width*sizeScale), ROUND2INT(size.height*sizeScale)));
}
void Window::Resize(GLFWwindow* window, int width, int height)
{
g_mapWindows[window]->Resize(cv::Size(width, height));
}
void Window::Key(int k, int /*scancode*/, int action, int mod)
{
switch (k) {
case GLFW_KEY_ESCAPE:
if (action == GLFW_RELEASE)
glfwSetWindowShouldClose(window, 1);
break;
case GLFW_KEY_DOWN:
if (action == GLFW_RELEASE) {
if (mod & GLFW_MOD_SHIFT) {
if (minViews > 2) {
minViews--;
if (clbkCompilePointCloud != NULL)
clbkCompilePointCloud();
}
} else {
pointSize = MAXF(pointSize-0.5f, 0.5f);
}
}
break;
case GLFW_KEY_UP:
if (action == GLFW_RELEASE) {
if (mod & GLFW_MOD_SHIFT) {
minViews++;
if (clbkCompilePointCloud != NULL)
clbkCompilePointCloud();
} else {
pointSize += 0.5f;
}
}
break;
case GLFW_KEY_LEFT:
if (action != GLFW_RELEASE) {
camera.prevCamID = camera.currentCamID;
camera.currentCamID--;
if (camera.currentCamID < NO_ID && camera.currentCamID >= camera.maxCamID)
camera.currentCamID = camera.maxCamID-1;
}
break;
case GLFW_KEY_RIGHT:
if (action != GLFW_RELEASE) {
camera.prevCamID = camera.currentCamID;
camera.currentCamID++;
if (camera.currentCamID >= camera.maxCamID)
camera.currentCamID = NO_ID;
}
break;
case GLFW_KEY_B:
if (action == GLFW_RELEASE) {
if (mod & GLFW_MOD_CONTROL) {
if (clbkCropToBounds != NULL)
clbkCropToBounds();
} else if (mod & GLFW_MOD_SHIFT) {
if (clbkTogleSceneBox != NULL)
clbkTogleSceneBox();
} else {
if (clbkCompileBounds != NULL)
clbkCompileBounds();
}
}
break;
case GLFW_KEY_C:
if (action == GLFW_RELEASE) {
if (mod & GLFW_MOD_SHIFT) {
bRenderCameraTrajectory = !bRenderCameraTrajectory;
} else if (mod & GLFW_MOD_CONTROL) {
if (clbkCenterScene != NULL)
clbkCenterScene();
} else {
bRenderCameras = !bRenderCameras;
}
}
break;
case GLFW_KEY_E:
if (action == GLFW_RELEASE && clbkExportScene != NULL)
clbkExportScene(NULL, NULL);
break;
case GLFW_KEY_P:
switch (sparseType) {
case SPR_POINTS: sparseType = SPR_LINES; break;
case SPR_LINES: sparseType = SPR_ALL; break;
case SPR_ALL: sparseType = SPR_POINTS; break;
}
if (clbkCompilePointCloud != NULL)
clbkCompilePointCloud();
break;
case GLFW_KEY_R:
if (action == GLFW_RELEASE)
Reset();
break;
case GLFW_KEY_S:
if (action == GLFW_RELEASE) {
if (clbkSaveScene != NULL)
clbkSaveScene(NULL, (mod & GLFW_MOD_SHIFT) != 0);
}
break;
case GLFW_KEY_T:
if (action == GLFW_RELEASE) {
bRenderTexture = !bRenderTexture;
if (clbkCompileMesh != NULL)
clbkCompileMesh();
}
break;
case GLFW_KEY_V:
if (action == GLFW_RELEASE) {
if (mod & GLFW_MOD_SHIFT) {
bRenderImageVisibility = !bRenderImageVisibility;
} else {
bRenderViews = !bRenderViews;
}
}
break;
case GLFW_KEY_W:
if (action == GLFW_RELEASE) {
if (bRenderSolid) {
bRenderSolid = false;
glPolygonMode(GL_FRONT, GL_LINE);
} else {
bRenderSolid = true;
glPolygonMode(GL_FRONT, GL_FILL);
}
}
break;
case GLFW_KEY_KP_SUBTRACT:
if (action == GLFW_RELEASE) {
if (mod & GLFW_MOD_CONTROL)
camera.SetFOV(camera.fov-5.f);
else if (mod & GLFW_MOD_SHIFT)
camera.scaleF *= 0.9f;
else
cameraBlend = MAXF(cameraBlend-0.1f, 0.f);
}
break;
case GLFW_KEY_KP_ADD:
if (action == GLFW_RELEASE) {
if (mod & GLFW_MOD_CONTROL)
camera.SetFOV(camera.fov+5.f);
else if (mod & GLFW_MOD_SHIFT)
camera.scaleF *= 1.1111f;
else
cameraBlend = MINF(cameraBlend+0.1f, 1.f);
}
break;
}
}
void Window::Key(GLFWwindow* window, int k, int scancode, int action, int mod)
{
g_mapWindows[window]->Key(k, scancode, action, mod);
}
void Window::MouseButton(int button, int action, int /*mods*/)
{
switch (button) {
case GLFW_MOUSE_BUTTON_LEFT: {
if (action == GLFW_PRESS) {
inputType.set(INP_MOUSE_LEFT);
} else
if (action == GLFW_RELEASE) {
inputType.unset(INP_MOUSE_LEFT);
glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_NORMAL);
}
if (clbkRayScene != NULL) {
typedef Eigen::Matrix<double,4,4,Eigen::ColMajor> Mat4;
Mat4 P, V;
glGetDoublev(GL_MODELVIEW_MATRIX, V.data());
glGetDoublev(GL_PROJECTION_MATRIX, P.data());
// 4d Homogeneous Clip Coordinates
const Eigen::Vector4d ray_clip(pos.x(), pos.y(), -1.0, 1.0);
// 4d Eye (Camera) Coordinates
Eigen::Vector4d ray_eye(P.inverse()*ray_clip);
ray_eye.z() = -1.0;
ray_eye.w() = 0.0;
// 4d World Coordinates
const Mat4 invV(V.inverse());
ASSERT(ISEQUAL(invV(3,3),1.0));
const Eigen::Vector3d start(invV.topRightCorner<3,1>());
const Eigen::Vector4d ray_wor(invV*ray_eye);
const Eigen::Vector3d dir(ray_wor.topRows<3>().normalized());
clbkRayScene(Ray3d(start, dir), action);
}
} break;
case GLFW_MOUSE_BUTTON_MIDDLE: {
if (action == GLFW_PRESS) {
inputType.set(INP_MOUSE_MIDDLE);
glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
} else
if (action == GLFW_RELEASE) {
inputType.unset(INP_MOUSE_MIDDLE);
glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_NORMAL);
}
} break;
case GLFW_MOUSE_BUTTON_RIGHT: {
if (action == GLFW_PRESS) {
inputType.set(INP_MOUSE_RIGHT);
} else
if (action == GLFW_RELEASE) {
inputType.unset(INP_MOUSE_RIGHT);
}
}
}
}
void Window::MouseButton(GLFWwindow* window, int button, int action, int mods)
{
g_mapWindows[window]->MouseButton(button, action, mods);
}
void Window::MouseMove(double xpos, double ypos)
{
UpdateMousePosition(xpos, ypos);
if (inputType.isSet(INP_MOUSE_LEFT)) {
glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
camera.Rotate(pos, prevPos);
} else
if (inputType.isSet(INP_MOUSE_MIDDLE)) {
camera.Translate(pos, prevPos);
}
}
void Window::MouseMove(GLFWwindow* window, double xpos, double ypos)
{
g_mapWindows[window]->MouseMove(xpos, ypos);
}
void Window::Scroll(double /*xoffset*/, double yoffset)
{
camera.dist *= (yoffset>0 ? POW(1.11,yoffset) : POW(0.9,-yoffset));
}
void Window::Scroll(GLFWwindow* window, double xoffset, double yoffset)
{
g_mapWindows[window]->Scroll(xoffset, yoffset);
}
void Window::Drop(int count, const char** paths)
{
if (clbkOpenScene && count > 0) {
SetVisible(false);
String fileName(paths[0]);
Util::ensureUnifySlash(fileName);
if (count > 1) {
String geometryFileName(paths[1]);
Util::ensureUnifySlash(geometryFileName);
clbkOpenScene(fileName, geometryFileName);
} else {
clbkOpenScene(fileName, NULL);
}
SetVisible(true);
}
}
void Window::Drop(GLFWwindow* window, int count, const char** paths)
{
g_mapWindows[window]->Drop(count, paths);
}
bool Window::IsShiftKeyPressed() const
{
return
glfwGetKey(window, GLFW_KEY_LEFT_SHIFT) == GLFW_PRESS ||
glfwGetKey(window, GLFW_KEY_RIGHT_SHIFT) == GLFW_PRESS;
}
bool Window::IsCtrlKeyPressed() const
{
return
glfwGetKey(window, GLFW_KEY_LEFT_CONTROL) == GLFW_PRESS ||
glfwGetKey(window, GLFW_KEY_RIGHT_CONTROL) == GLFW_PRESS;
}
bool Window::IsAltKeyPressed() const
{
return
glfwGetKey(window, GLFW_KEY_LEFT_ALT) == GLFW_PRESS ||
glfwGetKey(window, GLFW_KEY_RIGHT_ALT) == GLFW_PRESS;
}
/*----------------------------------------------------------------*/

167
apps/Viewer/Window.h

@ -0,0 +1,167 @@ @@ -0,0 +1,167 @@
/*
* Window.h
*
* Copyright (c) 2014-2015 SEACAVE
*
* Author(s):
*
* cDc <cdc.seacave@gmail.com>
*
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*
* Additional Terms:
*
* You are required to preserve legal notices and author attributions in
* that material or in the Appropriate Legal Notices displayed by works
* containing it.
*/
#ifndef _VIEWER_WINDOW_H_
#define _VIEWER_WINDOW_H_
// I N C L U D E S /////////////////////////////////////////////////
#include "Camera.h"
#include "Image.h"
// D E F I N E S ///////////////////////////////////////////////////
// S T R U C T S ///////////////////////////////////////////////////
namespace VIEWER {
class Window
{
public:
GLFWwindow* window; // window handle
Camera camera; // current camera
Eigen::Vector2d pos, prevPos; // current and previous mouse position (normalized)
Eigen::Matrix4d transform; // view matrix corresponding to the currently selected image
cv::Size size; // resolution in pixels, sometimes not equal to window resolution, ex. on Retina display
double sizeScale; // window/screen resolution scale
enum INPUT : unsigned {
INP_NA = 0,
INP_MOUSE_LEFT = (1 << 0),
INP_MOUSE_MIDDLE = (1 << 1),
INP_MOUSE_RIGHT = (1 << 2),
};
Flags inputType;
enum SPARSE {
SPR_NONE = 0,
SPR_POINTS = (1 << 0),
SPR_LINES = (1 << 1),
SPR_ALL = SPR_POINTS|SPR_LINES
};
SPARSE sparseType;
unsigned minViews;
float pointSize;
float cameraBlend;
bool bRenderCameras;
bool bRenderCameraTrajectory;
bool bRenderImageVisibility;
bool bRenderViews;
bool bRenderSolid;
bool bRenderTexture;
bool bRenderBounds;
enum SELECTION {
SEL_NA = 0,
SEL_POINT,
SEL_TRIANGLE
};
SELECTION selectionType;
Point3f selectionPoints[4];
double selectionTimeClick, selectionTime;
IDX selectionIdx;
typedef DELEGATE<bool (LPCTSTR, LPCTSTR)> ClbkOpenScene;
ClbkOpenScene clbkOpenScene;
typedef DELEGATE<bool (LPCTSTR, bool)> ClbkSaveScene;
ClbkSaveScene clbkSaveScene;
typedef DELEGATE<bool (LPCTSTR, LPCTSTR)> ClbkExportScene;
ClbkExportScene clbkExportScene;
typedef DELEGATE<void (void)> ClbkCenterScene;
ClbkCenterScene clbkCenterScene;
typedef DELEGATE<void (const Ray3&, int)> ClbkRayScene;
ClbkRayScene clbkRayScene;
typedef DELEGATE<void (void)> ClbkCompilePointCloud;
ClbkCompilePointCloud clbkCompilePointCloud;
typedef DELEGATE<void (void)> ClbkCompileMesh;
ClbkCompileMesh clbkCompileMesh;
typedef DELEGATE<void (void)> ClbkCompileBounds;
ClbkCompileBounds clbkCompileBounds;
typedef DELEGATE<void (void)> ClbkTogleSceneBox;
ClbkTogleSceneBox clbkTogleSceneBox;
typedef DELEGATE<void (void)> ClbkCropToBounds;
ClbkCropToBounds clbkCropToBounds;
typedef std::unordered_map<GLFWwindow*,Window*> WindowsMap;
static WindowsMap g_mapWindows;
public:
Window();
~Window();
void Release();
void ReleaseClbk();
inline bool IsValid() const { return window != NULL; }
inline GLFWwindow* GetWindow() { return window; }
bool Init(const cv::Size&, LPCTSTR name);
void SetCamera(const Camera&);
void SetName(LPCTSTR);
void SetVisible(bool);
bool IsVisible() const;
void Reset(SPARSE sparseType=SPR_ALL, unsigned minViews=2);
void CenterCamera(const Point3&);
void UpdateView(const ImageArr&, const MVS::ImageArr&);
void UpdateView(const Eigen::Matrix3d& R, const Eigen::Vector3d& t);
void UpdateMousePosition(double xpos, double ypos);
void GetFrame(Image8U3&) const;
cv::Size GetSize() const;
void Resize(const cv::Size&);
static void Resize(GLFWwindow* window, int width, int height);
void Key(int k, int scancode, int action, int mod);
static void Key(GLFWwindow* window, int k, int scancode, int action, int mod);
void MouseButton(int button, int action, int mods);
static void MouseButton(GLFWwindow* window, int button, int action, int mods);
void MouseMove(double xpos, double ypos);
static void MouseMove(GLFWwindow* window, double xpos, double ypos);
void Scroll(double xoffset, double yoffset);
static void Scroll(GLFWwindow* window, double xoffset, double yoffset);
void Drop(int count, const char** paths);
static void Drop(GLFWwindow* window, int count, const char** paths);
protected:
bool IsShiftKeyPressed() const;
bool IsCtrlKeyPressed() const;
bool IsAltKeyPressed() const;
};
/*----------------------------------------------------------------*/
} // namespace VIEWER
#endif // _VIEWER_WINDOW_H_

8
libs/CMakeLists.txt

@ -0,0 +1,8 @@ @@ -0,0 +1,8 @@
# Add libraries
ADD_SUBDIRECTORY(Common)
ADD_SUBDIRECTORY(Math)
ADD_SUBDIRECTORY(IO)
ADD_SUBDIRECTORY(MVS)
# Install
INSTALL(FILES "MVS.h" DESTINATION "${INSTALL_INCLUDE_DIR}")

121
libs/Common/AABB.h

@ -0,0 +1,121 @@ @@ -0,0 +1,121 @@
////////////////////////////////////////////////////////////////////
// AABB.h
//
// Copyright 2007 cDc@seacave
// Distributed under the Boost Software License, Version 1.0
// (See http://www.boost.org/LICENSE_1_0.txt)
#ifndef __SEACAVE_AABB_H__
#define __SEACAVE_AABB_H__
// I N C L U D E S /////////////////////////////////////////////////
// D E F I N E S ///////////////////////////////////////////////////
namespace SEACAVE {
// S T R U C T S ///////////////////////////////////////////////////
// Basic axis-aligned bounding-box class
template <typename TYPE, int DIMS>
class TAABB
{
STATIC_ASSERT(DIMS > 0 && DIMS <= 3);
public:
typedef TYPE Type;
typedef Eigen::Matrix<TYPE,DIMS,1> POINT;
typedef Eigen::Matrix<TYPE,DIMS,DIMS,Eigen::RowMajor> MATRIX;
enum { numChildren = (2<<(DIMS-1)) };
enum { numCorners = (DIMS==1 ? 2 : (DIMS==2 ? 4 : 8)) }; // 2^DIMS
enum { numScalar = (2*DIMS) };
POINT ptMin, ptMax; // box extreme points
//---------------------------------------
inline TAABB() {}
inline TAABB(bool);
inline TAABB(const POINT& _pt);
inline TAABB(const POINT& _ptMin, const POINT& _ptMax);
inline TAABB(const POINT& center, const TYPE& radius);
template <typename TPoint>
inline TAABB(const TPoint* pts, size_t n);
template <typename CTYPE>
inline TAABB(const TAABB<CTYPE, DIMS>&);
inline void Reset();
inline void Set(const POINT& _pt);
inline void Set(const POINT& _ptMin, const POINT& _ptMax);
inline void Set(const POINT& center, const TYPE& radius);
template <typename TPoint>
inline void Set(const TPoint* pts, size_t n);
inline bool IsEmpty() const;
inline TAABB& Enlarge(TYPE);
inline TAABB& EnlargePercent(TYPE);
void InsertFull(const POINT&);
void Insert(const POINT&);
void Insert(const TAABB&);
void BoundBy(const TAABB&);
inline void Translate(const POINT&);
inline void Transform(const MATRIX&);
inline POINT GetCenter() const;
inline void GetCenter(POINT&) const;
inline POINT GetSize() const;
inline void GetSize(POINT&) const;
inline void GetCorner(BYTE i, POINT&) const;
inline POINT GetCorner(BYTE i) const;
inline void GetCorners(POINT pts[numCorners]) const;
bool Intersects(const TAABB&) const;
bool IntersectsComplete(const TAABB&, TYPE) const;
bool IntersectsComplete(const TAABB&) const;
bool Intersects(const POINT&) const;
bool IntersectsComplete(const POINT&, TYPE) const;
bool IntersectsComplete(const POINT&) const;
unsigned SplitBy(TYPE, int, TAABB [2]) const;
unsigned SplitBy(const POINT&, TAABB [numChildren], unsigned&) const;
inline TYPE& operator [] (BYTE i) { ASSERT(i<numScalar); return ptMin.data()[i]; }
inline TYPE operator [] (BYTE i) const { ASSERT(i<numScalar); return ptMin.data()[i]; }
friend std::ostream& operator << (std::ostream& st, const TAABB& obb) {
st << obb.ptMin; st << std::endl;
st << obb.ptMax; st << std::endl;
return st;
}
friend std::istream& operator >> (std::istream& st, TAABB& obb) {
st >> obb.ptMin;
st >> obb.ptMax;
return st;
}
#ifdef _USE_BOOST
// implement BOOST serialization
template<class Archive>
void serialize(Archive& ar, const unsigned int /*version*/) {
ar & ptMin;
ar & ptMax;
}
#endif
}; // class TAABB
/*----------------------------------------------------------------*/
#include "AABB.inl"
/*----------------------------------------------------------------*/
} // namespace SEACAVE
#endif // __SEACAVE_AABB_H__

419
libs/Common/AABB.inl

@ -0,0 +1,419 @@ @@ -0,0 +1,419 @@
////////////////////////////////////////////////////////////////////
// AABB.inl
//
// Copyright 2007 cDc@seacave
// Distributed under the Boost Software License, Version 1.0
// (See http://www.boost.org/LICENSE_1_0.txt)
// D E F I N E S ///////////////////////////////////////////////////
// S T R U C T S ///////////////////////////////////////////////////
template <typename TYPE, int DIMS>
inline TAABB<TYPE,DIMS>::TAABB(bool)
:
ptMin(POINT::Constant(std::numeric_limits<TYPE>::max())),
ptMax(POINT::Constant(std::numeric_limits<TYPE>::lowest()))
{
}
template <typename TYPE, int DIMS>
inline TAABB<TYPE,DIMS>::TAABB(const POINT& _pt)
:
ptMin(_pt), ptMax(_pt)
{
}
template <typename TYPE, int DIMS>
inline TAABB<TYPE,DIMS>::TAABB(const POINT& _ptMin, const POINT& _ptMax)
:
ptMin(_ptMin), ptMax(_ptMax)
{
}
template <typename TYPE, int DIMS>
inline TAABB<TYPE,DIMS>::TAABB(const POINT& center, const TYPE& radius)
:
ptMin(center-POINT::Constant(radius)), ptMax(center+POINT::Constant(radius))
{
}
template <typename TYPE, int DIMS>
template <typename TPoint>
inline TAABB<TYPE,DIMS>::TAABB(const TPoint* pts, size_t n)
{
Set(pts, n);
} // constructor
template <typename TYPE, int DIMS>
template <typename CTYPE>
inline TAABB<TYPE,DIMS>::TAABB(const TAABB<CTYPE,DIMS>& rhs)
:
ptMin(rhs.ptMin.template cast<TYPE>()), ptMax(rhs.ptMax.template cast<TYPE>())
{
} // copy constructor
/*----------------------------------------------------------------*/
template <typename TYPE, int DIMS>
inline void TAABB<TYPE,DIMS>::Reset()
{
ptMin = POINT::Constant(std::numeric_limits<TYPE>::max());
ptMax = POINT::Constant(std::numeric_limits<TYPE>::lowest());
}
template <typename TYPE, int DIMS>
inline void TAABB<TYPE,DIMS>::Set(const POINT& _pt)
{
ptMin = ptMax = _pt;
}
template <typename TYPE, int DIMS>
inline void TAABB<TYPE,DIMS>::Set(const POINT& _ptMin, const POINT& _ptMax)
{
ptMin = _ptMin;
ptMax = _ptMax;
}
template <typename TYPE, int DIMS>
inline void TAABB<TYPE,DIMS>::Set(const POINT& center, const TYPE& radius)
{
ptMin = center-POINT::Constant(radius);
ptMax = center+POINT::Constant(radius);
}
template <typename TYPE, int DIMS>
template <typename TPoint>
inline void TAABB<TYPE,DIMS>::Set(const TPoint* pts, size_t n)
{
ASSERT(n > 0);
ptMin = ptMax = pts[0];
for (size_t i=1; i<n; ++i)
Insert(pts[i]);
} // Set
/*----------------------------------------------------------------*/
template <typename TYPE, int DIMS>
inline bool TAABB<TYPE,DIMS>::IsEmpty() const
{
for (int i=0; i<DIMS; ++i)
if (ptMin[i] >= ptMax[i])
return true;
return false;
} // IsEmpty
/*----------------------------------------------------------------*/
template <typename TYPE, int DIMS>
inline TAABB<TYPE,DIMS>& TAABB<TYPE,DIMS>::Enlarge(TYPE x)
{
ptMin.array() -= x;
ptMax.array() += x;
return *this;
}
template <typename TYPE, int DIMS>
inline TAABB<TYPE,DIMS>& TAABB<TYPE,DIMS>::EnlargePercent(TYPE x)
{
const POINT ptSizeDelta(GetSize() * (x - TYPE(1)) / TYPE(2));
ptMin -= ptSizeDelta;
ptMax += ptSizeDelta;
return *this;
} // Enlarge
/*----------------------------------------------------------------*/
template <typename TYPE, int DIMS>
inline typename TAABB<TYPE,DIMS>::POINT TAABB<TYPE,DIMS>::GetCenter() const
{
return (ptMax + ptMin) * TYPE(0.5);
}
template <typename TYPE, int DIMS>
inline void TAABB<TYPE,DIMS>::GetCenter(POINT& ptCenter) const
{
ptCenter = (ptMax + ptMin) * TYPE(0.5);
} // GetCenter
/*----------------------------------------------------------------*/
template <typename TYPE, int DIMS>
inline typename TAABB<TYPE,DIMS>::POINT TAABB<TYPE,DIMS>::GetSize() const
{
return ptMax - ptMin;
}
template <typename TYPE, int DIMS>
inline void TAABB<TYPE,DIMS>::GetSize(POINT& ptSize) const
{
ptSize = ptMax - ptMin;
} // GetSize
/*----------------------------------------------------------------*/
template <typename TYPE, int DIMS>
inline void TAABB<TYPE,DIMS>::GetCorner(BYTE i, POINT& ptCorner) const
{
ASSERT(i<numCorners);
if (DIMS == 1) {
ptCorner(0) = operator[](i);
}
if (DIMS == 2) {
ptCorner(0) = operator[]((i/2)*2 + 0);
ptCorner(1) = operator[]((i%2)*2 + 1);
}
if (DIMS == 3) {
ptCorner(0) = operator[]((i/4)*3 + 0);
ptCorner(1) = operator[](((i/2)%2)*3 + 1);
ptCorner(2) = operator[]((i%2)*3 + 2);
}
}
template <typename TYPE, int DIMS>
inline typename TAABB<TYPE,DIMS>::POINT TAABB<TYPE,DIMS>::GetCorner(BYTE i) const
{
POINT ptCorner;
GetCorner(i, ptCorner);
return ptCorner;
} // GetCorner
template <typename TYPE, int DIMS>
inline void TAABB<TYPE,DIMS>::GetCorners(POINT pts[numCorners]) const
{
for (BYTE i=0; i<numCorners; ++i)
GetCorner(i, pts[i]);
} // GetCorners
/*----------------------------------------------------------------*/
// Update the box by the given pos delta.
template <typename TYPE, int DIMS>
inline void TAABB<TYPE,DIMS>::Translate(const POINT& d)
{
ptMin += d;
ptMax += d;
}
/*----------------------------------------------------------------*/
// Update the box by the given transform.
template <typename TYPE, int DIMS>
inline void TAABB<TYPE,DIMS>::Transform(const MATRIX& m)
{
ptMin = m * ptMin;
ptMax = m * ptMax;
}
/*----------------------------------------------------------------*/
// Update the box such that it contains the given point.
template <typename TYPE, int DIMS>
void TAABB<TYPE,DIMS>::InsertFull(const POINT& pt)
{
if (ptMin[0] > pt[0])
ptMin[0] = pt[0];
if (ptMax[0] < pt[0])
ptMax[0] = pt[0];
if (DIMS > 1) {
if (ptMin[1] > pt[1])
ptMin[1] = pt[1];
if (ptMax[1] < pt[1])
ptMax[1] = pt[1];
}
if (DIMS > 2) {
if (ptMin[2] > pt[2])
ptMin[2] = pt[2];
if (ptMax[2] < pt[2])
ptMax[2] = pt[2];
}
}
// same as above, but for the initialized case
template <typename TYPE, int DIMS>
void TAABB<TYPE,DIMS>::Insert(const POINT& pt)
{
if (ptMin[0] > pt[0])
ptMin[0] = pt[0];
else if (ptMax[0] < pt[0])
ptMax[0] = pt[0];
if (DIMS > 1) {
if (ptMin[1] > pt[1])
ptMin[1] = pt[1];
else if (ptMax[1] < pt[1])
ptMax[1] = pt[1];
}
if (DIMS > 2) {
if (ptMin[2] > pt[2])
ptMin[2] = pt[2];
else if (ptMax[2] < pt[2])
ptMax[2] = pt[2];
}
}
/*----------------------------------------------------------------*/
// Update the box such that it contains the given bounding box.
template <typename TYPE, int DIMS>
void TAABB<TYPE,DIMS>::Insert(const TAABB& aabb)
{
if (ptMin[0] > aabb.ptMin[0])
ptMin[0] = aabb.ptMin[0];
if (ptMax[0] < aabb.ptMax[0])
ptMax[0] = aabb.ptMax[0];
if (DIMS > 1) {
if (ptMin[1] > aabb.ptMin[1])
ptMin[1] = aabb.ptMin[1];
if (ptMax[1] < aabb.ptMax[1])
ptMax[1] = aabb.ptMax[1];
}
if (DIMS > 2) {
if (ptMin[2] > aabb.ptMin[2])
ptMin[2] = aabb.ptMin[2];
if (ptMax[2] < aabb.ptMax[2])
ptMax[2] = aabb.ptMax[2];
}
}
/*----------------------------------------------------------------*/
// Update the box such that it does not exceed the given bounding box.
template <typename TYPE, int DIMS>
void TAABB<TYPE,DIMS>::BoundBy(const TAABB& aabb)
{
if (ptMin[0] < aabb.ptMin[0])
ptMin[0] = aabb.ptMin[0];
if (ptMax[0] > aabb.ptMax[0])
ptMax[0] = aabb.ptMax[0];
if (DIMS > 1) {
if (ptMin[1] < aabb.ptMin[1])
ptMin[1] = aabb.ptMin[1];
if (ptMax[1] > aabb.ptMax[1])
ptMax[1] = aabb.ptMax[1];
}
if (DIMS > 2) {
if (ptMin[2] < aabb.ptMin[2])
ptMin[2] = aabb.ptMin[2];
if (ptMax[2] > aabb.ptMax[2])
ptMax[2] = aabb.ptMax[2];
}
}
/*----------------------------------------------------------------*/
// intersection between two AABBs
template <typename TYPE, int DIMS>
bool TAABB<TYPE,DIMS>::Intersects(const TAABB& aabb) const
{
if (aabb.ptMax[0] < ptMin[0]) return false;
if (aabb.ptMin[0] >= ptMax[0]) return false;
if (DIMS > 1) {
if (aabb.ptMax[1] < ptMin[1]) return false;
if (aabb.ptMin[1] >= ptMax[1]) return false;
}
if (DIMS > 2) {
if (aabb.ptMax[2] < ptMin[2]) return false;
if (aabb.ptMin[2] >= ptMax[2]) return false;
}
return true;
}
template <typename TYPE, int DIMS>
bool TAABB<TYPE,DIMS>::IntersectsComplete(const TAABB& aabb, TYPE tolerance) const
{
if (aabb.ptMax[0]+tolerance < ptMin[0]) return false;
if (aabb.ptMin[0] > ptMax[0]+tolerance) return false;
if (DIMS > 1) {
if (aabb.ptMax[1]+tolerance < ptMin[1]) return false;
if (aabb.ptMin[1] > ptMax[1]+tolerance) return false;
}
if (DIMS > 2) {
if (aabb.ptMax[2]+tolerance < ptMin[2]) return false;
if (aabb.ptMin[2] > ptMax[2]+tolerance) return false;
}
return true;
}
template <typename TYPE, int DIMS>
bool TAABB<TYPE,DIMS>::IntersectsComplete(const TAABB& aabb) const
{
return IntersectsComplete(aabb, ZEROTOLERANCE<TYPE>());
} // Intersects(TAABB)
/*----------------------------------------------------------------*/
// does TAABB contain the given point
template <typename TYPE, int DIMS>
bool TAABB<TYPE,DIMS>::Intersects(const POINT& pt) const
{
if (pt[0] < ptMin[0]) return false;
if (pt[0] >= ptMax[0]) return false;
if (DIMS > 1) {
if (pt[1] < ptMin[1]) return false;
if (pt[1] >= ptMax[1]) return false;
}
if (DIMS > 2) {
if (pt[2] < ptMin[2]) return false;
if (pt[2] >= ptMax[2]) return false;
}
return true;
}
template <typename TYPE, int DIMS>
bool TAABB<TYPE,DIMS>::IntersectsComplete(const POINT& pt, TYPE tolerance) const
{
if (pt[0]+tolerance < ptMin[0]) return false;
if (pt[0] > ptMax[0]+tolerance) return false;
if (DIMS > 1) {
if (pt[1]+tolerance < ptMin[1]) return false;
if (pt[1] > ptMax[1]+tolerance) return false;
}
if (DIMS > 2) {
if (pt[2]+tolerance < ptMin[2]) return false;
if (pt[2] > ptMax[2]+tolerance) return false;
}
return true;
}
template <typename TYPE, int DIMS>
bool TAABB<TYPE,DIMS>::IntersectsComplete(const POINT& pt) const
{
return IntersectsComplete(pt, ZEROTOLERANCE<TYPE>());
} // Intersects(point)
/*----------------------------------------------------------------*/
// split this TAABB by the given axis
// returns number of children (0 or 2)
template <typename TYPE, int DIMS>
unsigned TAABB<TYPE,DIMS>::SplitBy(TYPE d, int a, TAABB child[2]) const
{
ASSERT(a < DIMS);
if (d >= ptMin[a] && d < ptMax[a]) {
TAABB& child0 = child[0];
child0.Set(ptMin, ptMax);
TAABB& child1 = child[1];
child1.Set(ptMin, ptMax);
child0.ptMax[a] = child1.ptMin[a] = d;
return 2;
}
return 0;
} // SplitBy(axis)
/*----------------------------------------------------------------*/
// split this TAABB by the given point
// returns number of children (0 - numScalar)
// idxFirst returns the index of the first aabb
template <typename TYPE, int DIMS>
unsigned TAABB<TYPE,DIMS>::SplitBy(const POINT& pt, TAABB child[numChildren], unsigned& idxFirst) const
{
idxFirst = 0;
unsigned size;
int a = 0;
do {
size = SplitBy(pt[a], a, child);
if (++a == DIMS)
return size;
} while (size == 0);
do {
unsigned n = 0;
for (unsigned b=0; b<size; ++b) {
n += child[(idxFirst+b)%numChildren].SplitBy(pt[a], a, child+((idxFirst+size+n)%numChildren));
if (n == 0)
goto MAIN_LOOP_END;
}
idxFirst += size;
size = n;
MAIN_LOOP_END:;
} while (++a < DIMS);
return size;
} // SplitBy(point)
/*----------------------------------------------------------------*/

668
libs/Common/AutoEstimator.h

@ -0,0 +1,668 @@ @@ -0,0 +1,668 @@
#ifndef __SEACAVE_AUTO_ESTIMATOR_H__
#define __SEACAVE_AUTO_ESTIMATOR_H__
//-------------------
// Generic implementation of ACRANSAC
//-------------------
// The A contrario parametrization have been first explained in [1] and
// later extended to generic model estimation in [2] (with a demonstration for
// the homography) and extended and use at large scale for Structure from
// Motion in [3].
//
//--
// [1] Lionel Moisan, Berenger Stival,
// A probalistic criterion to detect rigid point matches between
// two images and estimate the fundamental matrix.
// IJCV 04.
//--
// [2] Lionel Moisan, Pierre Moulon, Pascal Monasse.
// Automatic Homographic Registration of a Pair of Images,
// with A Contrario Elimination of Outliers
// Image Processing On Line (IPOL), 2012.
// http://dx.doi.org/10.5201/ipol.2012.mmm-oh
//--
// [3] Pierre Moulon, Pascal Monasse and Renaud Marlet.
// Adaptive Structure from Motion with a contrario mode estimation.
// In 11th Asian Conference on Computer Vision (ACCV 2012)
//--
// [4] cDc@seacave - rewrite it
//
// I N C L U D E S /////////////////////////////////////////////////
// D E F I N E S ///////////////////////////////////////////////////
// uncomment it to enable std::vector implementation
// (slower)
//#define ACRANSAC_STD_VECTOR
// uncomment it to enable sampling from inliers
// (sometimes does not find the right solution)
//#define ACRANSAC_SAMPLE_INLIERS
// P R O T O T Y P E S /////////////////////////////////////////////
namespace SEACAVE {
/// logarithm (base 10) of binomial coefficient
static inline double logcombi(size_t k, size_t n)
{
if (k>=n) return(0.0);
if (n-k<k) k=n-k;
double r = 0.0;
for (size_t i = 1; i <= k; i++)
r += log10((double)(n-i+1))-log10((double)i);
return r;
}
/// tabulate logcombi(.,n)
template<typename Type>
static inline void makelogcombi_n(size_t n, std::vector<Type>& l)
{
l.resize(n+1);
for (size_t k = 0; k <= n; ++k)
l[k] = static_cast<Type>(logcombi(k,n));
}
/// tabulate logcombi(k,.)
template<typename Type, int k>
static inline void makelogcombi_k(size_t nmax, std::vector<Type>& l)
{
l.resize(nmax+1);
for (size_t n = 0; n <= nmax; ++n)
l[n] = static_cast<Type>(logcombi(k,n));
}
/// Distance and associated index
struct ErrorIndex {
double first;
size_t second;
inline ErrorIndex() {}
inline ErrorIndex(double _first, size_t _second) : first(_first), second(_second) {}
inline bool operator==(const ErrorIndex& r) const { return (first == r.first); }
inline bool operator <(const ErrorIndex& r) const { return (first < r.first); }
};
#ifdef ACRANSAC_STD_VECTOR
typedef std::vector<ErrorIndex> ErrorIndexArr;
#else
typedef CLISTDEF0(ErrorIndex) ErrorIndexArr;
#endif
/// Find best NFA and its index wrt square error threshold in e.
template<size_t NSAMPLES>
static ErrorIndex bestNFA(
double logalpha0,
const ErrorIndexArr& e,
double loge0,
double maxThresholdSq,
const std::vector<float>& logc_n,
const std::vector<float>& logc_k,
double multError = 1.0)
{
ErrorIndex bestIndex(DBL_MAX, NSAMPLES);
const size_t n = e.size();
for (size_t k=NSAMPLES+1; k<=n && e[k-1].first<=maxThresholdSq; ++k) {
const double logalpha = logalpha0 + multError * log10(e[k-1].first+FLT_EPSILON);
const double NFA = loge0 +
logalpha * (double)(k-NSAMPLES) +
logc_n[k] +
logc_k[k];
if (NFA < bestIndex.first)
bestIndex = ErrorIndex(NFA, k);
}
return bestIndex;
}
/// Pick a random subset of the integers [0, total), in random order.
/// Note that this can behave badly if num_samples is close to total; runtime
/// could be unlimited!
///
/// This uses a quadratic rejection strategy and should only be used for small
/// num_samples.
///
/// \param NSAMPLES The number of samples to produce.
/// \param n The number of samples available.
/// \param samples num_samples of numbers in [0, total_samples) is placed
/// here on return.
struct UniformSampler {
template <size_t NSAMPLES>
inline void Sample(
size_t n,
size_t* samples)
{
ASSERT(NSAMPLES > 0);
size_t* const samplesBegin = samples;
size_t* const samplesEnd = samples + NSAMPLES;
*samples = size_t(RAND() % n);
while (++samples < samplesEnd) {
do {
*samples = size_t(RAND() % n);
} while (std::find(samplesBegin, samples, *samples) != samples);
}
}
};
/// Same as above, but ensure that the first point is always included
struct UniformSamplerLockFirst {
template <size_t NSAMPLES>
inline void Sample(
size_t n,
size_t* samples)
{
ASSERT(NSAMPLES > 1);
size_t* const samplesBegin = samples;
size_t* const samplesEnd = samples + NSAMPLES;
*samples++ = 0;
do {
do {
*samples = size_t(RAND() % n);
} while (std::find(samplesBegin, samples, *samples) != samples);
} while (++samples < samplesEnd);
}
};
/// Pick a random sample:
/// get a (sorted) random sample of size NSAMPLES in [0:n-1]
/// \param NSAMPLES The number of samples to produce.
/// \param vec_index The possible data indices.
/// \param n The number of samples available.
/// \param samples The random sample of NSAMPLES indices (output).
struct UniformSamplerSorted {
template <size_t NSAMPLES>
inline void Sample(
#ifdef ACRANSAC_SAMPLE_INLIERS
const std::vector<size_t>& vec_index,
#else
size_t n,
#endif
size_t* samples)
{
#ifdef ACRANSAC_SAMPLE_INLIERS
const size_t n = vec_index.size();
#endif
for (size_t i=0; i<NSAMPLES; ++i) {
size_t r = (RAND()>>3)%(n-i), j;
for (j=0; j<i && r>=samples[j]; ++j)
++r;
size_t j0 = j;
for (j=i; j>j0; --j)
samples[j] = samples[j-1];
samples[j0] = r;
}
#ifdef ACRANSAC_SAMPLE_INLIERS
for (size_t i=0; i<NSAMPLES; ++i)
samples[i] = vec_index[samples[i]];
#endif
}
};
/// Same as above, but ensure that the first point is always included
struct UniformSamplerSortedLockFirst {
template <size_t NSAMPLES>
inline void Sample(
size_t n,
size_t* samples)
{
ASSERT(NSAMPLES > 1);
samples[0] = 0;
for (size_t i=1; i<NSAMPLES; ++i) {
size_t r = (RAND()>>3)%(n-i), j;
for (j=0; j<i && r>=samples[j]; ++j)
++r;
size_t j0 = j;
for (j=i; j>j0; --j)
samples[j] = samples[j-1];
samples[j0] = r;
}
}
};
/// Returns the requested matrix rows
template <typename TMat, typename TRows>
TMat ExtractRows(const TMat &A, const TRows &rows) {
TMat compressed((int)rows.size(), A.cols);
for (size_t i=0; i<static_cast<size_t>(rows.size()); ++i)
A.row(rows[i]).copyTo(compressed.row((int)i));
return compressed;
}
/// ACRANSAC routine (ErrorThreshold, NFA)
template <typename Kernel, typename Sampler>
std::pair<double, double> ACRANSAC(
Kernel& kernel,
Sampler& sampler,
std::vector<size_t>& vec_inliers,
typename Kernel::Model& model,
double maxThreshold = DBL_MAX,
#ifndef ACRANSAC_SAMPLE_INLIERS
double confidence = 0.9999,
#endif
size_t nIter = 0)
{
const size_t nData = kernel.NumSamples();
if (nData < Kernel::MINIMUM_SAMPLES) {
vec_inliers.clear();
return std::make_pair(0.0,0.0);
}
const double maxThresholdSq = SQUARE(maxThreshold);
std::vector<size_t> vec_sample(Kernel::MINIMUM_SAMPLES); // sample indices
typename Kernel::Models vec_models; // up to max_models solutions
if (nData == Kernel::MINIMUM_SAMPLES) {
vec_inliers.resize(Kernel::MINIMUM_SAMPLES);
std::iota(vec_inliers.begin(), vec_inliers.end(), 0);
if (!kernel.Fit(vec_inliers, vec_models) || vec_models.size() != 1) {
vec_inliers.clear();
return std::make_pair(0.0,0.0);
}
model = vec_models[0];
return std::make_pair(maxThresholdSq,0.0);
}
#ifdef ACRANSAC_SAMPLE_INLIERS
// Possible sampling indices (could change in the optimization phase)
std::vector<size_t> vec_index(nData);
for (size_t i = 0; i < nData; ++i)
vec_index[i] = i;
#endif
// Precompute log combi
const double loge0 = log10((double)Kernel::MAX_MODELS * (nData-Kernel::MINIMUM_SAMPLES));
std::vector<float> vec_logc_n, vec_logc_k;
makelogcombi_n<float>(nData, vec_logc_n);
makelogcombi_k<float, Kernel::MINIMUM_SAMPLES>(nData, vec_logc_k);
// Output parameters
double minNFA = DBL_MAX;
double errorSqMax = DBL_MAX;
// Reserve 10% of iterations for focused sampling
if (nIter == 0)
nIter = MINF((size_t)4000, MAXF((size_t)300, nData*3/2));
const size_t nMinIter = nIter*8/100;
// Main estimation loop
ErrorIndexArr vec_residuals(nData); // [residual,index]
for (size_t iter=0; iter<nIter || iter<nMinIter; ++iter) {
// Get random sample
sampler.template Sample<Kernel::MINIMUM_SAMPLES>(
#ifdef ACRANSAC_SAMPLE_INLIERS
vec_index,
#else
nData,
#endif
&vec_sample[0]
);
if (!kernel.Fit(vec_sample, vec_models))
continue;
// Evaluate models
#ifdef ACRANSAC_SAMPLE_INLIERS
bool better = false;
#else
const size_t nTrialsRound = nIter;
#endif
for (typename Kernel::Models::const_iterator itModel=vec_models.cbegin(); itModel!=vec_models.cend(); ++itModel) {
// Residuals computation and ordering
kernel.EvaluateModel(*itModel);
for (size_t i = 0; i < nData; ++i)
vec_residuals[i] = ErrorIndex(kernel.Error(i), i);
std::sort(vec_residuals.begin(), vec_residuals.end());
// Most meaningful discrimination inliers/outliers
ErrorIndex best = bestNFA<Kernel::MINIMUM_SAMPLES>(
kernel.logalpha0(),
vec_residuals,
loge0,
maxThresholdSq,
vec_logc_n,
vec_logc_k,
kernel.multError());
if (best.first < minNFA /*&& vec_residuals[best.second-1].first < errorSqMax*/) {
// A better model was found
minNFA = best.first;
vec_inliers.resize(best.second);
for (size_t i=0; i<best.second; ++i)
vec_inliers[i] = vec_residuals[i].second;
errorSqMax = vec_residuals[best.second-1].first; // error threshold
model = *itModel;
DEBUG_LEVEL(4, "\titer=% 3u inliers=%u threshold=%g nfa=%g", iter, best.second, SQRT(errorSqMax), minNFA);
#ifdef ACRANSAC_SAMPLE_INLIERS
better = true;
#else
nIter = cvRANSACUpdateNumIters(confidence, (double)(nData - best.second)/nData, unsigned(Kernel::MINIMUM_SAMPLES), (unsigned)nTrialsRound);
#endif
}
}
vec_models.clear();
#ifdef ACRANSAC_SAMPLE_INLIERS
if (better && minNFA<0 && vec_inliers.size()>nData/3) {
// Optimization: draw samples among best set of inliers so far
vec_index = vec_inliers;
}
#endif
}
if (minNFA >= 0)
vec_inliers.clear();
return std::make_pair(errorSqMax, minNFA);
}
/*----------------------------------------------------------------*/
/// RANSAC routine (standard robust fitter, based on a known threshold)
template <typename Kernel, typename Sampler>
void RANSAC(
Kernel& kernel,
Sampler& sampler,
std::vector<size_t>& vec_inliers,
typename Kernel::Model& model,
double threshold,
#ifndef ACRANSAC_SAMPLE_INLIERS
double confidence = 0.9999,
#endif
size_t nIter = 0)
{
vec_inliers.clear();
const size_t nData = kernel.NumSamples();
if (nData < Kernel::MINIMUM_SAMPLES)
return;
const double thresholdSq = SQUARE(threshold);
std::vector<size_t> vec_sample(Kernel::MINIMUM_SAMPLES); // sample indices
typename Kernel::Models vec_models; // up to max_models solutions
#ifdef ACRANSAC_SAMPLE_INLIERS
// Possible sampling indices (could change in the optimization phase)
std::vector<size_t> vec_index(nData);
for (size_t i = 0; i < nData; ++i)
vec_index[i] = i;
#endif
// Reserve 10% of iterations for focused sampling
if (nIter == 0)
nIter = MINF((size_t)4000, MAXF((size_t)300, nData*3/2));
const size_t nMinIter = nIter*8/100;
// Main estimation loop
#ifdef _USE_MSAC
double best_score = thresholdSq*nData;
#endif
std::vector<size_t> vec_inliers_tmp; vec_inliers_tmp.reserve(nData); vec_inliers.reserve(nData);
for (size_t iter=0; iter<nIter || iter<nMinIter; ++iter) {
// Get random sample
sampler.template Sample<Kernel::MINIMUM_SAMPLES>(
#ifdef ACRANSAC_SAMPLE_INLIERS
vec_index,
#else
nData,
#endif
&vec_sample[0]
);
if (!kernel.Fit(vec_sample, vec_models))
continue;
// Evaluate models
#ifdef ACRANSAC_SAMPLE_INLIERS
bool better = false;
#else
const size_t nTrialsRound = nIter;
#endif
for (typename Kernel::Models::const_iterator itModel=vec_models.cbegin(); itModel!=vec_models.cend(); ++itModel) {
// Residuals computation and ordering
#ifdef _USE_MSAC
double score = 0;
#endif
vec_inliers_tmp.clear();
kernel.EvaluateModel(*itModel);
for (size_t i = 0; i < nData; ++i) {
const double errSq = kernel.Error(i);
if (errSq <= thresholdSq) {
#ifdef _USE_MSAC
score += errSq;
#endif
vec_inliers_tmp.push_back(i);
} else {
#ifdef _USE_MSAC
score += thresholdSq;
#endif
}
#ifdef _USE_MSAC
if (score >= best_score)
break;
#endif
}
// Store best model
#ifdef _USE_MSAC
if (score < best_score) {
#else
if (vec_inliers_tmp.size() > vec_inliers.size()) {
#endif
// A better model was found
model = *itModel;
vec_inliers.swap(vec_inliers_tmp);
if (vec_inliers.size() == nData) {
nIter = 0; // force loop exit
break;
}
#ifdef _USE_MSAC
best_score = score;
DEBUG_LEVEL(4, "\titer=%3u inliers=%u score=%g", iter, vec_inliers.size(), best_score);
#else
DEBUG_LEVEL(4, "\titer=%3u inliers=%u", iter, vec_inliers.size());
#endif
#ifdef ACRANSAC_SAMPLE_INLIERS
better = true;
#else
#ifdef _USE_MSAC
nIter = cvRANSACUpdateNumIters(confidence, (double)score/((double)nData*thresholdSq), unsigned(Kernel::MINIMUM_SAMPLES), (unsigned)nTrialsRound);
#else
nIter = cvRANSACUpdateNumIters(confidence, (double)(nData-vec_inliers.size())/nData, unsigned(Kernel::MINIMUM_SAMPLES), (unsigned)nTrialsRound);
#endif
#endif
}
}
vec_models.clear();
#ifdef ACRANSAC_SAMPLE_INLIERS
if (better && vec_inliers.size()>nData/3) {
// Optimization: draw samples among best set of inliers so far
vec_index = vec_inliers;
}
#endif
}
if (vec_inliers.size() < Kernel::MINIMUM_SAMPLES)
vec_inliers.clear();
}
/*----------------------------------------------------------------*/
/// Two view Kernel adaptator for the A contrario model estimator
/// Handle data normalization and compute the corresponding logalpha 0
/// that depends of the error model (point to line, or point to point)
/// This kernel adaptor is working for affine, homography, fundamental matrix
/// estimation.
template <typename SOLVER>
class ACKernelAdaptor
{
public:
typedef SOLVER Solver;
typedef typename SOLVER::Model Model;
typedef typename SOLVER::Models Models;
enum { MINIMUM_SAMPLES = Solver::MINIMUM_SAMPLES };
enum { MAX_MODELS = Solver::MAX_MODELS };
ACKernelAdaptor(
const DMatrix32F &x1, float w1, float h1,
const DMatrix32F &x2, float w2, float h2,
bool bPointToLine = true)
: x1_(x1), x2_(x2),
bPointToLine_(bPointToLine)
{
ASSERT(2 == x1_.cols);
ASSERT(x1_.rows == x2_.rows);
ASSERT(x1_.cols == x2_.cols);
ASSERT(NumSamples() >= MINIMUM_SAMPLES);
// LogAlpha0 is used to make error data scale invariant
const float s2 = 1.0f;
if (bPointToLine) {
// Ratio of containing diagonal image rectangle over image area
const float D = sqrt(w2*w2 + h2*h2); // diameter
const float A = w2*h2; // area
logalpha0_ = log10(2.0f*D/A/s2);
}
else {
// ratio of area : unit circle over image area
logalpha0_ = log10(M_PI/(w2*h2)/(s2*s2));
}
}
inline bool Fit(const std::vector<size_t>& samples, Models& models) const {
const DMatrix32F x1(ExtractRows(x1_, samples));
if (CheckCollinearity(x1.ptr<const Point2f>(), x1.rows))
return false;
const DMatrix32F x2(ExtractRows(x2_, samples));
if (CheckCollinearity(x2.ptr<const Point2f>(), x2.rows))
return false;
Solver::Solve(x1, x2, models);
return true;
}
inline void EvaluateModel(const Model& model) {
model2evaluate = model;
}
inline double Error(size_t sample) const {
return Solver::Error(model2evaluate, *x1_.ptr<const Point2f>((int)sample), *x2_.ptr<const Point2f>((int)sample));
}
inline size_t NumSamples() const { return static_cast<size_t>(x1_.rows); }
inline double logalpha0() const { return logalpha0_; }
inline double multError() const { return (bPointToLine_ ? 0.5 : 1.0); }
protected:
DMatrix32F x1_, x2_; // Normalized input data
double logalpha0_; // Alpha0 is used to make the error adaptive to the image size
bool bPointToLine_;// Store if error model is pointToLine or point to point
Model model2evaluate; // current model to be evaluated
};
/// Pose/Resection Kernel adaptator for the A contrario model estimator
template <typename SOLVER>
class ACKernelAdaptorResection
{
public:
typedef SOLVER Solver;
typedef typename SOLVER::Model Model;
typedef typename SOLVER::Models Models;
enum { MINIMUM_SAMPLES = Solver::MINIMUM_SAMPLES };
enum { MAX_MODELS = Solver::MAX_MODELS };
ACKernelAdaptorResection(const DMatrix32F &x2d, const DMatrix &x3D)
: x2d_(x2d), x3D_(x3D),
logalpha0_(log10(M_PI))
{
ASSERT(2 == x2d_.cols);
ASSERT(3 == x3D_.cols);
ASSERT(x2d_.rows == x3D_.rows);
ASSERT(NumSamples() >= MINIMUM_SAMPLES);
}
inline bool Fit(const std::vector<size_t>& samples, Models& models) const {
const DMatrix32F x1(ExtractRows(x2d_, samples));
if (CheckCollinearity(x1.ptr<const Point2f>(), x1.rows))
return false;
const DMatrix x2(ExtractRows(x3D_, samples));
Solver::Solve(x1, x2, models);
return true;
}
inline void EvaluateModel(const Model &model) {
model2evaluate = model;
}
inline double Error(size_t sample) const {
return Solver::Error(model2evaluate, *x2d_.ptr<const Point2f>((int)sample), *x3D_.ptr<const Point3>((int)sample));
}
inline size_t NumSamples() const { return x2d_.rows; }
inline double logalpha0() const { return logalpha0_; }
inline double multError() const { return 1.0; } // point to point error
protected:
DMatrix32F x2d_;
DMatrix x3D_;
double logalpha0_; // Alpha0 is used to make the error adaptive to the image size
Model model2evaluate; // current model to be evaluated
};
/// Essential matrix Kernel adaptator for the A contrario model estimator
template <typename SOLVER>
class ACKernelAdaptorEssential
{
public:
typedef SOLVER Solver;
typedef typename SOLVER::Model Model;
typedef typename SOLVER::Models Models;
enum { MINIMUM_SAMPLES = Solver::MINIMUM_SAMPLES };
enum { MAX_MODELS = Solver::MAX_MODELS };
ACKernelAdaptorEssential(
const DMatrix32F &x1, float w1, float h1, const Matrix3x3f& invK1,
const DMatrix32F &x2, float w2, float h2, const Matrix3x3f& invK2)
: solver(invK1, invK2),
x1_(x1), x2_(x2)
{
ASSERT(2 == x1_.cols);
ASSERT(x1_.rows == x2_.rows);
ASSERT(x1_.cols == x2_.cols);
ASSERT(NumSamples() >= MINIMUM_SAMPLES);
//Point to line probability (line is the epipolar line)
const float D = sqrt(w2*w2 + h2*h2); // diameter
const float A = w2*h2+1.f; // area
logalpha0_ = log10(2.0f*D/A*0.5f);
}
inline bool Fit(const std::vector<size_t>& samples, Models& models) const {
const DMatrix32F x1(ExtractRows(x1_, samples));
if (CheckCollinearity(x1.ptr<const Point2f>(), x1.rows))
return false;
const DMatrix32F x2(ExtractRows(x2_, samples));
if (CheckCollinearity(x2.ptr<const Point2f>(), x2.rows))
return false;
solver.Solve(x1, x2, models);
return true;
}
inline void EvaluateModel(const Model &model) {
solver.EvaluateModel(model);
}
inline double Error(size_t sample) const {
return solver.Error(*x1_.ptr<const Point2f>((int)sample), *x2_.ptr<const Point2f>((int)sample));
}
inline size_t NumSamples() const { return x1_.rows; }
inline double logalpha0() const { return logalpha0_; }
inline double multError() const { return 0.5; } // point to line error
protected:
Solver solver;
DMatrix32F x1_, x2_; // image point and camera plane point.
double logalpha0_; // Alpha0 is used to make the error adaptive to the image size
};
/*----------------------------------------------------------------*/
} // namespace SEACAVE
#endif // __SEACAVE_AUTO_ESTIMATOR_H__

275
libs/Common/AutoPtr.h

@ -0,0 +1,275 @@ @@ -0,0 +1,275 @@
////////////////////////////////////////////////////////////////////
// AutoPtr.h
//
// Copyright 2007 cDc@seacave
// Distributed under the Boost Software License, Version 1.0
// (See http://www.boost.org/LICENSE_1_0.txt)
#ifndef __SEACAVE_AUTOPTR_H__
#define __SEACAVE_AUTOPTR_H__
// I N C L U D E S /////////////////////////////////////////////////
// D E F I N E S ///////////////////////////////////////////////////
namespace SEACAVE {
// S T R U C T S ///////////////////////////////////////////////////
/**************************************************************************************
* CAutoPtr template
* ---------------
* simple smart pointer
**************************************************************************************/
struct AutoPtrMoveCopy {
template <typename TYPE>
static inline void Copy(TYPE*& ptrLeft, TYPE*& ptrRight) {
ptrLeft = ptrRight;
ptrRight = NULL;
}
};
struct AutoPtrDeepCopy {
template <typename TYPE>
static inline void Copy(TYPE*& ptrLeft, TYPE*& ptrRight) {
ptrLeft = (ptrRight != NULL ? new TYPE(*ptrRight) : (TYPE*)NULL);
}
};
template<typename TYPE, typename CopyPolicy=AutoPtrMoveCopy>
class CAutoPtr
{
private:
typedef TYPE Type;
typedef TYPE* TypePtr;
public:
inline CAutoPtr() : m_pointer(NULL)
{ // construct with NULL pointer
}
inline explicit CAutoPtr(TypePtr _Ptr) : m_pointer(_Ptr)
{ // construct from object pointer
}
inline CAutoPtr(CAutoPtr& _Right)
{ // copy-construct by assuming pointer from _Right CAutoPtr
CopyPolicy::Copy(m_pointer, _Right.m_pointer);
}
inline ~CAutoPtr()
{ // destroy the object
delete m_pointer;
}
void Swap(CAutoPtr& _Right)
{ // swap compatible _Right (assume pointer)
const TypePtr tmp(m_pointer);
m_pointer = _Right.m_pointer;
_Right.m_pointer = tmp;
}
CAutoPtr& operator=(CAutoPtr& _Right)
{ // assign compatible _Right (assume pointer)
if (this != &_Right) {
delete m_pointer;
CopyPolicy::Copy(m_pointer, _Right.m_pointer);
}
return (*this);
}
CAutoPtr& operator=(TypePtr _Ptr)
{ // assign compatible _Right (assume pointer)
if (m_pointer != _Ptr) {
delete m_pointer;
m_pointer = _Ptr;
}
return (*this);
}
inline Type& operator*() const
{ // return designated value
ASSERT(m_pointer);
return (*m_pointer);
}
inline Type* operator->() const
{ // return pointer to class object
ASSERT(m_pointer);
return m_pointer;
}
inline operator TypePtr() const
{ // return pointer to class object
return m_pointer;
}
inline operator TypePtr&()
{ // return reference to class object
return m_pointer;
}
inline bool operator==(const TypePtr _Right) const
{ // return pointer to class object
return (m_pointer == _Right);
}
inline bool operator!=(const TypePtr _Right) const
{ // return pointer to class object
return (m_pointer != _Right);
}
inline void Release()
{ // release pointer
delete m_pointer;
m_pointer = NULL;
}
inline void Reset(TypePtr _Ptr = NULL)
{ // reset pointer
m_pointer = _Ptr;
}
protected:
TypePtr m_pointer; // the wrapped object pointer
#ifdef _USE_BOOST
// implement BOOST serialization
friend class boost::serialization::access;
template<class Archive>
void save(Archive& ar, const unsigned int /*version*/) const
{
ar & m_pointer;
}
template<class Archive>
void load(Archive& ar, const unsigned int /*version*/)
{
TypePtr newPointer;
ar & newPointer;
operator=(newPointer);
}
BOOST_SERIALIZATION_SPLIT_MEMBER()
#endif
};
template<class TYPE>
class CAutoPtrArr
{
private:
typedef TYPE Type;
typedef TYPE* TypePtr;
public:
inline CAutoPtrArr() : m_pointer(NULL)
{ // construct with NULL pointer
}
inline explicit CAutoPtrArr(TypePtr _Ptr) : m_pointer(_Ptr)
{ // construct from object pointer
}
inline CAutoPtrArr(CAutoPtrArr& _Right) : m_pointer(_Right.m_pointer)
{ // construct by assuming pointer from _Right CAutoPtrArr
_Right.m_pointer = NULL;
}
inline ~CAutoPtrArr()
{ // destroy the object
delete[] m_pointer;
}
CAutoPtrArr& operator=(CAutoPtrArr& _Right)
{ // assign compatible _Right (assume pointer)
if (this != &_Right)
{
delete[] m_pointer;
m_pointer = _Right.m_pointer;
_Right.m_pointer = NULL;
}
return (*this);
}
CAutoPtrArr& operator=(TypePtr _Ptr)
{ // assign compatible _Right (assume pointer)
if (m_pointer != _Ptr)
{
delete[] m_pointer;
m_pointer = _Ptr;
}
return (*this);
}
inline Type& operator*() const
{ // return designated value
ASSERT(m_pointer);
return (*m_pointer);
}
inline Type* operator->() const
{ // return pointer to class object
ASSERT(m_pointer);
return m_pointer;
}
inline operator TypePtr() const
{ // return pointer to class object
return m_pointer;
}
inline operator TypePtr&()
{ // return reference to class object
return m_pointer;
}
inline bool operator==(const TypePtr _Right) const
{ // return pointer to class object
return (m_pointer == _Right);
}
inline bool operator!=(const TypePtr _Right) const
{ // return pointer to class object
return (m_pointer != _Right);
}
inline void Release()
{ // release pointer
delete[] m_pointer;
m_pointer = NULL;
}
inline void Reset(TypePtr _Ptr = NULL)
{ // reset pointer
m_pointer = _Ptr;
}
protected:
TypePtr m_pointer; // the wrapped object pointer
#ifdef _USE_BOOST
// implement BOOST serialization
friend class boost::serialization::access;
template<class Archive>
void save(Archive& ar, const unsigned int /*version*/) const
{
ar & m_pointer;
}
template<class Archive>
void load(Archive& ar, const unsigned int /*version*/)
{
TypePtr newPointer;
ar & newPointer;
operator=(newPointer);
}
BOOST_SERIALIZATION_SPLIT_MEMBER()
#endif
};
/*----------------------------------------------------------------*/
} // namespace SEACAVE
#endif // __SEACAVE_AUTOPTR_H__

25
libs/Common/CMakeLists.txt

@ -0,0 +1,25 @@ @@ -0,0 +1,25 @@
# List sources files
FILE(GLOB LIBRARY_FILES_C "*.cpp")
FILE(GLOB LIBRARY_FILES_H "*.h" "*.inl")
cxx_library_with_type(Common "Libs" "" "${cxx_default}"
${LIBRARY_FILES_C} ${LIBRARY_FILES_H}
)
# Manually set Common.h as the precompiled header
IF(CMAKE_VERSION VERSION_GREATER_EQUAL 3.16.0)
TARGET_PRECOMPILE_HEADERS(Common PRIVATE "Common.h")
endif()
# Link its dependencies
TARGET_LINK_LIBRARIES(Common ${Boost_LIBRARIES} ${OpenCV_LIBS})
# Install
SET_TARGET_PROPERTIES(Common PROPERTIES
PUBLIC_HEADER "${LIBRARY_FILES_H}")
INSTALL(TARGETS Common
EXPORT OpenMVSTargets
LIBRARY DESTINATION "${INSTALL_LIB_DIR}"
ARCHIVE DESTINATION "${INSTALL_LIB_DIR}"
RUNTIME DESTINATION "${INSTALL_BIN_DIR}"
PUBLIC_HEADER DESTINATION "${INSTALL_INCLUDE_DIR}/Common")

47
libs/Common/Common.cpp

@ -0,0 +1,47 @@ @@ -0,0 +1,47 @@
////////////////////////////////////////////////////////////////////
// Common.cpp
//
// Copyright 2007 cDc@seacave
// Distributed under the Boost Software License, Version 1.0
// (See http://www.boost.org/LICENSE_1_0.txt)
// Source file that includes just the standard includes
// Common.pch will be the pre-compiled header
// Common.obj will contain the pre-compiled type information
#include "Common.h"
namespace SEACAVE {
#if TD_VERBOSE == TD_VERBOSE_ON
int g_nVerbosityLevel(2);
#endif
#if TD_VERBOSE == TD_VERBOSE_DEBUG
int g_nVerbosityLevel(3);
#endif
String g_strWorkingFolder;
String g_strWorkingFolderFull;
} // namespace SEACAVE
#ifdef _USE_BOOST
#ifdef BOOST_NO_EXCEPTIONS
#if (BOOST_VERSION / 100000) > 1 || (BOOST_VERSION / 100 % 1000) > 72
#include <boost/assert/source_location.hpp>
#endif
namespace boost {
void throw_exception(std::exception const & e) {
VERBOSE("exception thrown: %s", e.what());
ASSERT("boost exception thrown" == NULL);
exit(EXIT_FAILURE);
}
#if (BOOST_VERSION / 100000) > 1 || (BOOST_VERSION / 100 % 1000) > 72
void throw_exception(std::exception const & e, boost::source_location const & loc) {
std::ostringstream ostr; ostr << loc;
VERBOSE("exception thrown at %s: %s", ostr.str().c_str(), e.what());
ASSERT("boost exception thrown" == NULL);
exit(EXIT_FAILURE);
}
#endif
} // namespace boost
#endif
#endif

308
libs/Common/Common.h

@ -0,0 +1,308 @@ @@ -0,0 +1,308 @@
////////////////////////////////////////////////////////////////////
// Common.h
//
// Copyright 2007 cDc@seacave
// Distributed under the Boost Software License, Version 1.0
// (See http://www.boost.org/LICENSE_1_0.txt)
#ifndef _COMMON_COMMON_H_
#define _COMMON_COMMON_H_
// D E F I N E S ///////////////////////////////////////////////////
#include "Config.h"
// macros controlling the verbosity
#define TD_VERBOSE_OFF 0
#define TD_VERBOSE_ON 1
#define TD_VERBOSE_DEBUG 2
#ifndef TD_VERBOSE
#ifdef _RELEASE
#define TD_VERBOSE TD_VERBOSE_ON
#else
#define TD_VERBOSE TD_VERBOSE_DEBUG
#endif
#endif
#if TD_VERBOSE == TD_VERBOSE_OFF
#define VERBOSE LOG
#define DEBUG_LEVEL(n,...)
#else
#ifndef VERBOSITY_LEVEL
namespace SEACAVE { extern int g_nVerbosityLevel; }
#define VERBOSITY_LEVEL g_nVerbosityLevel
#endif
#define VERBOSE LOG
#define DEBUG_LEVEL(n,...) { if (n < VERBOSITY_LEVEL) VERBOSE(__VA_ARGS__); }
#endif
#define DEBUG(...) DEBUG_LEVEL(0, __VA_ARGS__)
#define DEBUG_EXTRA(...) DEBUG_LEVEL(1, __VA_ARGS__)
#define DEBUG_ULTIMATE(...) DEBUG_LEVEL(2, __VA_ARGS__)
// macros that simplify timing tasks
#define TD_TIMER_OFF 0
#define TD_TIMER_ON 1
#ifndef TD_TIMER
#define TD_TIMER TD_TIMER_ON
#endif
#if TD_TIMER == TD_TIMER_OFF
#define TD_TIMER_START()
#define TD_TIMER_UPDATE()
#define TD_TIMER_GET() 0
#define TD_TIMER_GET_INT() 0
#define TD_TIMER_GET_FMT() String()
#define TD_TIMER_STARTD()
#define TD_TIMER_UPDATED()
#endif
#if TD_TIMER == TD_TIMER_ON
#define TD_TIMER_START() TIMER_START()
#define TD_TIMER_UPDATE() TIMER_UPDATE()
#define TD_TIMER_GET() TIMER_GET()
#define TD_TIMER_GET_INT() TIMER_GET_INT()
#define TD_TIMER_GET_FMT() TIMER_GET_FORMAT()
#if TD_VERBOSE == TD_VERBOSE_OFF
#define TD_TIMER_STARTD()
#define TD_TIMER_UPDATED()
#else
#define TD_TIMER_STARTD() TIMER_START()
#define TD_TIMER_UPDATED() TIMER_UPDATE()
#endif
#endif
// macros redirecting standard streams to the log
#define LOG_OUT() GET_LOG() //or std::cout
#define LOG_ERR() GET_LOG() //or std::cerr
// macros simplifying the task of composing file paths;
// WORKING_FOLDER and WORKING_FOLDER_FULL must be defined as strings
// containing the relative/full path to the working folder
#ifndef WORKING_FOLDER
namespace SEACAVE {
class String;
extern String g_strWorkingFolder; // empty by default (current folder)
extern String g_strWorkingFolderFull; // full path to current folder
}
#define WORKING_FOLDER g_strWorkingFolder // empty by default (current folder)
#define WORKING_FOLDER_FULL g_strWorkingFolderFull // full path to current folder
#endif
#define INIT_WORKING_FOLDER {SEACAVE::Util::ensureValidFolderPath(WORKING_FOLDER); WORKING_FOLDER_FULL = SEACAVE::Util::getFullPath(WORKING_FOLDER);} // initialize working folders
#define MAKE_PATH(str) SEACAVE::Util::getSimplifiedPath(WORKING_FOLDER+SEACAVE::String(str)) // add working directory to the given file name
#define MAKE_PATH_SAFE(str) (SEACAVE::Util::isFullPath((str).c_str()) ? SEACAVE::String(str) : MAKE_PATH(str)) // add working directory to the given file name only if not full path already
#define MAKE_PATH_FULL(p,s) (SEACAVE::Util::isFullPath((s).c_str()) ? SEACAVE::String(s) : SEACAVE::Util::getSimplifiedPath((p)+(s))) // add the given path to the given file name
#define MAKE_PATH_REL(p,s) SEACAVE::Util::getRelativePath(p,s) // remove the given path from the given file name
#define GET_PATH_FULL(str) (SEACAVE::Util::isFullPath((str).c_str()) ? SEACAVE::Util::getFilePath(str) : SEACAVE::Util::getSimplifiedPath(WORKING_FOLDER_FULL+SEACAVE::Util::getFilePath(str))) // retrieve the full path to the given file
// macros simplifying the task of managing options
#define DECOPT_SPACE(SPACE) namespace SPACE { \
void init(); \
void update(); \
extern SEACAVE::VoidArr arrFncOpt; \
extern SEACAVE::CConfigTable oConfig; \
}
#define DEFOPT_SPACE(SPACE, name) namespace SPACE { \
SEACAVE::CConfigTable oConfig(name); \
typedef LPCTSTR (*FNCINDEX)(); \
typedef void (*FNCINIT)(SEACAVE::IDX); \
typedef void (*FNCUPDATE)(); \
VoidArr arrFncOpt; \
void init() { \
FOREACH(i, arrFncOpt) \
((FNCINIT)arrFncOpt[i])(i); \
} \
void update() { \
FOREACH(i, arrFncOpt) \
((FNCUPDATE)arrFncOpt[i])(); \
} \
}
#define DEFVAR_OPTION(SPACE, flags, type, name, title, desc, ...) namespace SPACE { \
type name; \
LPCTSTR defval_##name(NULL); \
void update_##name() { \
SEACAVE::String::FromString(oConfig[title].val, name); \
} \
void init_##name(SEACAVE::IDX idx) { \
LPCTSTR const vals[] = {__VA_ARGS__}; \
arrFncOpt[idx] = (void*)update_##name; \
SMLVALUE& val = oConfig[title]; \
CFGITEM& opt = *((CFGITEM*)val.data); \
opt.state = flags; \
val.val = opt.defval = (defval_##name != NULL ? defval_##name : vals[0]); \
opt.vals.Insert(opt.defval); \
for (size_t i=1; i<sizeof(vals)/sizeof(LPCTSTR); ++i) \
opt.vals.Insert(vals[i]); \
SEACAVE::String::FromString(opt.defval, name); \
} \
LPCTSTR index_##name() { \
arrFncOpt.Insert((void*)init_##name); \
return title; \
} \
LPCTSTR const name_##name(index_##name()); \
}
#define FDEFVAR_string(SPACE, flags, name, title, desc, ...) DEFVAR_OPTION(SPACE, flags, String, name, title, desc, __VA_ARGS__)
#define FDEFVAR_bool(SPACE, flags, name, title, desc, ...) DEFVAR_OPTION(SPACE, flags, bool, name, title, desc, __VA_ARGS__)
#define FDEFVAR_int32(SPACE, flags, name, title, desc, ...) DEFVAR_OPTION(SPACE, flags, int32_t, name, title, desc, __VA_ARGS__)
#define FDEFVAR_uint32(SPACE, flags, name, title, desc, ...) DEFVAR_OPTION(SPACE, flags, uint32_t, name, title, desc, __VA_ARGS__)
#define FDEFVAR_flags(SPACE, flags, name, title, desc, ...) DEFVAR_OPTION(SPACE, flags, Flags, name, title, desc, __VA_ARGS__)
#define FDEFVAR_float(SPACE, flags, name, title, desc, ...) DEFVAR_OPTION(SPACE, flags, float, name, title, desc, __VA_ARGS__)
#define FDEFVAR_double(SPACE, flags, name, title, desc, ...) DEFVAR_OPTION(SPACE, flags, double, name, title, desc, __VA_ARGS__)
#define DEFVAR_string(SPACE, name, title, desc, ...) DEFVAR_OPTION(SPACE, CFGITEM::NA, String, name, title, desc, __VA_ARGS__)
#define DEFVAR_bool(SPACE, name, title, desc, ...) DEFVAR_OPTION(SPACE, CFGITEM::NA, bool, name, title, desc, __VA_ARGS__)
#define DEFVAR_int32(SPACE, name, title, desc, ...) DEFVAR_OPTION(SPACE, CFGITEM::NA, int32_t, name, title, desc, __VA_ARGS__)
#define DEFVAR_uint32(SPACE, name, title, desc, ...) DEFVAR_OPTION(SPACE, CFGITEM::NA, uint32_t, name, title, desc, __VA_ARGS__)
#define DEFVAR_flags(SPACE, name, title, desc, ...) DEFVAR_OPTION(SPACE, CFGITEM::NA, Flags, name, title, desc, __VA_ARGS__)
#define DEFVAR_float(SPACE, name, title, desc, ...) DEFVAR_OPTION(SPACE, CFGITEM::NA, float, name, title, desc, __VA_ARGS__)
#define DEFVAR_double(SPACE, name, title, desc, ...) DEFVAR_OPTION(SPACE, CFGITEM::NA, double, name, title, desc, __VA_ARGS__)
#define TDEFVAR_string(SPACE, name, title, desc, ...) DEFVAR_OPTION(SPACE, CFGITEM::TEMP, String, name, title, desc, __VA_ARGS__)
#define TDEFVAR_bool(SPACE, name, title, desc, ...) DEFVAR_OPTION(SPACE, CFGITEM::TEMP, bool, name, title, desc, __VA_ARGS__)
#define TDEFVAR_int32(SPACE, name, title, desc, ...) DEFVAR_OPTION(SPACE, CFGITEM::TEMP, int32_t, name, title, desc, __VA_ARGS__)
#define TDEFVAR_uint32(SPACE, name, title, desc, ...) DEFVAR_OPTION(SPACE, CFGITEM::TEMP, uint32_t, name, title, desc, __VA_ARGS__)
#define TDEFVAR_flags(SPACE, name, title, desc, ...) DEFVAR_OPTION(SPACE, CFGITEM::TEMP, Flags, name, title, desc, __VA_ARGS__)
#define TDEFVAR_float(SPACE, name, title, desc, ...) DEFVAR_OPTION(SPACE, CFGITEM::TEMP, float, name, title, desc, __VA_ARGS__)
#define TDEFVAR_double(SPACE, name, title, desc, ...) DEFVAR_OPTION(SPACE, CFGITEM::TEMP, double, name, title, desc, __VA_ARGS__)
// I N C L U D E S /////////////////////////////////////////////////
#include "Types.h"
// P R O T O T Y P E S /////////////////////////////////////////////
namespace SEACAVE {
typedef TSphere<float, 2> Sphere2f;
typedef TSphere<float, 3> Sphere3f;
typedef TAABB<float, 2> AABB2f;
typedef TAABB<float, 3> AABB3f;
typedef TOBB<float, 2> OBB2f;
typedef TOBB<float, 3> OBB3f;
typedef TRay<float, 2> Ray2f;
typedef TRay<float, 3> Ray3f;
typedef TLine<float, 2> Line2f;
typedef TLine<float, 3> Line3f;
typedef TTriangle<float, 2> Triangle2f;
typedef TTriangle<float, 3> Triangle3f;
typedef TPlane<float> Planef;
typedef TPoint2<float> Point2f;
typedef TPoint3<float> Point3f;
typedef TMatrix<float,2,1> Vec2f;
typedef TMatrix<float,3,1> Vec3f;
typedef TMatrix<float,4,1> Vec4f;
typedef TMatrix<float,2,2> Matrix2x2f;
typedef TMatrix<float,3,3> Matrix3x3f;
typedef TMatrix<float,3,4> Matrix3x4f;
typedef TMatrix<float,4,4> Matrix4x4f;
typedef TSphere<double, 2> Sphere2d;
typedef TSphere<double, 3> Sphere3d;
typedef TAABB<double, 2> AABB2d;
typedef TAABB<double, 3> AABB3d;
typedef TOBB<double, 2> OBB2d;
typedef TOBB<double, 3> OBB3d;
typedef TRay<double, 2> Ray2d;
typedef TRay<double, 3> Ray3d;
typedef TLine<double, 2> Line2d;
typedef TLine<double, 3> Line3d;
typedef TTriangle<double, 2> Triangle2d;
typedef TTriangle<double, 3> Triangle3d;
typedef TPlane<double> Planed;
typedef TPoint2<double> Point2d;
typedef TPoint3<double> Point3d;
typedef TMatrix<double,2,1> Vec2d;
typedef TMatrix<double,3,1> Vec3d;
typedef TMatrix<double,4,1> Vec4d;
typedef TMatrix<double,2,2> Matrix2x2d;
typedef TMatrix<double,3,3> Matrix3x3d;
typedef TMatrix<double,3,4> Matrix3x4d;
typedef TMatrix<double,4,4> Matrix4x4d;
typedef TSphere<REAL, 2> Sphere2;
typedef TSphere<REAL, 3> Sphere3;
typedef TAABB<REAL, 2> AABB2;
typedef TAABB<REAL, 3> AABB3;
typedef TOBB<REAL, 2> OBB2;
typedef TOBB<REAL, 3> OBB3;
typedef TRay<REAL, 2> Ray2;
typedef TRay<REAL, 3> Ray3;
typedef TLine<REAL, 2> Line2;
typedef TLine<REAL, 3> Line3;
typedef TTriangle<REAL, 2> Triangle2;
typedef TTriangle<REAL, 3> Triangle3;
typedef TPlane<REAL> Plane;
typedef TPoint2<REAL> Point2;
typedef TPoint3<REAL> Point3;
typedef TMatrix<REAL,2,1> Vec2;
typedef TMatrix<REAL,3,1> Vec3;
typedef TMatrix<REAL,4,1> Vec4;
typedef TMatrix<REAL,2,2> Matrix2x2;
typedef TMatrix<REAL,3,3> Matrix3x3;
typedef TMatrix<REAL,3,4> Matrix3x4;
typedef TMatrix<REAL,4,4> Matrix4x4;
typedef TQuaternion<REAL> Quaternion;
typedef TRMatrixBase<REAL> RMatrixBase;
// camera matrix types
typedef Point3 CMatrix;
typedef RMatrixBase RMatrix;
typedef Matrix3x3 KMatrix;
typedef Matrix3x4 PMatrix;
// reconstructed 3D point type
typedef Vec3 X3D;
typedef SEACAVE::cList<X3D, const X3D&, 0, 8192> X3DArr;
typedef SEACAVE::cList<uint32_t, uint32_t, 0> IndexArr;
typedef CLISTDEF0(REAL) REALArr;
typedef CLISTDEF0(Point2f) Point2fArr;
typedef CLISTDEF0(Point3f) Point3fArr;
typedef CLISTDEF0(Point2d) Point2dArr;
typedef CLISTDEF0(Point3d) Point3dArr;
typedef CLISTDEF0(Point2) Point2Arr;
typedef CLISTDEF0(Point3) Point3Arr;
typedef CLISTDEF0(Vec4) Vec4Arr;
typedef CLISTDEF0(Matrix3x3) Matrix3x3Arr;
typedef CLISTDEF0(Matrix3x4) Matrix3x4Arr;
typedef CLISTDEF0(CMatrix) CMatrixArr;
typedef CLISTDEF0(RMatrix) RMatrixArr;
typedef CLISTDEF0(KMatrix) KMatrixArr;
typedef CLISTDEF0(PMatrix) PMatrixArr;
typedef CLISTDEF0(ImageRef) ImageRefArr;
typedef CLISTDEF0(Pixel8U) Pixel8UArr;
typedef CLISTDEF0(Pixel32F) Pixel32FArr;
typedef CLISTDEF0(Color8U) Color8UArr;
typedef CLISTDEF0(Color32F) Color32FArr;
/*----------------------------------------------------------------*/
} // namespace SEACAVE
// define specialized cv:DataType<>
DEFINE_CVDATATYPE(SEACAVE::Vec2f)
DEFINE_CVDATATYPE(SEACAVE::Vec3f)
DEFINE_CVDATATYPE(SEACAVE::Vec4f)
DEFINE_CVDATATYPE(SEACAVE::Matrix2x2f)
DEFINE_CVDATATYPE(SEACAVE::Matrix3x3f)
DEFINE_CVDATATYPE(SEACAVE::Matrix3x4f)
DEFINE_CVDATATYPE(SEACAVE::Matrix4x4f)
DEFINE_CVDATATYPE(SEACAVE::Vec2d)
DEFINE_CVDATATYPE(SEACAVE::Vec3d)
DEFINE_CVDATATYPE(SEACAVE::Vec4d)
DEFINE_CVDATATYPE(SEACAVE::Matrix2x2d)
DEFINE_CVDATATYPE(SEACAVE::Matrix3x3d)
DEFINE_CVDATATYPE(SEACAVE::Matrix3x4d)
DEFINE_CVDATATYPE(SEACAVE::Matrix4x4d)
/*----------------------------------------------------------------*/
#endif // _COMMON_COMMON_H_

281
libs/Common/Config.h

@ -0,0 +1,281 @@ @@ -0,0 +1,281 @@
////////////////////////////////////////////////////////////////////
// Config.h
//
// Copyright 2007 cDc@seacave
// Distributed under the Boost Software License, Version 1.0
// (See http://www.boost.org/LICENSE_1_0.txt)
#ifndef __SEACAVE_CONFIG_H__
#define __SEACAVE_CONFIG_H__
// Configure everything that needs to be globally known
#include "ConfigLocal.h"
// D E F I N E S ///////////////////////////////////////////////////
#ifdef _MSC_VER
// Modify the following defines if you have to target a platform prior to the ones specified below.
// Refer to MSDN for the latest info on corresponding values for different platforms.
#if _MSC_VER > 1400
#ifndef NTDDI_VERSION // There's now just one symbol to specify the minimum target operating system.
#define NTDDI_VERSION NTDDI_WIN7 // All the other symbols are set automatically to the appropriate values for the target operating system.
#endif
#else
#ifndef WINVER // Allow use of features specific to Windows 95 and Windows NT 4 or later.
#define WINVER 0x0500 // Change this to the appropriate value to target Windows 98 and Windows 2000 or later.
#endif
#ifndef _WIN32_WINNT // Allow use of features specific to Windows NT 4 or later.
#define _WIN32_WINNT 0x0500 // Change this to the appropriate value to target Windows 98 and Windows 2000 or later.
#endif
#ifndef _WIN32_WINDOWS // Allow use of features specific to Windows 98 or later.
#define _WIN32_WINDOWS 0x0410 // Change this to the appropriate value to target Windows Me or later.
#endif
#ifndef _WIN32_IE // Allow use of features specific to IE 4.0 or later.
#define _WIN32_IE 0x0501 // Change this to the appropriate value to target IE 5.0 or later.
#endif
#endif
#ifndef VC_EXTRALEAN
#define VC_EXTRALEAN // Exclude rarely-used stuff from Windows headers
#endif
#ifndef STRICT
#define STRICT
#endif
#define WIN32_MEAN_AND_LEAN
#define NOMINMAX
#define _USE_MATH_DEFINES
#if _MSC_VER >= 1400
#ifndef _CRT_SECURE_NO_WARNINGS
#define _CRT_SECURE_NO_WARNINGS 1
#endif
#ifndef _CRT_SECURE_NO_DEPRECATE
#define _CRT_SECURE_NO_DEPRECATE 1
#endif
#ifndef _ATL_SECURE_NO_DEPRECATE
#define _ATL_SECURE_NO_DEPRECATE 1
#endif
#ifndef _CRT_NON_CONFORMING_SWPRINTFS
#define _CRT_NON_CONFORMING_SWPRINTFS 1
#endif
#ifndef _CRT_SECURE_CPP_OVERLOAD_SECURE_NAMES
#define _CRT_SECURE_CPP_OVERLOAD_SECURE_NAMES 0 // disable automatically overloading CPP names to secure versions
#endif
#if 0 && defined(_DEBUG) && !defined(_ITERATOR_DEBUG_LEVEL) // might not build if linking statically to 3rd party libraries
#define _ITERATOR_DEBUG_LEVEL 1 // disable std iterator debugging even in Debug, as it is very slow
#endif
#endif
//----------------------------------------------------------------------
// For Microsoft Visual C++, externally accessible symbols must be
// explicitly indicated with DLL_API.
//
// The following ifdef block is the standard way of creating macros
// which make exporting from a DLL simpler. All files within this DLL
// are compiled with the DLL_EXPORTS preprocessor symbol defined on the
// command line. In contrast, projects that use (or import) the DLL
// objects do not define the DLL_EXPORTS symbol. This way any other
// project whose source files include this file see DLL_API functions as
// being imported from a DLL, whereas this DLL sees symbols defined with
// this macro as being exported.
//----------------------------------------------------------------------
#define EXPORT_API __declspec(dllexport)
#define IMPORT_API __declspec(dllimport)
/*----------------------------------------------------------------*/
#ifdef _USRDLL
#ifdef Common_EXPORTS
#define GENERAL_API EXPORT_API
#define GENERAL_TPL
#else
#define GENERAL_API IMPORT_API
#define GENERAL_TPL extern
#endif
#else
#define GENERAL_API
#define GENERAL_TPL
#endif
/*----------------------------------------------------------------*/
// Define platform type
#if _WIN64
#define _ENVIRONMENT64
#else
#define _ENVIRONMENT32
#endif
#else // _MSC_VER
#if !defined(_DEBUG) && !defined(NDEBUG)
#define _DEBUG
#endif
//----------------------------------------------------------------------
// DLL_API is ignored for all other systems
//----------------------------------------------------------------------
#define EXPORT_API
#define IMPORT_API
#define GENERAL_API
#define GENERAL_TPL
// Define platform type
#if __x86_64__ || __ppc64__
#define _ENVIRONMENT64
#else
#define _ENVIRONMENT32
#endif
#endif // _MSC_VER
#if __cplusplus >= 201103L || (__clang_major__ >= 4 || (__clang_major__ >= 3 && __clang_minor__ >= 3))
#define _SUPPORT_CPP11
#endif
#if __cplusplus >= 201402L || (__clang_major__ >= 4 || (__clang_major__ >= 3 && __clang_minor__ >= 4))
#define _SUPPORT_CPP14
#endif
#if __cplusplus >= 201703L || __clang_major__ >= 5
#define _SUPPORT_CPP17
#endif
#if __cplusplus >= 202002L || __clang_major__ >= 10
#define _SUPPORT_CPP20
#endif
#if defined(__powerpc__)
#define _PLATFORM_PPC 1
#elif defined(__arm__) || defined (__arm64__) || defined(__aarch64__) || defined(_M_ARM) || defined(_M_ARMT)
#define _PLATFORM_ARM 1
#else
#define _PLATFORM_X86 1
#endif
#if !defined(_DEBUG) && !defined(_PROFILE)
#define _RELEASE // exclude code useful only for debug
#endif
//optimization flags
#if defined(_MSC_VER)
# define ALIGN(n) __declspec(align(n))
# define NOINITVTABLE __declspec(novtable) //disable generating code to initialize the vfptr in the constructor(s) and destructor of the class
# define DECRESTRICT __declspec(restrict) //applied to a function declaration or definition that returns a pointer type and tells the compiler that the function returns an object that will not be aliased with any other pointers
# define NOALIAS __declspec(noalias) //applied to a function declaration or definition that returns a pointer type and tells the compiler that the function call does not modify or reference visible global state and only modifies the memory pointed to directly by pointer parameters (first-level indirections)
# define RESTRICT __restrict //applied to a function parameter
# define MEMALLOC __declspec(noalias) __declspec(restrict)
# define DEPRECATED __declspec(deprecated)
# define MAYBEUNUSED
# define HOT
# define COLD
# define THREADLOCAL __declspec(thread)
# define FORCEINLINE __forceinline
#elif defined(__GNUC__)
# define ALIGN(n) __attribute__((aligned(n)))
# define NOINITVTABLE
# define DECRESTRICT
# define NOALIAS
# define RESTRICT __restrict__
# define MEMALLOC __attribute__ ((__malloc__))
# define DEPRECATED __attribute__ ((__deprecated__))
# define MAYBEUNUSED __attribute__ ((unused))
# define HOT __attribute__((hot)) __attribute__((optimize("-O3"))) __attribute__((optimize("-ffast-math"))) //optimize for speed, even in debug
# define COLD __attribute__((cold)) //optimize for size
# define THREADLOCAL __thread
# define FORCEINLINE inline //__attribute__((always_inline))
#else
# define ALIGN(n)
# define NOINITVTABLE
# define DECRESTRICT
# define NOALIAS
# define RESTRICT
# define MEMALLOC
# define DEPRECATED
# define MAYBEUNUSED
# define HOT
# define COLD
# define THREADLOCAL __thread
# define FORCEINLINE inline
#endif
#ifndef _SUPPORT_CPP11
# define constexpr inline
#endif
#ifdef _SUPPORT_CPP17
# undef MAYBEUNUSED
# define MAYBEUNUSED [[maybe_unused]]
#endif
#define SAFE_DELETE(p) { if (p!=NULL) { delete (p); (p)=NULL; } }
#define SAFE_DELETE_ARR(p) { if (p!=NULL) { delete [] (p); (p)=NULL; } }
#define SAFE_FREE(p) { if (p!=NULL) { free(p); (p)=NULL; } }
#define SAFE_RELEASE(p) { if (p!=NULL) { (p)->Release(); (p)=NULL; } }
#ifdef _DEBUG
#ifdef _MSC_VER
#define _DEBUGINFO
#define _CRTDBG_MAP_ALLOC //enable this to show also the filename (DEBUG_NEW should also be defined in each file)
#include <cstdlib>
#include <crtdbg.h>
#ifdef _INC_CRTDBG
#define ASSERT(exp) {if (!(exp) && 1 == _CrtDbgReport(_CRT_ASSERT, __FILE__, __LINE__, NULL, #exp)) _CrtDbgBreak();}
#else
#define ASSERT(exp) {if (!(exp)) __debugbreak();}
#endif // _INC_CRTDBG
#define TRACE(...) {TCHAR buffer[2048]; _sntprintf(buffer, 2048, __VA_ARGS__); OutputDebugString(buffer);}
#else // _MSC_VER
#include <assert.h>
#define ASSERT(exp) assert(exp)
#define TRACE(...)
#endif // _MSC_VER
#else
#ifdef _RELEASE
#define ASSERT(exp)
#else
#ifdef _MSC_VER
#define ASSERT(exp) {if (!(exp)) __debugbreak();}
#else // _MSC_VER
#define ASSERT(exp) {if (!(exp)) __builtin_trap();}
#endif // _MSC_VER
#endif
#define TRACE(...)
#endif // _DEBUG
#define ASSERTM(exp, msg) ASSERT(exp)
namespace SEACAVE_ASSERT
{
template <bool value> struct compile_time_assert;
template <> struct compile_time_assert<true> { enum {value=1}; };
template <typename T, typename U> struct assert_are_same_type;
template <typename T> struct assert_are_same_type<T,T> { enum{value=1}; };
template <typename T, typename U> struct assert_are_not_same_type { enum{value=1}; };
template <typename T> struct assert_are_not_same_type<T,T> {};
}
#define STATIC_ASSERT(expression) \
MAYBEUNUSED typedef char CTA##__LINE__[::SEACAVE_ASSERT::compile_time_assert<(bool)(expression)>::value]
#define ASSERT_ARE_SAME_TYPE(type1, type2) \
MAYBEUNUSED typedef char AAST##__LINE__[::SEACAVE_ASSERT::assert_are_same_type<type1,type2>::value]
#define ASSERT_ARE_NOT_SAME_TYPE(type1, type2) \
MAYBEUNUSED typedef char AANST##__LINE__[::SEACAVE_ASSERT::assert_are_not_same_type<type1,type2>::value]
/*----------------------------------------------------------------*/
#endif // __SEACAVE_CONFIG_H__

153
libs/Common/ConfigTable.cpp

@ -0,0 +1,153 @@ @@ -0,0 +1,153 @@
////////////////////////////////////////////////////////////////////
// ConfigTable.cpp
//
// Copyright 2007 cDc@seacave
// Distributed under the Boost Software License, Version 1.0
// (See http://www.boost.org/LICENSE_1_0.txt)
#include "Common.h"
#include "ConfigTable.h"
using namespace SEACAVE;
// D E F I N E S ///////////////////////////////////////////////////
// S T R U C T S ///////////////////////////////////////////////////
/*-----------------------------------------------------------*
* CConfigTable class implementation *
*-----------------------------------------------------------*/
/**
* Constructor
*/
CConfigTable::CConfigTable(const String& name) : m_oSML(name), m_pParent(NULL)
{
m_oSML.SetFncItem(ItemInitData, ItemSaveData, ItemReleaseData);
}
/**
* Destructor
*/
CConfigTable::~CConfigTable()
{
if (m_pParent)
m_pParent->RemoveChild(*this);
Release();
}
/*----------------------------------------------------------------*/
/**
* Released all used memory
*/
void CConfigTable::Release()
{
m_oSML.Release();
}
/*----------------------------------------------------------------*/
/**
* Create a new child
*/
void CConfigTable::Insert(const String& name)
{
SML* const pChildSML = new SML(name);
pChildSML->SetFncItem(ItemInitData, ItemSaveData, ItemReleaseData);
const IDX idx = m_oSML.InsertChild(pChildSML);
// if child already exists, delete created node
if (m_oSML.GetArrChildren()[idx] != pChildSML)
delete pChildSML;
}
/*----------------------------------------------------------------*/
/**
* Remove an existing child
*/
void CConfigTable::Remove(const String& name)
{
m_oSML.DestroyChild(name);
}
/*----------------------------------------------------------------*/
/**
* Get the config table for the given child
*/
const SML& CConfigTable::GetConfig(const String& name) const
{
const LPSMLARR& arrSML = m_oSML.GetArrChildren();
const IDX idx = arrSML.FindFirst(&name, SML::CompareName);
ASSERT(idx != LPSMLARR::NO_INDEX);
return *arrSML[idx];
} // GetConfig
SML& CConfigTable::GetConfig(const String& name)
{
const LPSMLARR& arrSML = m_oSML.GetArrChildren();
const IDX idx = arrSML.FindFirst(&name, SML::CompareName);
ASSERT(idx != LPSMLARR::NO_INDEX);
return *arrSML[idx];
} // GetConfig
/*----------------------------------------------------------------*/
/**
* Load the configuration from a file.
*/
bool CConfigTable::Load(const String& f)
{
return m_oSML.Load(f);
} // Load
bool CConfigTable::Load(ISTREAM& oStream)
{
return m_oSML.Load(oStream);
} // Load
/**
* Write to a file the values of this node and its children.
* Set to false the second parameter in order not to save the empty children.
*/
bool CConfigTable::Save(const String& f, SML::SAVEFLAG flags) const
{
return m_oSML.Save(f, flags);
} // Save
bool CConfigTable::Save(OSTREAM& oStream, SML::SAVEFLAG flags) const
{
return m_oSML.Save(oStream, flags);
} // Save
/*----------------------------------------------------------------*/
/**
* Create and initialize a config item for the given SML entry.
*/
void STCALL CConfigTable::ItemInitData(const String& key, SMLVALUE& val, void*)
{
CFGITEM* pItem = new CFGITEM;
pItem->name = key;
val.data = pItem;
} // ItemInitData
/*----------------------------------------------------------------*/
/**
* Save a config item for the given SML entry. Return false if this item should not be saved.
*/
bool STCALL CConfigTable::ItemSaveData(const SMLVALUE& val, void*)
{
const CFGITEM& item = *((const CFGITEM*)val.data);
return !item.state.isSet(CFGITEM::TEMP);
} // ItemSaveData
/*----------------------------------------------------------------*/
/**
* Release and destroy the config item for the given SML entry.
*/
void STCALL CConfigTable::ItemReleaseData(SMLVALUE& val, void*)
{
CFGITEM* pItem = (CFGITEM*)val.data;
val.data = NULL;
delete pItem;
} // ItemReleaseData
/*----------------------------------------------------------------*/

82
libs/Common/ConfigTable.h

@ -0,0 +1,82 @@ @@ -0,0 +1,82 @@
////////////////////////////////////////////////////////////////////
// ConfigTable.h
//
// Copyright 2007 cDc@seacave
// Distributed under the Boost Software License, Version 1.0
// (See http://www.boost.org/LICENSE_1_0.txt)
#ifndef __SEACAVE_CONFIGTABLE_H__
#define __SEACAVE_CONFIGTABLE_H__
// I N C L U D E S /////////////////////////////////////////////////
#include "SML.h"
// D E F I N E S ///////////////////////////////////////////////////
namespace SEACAVE {
// S T R U C T S ///////////////////////////////////////////////////
typedef struct CFGITEM_TYPE {
enum {
NA = 0, // no special states available
TEMP = (1 << 0), // this item lives only this instance and it will not be save
};
Flags state;
String name;
String desc;
String defval;
StringArr vals;
} CFGITEM;
// P R O T O T Y P E S /////////////////////////////////////////////
/**
* Configuration table interface.
*/
class GENERAL_API CConfigTable
{
public:
CConfigTable(const String&);
~CConfigTable();
void Release();
// main methods
void Insert(const String&);
void Remove(const String&);
const SML& GetConfig(const String&) const;
SML& GetConfig(const String&);
const SML& GetConfig() const { return m_oSML; }
SML& GetConfig() { return m_oSML; }
inline SMLVALUE& operator[] (const String& name) { return m_oSML[name]; }
inline IDX InsertChild(CConfigTable& oCfg) { oCfg.SetParent(this); return m_oSML.InsertChild(&oCfg.m_oSML); }
inline void RemoveChild(CConfigTable& oCfg) { oCfg.SetParent(NULL); m_oSML.RemoveChild(oCfg.m_oSML.GetName()); }
// misc methods
bool Load(const String&);
bool Load(ISTREAM&);
bool Save(const String&, SML::SAVEFLAG=SML::NONE) const;
bool Save(OSTREAM&, SML::SAVEFLAG=SML::NONE) const;
inline const String& GetName() const { return m_oSML.GetName(); }
inline CConfigTable* GetParent() const { return m_pParent; }
inline void SetParent(CConfigTable* pParent) { m_pParent = pParent; }
static void STCALL ItemInitData(const String&, SMLVALUE&, void*);
static bool STCALL ItemSaveData(const SMLVALUE&, void*);
static void STCALL ItemReleaseData(SMLVALUE&, void*);
private:
SML m_oSML;
CConfigTable* m_pParent;
};
/*----------------------------------------------------------------*/
} // namespace SEACAVE
#endif // __SEACAVE_CONFIGTABLE_H__

264
libs/Common/CriticalSection.h

@ -0,0 +1,264 @@ @@ -0,0 +1,264 @@
////////////////////////////////////////////////////////////////////
// CriticalSection.h
//
// Copyright 2007 cDc@seacave
// Distributed under the Boost Software License, Version 1.0
// (See http://www.boost.org/LICENSE_1_0.txt)
#ifndef __SEACAVE_CRITCALSECTION_H__
#define __SEACAVE_CRITCALSECTION_H__
// I N C L U D E S /////////////////////////////////////////////////
#include "Thread.h"
// D E F I N E S ///////////////////////////////////////////////////
namespace SEACAVE {
// S T R U C T S ///////////////////////////////////////////////////
class CriticalSection
{
#ifdef _MSC_VER
public:
CriticalSection() {
InitializeCriticalSection(&cs);
}
~CriticalSection() {
DeleteCriticalSection(&cs);
}
void Clear() {
DeleteCriticalSection(&cs);
InitializeCriticalSection(&cs);
}
void Enter() {
EnterCriticalSection(&cs);
}
bool TryEnter() {
return (TryEnterCriticalSection(&cs) != 0);
}
void Leave() {
LeaveCriticalSection(&cs);
}
protected:
CRITICAL_SECTION cs;
#else
public:
CriticalSection() {
#ifdef __APPLE__
pthread_mutex_init(&mtx, NULL);
#else
mtx = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
#endif
}
~CriticalSection() { pthread_mutex_destroy(&mtx); }
void Clear() {
pthread_mutex_destroy(&mtx);
#ifdef __APPLE__
pthread_mutex_init(&mtx, NULL);
#else
mtx = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
#endif
}
void Enter() { pthread_mutex_lock(&mtx); }
bool TryEnter() { return (pthread_mutex_trylock(&mtx) == 0); }
void Leave() { pthread_mutex_unlock(&mtx); }
pthread_mutex_t& getMutex() { return mtx; }
protected:
pthread_mutex_t mtx;
#endif
private:
CriticalSection(const CriticalSection&);
CriticalSection& operator=(const CriticalSection&);
};
/**
* A fast, non-recursive and unfair implementation of the Critical Section.
* It is meant to be used in situations where the risk for lock conflict is very low,
* i e locks that are held for a very short time. The lock is _not_ recursive, i e if
* the same thread will try to grab the lock it'll hang in a never-ending loop. The lock
* is not fair, i e the first to try to enter a locked lock is not guaranteed to be the
* first to get it when it's freed...
*/
class FastCriticalSection {
public:
FastCriticalSection() : state(0) {}
void Clear() {
Thread::safeExchange(state, 0);
}
void Enter() {
while (Thread::safeCompareExchange(state, 0, 1) != 0)
Thread::yield();
}
bool TryEnter() {
return (Thread::safeCompareExchange(state, 0, 1) == 0);
}
void Leave() {
Thread::safeDec(state);
}
protected:
volatile Thread::safe_t state;
};
template<class T>
class SimpleLock {
public:
SimpleLock(T& aCs) : cs(aCs) { cs.Enter(); }
~SimpleLock() { cs.Leave(); }
protected:
T& cs;
};
template<class T>
class SimpleLockTry {
public:
SimpleLockTry(T& aCs) : cs(aCs) { bLocked = cs.TryEnter(); }
~SimpleLockTry() { if (bLocked) cs.Leave(); }
bool IsLocked() const { return bLocked; }
protected:
T& cs;
bool bLocked;
};
typedef SimpleLock<CriticalSection> Lock;
typedef SimpleLock<FastCriticalSection> FastLock;
typedef SimpleLockTry<CriticalSection> LockTry;
typedef SimpleLockTry<FastCriticalSection> FastLockTry;
class RWLock
{
public:
RWLock() : cs(), readers(0) {}
~RWLock() { ASSERT(readers==0); }
void Clear() { cs.Clear(); readers = 0; }
// Read
void EnterRead() {
Lock l(cs);
++readers;
}
bool TryEnterRead() {
LockTry l(cs);
if (!l.IsLocked())
return false;
++readers;
return true;
}
void LeaveRead() {
Lock l(cs);
ASSERT(readers > 0);
--readers;
}
bool TryLeaveRead() {
LockTry l(cs);
if (!l.IsLocked())
return false;
ASSERT(readers > 0);
--readers;
return true;
}
// Write
void EnterWrite() {
cs.Enter();
while (readers) {
cs.Leave();
Thread::yield();
cs.Enter();
}
}
bool TryEnterWrite() {
if (cs.TryEnter()) {
if (readers == 0)
return true;
cs.Leave();
}
return false;
}
void LeaveWrite() {
cs.Leave();
}
private:
RWLock(const RWLock&);
RWLock& operator=(const RWLock&);
protected:
CriticalSection cs;
unsigned readers;
};
class RLock {
public:
RLock(RWLock& aCs) : cs(aCs) { cs.EnterRead(); }
~RLock() { cs.LeaveRead(); }
private:
RLock(const RLock&);
RLock& operator=(const RLock&);
protected:
RWLock& cs;
};
class RLockTry {
public:
RLockTry(RWLock& aCs) : cs(aCs) { bLocked = cs.TryEnterRead(); }
~RLockTry() { if (bLocked) cs.LeaveRead(); }
bool IsLocked() const { return bLocked; }
bool TryEnter() { return (bLocked = cs.TryEnterRead()); }
bool TryLeave() { return !(bLocked = !cs.TryLeaveRead()); }
private:
RLockTry(const RLockTry&);
RLockTry& operator=(const RLockTry&);
protected:
RWLock& cs;
bool bLocked;
};
class WLock {
public:
WLock(RWLock& aCs) : cs(aCs) { cs.EnterWrite(); }
~WLock() { cs.LeaveWrite(); }
private:
WLock(const WLock&);
WLock& operator=(const WLock&);
protected:
RWLock& cs;
};
class WLockTry {
public:
WLockTry(RWLock& aCs) : cs(aCs) { bLocked = cs.TryEnterWrite(); }
~WLockTry() { if (bLocked) cs.LeaveWrite(); }
bool IsLocked() const { return bLocked; }
bool TryEnter() { return (bLocked = cs.TryEnterWrite()); }
private:
WLockTry(const WLockTry&);
WLockTry& operator=(const WLockTry&);
protected:
RWLock& cs;
bool bLocked;
};
/*----------------------------------------------------------------*/
} // namespace SEACAVE
#endif // __SEACAVE_CRITCALSECTION_H__

101
libs/Common/EventQueue.cpp

@ -0,0 +1,101 @@ @@ -0,0 +1,101 @@
////////////////////////////////////////////////////////////////////
// EventQueue.cpp
//
// Copyright 2007 cDc@seacave
// Distributed under the Boost Software License, Version 1.0
// (See http://www.boost.org/LICENSE_1_0.txt)
#include "Common.h"
#include "EventQueue.h"
using namespace SEACAVE;
// D E F I N E S ///////////////////////////////////////////////////
// S T R U C T S ///////////////////////////////////////////////////
/*-----------------------------------------------------------*
* EventQueue class implementation *
*-----------------------------------------------------------*/
void EventQueue::Clear()
{
m_cs.Clear();
m_sem.Clear();
m_events.Empty();
}
void EventQueue::AddEvent(Event* evt)
{
Lock l(m_cs);
m_events.AddTail(evt);
m_sem.Signal();
}
void EventQueue::AddEventFirst(Event* evt)
{
Lock l(m_cs);
m_events.AddHead(evt);
m_sem.Signal();
}
Event* EventQueue::GetEvent()
{
m_sem.Wait();
Lock l(m_cs);
ASSERT(!m_events.IsEmpty());
return m_events.RemoveHead();
}
Event* EventQueue::GetEvent(uint32_t millis)
{
if (!m_sem.Wait(millis))
return NULL;
Lock l(m_cs);
if (m_events.IsEmpty())
return NULL;
return m_events.RemoveHead();
}
Event* EventQueue::GetEventLast()
{
m_sem.Wait();
Lock l(m_cs);
ASSERT(!m_events.IsEmpty());
return m_events.RemoveTail();
}
Event* EventQueue::GetEventLast(uint32_t millis)
{
if (!m_sem.Wait(millis))
return NULL;
Lock l(m_cs);
if (m_events.IsEmpty())
return NULL;
return m_events.RemoveTail();
}
/*----------------------------------------------------------------*/
bool EventQueue::IsEmpty() const
{
Lock l(m_cs);
return m_events.IsEmpty();
}
uint_t EventQueue::GetSize() const
{
Lock l(m_cs);
return m_events.GetSize();
}
/*----------------------------------------------------------------*/
/*-----------------------------------------------------------*
* EventThreadPool class implementation *
*-----------------------------------------------------------*/
void EventThreadPool::stop()
{
ThreadPool::stop();
EventQueue::Clear();
}
/*----------------------------------------------------------------*/

90
libs/Common/EventQueue.h

@ -0,0 +1,90 @@ @@ -0,0 +1,90 @@
////////////////////////////////////////////////////////////////////
// EventQueue.h
//
// Copyright 2007 cDc@seacave
// Distributed under the Boost Software License, Version 1.0
// (See http://www.boost.org/LICENSE_1_0.txt)
#ifndef __SEACAVE_EVENTQUEUE_H__
#define __SEACAVE_EVENTQUEUE_H__
// I N C L U D E S /////////////////////////////////////////////////
#include "Types.h"
#include "CriticalSection.h"
#include "Semaphore.h"
// D E F I N E S ///////////////////////////////////////////////////
namespace SEACAVE {
// S T R U C T S ///////////////////////////////////////////////////
class Event
{
public:
Event(uint32_t _id) : id(_id) {}
virtual ~Event() {}
uint32_t GetID() const { return id; }
virtual bool Run(void* /*pArgs*/ = NULL) { return true; }
protected:
const uint32_t id;
};
typedef cQueue<Event*,Event*,0> EVENTQUEUE;
/**************************************************************************************
* Events Queue
* --------------
* basic eventing mechanism
* multi-thread safe
**************************************************************************************/
class GENERAL_API EventQueue
{
public:
EventQueue() {}
~EventQueue() {}
void Clear(); // reset the state of the locks and empty the queue
void AddEvent(Event*); //add a new event to the end of the queue
void AddEventFirst(Event*); //add a new event to the beginning of the queue
Event* GetEvent(); //block until an event arrives and get the first event pending in the queue
Event* GetEvent(uint32_t millis); //block until an event arrives or time expires and get the first event pending in the queue
Event* GetEventLast(); //block until an event arrives and get the last event pending in the queue
Event* GetEventLast(uint32_t millis); //block until an event arrives or time expires and get the last event pending in the queue
bool IsEmpty() const; //are there any events in the queue?
uint_t GetSize() const; //number of events in the queue
protected:
Semaphore m_sem;
mutable CriticalSection m_cs;
EVENTQUEUE m_events;
};
/*----------------------------------------------------------------*/
// basic event and thread pool
class GENERAL_API EventThreadPool : public ThreadPool, public EventQueue
{
public:
inline EventThreadPool() {}
inline EventThreadPool(size_type nThreads) : ThreadPool(nThreads) {}
inline EventThreadPool(size_type nThreads, Thread::FncStart pfnStarter, void* pData=NULL) : ThreadPool(nThreads, pfnStarter, pData) {}
inline ~EventThreadPool() {}
void stop(); //stop threads, reset locks state and empty event queue
};
/*----------------------------------------------------------------*/
} // namespace SEACAVE
#endif // __SEACAVE_EVENTQUEUE_H__

2105
libs/Common/FastDelegate.h

File diff suppressed because it is too large Load Diff

240
libs/Common/FastDelegateBind.h

@ -0,0 +1,240 @@ @@ -0,0 +1,240 @@
// FastDelegateBind.h
// Helper file for FastDelegates. Provides bind() function, enabling
// FastDelegates to be rapidly compared to programs using boost::function and boost::bind.
//
// Documentation is found at http://www.codeproject.com/cpp/FastDelegate.asp
//
// Original author: Jody Hagins.
// Minor changes by Don Clugston.
//
// Warning: The arguments to 'bind' are ignored! No actual binding is performed.
// The behaviour is equivalent to boost::bind only when the basic placeholder
// arguments _1, _2, _3, etc are used in order.
//
// HISTORY:
// 1.4 Dec 2004. Initial release as part of FastDelegate 1.4.
#ifndef FASTDELEGATEBIND_H
#define FASTDELEGATEBIND_H
////////////////////////////////////////////////////////////////////////////////
// FastDelegate bind()
//
// bind() helper function for boost compatibility.
// (Original author: Jody Hagins).
//
// Add another helper, so FastDelegate can be a dropin replacement
// for boost::bind (in a fair number of cases).
// Note the ellipsis, because boost::bind() takes place holders
// but FastDelegate does not care about them. Getting the place holder
// mechanism to work, and play well with boost is a bit tricky, so
// we do the "easy" thing...
// Assume we have the following code...
// using boost::bind;
// bind(&Foo:func, &foo, _1, _2);
// we should be able to replace the "using" with...
// using fastdelegate::bind;
// and everything should work fine...
////////////////////////////////////////////////////////////////////////////////
#ifdef FASTDELEGATE_ALLOW_FUNCTION_TYPE_SYNTAX
namespace fastdelegate {
//N=0
template <class X, class Y, class RetType>
FastDelegate< RetType ( ) >
bind(
RetType (X::*func)( ),
Y * y,
...)
{
return FastDelegate< RetType ( ) >(y, func);
}
template <class X, class Y, class RetType>
FastDelegate< RetType ( ) >
bind(
RetType (X::*func)( ) const,
Y * y,
...)
{
return FastDelegate< RetType ( ) >(y, func);
}
//N=1
template <class X, class Y, class RetType, class Param1>
FastDelegate< RetType ( Param1 p1 ) >
bind(
RetType (X::*func)( Param1 p1 ),
Y * y,
...)
{
return FastDelegate< RetType ( Param1 p1 ) >(y, func);
}
template <class X, class Y, class RetType, class Param1>
FastDelegate< RetType ( Param1 p1 ) >
bind(
RetType (X::*func)( Param1 p1 ) const,
Y * y,
...)
{
return FastDelegate< RetType ( Param1 p1 ) >(y, func);
}
//N=2
template <class X, class Y, class RetType, class Param1, class Param2>
FastDelegate< RetType ( Param1 p1, Param2 p2 ) >
bind(
RetType (X::*func)( Param1 p1, Param2 p2 ),
Y * y,
...)
{
return FastDelegate< RetType ( Param1 p1, Param2 p2 ) >(y, func);
}
template <class X, class Y, class RetType, class Param1, class Param2>
FastDelegate< RetType ( Param1 p1, Param2 p2 ) >
bind(
RetType (X::*func)( Param1 p1, Param2 p2 ) const,
Y * y,
...)
{
return FastDelegate< RetType ( Param1 p1, Param2 p2 ) >(y, func);
}
//N=3
template <class X, class Y, class RetType, class Param1, class Param2, class Param3>
FastDelegate< RetType ( Param1 p1, Param2 p2, Param3 p3 ) >
bind(
RetType (X::*func)( Param1 p1, Param2 p2, Param3 p3 ),
Y * y,
...)
{
return FastDelegate< RetType ( Param1 p1, Param2 p2, Param3 p3 ) >(y, func);
}
template <class X, class Y, class RetType, class Param1, class Param2, class Param3>
FastDelegate< RetType ( Param1 p1, Param2 p2, Param3 p3 ) >
bind(
RetType (X::*func)( Param1 p1, Param2 p2, Param3 p3 ) const,
Y * y,
...)
{
return FastDelegate< RetType ( Param1 p1, Param2 p2, Param3 p3 ) >(y, func);
}
//N=4
template <class X, class Y, class RetType, class Param1, class Param2, class Param3, class Param4>
FastDelegate< RetType ( Param1 p1, Param2 p2, Param3 p3, Param4 p4 ) >
bind(
RetType (X::*func)( Param1 p1, Param2 p2, Param3 p3, Param4 p4 ),
Y * y,
...)
{
return FastDelegate< RetType ( Param1 p1, Param2 p2, Param3 p3, Param4 p4 ) >(y, func);
}
template <class X, class Y, class RetType, class Param1, class Param2, class Param3, class Param4>
FastDelegate< RetType ( Param1 p1, Param2 p2, Param3 p3, Param4 p4 ) >
bind(
RetType (X::*func)( Param1 p1, Param2 p2, Param3 p3, Param4 p4 ) const,
Y * y,
...)
{
return FastDelegate< RetType ( Param1 p1, Param2 p2, Param3 p3, Param4 p4 ) >(y, func);
}
//N=5
template <class X, class Y, class RetType, class Param1, class Param2, class Param3, class Param4, class Param5>
FastDelegate< RetType ( Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5 ) >
bind(
RetType (X::*func)( Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5 ),
Y * y,
...)
{
return FastDelegate< RetType ( Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5 ) >(y, func);
}
template <class X, class Y, class RetType, class Param1, class Param2, class Param3, class Param4, class Param5>
FastDelegate< RetType ( Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5 ) >
bind(
RetType (X::*func)( Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5 ) const,
Y * y,
...)
{
return FastDelegate< RetType ( Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5 ) >(y, func);
}
//N=6
template <class X, class Y, class RetType, class Param1, class Param2, class Param3, class Param4, class Param5, class Param6>
FastDelegate< RetType ( Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6 ) >
bind(
RetType (X::*func)( Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6 ),
Y * y,
...)
{
return FastDelegate< RetType ( Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6 ) >(y, func);
}
template <class X, class Y, class RetType, class Param1, class Param2, class Param3, class Param4, class Param5, class Param6>
FastDelegate< RetType ( Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6 ) >
bind(
RetType (X::*func)( Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6 ) const,
Y * y,
...)
{
return FastDelegate< RetType ( Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6 ) >(y, func);
}
//N=7
template <class X, class Y, class RetType, class Param1, class Param2, class Param3, class Param4, class Param5, class Param6, class Param7>
FastDelegate< RetType ( Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6, Param7 p7 ) >
bind(
RetType (X::*func)( Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6, Param7 p7 ),
Y * y,
...)
{
return FastDelegate< RetType ( Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6, Param7 p7 ) >(y, func);
}
template <class X, class Y, class RetType, class Param1, class Param2, class Param3, class Param4, class Param5, class Param6, class Param7>
FastDelegate< RetType ( Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6, Param7 p7 ) >
bind(
RetType (X::*func)( Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6, Param7 p7 ) const,
Y * y,
...)
{
return FastDelegate< RetType ( Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6, Param7 p7 ) >(y, func);
}
//N=8
template <class X, class Y, class RetType, class Param1, class Param2, class Param3, class Param4, class Param5, class Param6, class Param7, class Param8>
FastDelegate< RetType ( Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6, Param7 p7, Param8 p8 ) >
bind(
RetType (X::*func)( Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6, Param7 p7, Param8 p8 ),
Y * y,
...)
{
return FastDelegate< RetType ( Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6, Param7 p7, Param8 p8 ) >(y, func);
}
template <class X, class Y, class RetType, class Param1, class Param2, class Param3, class Param4, class Param5, class Param6, class Param7, class Param8>
FastDelegate< RetType ( Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6, Param7 p7, Param8 p8 ) >
bind(
RetType (X::*func)( Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6, Param7 p7, Param8 p8 ) const,
Y * y,
...)
{
return FastDelegate< RetType ( Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6, Param7 p7, Param8 p8 ) >(y, func);
}
#endif //FASTDELEGATE_ALLOW_FUNCTION_TYPE_SYNTAX
} // namespace fastdelegate
#endif // !defined(FASTDELEGATEBIND_H)

379
libs/Common/FastDelegateCPP11.h

@ -0,0 +1,379 @@ @@ -0,0 +1,379 @@
/** \file SRDelegate.hpp
*
* This is a C++11 implementation by janezz55(code.google) for the original "The Impossibly Fast C++ Delegates" authored by Sergey Ryazanov.
*
* This is a copy checkouted from https://code.google.com/p/cpppractice/source/browse/trunk/delegate.hpp on 2014/06/07.
* Last change in the chunk was r370 on Feb 9, 2014.
*
* The following modifications were added by Benjamin YanXiang Huang
* - replace light_ptr with std::shared_ptr
* - renamed src file
*
* Reference:
* - http://codereview.stackexchange.com/questions/14730/impossibly-fast-delegate-in-c11
* - http://www.codeproject.com/Articles/11015/The-Impossibly-Fast-C-Delegates
* - https://code.google.com/p/cpppractice/source/browse/trunk/delegate.hpp
*/
#pragma once
#ifndef SRDELEGATE_HPP
#define SRDELEGATE_HPP
#include <cassert>
#include <cstring>
#include <memory>
#include <new>
#include <type_traits>
#include <utility>
// VC work around for constexpr and noexcept: VC2013 and below do not support these 2 keywords
#if defined(_MSC_VER) && (_MSC_VER <= 1800)
#define constexpr const
#define noexcept throw()
#endif
namespace fastdelegate
{
template <typename T> class delegate;
template<class R, class ...A>
class delegate<R(A...)>
{
using stub_ptr_type = R(*)(void *, A&&...);
delegate(void * const o, stub_ptr_type const m) noexcept : object_ptr_(o), stub_ptr_(m) {}
public:
delegate(void) = default;
delegate(delegate const &) = default;
delegate(delegate && d)
: object_ptr_(d.object_ptr_), stub_ptr_(d.stub_ptr_), deleter_(d.deleter_), store_(d.store_), store_size_(d.store_size_)
{
d.object_ptr_ = nullptr;
d.stub_ptr_ = nullptr;
d.deleter_ = nullptr;
d.store_ = nullptr;
d.store_size_ = 0;
}
delegate(::std::nullptr_t const) noexcept : delegate() { }
template <class C, typename = typename ::std::enable_if< ::std::is_class<C>::value, C>::type>
explicit delegate(C const * const o) noexcept :
object_ptr_(const_cast<C *>(o))
{}
template <class C, typename = typename ::std::enable_if< ::std::is_class<C> {}>::type>
explicit delegate(C const & o) noexcept :
object_ptr_(const_cast<C *>(&o))
{}
template <class C>
delegate(C * const object_ptr, R(C::* const method_ptr)(A...))
{
*this = from(object_ptr, method_ptr);
}
template <class C>
delegate(C * const object_ptr, R(C::* const method_ptr)(A...) const)
{
*this = from(object_ptr, method_ptr);
}
template <class C>
delegate(C & object, R(C::* const method_ptr)(A...))
{
*this = from(object, method_ptr);
}
template <class C>
delegate(C const & object, R(C::* const method_ptr)(A...) const)
{
*this = from(object, method_ptr);
}
template <
typename T,
typename = typename ::std::enable_if<!::std::is_same<delegate, typename ::std::decay<T>::type>::value>::type
>
delegate(T&& f)
: store_(operator new(sizeof(typename ::std::decay<T>::type))
, functor_deleter<typename ::std::decay<T>::type>)
, store_size_(sizeof(typename ::std::decay<T>::type))
{
using functor_type = typename ::std::decay<T>::type;
new(store_.get()) functor_type(::std::forward<T>(f));
object_ptr_ = store_.get();
stub_ptr_ = functor_stub<functor_type>;
deleter_ = deleter_stub<functor_type>;
}
delegate & operator=(delegate const &) = default;
delegate & operator=(delegate&& d)
{
object_ptr_ = d.object_ptr_;
stub_ptr_ = d.stub_ptr_;
deleter_ = d.deleter_;
store_ = d.store_;
store_size_ = d.store_size_;
d.object_ptr_ = nullptr;
d.stub_ptr_ = nullptr;
d.deleter_ = nullptr;
d.store_ = nullptr;
d.store_size_ = 0;
return *this;
}
template <class C>
delegate & operator=(R(C::* const rhs)(A...))
{
return *this = from(static_cast<C *>(object_ptr_), rhs);
}
template <class C>
delegate & operator=(R(C::* const rhs)(A...) const)
{
return *this = from(static_cast<C const *>(object_ptr_), rhs);
}
template <
typename T
, typename = typename ::std::enable_if<!::std::is_same<delegate, typename ::std::decay<T>::type>::value>::type
>
delegate & operator=(T&& f)
{
using functor_type = typename ::std::decay<T>::type;
if ((sizeof(functor_type) > store_size_) || !store_.unique())
{
store_.reset(operator new(sizeof(functor_type)), functor_deleter<functor_type>);
store_size_ = sizeof(functor_type);
}
else
deleter_(store_.get());
new(store_.get()) functor_type(::std::forward<T>(f));
object_ptr_ = store_.get();
stub_ptr_ = functor_stub<functor_type>;
deleter_ = deleter_stub<functor_type>;
return *this;
}
template <R(* const function_ptr)(A...)>
static delegate from(void) noexcept
{
return { nullptr, function_stub<function_ptr> };
}
template <class C, R(C::* const method_ptr)(A...)>
static delegate from(C * const object_ptr) noexcept
{
return { object_ptr, method_stub<C, method_ptr> };
}
template <class C, R(C::* const method_ptr)(A...) const>
static delegate from(C const * const object_ptr) noexcept
{
return { const_cast<C *>(object_ptr), const_method_stub<C, method_ptr> };
}
template <class C, R(C::* const method_ptr)(A...)>
static delegate from(C & object) noexcept
{
return { &object, method_stub<C, method_ptr> };
}
template <class C, R(C::* const method_ptr)(A...) const>
static delegate from(C const & object) noexcept
{
return { const_cast<C *>(&object), const_method_stub<C, method_ptr> };
}
template <typename T>
static delegate from(T && f)
{
return ::std::forward<T>(f);
}
static delegate from(R(* const function_ptr)(A...))
{
return function_ptr;
}
template <class C>
using member_pair = ::std::pair<C * const, R(C::* const)(A...)>;
template <class C>
using const_member_pair = ::std::pair<C const * const, R(C::* const)(A...) const>;
template <class C>
static delegate from(C * const object_ptr, R(C::* const method_ptr)(A...))
{
return member_pair<C>(object_ptr, method_ptr);
}
template <class C>
static delegate from(C const * const object_ptr, R(C::* const method_ptr)(A...) const)
{
return const_member_pair<C>(object_ptr, method_ptr);
}
template <class C>
static delegate from(C & object, R(C::* const method_ptr)(A...))
{
return member_pair<C>(&object, method_ptr);
}
template <class C>
static delegate from(C const & object, R(C::* const method_ptr)(A...) const)
{
return const_member_pair<C>(&object, method_ptr);
}
void reset(void)
{
stub_ptr_ = nullptr;
store_.reset();
}
void reset_stub(void) noexcept { stub_ptr_ = nullptr; }
void swap(delegate & other) noexcept { ::std::swap(*this, other); }
bool operator==(delegate const & rhs) const noexcept
{
// comparison between functor and non-functor is left as undefined at the moment.
if (store_size_ && rhs.store_size_) // both functors
return (std::memcmp(store_.get(), rhs.store_.get(), store_size_) == 0) && (stub_ptr_ == rhs.stub_ptr_);
return (object_ptr_ == rhs.object_ptr_) && (stub_ptr_ == rhs.stub_ptr_);
}
bool operator!=(delegate const & rhs) const noexcept
{
return !operator==(rhs);
}
bool operator<(delegate const & rhs) const noexcept
{
return (object_ptr_ < rhs.object_ptr_) ||
((object_ptr_ == rhs.object_ptr_) && (stub_ptr_ < rhs.stub_ptr_));
}
bool operator==(::std::nullptr_t const) const noexcept
{
return !stub_ptr_;
}
bool operator!=(::std::nullptr_t const) const noexcept
{
return stub_ptr_;
}
explicit operator bool() const noexcept
{
return stub_ptr_;
}
R operator()(A... args) const
{
// assert(stub_ptr);
return stub_ptr_(object_ptr_, ::std::forward<A>(args)...);
}
private:
friend struct ::std::hash<delegate>;
using deleter_type = void (*)(void *);
void * object_ptr_ = nullptr;
stub_ptr_type stub_ptr_ {};
deleter_type deleter_ = nullptr;
::std::shared_ptr<void> store_ = nullptr;
::std::size_t store_size_ = 0;
template <class T>
static void functor_deleter(void * const p)
{
static_cast<T *>(p)->~T();
operator delete(p);
}
template <class T>
static void deleter_stub(void * const p)
{
static_cast<T *>(p)->~T();
}
template <R(*function_ptr)(A...)>
static R function_stub(void * const, A && ... args)
{
return function_ptr(::std::forward<A>(args)...);
}
template <class C, R(C::*method_ptr)(A...)>
static R method_stub(void * const object_ptr, A && ... args)
{
return (static_cast<C *>(object_ptr)->*method_ptr)(::std::forward<A>(args)...);
}
template <class C, R(C::*method_ptr)(A...) const>
static R const_method_stub(void * const object_ptr, A && ... args)
{
return (static_cast<C const *>(object_ptr)->*method_ptr)(::std::forward<A>(args)...);
}
template <typename>
struct is_member_pair : ::std::false_type { };
template <class C>
struct is_member_pair< ::std::pair<C * const, R(C::* const)(A...)> > : ::std::true_type {};
template <typename>
struct is_const_member_pair : ::std::false_type { };
template <class C>
struct is_const_member_pair< ::std::pair<C const * const, R(C::* const)(A...) const> > : ::std::true_type {};
template <typename T>
static typename ::std::enable_if<!(is_member_pair<T>::value || is_const_member_pair<T>::value), R>::type
functor_stub(void * const object_ptr, A && ... args)
{
return (*static_cast<T *>(object_ptr))(::std::forward<A>(args)...);
}
template <typename T>
static typename ::std::enable_if<is_member_pair<T>::value || is_const_member_pair<T>::value, R>::type
functor_stub(void * const object_ptr, A && ... args)
{
return (static_cast<T *>(object_ptr)->first->*static_cast<T *>(object_ptr)->second)(::std::forward<A>(args)...);
}
};
}
namespace std
{
template <typename R, typename ...A>
struct hash<::fastdelegate::delegate<R(A...)> >
{
size_t operator()(::fastdelegate::delegate<R(A...)> const & d) const noexcept
{
auto const seed(hash<void *>()(d.object_ptr_));
return hash<decltype(d.stub_ptr_)>()(d.stub_ptr_) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
}
};
}
#endif // SRDELEGATE_HPP

686
libs/Common/File.h

@ -0,0 +1,686 @@ @@ -0,0 +1,686 @@
////////////////////////////////////////////////////////////////////
// File.h
//
// Copyright 2007 cDc@seacave
// Distributed under the Boost Software License, Version 1.0
// (See http://www.boost.org/LICENSE_1_0.txt)
#ifndef __SEACAVE_FILE_H__
#define __SEACAVE_FILE_H__
// I N C L U D E S /////////////////////////////////////////////////
#include "Streams.h"
// Under both Windows and Unix, the stat function is used for classification
// Under Gnu/Linux, the following classifications are defined
// source: Gnu/Linux man page for stat(2) http://linux.die.net/man/2/stat
// S_IFMT 0170000 bitmask for the file type bitfields
// S_IFSOCK 0140000 socket (Note this overlaps with S_IFDIR)
// S_IFLNK 0120000 symbolic link
// S_IFREG 0100000 regular file
// S_IFBLK 0060000 block device
// S_IFDIR 0040000 directory
// S_IFCHR 0020000 character device
// S_IFIFO 0010000 FIFO
// There are also some Posix-standard macros:
// S_ISREG(m) is it a regular file?
// S_ISDIR(m) directory?
// S_ISCHR(m) character device?
// S_ISBLK(m) block device?
// S_ISFIFO(m) FIFO (named pipe)?
// S_ISLNK(m) symbolic link? (Not in POSIX.1-1996.)
// S_ISSOCK(m) socket? (Not in POSIX.1-1996.)
// Under Windows, the following are defined:
// source: Header file sys/stat.h distributed with Visual Studio 10
// _S_IFMT (S_IFMT) 0xF000 file type mask
// _S_IFREG (S_IFREG) 0x8000 regular
// _S_IFDIR (S_IFDIR) 0x4000 directory
// _S_IFCHR (S_IFCHR) 0x2000 character special
// _S_IFIFO 0x1000 pipe
#ifdef _MSC_VER
#include <io.h>
// file type tests are not defined for some reason on Windows despite them providing the stat() function!
#define F_OK 0
#define X_OK 1
#define W_OK 2
#define R_OK 4
// Posix-style macros for Windows
#ifndef S_ISREG
#define S_ISREG(mode) ((mode & _S_IFMT) == _S_IFREG)
#endif
#ifndef S_ISDIR
#define S_ISDIR(mode) ((mode & _S_IFMT) == _S_IFDIR)
#endif
#ifndef S_ISCHR
#define S_ISCHR(mode) ((mode & _S_IFMT) == _S_IFCHR)
#endif
#ifndef S_ISBLK
#define S_ISBLK(mode) (false)
#endif
#ifndef S_ISFIFO
#define S_ISFIFO(mode) ((mode & _S_IFMT) == _S_IFIFO)
#endif
#ifndef S_ISLNK
#define S_ISLNK(mode) (false)
#endif
#ifndef S_ISSOCK
#define S_ISSOCK(mode) (false)
#endif
#else
#include <unistd.h>
#include <dirent.h>
#define _taccess access
#endif
#ifdef __APPLE__
#define fdatasync fsync
#endif
// D E F I N E S ///////////////////////////////////////////////////
// size of the stored file size variable
#ifdef LARGEFILESIZE
#define FILESIZE size_f_t
#else
#define FILESIZE size_t
#endif
// invalid file handle
#ifdef _MSC_VER
#define FILE_INVALID_HANDLE INVALID_HANDLE_VALUE
#else
#define FILE_INVALID_HANDLE int(-1)
#endif
namespace SEACAVE {
// S T R U C T S ///////////////////////////////////////////////////
class GENERAL_API File : public IOStream {
public:
typedef struct FILEINFO_TYPE {
String path;
FILESIZE size;
DWORD attrib;
} FILEINFO;
typedef cList<FILEINFO> FileInfoArr;
typedef enum FMCREATE_TYPE {
OPEN = 0x01,
CREATE = 0x02,
TRUNCATE = 0x04
} FMCREATE;
typedef enum FMFLAGS_TYPE {
SYNC = 0x01,
NOBUFFER = 0x02,
RANDOM = 0x04,
SEQUENTIAL = 0x08
} FMFLAGS;
inline File() : h(FILE_INVALID_HANDLE) {
#ifndef _RELEASE
breakRead = -1;
breakWrite = -1;
#endif
}
inline File(LPCTSTR aFileName, int access, int mode, int flags=0) : h(FILE_INVALID_HANDLE) {
#ifndef _RELEASE
breakRead = -1;
breakWrite = -1;
#endif
File::open(aFileName, access, mode, flags);
}
virtual ~File() {
File::close();
}
#ifdef _SUPPORT_CPP11
inline File(File&& rhs) : h(rhs.h) {
#ifndef _RELEASE
breakRead = rhs.breakRead;
breakWrite = rhs.breakWrite;
#endif
rhs.h = FILE_INVALID_HANDLE;
}
inline File& operator=(File&& rhs) {
h = rhs.h;
#ifndef _RELEASE
breakRead = rhs.breakRead;
breakWrite = rhs.breakWrite;
#endif
rhs.h = FILE_INVALID_HANDLE;
return *this;
}
#endif
bool isOpen() const {
return h != FILE_INVALID_HANDLE;
}
#ifdef _MSC_VER
typedef enum FMACCESS_TYPE {
READ = GENERIC_READ,
WRITE = GENERIC_WRITE,
RW = READ | WRITE
} FMACCESS;
typedef enum FMCHECKACCESS_TYPE {
CA_EXIST = F_OK, // existence
CA_WRITE = W_OK, // write
CA_READ = R_OK, // read
CA_RW = R_OK | W_OK,
} FMCHECKACCESS;
/**
* Open the file specified.
* If there are errors, h is set to FILE_INVALID_HANDLE.
* Use isOpen() to check.
*/
virtual void open(LPCTSTR aFileName, int access, int mode, int flags=0) {
ASSERT(access == WRITE || access == READ || access == (READ | WRITE));
close();
DWORD m = 0;
if (mode & OPEN) {
if (mode & CREATE) {
m = (mode & TRUNCATE) ? CREATE_ALWAYS : OPEN_ALWAYS;
} else {
m = (mode & TRUNCATE) ? TRUNCATE_EXISTING : OPEN_EXISTING;
}
} else {
ASSERT(mode & CREATE);
m = (mode & TRUNCATE) ? CREATE_ALWAYS : CREATE_NEW;
}
DWORD f = 0;
if (flags & SYNC)
f |= FILE_FLAG_WRITE_THROUGH;
if (flags & NOBUFFER)
f |= FILE_FLAG_NO_BUFFERING;
if (flags & RANDOM)
f |= FILE_FLAG_RANDOM_ACCESS;
if (flags & SEQUENTIAL)
f |= FILE_FLAG_SEQUENTIAL_SCAN;
h = ::CreateFile(aFileName, access, FILE_SHARE_READ, NULL, m, f, NULL);
}
virtual void close() {
if (isOpen()) {
FlushFileBuffers(h);
CloseHandle(h);
h = FILE_INVALID_HANDLE;
}
}
uint32_t getLastModified() {
ASSERT(isOpen());
FILETIME f = {0};
::GetFileTime(h, NULL, NULL, &f);
return convertTime(&f);
}
static uint32_t convertTime(FILETIME* f) {
SYSTEMTIME s = { 1970, 1, 0, 1, 0, 0, 0, 0 };
FILETIME f2 = {0};
if (::SystemTimeToFileTime(&s, &f2)) {
uint64_t* a = (uint64_t*)f;
uint64_t* b = (uint64_t*)&f2;
*a -= *b;
*a /= (1000LL*1000LL*1000LL/100LL); // 100ns > s
return (uint32_t)*a;
}
return 0;
}
size_f_t getSize() const override {
ASSERT(isOpen());
DWORD x;
DWORD l = ::GetFileSize(h, &x);
if ((l == INVALID_FILE_SIZE) && (GetLastError() != NO_ERROR))
return SIZE_NA;
return (size_f_t)l | ((size_f_t)x)<<32;
}
virtual bool setSize(size_f_t newSize) {
const size_f_t pos = getPos();
if (pos == SIZE_NA)
return false;
if (!setPos(newSize))
return false;
if (!setEOF())
return false;
if (!setPos(pos))
return false;
return true;
}
size_f_t getPos() const override {
ASSERT(isOpen());
LONG x = 0;
const DWORD l = ::SetFilePointer(h, 0, &x, FILE_CURRENT);
if (l == INVALID_SET_FILE_POINTER)
return SIZE_NA;
return (size_f_t)l | ((size_f_t)x)<<32;
}
bool setPos(size_f_t pos) override {
ASSERT(isOpen());
LONG x = (LONG) (pos>>32);
return (::SetFilePointer(h, (DWORD)(pos & 0xffffffff), &x, FILE_BEGIN) != INVALID_SET_FILE_POINTER);
}
virtual bool setEndPos(size_f_t pos) {
ASSERT(isOpen());
LONG x = (LONG) (pos>>32);
return (::SetFilePointer(h, (DWORD)(pos & 0xffffffff), &x, FILE_END) != INVALID_SET_FILE_POINTER);
}
virtual bool movePos(size_f_t pos) {
ASSERT(isOpen());
LONG x = (LONG) (pos>>32);
return (::SetFilePointer(h, (DWORD)(pos & 0xffffffff), &x, FILE_CURRENT) != INVALID_SET_FILE_POINTER);
}
size_t read(void* buf, size_t len) override {
ASSERT(isOpen());
#ifndef _RELEASE
if (breakRead != (size_t)(-1)) {
if (breakRead <= len) {
ASSERT("FILE::read() break" == NULL);
breakRead = -1;
} else {
breakRead -= len;
}
}
#endif
DWORD x;
if (!::ReadFile(h, buf, (DWORD)len, &x, NULL))
return STREAM_ERROR;
return x;
}
size_t write(const void* buf, size_t len) override {
ASSERT(isOpen());
#ifndef _RELEASE
if (breakWrite != (size_t)(-1)) {
if (breakWrite <= len) {
ASSERT("FILE::write() break" == NULL);
breakWrite = -1;
} else {
breakWrite -= len;
}
}
#endif
DWORD x;
if (!::WriteFile(h, buf, (DWORD)len, &x, NULL))
return STREAM_ERROR;
ASSERT(x == len);
return x;
}
virtual bool setEOF() {
ASSERT(isOpen());
return (SetEndOfFile(h) != FALSE);
}
size_t flush() override {
ASSERT(isOpen());
return (FlushFileBuffers(h) ? 0 : STREAM_ERROR);
}
virtual bool getInfo(BY_HANDLE_FILE_INFORMATION* fileInfo) {
ASSERT(isOpen());
return (GetFileInformationByHandle(h, fileInfo) != FALSE);
}
static uint32_t getAttrib(LPCTSTR aFileName) {
return GetFileAttributes(aFileName);
}
static bool setAttrib(LPCTSTR aFileName, uint32_t attribs) {
return (SetFileAttributes(aFileName, attribs) != FALSE);
}
static void deleteFile(LPCTSTR aFileName) { ::DeleteFile(aFileName); }
static bool renameFile(LPCTSTR source, LPCTSTR target) {
if (!::MoveFile(source, target)) {
// Can't move, try copy/delete...
if (!::CopyFile(source, target, FALSE))
return false;
deleteFile(source);
}
return true;
}
static bool copyFile(LPCTSTR source, LPCTSTR target) { return ::CopyFile(source, target, FALSE) == TRUE; }
static size_f_t getSize(LPCTSTR aFileName) {
const HANDLE fh = ::CreateFile(aFileName, FILE_READ_ATTRIBUTES, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_NO_BUFFERING, NULL);
if (fh == FILE_INVALID_HANDLE)
return SIZE_NA;
DWORD x;
DWORD l = ::GetFileSize(fh, &x);
CloseHandle(fh);
if ((l == INVALID_FILE_SIZE) && (GetLastError() != NO_ERROR))
return SIZE_NA;
return (((size_f_t)l) | (((size_f_t)x)<<32));
}
static size_f_t findFiles(const String& _strPath, const String& strMask, bool bProcessSubdir, FileInfoArr& arrFiles) {
// List all the files.
WIN32_FIND_DATA fd;
HANDLE hFind;
size_f_t totalSize = 0;
String strPath(_strPath);
Util::ensureFolderSlash(strPath);
//Find all the files in this folder.
hFind = FindFirstFile((strPath + strMask).c_str(), &fd);
if (hFind != FILE_INVALID_HANDLE) {
do {
// this is a file that can be used
if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
continue;
// Store the file name.
FILEINFO& fileInfo = arrFiles.AddEmpty();
fileInfo.path = strPath + fd.cFileName;
#ifdef LARGEFILESIZE
fileInfo.size = (((size_f_t)fd.nFileSizeLow) | (((size_f_t)fd.nFileSizeHigh)<<32));
#else
fileInfo.size = fd.nFileSizeLow;
#endif
fileInfo.attrib = fd.dwFileAttributes;
totalSize += fileInfo.size;
} while (FindNextFile(hFind, &fd));
FindClose(hFind);
}
//Process the subfolders also...
if (!bProcessSubdir)
return totalSize;
hFind = FindFirstFile((strPath + '*').c_str(), &fd);
if (hFind != FILE_INVALID_HANDLE) {
do {
// if SUBDIR then process that too
if (!(fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
continue;
if (!_tcscmp(fd.cFileName, _T(".")))
continue;
if (!_tcscmp(fd.cFileName, _T("..")))
continue;
// Process all subfolders recursively
totalSize += findFiles(strPath + fd.cFileName + PATH_SEPARATOR, strMask, true, arrFiles);
} while (FindNextFile(hFind, &fd));
FindClose(hFind);
}
return totalSize;
}
#else // _MSC_VER
typedef enum FMACCESS_TYPE {
READ = 0x01,
WRITE = 0x02,
RW = READ | WRITE,
} FMACCESS;
typedef enum FMCHECKACCESS_TYPE {
CA_EXIST = F_OK, // existence
CA_WRITE = W_OK, // write
CA_READ = R_OK, // read
CA_RW = R_OK | W_OK,
CA_EXEC = X_OK, // execute
} FMCHECKACCESS;
/**
* Open the file specified.
* If there are errors, h is set to FILE_INVALID_HANDLE.
* Use isOpen() to check.
*/
virtual void open(LPCTSTR aFileName, int access, int mode, int flags=0) {
ASSERT(access == WRITE || access == READ || access == (READ | WRITE));
close();
int m = 0;
if (access == READ)
m |= O_RDONLY;
else if (access == WRITE)
m |= O_WRONLY;
else
m |= O_RDWR;
if (mode & CREATE)
m |= O_CREAT;
if (mode & TRUNCATE)
m |= O_TRUNC;
if (flags & SYNC)
m |= O_DSYNC;
#ifndef __APPLE__
if (flags & NOBUFFER)
m |= O_DIRECT;
#endif
h = ::open(aFileName, m, S_IRUSR | S_IWUSR);
}
virtual void close() {
if (h != FILE_INVALID_HANDLE) {
::close(h);
h = FILE_INVALID_HANDLE;
}
}
uint32_t getLastModified() {
ASSERT(isOpen());
struct stat s;
if (::fstat(h, &s) == -1)
return 0;
return (uint32_t)s.st_mtime;
}
size_f_t getSize() const override {
ASSERT(isOpen());
struct stat s;
if (::fstat(h, &s) == -1)
return SIZE_NA;
return (size_f_t)s.st_size;
}
size_f_t getPos() const override {
ASSERT(isOpen());
return (size_f_t)lseek(h, 0, SEEK_CUR);
}
bool setPos(size_f_t pos) override { ASSERT(isOpen()); return lseek(h, (off_t)pos, SEEK_SET) != (off_t)-1; }
virtual bool setEndPos(size_f_t pos) { ASSERT(isOpen()); return lseek(h, (off_t)pos, SEEK_END) != (off_t)-1; }
virtual bool movePos(size_f_t pos) { ASSERT(isOpen()); return lseek(h, (off_t)pos, SEEK_CUR) != (off_t)-1; }
size_t read(void* buf, size_t len) override {
ASSERT(isOpen());
#ifndef _RELEASE
if (breakRead != (size_t)(-1)) {
if (breakRead <= len) {
ASSERT("FILE::read() break" == NULL);
breakRead = -1;
} else {
breakRead -= len;
}
}
#endif
ssize_t x = ::read(h, buf, len);
if (x == -1)
return STREAM_ERROR;
return (size_t)x;
}
size_t write(const void* buf, size_t len) override {
ASSERT(isOpen());
#ifndef _RELEASE
if (breakWrite != (size_t)(-1)) {
if (breakWrite <= len) {
ASSERT("FILE::write() break" == NULL);
breakWrite = -1;
} else {
breakWrite -= len;
}
}
#endif
ssize_t x = ::write(h, buf, len);
if (x == -1)
return STREAM_ERROR;
if (x < (ssize_t)len)
return STREAM_ERROR;
return x;
}
virtual bool setEOF() {
ASSERT(isOpen());
return (ftruncate(h, (off_t)getPos()) != -1);
}
virtual bool setSize(size_f_t newSize) {
ASSERT(isOpen());
return (ftruncate(h, (off_t)newSize) != -1);
}
size_t flush() override {
ASSERT(isOpen());
return fdatasync(h);
}
static void deleteFile(LPCTSTR aFileName) { ::remove(aFileName); }
static bool renameFile(LPCTSTR source, LPCTSTR target) { return ::rename(source, target) == 0; }
static bool copyFile(LPCTSTR source, LPCTSTR target) {
std::ifstream src(source, std::ios::binary);
if (!src.is_open())
return false;
std::ofstream dst(target, std::ios::binary);
if (!dst.is_open())
return false;
dst << src.rdbuf();
return true;
}
static size_f_t getSize(LPCTSTR aFileName) {
struct stat buf;
if (stat(aFileName, &buf) != 0)
return SIZE_NA;
return buf.st_size;
}
#endif // _MSC_VER
// test for whether there's something (i.e. folder or file) with this name
// and what access mode is supported
static bool isPresent(LPCTSTR path) {
struct stat buf;
return stat(path, &buf) == 0;
}
static bool access(LPCTSTR path, int mode=CA_EXIST) {
return ::_taccess(path, mode) == 0;
}
// test for whether there's something present and its a folder
static bool isFolder(LPCTSTR path) {
struct stat buf;
if (!(stat(path, &buf) == 0))
return false;
// If the object is present, see if it is a directory
// this is the Posix-approved way of testing
return S_ISDIR(buf.st_mode);
}
// test for whether there's something present and its a file
// a file can be a regular file, a symbolic link, a FIFO or a socket, but not a device
static bool isFile(LPCTSTR path) {
struct stat buf;
if (!(stat(path, &buf) == 0))
return false;
// If the object is present, see if it is a file or file-like object
// Note that devices are neither folders nor files
// this is the Posix-approved way of testing
return S_ISREG(buf.st_mode) || S_ISLNK(buf.st_mode) || S_ISSOCK(buf.st_mode) || S_ISFIFO(buf.st_mode);
}
// time the file was originally created
static time_t getCreated(LPCTSTR path) {
struct stat buf;
if (stat(path, &buf) != 0) return 0;
return buf.st_ctime;
}
// time the file was last modified
static time_t getModified(LPCTSTR path) {
struct stat buf;
if (stat(path, &buf) != 0) return 0;
return buf.st_mtime;
}
// time the file was accessed
static time_t getAccessed(LPCTSTR path) {
struct stat buf;
if (stat(path, &buf) != 0) return 0;
return buf.st_atime;
}
// set the current folder
static bool setCurrentFolder(LPCTSTR path) {
if (!isFolder(path))
return false;
#ifdef _MSC_VER
// Windows implementation - this returns non-zero for success
return (SetCurrentDirectory(path) != 0);
#else
// Unix implementation - this returns zero for success
return (chdir(path) == 0);
#endif
}
template <class VECTOR>
inline size_t write(const VECTOR& arr) {
const typename VECTOR::IDX nSize(arr.GetSize());
size_t nBytes(write(&nSize, sizeof(typename VECTOR::IDX)));
nBytes += write(arr.GetData(), arr.GetDataSize());
return nBytes;
}
template <class VECTOR>
inline size_t read(VECTOR& arr) {
typename VECTOR::IDX nSize;
size_t nBytes(read(&nSize, sizeof(typename VECTOR::IDX)));
arr.Resize(nSize);
nBytes += read(arr.GetData(), arr.GetDataSize());
return nBytes;
}
enum { LAYER_ID_IN=3 };
InputStream* getInputStream(int typ=InputStream::LAYER_ID_IN) override { return (typ == LAYER_ID_IN ? static_cast<InputStream*>(this) : IOStream::getInputStream(typ)); }
enum { LAYER_ID_OUT=3 };
OutputStream* getOutputStream(int typ=OutputStream::LAYER_ID_OUT) override { return (typ == LAYER_ID_OUT ? static_cast<OutputStream*>(this) : IOStream::getOutputStream(typ)); }
protected:
#ifdef _MSC_VER
HANDLE h;
#else
int h;
#endif
public:
#ifndef _RELEASE
size_t breakRead;
size_t breakWrite;
#endif
private:
File(const File&);
File& operator=(const File&);
};
typedef File* LPFILE;
/*----------------------------------------------------------------*/
} // namespace SEACAVE
#endif // __SEACAVE_FILE_H__

612
libs/Common/Filters.h

@ -0,0 +1,612 @@ @@ -0,0 +1,612 @@
////////////////////////////////////////////////////////////////////
// Filters.h
//
// Copyright 2007 cDc@seacave
// Distributed under the Boost Software License, Version 1.0
// (See http://www.boost.org/LICENSE_1_0.txt)
#ifndef __SEACAVE_FILTERS_H__
#define __SEACAVE_FILTERS_H__
// I N C L U D E S /////////////////////////////////////////////////
#include "Streams.h"
// D E F I N E S ///////////////////////////////////////////////////
namespace SEACAVE {
// S T R U C T S ///////////////////////////////////////////////////
template<bool managed=true>
class BufferedInputStream : public LayerInputStream<managed> {
public:
typedef LayerInputStream<managed> Base;
BufferedInputStream(InputStream* aStream, size_t aBufSize)
: Base(aStream), buf(new uint8_t[aBufSize]), bufSize(aBufSize), cache(0), pos(0) { ASSERT(aBufSize > 0); }
virtual ~BufferedInputStream() {
delete[] buf;
}
size_t read(void* wbuf, size_t len) override {
uint8_t* b = (uint8_t*)wbuf;
const size_t l2 = len;
do {
ASSERT(pos <= cache);
if (pos == cache) {
if (len >= bufSize) {
const size_t r = Base::read(b, len);
if (r == STREAM_ERROR)
return STREAM_ERROR;
return l2 - len + r;
}
pos = 0;
switch (cache = Base::read(buf, bufSize)) {
case 0:
return l2 - len;
case STREAM_ERROR:
return STREAM_ERROR;
}
}
const size_t n = MINF(cache - pos, len);
memcpy(b, buf + pos, n);
b += n;
pos += n;
len -= n;
} while (len > 0);
return l2;
}
bool setPos(size_f_t wpos) override {
pos = cache = 0;
return Base::setPos(wpos);
}
size_f_t getPos() const override {
const size_f_t r = Base::getPos();
if (r == SIZE_NA)
return SIZE_NA;
return r-(cache-pos);
}
enum { LAYER_ID_IN=1 };
InputStream* getInputStream(int typ=InputStream::LAYER_ID_IN) override { return (typ == LAYER_ID_IN ? static_cast<InputStream*>(this) : Base::getInputStream(typ)); }
private:
uint8_t* const buf;
const size_t bufSize;
size_t cache;
size_t pos;
};
template<bool managed=true>
class BufferedOutputStream : public LayerOutputStream<managed> {
public:
typedef LayerOutputStream<managed> Base;
BufferedOutputStream(OutputStream* aStream, size_t aBufSize)
: Base(aStream), buf(new uint8_t[aBufSize]), bufSize(aBufSize), pos(0) { }
virtual ~BufferedOutputStream() {
flush();
delete[] buf;
}
size_t write(const void* wbuf, size_t len) override {
if (len < bufSize - pos) {
memcpy(buf + pos, (const uint8_t*)wbuf, len);
pos += len;
return len;
}
uint8_t* b = (uint8_t*)wbuf;
const size_t l2 = len;
do {
if (pos == bufSize) {
if (Base::write(buf, bufSize) == STREAM_ERROR)
return STREAM_ERROR;
pos = 0;
if (len < bufSize) {
if (Base::write(b, len) == STREAM_ERROR)
return STREAM_ERROR;
break;
}
}
const size_t n = MINF(bufSize - pos, len);
memcpy(buf + pos, b, n);
b += n;
pos += n;
len -= n;
} while (len > 0);
return l2;
}
size_f_t getSize() const override {
const size_f_t r = Base::getSize();
if (r == SIZE_NA)
return SIZE_NA;
return r + pos;
}
bool setPos(size_f_t wpos) override {
if (pos > 0) {
const size_t ret = Base::write(buf, pos);
pos = 0;
if (ret == STREAM_ERROR)
return false;
}
return Base::setPos(wpos);
}
size_f_t getPos() const override {
const size_f_t r = Base::getPos();
if (r == SIZE_NA)
return SIZE_NA;
return r + pos;
}
size_t flush() override {
size_t ret = 0;
if (pos > 0) {
ret = Base::write(buf, pos);
pos = 0;
if (ret == STREAM_ERROR)
return STREAM_ERROR;
}
return ret + Base::flush();
}
enum { LAYER_ID_OUT=1 };
OutputStream* getOutputStream(int typ=OutputStream::LAYER_ID_OUT) override { return (typ == LAYER_ID_OUT ? static_cast<OutputStream*>(this) : Base::getOutputStream(typ)); }
private:
uint8_t* const buf;
const size_t bufSize;
size_t pos;
};
/*----------------------------------------------------------------*/
template<class Filter, bool managed=true>
class FilteredInputStream : public LayerInputStream<managed> {
public:
typedef LayerInputStream<managed> Base;
FilteredInputStream(InputStream* pFile, size_t nFilteredSize, size_t nBufSize, void* pData)
: Base(pFile), buf(new uint8_t[nBufSize]), filteredSize(nFilteredSize), bufSize(nBufSize), valid(0), pos(0), filter(pData), more(true) {}
virtual ~FilteredInputStream() {
delete[] buf;
}
/**
* Read data through filter, keep calling until len returns 0.
* @param rbuf Data buffer
* @param len Buffer size on entry, bytes actually read on exit
* @return Length of data in buffer
*/
size_t read(void* rbuf, size_t len) override {
uint8_t* rb = (uint8_t*)rbuf;
const size_t l2 = len;
while (more && len) {
if (pos == valid) {
valid = Base::read(buf, bufSize);
if (valid == STREAM_ERROR)
return STREAM_ERROR;
pos = 0;
}
size_t m = valid - pos;
size_t n = len;
more = filter(buf + pos, m, rb, n);
pos += m;
rb += n;
len -= n;
}
return l2-len;
}
size_f_t getSize() const override {
return filteredSize;
}
bool setPos(size_f_t wpos) override {
valid = pos = 0;
return Base::setPos(wpos);
}
size_f_t getPos() const override {
const size_f_t r = Base::getPos();
if (r == SIZE_NA)
return SIZE_NA;
return r-(valid-pos);
}
enum { LAYER_ID_IN=2 };
InputStream* getInputStream(int typ=InputStream::LAYER_ID_IN) override { return (typ == LAYER_ID_IN ? static_cast<InputStream*>(this) : Base::getInputStream(typ)); }
private:
uint8_t* const buf;
const size_t filteredSize;
const size_t bufSize;
size_t valid;
size_t pos;
Filter filter;
bool more;
};
template<class Filter, bool managed=true>
class FilteredOutputStream : public LayerOutputStream<managed> {
public:
typedef LayerOutputStream<managed> Base;
FilteredOutputStream(OutputStream* aFile, size_t aBufSize, void* pData)
: Base(aFile), buf(new uint8_t[aBufSize]), bufSize(aBufSize), filter(pData), flushed(false) {}
virtual ~FilteredOutputStream() {
flush();
delete[] buf;
}
size_t write(const void* wbuf, size_t len) override {
if (flushed)
return STREAM_ERROR;
const uint8_t* wb = (const uint8_t*)wbuf;
size_t written = 0;
while (len > 0) {
size_t n = bufSize;
size_t m = len;
const bool more = filter(wb, m, buf, n);
wb += m;
len -= m;
const size_t r = Base::write(buf, n);
if (r == STREAM_ERROR)
return STREAM_ERROR;
written += r;
if (!more) {
if (len > 0)
return STREAM_ERROR;
flushed = true;
return written;
}
}
return written;
}
size_t flush() override {
if (flushed)
return Base::flush();
flushed = true;
size_t written = 0;
while (true) {
size_t n = bufSize;
size_t zero = 0;
bool more = filter(NULL, zero, buf, n);
written += Base::write(buf, n);
if (!more)
break;
}
const size_t r = Base::flush();
if (r == STREAM_ERROR)
return STREAM_ERROR;
return written + r;
}
enum { LAYER_ID_OUT=2 };
OutputStream* getOutputStream(int typ=OutputStream::LAYER_ID_OUT) override { return (typ == LAYER_ID_OUT ? static_cast<OutputStream*>(this) : Base::getOutputStream(typ)); }
private:
uint8_t* const buf;
const size_t bufSize;
Filter filter;
bool flushed;
};
/*----------------------------------------------------------------*/
#ifdef _UNICODE
#define TOKEN_SIZE 2 //in bytes
#else
#define TOKEN_SIZE 1 //in bytes
#endif
#define TOKEN_MAXBUF (2048*TOKEN_SIZE) //in bytes
#define TOKEN_MAXIGN 32 //in TCHARs
template<bool managed=true>
class TokenInputStream : public LayerInputStream<managed> {
public:
typedef LayerInputStream<managed> Base;
TokenInputStream(InputStream* aStream, TCHAR aToken=_T('\n'))
: Base(aStream), pos(TOKEN_MAXBUF), eos(false), token(aToken) { arrIgnore[0] = _T('\0'); }
virtual ~TokenInputStream() {
}
void emptyBuffer() {
setPos(getPos());
}
TCHAR getToken() const {
return token;
}
void setToken(TCHAR aToken) {
token = aToken;
}
LPCTSTR getTrimTokens() const {
return arrIgnore;
}
size_t setTrimTokens(LPCTSTR szIgnore) {
const size_t len = _tcslen(szIgnore);
if (len >= TOKEN_MAXIGN)
return 0;
_tcscpy(arrIgnore, szIgnore);
return len;
}
LPTSTR trimFrontLine(LPTSTR line) {
while (line[0] != _T('\0')) {
if (_tcschr(arrIgnore, line[0]) == NULL)
return line;
++line;
}
return line;
}
LPTSTR trimFrontLine(MemFile& memFile) {
memFile.ensureSize(1);
LPTSTR line = (LPTSTR)memFile.getData();
line[memFile.getSizeLeft()] = _T('\0');
LPTSTR newLine = trimFrontLine(line);
memFile.movePos(newLine-line);
return newLine;
}
size_t trimBackLine(LPTSTR line) {
const size_t len = _tcslen(line);
size_t i = len;
while (i > 0 && line[--i] != _T('\0')) {
if (_tcschr(arrIgnore, line[i]) == NULL)
return len-(i+1);
line[i] = _T('\0');
}
return len;
}
size_t trimBackLine(MemFile& memFile) {
memFile.ensureSize(1);
LPTSTR line = (LPTSTR)memFile.getData();
line[memFile.getSizeLeft()] = _T('\0');
const size_t trimedSize = trimBackLine(line);
memFile.moveSize(-((size_f_t)trimedSize));
return trimedSize;
}
// revert the deleted token during the last readLine()
void restoreToken() {
LPTSTR line = (LPTSTR)buf;
ASSERT(pos > 0);
line[--pos] = token;
}
size_t readLine(LPTSTR wbuf, size_t len) {
if (eos)
return 0;
uint8_t* b = (uint8_t*)wbuf;
const size_t l2 = len;
// process chunks of TOKEN_MAXBUF bytes
while (len >= TOKEN_MAXBUF / TOKEN_SIZE) {
const size_t n = read(b, TOKEN_MAXBUF);
if (n == STREAM_ERROR)
return STREAM_ERROR;
*((TCHAR*)(b+n)) = _T('\0');
LPTSTR t = _tcschr((TCHAR*)b, token);
// if token found...
if (t != NULL) {
// ... set the end of line and return it
t[0] = _T('\0');
if (n == TOKEN_SIZE && len != 1) {
eos = true;
return l2-len;
}
++t;
const size_t bytesParsed = (uint8_t*)t-b;
const size_t bytesNotParsed = n - bytesParsed;
pos = TOKEN_MAXBUF - bytesNotParsed;
// store the unprocessed data
memcpy(buf+pos, (uint8_t*)t, bytesNotParsed);
len -= (bytesParsed - TOKEN_SIZE) / TOKEN_SIZE;
return l2-len;
}
len -= n / TOKEN_SIZE;
// if end of stream return
if (n < TOKEN_MAXBUF) {
eos = true;
return l2-len;
}
b += n;
// if we reached the end of the buffer, signal it
if (len == 0)
return l2+1;
}
// process the last sub-chunk part
const size_t n = read(b, len*TOKEN_SIZE);
if (n == STREAM_ERROR)
return STREAM_ERROR;
*((TCHAR*)(b+n)) = _T('\0');
LPTSTR t = _tcschr((TCHAR*)b, token);
// if token found...
if (t != NULL) {
// ... set the end of line and return it
t[0] = _T('\0');
if (n == TOKEN_SIZE && len != 1) {
eos = true;
return l2-len;
}
++t;
const size_t bytesParsed = (uint8_t*)t-b;
const size_t bytesNotParsed = n - bytesParsed;
pos = TOKEN_MAXBUF - bytesNotParsed;
// store the unprocessed data
memcpy(buf+pos, (uint8_t*)t, bytesNotParsed);
len -= (bytesParsed - TOKEN_SIZE) / TOKEN_SIZE;
return l2-len;
}
// if end of stream return
if (n < len * TOKEN_SIZE) {
eos = true;
return n / TOKEN_SIZE;
}
// we reached the end of the buffer, signal it
return l2+1;
}
size_t readLine(MemFile& memFile)
{
if (eos)
return 0;
// make sure we read one full line
const size_f_t oldSize = memFile.getSize();
while (true) {
memFile.ensureSize((4096+1)*TOKEN_SIZE);
const size_t ret = readLine((LPTSTR)(memFile.getBuffer()+memFile.getSize()), 4096);
if (ret == STREAM_ERROR)
return STREAM_ERROR;
if (ret <= 4096) {
memFile.growSize(ret*TOKEN_SIZE);
break;
}
memFile.growSize(4096*TOKEN_SIZE);
}
return size_t(memFile.getSize()-oldSize);
}
// read all to the memfile
size_t read(MemFile& memFile)
{
if (eos)
return 0;
const size_t len = (size_t)(Base::getSize() - getPos());
memFile.ensureSize(len);
const size_t ret = read(memFile.getBuffer()+memFile.getSize(), len);
if (ret == STREAM_ERROR)
return STREAM_ERROR;
memFile.growSize(ret);
return ret;
}
size_t read(void* wbuf, size_t len) override {
size_t n = 0;
if (pos < TOKEN_MAXBUF) {
n = TOKEN_MAXBUF-pos;
if (n > len)
n = len;
memcpy(wbuf, buf+pos, n);
len -= n;
pos += n;
}
if (len == 0)
return n;
const size_t r = Base::read((uint8_t*)wbuf+n, len);
if (r == STREAM_ERROR)
return STREAM_ERROR;
return n+r;
}
bool setPos(size_f_t wpos) override {
eos = false;
pos = TOKEN_MAXBUF;
return Base::setPos(wpos);
}
size_f_t getPos() const override {
const size_f_t r = Base::getPos();
if (r == SIZE_NA)
return SIZE_NA;
return r-(TOKEN_MAXBUF-pos);
}
bool isEOS() const {
return eos;
}
enum { LAYER_ID_IN=5 };
InputStream* getInputStream(int typ=InputStream::LAYER_ID_IN) override { return (typ == LAYER_ID_IN ? static_cast<InputStream*>(this) : Base::getInputStream(typ)); }
private:
uint8_t buf[TOKEN_MAXBUF+TOKEN_SIZE];
size_t pos;
bool eos;
TCHAR token;
TCHAR arrIgnore[TOKEN_MAXIGN];
};
/*----------------------------------------------------------------*/
template<bool managed=true>
class MaskInputStream : public LayerInputStream<managed> {
public:
typedef LayerInputStream<managed> Base;
MaskInputStream(InputStream* aStream, size_f_t nPos, size_f_t nSize)
: Base(aStream), startPos(nPos), size(nSize), pos(0) { }
virtual ~MaskInputStream() { }
size_t read(void* wbuf, size_t len) override {
if (pos >= size)
return 0;
if (!Base::setPos(startPos + pos))
return STREAM_ERROR;
if (pos+(size_f_t)len > size)
len = (size_t)(size - pos);
const size_t r = Base::read(wbuf, len);
if (r == STREAM_ERROR)
return STREAM_ERROR;
pos += r;
return r;
}
size_f_t getSize() const override {
return size;
}
bool setPos(size_f_t wpos) override {
if (wpos > size)
pos = size;
else
pos = wpos;
return true;
}
size_f_t getPos() const override {
return pos;
}
enum { LAYER_ID_IN=6 };
InputStream* getInputStream(int typ=InputStream::LAYER_ID_IN) override { return (typ == LAYER_ID_IN ? static_cast<InputStream*>(this) : Base::getInputStream(typ)); }
private:
const size_f_t size;
const size_f_t startPos;
size_f_t pos;
};
/*----------------------------------------------------------------*/
} // namespace SEACAVE
#endif // __SEACAVE_FILTERS_H__

457
libs/Common/HTMLDoc.h

@ -0,0 +1,457 @@ @@ -0,0 +1,457 @@
/*
* Modified version of:
*
* @file htmlDoc.h
* @brief Simple HTML document writer and SVG drawer
* @author Pierre MOULON
*
* Copyright (c) 2011, 2012, 2013 Pierre MOULON
* All rights reserved.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
#ifndef _HTMLDOC_H_
#define _HTMLDOC_H_
// I N C L U D E S /////////////////////////////////////////////////
// D E F I N E S ///////////////////////////////////////////////////
#define JSXCHART_BORDER 0.2f
// S T R U C T S ///////////////////////////////////////////////////
namespace HTML {
inline const std::string htmlMarkup(const std::string& markup, const std::string& text) {
std::ostringstream os;
os << '<'<< markup<<'>' << text << "</"<<markup<<'>' <<"\n";
return os.str();
}
inline const std::string htmlMarkup(const std::string& markup, const std::string& attributes, const std::string& text) {
std::ostringstream os;
os << '<'<<markup<<' '<<attributes<<'>' << text << "</"<<markup<<'>' <<"\n";
return os.str();
}
inline const std::string htmlOpenMarkup(const std::string& markup, const std::string& attributes) {
std::ostringstream os;
os << '<'<<markup<<' '<<attributes<<"/>" <<"\n";
return os.str();
}
inline const std::string htmlComment(const std::string& text) {
std::ostringstream os;
os << "<!-- "<< text << " -->" << "\n";
return os.str();
}
/// Return a chain in the form attributes="val"
template<typename T>
inline const std::string quotedAttributes(const std::string& attributes, const T & val) {
std::ostringstream os;
os << attributes << "='" << val << '\'';
return os.str();
}
/// Return a chain of the value T
template<typename T>
inline const std::string toString(const T& val) {
std::ostringstream os;
os << val;
return os.str();
}
class htmlDocumentStream
{
public:
htmlDocumentStream(const std::string& title) {
htmlStream << "\n";
htmlStream << htmlMarkup("head",
"\n"
"<link rel='stylesheet' type='text/css' href='http://jsxgraph.uni-bayreuth.de/distrib/jsxgraph.css' />\n"
"<script type='text/javascript' src='http://jsxgraph.uni-bayreuth.de/distrib/jsxgraphcore-0.96.js'></script>\n");
htmlStream << htmlMarkup("title",title);
}
htmlDocumentStream(const std::string& title,
const std::vector<std::string>& vec_css,
const std::vector<std::string>& vec_js)
{
htmlStream << "\n<head>\n";
htmlStream << htmlMarkup("title",title);
// CSS and JS resources
for (std::vector<std::string>::const_iterator iter = vec_css.begin(); iter != vec_css.end(); ++iter)
htmlStream << "<link rel='stylesheet' type='text/css' href='" << *iter <<"' />\n";
for (std::vector<std::string>::const_iterator iter = vec_js.begin(); iter != vec_js.end(); ++iter)
htmlStream << "<script type='text/javascript' src='" << *iter <<"'> </script>\n";
htmlStream << "</head>\n";
}
void pushInfo(const std::string& text) {
htmlStream << text;
}
std::string getDoc() {
return htmlMarkup("html", htmlStream.str());
}
std::ostringstream htmlStream;
};
/// Class to draw with the JSXGraph library in HTML page.
class JSXGraphWrapper
{
public:
typedef float TRANGE;
typedef std::pair< std::pair<TRANGE,TRANGE>, std::pair<TRANGE,TRANGE> > RANGE;
JSXGraphWrapper() {
cpt = 0;
}
void reset() {
stream.str("");
stream.precision(4);
//stream.setf(std::ios::fixed,std::ios::floatfield);
cpt = 0;
}
void init(unsigned W, unsigned H, LPCTSTR szGraphName=NULL) {
reset();
std::string strGraphName;
if (szGraphName == NULL) {
strGraphName = SEACAVE::Util::getUniqueName();
szGraphName = strGraphName.c_str();
}
stream <<
"\n"
"<div id='" << szGraphName << "' class='jxgbox' style='width:"<< W << "px; height:" << H <<"px;'></div>\n"
"<script type='text/javascript'>\n"
"var board = JXG.JSXGraph.initBoard('"<< szGraphName <<"', {axis:true,showCopyright:false});\n"
"board.suspendUpdate();\n";
}
template<typename VECTX, typename VECTY, typename STR>
void addXYChart(const VECTX& vec_x, const VECTY& vec_y, const STR& stype, LPCTSTR sface=_T("o"), LPCTSTR sizeStroke=_T("2"), LPCTSTR colFill=_T("#0077cc"), LPCTSTR colStroke=_T("#0044ee")) {
typedef typename VECTX::value_type TX;
typedef typename VECTY::value_type TY;
size_t index0 = cpt++;
size_t index1 = cpt++;
stream << "var data"<< index0<<"= [";
std::copy(vec_x.begin(), vec_x.end(), std::ostream_iterator<TX>(stream, ","));
stream << "];\n";
stream << "var data"<< index1<<"= [";
std::copy(vec_y.begin(), vec_y.end(), std::ostream_iterator<TY>(stream, ","));
stream << "];\n";
std::ostringstream osData;
osData << "[data" <<index0<<","<<"data"<<index1<<"]";
stream << "board.createElement('chart', " <<osData.str()
<< ", {chartStyle:'"<< stype << "',labels:" << osData.str() << ",face:'" << sface
<< "',strokeWidth:" << sizeStroke << ",fillColor:'" << colFill << "',highlightStrokeColor:'" << colStroke << "',fixed:true});\n";
}
template<typename VECTY, typename STR>
void addYChart(const VECTY& vec_y, const STR& stype, LPCTSTR sface=_T("o"), LPCTSTR sizeStroke=_T("2"), LPCTSTR colFill=_T("#0077cc"), LPCTSTR colStroke=_T("#0044ee")) {
typedef typename VECTY::value_type TY;
size_t index0 = cpt++;
stream << "var data"<< index0<<"= [";
std::copy(vec_y.begin(), vec_y.end(), std::ostream_iterator<TY>(stream, ","));
stream << "];\n";
stream << "board.createElement('chart', " << "data"<<index0
<< ", {chartStyle:'" << stype << "',labels:" << "data"<<index0 << ",face:'" << sface
<< "',strokeWidth:" << sizeStroke << ",fillColor:'" << colFill << "',highlightStrokeColor:'" << colStroke << "',fixed:true});\n";
}
template<typename TX, typename TY>
void addLine(TX x0, TY y0, TX x1, TY y1, LPCTSTR color=_T("#00ff00")) {
size_t index0 = cpt++;
size_t index1 = cpt++;
stream <<
"var p"<<index0<<" = board.create('point',["<<x0<<","<<y0<<"], {fixed:true});\n"
"var p"<<index1<<" = board.create('point',["<<x1<<","<<y1<<"], {fixed:true});\n"
"var li = board.create('line',[p"<<index0<<",p"<<index1<<"], "
"{strokeColor:'"<< color <<"',strokeWidth:2});\n";
}
void setViewport(const RANGE& range) {
stream
<< "board.setBoundingBox(["
<< range.first.first << ","<< range.second.second <<","
<< range.first.second << ","<< range.second.first <<"]);\n";
}
void UnsuspendUpdate() {
stream << "board.unsuspendUpdate();\n";
}
void close() {
stream << "</script>\n";
}
std::string toStr() const {
return stream.str();
}
template<typename TX, typename TY>
static inline RANGE autoViewport(TX maxValX, TY maxValY, TX minValX, TY minValY) {
//Use the value with a little margin
const TX rangeX = maxValX-minValX;
const TY rangeY = maxValY-minValY;
return std::make_pair(
std::make_pair(-JSXCHART_BORDER*rangeX+minValX,JSXCHART_BORDER*rangeX+maxValX),
std::make_pair(-JSXCHART_BORDER*rangeY+minValY,JSXCHART_BORDER*rangeY+maxValY));
}
template<typename VECTX, typename VECTY>
static RANGE autoViewport(const VECTX& vec_x, const VECTY& vec_y) {
typedef typename VECTX::value_type TX;
typedef typename VECTY::value_type TY;
if (vec_x.empty() || vec_y.empty() || vec_x.size() != vec_y.size())
return RANGE();
//For X values
const TX minValX = *std::min_element(vec_x.begin(), vec_x.end());
const TX maxValX = *std::max_element(vec_x.begin(), vec_x.end());
//For Y values
const TY minValY = *std::min_element(vec_y.begin(), vec_y.end());
const TY maxValY = *std::max_element(vec_y.begin(), vec_y.end());
return autoViewport(maxValX, maxValY, minValX, minValY);
}
template<typename T, typename VECTY>
static RANGE autoViewport(const VECTY& vec_y, bool bForceY0=true) {
typedef T TX;
typedef typename VECTY::value_type TY;
if (vec_y.empty())
return RANGE();
//For X values
const TX minValX = TX(0);
const TX maxValX = static_cast<TX>(vec_y.size());
//For Y values
const TY minValY = (bForceY0 ? TY(0) : *std::min_element(vec_y.begin(), vec_y.end()));
const TY maxValY = *std::max_element(vec_y.begin(), vec_y.end());
return autoViewport(maxValX, maxValY, minValX, minValY);
}
std::ostringstream stream;
size_t cpt; //increment for variable
};
/*----------------------------------------------------------------*/
} // namespace HTML
namespace SVG {
/// Basic SVG style
class svgStyle
{
public:
svgStyle():_sFillCol(""), _sStrokeCol("black"), _sToolTip(""), _fillOpacity(1.f), _strokeW(1.f), _strokeOpacity(1.f) {}
// Configure fill color
svgStyle& fill(const std::string& col, float opacity = 1.f)
{ _sFillCol = col; _fillOpacity = opacity; return *this; }
// Configure stroke color and width
svgStyle& stroke(const std::string& col, float witdh = 1.f, float opacity = 1.f)
{ _sStrokeCol = col; _strokeW = witdh; _strokeOpacity = opacity; return *this; }
// Configure with no stroke
svgStyle& noStroke()
{ _sStrokeCol = ""; _strokeW = 0.f; _strokeOpacity = 0.f; return *this; }
// Configure tooltip
svgStyle& tooltip(const std::string& sTooltip)
{ _sToolTip = sTooltip; return *this; }
const std::string getSvgStream() const {
std::ostringstream os;
if (!_sStrokeCol.empty()) {
os << " stroke='" << _sStrokeCol << "' stroke-width='" << _strokeW << "'";
if (_strokeOpacity < 1)
os << " stroke-opacity='" << _strokeOpacity << "'";
}
if (!_sFillCol.empty()) {
os << " fill='" << _sFillCol << "'";
if (_fillOpacity < 1)
os << " fill-opacity='" << _fillOpacity << "'";
} else {
os << " fill='none'";
}
if (!_sToolTip.empty()) {
os << " tooltip='enable'>" << "<title>" << _sToolTip << "</title>";
}
return os.str();
}
bool bTooltip() const { return !_sToolTip.empty();}
std::string _sFillCol, _sStrokeCol, _sToolTip;
float _fillOpacity, _strokeW, _strokeOpacity;
};
/// Basic class to handle simple SVG draw.
/// You can draw line, square, rectangle, text and image (xlink)
class svgDrawer
{
public:
///Constructor
svgDrawer(size_t W = 0, size_t H = 0) {
svgStream <<
"<?xml version='1.0' standalone='yes'?>\n"
"<!-- SVG graphic -->\n"
"<svg";
if (W > 0 && H > 0)
svgStream <<
" width='" << W << "px' height='"<< H << "px'"
" preserveAspectRatio='xMinYMin meet'"
" viewBox='0 0 " << W << ' ' << H <<"'";
svgStream <<
" xmlns='http://www.w3.org/2000/svg'"
" xmlns:xlink='http://www.w3.org/1999/xlink'"
" version='1.1'>\n";
}
///Circle draw -> x,y position and radius
void drawCircle(float cx, float cy, float r, const svgStyle& style) {
svgStream
<< "<circle cx='" << cx << "'" << " cy='" << cy << "'"
<< " r='" << r << "'"
<< style.getSvgStream() + (style.bTooltip() ? "</circle>\n" : "/>\n");
}
///Line draw -> start and end point
void drawLine(float ax, float ay, float bx, float by, const svgStyle& style) {
svgStream
<< "<line x1='"<<ax<< "' y1='"<<ay<< "' x2='"<<bx<< "' y2='"<<by<< "'"
<< style.getSvgStream() + (style.bTooltip() ? "</line>\n" : "/>\n");
}
///Reference to an image (path must be relative to the SVG file)
void drawImage(const std::string& simagePath, int W, int H,
int posx = 0, int posy = 0, float opacity =1.f)
{
svgStream <<
"<image x='"<< posx << "'" << " y='"<< posy << "'"
" width='"<< W << "px'" << " height='"<< H << "px'"
" opacity='"<< opacity << "'"
" xlink:href='" << simagePath << "'/>\n";
}
///Square draw -> x,y position and size
void drawSquare(float cx, float cy, float W, const svgStyle& style) {
drawRectangle(cx, cy, W, W, style);
}
///Circle draw -> x,y position and width and height
void drawRectangle(float cx, float cy, float W, float H, const svgStyle& style) {
svgStream
<< "<rect x='" << cx << "'"
<< " y='" << cy << "'"
<< " width='" << W << "'"
<< " height='" << H << "'"
<< style.getSvgStream() + (style.bTooltip() ? "</rect>\n" : "/>\n");
}
///Text display -> x,y position, font size
void drawText(float cx, float cy, const std::string& stext, const std::string& scol = "", const std::string& sattr = "", float fontSize = 1.f) {
svgStream << "<text" << " x='" << cx << "'" << " y='" << cy << "'"
<< " font-size='" << fontSize << "'";
if (!sattr.empty())
svgStream << ' ' << sattr;
if (!scol.empty())
svgStream << " fill='" << scol << "'";
svgStream << ">" << stext << "</text>\n";
}
template< typename DataInputIteratorX, typename DataInputIteratorY>
void drawPolyline(DataInputIteratorX xStart, DataInputIteratorX xEnd,
DataInputIteratorY yStart, DataInputIteratorY /*yEnd*/,
const svgStyle& style)
{
svgStream << "<polyline points='";
DataInputIteratorY itery = yStart;
for(DataInputIteratorX iterx = xStart;
iterx != xEnd; std::advance(iterx, 1), std::advance(itery, 1))
{
svgStream << *iterx << ',' << *itery << ' ';
}
svgStream << "'" << style.getSvgStream() + (style.bTooltip() ? "</polyline>\n" : "/>\n");
}
///Close the svg tag.
std::ostringstream& closeSvgFile() {
svgStream << "</svg>";
return svgStream;
}
std::ostringstream svgStream;
};
/// Helper to draw a SVG histogram
/// ____
/// | | ___ |
/// | |__| | |
/// | | | | |
/// -----------|
struct svgHisto
{
template<typename VECT>
std::string draw(const VECT& vec_value,
const std::pair<float, float>& range,
float W, float H)
{
if (vec_value.empty())
return "";
//-- Max value
typedef typename VECT::value_type T;
const T maxi = *max_element(vec_value.begin(), vec_value.end());
const size_t n = vec_value.size();
const float scaleFactorY = H / static_cast<float>(maxi);
const float scaleFactorX = W / static_cast<float>(n);
svgDrawer svgStream;
for (typename VECT::const_iterator iter = vec_value.begin(); iter != vec_value.end(); ++iter)
{
const T dist = std::distance(vec_value.begin(), iter);
const T& val = *iter;
std::ostringstream os;
os << '(' << range.first + dist/float(n) * (range.second-range.first) << ',' << val << ')';
svgStyle style = svgStyle().fill("blue").stroke("black", 1.0).tooltip(os.str());
svgStream.drawRectangle(
scaleFactorX * dist, H-val * scaleFactorY,
scaleFactorX, val * scaleFactorY,
style);
//_________
//| |_________
//| || |
//| || |
//| || |
//0 sFactorX 2*sFactorX
}
svgStyle styleAxis = svgStyle().stroke("black", 1.0f);
// Draw X Axis
svgStream.drawText(.05f*W, 1.2f*H, HTML::toString(range.first), "black", "", .1f*H);
svgStream.drawText(W, 1.2*H, HTML::toString(range.second), "black", "", .1f*H);
svgStream.drawLine(0, 1.1f*H, W, 1.1f*H, styleAxis);
// Draw Y Axis
svgStream.drawText(1.2f*W, .1f*H, HTML::toString(maxi), "black", "", .1f*H);
svgStream.drawText(1.2f*W, H, "0", "black", "", .1f*H);
svgStream.drawLine(1.1f*W, 0, 1.1f*W, H, styleAxis);
return svgStream.closeSvgFile().str();
}
};
/*----------------------------------------------------------------*/
} // namespace SVG
#endif // _HTMLDOC_H_

159
libs/Common/HalfFloat.h

@ -0,0 +1,159 @@ @@ -0,0 +1,159 @@
////////////////////////////////////////////////////////////////////
// HalfFoat.h
//
// Copyright 2007 cDc@seacave
// Distributed under the Boost Software License, Version 1.0
// (See http://www.boost.org/LICENSE_1_0.txt)
#ifndef __SEACAVE_HALFFLOAT_H__
#define __SEACAVE_HALFFLOAT_H__
// I N C L U D E S /////////////////////////////////////////////////
// D E F I N E S ///////////////////////////////////////////////////
namespace SEACAVE {
// S T R U C T S ///////////////////////////////////////////////////
// hfloat - a 16-bit floating point number class
//
// Can represent positive and negative numbers whose magnitude is between
// roughly 6.1e-5 and 6.5e+4 with a relative error of 9.8e-4; numbers
// smaller than 6.1e-5 can be represented with an absolute error of
// 6.0e-8. All integers from -2048 to +2048 can be represented exactly.
// Supports Denormals-as-zero (DAZ), but does not support infinities or NaN.
//
// Only conversions from half to float are lossless.
class GENERAL_API hfloat
{
public:
typedef short Type;
inline hfloat() : val(fromFloat(float())) {}
explicit inline hfloat(float v) : val(fromFloat(v)) {}
template <typename T>
inline hfloat(T v) : val(fromFloat(static_cast<float>(v))) {}
inline hfloat& operator = (float v) {
val = fromFloat(v);
return *this;
}
template <typename T>
inline hfloat& operator = (T v) {
val = fromFloat(static_cast<float>(v));
return *this;
}
inline operator float() const {
return toFloat(val);
}
inline hfloat operator - () const {
return fromFloat2HFloat(-toFloat(val));
}
inline hfloat operator + (hfloat v) {
return fromFloat2HFloat(toFloat(val) + toFloat(v.val));
}
inline hfloat operator + (float v) {
return fromFloat2HFloat(toFloat(val) + v);
}
inline hfloat& operator += (hfloat v) {
return *this = *this + v;
}
inline hfloat& operator += (float v) {
return *this = *this + v;
}
inline hfloat operator - (hfloat v) {
return fromFloat2HFloat(toFloat(val) - toFloat(v.val));
}
inline hfloat operator - (float v) {
return fromFloat2HFloat(toFloat(val) - v);
}
inline hfloat& operator -= (hfloat v) {
return *this = *this - v;
}
inline hfloat& operator -= (float v) {
return *this = *this - v;
}
inline hfloat operator * (hfloat v) {
return fromFloat2HFloat(toFloat(val) * toFloat(v.val));
}
inline hfloat operator * (float v) {
return fromFloat2HFloat(toFloat(val) * v);
}
inline hfloat& operator *= (hfloat v) {
return *this = *this * v;
}
inline hfloat& operator *= (float v) {
return *this = *this * v;
}
inline hfloat operator / (hfloat v) {
return fromFloat2HFloat(toFloat(val) / toFloat(v.val));
}
inline hfloat operator / (float v) {
return fromFloat2HFloat(toFloat(val) / v);
}
inline hfloat& operator /= (hfloat v) {
return *this = *this / v;
}
inline hfloat& operator /= (float v) {
return *this = *this / v;
}
static float min() { return 6.1e-5f; }
static float max() { return 6.5e+4f; }
static inline short fromFloat(float f) {
ASSERT(ISFINITE(f) && (ABS(f) == 0.f || ABS(f) >= min()) && ABS(f) <= max());
// ~8 clock cycles on modern x86-64
const CastF2I ufi(f);
int32_t t1 = ufi.i & 0x7fffffff; // Non-sign bits
int32_t t2 = ufi.i & 0x80000000; // Sign bit
int32_t t3 = ufi.i & 0x7f800000; // Exponent
t1 >>= 13; // Align mantissa on MSB
t2 >>= 16; // Shift sign bit into position
t1 -= 0x1c000; // Adjust bias
t1 = (t3 < 0x38800000) ? 0 : t1; // Flush-to-zero
t1 = (t3 > 0x47000000) ? 0x7bff : t1; // Clamp-to-max
t1 = (t3 == 0 ? 0 : t1); // Denormals-as-zero
return (short)(t1 | t2); // Re-insert sign bit
}
static inline float toFloat(short h) {
// ~6 clock cycles on modern x86-64
CastF2I ufi;
int32_t t1 = h & 0x7fff; // Non-sign bits
int32_t t2 = h & 0x8000; // Sign bit
int32_t t3 = h & 0x7c00; // Exponent
t1 <<= 13; // Align mantissa on MSB
t2 <<= 16; // Shift sign bit into position
t1 += 0x38000000; // Adjust bias
t1 = (t3 == 0 ? 0 : t1); // Denormals-as-zero
ufi.i = t1 | t2; // Re-insert sign bit
return ufi.f;
}
#ifdef _USE_BOOST
// serialize
template <class Archive>
void serialize(Archive& ar, const unsigned int /*version*/) {
ar & val;
}
#endif
protected:
static inline hfloat fromFloat2HFloat(float f) {
return TAliasCast<short,hfloat>(fromFloat(f)).i;
}
protected:
Type val;
};
/*----------------------------------------------------------------*/
} // namespace SEACAVE
#endif // __SEACAVE_HALFFLOAT_H__

303
libs/Common/Hash.h

@ -0,0 +1,303 @@ @@ -0,0 +1,303 @@
////////////////////////////////////////////////////////////////////
// Hash.h
//
// Copyright 2007 cDc@seacave
// Distributed under the Boost Software License, Version 1.0
// (See http://www.boost.org/LICENSE_1_0.txt)
#ifndef __SEACAVE_HASH_H__
#define __SEACAVE_HASH_H__
// I N C L U D E S /////////////////////////////////////////////////
// D E F I N E S ///////////////////////////////////////////////////
namespace SEACAVE {
// S T R U C T S ///////////////////////////////////////////////////
/**************************************************************************************
* StringHash template
* ---------------
* compile-time string hash template
**************************************************************************************/
class StringHash
{
private:
uint32_t m_val;
template<size_t N> inline uint32_t _Hash(const char (&str)[N]) const
{
typedef const char (&truncated_str)[N-2];
return str[N-1] + 65599 * (str[N-2] + 65599 * _Hash((truncated_str)str));
}
inline uint32_t _Hash(const char (&str)[3]) const { return str[2] + 65599 * (str[1] + 65599 * str[0]); }
inline uint32_t _Hash(const char (&str)[2]) const { return str[1] + 65599 * str[0]; }
public:
template <size_t N> StringHash(const char (&str)[N]) { m_val = _Hash(str); }
inline operator uint32_t() const { return m_val; }
};
/**************************************************************************************
* cHashTable template
* ---------------
* simple hash template (performs relatively well for less than 300 elements)
**************************************************************************************/
#define KEY_NA 0xFFFF
#define KEY_MAXSIZE 0x1000000
template <typename Type>
class cHashTable
{
public:
typedef uint32_t Key;
typedef uint16_t Idx;
typedef uint32_t Size;
typedef cList<Type> Values;
typedef cList<Key, Key> Keys;
typedef cList<Idx, Idx> Indices;
protected:
Keys m_keys; // Array of unique keys that have been set
Values m_values; // Matching array of unique values that have been set
Indices m_indices; // 1-based index array referencing the two arrays above
public:
cHashTable() : m_indices(32) { m_indices.Memset(0); }
~cHashTable() { Release(); }
inline void Release()
{
m_keys.Release();
m_values.Release();
m_indices.Release();
}
inline bool IsEmpty() const { return m_indices.IsEmpty(); }
inline size_t GetSize() const { return m_keys.GetSize(); }
inline const Type* GetBegin() const { return m_values.GetData(); }
inline const Type* GetEnd() const { return m_values.GetData()+m_values.GetSize(); }
inline Type* GetBegin() { return m_values.GetData(); }
inline Type* GetEnd() { return m_values.GetData()+m_values.GetSize(); }
inline const Indices& GetArrIndices() const { return m_indices; }
inline const Keys& GetArrKeys() const { return m_keys; }
inline const Values& GetArrValues() const { return m_values; }
inline Values& GetArrValues() { return m_values; }
private:
inline Size _StaticKeyToID(Key key, Size size) const { return key & (size-1); }
inline Size _KeyToID(Key key) const { return _StaticKeyToID(key, (Size)m_indices.GetSize()); }
inline Idx _IDToIndex(Size id) const { return m_indices[id] - 1; }
inline Idx _KeyToIndex(Key key) const { return (m_indices.IsEmpty() ? KEY_NA : _IDToIndex(_KeyToID(key))); }
// Completely discards then rebuilds all indices based on the current set of keys
void _RebuildIndices()
{
// Clear all current memory
m_indices.Memset(0);
// Run through all keys and recreate the indices
for (Idx i=0; i<m_keys.GetSize(); )
{
const Size index = _KeyToID(m_keys[i]);
m_indices[index] = ++i;
}
}
public:
// Returns a pointer to the value of a key if it exists, 0 otherwise
inline const Type* Find(Key key) const
{
const Idx index = _KeyToIndex(key);
return (index < m_keys.GetSize() && m_keys[index] == key) ? &m_values[index] : NULL;
}
// Non-constant version of the function above
inline Type* Find(Key key)
{
const Idx index = _KeyToIndex(key);
return (index < m_keys.GetSize() && m_keys[index] == key) ? &m_values[index] : NULL;
}
// Checks whether the specified key exists is in the hash
inline bool Exists(Key key) const
{
const Idx index = _KeyToIndex(key);
return (index < m_keys.GetSize() && m_keys[index] == key);
}
// Removes a single entry from the list
void Delete(Key key)
{
const Size id = _KeyToID(key);
const Idx index = _IDToIndex(id);
if (index < m_keys.GetSize() && m_keys[index] == key)
{
m_keys.RemoveAt(index);
m_values.RemoveAt(index);
m_indices[id] = 0;
// Adjust all the indices that follow this one
for (Size i=m_indices.GetSize(); i>0; )
{
if (m_indices[--i] > index)
{
--m_indices[i];
}
}
}
}
// Retrieves a value from the hash -- but it will only be valid if it exists, so be careful. In most
// cases you will either want to use an Exists() check first, or simply use the Find function.
inline const Type& operator [] (Key key) const
{
const Idx index = _KeyToIndex(key);
return m_values[index];
}
// Retrieves a value from the hash, inserting a new one if necessary
Type& operator [] (Key key)
{
// Get the index for this key
const Idx index = _KeyToIndex(key);
if (index != KEY_NA)
{
// If we found a valid entry, we need to match the actual key
const Key oldKey = m_keys[index];
// If the key matches, return the value
if (oldKey == key)
{
return m_values[index];
}
else
{
// Setting the key was unsuccessful due to another entry colliding with our key;
// we must expand the indices until we find a set of keys that will match.
Size newSize = m_indices.GetSize();
while (newSize < (KEY_MAXSIZE>>1))
{
newSize = newSize << 1;
// Find the next best size for the hash that would make both keys unique
if (_StaticKeyToID(key, newSize) != _StaticKeyToID(oldKey, newSize))
{
m_indices.ResizeExact(newSize);
_RebuildIndices();
break;
}
}
// Critical error if we didn't find a working size
ASSERT(_StaticKeyToID(key, newSize) != _StaticKeyToID(oldKey, newSize));
}
}
// assert the total number of values stored is in Idx range
ASSERT(m_keys.GetSize() < (Size)((Idx)-1));
// Append the new key to the end
m_keys.Insert(key);
// Add this new entry to the index list using the current array size as the 1-based index
m_indices[_KeyToID(key)] = (Idx)m_keys.GetSize();
// Return the value
return m_values.AddEmpty();
}
public:
// Fowler / Noll / Vo (FNV) Hash
// magic numbers from http://www.isthe.com/chongo/tech/comp/fnv/
static inline Key HashKeyFNV(const uint8_t* data, Size size)
{
Key hash = 2166136261U; //InitialFNV
for (size_t i = 0; i < size; i++)
{
hash = hash ^ (data[i]); // xor the low 8 bits
hash = hash * 16777619; // multiply by the magic number (FNVMultiple)
}
return hash;
}
// Function that calculates a hash value from a given data
// tested for random binary data (3 <= size <= 33) and performs VERY bad
static inline Key HashKeyR5(const uint8_t* data, Size size)
{
Key key = 0;
Key offset = 1357980759;
for (Size i=0; i<size; ++i, ++data)
{
key += (*data & 31) ^ offset;
key += i & (offset >> 15);
offset = key ^ (((~offset) >> 7) | (offset << 25));
}
return key;
}
static inline Key HashKey(const uint8_t* data, Size size) { return HashKeyFNV(data, size); }
static inline Key HashKey(LPCTSTR sz) { return HashKey((const uint8_t*)sz, (Size)_tcslen(sz)); }
static inline Key HashKey(const String& str) { return HashKey((const uint8_t*)str.c_str(), (Size)str.size()); }
// Convenience functions
inline Type* Find (LPCTSTR key) { return Find ( HashKey(key) ); }
inline Type* Find (const String& key) { return Find ( HashKey(key) ); }
inline const Type* Find (LPCTSTR key) const { return Find ( HashKey(key) ); }
inline const Type* Find (const String& key) const { return Find ( HashKey(key) ); }
inline void Delete (LPCTSTR key) { Delete( HashKey(key) ); }
inline void Delete (const String& key) { Delete( HashKey(key.c_str()) ); }
inline bool Exists (LPCTSTR key) const { return Exists( HashKey(key) ); }
inline bool Exists (const String& key) const { return Exists( HashKey(key.c_str()) ); }
inline Type& operator [] (LPCTSTR key) { return (*this)[ HashKey(key) ]; }
inline Type& operator [] (const String& key) { return (*this)[ HashKey(key.c_str()) ]; }
inline const Type& operator [] (LPCTSTR key) const { return (*this)[ HashKey(key) ]; }
inline const Type& operator [] (const String& key) const { return (*this)[ HashKey(key.c_str()) ]; }
};
/**************************************************************************************
* cMapWrap template
* ---------------
* STL map wrapper
**************************************************************************************/
template <typename Key, typename Type>
class cMapWrap : public std::unordered_map<Key, Type>
{
public:
typedef typename std::unordered_map<Key, Type> Base;
typedef typename Base::const_iterator const_iterator;
typedef typename Base::iterator iterator;
public:
cMapWrap() {}
~cMapWrap() {}
inline void Release() { this->clear(); }
inline bool IsEmpty() const { return this->empty(); }
inline size_t GetSize() const { return this->size(); }
inline const_iterator GetBegin() const { return this->begin(); }
inline const_iterator GetEnd() const { return this->end(); }
inline iterator GetBegin() { return this->begin(); }
inline iterator GetEnd() { return this->end(); }
inline const_iterator Find(const Key& key) const { return this->find(key); }
inline iterator Find(const Key& key) { return this->find(key); }
inline iterator Insert(const Key& key, bool& bExisted) { return Insert(key, Type(), bExisted); }
inline iterator Insert(const Key& key, const Type& val, bool& bExisted) {
const std::pair<iterator,bool> ret(this->emplace(key, val));
bExisted = !ret.second;
return ret.first;
}
};
/*----------------------------------------------------------------*/
} // namespace SEACAVE
#endif // __SEACAVE_HASH_H__

95
libs/Common/Line.h

@ -0,0 +1,95 @@ @@ -0,0 +1,95 @@
////////////////////////////////////////////////////////////////////
// Line.h
//
// Copyright 2023 cDc@seacave
// Distributed under the Boost Software License, Version 1.0
// (See http://www.boost.org/LICENSE_1_0.txt)
#ifndef __SEACAVE_LINE_H__
#define __SEACAVE_LINE_H__
// I N C L U D E S /////////////////////////////////////////////////
// D E F I N E S ///////////////////////////////////////////////////
namespace SEACAVE {
// S T R U C T S ///////////////////////////////////////////////////
// Generic line class represented as two points
template <typename TYPE, int DIMS>
class TLine
{
STATIC_ASSERT(DIMS > 1 && DIMS <= 3);
public:
typedef Eigen::Matrix<TYPE,DIMS,1> VECTOR;
typedef Eigen::Matrix<TYPE,DIMS,1> POINT;
typedef SEACAVE::TAABB<TYPE,DIMS> AABB;
typedef SEACAVE::TRay<TYPE,DIMS> RAY;
enum { numScalar = (2*DIMS) };
enum { numParams = numScalar-1 };
POINT pt1, pt2; // line description
//---------------------------------------
inline TLine() {}
inline TLine(const POINT& pt1, const POINT& pt2);
template <typename CTYPE>
inline TLine(const TLine<CTYPE,DIMS>&);
inline void Set(const POINT& pt1, const POINT& pt2);
int Optimize(const POINT*, size_t, int maxIters=100);
template <typename RobustNormFunctor>
int Optimize(const POINT*, size_t, const RobustNormFunctor& robust, int maxIters=100);
inline TYPE GetLength() const;
inline TYPE GetLengthSq() const;
inline POINT GetCenter() const;
inline VECTOR GetDir() const;
inline VECTOR GetNormDir() const;
inline RAY GetRay() const;
inline bool IsSame(const TLine&, TYPE th) const;
bool Intersects(const AABB& aabb) const;
bool Intersects(const AABB& aabb, TYPE& t) const;
inline TYPE DistanceSq(const POINT&) const;
inline TYPE Distance(const POINT&) const;
inline TYPE Classify(const POINT&) const;
inline POINT ProjectPoint(const POINT&) const;
inline TYPE& operator [] (BYTE i) { ASSERT(i<numScalar); return pt1.data()[i]; }
inline TYPE operator [] (BYTE i) const { ASSERT(i<numScalar); return pt1.data()[i]; }
#ifdef _USE_BOOST
// implement BOOST serialization
template<class Archive>
void serialize(Archive& ar, const unsigned int /*version*/) {
ar & pt1;
ar & pt2;
}
#endif
}; // class TLine
/*----------------------------------------------------------------*/
template <typename TYPE, typename TYPEW=TYPE>
struct FitLineOnline : FitPlaneOnline<TYPE,TYPEW,true> {
template <typename TYPEE> TPoint3<TYPEE> GetLine(TLine<TYPEE,3>& line) const;
};
/*----------------------------------------------------------------*/
#include "Line.inl"
/*----------------------------------------------------------------*/
} // namespace SEACAVE
#endif // __SEACAVE_LINE_H__

215
libs/Common/Line.inl

@ -0,0 +1,215 @@ @@ -0,0 +1,215 @@
// D E F I N E S ///////////////////////////////////////////////////
// S T R U C T S ///////////////////////////////////////////////////
// C L A S S //////////////////////////////////////////////////////
template <typename TYPE, int DIMS>
inline TLine<TYPE,DIMS>::TLine(const POINT& _pt1, const POINT& _pt2)
:
pt1(_pt1), pt2(_pt2)
{
ASSERT(!ISZERO((_pt1-_pt2).norm()));
} // constructor
template <typename TYPE, int DIMS>
template <typename CTYPE>
inline TLine<TYPE,DIMS>::TLine(const TLine<CTYPE,DIMS>& rhs)
:
pt1(rhs.pt1.template cast<TYPE>()), pt2(rhs.pt2.template cast<TYPE>())
{
} // copy constructor
/*----------------------------------------------------------------*/
// set attributes
template <typename TYPE, int DIMS>
inline void TLine<TYPE,DIMS>::Set(const POINT& _pt1, const POINT& _pt2)
{
ASSERT(!ISZERO((_pt1-_pt2).norm()));
pt1 = _pt1;
pt2 = _pt2;
}
/*----------------------------------------------------------------*/
// least squares refinement of the line to the given 3D point set
// (return the number of iterations)
template <typename TYPE, int DIMS>
template <typename RobustNormFunctor>
int TLine<TYPE,DIMS>::Optimize(const POINT* points, size_t size, const RobustNormFunctor& robust, int maxIters)
{
ASSERT(DIMS == 3);
ASSERT(size >= numParams);
struct OptimizationFunctor {
const POINT* points;
size_t size;
double scale;
const RobustNormFunctor& robust;
// construct with the data points
OptimizationFunctor(const POINT* _points, size_t _size, const RobustNormFunctor& _robust)
: points(_points), size(_size), robust(_robust) { ASSERT(size < std::numeric_limits<int>::max()); }
static void Residuals(const double* x, int nPoints, const void* pData, double* fvec, double* fjac, int* /*info*/) {
const OptimizationFunctor& data = *reinterpret_cast<const OptimizationFunctor*>(pData);
ASSERT((size_t)nPoints == data.size && fvec != NULL && fjac == NULL);
TLine<double,DIMS> line;
for (int j=0; j<DIMS; ++j)
line.pt1(j) = x[j];
DirScale2Vector(x+DIMS, &data.scale, line.pt2.data());
line.pt2 += line.pt1;
for (size_t i=0; i<data.size; ++i)
fvec[i] = data.robust(line.Distance(data.points[i].template cast<double>()));
}
} functor(points, size, robust);
double arrParams[numParams];
for (int j=0; j<DIMS; ++j)
arrParams[j] = (double)pt1(j);
POINT dir(pt2-pt1);
Vector2DirScale(dir.data(), arrParams+DIMS, &functor.scale);
lm_control_struct control = {1.e-6, 1.e-7, 1.e-8, 1.e-8, 100.0, maxIters}; // lm_control_float;
lm_status_struct status;
lmmin(numParams, arrParams, (int)size, &functor, OptimizationFunctor::Residuals, &control, &status);
switch (status.info) {
//case 4:
case 5:
case 6:
case 7:
case 8:
case 9:
case 10:
case 11:
case 12:
DEBUG_ULTIMATE("error: refine line: %s", lm_infmsg[status.info]);
return 0;
}
for (int j=0; j<DIMS; ++j)
pt1(j) = (TYPE)arrParams[j];
DirScale2Vector(arrParams+DIMS, &functor.scale, dir.data());
pt2 = pt1+dir;
return status.nfev;
}
template <typename TYPE, int DIMS>
int TLine<TYPE,DIMS>::Optimize(const POINT* points, size_t size, int maxIters)
{
const auto identity = [](double x) { return x; };
return Optimize(points, size, identity, maxIters);
} // Optimize
/*----------------------------------------------------------------*/
// get attributes
template <typename TYPE, int DIMS>
inline TYPE TLine<TYPE,DIMS>::GetLength() const
{
return (pt2 - pt1).norm();
}
template <typename TYPE, int DIMS>
inline TYPE TLine<TYPE,DIMS>::GetLengthSq() const
{
return (pt2 - pt1).squaredNorm();
}
template <typename TYPE, int DIMS>
inline typename TLine<TYPE,DIMS>::POINT TLine<TYPE,DIMS>::GetCenter() const
{
return (pt2 + pt1) / TYPE(2);
}
template <typename TYPE, int DIMS>
inline typename TLine<TYPE,DIMS>::VECTOR TLine<TYPE,DIMS>::GetDir() const
{
return (pt2 - pt1);
}
template <typename TYPE, int DIMS>
inline typename TLine<TYPE,DIMS>::VECTOR TLine<TYPE,DIMS>::GetNormDir() const
{
return (pt2 - pt1).normalized();
}
template <typename TYPE, int DIMS>
inline typename TLine<TYPE,DIMS>::RAY TLine<TYPE,DIMS>::GetRay() const
{
return RAY(pt1, GetNormDir());
}
/*----------------------------------------------------------------*/
template <typename TYPE, int DIMS>
inline bool TLine<TYPE,DIMS>::IsSame(const TLine& line, TYPE th) const
{
const TYPE thSq(SQUARE(th));
const VECTOR l(pt2-pt1);
const TYPE invLenSq(INVERT(l.squaredNorm()));
const VECTOR r1(pt1-line.pt1);
const TYPE dSq1((l.cross(r1)).squaredNorm()*invLenSq);
if (dSq1 > thSq)
return false;
const VECTOR r2(pt1-line.pt2);
const TYPE dSq2((l.cross(r2)).squaredNorm()*invLenSq);
return dSq2 <= thSq;
}
/*----------------------------------------------------------------*/
// test for intersection with aabb
template <typename TYPE, int DIMS>
bool TLine<TYPE,DIMS>::Intersects(const AABB &aabb) const
{
return GetRay().Intersects(aabb);
} // Intersects(AABB)
template <typename TYPE, int DIMS>
bool TLine<TYPE,DIMS>::Intersects(const AABB &aabb, TYPE& t) const
{
return GetRay().Intersects(aabb, t);
} // Intersects(AABB)
/*----------------------------------------------------------------*/
// Computes the distance between the line and a point.
template <typename TYPE, int DIMS>
inline TYPE TLine<TYPE,DIMS>::DistanceSq(const POINT& pt) const
{
const VECTOR l(pt2-pt1), r(pt1-pt);
if (DIMS == 2)
return TYPE(SQUARE(l[0]*r[1]-r[0]*l[1])/(l[0]*l[0]+l[1]*l[1]));
ASSERT(DIMS == 3);
return TYPE((l.cross(r)).squaredNorm()/l.squaredNorm());
} // DistanceSq(POINT)
template <typename TYPE, int DIMS>
inline TYPE TLine<TYPE,DIMS>::Distance(const POINT& pt) const
{
return SQRT(DistanceSq(pt));
} // Distance(POINT)
/*----------------------------------------------------------------*/
// Computes the position on the line segment of the point projection.
// Returns 0 if it coincides with the first point, and 1 if it coincides with the second point.
template <typename TYPE, int DIMS>
inline TYPE TLine<TYPE,DIMS>::Classify(const POINT& p) const
{
const VECTOR vL(pt2 - pt1);
ASSERT(!ISZERO(vL.squaredNorm()));
const VECTOR vP(p - pt1);
return vL.dot(vP) / vL.squaredNorm();
} // Classify(POINT)
// Calculate point's projection on this line (closest point to this line).
template <typename TYPE, int DIMS>
inline typename TLine<TYPE,DIMS>::POINT TLine<TYPE,DIMS>::ProjectPoint(const POINT& p) const
{
const VECTOR vL(pt2 - pt1);
ASSERT(!ISZERO(vL.squaredNorm()));
const VECTOR vP(p - pt1);
return pt1 + vL * (vL.dot(vP) / vL.squaredNorm());
} // ProjectPoint
/*----------------------------------------------------------------*/
template <typename TYPE, typename TYPEW>
template <typename TYPEE>
TPoint3<TYPEE> FitLineOnline<TYPE,TYPEW>::GetLine(TLine<TYPEE,3>& line) const
{
TPoint3<TYPEW> avg, dir;
const TPoint3<TYPEW> quality(this->GetModel(avg, dir));
const TPoint3<TYPEW> pt2(avg+dir);
line.Set(TPoint3<TYPEE>(avg), TPoint3<TYPEE>(pt2));
return TPoint3<TYPEE>(quality);
}
/*----------------------------------------------------------------*/

111
libs/Common/LinkLib.h

@ -0,0 +1,111 @@ @@ -0,0 +1,111 @@
////////////////////////////////////////////////////////////////////
// LinkLib.h
//
// Copyright 2007 cDc@seacave
// Distributed under the Boost Software License, Version 1.0
// (See http://www.boost.org/LICENSE_1_0.txt)
#ifndef __SEACAVE_LINKLIB_H__
#define __SEACAVE_LINKLIB_H__
// I N C L U D E S /////////////////////////////////////////////////
#ifdef _MSC_VER
#else
#include <dlfcn.h>
#endif
// D E F I N E S ///////////////////////////////////////////////////
namespace SEACAVE {
// S T R U C T S ///////////////////////////////////////////////////
/**************************************************************************************
* CLinkLib
* ---------------
* manage dynamic linked libraries
**************************************************************************************/
class GENERAL_API CLinkLib
{
public:
#ifdef _MSC_VER
typedef HINSTANCE LibID;
#else
typedef void* LibID;
#endif
CLinkLib() : m_hLib(NULL)
{
}
CLinkLib(const String& dllName) : m_hLib(NULL)
{
Load(dllName);
}
CLinkLib(const CLinkLib& lib) : m_hLib(lib.m_hLib)
{
((CLinkLib&)lib).m_hLib = NULL;
}
~CLinkLib()
{
Free();
}
bool Load(const String& dllName)
{
Free();
#ifdef _MSC_VER
m_hLib = LoadLibrary(dllName);
#else
m_hLib = dlopen(dllName, RTLD_NOW);
#endif
return (m_hLib != NULL);
}
void Free()
{
if (!IsLoaded())
return;
#ifdef _MSC_VER
FreeLibrary(m_hLib);
#else
dlclose(m_hLib);
#endif
m_hLib = NULL;
}
void* GetFunction(const String& funcName) const
{
#ifdef _MSC_VER
return GetProcAddress(m_hLib, funcName);
#else
return dlsym(m_hLib, funcName);
#endif
}
bool IsLoaded() const
{
return (m_hLib != NULL);
}
LibID GetLibID() const
{
return m_hLib;
}
protected:
LibID m_hLib;
};
/*----------------------------------------------------------------*/
} // namespace SEACAVE
#endif // __SEACAVE_LINKLIB_H__

1704
libs/Common/List.h

File diff suppressed because it is too large Load Diff

453
libs/Common/Log.cpp

@ -0,0 +1,453 @@ @@ -0,0 +1,453 @@
////////////////////////////////////////////////////////////////////
// Log.cpp
//
// Copyright 2007 cDc@seacave
// Distributed under the Boost Software License, Version 1.0
// (See http://www.boost.org/LICENSE_1_0.txt)
#include "Common.h"
#include "Log.h"
using namespace SEACAVE;
// D E F I N E S ///////////////////////////////////////////////////
// S T R U C T S ///////////////////////////////////////////////////
/*-----------------------------------------------------------*
* Log class implementation *
*-----------------------------------------------------------*/
#ifndef DEFAULT_LOGTYPE
Log::LogType Log::g_appType;
#endif
/**
* Constructor
*/
Log::Log()
{
// generate default log type
#ifndef DEFAULT_LOGTYPE
String appName = Util::getAppName();
appName = (Util::getFileExt(appName) == _T(".exe") ? Util::getFileName(appName) : Util::getFileNameExt(appName));
Idx n = MINF((Idx)appName.length(), (Idx)LOGTYPE_SIZE);
_tcsncpy(g_appType.szName, appName, n);
while (n < LOGTYPE_SIZE)
g_appType.szName[n++] = _T(' ');
g_appType.szName[LOGTYPE_SIZE] = _T('\0');
#endif
ResetTypes();
}
void Log::RegisterListener(ClbkRecordMsg clbk)
{
ASSERT(m_arrRecordClbk != NULL);
if (m_arrRecordClbk == NULL)
return;
m_arrRecordClbk->Insert(clbk);
}
void Log::UnregisterListener(ClbkRecordMsg clbk)
{
ASSERT(m_arrRecordClbk != NULL);
if (m_arrRecordClbk == NULL)
return;
m_arrRecordClbk->Remove(clbk);
}
//Register a new type of log messages (LOGTYPE_SIZE chars)
Log::Idx Log::RegisterType(LPCTSTR lt)
{
ASSERT(strlen(lt) == LOGTYPE_SIZE);
const Idx idx = (Idx)m_arrLogTypes.GetSize();
LogType& logType = m_arrLogTypes.AddEmpty();
Idx n = MINF((Idx)strlen(lt), (Idx)LOGTYPE_SIZE);
_tcsncpy(logType.szName, lt, n);
while (n < LOGTYPE_SIZE)
logType.szName[n++] = _T(' ');
logType.szName[LOGTYPE_SIZE] = _T('\0');
return idx;
}
/**
* Empty the array with registered log types
*/
void Log::ResetTypes()
{
m_arrLogTypes.Empty();
}
void Log::Write(LPCTSTR szFormat, ...)
{
if (m_arrRecordClbk == NULL)
return;
va_list args;
va_start(args, szFormat);
_Record(NO_ID, szFormat, args);
va_end(args);
}
void Log::Write(Idx lt, LPCTSTR szFormat, ...)
{
if (m_arrRecordClbk == NULL)
return;
va_list args;
va_start(args, szFormat);
_Record(lt, szFormat, args);
va_end(args);
}
/**
* Write message to the log if this exists
* -> IN: Idx - log type
* LPCTSTR - format message
* ... - values
*/
void Log::_Record(Idx lt, LPCTSTR szFormat, va_list args)
{
ASSERT(m_arrRecordClbk != NULL);
if (m_arrRecordClbk->IsEmpty())
return;
// Format a message by adding the date (auto adds new line)
TCHAR szTime[256];
TCHAR szBuffer[2048];
#if defined(LOG_DATE) || defined(LOG_TIME)
TCHAR* szPtrTime = szTime;
#ifdef _MSC_VER
SYSTEMTIME st;
GetLocalTime(&st);
#ifdef LOG_THREAD
WLock l(m_lock);
#endif
#ifdef LOG_DATE
szPtrTime += GetDateFormat(LOCALE_USER_DEFAULT,0,&st,_T("dd'.'MM'.'yy"),szPtrTime,80)-1;
#endif
#if defined(LOG_DATE) && defined(LOG_TIME)
szPtrTime[0] = _T('-'); ++szPtrTime;
#endif
#ifdef LOG_TIME
szPtrTime += GetTimeFormat(LOCALE_USER_DEFAULT,0,&st,_T("HH':'mm':'ss"),szPtrTime,80)-1;
#endif
#else // _MSC_VER
const time_t t = time(NULL);
const struct tm *tmp = localtime(&t);
#ifdef LOG_DATE
szPtrTime += strftime(szPtrTime, 80, "%y.%m.%d", tmp);
#endif
#if defined(LOG_DATE) && defined(LOG_TIME)
szPtrTime[0] = _T('-'); ++szPtrTime;
#endif
#ifdef LOG_TIME
szPtrTime += strftime(szPtrTime, 80, "%H:%M:%S", tmp);
#endif
#endif // _MSC_VER
#endif // LOG_DATE || LOG_TIME
#ifdef DEFAULT_LOGTYPE
LPCTSTR const logType(lt<m_arrLogTypes.GetSize() ? m_arrLogTypes[lt] : (LPCSTR)DEFAULT_LOGTYPE);
#else
LPCTSTR const logType(lt<m_arrLogTypes.GetSize() ? m_arrLogTypes[lt] : g_appType);
#endif
if ((size_t)_vsntprintf(szBuffer, 2048, szFormat, args) > 2048) {
// not enough space for the full string, reprint dynamically
m_message.FormatSafe("%s [%s] %s" LINE_SEPARATOR_STR, szTime, logType, String::FormatStringSafe(szFormat, args).c_str());
} else {
// enough space for all the string, print directly
m_message.Format("%s [%s] %s" LINE_SEPARATOR_STR, szTime, logType, szBuffer);
}
TRACE(m_message);
// signal listeners
FOREACHPTR(pClbk, *m_arrRecordClbk)
(*pClbk)(m_message);
}
/*----------------------------------------------------------------*/
/*-----------------------------------------------------------*
* LogFile class implementation *
*-----------------------------------------------------------*/
/**
* Constructor
*/
LogFile::LogFile()
{
}
bool LogFile::Open(LPCTSTR logName)
{
Util::ensureFolder(logName);
m_ptrFile = new File(logName, File::WRITE, File::CREATE | File::TRUNCATE);
if (!m_ptrFile->isOpen()) {
m_ptrFile = NULL;
return false;
}
GET_LOG().RegisterListener(DELEGATEBINDCLASS(Log::ClbkRecordMsg, &LogFile::Record, this));
return true;
}
void LogFile::Close()
{
if (m_ptrFile == NULL)
return;
GET_LOG().UnregisterListener(DELEGATEBINDCLASS(Log::ClbkRecordMsg, &LogFile::Record, this));
m_ptrFile = NULL;
}
void LogFile::Pause()
{
if (m_ptrFile == NULL)
return;
GET_LOG().UnregisterListener(DELEGATEBINDCLASS(Log::ClbkRecordMsg, &LogFile::Record, this));
}
void LogFile::Play()
{
if (m_ptrFile == NULL)
return;
GET_LOG().RegisterListener(DELEGATEBINDCLASS(Log::ClbkRecordMsg, &LogFile::Record, this));
}
void LogFile::Record(const String& msg)
{
ASSERT(m_ptrFile != NULL);
m_ptrFile->print(msg);
m_ptrFile->flush();
}
/*----------------------------------------------------------------*/
/*-----------------------------------------------------------*
* LogConsole class implementation *
*-----------------------------------------------------------*/
#define MAX_CONSOLE_WIDTH 100
#define MAX_CONSOLE_LINES 2000
#ifdef _MSC_VER
#include <io.h>
#define STDIN_FILENO 0 /* file descriptor for stdin */
#define STDOUT_FILENO 1 /* file descriptor for stdout */
#define STDERR_FILENO 2 /* file descriptor for stderr */
#else
#include <unistd.h>
#endif
#include <fcntl.h>
class LogConsoleOutbuf : public std::streambuf {
public:
LogConsoleOutbuf() {
setp(0, 0);
}
virtual int_type overflow(int_type c = traits_type::eof()) {
return fputc(c, stdout) == EOF ? traits_type::eof() : c;
}
};
/**
* Constructor
*/
LogConsole::LogConsole()
:
#ifdef _USE_COSOLEFILEHANDLES
m_fileIn(NULL), m_fileOut(NULL), m_fileErr(NULL),
#else
m_fileIn(-1), m_fileOut(-1), m_fileErr(-1),
#endif
m_cout(NULL), m_coutOld(NULL),
m_cerr(NULL), m_cerrOld(NULL),
bManageConsole(false)
{
}
bool LogConsole::IsOpen() const
{
#ifdef _USE_COSOLEFILEHANDLES
return (m_fileOut != NULL || m_fileIn != NULL || m_fileErr != NULL);
#else
return (m_fileOut != -1 || m_fileIn != -1 || m_fileErr != -1);
#endif
}
#ifdef _MSC_VER
void LogConsole::Open()
{
if (IsOpen())
return;
// allocate a console for this app
bManageConsole = (AllocConsole()!=FALSE?true:false);
// capture std::cout and std::cerr
if (bManageConsole && !m_cout) {
// set std::cout to use our custom streambuf
m_cout = new LogConsoleOutbuf;
m_coutOld = std::cout.rdbuf(m_cout);
// use same buffer for std::cerr as well
m_cerr = NULL;
m_cerrOld = std::cerr.rdbuf(m_cout);
}
// set the screen buffer to be big enough to let us scroll text
CONSOLE_SCREEN_BUFFER_INFO coninfo;
GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &coninfo);
coninfo.dwSize.X = MAX_CONSOLE_WIDTH; // does not resize the console window
coninfo.dwSize.Y = MAX_CONSOLE_LINES;
SetConsoleScreenBufferSize(GetStdHandle(STD_OUTPUT_HANDLE), coninfo.dwSize);
#if 1 && defined(_USE_COSOLEFILEHANDLES)
// redirect standard stream to the console
m_fileIn = freopen("CONIN$", "r", stdin);
m_fileOut = freopen("CONOUT$", "w", stdout);
// there isn't any CONERR$, so that we merge stderr into CONOUT$
// http://msdn.microsoft.com/en-us/library/windows/desktop/ms683231%28v=vs.85%29.aspx
m_fileErr = freopen("CONOUT$", "w", stderr);
#elif 1 && defined(_USE_COSOLEFILEHANDLES)
// Fix up the allocated console so that output works in all cases.
// Change std handles to refer to new console handles. Before doing so, ensure
// that stdout/stderr haven't been redirected to a valid file
// See http://support.microsoft.com/kb/105305/en-us
// stdout
if (_fileno(stdout) == -1 || _get_osfhandle(fileno(stdout)) == -1) {
int hCrt = ::_open_osfhandle((intptr_t)GetStdHandle(STD_OUTPUT_HANDLE), _O_TEXT);
if (hCrt != -1) {
m_fileOut = ::_fdopen(hCrt, "w");
if (m_fileOut)
*stdout = *m_fileOut;
}
}
// stderr
if (_fileno(stderr) == -1 || _get_osfhandle(fileno(stderr)) == -1) {
int hCrt = ::_open_osfhandle((intptr_t)::GetStdHandle(STD_ERROR_HANDLE), _O_TEXT);
if (hCrt != -1) {
m_fileErr = ::_fdopen(hCrt, "w");
if (m_fileErr)
*stderr = *m_fileErr;
}
}
// stdin
if (_fileno(stdin) == -1 || _get_osfhandle(fileno(stdin)) == -1) {
int hCrt = ::_open_osfhandle((intptr_t)::GetStdHandle(STD_INPUT_HANDLE), _O_TEXT);
if (hCrt != -1) {
m_fileIn = ::_fdopen(hCrt, "r");
if (m_fileIn)
*stdin = *m_fileIn;
}
}
#else
int hConHandle;
// redirect unbuffered STDIN to the console
hConHandle = _open_osfhandle((intptr_t)GetStdHandle(STD_INPUT_HANDLE), _O_TEXT);
m_fileIn = _dup(STDIN_FILENO);
_dup2(hConHandle, STDIN_FILENO);
_close(hConHandle);
setvbuf(stdin, NULL, _IONBF, 0);
// redirect unbuffered STDOUT to the console
hConHandle = _open_osfhandle((intptr_t)GetStdHandle(STD_OUTPUT_HANDLE), _O_TEXT);
m_fileOut = _dup(STDOUT_FILENO);
_dup2(hConHandle, STDOUT_FILENO);
_close(hConHandle);
setvbuf(stdout, NULL, _IONBF, 0);
// redirect unbuffered STDERR to the console
hConHandle = _open_osfhandle((intptr_t)GetStdHandle(STD_ERROR_HANDLE), _O_TEXT);
m_fileErr = _dup(STDERR_FILENO);
_dup2(hConHandle, STDERR_FILENO);
_close(hConHandle);
setvbuf(stderr, NULL, _IONBF, 0);
// make cout, wcout, cin, wcin, wcerr, cerr, wclog and clog
// point to console as well
std::ios::sync_with_stdio();
#endif
// register with our log system
GET_LOG().RegisterListener(DELEGATEBINDCLASS(Log::ClbkRecordMsg, &LogConsole::Record, this));
}
void LogConsole::Close()
{
if (!IsOpen())
return;
GET_LOG().UnregisterListener(DELEGATEBINDCLASS(Log::ClbkRecordMsg, &LogConsole::Record, this));
#ifdef _USE_COSOLEFILEHANDLES
// close console stream handles
fclose(m_fileIn); m_fileIn = NULL;
fclose(m_fileOut); m_fileOut = NULL;
fclose(m_fileErr); m_fileErr = NULL;
#else
// restore STDIN
_dup2(m_fileIn, STDIN_FILENO);
_close(m_fileIn);
m_fileIn = -1;
// restore STDOUT
_dup2(m_fileOut, STDOUT_FILENO);
_close(m_fileOut);
m_fileOut = -1;
// restore STDERR
_dup2(m_fileErr, STDERR_FILENO);
_close(m_fileErr);
m_fileErr = -1;
#endif
// close console
if (bManageConsole) {
if (m_cout) {
// set std::cout to the original streambuf
std::cout.rdbuf(m_coutOld);
std::cerr.rdbuf(m_cerrOld);
delete m_cout; m_cout = NULL;
}
FreeConsole();
}
#ifndef _USE_COSOLEFILEHANDLES
std::ios::sync_with_stdio();
#endif
}
void LogConsole::Record(const String& msg)
{
ASSERT(IsOpen());
printf(msg);
fflush(stdout);
}
#else
void LogConsole::Open()
{
if (IsOpen())
return;
++m_fileIn;
// register with our log system
GET_LOG().RegisterListener(DELEGATEBINDCLASS(Log::ClbkRecordMsg, &LogConsole::Record, this));
}
void LogConsole::Close()
{
if (!IsOpen())
return;
// unregister with our log system
GET_LOG().UnregisterListener(DELEGATEBINDCLASS(Log::ClbkRecordMsg, &LogConsole::Record, this));
--m_fileIn;
}
void LogConsole::Record(const String& msg)
{
ASSERT(IsOpen());
printf(_T("%s"), msg.c_str());
fflush(stdout);
}
#endif // _MSC_VER
void LogConsole::Pause()
{
if (IsOpen())
GET_LOG().UnregisterListener(DELEGATEBINDCLASS(Log::ClbkRecordMsg, &LogConsole::Record, this));
}
void LogConsole::Play()
{
if (IsOpen())
GET_LOG().RegisterListener(DELEGATEBINDCLASS(Log::ClbkRecordMsg, &LogConsole::Record, this));
}
/*----------------------------------------------------------------*/

211
libs/Common/Log.h

@ -0,0 +1,211 @@ @@ -0,0 +1,211 @@
////////////////////////////////////////////////////////////////////
// Log.h
//
// Copyright 2007 cDc@seacave
// Distributed under the Boost Software License, Version 1.0
// (See http://www.boost.org/LICENSE_1_0.txt)
#ifndef __SEACAVE_LOG_H__
#define __SEACAVE_LOG_H__
// I N C L U D E S /////////////////////////////////////////////////
// D E F I N E S ///////////////////////////////////////////////////
// use file handles to access console
#define _USE_COSOLEFILEHANDLES
//#define LOG_DATE // add date info for every log
#define LOG_TIME // add time info for every log
#define LOG_THREAD // make log multi-thread safe
#define LOG_STREAM // add stream support (operator <<)
#define LOGTYPE_SIZE 8
#define DEFAULT_LOGTYPE _T("App ")
#define DECLARE_LOG() \
protected: static const Log::Idx ms_nLogType;
#define DEFINE_LOG(classname, log) \
const Log::Idx classname::ms_nLogType(REGISTER_LOG(log));
#ifdef LOG_THREAD
#include "CriticalSection.h"
#endif
namespace SEACAVE {
// S T R U C T S ///////////////////////////////////////////////////
class GENERAL_API Log
{
DECLARE_SINGLETON(Log);
public:
typedef uint32_t Idx;
typedef DELEGATE<void (const String&)> ClbkRecordMsg;
typedef cList<ClbkRecordMsg> ClbkRecordMsgArray;
typedef CSharedPtr<ClbkRecordMsgArray> ClbkRecordMsgArrayPtr;
public:
// log methods
void Open() { m_arrRecordClbk = new ClbkRecordMsgArray; }
void Close() { m_arrRecordClbk = NULL; }
void Join(Log& log) { m_arrRecordClbk = log.m_arrRecordClbk; }
void RegisterListener(ClbkRecordMsg);
void UnregisterListener(ClbkRecordMsg);
Idx RegisterType(LPCTSTR);
void ResetTypes();
void Write(LPCTSTR, ...);
void Write(Idx, LPCTSTR, ...);
#ifdef LOG_STREAM
template<class T> inline Log& operator<<(const T& val) {
#ifdef LOG_THREAD
Lock l(m_cs);
std::ostringstream& ostr = m_streams[__THREAD__];
#else
std::ostringstream& ostr = m_stream;
#endif
ostr << val;
const std::string& line = ostr.str();
if (!line.empty() && *(line.end()-1) == _T('\n')) {
Write(line.substr(0, line.size()-1).c_str());
ostr.str(_T(""));
}
return *this;
}
// the type of std::cout
typedef std::basic_ostream<char, std::char_traits<char> > CoutType;
// the function signature of std::endl
typedef CoutType& (*StandardEndLine)(CoutType&);
// define an operator<< to take in std::endl
inline Log& operator<<(StandardEndLine) {
#ifdef LOG_THREAD
Lock l(m_cs);
std::ostringstream& ostr = m_streams[__THREAD__];
#else
std::ostringstream& ostr = m_stream;
#endif
Write(ostr.str().c_str());
ostr.str(_T(""));
return *this;
}
#endif
protected:
// write a message of a certain type to the log
void _Record(Idx, LPCTSTR, va_list);
protected:
struct LogType {
TCHAR szName[LOGTYPE_SIZE+1];
inline operator LPCTSTR () const { return szName; }
inline operator LPTSTR () { return szName; }
};
typedef cList<LogType, const LogType&, 0, 8> LogTypeArr;
// log members
String m_message; // last recorded message
ClbkRecordMsgArrayPtr m_arrRecordClbk;// the array with all registered listeners
LogTypeArr m_arrLogTypes; // the array with all the registered log types
#ifdef LOG_THREAD
// threading
RWLock m_lock; // mutex used to ensure multi-thread safety
#endif
#ifdef LOG_STREAM
// streaming
#ifdef LOG_THREAD
typedef std::unordered_map<unsigned,std::ostringstream> StreamMap;
StreamMap m_streams; // stream object used to handle one log with operator << (one for each thread)
CriticalSection m_cs; // mutex used to ensure multi-thread safety for accessing m_streams
#else
std::ostringstream m_stream; // stream object used to handle one log with operator <<
#endif
#endif
// static
#ifndef DEFAULT_LOGTYPE
static LogType g_appType;
#endif
};
#define GET_LOG() SEACAVE::Log::GetInstance()
#define OPEN_LOG() GET_LOG().Open()
#define CLOSE_LOG() GET_LOG().Close()
#define JOIN_LOG(log) GET_LOG().Join(log)
#define REGISTER_LOG(lt) GET_LOG().RegisterType(lt)
#define LOG GET_LOG().Write
#define SLOG(msg) GET_LOG() << msg
#ifndef _RELEASE
#define LOGV LOG // include extra details in the log
#else
#define LOGV(...)
#endif
/*----------------------------------------------------------------*/
class GENERAL_API LogFile
{
DECLARE_SINGLETON(LogFile);
public:
~LogFile() { Close(); }
// log methods
bool Open(LPCTSTR);
void Close();
void Pause();
void Play();
void Record(const String&);
protected:
FilePtr m_ptrFile; // the log file
};
#define GET_LOGFILE() LogFile::GetInstance()
#define OPEN_LOGFILE(log) GET_LOGFILE().Open(log)
#define CLOSE_LOGFILE() GET_LOGFILE().Close()
/*----------------------------------------------------------------*/
class GENERAL_API LogConsole
{
DECLARE_SINGLETON(LogConsole);
public:
~LogConsole() { Close(); }
bool IsOpen() const;
// log methods
void Open();
void Close();
void Pause();
void Play();
void Record(const String&);
protected:
#ifdef _USE_COSOLEFILEHANDLES
typedef FILE* StreamHandle;
#else
typedef int StreamHandle;
#endif
StreamHandle m_fileIn; // the log file in
StreamHandle m_fileOut; // the log file out
StreamHandle m_fileErr; // the log file error
std::streambuf* m_cout; // the redirected cout stream
std::streambuf* m_coutOld; // the original cout stream
std::streambuf* m_cerr; // the redirected cerr stream
std::streambuf* m_cerrOld; // the original cout stream
bool bManageConsole; // remember if the console is created here or is an existing console
};
#define GET_LOGCONSOLE() LogConsole::GetInstance()
#define OPEN_LOGCONSOLE() GET_LOGCONSOLE().Open()
#define CLOSE_LOGCONSOLE() GET_LOGCONSOLE().Close()
/*----------------------------------------------------------------*/
} // namespace SEACAVE
#endif // __SEACAVE_LOG_H__

182
libs/Common/MemFile.h

@ -0,0 +1,182 @@ @@ -0,0 +1,182 @@
////////////////////////////////////////////////////////////////////
// MemFile.h
//
// Copyright 2007 cDc@seacave
// Distributed under the Boost Software License, Version 1.0
// (See http://www.boost.org/LICENSE_1_0.txt)
#ifndef __SEACAVE_MEMFILE_H__
#define __SEACAVE_MEMFILE_H__
// I N C L U D E S /////////////////////////////////////////////////
#include "Streams.h"
// D E F I N E S ///////////////////////////////////////////////////
namespace SEACAVE {
// S T R U C T S ///////////////////////////////////////////////////
class GENERAL_API MemFile : public IOStream {
public:
MemFile() : m_buffer(NULL), m_sizeBuffer(0), m_size(0), m_pos(0) {
}
MemFile(size_t initialSize) : m_buffer(new BYTE[initialSize]), m_sizeBuffer(initialSize), m_size(0), m_pos(0) {
}
MemFile(BYTE* data, size_t size) : m_buffer(data), m_sizeBuffer(0), m_size(size), m_pos(0) {
}
virtual ~MemFile() {
close();
}
static LPCSTR getClassType() { return "MemFile"; }
virtual LPCSTR getClassName() const { return MemFile::getClassType(); }
bool isOpen() const { return m_buffer != NULL; }
virtual void close() {
if (!isOpen())
return;
if (m_sizeBuffer)
delete[] m_buffer;
m_sizeBuffer = 0;
m_buffer = NULL;
m_size = 0;
m_pos = 0;
}
virtual size_f_t getSizeBuffer() const {
return m_sizeBuffer;
}
virtual size_f_t getSizeLeft() const {
return m_size - m_pos;
}
virtual size_f_t getSize() const {
return m_size;
}
virtual bool setSize(size_f_t newSize) {
ASSERT(newSize >= 0);
if (newSize > m_sizeBuffer)
setMaxSize(newSize);
m_size = newSize;
if (m_pos > m_size)
m_pos = m_size;
return true;
}
virtual bool growSize(size_f_t deltaSize) {
return setSize(m_size+deltaSize);
}
virtual bool moveSize(size_f_t delataSize) {
return setSize(m_size + delataSize);
}
virtual bool setMaxSize(size_f_t newSize) {
ASSERT(newSize > m_sizeBuffer);
// grow by 50% or at least to minNewVectorSize
const size_f_t expoSize(m_sizeBuffer + (m_sizeBuffer>>1));
if (newSize < expoSize)
newSize = expoSize;
// allocate a larger chunk of memory, copy the data and delete the old chunk
BYTE* const tmp(m_buffer);
m_buffer = new BYTE[(size_t)newSize];
if (!m_buffer) {
m_buffer = tmp;
return false;
}
if (m_size > newSize) {
m_size = newSize;
if (m_pos > m_size)
m_pos = m_size;
}
memcpy(m_buffer, tmp, (size_t)m_size);
if (m_sizeBuffer)
delete[] tmp;
m_sizeBuffer = newSize;
return true;
}
virtual bool growMaxSize(size_f_t deltaSize) {
return setMaxSize(m_sizeBuffer+deltaSize);
}
virtual bool ensureSize(size_f_t extraSize) {
const size_f_t newSize = m_size + extraSize;
if (newSize > m_sizeBuffer)
return setMaxSize(newSize);
return true;
}
virtual size_f_t getPos() const {
return m_pos;
}
virtual bool setPos(size_f_t pos) {
if (pos > m_size && !setSize(pos))
return false;
m_pos = pos;
return true;
}
virtual bool movePos(size_f_t delataPos) {
return setPos(m_pos + delataPos);
}
virtual bool isEOF() {
return (m_pos == m_size);
}
virtual size_t read(void* buf, size_t len) {
if (m_pos >= m_size)
return 0;
if (m_pos+(size_f_t)len > m_size)
len = (size_t)(m_size-m_pos);
memcpy(buf, m_buffer+m_pos, len);
m_pos += len;
return len;
}
virtual size_t write(const void* buf, size_t len) {
const size_f_t endSize = m_pos+len;
if (endSize > m_size && !setSize(endSize))
return 0;
memcpy(m_buffer+m_pos, buf, len);
m_pos += len;
return len;
}
virtual BYTE* getData() {
return m_buffer + m_pos;
}
virtual BYTE* getBuffer() {
return m_buffer;
}
virtual size_t flush() {
return 0;
}
protected:
BYTE* m_buffer; //buffer where we will store the data
size_f_t m_sizeBuffer; //size of the whole buffer, 0 if no buffer or not allocated here
size_f_t m_size; //size of the stored data
size_f_t m_pos; //current position in the stored data
private:
MemFile(const MemFile&);
MemFile& operator=(const MemFile&);
};
/*----------------------------------------------------------------*/
} // namespace SEACAVE
#endif // __SEACAVE_MEMFILE_H__

127
libs/Common/OBB.h

@ -0,0 +1,127 @@ @@ -0,0 +1,127 @@
////////////////////////////////////////////////////////////////////
// OBB.h
//
// Copyright 2007 cDc@seacave
// Distributed under the Boost Software License, Version 1.0
// (See http://www.boost.org/LICENSE_1_0.txt)
#ifndef __SEACAVE_OBB_H__
#define __SEACAVE_OBB_H__
// I N C L U D E S /////////////////////////////////////////////////
// D E F I N E S ///////////////////////////////////////////////////
namespace SEACAVE {
// S T R U C T S ///////////////////////////////////////////////////
template <typename TYPE, int DIMS>
class TAABB;
template <typename TYPE, int DIMS>
class TRay;
// Basic oriented bounding-box class
template <typename TYPE, int DIMS>
class TOBB
{
STATIC_ASSERT(DIMS > 0 && DIMS <= 3);
public:
typedef TYPE Type;
typedef Eigen::Matrix<TYPE,DIMS,1> POINT;
typedef Eigen::Matrix<TYPE,DIMS,DIMS,Eigen::RowMajor> MATRIX;
typedef SEACAVE::TAABB<TYPE,DIMS> AABB;
typedef SEACAVE::TRay<TYPE,DIMS> RAY;
typedef unsigned ITYPE;
typedef Eigen::Matrix<ITYPE,DIMS,1> TRIANGLE;
enum { numCorners = (DIMS==1 ? 2 : (DIMS==2 ? 4 : 8)) }; // 2^DIMS
enum { numScalar = (5*DIMS) };
MATRIX m_rot; // rotation matrix from world to local (orthonormal axes)
POINT m_pos; // translation from local to world (center-point)
POINT m_ext; // bounding box extents in local (half axis length)
//---------------------------------------
inline TOBB() {}
inline TOBB(bool);
inline TOBB(const AABB&);
inline TOBB(const MATRIX& rot, const POINT& ptMin, const POINT& ptMax);
inline TOBB(const POINT* pts, size_t n);
inline TOBB(const POINT* pts, size_t n, const TRIANGLE* tris, size_t s);
template <typename CTYPE>
inline TOBB(const TOBB<CTYPE, DIMS>&);
inline void Set(const AABB&); // build from AABB
inline void Set(const MATRIX& rot, const POINT& ptMin, const POINT& ptMax); // build from rotation matrix from world to local, and local min/max corners
inline void Set(const POINT* pts, size_t n); // build from points
inline void Set(const POINT* pts, size_t n, const TRIANGLE* tris, size_t s); // build from triangles
inline void Set(const MATRIX& C, const POINT* pts, size_t n); // build from covariance matrix
inline void SetRotation(const MATRIX& C); // build rotation only from covariance matrix
inline void SetBounds(const POINT* pts, size_t n); // build size and center only from given points
inline void BuildBegin(); // start online build for computing the rotation
inline void BuildAdd(const POINT&); // add a new point to the online build
inline void BuildEnd(); // end online build for computing the rotation
inline bool IsValid() const;
inline TOBB& Enlarge(TYPE);
inline TOBB& EnlargePercent(TYPE);
inline void Translate(const POINT&);
inline void Transform(const MATRIX&);
inline POINT GetCenter() const;
inline void GetCenter(POINT&) const;
inline POINT GetSize() const;
inline void GetSize(POINT&) const;
inline void GetCorners(POINT pts[numCorners]) const;
inline AABB GetAABB() const;
inline TYPE GetVolume() const;
bool Intersects(const POINT&) const;
inline TYPE& operator [] (BYTE i) { ASSERT(i<numScalar); return m_rot.data()[i]; }
inline TYPE operator [] (BYTE i) const { ASSERT(i<numScalar); return m_rot.data()[i]; }
friend std::ostream& operator << (std::ostream& st, const TOBB& obb) {
st << obb.m_rot; st << std::endl;
st << obb.m_pos; st << std::endl;
st << obb.m_ext; st << std::endl;
return st;
}
friend std::istream& operator >> (std::istream& st, TOBB& obb) {
st >> obb.m_rot;
st >> obb.m_pos;
st >> obb.m_ext;
return st;
}
#ifdef _USE_BOOST
// implement BOOST serialization
template<class Archive>
void serialize(Archive& ar, const unsigned int /*version*/) {
ar & m_rot;
ar & m_pos;
ar & m_ext;
}
#endif
}; // class TOBB
/*----------------------------------------------------------------*/
#include "OBB.inl"
/*----------------------------------------------------------------*/
} // namespace SEACAVE
#endif // __SEACAVE_OBB_H__

401
libs/Common/OBB.inl

@ -0,0 +1,401 @@ @@ -0,0 +1,401 @@
////////////////////////////////////////////////////////////////////
// OBB.inl
//
// Copyright 2007 cDc@seacave
// Distributed under the Boost Software License, Version 1.0
// (See http://www.boost.org/LICENSE_1_0.txt)
// D E F I N E S ///////////////////////////////////////////////////
// S T R U C T S ///////////////////////////////////////////////////
template <typename TYPE, int DIMS>
inline TOBB<TYPE,DIMS>::TOBB(bool)
:
m_rot(MATRIX::Identity()),
m_pos(POINT::Zero()),
m_ext(POINT::Zero())
{
}
template <typename TYPE, int DIMS>
inline TOBB<TYPE,DIMS>::TOBB(const AABB& aabb)
{
Set(aabb);
}
template <typename TYPE, int DIMS>
inline TOBB<TYPE,DIMS>::TOBB(const MATRIX& rot, const POINT& ptMin, const POINT& ptMax)
{
Set(rot, ptMin, ptMax);
}
template <typename TYPE, int DIMS>
inline TOBB<TYPE,DIMS>::TOBB(const POINT* pts, size_t n)
{
Set(pts, n);
}
template <typename TYPE, int DIMS>
inline TOBB<TYPE,DIMS>::TOBB(const POINT* pts, size_t n, const TRIANGLE* tris, size_t s)
{
Set(pts, n, tris, s);
} // constructor
template <typename TYPE, int DIMS>
template <typename CTYPE>
inline TOBB<TYPE,DIMS>::TOBB(const TOBB<CTYPE,DIMS>& rhs)
:
m_rot(rhs.m_rot.template cast<TYPE>()),
m_pos(rhs.m_pos.template cast<TYPE>()),
m_ext(rhs.m_ext.template cast<TYPE>())
{
} // copy constructor
/*----------------------------------------------------------------*/
template <typename TYPE, int DIMS>
inline void TOBB<TYPE,DIMS>::Set(const AABB& aabb)
{
m_rot.setIdentity();
m_pos = aabb.GetCenter();
m_ext = aabb.GetSize()/TYPE(2);
}
// build from rotation matrix from world to local, and local min/max corners
template <typename TYPE, int DIMS>
inline void TOBB<TYPE,DIMS>::Set(const MATRIX& rot, const POINT& ptMin, const POINT& ptMax)
{
m_rot = rot;
m_pos = (ptMax + ptMin) * TYPE(0.5);
m_ext = (ptMax - ptMin) * TYPE(0.5);
}
// Inspired from "Fitting Oriented Bounding Boxes" by James Gregson
// http://jamesgregson.blogspot.ro/2011/03/latex-test.html
// build an OBB from a vector of input points. This
// method just forms the covariance matrix and hands
// it to the build_from_covariance_matrix method
// which handles fitting the box to the points
template <typename TYPE, int DIMS>
inline void TOBB<TYPE,DIMS>::Set(const POINT* pts, size_t n)
{
ASSERT(n >= DIMS);
// loop over the points to find the mean point
// location and to build the covariance matrix;
// note that we only have
// to build terms for the upper triangular
// portion since the matrix is symmetric
POINT mu(POINT::Zero());
TYPE cxx=0, cxy=0, cxz=0, cyy=0, cyz=0, czz=0;
for (size_t i=0; i<n; ++i) {
const POINT& p = pts[i];
mu += p;
cxx += p(0)*p(0);
cxy += p(0)*p(1);
cxz += p(0)*p(2);
cyy += p(1)*p(1);
cyz += p(1)*p(2);
czz += p(2)*p(2);
}
const TYPE invN(TYPE(1)/TYPE(n));
cxx = (cxx - mu(0)*mu(0)*invN)*invN;
cxy = (cxy - mu(0)*mu(1)*invN)*invN;
cxz = (cxz - mu(0)*mu(2)*invN)*invN;
cyy = (cyy - mu(1)*mu(1)*invN)*invN;
cyz = (cyz - mu(1)*mu(2)*invN)*invN;
czz = (czz - mu(2)*mu(2)*invN)*invN;
// now build the covariance matrix
MATRIX C;
C(0,0) = cxx; C(0,1) = cxy; C(0,2) = cxz;
C(1,0) = cxy; C(1,1) = cyy; C(1,2) = cyz;
C(2,0) = cxz; C(2,1) = cyz; C(2,2) = czz;
// set the OBB parameters from the covariance matrix
Set(C, pts, n);
}
// builds an OBB from triangles specified as an array of
// points with integer indices into the point array. Forms
// the covariance matrix for the triangles, then uses the
// method build_from_covariance_matrix method to fit
// the box. ALL points will be fit in the box, regardless
// of whether they are indexed by a triangle or not.
template <typename TYPE, int DIMS>
inline void TOBB<TYPE,DIMS>::Set(const POINT* pts, size_t n, const TRIANGLE* tris, size_t s)
{
ASSERT(n >= DIMS);
// loop over the triangles this time to find the
// mean location
POINT mu(POINT::Zero());
TYPE Am=0;
TYPE cxx=0, cxy=0, cxz=0, cyy=0, cyz=0, czz=0;
for (size_t i=0; i<s; ++i) {
ASSERT(tris[i](0)<n && tris[i](1)<n && tris[i](2)<n);
const POINT& p = pts[tris[i](0)];
const POINT& q = pts[tris[i](1)];
const POINT& r = pts[tris[i](2)];
const POINT mui = (p+q+r)/TYPE(3);
const TYPE Ai = (q-p).cross(r-p).normalize()/TYPE(2);
mu += mui*Ai;
Am += Ai;
// these bits set the c terms to Am*E[xx], Am*E[xy], Am*E[xz]....
const TYPE Ai12 = Ai/TYPE(12);
cxx += (TYPE(9)*mui(0)*mui(0) + p(0)*p(0) + q(0)*q(0) + r(0)*r(0))*Ai12;
cxy += (TYPE(9)*mui(0)*mui(1) + p(0)*p(1) + q(0)*q(1) + r(0)*r(1))*Ai12;
cxz += (TYPE(9)*mui(0)*mui(2) + p(0)*p(2) + q(0)*q(2) + r(0)*r(2))*Ai12;
cyy += (TYPE(9)*mui(1)*mui(1) + p(1)*p(1) + q(1)*q(1) + r(1)*r(1))*Ai12;
cyz += (TYPE(9)*mui(1)*mui(2) + p(1)*p(2) + q(1)*q(2) + r(1)*r(2))*Ai12;
czz += (TYPE(9)*mui(2)*mui(2) + p(2)*p(2) + q(2)*q(2) + r(2)*r(2))*Ai12;
}
// divide out the Am fraction from the average position and
// covariance terms
mu /= Am;
cxx /= Am; cxy /= Am; cxz /= Am; cyy /= Am; cyz /= Am; czz /= Am;
// now subtract off the E[x]*E[x], E[x]*E[y], ... terms
cxx -= mu(0)*mu(0); cxy -= mu(0)*mu(1); cxz -= mu(0)*mu(2);
cyy -= mu(1)*mu(1); cyz -= mu(1)*mu(2); czz -= mu(2)*mu(2);
// now build the covariance matrix
MATRIX C;
C(0,0)=cxx; C(0,1)=cxy; C(0,2)=cxz;
C(1,0)=cxy; C(1,1)=cyy; C(1,2)=cyz;
C(2,0)=cxz; C(1,2)=cyz; C(2,2)=czz;
// set the obb parameters from the covariance matrix
Set(C, pts, n);
}
// method to set the OBB parameters which produce a box oriented according to
// the covariance matrix C, and that contains the given points
template <typename TYPE, int DIMS>
inline void TOBB<TYPE,DIMS>::Set(const MATRIX& C, const POINT* pts, size_t n)
{
// extract rotation from the covariance matrix
SetRotation(C);
// extract size and center from the given points
SetBounds(pts, n);
}
// method to set the OBB rotation which produce a box oriented according to
// the covariance matrix C (only the rotations is set)
template <typename TYPE, int DIMS>
inline void TOBB<TYPE,DIMS>::SetRotation(const MATRIX& C)
{
// extract the eigenvalues and eigenvectors from C
const Eigen::SelfAdjointEigenSolver<MATRIX> es(C);
ASSERT(es.info() == Eigen::Success);
// find the right, up and forward vectors from the eigenvectors
// and set the rotation matrix using the eigenvectors
ASSERT(es.eigenvalues()(0) < es.eigenvalues()(1) && es.eigenvalues()(1) < es.eigenvalues()(2));
m_rot = es.eigenvectors().transpose();
if (m_rot.determinant() < 0)
m_rot = -m_rot;
}
// method to set the OBB center and size that contains the given points
// the rotations should be already set
template <typename TYPE, int DIMS>
inline void TOBB<TYPE,DIMS>::SetBounds(const POINT* pts, size_t n)
{
ASSERT(n >= DIMS);
ASSERT(ISEQUAL((m_rot*m_rot.transpose()).trace(), TYPE(3)) && ISEQUAL(m_rot.determinant(), TYPE(1)));
// build the bounding box extents in the rotated frame
const TYPE tmax = std::numeric_limits<TYPE>::max();
POINT minim(tmax, tmax, tmax), maxim(-tmax, -tmax, -tmax);
for (size_t i=0; i<n; ++i) {
const POINT p_prime(m_rot * pts[i]);
if (minim(0) > p_prime(0)) minim(0) = p_prime(0);
if (minim(1) > p_prime(1)) minim(1) = p_prime(1);
if (minim(2) > p_prime(2)) minim(2) = p_prime(2);
if (maxim(0) < p_prime(0)) maxim(0) = p_prime(0);
if (maxim(1) < p_prime(1)) maxim(1) = p_prime(1);
if (maxim(2) < p_prime(2)) maxim(2) = p_prime(2);
}
// set the center of the OBB to be the average of the
// minimum and maximum, and the extents be half of the
// difference between the minimum and maximum
const POINT center((maxim+minim)*TYPE(0.5));
m_pos = m_rot.transpose() * center;
m_ext = (maxim-minim)*TYPE(0.5);
} // Set
/*----------------------------------------------------------------*/
template <typename TYPE, int DIMS>
inline void TOBB<TYPE,DIMS>::BuildBegin()
{
m_rot = MATRIX::Zero();
m_pos = POINT::Zero();
m_ext = POINT::Zero();
}
template <typename TYPE, int DIMS>
inline void TOBB<TYPE,DIMS>::BuildAdd(const POINT& p)
{
// store mean in m_pos
m_pos += p;
// store covariance params in m_rot
m_rot(0,0) += p(0)*p(0);
m_rot(0,1) += p(0)*p(1);
m_rot(0,2) += p(0)*p(2);
m_rot(1,0) += p(1)*p(1);
m_rot(1,1) += p(1)*p(2);
m_rot(1,2) += p(2)*p(2);
// store count in m_ext
++(*((size_t*)m_ext.data()));
}
template <typename TYPE, int DIMS>
inline void TOBB<TYPE,DIMS>::BuildEnd()
{
const TYPE invN(TYPE(1)/TYPE(*((size_t*)m_ext.data())));
const TYPE cxx = (m_rot(0,0) - m_pos(0)*m_pos(0)*invN)*invN;
const TYPE cxy = (m_rot(0,1) - m_pos(0)*m_pos(1)*invN)*invN;
const TYPE cxz = (m_rot(0,2) - m_pos(0)*m_pos(2)*invN)*invN;
const TYPE cyy = (m_rot(1,0) - m_pos(1)*m_pos(1)*invN)*invN;
const TYPE cyz = (m_rot(1,1) - m_pos(1)*m_pos(2)*invN)*invN;
const TYPE czz = (m_rot(1,2) - m_pos(2)*m_pos(2)*invN)*invN;
// now build the covariance matrix
MATRIX C;
C(0,0) = cxx; C(0,1) = cxy; C(0,2) = cxz;
C(1,0) = cxy; C(1,1) = cyy; C(1,2) = cyz;
C(2,0) = cxz; C(2,1) = cyz; C(2,2) = czz;
SetRotation(C);
} // Build
/*----------------------------------------------------------------*/
// check if the oriented bounding box has positive size
template <typename TYPE, int DIMS>
inline bool TOBB<TYPE,DIMS>::IsValid() const
{
return m_ext.minCoeff() > TYPE(0);
} // IsValid
/*----------------------------------------------------------------*/
template <typename TYPE, int DIMS>
inline TOBB<TYPE,DIMS>& TOBB<TYPE,DIMS>::Enlarge(TYPE x)
{
m_ext.array() += x;
return *this;
}
template <typename TYPE, int DIMS>
inline TOBB<TYPE,DIMS>& TOBB<TYPE,DIMS>::EnlargePercent(TYPE x)
{
m_ext *= x;
return *this;
} // Enlarge
/*----------------------------------------------------------------*/
// Update the box by the given pos delta.
template <typename TYPE, int DIMS>
inline void TOBB<TYPE,DIMS>::Translate(const POINT& d)
{
m_pos += d;
}
// Update the box by the given transform.
template <typename TYPE, int DIMS>
inline void TOBB<TYPE,DIMS>::Transform(const MATRIX& m)
{
m_rot = m * m_rot;
m_pos = m * m_pos;
}
/*----------------------------------------------------------------*/
template <typename TYPE, int DIMS>
inline typename TOBB<TYPE,DIMS>::POINT TOBB<TYPE,DIMS>::GetCenter() const
{
return m_pos;
}
template <typename TYPE, int DIMS>
inline void TOBB<TYPE,DIMS>::GetCenter(POINT& ptCenter) const
{
ptCenter = m_pos;
} // GetCenter
/*----------------------------------------------------------------*/
template <typename TYPE, int DIMS>
inline typename TOBB<TYPE,DIMS>::POINT TOBB<TYPE,DIMS>::GetSize() const
{
return m_ext*2;
}
template <typename TYPE, int DIMS>
inline void TOBB<TYPE,DIMS>::GetSize(POINT& ptSize) const
{
ptSize = m_ext*2;
} // GetSize
/*----------------------------------------------------------------*/
template <typename TYPE, int DIMS>
inline void TOBB<TYPE,DIMS>::GetCorners(POINT pts[numCorners]) const
{
if (DIMS == 2) {
const POINT pEAxis[2] = {
m_rot.row(0)*m_ext[0],
m_rot.row(1)*m_ext[1]
};
const POINT pos(m_rot.transpose()*m_pos);
pts[0] = pos - pEAxis[0] - pEAxis[1];
pts[1] = pos + pEAxis[0] - pEAxis[1];
pts[2] = pos + pEAxis[0] + pEAxis[1];
pts[3] = pos - pEAxis[0] + pEAxis[1];
}
if (DIMS == 3) {
const POINT pEAxis[3] = {
m_rot.row(0)*m_ext[0],
m_rot.row(1)*m_ext[1],
m_rot.row(2)*m_ext[2]
};
const POINT pos(m_rot.transpose()*m_pos);
pts[0] = pos - pEAxis[0] - pEAxis[1] - pEAxis[2];
pts[1] = pos - pEAxis[0] - pEAxis[1] + pEAxis[2];
pts[2] = pos + pEAxis[0] - pEAxis[1] - pEAxis[2];
pts[3] = pos + pEAxis[0] - pEAxis[1] + pEAxis[2];
pts[4] = pos + pEAxis[0] + pEAxis[1] - pEAxis[2];
pts[5] = pos + pEAxis[0] + pEAxis[1] + pEAxis[2];
pts[6] = pos - pEAxis[0] + pEAxis[1] - pEAxis[2];
pts[7] = pos - pEAxis[0] + pEAxis[1] + pEAxis[2];
}
} // GetCorners
// constructs the corner of the aligned bounding box in world space
template <typename TYPE, int DIMS>
inline typename TOBB<TYPE,DIMS>::AABB TOBB<TYPE,DIMS>::GetAABB() const
{
POINT pts[numCorners];
GetCorners(pts);
return AABB(pts, numCorners);
} // GetAABB
/*----------------------------------------------------------------*/
// computes the volume of the OBB, which is a measure of
// how tight the fit is (better OBBs will have smaller volumes)
template <typename TYPE, int DIMS>
inline TYPE TOBB<TYPE,DIMS>::GetVolume() const
{
return m_ext.prod()*numCorners;
}
/*----------------------------------------------------------------*/
template <typename TYPE, int DIMS>
bool TOBB<TYPE,DIMS>::Intersects(const POINT& pt) const
{
const POINT dist(m_rot * (pt - m_pos));
if (DIMS == 2) {
return ABS(dist[0]) <= m_ext[0]
&& ABS(dist[1]) <= m_ext[1];
}
if (DIMS == 3) {
return ABS(dist[0]) <= m_ext[0]
&& ABS(dist[1]) <= m_ext[1]
&& ABS(dist[2]) <= m_ext[2];
}
} // Intersects(POINT)
/*----------------------------------------------------------------*/

237
libs/Common/Octree.h

@ -0,0 +1,237 @@ @@ -0,0 +1,237 @@
////////////////////////////////////////////////////////////////////
// Octree.h
//
// Copyright 2007 cDc@seacave
// Distributed under the Boost Software License, Version 1.0
// (See http://www.boost.org/LICENSE_1_0.txt)
#ifndef __SEACAVE_OCTREE_H__
#define __SEACAVE_OCTREE_H__
// I N C L U D E S /////////////////////////////////////////////////
#include "AABB.h"
#include "Ray.h"
// D E F I N E S ///////////////////////////////////////////////////
namespace SEACAVE {
// S T R U C T S ///////////////////////////////////////////////////
// basic octree class
// each item should define the operator const POINT_TYPE& returning its center;
// at build time, a functor must be supplied that returns true as long as
// the current cell should be split farther, given its number of items and radius, ex:
// [](Octree::IDX_TYPE size, Octree::Type radius) {
// return size > SIZE && radius > RADIUS;
// }
// where:
// SIZE is the minimum number of items contained by the cell so that this to be divided further
// RADIUS is the minimum size of the cell allowed to be divided further
// both conditions represent exclusive limits and both should be true for the division to take place
template <typename ITEMARR_TYPE, typename TYPE, int DIMS, typename DATA_TYPE=uint32_t>
class TOctree
{
STATIC_ASSERT(DIMS > 0 && DIMS <= 3);
public:
typedef TYPE Type;
typedef typename ITEMARR_TYPE::Type ITEM_TYPE;
typedef typename ITEMARR_TYPE::IDX IDX_TYPE;
typedef SEACAVE::cList<IDX_TYPE,IDX_TYPE,0,1024,IDX_TYPE> IDXARR_TYPE;
typedef Eigen::Matrix<TYPE,DIMS,1> POINT_TYPE;
typedef SEACAVE::TAABB<TYPE,DIMS> AABB_TYPE;
typedef uint32_t SIZE_TYPE;
class CELL_TYPE {
public:
typedef struct {
POINT_TYPE center; // center of the current cell
} NODE_TYPE;
typedef struct {
IDX_TYPE idxBegin; // index in the global array of the first item contained by this cell
SIZE_TYPE size; // number of items contained by this cell in the global array
DATA_TYPE data; // user data associated with this leaf
} LEAF_TYPE;
enum { numChildren = (2<<(DIMS-1)) };
public:
inline CELL_TYPE();
inline ~CELL_TYPE();
inline void Release();
inline void Swap(CELL_TYPE&);
inline unsigned ComputeChild(const POINT_TYPE& item) const;
static void ComputeCenter(POINT_TYPE []);
static inline POINT_TYPE ComputeChildCenter(const POINT_TYPE&, TYPE, unsigned);
inline bool IsLeaf() const { return (m_child==NULL); }
inline const CELL_TYPE& GetChild(int i) const { ASSERT(!IsLeaf() && i<numChildren); return m_child[i]; }
inline const NODE_TYPE& Node() const { ASSERT(!IsLeaf()); return m_node; }
inline NODE_TYPE& Node() { ASSERT(!IsLeaf()); return m_node; }
inline const LEAF_TYPE& Leaf() const { ASSERT(IsLeaf()); return m_leaf; }
inline LEAF_TYPE& Leaf() { ASSERT(IsLeaf()); return m_leaf; }
inline const POINT_TYPE& GetCenter() const { return Node().center; }
inline AABB_TYPE GetAabb(TYPE radius) const { return AABB_TYPE(Node().center, radius); }
inline AABB_TYPE GetChildAabb(unsigned idxChild, TYPE radius) const { const TYPE childRadius(radius / TYPE(2)); return AABB_TYPE(ComputeChildCenter(Node().center, childRadius, idxChild), childRadius); }
inline IDX_TYPE GetFirstItemIdx() const { return Leaf().idxBegin; }
inline IDX_TYPE GetLastItemIdx() const { return Leaf().idxBegin + Leaf().size; }
inline SIZE_TYPE GetNumItems() const { return Leaf().size; }
inline const DATA_TYPE& GetUserData() const { return Leaf().data; }
inline DATA_TYPE& GetUserData() { return Leaf().data; }
size_t GetNumItemsHeld() const;
public:
CELL_TYPE* m_child; // if not a leaf, 2^DIMS child objects
union { // a LEAF_TYPE or NODE_TYPE object, if it is a leaf or not respectively
NODE_TYPE m_node;
LEAF_TYPE m_leaf;
};
};
typedef SEACAVE::cList<CELL_TYPE*,CELL_TYPE*,0,256,IDX_TYPE> CELLPTRARR_TYPE;
struct IndexInserter {
IDXARR_TYPE& indices;
IndexInserter(IDXARR_TYPE& _indices) : indices(_indices) {}
void operator()(IDX_TYPE idx) { indices.Insert(idx); }
void operator()(const IDX_TYPE* idices, SIZE_TYPE size) { indices.Join(idices, size); }
};
struct CellInserter {
CELLPTRARR_TYPE& cells;
CellInserter(CELLPTRARR_TYPE& _cells) : cells(_cells) {}
void operator()(CELL_TYPE& cell) { cells.Insert(&cell); }
};
public:
inline TOctree() {}
template <typename Functor>
inline TOctree(const ITEMARR_TYPE&, Functor split);
template <typename Functor>
inline TOctree(const ITEMARR_TYPE&, const AABB_TYPE&, Functor split);
inline void Release();
inline void Swap(TOctree&);
template <typename Functor>
void Insert(const ITEMARR_TYPE&, Functor split);
template <typename Functor>
void Insert(const ITEMARR_TYPE&, const AABB_TYPE&, Functor split);
template <typename INSERTER>
inline void CollectCells(INSERTER&) const;
template <typename INSERTER>
void CollectCells(const CELL_TYPE&, INSERTER&) const;
template <typename PARSER>
inline void ParseCells(PARSER&);
template <typename INSERTER>
inline void Collect(INSERTER& inserter, const AABB_TYPE& aabb) const;
inline void Collect(IDXARR_TYPE& indices, const AABB_TYPE& aabb) const;
inline void Collect(IDX_TYPE maxNeighbors, IDXARR_TYPE& indices, const AABB_TYPE& aabb) const;
template <typename INSERTER>
inline void Collect(INSERTER& inserter, const POINT_TYPE& center, TYPE radius) const;
inline void Collect(IDXARR_TYPE& indices, const POINT_TYPE& center, TYPE radius) const;
inline void Collect(IDX_TYPE maxNeighbors, IDXARR_TYPE& indices, const POINT_TYPE& center, TYPE radius) const;
template <typename INSERTER, typename COLLECTOR>
inline void Collect(INSERTER& inserter, const COLLECTOR& collector) const;
template <typename COLLECTOR>
inline void Collect(IDXARR_TYPE& indices, const COLLECTOR& collector) const;
template <typename FTYPE, int FDIMS, typename INSERTER>
inline void Traverse(const TFrustum<FTYPE,FDIMS>&, INSERTER&) const;
template <typename FTYPE, int FDIMS>
inline void Traverse(const TFrustum<FTYPE,FDIMS>&, IDXARR_TYPE&) const;
template <typename FTYPE, int FDIMS, typename PARSER>
inline void TraverseCells(const TFrustum<FTYPE,FDIMS>&, PARSER&);
template <typename FTYPE, int FDIMS>
inline void TraverseCells(const TFrustum<FTYPE,FDIMS>&, CELLPTRARR_TYPE&);
template <typename AREAESTIMATOR, typename CHUNKINSERTER>
void SplitVolume(float maxArea, AREAESTIMATOR& areaEstimator, CHUNKINSERTER& chunkInserter);
inline const CELL_TYPE& GetRoot() const { return m_root; }
inline TYPE GetRadius() const { return m_radius; }
inline AABB_TYPE GetAabb() const { return m_root.GetAabb(m_radius); }
inline bool IsEmpty() const { return m_indices.empty(); }
inline size_t GetNumItems() const { return m_indices.size(); }
inline const IDXARR_TYPE& GetIndexArr() const { return m_indices; }
inline const ITEM_TYPE* GetItems() const { return m_items; }
inline void ResetItems() { m_items = NULL; }
protected:
template <typename Functor>
struct _InsertData {
enum : IDX_TYPE { NO_INDEX = DECLARE_NO_INDEX(IDX_TYPE) };
IDXARR_TYPE successors; // single connected list of next item indices
Functor split; // used to decide if a cell needs to be split farther
};
template <typename Functor, bool bForceSplit>
void _Insert(CELL_TYPE&, const POINT_TYPE& center, TYPE radius, IDX_TYPE start, IDX_TYPE size, _InsertData<Functor>&);
template <typename PARSER>
void _ParseCells(CELL_TYPE&, TYPE, PARSER&);
template <typename INSERTER>
void _Collect(const CELL_TYPE&, const AABB_TYPE&, INSERTER&) const;
template <typename INSERTER, typename COLLECTOR>
void _Collect(const CELL_TYPE&, TYPE, const COLLECTOR&, INSERTER&) const;
template <typename FTYPE, int FDIMS, typename INSERTER>
void _Traverse(const CELL_TYPE&, TYPE, const TFrustum<FTYPE,FDIMS>&, INSERTER&) const;
template <typename FTYPE, int FDIMS, typename PARSER>
void _TraverseCells(CELL_TYPE&, TYPE, const TFrustum<FTYPE,FDIMS>&, PARSER&);
template <typename AREAESTIMATOR, typename CHUNKINSERTER>
void _SplitVolume(const CELL_TYPE& parentCell, TYPE parentRadius, unsigned idxChild, float maxArea, AREAESTIMATOR& areaEstimator, CHUNKINSERTER& chunkInserter, const UnsignedArr& indices=UnsignedArr{0,1,2,3,4,5,6,7});
protected:
const ITEM_TYPE* m_items; // original input items (the only condition is that every item to resolve to a position)
IDXARR_TYPE m_indices; // indices to input items re-arranged spatially (as dictated by the octree)
CELL_TYPE m_root; // first cell of the tree (always of Node type)
TYPE m_radius; // size of the sphere containing all cells
public:
typedef struct DEBUGINFO_TYPE {
size_t memSize; // total memory used
size_t memStruct; // memory used for the tree structure
size_t memItems; // memory used for the contained items
size_t numItems; // number of contained items
size_t numNodes; // total nodes...
size_t numLeaves; // ... from which this number of leaves
size_t minDepth; // minimum tree depth
size_t maxDepth; // maximum tree depth
float avgDepth; // average tree depth
void Init() { memset(this, 0, sizeof(DEBUGINFO_TYPE)); }
void operator += (const DEBUGINFO_TYPE& r) {
avgDepth = avgDepth*numNodes + r.avgDepth*r.numNodes;
memSize += r.memSize; memStruct += r.memStruct; memItems += r.memItems;
numItems += r.numItems; numNodes += r.numNodes; numLeaves += r.numLeaves;
if (minDepth > r.minDepth) minDepth = r.minDepth;
if (maxDepth < r.maxDepth) maxDepth = r.maxDepth;
avgDepth /= numNodes;
}
} DEBUGINFO;
void GetDebugInfo(DEBUGINFO* =NULL, bool bPrintStats=false) const;
static void LogDebugInfo(const DEBUGINFO&);
protected:
void _GetDebugInfo(const CELL_TYPE&, unsigned, DEBUGINFO&) const;
}; // class TOctree
/*----------------------------------------------------------------*/
#include "Octree.inl"
/*----------------------------------------------------------------*/
} // namespace SEACAVE
#endif // __SEACAVE_OCTREE_H__

851
libs/Common/Octree.inl

@ -0,0 +1,851 @@ @@ -0,0 +1,851 @@
////////////////////////////////////////////////////////////////////
// Octree.inl
//
// Copyright 2007 cDc@seacave
// Distributed under the Boost Software License, Version 1.0
// (See http://www.boost.org/LICENSE_1_0.txt)
// D E F I N E S ///////////////////////////////////////////////////
#ifdef _USE_OPENMP
// minimum number of polygons for which we do multi-threading
#define OCTREE_MIN_ITEMS_MINTHREAD 1024*2
#endif
// S T R U C T S ///////////////////////////////////////////////////
template <typename ITEMARR_TYPE, typename TYPE, int DIMS, typename DATA_TYPE>
inline TOctree<ITEMARR_TYPE,TYPE,DIMS,DATA_TYPE>::CELL_TYPE::CELL_TYPE()
:
m_child(NULL)
{
} // constructor
template <typename ITEMARR_TYPE, typename TYPE, int DIMS, typename DATA_TYPE>
inline TOctree<ITEMARR_TYPE,TYPE,DIMS,DATA_TYPE>::CELL_TYPE::~CELL_TYPE()
{
delete[] m_child;
} // destructor
/*----------------------------------------------------------------*/
template <typename ITEMARR_TYPE, typename TYPE, int DIMS, typename DATA_TYPE>
inline void TOctree<ITEMARR_TYPE,TYPE,DIMS,DATA_TYPE>::CELL_TYPE::Release()
{
delete[] m_child;
m_child = NULL;
} // Release
// swap the two octrees
template <typename ITEMARR_TYPE, typename TYPE, int DIMS, typename DATA_TYPE>
inline void TOctree<ITEMARR_TYPE,TYPE,DIMS,DATA_TYPE>::CELL_TYPE::Swap(CELL_TYPE& rhs)
{
std::swap(m_child, rhs.m_child);
if (IsLeaf())
std::swap(m_leaf, rhs.m_leaf);
else
std::swap(m_node, rhs.m_node);
} // Swap
/*----------------------------------------------------------------*/
// compute item's index corresponding to the containing cell
template <typename ITEMARR_TYPE, typename TYPE, int DIMS, typename DATA_TYPE>
inline unsigned TOctree<ITEMARR_TYPE,TYPE,DIMS,DATA_TYPE>::CELL_TYPE::ComputeChild(const POINT_TYPE& item) const
{
ASSERT(!IsLeaf());
unsigned idx = 0;
if (item[0] >= Node().center[0])
idx |= (1<<0);
if (DIMS > 1)
if (item[1] >= Node().center[1])
idx |= (1<<1);
if (DIMS > 2)
if (item[2] >= Node().center[2])
idx |= (1<<2);
return idx;
} // ComputeChild
/*----------------------------------------------------------------*/
template <typename ITEMARR_TYPE, typename TYPE, int DIMS, typename DATA_TYPE>
void TOctree<ITEMARR_TYPE,TYPE,DIMS,DATA_TYPE>::CELL_TYPE::ComputeCenter(POINT_TYPE centers[])
{
if (DIMS == 1) {
centers[0] << -1;
centers[1] << 1;
} else
if (DIMS == 2) {
centers[0] << -1,-1;
centers[1] << 1,-1;
centers[2] << -1, 1;
centers[3] << 1, 1;
} else
if (DIMS == 3) {
centers[0] << -1,-1,-1;
centers[1] << 1,-1,-1;
centers[2] << -1, 1,-1;
centers[3] << 1, 1,-1;
centers[4] << -1,-1, 1;
centers[5] << 1,-1, 1;
centers[6] << -1, 1, 1;
centers[7] << 1, 1, 1;
}
} // ComputeCenter
/*----------------------------------------------------------------*/
template <typename ITEMARR_TYPE, typename TYPE, int DIMS, typename DATA_TYPE>
inline typename TOctree<ITEMARR_TYPE,TYPE,DIMS,DATA_TYPE>::POINT_TYPE TOctree<ITEMARR_TYPE,TYPE,DIMS,DATA_TYPE>::CELL_TYPE::ComputeChildCenter(const POINT_TYPE& center, TYPE radius, unsigned idxChild)
{
struct CENTERARR_TYPE {
POINT_TYPE child[CELL_TYPE::numChildren];
inline CENTERARR_TYPE() { CELL_TYPE::ComputeCenter(child); }
};
static const CENTERARR_TYPE centers;
return center + centers.child[idxChild] * radius;
} // ComputeChildCenter
/*----------------------------------------------------------------*/
// count the number of items contained by the given octree-cell
template <typename ITEMARR_TYPE, typename TYPE, int DIMS, typename DATA_TYPE>
size_t TOctree<ITEMARR_TYPE,TYPE,DIMS,DATA_TYPE>::CELL_TYPE::GetNumItemsHeld() const
{
if (IsLeaf())
return GetNumItems();
size_t numItems = 0;
for (int i=0; i<numChildren; ++i)
numItems += GetChild(i).GetNumItemsHeld();
return numItems;
}
/*----------------------------------------------------------------*/
// S T R U C T S ///////////////////////////////////////////////////
// build tree with the given items
template <typename ITEMARR_TYPE, typename TYPE, int DIMS, typename DATA_TYPE>
template <typename Functor>
inline TOctree<ITEMARR_TYPE,TYPE,DIMS,DATA_TYPE>::TOctree(const ITEMARR_TYPE& items, Functor split)
{
Insert(items, split);
}
template <typename ITEMARR_TYPE, typename TYPE, int DIMS, typename DATA_TYPE>
template <typename Functor>
inline TOctree<ITEMARR_TYPE,TYPE,DIMS,DATA_TYPE>::TOctree(const ITEMARR_TYPE& items, const AABB_TYPE& aabb, Functor split)
{
Insert(items, aabb, split);
} // constructor
/*----------------------------------------------------------------*/
// destroy tree
template <typename ITEMARR_TYPE, typename TYPE, int DIMS, typename DATA_TYPE>
inline void TOctree<ITEMARR_TYPE,TYPE,DIMS,DATA_TYPE>::Release()
{
m_indices.Release();
m_root.Release();
} // Release
// swap the two octrees
template <typename ITEMARR_TYPE, typename TYPE, int DIMS, typename DATA_TYPE>
inline void TOctree<ITEMARR_TYPE,TYPE,DIMS,DATA_TYPE>::Swap(TOctree& rhs)
{
std::swap(m_items, rhs.m_items);
m_indices.Swap(rhs.m_indices);
m_root.Swap(rhs.m_root);
std::swap(m_radius, rhs.m_radius);
} // Swap
/*----------------------------------------------------------------*/
// add the given item to the tree
template <typename ITEMARR_TYPE, typename TYPE, int DIMS, typename DATA_TYPE>
template <typename Functor, bool bForceSplit>
void TOctree<ITEMARR_TYPE,TYPE,DIMS,DATA_TYPE>::_Insert(CELL_TYPE& cell, const POINT_TYPE& center, TYPE radius, IDX_TYPE start, IDX_TYPE size, _InsertData<Functor>& insertData)
{
ASSERT(size > 0);
// if this child cell needs to be divided further
if (bForceSplit || insertData.split(size, radius)) {
// init node and proceed recursively
ASSERT(cell.m_child == NULL);
cell.m_child = new CELL_TYPE[CELL_TYPE::numChildren];
cell.Node().center = center;
struct ChildData {
enum { ESTART=0, EEND=CELL_TYPE::numChildren, ESIZE=CELL_TYPE::numChildren*2, EALL=CELL_TYPE::numChildren*3};
IDX_TYPE data[EALL];
ChildData() { memset(data, 0, sizeof(IDX_TYPE)*EALL); }
inline IDX_TYPE Start(unsigned i) const { return data[ESTART+i]; }
inline IDX_TYPE& Start(unsigned i) { return data[ESTART+i]; }
inline IDX_TYPE End(unsigned i) const { return data[EEND+i]; }
inline IDX_TYPE& End(unsigned i) { return data[EEND+i]; }
inline IDX_TYPE Size(unsigned i) const { return data[ESIZE+i]; }
inline IDX_TYPE& Size(unsigned i) { return data[ESIZE+i]; }
} childD;
IDX_TYPE idx(start);
for (IDX_TYPE i=0; i<size; ++i) {
const unsigned idxChild(cell.ComputeChild(m_items[idx]));
if (childD.Size(idxChild) == 0)
childD.Start(idxChild) = idx;
else
insertData.successors[childD.End(idxChild)] = idx;
childD.End(idxChild) = idx;
++childD.Size(idxChild);
idx = insertData.successors[idx];
}
ASSERT(idx == _InsertData<Functor>::NO_INDEX);
const TYPE childRadius(radius / TYPE(2));
for (unsigned i=0; i<CELL_TYPE::numChildren; ++i) {
CELL_TYPE& child = cell.m_child[i];
if (childD.Size(i) == 0) {
child.Leaf().idxBegin = m_indices.size();
child.Leaf().size = 0;
continue;
}
insertData.successors[childD.End(i)] = _InsertData<Functor>::NO_INDEX; // mark the end of child successors
const POINT_TYPE childCenter(CELL_TYPE::ComputeChildCenter(center, childRadius, i));
_Insert<Functor,false>(child, childCenter, childRadius, childD.Start(i), childD.Size(i), insertData);
}
} else {
// init leaf
cell.Leaf().idxBegin = m_indices.size();
cell.Leaf().size = (SIZE_TYPE)size;
for (IDX_TYPE idx=start; idx!=_InsertData<Functor>::NO_INDEX; idx=insertData.successors[idx])
m_indices.push_back(idx);
}
} // _Insert
/*----------------------------------------------------------------*/
template <typename ITEMARR_TYPE, typename TYPE, int DIMS, typename DATA_TYPE>
template <typename Functor>
inline void TOctree<ITEMARR_TYPE,TYPE,DIMS,DATA_TYPE>::Insert(const ITEMARR_TYPE& items, const AABB_TYPE& aabb, Functor split)
{
Release();
m_items = items.data();
// create root as node, even if we do not need to divide
m_indices.Reserve(items.size());
// divide cell
const POINT_TYPE center = aabb.GetCenter();
m_radius = aabb.GetSize().maxCoeff()/Type(2);
// single connected list of next item indices
_InsertData<Functor> insertData = {items.size(), split};
std::iota(insertData.successors.begin(), insertData.successors.end(), IDX_TYPE(1));
insertData.successors.back() = _InsertData<Functor>::NO_INDEX;
// setup each cell
_Insert<Functor,true>(m_root, center, m_radius, 0, items.size(), insertData);
}
template <typename ITEMARR_TYPE, typename TYPE, int DIMS, typename DATA_TYPE>
template <typename Functor>
inline void TOctree<ITEMARR_TYPE,TYPE,DIMS,DATA_TYPE>::Insert(const ITEMARR_TYPE& items, Functor split)
{
ASSERT(!items.IsEmpty());
ASSERT(sizeof(POINT_TYPE) == sizeof(typename ITEMARR_TYPE::Type));
AABB_TYPE aabb((const POINT_TYPE*)items.data(), items.size());
aabb.Enlarge(ZEROTOLERANCE<TYPE>()*TYPE(10));
Insert(items, aabb, split);
} // Insert
/*----------------------------------------------------------------*/
template <typename ITEMARR_TYPE, typename TYPE, int DIMS, typename DATA_TYPE>
template <typename INSERTER>
void TOctree<ITEMARR_TYPE,TYPE,DIMS,DATA_TYPE>::CollectCells(const CELL_TYPE& cell, INSERTER& inserter) const
{
if (cell.IsLeaf()) {
inserter(m_indices.data()+cell.GetFirstItemIdx(), cell.GetNumItems());
return;
}
for (int i=0; i<CELL_TYPE::numChildren; ++i)
CollectCells(cell.m_child[i], inserter);
}
template <typename ITEMARR_TYPE, typename TYPE, int DIMS, typename DATA_TYPE>
template <typename PARSER>
void TOctree<ITEMARR_TYPE,TYPE,DIMS,DATA_TYPE>::_ParseCells(CELL_TYPE& cell, TYPE radius, PARSER& parser)
{
if (cell.IsLeaf()) {
parser(cell, radius);
return;
}
const TYPE childRadius = radius / TYPE(2);
for (int i=0; i<CELL_TYPE::numChildren; ++i)
_ParseCells(cell.m_child[i], childRadius, parser);
}
/*----------------------------------------------------------------*/
// calls parser for each leaf of the octree (the IDX_TYPE operator has to be defined)
template <typename ITEMARR_TYPE, typename TYPE, int DIMS, typename DATA_TYPE>
template <typename INSERTER>
void TOctree<ITEMARR_TYPE,TYPE,DIMS,DATA_TYPE>::CollectCells(INSERTER& inserter) const
{
CollectCells(m_root, inserter);
}
// calls parser for each leaf of the octree (the CELL_TYPE operator has to be defined)
template <typename ITEMARR_TYPE, typename TYPE, int DIMS, typename DATA_TYPE>
template <typename PARSER>
void TOctree<ITEMARR_TYPE,TYPE,DIMS,DATA_TYPE>::ParseCells(PARSER& parser)
{
_ParseCells(m_root, m_radius, parser);
}
/*----------------------------------------------------------------*/
// find all items contained by the given bounding box
template <typename ITEMARR_TYPE, typename TYPE, int DIMS, typename DATA_TYPE>
template <typename INSERTER>
void TOctree<ITEMARR_TYPE,TYPE,DIMS,DATA_TYPE>::_Collect(const CELL_TYPE& cell, const AABB_TYPE& aabb, INSERTER& inserter) const
{
if (cell.IsLeaf()) {
// add all items contained by the bounding-box
for (IDX_TYPE i=0; i<cell.Leaf().size; ++i) {
const IDX_TYPE idx = m_indices[cell.Leaf().idxBegin + i];
if (aabb.Intersects(m_items[idx]))
inserter(idx);
}
return;
}
// find the cells containing the box extremes
const unsigned idxMin = cell.ComputeChild(aabb.ptMin);
const unsigned idxMax = cell.ComputeChild(aabb.ptMax);
// if both extremes are in the same cell
if (idxMin == idxMax) {
// all searched items are inside a single child
return _Collect(cell.m_child[idxMin], aabb, inserter);
}
// divide the bounding-box in 2^DIMS boxes split by the cell center
AABB_TYPE aabbs[CELL_TYPE::numChildren];
unsigned first;
unsigned n = aabb.SplitBy(cell.Node().center, aabbs, first);
if (n == 0) {
// the aabb has one of the sides right on the cell division line
#if 0
// so find the cell containing the aabb center and add its items
const unsigned idx = cell.ComputeChild(aabb.GetCenter());
_Collect(cell.m_child[idx], aabb, inserter);
#else
// so collect both intersecting cells
// (seems to work better for points right on the border)
_Collect(cell.m_child[idxMin], aabb, inserter);
_Collect(cell.m_child[idxMax], aabb, inserter);
#endif
return;
}
// collect each cell
for (unsigned i=0; i<n; ++i) {
// all searched items are inside a single child
const AABB_TYPE& aabbChild = aabbs[(first+i)%CELL_TYPE::numChildren];
const unsigned idx = cell.ComputeChild(aabbChild.ptMin);
_Collect(cell.m_child[idx], aabbChild, inserter);
}
} // Collect
// find all items contained by the cells intersected by the given line
template <typename ITEMARR_TYPE, typename TYPE, int DIMS, typename DATA_TYPE>
template <typename INSERTER, typename COLLECTOR>
void TOctree<ITEMARR_TYPE,TYPE,DIMS,DATA_TYPE>::_Collect(const CELL_TYPE& cell, TYPE radius, const COLLECTOR& collector, INSERTER& inserter) const
{
ASSERT(!cell.IsLeaf());
const TYPE childRadius = radius / TYPE(2);
for (int i=0; i<CELL_TYPE::numChildren; ++i) {
const CELL_TYPE& childCell = cell.m_child[i];
if (childCell.IsLeaf()) {
if (collector.Intersects(CELL_TYPE::ComputeChildCenter(cell.GetCenter(), childRadius, i), childRadius))
inserter(m_indices.data()+childCell.GetFirstItemIdx(), childCell.GetNumItems());
} else {
if (collector.Intersects(childCell.Node().center, childRadius))
_Collect(childCell, childRadius, collector, inserter);
}
}
} // Collect
/*----------------------------------------------------------------*/
template <typename ITEMARR_TYPE, typename TYPE, int DIMS, typename DATA_TYPE>
template <typename INSERTER>
inline void TOctree<ITEMARR_TYPE,TYPE,DIMS,DATA_TYPE>::Collect(INSERTER& inserter, const AABB_TYPE& aabb) const
{
_Collect(m_root, aabb, inserter);
}
template <typename ITEMARR_TYPE, typename TYPE, int DIMS, typename DATA_TYPE>
inline void TOctree<ITEMARR_TYPE,TYPE,DIMS,DATA_TYPE>::Collect(IDXARR_TYPE& indices, const AABB_TYPE& aabb) const
{
IndexInserter inserter(indices);
_Collect(m_root, aabb, inserter);
}
template <typename ITEMARR_TYPE, typename TYPE, int DIMS, typename DATA_TYPE>
template <typename INSERTER>
inline void TOctree<ITEMARR_TYPE,TYPE,DIMS,DATA_TYPE>::Collect(INSERTER& inserter, const POINT_TYPE& center, TYPE radius) const
{
_Collect(m_root, AABB_TYPE(center, radius), inserter);
}
template <typename ITEMARR_TYPE, typename TYPE, int DIMS, typename DATA_TYPE>
inline void TOctree<ITEMARR_TYPE,TYPE,DIMS,DATA_TYPE>::Collect(IDXARR_TYPE& indices, const POINT_TYPE& center, TYPE radius) const
{
IndexInserter inserter(indices);
_Collect(m_root, AABB_TYPE(center, radius), inserter);
}
template <typename ITEMARR_TYPE, typename TYPE, int DIMS, typename DATA_TYPE>
template <typename INSERTER, typename COLLECTOR>
inline void TOctree<ITEMARR_TYPE,TYPE,DIMS,DATA_TYPE>::Collect(INSERTER& inserter, const COLLECTOR& collector) const
{
_Collect(m_root, m_radius, collector, inserter);
}
template <typename ITEMARR_TYPE, typename TYPE, int DIMS, typename DATA_TYPE>
template <typename COLLECTOR>
inline void TOctree<ITEMARR_TYPE,TYPE,DIMS,DATA_TYPE>::Collect(IDXARR_TYPE& indices, const COLLECTOR& collector) const
{
IndexInserter inserter(indices);
_Collect(m_root, m_radius, collector, inserter);
}
template <typename ITEMARR_TYPE, typename TYPE, int DIMS, typename DATA_TYPE>
inline void TOctree<ITEMARR_TYPE,TYPE,DIMS,DATA_TYPE>::Collect(IDX_TYPE maxNeighbors, IDXARR_TYPE& indices, const AABB_TYPE& aabb) const
{
_Collect(m_root, aabb, IndexInserter(indices));
if (indices.size() > maxNeighbors) {
// keep only the closest neighbors
typedef TIndexScore<IDX_TYPE,TYPE> ItemIndexScore;
typedef cList<ItemIndexScore, const ItemIndexScore&, 0> ItemIndexScoreArr;
ItemIndexScoreArr indexscores(indices.size());
const POINT_TYPE center(aabb.GetCenter());
FOREACH(i, indices) {
const IDX_TYPE& idx = indices[i];
const TYPE score(-(center-m_items[idx]).squaredNorm());
indexscores[i] = ItemIndexScore(idx,score);
}
indices.Empty();
indexscores.Sort();
for (IDX_TYPE i=0; i<maxNeighbors; ++i)
indices.Insert(indexscores[i].idx);
}
}
template <typename ITEMARR_TYPE, typename TYPE, int DIMS, typename DATA_TYPE>
inline void TOctree<ITEMARR_TYPE,TYPE,DIMS,DATA_TYPE>::Collect(IDX_TYPE maxNeighbors, IDXARR_TYPE& indices, const POINT_TYPE& center, TYPE radius) const
{
Collect(maxNeighbors, indices, AABB_TYPE(center, radius));
} // Collect
/*----------------------------------------------------------------*/
// walk through the tree and collect visible indices
template <typename ITEMARR_TYPE, typename TYPE, int DIMS, typename DATA_TYPE>
template <typename FTYPE, int FDIMS, typename INSERTER>
void TOctree<ITEMARR_TYPE,TYPE,DIMS,DATA_TYPE>::_Traverse(const CELL_TYPE& cell, TYPE radius, const TFrustum<FTYPE,FDIMS>& frustum, INSERTER& inserter) const
{
ASSERT(!cell.IsLeaf());
switch (frustum.Classify(cell.GetAabb(radius))) {
case CLIPPED: {
const TYPE childRadius = radius / TYPE(2);
for (int i=0; i<CELL_TYPE::numChildren; ++i) {
const CELL_TYPE& childCell = cell.m_child[i];
if (childCell.IsLeaf()) {
const AABB_TYPE childAabb(CELL_TYPE::ComputeChildCenter(cell.GetCenter(), childRadius, i), childRadius);
if (frustum.Classify(childAabb) != CULLED)
inserter(m_indices.data()+childCell.GetFirstItemIdx(), childCell.GetNumItems());
} else {
_Traverse(childCell, childRadius, frustum, inserter);
}
}
break; }
case VISIBLE: {
for (int i=0; i<CELL_TYPE::numChildren; ++i)
CollectCells(cell.m_child[i], inserter);
break; }
}
}
// walk through the tree and collect visible leafs
template <typename ITEMARR_TYPE, typename TYPE, int DIMS, typename DATA_TYPE>
template <typename FTYPE, int FDIMS, typename PARSER>
void TOctree<ITEMARR_TYPE,TYPE,DIMS,DATA_TYPE>::_TraverseCells(CELL_TYPE& cell, TYPE radius, const TFrustum<FTYPE,FDIMS>& frustum, PARSER& parser)
{
ASSERT(!cell.IsLeaf());
switch (frustum.Classify(cell.GetAabb(radius))) {
case CLIPPED: {
const TYPE childRadius = radius / TYPE(2);
for (int i=0; i<CELL_TYPE::numChildren; ++i) {
const CELL_TYPE& childCell = cell.m_child[i];
if (childCell.IsLeaf()) {
const AABB_TYPE childAabb(CELL_TYPE::ComputeChildCenter(cell.GetCenter(), childRadius, i), childRadius);
if (frustum.Classify(childAabb) != CULLED)
parser(childCell);
} else {
_TraverseCells(childCell, childRadius, frustum, parser);
}
}
break; }
case VISIBLE: {
for (int i=0; i<CELL_TYPE::numChildren; ++i)
_ParseCells(cell.m_child[i], parser);
break; }
}
}
template <typename ITEMARR_TYPE, typename TYPE, int DIMS, typename DATA_TYPE>
template <typename FTYPE, int FDIMS, typename INSERTER>
inline void TOctree<ITEMARR_TYPE,TYPE,DIMS,DATA_TYPE>::Traverse(const TFrustum<FTYPE,FDIMS>& frustum, INSERTER& inserter) const
{
_Traverse(m_root, m_radius, frustum, inserter);
}
template <typename ITEMARR_TYPE, typename TYPE, int DIMS, typename DATA_TYPE>
template <typename FTYPE, int FDIMS>
inline void TOctree<ITEMARR_TYPE,TYPE,DIMS,DATA_TYPE>::Traverse(const TFrustum<FTYPE,FDIMS>& frustum, IDXARR_TYPE& indices) const
{
_Traverse(m_root, m_radius, frustum, IndexInserter(indices));
}
template <typename ITEMARR_TYPE, typename TYPE, int DIMS, typename DATA_TYPE>
template <typename FTYPE, int FDIMS, typename PARSER>
inline void TOctree<ITEMARR_TYPE,TYPE,DIMS,DATA_TYPE>::TraverseCells(const TFrustum<FTYPE,FDIMS>& frustum, PARSER& parser)
{
_TraverseCells(m_root, m_radius, frustum, parser);
}
template <typename ITEMARR_TYPE, typename TYPE, int DIMS, typename DATA_TYPE>
template <typename FTYPE, int FDIMS>
inline void TOctree<ITEMARR_TYPE,TYPE,DIMS,DATA_TYPE>::TraverseCells(const TFrustum<FTYPE,FDIMS>& frustum, CELLPTRARR_TYPE& leaves)
{
_TraverseCells(m_root, m_radius, frustum, CellInserter(leaves));
} // Traverse
/*----------------------------------------------------------------*/
template <typename ITEMARR_TYPE, typename TYPE, int DIMS, typename DATA_TYPE>
template <typename AREAESTIMATOR, typename CHUNKINSERTER>
void TOctree<ITEMARR_TYPE,TYPE,DIMS,DATA_TYPE>::_SplitVolume(const CELL_TYPE& parentCell, TYPE parentRadius, unsigned idxChild, float maxArea, AREAESTIMATOR& areaEstimator, CHUNKINSERTER& chunkInserter, const UnsignedArr& indices)
{
ASSERT(!indices.empty());
typedef std::pair<UnsignedArr,UnsignedArr> PairIndices;
struct GenerateSamples {
const UnsignedArr& indices;
const unsigned numSamples;
const unsigned halfSamples;
const unsigned numCommonAxis;
POINT_TYPE centers[8];
cList<PairIndices> arrHalfIndices;
GenerateSamples(const UnsignedArr& _indices)
: indices(_indices), numSamples((unsigned)indices.size()), halfSamples(numSamples/2), numCommonAxis(halfSamples==4?1:2), arrHalfIndices(0, numSamples)
{
ASSERT(indices.size()%2 == 0 && indices.IsSorted());
ASSERT(halfSamples == 4 || halfSamples == 2);
CELL_TYPE::ComputeCenter(centers);
UnsignedArr samples(halfSamples);
for (unsigned hs=0; hs<halfSamples; ++hs) {
samples[0] = hs;
GenerateIndices(1, samples);
}
}
void GenerateIndices(unsigned idxSample, UnsignedArr& samples) {
if (idxSample == samples.size()) {
InsertIndices(samples);
return;
}
for (unsigned i=samples[idxSample-1]+1; i<numSamples; ++i) {
samples[idxSample] = i;
GenerateIndices(idxSample+1, samples);
}
}
void InsertIndices(const UnsignedArr& samples) {
UnsignedArr halfIndices(halfSamples);
for (unsigned s=0; s<halfSamples; ++s)
halfIndices[s] = indices[samples[s]];
// check all samples have one/two common axis
unsigned commonAxis(0);
for (unsigned a=0; a<3; ++a) {
unsigned na(1);
for (unsigned s=1; s<halfSamples; ++s) {
if (centers[halfIndices[0]][a] == centers[halfIndices[s]][a])
++na;
}
if (na == halfSamples && ++commonAxis == numCommonAxis)
goto CommonAxis;
}
return;
CommonAxis:
// check not complementary samples
for (const PairIndices& pairHalfIndices: arrHalfIndices) {
unsigned nCommon(0);
for (unsigned s=0; s<halfSamples; ++s)
if (halfIndices[s] == pairHalfIndices.second[s])
++nCommon;
if (nCommon == halfSamples)
return;
}
// generate complementary indices
ASSERT(halfIndices.IsSorted());
UnsignedArr compHalfIndices(0, halfSamples);
unsigned i(0);
for (unsigned idx: indices) {
if (i < halfSamples && idx == halfIndices[i])
++i;
else
compHalfIndices.push_back(idx);
}
ASSERT(compHalfIndices.size() == halfSamples);
ASSERT(compHalfIndices.IsSorted());
arrHalfIndices.emplace_back(std::move(halfIndices), std::move(compHalfIndices));
}
};
const CELL_TYPE& cell(parentCell.GetChild(idxChild));
const TYPE radius(parentRadius / TYPE(2));
// handle particular cases
if (cell.IsLeaf()) {
chunkInserter(parentCell, parentRadius, UnsignedArr{idxChild});
return;
}
if (indices.size() == 1) {
_SplitVolume(cell, radius, indices.front(), maxArea, areaEstimator, chunkInserter);
return;
}
if (indices.size() == 2) {
_SplitVolume(cell, radius, indices.front(), maxArea, areaEstimator, chunkInserter);
_SplitVolume(cell, radius, indices.back(), maxArea, areaEstimator, chunkInserter);
return;
}
// measure surface area for each child
float childArea[8], totalArea(0);
for (unsigned c=0; c<8; ++c) {
CollectCells(cell.GetChild(c), areaEstimator);
totalArea += childArea[c] = areaEstimator.PopArea();
}
if (totalArea < maxArea*1.01f) {
chunkInserter(parentCell, parentRadius, UnsignedArr{idxChild});
return;
}
// check if all parts are over the limit
unsigned numOverAreas(0);
for (unsigned c: indices)
if (childArea[c] == 0 || childArea[c] >= maxArea)
++numOverAreas;
if (numOverAreas == indices.size()) {
for (unsigned c: indices)
if (childArea[c] > 0)
_SplitVolume(cell, radius, c, maxArea, areaEstimator, chunkInserter);
return;
}
// split mesh children and retain the components with surface smaller than the given area
const cList<PairIndices> halfIndices(std::move(GenerateSamples(indices).arrHalfIndices));
IDX bestSplit(NO_ID);
float bestArea(0);
Point2f bestAs;
FOREACH(idx, halfIndices) {
const PairIndices& pairHalfIndices = halfIndices[idx];
ASSERT(pairHalfIndices.first.size() == pairHalfIndices.second.size());
Point2f as(Point2f::ZERO);
for (unsigned i=0; i<pairHalfIndices.first.size(); ++i) {
as[0] += childArea[pairHalfIndices.first[i]];
as[1] += childArea[pairHalfIndices.second[i]];
}
for (unsigned i=0; i<2; ++i) {
if (as[i] < maxArea && bestArea < as[i]) {
bestArea = as[i];
bestAs = as;
bestSplit = idx;
}
}
}
if (bestSplit != NO_ID) {
// store found clusters
const PairIndices& pairHalfIndices = halfIndices[bestSplit];
if (bestAs[0] < maxArea)
chunkInserter(cell, radius, pairHalfIndices.first);
else
_SplitVolume(parentCell, parentRadius, idxChild, maxArea, areaEstimator, chunkInserter, pairHalfIndices.first);
if (bestAs[1] < maxArea)
chunkInserter(cell, radius, pairHalfIndices.second);
else
_SplitVolume(parentCell, parentRadius, idxChild, maxArea, areaEstimator, chunkInserter, pairHalfIndices.second);
return;
}
// farther split each half into quarters
if (halfIndices.front().first.size() == 4) {
UnsignedArr bestQIndices[4];
float bestArea(0);
Eigen::Vector4f bestAs;
for (const PairIndices& pairHalfIndices: halfIndices) {
const cList<PairIndices> qIndicesFirst(std::move(GenerateSamples(pairHalfIndices.first).arrHalfIndices));
const cList<PairIndices> qIndicesSecond(std::move(GenerateSamples(pairHalfIndices.second).arrHalfIndices));
ASSERT(qIndicesFirst.size() == qIndicesSecond.size());
FOREACH(q, qIndicesFirst) {
const PairIndices& qFirst = qIndicesFirst[q];
const PairIndices& qSecond = qIndicesSecond[q];
Eigen::Vector4f as(Eigen::Vector4f::Zero());
for (unsigned i=0; i<qFirst.first.size(); ++i) {
as[0] += childArea[qFirst.first[i]];
as[1] += childArea[qFirst.second[i]];
as[2] += childArea[qSecond.first[i]];
as[3] += childArea[qSecond.second[i]];
}
float area(0);
for (unsigned i=0; i<4; ++i) {
if (as[i] < maxArea)
area += as[i];
}
if (bestArea < area) {
bestArea = area;
bestAs = as;
bestQIndices[0] = qFirst.first;
bestQIndices[1] = qFirst.second;
bestQIndices[2] = qSecond.first;
bestQIndices[3] = qSecond.second;
}
}
}
if (bestArea > 0) {
// store found clusters
for (unsigned i=0; i<4; ++i) {
if (bestAs[i] < maxArea) {
chunkInserter(cell, radius, bestQIndices[i]);
} else {
_SplitVolume(cell, radius, bestQIndices[i][0], maxArea, areaEstimator, chunkInserter);
_SplitVolume(cell, radius, bestQIndices[i][1], maxArea, areaEstimator, chunkInserter);
}
}
return;
}
}
// split each child
for (unsigned c: indices) {
if (childArea[c] == 0)
continue;
if (childArea[c] < maxArea)
chunkInserter(cell, radius, UnsignedArr{c});
else
_SplitVolume(cell, radius, c, maxArea, areaEstimator, chunkInserter);
}
}
template <typename ITEMARR_TYPE, typename TYPE, int DIMS, typename DATA_TYPE>
template <typename AREAESTIMATOR, typename CHUNKINSERTER>
void TOctree<ITEMARR_TYPE,TYPE,DIMS,DATA_TYPE>::SplitVolume(float maxArea, AREAESTIMATOR& areaEstimator, CHUNKINSERTER& chunkInserter)
{
CELL_TYPE parent;
parent.m_child = new CELL_TYPE[1];
parent.m_child[0].m_child = m_root.m_child;
parent.m_child[0].Node() = m_root.Node();
parent.Node().center = m_root.Node().center + POINT_TYPE::Constant(m_radius);
_SplitVolume(parent, m_radius*TYPE(2), 0, maxArea, areaEstimator, chunkInserter);
parent.m_child[0].m_child = NULL;
} // SplitVolume
/*----------------------------------------------------------------*/
template <typename ITEMARR_TYPE, typename TYPE, int DIMS, typename DATA_TYPE>
void TOctree<ITEMARR_TYPE,TYPE,DIMS,DATA_TYPE>::_GetDebugInfo(const CELL_TYPE& cell, unsigned nDepth, DEBUGINFO& info) const
{
if (cell.IsLeaf()) {
if (info.minDepth > nDepth)
info.minDepth = nDepth;
if (info.maxDepth < nDepth)
info.maxDepth = nDepth;
info.avgDepth += nDepth;
info.numLeaves++;
return;
}
nDepth++;
info.numNodes++;
for (int i=0; i<CELL_TYPE::numChildren; ++i)
_GetDebugInfo(cell.m_child[i], nDepth, info);
}
template <typename ITEMARR_TYPE, typename TYPE, int DIMS, typename DATA_TYPE>
void TOctree<ITEMARR_TYPE,TYPE,DIMS,DATA_TYPE>::GetDebugInfo(DEBUGINFO* pInfo, bool bPrintStats) const
{
DEBUGINFO localInfo;
DEBUGINFO& info = (pInfo ? *pInfo : localInfo);
info.Init();
_GetDebugInfo(m_root, 0, info);
info.avgDepth /= info.numLeaves;
info.numItems = GetNumItems();
info.numNodes += info.numLeaves;
info.memStruct = info.numNodes*sizeof(CELL_TYPE) + sizeof(TOctree);
info.memItems = sizeof(IDX_TYPE)*info.numItems;
info.memSize = info.memStruct + info.memItems;
if (pInfo == NULL || bPrintStats)
LogDebugInfo(info);
} // GetDebugInfo
/*----------------------------------------------------------------*/
template <typename ITEMARR_TYPE, typename TYPE, int DIMS, typename DATA_TYPE>
void TOctree<ITEMARR_TYPE,TYPE,DIMS,DATA_TYPE>::LogDebugInfo(const DEBUGINFO& info)
{
//VERBOSE("NoItems: %d; Mem %s; MemItems %s; MemStruct %s; AvgMemStruct %.2f%%%%; NoNodes %d; NoLeaf %d; AvgLeaf %.2f%%%%; AvgDepth %.2f; MinDepth %d; MaxDepth %d",
VERBOSE("NumItems %d; Mem %s (%s items, %s struct - %.2f%%%%); NumNodes %d (leaves %d - %.2f%%%%); Depth %.2f (%d min, %d max)",
info.numItems,
Util::formatBytes(info.memSize).c_str(), Util::formatBytes(info.memItems).c_str(), Util::formatBytes(info.memStruct).c_str(), double(info.memStruct)*100.0/info.memSize,
info.numNodes, info.numLeaves, float(info.numLeaves*100)/info.numNodes,
info.avgDepth, info.minDepth, info.maxDepth);
} // LogDebugInfo
/*----------------------------------------------------------------*/
// if everything works fine, this function should return true
template <typename TYPE, int DIMS>
inline bool OctreeTest(unsigned iters, unsigned maxItems=1000, bool bRandom=true) {
STATIC_ASSERT(DIMS > 0 && DIMS <= 3);
srand(bRandom ? (unsigned)time(NULL) : 0);
typedef Eigen::Matrix<TYPE,DIMS,1> POINT_TYPE;
typedef CLISTDEF0(POINT_TYPE) TestArr;
typedef TOctree<TestArr,TYPE,DIMS,uint32_t> TestTree;
const TYPE ptMinData[] = {0,0,0}, ptMaxData[] = {640,480,240};
typename TestTree::AABB_TYPE aabb;
aabb.Set(Eigen::Map<const POINT_TYPE>(ptMinData), Eigen::Map<const POINT_TYPE>(ptMaxData));
aabb.Enlarge(ZEROTOLERANCE<TYPE>()*TYPE(10));
unsigned nTotalMatches = 0;
unsigned nTotalMissed = 0;
unsigned nTotalExtra = 0;
#ifndef _RELEASE
typename TestTree::DEBUGINFO_TYPE totalInfo;
totalInfo.Init();
#endif
for (unsigned iter=0; iter<iters; ++iter) {
// generate random items
const unsigned elems = maxItems/10+RAND()%maxItems;
TestArr items(elems);
FOREACH(i, items)
for (int j=0; j<DIMS; ++j)
items[i](j) = static_cast<TYPE>(RAND()%ROUND2INT(ptMaxData[j]));
// random query point
POINT_TYPE pt;
for (int j=0; j<DIMS; ++j)
pt(j) = static_cast<TYPE>(RAND()%ROUND2INT(ptMaxData[j]));
const TYPE radius(TYPE(3+RAND()%30));
// build octree and find interest items
TestTree tree(items, aabb, [](typename TestTree::IDX_TYPE size, typename TestTree::Type radius) {
return size > 16 && radius > 10;
});
typename TestTree::IDXARR_TYPE indices;
tree.Collect(indices, pt, radius);
// find interest items by brute force
typename TestTree::IDXARR_TYPE trueIndices;
#if 1
// use square bound
typename TestTree::AABB_TYPE aabbQuery(pt, radius);
FOREACH(i, items)
if (aabbQuery.Intersects(items[i]))
trueIndices.Insert(i);
#else
// use circle bound
FOREACH(i, items)
if ((items[i]-pt).norm() < radius)
trueIndices.Insert(i);
#endif
// compare results
unsigned nMatches = 0;
FOREACH(i, trueIndices) {
const typename TestTree::IDX_TYPE idx = trueIndices[i];
FOREACH(j, indices) {
if (indices[j] == idx) {
++nMatches;
break;
}
}
}
nTotalMatches += nMatches;
nTotalMissed += (unsigned)trueIndices.size()-nMatches;
nTotalExtra += (unsigned)indices.size()-nMatches;
#ifndef _RELEASE
// print stats
typename TestTree::DEBUGINFO_TYPE info;
tree.GetDebugInfo(&info);
totalInfo += info;
#endif
}
#ifndef _RELEASE
TestTree::LogDebugInfo(totalInfo);
VERBOSE("Test %s (TotalMissed %d, TotalExtra %d)", (nTotalMissed == 0 && nTotalExtra == 0 ? "successful" : "FAILED"), nTotalMissed, nTotalExtra);
#endif
return (nTotalMissed == 0 && nTotalExtra == 0);
}

161
libs/Common/Plane.h

@ -0,0 +1,161 @@ @@ -0,0 +1,161 @@
////////////////////////////////////////////////////////////////////
// Plane.h
//
// Copyright 2007 cDc@seacave
// Distributed under the Boost Software License, Version 1.0
// (See http://www.boost.org/LICENSE_1_0.txt)
#ifndef __SEACAVE_PLANE_H__
#define __SEACAVE_PLANE_H__
// I N C L U D E S /////////////////////////////////////////////////
// D E F I N E S ///////////////////////////////////////////////////
namespace SEACAVE {
// S T R U C T S ///////////////////////////////////////////////////
// Basic hyper-plane class
// (plane represented in Hessian Normal Form: n.x+d=0 <=> ax+by+cz+d=0)
template <typename TYPE, int DIMS=3>
class TPlane
{
STATIC_ASSERT(DIMS > 0 && DIMS <= 3);
public:
typedef Eigen::Matrix<TYPE,DIMS,1> VECTOR;
typedef Eigen::Matrix<TYPE,DIMS,1> POINT;
typedef SEACAVE::TAABB<TYPE,DIMS> AABB;
typedef SEACAVE::TRay<TYPE,DIMS> RAY;
enum { numScalar = DIMS+1 };
enum { numParams = numScalar-1 };
VECTOR m_vN; // plane normal vector
TYPE m_fD; // distance to origin
//---------------------------------------
inline TPlane() {}
inline TPlane(const VECTOR&, TYPE);
inline TPlane(const VECTOR&, const POINT&);
inline TPlane(const POINT&, const POINT&, const POINT&);
inline TPlane(const TYPE p[DIMS+1]);
inline TPlane(const Eigen::Matrix<TYPE,DIMS+1,1>&);
inline void Set(const VECTOR&, TYPE);
inline void Set(const VECTOR&, const POINT&);
inline void Set(const POINT&, const POINT&, const POINT&);
inline void Set(const TYPE p[DIMS+1]);
inline void Set(const Eigen::Matrix<TYPE,DIMS+1,1>&);
int Optimize(const POINT*, size_t, int maxIters=100);
template <typename RobustNormFunctor>
int Optimize(const POINT*, size_t, const RobustNormFunctor& robust, int maxIters=100);
inline void Invalidate();
inline bool IsValid() const;
inline void Negate();
inline TPlane Negated() const;
inline TYPE Distance(const TPlane&) const;
inline TYPE Distance(const POINT&) const;
inline TYPE DistanceAbs(const POINT&) const;
inline POINT ProjectPoint(const POINT&) const;
inline GCLASS Classify(const POINT&) const;
inline GCLASS Classify(const AABB&) const;
bool Clip(const RAY&, TYPE, RAY*, RAY*) const;
bool Intersects(const POINT& p0, const POINT& p1, const POINT& p2) const;
bool Intersects(const TPlane& plane, RAY& ray) const;
bool Intersects(const AABB& aabb) const;
inline TYPE& operator [] (BYTE i) { ASSERT(i<numScalar); return m_vN.data()[i]; }
inline TYPE operator [] (BYTE i) const { ASSERT(i<numScalar); return m_vN.data()[i]; }
#ifdef _USE_BOOST
// implement BOOST serialization
template<class Archive>
void serialize(Archive& ar, const unsigned int /*version*/) {
ar & m_vN;
ar & m_fD;
}
#endif
}; // class TPlane
/*----------------------------------------------------------------*/
template <typename TYPE, typename TYPEW=TYPE, bool bFitLineMode=false>
struct FitPlaneOnline {
TYPEW sumX, sumSqX, sumXY, sumXZ;
TYPEW sumY, sumSqY, sumYZ;
TYPEW sumZ, sumSqZ;
size_t size;
FitPlaneOnline();
void Update(const TPoint3<TYPE>& P);
TPoint3<TYPEW> GetModel(TPoint3<TYPEW>& avg, TPoint3<TYPEW>& dir) const;
template <typename TYPEE> TPoint3<TYPEE> GetPlane(TPlane<TYPEE,3>& plane) const;
};
/*----------------------------------------------------------------*/
// Basic 3D frustum class
// (represented as 6 planes oriented toward outside the frustum volume)
template <typename TYPE, int DIMS=6>
class TFrustum
{
STATIC_ASSERT(DIMS > 0 && DIMS <= 6);
public:
typedef Eigen::Matrix<TYPE,4,4,Eigen::RowMajor> MATRIX4x4;
typedef Eigen::Matrix<TYPE,3,4,Eigen::RowMajor> MATRIX3x4;
typedef Eigen::Matrix<TYPE,3,1> VECTOR;
typedef Eigen::Matrix<TYPE,3,1> POINT;
typedef SEACAVE::TPlane<TYPE,3> PLANE;
typedef SEACAVE::TSphere<TYPE,3> SPHERE;
typedef SEACAVE::TAABB<TYPE,3> AABB;
enum { numCorners = (1<<3) };
PLANE m_planes[DIMS]; // left, right, top, bottom, near and far planes
//---------------------------------------
inline TFrustum() {}
inline TFrustum(const MATRIX4x4&, TYPE width, TYPE height, TYPE nearZ=TYPE(0.0001), TYPE farZ=TYPE(1000));
inline TFrustum(const MATRIX3x4&, TYPE width, TYPE height, TYPE nearZ=TYPE(0.0001), TYPE farZ=TYPE(1000));
void Set(const MATRIX4x4&, TYPE width, TYPE height, TYPE nearZ=TYPE(0.0001), TYPE farZ=TYPE(1000));
void Set(const MATRIX3x4&, TYPE width, TYPE height, TYPE nearZ=TYPE(0.0001), TYPE farZ=TYPE(1000));
void Set(const VECTOR corners[numCorners]);
void SetProjectionGL(const MATRIX4x4&);
GCLASS Classify(const POINT&) const;
GCLASS Classify(const SPHERE&) const;
GCLASS Classify(const AABB&) const;
inline TYPE& operator [] (BYTE i) { ASSERT(i<DIMS); return m_planes[i]; }
inline TYPE operator [] (BYTE i) const { ASSERT(i<DIMS); return m_planes[i]; }
#ifdef _USE_BOOST
// implement BOOST serialization
template<class Archive>
void serialize(Archive& ar, const unsigned int /*version*/) {
ar & m_planes;
}
#endif
}; // class TPlane
/*----------------------------------------------------------------*/
#include "Plane.inl"
/*----------------------------------------------------------------*/
} // namespace SEACAVE
#endif // __SEACAVE_PLANE_H__

623
libs/Common/Plane.inl

@ -0,0 +1,623 @@ @@ -0,0 +1,623 @@
////////////////////////////////////////////////////////////////////
// Plane.inl
//
// Copyright 2007 cDc@seacave
// Distributed under the Boost Software License, Version 1.0
// (See http://www.boost.org/LICENSE_1_0.txt)
// D E F I N E S ///////////////////////////////////////////////////
// S T R U C T S ///////////////////////////////////////////////////
// Construct plane given its normal and the distance from the origin.
template <typename TYPE, int DIMS>
inline TPlane<TYPE,DIMS>::TPlane(const VECTOR& vN, TYPE fD)
:
m_vN(vN), m_fD(fD)
{
}
// Construct plane given its normal and a point on the plane.
template <typename TYPE, int DIMS>
inline TPlane<TYPE,DIMS>::TPlane(const VECTOR& vN, const POINT& p)
:
m_vN(vN), m_fD(-vN.dot(p))
{
}
// Construct plane given three points on the plane.
template <typename TYPE, int DIMS>
inline TPlane<TYPE,DIMS>::TPlane(const POINT& p0, const POINT& p1, const POINT& p2)
{
Set(p0, p1, p2);
}
// Construct plane given its standard equation: Ax + By + Cz + D = 0
template <typename TYPE, int DIMS>
inline TPlane<TYPE,DIMS>::TPlane(const TYPE p[DIMS+1])
{
Set(p);
}
// Construct plane given its standard equation: Ax + By + Cz + D = 0
template <typename TYPE, int DIMS>
inline TPlane<TYPE,DIMS>::TPlane(const Eigen::Matrix<TYPE,DIMS+1,1>& v)
{
Set(v);
} // constructors
/*----------------------------------------------------------------*/
template <typename TYPE, int DIMS>
inline void TPlane<TYPE,DIMS>::Set(const VECTOR& vN, TYPE fD)
{
m_vN = vN;
m_fD = fD;
}
template <typename TYPE, int DIMS>
inline void TPlane<TYPE,DIMS>::Set(const VECTOR& vN, const POINT& p)
{
m_vN = vN;
m_fD = -vN.dot(p);
}
template <typename TYPE, int DIMS>
inline void TPlane<TYPE,DIMS>::Set(const POINT& p0, const POINT& p1, const POINT& p2)
{
const VECTOR vcEdge1 = p1 - p0;
const VECTOR vcEdge2 = p2 - p0;
m_vN = vcEdge1.cross(vcEdge2).normalized();
m_fD = -m_vN.dot(p0);
}
template <typename TYPE, int DIMS>
inline void TPlane<TYPE,DIMS>::Set(const TYPE p[DIMS+1])
{
const Eigen::Map<const VECTOR> vN(p);
const TYPE invD(INVERT(vN.norm()));
Set(vN*invD, p[DIMS]*invD);
}
template <typename TYPE, int DIMS>
inline void TPlane<TYPE,DIMS>::Set(const Eigen::Matrix<TYPE,DIMS+1,1>& v)
{
const VECTOR vN = v.template topLeftCorner<3,1>();
const TYPE invD(INVERT(vN.norm()));
Set(vN*invD, v(DIMS)*invD);
} // Set
/*----------------------------------------------------------------*/
// least squares refinement of the given plane to the 3D point set
// (return the number of iterations)
template <typename TYPE, int DIMS>
template <typename RobustNormFunctor>
int TPlane<TYPE,DIMS>::Optimize(const POINT* points, size_t size, const RobustNormFunctor& robust, int maxIters)
{
ASSERT(DIMS == 3);
ASSERT(size >= numParams);
struct OptimizationFunctor {
const POINT* points;
const size_t size;
const RobustNormFunctor& robust;
// construct with the data points
OptimizationFunctor(const POINT* _points, size_t _size, const RobustNormFunctor& _robust)
: points(_points), size(_size), robust(_robust) { ASSERT(size < (size_t)std::numeric_limits<int>::max()); }
static void Residuals(const double* x, int nPoints, const void* pData, double* fvec, double* fjac, int* /*info*/) {
const OptimizationFunctor& data = *reinterpret_cast<const OptimizationFunctor*>(pData);
ASSERT((size_t)nPoints == data.size && fvec != NULL && fjac == NULL);
TPlane<double,3> plane; {
Point3d N;
plane.m_fD = x[0];
Dir2Normal(reinterpret_cast<const Point2d&>(x[1]), N);
plane.m_vN = N;
}
for (size_t i=0; i<data.size; ++i)
fvec[i] = data.robust(plane.Distance(data.points[i].template cast<double>()));
}
} functor(points, size, robust);
double arrParams[numParams]; {
arrParams[0] = (double)m_fD;
const Point3d N(m_vN.x(), m_vN.y(), m_vN.z());
Normal2Dir(N, reinterpret_cast<Point2d&>(arrParams[1]));
}
lm_control_struct control = {1.e-6, 1.e-7, 1.e-8, 1.e-7, 100.0, maxIters}; // lm_control_float;
lm_status_struct status;
lmmin(numParams, arrParams, (int)size, &functor, OptimizationFunctor::Residuals, &control, &status);
switch (status.info) {
//case 4:
case 5:
case 6:
case 7:
case 8:
case 9:
case 10:
case 11:
case 12:
DEBUG_ULTIMATE("error: refine plane: %s", lm_infmsg[status.info]);
return 0;
}
// set plane
{
Point3d N;
Dir2Normal(reinterpret_cast<const Point2d&>(arrParams[1]), N);
Set(Cast<TYPE>(N), (TYPE)arrParams[0]);
}
return status.nfev;
}
template <typename TYPE, int DIMS>
int TPlane<TYPE,DIMS>::Optimize(const POINT* points, size_t size, int maxIters)
{
const auto identity = [](double x) { return x; };
return Optimize(points, size, identity, maxIters);
} // Optimize
/*----------------------------------------------------------------*/
template <typename TYPE, int DIMS>
inline void TPlane<TYPE,DIMS>::Invalidate()
{
m_fD = std::numeric_limits<TYPE>::max();
} // Invalidate
template <typename TYPE, int DIMS>
inline bool TPlane<TYPE,DIMS>::IsValid() const
{
return m_fD != std::numeric_limits<TYPE>::max();
} // IsValid
/*----------------------------------------------------------------*/
template <typename TYPE, int DIMS>
inline void TPlane<TYPE,DIMS>::Negate()
{
m_vN = -m_vN;
m_fD = -m_fD;
} // Negate
template <typename TYPE, int DIMS>
inline TPlane<TYPE,DIMS> TPlane<TYPE,DIMS>::Negated() const
{
return TPlane(-m_vN, -m_fD);
} // Negated
/*----------------------------------------------------------------*/
template <typename TYPE, int DIMS>
inline TYPE TPlane<TYPE,DIMS>::Distance(const TPlane& p) const
{
return ABS(m_fD - p.m_fD);
}
/*----------------------------------------------------------------*/
// Calculate distance to point. Plane normal must be normalized.
template <typename TYPE, int DIMS>
inline TYPE TPlane<TYPE,DIMS>::Distance(const POINT& p) const
{
return m_vN.dot(p) + m_fD;
}
template <typename TYPE, int DIMS>
inline TYPE TPlane<TYPE,DIMS>::DistanceAbs(const POINT& p) const
{
return ABS(Distance(p));
}
/*----------------------------------------------------------------*/
// Calculate point's projection on this plane (closest point to this plane).
template <typename TYPE, int DIMS>
inline typename TPlane<TYPE,DIMS>::POINT TPlane<TYPE,DIMS>::ProjectPoint(const POINT& p) const
{
return p - m_vN*Distance(p);
}
/*----------------------------------------------------------------*/
// Classify point to plane.
template <typename TYPE, int DIMS>
inline GCLASS TPlane<TYPE,DIMS>::Classify(const POINT& p) const
{
const TYPE f(Distance(p));
if (f > ZEROTOLERANCE<TYPE,DIMS>()) return FRONT;
if (f < -ZEROTOLERANCE<TYPE,DIMS>()) return BACK;
return PLANAR;
}
/*----------------------------------------------------------------*/
// Classify bounding box to plane.
template <typename TYPE, int DIMS>
inline GCLASS TPlane<TYPE,DIMS>::Classify(const AABB& aabb) const
{
const GCLASS classMin = Classify(aabb.ptMin);
const GCLASS classMax = Classify(aabb.ptMax);
if (classMin == classMax) return classMin;
return CLIPPED;
}
/*----------------------------------------------------------------*/
// clips a ray into two segments if it intersects the plane
template <typename TYPE, int DIMS>
bool TPlane<TYPE,DIMS>::Clip(const RAY& ray, TYPE fL, RAY* pF, RAY* pB) const
{
POINT ptHit = POINT::ZERO;
// ray intersects plane at all?
if (!ray.Intersects(*this, false, fL, NULL, &ptHit))
return false;
GCLASS n = Classify(ray.m_pOrig);
// ray comes from planes backside
if ( n == BACK ) {
if (pB) pB->Set(ray.m_pOrig, ray.m_vDir);
if (pF) pF->Set(ptHit, ray.m_vDir);
}
// ray comes from planes front side
else if ( n == FRONT ) {
if (pF) pF->Set(ray.m_pOrig, ray.m_vDir);
if (pB) pB->Set(ptHit, ray.m_vDir);
}
return true;
} // Clip(Ray)
/*----------------------------------------------------------------*/
// Intersection of two planes.
// Returns the line of intersection.
// http://paulbourke.net/geometry/pointlineplane/
template <typename TYPE, int DIMS>
bool TPlane<TYPE,DIMS>::Intersects(const TPlane& plane, RAY& ray) const
{
// if crossproduct of normals 0 than planes parallel
const VECTOR vCross(m_vN.cross(plane.m_vN));
const TYPE fSqrLength(vCross.squaredNorm());
if (fSqrLength < ZEROTOLERANCE<TYPE,DIMS>())
return false;
// find line of intersection
#if 0
// the general case
const TYPE fN00 = m_vN.squaredNorm();
const TYPE fN01 = m_vN.dot(plane.m_vN);
const TYPE fN11 = plane.m_vN.squaredNorm();
const TYPE fDet = fN01*fN01 - fN00*fN11;
const TYPE fInvDet = INVERT(fDet);
const TYPE fC0 = (fN11*m_fD - fN01*plane.m_fD) * fInvDet;
const TYPE fC1 = (fN00*plane.m_fD - fN01*m_fD) * fInvDet;
#else
// plane normals assumed to be normalized vectors
ASSERT(ISEQUAL(m_vN.norm(), TYPE(1)));
ASSERT(ISEQUAL(plane.m_vN.norm(), TYPE(1)));
const TYPE fN01 = m_vN.dot(plane.m_vN);
const TYPE fInvDet = INVERT(fN01*fN01-TYPE(1));
const TYPE fC0 = (m_fD - fN01*plane.m_fD) * fInvDet;
const TYPE fC1 = (plane.m_fD - fN01*m_fD) * fInvDet;
#endif
ray.m_vDir = vCross * RSQRT(fSqrLength);
ray.m_pOrig = m_vN*fC0 + plane.m_vN*fC1;
return true;
} // Intersects(Plane)
/*----------------------------------------------------------------*/
// Intersection of a plane with a triangle. If all vertices of the
// triangle are on the same side of the plane, no intersection occurred.
template <typename TYPE, int DIMS>
bool TPlane<TYPE,DIMS>::Intersects(const POINT& p0, const POINT& p1, const POINT& p2) const
{
const GCLASS n(Classify(p0));
if ((n == Classify(p1)) &&
(n == Classify(p2)))
return false;
return true;
} // Intersects(Tri)
/*----------------------------------------------------------------*/
// Intersection with AABB. Search for AABB diagonal that is most
// aligned to plane normal. Test its two vertices against plane.
// (M<EFBFBD>ller/Haines, "Real-Time Rendering")
template <typename TYPE, int DIMS>
bool TPlane<TYPE,DIMS>::Intersects(const AABB& aabb) const
{
POINT Vmin, Vmax;
// x component
if (m_vN(0) >= TYPE(0)) {
Vmin(0) = aabb.ptMin(0);
Vmax(0) = aabb.ptMax(0);
}
else {
Vmin(0) = aabb.ptMax(0);
Vmax(0) = aabb.ptMin(0);
}
// y component
if (m_vN(1) >= TYPE(0)) {
Vmin(1) = aabb.ptMin(1);
Vmax(1) = aabb.ptMax(1);
}
else {
Vmin(1) = aabb.ptMax(1);
Vmax(1) = aabb.ptMin(1);
}
// z component
if (m_vN(2) >= TYPE(0)) {
Vmin(2) = aabb.ptMin(2);
Vmax(2) = aabb.ptMax(2);
}
else {
Vmin(2) = aabb.ptMax(2);
Vmax(2) = aabb.ptMin(2);
}
if (((m_vN * Vmin) + m_fD) > TYPE(0))
return false;
if (((m_vN * Vmax) + m_fD) >= TYPE(0))
return true;
return false;
} // Intersects(AABB)
/*----------------------------------------------------------------*/
// same as above, but online version
template <typename TYPE, typename TYPEW, bool bFitLineMode>
FitPlaneOnline<TYPE,TYPEW,bFitLineMode>::FitPlaneOnline()
: sumX(0), sumSqX(0), sumXY(0), sumXZ(0), sumY(0), sumSqY(0), sumYZ(0), sumZ(0), sumSqZ(0), size(0)
{
}
template <typename TYPE, typename TYPEW, bool bFitLineMode>
void FitPlaneOnline<TYPE,TYPEW,bFitLineMode>::Update(const TPoint3<TYPE>& P)
{
const TYPEW X((TYPEW)P.x), Y((TYPEW)P.y), Z((TYPEW)P.z);
sumX += X; sumSqX += X*X; sumXY += X*Y; sumXZ += X*Z;
sumY += Y; sumSqY += Y*Y; sumYZ += Y*Z;
sumZ += Z; sumSqZ += Z*Z;
++size;
}
template <typename TYPE, typename TYPEW, bool bFitLineMode>
TPoint3<TYPEW> FitPlaneOnline<TYPE,TYPEW,bFitLineMode>::GetModel(TPoint3<TYPEW>& avg, TPoint3<TYPEW>& dir) const
{
const TYPEW avgX(sumX/(TYPEW)size), avgY(sumY/(TYPEW)size), avgZ(sumZ/(TYPEW)size);
// assemble covariance (lower-triangular) matrix
typedef Eigen::Matrix<TYPEW,3,3> Mat3x3;
Mat3x3 A;
A(0,0) = sumSqX - TYPEW(2)*sumX*avgX + avgX*avgX*(TYPEW)size;
A(1,0) = sumXY - sumX*avgY - avgX*sumY + avgX*avgY*(TYPEW)size;
A(1,1) = sumSqY - TYPEW(2)*sumY*avgY + avgY*avgY*(TYPEW)size;
A(2,0) = sumXZ - sumX*avgZ - avgX*sumZ + avgX*avgZ*(TYPEW)size;
A(2,1) = sumYZ - sumY*avgZ - avgY*sumZ + avgY*avgZ*(TYPEW)size;
A(2,2) = sumSqZ - TYPEW(2)*sumZ*avgZ + avgZ*avgZ*(TYPEW)size;
// the plane normal is simply the eigenvector corresponding to least eigenvalue
const int nAxis(bFitLineMode ? 2 : 0);
const Eigen::SelfAdjointEigenSolver<Mat3x3> es(A);
ASSERT(ISEQUAL(es.eigenvectors().col(nAxis).norm(), TYPEW(1)));
avg = TPoint3<TYPEW>(avgX,avgY,avgZ);
dir = es.eigenvectors().col(nAxis);
const TYPEW* const vals(es.eigenvalues().data());
ASSERT(vals[0] <= vals[1] && vals[1] <= vals[2]);
return *reinterpret_cast<const TPoint3<TYPEW>*>(vals);
}
template <typename TYPE, typename TYPEW, bool bFitLineMode>
template <typename TYPEE>
TPoint3<TYPEE> FitPlaneOnline<TYPE,TYPEW,bFitLineMode>::GetPlane(TPlane<TYPEE,3>& plane) const
{
TPoint3<TYPEW> avg, dir;
const TPoint3<TYPEW> quality(GetModel(avg, dir));
plane.Set(TPoint3<TYPEE>(dir), TPoint3<TYPEE>(avg));
return TPoint3<TYPEE>(quality);
}
/*----------------------------------------------------------------*/
// Least squares fits a plane to a 3D point set.
// See http://www.geometrictools.com/Documentation/LeastSquaresFitting.pdf
// Returns a fitting quality (1 - lambda_min/lambda_max):
// 1 is best (zero variance orthogonally to the fitting line)
// 0 is worst (isotropic case, returns a plane with default direction)
template <typename TYPE>
TYPE FitPlane(const TPoint3<TYPE>* points, size_t size, TPlane<TYPE>& plane) {
// compute a point on the plane, which is shown to be the centroid of the points
const Eigen::Map< const Eigen::Matrix<TYPE,Eigen::Dynamic,3,Eigen::RowMajor> > vPoints((const TYPE*)points, size, 3);
const TPoint3<TYPE> c(vPoints.colwise().mean());
// assemble covariance matrix; matrix numbering:
// 0
// 1 2
// 3 4 5
Eigen::Matrix<TYPE,3,3,Eigen::RowMajor> A(Eigen::Matrix<TYPE,3,3,Eigen::RowMajor>::Zero());
FOREACHRAWPTR(pPt, points, size) {
const TPoint3<TYPE> X(*pPt - c);
A(0,0) += X.x*X.x;
A(1,0) += X.x*X.y;
A(1,1) += X.y*X.y;
A(2,0) += X.x*X.z;
A(2,1) += X.y*X.z;
A(2,2) += X.z*X.z;
}
// the plane normal is simply the eigenvector corresponding to least eigenvalue
const Eigen::SelfAdjointEigenSolver< Eigen::Matrix<TYPE,3,3,Eigen::RowMajor> > es(A);
ASSERT(ISEQUAL(es.eigenvectors().col(0).norm(), TYPE(1)));
plane.Set(es.eigenvectors().col(0), c);
const TYPE* const vals(es.eigenvalues().data());
ASSERT(vals[0] <= vals[1] && vals[1] <= vals[2]);
return TYPE(1) - vals[0]/vals[1];
}
/*----------------------------------------------------------------*/
// C L A S S //////////////////////////////////////////////////////
// Construct frustum given a projection matrix.
template <typename TYPE, int DIMS>
inline TFrustum<TYPE,DIMS>::TFrustum(const MATRIX4x4& m, TYPE w, TYPE h, TYPE n, TYPE f)
{
Set(m, w, h, n, f);
}
template <typename TYPE, int DIMS>
inline TFrustum<TYPE,DIMS>::TFrustum(const MATRIX3x4& m, TYPE w, TYPE h, TYPE n, TYPE f)
{
Set(m, w, h, n, f);
} // Constructor
/*----------------------------------------------------------------*/
// Set frustum planes, normals pointing outwards, from SfM projection matrix and image plane details
template <typename TYPE, int DIMS>
void TFrustum<TYPE,DIMS>::Set(const MATRIX4x4& m, TYPE w, TYPE h, TYPE n, TYPE f)
{
const VECTOR ltn(0,0,n), rtn(w*n,0,n), lbn(0,h*n,n), rbn(w*n,h*n,n);
const VECTOR ltf(0,0,f), rtf(w*f,0,f), lbf(0,h*f,f), rbf(w*f,h*f,f);
const MATRIX4x4 inv(m.inverse());
const VECTOR corners[] = {
(inv*ltn.homogeneous()).template topRows<3>(),
(inv*rtn.homogeneous()).template topRows<3>(),
(inv*lbn.homogeneous()).template topRows<3>(),
(inv*rbn.homogeneous()).template topRows<3>(),
(inv*ltf.homogeneous()).template topRows<3>(),
(inv*rtf.homogeneous()).template topRows<3>(),
(inv*lbf.homogeneous()).template topRows<3>(),
(inv*rbf.homogeneous()).template topRows<3>()
};
Set(corners);
}
template <typename TYPE, int DIMS>
void TFrustum<TYPE,DIMS>::Set(const MATRIX3x4& m, TYPE w, TYPE h, TYPE n, TYPE f)
{
MATRIX4x4 M(MATRIX4x4::Identity());
M.template topLeftCorner<3,4>() = m;
Set(M, w, h, n, f);
}
// Set frustum planes, normals pointing outwards, from the given corners
template <typename TYPE, int DIMS>
void TFrustum<TYPE,DIMS>::Set(const VECTOR corners[numCorners])
{
// left clipping plane
m_planes[0].Set(corners[0], corners[4], corners[6]);
if (DIMS > 1) // right clipping plane
m_planes[1].Set(corners[1], corners[7], corners[5]);
if (DIMS > 2) // top clipping plane
m_planes[2].Set(corners[0], corners[5], corners[4]);
if (DIMS > 3) // bottom clipping plane
m_planes[3].Set(corners[2], corners[6], corners[7]);
if (DIMS > 4) // near clipping plane
m_planes[4].Set(corners[0], corners[2], corners[3]);
if (DIMS > 5) // far clipping plane
m_planes[5].Set(corners[4], corners[5], corners[7]);
} // Set
// Set frustum planes, normals pointing outwards, from the OpenGL projection-view matrix
template <typename TYPE, int DIMS>
void TFrustum<TYPE,DIMS>::SetProjectionGL(const MATRIX4x4& mProjectionView)
{
// left clipping plane
m_planes[0].Set(-mProjectionView.row(3) - mProjectionView.row(0));
if (DIMS > 1) // right clipping plane
m_planes[1].Set(-mProjectionView.row(3) + mProjectionView.row(0));
if (DIMS > 2) // top clipping plane
m_planes[2].Set(-mProjectionView.row(3) + mProjectionView.row(1));
if (DIMS > 3) // bottom clipping plane
m_planes[3].Set(-mProjectionView.row(3) - mProjectionView.row(1));
if (DIMS > 4) // near clipping plane
m_planes[4].Set(-mProjectionView.row(3) - mProjectionView.row(2));
if (DIMS > 5) // far clipping plane
m_planes[5].Set(-mProjectionView.row(3) + mProjectionView.row(2));
}
/*----------------------------------------------------------------*/
/**
* Culls POINT to n sided frustum. Normals pointing outwards.
* -> IN: POINT - point to be tested
* OUT: VISIBLE - point inside frustum
* CULLED - point outside frustum
*/
template <typename TYPE, int DIMS>
GCLASS TFrustum<TYPE,DIMS>::Classify(const POINT& p) const
{
// check if on the front side of any of the planes
for (int i=0; i<DIMS; ++i) {
if (m_planes[i].Classify(p) == FRONT)
return CULLED;
} // for
return VISIBLE;
}
/**
* Culls SPHERE to n sided frustum. Normals pointing outwards.
* -> IN: POINT - center of the sphere to be tested
* TYPE - radius of the sphere to be tested
* OUT: VISIBLE - sphere inside frustum
* CLIPPED - sphere clipped by frustum
* CULLED - sphere outside frustum
*/
template <typename TYPE, int DIMS>
GCLASS TFrustum<TYPE,DIMS>::Classify(const SPHERE& s) const
{
// compute distances to each of the planes
for (int i=0; i<DIMS; ++i) {
// compute the distance to this plane
const TYPE dist(m_planes[i].Distance(s.center));
// if distance is bigger than the sphere radius, the sphere is outside
if (dist > s.radius)
return CULLED;
// if the distance is between +- radius, the sphere intersects the frustum
if (ABS(dist) < s.radius)
return CLIPPED;
} // for
// otherwise sphere is fully in view
return VISIBLE;
}
/**
* Culls AABB to n sided frustum. Normals pointing outwards.
* -> IN: AABB - bounding box to be tested
* OUT: VISIBLE - aabb totally inside frustum
* CLIPPED - aabb clipped by frustum
* CULLED - aabb totally outside frustum
*/
template <typename TYPE, int DIMS>
GCLASS TFrustum<TYPE,DIMS>::Classify(const AABB& aabb) const
{
bool bIntersects = false;
// find and test extreme points
for (int i=0; i<DIMS; ++i) {
const PLANE& plane = m_planes[i];
POINT ptPlaneMin, ptPlaneMax;
// x coordinate
if (plane.m_vN(0) >= TYPE(0)) {
ptPlaneMin(0) = aabb.ptMin(0);
ptPlaneMax(0) = aabb.ptMax(0);
} else {
ptPlaneMin(0) = aabb.ptMax(0);
ptPlaneMax(0) = aabb.ptMin(0);
}
// y coordinate
if (plane.m_vN(1) >= TYPE(0)) {
ptPlaneMin(1) = aabb.ptMin(1);
ptPlaneMax(1) = aabb.ptMax(1);
} else {
ptPlaneMin(1) = aabb.ptMax(1);
ptPlaneMax(1) = aabb.ptMin(1);
}
// z coordinate
if (plane.m_vN(2) >= TYPE(0)) {
ptPlaneMin(2) = aabb.ptMin(2);
ptPlaneMax(2) = aabb.ptMax(2);
} else {
ptPlaneMin(2) = aabb.ptMax(2);
ptPlaneMax(2) = aabb.ptMin(2);
}
if (plane.m_vN.dot(ptPlaneMin) > -plane.m_fD)
return CULLED;
if (plane.m_vN.dot(ptPlaneMax) >= -plane.m_fD)
bIntersects = true;
} // for
if (bIntersects) return CLIPPED;
return VISIBLE;
}
/*----------------------------------------------------------------*/

296
libs/Common/Queue.h

@ -0,0 +1,296 @@ @@ -0,0 +1,296 @@
////////////////////////////////////////////////////////////////////
// Queue.h
//
// Copyright 2007 cDc@seacave
// Distributed under the Boost Software License, Version 1.0
// (See http://www.boost.org/LICENSE_1_0.txt)
#ifndef __SEACAVE_QUEUE_H__
#define __SEACAVE_QUEUE_H__
// I N C L U D E S /////////////////////////////////////////////////
// D E F I N E S ///////////////////////////////////////////////////
namespace SEACAVE {
// S T R U C T S ///////////////////////////////////////////////////
/**************************************************************************************
// Queue template
**************************************************************************************/
template <class TYPE, class ARG_TYPE=const TYPE&, int useConstruct=1, int xGrow=16>
class cQueue
{
public:
cQueue(int_t xSize = 16) :
first(-1), last(-1), vectorSize(0), vector(NULL)
{
if (xSize > 0)
Grow(xSize);
}
~cQueue()
{
Release();
}
void Grow(int_t gr)
{
ASSERT(gr > 0);
if (IsEmpty()) {
ASSERT(first == -1 && last == -1);
operator delete[] (vector);
vectorSize += gr;
vector = (TYPE*) operator new[] (vectorSize * sizeof(TYPE));
return;
}
TYPE* tmp = (TYPE*) operator new[] ((vectorSize + gr) * sizeof(TYPE));
if (first > last) {
memcpy(tmp, vector + first, (vectorSize - first) * sizeof(TYPE));
memcpy(tmp + (vectorSize - first), vector, (last + 1) * sizeof(TYPE));
last += vectorSize - first;
} else {
memcpy(tmp, vector + first, (last - first + 1) * sizeof(TYPE));
last = last - first;
}
first = 0;
vectorSize += gr;
operator delete[] (vector);
vector = tmp;
}
inline TYPE* GetData()
{
return vector;
}
inline void PushHead()
{
_PushHead();
if (useConstruct)
new(vector+(--first)) TYPE();
else
--first;
}
inline void PushTail()
{
_PushTail();
if (useConstruct)
new(vector+(++last)) TYPE();
else
++last;
}
inline TYPE& AddEmptyHead()
{
_PushHead();
if (useConstruct)
return *(new(vector+(--first)) TYPE());
else
return vector[--first];
}
inline TYPE& AddEmptyTail()
{
_PushTail();
if (useConstruct)
return *(new(vector+(++last)) TYPE());
else
return vector[++last];
}
inline void AddHead(ARG_TYPE elem)
{
_PushHead();
if (useConstruct)
new(vector+(--first)) TYPE(elem);
else
vector[--first] = elem;
}
inline void AddTail(ARG_TYPE elem)
{
_PushTail();
if (useConstruct)
new(vector+(++last)) TYPE(elem);
else
vector[++last] = elem;
}
inline TYPE& GetHead() const
{
ASSERT(first >= 0 && last >= 0);
return vector[first];
}
inline TYPE& GetTail() const
{
ASSERT(first >= 0 && last >= 0);
return vector[last];
}
inline TYPE& GetHead(uint_t delta) const
{
ASSERT(first >= 0 && last >= 0 && delta < GetSize());
delta += first;
if ((int_t)delta < vectorSize)
return vector[delta];
return vector[delta-vectorSize];
}
inline TYPE& GetTail(uint_t delta) const
{
ASSERT(first >= 0 && last >= 0 && delta < GetSize());
if ((int_t)delta <= last)
return vector[last-delta];
return vector[vectorSize-(delta-last)];
}
inline void PopHead()
{
ASSERT(first >= 0 && last >= 0);
if (useConstruct)
(vector+first)->~TYPE();
if (first > last) {
if (++first >= vectorSize)
first = 0;
} else {
if (++first > last)
first = last = -1;
}
}
inline void PopTail()
{
ASSERT(first >= 0 && last >= 0);
if (useConstruct)
(vector+last)->~TYPE();
if (last >= first) {
if (--last < first)
first = last = -1;
} else {
if (--last < 0)
last = vectorSize-1;
}
}
inline TYPE RemoveHead()
{
ASSERT(first >= 0 && last >= 0);
if (useConstruct) {
TYPE elem(vector[first]);
PopHead();
return elem;
} else {
TYPE& elem(vector[first]);
PopHead();
return elem;
}
}
inline TYPE RemoveTail()
{
ASSERT(first >= 0 && last >= 0);
if (useConstruct) {
TYPE elem(vector[last]);
PopTail();
return elem;
} else {
TYPE& elem(vector[last]);
PopTail();
return elem;
}
}
inline uint_t GetSize() const
{
if (first < 0)
return 0;
if (first > last) // circular
return (vectorSize - (first - last) + 1);
return uint_t(last - first + 1);
}
inline void Empty()
{
first = last = -1;
}
// same as Empty(), plus free all allocated memory
inline void Release()
{
ASSERT((vector != NULL && vectorSize > 0) || (vector == NULL && vectorSize == 0));
if (vector != NULL) {
if (useConstruct) {
if (first > last) {
for (; first < vectorSize; first++)
(vector+first)->~TYPE();
for (; last >= 0; last--)
(vector+last)->~TYPE();
} else if (first >= 0) {
for (; first <= last; first++)
(vector+first)->~TYPE();
}
}
operator delete[] (vector);
vector = NULL;
vectorSize = 0;
}
Empty();
}
inline bool IsEmpty() const
{
return (first < 0);
}
inline TYPE& operator[](uint_t index) const
{
return GetHead(index);
}
protected:
inline void _PushHead()
{
if (first > last) {
if (last + 1 == first) {
Grow(xGrow);
first = vectorSize;
}
} else {
if (first <= 0) {
if (last + 1 >= vectorSize)
Grow(xGrow);
first = vectorSize;
}
if (last < 0)
last = first - 1;
}
}
inline void _PushTail()
{
if (last >= first) {
if (last + 1 >= vectorSize) {
if (first <= 0)
Grow(xGrow);
else
last = -1;
}
if (first < 0)
first = 0;
} else {
if (last + 1 == first)
Grow(xGrow);
}
}
protected:
int_t first;
int_t last;
int_t vectorSize;
TYPE* vector;
};
/*----------------------------------------------------------------*/
} // namespace SEACAVE
#endif // __SEACAVE_QUEUE_H__

164
libs/Common/Random.h

@ -0,0 +1,164 @@ @@ -0,0 +1,164 @@
////////////////////////////////////////////////////////////////////
// Random.h
//
// Copyright 2007 cDc@seacave
// Distributed under the Boost Software License, Version 1.0
// (See http://www.boost.org/LICENSE_1_0.txt)
#ifndef __SEACAVE_RANDOM_H__
#define __SEACAVE_RANDOM_H__
// I N C L U D E S /////////////////////////////////////////////////
// D E F I N E S ///////////////////////////////////////////////////
namespace SEACAVE {
// S T R U C T S ///////////////////////////////////////////////////
// Stateless random number generation
// uniform random number generation
FORCEINLINE float random() {
return RANDOM<float>();
}
FORCEINLINE double randomd() {
return RANDOM<double>();
}
FORCEINLINE long double randomld() {
return RANDOM<long double>();
}
STATIC_ASSERT(RAND_MAX < 2147483648); // integer randomRange assumes this is capped
template<typename T>
FORCEINLINE T randomRange(T nMin, T nMax) {
ASSERT(nMin <= nMax && nMax-nMin+1 < 8589934596); // not to overflow a uint64_t
return nMin + T((uint64_t(nMax-nMin)*RAND()+RAND_MAX/2)/RAND_MAX);
}
template<>
FORCEINLINE float randomRange<float>(float fMin, float fMax) {
return fMin + (fMax - fMin) * random();
}
template<>
FORCEINLINE double randomRange<double>(double fMin, double fMax) {
return fMin + (fMax - fMin) * randomd();
}
template<>
FORCEINLINE long double randomRange<long double>(long double fMin, long double fMax) {
return fMin + (fMax - fMin) * randomld();
}
template<typename T>
FORCEINLINE T randomMeanRange(T mean, T delta/*=(max-min)/2*/) {
ASSERT(delta >= 0 && delta*2+1 < 8589934596); // not to overflow a uint64_t
return (mean + T((uint64_t(delta)*2*RAND()+RAND_MAX/2)/RAND_MAX)) - delta;
}
template<>
FORCEINLINE float randomMeanRange<float>(float mean, float delta/*=(max-min)/2*/) {
return mean + delta * (2.f * random() - 1.f);
}
template<>
FORCEINLINE double randomMeanRange<double>(double mean, double delta/*=(max-min)/2*/) {
return mean + delta * (2.0 * randomd() - 1.0);
}
template<>
FORCEINLINE long double randomMeanRange<long double>(long double mean, long double delta/*=(max-min)/2*/) {
return mean + delta * (2.0L * randomld() - 1.0L);
}
// gaussian random number generation
template<typename T>
FORCEINLINE T gaussian(T val, T sigma) {
return EXP(-SQUARE(val/sigma)/2)/(SQRT(T(M_PI*2))*sigma);
}
template<typename T>
FORCEINLINE T randomGaussian(T mean, T sigma) {
T x, y, r2;
do {
x = T(-1) + T(2) * RANDOM<T>();
y = T(-1) + T(2) * RANDOM<T>();
r2 = x * x + y * y;
} while (r2 > T(1) || r2 == T(0));
return mean + sigma * y * SQRT(T(-2) * LOGN(r2) / r2);
}
template<typename T>
FORCEINLINE T randomGaussian(T sigma) {
return randomGaussian(T(0), sigma);
}
FORCEINLINE float randomGaussian() {
return randomGaussian(0.f, 1.f);
}
FORCEINLINE double randomGaussiand() {
return randomGaussian(0.0, 1.0);
}
FORCEINLINE long double randomGaussianld() {
return randomGaussian(0.0L, 1.0L);
}
/*----------------------------------------------------------------*/
// Encapsulates state for random number generation
// based on C++11 random number generator functionality
struct Random : std::mt19937 {
typedef std::mt19937 generator_type;
Random() : generator_type(std::random_device()()) {}
Random(result_type seed) : generator_type(seed) {}
// integer randomRange assumes this is capped
STATIC_ASSERT(max() < 4294967296);
// returns a uniform random number in the range [0, 1]
template<typename T=result_type>
FORCEINLINE typename std::enable_if<std::is_floating_point<T>::value, T>::type random() {
return (T)random()/(T)max();
}
// returns a uniform random number in the range [0, max()]
template<typename T=result_type>
FORCEINLINE typename std::enable_if<std::is_integral<T>::value, T>::type random() {
return (T)operator()();
}
// returns a uniform random number in the range [nMin, nMax]
template<typename T=result_type>
FORCEINLINE typename std::enable_if<std::is_floating_point<T>::value, T>::type randomRange(T nMin, T nMax) {
return nMin + (nMax-nMin) * random<T>();
}
template<typename T=result_type>
FORCEINLINE typename std::enable_if<std::is_integral<T>::value, T>::type randomRange(T nMin, T nMax) {
ASSERT(nMin <= nMax && nMax-nMin+1 < 4294967297); // not to overflow a uint64_t
return nMin + (T)(((uint64_t)(nMax-nMin) * random() + max()/2)/max());
}
// returns a uniform random number in the range [mean-delta, mean+delta]
template<typename T=result_type>
FORCEINLINE typename std::enable_if<std::is_floating_point<T>::value, T>::type randomMeanRange(T mean, T delta/*=(max-min)/2*/) {
return mean + delta * (T(2) * random<T>() - T(1));
}
template<typename T=result_type>
FORCEINLINE typename std::enable_if<std::is_integral<T>::value, T>::type randomMeanRange(T mean, T delta/*=(max-min)/2*/) {
ASSERT(delta >= 0 && delta*T(2)+1 < 4294967297); // not to overflow a uint64_t
return mean + (T)(((uint64_t)delta*2 * random() + max()/2)/max()) - delta;
}
// returns a uniform random number in the range [nMin, nMax] using std implementation
template<typename T=result_type>
FORCEINLINE typename std::enable_if<std::is_floating_point<T>::value, T>::type randomUniform(T nMin, T nMax) {
return std::uniform_real_distribution<T>(nMin, nMax)(*this);
}
template<typename T=result_type>
FORCEINLINE typename std::enable_if<std::is_integral<T>::value, T>::type randomUniform(T nMin, T nMax) {
return std::uniform_int_distribution<T>(nMin, nMax)(*this);
}
// returns a gaussian random number using std implementation
template<typename T=result_type>
FORCEINLINE T randomGaussian(T mean, T stddev) {
return std::normal_distribution<T>(mean, stddev)(*this);
}
};
/*----------------------------------------------------------------*/
} // namespace SEACAVE
#endif // __SEACAVE_RANDOM_H__

226
libs/Common/Ray.h

@ -0,0 +1,226 @@ @@ -0,0 +1,226 @@
////////////////////////////////////////////////////////////////////
// Ray.h
//
// Copyright 2007 cDc@seacave
// Distributed under the Boost Software License, Version 1.0
// (See http://www.boost.org/LICENSE_1_0.txt)
#ifndef __SEACAVE_RAY_H__
#define __SEACAVE_RAY_H__
// I N C L U D E S /////////////////////////////////////////////////
// D E F I N E S ///////////////////////////////////////////////////
namespace SEACAVE {
// S T R U C T S ///////////////////////////////////////////////////
// Basic triangle class
template <typename TYPE, int DIMS>
class TTriangle
{
STATIC_ASSERT(DIMS > 1 && DIMS <= 3);
public:
typedef Eigen::Matrix<TYPE,DIMS,1> VECTOR;
typedef Eigen::Matrix<TYPE,DIMS,1> POINT;
typedef SEACAVE::TAABB<TYPE,DIMS> AABB;
typedef SEACAVE::TPlane<TYPE,DIMS> PLANE;
enum { numScalar = (3*DIMS) };
POINT a, b, c; // triangle vertices
//---------------------------------------
inline TTriangle() {}
inline TTriangle(const POINT&, const POINT&, const POINT&);
inline void Set(const POINT&, const POINT&, const POINT&);
inline POINT GetCenter() const;
inline AABB GetAABB() const;
inline PLANE GetPlane() const;
inline const POINT& operator [] (BYTE i) const { ASSERT(i<3); return (&a)[i]; }
inline POINT& operator [] (BYTE i) { ASSERT(i<3); return (&a)[i]; }
}; // class TTriangle
/*----------------------------------------------------------------*/
// Basic ray class
template <typename TYPE, int DIMS>
class TRay
{
STATIC_ASSERT(DIMS > 1 && DIMS <= 3);
public:
typedef Eigen::Matrix<TYPE,DIMS+1,DIMS+1,Eigen::RowMajor> MATRIX;
typedef Eigen::Matrix<TYPE,DIMS,1> VECTOR;
typedef Eigen::Matrix<TYPE,DIMS,1> POINT;
typedef SEACAVE::TTriangle<TYPE,DIMS> TRIANGLE;
typedef SEACAVE::TSphere<TYPE,DIMS> SPHERE;
typedef SEACAVE::TAABB<TYPE,DIMS> AABB;
typedef SEACAVE::TOBB<TYPE,DIMS> OBB;
typedef SEACAVE::TPlane<TYPE,DIMS> PLANE;
enum { numScalar = (2*DIMS) };
VECTOR m_vDir; // ray direction (normalized)
POINT m_pOrig; // ray origin
//---------------------------------------
inline TRay() {}
inline TRay(const POINT& pOrig, const VECTOR& vDir);
inline TRay(const POINT& pt0, const POINT& pt1, bool bPoints);
inline void Set(const POINT& pOrig, const VECTOR& vDir);
inline void SetFromPoints(const POINT& pt0, const POINT& pt1);
inline TYPE SetFromPointsLen(const POINT& pt0, const POINT& pt1);
inline void DeTransform(const MATRIX&); // move to matrix space
template <bool bCull>
bool Intersects(const TRIANGLE&, TYPE *t) const;
template <bool bCull>
bool Intersects(const TRIANGLE&, TYPE fL, TYPE *t) const;
bool Intersects(const PLANE& plane, bool bCull,
TYPE *t, POINT* pPtHit) const;
inline TYPE IntersectsDist(const PLANE& plane) const;
inline POINT Intersects(const PLANE& plane) const;
bool Intersects(const PLANE& plane, bool bCull,
TYPE fL, TYPE *t, POINT* pPtHit) const;
bool Intersects(const SPHERE& sphere) const;
bool Intersects(const SPHERE& sphere, TYPE& t) const;
bool Intersects(const AABB& aabb) const;
bool Intersects(const AABB& aabb, TYPE& t) const;
bool Intersects(const AABB& aabb, TYPE fL, TYPE *t) const;
bool Intersects(const OBB& obb) const;
bool Intersects(const OBB& obb, TYPE &t) const;
bool Intersects(const OBB& obb, TYPE fL, TYPE *t) const;
bool Intersects(const TRay& ray, TYPE& s) const;
bool Intersects(const TRay& ray, POINT& p) const;
bool Intersects(const TRay& ray, TYPE& s1, TYPE& s2) const;
bool IntersectsAprox(const TRay& ray, POINT& p) const;
bool IntersectsAprox2(const TRay& ray, POINT& p) const;
inline TYPE CosAngle(const TRay&) const;
inline bool Coplanar(const TRay&) const;
inline bool Parallel(const TRay&) const;
inline TYPE Classify(const POINT&) const;
inline POINT ProjectPoint(const POINT&) const;
bool DistanceSq(const POINT&, TYPE&) const;
bool Distance(const POINT&, TYPE&) const;
TYPE DistanceSq(const POINT&) const;
TYPE Distance(const POINT&) const;
POINT GetPoint(TYPE) const;
TRay operator * (const MATRIX&) const; // matrix multiplication
inline TRay& operator *= (const MATRIX&); // matrix multiplication
inline TYPE& operator [] (BYTE i) { ASSERT(i<6); return m_vDir.data()[i]; }
inline TYPE operator [] (BYTE i) const { ASSERT(i<6); return m_vDir.data()[i]; }
}; // class TRay
/*----------------------------------------------------------------*/
// Basic cylinder class
template <typename TYPE, int DIMS>
class TCylinder
{
STATIC_ASSERT(DIMS > 1 && DIMS <= 3);
public:
typedef Eigen::Matrix<TYPE,DIMS,1> VECTOR;
typedef Eigen::Matrix<TYPE,DIMS,1> POINT;
typedef SEACAVE::TRay<TYPE,DIMS> RAY;
typedef SEACAVE::TSphere<TYPE,DIMS> SPHERE;
enum { numScalar = (2*DIMS+2) };
RAY ray; // central ray defining starting the point and direction
TYPE radius; // cylinder radius
TYPE minHeight; // cylinder heights satisfying:
TYPE maxHeight; // std::numeric_limits<TYPE>::lowest() <= minHeight <= 0 < maxHeight <= std::numeric_limits<TYPE>::max()
//---------------------------------------
inline TCylinder() {}
inline TCylinder(const RAY& _ray, TYPE _radius, TYPE _minHeight=TYPE(0), TYPE _maxHeight=std::numeric_limits<TYPE>::max())
: ray(_ray), radius(_radius), minHeight(_minHeight), maxHeight(_maxHeight) { ASSERT(TYPE(0) >= minHeight && minHeight < maxHeight); }
inline bool IsFinite() const { return maxHeight < std::numeric_limits<TYPE>::max() && minHeight > std::numeric_limits<TYPE>::lowest(); }
inline TYPE GetLength() const { return maxHeight - minHeight; }
bool Intersects(const SPHERE&) const;
inline GCLASS Classify(const POINT&, TYPE& t) const;
}; // class TCylinder
/*----------------------------------------------------------------*/
// Basic cone class
template <typename TYPE, int DIMS>
class TCone
{
STATIC_ASSERT(DIMS > 1 && DIMS <= 3);
public:
typedef Eigen::Matrix<TYPE,DIMS,1> VECTOR;
typedef Eigen::Matrix<TYPE,DIMS,1> POINT;
typedef SEACAVE::TRay<TYPE,DIMS> RAY;
typedef SEACAVE::TSphere<TYPE,DIMS> SPHERE;
enum { numScalar = (2*DIMS+3) };
RAY ray; // ray origin is the cone vertex and ray direction is the cone axis direction
TYPE angle; // cone angle, in radians, must be inside [0, pi/2]
TYPE minHeight; // heights satisfying:
TYPE maxHeight; // 0 <= minHeight < maxHeight <= std::numeric_limits<TYPE>::max()
//---------------------------------------
inline TCone() {}
inline TCone(const RAY& _ray, TYPE _angle, TYPE _minHeight=TYPE(0), TYPE _maxHeight=std::numeric_limits<TYPE>::max())
: ray(_ray), angle(_angle), minHeight(_minHeight), maxHeight(_maxHeight) { ASSERT(TYPE(0) <= minHeight && minHeight < maxHeight); }
}; // class TCone
// Structure used to compute the intersection between a cone and the given sphere/point
template <typename TYPE, int DIMS>
class TConeIntersect
{
STATIC_ASSERT(DIMS > 1 && DIMS <= 3);
public:
typedef Eigen::Matrix<TYPE,DIMS,1> VECTOR;
typedef Eigen::Matrix<TYPE,DIMS,1> POINT;
typedef SEACAVE::TCone<TYPE,DIMS> CONE;
typedef SEACAVE::TSphere<TYPE,DIMS> SPHERE;
const CONE& cone;
// cache angle derivatives, to avoid calling trigonometric functions in geometric queries
const TYPE cosAngle, sinAngle, sinAngleSq, cosAngleSq, invSinAngle;
//---------------------------------------
inline TConeIntersect(const CONE& _cone)
: cone(_cone), cosAngle(COS(cone.angle)), sinAngle(SIN(cone.angle)),
sinAngleSq(SQUARE(sinAngle)), cosAngleSq(SQUARE(cosAngle)),
invSinAngle(TYPE(1)/sinAngle) {}
bool operator()(const SPHERE&) const;
GCLASS Classify(const POINT&, TYPE& t) const;
}; // class TConeIntersect
/*----------------------------------------------------------------*/
#include "Ray.inl"
/*----------------------------------------------------------------*/
} // namespace SEACAVE
#endif // __SEACAVE_RAY_H__

951
libs/Common/Ray.inl

@ -0,0 +1,951 @@ @@ -0,0 +1,951 @@
////////////////////////////////////////////////////////////////////
// Ray.inl
//
// Copyright 2007 cDc@seacave
// Distributed under the Boost Software License, Version 1.0
// (See http://www.boost.org/LICENSE_1_0.txt)
// D E F I N E S ///////////////////////////////////////////////////
// S T R U C T S ///////////////////////////////////////////////////
template <typename TYPE, int DIMS>
inline TTriangle<TYPE,DIMS>::TTriangle(const POINT& p0, const POINT& p1, const POINT& p2)
:
a(p0), b(p1), c(p2)
{
} // constructors
/*----------------------------------------------------------------*/
// set attributes
template <typename TYPE, int DIMS>
inline void TTriangle<TYPE,DIMS>::Set(const POINT& p0, const POINT& p1, const POINT& p2)
{
a = p0;
b = p1;
c = p2;
}
/*----------------------------------------------------------------*/
// get center
template <typename TYPE, int DIMS>
inline typename TTriangle<TYPE,DIMS>::POINT TTriangle<TYPE,DIMS>::GetCenter() const
{
return (a + b + c) / TYPE(3);
}
// get AABB
template <typename TYPE, int DIMS>
inline typename TTriangle<TYPE,DIMS>::AABB TTriangle<TYPE,DIMS>::GetAABB() const
{
return AABB(& operator [] (0), 3);
}
// get plane
template <typename TYPE, int DIMS>
inline typename TTriangle<TYPE,DIMS>::PLANE TTriangle<TYPE,DIMS>::GetPlane() const
{
return PLANE(a, b, c);
}
/*----------------------------------------------------------------*/
// S T R U C T S ///////////////////////////////////////////////////
template <typename TYPE, int DIMS>
inline TRay<TYPE,DIMS>::TRay(const POINT& pOrig, const VECTOR& vDir)
:
m_vDir(vDir), m_pOrig(pOrig)
{
ASSERT(ISEQUAL(m_vDir.squaredNorm(), TYPE(1)));
} // constructors
template <typename TYPE, int DIMS>
inline TRay<TYPE,DIMS>::TRay(const POINT& pt0, const POINT& pt1, bool /*bPoints*/)
:
m_vDir((pt1-pt0).normalized()), m_pOrig(pt0)
{
} // constructors
/*----------------------------------------------------------------*/
// set attributes
template <typename TYPE, int DIMS>
inline void TRay<TYPE,DIMS>::Set(const POINT& pOrig, const VECTOR& vDir)
{
m_vDir = vDir;
m_pOrig = pOrig;
ASSERT(ISEQUAL(m_vDir.squaredNorm(), TYPE(1)));
}
template <typename TYPE, int DIMS>
inline void TRay<TYPE,DIMS>::SetFromPoints(const POINT& pt0, const POINT& pt1)
{
m_vDir = (pt1-pt0).normalized();
m_pOrig = pt0;
}
template <typename TYPE, int DIMS>
inline TYPE TRay<TYPE,DIMS>::SetFromPointsLen(const POINT& pt0, const POINT& pt1)
{
m_vDir = pt1-pt0;
const TYPE len = m_vDir.norm();
if (len > TYPE(0))
m_vDir *= TYPE(1)/len;
m_pOrig = pt0;
return len;
}
/*----------------------------------------------------------------*/
// transform ray into matrix space
template <typename TYPE, int DIMS>
inline void TRay<TYPE,DIMS>::DeTransform(const MATRIX& _m)
{
STATIC_ASSERT(DIMS == 3);
MATRIX m(_m);
// invert translation
typedef Eigen::Matrix<TYPE,4,1> VECTOR4;
const VECTOR4 vcOrig(m_pOrig[0]-m(3,0), m_pOrig[1]-m(3,1), m_pOrig[2]-m(3,2));
// delete it from matrix
m(3,0) = m(3,1) = m(3,2) = TYPE(0);
// invert matrix and apply to ray
const MATRIX mInv = m.inverse();
m_pOrig = vcOrig * mInv;
m_vDir = VECTOR4(m_vDir) * mInv;
}
/*----------------------------------------------------------------*/
// Transform the ray by a matrix.
template <typename TYPE, int DIMS>
TRay<TYPE,DIMS> TRay<TYPE,DIMS>::operator*(const MATRIX& m) const
{
TRay ray;
ray.SetFromPoints(m_pOrig*m, (m_pOrig+m_vDir)*m);
return ray;
} // operator *
template <typename TYPE, int DIMS>
inline TRay<TYPE,DIMS>& TRay<TYPE,DIMS>::operator*=(const MATRIX& m)
{
*this = operator * (m);
return *this;
} // operator *=
/*----------------------------------------------------------------*/
// test for intersection with triangle
template <typename TYPE, int DIMS>
template <bool bCull>
bool TRay<TYPE,DIMS>::Intersects(const TRIANGLE& tri, TYPE *pt) const
{
const VECTOR edge1(tri.b - tri.a);
const VECTOR edge2(tri.c - tri.a);
const VECTOR pvec(m_vDir.cross(edge2));
const TYPE det(edge1.dot(pvec));
// check if ray and triangle plane are parallel
if ((bCull ? det : ABS(det)) < ZEROTOLERANCE<TYPE>() * TYPE(0.01))
return false;
const TYPE invDet(TYPE(1) / det);
const VECTOR tvec(m_pOrig - tri.a);
const TYPE u(tvec.dot(pvec) * invDet);
// check if intersection is outside of the line segment defined by points a and c
if (u < -ZEROTOLERANCE<TYPE>() * TYPE(10))
return false;
const VECTOR qvec(tvec.cross(edge1));
const TYPE v(m_vDir.dot(qvec) * invDet);
// check if intersection is outside of the line segment defined by points a and b
if (v < -ZEROTOLERANCE<TYPE>() * TYPE(10))
return false;
// check if intersection is outside of the line segment defined by points b and c
if (u + v > TYPE(1) + ZEROTOLERANCE<TYPE>() * TYPE(10))
return false;
const TYPE t(edge2.dot(qvec) * invDet);
// check if intersection is behind the ray origin
if (bCull && t < -ZEROTOLERANCE<TYPE>())
return false;
if (pt)
*pt = t;
return true;
} // Intersects(Tri)
/*----------------------------------------------------------------*/
// test for intersection with triangle at certain length (line segment);
// same as above, but test distance to intersection vs segment length
template <typename TYPE, int DIMS>
template <bool bCull>
bool TRay<TYPE,DIMS>::Intersects(const TRIANGLE& tri, TYPE fL, TYPE *t) const
{
TYPE _t;
TYPE* const pt(t ? t : &_t);
Intersects(tri, pt);
// collision but not on segment?
return *pt <= fL;
} // Intersects(Tri at length)
/*----------------------------------------------------------------*/
// test if the ray intersects the given sphere
template <typename TYPE, int DIMS>
bool TRay<TYPE,DIMS>::Intersects(const SPHERE& sphere) const
{
TYPE dSq;
if (!DistanceSq(sphere.center, dSq))
return false;
return dSq <= SQUARE(sphere.radius);
}
// same as above, but returns also the distance on the ray corresponding to the closest point
template <typename TYPE, int DIMS>
bool TRay<TYPE,DIMS>::Intersects(const SPHERE& sphere, TYPE& t) const
{
const VECTOR a(sphere.center - m_pOrig);
t = a.dot(m_vDir);
// point behind the ray origin
if (t < TYPE(0))
return false;
const TYPE dSq((a - m_vDir*t).squaredNorm());
return dSq <= SQUARE(sphere.radius);
} // Intersects(Sphere)
/*----------------------------------------------------------------*/
template <typename TYPE, int DIMS>
bool TRay<TYPE,DIMS>::Intersects(const AABB &aabb) const
{
bool to_infinity = true;
TYPE _min, _max;
// first on x value
if (m_vDir[0] == TYPE(0)) {
if (m_pOrig[0] < aabb.ptMin[0] || m_pOrig[0] > aabb.ptMax[0])
return false; // NO_INTERSECTION
} else {
if (m_vDir[0] > TYPE(0)) {
_min = (aabb.ptMin[0]-m_pOrig[0])/m_vDir[0];
_max = (aabb.ptMax[0]-m_pOrig[0])/m_vDir[0];
} else {
_min = (aabb.ptMax[0]-m_pOrig[0])/m_vDir[0];
_max = (aabb.ptMin[0]-m_pOrig[0])/m_vDir[0];
}
to_infinity = false;
}
// now on y value
if (m_vDir[1] == TYPE(0)) {
if (m_pOrig[1] < aabb.ptMin[1] || m_pOrig[1] > aabb.ptMax[1])
return false; // NO_INTERSECTION
} else {
TYPE newmin, newmax;
if (m_vDir[1] > TYPE(0)) {
newmin = (aabb.ptMin[1]-m_pOrig[1])/m_vDir[1];
newmax = (aabb.ptMax[1]-m_pOrig[1])/m_vDir[1];
} else {
newmin = (aabb.ptMax[1]-m_pOrig[1])/m_vDir[1];
newmax = (aabb.ptMin[1]-m_pOrig[1])/m_vDir[1];
}
#if 0
if (to_infinity) {
_min = newmin;
_max = newmax;
} else {
#else
if (to_infinity)
return true;
{
#endif
if (newmin > _min)
_min = newmin;
if (newmax < _max)
_max = newmax;
if (_max < _min)
return false; // NO_INTERSECTION
}
to_infinity = false;
}
// now on z value
if (DIMS == 3) {
if (m_vDir[2] == TYPE(0)) {
if (m_pOrig[2] < aabb.ptMin[2] || m_pOrig[2] > aabb.ptMax[2])
return false; // NO_INTERSECTION
} else {
TYPE newmin, newmax;
if (m_vDir[2] > TYPE(0)) {
newmin = (aabb.ptMin[2]-m_pOrig[2])/m_vDir[2];
newmax = (aabb.ptMax[2]-m_pOrig[2])/m_vDir[2];
} else {
newmin = (aabb.ptMax[2]-m_pOrig[2])/m_vDir[2];
newmax = (aabb.ptMin[2]-m_pOrig[2])/m_vDir[2];
}
#if 0
if (to_infinity) {
_min = newmin;
_max = newmax;
} else {
#else
if (to_infinity)
return true;
{
#endif
if (newmin > _min)
_min = newmin;
if (newmax < _max)
_max = newmax;
if (_max < _min)
return false; // NO_INTERSECTION
}
to_infinity = false;
}
}
ASSERT(!to_infinity);
#if 0
if (_max < _min)
return true; // POINT_INTERSECTION
#endif
return true; // SEGMENT_INTERSECTION
} // Intersects(AABB)
// test for intersection with aabb, original code by Andrew Woo,
// from "Geometric Tools...", Morgan Kaufmann Publ., 2002
template <typename TYPE, int DIMS>
bool TRay<TYPE,DIMS>::Intersects(const AABB &aabb, TYPE& t) const
{
TYPE t0, t1, tmp;
TYPE tNear(-999999);
TYPE tFar ( 999999);
// first pair of planes
if (ISZERO(m_vDir[0])) {
if ((m_pOrig[0] < aabb.ptMin[0]) ||
(m_pOrig[0] > aabb.ptMax[0]))
return false;
}
t0 = (aabb.ptMin[0] - m_pOrig[0]) / m_vDir[0];
t1 = (aabb.ptMax[0] - m_pOrig[0]) / m_vDir[0];
if (t0 > t1) { tmp=t0; t0=t1; t1=tmp; }
if (t0 > tNear) tNear = t0;
if (t1 < tFar) tFar = t1;
if (tNear > tFar) return false;
if (tFar < TYPE(0)) return false;
// second pair of planes
if (ISZERO(m_vDir[1])) {
if ((m_pOrig[1] < aabb.ptMin[1]) ||
(m_pOrig[1] > aabb.ptMax[1]) )
return false;
}
t0 = (aabb.ptMin[1] - m_pOrig[1]) / m_vDir[1];
t1 = (aabb.ptMax[1] - m_pOrig[1]) / m_vDir[1];
if (t0 > t1) { tmp=t0; t0=t1; t1=tmp; }
if (t0 > tNear) tNear = t0;
if (t1 < tFar) tFar = t1;
if (tNear > tFar) return false;
if (tFar < TYPE(0)) return false;
if (DIMS == 3) {
// third pair of planes
if (ISZERO(m_vDir[2])) {
if ((m_pOrig[2] < aabb.ptMin[2]) ||
(m_pOrig[2] > aabb.ptMax[2]) )
return false;
}
t0 = (aabb.ptMin[2] - m_pOrig[2]) / m_vDir[2];
t1 = (aabb.ptMax[2] - m_pOrig[2]) / m_vDir[2];
if (t0 > t1) { tmp=t0; t0=t1; t1=tmp; }
if (t0 > tNear) tNear = t0;
if (t1 < tFar) tFar = t1;
if (tNear > tFar) return false;
if (tFar < TYPE(0)) return false;
}
t = (tNear > TYPE(0) ? tNear : tFar);
return true;
} // Intersects(AABB)
// test for intersection with aabb, original code by Andrew Woo,
// from "Geometric Tools...", Morgan Kaufmann Publ., 2002
template <typename TYPE, int DIMS>
bool TRay<TYPE,DIMS>::Intersects(const AABB &aabb, TYPE fL, TYPE *t) const
{
TYPE t0, t1, tmp, tFinal;
TYPE tNear(-999999);
TYPE tFar ( 999999);
// first pair of planes
if (ISZERO(m_vDir[0])) {
if ((m_pOrig[0] < aabb.ptMin[0]) ||
(m_pOrig[0] > aabb.ptMax[0]) )
return false;
}
t0 = (aabb.ptMin[0] - m_pOrig[0]) / m_vDir[0];
t1 = (aabb.ptMax[0] - m_pOrig[0]) / m_vDir[0];
if (t0 > t1) { tmp=t0; t0=t1; t1=tmp; }
if (t0 > tNear) tNear = t0;
if (t1 < tFar) tFar = t1;
if (tNear > tFar) return false;
if (tFar < TYPE(0)) return false;
// second pair of planes
if (ISZERO(m_vDir[1])) {
if ((m_pOrig[1] < aabb.ptMin[1]) ||
(m_pOrig[1] > aabb.ptMax[1]) )
return false;
}
t0 = (aabb.ptMin[1] - m_pOrig[1]) / m_vDir[1];
t1 = (aabb.ptMax[1] - m_pOrig[1]) / m_vDir[1];
if (t0 > t1) { tmp=t0; t0=t1; t1=tmp; }
if (t0 > tNear) tNear = t0;
if (t1 < tFar) tFar = t1;
if (tNear > tFar) return false;
if (tFar < TYPE(0)) return false;
if (DIMS == 3) {
// third pair of planes
if (ISZERO(m_vDir[2])) {
if ((m_pOrig[2] < aabb.ptMin[2]) ||
(m_pOrig[2] > aabb.ptMax[2]) )
return false;
}
t0 = (aabb.ptMin[2] - m_pOrig[2]) / m_vDir[2];
t1 = (aabb.ptMax[2] - m_pOrig[2]) / m_vDir[2];
if (t0 > t1) { tmp=t0; t0=t1; t1=tmp; }
if (t0 > tNear) tNear = t0;
if (t1 < tFar) tFar = t1;
if (tNear > tFar) return false;
if (tFar < 0) return false;
}
tFinal = (tNear > TYPE(0) ? tNear : tFar);
if (tFinal > fL) return false;
if (t) *t = tFinal;
return true;
} // Intersects(AABB) at length
/*----------------------------------------------------------------*/
template <typename TYPE, int DIMS>
bool TRay<TYPE,DIMS>::Intersects(const OBB& obb) const
{
TYPE t;
return Intersects(obb, t);
} // Intersects(OBB)
// test for intersection with obb, slaps method
template <typename TYPE, int DIMS>
bool TRay<TYPE,DIMS>::Intersects(const OBB& obb, TYPE& t) const
{
TYPE e, f, t1, t2, temp;
TYPE tmin(-999999);
TYPE tmax( 999999);
const POINT vP = obb.m_pos - m_pOrig;
// 1st slap
e = obb.m_rot.row(0) * vP;
f = obb.m_rot.row(0) * m_vDir;
if (!ISZERO(f)) {
t1 = (e + obb.m_ext[0]) / f;
t2 = (e - obb.m_ext[0]) / f;
if (t1 > t2) { temp=t1; t1=t2; t2=temp; }
if (t1 > tmin) tmin = t1;
if (t2 < tmax) tmax = t2;
if (tmin > tmax) return false;
if (tmax < 0.0f) return false;
} else
if (((-e - obb.m_ext[0]) > 0.0f) || ((-e + obb.m_ext[0]) < 0.0f))
return false;
// 2nd slap
e = obb.m_rot.row(1) * vP;
f = obb.m_rot.row(1) * m_vDir;
if (!ISZERO(f)) {
t1 = (e + obb.m_ext[1]) / f;
t2 = (e - obb.m_ext[1]) / f;
if (t1 > t2) { temp=t1; t1=t2; t2=temp; }
if (t1 > tmin) tmin = t1;
if (t2 < tmax) tmax = t2;
if (tmin > tmax) return false;
if (tmax < 0.0f) return false;
} else
if (((-e - obb.m_ext[1]) > 0.0f) || ((-e + obb.m_ext[1]) < 0.0f))
return false;
// 3rd slap
e = obb.m_rot.row(2) * vP;
f = obb.m_rot.row(2) * m_vDir;
if (!ISZERO(f)) {
t1 = (e + obb.m_ext[2]) / f;
t2 = (e - obb.m_ext[2]) / f;
if (t1 > t2) { temp=t1; t1=t2; t2=temp; }
if (t1 > tmin) tmin = t1;
if (t2 < tmax) tmax = t2;
if (tmin > tmax) return false;
if (tmax < 0.0f) return false;
} else
if (((-e - obb.m_ext[2]) > 0) || ((-e + obb.m_ext[2]) < 0))
return false;
if (tmin > 0) {
if (t) *t = tmin;
return true;
}
t = tmax;
return true;
} // Intersects(AABB)
// test for intersection with obb at certain length (line segment),
// slaps method but compare result if true to length prior return.
template <typename TYPE, int DIMS>
bool TRay<TYPE,DIMS>::Intersects(const OBB& obb, TYPE fL, TYPE *t) const
{
TYPE e, f, t1, t2, temp;
TYPE tmin(-999999);
TYPE tmax( 999999);
const POINT vP = obb.m_pos - m_pOrig;
// 1st slap
e = obb.m_rot.row(0) * vP;
f = obb.m_rot.row(0) * m_vDir;
if (!ISZERO(f)) {
t1 = (e + obb.m_ext[0]) / f;
t2 = (e - obb.m_ext[0]) / f;
if (t1 > t2) { temp=t1; t1=t2; t2=temp; }
if (t1 > tmin) tmin = t1;
if (t2 < tmax) tmax = t2;
if (tmin > tmax) return false;
if (tmax < 0.0f) return false;
} else
if (((-e - obb.m_ext[0]) > 0) || ((-e + obb.m_ext[0]) < 0))
return false;
// 2nd slap
e = obb.m_rot.row(1) * vP;
f = obb.m_rot.row(1) * m_vDir;
if (!ISZERO(f)) {
t1 = (e + obb.m_ext[1]) / f;
t2 = (e - obb.m_ext[1]) / f;
if (t1 > t2) { temp=t1; t1=t2; t2=temp; }
if (t1 > tmin) tmin = t1;
if (t2 < tmax) tmax = t2;
if (tmin > tmax) return false;
if (tmax < 0.0f) return false;
} else
if (((-e - obb.m_ext[1]) > 0) || ((-e + obb.m_ext[1]) < 0))
return false;
// 3rd slap
e = obb.m_rot.row(2) * vP;
f = obb.m_rot.row(2) * m_vDir;
if (!ISZERO(f)) {
t1 = (e + obb.m_ext[2]) / f;
t2 = (e - obb.m_ext[2]) / f;
if (t1 > t2) { temp=t1; t1=t2; t2=temp; }
if (t1 > tmin) tmin = t1;
if (t2 < tmax) tmax = t2;
if (tmin > tmax) return false;
if (tmax < 0.0f) return false;
} else
if (((-e - obb.m_ext[2]) > 0) || ((-e + obb.m_ext[2]) < 0))
return false;
if ((tmin > 0) && (tmin <= fL)) {
if (t) *t = tmin;
return true;
}
// intersection on line but not on segment
if (tmax > fL) return false;
if (t) *t = tmax;
return true;
} // Intersects(AABB) at length
/*----------------------------------------------------------------*/
// Intersection with PLANE from origin till infinity.
template <typename TYPE, int DIMS>
bool TRay<TYPE,DIMS>::Intersects(const PLANE& plane, bool bCull, TYPE *t, POINT* pPtHit) const
{
const TYPE Vd(plane.m_vN.dot(m_vDir));
// ray parallel to plane
if (ISZERO(Vd))
return false;
// normal pointing away from ray dir
// => intersection backface if any
if (bCull && (Vd > TYPE(0)))
return false;
const TYPE Vo(-plane.Distance(m_pOrig));
const TYPE _t(Vo / Vd);
// intersection behind ray origin
if (_t < TYPE(0))
return false;
if (pPtHit)
(*pPtHit) = m_pOrig + (m_vDir * _t);
if (t)
(*t) = _t;
return true;
} // Intersects(PLANE)
// same as above, but no checks
template <typename TYPE, int DIMS>
inline TYPE TRay<TYPE,DIMS>::IntersectsDist(const PLANE& plane) const
{
const TYPE Vd(plane.m_vN.dot(m_vDir));
const TYPE Vo(-plane.Distance(m_pOrig));
return SAFEDIVIDE(Vo, Vd);
} // IntersectsDist(PLANE)
template <typename TYPE, int DIMS>
inline typename TRay<TYPE,DIMS>::POINT TRay<TYPE,DIMS>::Intersects(const PLANE& plane) const
{
return m_pOrig + (m_vDir * IntersectsDist(plane));
} // Intersects(PLANE)
/*----------------------------------------------------------------*/
// Intersection with PLANE at distance fL.
template <typename TYPE, int DIMS>
bool TRay<TYPE,DIMS>::Intersects(const PLANE& plane, bool bCull, TYPE fL, TYPE *t, POINT* pPtHit) const
{
const TYPE Vd = plane.m_vN.dot(m_vDir);
// ray parallel to plane
if (ISZERO(Vd))
return false;
// normal pointing away from ray dir
// => intersection backface if any
if (bCull && (Vd > TYPE(0)))
return false;
const TYPE Vo(-plane.Distance(m_pOrig));
const TYPE _t(Vo / Vd);
// intersection behind ray origin or beyond valid range
if ((_t < TYPE(0)) || (_t > fL))
return false;
if (pPtHit)
(*pPtHit) = m_pOrig + (m_vDir * _t);
if (t)
(*t) = _t;
return true;
} // Intersects(PLANE)
/*----------------------------------------------------------------*/
// Intersection with another ray.
// Rays assumed to be coplanar; returns only the distance from the origin of the first ray.
// P = R.origin + s * R.dir
// http://mathworld.wolfram.com/Line-LineIntersection.html
// (works also for 2D lines if z components is set to 0)
template <typename TYPE, int DIMS>
bool TRay<TYPE,DIMS>::Intersects(const TRay& ray, TYPE& s) const
{
const POINT dir(ray.m_pOrig - m_pOrig);
const POINT n(m_vDir.cross(ray.m_vDir));
if (!ISZERO(dir.dot(n)))
return false; // lines are not coplanar
s = dir.cross(ray.m_vDir).dot(n) / n.squaredNorm();
return true;
} // Intersects(Ray)
// Same as above, but returns the actual intersection point:
template <typename TYPE, int DIMS>
bool TRay<TYPE,DIMS>::Intersects(const TRay& ray, POINT& p) const
{
TYPE s;
if (!Intersects(ray, s))
return false; // lines are not coplanar
if (s < TYPE(0))
return false; // intersection behind ray origin
p = m_pOrig + m_vDir * s;
return true;
} // Intersects(Ray)
// No coplanarity assumption; returns the shortest line segment connecting the two rays:
// P1 = R1.origin + s1 * R1.dir
// P2 = R2.origin + s2 * R2.dir
// http://paulbourke.net/geometry/pointlineplane/
template <typename TYPE, int DIMS>
bool TRay<TYPE,DIMS>::Intersects(const TRay& ray, TYPE& s1, TYPE& s2) const
{
const TYPE d4321(ray.m_vDir.dot(m_vDir));
const TYPE d4343(ray.m_vDir.dot(ray.m_vDir));
const TYPE d2121(m_vDir.dot(m_vDir));
const TYPE denom = d2121 * d4343 - d4321 * d4321;
if (ISZERO(denom))
return false; // lines are parallel
const POINT dir(m_pOrig - ray.m_pOrig);
const TYPE d1321(dir.dot(m_vDir));
const TYPE d1343(dir.dot(ray.m_vDir));
const TYPE numer = d1343 * d4321 - d1321 * d4343;
s1 = numer / denom;
s2 = (d1343 + d4321 * s1) / d4343;
return true;
} // Intersects(Ray)
// Same as above, but returns only middle point of the shortest line segment:
// P = (P1 + P2) / 2
template <typename TYPE, int DIMS>
bool TRay<TYPE,DIMS>::IntersectsAprox(const TRay& ray, POINT& p) const
{
TYPE s1, s2;
if (!Intersects(ray, s1, s2))
return false;
const POINT P1 = m_pOrig + m_vDir * s1;
const POINT P2 = ray.m_pOrig + ray.m_vDir * s2;
p = (P1+P2)*TYPE(0.5);
return true;
} // Intersects(Ray)
// Same as above, but returns the point on the given ray.
// Returns false if intersection not in front of main ray.
template <typename TYPE, int DIMS>
bool TRay<TYPE,DIMS>::IntersectsAprox2(const TRay& ray, POINT& p) const
{
TYPE s1, s2;
if (!Intersects(ray, s1, s2))
return false;
if (s1 < TYPE(0))
return false; // intersection behind main ray origin
p = ray.m_pOrig + ray.m_vDir * s2;
return true;
} // Intersects(Ray)
/*----------------------------------------------------------------*/
// computes the angle between the two lines
template <typename TYPE, int DIMS>
inline TYPE TRay<TYPE,DIMS>::CosAngle(const TRay& ray) const
{
ASSERT(ISEQUAL(m_vDir.norm(), TYPE(1)));
ASSERT(ISEQUAL(ray.m_vDir.norm(), TYPE(1)));
return m_vDir.dot(ray.m_vDir);
}
// tests if the two lines are coplanar (intersect)
template <typename TYPE, int DIMS>
inline bool TRay<TYPE,DIMS>::Coplanar(const TRay& ray) const
{
const POINT dir(ray.m_pOrig - m_pOrig);
const POINT n(m_vDir.cross(ray.m_vDir));
return ISZERO(dir.dot(n));
}
// tests if the two lines are parallel
template <typename TYPE, int DIMS>
inline bool TRay<TYPE,DIMS>::Parallel(const TRay& ray) const
{
return ISZERO(m_vDir.cross(ray.m_vDir).norm());
}
/*----------------------------------------------------------------*/
// Computes the position on the ray of the point projection.
// Returns 0 if it coincides with the ray origin, positive value if in front, and negative if behind.
template <typename TYPE, int DIMS>
inline TYPE TRay<TYPE,DIMS>::Classify(const POINT& pt) const
{
ASSERT(ISEQUAL(m_vDir.norm(), TYPE(1)));
const VECTOR a(pt - m_pOrig);
return a.dot(m_vDir);
} // Classify(POINT)
template <typename TYPE, int DIMS>
inline typename TRay<TYPE,DIMS>::POINT TRay<TYPE,DIMS>::ProjectPoint(const POINT& pt) const
{
ASSERT(ISEQUAL(m_vDir.norm(), TYPE(1)));
return m_pOrig + m_vDir*Classify(pt);
} // ProjectPoint
/*----------------------------------------------------------------*/
// Computes the distance between the ray and a point.
// Returns false if the point is projecting behind the ray origin.
template <typename TYPE, int DIMS>
bool TRay<TYPE,DIMS>::DistanceSq(const POINT& pt, TYPE& d) const
{
const VECTOR a(pt - m_pOrig);
const TYPE LenACos(a.dot(m_vDir));
// point behind the ray origin
if (LenACos < TYPE(0))
return false;
d = (a - m_vDir*LenACos).squaredNorm();
return true;
} // DistanceSq(POINT)
template <typename TYPE, int DIMS>
bool TRay<TYPE,DIMS>::Distance(const POINT& pt, TYPE& d) const
{
const VECTOR a(pt - m_pOrig);
const TYPE LenACos(a.dot(m_vDir));
// point behind the ray origin
if (LenACos < TYPE(0))
return false;
d = (a - m_vDir*LenACos).norm();
return true;
} // Distance(POINT)
// Same as above, but returns the distance even if the point projection is behind the origin.
template <typename TYPE, int DIMS>
TYPE TRay<TYPE,DIMS>::DistanceSq(const POINT& pt) const
{
ASSERT(ISEQUAL(m_vDir.norm(), TYPE(1)));
const VECTOR a(pt - m_pOrig);
const VECTOR b(a - m_vDir);
return b.cross(a).squaredNorm();
} // DistanceSq(POINT)
template <typename TYPE, int DIMS>
TYPE TRay<TYPE,DIMS>::Distance(const POINT& pt) const
{
return SQRT(DistanceSq(pt));
} // Distance(POINT)
// Get the point on the ray at distance t from origin
template <typename TYPE, int DIMS>
typename TRay<TYPE, DIMS>::POINT TRay<TYPE,DIMS>::GetPoint(TYPE t) const
{
return m_pOrig + m_vDir * t;
} // GetPoint
/*----------------------------------------------------------------*/
// S T R U C T S ///////////////////////////////////////////////////
template <typename TYPE, int DIMS>
bool TCylinder<TYPE,DIMS>::Intersects(const SPHERE& sphere) const
{
struct Check {
static bool Intersection(const RAY& ray, const SPHERE& sphere, const VECTOR& CmV, TYPE t, TYPE radius, TYPE height) {
const POINT O(ray.m_pOrig + ray.m_vDir * height);
const VECTOR a(sphere.center - O);
if (a.squaredNorm() <= SQUARE(sphere.radius))
return true; // ray origin inside the sphere
const POINT D((CmV - ray.m_vDir * t).normalized());
const TYPE d = a.dot(D);
if (d < TYPE(0))
return false; // intersection behind the ray origin
const TYPE dSq((a - D*d).squaredNorm());
const TYPE srSq = SQUARE(sphere.radius);
if (dSq <= srSq) {
const TYPE r = d - SQRT(srSq-dSq);
ASSERT(r >= TYPE(0));
return r <= radius; // intersection before the ray end
}
return false;
}
};
const VECTOR CmV(sphere.center - ray.m_pOrig);
const TYPE t(ray.m_vDir.dot(CmV));
// sphere projects behind the cylinder origin
if (t < minHeight) {
if (t+sphere.radius < minHeight)
return false;
return Check::Intersection(ray, sphere, CmV, t, radius, minHeight);
}
// sphere projects after the cylinder end
if (t > maxHeight) {
if (t-sphere.radius > maxHeight)
return false;
return Check::Intersection(ray, sphere, CmV, t, radius, maxHeight);
}
const TYPE lenSq((CmV - ray.m_vDir * t).squaredNorm());
return lenSq <= SQUARE(sphere.radius + radius);
} // Intersects
/*----------------------------------------------------------------*/
// Classify point to cylinder.
template <typename TYPE, int DIMS>
GCLASS TCylinder<TYPE,DIMS>::Classify(const POINT& p, TYPE& t) const
{
ASSERT(ISEQUAL(ray.m_vDir.norm(), TYPE(1)));
const VECTOR D(p - ray.m_pOrig);
t = ray.m_vDir.dot(D);
if (t < minHeight) return BACK;
if (t > maxHeight) return FRONT;
const TYPE rSq((D - ray.m_vDir*t).squaredNorm());
const TYPE radiusSq(SQUARE(radius));
if (rSq > radiusSq) return CULLED;
if (rSq < radiusSq) return VISIBLE;
return PLANAR;
} // Classify
/*----------------------------------------------------------------*/
// S T R U C T S ///////////////////////////////////////////////////
template <typename TYPE, int DIMS>
bool TConeIntersect<TYPE,DIMS>::operator()(const SPHERE& sphere) const
{
const VECTOR CmV(sphere.center - cone.ray.m_pOrig);
const VECTOR D(CmV + cone.ray.m_vDir * (sphere.radius*invSinAngle));
TYPE e = D.dot(cone.ray.m_vDir);
if (e <= TYPE(0) || e*e < D.squaredNorm()*cosAngleSq)
return false;
e = CmV.dot(cone.ray.m_vDir);
if (e-sphere.radius > cone.maxHeight)
return false;
if (e < cone.minHeight) {
const TYPE lenSq = CmV.squaredNorm();
if (e*e >= lenSq*sinAngleSq)
return lenSq <= SQUARE(sphere.radius);
}
return true;
} // Intersect
/*----------------------------------------------------------------*/
// Classify point to cone.
template <typename TYPE, int DIMS>
GCLASS TConeIntersect<TYPE,DIMS>::Classify(const POINT& p, TYPE& t) const
{
ASSERT(ISEQUAL(cone.ray.m_vDir.norm(), TYPE(1)));
const VECTOR D(p - cone.ray.m_pOrig);
t = cone.ray.m_vDir.dot(D);
if (ISZERO(t))
return PLANAR;
if (t < cone.minHeight) return BACK;
if (t > cone.maxHeight) return FRONT;
ASSERT(!ISZERO(D.norm()));
const TYPE tSq(SQUARE(t));
const TYPE dSq(cosAngleSq*D.squaredNorm());
if (tSq < dSq) return CULLED;
if (tSq > dSq) return VISIBLE;
return PLANAR;
} // Classify
/*----------------------------------------------------------------*/
template <typename TYPE>
bool TestRayTriangleIntersection(unsigned iters) {
typedef SEACAVE::TTriangle<TYPE,3> Triangle;
typedef SEACAVE::TRay<TYPE,3> Ray;
typedef typename Ray::POINT Point;
typedef typename Point::Scalar Type;
constexpr Type zeroEps(std::is_same<TYPE,float>::value ? 0.01f : 0.00001f);
for (unsigned iter=0, lives=iters/100; iter<iters; ++iter) {
const Type scale(10);
const Triangle triangle(Point::Random()*scale, Point::Random()*scale, Point::Random()*scale);
Type t;
const Point center(triangle.GetCenter());
const Ray rayCenter(Point::Random()*scale, center, true);
if (!rayCenter.template Intersects<false>(triangle, &t) && !lives--)
return false;
const Point _center(rayCenter.GetPoint(t));
if ((_center-center).norm() > zeroEps && !lives--)
return false;
const BYTE o((BYTE)(RAND()%3));
const Point side(((triangle[o].template cast<double>()+triangle[(o+1)%3].template cast<double>()) / 2.0).template cast<TYPE>());
const Ray raySide(rayCenter.m_pOrig, side, true);
if (!raySide.template Intersects<false>(triangle, &t) && !lives--)
return false;
const Point _side(raySide.GetPoint(t));
if ((_side-side).norm() > zeroEps && !lives--)
return false;
}
return true;
}

595
libs/Common/Rotation.h

@ -0,0 +1,595 @@ @@ -0,0 +1,595 @@
////////////////////////////////////////////////////////////////////
// Rotation.h
//
// Copyright 2007 cDc@seacave
// Distributed under the Boost Software License, Version 1.0
// (See http://www.boost.org/LICENSE_1_0.txt)
#ifndef __SEACAVE_ROTATION_H__
#define __SEACAVE_ROTATION_H__
// I N C L U D E S /////////////////////////////////////////////////
// D E F I N E S ///////////////////////////////////////////////////
#ifndef _RELEASE
#define _CPC_DEBUG
#endif
#define CPC_ERROR(msg) { SLOG(msg); ASSERT(false); }
#if TD_VERBOSE != TD_VERBOSE_OFF
#define CPC_SLOG(msg) if (VERBOSITY_LEVEL > 0) SLOG(msg)
#define CPC_LOG(t, msg) CPC_SLOG(msg)
#else
#define CPC_SLOG(msg)
#define CPC_LOG(t, msg)
#endif
namespace SEACAVE {
// S T R U C T S ///////////////////////////////////////////////////
// generic matrix struct
#define QUATERNION_EPSILON 1E-10 // DBL_EPSILON, too small (dherzog)
// forward declaration to avoid mutual inclusion
template <typename TYPE> class TRMatrixBase;
/** @class TQuaternion
* @test tested with TestRotationConversion.cpp
@ingroup g_geometry
@brief class for rotation with axis and angle
Quaternions can be used for rotation around an axis through the
origin by spcecifing the normal direction vector of the axis and
an angle.
The rotation for positive angles is clockwise,
when looking in direction of the axis.
Contains qx=[0], qy=[1], qz=[2], qw=[3].
Not every quaternion describes a rotation.
Only quaternions of the form:
qx = x * sin(phi/2) , which is the imaginary part i
qy = y * sin(phi/2) , which is the imaginary part j
qz = z * sin(phi/2) , which is the imaginary part k
qw = cos(phi/2), , which is the real part
where (x, y, z) is the normalized direction vector of the axis
and phi is the angle of rtoation.
Some useful quaternions:
x y z w Description
0 0 0 1 Identity quaternion,
no rotation
1 0 0 0 180' turn around X axis
0 1 0 0 180' turn around Y axis
0 0 1 0 180' turn around Z axis
sqrt(0.5) 0 0 sqrt(0.5) 90' rotation around X axis
0 sqrt(0.5) 0 sqrt(0.5) 90' rotation around Y axis
0 0 sqrt(0.5) sqrt(0.5) 90' rotation around Z axis
-sqrt(0.5) 0 0 sqrt(0.5) -90' rotation around X axis
0 -sqrt(0.5) 0 sqrt(0.5) -90' rotation around Y axis
0 0 -sqrt(0.5) sqrt(0.5) -90' rotation around Z axis
@author grest 06/2003
*/
template <typename TYPE>
class TQuaternion : public TMatrix<TYPE,4,1>
{
public:
typedef TMatrix<TYPE,4,1> Base;
typedef TMatrix<TYPE,4,4> Mat;
typedef TMatrix<TYPE,3,1> Vec;
typedef TMatrix<TYPE,3,3> Rot;
using Base::val;
public:
static const TQuaternion IDENTITY;
public:
inline ~TQuaternion();
inline TQuaternion();
inline TQuaternion(const Base& q);
inline TQuaternion(TYPE i, TYPE j, TYPE k, TYPE r);
inline TQuaternion(const Rot& R);
inline void SetIdentity();
/**
* Sets all parts of quaternion in mathematical order,
* real part first, then 1.,2. and 3. imaginary part
*/
inline void SetQuaternion(TYPE real, TYPE i, TYPE j, TYPE k);
/** returns the Inverse rotation TQuaternion
*/
inline TQuaternion<TYPE> Inverse() const;
/** inverts this, by changing the rotation axis by *=-1
*/
inline void Invert();
/** makes TQuaternion-representation unique, ensuring vector lies
in upper (by real part) hemisphere. (inplace)
*/
inline void MakeUnique();
/** quaternion multiplication: this= this * quat
*/
inline void Mult(const TQuaternion<TYPE> &quat);
/** quaternion multiplication: res = this * arg
*/
inline void Mult(const TQuaternion<TYPE> &arg,
TQuaternion<TYPE> &res) const;
/** quaternion multiplication: this = quat * this
*/
inline void MultLeft(const TQuaternion<TYPE> &quat);
/** rotates the given Vector with the quaternion ( q v q* )
the resulting vector is given in res
@returns 0 in case of no error
@author Daniel Grest, June 2003
*/
inline int MultVec(const Vec &vec, Vec &res) const;
/* rets result from MultVec()
@author herzog 2005-07-15 */
inline Vec MultVec(const Vec &vec) const;
/** Computes this^(scale), which scales the angle from axis/angle-
representation with 'scale'. This is the same like inter-/extra-
polating between (0,0,0,1)-quaternion and this!
*/
TQuaternion<TYPE> Power(const TYPE & scale) const;
/** Linear interpolation between this and given quaternion.
@note Quaternions are assumed to be unit quaternions! */
TQuaternion<TYPE> InterpolateLinear(const TQuaternion<TYPE> &to, const TYPE & t) const;
/** Spherical interpolation between this and given quaternion.
@note Quaternions are assumed to be unit quaternions! */
TQuaternion<TYPE> Interpolate(const TQuaternion<TYPE> &to, const TYPE & t) const;
/** sets the quaternion with given rotation axis and angle (in rad)
@returns 0 in case of no error
@author Daniel Grest, June 2003
*/
inline void SetValueAsAxisRad(const Vec &axis, TYPE angle);
/** sets the quaternion with given rotation axis and angle (in rad)
@returns 0 in case of no error
@author Daniel Grest, June 2003
*/
inline void SetValueAsAxisRad(TYPE axisX, TYPE axisY, TYPE axisZ, TYPE angle);
/** Returns matrix which expresses (left) quaternion multiplication.
* A quaternions stored in an object can be understood as a 4-vector
* q =(ix iy iz r)^T.
* Left multiplying a quaternion q2 with a quaternion q1 can be
* expressed in terms of matrix multiplication as
* qRes = q1*q2 = M(q1)*q2.
* This method returns the matrix M(*this).
*/
Mat GetQuaternionMultMatrixLeft() const;
/** Returns matrix which expresses right quaternion multiplication.
* Right multiplying a quaternion q1 with quaternion q2 can be
* expressed as qRes = q1*q2 = N(q2)*q1.
* This method returns the matrix N(*this).
*/
Mat GetQuaternionMultMatrixRight() const;
/** Returns rotation axis. See GetRotationAxisAngle(). */
Vec GetRotationAxis() const;
/** Returns rotation angle notation in radians.
Note that the returned angle resides in the interval [0,PI)! */
TYPE GetRotationAngle() const;
/** Returns rotation in axis and angle notation (angle in radians).
Note that the returned angle resides in the interval [0,PI) and
the axis points into the according direction!
@author woelk 12 2002 */
int GetAxisAngle(Vec& axis, TYPE& angle) const;
/** Sets quaternion as concatenated rotations around
x,y,z-axis; this = q_x * q_y * q_z (BIAS-RMatrix conform)
@author herzog 2005-07-19 */
int SetXYZ(TYPE radX, TYPE radY, TYPE radZ);
/** Sets quaternion as concatenated rotations around
x,y,z-axis; this = q_z * q_y * q_x (BIAS-RMatrix conform)
@author herzog 2005-07-19 */
int SetZYX(TYPE radX, TYPE radY, TYPE radZ);
/** Scales quaternion to unit length, i.e. norm equals 1. */
inline void Normalize() {
const TYPE n = norm(*this);
if (ISZERO(n))
*this = IDENTITY;
else
*this *= TYPE(1) / n;
}
inline bool IsNormalized() const {
return ISEQUAL(norm(*this), TYPE(1));
}
/** assignment operator
@author grest 06 2003 */
TQuaternion<TYPE>& operator=(const TQuaternion<TYPE>& vec);
/** Enforces rigid coupling constraint for this and the other given unit
quaternion (i.e. equal rotation angle resp. scalar part of both).
Computes direct simple interpolation of both unit quaternion where
the scalar parts of the resulting unit quaternion are identical.
This solution can be used as initial guess for numerical refinement.
@author esquivel 02/2012 */
void EnforceRigidCouplingConstraint(TQuaternion<TYPE> &other);
/** Enforces rigid coupling constraint for all quaternion in given vector.
@author esquivel 02/2012 */
static void EnforceRigidCouplingConstraint(std::vector< TQuaternion<TYPE> > &quats);
}; // class
template <typename TYPE> const TQuaternion<TYPE> TQuaternion<TYPE>::IDENTITY(0,0,0,1);
/*----------------------------------------------------------------*/
typedef TQuaternion<float> QuaternionF;
typedef TQuaternion<double> QuaternionD;
/*----------------------------------------------------------------*/
// generic rotation matrix struct
#define ROTATION_MATRIX_EPSILON DBL_EPSILON
/** @class TRMatrixBase
@ingroup g_geometry
@brief 3D rotation matrix
@author frahm, woelk **/
template <typename TYPE>
class TRMatrixBase : public TMatrix<TYPE,3,3>
{
public:
typedef TMatrix<TYPE,3,3> Base;
typedef Base Mat;
typedef TMatrix<TYPE,3,1> Vec;
typedef TQuaternion<TYPE> Quat;
typedef typename Base::Base BaseBase;
using Base::val;
public:
/** @brief Constructor setting matrix to identity */
inline TRMatrixBase();
inline TRMatrixBase(const BaseBase& rhs) : Base(rhs) {}
template <typename T> inline TRMatrixBase(const cv::Matx<T,3,3>& rhs) : Base(rhs) {}
/** @brief Copy constructor from 3x3 matrix
@attention Orthonormality of matrix is enforced automatically! */
inline TRMatrixBase(const Mat& mat);
/** @brief Initialization from parametrized rotation (axis-angle) */
inline TRMatrixBase(const Vec& rot);
/** @brief Initialization from rotation axis w and angle phi (in rad) using Rodrigues' formula */
inline TRMatrixBase(const Vec& w, const TYPE phi);
/** @brief Initialization from quaternion */
inline TRMatrixBase(const Quat& q);
/** @brief Initialization with the rotation from roll/pitch/yaw (in rad) */
inline TRMatrixBase(TYPE roll, TYPE pitch, TYPE yaw);
/** @brief Initialization with the rotation from direction dir0 to direction dir1 */
inline TRMatrixBase(const Vec& dir0, const Vec& dir1);
template <typename T> inline TRMatrixBase& operator = (const cv::Matx<T,3,3>& rhs) { BaseBase::operator = (rhs); return *this; }
inline TRMatrixBase& operator = (const cv::Mat& rhs) { BaseBase::operator = (rhs); return *this; }
#ifdef _USE_EIGEN
inline TRMatrixBase& operator = (const typename Base::EMat& rhs) { Base::operator = (rhs); return *this; }
#endif
/** @brief Set Euler angles (in rad) in order XYZ
Set angles either in order 1. z, 2. y, 3. x and fixed axes
or in order 1. x, 2. y, 3. z and moving axes.
Angles are measured as in mathematics (counter-clockwise), i.e.
Set(x, 0, 0) results in the following matrix:
| 1 0 0 |
| 0 cos(x) -sin(x) |
| 0 sin(x) cos(x) |
(*this) = Rx*Ry*Rz =
| c(y)c(z) -c(y)s(z) s(y) |
| c(z)s(x)s(y)+c(x)s(z) c(x)c(z)-s(x)s(y)s(z) -c(y)s(x) |
| -c(x)c(z)s(y)+s(x)s(z) c(z)s(x)+c(x)s(y)s(z) c(x)c(y) |
with s(g) = sin(g), c(g) = cos(g), x = PhiX, y = PhiY and z = PhiZ
@author frahm, woelk */
void SetXYZ(TYPE PhiX, TYPE PhiY, TYPE PhiZ);
/** @brief Set Euler angles (in rad) in order XYZ from vector */
inline void SetXYZ(const Vec& r)
{ SetXYZ(r[0], r[1], r[2]); }
/** @brief Set Euler angles (in rad) in order ZYX
Set angles either in order 1. z, 2. y, 3. x and moving axes
or in order 1. x, 2. y, 3. z and fixed axes.
Angles are measured as in mathematics (counter-clockwise), i.e.
Set(x, 0, 0) results in the following matrix:
| 1 0 0 |
| 0 cos(x) -sin(x) |
| 0 sin(x) cos(x) |
(*this) = Rz*Ry*Rx =
| c(y)c(z) s(x)s(y)c(z)-c(x)s(z) c(x)s(y)c(z)+s(x)s(z) |
| c(y)s(z) s(x)s(y)s(z)+c(x)c(z) c(x)s(y)s(z)-s(x)s(z) |
| -s(y) s(x)c(y) c(x)c(y) |
with s(g) = sin(g), c(g) = cos(g), x = PhiX, y = PhiY and z = PhiZ
@author frahm, woelk */
void SetZYX(TYPE PhiX, TYPE PhiY, TYPE PhiZ);
/** @brief Set Euler angles (in rad) in order ZYX from vector */
inline void SetZYX(const Vec& r)
{ SetZYX(r[0], r[1], r[2]); }
/** @brief Set Euler angles (in rad) in order ZXY
Set angles either in order 1. z, 2. x, 3. y and moving axes
or in order 1. y, 2. x, 3. z and fixed axes.
Angles are measured as in mathematics (counter-clockwise), i.e.
Set(x, 0, 0) results in the following matrix:
| 1 0 0 |
| 0 cos(x) -sin(x) |
| 0 sin(x) cos(x) |
(*this) = Rz*Rx*Ry =
| c(y)c(z)-s(x)s(y)s(z), c(x)s(z), s(y)c(z)+s(x)c(y)s(z) |
| s(z)c(y)+s(x)s(y)c(z), c(x)c(z), s(y)*s(z)+s(x)-c(y)c(z) |
| -c(x)s(y) , s(x) , c(x)c(y) |
with s(g) = sin(g), c(g) = cos(g), x = PhiX, y = PhiY and z = PhiZ
@author haase */
void SetZXY(TYPE PhiX, TYPE PhiY, TYPE PhiZ);
/** @brief Set Euler angles (in rad) in order ZXY from vector */
inline void SetZXY(const Vec& r)
{ SetZXY(r[0], r[1], r[2]); }
/** @brief Set Euler angles (in rad) in order YXZ
Set angles either in order 1. y, 2. x, 3. z and moving axes
or in order 1. z, 2. x, 3. y and fixed axes.
Angles are measured as in mathematics (counter-clockwise), i.e.
Set(x, 0, 0) results in the following matrix:
| 1 0 0 |
| 0 cos(x) sin(x) |
| 0 -sin(x) cos(x) |
(*this) = Rz*Rx*Ry =
| c(y)c(z)+s(x)s(y)s(z), c(x)s(z), -s(y)c(z)+s(x)c(y)s(z) |
| -s(z)c(y)+s(x)s(y)c(z), c(x)c(z), -s(y)*-s(z)+s(x)c(y)c(z) |
| c(x)s(y) , -s(x) , c(x)c(y) |
with s(g) = sin(g), c(g) = cos(g), x = PhiX, y = PhiY and z = PhiZ
@author haase */
void SetYXZ(TYPE PhiZ, TYPE PhiX, TYPE PhiY);
/** @brief Set Euler angles (in rad) in order YXZ from vector */
inline void SetYXZ(const Vec& r)
{ SetYXZ(r[0], r[1], r[2]); }
/** @brief Set from rotation axis w and angle phi (in rad)
@param w Axis vector w will be normalized to length 1, so we need
|w|>1e-6 if phi != 0, otherwise an exception is thrown
@param phi Rotation angle is given in radians
@author evers, woelk */
void Set(const Vec& w, TYPE phi);
/** set this matrix from 3 vectors each representing a column*/
void SetFromColumnVectors(const Vec& v0,
const Vec& v1,
const Vec& v2);
/** set this matrix from 3 vectors, each representing a row */
void SetFromRowVectors(const Vec& v0,
const Vec& v1,
const Vec& v2);
/** @brief Set from rotation axis * angle (modified Rodrigues vector)
@author evers */
void SetFromAxisAngle(const Vec& w);
/* @brief Set rotation matrix from an orthogonal basis given in world
coordinate system (WCS)
As R' is expressing the transformation from WCS to ICS (image
coordinate system), R is ICS to WCS and hereby represents the ICS
base vectors expressed in WCS. These are the *rows* of R because
the transformation is the scalar product.
Assume xxx,yyy,zzz are right-hand orthogonal (RHS), e.g. the orthogonal
image coordinate system (ICS) base vectors expressed in world
coordinates (WCS).
(Inverse doesn't make sense because base would be identity then).
Normal vectors of length 1 are computed.
@todo Warning if (xxx,yyy,zzz) are not an orthogonal basis!
@author jw 09/2003, added exception */
void SetFromOrthogonalBasis(const Vec &xxx, const Vec &yyy, const Vec &zzz);
/** @brief Set rotation matrix from two vectors from an orthonormal basis
@param xh represents the first base vector and is left unchanged
@param vy should be orthogonal to xh, it is orthogonalized otherwise
You can think of this routine as computing R from an image plane
given by two (usually orthogonal) base vectors.
If the given base vectors are not orthogonal, xh is kept and yv is
orthogonalized appropriately.
@author jw */
void SetFromHV(const Vec& xh, const Vec& vy);
/** @brief Create rotation matrix that rotates from dir0 to dir1
@param dir0 represents the first (reference) direction vector
@param dir1 represents the second (target) direction vector
@author cDc */
TRMatrixBase& SetFromDir2Dir(const Vec& dir0, const Vec& dir1);
/** @brief Calculates quaternion representation for this rotation matrix
@attention Scalar part of quaternion will always be non-negative
for sake of uniqueness of the resulting quaternion!
@author woelk 12/2003
@author esquivel 03/2011 (changed algorithm, bugfix) */
void GetQuaternion(Quat& q) const;
inline Quat GetQuaternion() const { Quat q; GetQuaternion(q); return q; }
/** @brief Set rotation matrix from a quaternion
@author grest 06/2003 */
void SetFromQuaternion(const Quat& q);
/** @brief Set rotation matrix from direction and up vector
@note This is openGL conform, similar to gluLookAt(), i.e. if
direction is (0,0,-1) and up is (0,1,0) the resulting matrix is identity.
@author grest 12/2005 */
void SetFromDirUpGL(const Vec& viewDir, const Vec& viewUp);
/** @brief Set rotation matrix from direction and up vector */
void SetFromDirUp(const Vec& viewDir, const Vec& viewUp);
/** @brief Set rotation matrix from an eye point and a look-at target point and the up vector */
void LookAt(const Vec& from, const Vec& to, const Vec& up);
// get parametrized rotation (axis-angle) from the rotation matrix
inline void SetRotationAxisAngle(const Vec& rot);
// modify the rotation matrix by the given parametrized delta rotation (axis-angle)
inline void Apply(const Vec& delta);
// set rotation matrix to the given parametrized rotation (axis-angle)
inline Vec GetRotationAxisAngle() const;
/** @brief Calculates angle and rotation axis representation for
this rotation matrix
@param angle Rotation angle is returned in radians
@author woelk 12/2003 */
void GetRotationAxisAngle(Vec& axis, TYPE& angle) const;
/** @brief Interface for axis component of GetRotationAxisAngle() */
Vec GetRotationAxis() const;
/** @brief Interface for angle component of GetRotationAxisAngle() */
TYPE GetRotationAngle() const;
/** @brief Get Euler angles for this rotation matrix in order XYZ
@attention Representation is not unique and has singularities at
+-pi/2. Assume for example (*this) = Rx * Ry * Rz, then we have
either Euler angles in order 1. x, 2. y, 3. z and moving axes
or in order 1. z, 2. y, 3. x and fixed axes.
@author woelk 01/2003 */
int GetRotationAnglesXYZ(TYPE& PhiX, TYPE& PhiY, TYPE& PhiZ) const;
/** @brief Get Euler angles for this rotation matrix in order XYZ
@see GetRotationAnglesXYZ(TYPE&, TYPE&, TYPE&) */
inline int GetRotationAnglesXYZ(Vec& r) const
{ return GetRotationAnglesXYZ(r[0], r[1], r[2]); }
/** @brief Get Euler angles for this rotation matrix in order YZX
@attention Representation is not unique and has singularities at
+-pi/2. Assume for example (*this) = Rz * Ry * Rx, then we have
either Euler angles in order 1. x, 2. y, 3. z and fixed axes
or in order 1. z, 2. y, 3. x and moving axes.
@author woelk 01 2003 */
int GetRotationAnglesZYX(TYPE& PhiX, TYPE& PhiY, TYPE& PhiZ) const;
/** @brief Get Euler angles for this rotation matrix in order ZYX
@see GetRotationAnglesZYX(TYPE&, TYPE&, TYPE&) */
inline int GetRotationAnglesZYX(Vec& r) const
{ return GetRotationAnglesZYX(r[0], r[1], r[2]); }
/** @brief Get Euler angles for this rotation matrix in order ZXY
@attention Representation is not unique and has singularities at
+-pi/2. Rotation order ZXY refers to rotation around point/vector!
@author haase 2007 */
int GetRotationAnglesZXY(TYPE& PhiZ, TYPE& PhiX, TYPE& PhiY) const;
/** @brief Get Euler angles for this rotation matrix in order ZXY
@see GetRotationAnglesZXY(TYPE&, TYPE&, TYPE&) */
inline int GetRotationAnglesZXY(Vec& r) const
{ return GetRotationAnglesZXY(r[2], r[0], r[1]); }
/** @brief Get Euler angles for this rotation matrix in order YXZ
@attention Representation is not unique and has singularities at
+-pi/2. Rotation order YXZ refers to rotation with moving axes!
@author haase 2007 */
int GetRotationAnglesYXZ(TYPE& PhiY, TYPE& PhiX, TYPE& PhiZ) const;
/** @brief Get Euler angles for this rotation matrix in order YXZ
@see GetRotationAnglesYXZ(TYPE&, TYPE&, TYPE&) */
inline int GetRotationAnglesYXZ(Vec& r) const
{ return GetRotationAnglesYXZ(r[1], r[0], r[2]); }
/** @brief Check that the matrix is a valid rotation matrix (orthogonal) */
inline bool IsValid() const {
// the trace should be three and the determinant should be one
#if 1
return (ISEQUAL((float)cv::determinant(*this), 1.f) && ISEQUAL((float)cv::trace((*this)*(*this).t()), 3.f));
#else
return (ISEQUAL((TYPE)cv::determinant(*this), TYPE(1)) && ISEQUAL((TYPE)cv::trace((*this)*(*this).t()), TYPE(3)));
#endif
}
/** @brief Check if this is a rotation matrix, i.e. if the determinant
is +1 and the columns are orthonormal
@param eps Numerical limit for constraint evaluation
@param verbose Show reason in case of failure */
bool Check(const TYPE eps = std::numeric_limits<TYPE>::epsilon(),
int verbose = 0) const;
/** @brief Enforce orthogonality constraint on rotation matrix and
sets determinant to +1 */
void EnforceOrthogonality();
}; // class
/*----------------------------------------------------------------*/
typedef TRMatrixBase<float> RMatrixBaseF;
typedef TRMatrixBase<double> RMatrixBaseD;
/*----------------------------------------------------------------*/
} // namespace SEACAVE
#include "Rotation.inl"
#endif // __SEACAVE_ROTATION_H__

1563
libs/Common/Rotation.inl

File diff suppressed because it is too large Load Diff

419
libs/Common/SML.cpp

@ -0,0 +1,419 @@ @@ -0,0 +1,419 @@
////////////////////////////////////////////////////////////////////
// SML.cpp
//
// Copyright 2007 cDc@seacave
// Distributed under the Boost Software License, Version 1.0
// (See http://www.boost.org/LICENSE_1_0.txt)
#include "Common.h"
#include "SML.h"
using namespace SEACAVE;
// D E F I N E S ///////////////////////////////////////////////////
#define SML_AUTOVALUES_OFF 0
#define SML_AUTOVALUES_ON 1
#ifndef SML_AUTOVALUES
#define SML_AUTOVALUES SML_AUTOVALUES_ON
#endif
#define SML_SECTIONOPENBRACKET _T("{")
#define SML_SECTIONCLOSEBRACKET _T("}")
#define SML_NAMEOPENBRACKET _T("[")
#define SML_NAMECLOSEBRACKET _T("]")
#define SML_NAMETOKEN _T("=")
#define SML_VALUETOKEN _T("\n")
#define SML_INDENT _T("\t")
#define SML_TOKEN_SECTIONOPENBRACKET SML_SECTIONOPENBRACKET[0]
#define SML_TOKEN_SECTIONCLOSEBRACKET SML_SECTIONCLOSEBRACKET[0]
#define SML_TOKEN_NAMEOPENBRACKET SML_NAMEOPENBRACKET[0]
#define SML_TOKEN_NAMECLOSEBRACKET SML_NAMECLOSEBRACKET[0]
#define SML_TOKEN_NAMETOKEN SML_NAMETOKEN[0]
#define SML_TOKEN_VALUETOKEN SML_VALUETOKEN[0]
#define SML_TOKEN_INDENT SML_INDENT[0]
#define SML_TOKEN_IGNORECHARS _T("\n\r\t ")
// S T R U C T S ///////////////////////////////////////////////////
/*-----------------------------------------------------------*
* SML class implementation *
*-----------------------------------------------------------*/
/**
* Constructor
*/
SML::SML(const String& name)
:
m_strName(name),
m_fncInitItem(NULL),
m_fncSaveItem(NULL),
m_fncReleaseItem(NULL),
m_fncItemData(NULL)
{
}
/**
* Destructor
*/
SML::~SML()
{
Release();
}
/*----------------------------------------------------------------*/
/**
* Released all used memory for this nod and its children
*/
void SML::Release()
{
m_arrChildren.ReleaseDelete();
if (m_fncReleaseItem != NULL) {
for (SMLITEMMAP::iterator it=GetBegin(); it!=GetEnd(); ++it)
m_fncReleaseItem(it->second, m_fncItemData);
}
SMLITEMMAP::Release();
}
/*----------------------------------------------------------------*/
/**
* Load the values from a file;
* all children are generated if they do not exist
*/
bool SML::Load(const String& fileName)
{
File oStream(fileName, File::READ, File::OPEN);
if (!oStream.isOpen())
return false;
return Load(oStream);
}
bool SML::Load(ISTREAM& oStream)
{
// create the section token filter and mem-file
MemFile memFile;
TokenIStream filter(&oStream);
filter.setTrimTokens(SML_TOKEN_IGNORECHARS);
// and parse section
return ParseSection(filter, memFile);
}
bool SML::ParseSection(TokenIStream& filter, MemFile& memFile)
{
while (true) {
// find first section start (NameOpenBracket)
filter.setToken(SML_TOKEN_NAMEOPENBRACKET);
filter.readLine(memFile);
// parse values before the new section or end of section
const size_f_t posMemFile = memFile.getPos();
MemFile valuesMemFile;
TokenIStream sectionFilter(&memFile, SML_TOKEN_SECTIONCLOSEBRACKET);
const size_f_t lenValues = sectionFilter.readLine(valuesMemFile);
if (ParseValues(valuesMemFile) == false)
return false; // Parse Error: invalid values
ASSERT(valuesMemFile.getSize() == 0);
// if end of section found, return
if (!sectionFilter.isEOS()) {
// if a name open bracket was found before the EOS,
// then restore it for the parent next ParseSection()
memFile.setPos(posMemFile+(lenValues+1)*sizeof(TCHAR));
if (!filter.isEOS())
filter.restoreToken();
break;
}
else {
ASSERT(memFile.getSize()-posMemFile == lenValues*(size_f_t)sizeof(TCHAR));
ASSERT(posMemFile == 0 || memFile.getSize()-posMemFile == memFile.getSizeLeft() || memFile.getSizeLeft() == 0);
memFile.setSize(0);
}
// if no more data, return
if (filter.isEOS())
break;
// parse child section name
filter.setToken(SML_TOKEN_NAMECLOSEBRACKET);
const size_t lenName = filter.readLine(memFile);
ASSERT(!filter.isEOS());
if (lenName == 0)
return false; // Parse Error: invalid section name
const String strChildName((LPCTSTR)memFile.getData(), lenName);
memFile.growSize(-((size_f_t)(lenName*sizeof(TCHAR))));
ASSERT(memFile.getSize() == 0);
// create the child with the given name
const IDX idxChild = CreateChildUnique(strChildName);
LPSML const pChild = m_arrChildren[idxChild];
pChild->SetFncItem(m_fncInitItem, m_fncSaveItem, m_fncReleaseItem, m_fncItemData);
// parse child section
filter.setToken(SML_TOKEN_SECTIONOPENBRACKET);
filter.readLine(memFile);
filter.trimBackLine(memFile);
ASSERT(memFile.getSize() == 0);
if (pChild->ParseSection(filter, memFile) == false)
return false;
}
return true;
}
bool SML::ParseValues(MemFile& valuesMemFile)
{
if (valuesMemFile.getSize() == 0)
return true;
// loop through all name/value pairs
MemFile memLine;
TokenIStream filterLine(&valuesMemFile);
filterLine.setTrimTokens(SML_TOKEN_IGNORECHARS);
filterLine.setToken(SML_TOKEN_VALUETOKEN);
MemFile memValue;
TokenIStream filterValue(&memLine);
filterValue.setTrimTokens(SML_TOKEN_IGNORECHARS);
filterValue.setToken(SML_TOKEN_NAMETOKEN);
do {
// parse value name and value
filterLine.readLine(memLine);
filterLine.trimBackLine(memLine);
filterLine.trimFrontLine(memLine);
if (memLine.isEOF())
continue; // empty line, return
// parse value name
const size_t lenNameValueReal = (size_t)memLine.getSizeLeft();
const size_t lenName = filterValue.readLine(memValue);
#if SML_AUTOVALUES == SML_AUTOVALUES_ON
String szName;
if (filterValue.trimBackLine(memValue) == lenName || lenNameValueReal == lenName) {
// no name found, auto generate the name
szName = _T("Item") + String::ToString(size());
} else {
// read the name
szName = (LPCTSTR)memValue.getData();
memValue.setSize(0);
}
#else
filterValue.trimBackLine(memValue);
String szName = (LPCTSTR)memValue.getData();
ASSERT(!filterValue.isEOS() && !szName.IsEmpty());
if (filterValue.isEOS() || szName.IsEmpty()) {
memValue.setSize(0);
memLine.setSize(0);
filterValue.setPos(0);
continue; // Parse Error: invalid syntax ('=' not found)
}
ASSERT(szName.size() == memValue.getSizeLeft());
memValue.setSize(0);
#endif
SMLVALUE& val = operator[](szName);
// parse value
filterValue.read(memValue);
LPCTSTR szValue = filterValue.trimFrontLine(memValue);
val.val = szValue;
ASSERT((size_f_t)_tcslen(szValue) == memValue.getSizeLeft());
memValue.setSize(0);
memLine.setSize(0);
filterValue.setPos(0);
} while (!filterLine.isEOS());
// all file processed, safe to reset it to 0
valuesMemFile.setSize(0);
return true;
}
/*----------------------------------------------------------------*/
/**
* Write to a file the values of this node and its children.
* Set to false the second parameter in order not to save the empty children.
*/
bool SML::Save(const String& fileName, SAVEFLAG flags) const
{
File oStream(fileName, File::WRITE, File::CREATE | File::TRUNCATE);
if (!oStream.isOpen())
return false;
return Save(oStream, flags);
}
bool SML::Save(OSTREAM& oStream, SAVEFLAG flags) const
{
// save all values and all children
return SaveIntern(oStream, _T(""), flags);
}
bool SML::SaveBracketize(OSTREAM& oStream, const String& strIndent, SAVEFLAG flags) const
{
// save its name
oStream.print(_T("%s" SML_NAMEOPENBRACKET "%s" SML_NAMECLOSEBRACKET "\n"), strIndent.c_str(), m_strName.c_str());
oStream.print(_T("%s" SML_SECTIONOPENBRACKET "\n"), strIndent.c_str());
// save all values and all children
SaveIntern(oStream, strIndent+SML_TOKEN_INDENT, flags);
// close bracket
oStream.print(_T("%s" SML_SECTIONCLOSEBRACKET "\n"), strIndent.c_str());
return true;
}
bool SML::SaveIntern(OSTREAM& oStream, const String& strIndent, SAVEFLAG flags) const
{
const Flags& flgs = (const Flags&)flags;
if (flgs.isAnySet(SORT | SORTINV)) {
// sort values alphabetically and save them
StringArr lines(0, GetSize());
for (SMLITEMMAP::const_iterator item=GetBegin(); item!=GetEnd(); ++item)
if (!m_fncSaveItem || m_fncSaveItem((*item).second, m_fncItemData))
lines.InsertSort(String::FormatString(_T("%s%s " SML_NAMETOKEN " %s" SML_VALUETOKEN), strIndent.c_str(), (*item).first.c_str(), (*item).second.val.c_str()),
(flgs.isAnySet(SORT) ? String::CompareAlphabetically : String::CompareAlphabeticallyInv));
FOREACH(l, lines)
oStream.print(lines[l]);
} else {
// save all values directly
for (SMLITEMMAP::const_iterator item=GetBegin(); item!=GetEnd(); ++item)
if (!m_fncSaveItem || m_fncSaveItem((*item).second, m_fncItemData))
oStream.print(_T("%s%s " SML_NAMETOKEN " %s" SML_VALUETOKEN), strIndent.c_str(), (*item).first.c_str(), (*item).second.val.c_str());
}
// save now all children
bool bFirst = IsEmpty();
FOREACH(i, m_arrChildren) {
const SML* pSML = m_arrChildren[i];
// skip empty children
if (!flgs.isSet(SAVEEMPTY) && pSML->IsEmpty() && pSML->m_arrChildren.IsEmpty())
continue;
// insert a new line to separate from the above section (only for visual aspect)
if (bFirst)
bFirst = false;
else
oStream.print(_T("\n"));
// save child
pSML->SaveBracketize(oStream, strIndent, flags);
}
return true;
}
/*----------------------------------------------------------------*/
/**
* Create and insert a new child
*/
IDX SML::CreateChild(const String& strChildName)
{
return InsertChild(new SML(strChildName));
}
IDX SML::CreateChildUnique(const String& strChildName)
{
return InsertChildUnique(new SML(strChildName));
}
/*----------------------------------------------------------------*/
/**
* Insert a new child
*/
IDX SML::InsertChild(const LPSML pSML)
{
return m_arrChildren.InsertSort(pSML, SML::Compare);
}
IDX SML::InsertChildUnique(const LPSML pSML)
{
const std::pair<IDX,bool> res(m_arrChildren.InsertSortUnique(pSML, SML::Compare));
if (res.second)
delete pSML;
return res.first;
}
/*----------------------------------------------------------------*/
/**
* Remove an existing child
*/
void SML::RemoveChild(IDX idx)
{
m_arrChildren.RemoveAtMove(idx);
}
/*----------------------------------------------------------------*/
/**
* Remove and destroy an existing child
*/
void SML::DestroyChild(IDX idx)
{
delete m_arrChildren[idx];
RemoveChild(idx);
}
/*----------------------------------------------------------------*/
/**
* Retrieve an child by its name; NO_INDEX if unexistent
*/
IDX SML::GetChild(const String& name) const
{
return m_arrChildren.FindFirst(&name, SML::CompareName);
}
/*----------------------------------------------------------------*/
/**
* Retrieve an item; NULL if unexistent
*/
const SMLVALUE* SML::GetValue(const String& key) const
{
const_iterator it = Find(key);
if (it == GetEnd())
return NULL;
return &(it->second);
}
/*----------------------------------------------------------------*/
/**
* Insert or retrieve an item; initialize it if necessary
*/
SMLVALUE& SML::GetValue(const String& key)
{
bool bExisted;
SMLVALUE& val = (*Insert(key, bExisted)).second;
if (!bExisted && m_fncInitItem != NULL)
m_fncInitItem(key, val, m_fncItemData);
return val;
}
/*----------------------------------------------------------------*/
/**
* Retrieve an unnamed item by index
*/
const SMLVALUE& SML::GetValue(IDX idx) const
{
ASSERT(idx < this->size());
const String szName(_T("Item") + String::ToString(idx));
return this->at(szName);
}
/*----------------------------------------------------------------*/
/**
* Reset items' init and release functions
*/
void SML::SetFncItem(TFncInitItem fncInit, TFncSaveItem fncSave, TFncReleaseItem fncRelease, void* data)
{
m_fncInitItem = fncInit;
m_fncSaveItem = fncSave;
m_fncReleaseItem = fncRelease;
m_fncItemData = data;
}
/*----------------------------------------------------------------*/
/**
* Compare two SML nodes by name
*/
int STCALL SML::Compare(const void* l, const void* r)
{
return _tcscmp((*((const SML**)l))->GetName(), (*((const SML**)r))->GetName());
}
/*----------------------------------------------------------------*/
/**
* Compare a name and a SML node name
*/
int STCALL SML::CompareName(const void* l, const void* r)
{
return _tcscmp((*((const SML**)l))->GetName(), ((const String*)r)->c_str());
}
/*----------------------------------------------------------------*/

116
libs/Common/SML.h

@ -0,0 +1,116 @@ @@ -0,0 +1,116 @@
////////////////////////////////////////////////////////////////////
// SML.h
//
// Copyright 2007 cDc@seacave
// Distributed under the Boost Software License, Version 1.0
// (See http://www.boost.org/LICENSE_1_0.txt)
#ifndef __SEACAVE_SML_H__
#define __SEACAVE_SML_H__
// I N C L U D E S /////////////////////////////////////////////////
#include "Filters.h"
// D E F I N E S ///////////////////////////////////////////////////
namespace SEACAVE {
// S T R U C T S ///////////////////////////////////////////////////
typedef struct SMLVALUE_TYPE {
String val; //item's value
void* data; //user data
SMLVALUE_TYPE() : data(NULL) {}
SMLVALUE_TYPE(const SMLVALUE_TYPE& r) : data(r.data) { ((SMLVALUE_TYPE&)r).data = NULL; }
~SMLVALUE_TYPE() { delete data; }
} SMLVALUE;
class SML;
typedef SML* LPSML;
typedef cList<LPSML, LPSML, 0, 8> LPSMLARR;
//typedef cHashTable<SMLVALUE> SMLITEMMAP;
typedef cMapWrap<String, SMLVALUE> SMLITEMMAP;
/**************************************************************************************
* Simple Markup Language
* --------------
* fast and easy to use markup language;
* contains a map of (name, value) pairs
**************************************************************************************/
class GENERAL_API SML : public SMLITEMMAP
{
public:
typedef void (STCALL *TFncInitItem)(const String&, SMLVALUE&, void*);
typedef bool (STCALL *TFncSaveItem)(const SMLVALUE&, void*);
typedef void (STCALL *TFncReleaseItem)(SMLVALUE&, void*);
enum SAVEFLAG {
NONE = 0,
SAVEEMPTY = (1 << 0), //save empty children
SORT = (1 << 1), //sort all entries alphabetically
SORTINV = (1 << 2), //sort all entries inverse alphabetically
};
public:
SML(const String& =String());
~SML();
void Release();
// main methods
bool Load(const String&);
bool Load(ISTREAM&);
bool Save(const String&, SAVEFLAG=NONE) const;
bool Save(OSTREAM&, SAVEFLAG=NONE) const;
IDX CreateChild(const String& =String());
IDX CreateChildUnique(const String& =String());
IDX InsertChild(const LPSML);
IDX InsertChildUnique(const LPSML);
void RemoveChild(IDX);
inline void RemoveChild(const String& name) { RemoveChild(GetChild(name)); }
void DestroyChild(IDX);
inline void DestroyChild(const String& name) { DestroyChild(GetChild(name)); }
IDX GetChild(const String&) const;
const SMLVALUE* GetValue(const String&) const;
SMLVALUE& GetValue(const String&);
const SMLVALUE& GetValue(IDX) const;
inline SMLVALUE& operator[] (const String& key) { return GetValue(key); }
// misc methods
inline const String& GetName() const { return m_strName; }
inline LPSML GetChild(IDX idx) const { return m_arrChildren[idx]; }
inline const LPSMLARR& GetArrChildren() const { return m_arrChildren; }
inline LPSMLARR& GetArrChildren() { return m_arrChildren; }
void SetFncItem(TFncInitItem, TFncSaveItem, TFncReleaseItem, void* data=NULL);
public:
static int STCALL Compare(const void*, const void*);
static int STCALL CompareName(const void*, const void*);
private:
typedef TokenInputStream<false> TokenIStream;
bool ParseSection(TokenIStream&, MemFile&);
bool ParseValues(MemFile&);
bool SaveBracketize(OSTREAM&, const String&, SAVEFLAG) const;
bool SaveIntern(OSTREAM&, const String&, SAVEFLAG) const;
private:
const String m_strName; // node name
LPSMLARR m_arrChildren; // the array with all the sub-nodes
TFncInitItem m_fncInitItem; // callback function used to initialize each item
TFncSaveItem m_fncSaveItem; // callback function used to save each item
TFncReleaseItem m_fncReleaseItem; // callback function used to release each item
void* m_fncItemData;
};
/*----------------------------------------------------------------*/
} // namespace SEACAVE
#endif // __SEACAVE_SML_H__

274
libs/Common/Sampler.inl

@ -0,0 +1,274 @@ @@ -0,0 +1,274 @@
// Copyright (c) 2012-2015 OpenMVG.
// Copyright (c) 2012-2015 Pierre MOULON.
// Copyright (c) 2015 Romuald Perrot.
// Copyright (c) 2015 cDc@seacave.
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
namespace Sampler {
// Sampling functors
// These functors computes weight associated to each pixels
//
// For a (relative) sampling position x (in [0,1]) between two (consecutive) points :
//
// A .... x ......... B
//
// w[0] is the weight associated to A
// w[1] is the weight associated to B
//
// Note: The following functors generalize the sampling to more than two neighbors
// They all contains the width variable that specify the number of neighbors used for sampling
//
// All contain the operator() with the following definition:
//
// @brief Computes weight associated to neighboring pixels
// @author Romuald Perrot <perrot.romuald_AT_gmail.com>
// @param x Sampling position
// @param[out] weight Sampling factors associated to the neighboring
// @note weight must be at least width length
// Linear sampling (ie: linear interpolation between two pixels)
template <typename TYPE>
struct Linear {
typedef TYPE Type;
enum { halfWidth = 1 };
enum { width = halfWidth*2 };
inline Linear() {}
inline void operator() (const TYPE x, TYPE* const weight) const {
weight[0] = TYPE(1) - x;
weight[1] = x;
}
};
// Cubic interpolation between 4 pixels
//
// Interpolation weight is for A,B,C and D pixels given a x position as illustrated as follow :
//
// A B x C D
//
// Cubic Convolution Interpolation for Digital Image Processing , R. Keys, eq(4)
//
// The cubic filter family with the two free parameters usually named B and C:
// if |x|<1
// ((12-9B-6C)|x|^3 + (-18+12B+6C)|x|^2 + (6-2B))/6
// if 1<=|x|<2
// ((-B-6C)|x|^3 + (6B+30C)|x|^2 + (-12B-48C)|x| + (8B+24C))/6
template <typename TYPE>
struct Cubic {
typedef TYPE Type;
enum { halfWidth = 2 };
enum { width = halfWidth*2 };
// Sharpness coefficient used to control sharpness of the cubic curve (between 0.5 to 0.75)
// cubic(0,1/2): 0.5 gives better mathematically result (ie: approximation at 3 order precision - Catmull-Rom)
const TYPE sharpness;
inline Cubic(const TYPE& _sharpness=TYPE(0.5)) : sharpness(_sharpness) {}
inline void operator() (const TYPE x, TYPE* const weight) const {
// remember :
// A B x C D
// weight[0] -> weight for A
// weight[1] -> weight for B
// weight[2] -> weight for C
// weight[3] -> weight for D
weight[0] = CubicInter12(x + TYPE(1));
weight[1] = CubicInter01(x);
weight[2] = CubicInter01(TYPE(1) - x);
weight[3] = CubicInter12(TYPE(2) - x);
}
// Cubic interpolation for x (in [0,1])
inline TYPE CubicInter01(const TYPE x) const {
// A = -sharpness
// f(x) = (A + 2) * x^3 - (A + 3) * x^2 + 1
return ((TYPE(2)-sharpness)*x - (TYPE(3)-sharpness))*x*x + TYPE(1);
}
// Cubic interpolation for x (in [1,2])
inline TYPE CubicInter12(const TYPE x) const {
// A = -sharpness
// f(x) = A * x^3 - 5 * A * x^2 + 8 * A * x - 4 * A
return (((TYPE(5)-x)*x - TYPE(8))*x + TYPE(4))*sharpness;
}
};
// Sampler spline16 -> Interpolation on 4 points used for 2D ressampling (16 = 4x4 sampling)
// Cubic interpolation with 0-derivative at edges (ie at A and D points)
// See Helmut Dersch for more details
//
// Some refs :
// - http://forum.doom9.org/archive/index.php/t-147117.html
// - http://avisynth.nl/index.php/Resampling
// - http://www.ipol.im/pub/art/2011/g_lmii/
//
// The idea is to consider 3 cubic splines (f1,f2,f3) in the sampling interval :
//
// A f1 B f2 C f3 D
//
// with curves defined as follow :
// f1(x) = a1 x^3 + b1 x^2 + c1 x + d1
// f2(x) = a2 x^3 + b2 x^2 + c2 x + d2
// f3(x) = a3 x^3 + b2 x^2 + c3 x + d3
//
// We want to compute spline coefs for A,B,C,D assuming that:
// y0 = coef[A] = f1(-1)
// y1 = coef[B] = f1(0) = f2(0)
// y2 = coef[C] = f2(1) = f3(1)
// y3 = coef[D] = f3(2)
//
// coef are computed using the following constraints :
// Curve is continuous, ie:
// f1(0) = f2(0)
// f2(1) = f3(1)
// First derivative are equals, ie:
// f1'(0) = f2'(0)
// f2'(1) = f3'(1)
// Second derivative are equals, ie:
// f1''(0) = f2''(0)
// f2''(1) = f3''(0)
// Curve is, at boundary, with second derivative set to zero (it's a constraint introduced by Dersch), ie:
// f1''(-1) = 0
// f3''(2) = 0
//
// Then, you can solve for (a1,a2,a3,b1,b2,b3,c1,c2,c3,d1,d2,d3)
//
// for ex, for curve f2 you find :
//
// d2 = y1 // easy since y1 = f2(0)
// c2 = - 7/15 y0 - 1/5 y1 + 4/5 y2 - 2/15 y3
// b2 = 4/5 y0 - 9/5 y1 + 6/5 y2 - 1/5 y3
// a2 = - 1/3 y0 + y1 - y2 + 1/3 y3
//
//
// When you have coefs, you just have to express your curve as a linear combination of the control points, fort ex
// with f2 :
//
//
// f2(x) = w0(x) * y0 + w1(x) + y1 + w2(x) * y2 + w3(x) * y3
//
// with :
//
// w0(x) = - 1/3 * x^3 + 4/5 * x^2 - 7/15 * x
// w1(x) = x^3 - 9/5 * x^2 - 1/5 * x + 1
// w2(x) = -x^3 + 6/5 * x^2 + 4/5 * x
// w3(x) = 1/3 * x^3 - 1/5 * x^2 - 2/15 * x
//
// substituting boundary conditions gives the correct coefficients for y0,y1,y2,y3 giving the final sampling scheme
template <typename TYPE>
struct Spline16 {
typedef TYPE Type;
enum { halfWidth = 2 };
enum { width = halfWidth*2 };
inline Spline16() {}
inline void operator() (const TYPE x, TYPE* const weight) const {
weight[0] = ((TYPE(-1) / TYPE(3) * x + TYPE(4) / TYPE(5)) * x - TYPE(7) / TYPE(15)) * x;
weight[1] = ((x - TYPE(9) / TYPE(5)) * x - TYPE(1) / TYPE(5)) * x + TYPE(1);
weight[2] = ((TYPE(6) / TYPE(5) - x) * x + TYPE(4) / TYPE(5)) * x;
weight[3] = ((TYPE(1) / TYPE(3) * x - TYPE(1) / TYPE(5)) * x - TYPE(2) / TYPE(15)) * x;
}
};
// Sampler spline 36
// Same as spline 16 but on 6 neighbors (used for 6x6 frame)
template <typename TYPE>
struct Spline36 {
typedef TYPE Type;
enum { halfWidth = 3 };
enum { width = halfWidth*2 };
inline Spline36() {}
inline void operator() (const TYPE x, TYPE* const weight) const {
weight[0] = ((TYPE(1) / TYPE(11) * x - TYPE(45) / TYPE(209)) * x + TYPE(26) / TYPE(209)) * x;
weight[1] = ((TYPE(-6) / TYPE(11) * x + TYPE(270) / TYPE(209)) * x - TYPE(156) / TYPE(209)) * x;
weight[2] = ((TYPE(13) / TYPE(11) * x - TYPE(453) / TYPE(209)) * x - TYPE(3) / TYPE(209)) * x + TYPE(1);
weight[3] = ((TYPE(-13) / TYPE(11) * x + TYPE(288) / TYPE(209)) * x + TYPE(168) / TYPE(209)) * x;
weight[4] = ((TYPE(6) / TYPE(11) * x - TYPE(72) / TYPE(209)) * x - TYPE(42) / TYPE(209)) * x;
weight[5] = ((TYPE(-1) / TYPE(11) * x + TYPE(12) / TYPE(209)) * x + TYPE(7) / TYPE(209)) * x;
}
};
// Sampler spline 64
// Same as spline 16 but on 8 neighbors (used for 8x8 frame)
template <typename TYPE>
struct Spline64 {
typedef TYPE Type;
enum { halfWidth = 4 };
enum { width = halfWidth*2 };
inline Spline64() {}
inline void operator() (const TYPE x, TYPE* const weight) const {
weight[0] = ((TYPE(-1) / TYPE(41) * x + TYPE(168) / TYPE(2911)) * x - TYPE(97) / TYPE(2911)) * x;
weight[1] = ((TYPE(6) / TYPE(41) * x - TYPE(1008) / TYPE(2911)) * x + TYPE(582) / TYPE(2911)) * x;
weight[2] = ((TYPE(-24) / TYPE(41) * x + TYPE(4032) / TYPE(2911)) * x - TYPE(2328) / TYPE(2911)) * x;
weight[3] = ((TYPE(49) / TYPE(41) * x - TYPE(6387) / TYPE(2911)) * x - TYPE(3) / TYPE(2911)) * x + TYPE(1);
weight[4] = ((TYPE(-49) / TYPE(41) * x + TYPE(4050) / TYPE(2911)) * x + TYPE(2340) / TYPE(2911)) * x;
weight[5] = ((TYPE(24) / TYPE(41) * x - TYPE(1080) / TYPE(2911)) * x - TYPE(624) / TYPE(2911)) * x;
weight[6] = ((TYPE(-6) / TYPE(41) * x + TYPE(270) / TYPE(2911)) * x + TYPE(156) / TYPE(2911)) * x;
weight[7] = ((TYPE(1) / TYPE(41) * x - TYPE(45) / TYPE(2911)) * x - TYPE(26) / TYPE(2911)) * x;
}
};
// Sample image at a specified position
// @param image to be sampled
// @param sampler used to make the sampling
// @param pt X and Y-coordinate of sampling
// @return sampled value
template <typename IMAGE, typename SAMPLER, typename POINT, typename TYPE>
inline TYPE Sample(const IMAGE& image, const SAMPLER& sampler, const POINT& pt)
{
typedef typename SAMPLER::Type T;
// integer position of sample (x,y)
const int grid_x(FLOOR2INT(pt.x));
const int grid_y(FLOOR2INT(pt.y));
// compute difference between exact pixel location and sample
const T dx(pt.x-(T)grid_x);
const T dy(pt.y-(T)grid_y);
// get sampler weights
T coefs_x[SAMPLER::width];
sampler(dx, coefs_x);
T coefs_y[SAMPLER::width];
sampler(dy, coefs_y);
// Sample a grid around specified grid point
TYPE res(0);
for (int i = 0; i < SAMPLER::width; ++i) {
// get current i value
// +1 for correct scheme (draw it to be convinced)
const int cur_i(grid_y + 1 + i - SAMPLER::halfWidth);
// handle out of range
if (cur_i < 0 || cur_i >= image.rows)
continue;
for (int j = 0; j < SAMPLER::width; ++j) {
// get current j value
// +1 for the same reason
const int cur_j(grid_x + 1 + j - SAMPLER::halfWidth);
// handle out of range
if (cur_j < 0 || cur_j >= image.cols)
continue;
// sample input image and weight according to sampler
const T w = coefs_x[j] * coefs_y[i];
const TYPE pixel = image(cur_i, cur_j);
res += pixel * w;
}
}
return res;
}
} // namespace Sampler

170
libs/Common/Semaphore.h

@ -0,0 +1,170 @@ @@ -0,0 +1,170 @@
////////////////////////////////////////////////////////////////////
// Semaphore.h
//
// Copyright 2007 cDc@seacave
// Distributed under the Boost Software License, Version 1.0
// (See http://www.boost.org/LICENSE_1_0.txt)
#ifndef __SEACAVE_SEMAPHORE_H__
#define __SEACAVE_SEMAPHORE_H__
// I N C L U D E S /////////////////////////////////////////////////
#include "Thread.h"
#ifndef _MSC_VER
#ifdef _SUPPORT_CPP11
#include <mutex>
#include <condition_variable>
#else
#include "CriticalSection.h"
#include <sys/time.h>
#endif
#endif
// D E F I N E S ///////////////////////////////////////////////////
namespace SEACAVE {
// S T R U C T S ///////////////////////////////////////////////////
class Semaphore
{
#ifdef _MSC_VER
public:
Semaphore(unsigned count=0) {
h = CreateSemaphore(NULL, count, MAXLONG, NULL);
};
~Semaphore() {
CloseHandle(h);
};
void Clear(unsigned count=0) {
CloseHandle(h);
h = CreateSemaphore(NULL, count, MAXLONG, NULL);
}
void Signal() {
ReleaseSemaphore(h, 1, NULL);
}
void Signal(unsigned count) {
ASSERT(count > 0);
ReleaseSemaphore(h, count, NULL);
}
void Wait() {
WaitForSingleObject(h, INFINITE);
}
bool Wait(uint32_t millis) {
return WaitForSingleObject(h, millis) == WAIT_OBJECT_0;
}
protected:
HANDLE h;
#elif !defined(_SUPPORT_CPP11)
// pthread implementation
public:
Semaphore(unsigned c=0) : count(c) { pthread_cond_init(&cond, NULL); }
~Semaphore() { pthread_cond_destroy(&cond); }
void Clear(unsigned c=0) {
cs.Clear();
pthread_cond_destroy(&cond);
pthread_cond_init(&cond, NULL);
count = c;
}
void Signal() {
Lock l(cs);
++count;
pthread_cond_signal(&cond);
}
void Signal(unsigned c) {
ASSERT(c > 0);
for (unsigned i=0; i<c; ++i)
Signal();
}
void Wait() {
Lock l(cs);
while (!count)
pthread_cond_wait(&cond, &cs.getMutex());
--count;
}
bool Wait(uint32_t millis) {
Lock l(cs);
if (count == 0) {
timeval timev;
gettimeofday(&timev, NULL);
millis += timev.tv_usec/1000;
timespec t = {
timev.tv_sec + (millis/1000),
(millis%1000)*1000*1000
};
pthread_cond_timedwait(&cond, &cs.getMutex(), &t);
if (count == 0)
return false;
}
--count;
return true;
}
protected:
pthread_cond_t cond;
CriticalSection cs;
unsigned count;
#else
// C++11 implementation
public:
Semaphore(unsigned c=0) : count(c) {}
~Semaphore() {}
void Clear(unsigned c=0) {
std::lock_guard<std::mutex> lock{mtx};
count = c;
}
void Signal() {
std::lock_guard<std::mutex> lock{mtx};
++count;
cv.notify_one();
}
void Signal(unsigned c) {
ASSERT(c > 0);
for (unsigned i=0; i<c; ++i)
Signal();
}
void Wait() {
std::unique_lock<std::mutex> lock{mtx};
cv.wait(lock, [&] { return count > 0; });
--count;
}
bool Wait(uint32_t millis) {
std::unique_lock<std::mutex> lock{mtx};
if (!cv.wait_for(lock, std::chrono::milliseconds(millis), [&] { return count > 0; }))
return false;
--count;
return true;
}
protected:
std::condition_variable cv;
std::mutex mtx;
unsigned count;
#endif
private:
Semaphore(const Semaphore&);
Semaphore& operator=(const Semaphore&);
};
/*----------------------------------------------------------------*/
} // namespace SEACAVE
#endif // __SEACAVE_SEMAPHORE_H__

209
libs/Common/SharedPtr.h

@ -0,0 +1,209 @@ @@ -0,0 +1,209 @@
////////////////////////////////////////////////////////////////////
// SharedPtr.h
//
// Copyright 2007 cDc@seacave
// Distributed under the Boost Software License, Version 1.0
// (See http://www.boost.org/LICENSE_1_0.txt)
#ifndef __SEACAVE_SHAREDPTR_H__
#define __SEACAVE_SHAREDPTR_H__
// I N C L U D E S /////////////////////////////////////////////////
// D E F I N E S ///////////////////////////////////////////////////
namespace SEACAVE {
// S T R U C T S ///////////////////////////////////////////////////
/**************************************************************************************
* CSharedPtr template
* ---------------
* shared smart pointer
**************************************************************************************/
template<class TYPE>
struct SharedRef {
typedef TYPE TYPE_REF;
#ifdef SEACAVE_NO_MULTITHREAD
TYPE_REF val;
#else
volatile TYPE_REF val;
#endif
inline SharedRef() {}
inline SharedRef(TYPE_REF v) : val(v) {}
inline TYPE_REF Inc() {
#ifdef SEACAVE_NO_MULTITHREAD
ASSERT(val >= 0);
return ++val;
#else
return Thread::safeInc(val);
#endif
}
inline TYPE_REF Dec() {
#ifdef SEACAVE_NO_MULTITHREAD
ASSERT(val > 0);
return --val;
#else
return Thread::safeDec(val);
#endif
}
#ifdef _USE_BOOST
// serialize
template <class Archive>
void serialize(Archive& ar, const unsigned int /*version*/) {
#ifdef SEACAVE_NO_MULTITHREAD
ar & val;
#else
ar & (TYPE_REF&)val;
#endif
}
#endif
};
/*----------------------------------------------------------------*/
template<class TYPE>
class CSharedPtr
{
protected:
typedef SharedRef<int32_t> TYPE_REF;
typedef TYPE* TYPE_PTR;
public:
inline CSharedPtr() : m_pointer(NULL), m_pNoRef(NULL)
{ // empty construct
}
inline explicit CSharedPtr(TYPE_PTR _Ptr) : m_pointer(_Ptr), m_pNoRef(_Ptr ? new TYPE_REF(1) : NULL)
{ // construct from object pointer
}
inline CSharedPtr(const CSharedPtr& _Right) : m_pointer(_Right.m_pointer), m_pNoRef(_Right.m_pNoRef)
{ // construct by assuming pointer from _Right CSharedPtr
IncRef();
}
inline ~CSharedPtr()
{ // destroy the object
DecRef();
}
CSharedPtr& operator=(const CSharedPtr& _Right)
{ // assign compatible _Right (assume pointer)
if (this != &_Right)
{
ASSERT(m_pointer != _Right.m_pointer || m_pNoRef == _Right.m_pNoRef);
DecRef();
m_pointer = _Right.m_pointer;
m_pNoRef = _Right.m_pNoRef;
IncRef();
}
return (*this);
}
CSharedPtr& operator=(TYPE_PTR _Ptr)
{ // assign compatible _Right (assume pointer)
if (m_pointer != _Ptr)
{
DecRef();
m_pointer = _Ptr;
m_pNoRef = (_Ptr ? new TYPE_REF(1) : NULL);
}
return (*this);
}
inline TYPE& operator*() const
{ // return designated value
ASSERT(m_pointer);
return (*m_pointer);
}
inline TYPE* operator->() const
{ // return pointer to class object
ASSERT(m_pointer);
return m_pointer;
}
inline operator TYPE_PTR() const
{ // return pointer to class object
return m_pointer;
}
inline bool operator==(const CSharedPtr& _Right) const
{ // return pointer to class object
return (m_pointer == _Right.m_pointer);
}
inline bool operator!=(const CSharedPtr& _Right) const
{ // return pointer to class object
return (m_pointer != _Right.m_pointer);
}
inline bool operator==(const void* _Right) const
{ // return pointer to class object
return (m_pointer == _Right);
}
inline bool operator!=(const void* _Right) const
{ // return pointer to class object
return (m_pointer != _Right);
}
void Release()
{ // release pointer
DecRef();
m_pointer = NULL;
m_pNoRef = NULL;
}
protected:
inline void IncRef()
{
if (m_pointer == NULL)
return;
ASSERT(m_pNoRef);
m_pNoRef->Inc();
}
inline void DecRef()
{
if (m_pointer == NULL)
return;
ASSERT(m_pNoRef);
if (m_pNoRef->Dec() == 0)
{
delete m_pointer;
m_pointer = NULL;
delete m_pNoRef;
m_pNoRef = NULL;
}
}
TYPE_PTR m_pointer; // the wrapped object pointer
TYPE_REF* m_pNoRef; // number of references to this pointer
#ifdef _USE_BOOST
protected:
// implement BOOST serialization
friend class boost::serialization::access;
template <class Archive>
void serialize(Archive& ar, const unsigned int /*version*/) {
ar & m_pointer;
ar & m_pNoRef;
}
#endif
};
/*----------------------------------------------------------------*/
} // namespace SEACAVE
#endif // __SEACAVE_SHAREDPTR_H__

56
libs/Common/Sphere.h

@ -0,0 +1,56 @@ @@ -0,0 +1,56 @@
////////////////////////////////////////////////////////////////////
// Sphere.h
//
// Copyright 2007 cDc@seacave
// Distributed under the Boost Software License, Version 1.0
// (See http://www.boost.org/LICENSE_1_0.txt)
#ifndef __SEACAVE_SPHERE_H__
#define __SEACAVE_SPHERE_H__
// I N C L U D E S /////////////////////////////////////////////////
// D E F I N E S ///////////////////////////////////////////////////
namespace SEACAVE {
// S T R U C T S ///////////////////////////////////////////////////
// Basic sphere class
template <typename TYPE, int DIMS>
class TSphere
{
STATIC_ASSERT(DIMS > 1 && DIMS <= 3);
public:
typedef TYPE Type;
typedef Eigen::Matrix<TYPE,DIMS,1> POINT;
POINT center; // sphere center point
TYPE radius; // sphere radius
//---------------------------------------
inline TSphere() {}
inline TSphere(const POINT& c, TYPE r) : center(c), radius(r) {}
inline TSphere(const POINT& p1, const POINT& p2, const POINT& p3);
inline void Set(const POINT& c, TYPE r);
inline void Set(const POINT& p1, const POINT& p2, const POINT& p3);
inline void Enlarge(TYPE);
inline void EnlargePercent(TYPE);
inline GCLASS Classify(const POINT&) const;
}; // class TSphere
/*----------------------------------------------------------------*/
#include "Sphere.inl"
/*----------------------------------------------------------------*/
} // namespace SEACAVE
#endif // __SEACAVE_SPHERE_H__

80
libs/Common/Sphere.inl

@ -0,0 +1,80 @@ @@ -0,0 +1,80 @@
////////////////////////////////////////////////////////////////////
// Sphere.inl
//
// Copyright 2007 cDc@seacave
// Distributed under the Boost Software License, Version 1.0
// (See http://www.boost.org/LICENSE_1_0.txt)
// D E F I N E S ///////////////////////////////////////////////////
// S T R U C T S ///////////////////////////////////////////////////
template <typename TYPE, int DIMS>
inline TSphere<TYPE,DIMS>::TSphere(const POINT& p1, const POINT& p2, const POINT& p3)
{
Set(p1, p2, p3);
} // constructor
/*----------------------------------------------------------------*/
template <typename TYPE, int DIMS>
inline void TSphere<TYPE,DIMS>::Set(const POINT& c, TYPE r)
{
center = c;
radius = r;
}
template <typename TYPE, int DIMS>
inline void TSphere<TYPE, DIMS>::Set(const POINT& p1, const POINT& p2, const POINT& p3)
{
// compute relative distances
TYPE A((p1 - p2).squaredNorm());
TYPE B((p2 - p3).squaredNorm());
TYPE C((p3 - p1).squaredNorm());
// re-orient triangle (make A longest side)
const POINT *a(&p3), *b(&p1), *c(&p2);
if (B < C) std::swap(B, C), std::swap(b, c);
if (A < B) std::swap(A, B), std::swap(a, b);
// if obtuse
if (B + C <= A) {
// just use longest diameter
radius = SQRT(A) / TYPE(2);
center = (*b + *c) / TYPE(2);
} else {
// otherwise circumscribe (http://en.wikipedia.org/wiki/Circumscribed_circle)
const TYPE cos_a(SQUARE(B + C - A) / (B*C*TYPE(4)));
radius = SQRT(A / ((TYPE(1) - cos_a)*TYPE(4)));
const POINT alpha(*a - *c), beta(*b - *c);
const POINT alphaXbeta(alpha.cross(beta));
center = (beta * alpha.squaredNorm() - alpha * beta.squaredNorm()).cross(alphaXbeta) /
(alphaXbeta.squaredNorm() * TYPE(2)) + *c;
}
}
/*----------------------------------------------------------------*/
template <typename TYPE, int DIMS>
inline void TSphere<TYPE,DIMS>::Enlarge(TYPE x)
{
radius += x;
}
template <typename TYPE, int DIMS>
inline void TSphere<TYPE,DIMS>::EnlargePercent(TYPE x)
{
radius *= x;
} // Enlarge
/*----------------------------------------------------------------*/
// Classify point to sphere.
template <typename TYPE, int DIMS>
inline GCLASS TSphere<TYPE, DIMS>::Classify(const POINT& p) const
{
if ((center - p).squaredNorm() > SQUARE(radius))
return CULLED;
return VISIBLE;
}
/*----------------------------------------------------------------*/

244
libs/Common/Streams.h

@ -0,0 +1,244 @@ @@ -0,0 +1,244 @@
////////////////////////////////////////////////////////////////////
// Streams.h
//
// Copyright 2007 cDc@seacave
// Distributed under the Boost Software License, Version 1.0
// (See http://www.boost.org/LICENSE_1_0.txt)
#ifndef __SEACAVE_STREAMS_H__
#define __SEACAVE_STREAMS_H__
// I N C L U D E S /////////////////////////////////////////////////
#include "AutoPtr.h"
// D E F I N E S ///////////////////////////////////////////////////
#define SIZE_NA ((size_f_t)-1)
#define STREAM_ERROR ((size_t)-1)
namespace SEACAVE {
// S T R U C T S ///////////////////////////////////////////////////
class InputStream;
typedef InputStream ISTREAM;
class OutputStream;
typedef OutputStream OSTREAM;
class IOStream;
typedef IOStream IOSTREAM;
class GENERAL_API NOINITVTABLE Stream {
public:
virtual ~Stream() { }
// Identify stream type
virtual InputStream* getInputStream(int) { return NULL; }
virtual OutputStream* getOutputStream(int) { return NULL; }
virtual IOStream* getIOStream(int, int) { return NULL; }
// Get the length of the stream.
// SIZE_NA if there were errors.
virtual size_f_t getSize() const = 0;
// Position the stream at the given offset from the beginning.
virtual bool setPos(size_f_t pos) = 0;
// Get the current position in the stream (offset from the beginning).
// SIZE_NA if there were errors.
virtual size_f_t getPos() const = 0;
};
class GENERAL_API NOINITVTABLE InputStream : public Stream {
public:
virtual ~InputStream() { }
/**
* Call this function until it returns 0 to get all bytes.
* @return The number of bytes read. len reflects the number of bytes
* actually read from the stream source in this call.
* STREAM_ERROR if there were errors.
*/
virtual size_t read(void* buf, size_t len) = 0;
enum { LAYER_ID_IN=0 };
InputStream* getInputStream(int typ=LAYER_ID_IN) override { return (typ == LAYER_ID_IN ? this : (InputStream*)NULL); }
};
class GENERAL_API NOINITVTABLE OutputStream : public Stream {
public:
virtual ~OutputStream() { }
/**
* @return The actual number of bytes written. len bytes will always be
* consumed, but fewer or more bytes may actually be written,
* for example if the stream is being compressed.
* STREAM_ERROR if there were errors.
*/
virtual size_t write(const void* buf, size_t len) = 0;
inline size_t print(LPCTSTR szFormat, ...) {
va_list args;
va_start(args, szFormat);
TCHAR szBuffer[2048];
const size_t len((size_t)_vsntprintf(szBuffer, 2048, szFormat, args));
if (len > 2048) {
const size_t count((size_t)_vsctprintf(szFormat, args));
ASSERT(count != (size_t)-1);
CAutoPtrArr<TCHAR> szBufferDyn(new TCHAR[count+1]);
_vsntprintf(szBufferDyn, count+1, szFormat, args);
va_end(args);
return write(szBufferDyn, count);
}
va_end(args);
return write(szBuffer, len);
}
inline size_t print(const std::string& str) {
return write(str.c_str(), str.length());
}
template<class T>
inline OutputStream& operator<<(const T& val) {
std::ostringstream ostr;
ostr << val;
print(ostr.str());
return *this;
}
/**
* This must be called before destroying the object to make sure all data
* is properly written. Note that some implementations
* might not need it...
*
* @return The actual number of bytes written.
* STREAM_ERROR if there were errors.
*/
virtual size_t flush() = 0;
enum { LAYER_ID_OUT=0 };
OutputStream* getOutputStream(int typ=LAYER_ID_OUT) override { return (typ == LAYER_ID_OUT ? this : (OutputStream*)NULL); }
};
class GENERAL_API NOINITVTABLE IOStream : public InputStream, public OutputStream {
public:
InputStream* getInputStream(int typ=LAYER_ID_IN) override { return InputStream::getInputStream(typ); }
OutputStream* getOutputStream(int typ=LAYER_ID_OUT) override { return OutputStream::getOutputStream(typ); }
IOStream* getIOStream(int typIn=LAYER_ID_IN, int typOut=LAYER_ID_OUT) override {
return ((InputStream*)this)->getInputStream(typIn) != NULL &&
((OutputStream*)this)->getOutputStream(typOut) != NULL ? this : (IOStream*)NULL;
}
};
/*----------------------------------------------------------------*/
template<bool managed=true>
class LayerInputStream : public InputStream {
public:
LayerInputStream(InputStream* aStream) : s(aStream) { ASSERT(s != NULL); }
virtual ~LayerInputStream() noexcept { if (managed) delete s; }
InputStream* getInputStream(int typ=InputStream::LAYER_ID_IN) override { return s->getInputStream(typ); }
size_t read(void* wbuf, size_t len) override {
return s->read(wbuf, len);
}
size_f_t getSize() const override {
return s->getSize();
}
bool setPos(size_f_t wpos) override {
return s->setPos(wpos);
}
size_f_t getPos() const override {
return s->getPos();
}
protected:
InputStream* const s;
};
template<bool managed=true>
class LayerOutputStream : public OutputStream {
public:
LayerOutputStream(OutputStream* aStream) : s(aStream) {}
virtual ~LayerOutputStream() noexcept { if (managed) delete s; }
OutputStream* getOutputStream(int typ=OutputStream::LAYER_ID_OUT) override { return s->getOutputStream(typ); }
size_t write(const void* buf, size_t len) override {
return s->write(buf, len);
}
size_t flush() override {
return s->flush();
}
size_f_t getSize() const override {
return s->getSize();
}
bool setPos(size_f_t wpos) override {
return s->setPos(wpos);
}
size_f_t getPos() const override {
return s->getPos();
}
protected:
OutputStream* const s;
};
/*----------------------------------------------------------------*/
template<bool managed=true>
class LayerIOStream : public IOStream {
public:
LayerIOStream(InputStream* aStream) : s(aStream), sIn(aStream), sOut(NULL) { ASSERT(aStream != NULL); }
LayerIOStream(OutputStream* aStream) : s(aStream), sIn(NULL), sOut(aStream) { ASSERT(aStream != NULL); }
LayerIOStream(InputStream* streamIn, OutputStream* streamOut) : s(NULL), sIn(streamIn), sOut(streamOut) { ASSERT(sIn != NULL || sOut != NULL); }
virtual ~LayerIOStream() noexcept { if (managed) { delete sIn; delete sOut; } }
InputStream* getInputStream(int typ=InputStream::LAYER_ID_IN) override { return (sIn ? sIn->getInputStream(typ) : NULL); }
OutputStream* getOutputStream(int typ=OutputStream::LAYER_ID_OUT) override { return (sOut ? sOut->getOutputStream(typ) : NULL); }
size_f_t getSize() const override {
return s->getSize();
}
bool setPos(size_f_t wpos) override {
return s->setPos(wpos);
}
size_f_t getPos() const override {
return s->getPos();
}
size_t read(void* wbuf, size_t len) override {
return sIn->read(wbuf, len);
}
size_t write(const void* buf, size_t len) override {
return sOut->write(buf, len);
}
size_t flush() override {
return sOut->flush();
}
operator InputStream* () const { return sIn; }
operator OutputStream* () const { return sOut; }
protected:
Stream* const s;
InputStream* const sIn;
OutputStream* const sOut;
};
/*----------------------------------------------------------------*/
} // namespace SEACAVE
#endif // __SEACAVE_STREAMS_H__

239
libs/Common/Strings.h

@ -0,0 +1,239 @@ @@ -0,0 +1,239 @@
////////////////////////////////////////////////////////////////////
// Strings.h
//
// Copyright 2007 cDc@seacave
// Distributed under the Boost Software License, Version 1.0
// (See http://www.boost.org/LICENSE_1_0.txt)
#ifndef __SEACAVE_STRING_H__
#define __SEACAVE_STRING_H__
// I N C L U D E S /////////////////////////////////////////////////
#include "Streams.h"
// D E F I N E S ///////////////////////////////////////////////////
namespace SEACAVE {
// S T R U C T S ///////////////////////////////////////////////////
/// String class: enhanced std::string
class GENERAL_API String : public std::string
{
public:
typedef std::string Base;
public:
inline String() {}
inline String(LPCTSTR sz) : Base(sz) {}
inline String(const Base& str) : Base(str) {}
inline String(size_t n, value_type v) : Base(n, v) {}
inline String(LPCTSTR sz, size_t count) : Base(sz, count) {}
inline String(LPCTSTR sz, size_t offset, size_t count) : Base(sz, offset, count) {}
#ifdef _SUPPORT_CPP11
inline String(Base&& rhs) : Base(std::forward<Base>(rhs)) {}
inline String(String&& rhs) : Base(std::forward<Base>(rhs)) {}
inline String(const String& rhs) : Base(rhs) {}
inline String& operator=(Base&& rhs) { Base::operator=(std::forward<Base>(rhs)); return *this; }
inline String& operator=(String&& rhs) { Base::operator=(std::forward<Base>(rhs)); return *this; }
#endif
inline String& operator=(TCHAR rhs) { Base::operator=(rhs); return *this; }
inline String& operator=(LPCTSTR rhs) { Base::operator=(rhs); return *this; }
inline String& operator=(const String& rhs) { Base::operator=(rhs); return *this; }
inline String& operator+=(TCHAR rhs) { *this = (*this) + rhs; return *this; }
inline String& operator+=(LPCTSTR rhs) { *this = (*this) + rhs; return *this; }
inline String& operator+=(const Base& rhs) { *this = (*this) + rhs; return *this; }
inline String& operator+=(const String& rhs) { *this = (*this) + rhs; return *this; }
inline void Release() { return clear(); }
inline bool IsEmpty() const { return empty(); }
inline operator LPCTSTR() const { return c_str(); }
String& Format(LPCTSTR szFormat, ...) {
va_list args;
va_start(args, szFormat);
TCHAR szBuffer[2048];
const size_t len((size_t)_vsntprintf(szBuffer, 2048, szFormat, args));
if (len > 2048) {
*this = FormatStringSafe(szFormat, args);
va_end(args);
} else {
va_end(args);
this->assign(szBuffer, len);
}
return *this;
}
String& FormatSafe(LPCTSTR szFormat, ...) {
va_list args;
va_start(args, szFormat);
const size_t len((size_t)_vsctprintf(szFormat, args));
ASSERT(len != (size_t)-1);
TCHAR* szBuffer(new TCHAR[len]);
_vsntprintf(szBuffer, len, szFormat, args);
va_end(args);
this->assign(szBuffer, len);
delete[] szBuffer;
return *this;
}
static String FormatString(LPCTSTR szFormat, ...) {
va_list args;
va_start(args, szFormat);
TCHAR szBuffer[2048];
const size_t len((size_t)_vsntprintf(szBuffer, 2048, szFormat, args));
if (len > 2048) {
const String str(FormatStringSafe(szFormat, args));
va_end(args);
return str;
}
va_end(args);
return String(szBuffer, len);
}
static inline String FormatStringSafe(LPCTSTR szFormat, va_list args) {
const size_t len((size_t)_vsctprintf(szFormat, args));
ASSERT(len != (size_t)-1);
TCHAR* szBuffer(new TCHAR[len]);
_vsntprintf(szBuffer, len, szFormat, args);
String str(szBuffer, len);
delete[] szBuffer;
return str;
}
inline void ToUpper(String& out) const {
out.resize(size());
std::transform(begin(), end(), out.begin(), [](TCHAR c) { return (TCHAR)std::toupper(c); });
}
inline String ToUpper() const {
String str;
ToUpper(str);
return str;
}
inline void ToLower(String& out) const {
out.resize(size());
std::transform(begin(), end(), out.begin(), [](TCHAR c) { return (TCHAR)std::tolower(c); });
}
inline String ToLower() const {
String str;
ToLower(str);
return str;
}
inline void Save(OSTREAM& oStream) const {
const WORD nSize = (WORD)size();
oStream.write(&nSize, sizeof(WORD));
oStream.write(c_str(), nSize);
}
inline void Load(ISTREAM& oStream) {
WORD nSize;
oStream.read(&nSize, sizeof(WORD));
if (nSize == 0) {
clear();
return;
}
char* pBuffer = new char[nSize];
oStream.read(pBuffer, nSize);
assign(pBuffer, nSize);
delete[] pBuffer;
}
template <class T>
static String ToString(const T& val) {
std::ostringstream os;
os << val;
return os.str();
}
template <class T>
static String ToStringHex(const T& val) {
std::ostringstream os;
os << std::hex << val;
return os.str();
}
template <class T>
static void FromString(const String& str, T& val) {
std::istringstream is(str);
is >> val;
}
template <class T>
static T FromString(const String& str, const T& def) {
T val(def);
FromString(str, val);
return val;
}
template <class T>
static T FromString(const String& str) {
T val;
FromString(str, val);
return val;
}
template <class T>
inline void From(T& val) const {
FromString(*this, val);
}
template <class T>
inline T From(const T& def) const {
T val(def);
From(val);
return val;
}
template <class T>
inline T From() const {
T val;
From(val);
return val;
}
static int CompareAlphabetically(const void* elem, const void* key) { return _tcscmp(((const String*)elem)->c_str(), ((const String*)key)->c_str()); }
static int CompareAlphabeticallyInv(const void* elem, const void* key) { return _tcscmp(((const String*)key)->c_str(), ((const String*)elem)->c_str()); }
#ifdef _USE_BOOST
protected:
// implement BOOST serialization
friend class boost::serialization::access;
template<class Archive>
void serialize(Archive& ar, const unsigned int /*version*/) {
ar & boost::serialization::base_object<Base>(*this);
}
#endif
};
/*----------------------------------------------------------------*/
inline String operator+(const String& lhs, TCHAR rhs) { return std::operator+(lhs, rhs); }
inline String operator+(const String& lhs, LPCTSTR rhs) { return std::operator+(lhs, rhs); }
inline String operator+(const String& lhs, const std::string& rhs) { return std::operator+(lhs, rhs); }
inline String operator+(TCHAR lhs, const String& rhs) { return std::operator+(lhs, rhs); }
inline String operator+(LPCTSTR lhs, const String& rhs) { return std::operator+(lhs, rhs); }
inline String operator+(const std::string& lhs, const String& rhs) { return std::operator+(lhs, rhs); }
inline String operator+(const String& lhs, const String& rhs) { return std::operator+(lhs, rhs); }
#ifdef _SUPPORT_CPP11
inline String operator+(String&& lhs, TCHAR rhs) { return std::operator+(std::forward<String::Base>(lhs), rhs); }
inline String operator+(String&& lhs, LPCTSTR rhs) { return std::operator+(std::forward<String::Base>(lhs), rhs); }
inline String operator+(String&& lhs, const std::string& rhs) { return std::operator+(std::forward<String::Base>(lhs), rhs); }
inline String operator+(TCHAR lhs, String&& rhs) { return std::operator+(lhs, std::forward<String::Base>(rhs)); }
inline String operator+(LPCTSTR lhs, String&& rhs) { return std::operator+(lhs, std::forward<String::Base>(rhs)); }
inline String operator+(const std::string& lhs, String&& rhs) { return std::operator+(lhs, std::forward<String::Base>(rhs)); }
inline String operator+(const String& lhs, String&& rhs) { return std::operator+(lhs, std::forward<String::Base>(rhs)); }
inline String operator+(String&& lhs, const String& rhs) { return std::operator+(std::forward<String::Base>(lhs), rhs); }
inline String operator+(String&& lhs, String&& rhs) { return std::operator+(std::forward<String::Base>(lhs), std::forward<String::Base>(rhs)); }
#endif
/*----------------------------------------------------------------*/
} // namespace SEACAVE
namespace std {
//namespace tr1 {
// Specializations for unordered containers
template <> struct hash<SEACAVE::String> : public hash<string>{};
//} // namespace tr1
template <> struct equal_to<SEACAVE::String> : public equal_to<string>{};
} // namespace std
#endif // __SEACAVE_STRING_H__

435
libs/Common/Thread.h

@ -0,0 +1,435 @@ @@ -0,0 +1,435 @@
////////////////////////////////////////////////////////////////////
// Thread.h
//
// Copyright 2007 cDc@seacave
// Distributed under the Boost Software License, Version 1.0
// (See http://www.boost.org/LICENSE_1_0.txt)
#ifndef __SEACAVE_THREAD_H__
#define __SEACAVE_THREAD_H__
// I N C L U D E S /////////////////////////////////////////////////
#ifdef _MSC_VER
#include <windows.h>
#ifdef _SUPPORT_CPP11
#include <cstdint>
#else
#include <stdint.h>
#endif
#else
#include <pthread.h>
#include <sched.h>
#include <sys/resource.h>
#include <unistd.h>
#endif
// D E F I N E S ///////////////////////////////////////////////////
namespace SEACAVE {
// S T R U C T S ///////////////////////////////////////////////////
/**************************************************************************************
* Thread
* --------------
* basic thread control
**************************************************************************************/
class GENERAL_API Thread
{
public:
#ifdef _ENVIRONMENT64
typedef int64_t safe_t;
#else
typedef int32_t safe_t;
#endif
enum Priority {
IDLE = -2,
LOW = -1,
NORMAL = 0,
HIGH = 1
};
#ifdef _MSC_VER
typedef HANDLE thread_t;
typedef void* (STCALL *FncStart)(void*);
typedef struct STARTER_DATA {
FncStart fnStarter;
void* pData;
STARTER_DATA(FncStart _fnStarter, void* _pData) : fnStarter(_fnStarter), pData(_pData) {}
} StarterData;
Thread() : threadHandle(NULL), threadId(0) {}
virtual ~Thread() { stop(); }
inline bool isRunning() const { return (threadHandle != NULL); }
bool start(FncStart pfnStarter, void* pData=NULL) {
join();
return ((threadHandle = CreateThread(NULL, 0, &starterConvertor, new StarterData(pfnStarter, pData), 0, &threadId)) != NULL);
}
inline bool start() {
return start(&starter, this);
}
void stop() {
if (threadHandle) {
TerminateThread(threadHandle, -1);
CloseHandle(threadHandle);
threadHandle = NULL;
threadId = 0;
}
}
void join() {
if (threadHandle == NULL)
return;
WaitForSingleObject(threadHandle, INFINITE);
CloseHandle(threadHandle);
threadHandle = NULL;
threadId = 0;
}
inline void setThreadPriority(Priority p) const { ::SetThreadPriority(threadHandle, convertPriority(p)); }
inline Priority getThreadPriority() const { return convertPriority((PriorityOS)::GetThreadPriority(threadHandle)); }
static inline void setThreadPriority(thread_t th, Priority p) { ::SetThreadPriority(th, convertPriority(p)); }
static inline Priority getThreadPriority(thread_t th) { return convertPriority((PriorityOS)::GetThreadPriority(th)); }
static inline void sleep(uint32_t millis) { ::Sleep(millis); }
static inline void yield() { ::Sleep(0); }
static inline thread_t currentThread() { return ::GetCurrentThread(); }
static uint32_t hardwareConcurrency() {
SYSTEM_INFO info={{0}};
GetSystemInfo(&info);
return info.dwNumberOfProcessors;
}
STATIC_ASSERT(sizeof(int32_t)==sizeof(LONG));
static inline int32_t safeInc(volatile int32_t& v) { return InterlockedIncrement((volatile LONG*)&v); };
static inline int32_t safeDec(volatile int32_t& v) { return InterlockedDecrement((volatile LONG*)&v); };
static inline int32_t safeExchange(volatile int32_t& target, int32_t value) { return InterlockedExchange((volatile LONG*)&target, value); };
static inline int32_t safeCompareExchange(volatile int32_t& target, int32_t comp, int32_t value) { return InterlockedCompareExchange((volatile LONG*)&target, value, comp); };
STATIC_ASSERT(sizeof(int64_t)==sizeof(LONGLONG));
static inline int64_t safeInc(volatile int64_t& v) { return InterlockedIncrement64((volatile LONGLONG*)&v); };
static inline int64_t safeDec(volatile int64_t& v) { return InterlockedDecrement64((volatile LONGLONG*)&v); };
static inline int64_t safeExchange(volatile int64_t& target, int64_t value) { return InterlockedExchange64((volatile LONGLONG*)&target, value); };
static inline int64_t safeCompareExchange(volatile int64_t& target, int64_t comp, int64_t value) { return InterlockedCompareExchange64((volatile LONGLONG*)&target, value, comp); };
#else //_MSC_VER
typedef pthread_t thread_t;
typedef void* (STCALL *FncStart)(void*);
Thread() : threadHandle(0) {}
virtual ~Thread() { stop(); }
inline bool isRunning() const { return (threadHandle != 0); }
bool start(FncStart pfnStarter, void* pData=NULL) {
join();
return (pthread_create(&threadHandle, NULL, pfnStarter, pData) == 0);
}
bool start() {
return start(&starter, this);
}
void stop() {
if (threadHandle != 0) {
pthread_detach(threadHandle);
threadHandle = 0;
}
}
void join() {
if (threadHandle) {
pthread_join(threadHandle, 0);
threadHandle = 0;
}
}
inline void setThreadPriority(Priority p) const { setThreadPriority(threadHandle, p); }
inline Priority getThreadPriority() const { return getThreadPriority(threadHandle); }
static void setThreadPriority(thread_t th, Priority p) {
struct sched_param param;
param.sched_priority = convertPriority(p);
pthread_setschedparam(th, SCHED_OTHER, &param);
}
static Priority getThreadPriority(thread_t th) {
struct sched_param param;
int policy;
pthread_getschedparam(th, &policy, &param);
return convertPriority((PriorityOS)param.sched_priority);
}
static void sleep(uint32_t millis) { ::usleep(millis*1000); }
static void yield() { ::sched_yield(); }
static inline thread_t currentThread(){ return pthread_self(); }
static uint32_t hardwareConcurrency() { return sysconf(_SC_NPROCESSORS_ONLN); }
static inline int32_t safeInc(volatile int32_t& v) { return __sync_add_and_fetch(&v, 1); }
static inline int32_t safeDec(volatile int32_t& v) { return __sync_sub_and_fetch(&v, 1); }
static inline int32_t safeExchange(volatile int32_t& target, int32_t value) { return __sync_val_compare_and_swap(&target, target, value); }
static inline int32_t safeCompareExchange(volatile int32_t& target, int32_t comp, int32_t value) { return __sync_val_compare_and_swap(&target, comp, value); }
static inline int64_t safeInc(volatile int64_t& v) { return __sync_add_and_fetch(&v, 1); }
static inline int64_t safeDec(volatile int64_t& v) { return __sync_sub_and_fetch(&v, 1); }
static inline int64_t safeExchange(volatile int64_t& target, int64_t value) { return __sync_val_compare_and_swap(&target, target, value); }
static inline int64_t safeCompareExchange(volatile int64_t& target, int64_t comp, int64_t value) { return __sync_val_compare_and_swap(&target, comp, value); }
#endif //_MSC_VER
static unsigned getMaxThreads(unsigned threads) {
if (threads == 1)
return 1;
const unsigned maxThreads = hardwareConcurrency();
if (threads > 0 && threads < maxThreads)
return threads;
return maxThreads;
}
protected:
virtual void run() {}
protected:
#ifdef _MSC_VER
enum PriorityOS {
OS_IDLE = THREAD_PRIORITY_IDLE,
OS_LOW = THREAD_PRIORITY_BELOW_NORMAL,
OS_NORMAL = THREAD_PRIORITY_NORMAL,
OS_HIGH = THREAD_PRIORITY_ABOVE_NORMAL
};
static DWORD WINAPI starterConvertor(void* p)
{
CAutoPtr<StarterData> pData((StarterData*)p);
return (DWORD)reinterpret_cast<size_t>(pData->fnStarter(pData->pData));
}
#else //_MSC_VER
enum PriorityOS {
OS_IDLE = 19,
OS_LOW = 10,
OS_NORMAL = 0,
OS_HIGH = -10
};
#endif //_MSC_VER
static inline PriorityOS convertPriority(Priority p) {
switch (p) {
case IDLE: return OS_IDLE;
case LOW: return OS_LOW;
case NORMAL: return OS_NORMAL;
case HIGH: return OS_HIGH;
}
return OS_NORMAL;
}
static inline Priority convertPriority(PriorityOS p) {
switch (p) {
case OS_IDLE: return IDLE;
case OS_LOW: return LOW;
case OS_NORMAL: return NORMAL;
case OS_HIGH: return HIGH;
}
return NORMAL;
}
static void* starter(void* p)
{
((Thread*)p)->run();
return NULL;
}
protected:
thread_t threadHandle;
#ifdef _MSC_VER
DWORD threadId;
#endif
};
/*----------------------------------------------------------------*/
/**************************************************************************************
* ThreadPool
* --------------
* basic thread pool
**************************************************************************************/
class GENERAL_API ThreadPool
{
public:
typedef uint32_t size_type;
typedef Thread value_type;
typedef value_type* iterator;
typedef const value_type* const_iterator;
typedef value_type& reference;
typedef const value_type& const_reference;
typedef CLISTDEFIDX(Thread,size_type) Threads;
public:
inline ThreadPool() {}
inline ThreadPool(size_type nThreads) : _threads(nThreads>0?nThreads:Thread::hardwareConcurrency()) {}
inline ThreadPool(size_type nThreads, Thread::FncStart pfnStarter, void* pData=NULL) : _threads(nThreads>0?nThreads:Thread::hardwareConcurrency()) { start(pfnStarter, pData); }
inline ~ThreadPool() { join(); }
#ifdef _SUPPORT_CPP11
inline ThreadPool(ThreadPool&& rhs) : _threads(std::forward<Threads>(rhs._threads)) {
}
inline ThreadPool& operator=(ThreadPool&& rhs) {
_threads.Swap(rhs._threads);
return *this;
}
#endif
// wait for all running threads to finish and resize threads array
void resize(size_type nThreads) {
join();
_threads.resize(nThreads);
}
// start all threads with the given function
bool start(Thread::FncStart pfnStarter, void* pData=NULL) {
for (Thread& thread: _threads)
if (!thread.start(pfnStarter, pData))
return false;
return true;
}
// wait for all running threads to finish
void join() {
for (Thread& thread: _threads)
thread.join();
}
// stop all threads
void stop() {
for (Thread& thread: _threads)
thread.stop();
}
// wait for all running threads to finish and release threads array
void Release() {
join();
_threads.Release();
}
inline bool empty() const { return _threads.empty(); }
inline size_type size() const { return _threads.size(); }
inline const_iterator cbegin() const { return _threads.cbegin(); }
inline const_iterator cend() const { return _threads.cend(); }
inline const_iterator begin() const { return _threads.begin(); }
inline const_iterator end() const { return _threads.end(); }
inline iterator begin() { return _threads.begin(); }
inline iterator end() { return _threads.end(); }
inline const_reference operator[](size_type index) const { return _threads[index]; }
inline reference operator[](size_type index) { return _threads[index]; }
protected:
Threads _threads;
};
/*----------------------------------------------------------------*/
/**************************************************************************************
* Process
* --------------
* basic process control
**************************************************************************************/
class GENERAL_API Process
{
public:
enum Priority {
IDLE = -3,
LOW = -2,
BELOWNORMAL = -1,
NORMAL = 0,
ABOVENORMAL = 1,
HIGH = 2,
REALTIME = 3
};
#ifdef _MSC_VER
typedef HANDLE process_t;
static inline process_t getCurrentProcessID() { return ::GetCurrentProcess(); }
static inline void setProcessPriority(process_t id, Priority p) { ::SetPriorityClass(id, convertPriority(p)); }
static inline Priority getProcessPriority(process_t id) { return convertPriority((PriorityOS)::GetPriorityClass(id)); }
#else //_MSC_VER
typedef id_t process_t;
static inline process_t getCurrentProcessID() { return ::getpid(); }
static inline void setProcessPriority(process_t id, Priority p) { ::setpriority(PRIO_PROCESS, id, convertPriority(p)); }
static inline Priority getProcessPriority(process_t id) { return convertPriority((PriorityOS)::getpriority(PRIO_PROCESS, id)); }
#endif //_MSC_VER
static inline void setCurrentProcessPriority(Priority p) { setProcessPriority(getCurrentProcessID(), p); }
static inline Priority getCurrentProcessPriority() { return getProcessPriority(getCurrentProcessID()); }
protected:
#ifdef _MSC_VER
enum PriorityOS {
OS_IDLE = IDLE_PRIORITY_CLASS,
OS_LOW = PROCESS_MODE_BACKGROUND_BEGIN,
OS_BELOWNORMAL = BELOW_NORMAL_PRIORITY_CLASS,
OS_NORMAL = NORMAL_PRIORITY_CLASS,
OS_ABOVENORMAL = ABOVE_NORMAL_PRIORITY_CLASS,
OS_HIGH = HIGH_PRIORITY_CLASS,
OS_REALTIME = REALTIME_PRIORITY_CLASS
};
#else //_MSC_VER
enum PriorityOS {
OS_IDLE = 19,
OS_LOW = 15,
OS_BELOWNORMAL = 10,
OS_NORMAL = 0,
OS_ABOVENORMAL = -10,
OS_HIGH = -15,
OS_REALTIME = -20
};
#endif //_MSC_VER
static inline PriorityOS convertPriority(Priority p) {
switch (p) {
case IDLE: return OS_IDLE;
case LOW: return OS_LOW;
case BELOWNORMAL: return OS_BELOWNORMAL;
case NORMAL: return OS_NORMAL;
case ABOVENORMAL: return OS_ABOVENORMAL;
case HIGH: return OS_HIGH;
case REALTIME: return OS_REALTIME;
}
return OS_NORMAL;
}
static inline Priority convertPriority(PriorityOS p) {
switch (p) {
case OS_IDLE: return IDLE;
case OS_LOW: return LOW;
case OS_BELOWNORMAL: return BELOWNORMAL;
case OS_NORMAL: return NORMAL;
case OS_ABOVENORMAL: return ABOVENORMAL;
case OS_HIGH: return HIGH;
case OS_REALTIME: return REALTIME;
}
return NORMAL;
}
};
/*----------------------------------------------------------------*/
} // namespace SEACAVE
#endif // __SEACAVE_THREAD_H__

165
libs/Common/Timer.cpp

@ -0,0 +1,165 @@ @@ -0,0 +1,165 @@
////////////////////////////////////////////////////////////////////
// Timer.cpp
//
// Copyright 2007 cDc@seacave
// Distributed under the Boost Software License, Version 1.0
// (See http://www.boost.org/LICENSE_1_0.txt)
#include "Common.h"
#include "Timer.h"
using namespace SEACAVE;
// D E F I N E S ///////////////////////////////////////////////////
// S T R U C T S ///////////////////////////////////////////////////
/**
* Constructor
*/
Timer::Timer(uint8_t nHH, uint8_t nMM)
{
m_fElapsedTime = 1.f;
m_fTimeScale = 1.f;
#ifdef FIX_FPS
m_fCurrentFramesTime = 0.f;
m_nCurrentFrames = 0;
m_nLastSecFrames = 0;
#endif // FIX_FPS
// set clock
m_fClock = 0.f;
if (nHH > 23)
nHH = 0;
if (nMM > 59)
nMM = 0;
m_nHH = nHH;
m_nMM = nMM;
m_nSS = 0;
m_nCrntTime = GetSysTime();
} // constructor
/*----------------------------------------------------------------*/
Timer::~Timer()
{
}
/*----------------------------------------------------------------*/
/**
* Counts the actual time and adjusts clock.
*/
void Timer::Update()
{
const SysType timeNow = GetSysTime();
m_fElapsedTime = SysTime2Time(timeNow - m_nCrntTime);
m_nCrntTime = timeNow;
#ifdef FIX_FPS
// compute FPS
++m_nCurrentFrames;
if ((m_fCurrentFramesTime += m_fElapsedTime) >= 1.f) {
m_nLastSecFrames = (uint32_t)((Type)m_nCurrentFrames/m_fCurrentFramesTime);
m_fCurrentFramesTime = 0.f;
m_nCurrentFrames = 0;
}
#endif // FIX_FPS
// adjust clock by seconds passed
m_fClock += (m_fElapsedTime * m_fTimeScale);
if (m_fClock >= 1.f) {
m_nSS++;
m_fClock = 0.f;
}
if (m_nSS >= 60) {
m_nMM++;
m_nSS = 0;
}
if (m_nMM >= 60) {
m_nHH++;
m_nMM = 0;
}
if (m_nHH >= 24)
m_nHH = 0;
} // Update
/*----------------------------------------------------------------*/
/**
* Sets the clock to any given starting time.
*/
void Timer::SetClock(uint8_t nHH, uint8_t nMM)
{
// set clock
if (nHH > 23)
nHH = 0;
if (nMM > 59)
nMM = 0;
m_nHH = nHH;
m_nMM = nMM;
} // SetClock
/*----------------------------------------------------------------*/
/**
* Gives you a time string with hours, minutes and seconds as return
* and hours and/or minutes as reference parameters.
*/
LPTSTR Timer::GetClock(uint8_t* nHH, uint8_t* nMM, LPTSTR szChar) const
{
if (nHH != NULL)
*nHH = m_nHH;
if (nMM != NULL)
*nMM = m_nMM;
if (szChar == NULL)
return NULL;
_sntprintf(szChar, 32, "%.2d:%.2d:%.2d", m_nHH, m_nMM, m_nSS);
return szChar;
} // GetClock
/*----------------------------------------------------------------*/
#ifdef TIMER_OLDSUPPORT
// Check performance counter
bool HasPerfCounter()
{
SysType nPerfCnt;
return QueryPerformanceFrequency((LARGE_INTEGER*)&nPerfCnt) == TRUE;
}
const bool Timer::ms_bPerfFlag = HasPerfCounter();
/*----------------------------------------------------------------*/
#endif
// Get system time factor used to convert to milliseconds
Timer::Type GetSysTimeFactor()
{
#ifdef _MSC_VER
Timer::SysType nPerfCnt;
const BOOL bPerfFlag = QueryPerformanceFrequency((LARGE_INTEGER*)&nPerfCnt);
#ifdef TIMER_OLDSUPPORT
// either QueryPerformanceCounter or GetTickCount
return (bPerfFlag ? 1000.f / nPerfCnt : 1.f);
#else
ASSERT(bPerfFlag);
return 1000.f / nPerfCnt;
#endif
#else // _MSC_VER
return 0.001f;
#endif // _MSC_VER
}
const Timer::Type Timer::ms_fTimeFactor = GetSysTimeFactor();
/*----------------------------------------------------------------*/
Timer::Type Timer::GetTimeFactor()
{
return ms_fTimeFactor;
}
/*----------------------------------------------------------------*/

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save