1 // This file is part of OpenCV project.
2 // It is subject to the license terms in the LICENSE file found in the top-level directory
3 // of this distribution and at http://opencv.org/license.html
4 
5 // This code is also subject to the license terms in the LICENSE_KinectFusion.md file found in this
6 // module's directory
7 
8 #include "fast_icp.hpp"
9 #include "hash_tsdf.hpp"
10 #include "kinfu_frame.hpp"
11 #include "precomp.hpp"
12 #include "submap.hpp"
13 #include "tsdf.hpp"
14 
15 namespace cv
16 {
17 namespace large_kinfu
18 {
19 using namespace kinfu;
20 
defaultParams()21 Ptr<Params> Params::defaultParams()
22 {
23     Params p;
24 
25     //! Frame parameters
26     {
27         p.frameSize = Size(640, 480);
28 
29         float fx, fy, cx, cy;
30         fx = fy = 525.f;
31         cx      = p.frameSize.width / 2.0f - 0.5f;
32         cy      = p.frameSize.height / 2.0f - 0.5f;
33         p.intr  = Matx33f(fx, 0, cx, 0, fy, cy, 0, 0, 1);
34 
35         // 5000 for the 16-bit PNG files
36         // 1 for the 32-bit float images in the ROS bag files
37         p.depthFactor = 5000;
38 
39         // sigma_depth is scaled by depthFactor when calling bilateral filter
40         p.bilateral_sigma_depth   = 0.04f;  // meter
41         p.bilateral_sigma_spatial = 4.5;    // pixels
42         p.bilateral_kernel_size   = 7;      // pixels
43         p.truncateThreshold       = 0.f;    // meters
44     }
45     //! ICP parameters
46     {
47         p.icpAngleThresh = (float)(30. * CV_PI / 180.);  // radians
48         p.icpDistThresh  = 0.1f;                         // meters
49 
50         p.icpIterations = { 10, 5, 4 };
51         p.pyramidLevels = (int)p.icpIterations.size();
52     }
53     //! Volume parameters
54     {
55         float volumeSize                   = 3.0f;
56         p.volumeParams.type                = VolumeType::TSDF;
57         p.volumeParams.resolution          = Vec3i::all(512);
58         p.volumeParams.pose                = Affine3f().translate(Vec3f(-volumeSize / 2.f, -volumeSize / 2.f, 0.5f));
59         p.volumeParams.voxelSize           = volumeSize / 512.f;            // meters
60         p.volumeParams.tsdfTruncDist       = 7 * p.volumeParams.voxelSize;  // about 0.04f in meters
61         p.volumeParams.maxWeight           = 64;                            // frames
62         p.volumeParams.raycastStepFactor   = 0.25f;                         // in voxel sizes
63         p.volumeParams.depthTruncThreshold = p.truncateThreshold;
64     }
65     //! Unused parameters
66     p.tsdf_min_camera_movement = 0.f;              // meters, disabled
67     p.lightPose                = Vec3f::all(0.f);  // meters
68 
69     return makePtr<Params>(p);
70 }
71 
coarseParams()72 Ptr<Params> Params::coarseParams()
73 {
74     Ptr<Params> p = defaultParams();
75 
76     //! Reduce ICP iterations and pyramid levels
77     {
78         p->icpIterations = { 5, 3, 2 };
79         p->pyramidLevels = (int)p->icpIterations.size();
80     }
81     //! Make the volume coarse
82     {
83         float volumeSize                  = 3.f;
84         p->volumeParams.resolution        = Vec3i::all(128);  // number of voxels
85         p->volumeParams.voxelSize         = volumeSize / 128.f;
86         p->volumeParams.tsdfTruncDist     = 2 * p->volumeParams.voxelSize;  // 0.04f in meters
87         p->volumeParams.raycastStepFactor = 0.75f;                          // in voxel sizes
88     }
89     return p;
90 }
hashTSDFParams(bool isCoarse)91 Ptr<Params> Params::hashTSDFParams(bool isCoarse)
92 {
93     Ptr<Params> p;
94     if (isCoarse)
95         p = coarseParams();
96     else
97         p = defaultParams();
98 
99     p->volumeParams.type                = VolumeType::HASHTSDF;
100     p->volumeParams.depthTruncThreshold = rgbd::Odometry::DEFAULT_MAX_DEPTH();
101     p->volumeParams.unitResolution      = 16;
102     return p;
103 }
104 
105 // MatType should be Mat or UMat
106 template<typename MatType>
107 class LargeKinfuImpl : public LargeKinfu
108 {
109    public:
110     LargeKinfuImpl(const Params& _params);
111     virtual ~LargeKinfuImpl();
112 
113     const Params& getParams() const CV_OVERRIDE;
114 
115     void render(OutputArray image) const CV_OVERRIDE;
116     void render(OutputArray image, const Matx44f& cameraPose) const CV_OVERRIDE;
117 
118     virtual void getCloud(OutputArray points, OutputArray normals) const CV_OVERRIDE;
119     void getPoints(OutputArray points) const CV_OVERRIDE;
120     void getNormals(InputArray points, OutputArray normals) const CV_OVERRIDE;
121 
122     void reset() CV_OVERRIDE;
123 
124     const Affine3f getPose() const CV_OVERRIDE;
125 
126     bool update(InputArray depth) CV_OVERRIDE;
127 
128     bool updateT(const MatType& depth);
129 
130    private:
131     Params params;
132 
133     cv::Ptr<ICP> icp;
134     //! TODO: Submap manager and Pose graph optimizer
135     cv::Ptr<SubmapManager<MatType>> submapMgr;
136 
137     int frameCounter;
138     Affine3f pose;
139 };
140 
141 template<typename MatType>
LargeKinfuImpl(const Params & _params)142 LargeKinfuImpl<MatType>::LargeKinfuImpl(const Params& _params)
143     : params(_params)
144 {
145     icp = makeICP(params.intr, params.icpIterations, params.icpAngleThresh, params.icpDistThresh);
146 
147     submapMgr = cv::makePtr<SubmapManager<MatType>>(params.volumeParams);
148     reset();
149     submapMgr->createNewSubmap(true);
150 
151 }
152 
153 template<typename MatType>
reset()154 void LargeKinfuImpl<MatType>::reset()
155 {
156     frameCounter = 0;
157     pose         = Affine3f::Identity();
158     submapMgr->reset();
159 }
160 
161 template<typename MatType>
~LargeKinfuImpl()162 LargeKinfuImpl<MatType>::~LargeKinfuImpl()
163 {
164 }
165 
166 template<typename MatType>
getParams() const167 const Params& LargeKinfuImpl<MatType>::getParams() const
168 {
169     return params;
170 }
171 
172 template<typename MatType>
getPose() const173 const Affine3f LargeKinfuImpl<MatType>::getPose() const
174 {
175     return pose;
176 }
177 
178 template<>
update(InputArray _depth)179 bool LargeKinfuImpl<Mat>::update(InputArray _depth)
180 {
181     CV_Assert(!_depth.empty() && _depth.size() == params.frameSize);
182 
183     Mat depth;
184     if (_depth.isUMat())
185     {
186         _depth.copyTo(depth);
187         return updateT(depth);
188     }
189     else
190     {
191         return updateT(_depth.getMat());
192     }
193 }
194 
195 template<>
update(InputArray _depth)196 bool LargeKinfuImpl<UMat>::update(InputArray _depth)
197 {
198     CV_Assert(!_depth.empty() && _depth.size() == params.frameSize);
199 
200     UMat depth;
201     if (!_depth.isUMat())
202     {
203         _depth.copyTo(depth);
204         return updateT(depth);
205     }
206     else
207     {
208         return updateT(_depth.getUMat());
209     }
210 }
211 
212 
213 template<typename MatType>
updateT(const MatType & _depth)214 bool LargeKinfuImpl<MatType>::updateT(const MatType& _depth)
215 {
216     CV_TRACE_FUNCTION();
217 
218     MatType depth;
219     if (_depth.type() != DEPTH_TYPE)
220         _depth.convertTo(depth, DEPTH_TYPE);
221     else
222         depth = _depth;
223 
224     std::vector<MatType> newPoints, newNormals;
225     makeFrameFromDepth(depth, newPoints, newNormals, params.intr, params.pyramidLevels, params.depthFactor,
226                        params.bilateral_sigma_depth, params.bilateral_sigma_spatial, params.bilateral_kernel_size,
227                        params.truncateThreshold);
228 
229     CV_LOG_INFO(NULL, "Current frameID: " << frameCounter);
230     for (const auto& it : submapMgr->activeSubmaps)
231     {
232         int currTrackingId = it.first;
233         auto submapData = it.second;
234         Ptr<Submap<MatType>> currTrackingSubmap = submapMgr->getSubmap(currTrackingId);
235         Affine3f affine;
236         CV_LOG_INFO(NULL, "Current tracking ID: " << currTrackingId);
237 
238         if(frameCounter == 0) //! Only one current tracking map
239         {
240             currTrackingSubmap->integrate(depth, params.depthFactor, params.intr, frameCounter);
241             currTrackingSubmap->pyrPoints = newPoints;
242             currTrackingSubmap->pyrNormals = newNormals;
243             continue;
244         }
245 
246         //1. Track
247         bool trackingSuccess =
248             icp->estimateTransform(affine, currTrackingSubmap->pyrPoints, currTrackingSubmap->pyrNormals, newPoints, newNormals);
249         if (trackingSuccess)
250             currTrackingSubmap->composeCameraPose(affine);
251         else
252         {
253             CV_LOG_INFO(NULL, "Tracking failed");
254             continue;
255         }
256 
257         //2. Integrate
258         if(submapData.type == SubmapManager<MatType>::Type::NEW || submapData.type == SubmapManager<MatType>::Type::CURRENT)
259         {
260             float rnorm = (float)cv::norm(affine.rvec());
261             float tnorm = (float)cv::norm(affine.translation());
262             // We do not integrate volume if camera does not move
263             if ((rnorm + tnorm) / 2 >= params.tsdf_min_camera_movement)
264                 currTrackingSubmap->integrate(depth, params.depthFactor, params.intr, frameCounter);
265         }
266 
267         //3. Raycast
268         currTrackingSubmap->raycast(currTrackingSubmap->cameraPose, params.intr, params.frameSize, currTrackingSubmap->pyrPoints[0], currTrackingSubmap->pyrNormals[0]);
269 
270         currTrackingSubmap->updatePyrPointsNormals(params.pyramidLevels);
271 
272         CV_LOG_INFO(NULL, "Submap: " << currTrackingId << " Total allocated blocks: " << currTrackingSubmap->getTotalAllocatedBlocks());
273         CV_LOG_INFO(NULL, "Submap: " << currTrackingId << " Visible blocks: " << currTrackingSubmap->getVisibleBlocks(frameCounter));
274 
275     }
276     //4. Update map
277     bool isMapUpdated = submapMgr->updateMap(frameCounter, newPoints, newNormals);
278 
279     if(isMapUpdated)
280     {
281         // TODO: Convert constraints to posegraph
282         Ptr<kinfu::detail::PoseGraph> poseGraph = submapMgr->MapToPoseGraph();
283         CV_LOG_INFO(NULL, "Created posegraph");
284         int iters = poseGraph->optimize();
285         if (iters < 0)
286         {
287             CV_LOG_INFO(NULL, "Failed to perform pose graph optimization");
288             return false;
289         }
290 
291         submapMgr->PoseGraphToMap(poseGraph);
292 
293     }
294     CV_LOG_INFO(NULL, "Number of submaps: " << submapMgr->submapList.size());
295 
296     frameCounter++;
297     return true;
298 }
299 
300 
301 template<typename MatType>
render(OutputArray image) const302 void LargeKinfuImpl<MatType>::render(OutputArray image) const
303 {
304     CV_TRACE_FUNCTION();
305     auto currSubmap = submapMgr->getCurrentSubmap();
306     //! TODO: Can render be dependent on current submap
307     renderPointsNormals(currSubmap->pyrPoints[0], currSubmap->pyrNormals[0], image, params.lightPose);
308 }
309 
310 
311 template<typename MatType>
render(OutputArray image,const Matx44f & _cameraPose) const312 void LargeKinfuImpl<MatType>::render(OutputArray image, const Matx44f& _cameraPose) const
313 {
314     CV_TRACE_FUNCTION();
315 
316     Affine3f cameraPose(_cameraPose);
317     auto currSubmap = submapMgr->getCurrentSubmap();
318     MatType points, normals;
319     currSubmap->raycast(cameraPose, params.intr, params.frameSize, points, normals);
320     renderPointsNormals(points, normals, image, params.lightPose);
321 }
322 
323 
324 template<typename MatType>
getCloud(OutputArray p,OutputArray n) const325 void LargeKinfuImpl<MatType>::getCloud(OutputArray p, OutputArray n) const
326 {
327     auto currSubmap = submapMgr->getCurrentSubmap();
328     currSubmap->volume->fetchPointsNormals(p, n);
329 }
330 
331 template<typename MatType>
getPoints(OutputArray points) const332 void LargeKinfuImpl<MatType>::getPoints(OutputArray points) const
333 {
334     auto currSubmap = submapMgr->getCurrentSubmap();
335     currSubmap->volume->fetchPointsNormals(points, noArray());
336 }
337 
338 template<typename MatType>
getNormals(InputArray points,OutputArray normals) const339 void LargeKinfuImpl<MatType>::getNormals(InputArray points, OutputArray normals) const
340 {
341     auto currSubmap = submapMgr->getCurrentSubmap();
342     currSubmap->volume->fetchNormals(points, normals);
343 }
344 
345 // importing class
346 
347 #ifdef OPENCV_ENABLE_NONFREE
348 
create(const Ptr<Params> & params)349 Ptr<LargeKinfu> LargeKinfu::create(const Ptr<Params>& params)
350 {
351     CV_Assert((int)params->icpIterations.size() == params->pyramidLevels);
352     CV_Assert(params->intr(0, 1) == 0 && params->intr(1, 0) == 0 && params->intr(2, 0) == 0 && params->intr(2, 1) == 0 &&
353               params->intr(2, 2) == 1);
354 #ifdef HAVE_OPENCL
355     if (cv::ocl::useOpenCL())
356         return makePtr<LargeKinfuImpl<UMat>>(*params);
357 #endif
358     return makePtr<LargeKinfuImpl<Mat>>(*params);
359 }
360 
361 #else
create(const Ptr<Params> &)362 Ptr<LargeKinfu> LargeKinfu::create(const Ptr<Params>& /* params */)
363 {
364     CV_Error(Error::StsNotImplemented,
365              "This algorithm is patented and is excluded in this configuration; "
366              "Set OPENCV_ENABLE_NONFREE CMake option and rebuild the library");
367 }
368 #endif
369 }  // namespace large_kinfu
370 }  // namespace cv
371