Skip to content

Commit ff8d939

Browse files
committed
CObservation3DRangeScan: add mask; more SSE2 optimized code
1 parent b8345b3 commit ff8d939

File tree

4 files changed

+65
-30
lines changed

4 files changed

+65
-30
lines changed

doc/doxygen-pages/changeLog_doc.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,17 @@
1818
- Changes in apps:
1919
- [RawLogViewer](http://www.mrpt.org/list-of-mrpt-apps/rawlogviewer/): Now displays a textual and graphical representation of all observation timestamps, useful to quickly detect sensor "shortages" or temporary failures.
2020
- Changes in libraries:
21+
- \ref mrpt_obs_grp
22+
- mrpt::obs::CObservation3DRangeScan:
23+
- Now uses more SSE2 optimized code
24+
- Now allows filtering depth images by minimum depth mask in mrpt::obs::CObservation3DRangeScan::project3DPointsFromDepthImageInto() and mrpt::obs::CObservation3DRangeScan::convertTo2DScan()
2125
- \ref mrpt_hwdrivers_grp
2226
- mrpt::hwdrivers::CGenericSensor: external image format is now `png` by default instead of `jpg` to avoid losses.
2327
- mrpt::hwdrivers::COpenNI2Generic:
2428
- refactored to expose more methods and allow changing parameters via its constructor.
2529
- Now supports reading from an IR, RGB and Depth channels independenty.
2630
- Changes in build system:
27-
- [Windows only] `DLL`s/`LIB`s now have the signature `lib-${name}${2-digits-version}${compiler-name}_{x32|x64}.{dll/lib}`, allowing several MRPT versions to coexist in the system PATH.
31+
- [Windows only] `DLL`s/`LIB`s now have the signature `lib-${name}${2-digits-version}${compiler-name}_{x32|x64}.{dll/lib}`, allowing several MRPT versions to coexist in the system PATH.
2832
- Debian package: depends on libopenni-dev
2933
- BUG FIXES:
3034
- Fix inconsistent state after calling mrpt::obs::CObservation3DRangeScan::swap()

libs/obs/include/mrpt/obs/CObservation3DRangeScan.h

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ namespace obs
3131
namespace detail {
3232
// Implemented in CObservation3DRangeScan_project3D_impl.h
3333
template <class POINTMAP>
34-
void project3DPointsFromDepthImageInto(CObservation3DRangeScan & src_obs,POINTMAP & dest_pointcloud,const bool takeIntoAccountSensorPoseOnRobot,const mrpt::poses::CPose3D * robotPoseInTheWorld,const bool PROJ3D_USE_LUT);
34+
void project3DPointsFromDepthImageInto(CObservation3DRangeScan & src_obs,POINTMAP & dest_pointcloud,const bool takeIntoAccountSensorPoseOnRobot,const mrpt::poses::CPose3D * robotPoseInTheWorld,const bool PROJ3D_USE_LUT, const mrpt::math::CMatrix * minRangeMask);
3535
}
3636

3737
/** Declares a class derived from "CObservation" that encapsules a 3D range scan measurement, as from a time-of-flight range camera or any other RGBD sensor.
@@ -172,6 +172,7 @@ namespace obs
172172
* the points are transformed with \a sensorPose. Furthermore, if provided, those coordinates are transformed with \a robotPoseInTheWorld
173173
*
174174
* \param[in] PROJ3D_USE_LUT (Only when range_is_depth=true) Whether to use a Look-up-table (LUT) to speed up the conversion. It's thread safe in all situations <b>except</b> when you call this method from different threads <b>and</b> with different camera parameter matrices. In all other cases, it's a good idea to left it enabled.
175+
* \param[in] minRangeMask If provided, a matrix with the minimum range (in meters) allowed at each direction (row,col) for a range to be considered valid.
175176
* \tparam POINTMAP Supported maps are all those covered by mrpt::utils::PointCloudAdapter (mrpt::maps::CPointsMap and derived, mrpt::opengl::CPointCloudColoured, PCL point clouds,...)
176177
*
177178
* \note In MRPT < 0.9.5, this method always assumes that ranges were in Kinect-like format.
@@ -181,9 +182,11 @@ namespace obs
181182
POINTMAP & dest_pointcloud,
182183
const bool takeIntoAccountSensorPoseOnRobot,
183184
const mrpt::poses::CPose3D *robotPoseInTheWorld=NULL,
184-
const bool PROJ3D_USE_LUT=true)
185+
const bool PROJ3D_USE_LUT=true,
186+
const mrpt::math::CMatrix * minRangeMask = NULL
187+
)
185188
{
186-
detail::project3DPointsFromDepthImageInto<POINTMAP>(*this,dest_pointcloud,takeIntoAccountSensorPoseOnRobot,robotPoseInTheWorld,PROJ3D_USE_LUT);
189+
detail::project3DPointsFromDepthImageInto<POINTMAP>(*this,dest_pointcloud,takeIntoAccountSensorPoseOnRobot,robotPoseInTheWorld,PROJ3D_USE_LUT,minRangeMask);
187190
}
188191

189192
/** This method is equivalent to \c project3DPointsFromDepthImageInto() storing the projected 3D points (without color, in local coordinates) in this same class.
@@ -218,6 +221,7 @@ namespace obs
218221
* \param[in] angle_sup (Default=5deg) The upper half-FOV angle (in radians)
219222
* \param[in] angle_sup (Default=5deg) The lower half-FOV angle (in radians)
220223
* \param[in] oversampling_ratio (Default=1.2=120%) How many more laser scans rays to create (read above).
224+
* \param[in] minRangeMask If provided, a matrix with the minimum range (in meters) allowed at each direction (row,col) for a range to be considered valid.
221225
*
222226
* \sa The example in http://www.mrpt.org/Example_Kinect_To_2D_laser_scan
223227
*/
@@ -226,7 +230,9 @@ namespace obs
226230
const std::string & sensorLabel,
227231
const double angle_sup = mrpt::utils::DEG2RAD(5),
228232
const double angle_inf = mrpt::utils::DEG2RAD(5),
229-
const double oversampling_ratio = 1.2 );
233+
const double oversampling_ratio = 1.2,
234+
const mrpt::math::CMatrix * minRangeMask = NULL
235+
);
230236

