В этом репозитории представлен семестровый проект по курсу "Трёхмерное компьютерное зрение". Его выполнение разделено на три этапа (каждый из которых представлял собой отдельно большое домашнее задание в курсе). Итоговый результат (после выполнения всех трёх частей) - это полноценный трекер камеры, который может по одному только видеофайлу (ну и ещё параметрам камеры) найти положения камеры (которая снимала данное видео) в каждом кадре.
-
Тестирование кода я проводил с помощью данных из репозитория
dataset
, который у меня располагался на том же уровне вложенности, что и директорияcamtrack
. Запуск кода я производил с помощью командной строки, находясь в директорииcamtrack
- поэтому далее примеры запуска соответствующие.
Замчание:dataset
слишком большой, чтобы загрузить его на гитхаб. -
Проверить эффективность решения конкретного домашнего задания можно с помощью
testrunner.py
. Для этого нужно запустить:
testrunner.py [путь до конфигурации запуска тестов] [путь до директории, куда сохранять данные в процессе тестирования]
В качестве результата будет выведено количество пройденных тестов, а для 2 и 3 домашки ещё и среднее качество трекинга камеры по всем тестам (чем ближе к 1 - тем лучше).
Пример запуска:
testrunner.py ../dataset/dataset_ha3.yml ../results/result_ha3/try9
Для 1 и 2 домашки конфигурации запуска:../dataset/dataset_ha1.yml
и../dataset/dataset_ha2.yml
соответственно. -
Далее, когда будут просить путь до видеофайла, вместо видеофайла можно передавать путь до папки, в которой хранится последовательность изображений, тогда эти изображения будут выступать в качестве кадров видео - на них по порядку будут детектироваться и трекаться уголки, искаться положения камеры.
-
Данный код не содержит каких-то проверок на сами входные данные, так как в реальных задачах используются достаточно качественные видеофайлы, содержащие плавное видеоизображение какой-то одной сцены. В принципе, этому коду главное, чтобы в видео не было однотонных кадров (на которых почти нет уголков) и не было резких смен сцен, из-за которых все треки уголков оборвутся.
-
В процессе работы все видео и изображения переводятся в чёрно-белый формат, так как большиснтво алгоритмов компьютерного зрения работает именно с таким форматом. Но на вход подавать можно и цветные данные - они будут преобразованы в чёрно-белые уже внутри программы.
Весь код этого домашнего задния написан в файле corners.py
. Само задание
заключалось в детекции уголков на изображении и их трекинг - то есть отслеживание
перемещения этих уголков от кадра к кадру в последовательности
(например, в кадрах видео). Подробные комментарии к моей реализации могут быть
найдены в коде.
-
Чтобы запустить подсчёт уголков и их треков и сохранить результат в файл, воспользуйтесь такой командой:
corners.py --dump-corners [путь до директории] [путь до видеофайла]
В качестве результата в указанной директории появится файлcorners
, который будет содержать уголки и треки, найденные на кадрах указанного видеофайла.
Примеры запуска:corners.py --dump-corners ../dataset/room/corners ../dataset/room/rgb.mov
corners.py --dump-corners ../dataset/house_free_motion/corners ../dataset/house_free_motion/rgb
corners.py --dump-corners ../dataset/fox_head_full/corners ../dataset/fox_head_full/rgb.mov
corners.py --dump-corners ../dataset/bike_translation_slow/corners ../dataset/bike_translation_slow/rgb/
corners.py --dump-corners ../dataset/soda_free_motion/corners ../dataset/soda_free_motion/rgb/
corners.py --dump-corners ../dataset/fox_head_short/corners ../dataset/fox_head_short/rgb
-
Чтобы запустить демонстрацию уголков, запустите скрипт:
corners.py --show [путь до видеофайла]
В качестве результата появится окно, в котором будут отрисовываться кадры видео с нарисованными на них уголками (точнее - нарисованы будут окружности, их центр и обозначает посчитанный уголок). Далее кадры можно листать (как именно - будет выведено в терминале) и смотреть за тем, куда перемещаются уголки (один и тот же уголок на разных кадрах нарисован одним и тем же цветом (но если уголков очень много, то цвета, конечно, могут у разных уголков повторяться) - в коде цвет характеризует id уголка (у разных уголков id разный, у одного и того же уголка id сохраняется)) - в этом и заключается трекинг уголков. В какой-то момент уголок может пропасть со следующего кадра - это значит, что на новом кадре уголок отследить не удалось, то есть трек оборвался. Также на новых кадрах могут повляться новые уголки - это значит, что помимо трекинга старых уголков с предыдущих кадров, мы задетектировали новые уголки на новом кадре.
Код этого домашнего задания находится в файле camtrack.py
- всё, кроме функции
get_initial_frames
. В этом домашнем задании необходимо по видеофайлу, а также по
известным положениям камеры в двух кадрах, найти положения камеры во всех кадрах
видео (имеется в виду камеры, которая это видео снимала; предполагается, что
нам известны характеристики камеры такие, как фокусное расстояние). Положение камеры задаётся матрицей поворота
и вектором переноса относительно некоторой начальной точки (какой именно - не важно, так как нас интересует только
взаимные положения камеры (то есть положения друг относительно друга на разных кадрах)).
Это довольно сложное задание и решать его можно многими способами; как делал я - можно прочитать в комментариях к коду.
Так как в данном случае два начальных кадра с известными положениями камеры нам предоставляют, то с одной стороны это упрощает задачу (так как нет ошибки с определением положения камеры в этих двух кадрах), но с другой стороны сильно усложняет её, ведь нам могут дать очень неудобные начальные кадры (на большом расстоянии или те, где у нас плохо задетектились уголки...). Из-за этого данное домашнее задание крайне чувствительно к параметрам - наилучший результат на тестах моя реализация показывала при таких параметрах (их подбор занял колоссальное количество времени, сил и интуиции):
REPROJECTION_ERROR = 0.1
MIN_TRIANGULATION_ANGLE = 2
MIN_DEPTH = 0
PNP_ERROR = 2
PNP_ERROR = 2.5 (или 2)
MIN_INLIERS = 20
MAX_RETRIANGL = 25
Параметры крайне чувствительны и малейшее их изменение может сильно ухудшить результат!
-
Чтобы запустить трэкер камеры и сохранить результат в файл, используйте команду:
camtrack.py --camera-poses [файл1] --frame-1 [n] --frame-2 [m] --load-corners [файл2] [файл3] [файл4] [файл5] [файл6]
, где:файл1
- файл с истинными положениями камеры в каждом кадре видеоn
иm
- номера кадров, в которых положения камеры будут изначально предоставлены коду (эти положения как раз будут взяты изфайл1
)файл2
- файл с уголками для этого (котороефайл3
) видео (тот самый файлcorners
, полученный в предыдущей домашке)файл3
- видеофайл, для которого трекаем камеруфайл4
- файл, где указаны параметры камерыфайл5
- файл, куда сохранятся полученные положения камеры в каждом кадрефайл6
- файл, куда сохранится облако 3d-точек
Примеры запуска:camtrack.py --camera-poses ../dataset/room/ground_truth.yml --frame-1 10 --frame-2 40 --load-corners ../dataset/room/corners ../dataset/room/rgb.mov ../dataset/room/camera.yml ../dataset/room/track.txt ../dataset/room/points_cloud.txt
camtrack.py --camera-poses ../dataset/house_free_motion/ground_truth.yml --frame-1 0 --frame-2 10 --load-corners ../dataset/house_free_motion/corners ../dataset/house_free_motion/rgb ../dataset/house_free_motion/camera.yml ../dataset/house_free_motion/track.txt ../dataset/house_free_motion/points_cloud.txt
camtrack.py --camera-poses ../dataset/fox_head_full/ground_truth.yml --frame-1 10 --frame-2 70 --load-corners ../dataset/fox_head_full/corners ../dataset/fox_head_full/rgb.mov ../dataset/fox_head_full/camera.yml ../dataset/fox_head_full/track.txt ../dataset/fox_head_full/points_cloud.txt
camtrack.py --camera-poses ../dataset/bike_translation_slow/ground_truth.yml --frame-1 1 --frame-2 5 --load-corners ../dataset/bike_translation_slow/corners ../dataset/bike_translation_slow/rgb ../dataset/bike_translation_slow/camera.yml ../dataset/bike_translation_slow/track.txt ../dataset/bike_translation_slow/points_cloud.txt
-
Чтобы немного визуализировать трекание камеры, можно дополнительно передать в предыдущие команды параметр
--show
. Тогда появится окно, где покадрово будет отрисовываться видео, а на каждом кадре будут отрисованы синие отрезки - "невязки", отражающие ошибку трекинга камеры (чем они длиннее, тем хуже результат трекинга)
Код этого домашнего задания - это функция get_initial_frames
в camtrack.py
.
Единственное отличие от предыдущей части, что теперь нам не дают положения камеры в
двух кадрах - их мы должны найти сами (для этого используется сложная
эпиполярная геомтрия, поэтому данная часть вынесена отдельно), что и делает
функция get_initial_frames
: она ищет два кадра, где удобнее всего найти положения камеры
и возвращает эти кадры вместе с найденными положениями. Это называетя
инициализацией. Далее начинается трекинг из
предыдущей части (из домашнего задания 2). Таким образом, по результату домашнего задания 3 у нас получается
полностью работоспособный трекер камеры, который только по видео может сначала посчитать уголки,
а затем с их помощью найти положения камеры в каждом кадре видео.
Важно: конечно, по видео невозможно определить масштаб, поэтому весь трекинг камеры происходит с точностью до масштаба (то есть все векторы переноса можно домножать на любой ненулевой коэффициент - результат от этого не поменяется). Также заметим, что положения камер считаются относительно некоторой точки отсчёта, которую мы можем выбирать как угодно (так как на практике важно только взаимное положение камеры друг относительно друга) - в моём коде в качестве точки отсчёта берётся положение камеры в первом из инициализированных кадров (то есть для этого кадра матрица поворота считается единичной, а вектор переноса - нулевым).
Если говорить о параметрах, то так как тут первые два кадра с известными положениями
камеры мы выбираем самостоятельно, то код стал гораздо менее чувствителен к
параметрам трекинга камеры, которые были представлены в предыдущей части (наиболее
подходящие параметры для итогового проекта (то есть вместе с 3 домашкой) - см. в коде
финальной (последней закоммиченной) версии). Но вот сама задача стала сильно сложнее, ведь ошибка в инициализации потянет за собой ошибки
в определении положений камеры во всех последующих кадрах - поэтому, хоть сам код
функции get_initial_frames
довольно короткий, было не так-то просто подобрать
парамеры (внутри этой функции) и научиться выбирать подходящие кадры для инициализации.
Но после огромного количества проб получилась та финальная версия, которая сейчас
находится на гитхабе (в последнем коммите).
На самом деле тут всё, как в предыдущей части, но истинные положения камеры уже не нужны - код всё делает сам.
Итак, необходимо запустить: camtrack.py --load-corners [файл2] [файл3] [файл4] [файл5] [файл6]
, где названия файлов совпадают с предыдущей
частью.
Примеры запсука:
camtrack.py --load-corners ../dataset/room/corners ../dataset/room/rgb.mov ../dataset/room/camera.yml ../dataset/room/track.txt ../dataset/room/points_cloud.txt
camtrack.py --load-corners ../dataset/house_free_motion/corners ../dataset/house_free_motion/rgb ../dataset/house_free_motion/camera.yml ../dataset/house_free_motion/track.txt ../dataset/house_free_motion/points_cloud.txt
camtrack.py --load-corners ../dataset/fox_head_full/corners ../dataset/fox_head_full/rgb.mov ../dataset/fox_head_full/camera.yml ../dataset/fox_head_full/track.txt ../dataset/fox_head_full/points_cloud.txt
camtrack.py --load-corners ../dataset/bike_translation_slow/corners ../dataset/bike_translation_slow/rgb ../dataset/bike_translation_slow/camera.yml ../dataset/bike_translation_slow/track.txt ../dataset/bike_translation_slow/points_cloud.txt
camtrack.py --load-corners ../dataset/soda_free_motion/corners ../dataset/soda_free_motion/rgb ../dataset/soda_free_motion/camera.yml ../dataset/soda_free_motion/track.txt ../dataset/soda_free_motion/points_cloud.txt
camtrack.py --load-corners ../dataset/fox_head_short/corners ../dataset/fox_head_short/rgb ../dataset/fox_head_short/camera.yml ../dataset/fox_head_short/track.txt ../dataset/fox_head_short/points_cloud.txt
Резюмируя сказанное ранее, данный проект позволяет целиком и полностью решить задачу отслеживания камеры в видео (или в последовательности кадров) и получить на выходе файл с положениями камеры (в виде матрицы поворота и вектора переноса) в каждом кадре. Для этого сначала нужно подсчитать для видео уголки (см. домашнее задание 1), а затем, используя найденные уголки, определить положения камеры (см. домашнее задание 3).
Если у вас есть истинные положения камеры, то можно проверить результат, полученный
данной программой с помощью cmptrack
. Для этого нужно запустить команду:
cmptrack.py -p [истинные положения камеры] [результат программы]
,
где
[истинные положения камеры]
- файл с истинными положениями камеры (обозначался[файл1]
в домашнем задании 2)[результат программы]
- файл с положениями камеры, полученный программой (обозначалсяфайл5
в домашках 2 и 3)
Далее cmptrack
сравнит истинные положения камеры и полученные программой (разумеется, это
сравнение будет проведено с точностью до масштаба (то есть от изменения
масштаба (от домножения векторов переноса на одинаковый ненулевой коэффициент) результат
сравнения не поменятеся) и с точностью до точки отсчёта (сравниваться будут только относительные
положения камер в кадрах - то есть можно считать, что перед началом сравнения
точку отсчёта и для истинных, и для найденных положений камеры смещают в перый кадр - и уже потом сравнивают))
и выведет результат вида:
Rotation errors (degrees)
max = 0.7730728228521626
median = 0.4979825072937713
AUC = 0.9788618097216172
Translation errors
max = 0.04022259655879134
median = 0.029326882964964596
AUC = 0.8904899546630537
Volume under surface = 0.8719795735358125
Здесь сначала выводятся ошибки определения поворота камеры (то есть насколько
точно мы получили матрицу поворота), а затем ошибки определения
смещения (насколько точно посчитали вектор переноса). Для каждой ошибки есть три характеристики: максимальная ошибка среди всех
кадров (графа max
), медианная ошибка по всем кадрам (графа median
), а также
площадь под ROC-кривой (графа AUC
). Заметим, что для поворота ошибки считаются в градусах (например,
max = 0.7730728228521626
- это значит, что среди всех кадров наибольшая ошибка
определения поворота камеры - это 0.77 градусов - очень немного), а для смещения
ошибки (кажется) считаются в долях от ширины кадра.
Разумеется, чем больше значение AUC
(чем ближе оно к 1), тем лучше результат.
На последней строке (графа Volume under surface
) выводится произведение значений
AUC
для ошибок поворота и смещения. Данное число можно считать итоговой
метрикой трекинга камеры. Оно всегда находится от 0 до 1 и чем ближе к 1, тем лучше
мы произвели трекинг (тем точнее определённые нами положения камеры). Для сложных тестов
(где камеру быстро перемещают или быстро поворачивают (как в house_free_motion
),
или, но уже в меньшей степени это усложняет, где перед кадром появляется препятствие (как в fox_head_full
))
хорошим результатом считается Volume under surface
равный 0.8...0.85. Для остальных
тестов желательно стремится к 0.9 и более.
Также в результате запуска cmptrack
появится окно с четырьмя графиками: для каждой
из двух ошибок (поворота и смещения) по два графика. Первый из них нарисован
зелёным цветом - это график зависимости ошибки от номера кадра (этот график
полезен, чтобы понимать, в каком кадре мы сильно ошиблись при трекинге); второй
график красного цвета - это соответствующая ROC кривая (площадь, под которой
посчитана в AUC
).
Примеры запсука:
cmptrack.py -p ../dataset/room/ground_truth.yml ../dataset/room/track.txt
cmptrack.py -p ../dataset/house_free_motion/ground_truth.yml ../dataset/house_free_motion/track.txt
cmptrack.py -p ../dataset/fox_head_full/ground_truth.yml ../dataset/fox_head_full/track.txt
cmptrack.py -p ../dataset/bike_translation_slow/ground_truth.yml ../dataset/bike_translation_slow/track.txt
cmptrack.py -p ../dataset/soda_free_motion/ground_truth.yml ../dataset/soda_free_motion/track.txt
cmptrack.py -p ../dataset/fox_head_short/ground_truth.yml ../dataset/fox_head_short/track.txt
-
При запуске кода не забудьте установить библиотеки из
requirements.txt
- это делается одной командой. Иногда устанавливаются не все библиотеки почему-то - остальные придётся вручную черезpip install
утанавливать. -
Если при открытии проекта в PyCharm он ругается на то, что в каких-то файлах проекта (в файлах
.py
в директорииcamtrack
) нельзя импортировать другие файлы проекта (например, вcamtrak.py
не работает командаfrom corners import CornerStorage
, хотя файлcorners.py
у нас написан и лежит в той же директории), то, чтобы это исправить, нужно пометить всю папкуcamtrack
какSources
. Для этого в PyCharm делаем следующее:
File
(левый верхний угол) ->Settings...
-> в открывшемся окне с левой стороны выбираем вкладкуProject: [название проекта]
-> далее в этой вкладке выбираемProject Structure
-> и теперь в окне посередине мы можем кликнуть на нужную нам папку (в данном случаеcamtrack
) и сверху в графеMark as
будет кнопкаSources
(с изображением синей папки). -
Если говорит в целом о проекте, то работает он довольно неплохо. Детекция уголков происходит очень хорошо - это можно отследить визуально. Да и трекинг камеры (домашнее задание 3) работает хорошо - во-первых, он проходит все тесты
dataset_ha3.yml
- чего уже добиться довольно сложно, но ещё и средний результат (среднее значениеVolume under surface
) по всем тестам около 0.84, что очень хорошо, учитывая сложность некоторых тестов. -
Сам проект работает не очень долго: уголки даже для самых длинных тестов (из более 400 кадров) считаются не дольше пары минут, сам трекинг тоже работает довольно быстро. Единственная долгая часть - это инициализация (когда нам нужно выбрать два первых кадра и на них получить положения камер), так как в ней довольно длинный перебор. Для длинных тестов эта часть может работать минут 20. Весь процесс трекинга выводится на экран, что упрощает понимание, на каком этапе программа сейчас.
-
Комментарии в коде для опытного специалиста могут показаться излишними и очень громоздкими, но с моей точки зрения данная программа - это не столько код рабочего инструмента для реальных задач, сколько практическое учебное пособие, в котором демонстрируется, как теоретические алгоритмы трёхмерного компьютерного зрения могут быть использованы на практике. А огромное количество подробных комментариев помогает освоить и понять новичку (в том числе и мне, если я забуду какой-то кусок курса компьютерного зрения) что и как работает.