Меня зовут Бромбин Андрей, я студент МГТУ имени Баумана. В этой статье я расскажу, как погружался в исследование алгоритмов автономной навигации.

Введение

Визуальная одометрия играет ключевую роль в навигации малогабаритных беспилотных летательных аппаратов (БЛА), особенно в условиях, где GPS недоступен или его точность ограничена. В этой статье я делюсь результатами исследования популярных алгоритмов визуальной одометрии, реализованных на Java с использованием OpenCV. Мы сравним точность и производительность методов ORB, R2D2, SIFT и их комбинаций, а также оценим их пригодность для систем навигации БЛА.

Постановка задачи

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

Алгоритмы для анализа:

  • SIFT (Scale-Invariant Feature Transform)

  • SIFT + FOF (Farneback Optical Flow)

  • ORB (Oriented Fast and Rotated BRIEF)

  • R2D2 и Faster2D2

Данные для исследования

Мною был выбран dataset KITTI – набор данных для исследования в области визуальной одометрии. Потому что он содержит самые важные данные: параметры камер, радаров, лидаров, навигационной системы, которые позволяют совершать исследования в самых обширных областях. KITTI является оптимальным решением в области исследования методов детектирования ключевых точек, так как содержит качественные, точные и обширные характеристики каждого кадра. На самом деле датасет'ов с полноценным набором необходимых данных не так много, и старый добрый KITTI выручает.

Теоретическая основа

Simultaneous Localization and Mapping (SLAM) — это технология, позволяющая одновременно определять местоположение устройства и строить карту окружающей среды.

Структурная хема алгоритмов автономной навигации
Структурная хема алгоритмов автономной навигации

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

Методы компьютерного зрения играют ключевую роль в визуальной одометрии для навигации дронов.

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

Фундаментальная матрица F – математическая абстракция, представляющая собой связь между камерами в трёхмерной сцене. Она описывает геометрические свойства проекции точек из одного положения камеры в другое, играя важную роль в задачах компьютерного зрения. Представляет собой матрицу 3x3, обозначаемую как F и описываемую формулой:

F = (K_1^T)^{-1} \cdot [t]_x \cdot R \cdot K_2^{-1}

где R — матрица поворота,
K — матрица калибровки камеры:

K_n = \begin{bmatrix}f_x & s & c_x \\0 & f_y & c_y \\0 & 0 & 1\end{bmatrix}

где ( fx, fy ) — фокусные расстояния,
s — параметр наклона осей координат,
( cx, cy ) — координаты оптического центра.

Кососимметричная матрица [ tx ] вычисляется следующим образом:

[t]_x = \begin{bmatrix}0 & -z & y \\z & 0 & -x \\-y & x & 0\end{bmatrix}

где ( x, y, z ) — координаты вектора смещения.

Альтернативный способ вычисления F

Фундаментальная матрица также удовлетворяет следующему уравнению:

x'^T \cdot F \cdot x = 0

где x и x′ — координаты соответствующих ключевых точек на двух изображениях.

Расстояние до эпиполярной линии

Метрики точности рассчитываются на основе расстояния до эпиполярных линий. Оно определяется как:

D = d(x, F^T \cdot x') + d(x', F \cdot x)

где d( x, l) — расстояние от точки до эпиполярной линии lll, вычисляемое по формуле:

d(x, l) = \frac{|ax + by + c|}{\sqrt{a^2 + b^2}}

где l = [a, b, c] — параметры эпиполярной линии,
x и x' — координаты точек на изображениях.

В формуле (6), l1 и l2 используются для нормализации расстояния, учитывая параметризацию эпиполярной линии. Пример расстояний до эпиполярной линии представлен на рисунке 1.

Рисунок 1 - Расстояния от точек до эпиполярных линий
Рисунок 1 - Расстояния от точек до эпиполярных линий

Подробнее про конкретные реализации SLAM: ORB, SIFT, r2d2 поясню в следующих статьях по вашей заинтересованности.

Методология

Эксперименты включали два сценария:

  1. Малое межкадровое смещение: пары изображений со смещением ~1 м.

  2. Большое межкадровое смещение: пары изображений со смещением ~10 м.

Основные этапы:

  • Выделение ключевых точек.

  • Сопоставление и фильтрация точек (например, тест Лёва).

  • Вычисление фундаментальной матрицы и эпиполярных линий.

  • Оценка точности алгоритмов с использованием средних и медианных расстояний до эпиполярных линий.

Java реализация с использование openCV на примере SIFT

Стоит оговориться, что код написан исключительно в исследовательских целях. Например, отсутствие логирования, api и так далее - не предусмотрены в рамках исследования.

Блок схема:

