Как правильно нормализовать соответствующие точки перед оценкой Фундаментальной матрицы в OpenCV C ++? - PullRequest
0 голосов
/ 23 октября 2018

Я пытаюсь вручную реализовать функцию оценки фундаментальной матрицы для соответствующих точек (на основе сходства между двумя изображениями).Соответствующие точки получают после выполнения обнаружения, извлечения, сопоставления и соотношения признаков 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;
}

Это правильный путь?Есть ли другие способы нормализации?

1 Ответ

0 голосов
/ 24 октября 2018

Я думаю, что вы можете сделать это по-своему, но в «Геометрии нескольких видов в компьютерном зрении» Хартли и Циссерман рекомендуют изотропное масштабирование (стр. 107):

Изотропное масштабирование ,В качестве первого шага нормализации координаты в каждом изображении переводятся (по-разному для каждого изображения), чтобы привести центр тяжести множества всех точек в начало координат.Координаты также масштабируются таким образом, чтобы в среднем точка x имела форму x = (x, y, w) T, причем каждый из x, y и w имел одинаковую среднюю величину.Вместо того, чтобы выбирать разные масштабные коэффициенты для каждого направления координат, изотропный масштабный коэффициент выбирается таким образом, чтобы координаты x и y точки масштабировались одинаково.С этой целью мы выбираем масштабирование координат таким образом, чтобы среднее расстояние точки x от начала координат было равно √ 2. Это означает, что «средняя» точка равна (1, 1, 1) T.Таким образом, преобразование выглядит следующим образом:
(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;
}
...