Ошибка сегментации при триангуляции большой сети в OpenCV

Рейтинг: 1Ответов: 1Опубликовано: 09.08.2023

Мне необходимо построить триангуляционную сеть для очень большого количества уникальных точек - в моём примере, около 270000000 (270 миллионов) точек.

Для построения триангуляционной сети я использую класс cv::Subdiv2D.

В процессе добавления точек в триангуляционную сеть приложение завершается с segmentation fault(SIGSEGV) на моменте добавления точки № 178956970.

При дальнейшей отладке выяснилось:

  1. Объема RAM+swap на моём ПК достаточно (нужно около 40 Гб);
  2. Триангуляционная сеть из 178956969 точек, возвращаемая методом getTriangleList() состоит из 357913940 треугольников (что чуть-чуть меньше 8 Гб памяти);
  3. При добавлении точки 178956970 наблюдается резкое увеличение расхода памяти (до максимального значения в 40 Гб).

Измерения проводились на двух ЭВМ:

  1. ПК (Debian 11; OpenCV 4.5.1): RAM 32 Гб + swap 16 Гб (максимально допустимый расход памяти 48 Гб)
  2. Сервер (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;
}

Вопросы:

  1. Кто-нибудь знает, в OpenCV есть ограничения на размер триангуляционной сети? (В Интернете и исходниках OpenCV я такой информации не нашёл; возможно, плохо искал; за пределы диапазона [0..2^31-1] идентификаторов вершин и рёбер графа сети, вроде, не выхожу)
  2. Если кто-то с таким уже сталкивался: можно ли обойти это ограничение, оставаясь в рамках OpenCV? (потайловая обработка прямоугольника 2D-точек не годится, т.к. треугольники на краях тайлов не будут соответствовать треугольникам исходной сети, а использование перекрытия тайлов, в общем случае, не решает эту проблему).

Ответы

▲ 0

Размер ограничен диапазоном int - около 2 млрд треугольников.