Блок-схема математической модели
Блок-схема математической модели

Создание матрицы камеры:

public static Mat createCameraMatrix(double fx, double fy, double cx, double cy) {
    Mat cameraMatrix = new Mat(3, 3, CvType.CV_64F);
    cameraMatrix.put(0, 0, fx);
    cameraMatrix.put(1, 1, fy);
    cameraMatrix.put(0, 2, cx);
    cameraMatrix.put(1, 2, cy);
    cameraMatrix.put(2, 2, 1);
    return cameraMatrix;
}

Этот метод создает матрицу камеры K с параметрами фокусного расстояния и координат оптического центра. Используется для преобразования изображений и последующего вычисления фундаментальной матрицы.

Вычисление фундаментальной матрицы:

Для улучшения описания этого метода в статье, уточним, что библиотека OpenCV предоставляет свой метод для расчета фундаментальной матрицы, а также уточним, что T_cam1_cam0 относится к трансформации между кадрами 1 и 2, а не просто двум камерам.

public static Mat computeFundamentalMatrix(Mat T_cam1_cam0, Mat cameraMatrix0) {
        Mat shift = T_cam1_cam0.col(3).rowRange(0, 3);
        Mat skew = new Mat(3, 3, CvType.CV_64F);
        skew.put(0, 0, 0, -shift.get(2, 0)[0], shift.get(1, 0)[0],
             shift.get(2, 0)[0], 0, -shift.get(0, 0)[0],
             -shift.get(1, 0)[0], shift.get(0, 0)[0]);
  
        Mat rotation = T_cam1_cam0.submat(0, 3, 0, 3);
        Mat ess = new Mat();
        Core.gemm(skew, rotation, 1.0, new Mat(), 0.0, ess);

        Mat transposedMtx = new Mat();
        Core.transpose(cameraMatrix0, transposedMtx);
  
        Mat invMtx = new Mat();
        Core.invert(transposedMtx, invMtx);

        Mat temp = new Mat();
        Core.gemm(invMtx, ess, 1.0, new Mat(), 0.0, temp);
        Mat fundamentalMatrix = new Mat();
        Core.gemm(temp, cameraMatrix0.inv(), 1.0, new Mat(), 0.0, fundamentalMatrix);

        return fundamentalMatrix;
    }

Также можно добавить описание, что в OpenCV есть встроенный методCalib3d.findFundamentalMat, который предоставляет более удобный способ вычисления фундаментальной матрицы.

Вычисление эпиполярных расстояний:

public static Mat calculateEpipolarDistances(MatOfPoint2f points0, 
                                             MatOfPoint2f points1, 
                                             Mat lines0, Mat lines1) {
    int numPoints = (int) points0.size().height;
    Mat distances = new Mat(numPoints, 1, CvType.CV_64F);
    List<Point> pointsList0 = points0.toList();
    List<Point> pointsList1 = points1.toList();
    List<Double> distancesList = new ArrayList<>();

    for (int i = 0; i < numPoints; i++) {
        Point point0 = pointsList0.get(i);
        Point point1 = pointsList1.get(i);
        double d1 = calculateDistance(point0, lines1.row(i));
        double d2 = calculateDistance(point1, lines0.row(i));
        distancesList.add(d1 + d2);
    }

    MatOfDouble distancesMat = new MatOfDouble();
    distancesMat.fromList(distancesList);
    distancesMat.copyTo(distances);

    return distances;
}

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

Вывод статистики из расстояний:

public static MyPair calculateAndPrintStatisticsForMat(Mat distances) {
    Collections.sort(distances);
    double sum = 0;
    for (Double distance : distances.toList()) {
        sum += distance;
    }
    double mean = sum / distances.rows();

    double median;
    if (distances.rows() % 2 == 0) {
        median = (distances.get(distances.rows() / 2, 0)[0] + 
                  + distances.get(distances.rows() / 2 - 1, 0)[0]) / 2;
    } else {
        median = distances.get(distances.rows() / 2, 0)[0];
    }

    return new MyPair(mean, median);
}

Main Class

Загрузка библиотеки OpenCV и создание матрицы камеры:

    System.loadLibrary(Core.NATIVE_LIBRARY_NAME);

    double fx = 501.4757919305817;
    double fy = 501.4757919305817;
    double cx = 421.7953735163109;
    double cy = 167.65799492501083;

    Mat cameraMatrix0 = createCameraMatrix(fx, fy, cx, cy);
    System.out.println("Camera Matrix:");
    System.out.println(cameraMatrix0.dump());

Этот этап загружает библиотеку OpenCV и создает матрицу камеры с параметрами фокусного расстояния и координатами оптического центра.

