Подтвердить что ты не робот

Согласование термографических/нетермографических изображений с детекторами функций OpenCV

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

Вариант использования: изображение берется из штатива неподвижной точки с помощью инфракрасной термографической камеры и стандартной камеры. После фотографирования фотограф хочет совместить изображения с каждой камеры. Будут некоторые сценарии, где изображение берется только с одной камерой, так как другой тип изображения не нужен. Да, возможно, что изображения могут быть сопоставлены с использованием временных меток, но конечный пользователь требует, чтобы они соответствовали компьютерному видению.

Я смотрел другие записи соответствия изображений на StackOverflow - они часто фокусировались на использовании сопоставления гистограммы и детекторов функций. Соответствие гистограмм здесь не является вариантом, так как мы не можем сопоставлять цвета между двумя типами изображений. В результате я разработал приложение, которое обнаруживает функцию. Помимо стандартного обнаружения функций, Ive также добавила некоторую логику, в которой говорится, что две ключевые точки не могут быть сопоставлены, если они не находятся в пределах определенного поля друг от друга (ключевая точка в левом углу изображения запроса не может совпадать с ключевой точкой справа изображения кандидата) - этот процесс происходит на третьем этапе кода ниже.

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

Допустимое совпадение: Valid match

Неверное совпадение: Invalid match

