Mastering OpenCV with Practical Computer Vision Projects / Опановуюємо OpenCV через практичні проекти комп'ютерного зору: Розділ 3. Безмаркерна доповнена реальність

Английский оригинал Перевод на украинский

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

Щоб зробити алгоритм ще більш ефективним, можемо відфільтрувати зайві точки шляхом використання методу консенсусу випадкової вибірки RANSAC. Оскільки ми працюємо із зображенням (плоским об'єктом) та вважаємо, що воно суцільне, можна шукати перетворення гомографії між особливими точками шаблону, з одного боку, та зображення запиту з іншого. Їх метою є перенесення точок шаблону в координатну систему останнього. Для цього скористаємось функцією cv::findHomography, що застосовує RANSAC для пошуку найкращої матриці гомографії методом випробування підмножин вхідних точок. Як побічний ефект, функціія відмічає кожну відповідність як ключову чи зайву точку, залежно від похибки репроєкції для обчисленої матриці.

Atronax 20.02.21 в 11:43

0

PatternDetector.cpp
Наступний код використовує обчислення матриці гомографії з використанням алгоритму RANSAC для відфільтрування геометрично невірних співпадінь:
bool PatternDetector::refineMatchesWithHomography
(
const std::vector<cv::KeyPoint>& queryKeypoints,
const std::vector<cv::KeyPoint>& trainKeypoints,
float reprojectionThreshold,
std::vector<cv::DMatch>& matches,
cv::Mat& homography
)
{
const int minNumberMatchesAllowed = 8;
if (matches.size() < minNumberMatchesAllowed)
return false;
// Підготуємо дані для cv::findHomography
std::vector<cv::Point2f> srcPoints(matches.size());
std::vector<cv::Point2f> dstPoints(matches.size());
for (size_t i = 0; i < matches.size(); i++)
{
srcPoints[i] = trainKeypoints[matches[i].trainIdx].pt;
dstPoints[i] = queryKeypoints[matches[i].queryIdx].pt;
}

Atronax 20.02.21 в 11:45

0

/ Знайдемо матрицю гомографії та маску внутрішніх точок
std::vector<unsigned char> inliersMask(srcPoints.size());
homography = cv::findHomography(srcPoints,
dstPoints,
CV_FM_RANSAC,
reprojectionThreshold,
inliersMask);
std::vector<cv::DMatch> inliers;
for (size_t i=0; i<inliersMask.size(); i++)
{
if (inliersMask[i])
inliers.push_back(matches[i]);
}
matches.swap(inliers);
return matches.size() > minNumberMatchesAllowed;
}

Atronax 20.02.21 в 11:46

0

Це візуалізація співпадінь, оброблених з використанням даної техніки:
Крок пошуку гомографії важливий, адже отримані перетворення є ключем для пошуку положення шаблона в зображенні запиту.

Atronax 20.02.21 в 11:48

0

Уточнюємо гомографію

Atronax 20.02.21 в 11:49

0

Коли ми знаходимо перетворення гомографії, вже наявні всі дані, необхідні для пошуку положення точок у тривимірному просторі. Однак, можна значно покращити результат, знайшовши більш точні кути шаблона. Для цього деформуємо вхідний кадр з використанням обрахованої гомографії, щоб отримати знайдений шаблон. Результат має бути досить близьким до вихідного тренувального зображення. Уточнення гомографії допоможе знайти ефективніші перетворення.

Atronax 20.02.21 в 12:01

0

Далі отримаємо інші гомографію та набір внутрішніх точок. Кращим результатом буде матриця, що дорівнює добутку першої (H1) та другої (H2).

Atronax 20.02.21 в 12:04

0

PatternDetector.cpp
Наступний блок коду описує фінальну версію процедури розпізнавання шаблона:
bool PatternDetector::findPattern(const cv::Mat& image,
PatternTrackingInfo& info)
{
// Конвертуємо вхідне зображення у відтінки сірого
getGray(image, m_grayImg);
// Видобуваємо особливі точки
extractFeatures(m_grayImg, m_queryKeypoints, m_queryDescriptors);
// Знаходимо співпадіння з поточним шаблоном
getMatches(m_queryDescriptors, m_matches);
// Знаходимо перетворення гомографії та розпізнаємо якісні співпадіння
bool homographyFound = refineMatchesWithHomography(
m_queryKeypoints,
m_pattern.keypoints,
homographyReprojectionThreshold,
m_matches,
m_roughHomography);
if (homographyFound)
{
// Якщо уточнення гомографії увімкнено,
// покращуємо знайдені перетворення
if (enableHomographyRefinement)
{
// Деформуємо зображення знайденою гомографією
cv::warpPerspective(m_grayImg, m_warpedImg,

Atronax 20.02.21 в 12:15

0

cv::warpPerspective(m_grayImg, m_warpedImg,
m_roughHomography, m_pattern.size, cv::WARP_INVERSE_MAP |
cv::INTER_CUBIC);
// Отримуємо оброблені співпадіння:
std::vector<cv::KeyPoint> warpedKeypoints;
std::vector<cv::DMatch> refinedMatches;
// Розпізнаємо особливі точки на деформованому зображенні
extractFeatures(m_warpedImg, warpedKeypoints,
m_queryDescriptors);
// Шукаємо співпадіння з шаблоном
getMatches(m_queryDescriptors, refinedMatches);
// Обчислюємо нову уточнену гомографію
homographyFound = refineMatchesWithHomography(
warpedKeypoints,
m_pattern.keypoints,
homographyReprojectionThreshold,
refinedMatches,
m_refinedHomography);

Atronax 20.02.21 в 12:16

0

// Отримуємо результат як добуток матриць
// уточненої та грубої гомографій:
info.homography = m_roughHomography * m_refinedHomography;
// Трансформуємо контур з використанням точної гомографії
cv::perspectiveTransform(m_pattern.points2d,
info.points2d, info.homography);
}
else
{
info.homography = m_roughHomography;
// Трансформуємо контур з використанням грубої гомографії
cv::perspectiveTransform(m_pattern.points2d,
info.points2d, m_roughHomography);
}
}
return homographyFound;
}

Atronax 20.02.21 в 12:18

0

Якщо після етапів видалення зайвих точок кількість співпадінь все ще досить велика (як мінімум 25 відсотків особливих точок шаблона мають відповідності із вхідним зображенням), можна бути певними, що шаблон розміщений правильно. Якщо це так, переходимо на наступний етап - оцінку 3D пози шаблона відносно камери.

Atronax 20.02.21 в 12:20

0

З'єднуємо все докупи

Atronax 20.02.21 в 12:20

0

Для зберігання екземплярів детектора особливих точок, видобувача дескрипторів та алгоритмів пошуку співпадінь використаємо клас PatternMatcher, що інкапсулюватиме ці дані. Він керуватиме алгоритмами пошуку особливих точок та дескрипторів, логікою порівняння та налаштуваннями, що контролюють процес розпізнавання.

Atronax 20.02.21 в 12:24

0

Клас пропонує методи обчислення всіх необхідних даних для формування шаблону із вказаного зображення:
void PatternDetector::computePatternFromImage(const cv::Mat& image,
Pattern& pattern);

Atronax 20.02.21 в 12:26

0

Цей метод шукає особливі точки на вхідному зображенні та видобуває дескриптори, використовуючи налаштовані алгоритми детектора та екстрактора, та формує шаблон для більш пізнього використання. Коли його знайдено, можемо натренувати детектор викликом методу:
void PatternDetector::train(const Pattern& pattern)

Atronax 20.02.21 в 12:30

0

Аргументом функції є поточний цільовий шаблон, який має бути знайденим. До того ж, вона тренує механізм пошуку співпадінь набором дескрипторів. Викликавши метод, ми вже готові знайти тренувальне зображення. Розпізнавання шаблона здійснюється в останній відкритій функції findPattern. Цей метод інкапсулює процедуру як описано раніше, включно з розпізнаванням, видобуванням дескрипторів та пошуком співпадінь з фільтрацією зайвих точок.

Atronax 20.02.21 в 12:38

0

Підіб'ємо підсумки коротким списком виконаних кроків:
1. Конвертували вхідне зображення у відтінки сірого.
2. Розпізнали особливі точки на кадрі запиту з використанням відповідного алгоритму.
3. Видобули дескриптори із вхідного зображення для знайдених особливих точок.
4. Порівняли дескриптори зображення з дескрипторами шаблона.
5. Виконали перехресні перевірки та тест відношень для видалення зайвих точок.
6. Знайшли перетворення гомографії, використовуючи внутрішні співпадіння.
7. Уточнили гомографію, деформувавши зображення запиту гомографією з попереднього кроку.
8. Знайшли точну гомографію як результат добутку грубої та уточненої.
9. Трансформували кути шаблона в координатну систему зображення, щоб отримати положення шаблона на вхідному кадру.

Atronax 20.02.21 в 12:43

0

Оцінюємо позу шаблона

Atronax 21.02.21 в 08:19

0

Цей процес виконується подібно до оцінки пози маркера з попереднього розділу. Як звичайно, для визначення зовнішніх параметрів камери нам знадобляться 2D-3D відповідності. Чотири тривимірні точки мають відповідати кутам одиничного прямокутника, що лежить в площині XY (вісь Z напрямлена вверх), а двовимірні - кутам растрового зображення.

Atronax 21.02.21 в 08:28

0

PatternDetector.cpp
Процедура buildPatternFromImage створює об'єкт Pattern із вхідного зображення таким чином:
void PatternDetector::buildPatternFromImage(const cv::Mat& image,
Pattern& pattern) const
{
int numImages = 4;
float step = sqrtf(2.0f);
// Зберігаємо початкове зображення в структурі шаблона
pattern.size = cv::Size(image.cols, image.rows);
pattern.frame = image.clone();
getGray(image, pattern.grayImg);
// Будуємо 2d та 3d контури (3d контур лежить в площині XY,
// оскільки він плоский)
pattern.points2d.resize(4);
pattern.points3d.resize(4);

Atronax 21.02.21 в 08:31

0

// Розміри зображення
const float w = image.cols;
const float h = image.rows;
// Нормалізовані розміри:
const float maxSize = std::max(w,h);
const float unitW = w / maxSize;
const float unitH = h / maxSize;
pattern.points2d[0] = cv::Point2f(0,0);
pattern.points2d[1] = cv::Point2f(w,0);
pattern.points2d[2] = cv::Point2f(w,h);
pattern.points2d[3] = cv::Point2f(0,h);
pattern.points3d[0] = cv::Point3f(-unitW, -unitH, 0);
pattern.points3d[1] = cv::Point3f( unitW, -unitH, 0);
pattern.points3d[2] = cv::Point3f( unitW, unitH, 0);
pattern.points3d[3] = cv::Point3f(-unitW, unitH, 0);
extractFeatures(pattern.grayImg, pattern.keypoints,
pattern.descriptors);
}

Atronax 21.02.21 в 08:32

0

Дана конфігурація кутів вигідна, адже початок системи координат знаходитиметься прямо в його центрі місцеположення шаблона, що лежить в площині XY, а вісь Z при цьому напрямлена в бік камери.

Atronax 21.02.21 в 08:39

0

Отримуємо матрицю внутрішніх характеристик камери

Atronax 21.02.21 в 08:40

0

Внутрішні параметри камери можна вирахувати за допомогою програми camera_cailbration.exe, що включена до дистрибутивного пакету бібліотеки OpenCV. За рахунок обробки шаблонних зображень вона визначить властивості лінзи, серед яких фокусна відстань, кардинальна точка та коефіцієнти викривлення. Скажімо, маємо набір з восьми зображень для калібрування, що зняті з різних точок зору:

Atronax 21.02.21 в 08:49

0

У такому разі команда для здійснення калібрування виглядатиме таким чином:
imagelist_creator imagelist.yaml *.png
calibration -w 9 -h 6 -o camera_intrinsic.yaml imagelist.yaml

Atronax 21.02.21 в 08:50

0

Перша команда створить список зображень PNG поточної теки у форматі YAML, який інструмент для калібрування очікує отримати як вхідні дані. Можна також вказати точні назви файлів, наприклад img1.png, img2.png чи img3.png. Згенерований файл imagelist.yaml потім передається застосунку для калібрування. Крім цього, інструмент може отримувати зображення зі звичайної вебкамери.

Atronax 21.02.21 в 08:55

0

Ми визначаємо розміри шаблона калібрування, а також вхідні та вихідні файли для отриманих даних.

Atronax 21.02.21 в 08:56

0

Після завершення калібрування файл формату YAML матиме наступний вигляд:
%YAML:1.0
calibration_time: "06/12/12 11:17:56"
image_width: 640
image_height: 480
board_width: 9
board_height: 6
square_size: 1.
flags: 0
camera_matrix: !!opencv-matrix
rows: 3
cols: 3
dt: d
data: [ 5.2658037684199849e+002, 0., 3.1841744018680112e+002, 0.,
5.2465577209994706e+002, 2.0296659047014398e+002, 0., 0., 1. ]
distortion_coefficients: !!opencv-matrix
rows: 5
cols: 1
dt: d
data: [ 7.3253671786835686e-002, -8.6143199924308911e-002,
-2.0800255026966759e-002, -6.8004894417795971e-004,
-1.7750733073535208e-001 ]
avg_reprojection_error: 3.6539552933501085e-001

Atronax 21.02.21 в 08:57

0

У першу чергу нас цікавить camera_matrix, що описує матрицю 3х3 внутрішніх характеристик. Вона має наступний вигляд:

Atronax 21.02.21 в 08:58

0

Нас цікавлять чотири компоненти: fx, fy, cx та cy. Використавши ці дані, можемо створити екземпляр об'єкта для калібрування камери таким чином:
CameraCalibration calibration(526.58037684199849e,
524.65577209994706e, 318.41744018680112, 202.96659047014398)

Atronax 21.02.21 в 09:01

0

Без коректного калібрування камери неможливо створити доповнену реальність з природнім виглядом. Знайдені перетворення перспективи відрізнятимуться від трансформацій камери. Це змусить доповнені об'єкти виглядати так, немовби вони знаходяться надто близько чи, навпаки, далеко. Далі знаходиться приклад скріншоту зі штучно зміненими параметрами калібрування:

Atronax 21.02.21 в 09:07

0

Вочевидь, перспективний погляд на куб відрізняється від решти сцени.
Щоб оцінити позу шаблона, скористаємось функцією cv::solvePnP бібліотеки OpenCV для пошуку перспективи й позиції. Вірогідно, ви вже знайомі з нею, адже ми застосовували процес для маркерної доповненої реальності. Нам необхідні координати кутів шаблона на поточному зображенні та відповідні 3D-координати, визначені раніше.

Atronax 21.02.21 в 09:21

0

Примітка
Функція cv::solvePnP може працювати більш ніж з 4 точками. Крім цього, вона відіграє ключову роль у створенні доповненої реальності з шаблонами складної форми. Ідея залишається тією ж - достатньо визначити 3D структуру шаблона та знайти відповідні 2D точки. Звісно, пошук гомографії в цьому разі не застосовується.

Atronax 21.02.21 в 09:27

0

Розглянемо тривимірні точки тренувального об'єкта шаблона та відповідні проєкції у двовимірному просторі зі структури PatternTrackingInfo; калібрування камери зберігається в приватних полях класу PatternDetector.

Atronax 21.02.21 в 09:29

0

Pattern.cpp
Оцінюємо положення шаблона в тривимірному просторі за рахунок функції computePose:
void PatternTrackingInfo::computePose(const Pattern& pattern, const
CameraCalibration& calibration)
{
cv::Mat camMatrix, distCoeff;
cv::Mat(3,3, CV_32F, const_cast<float*>
(&calibration.getIntrinsic().data[0])).copyTo(camMatrix);
cv::Mat(4,1, CV_32F, const_cast<float*>
(&calibration.getDistorsion().data[0])).copyTo(distCoeff);
cv::Mat Rvec;
cv::Mat_<float> Tvec;
cv::Mat raux,taux;
cv::solvePnP(pattern.points3d, points2d, camMatrix,
distCoeff,raux,taux);
raux.convertTo(Rvec,CV_32F);
taux.convertTo(Tvec ,CV_32F);
cv::Mat_<float> rotMat(3,3);
cv::Rodrigues(Rvec, rotMat);

Atronax 21.02.21 в 09:31

0

// Копіюємо до матриці перетворень
pose3d = Transformation();
for (int col=0; col<3; col++)
{
for (int row=0; row<3; row++)
{
pose3d.r().mat[row][col] = rotMat(row,col);
// Копіюємо компоненту повороту
}
pose3d.t().data[col] = Tvec(col);
// Копіюємо компоненту перенесення
}
// Оскільки solvePnP знаходить позу камери відносно маркера,
// щоб отримати позу маркера, інвертуємо її.
pose3d = pose3d.getInverted();
}

Atronax 21.02.21 в 09:43

0

Інфраструктура застосунку

Atronax 21.02.21 в 09:43

0

Дотепер ми познайомились з процесом розпізнавання шаблона та оцінки його позиції відносно камери. Настав час поєднати ці алгоритми в готовий застосунок. Ця частина призначена пояснити процес використання OpenCV для захоплення відео з вебкамери та створення контексту для 3D візуалізації.

Atronax 21.02.21 в 09:49

0

Оскільки ми бажаємо пояснити, як використати ключові особливості безмаркерної AR, створимо простий застосунок для командної стрічки, що зможе розпізнавати будь-які шаблони на послідовності відеокадрів чи звичайних зображеннях.

Atronax 21.02.21 в 09:51

0

Для зберігання логіки обробки зображення та посередніх даних використаємо клас ARPipeline. Це базовий об'єкт, що включає всі компоненти, необхідні для створення доповненої реальності, та здійснює всі процедури обробки вхідних кадрів. Далі знаходиться UML-діаграма класу ARPipeline та його елементи:
Він складається з:
Об'єкта калібрування камери
Екземпляра детектора
Тренованого об'єкта шаблона
Посередніх даних для його відслідковування.

Atronax 21.02.21 в 09:58

0

ARPipeline.hpp
Наступний код оголошує клас ARPipeline:
class ARPipeline
{
public:
ARPipeline(const cv::Mat& patternImage, const CameraCalibration&
calibration);
bool processFrame(const cv::Mat& inputFrame);
const Transformation& getPatternLocation() const;
private:
CameraCalibration m_calibration;
Pattern m_pattern;
PatternTrackingInfo m_patternInfo;
PatternDetector m_patternDetector;
};

Atronax 21.02.21 в 10:00

0

У конструкторі ARPipeline налаштовується об'єкт шаблона, а також у приватних полях зберігаються дані калібрування. Функція processFrame реалізує алгоритм розпізнавання та процедуру оцінки пози. Повернене значення при цьому вказує на успіх процесу. Знайдену позу шаблона можна отримати, викликавши функцію getPatternLocation.

Atronax 21.02.21 в 10:20

0

ARPipeline.cpp
Наступний код відноситься до реалізації класу ARPipeline:
ARPipeline::ARPipeline(const cv::Mat& patternImage, const
CameraCalibration& calibration)
: m_calibration(calibration)
{
m_patternDetector.buildPatternFromImage (patternImage, m_pattern);
m_patternDetector.train(m_pattern);
}
bool ARPipeline::processFrame(const cv::Mat& inputFrame)
{
bool patternFound = m_patternDetector.findPattern(inputFrame,
m_patternInfo);
if (patternFound)
{
m_patternInfo.computePose(m_pattern, m_calibration);
}
return patternFound;
}
const Transformation& ARPipeline::getPatternLocation() const
{
return m_patternInfo.pose3d;
}

Atronax 21.02.21 в 10:21

0

Під'єднуємо підтримку 3D візуалізації в OpenCV

Atronax 21.02.21 в 10:22

0

Як і в попередньому розділі, для роботи з 3D застосуємо OpenGL. Але на відміну від середовища iOS, для якого ми мали дотримуватись певних вимог до архітектури, у цьому разі отримаємо більше свободи. На платформах Windows чи Mac можна обирати серед доступних двигунів. Далі в розділі навчимось створювати кросплатформову 3D візуалізацію з використанням OpenCV. Починаючи з версії 2.4.2, бібліотека підтримує спільну роботу з OpenGL. Це значить, що можна легко візуалізувати будь-який 3D контент в OpenCV.

Atronax 21.02.21 в 10:47

0

Щоб налаштувати вікно OpenGL, спершу потрібно побудувати OpenCV з підтримкою цієї бібліотеки. Інакше у разі використання необхідних функцій формуються вийнятки. Щоб здобути підтримку бібліотеки, будувати необхідно з активованим флагом ENABLE_OPENGL=YES.

Atronax 21.02.21 в 10:58

0

Примітка
Що ж до поточної версії (OpenCV 2.4.2), підтримка OpenGL відімкнена за замовчуванням. Не існує гарантії, але це може змінитися в майбутньому. В такому разі не існуватиме потреби будувати OpenCV власноруч.

Atronax 21.02.21 в 11:04

0

Щоб мати можливість використовувати вікно OpenGL в OpenCV, здійснимо наступні кроки:
Клонуємо репозиторій OpenCV, розміщений на https://github.com/Itseez/opencv. Для цього необхідні або інструменти git для командної стрічки, або застосунок GitHub.

Atronax 21.02.21 в 11:05

0

Обираємо необхідні параметри OpenCV та формуємо робочий простір для IDE. Для виконання цього кроку потрібен застосунок CMake, який можна завантажити з ресурсу: http://www.cmake.org/cmake/resources/software.html.

Atronax 22.02.21 в 08:29

0

Щоб налаштувати OpenCV, можна або виконати команду CMake таким чином (у теці, де має бути розміщеним згенерований проєкт):
cmake -D ENABLE_OPENGL=YES <шлях до теки з вихідними файлами OpenCV>

Atronax 22.02.21 в 08:37

0
Минутку...

← Предыдущая страница

Следующая страница →