Перейти к содержимому
Compvision.ru
mrgloom

FeaturesMatcher

Recommended Posts

matchers.hpp matchers.cpp

class CV_EXPORTS FeaturesMatcher

{

public:

    virtual ~FeaturesMatcher() {}

    void operator ()(const ImageFeatures &features1, const ImageFeatures &features2,

                     MatchesInfo& matches_info) { match(features1, features2, matches_info); }

    void operator ()(const std::vector<ImageFeatures> &features, std::vector<MatchesInfo> &pairwise_matches,

                     const cv::Mat &mask = cv::Mat());

    bool isThreadSafe() const { return is_thread_safe_; }

    virtual void collectGarbage() {}

protected:

    FeaturesMatcher(bool is_thread_safe = false) : is_thread_safe_(is_thread_safe) {}

    virtual void match(const ImageFeatures &features1, const ImageFeatures &features2,

                       MatchesInfo& matches_info) = 0;

    bool is_thread_safe_;

};

class CV_EXPORTS BestOf2NearestMatcher : public FeaturesMatcher

{

public:

    BestOf2NearestMatcher(bool try_use_gpu = false, float match_conf = 0.3f, int num_matches_thresh1 = 6,

                          int num_matches_thresh2 = 6);

    void collectGarbage();

protected:

    void match(const ImageFeatures &features1, const ImageFeatures &features2, MatchesInfo &matches_info);

    int num_matches_thresh1_;

    int num_matches_thresh2_;

    Ptr<FeaturesMatcher> impl_;

};

void BestOf2NearestMatcher::match(const ImageFeatures &features1, const ImageFeatures &features2,

                                  MatchesInfo &matches_info)