231237

232238
bool hasPoints3D; //!< true means the field points3D contains valid data.

libs/obs/include/mrpt/obs/CObservation3DRangeScan_project3D_impl.h

Lines changed: 43 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -15,16 +15,17 @@ namespace mrpt {
1515
namespace obs {
1616
namespace detail {
1717
// Auxiliary functions which implement SSE-optimized proyection of 3D point cloud:
18-
template <class POINTMAP> void do_project_3d_pointcloud(const int H,const int W,const float *kys,const float *kzs,const mrpt::math::CMatrix &rangeImage, mrpt::utils::PointCloudAdapter<POINTMAP> &pca, std::vector<uint16_t> &idxs_x, std::vector<uint16_t> &idxs_y);
19-
template <class POINTMAP> void do_project_3d_pointcloud_SSE2(const int H,const int W,const float *kys,const float *kzs,const mrpt::math::CMatrix &rangeImage, mrpt::utils::PointCloudAdapter<POINTMAP> &pca, std::vector<uint16_t> &idxs_x, std::vector<uint16_t> &idxs_y);
18+
template <class POINTMAP> void do_project_3d_pointcloud(const int H,const int W,const float *kys,const float *kzs,const mrpt::math::CMatrix &rangeImage, mrpt::utils::PointCloudAdapter<POINTMAP> &pca, std::vector<uint16_t> &idxs_x, std::vector<uint16_t> &idxs_y,const mrpt::math::CMatrix * minRangeMask);
19+
template <class POINTMAP> void do_project_3d_pointcloud_SSE2(const int H,const int W,const float *kys,const float *kzs,const mrpt::math::CMatrix &rangeImage, mrpt::utils::PointCloudAdapter<POINTMAP> &pca, std::vector<uint16_t> &idxs_x, std::vector<uint16_t> &idxs_y,const mrpt::math::CMatrix * minRangeMask);
2020

2121
template <class POINTMAP>
2222
void project3DPointsFromDepthImageInto(
2323
CObservation3DRangeScan & src_obs,
2424
POINTMAP & dest_pointcloud,
2525
const bool takeIntoAccountSensorPoseOnRobot,
2626
const mrpt::poses::CPose3D * robotPoseInTheWorld,
27-
const bool PROJ3D_USE_LUT)
27+
const bool PROJ3D_USE_LUT,
28+
const mrpt::math::CMatrix * minRangeMask)
2829
{
2930
using namespace mrpt::math;
3031

@@ -37,6 +38,7 @@ namespace detail {
3738
// ------------------------------------------------------------
3839
const int W = src_obs.rangeImage.cols();
3940
const int H = src_obs.rangeImage.rows();
41+
ASSERT_(W!=0 && H!=0);
4042
const size_t WH = W*H;
4143

4244
src_obs.resizePoints3DVectors(WH); // This is to make sure points3D_idxs_{x,y} have the expected sizes.
@@ -76,12 +78,16 @@ namespace detail {
7678
float *kys = &src_obs.m_3dproj_lut.Kys[0];
7779
float *kzs = &src_obs.m_3dproj_lut.Kzs[0];
7880

81+
if (minRangeMask) { // sanity check:
82+
ASSERT_EQUAL_(minRangeMask->cols(), src_obs.rangeImage.cols());
83+
ASSERT_EQUAL_(minRangeMask->rows(), src_obs.rangeImage.rows());
84+
}
7985
#if MRPT_HAS_SSE2
8086
if ((W & 0x07)==0)
81-
do_project_3d_pointcloud_SSE2(H,W,kys,kzs,src_obs.rangeImage,pca, src_obs.points3D_idxs_x, src_obs.points3D_idxs_y );
82-
else do_project_3d_pointcloud(H,W,kys,kzs,src_obs.rangeImage,pca, src_obs.points3D_idxs_x, src_obs.points3D_idxs_y ); // if image width is not 8*N, use standard method
87+
do_project_3d_pointcloud_SSE2(H,W,kys,kzs,src_obs.rangeImage,pca, src_obs.points3D_idxs_x, src_obs.points3D_idxs_y,minRangeMask );
88+
else do_project_3d_pointcloud(H,W,kys,kzs,src_obs.rangeImage,pca, src_obs.points3D_idxs_x, src_obs.points3D_idxs_y,minRangeMask ); // if image width is not 8*N, use standard method
8389
#else
84-
do_project_3d_pointcloud(H,W,kys,kzs,src_obs.rangeImage,pca,src_obs.points3D_idxs_x, src_obs.points3D_idxs_y);
90+
do_project_3d_pointcloud(H,W,kys,kzs,src_obs.rangeImage,pca,src_obs.points3D_idxs_x, src_obs.points3D_idxs_y,minRangeMask);
8591
#endif
8692
}
8793
else
@@ -264,14 +270,15 @@ namespace detail {
264270

265271
// Auxiliary functions which implement proyection of 3D point clouds:
266272
template <class POINTMAP>
267-
inline void do_project_3d_pointcloud(const int H,const int W,const float *kys,const float *kzs,const mrpt::math::CMatrix &rangeImage, mrpt::utils::PointCloudAdapter<POINTMAP> &pca, std::vector<uint16_t> &idxs_x, std::vector<uint16_t> &idxs_y)
273+
inline void do_project_3d_pointcloud(const int H,const int W,const float *kys,const float *kzs,const mrpt::math::CMatrix &rangeImage, mrpt::utils::PointCloudAdapter<POINTMAP> &pca, std::vector<uint16_t> &idxs_x, std::vector<uint16_t> &idxs_y,const mrpt::math::CMatrix * minRangeMask)
268274
{
275+
// Preconditions: minRangeMask() has the right size
269276
size_t idx=0;
270277
for (int r=0;r<H;r++)
271278
for (int c=0;c<W;c++)
272279
{
273280
const float D = rangeImage.coeff(r,c);
274-
if (D!=.0f) {
281+
if (D!=.0f && (!minRangeMask || D>minRangeMask->coeff(r,c)) ) {
275282
pca.setPointXYZ(idx, D /*x*/, *kys++ * D /*y*/, *kzs++ * D /*z*/);
276283
idxs_x[idx]=c;
277284
idxs_y[idx]=r;
@@ -283,36 +290,49 @@ namespace detail {
283290

284291
// Auxiliary functions which implement proyection of 3D point clouds:
285292
template <class POINTMAP>
286-
inline void do_project_3d_pointcloud_SSE2(const int H,const int W,const float *kys,const float *kzs,const mrpt::math::CMatrix &rangeImage, mrpt::utils::PointCloudAdapter<POINTMAP> &pca, std::vector<uint16_t> &idxs_x, std::vector<uint16_t> &idxs_y)
293+
inline void do_project_3d_pointcloud_SSE2(const int H,const int W,const float *kys,const float *kzs,const mrpt::math::CMatrix &rangeImage, mrpt::utils::PointCloudAdapter<POINTMAP> &pca, std::vector<uint16_t> &idxs_x, std::vector<uint16_t> &idxs_y,const mrpt::math::CMatrix * minRangeMask)
287294
{
288295
#if MRPT_HAS_SSE2
296+
// Preconditions: minRangeMask() has the right size
289297
// Use optimized version:
290298
const int W_4 = W >> 2; // /=4 , since we process 4 values at a time.
291299
size_t idx=0;
292300
MRPT_ALIGN16 float xs[4],ys[4],zs[4];
301+
const __m128 D_zeros = _mm_set_ps(.0f,.0f,.0f,.0f);
293302
for (int r=0;r<H;r++)
294303
{
295304
const float *D_ptr = &rangeImage.coeffRef(r,0); // Matrices are 16-aligned
305+
const float *Dmin_ptr = !minRangeMask ? NULL : &minRangeMask->coeffRef(r,0);
296306

297307
for (int c=0;c<W_4;c++)
298308
{
299309
const __m128 D = _mm_load_ps(D_ptr);
310+
__m128 valid_range_mask = _mm_cmpgt_ps(D, D_zeros);
300311

301-
const __m128 KY = _mm_load_ps(kys);
302-
const __m128 KZ = _mm_load_ps(kzs);
303-
304-
_mm_storeu_ps(xs , D);
305-
_mm_storeu_ps(ys , _mm_mul_ps(KY,D));
306-
_mm_storeu_ps(zs , _mm_mul_ps(KZ,D));
307-
308-
for (int q=0;q<4;q++)
309-
if (xs[q]!=.0f) {
310-
pca.setPointXYZ(idx,xs[q],ys[q],zs[q]);
311-
idxs_x[idx]=(c<<2)+q;
312-
idxs_y[idx]=r;
313-
++idx;
314-
}
312+
if (minRangeMask) {
313+
const __m128 gt_mask = _mm_cmpgt_ps(D, _mm_load_ps(Dmin_ptr) );
314+
valid_range_mask = _mm_and_ps(valid_range_mask, gt_mask );
315+
}
316+
const int valid_range_maski = _mm_movemask_epi8(_mm_castps_si128(valid_range_mask)); // 0x{f|0}{f|0}{f|0}{f|0}
317+
if (valid_range_maski) // Any of the 4 values is valid?
318+
{
319+
const __m128 KY = _mm_load_ps(kys);
320+
const __m128 KZ = _mm_load_ps(kzs);
321+
322+
_mm_storeu_ps(xs , D);
323+
_mm_storeu_ps(ys , _mm_mul_ps(KY,D));
324+
_mm_storeu_ps(zs , _mm_mul_ps(KZ,D));
325+
326+
for (int q=0;q<4;q++)
327+
if ((valid_range_maski & (1<<(q*4))) !=0) {
328+
pca.setPointXYZ(idx,xs[q],ys[q],zs[q]);
329+
idxs_x[idx]=(c<<2)+q;
330+
idxs_y[idx]=r;
331+
++idx;
332+
}
333+
}
315334
D_ptr+=4;
335+
if (Dmin_ptr) Dmin_ptr+=4;
316336
kys+=4;
317337
kzs+=4;
318338
}

libs/obs/src/CObservation3DRangeScan.cpp

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -856,7 +856,8 @@ void CObservation3DRangeScan::convertTo2DScan(
856856
const std::string &sensorLabel,
857857
const double angle_sup,
858858
const double angle_inf,
859-
const double oversampling_ratio
859+
const double oversampling_ratio,
860+
const mrpt::math::CMatrix * minRangeMask
860861
)
861862
{
862863
out_scan2d.sensorLabel = sensorLabel;
@@ -871,6 +872,10 @@ void CObservation3DRangeScan::convertTo2DScan(
871872

872873
const size_t nCols = this->rangeImage.cols();
873874
const size_t nRows = this->rangeImage.rows();
875+
if (minRangeMask) { // sanity check:
876+
ASSERT_EQUAL_(minRangeMask->cols(), rangeImage.cols());
877+
ASSERT_EQUAL_(minRangeMask->rows(), rangeImage.rows());
878+
}
874879

875880
// Compute the real horizontal FOV from the range camera intrinsic calib data:
876881
// Note: this assumes the range image has been "undistorted", which is true for data
@@ -933,7 +938,7 @@ void CObservation3DRangeScan::convertTo2DScan(
933938
for (size_t r=0;r<nRows;r++)
934939
{
935940
const float D = this->rangeImage.coeff(r,c);
936-
if (D>0)
941+
if (D>0 && (!minRangeMask || D>minRangeMask->coeff(r,c)))
937942
{
938943
const float this_point_tan = vert_ang_tan[r] * D;
939944
if (this_point_tan>tan_min && this_point_tan<tan_max)

0 commit comments

Comments
 (0)