Bootstrap

AD-Census代码的理解(十字臂交叉聚合)

首先引用一下李博的AD-Census

https://ethanli.blog.csdn.net/article/details/108876143

在看AD-Census十字交叉臂代价聚合的部分时产生了一些困惑,解惑以后在此贴写下笔记方便日后复习。问题就在于以下这段代码:

void CrossAggregator::ComputeSubPixelCount()
{
	//计算每个像素的支持区像素数量
	//注意两种不同的聚合方向,像素的支持区像素是不同的,需要分开计算
	/*下面有多层循环,
	* k = 0为左右,k = 1 为上下
	*/
	bool horizontal_first = true;
	for (sint32 n = 0; n < 2; n++)
	{
		const sint32 id = horizontal_first ? 0 : 1;
		for (sint32 k = 0; k < 2; k++) {
			for (sint32 y = 0; y < height_; y++) {
				for (sint32 x = 0; x < width_; x++)
				{
					auto& arm = vec_cross_arms_[y * width_ + x];
					sint32 count = 0;
					if (horizontal_first)
					{
						if (k == 0) {
							for (sint32 t = -arm.left; t <= arm.right; t++)
								count++;
						}
						else {
							for (sint32 t = -arm.top; t <= arm.bottom; t++)
								count += vec_sup_count_tmp_[(y + t) * width_ + x];
						}
					}
					else 
					{
						if (k == 0) {
							for (sint32 t = -arm.top; t <= arm.bottom; t++)
								count++;
						}
						else {
							for (sint32 t = -arm.left; t <= arm.right; t++) {
								count += vec_sup_count_tmp_[y * width_ + x + t];
							}
						}
					}

					if (k == 0)
						vec_sup_count_tmp_[y * width_ + x] = count;
					else
						vec_sup_count_[id][y * width_ + x] = count;
				}
			}
		}
		horizontal_first = !horizontal_first;
	}
}

以上的代码是为了数出整个区域的像素数量,方便后面作除数。文章中有这么一个图:
https://ethanli.blog.csdn.net/article/details/108876143在看原理的时候,不自觉认为,每个像素应当单独计算其区域。却忘了每个像素的延伸规则是相同的,完全可以重复利用,省下了很多计算资源。也就是说,在聚合前那一步,只要首先把所有像素的十字方向交叉臂求出来了,后面调用就行了。
在代码中的体现如下:

for (sint32 t = -arm.top; t <= arm.bottom; t++)
		count += vec_sup_count_tmp_[(y + t) * width_ + x];

即从上到下搜索每个像素的横向臂。

随后就是代价聚合了。

void CrossAggregator::AggregateInArms(const sint32& disparity, const bool& horizontal_first)
{
	//此函数聚合所有像素当视差为disparity时的代价
	if (disparity < min_disparity_ || disparity >= max_disparity_)
		return;
	const auto disp = disparity - min_disparity_;
	const sint32 disp_range = max_disparity_ - min_disparity_;
	if (disp_range <= 0) 
		return;

	//将disp层的代价导入局部数组vec_cost_tmp_[0]
	//可以避免过多访问更大的cost_aggr_,提高访问效率
	for (sint32 y = 0; y < height_; y++) {
		for (sint32 x = 0; x < width_; x++)
		{
			//提取每个像素的视差d下的代价值
			vec_cost_tmp_[0][y * width_ + x] = cost_aggr_[y * width_ * disp_range + x * disp_range + disp];
		}
	}

	//逐像素聚合
	const sint32 ct_id = horizontal_first ? 0 : 1;
	for (sint32 k = 0; k < 2; k++) {
		for (sint32 y = 0; y < height_; y++) {
			for (sint32 x = 0; x < width_; x++)
			{
				//获取arm数值
				auto& arm = vec_cross_arms_[y * width_ + x];
				//开始聚合
				float32 cost = 0.0f;
				if (horizontal_first) {
					if (k == 0) {
						for (sint32 t = -arm.left; t <= arm.right; t++)
							cost += vec_cost_tmp_[0][y * width_ + x + t];
					}
					else {
						for (sint32 t = -arm.top; t <= arm.bottom; t++) {
							cost += vec_cost_tmp_[1][(y + t) * width_ + x];
						}
					}
				}
				else {
					//竖直
					if (k == 0) {
						for (sint32 t = -arm.top; t <= arm.bottom; t++)
							cost += vec_cost_tmp_[0][(y + t) * width_ + x];
					}
					else {
						for (sint32 t = -arm.left; t <= arm.right; t++)
							cost += vec_cost_tmp_[1][y * width_ + x + t];
					}
				}
				if (k == 0)
					vec_cost_tmp_[1][y * width_ + x] = cost;
				else
					cost_aggr_[y * width_ * disp_range + x * disp_range + disp] = cost / vec_sup_count_[ct_id][y*width_+x];
			}
		}
	}
}

上面这段代码的总体思想如下图:
https://ethanli.blog.csdn.net/article/details/108876143
首先对每个像素做一个方向的聚合,再对每个像素的另一个方向做聚合
但是这里有个隐藏条件,就是该像素十字交叉臂区域内的像素视差相同!
在这里插入图片描述
如上图所示,每个像素在某个视差d下由其相邻像素的代价值聚合而成,此时其相邻像素的视差值也为d。以此计算得到视差范围内的所有代价值,最后从中选出最小代价值所对应的视差作为该像素的视差值。

在这里插入图片描述
那么代价值是如何随视差变化的呢?因为在某个视差下,某些像素的代价值可能比较高,而某些像素又比较低。因此选取最优视差值的过程,其实是找到了该像素在某个视差值下,其邻域像素的代价值之和最小。

;