Вот код:

    // for each candidate image specified on the command line, compare it against the query image
        Mat img1 = imread(argv[1], CV_LOAD_IMAGE_GRAYSCALE); // loading query image
        for(int candidateImage = 0; candidateImage < (argc - 2); candidateImage++) {
            Mat img2 = imread(argv[candidateImage + 2], CV_LOAD_IMAGE_GRAYSCALE); // loading candidate image
            if(img1.empty() || img2.empty())
            {
                printf("Can't read one of the images\n");
                return -1;
            }

            // detecting keypoints
            SiftFeatureDetector detector;
            vector<KeyPoint> keypoints1, keypoints2;
            detector.detect(img1, keypoints1);
            detector.detect(img2, keypoints2);

            // computing descriptors
            SiftDescriptorExtractor extractor;
            Mat descriptors1, descriptors2;
            extractor.compute(img1, keypoints1, descriptors1);
            extractor.compute(img2, keypoints2, descriptors2);

            // matching descriptors
            BFMatcher matcher(NORM_L1);
            vector< vector<DMatch> > matches_stage1;
            matcher.knnMatch(descriptors1, descriptors2, matches_stage1, 2);

            // use nndr to eliminate weak matches
            float nndrRatio = 0.80f;
            vector< DMatch > matches_stage2;
            for (size_t i = 0; i < matches_stage1.size(); ++i)
            {
                if (matches_stage1[i].size() < 2)
                    continue;
                const DMatch &m1 = matches_stage1[i][0];
                const DMatch &m2 = matches_stage1[i][3];
                if(m1.distance <= nndrRatio * m2.distance)
                    matches_stage2.push_back(m1);
            }

            // eliminate points which are too far away from each other
            vector<DMatch> matches_stage3;
            for(int i = 0; i < matches_stage2.size(); i++) {
                Point queryPt = keypoints1.at(matches_stage2.at(i).queryIdx).pt;
                Point trainPt = keypoints2.at(matches_stage2.at(i).trainIdx).pt;

                // determine the lowest number here
                int lowestXAxis;
                int greaterXAxis;
                if(queryPt.x <= trainPt.x) { lowestXAxis = queryPt.x; greaterXAxis = trainPt.x; }
                else { lowestXAxis = trainPt.x; greaterXAxis = queryPt.x; }

                int lowestYAxis;
                int greaterYAxis;
                if(queryPt.y <= trainPt.y) { lowestYAxis = queryPt.y; greaterYAxis = trainPt.y; }
                else { lowestYAxis = trainPt.y; greaterYAxis = queryPt.y; }

                // determine if these points are acceptable
                bool acceptable = true;
                if( (lowestXAxis + MARGIN) < greaterXAxis) { acceptable = false; }
                if( (lowestYAxis + MARGIN) < greaterYAxis) { acceptable = false; }
                if(acceptable == false) { continue; }

                //// it acceptable -- provide details, perform input
                matches_stage3.push_back(matches_stage2.at(i));
            }

            // output how many individual matches were found for this training image
            cout << "good matches found for candidate image # " << (candidateImage+1) << " = " << matches_stage3.size() << endl;

Я использовал этот код сайта в качестве примера. Проблема Im заключается в том, что обнаружение функции не является надежным, и, похоже, мне не хватает цели соотношения NNDR. Я понимаю, что я нахожу K возможных совпадений для каждой точки в изображении запроса и что у меня есть K = 2. Но я не понимаю цели этой части в примере кода:

vector< DMatch > matches_stage2;
for (size_t i = 0; i < matches_stage1.size(); ++i)
{
    if (matches_stage1[i].size() < 2)
        continue;
    const DMatch &m1 = matches_stage1[i][0];
    const DMatch &m2 = matches_stage1[i][1];
    if(m1.distance <= nndrRatio * m2.distance)
        matches_stage2.push_back(m1);
}

Любые идеи о том, как я могу улучшить это дальше? Любые советы будут оценены как всегда.

4b9b3361

Ответ 1

Подтверждение, которое вы используете в настоящее время

Первый этап

Прежде всего, расскажите о той части кода, которую вы не понимаете. Идея состоит в том, чтобы держать только "сильные спички". Фактически, ваш вызов knnMatch находит для каждого дескриптора наилучшие два соответствия относительно евклидова расстояния "L2" (*). Это вовсе не означает, что это хорошие совпадения в реальности, но только то, что эти особенности очень похожи.

Позвольте мне попытаться объяснить вашу проверку сейчас, учитывая только одну особенность в изображении A (она обобщает их все):

  • Вы сопоставляете дескриптор этой точки с изображением B
  • Вы получите два лучших соответствия относительно евклидова расстояния (т.е. вы получите две наиболее похожие точки на изображении B)
  • Если расстояние от вашей точки до наилучшего соответствия намного меньше, чем расстояние от вашей точки до второго наилучшего соответствия, то вы предполагаете, что это хорошее совпадение. Другими словами, в изображении B была только одна точка, которая была действительно схожей (т.е. Имела небольшое евклидово расстояние) до точки на изображении A.
  • Если оба совпадения слишком похожи (т.е. !(m1.distance <= nndrRatio * m2.distance)), вы не можете различать их между собой, и вы не считаете совпадение.

Эта проверка имеет некоторые основные недостатки, как вы, вероятно, наблюдали:

  • Во-первых, если лучшие совпадения, полученные вами из knnMatch, ужасно плохие, тогда наилучшие из них могут быть приняты в любом случае.
  • Это не учитывает геометрию. Поэтому точка, расположенная слева от изображения, может быть похожа на точку, расположенную справа, хотя в действительности они явно не совпадают.

* EDIT: Используя SIFT, вы описываете каждую точку объекта на своем изображении, используя вектор с плавающей запятой. Вычисляя евклидово расстояние между двумя векторами, вы знаете, насколько они похожи. Если оба вектора точно совпадают, то расстояние равно нулю. Чем меньше расстояние, тем больше сходны точки. Но это не геометрический: точка в левой части вашего изображения может быть похожа на точку в правой части. Итак, вы сначала находите пары точек, которые выглядят одинаково (т.е. "Эта точка в похожа на эту точку в B, потому что евклидово расстояние между их векторами признаков невелико" ), а затем вам нужно проверить, что это совпадение является когерентным (т.е. "Возможно, эти похожие точки на самом деле одинаковы, потому что они оба находятся на левой стороне моего изображения" или "Они похожи, но это некогерентно, потому что я знаю, что они должны лежать на одной стороне изображения и они не" ).

Второй этап

То, что вы делаете на втором этапе, интересно, поскольку оно рассматривает геометрию: зная, что оба изображения были взяты из одной и той же точки (или почти одной и той же точки?), вы устраняете совпадения, которые не находятся в одном и том же регионе на обоих изображениях.

Проблема, которую я вижу в этом, заключается в том, что если оба изображения не были взяты в том же самом положении с одним и тем же углом, то это не сработает.

Предложение дальнейшего улучшения вашей проверки

Я бы лично работал на втором этапе. Хотя оба изображения не обязательно одинаковы, они описывают одну и ту же сцену. И вы можете воспользоваться геометрией этого.

Идея состоит в том, что вы должны иметь возможность найти преобразование от первого изображения ко второму (т.е. способ, которым точка, перемещенная из изображения A в изображение B, фактически связана с тем, как все перемещенные точки), И в вашей ситуации я бы поспорил, что адаптирован простой homography.

Вот мое предложение:

  • Вычислить совпадения с помощью knnMatch и сохранить этап 1 (возможно, вы захотите попытаться удалить его позже и наблюдать за последствиями)
  • Вычислите наилучшее возможное преобразование гомографии между этими совпадениями с помощью cv::findHomography (выберите алгоритм RANSAC).
  • findHomography имеет вывод mask, который даст вам листы (т.е. совпадения, которые использовались для вычисления преобразования гомографии).

Линейки, скорее всего, будут хорошими совпадениями, так как они будут когерентно геометрически.

EDIT: Я просто нашел пример, используя findHomography здесь.

Ответ 2

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

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

Ответ 3

После извлечения ключевых точек, формирование дескрипторов и сопоставления использует некоторый алгоритм удаления outlier, такой как RANSAC. Opencv предоставляет RANSAC с функцией findHomography. Вы можете увидеть реализацию. Я использовал это с SURF, и это дало мне неплохие результаты.

Ответ 4

Идеи:

a) используйте модуль суперразрешения для улучшения ввода (OpenCV245).

b) использовать максимально устойчивые локальные цветовые регионы как соответствующие функции (MSER).