// This file is part of OpenCV project. // It is subject to the license terms in the LICENSE file found in the top-level directory // of this distribution and at http://opencv.org/license.html // Partially rewritten from https://github.com/Nerei/kinfu_remake // Copyright(c) 2012, Anatoly Baksheev. All rights reserved. #ifndef OPENCV_3D_HASH_TSDF_FUNCTIONS_HPP #define OPENCV_3D_HASH_TSDF_FUNCTIONS_HPP #include #include "utils.hpp" #include "tsdf_functions.hpp" #define USE_INTERPOLATION_IN_GETNORMAL 1 #define VOLUMES_SIZE 8192 namespace cv { //! Spatial hashing struct tsdf_hash { size_t operator()(const Vec3i& x) const noexcept { size_t seed = 0; constexpr uint32_t GOLDEN_RATIO = 0x9e3779b9; for (uint16_t i = 0; i < 3; i++) { seed ^= std::hash()(x[i]) + GOLDEN_RATIO + (seed << 6) + (seed >> 2); } return seed; } }; struct VolumeUnit { cv::Vec3i coord; int index; cv::Matx44f pose; int lastVisibleIndex = 0; bool isActive; }; class CustomHashSet { public: static const int hashDivisor = 32768; static const int startCapacity = 2048; std::vector hashes; // 0-3 for key, 4th for internal use // don't keep keep value std::vector data; int capacity; int last; CustomHashSet() { hashes.resize(hashDivisor); for (int i = 0; i < hashDivisor; i++) hashes[i] = -1; capacity = startCapacity; data.resize(capacity); for (int i = 0; i < capacity; i++) data[i] = { 0, 0, 0, -1 }; last = 0; } ~CustomHashSet() { } inline size_t calc_hash(Vec3i x) const { uint32_t seed = 0; constexpr uint32_t GOLDEN_RATIO = 0x9e3779b9; for (int i = 0; i < 3; i++) { seed ^= x[i] + GOLDEN_RATIO + (seed << 6) + (seed >> 2); } return seed; } // should work on existing elements too // 0 - need resize // 1 - idx is inserted // 2 - idx already exists int insert(Vec3i idx) { if (last < capacity) { int hash = int(calc_hash(idx) % hashDivisor); int place = hashes[hash]; if (place >= 0) { int oldPlace = place; while (place >= 0) { if (data[place][0] == idx[0] && data[place][1] == idx[1] && data[place][2] == idx[2]) return 2; else { oldPlace = place; place = data[place][3]; //std::cout << "place=" << place << std::endl; } } // found, create here data[oldPlace][3] = last; } else { // insert at last hashes[hash] = last; } data[last][0] = idx[0]; data[last][1] = idx[1]; data[last][2] = idx[2]; data[last][3] = -1; last++; return 1; } else return 0; } int find(Vec3i idx) const { int hash = int(calc_hash(idx) % hashDivisor); int place = hashes[hash]; // search a place while (place >= 0) { if (data[place][0] == idx[0] && data[place][1] == idx[1] && data[place][2] == idx[2]) break; else { place = data[place][3]; } } return place; } }; // TODO: remove this structure as soon as HashTSDFGPU data is completely on GPU; // until then CustomHashTable can be replaced by this one if needed const int NAN_ELEMENT = -2147483647; struct Volume_NODE { Vec4i idx = Vec4i(NAN_ELEMENT); int32_t row = -1; int32_t nextVolumeRow = -1; int32_t dummy = 0; int32_t dummy2 = 0; }; const int _hash_divisor = 32768; const int _list_size = 4; class VolumesTable { public: const int hash_divisor = _hash_divisor; const int list_size = _list_size; const int32_t free_row = -1; const int32_t free_isActive = 0; const cv::Vec4i nan4 = cv::Vec4i(NAN_ELEMENT); int bufferNums; cv::Mat volumes; VolumesTable() : bufferNums(1) { this->volumes = cv::Mat(hash_divisor * list_size, 1, rawType()); for (int i = 0; i < volumes.size().height; i++) { Volume_NODE* v = volumes.ptr(i); v->idx = nan4; v->row = -1; v->nextVolumeRow = -1; } } const VolumesTable& operator=(const VolumesTable& vt) { this->volumes = vt.volumes; this->bufferNums = vt.bufferNums; return *this; } ~VolumesTable() {}; bool insert(Vec3i idx, int row) { CV_Assert(row >= 0); int bufferNum = 0; int hash = int(calc_hash(idx) % hash_divisor); int start = getPos(idx, bufferNum); int i = start; while (i >= 0) { Volume_NODE* v = volumes.ptr(i); if (v->idx[0] == NAN_ELEMENT) { Vec4i idx4(idx[0], idx[1], idx[2], 0); bool extend = false; if (i != start && i % list_size == 0) { if (bufferNum >= bufferNums - 1) { extend = true; volumes.resize(hash_divisor * bufferNums); bufferNums++; } bufferNum++; v->nextVolumeRow = (bufferNum * hash_divisor + hash) * list_size; } else { v->nextVolumeRow = i + 1; } v->idx = idx4; v->row = row; return extend; } i = v->nextVolumeRow; } return false; } int findRow(Vec3i idx) const { int bufferNum = 0; int i = getPos(idx, bufferNum); while (i >= 0) { const Volume_NODE* v = volumes.ptr(i); if (v->idx == Vec4i(idx[0], idx[1], idx[2], 0)) return v->row; else i = v->nextVolumeRow; } return -1; } inline int getPos(Vec3i idx, int bufferNum) const { int hash = int(calc_hash(idx) % hash_divisor); return (bufferNum * hash_divisor + hash) * list_size; } inline size_t calc_hash(Vec3i x) const { uint32_t seed = 0; constexpr uint32_t GOLDEN_RATIO = 0x9e3779b9; for (int i = 0; i < 3; i++) { seed ^= x[i] + GOLDEN_RATIO + (seed << 6) + (seed >> 2); } return seed; } }; int calcVolumeUnitDegree(Point3i volumeResolution); typedef std::unordered_map VolumeUnitIndexes; void integrateHashTsdfVolumeUnit( const VolumeSettings& settings, const Matx44f& cameraPose, int& lastVolIndex, const int frameId, const int volumeUnitDegree, bool enableGrowth, InputArray _depth, InputArray _pixNorms, InputOutputArray _volUnitsData, VolumeUnitIndexes& volumeUnits); void raycastHashTsdfVolumeUnit( const VolumeSettings& settings, const Matx44f& cameraPose, int height, int width, InputArray intr, const int volumeUnitDegree, InputArray _volUnitsData, const VolumeUnitIndexes& volumeUnits, OutputArray _points, OutputArray _normals); void fetchNormalsFromHashTsdfVolumeUnit( const VolumeSettings& settings, InputArray _volUnitsData, const VolumeUnitIndexes& volumeUnits, const int volumeUnitDegree, InputArray _points, OutputArray _normals); void fetchPointsNormalsFromHashTsdfVolumeUnit( const VolumeSettings& settings, InputArray _volUnitsData, const VolumeUnitIndexes& volumeUnits, const int volumeUnitDegree, OutputArray _points, OutputArray _normals); #ifdef HAVE_OPENCL void ocl_integrateHashTsdfVolumeUnit( const VolumeSettings& settings, const Matx44f& cameraPose, int& lastVolIndex, const int frameId, int& bufferSizeDegree, const int volumeUnitDegree, bool enableGrowth, InputArray _depth, InputArray _pixNorms, InputArray _lastVisibleIndices, InputOutputArray _volUnitsDataCopy, InputOutputArray _volUnitsData, CustomHashSet& hashTable, InputArray _isActiveFlags); void ocl_raycastHashTsdfVolumeUnit( const VolumeSettings& settings, const Matx44f& cameraPose, int height, int width, InputArray intr, const int volumeUnitDegree, const CustomHashSet& hashTable, InputArray _volUnitsData, OutputArray _points, OutputArray _normals); void ocl_fetchNormalsFromHashTsdfVolumeUnit( const VolumeSettings& settings, const int volumeUnitDegree, InputArray _volUnitsData, InputArray _volUnitsDataCopy, const CustomHashSet& hashTable, InputArray _points, OutputArray _normals); void ocl_fetchPointsNormalsFromHashTsdfVolumeUnit( const VolumeSettings& settings, const int volumeUnitDegree, InputArray _volUnitsData, InputArray _volUnitsDataCopy, const CustomHashSet& hashTable, OutputArray _points, OutputArray _normals); #endif } // namespace cv #endif