C++調用HNSW實現圖像配准


HNSW

分層的可導航小世界(Hierarchical Navigable Small World, HNSW)

特點

1、一種基於圖的數據結構

2、使用貪婪搜索算法的變體進行ANN搜索,每次選擇最接近查詢的未訪問的相鄰元素時,它都會從元素到另一個元素地遍歷整個圖,直到達到停止條件

原理參考:

https://blog.csdn.net/u011233351/article/details/85116719

https://zhuanlan.zhihu.com/p/80552211?utm_source=wechat_session

https://blog.csdn.net/qq_38156298/article/details/100085892

一、什么特征作為輸入

1、原始特征,圖像本身作為輸入,顯然這種情況不說效果如何,最起碼數據量上就會很大,對RAM要求極大。一般不會作為輸入特征

2、SIFT特征, 圖像處理中有很多特征提取方法,其中SIFT有代表性。當然也可以選擇其他。這樣大大的降低數據量的同時,也能起到降維的作用。將有效特征作為相似度比較的依據比較合理。

3、DEEP特征,目前神經網絡比較流行,可以利用這個方法提取特征。

4、其他特征

特征選擇有一個原則:歸一化的降維的主要特征。去除冗余點,這對我們后續的相似度檢測有着很大的好處。

二、參數M、efConstruction和max_elements

1、M - the number of bi-directional links created for every new element during construction.

M的合理范圍在[2,200]。M越高,對於本身具有高維特征的數據集來講,recall可能越高,性能越好;M越低,對於本身具有低維特征的數據集來講,性能越好。

建議M:12,16,32。因為特征已經選擇過了,維度一般不會太高。

2、efConstruction - the parameter has the same meaning as ef, but controls the index_time/index_accuracy.

ef - the size of the dynamic list for the nearest neighbors (used during the search).

efConstruction越大,構造時間越長,index quality越好。有時,efConstruction增加的過快並不能提升index quality。有一種方法可以檢查efConstruction的選擇是否可以接受。計算recall,當ef=efConstruction,在M值時,如果recall低於0.9,那么可以適當增加efConstruction的數值。

3、max_elements - 檢索的最大元素。

這個參數取決於你創建的索引庫的特征數量。如果你想要在1000,0000個特征中檢測是否有相似的圖像時,這個max_elements就要設置為1000,0000. 當然這也要看RAM是否支持這么多數據同時加載進來。

三、demo

https://github.com/pengzerong/hnsw-test

流程

1、讀取兩幅圖像,分別對兩張圖提取SIFT特征

Mat srcImg1 = imread("basketball1.png", 1);
Mat srcImg2 = imread("basketball2.png", 1);
if (srcImg1.empty() || srcImg2.empty())
    return -1;

Ptr<cv::Feature2D> detector = xfeatures2d::SIFT::create(128);
std::vector<KeyPoint> keypoints1, keypoints2;
Mat descriptors1, descriptors2;
// 提取圖片的特征點及特征點描述
detector->detect(srcImg1, keypoints1);
detector->compute(srcImg1, keypoints1, descriptors1);
rootSift(descriptors1);

detector->detect(srcImg2, keypoints2);
detector->compute(srcImg2, keypoints2, descriptors2);
rootSift(descriptors2);

2、特征歸一化

void rootSift(Mat &descriptors, const float eps = 1e-7)
{
    // Compute sums for L1 Norm
    Mat sums_vec;
    descriptors = abs(descriptors); //otherwise we draw sqrt of negative vals
    reduce(descriptors, sums_vec, 1 /*sum over columns*/, CV_REDUCE_SUM, CV_32FC1);
    for (int row = 0; row < descriptors.rows; row++) {
        int offset = row * descriptors.cols;
        for (int col = 0; col < descriptors.cols; col++) {
            descriptors.at<float>(offset + col) = sqrt(descriptors.at<float>(offset + col) /
                (sums_vec.at<float>(row) + eps) /*L1-Normalize*/);
        }
        // L2 distance
        normalize(descriptors.row(row), descriptors.row(row), 1.0, 0.0, NORM_L2);

    }
    return;
}

3、創建HNSW

size_t maxCount = descriptors1.rows;
int M = 32;
int efConstruction = 100;// TestFindSelf,140以下有不能查找到自己的情況
int vecdim = descriptors1.cols;// 特征維度
hnswlib::InnerProductSpace *L2Space = new hnswlib::InnerProductSpace(vecdim);
hnswlib::HierarchicalNSW<float> *hnsw = new hnswlib::HierarchicalNSW<float>(L2Space, maxCount, M, efConstruction);
int count = 0;
for (size_t i = 0; i < descriptors1.rows; i++)
{
    float *mass = new float[vecdim];
    for (size_t j = 0; j < vecdim; j++)
    {
        mass[j] = descriptors1.at<float>(i, j);
    }
    hnsw->addPoint((void*)mass, count);
    ++count;
    delete[] mass;
}

4、匹配特征點,篩選出可以用來配准的特征點

int nn = 3;
std::vector<DMatch> matchPoints;
Mat indices, dists;
for (size_t i = 0; i < descriptors2.rows; i++)
{
    float *mass = new float[vecdim];
    for (size_t j = 0; j < vecdim; j++)
    {
        mass[j] = descriptors2.at<float>(i, j);
    }
    std::priority_queue<std::pair<float, hnswlib::labeltype>> candidates = hnsw->searchKnn((void*)mass, nn);
    delete[] mass;

    for (int k = 0; k < nn; k++)
    {
        double dAngle = abs(keypoints2.at(i).angle - keypoints1.at(candidates.top().second).angle);
        if (abs(candidates.top().first) < 0.1 && dAngle < 10)
        {
            std::cout << i << " angle diff: " << dAngle << ", dist: " << candidates.top().first << " "<< candidates.top().second << std::endl;
            DMatch dmatches(candidates.top().second, i, candidates.top().first);
            matchPoints.push_back(dmatches);
        }
        candidates.pop();
    }
}

5、匹配效果

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM