Skip to content

Commit 48c1062

Browse files
committed
depthTo3d: fixed bug, added regression test
RgbdNormals: setMethod() removed as useless RgbdNormals: tests + cross product, to be fixed + cross product LINEMOD: diffThreshold param added + tests fixed minor diffThreshold fix points3dToDepth16U fix normals computer diffThreshold fix random plane generation fixed + diffThreshold fix Rendered normals test rewritten to GTest Params random plane generation: scale RGBD_Normals tests: thresholds tuned Rendered normals tests: 64F support added Random planes normal tests rewritten to GTest Params LINEMOD and CrossProduct fix SRI threshold raised NormalsRandomPlanes: thresholds raised assert on unknown alg; minor fix frame size reduced TIFF replaced by YAML.GZ depthTo3d test changed cv::transform is used fix warning nanMask() flipAxes() absDotPixel() + forgotten code helper functions removed RGBDNormals: checkNormals() and compare LINEMOD's pts3d to depth input Rendered: another criteria; thresholds; LINEMOD's pts3d to depth input comparison thresholds raised a bit SRI slightly optimized assert change normal tests refactored, parametrized, split trailing namespace, thresholds raised SRI caching optimized a lot normal tests rewritten to fixture, no loop minor runCase() joined with testIt() thresholds were put into GTest params ternary operator RgbdNormalsTest merged into NormalsRandomPlanes; RgbdPlanes moved closer to tests normal test minor refactoring plane finder tests refactored to GTest Params skip tests thresholds raised plane test minor plane tests: timers dropped, nPlanes put into GTest Params; refactoring generated normals tests: minor refactoring flipAxes() templated rendered normals tests refactored: thresholds to GTest Params CV_Error -> ASSERT_FALSE
1 parent 99d216c commit 48c1062

File tree

5 files changed

+757
-333
lines changed

5 files changed

+757
-333
lines changed

modules/3d/include/opencv2/3d/depth.hpp

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,8 @@ class CV_EXPORTS_W RgbdNormals
3030
{
3131
RGBD_NORMALS_METHOD_FALS = 0,
3232
RGBD_NORMALS_METHOD_LINEMOD = 1,
33-
RGBD_NORMALS_METHOD_SRI = 2
33+
RGBD_NORMALS_METHOD_SRI = 2,
34+
RGBD_NORMALS_METHOD_CROSS_PRODUCT = 3
3435
};
3536

3637
RgbdNormals() { }
@@ -42,9 +43,11 @@ class CV_EXPORTS_W RgbdNormals
4243
* @param depth the depth of the normals (only CV_32F or CV_64F)
4344
* @param K the calibration matrix to use
4445
* @param window_size the window size to compute the normals: can only be 1,3,5 or 7
46+
* @param diff_threshold threshold in depth difference, used in LINEMOD algirithm
4547
* @param method one of the methods to use: RGBD_NORMALS_METHOD_SRI, RGBD_NORMALS_METHOD_FALS
4648
*/
4749
CV_WRAP static Ptr<RgbdNormals> create(int rows = 0, int cols = 0, int depth = 0, InputArray K = noArray(), int window_size = 5,
50+
float diff_threshold = 50.f,
4851
RgbdNormals::RgbdNormalsMethod method = RgbdNormals::RgbdNormalsMethod::RGBD_NORMALS_METHOD_FALS);
4952