Загрузка изображений и преобразование их в серый цвет:

    File[] listOfFiles = folder.listFiles();

    for (int i = 0; i < listOfFiles.length - 31; i++) {
        String imagePath0 = listOfFiles[i].getAbsolutePath();
        String imagePath1 = listOfFiles[i + 30].getAbsolutePath();
        Mat image0 = Imgcodecs.imread(imagePath0);
        Mat image1 = Imgcodecs.imread(imagePath1);

        if (image0.empty() || image1.empty()) {
            System.err.println("One of the images is empty or could not be read.");
            continue;
        }

        Imgproc.cvtColor(image0, image0, Imgproc.COLOR_BGR2GRAY);
        Imgproc.cvtColor(image1, image1, Imgproc.COLOR_BGR2GRAY);
    }

Здесь загружаются изображения из указанной папки, и оба изображения преобразуются в серый цвет средствами OpenCV.

Обнаружение ключевых точек и сопоставление дескрипторов:

    MatOfKeyPoint keypoints0 = new MatOfKeyPoint();
    MatOfKeyPoint keypoints1 = new MatOfKeyPoint();
    SIFT sift = SIFT.create();
    sift.detect(image0, keypoints0);
    sift.detect(image1, keypoints1);

    Mat descriptors0 = new Mat();
    Mat descriptors1 = new Mat();
    sift.compute(image0, keypoints0, descriptors0);
    sift.compute(image1, keypoints1, descriptors1);

Метод SIFT.create() используется для обнаружения ключевых точек, а также вычисляются дескрипторы для изображений с помощью OpenCV.

Сопоставление ключевых точек:

    DescriptorMatcher matcher = DescriptorMatcher.create(DescriptorMatcher.BRUTEFORCE);
    MatOfDMatch matches = new MatOfDMatch();
    matcher.match(descriptors0, descriptors1, matches);

    MatOfDMatch goodMatches = new MatOfDMatch();
    List<DMatch> matchesList = matches.toList();
    double minDist = 100.0;
    for (DMatch match : matchesList) {
        if (match.distance < minDist) {
            minDist = match.distance;
        }
    }

    for (DMatch match : matchesList) {
        if (match.distance < 2 * minDist) {
            goodMatchesList.add(match);
        }
    }

    goodMatches.fromList(goodMatchesList);

В этом этапе выполняется сопоставление дескрипторов с использованием BruteForce Matcher, а затем отбираются хорошие (наименьшие) совпадения.

Вычисление эпиполярных линий и отображение результатов:

    Mat lines0 = new Mat();
    Mat lines1 = new Mat();
    Mat fundamentalMatrix = computeFundamentalMatrix(cameraMatrix0, new Mat());
    if (fundamentalMatrix.size().equals(new Size(3, 3))) {
        Calib3d.computeCorrespondEpilines(keypoints0, 1, fundamentalMatrix, lines0);
        Calib3d.computeCorrespondEpilines(keypoints1, 2, fundamentalMatrix.t(), lines1);

        Features2d.drawMatches(image0, keypoints0, image1, keypoints1, goodMatches, outputImage);
        HighGui.imshow("Matches", outputImage);
        HighGui.waitKey();
    } else {
        System.out.println("Ошибка: Неверный размер фундаментальной матрицы.");
    }

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

Результаты исследования

Описание экспериментов

Были выбраны 5 эффективных на сегодняшний день алгоритмов для исследования: SIFT, SIFT+FOF (Farneback Optical Flow), ORB, r2d2 и его ускоренная версия faster2d2.

Метод

Обучаемый/необучаемый

Тип области интереса детектора

SIFT

hand-crafted

blob

SIFT+FOF

hand-crafted

Corners + blob

ORB

hand-crafted + trainable

corners

Faster2d2

trainable

blob

R2d2

trainable

blob

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

ORB не входит в число высокоточных методов, но является одним из самых быстрых алгоритмов. Хоть ORB, очевидно, проиграет по точности остальным, я включил его в исследование, поскольку полезно оценить его характеристики и узнать, насколько именно он проигрывает.

Для каждого из выбранных методов было проведено по 2 эксперимента:

Эксперимент с серией пар изображений с малым межкадровым смещением. Выбранный набор данных содержал 2000 изображений, из которых были сформированы пары таким образом, чтобы среднее расстояние в процессе съёмки между кадрами составляло в среднем 1 метр.

Пара изображений с малым межкадровым смещением
Пара изображений с малым межкадровым смещением

Эксперимент с серией пар изображений с большим межкадровым смещением.
Я получил данную серию, создав пары из 2000 изображений таким образом, чтобы межкадровое смещение между двумя изображениями вновь образованных пар составляло порядка 10 метров. Для этого я формировал пары, пропуская каждые 30 изображений первоначальной серии, то есть создал пары из изображений: №0 - №30, №1 - №31 и так далее.