{

    (*impl_)(features1, features2, matches_info);

//...
не могу понять код. зачем нужна ссылка на базовый клас в производном классе?
Ptr<FeaturesMatcher> impl_;
в BestOf2NearestMatcher::match первой строчкой вызывается оператор ()
(*impl_)(features1, features2, matches_info);
в переопределенном операторе
void operator ()(const ImageFeatures &features1, const ImageFeatures &features2,

                     MatchesInfo& matches_info) { match(features1, features2, matches_info); }
и match используется уже от BestOf2NearestMatcher? причем мы нигде не указали, что impl_ должен использовать BestOf2NearestMatcher::match. + получается что BestOf2NearestMatcher::match внутри BestOf2NearestMatcher::match и рекурсия?(вообщем этот момент непонятен) и что делает ?
void operator ()(const std::vector<ImageFeatures> &features, std::vector<MatchesInfo> &pairwise_matches,

                     const cv::Mat &mask = cv::Mat());

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

void BestOf2NearestMatcher::match(const ImageFeatures &features1, const ImageFeatures &features2,

                                  MatchesInfo &matches_info)

{

    (*impl_)(features1, features2, matches_info);


    // Check if it makes sense to find homography

    if (matches_info.matches.size() < static_cast<size_t>(num_matches_thresh1_))

        return;


    // Construct point-point correspondences for homography estimation

    Mat src_points(1, static_cast<int>(matches_info.matches.size()), CV_32FC2);

    Mat dst_points(1, static_cast<int>(matches_info.matches.size()), CV_32FC2);

    for (size_t i = 0; i < matches_info.matches.size(); ++i)

    {

        const DMatch& m = matches_info.matches[i];


        Point2f p = features1.keypoints[m.queryIdx].pt;

        p.x -= features1.img_size.width * 0.5f;

        p.y -= features1.img_size.height * 0.5f;

        src_points.at<Point2f>(0, static_cast<int>(i)) = p;


        p = features2.keypoints[m.trainIdx].pt;

        p.x -= features2.img_size.width * 0.5f;

        p.y -= features2.img_size.height * 0.5f;

        dst_points.at<Point2f>(0, static_cast<int>(i)) = p;

    }


    // Find pair-wise motion

    matches_info.H = findHomography(src_points, dst_points, matches_info.inliers_mask, CV_RANSAC);

    if (std::abs(determinant(matches_info.H)) < numeric_limits<double>::epsilon())

        return;


    // Find number of inliers

    matches_info.num_inliers = 0;

    for (size_t i = 0; i < matches_info.inliers_mask.size(); ++i)

        if (matches_info.inliers_mask[i])

            matches_info.num_inliers++;


    // These coeffs are from paper M. Brown and D. Lowe. "Automatic Panoramic Image Stitching

    // using Invariant Features"

    matches_info.confidence = matches_info.num_inliers / (8 + 0.3 * matches_info.matches.size());


    // Set zero confidence to remove matches between too close images, as they don't provide

    // additional information anyway. The threshold was set experimentally.

    matches_info.confidence = matches_info.confidence > 3. ? 0. : matches_info.confidence;


    // Check if we should try to refine motion

    if (matches_info.num_inliers < num_matches_thresh2_)

        return;


    // Construct point-point correspondences for inliers only

    src_points.create(1, matches_info.num_inliers, CV_32FC2);

    dst_points.create(1, matches_info.num_inliers, CV_32FC2);

    int inlier_idx = 0;

    for (size_t i = 0; i < matches_info.matches.size(); ++i)

    {

        if (!matches_info.inliers_mask[i])

            continue;


        const DMatch& m = matches_info.matches[i];


        Point2f p = features1.keypoints[m.queryIdx].pt;

        p.x -= features1.img_size.width * 0.5f;

        p.y -= features1.img_size.height * 0.5f;

        src_points.at<Point2f>(0, inlier_idx) = p;


        p = features2.keypoints[m.trainIdx].pt;

        p.x -= features2.img_size.width * 0.5f;

        p.y -= features2.img_size.height * 0.5f;

        dst_points.at<Point2f>(0, inlier_idx) = p;


        inlier_idx++;

    }


    // Rerun motion estimation on inliers only

    matches_info.H = findHomography(src_points, dst_points, CV_RANSAC);

}
я так понимаю этой строчкой производится матчинг
(*impl_)(features1, features2, matches_info);
но непонятно где определена функция match
void operator ()(const ImageFeatures &features1, const ImageFeatures &features2,

                     MatchesInfo& matches_info) { match(features1, features2, matches_info); }
зачем вычитается по пол изображения?
p.x -= features1.img_size.width * 0.5f;

        p.y -= features1.img_size.height * 0.5f;

почему мы сначала определяем гомографию, а потом только на инлайнерах повторно, по идее можем сразу получить же.

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

зачем нужна ссылка на базовый клас в производном классе?

Ptr<FeaturesMatcher> impl_;

Это не ссылка на базовый класс, а ссылка на его наследника, имплементирующего сам алгоритм. Так наследники могут быть разными (CPU или GPU реализация алгоритма), то они приводятся к указателю на базовый класс и используются единообразно. Это основной принцип ООП в С++, то для чего и были придуманы классы и виртуальные функции.

Все дальнейшие вопросы - это также делали реализации ООП в С++. Если хочется узнать, что конкретно вызывается и используется в твоём случае, то используй отладчик или логи. Сразу всё станет понятно.

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

почему тогда написано

Ptr<FeaturesMatcher> impl_;

FeaturesMatcher как раз базовый класс, а BestOf2NearestMatcher наследник, не?

причем уже в cpp определены

CpuMatcher : public FeaturesMatcher

GpuMatcher : public FeaturesMatcher

и имеют

CpuMatcher::match

GpuMatcher::match

только я не понимаю какое они отношение имеют к BestOf2NearestMatcher ?

а всё понял в конструкторе выбирается тип.

BestOf2NearestMatcher::BestOf2NearestMatcher(bool try_use_gpu, float match_conf, int num_matches_thresh1, int num_matches_thresh2)

{

#ifdef HAVE_OPENCV_GPU

if (try_use_gpu && getCudaEnabledDeviceCount() > 0)

impl_ = new GpuMatcher(match_conf);

else

#else

(void)try_use_gpu;

#endif

impl_ = new CpuMatcher(match_conf);

is_thread_safe_ = impl_->isThreadSafe();

num_matches_thresh1_ = num_matches_thresh1;

num_matches_thresh2_ = num_matches_thresh2;

}

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Спасибо, понял!

Вот еще хочу задать вопрос. Напишу здесь, чтобы темы не плодить. Если что (дискуссия пойдет), то перенести можно.

Можно ли как-то получить Keypoint для заданных координат изображения. Ну, то есть, грубо говоря, нажал на изображение,

получил координаты и из них Keypoint каким-нибудь детектором (Surf,Sift...)?

Имея кейпойнты уже понятно, как считать дескрипторы и так далее. Но вот как заставить детектор принудительно рассчитать характерную точку?

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Насколько я помню, если передавать вычислялке SURF ключевые точки, то он в них и вычисляет.

Вот откопал у себя исходник, правда там через GPU и делал давно, могло что то поменяться:

main.cpp

Насколько помню, как дескриптор текстуры работал он неважно (фильтры Габора лучше).

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Дело в том, что специальная точка это не просто точка (x,y), а еще доп. свойства типа скейл, направление и т.д. (в примере это вроде бы параметр surfradius)

так видимо чтобы дескрипторы работали лучше надо брать их на разных скейлах и пихать в общий вектор.

так что возможно дескриптор не в любой точке имеет смысл, хотя вроде как есть dense sift

кстати в vlfeat есть даже визуализация (патчей)дескрипторов не знаю насколько это адекватно(может там тупо как то вырезается небольшая область вокруг точки)

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах
специальная точка это не просто точка (x,y), а еще доп. свойства типа скейл, направление

Вот в том-то и дело. И просто так x,y на вход вычислялке не подать. Конечно, есть вариант подать, как в примере, вот эту структуру:

KeyPoint kp;

kp.angle=0;

kp.octave=0;

kp.pt.x=c;

kp.pt.y=r;

kp.size=surfradius;

keypoints.push_back(kp);

Но это тогда какой-то ущербный кейпойнт. Ведь при сопоставлении точек (и, я так понимаю, вычислении дескрипторов) там же все учитывают. И масштаб, и направление. А тут мы все обнуляем.

Нужно как-то детектор принудительно заставить посчитать полную структуру Keypoint для заданной точки. Но, вроде как, такого инструмента нет...

Вот, что интересно - по кейпойнту вычисляется дескриптор. Но что тогда дескриптор-то? Просто фрагмент пикселей? Но по теории вот эта вся "требуха" типа направления итп входит именно в дескриптор. Но врядли в opencv все реализовано так, что одно и тоже считается и в кейпойнтах, и в дескрипторах.

Скорее всего, например, алгоритм смотрит на октаву кейпойнта и для нее считает дескриптор. Как-то так и для остального. Задавая угол и октаву нулевыми мы повлияем на вычисление дескриптора. И потом их будет просто некорректно сопоставлять. Блин, печаль какая-то...

Стоп...а вот у класса KeyPoint есть такой конструктор: KeyPoint(float x, float y, float _size).

Это может помочь? Тут просто size зададим от балды, 8, скажем...Интересно, программа рассчитает остальное? Хотя врядли. Если там изначально, например, нет особенности, то что будет записано во все поля?

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

задаёте жёстко (x,y), а остальные параметры с шагом варьируете в пределах [a b](для каждого параметра свой диапазон) в итоге получаете разные комбинации параметров и в одной точке получаете N точек и для этих точек считаете дескрипторы и эти дескрипторы соединяете в один большой вектор.

вообщем вопрос остается только в том какой диапазон для каждого параметра + сколько это будет занимать времени.

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах
задаёте жёстко (x,y), а остальные параметры с шагом варьируете в пределах [a b](для каждого параметра свой диапазон) в итоге получаете разные комбинации параметров и в одной точке получаете N точек и для этих точек считаете дескрипторы и эти дескрипторы соединяете в один большой вектор.

вообщем вопрос остается только в том какой диапазон для каждого параметра + сколько это будет занимать времени.

Ну да, я по этому пути и пошел. Времени жрет, естественно, много. А у меня и разрешения большие, да и в динамике надо это все обрабатывать. Короче говоря, буду какой-то другой подход просто применять.

Кстати, в развитие темы:) Я все не могу найти, как средствами opencv произвести само объединение изображений! То есть посчитали гомографию, через findHomograpy, потом warpPerspective, а как потом объединить-то эффективно два изображения (опорное и "warped")?

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

можно просто написать свою просто с addweighted или использовать готовую более сложную из модуля warper.

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Раз о склейке речь пошла, то перенес в эту ветку.

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Создайте учётную запись или войдите для комментирования

Вы должны быть пользователем, чтобы оставить комментарий

Создать учётную запись

Зарегистрируйтесь для создания учётной записи. Это просто!

Зарегистрировать учётную запись

Войти

Уже зарегистрированы? Войдите здесь.

Войти сейчас


  • Сейчас на странице   0 пользователей

    Нет пользователей, просматривающих эту страницу

×