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

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

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

Відновлюємо сцену

Atronax 25.02.21 в 11:17

0

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

Atronax 25.02.21 в 11:24

0

Час пояснити, як саме здійснювати триангуляцію за допомогою OpenCV. Цього разу слідуватимемо крокам Хартлі та Штурма, описаними в статті "Триангуляція", у якій вони реалізують та порівнюють декілька методів. Ми запрограмуємо один з лінійних способів, оскільки це можна легко зробити за рахунок можливостей бібліотеки.

Atronax 25.02.21 в 11:57

0

Пам'ятаєте, ми мали два ключові рівняння для співпадіння двовимірних точок та матриць P: x = PX та x'=
P'X, де x та x' - це співвідносні 2D точки, а X - реальна 3D позиція, відображена двома камерами? Переписавши рівняння, сформулюємо систему, яку можна вирішити відносно невідомого значення X. Припустивши, що X = (x, y, z, 1)t (справедливо для точок, що знаходяться не надто близько та не надто далеко від центра камери) створює негомогенну лінійну систему рівнянь вигляду AX = B, можемо її закодувати та вирішити таким чином:

Atronax 25.02.21 в 12:04

0

Mat_<double> LinearLSTriangulation(
Point3d u,//гомогенна точка зображення (u,v,1)
Matx34d P,//матриця камери 1
Point3d u1,//гомогенна точка зображення з другої камери
Matx34d P1//матриця камери 2
)
{
//будуємо матрицю A
Matx43d A(u.x*P(2,0)-P(0,0),u.x*P(2,1)-P(0,1),u.x*P(2,2)-P(0,2),
u.y*P(2,0)-P(1,0),u.y*P(2,1)-P(1,1),u.y*P(2,2)-P(1,2),
u1.x*P1(2,0)-P1(0,0), u1.x*P1(2,1)-P1(0,1),u1.x*P1(2,2)-P1(0,2),
u1.y*P1(2,0)-P1(1,0), u1.y*P1(2,1)-P1(1,1),u1.y*P1(2,2)-P1(1,2)
);
//будуємо вектор B
Matx41d B(-(u.x*P(2,3)-P(0,3)),
-(u.y*P(2,3)-P(1,3)),
-(u1.x*P1(2,3)-P1(0,3)),
-(u1.y*P1(2,3)-P1(1,3)));
//шукаємо розв'язок відносно X
Mat_<double> X;
solve(A,B,X,DECOMP_SVD);
return X;
}

Atronax 25.02.21 в 12:05

0

В результаті отримаємо наближене положення позиції у 3D просторі, пов'язаної з парою двовимірних точок. Варто зауважити, що останні представлені гомогенними координатами, а це значить, що до значень x та y додається одиниця. Маємо впевнитись, що точки мають нормалізовані координати, тобто вони були заздалегідь помножені на матрицю калібрування K. Замість пошуку добутку кожної на матрицю K можна просто використати KP (добуток матриць K та P), як пропонують Х. та З. у розділі 9. Запрограмуємо цикл по співпадінням точок, щоб отримати повну триангуляцію так:

Atronax 25.02.21 в 12:14

0

double TriangulatePoints(
const vector<KeyPoint>& pt_set1,
const vector<KeyPoint>& pt_set2,
const Mat&Kinv,
const Matx34d& P,
const Matx34d& P1,
vector<Point3d>& pointcloud)
{
vector<double> reproj_error;
for (unsigned int i=0; i<pts_size; i++) {
//конвертуємо в нормалізовані гомогенні координати
Point2f kp = pt_set1[i].pt;
Point3d u(kp.x,kp.y,1.0);
Mat_<double> um = Kinv * Mat_<double>(u);
u = um.at<Point3d>(0);
Point2f kp1 = pt_set2[i].pt;
Point3d u1(kp1.x,kp1.y,1.0);
Mat_<double> um1 = Kinv * Mat_<double>(u1);
u1 = um1.at<Point3d>(0);
//триангулюємо
Mat_<double> X = LinearLSTriangulation(u,P,u1,P1);
//обчислюємо похибку репроєкції
Mat_<double> xPt_img = K * Mat(P1) * X;
Point2f xPt_img_(xPt_img(0)/xPt_img(2),xPt_img(1)/xPt_img(2));
reproj_error.push_back(norm(xPt_img_-kp1));
//зберігаємо тривимірну точку
pointcloud.push_back(Point3d(X(0),X(1),X(2)));
}
//повертаємо середнє значення похибки
Scalar me = mean(reproj_error);
return me[0];
}