Пара изображений с большим межкадровым смещением
Пара изображений с большим межкадровым смещением

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

Визуализация ключевых точек метода SIFT
Визуализация ключевых точек метода SIFT

Результаты для серии пар изображений с малым межкадровым смещением:

В результате работы математической модели были получены целевые метрики по всем алгоритмам для малого межкадрового смещения (таблица 1).

Таблица 1 – целевые метрики для изображений с малым межкадровым смещением.

Метод

Среднее расстояние, px

Медианное расстояние, px

Базовое

После

фильтрации

Базовое

После

фильтрации

SIFT

57,47

7,62

1,95

1,88

SIFT+FOF

5,34

5,96

1,62

2,05

ORB

36,93

17,94

4,95

5,5

faster2d2

4,42

6,36

1,93

2,77

r2d2

4,5

6,9

2,16

2,95

Как видно из таблицы 1, для серии изображений с малым межкадровым смещением метод SIFT + FOF показал максимальную точность идентификации ключевых точек, то есть минимальное медианное расстояние от точек до эпиполярных линий.

Также хорошие показатели медианного расстояния дали алгоритмы r2d2 и SIFT. При этом SIFT характеризуется большим количеством выбросов и худшим результатом по средним расстояниям без фильтрации.

Заметим, что SIFT + FOF оказались ближе всех к субпиксельной точности.

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

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

График 1 – Средние расстояния в пикселях для малого межкадрового смещения
График 1 – Средние расстояния в пикселях для малого межкадрового смещения
График 2 – Медианные расстояния в пикселях для малого межкадрового смещения
График 2 – Медианные расстояния в пикселях для малого межкадрового смещения

Результаты для серии пар с большим межкадровым смещением:

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

Таблица 2 – целевые метрики для изображений с большим межкадровым смещением.

Метод

Среднее расстояние, px

Медианное расстояние, px

Базовое

После

фильтрации

Базовое

После

фильтрации

SIFT

138,48

54,32

61,75

12,11

SIFT+FOF

12,32

13

2,97

4,76

ORB

72,94

54,32

32,33

12,11

faster2d2

22,6

37,99

6,2

13,67

r2d2

9,6

27,94

1,92

8,8

Как видно из таблицы 2, для серии пар изображений с большим межкадровым смещением максимальную точность идентификации ключевых точек, то есть минимальное медианное расстояние от точек до эпиполярных линий, дал R2D2. Второе место занимает SIFT+FOF. Ускоренная версия R2D2 очевидно имеет большее количество ошибочных идентификаций, что и отразилось на результатах.

Худший результат медианного расстояния у метода SIFT: в 31 раз хуже чем у R2D2. Также он оказался методом с самыми большими выбросами, что отразилось на среднем расстоянии, которое больше в 13 раз, чем у лучшего результата R2D2.

Стоит заметить, что фильтрация по тесту Лёва дала значительное улучшение целевых метрик для алгоритмов: SIFT, ORB. В случае с SIFT улучшение результата более чем в 5 раз. Наглядно таблицу 2 можно представить на графиках 3 - 4.

График 3 - Средние расстояния в пикселях для большого межкадрового смещения
График 3 - Средние расстояния в пикселях для большого межкадрового смещения
График 4 - Медианные расстояния в пикселях для большого межкадрового смещения
График 4 - Медианные расстояния в пикселях для большого межкадрового смещения

Выводы

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

  1. Метод SIFT + FOF для данных с малым межкадровым смещением.

  2. Метод R2D2 для данных с большим межкадровым смещением.

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

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

Комментарии (5)


  1. y000da
    12.12.2024 18:22

    Хорошая статья, не хватило пояснений за методы и как конкретно они работают


    1. br0mberg Автор
      12.12.2024 18:22

      Спасибо за ваш комментарий! К сожалению, статья получилась бы громоздкой, поэтому не удалось подробно описать подноготную таких мощных и сложноорганизованных алгоритмов как SIFT, ORB и т.д. Если эта тема вызовет повышенный интерес и получит много вопросов, я обязательно подготовлю более детальный материал в будущем.


  1. PavelKyshtymov
    12.12.2024 18:22

    Хорошая статья, добротная. Да только вот предметную область Вы выбрали из самых паскудных, в текущем-то моменте. Не лучше изучения свойств инсектицидов во времена Третьего Рейха. Бросайте это нехорошее дело, пока не влипли окончательно.


    1. y000da
      12.12.2024 18:22

      Во что автор пока еще не влип?


    1. Kandimus
      12.12.2024 18:22

      А какой сейчас момент?