5053
/** Given a set of 3d points in a depth image, compute the normals at each point.
@@ -68,7 +71,6 @@ class CV_EXPORTS_W RgbdNormals
6871
CV_WRAP virtual void getK(OutputArray val) const = 0;
6972
CV_WRAP virtual void setK(InputArray val) = 0;
7073
CV_WRAP virtual RgbdNormals::RgbdNormalsMethod getMethod() const = 0;
71-
CV_WRAP virtual void setMethod(RgbdNormals::RgbdNormalsMethod val) = 0;
7274
};
7375

7476

@@ -146,7 +148,7 @@ enum RgbdPlaneMethod
146148

147149
/** Find the planes in a depth image
148150
* @param points3d the 3d points organized like the depth image: rows x cols with 3 channels
149-
* @param normals the normals for every point in the depth image
151+
* @param normals the normals for every point in the depth image; optional, can be empty
150152
* @param mask An image where each pixel is labeled with the plane it belongs to
151153
* and 255 if it does not belong to any plane
152154
* @param plane_coefficients the coefficients of the corresponding planes (a,b,c,d) such that ax+by+cz+d=0, norm(a,b,c)=1

modules/3d/src/rgbd/depth_to_3d.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ static void depthTo3d_from_uvz(const cv::Mat& in_K, const cv::Mat& u_mat, const
4646
coordinates[0] = coordinates[0].mul(z_mat);
4747
coordinates[1] = (v_mat - cy).mul(z_mat) * (1. / fy);
4848
coordinates[2] = z_mat;
49-
coordinates[3] = 0;
49+
coordinates[3] = Mat(u_mat.size(), CV_32F, Scalar(0));
5050
cv::merge(coordinates, points3d);
5151
}
5252

modules/3d/src/rgbd/normal.cpp

Lines changed: 159 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -225,24 +225,7 @@ class RgbdNormalsImpl : public RgbdNormals
225225
{
226226
return method;
227227
}
228-
virtual void setMethod(RgbdNormalsMethod val) CV_OVERRIDE
229-
{
230-
method = val; cacheIsDirty = true;
231-
}
232-
233-
// Helper functions for apply()
234-
virtual void assertOnBadArg(const Mat& points3d_ori) const = 0;
235-
virtual void calcRadiusAnd3d(const Mat& points3d_ori, Mat& points3d, Mat& radius) const
236-
{
237-
// Make the points have the right depth
238-
if (points3d_ori.depth() == dtype)
239-
points3d = points3d_ori;
240-
else
241-
points3d_ori.convertTo(points3d, dtype);
242228

243-
// Compute the distance to the points
244-
radius = computeRadius<T>(points3d);
245-
}
246229
virtual void compute(const Mat& in, Mat& normals) const = 0;
247230

248231
/** Given a set of 3d points in a depth image, compute the normals at each point
@@ -256,29 +239,66 @@ class RgbdNormalsImpl : public RgbdNormals
256239
CV_Assert(points3d_ori.dims == 2);
257240

258241
// Either we have 3d points or a depth image
259-
assertOnBadArg(points3d_ori);
242+
243+
bool ptsAre4F = (points3d_ori.channels() == 4) && (points3d_ori.depth() == CV_32F || points3d_ori.depth() == CV_64F);
244+
bool ptsAreDepth = (points3d_ori.channels() == 1) && (points3d_ori.depth() == CV_16U || points3d_ori.depth() == CV_32F || points3d_ori.depth() == CV_64F);
245+
if (method == RGBD_NORMALS_METHOD_FALS || method == RGBD_NORMALS_METHOD_SRI || method == RGBD_NORMALS_METHOD_CROSS_PRODUCT)
246+
{
247+
if (!ptsAre4F)
248+
{
249+
CV_Error(Error::StsBadArg, "Input image should have 4 float-point channels");
250+
}
251+
}
252+
else if (method == RGBD_NORMALS_METHOD_LINEMOD)
253+
{
254+
if (!ptsAre4F && !ptsAreDepth)
255+
{
256+
CV_Error(Error::StsBadArg, "Input image should have 4 float-point channels or have 1 ushort or float-point channel");
257+
}
258+
}
259+
else
260+
{
261+
CV_Error(Error::StsInternal, "Unknown normal computer algorithm");
262+
}
260263

261264
// Initialize the pimpl
262265
cache();
263266

264267
// Precompute something for RGBD_NORMALS_METHOD_SRI and RGBD_NORMALS_METHOD_FALS
265-
Mat points3d, radius;
266-
calcRadiusAnd3d(points3d_ori, points3d, radius);
268+
Mat points3d;
269+
if (method != RGBD_NORMALS_METHOD_LINEMOD)
270+
{
271+
// Make the points have the right depth
272+
if (points3d_ori.depth() == dtype)
273+
points3d = points3d_ori;
274+
else
275+
points3d_ori.convertTo(points3d, dtype);
276+
}
267277

268278
// Get the normals
269279
normals_out.create(points3d_ori.size(), CV_MAKETYPE(dtype, 4));
270-
if (points3d_in.empty())
280+
if (points3d_ori.empty())
271281
return;
272282

273283
Mat normals = normals_out.getMat();
274284
if ((method == RGBD_NORMALS_METHOD_FALS) || (method == RGBD_NORMALS_METHOD_SRI))
275285
{
286+
// Compute the distance to the points
287+
Mat radius = computeRadius<T>(points3d);
276288
compute(radius, normals);
277289
}
278-
else // LINEMOD
290+
else if (method == RGBD_NORMALS_METHOD_LINEMOD)
279291
{
280292
compute(points3d_ori, normals);
281293
}
294+
else if (method == RGBD_NORMALS_METHOD_CROSS_PRODUCT)
295+
{
296+
compute(points3d, normals);
297+
}
298+
else
299+
{
300+
CV_Error(Error::StsInternal, "Unknown normal computer algorithm");
301+
}
282302
}
283303

284304
int rows, cols;
@@ -406,13 +426,6 @@ class FALS : public RgbdNormalsImpl<T>
406426
}
407427
}
408428

409-
virtual void assertOnBadArg(const Mat& points3d_ori) const CV_OVERRIDE
410-
{
411-
//CV_Assert(points3d_ori.channels() == 3);
412-
CV_Assert(points3d_ori.channels() == 4);
413-
CV_Assert(points3d_ori.depth() == CV_32F || points3d_ori.depth() == CV_64F);
414-
}
415-
416429
// Cached data
417430
mutable Mat_<Vec3T> V_;
418431
mutable Mat_<Vec9T> M_inv_;
@@ -448,14 +461,17 @@ class LINEMOD : public RgbdNormalsImpl<T>
448461
typedef Vec<T, 3> Vec3T;
449462
typedef Matx<T, 3, 3> Mat33T;
450463

451-
LINEMOD(int _rows, int _cols, int _windowSize, const Mat& _K) :
452-
RgbdNormalsImpl<T>(_rows, _cols, _windowSize, _K, RgbdNormals::RGBD_NORMALS_METHOD_LINEMOD)
464+
LINEMOD(int _rows, int _cols, int _windowSize, const Mat& _K, float _diffThr = 50.f) :
465+
RgbdNormalsImpl<T>(_rows, _cols, _windowSize, _K, RgbdNormals::RGBD_NORMALS_METHOD_LINEMOD),
466+
differenceThreshold(_diffThr)
453467
{ }
454468

455469
/** Compute cached data
456470
*/
457471
virtual void cache() const CV_OVERRIDE
458-
{ }
472+
{
473+
this->cacheIsDirty = false;
474+
}
459475