Atronax 25.02.21 в 12:16

0

Далі зображено результат триангуляції на основі двох знімків фонтану, що доступні за адресою http://cvlab.epfl.ch/~strecha/multiview/denseMVS.html. У верхній частині зображено два початкові види сцени, а в нижній - вид на реконструйовану хмару точок з двох позицій, включно зі знайденими камерами, що спрямовані на фонтан. Можна бачити, як відновлено частину червоної цегляної стіни, а також вбудований фонтан.

Atronax 25.02.21 в 12:24

0

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

Atronax 25.02.21 в 12:30

0

Тепер розглянемо, як саме вимірювання похибки допомагає знайти більш точну реконструкцію. Варто зауважити, репроєкція означає вибір триангульованої тривимірної позиції та її проєкцію на площину камери з метою отримати відповідну двовимірну. Потім оцінюємо відстань між початковою та проєктованою точками. Якщо вона велика, можлива помилка триангуляції, тому таку точку не беремо до уваги. Глобальним виміром є середня відстань репроєкції, яка вказуватиме на загальну якість триангуляції. Її високе значення може вказувати на недоліки P, а, отже, й на можливі помилки в обчисленні базової матриці співвідносних ключових точок.

Atronax 25.02.21 в 13:13

0

Повернімось до обговорення матриць камери з попередньої частини. Ми вказали, що побудову матриці P1 можна здійснити чотирма різними способами, але лише один з них формує коректний результат. Знаючи як триангулювати точку, реалізуємо пошук дійсної матриці. На даному етапі пропустимо деталі реалізації, оскільки вони наведені в коді, доданому до книги.
Далі поглянемо на відновлення інших камер, що спрямовані на ту ж сцену, та комбінування результатів 3D реконструкції.

Atronax 25.02.21 в 13:18

0

Відновлюємо сцену з багатьох видів

Atronax 25.02.21 в 13:18

0

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

Atronax 25.02.21 в 13:24

0

Існує кілька способів коректно відновити дані тривимірної сцени з декількох видів. Перший - це зміна та оцінка пози камери, відоме як перспективна N-точка (PNP), згідно з яким намагатимемось знайти позицію на основі вже знайдених точок сцени. Інший спосіб - триангулювати більше 2D позицій та поглянути на їх взаємне розміщення на геометрії сцени: це надасть інформацію про положення нової камери за рахунок використання ітеративної найближчої точки (ICP). У цьому розділі розглянемо, як застосувати функції solvePnP бібліотеки OpenCV для реалізації першого варіанту.

Atronax 25.02.21 в 13:36

0

На початковому кроці поступової 3D реконструкції зі зміною положення камери маємо отримати базову структуру сцени. Оскільки ми шукатимемо позицію будь-якого нового виду залежно від відомої структури сцени, варто знайти початкову структуру та базис. Можна скористатися попередньо вказаним методом - наприклад, для першого та другого кадрів - щоб отримати дані для пошуку матриць камери (з використанням функції FindCameraMatrices) та триангуляції геометрії (за допомогою TriangulatePoints).

Atronax 25.02.21 в 13:37

0

Отримавши початкову структуру, можна продовжити процес; однак, метод вимагає дотримуватись певних обмежень. По-перше, варто зауважити, що функція solvePnP потребує два вирівнені вектори 3D та 2D точок. Це значить, що i-тий елемент в одному збігається з i-тим в іншому. Щоб віднайти їх, маємо оглянути точки навколо отриманої раніше тривимірної точки, що відноситься до двовимірної на нашому кадрі. Простий спосіб досягти цього - до кожної 3D позиції хмари доєднати вектор 2D точок, з яких вона здобута. Потім можна застосувати процес співпадіння особливих точок, щоб знайти необхідну пару.

Atronax 25.02.21 в 13:57

0

Познайомимось з новою структурою:
struct CloudPoint {
cv::Point3d pt;
std::vector<int>index_of_2d_origin;
};
Крім власне 3D точки вона зберігає індекси двовимірних точок у відповідних векторах кожного кадра, які були враховані в процесі триангуляції. Дані index_of_2d_origin мають бути налаштовані до пошуку нової 3D точки, вказуючи на використані в процесі камери. Можна скористатися цією структурою, щоб повернутися від тривимірної точки хмари до двовимірної на кожному кадрі, таким чином:

Atronax 25.02.21 в 14:01

0

std::vector<CloudPoint> pcloud; //глобальна хмара тривимірних точок
//шукаємо співпадіння між i-тим та 0-вим кадрами (тобто
поточною хмарою)
std::vector<cv::Point3f> ppcloud;
std::vector<cv::Point2f> imgPoints;
vector<int> pcloud_status(pcloud.size(),0);
//скануємо використані види (good_views)
for (set<int>::iterator done_view = good_views.begin(); done_view !=
good_views.end(); ++done_view)
{
int old_view = *done_view; //вже використаний для реконструкції вид
//перевіряємо на співпадіння кадри <working_view>
та <old_view> (тобто поточну хмару)
std::vector<cv::DMatch> matches_from_old_to_working =
matches_matrix[std::make_pair(old_view,working_view)];
//скануємо 2D-2D співпадіння точок
for (unsigned int match_from_old_view=0;
match_from_old_view<matches_from_old_to_working.size();
match_from_old_view++) {

Atronax 25.02.21 в 14:06

0

// індекс співпадіння 2D точки в <old_view>
int idx_in_old_view =
matches_from_old_to_working[match_from_old_view].queryIdx;
//скануємо наявну хмару, щоб побачити, чи точка з <old_view> існує
for (unsigned int pcldp=0; pcldp<pcloud.size(); pcldp++) {
// перевіряємо, чи 2D точка з <old_view> врахована при пошуку 3D
точки хмари
if (idx_in_old_view == pcloud[pcldp].index_of_2d_origin[old_view]
&& pcloud_status[pcldp] == 0) //запобігаємо дублюванню
{
//3d точка хмари
ppcloud.push_back(pcloud[pcldp].pt);
//2d точка зображення <working_view>
Point2d pt_ = imgpts[working_view]
[matches_from_old_to_working[match_from_old_view].trainIdx].pt;
imgPoints.push_back(pt_);
pcloud_status[pcldp] = 1;
break;
}
}
}
}
cout<<"знайдено "<<ppcloud.size() <<" 3d-2d відповідностей точок"<<endl;
Тепер маємо вирівнені пари тривимірних точок сцени з урахуванням використаних двовимірних точок на кожному кадрі, тому можна використати їх для відновлення позиції камери таким чином:

Atronax 25.02.21 в 14:11

0

cv::Mat_<double> t,rvec,R;
cv::solvePnPRansac(ppcloud, imgPoints, K, distcoeff, rvec, t, false);
//отримуємо матрицю повороту розмірності 3x3
Rodrigues(rvec, R);
P1 = cv::Matx34d(R(0,0),R(0,1),R(0,2),t(0),
R(1,0),R(1,1),R(1,2),t(1),
R(2,0),R(2,1),R(2,2),t(2));

Atronax 25.02.21 в 14:11

0

Використовуючи функцію solvePnPRansac замість solvePnP, отримаємо менше зайвих точок. Маючи нову матрицю P1, можна просто викликати функцію TriangulatePoints, визначену раніше, та населити хмару більшою кількістю тривимірних точок. Далі, починаючи з 4-го малюнку, зображено поступову реконструкцію сцени фонтану з ресурсу http://cvlab.epfl.ch/~strecha/multiview/denseMVS.html. Верхнє ліве зображення - це результат відновлення сцени на основі 4 використаних кадрів; камери позначені червоними пірамідами та білими лініями, що вказують напрям. Решта зображень показують, як інші види формують додаткові точки хмари.

Atronax 25.02.21 в 14:18

0

Уточнюємо реконструкцію

Atronax 25.02.21 в 14:18

0

Одним із найважливіших етапів побудови структури з руху є уточнення та оптимізація відновленої сцени, також відоме як процес регулювання груп (BA). Це крок, на якому всі зібрані дані підлаштовуються під певну монолітну модель. Оптимізуються положення і точок, і камери, тому похибки репроєкції мінімізовані (тобто, апроксимовані 3D точки проєктуються на зображення поряд з врахованими двовимірними). Зазвичай, цей процес включає пошук розв'язків досить великих лінійних рівнянь з десятками тисяч параметрів. Можливо, це потребуватиме важкої роботи, але зроблені раніше кроки дозволять легко інтегрувати регулювання груп. Деякі речі, які раніше видавалися дивними, стануть більш зрозумілими; наприклад, причина того, чому ми зберігаємо початкові двовимірні точки для кожної тривимірної у хмарі.

Atronax 26.02.21 в 09:02

0

Серед реалізацій відповідних алгортимів виділяють бібліотеку простого розрідженого регулювання груп (SSBA); використаємо саме його через простоту програмного інтерфейсу. Він потребує лише декілька вхідних аргументів, які можна доволі легко отримати з наявних структур даних. Перш за все нас цікавить фукнція CommonInternalsMetricBundleOptimizer, що здійснює власне оптимізацію. Їй потрібні внутрішні параметри камери, хмара тривимірних точок та відповідні їм двовимірні, спрямовані на сцену камери. Як саме отримати ці дані, вже відомо. Маємо відмітити, даний метод припускає, що всі зображення зняті одним пристроєм і мають однакові внутрішні параметри, інші способи можуть не враховувати ці обмеження. Здійснити регулювання груп можна таким чином:

Atronax 26.02.21 в 09:21

0

void BundleAdjuster::adjustBundle(
vector<CloudPoint>&pointcloud,
const Mat&cam_intrinsics,
conststd::vector<std::vector<cv::KeyPoint>>&imgpts,
std::map<int ,cv::Matx34d>&Pmats
)
{
int N = Pmats.size(), M = pointcloud.size(), K = -1;
cout<<"камер = "<< N <<" точок = "<< M <<" вимірів =
"<< K <<endl;
StdDistortionFunction distortion;
// матриця внутрішніх параметрів
Matrix3x3d KMat;
makeIdentityMatrix(KMat);
KMat[0][0] = cam_intrinsics.at<double>(0,0);
KMat[0][1] = cam_intrinsics.at<double>(0,1);
KMat[0][2] = cam_intrinsics.at<double>(0,2);
KMat[1][1] = cam_intrinsics.at<double>(1,1);
KMat[1][2] = cam_intrinsics.at<double>(1,2);
...

Atronax 26.02.21 в 09:23

0

// хмара 3D точок
vector<Vector3d >Xs(M);
for (int j = 0; j < M; ++j)
{
Xs[j][0] = pointcloud[j].pt.x;
Xs[j][1] = pointcloud[j].pt.y;
Xs[j][2] = pointcloud[j].pt.z;
}
cout<<"3D точки сформовані."<<endl;
// конвертуємо камери в структури для BA
vector<CameraMatrix> cams(N);
for (inti = 0; i< N; ++i)

Atronax 26.02.21 в 09:25

0

for (inti = 0; i< N; ++i)
{
intcamId = i;
Matrix3x3d R;
Vector3d T;
Matx34d& P = Pmats[i];
R[0][0] = P(0,0); R[0][1] = P(0,1); R[0][2] = P(0,2); T[0] = P(0,3);
R[1][0] = P(1,0); R[1][1] = P(1,1); R[1][2] = P(1,2); T[1] = P(1,3);
R[2][0] = P(2,0); R[2][1] = P(2,1); R[2][2] = P(2,2); T[2] = P(2,3);
cams[i].setIntrinsic(Knorm);
cams[i].setRotation(R);
cams[i].setTranslation(T);
}
cout<<"Камери створено."<<endl;
vector<Vector2d > measurements;
vector<int> correspondingView;
vector<int> correspondingPoint;

Atronax 26.02.21 в 09:25

0

// відповідні двовимірні точки
for (unsigned int k = 0; k <pointcloud.size(); ++k)
{
for (unsigned int i=0; i<pointcloud[k].imgpt_for_img.size(); i++) {
if (pointcloud[k].imgpt_for_img[i] >= 0) {
int view = i, point = k;
Vector3d p, np;
Point cvp = imgpts[i][pointcloud[k].imgpt_for_img[i]].pt;
p[0] = cvp.x;
p[1] = cvp.y;
p[2] = 1.0;
// Нормалізуємо результати, щоб вони відповідали одиниці фокусної довжини.
scaleVectorIP(1.0/f0, p);
measurements.push_back(Vector2d(p[0], p[1]));
correspondingView.push_back(view);
correspondingPoint.push_back(point);
}
}
} // кінець k
K = measurements.size();
cout<<"Зчитано "<< K <<" дійсних 2D вимірів."<<endl;
...
// здійснюємо регулювання груп
{
CommonInternalsMetricBundleOptimizeropt(V3D::FULL_BUNDLE_FOCAL_LENGTH_
PP, inlierThreshold, K0, distortion, cams, Xs, measurements,
correspondingView, correspondingPoint);
opt.tau = 1e-3;
opt.maxIterations = 50;
opt.minimize();
cout<<"статус оптимізатора = "<<opt.status<<endl;

Atronax 26.02.21 в 09:29

0

}
...
//виокремлюємо 3D точки
for (unsigned int j = 0; j <Xs.size(); ++j)
{
pointcloud[j].pt.x = Xs[j][0];
pointcloud[j].pt.y = Xs[j][1];
pointcloud[j].pt.z = Xs[j][2];
}
//здобуваємо налаштовані камери
for (int i = 0; i< N; ++i)
{
Matrix3x3d R = cams[i].getRotation();
Vector3d T = cams[i].getTranslation();
Matx34d P;
P(0,0) = R[0][0]; P(0,1) = R[0][1]; P(0,2) = R[0][2]; P(0,3) = T[0];
P(1,0) = R[1][0]; P(1,1) = R[1][1]; P(1,2) = R[1][2]; P(1,3) = T[1];
P(2,0) = R[2][0]; P(2,1) = R[2][1]; P(2,2) = R[2][2]; P(2,3) = T[2];
Pmats[i] = P;
}
}

