AprilTag3中輪廓聚類的哈希表存儲方法

哈希表初始化:

struct uint64_zarray_entry **clustermap = calloc(nclustermap, sizeof(struct uint64_zarray_entry*));

clustermap 是一個二維指針。哈希表可以理解爲數組+鏈表,這裏的數組大小便是nclustermap,每個數組存儲一個鏈表的起始地址,鏈表中的每個元素類型是uint64_zarray_entry*。

struct uint64_zarray_entry
{
    uint64_t id; // 待存儲的邊界點ID
    zarray_t *cluster;// 該像素點的聚類信息

    struct uint64_zarray_entry *next;// 下一個鏈表節點地址
};

然後對圖像遍歷每行每列,分別檢索對比當前點與"下方、右側、右上方、右下方"的像素值梯度,按梯度進行聚類並進行哈希存儲,具體步驟見下:

if (v0 + v1 == 255) {     
 if (unionfind_get_set_size(uf, rep1) > 24) {

輪廓檢驗時的條件是:1)兩個鄰接像素值相加爲255;2)鄰接像素所處的連接區域(由聯合查找算法確定)像素點數量大於24。
然後計算待存儲的哈希表鍵值:clusterid,也是聚類ID,滿足上述條件的鄰接像素點ID決定。

uint64_t clusterid;       \
/*將兩個點的ID存儲於64位clusterid量中,大的位於前面*/              \
 if (rep0 < rep1)                                    \
         clusterid = (rep1 << 32) + rep0;                \
else                                                \
         clusterid = (rep0 << 32) + rep1;                \

然後,基於該哈希表的鍵值,也是聚類點ID,計算數組索引:

uint32_t clustermap_bucket = u64hash_2(clusterid) % nclustermap; /*哈希表的數組的編號*/

clustermap_bucket 便爲當前哈希表的一維索引位置。每個像素點聚類後存儲的位置由clustermap_bucket 決定,也就是取模後值相等那些像素點都會存儲在一個鏈表裏,該鏈表的起始地址爲索引號爲clustermap_bucket的數組。clustermap[clustermap_bucket] 。
新建一個哈希表入口變量entry,新建鏈表節點地址便是對應桶/數組中上一個鏈表節點的地址:

 struct uint64_zarray_entry *entry = clustermap[clustermap_bucket];/*新建鏈表節點地址便是上一個對應桶中鏈表節點的地址*/ 

每次開始前會判斷當前的邊界聚類ID是不是和當前對應數組/桶裏存儲的鏈表節點裏存儲的ID是否一致,也就是檢索當前的ID值以前是否存儲過,在程序裏會逐個和之前的clusterid對比,若不等於,則entry繼續指向該數組下的上一個鏈表地址,直到找到對應的聚類ID(此時entry指向上一個節點,下面會進行地址遞增)或者entry指向空(表示找完了都沒有),此時纔會進行下一步:

while (entry && entry->id != clusterid) {           \
                            entry = entry->next;
                            }

最後,對於每個不爲空的鏈表節點,首先更新當前的鏈表節點地址,也就是疊加鏈表元素大小的地址偏移:

if (mem_pool_loc == mem_chunk_size) {           \
                                mem_pool_loc = 0;                           \
                                mem_pool_idx++;                             \
                                mem_pools[mem_pool_idx] = calloc(mem_chunk_size, sizeof(struct uint64_zarray_entry)); \
                            }                                               \
                            /*鏈表節點地址遞增*/  \
                            entry = mem_pools[mem_pool_idx] + mem_pool_loc; \
                            mem_pool_loc++;         

將當前的“聚類ID,聚類點信息,當前鏈表節點的起始地址(數組索引)”添加到當前鏈表節點中:

 entry->id = clusterid; /*將邊界聚類id賦值,也就是哈希表的鍵值*/                         \
 entry->cluster = zarray_create(sizeof(struct pt));/*當前聚類信息*/ \
 entry->next = clustermap[clustermap_bucket];  /*哈希表的下一個存儲桶或者數組*/  \

然後,哈希表的當前索引位置數組clustermap[clustermap_bucket] 指向entry:

 clustermap[clustermap_bucket] = entry;  /*當前哈希表桶的入口地址存儲當前聚類的地址*/   

同時,不管當前的鏈表節點是否爲空,都更新當前的聚類像素點座標和梯度值,座標按照2倍關係存儲,梯度按照鄰接像素的位置存儲:

 struct pt p = { .x = 2*x + dx, .y = 2*y + dy, .gx = dx*((int) v1-v0), .gy = dy*((int) v1-v0)}; \
                        zarray_add(entry->cluster, &p);                     \

此時的entry和clustermap[clustermap_bucket]指向的是同一個地址。所以增加的像素點聚類信息也就是增加到了clustermap[clustermap_bucket]指向的鏈表節點中。

最後運行結束後,將clustermap存儲的信息,按順序存儲在clusters中,並返回。clusters便是聚類後的結果,並完全按照哈希表的方式存儲。

for (int i = 0; i < nclustermap; i++) {// 按桶遍歷哈希表,也就是遍歷每個聚類
        int start = zarray_size(clusters);
        // 遍歷哈希表中每個桶(每個聚類)裏的聚類點信息,並存儲進clusters
        for (struct uint64_zarray_entry *entry = clustermap[i]; entry; entry = entry->next) {
            struct cluster_hash* cluster_hash = malloc(sizeof(struct cluster_hash));
            cluster_hash->hash = u64hash_2(entry->id) % nclustermap;// 哈希表賦值
            cluster_hash->id = entry->id;
            cluster_hash->data = entry->cluster;
            zarray_add(clusters, &cluster_hash);
        }
        int end = zarray_size(clusters);

        // Do a quick bubblesort on the secondary key.
        // 對每個桶裏的ID按照冒泡排序
        int n = end - start;
        for (int j = 0; j < n - 1; j++) {
            for (int k = 0; k < n - j - 1; k++) {
                struct cluster_hash* hash1;
                struct cluster_hash* hash2;
                zarray_get(clusters, start + k, &hash1);// 將聚類裏第 start + k個檢索值賦值給hash1
                zarray_get(clusters, start + k + 1, &hash2);
                if (hash1->id > hash2->id) { // 若ID大於
                    struct cluster_hash tmp = *hash2; // 將兩個哈希數據位置交換
                    *hash2 = *hash1;
                    *hash1 = tmp;
                }
            }
        }
    }
        // 釋放內存
    for (int i = 0; i <= mem_pool_idx; i++) {
        free(mem_pools[i]);
    }
    free(clustermap);

    // 返回聚類結果
    return clusters;
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章