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