я пытаюсь внедрить вручную функция оценки фундаментальной матрицы за соответствующие точки (на основе сходства между двумя изображениями). Соответствующие точки получают после выполнения обнаружения, извлечения, сопоставления и соотношения признаков ORB.
На эту тему доступно много литературы из хороших источников. Однако ни один из них не дает хорошего псевдокода для выполнения процесса. Я просмотрел различные главы книги о геометрии с несколькими видами; а также много интернет-источников.
это источник по-видимому, дает формулу для нормализации, и я следовал формуле, упомянутой на странице 13 этого источника.
На основе этой формулы я создал следующий алгоритм. Я не уверен, что делаю все правильно!
Normalization.hpp
class Normalization {
typedef std::vector <cv::Point2f> intercepts;
typedef std::vector<cv::Mat> matVec;
public:
Normalization () {}
~Normalization () {}
void makeAverage(intercepts pointsVec);
std::tuple <cv::Mat, cv::Mat> normalize(intercepts pointsVec);
matVec getNormalizedPoints(intercepts pointsVec);
private:
double xAvg = 0;
double yAvg = 0;
double count = 0;
matVec normalizedPts;
double distance = 0;
matVec matVecData;
cv::Mat forwardTransform;
cv::Mat reverseTransform;
};
Normalization.cpp
#include "Normalization.hpp"
typedef std::vector <cv::Point2f> intercepts;
typedef std::vector<cv::Mat> matVec;
/*******
*@brief : The makeAverage function receives the input 2D coordinates (x, y)
* and creates the average of x and y
*@params : The input parameter is a set of all matches (x, y pairs) in image A
************/
void Normalization::makeAverage(intercepts pointsVec) {
count = pointsVec.size();
for (auto& member : pointsVec) {
xAvg = xAvg + member.x;
yAvg = yAvg + member.y;
}
xAvg = xAvg / count;
yAvg = yAvg / count;
}
/*******
*@brief : The normalize function accesses the average distance calculated
* in the previous step and calculates the forward and inverse transformation
* matrices
*@params : The input to this function is a vector of corresponding points in given image
*@return : The returned data is a tuple of forward and inverse transformation matrices
*************/
std::tuple <cv::Mat, cv::Mat> Normalization::normalize(intercepts pointsVec) {
for (auto& member : pointsVec) {
// Accumulate the distance for every point
distance += ((1 / (count * std::sqrt(2))) *\
(std::sqrt(std::pow((member.x - xAvg), 2)\
+ std::pow((member.y - yAvg), 2))));
}
forwardTransform = (cv::Mat_<double>(3, 3) << (1 / distance), \
0, -(xAvg / distance), 0, (1 / distance), \
-(yAvg / distance), 0, 0, 1);
reverseTransform = (cv::Mat_<double>(3, 3) << distance, 0, xAvg, \
0, distance, yAvg, 0, 0, 1);
return std::make_tuple(forwardTransform, reverseTransform);
}
/*******
*@brief : The getNormalizedPoints function trannsforms the raw image coordinates into
* transformed coordinates using the forwardTransform matrix estimated in previous step
*@params : The input to this function is a vector of corresponding points in given image
*@return : The returned data is vector of transformed coordinates
*************/
matVec Normalization::getNormalizedPoints(intercepts pointsVec) {
cv::Mat triplet;
for (auto& member : pointsVec) {
triplet = (cv::Mat_<double>(3, 1) << member.x, member.y, 1);
matVecData.emplace_back(forwardTransform * triplet);
}
return matVecData;
}
Это правильный путь? Есть ли другие способы нормализации?
Я думаю, что вы можете сделать это по-своему, но в «Геометрии множественного вида в компьютерном зрении» Хартли и Циссерман рекомендуют изотропное масштабирование (стр. 107):
Изотропное масштабирование. В качестве первого шага нормализации координаты в каждом изображении
переведен (по-разному перевод для каждого изображения), чтобы привести центроид
набор всех точек на начало координат. Координаты также масштабируются так, чтобы в среднем
точка x имеет вид x = (x, y, w) T, причем каждое из x, y и w имеет одинаковое среднее значение
величина. Вместо того, чтобы выбирать разные масштабные коэффициенты для каждого направления координат,
коэффициент изотропного масштабирования выбирается таким образом, чтобы координаты x и y точки масштабировались
в равной степени. С этой целью мы решили масштабировать координаты так, чтобы среднее расстояние
точка х от начала координат равна
√
2. Это означает, что «средняя» точка равна
до (1, 1, 1) т. В итоге преобразование выглядит следующим образом:
(i) Точки переведены так, чтобы их центроид находился в начале координат.
(ii) Затем точки масштабируются так, чтобы среднее расстояние от начала координат было равно
до √2.
(iii) Это преобразование применяется к каждому из двух изображений независимо.
Они утверждают, что это важно для прямого линейного преобразования (DLT), но еще более важно для вычисления фундаментальной матрицы, как вы хотите.
Алгоритм, который вы выбрали, нормализовал координаты точки к (1, 1, 1), но сделал не примените масштабирование так, чтобы среднее расстояние от начала координат было равно √2.
Вот некоторый код для этого типа нормализации. Шаг усреднения остался прежним:
std::vector<cv::Mat> normalize(std::vector<cv::Point2d> pointsVec) {
// Averaging
double count = (double) pointsVec.size();
double xAvg = 0;
double yAvg = 0;
for (auto& member : pointsVec) {
xAvg = xAvg + member.x;
yAvg = yAvg + member.y;
}
xAvg = xAvg / count;
yAvg = yAvg / count;
// Normalization
std::vector<cv::Mat> points3d;
std::vector<double> distances;
for (auto& member : pointsVec) {
double distance = (std::sqrt(std::pow((member.x - xAvg), 2) + std::pow((member.y - yAvg), 2)));
distances.push_back(distance);
}
double xy_norm = std::accumulate(distances.begin(), distances.end(), 0.0) / distances.size();
// Create a matrix transforming the points into having mean (0,0) and mean distance to the center equal to sqrt(2)
cv::Mat_<double> Normalization_matrix(3, 3);
double diagonal_element = sqrt(2) / xy_norm;
double element_13 = -sqrt(2) * xAvg / xy_norm;
double element_23 = -sqrt(2)* yAvg/ xy_norm;
Normalization_matrix << diagonal_element, 0, element_13,
0, diagonal_element, element_23,
0, 0, 1;
// Multiply the original points with the normalization matrix
for (auto& member : pointsVec) {
cv::Mat triplet = (cv::Mat_<double>(3, 1) << member.x, member.y, 1);
points3d.emplace_back(Normalization_matrix * triplet);
}
return points3d;
}
Других решений пока нет …