Skip to content
This repository was archived by the owner on Jul 20, 2023. It is now read-only.

Commit 24e0786

Browse files
committed
Merge branch 'develop' into feat/deploy-dir
2 parents 4645ce5 + 0623c39 commit 24e0786

File tree

12 files changed

+249
-26
lines changed

12 files changed

+249
-26
lines changed

interfaces/SolARDescriptorMatcherGeometricOpencv.h

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@
2525
#include "opencv2/imgcodecs.hpp"
2626
#include "opencv2/highgui.hpp"
2727
#include <opencv2/calib3d.hpp>
28+
#ifdef WITHCUDA
29+
#include <opencv2/cudafeatures2d.hpp>
30+
#endif
2831

2932
namespace SolAR {
3033
namespace MODULES {
@@ -70,8 +73,9 @@ class SOLAROPENCV_EXPORT_API SolARDescriptorMatcherGeometricOpencv : public base
7073
/// @param[in] camParams1 The intrinsic parameters of the camera 1.
7174
/// @param[in] camParams2 The intrinsic parameters of the camera 2.
7275
/// @param[out] matches A vector of matches representing pairs of indices relatively to the first and second set of descriptors.
73-
/// @param[in] mask The indices of descriptors in the first frame are used for matching to the second frame. If it is empty then all will be used.
74-
/// @return FrameworkReturnCode::_SUCCESS if matching succeed, else FrameworkReturnCode::_ERROR_
76+
/// @param[in] mask1 The indices of descriptors in the first frame are used for matching to the second frame. If it is empty then all will be used.
77+
/// @param[in] mask2 The indices of descriptors in the second frame are used for matching to the first frame. If it is empty then all will be used.
78+
/// @return FrameworkReturnCode::_SUCCESS if matching succeed, else FrameworkReturnCode::_ERROR_
7579
FrameworkReturnCode match(const SRef<SolAR::datastructure::DescriptorBuffer> descriptors1,
7680
const SRef<SolAR::datastructure::DescriptorBuffer> descriptors2,
7781
const std::vector<SolAR::datastructure::Keypoint> &undistortedKeypoints1,
@@ -81,14 +85,24 @@ class SOLAROPENCV_EXPORT_API SolARDescriptorMatcherGeometricOpencv : public base
8185
const SolAR::datastructure::CameraParameters & camParams1,
8286
const SolAR::datastructure::CameraParameters & camParams2,
8387
std::vector<SolAR::datastructure::DescriptorMatch> & matches,
84-
const std::vector<uint32_t>& mask = {}) override;
88+
const std::vector<uint32_t>& mask1 = {},
89+
const std::vector<uint32_t>& mask2 = {}) override;
8590

91+
org::bcom::xpcf::XPCFErrorCode onConfigured() override final;
8692
void unloadComponent() override;
8793

8894
private:
8995
float m_distanceRatio = 0.75f;
9096
float m_paddingRatio = 0.003f;
9197
float m_matchingDistanceMax = 500.f;
98+
/// matcher type
99+
std::string m_type = "BruteForce";
100+
/// Matcher
101+
#ifdef WITHCUDA
102+
cv::Ptr<cv::cuda::DescriptorMatcher> m_matcher;
103+
#else
104+
cv::Ptr<cv::DescriptorMatcher> m_matcher;
105+
#endif
92106
};
93107

94108
}

interfaces/SolARSVDTriangulationOpencv.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,8 @@ class SOLAROPENCV_EXPORT_API SolARSVDTriangulationOpencv : public org::bcom::xpc
133133
SRef<SolAR::datastructure::Frame> frame2,
134134
const std::vector<SolAR::datastructure::DescriptorMatch> &matches,
135135
const std::pair<uint32_t, uint32_t> & working_views,
136+
const SolAR::datastructure::CameraParameters & camParams1,
137+
const SolAR::datastructure::CameraParameters & camParams2,
136138
std::vector<SRef<SolAR::datastructure::CloudPoint>> & pcloud,
137139
const bool& onlyDepth = false) override;
138140

src/SolARDescriptorMatcherGeometricOpencv.cpp

Lines changed: 130 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@
2020

2121
namespace xpcf = org::bcom::xpcf;
2222

23+
// test optimization code implemented by yzhou
24+
#define OPTIM_ON
25+
2326
XPCF_DEFINE_FACTORY_CREATE_INSTANCE(SolAR::MODULES::OPENCV::SolARDescriptorMatcherGeometricOpencv)
2427

2528
namespace 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+
4362
FrameworkReturnCode 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

src/SolARImageViewerOpencv.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,10 @@ FrameworkReturnCode SolARImageViewerOpencv::displayKey(const SRef<Image> img, ch
128128
key = cv::waitKey(1); // wait for a keystroke to display window
129129

130130
if(key == (char)(m_exitKey))
131+
{
132+
cv::destroyWindow(m_title);
131133
return FrameworkReturnCode::_STOP;
134+
}
132135

133136
return FrameworkReturnCode::_SUCCESS;
134137
}

src/SolARKeypointDetectorOpencv.cpp

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -225,8 +225,13 @@ void SolARKeypointDetectorOpencv::detect(const SRef<Image> image, std::vector<Ke
225225
float px = keypoint.pt.x;
226226
float py = keypoint.pt.y;
227227
cv::Vec3b bgr{ 0, 0, 0 };
228-
if (opencvImage.channels() == 3)
229-
bgr = opencvImage.at<cv::Vec3b>((int)py, (int)px);
228+
if (opencvImage.channels() == 3) {
229+
bgr = image->getPixel<cv::Vec3b>((int)py, (int)px);
230+
}
231+
else {
232+
uint8_t value = image->getPixel<uint8_t>((int)py, (int)px);
233+
bgr = cv::Vec3b(value, value, value);
234+
}
230235
kpa.init(kpID++, px, py, bgr[2], bgr[1], bgr[0], keypoint.size, keypoint.angle, keypoint.response, keypoint.octave, keypoint.class_id) ;
231236
keypoints.push_back(kpa);
232237
}

src/SolARMapFusionOpencv.cpp

Lines changed: 52 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,12 @@ void SolARMapFusionOpencv::fuseMap(const std::vector<std::pair<uint32_t, uint32_
164164
globalMap->getKeyframeCollection(globalKeyframeCollection);
165165
globalKeyframeCollection->getAllKeyframes(globalKeyframes);
166166

167-
// get covisibility graph
167+
// get Camera Parameters
168+
const SRef<CameraParametersCollection>& cameraParametersCollection = map->getConstCameraParametersCollection();
169+
SRef<CameraParametersCollection> globalCameraParametersCollection;
170+
globalMap->getCameraParametersCollection(globalCameraParametersCollection);
171+
172+
// get covisibility graph
168173
const SRef<CovisibilityGraph>& covisibilityGraph = map->getConstCovisibilityGraph();
169174
SRef<CovisibilityGraph> globalCovisibilityGraph;
170175
globalMap->getCovisibilityGraph(globalCovisibilityGraph);
@@ -191,10 +196,54 @@ void SolARMapFusionOpencv::fuseMap(const std::vector<std::pair<uint32_t, uint32_
191196
idxCPOldNew[idxOld] = cp->getId();
192197
}
193198

199+
// update global map camera parameters with local map ones
200+
std::vector<SRef<CameraParameters>> localMapCameraParameters;
201+
cameraParametersCollection->getAllCameraParameters(localMapCameraParameters);
202+
std::vector<SRef<CameraParameters>> globalMapCameraParameters;
203+
globalCameraParametersCollection->getAllCameraParameters(globalMapCameraParameters);
204+
std::map<uint32_t, uint32_t> local2GlobalMapCameraID; // A table matching camera ID between the local and global map
205+
for (const auto &itLocal: localMapCameraParameters) {
206+
bool foundCameraParam = false;
207+
for (const auto &itGlobal: globalMapCameraParameters) {
208+
if (itLocal->intrinsic == itGlobal->intrinsic &&
209+
itLocal->distortion == itGlobal->distortion &&
210+
itLocal->type == itGlobal->type &&
211+
itLocal->resolution.width == itGlobal->resolution.width &&
212+
itLocal->resolution.height == itGlobal->resolution.height &&
213+
itLocal->name == itGlobal->name)
214+
{
215+
LOG_DEBUG("Camera parameters found in global map with ID = {}", itGlobal->id);
216+
local2GlobalMapCameraID.insert(std::make_pair(itLocal->id, itGlobal->id));
217+
foundCameraParam = true;
218+
continue;
219+
}
220+
}
221+
if (!foundCameraParam) {
222+
SRef<CameraParameters> newCameraParameters = xpcf::utils::make_shared<CameraParameters>(*itLocal);
223+
globalCameraParametersCollection->addCameraParameters(newCameraParameters);
224+
local2GlobalMapCameraID.insert(std::make_pair(itLocal->id, newCameraParameters->id));
225+
LOG_DEBUG("Add new camera parameters in global map with ID = {}", newCameraParameters->id);
226+
}
227+
}
228+
194229
// add keyframes of local map to global map
195230
std::map<uint32_t, uint32_t> idxKfOldNew;
196-
for (const auto &kf : keyframes) {
197-
// unscale keyframe pose
231+
for (const auto &kf : keyframes) {
232+
// Check if CameraID exists in matching table
233+
std::map<uint32_t, uint32_t>::iterator camIt = local2GlobalMapCameraID.find(kf->getCameraID());
234+
235+
if (camIt != local2GlobalMapCameraID.end()) {
236+
LOG_DEBUG("Camera parameters for current key frame found => set to global map ID ({} -> {})",
237+
kf->getCameraID(), camIt->second);
238+
239+
kf->setCameraID(camIt->second);
240+
}
241+
else {
242+
LOG_ERROR("camera parameters with ID {} does not exists in local map", kf->getCameraID());
243+
continue;
244+
}
245+
246+
// unscale keyframe pose
198247
Transform3Df kfPose = kf->getPose();
199248
Eigen::Matrix3f scale;
200249
Eigen::Matrix3f rot;

0 commit comments

Comments
 (0)