460476
/** Compute the normals
461477
* @param r
@@ -536,7 +552,9 @@ class LINEMOD : public RgbdNormalsImpl<T>
536552

537553
Vec3T X1_minus_X, X2_minus_X;
538554

539-
ContainerDepth difference_threshold = 50;
555+
ContainerDepth difference_threshold(differenceThreshold);
556+
//TODO: fixit, difference threshold should not depend on input type
557+
difference_threshold *= (std::is_same<DepthDepth, ushort>::value ? 1000.f : 1.f);
540558
normals.setTo(std::numeric_limits<DepthDepth>::quiet_NaN());
541559
for (int y = r; y < this->rows - r - 1; ++y)
542560
{
@@ -591,14 +609,7 @@ class LINEMOD : public RgbdNormalsImpl<T>
591609
return normals;
592610
}
593611

594-
virtual void assertOnBadArg(const Mat& points3d_ori) const CV_OVERRIDE
595-
{
596-
CV_Assert(((points3d_ori.channels() == 4) && (points3d_ori.depth() == CV_32F || points3d_ori.depth() == CV_64F)) ||
597-
((points3d_ori.channels() == 1) && (points3d_ori.depth() == CV_16U || points3d_ori.depth() == CV_32F || points3d_ori.depth() == CV_64F)));
598-
}
599-
600-
virtual void calcRadiusAnd3d(const Mat& /*points3d_ori*/, Mat& /*points3d*/, Mat& /*radius*/) const CV_OVERRIDE
601-
{ }
612+
float differenceThreshold;
602613
};
603614

