由于不同视角的遮挡或者错误的匹配,经过SGBM算法计算的视差图存在一些无效区。为了获得稠密的视差图,或者对关键点的无效视差进行近似,我们需要填充这些无效区。
这些乌漆嘛黑的区域就是需要填充的无效区
这里不讨论遮挡与误匹配的判定,有需要请移步大佬的博客:
【码上实战】【立体匹配系列】经典SGM:(6)视差填充
这篇博客也提供了视差填充的方法和代码,我这里提供原理上更简易(简陋)的实现
等角度往外发射8条射线进行采样,计算均值
原理很简单,直接放代码,就是写的很丑。。。
#include <opencv2/opencv.hpp>
#include <iostream>
#include <vector>
using namespace std;
int main()
{
cv::Mat image = cv::imread("../disparity/000000.png", 0);
const int width = image.cols;
const int height = image.rows;
cv::Mat newImage = cv::Mat::zeros(cv::Size(width, height), CV_8UC1);
std::vector<float> disp_collects;
for(int col = 0; col < width; ++col)
{
for(int row = 0; row < height; ++row)
{
// cout << "col: " << col << ", row: " << row << endl;
if(image.at<uchar>(row, col) == 0)
{
int usefulPoint = 0, sumPixel = 0;
int v = col, u = row;
// 0
while(v < width)
{
++v;
if(image.at<uchar>(u, v) > 0)
{
sumPixel += image.at<uchar>(u, v);
++usefulPoint;
break;
}
}
// pi/4
v = col, u = row;
while(v < width && u < height)
{
++v;
++u;
if(image.at<uchar>(u, v) > 0)
{
sumPixel += image.at<uchar>(u, v);
++usefulPoint;
break;
}
}
// pi/2
v = col, u = row;
while(u < height)
{
++u;
if(image.at<uchar>(u, v) > 0)
{
sumPixel += image.at<uchar>(u, v);
++usefulPoint;
break;
}
}
// pi*3/4
v = col, u = row;
while(v > 0 && u < height)
{
--v;
++u;
if(image.at<uchar>(u, v) > 0)
{
sumPixel += image.at<uchar>(u, v);
++usefulPoint;
break;
}
}
// pi
v = col, u = row;
while(v > 0)
{
--v;
if(image.at<uchar>(u, v) > 0)
{
sumPixel += image.at<uchar>(u, v);
++usefulPoint;
break;
}
}
// pi*5/4
v = col, u = row;
while(v > 0 && u > 0)
{
--v;
--u;
if(image.at<uchar>(u, v) > 0)
{
sumPixel += image.at<uchar>(u, v);
++usefulPoint;
break;
}
}
// pi*3/2
v = col, u = row;
while(u > 0)
{
--u;
if(image.at<uchar>(u, v) > 0)
{
sumPixel += image.at<uchar>(u, v);
++usefulPoint;
break;
}
}
// pi*7/4
v = col, u = row;
while(v < width && u > 0)
{
++v;
--u;
if(image.at<uchar>(u, v) > 0)
{
sumPixel += image.at<uchar>(u, v);
++usefulPoint;
break;
}
}
// cout << sumPixel / usefulPoint << " and " << (unsigned char)(sumPixel / usefulPoint) << endl;
newImage.at<uchar>(row, col) = (unsigned char)(sumPixel / usefulPoint);
}
else newImage.at<uchar>(row, col) = image.at<uchar>(row, col);
}
}
cv::Mat saveImage;
cv::medianBlur(newImage, saveImage, 3);
cv::imwrite("../test.png", saveImage);
return 0;
}
这是用这种方法填充的效果,一片原本的无效区域内的值都差不多,缺乏过渡
等角度往外发射8条射线进行采样,进行线性插值
为了获取有更好过渡的视差填充,可以在采样后做4次简单的单线性插值,再算平均值
首先把8个方向的射线分为4组,0和π,π/4和5π/4,π/2和3π/2, 3π/4和7π/4
这4组方向都在一条直线上,对这条直线的两个端点进行采样插值,可以使填充的过渡效果更好一些
这里再简单推导一下单线性插值的算法:
代码如下:
#include <opencv2/opencv.hpp>
#include <iostream>
#include <vector>
#include "math.h"
using namespace std;
int main()
{
cv::Mat image = cv::imread("../disparity/000000.png", 0);
const int width = image.cols;
const int height = image.rows;
cv::Mat newImage = cv::Mat::zeros(cv::Size(width, height), CV_8UC1);
bool flag = false;
for(int col = 0; col < width; ++col)
{
for(int row = 0; row < height; ++row)
{
cout << "col: " << col << ", row: " << row << endl;
if(image.at<uchar>(row, col) == 0)
{
double sumPixel = 0;
double x;
int v = col, u = row;
int y_1, y_2, x_length;
int coordX_1, coordX_2, coordY_1, coordY_2;
// Group 1 --------------------------------------------------------------------------------------
// 0
flag = false;
while(v < width)
{
++v;
if(image.at<uchar>(u, v) > 0)
{
y_1 = image.at<uchar>(u, v);
flag = true;
break;
}
}
if(!flag) y_1 = 0;
coordX_1 = v;
coordY_1 = u;
// pi
flag = false;
v = col, u = row;
while(v > 0)
{
--v;
if(image.at<uchar>(u, v) > 0)
{
y_2 = image.at<uchar>(u, v);
flag = true;
break;
}
}
if(!flag) y_2 = 0;
coordX_2 = v;
coordY_2 = u;
// compute line 1
x_length = sqrt(pow((coordX_2 - coordX_1), 2) + pow((coordY_2 - coordY_1), 2));
x = sqrt(pow((col - coordX_1), 2) + pow((row - coordY_1), 2)) / x_length;
sumPixel += (1 - x) * (y_1 - y_2) + y_2;
// cout << "col: " << col << ", row: " << row << ", coordX_1: " << coordX_1 << ", coordY_1: " << coordY_1 << ", coordX_2: " << coordX_2 << ", coordY_2: " << coordY_2 << endl;
// cout << "x_length: " << x_length << ", x: " << x << ", y_1: " << y_1 << ", y_2: " << y_2 << endl;
// cout << "result: " << (1 - x) * (y_1 - y_2) + y_2 << endl;
// Group 2 --------------------------------------------------------------------------------------
// pi/4
flag = false;
v = col, u = row;
while(v < width && u < height)
{
++v;
++u;
if(image.at<uchar>(u, v) > 0)
{
y_1 = image.at<uchar>(u, v);
flag = true;
break;
}
}
if(!flag) y_1 = 0;
coordX_1 = v;
coordY_1 = u;
// pi*5/4
flag = false;
v = col, u = row;
while(v > 0 && u > 0)
{
--v;
--u;
if(image.at<uchar>(u, v) > 0)
{
y_2 = image.at<uchar>(u, v);
flag = true;
break;
}
}
if(!flag) y_2 = 0;
coordX_2 = v;
coordY_2 = u;
// compute line 2
x_length = sqrt(pow((coordX_2 - coordX_1), 2) + pow((coordY_2 - coordY_1), 2));
x = sqrt(pow((col - coordX_1), 2) + pow((row - coordY_1), 2)) / x_length;
sumPixel += (1 - x) * (y_1 - y_2) + y_2;
// Group 3 --------------------------------------------------------------------------------------
// pi/2
flag = false;
v = col, u = row;
while(u < height)
{
++u;
if(image.at<uchar>(u, v) > 0)
{
y_1 = image.at<uchar>(u, v);
flag = true;
break;
}
}
if(!flag) y_1 = 0;
coordX_1 = v;
coordY_1 = u;
// pi*3/2
flag = false;
v = col, u = row;
while(u > 0)
{
--u;
if(image.at<uchar>(u, v) > 0)
{
y_2 = image.at<uchar>(u, v);
flag = true;
break;
}
}
if(!flag) y_2 = 0;
coordX_2 = v;
coordY_2 = u;
// compute line 3
x_length = sqrt(pow((coordX_2 - coordX_1), 2) + pow((coordY_2 - coordY_1), 2));
x = sqrt(pow((col - coordX_1), 2) + pow((row - coordY_1), 2)) / x_length;
sumPixel += (1 - x) * (y_1 - y_2) + y_2;
// Group 4 --------------------------------------------------------------------------------------
// pi*3/4
v = col, u = row;
flag = false;
while(v > 0 && u < height)
{
--v;
++u;
if(image.at<uchar>(u, v) > 0)
{
y_1 = image.at<uchar>(u, v);
flag = true;
break;
}
}
if(!flag) y_1 = 0;
coordX_1 = v;
coordY_1 = u;
// pi*7/4
v = col, u = row;
flag = false;
while(v < width && u > 0)
{
++v;
--u;
if(image.at<uchar>(u, v) > 0)
{
y_2 = image.at<uchar>(u, v);
flag = true;
break;
}
}
if(!flag) y_2 = 0;
coordX_2 = v;
coordY_2 = u;
// compute line 4
x_length = sqrt(pow((coordX_2 - coordX_1), 2) + pow((coordY_2 - coordY_1), 2));
x = sqrt(pow((col - coordX_1), 2) + pow((row - coordY_1), 2)) / x_length;
sumPixel += (1 - x) * (y_1 - y_2) + y_2;
// cout << sumPixel / usefulPoint << " and " << (unsigned char)(sumPixel / usefulPoint) << endl;
newImage.at<uchar>(row, col) = (unsigned char)(sumPixel / 4);
}
else newImage.at<uchar>(row, col) = image.at<uchar>(row, col);
}
}
cv::Mat saveImage;
cv::medianBlur(newImage, saveImage, 3);
cv::imwrite("../test.png", saveImage);
return 0;
}
效果如下:
可以看到过渡好了一些