@@ -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