604615
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -648,25 +659,28 @@ class SRI : public RgbdNormalsImpl<T>
648659
for (int phi_int = 0, k = 0; phi_int < this->rows; ++phi_int)
649660
{
650661
float phi = min_phi + phi_int * phi_step_;
662+
float phi_sin = std::sin(phi), phi_cos = std::cos(phi);
651663
for (int theta_int = 0; theta_int < this->cols; ++theta_int, ++k)
652664
{
653665
float theta = min_theta + theta_int * theta_step_;
666+
float theta_sin = std::sin(theta), theta_cos = std::cos(theta);
654667
// Store the 3d point to project it later
655-
points3d[k] = Point3f(std::sin(theta) * std::cos(phi), std::sin(phi), std::cos(theta) * std::cos(phi));
668+
Point3f pp(theta_sin * phi_cos, phi_sin, theta_cos * phi_cos);
669+
points3d[k] = pp;
656670

657671
// Cache the rotation matrix and negate it
658-
Mat_<T> mat =
659-
(Mat_ < T >(3, 3) << 0, 1, 0, 0, 0, 1, 1, 0, 0) *
660-
((Mat_ < T >(3, 3) << std::cos(theta), -std::sin(theta), 0, std::sin(theta), std::cos(theta), 0, 0, 0, 1)) *
661-
((Mat_ < T >(3, 3) << std::cos(phi), 0, -std::sin(phi), 0, 1, 0, std::sin(phi), 0, std::cos(phi)));
672+
Matx<T, 3, 3> mat = Matx<T, 3, 3> (0, 1, 0, 0, 0, 1, 1, 0, 0) *
673+
Matx<T, 3, 3> (theta_cos, -theta_sin, 0, theta_sin, theta_cos, 0, 0, 0, 1) *
674+
Matx<T, 3, 3> (phi_cos, 0, -phi_sin, 0, 1, 0, phi_sin, 0, phi_cos);
675+
662676
for (unsigned char i = 0; i < 3; ++i)
663-
mat(i, 1) = mat(i, 1) / std::cos(phi);
677+
mat(i, 1) = mat(i, 1) / phi_cos;
664678
// The second part of the matrix is never explained in the paper ... but look at the wikipedia normal article
665-
mat(0, 0) = mat(0, 0) - 2 * std::cos(phi) * std::sin(theta);
666-
mat(1, 0) = mat(1, 0) - 2 * std::sin(phi);
667-
mat(2, 0) = mat(2, 0) - 2 * std::cos(phi) * std::cos(theta);
679+
mat(0, 0) = mat(0, 0) - 2 * pp.x;
680+
mat(1, 0) = mat(1, 0) - 2 * pp.y;
681+
mat(2, 0) = mat(2, 0) - 2 * pp.z;
668682

669-
R_hat_(phi_int, theta_int) = Vec9T((T*)(mat.data));
683+
R_hat_(phi_int, theta_int) = Vec9T(mat.val);
670684
}
671685
}
672686

@@ -747,8 +761,8 @@ class SRI : public RgbdNormalsImpl<T>
747761
T r_phi_over_r = (*r_phi_ptr) / (*r_ptr);
748762
// R(1,1) is 0
749763
signNormal((*R)(0, 0) + (*R)(0, 1) * r_theta_over_r + (*R)(0, 2) * r_phi_over_r,
750-
(*R)(1, 0) + (*R)(1, 2) * r_phi_over_r,
751-
(*R)(2, 0) + (*R)(2, 1) * r_theta_over_r + (*R)(2, 2) * r_phi_over_r, *normal);
764+
(*R)(1, 0) + (*R)(1, 2) * r_phi_over_r,
765+
(*R)(2, 0) + (*R)(2, 1) * r_theta_over_r + (*R)(2, 2) * r_phi_over_r, *normal);
752766
}
753767
}
754768

@@ -759,11 +773,6 @@ class SRI : public RgbdNormalsImpl<T>
759773
signNormal((*normal)[0], (*normal)[1], (*normal)[2], *normal);
760774
}
761775

762-
virtual void assertOnBadArg(const Mat& points3d_ori) const CV_OVERRIDE
763-
{
764-
CV_Assert(((points3d_ori.channels() == 4) && (points3d_ori.depth() == CV_32F || points3d_ori.depth() == CV_64F)));
765-
}
766-
767776
// Cached data
768777
/** Stores R */
769778
mutable Mat_<Vec9T> R_hat_;
@@ -781,14 +790,91 @@ class SRI : public RgbdNormalsImpl<T>
781790

782791
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
783792

