Как использовать opencv flann:: Index?

У меня есть некоторые проблемы с opencv flann:: Index -

Я создаю индекс

    Mat samples = Mat::zeros(vfv_net_quie.size(),24,CV_32F);
        for (int i =0; i < vfv_net_quie.size();i++)
        {
           for (int j = 0;j<24;j++)
           {
              samples.at<float>(i,j)=(float)vfv_net_quie[i].vfv[j];
           }
        }
    cv::flann::Index flann_index(
            samples,
            cv::flann::KDTreeIndexParams(4),
            cvflann::FLANN_DIST_EUCLIDEAN
               );
    flann_index.save("c:\\index.fln");

Если я попытаюсь загрузить его и найти ближайшую никову

cv::flann::Index flann_index(Mat(),
    cv::flann::SavedIndexParams("c:\\index.fln"),
    cvflann::FLANN_DIST_EUCLIDEAN
    );

cv::Mat resps(vfv_reg_quie.size(), K, CV_32F);
cv::Mat nresps(vfv_reg_quie.size(), K, CV_32S);
cv::Mat dists(vfv_reg_quie.size(), K, CV_32F);

flann_index.knnSearch(sample,nresps,dists,K,cv::flann::SearchParams(64));

И иметь нарушение прав в miniflann.cpp в строке

((IndexType*)index)->knnSearch(_query, _indices, _dists, knn,
                          (const ::cvflann::SearchParams&)get_params(params));

Пожалуйста, помогите

Ответ 1

Вы не должны загружать файл flann в Mat(), так как это место, где хранится индекс. Это временный объект, уничтоженный после вызова конструктора. Вот почему индекс не указывает куда полезнее, когда вы вызываете knnSearch().

Я пробовал следующее:

cv::Mat indexMat;
cv::flann::Index flann_index(
    indexMat,
    cv::flann::SavedIndexParams("c:\\index.fln"),
    cvflann::FLANN_DIST_EUCLIDEAN
);

в результате:

Reading FLANN index error: the saved data size (100, 64) or type (5) is different from the passed one (0, 0), 0

что означает, что матрица должна быть инициализирована с правильными размерами (мне кажется, что это очень глупо, поскольку я не обязательно знаю, сколько элементов хранится в моем индексе).

cv::Mat indexMat(samples.size(), CV_32FC1);
cv::flann::Index flann_index(
    indexMat,
    cv::flann::SavedIndexParams("c:\\index.fln"),
    cvflann::FLANN_DIST_EUCLIDEAN
);

делает трюк.

Ответ 2

В принятом ответе как-то непонятно и вводит в заблуждение, почему матрица ввода в конструкторе cv::flann::Index должна иметь тот же размер, что и матрица, используемая для генерации сохраненного индекса. Я подробно рассмотрю комментарий @Sau на примере.

KDTreeIndex был сгенерирован с использованием ввода a cv::Mat sample, а затем сохранен. Когда вы загружаете его, вы должны предоставить ту же матрицу sample, чтобы сгенерировать ее, что-то вроде (с использованием шаблона GenericIndex):

cv::Mat sample(sample_num, sample_size, ... /* other params */);
cv::flann::SavedIndexParams index_params("c:\\index.fln");
cv::flann::GenericIndex<cvflann::L2<float>> flann_index(sample, index_params);

L2 - обычное евклидово расстояние (другие типы можно найти в opencv2/flann/dist.h).

Теперь индекс можно использовать, как показано нахождение K ближайших соседей точки query:

std::vector<float> query(sample_size);
std::vector<int> indices(K);
std::vector<float> distances(K);

flann_index.knnSearch(query, indices, distances, K, cv::flann::SearchParams(64));

Матрица indices будет содержать местоположения ближайших соседей в матрице sample, которая сначала использовалась для сгенерируйте индекс. Поэтому вам нужно загрузить сохраненный индекс самой матрицей, используемой для генерации индекса, иначе возвращаемый вектор будет содержать индексы, указывающие на бессмысленных "ближайших соседей".

Кроме того, вы получаете матрицу distances, содержащую, как далеко находятся найденные соседи из вашей точки query, которую впоследствии можно использовать для выполнения например, взвешенное обратное расстояние.

Также обратите внимание, что sample_size должен совпадать с матрицей sample и query.