2020
2121namespace xpcf = org::bcom::xpcf;
2222
23+ // test optimization code implemented by yzhou
24+ #define OPTIM_ON
25+
2326XPCF_DEFINE_FACTORY_CREATE_INSTANCE (SolAR::MODULES::OPENCV::SolARDescriptorMatcherGeometricOpencv)
2427
2528namespace SolAR {
@@ -40,6 +43,22 @@ SolARDescriptorMatcherGeometricOpencv::~SolARDescriptorMatcherGeometricOpencv()
4043 LOG_DEBUG (" SolARDescriptorMatcherGeometricOpencv destructor" )
4144}
4245
46+ xpcf::XPCFErrorCode SolARDescriptorMatcherGeometricOpencv::onConfigured ()
47+ {
48+ #ifdef OPTIM_ON
49+ LOG_DEBUG (" SolARDescriptorMatcherGeometricOpencv onConfigured" );
50+ #ifdef WITHCUDA
51+ m_matcher = cv::cuda::DescriptorMatcher::createBFMatcher (cv::NORM_L2);
52+ #else
53+ if (SolAROpenCVHelper::createMatcher (m_type, m_matcher) != FrameworkReturnCode::_SUCCESS) {
54+ LOG_ERROR (" Descriptor matcher type {} is not supported" , m_type);
55+ return xpcf::XPCFErrorCode::_FAIL;
56+ }
57+ #endif // WITHCUDA
58+ #endif
59+ return xpcf::XPCFErrorCode::_SUCCESS;
60+ }
61+
4362FrameworkReturnCode SolARDescriptorMatcherGeometricOpencv::match (const SRef<SolAR::datastructure::DescriptorBuffer> descriptors1,
4463 const SRef<SolAR::datastructure::DescriptorBuffer> descriptors2,
4564 const std::vector<SolAR::datastructure::Keypoint> &undistortedKeypoints1,
@@ -49,7 +68,8 @@ FrameworkReturnCode SolARDescriptorMatcherGeometricOpencv::match(const SRef<SolA
4968 const SolAR::datastructure::CameraParameters & camParams1,
5069 const SolAR::datastructure::CameraParameters & camParams2,
5170 std::vector<SolAR::datastructure::DescriptorMatch> & matches,
52- const std::vector<uint32_t >& mask)
71+ const std::vector<uint32_t >& mask1,
72+ const std::vector<uint32_t >& mask2)
5373{
5474 matches.clear ();
5575 // check conditions
@@ -58,7 +78,20 @@ FrameworkReturnCode SolARDescriptorMatcherGeometricOpencv::match(const SRef<SolA
5878 return FrameworkReturnCode::_ERROR_;
5979 }
6080 // calculate fundametal matrix
81+ #ifdef OPTIM_ON
82+ Transform3Df pose1Inv;
83+ for (int i = 0 ; i < 3 ; i++)
84+ for (int j = 0 ; j < 3 ; j++)
85+ pose1Inv (i, j) = pose1 (j, i);
86+ for (int i = 0 ; i < 3 ; i++)
87+ pose1Inv (3 , i) = 0 .f ;
88+ pose1Inv (3 , 3 ) = 1 .f ;
89+ for (int i = 0 ; i < 3 ; i++)
90+ pose1Inv (i, 3 ) = -(pose1 (0 , 3 )*pose1 (0 , i) + pose1 (1 , 3 )*pose1 (1 , i) + pose1 (2 , 3 )*pose1 (2 , i));
91+ Transform3Df pose12 = pose1Inv * pose2;
92+ #else
6193 Transform3Df pose12 = pose1.inverse () * pose2;
94+ #endif
6295 cv::Mat R12 = (cv::Mat_<float >(3 , 3 ) << pose12 (0 , 0 ), pose12 (0 , 1 ), pose12 (0 , 2 ),
6396 pose12 (1 , 0 ), pose12 (1 , 1 ), pose12 (1 , 2 ),
6497 pose12 (2 , 0 ), pose12 (2 , 1 ), pose12 (2 , 2 ));
@@ -68,21 +101,44 @@ FrameworkReturnCode SolARDescriptorMatcherGeometricOpencv::match(const SRef<SolA
68101 -T12.at <float >(1 ), T12.at <float >(0 ), 0 );
69102 cv::Mat K1 (3 , 3 , CV_32FC1, (void *)camParams1.intrinsic .data ());
70103 cv::Mat K2 (3 , 3 , CV_32FC1, (void *)camParams2.intrinsic .data ());
104+ #ifdef OPTIM_ON
105+ cv::Mat K1tInv = cv::Mat::zeros (3 , 3 , CV_32FC1);
106+ K1tInv.at <float >(0 , 0 ) = 1 .f / K1.at <float >(0 , 0 );
107+ K1tInv.at <float >(1 , 1 ) = 1 .f / K1.at <float >(1 , 1 );
108+ K1tInv.at <float >(2 , 2 ) = 1 .f ;
109+ K1tInv.at <float >(2 , 0 ) = -K1.at <float >(0 , 2 ) / K1.at <float >(0 , 0 );
110+ K1tInv.at <float >(2 , 1 ) = -K1.at <float >(1 , 2 ) / K1.at <float >(1 , 1 );
111+ cv::Mat K2Inv = cv::Mat::zeros (3 , 3 , CV_32FC1);
112+ K2Inv.at <float >(0 , 0 ) = 1 .f / K2.at <float >(0 , 0 );
113+ K2Inv.at <float >(1 , 1 ) = 1 .f / K2.at <float >(1 , 1 );
114+ K2Inv.at <float >(2 , 2 ) = 1 .f ;
115+ K2Inv.at <float >(0 , 2 ) = -K2.at <float >(0 , 2 ) / K2.at <float >(0 , 0 );
116+ K2Inv.at <float >(1 , 2 ) = -K2.at <float >(1 , 2 ) / K2.at <float >(1 , 1 );
117+ cv::Mat F = K1tInv * T12x * R12 * K2Inv;
118+ #else
71119 cv::Mat F = K1.t ().inv () * T12x * R12 * K2.inv ();
120+ #endif
72121
73122 // get input points in the first frame
74123 std::vector<cv::Point2f> pts1;
75- std::vector<uint32_t > indices1;
76- if (mask .size () == 0 ) {
124+ std::vector<uint32_t > indices1, indices2 ;
125+ if (mask1 .size () == 0 ) {
77126 for (int i = 0 ; i < undistortedKeypoints1.size (); ++i) {
78127 pts1.push_back (cv::Point2f (undistortedKeypoints1[i].getX (), undistortedKeypoints1[i].getY ()));
79128 indices1.push_back (i);
80129 }
81130 }
82131 else {
83- indices1 = mask;
84- for (int i = 0 ; i < mask.size (); ++i)
85- pts1.push_back (cv::Point2f (undistortedKeypoints1[mask[i]].getX (), undistortedKeypoints1[mask[i]].getY ()));
132+ indices1 = mask1;
133+ for (int i = 0 ; i < mask1.size (); ++i)
134+ pts1.push_back (cv::Point2f (undistortedKeypoints1[mask1[i]].getX (), undistortedKeypoints1[mask1[i]].getY ()));
135+ }
136+ if (mask2.size () == 0 ) {
137+ for (int i = 0 ; i < undistortedKeypoints2.size (); ++i)
138+ indices2.push_back (i);
139+ }
140+ else {
141+ indices2 = mask2;
86142 }
87143
88144 // compute epipolar lines
@@ -91,24 +147,88 @@ FrameworkReturnCode SolARDescriptorMatcherGeometricOpencv::match(const SRef<SolA
91147
92148 // convert frame descriptor to opencv's descriptor
93149 uint32_t type_conversion = SolAR::MODULES::OPENCV::SolAROpenCVHelper::deduceOpenDescriptorCVType (descriptors1->getDescriptorDataType ());
150+ #ifdef OPTIM_ON
151+ cv::Mat cvDescriptor1 (indices1.size (), descriptors1->getNbElements (), type_conversion);
152+ uchar* buf_in = (uchar*)descriptors1->data ();
153+ uchar* buf_out = cvDescriptor1.data ;
154+ uint32_t num_elements = static_cast <uint32_t >(descriptors1->getNbElements ());
155+ for (auto i = 0 ; i < indices1.size (); i++) {
156+ std::memcpy (buf_out, buf_in + num_elements * indices1[i], num_elements);
157+ buf_out += num_elements;
158+ }
159+ cv::Mat cvDescriptor2 (indices2.size (), descriptors2->getNbElements (), type_conversion);
160+ buf_in = (uchar*)descriptors2->data ();
161+ buf_out = cvDescriptor2.data ;
162+ num_elements = static_cast <uint32_t >(descriptors2->getNbElements ());
163+ for (auto i = 0 ; i < indices2.size (); i++) {
164+ std::memcpy (buf_out, buf_in + num_elements * indices2[i], num_elements);
165+ buf_out += num_elements;
166+ }
167+ #else
94168 cv::Mat cvDescriptor1 (descriptors1->getNbDescriptors (), descriptors1->getNbElements (), type_conversion);
95169 cvDescriptor1.data = (uchar*)descriptors1->data ();
96170 cv::Mat cvDescriptor2 (descriptors2->getNbDescriptors (), descriptors2->getNbElements (), type_conversion);
97171 cvDescriptor2.data = (uchar*)descriptors2->data ();
172+ #endif
173+
98174
99- // match
175+ #ifdef OPTIM_ON
176+ // perform descriptor matching first on GPU using Cuda if SolARModuleOpenCVCuda
177+ std::vector< std::vector<cv::DMatch> > nn_matches;
178+ #ifdef WITHCUDA
179+ if (descriptors1->getDescriptorDataType () != DescriptorDataType::TYPE_32F) // convert to float because cuda descriptor match only supports float type
180+ cvDescriptor1.convertTo (cvDescriptor1, CV_32F);
181+ if (descriptors2->getDescriptorDataType () != DescriptorDataType::TYPE_32F)
182+ cvDescriptor2.convertTo (cvDescriptor2, CV_32F);
183+ cv::cuda::GpuMat cvDescriptor1Gpu, cvDescriptor2Gpu;
184+ cvDescriptor1Gpu.upload (cvDescriptor1);
185+ cvDescriptor2Gpu.upload (cvDescriptor2);
186+ m_matcher->knnMatch (cvDescriptor1Gpu, cvDescriptor2Gpu, nn_matches, 2 );
187+ #else
188+ m_matcher->knnMatch (cvDescriptor1, cvDescriptor2, nn_matches, 2 );
189+ #endif
190+
191+ // filter the matches by three conditions (best distance, 2nd best distance and epipolar constraint)
192+ std::vector<bool > checkMatches (indices2.size (), true );
193+ float acceptedDist = m_paddingRatio * camParams2.resolution .width ;
194+ for (auto i = 0 ; i < indices1.size (); i++) {
195+ const auto & best_matches = nn_matches[i];
196+ if (!checkMatches[best_matches[0 ].trainIdx ])
197+ continue ;
198+ if (best_matches[0 ].distance >= m_matchingDistanceMax)
199+ continue ;
200+ if (best_matches[0 ].distance >= m_distanceRatio * best_matches[1 ].distance ) // no big difference between best and 2nd best, Lowe's ratio test
201+ continue ;
202+ // the match is accepted
203+ // now we should check the epipolar constraint
204+ int idx1 = indices1[best_matches[0 ].queryIdx ];
205+ int idx2 = indices2[best_matches[0 ].trainIdx ];
206+ float distance = best_matches[0 ].distance ;
207+ cv::Point3f l = lines2[i]; // line equation a*x + b*y +c = 0
208+ float x = undistortedKeypoints2[idx2].getX ();
209+ float y = undistortedKeypoints2[idx2].getY ();
210+ float disPointLine = std::abs (x * l.x + y * l.y + l.z ) / std::sqrt (l.x * l.x + l.y * l.y );
211+ if (disPointLine < acceptedDist) {
212+ matches.push_back (DescriptorMatch (idx1, idx2, distance));
213+ checkMatches[best_matches[0 ].trainIdx ] = false ;
214+ }
215+ }
216+ #else
217+ // first apply epipolar constraint then apply descriptor matching
100218 std::vector<bool > checkMatches (undistortedKeypoints2.size (), true );
101219 float acceptedDist = m_paddingRatio * camParams2.resolution .width ;
102220 for (int i = 0 ; i < indices1.size (); i++) {
103221 const cv::Mat cvDes1 = cvDescriptor1.row (indices1[i]);
104222 float bestDist = std::numeric_limits<float >::max ();
105223 float bestDist2 = std::numeric_limits<float >::max ();
106224 int bestIdx = -1 ;
225+ cv::Point3f l = lines2[i];
226+ float lxyz_norm = std::sqrt (l.x * l.x + l.y * l.y );
227+
107228 for (int j = 0 ; j < undistortedKeypoints2.size (); j++) {
108229 float x = undistortedKeypoints2[j].getX ();
109230 float y = undistortedKeypoints2[j].getY ();
110- cv::Point3f l = lines2[i];
111- float disPointLine = std::abs (x * l.x + y * l.y + l.z ) / std::sqrt (l.x * l.x + l.y * l.y );
231+ float disPointLine = std::abs (x * l.x + y * l.y + l.z ) / lxyz_norm;
112232 if (disPointLine < acceptedDist) {
113233 float dist = cv::norm (cvDes1, cvDescriptor2.row (j), cv::NORM_L2);
114234 if (dist < bestDist)
@@ -128,7 +248,7 @@ FrameworkReturnCode SolARDescriptorMatcherGeometricOpencv::match(const SRef<SolA
128248 checkMatches[bestIdx] = false ;
129249 }
130250 }
131-
251+ # endif
132252 return FrameworkReturnCode::_SUCCESS;
133253}
134254
0 commit comments