Ошибка сегментации при триангуляции большой сети в OpenCV
Мне необходимо построить триангуляционную сеть для очень большого количества уникальных точек - в моём примере, около 270000000 (270 миллионов) точек.
Для построения триангуляционной сети я использую класс cv::Subdiv2D.
В процессе добавления точек в триангуляционную сеть приложение завершается с segmentation fault
(SIGSEGV
) на моменте добавления точки № 178956970.
При дальнейшей отладке выяснилось:
- Объема RAM+swap на моём ПК достаточно (нужно около 40 Гб);
- Триангуляционная сеть из 178956969 точек, возвращаемая методом getTriangleList() состоит из 357913940 треугольников (что чуть-чуть меньше 8 Гб памяти);
- При добавлении точки 178956970 наблюдается резкое увеличение расхода памяти (до максимального значения в 40 Гб).
Измерения проводились на двух ЭВМ:
- ПК (Debian 11; OpenCV 4.5.1): RAM 32 Гб + swap 16 Гб (максимально допустимый расход памяти 48 Гб)
- Сервер (Astra Linux 1.6; OpenCV 2.4.9.1): RAM 256 Гб + swap 256 Гб (максимально допустимый расход памяти 512 Гб)
Потребление я проверял, отслеживая показания диспетчера задач.
Потребление на ПК (сразу перед segfault): RAM - 32 Гб; swap - ~12 Гб; итоговый расход памяти 32+12=44 Гб (заметно меньше максимально допустимого расхода; ~4 Гб расходуют сторонние программы).
Потребление на сервере (сразу перед segfault): RAM - ~40 Гб; swap - 0 Гб; итоговый расход памяти ~40 Гб (заметно меньше максимально допустимого расхода).
На обоих ЭВМ возникает указанная проблема.
Из этих показаний я делаю вывод, что памяти мне хватает (хотя уже не исключаю возможность резкого возрастания потребления памяти, которое было не видно в диспетчере).
Стек:
main.cpp
cv::Subdiv2D::insert(cv::Point_<float>)
cv::Subdiv2D::connectEdges(int, int)
cv::Subdiv2D::newEdge()
Вот код с двумя примерами (вкл./выкл. SLOW_EXAMPLE
), на которых у меня корректно не работает opencv; ошибка (и стек) одна и та же - при добавлении в Subdiv2D точки с индексом points[178956969]
приложение аварийно завершается:
#include <iostream>
#include <cstdlib>
#include <opencv2/imgproc.hpp>
//#define SLOW_EXAMPLE
//#define CHECK_TRIANGLES
int main() {
#ifdef SLOW_EXAMPLE
constexpr size_t w = 27000;
constexpr size_t h = 27000;
#else
constexpr size_t w = 61300;
constexpr size_t h = 104334;
#endif
std::cout << "OpenCV ver. " << CV_VERSION << std::endl;
const cv::Rect rect(0, 0, w, h);
cv::Subdiv2D subdiv(rect);
std::vector<cv::Point2f> points;
#ifdef SLOW_EXAMPLE
const size_t cell_size = 2;
for (size_t row = 0; row < h; row += cell_size) {
for (size_t col = 0; col < w; col += cell_size) {
points.emplace_back((float) col, (float) row);
}
}
#else
std::srand(666);
const size_t cell_size = 4;
for (size_t row = 0; row < h / cell_size; row++) {
for (size_t col = 0; col < w / cell_size; col++) {
const uint32_t x = std::rand() % cell_size;
const uint32_t y = std::rand() % cell_size;
points.emplace_back((float) (col * cell_size + x), (float) (row * cell_size + y));
}
}
#endif
const size_t points_count = points.size();
std::cout << points[0].x << " " << points[0].y << std::endl;
std::cout << points[points_count - 1].x << " " << points[points_count - 1].y << std::endl;
std::cout << "points = " << points_count << std::endl;
auto get_triangle = [&subdiv]() {
std::vector<cv::Vec6f> triangles;
subdiv.getTriangleList(triangles);
std::cout << "Triangle network: " << triangles.size() << " triangles ( "
<< (triangles.size() * 6.0 * sizeof(float)) / (1024.0 * 1024.0 * 1024.0) << " GB)" << std::endl;
};
for (int64_t i = 0; i < points_count; i++) {
if (i % 50000 == 0 || i > 178950000) {
std::cout << "\rsubdiv.insert: " << i << "/" << points_count << " ( " << (i * 100.0) / points_count << " %) ";
std::cout.flush();
}
subdiv.insert(points[i]);
#ifdef CHECK_TRIANGLES
if (i == 178956968) { get_triangle(); }
#endif
}
std::cout << "\rsubdiv.insert: " << points_count << "/" << points_count << " ( 100 %) " << std::endl;
get_triangle();
return 0;
}
Вопросы:
- Кто-нибудь знает, в OpenCV есть ограничения на размер триангуляционной сети? (В Интернете и исходниках OpenCV я такой информации не нашёл; возможно, плохо искал; за пределы диапазона [0..2^31-1] идентификаторов вершин и рёбер графа сети, вроде, не выхожу)
- Если кто-то с таким уже сталкивался: можно ли обойти это ограничение, оставаясь в рамках OpenCV? (потайловая обработка прямоугольника 2D-точек не годится, т.к. треугольники на краях тайлов не будут соответствовать треугольникам исходной сети, а использование перекрытия тайлов, в общем случае, не решает эту проблему).