SLAM на Java с OpenCV: сравнение алгоритмов автономной навигации

от автора

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

Введение

Визуальная одометрия играет ключевую роль в навигации малогабаритных беспилотных летательных аппаратов (БЛА), особенно в условиях, где 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 – математическая абстракция, представляющая собой связь между камерами в трёхмерной сцене. Она описывает геометрические свойства проекции точек из одного положения камеры в другое, играя важную роль в задачах компьютерного зрения. Представляет собой матрицу 3×3, обозначаемую как 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 для данных с большим межкадровым смещением.

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

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


ссылка на оригинал статьи https://habr.com/ru/articles/866092/