Atronax 26.02.21 в 09:31

0

Не зважаючи на довжину коду, в основному здійснюється конвертація внутрішніх структур даних у відповідні, затребувані бібліотекою SSBA, і навпаки, а також активується процес оптимізації.
Далі зображено результат застосування регулювання груп. Кадри зліва показують з двох точок зору точки хмари до застосування процесу, а справа - оптимізовану хмару. Зміни досить значні, адже більшість неспівпадінь між триангульованими точками зараз практично консолідовано. Легко бачити, як регулювання дозволило сформувати набагато кращу реконструкцію плоских поверхонь.

Atronax 26.02.21 в 09:43

0

Використовуємо PCL для візуалізації хмари тривимірних точок

Atronax 26.02.21 в 09:43

0

Працюючи з 3D даними, важко швидко зрозуміти, чи є результат коректним, просто поглянувши на виміри похибок репроєкції чи грубу інформацію про точки. З іншого боку, дивлячись на власне хмару, можна негайно підтвердити її правильність чи наявність помилок. Для візуалізації застосуємо сестринський проєкт OpenCV, що називають бібліотекою хмар точок (PCL). Вона постачає множину іструментів для візуалізації та аналізу хмар, наприклад пошук плоских поверхонь, співпадіння, сегментацію об'єктів та видалення зайвих елементів. Ці методи досить корисні, якщо нас цікавить не так хмара, як інформація вищого рівня про тривимірну модель.

Atronax 26.02.21 в 09:49

0

По-перше, маємо представити хмару (по суті, список тривимірних точок) у вигляді структур даних PCL. Це можна зробити так:
pcl::PointCloud<pcl::PointXYZRGB>::Ptr cloud;
void PopulatePCLPointCloud(const vector<Point3d>& pointcloud,
const std::vector<cv::Vec3b>& pointcloud_RGB
)
//Населимо хмару точок
{
cout<<"Створюємо хмару точок...";
cloud.reset(new pcl::PointCloud<pcl::PointXYZRGB>);
for (unsigned int i=0; i<pointcloud.size(); i++) {
// отримуємо значення кольору RGB для точки
Vec3b rgbv(255,255,255);
if (pointcloud_RGB.size() >= i) {
rgbv = pointcloud_RGB[i];
}

Atronax 26.02.21 в 09:53

0

// перевіряємо на наявність помилкових координат (NaN, Inf і так далі)
if (pointcloud[i].x != pointcloud[i].x || isnan(pointcloud[i].x) ||
pointcloud[i].y != pointcloud[i].y || isnan(pointcloud[i].y) ||
pointcloud[i].z != pointcloud[i].z || isnan(pointcloud[i].z) ||
fabsf(pointcloud[i].x) > 10.0 ||
fabsf(pointcloud[i].y) > 10.0 ||
fabsf(pointcloud[i].z) > 10.0) {
continue;
}
pcl::PointXYZRGB pclp;
// тривимірні координати
pclp.x = pointcloud[i].x;
pclp.y = pointcloud[i].y;
pclp.z = pointcloud[i].z;
// колір RGB, має бути представлений у вигляді цілого числа
uint32_t rgb = ((uint32_t)rgbv[2] << 16 | (uint32_t)rgbv[1] << 8 |
(uint32_t)rgbv[0]);
pclp.rgb = *reinterpret_cast<float*>(&rgb);
cloud->push_back(pclp);
}
cloud->width = (uint32_t) cloud->points.size(); // кількість точок
cloud->height = 1; // список точок, один ряд даних
}

Atronax 26.02.21 в 09:55

0

Щоб зробити візуалізацію зручнішою для аналізу, можна використати відповідні значення RGB зображень. Також до грубої хмари можна застосувати фільтр, який видалить очевидні зайві точки за допомогою нструменту статистичного видалення зайвих точок (SOR) таким чином:

Atronax 26.02.21 в 09:58

0

void SORFilter() {
pcl::PointCloud<pcl::PointXYZRGB>::Ptr cloud_filtered (new
pcl::PointCloud<pcl::PointXYZRGB>);
std::cerr<<"Хмара до фільтрування SOR: "<< cloud->width * cloud-
>height <<" точок даних"<<std::endl;
// Створюємо об'єкт фільтра
pcl::StatisticalOutlierRemoval<pcl::PointXYZRGB>sor;
sor.setInputCloud (cloud);
sor.setMeanK (50);
sor.setStddevMulThresh (1.0);
sor.filter (*cloud_filtered);
std::cerr<<"Хмара після фільтрування SOR: "<<cloud_filtered->width *
cloud_filtered->height <<" data points "<<std::endl;
copyPointCloud(*cloud_filtered,*cloud);
}

Atronax 26.02.21 в 10:24

0

Тепер можемо використати програмний інтерфейс PCL для простої візуалізації хмари таким чином:
Void RunVisualization(const vector<cv::Point3d>& pointcloud,
const std::vector<cv::Vec3b>& pointcloud_RGB) {
PopulatePCLPointCloud(pointcloud,pointcloud_RGB);
SORFilter();
copyPointCloud(*cloud,*orig_cloud);
pcl::visualization::CloudViewer viewer("Огляд хмари");
// формуємо вид хмари
viewer.showCloud(orig_cloud,"оригінальна");
while (!viewer.wasStopped ())
{
// NOP
}
}

Atronax 26.02.21 в 10:27

0

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

Atronax 26.02.21 в 13:52

0

Використовуємо код прикладу

Atronax 26.02.21 в 13:53

0

Приклад застосування SfM постачається разом з цією книгою. Поглянемо, як можна скомпілювати, запустити програму та скористатися її виводом. Для цього знадобиться CMake, кросплатформове середовище побудови, подібне до Maven чи SCons. Також необхідно забезпечити виконання таких вимог:
OpenCV v2.3 чи вище
PCL v1.6 чи вище
SSBA v3.0 чи вище

Atronax 26.02.21 в 13:59

0

Спочатку маємо налаштувати середовище побудови. Для цього можна створити теку з назвою "build", яка зберігатиме всі потрібні файли; далі вважатимемо, що всі операції командної стрічки знаходяться в ній, хоча процес не змінюється, навіть якщо не користуватися цією текою (важливе лише розміщення файлів).

Atronax 26.02.21 в 14:07

0

Маємо впевнитися, що CMake знаходить SSBA та PCL. Якщо PCL встановлено правильно, проблем бути не має; однак, потрібно встановити коректне розміщення встановленої бібліотеки SSBA як значення параметра побудови DSSBA_LIBRARY_DIR. Якщо операційною системою є Windows, для побудови можна використати Microsoft Visual Studio; для цього виконаємо таку команду:
cmake –G "Visual Studio 10" -DSSBA_LIBRARY_DIR=../3rdparty/SSBA3.0/build/ ..
Якщо ж працюємо в Linux, Mac OS чи іншій Unix-подібній ОС, матимемо наступну стрічку:
cmake –G "Unix Makefiles" -DSSBA_LIBRARY_DIR=../3rdparty/SSBA3.0/build/ ..
Для XCode та Mac OS команда така:
cmake –G Xcode -DSSBA_LIBRARY_DIR=../3rdparty/SSBA-3.0/build/ ..

