代价计算与视差计算

上次实现了census变换和计算Hamming距离后,就可以得到像素的二进制编码位串,并对两个编码过的像素(实际上是编码过的位串)进行Hamming距离计算,以计算他们在某种意义上的相似性。有了他俩的帮助,就能进行代价计算和视差计算了。

代价计算

代价计算的默认前提是两副图像已经经过极线矫正了,这样才能使图像尽量达到我们想要的理想情况,即像素只有横向上的位移。代价计算的算法也是基于这个前提的。正是由于有一个横向位移,所以需要指定一个视差范围,在这个视差范围内寻找最为匹配的像素。这里我先搬来了李博的代码,对着代码分析是如何计算代价的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
void SemiGlobalMatching::ComputeCost() const {
const sint32& min_disparity = option_.min_disparity;
const sint32& max_disparity = option_.max_disparity;
const sint32 disp_range = max_disparity - min_disparity;

// 计算代价(基于Hamming距离)
for (sint32 i = 0; i < height_; i++) {
for (sint32 j = 0; j < width_; j++) {
// 逐视差计算代价值
for (sint32 d = min_disparity; d < max_disparity; d++) {
// cost_init_是视差空间图像,cost是单个视差的引用
auto& cost = cost_init_[i * width_ * disp_range + j * disp_range + (d - min_disparity)];
// j-d 代表行上的索引,超出索引范围后,为其赋值为 127,表示灰度中值
if (j - d < 0 || j - d >= width_) {
cost = UINT8_MAX/2;
continue;
}
// 左影像census值
const auto& census_val_l = static_cast<uint32*>(census_left_)[i * width_ + j];
// 右影像对应像点的census值
const auto& census_val_r = static_cast<uint32*>(census_right_)[i * width_ + j - d];

// 计算匹配代价,以左图为基准,右图中对应位置的像素点以及这一点所在行的前d个像素进行对比,获得第三个维度的多个视差
cost = sgm_util::Hamming32(census_val_l, census_val_r);
}
}
}
}

比如指定视差范围为0~64,以左图为基准,使用右图对应位置的像素点以及这一点所在行的前d个像素进行对比,获得第三个维度的64个视差,if (j - d < 0 || j - d >= width_)用来处理边缘那些超出图像索引范围的像素,给他们赋予灰度中值,从而可以得到一个视差空间图像(DSI)
gQlvRg.png

得到这个视差空间图像后,就可以进行视差计算了:

视差计算

视差计算采用赢家通吃(WTA)算法,很简单的,就是对单个像素的64个代价值求其最小值,作为这个像素点最终的视差值:

ghRLF0.png

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
void SemiGlobalMatching::ComputeDisparity() const {
// 最小最大视差
const sint32& min_disparity = option_.min_disparity;
const sint32& max_disparity = option_.max_disparity;
const sint32 disp_range = max_disparity - min_disparity;

const sint32 width = width_;
const sint32 height = height_;
// 未实现聚合步骤,暂用初始代价值来代替
auto cost_ptr = cost_init_;

// 逐像素计算最优视差
for (sint32 i = 0; i < height_; i++) {
for (sint32 j = 0; j < width_; j++) {

uint16 min_cost = UINT16_MAX;
uint16 max_cost = 0;
sint32 best_disparity = 0;

// 遍历视差范围内的所有代价值,输出最小代价值及对应的视差值
for (sint32 d = min_disparity; d < max_disparity; d++) {
const sint32 d_idx = d - min_disparity; // 视差索引
const auto& cost = cost_ptr[i * width * disp_range + j * disp_range + d_idx];
if(min_cost > cost) {
min_cost = cost;
best_disparity = d;
}
max_cost = std::max(max_cost, static_cast<uint16>(cost));
}

// 最小代价值对应的视差值即为像素的最优视差
if (max_cost != min_cost) {
disp_left_[i * width_ + j] = static_cast<float>(best_disparity);
}
else {
// 如果所有视差下的代价值都一样,则该像素无效
disp_left_[i * width_ + j] = Invalid_Float;
}
}
}
}

这样就有了一个完整可运行的框架,剩下的就只是优化工作了,运行主函数可以获得实验结果:

g4QFy9.png

1. 【理论恒叨】【立体匹配系列】经典SGM:(4)视差计算、视差优化
2. 【码上实战】【立体匹配系列】经典SGM:(2)代价计算