784-
Ptr<RgbdNormals> RgbdNormals::create(int rows, int cols, int depth, InputArray K, int windowSize, RgbdNormalsMethod method)
793+
/* Uses the simpliest possible method for normals calculation: calculates cross product between two vectors
794+
(pointAt(x+1, y) - pointAt(x, y)) and (pointAt(x, y+1) - pointAt(x, y)) */
795+
796+
template<typename DataType>
797+
class CrossProduct : public RgbdNormalsImpl<DataType>
798+
{
799+
public:
800+
typedef Vec<DataType, 3> Vec3T;
801+
typedef Vec<DataType, 4> Vec4T;
802+
typedef Point3_<DataType> Point3T;
803+
804+
CrossProduct(int _rows, int _cols, int _windowSize, const Mat& _K) :
805+
RgbdNormalsImpl<DataType>(_rows, _cols, _windowSize, _K, RgbdNormals::RGBD_NORMALS_METHOD_CROSS_PRODUCT)
806+
{ }
807+
808+
/** Compute cached data
809+
*/
810+
virtual void cache() const CV_OVERRIDE
811+
{
812+
this->cacheIsDirty = false;
813+
}
814+
815+
static inline Point3T fromVec(Vec4T v)
816+
{
817+
return {v[0], v[1], v[2]};
818+
}
819+
820+
static inline Vec4T toVec4(Point3T p)
821+
{
822+
return {p.x, p.y, p.z, 0};
823+
}
824+
825+
static inline bool haveNaNs(Point3T p)
826+
{
827+
return cvIsNaN(p.x) || cvIsNaN(p.y) || cvIsNaN(p.z);
828+
}
829+
830+
/** Compute the normals
831+
* @param points reprojected depth points
832+
* @param normals generated normals
833+
* @return
834+
*/
835+
virtual void compute(const Mat& points, Mat& normals) const CV_OVERRIDE
836+
{
837+
for(int y = 0; y < this->rows; y++)
838+
{
839+
const Vec4T* ptsRow0 = points.ptr<Vec4T>(y);
840+
const Vec4T* ptsRow1 = (y < this->rows - 1) ? points.ptr<Vec4T>(y + 1) : nullptr;
841+
Vec4T *normRow = normals.ptr<Vec4T>(y);
842+
843+
for (int x = 0; x < this->cols; x++)
844+
{
845+
Point3T v00 = fromVec(ptsRow0[x]);
846+
const float qnan = std::numeric_limits<float>::quiet_NaN();
847+
Point3T n(qnan, qnan, qnan);
848+
849+
if ((x < this->cols - 1) && (y < this->rows - 1) && !haveNaNs(v00))
850+
{
851+
Point3T v01 = fromVec(ptsRow0[x + 1]);
852+
Point3T v10 = fromVec(ptsRow1[x]);
853+
854+
if (!haveNaNs(v01) && !haveNaNs(v10))
855+
{
856+
Vec3T vec = (v10 - v00).cross(v01 - v00);
857+
n = normalize(vec);
858+
}
859+
}
860+
861+
normRow[x] = toVec4(n);
862+
}
863+
}
864+
}
865+
};
866+
867+
868+
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
869+
870+
871+
Ptr<RgbdNormals> RgbdNormals::create(int rows, int cols, int depth, InputArray K, int windowSize, float diffThreshold, RgbdNormalsMethod method)
785872
{
786873
CV_Assert(rows > 0 && cols > 0 && (depth == CV_32F || depth == CV_64F));
787874
CV_Assert(windowSize == 1 || windowSize == 3 || windowSize == 5 || windowSize == 7);
788875
CV_Assert(K.cols() == 3 && K.rows() == 3 && (K.depth() == CV_32F || K.depth() == CV_64F));
789876

790877
Mat mK = K.getMat();
791-
CV_Assert(method == RGBD_NORMALS_METHOD_FALS || method == RGBD_NORMALS_METHOD_LINEMOD || method == RGBD_NORMALS_METHOD_SRI);
792878
Ptr<RgbdNormals> ptr;
793879
switch (method)
794880
{
@@ -802,10 +888,11 @@ Ptr<RgbdNormals> RgbdNormals::create(int rows, int cols, int depth, InputArray K
802888
}
803889
case (RGBD_NORMALS_METHOD_LINEMOD):
804890
{
891+
CV_Assert(diffThreshold > 0);
805892
if (depth == CV_32F)
806-
ptr = makePtr<LINEMOD<float> >(rows, cols, windowSize, mK);
893+
ptr = makePtr<LINEMOD<float> >(rows, cols, windowSize, mK, diffThreshold);
807894
else
808-
ptr = makePtr<LINEMOD<double>>(rows, cols, windowSize, mK);
895+
ptr = makePtr<LINEMOD<double>>(rows, cols, windowSize, mK, diffThreshold);
809896
break;
810897
}
811898
case RGBD_NORMALS_METHOD_SRI:
@@ -816,6 +903,16 @@ Ptr<RgbdNormals> RgbdNormals::create(int rows, int cols, int depth, InputArray K
816903
ptr = makePtr<SRI<double>>(rows, cols, windowSize, mK);
817904
break;
818905
}
906+
case RGBD_NORMALS_METHOD_CROSS_PRODUCT:
907+
{
908+
if (depth == CV_32F)
909+
ptr = makePtr<CrossProduct<float> >(rows, cols, windowSize, mK);
910+
else
911+
ptr = makePtr<CrossProduct<double>>(rows, cols, windowSize, mK);
912+
break;
913+
}
914+
default:
915+
CV_Error(Error::StsBadArg, "Unknown normals compute algorithm");
819916
}
820917

821918
return ptr;

0 commit comments

Comments
 (0)