Atronax 26.02.21 в 14:13

0

CMake також здатна створювати макроси для Eclipse, Codeblocks та інших IDE. Коли закінчиться формування середовища, приступимо й до побудови рішення. В Unix-подібній системі можна просто скористатися утилітою make, в інших випадках достатньо застосувати процес побудови певного середовища розробки. Коли побудова завершиться, в результаті отримаємо файл ExploringSfMExec, який покаже в дії процес SfM. Запустивши його без аргументів, отримаємо наступну стрічку: USAGE: ./ExploringSfMExec <path_to_images>

Atronax 26.02.21 в 14:34

0

Щоб застосувати алгоритм для набору зображень, маємо вказати їх розміщення на диску. Якщо вхідні дані дійсні, має розпочатися процес, і ми побачимо на екрані прогрес та інформацію налагодження. В результаті отримаємо набір тривимірних точок, що відповідає використаним зображенням. Натискаючи 1 чи 2, можна змінювати режим між уточненою та неуточненою хмарою.

Atronax 26.02.21 в 14:50

0

Висновки

Atronax 26.02.21 в 14:14

0

У цьому розділі ми побачили, яким саме чином OpenCV допомагає побудувати структуру з руху в спосіб, який легко закодувати та зрозуміти. Програмний інтерфейс бібліотеки включає набір корисних функцій та структур даних, які полегшують життя та спрощують реалізацію.
Однак, шедевральні методи SfM набагато складніші. Багато нюансів ми відкидаємо заради простоти, а ще ж необхідні інші перевірки на помилки. До того ж, обрані методи для різних елементів SfM можна удосконалити. Наприклад, Х. та З. пропонують точний метод триангуляції, який мінімізує похибки репроєкції. Деякі з них використовують навіть N-видову триангуляцію у разі появи зв'язків між особливими точками на декількох зображеннях.

Atronax 26.02.21 в 15:02

0

Якщо потрібно розширити та поглибити знання про відновлення структури з руху, можна відшукати їх в інших відкритих тематичних бібліотеках. Особливо цікавим проєктом є libMV, що реалізує широкий масив елементів SfM, взаємозамінних задля кращих результатів. Існує також декілька праць Університету Вашингтона, в яких описані інструменти для багатьох сфер SfM (Bundler та VisualSfM). Ця праця надихнула розробників онлайн продукту Microsoft під назвою PhotoSynth. В мережі доступні й інші реалізації SfM, достатньо лише віднайти їх.

Atronax 26.02.21 в 15:24

0

Ще одним важливим фактом, який ми не обговорювали раніше, є взаємозв'язок SfM та VLM, краще відомий як методи одночасної локалізації та відображення (SLAM). У цьому розділі ми мали справу із заданим набором зображень та послідовністю відеокадрів, застосування SfM доречне в таких випадках; але деякі програми не мають попередньо записаних наборів даних та мають проводити реконструкцію в режимі реального часу. Цей процес відомий як відображення, він застосований для створення 3D карти світу на основі співпадіння ключових точок, відслідковування у 2D просторі, та триангуляції.
У наступному розділі побачимо, як використати OpenCV для пошуку ліцензованих номерних пластин на зображеннях, застосовуючи різні техніки машинного навчання.

Atronax 26.02.21 в 15:38

0

Посилання
Multiple View Geometry in Computer Vision, Richard Hartley and Andrew Zisserman,
Cambridge University Press
Triangulation, Richard I. Hartley and Peter Sturm, Computer vision and image
understanding, Vol. 68, pp. 146-157
http://cvlab.epfl.ch/~strecha/multiview/denseMVS.html
On Benchmarking Camera Calibration and Multi-View Stereo for High Resolution
Imagery,C. Strecha, W. von Hansen, L. Van Gool, P. Fua, and U. Thoennessen, CVPR
http://www.inf.ethz.ch/personal/chzach/opensource.html
http://www.ics.forth.gr/~lourakis/sba/
http://code.google.com/p/libmv/
http://www.cs.washington.edu/homes/ccwu/vsfm/
http://phototour.cs.washington.edu/bundler/
http://photosynth.net/
http://en.wikipedia.org/wiki/Simultaneous_localization_and_mapping
http://pointclouds.org
http://www.cmake.org

Atronax 26.02.21 в 14:14

0
Минутку...

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