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;
  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值