colmap一般使用sift-gpu,涉及的知识:
其中由于Euclidean distance 容易受较大值的影响,使用
Hellinger distance更稳定。colmap先对sift的结果进行L1 normalize,再对每一个元素求平方根,得到的结果便是L2 normalized。在论文中给出了使用RootSIFT匹配的结果优于SIFT
Octave,Sublevel,模糊尺度,极值筛选阈值,初始直方图的平滑次数,描述子的归一化,DSP-Sift特征等。
直接看源码:
struct FeatureKeypoint {
FeatureKeypoint();
FeatureKeypoint(const float x, const float y);
FeatureKeypoint(const float x, const float y, const float scale,
const float orientation);
FeatureKeypoint(const float x, const float y, const float a11,
const float a12, const float a21, const float a22);
static FeatureKeypoint FromParameters(const float x, const float y,
const float scale_x,
const float scale_y,
const float orientation,
const float shear);
// Rescale the feature location and shape size by the given scale factor.
void Rescale(const float scale);
void Rescale(const float scale_x, const float scale_y);
// Compute similarity shape parameters from affine shape.
float ComputeScale() const;
float ComputeScaleX() const;
float ComputeScaleY() const;
float ComputeOrientation() const;
float ComputeShear() const;
// Location of the feature, with the origin at the upper left image corner,
// i.e. the upper left pixel has the coordinate (0.5, 0.5).
float x;
float y;
// Affine shape of the feature.
float a11;
float a12;
float a21;
float a22;
};
FeatureKeypointsBlob FeatureKeypointsToBlob(const FeatureKeypoints& keypoints) {
const FeatureKeypointsBlob::Index kNumCols = 6;
FeatureKeypointsBlob blob(keypoints.size(), kNumCols);
for (size_t i = 0; i < keypoints.size(); ++i) {
blob(i, 0) = keypoints[i].x;
blob(i, 1) = keypoints[i].y;
blob(i, 2) = keypoints[i].a11;
blob(i, 3) = keypoints[i].a12;
blob(i, 4) = keypoints[i].a21;
blob(i, 5) = keypoints[i].a22;
}
return blob;
}
FeatureKeypoints FeatureKeypointsFromBlob(const FeatureKeypointsBlob& blob) {
FeatureKeypoints keypoints(static_cast<size_t>(blob.rows()));
if (blob.cols() == 2) {
for (FeatureKeypointsBlob::Index i = 0; i < blob.rows(); ++i) {
keypoints[i] = FeatureKeypoint(blob(i, 0), blob(i, 1));
}
} else if (blob.cols() == 4) {
for (FeatureKeypointsBlob::Index i = 0; i < blob.rows(); ++i) {
keypoints[i] =
FeatureKeypoint(blob(i, 0), blob(i, 1), blob(i, 2), blob(i, 3));
}
} else if (blob.cols() == 6) {
for (FeatureKeypointsBlob::Index i = 0; i < blob.rows(); ++i) {
keypoints[i] = FeatureKeypoint(blob(i, 0), blob(i, 1), blob(i, 2),
blob(i, 3), blob(i, 4), blob(i, 5));
}
} else {
LOG(FATAL) << "Keypoint format not supported";
}
return keypoints;
}
FeatureMatchesBlob FeatureMatchesToBlob(const FeatureMatches& matches) {
const FeatureMatchesBlob::Index kNumCols = 2;
FeatureMatchesBlob blob(matches.size(), kNumCols);
for (size_t i = 0; i < matches.size(); ++i) {
blob(i, 0) = matches[i].point2D_idx1;
blob(i, 1) = matches[i].point2D_idx2;
}
return blob;
}
FeatureMatches FeatureMatchesFromBlob(const FeatureMatchesBlob& blob) {
CHECK_EQ(blob.cols(), 2);
FeatureMatches matches(static_cast<size_t>(blob.rows()));
for (FeatureMatchesBlob::Index i = 0; i < blob.rows(); ++i) {
matches[i].point2D_idx1 = blob(i, 0);
matches[i].point2D_idx2 = blob(i, 1);
}
return matches;
}
暴力匹配(遍历费时),顺序匹配(序列图像用)、词汇树匹配(兼顾时效)、空间匹配(有额外的空间信息用)和转移匹配:
上图的Custom为自定义,自己去定义哪几张图像匹配哪几张不匹配,算是一种人工调参手段。
// Determine inlier ratios of different models.
const double E_F_inlier_ratio =
static_cast<double>(E_report.support.num_inliers) /
F_report.support.num_inliers;
const double H_F_inlier_ratio =
static_cast<double>(H_report.support.num_inliers) /
F_report.support.num_inliers;
const double H_E_inlier_ratio =
static_cast<double>(H_report.support.num_inliers) /
E_report.support.num_inliers;
// Uncalibrated configuration.
num_inliers = F_report.support.num_inliers;
best_inlier_mask = &F_report.inlier_mask;
if (H_F_inlier_ratio > options.max_H_inlier_ratio) {
config = ConfigurationType::PLANAR_OR_PANORAMIC;
if (H_report.support.num_inliers > num_inliers) {
num_inliers = H_report.support.num_inliers;
best_inlier_mask = &H_report.inlier_mask;
}
} else {
config = ConfigurationType::UNCALIBRATED;
}
} else if (H_report.success &&
H_report.support.num_inliers >= options.min_num_inliers) {
num_inliers = H_report.support.num_inliers;
best_inlier_mask = &H_report.inlier_mask;
config = ConfigurationType::PLANAR_OR_PANORAMIC;
} else {
config = ConfigurationType::DEGENERATE;
return;
}
索引对(替换前端只要统一为以下输出格式就好了)
FeatureMatchesBlob FeatureMatchesToBlob(const FeatureMatches& matches) {
const FeatureMatchesBlob::Index kNumCols = 2;
FeatureMatchesBlob blob(matches.size(), kNumCols);
for (size_t i = 0; i < matches.size(); ++i) {
blob(i, 0) = matches[i].point2D_idx1;
blob(i, 1) = matches[i].point2D_idx2;
}
return blob;
}
FeatureMatches FeatureMatchesFromBlob(const FeatureMatchesBlob& blob) {
CHECK_EQ(blob.cols(), 2);
FeatureMatches matches(static_cast<size_t>(blob.rows()));
for (FeatureMatchesBlob::Index i = 0; i < blob.rows(); ++i) {
matches[i].point2D_idx1 = blob(i, 0);
matches[i].point2D_idx2 = blob(i, 1);
}
return matches;
}
剔除Mask以外的特征
void MaskKeypoints(const Bitmap& mask, FeatureKeypoints* keypoints,
FeatureDescriptors* descriptors) {
size_t out_index = 0;
BitmapColor<uint8_t> color;
for (size_t i = 0; i < keypoints->size(); ++i) {
if (!mask.GetPixel(static_cast<int>(keypoints->at(i).x),
static_cast<int>(keypoints->at(i).y), &color) ||
color.r == 0) {
// Delete this keypoint by not copying it to the output.
} else {
// Retain this keypoint by copying it to the output index (in case this
// index differs from its current position).
if (out_index != i) {
keypoints->at(out_index) = keypoints->at(i);
for (int col = 0; col < descriptors->cols(); ++col) {
(*descriptors)(out_index, col) = (*descriptors)(i, col);
}
}
out_index += 1;
}
}
keypoints->resize(out_index);
descriptors->conservativeResize(out_index, descriptors->cols());
}
看一下GUI:
输出为1.4的db格式,246
输出为2.4的db格式,索引值