在上一篇博客中,遺留下來以下問題:
- 視差圖填充,視差圖格式,深度範圍限制(防止出現inf出現,導致視圖顯示不完整),視差圖轉色溫圖(爲了顯示好看)
- 另外,在保存視差圖的時候發現:視差圖的精度會受格式的影響,有的博主說可以將視差圖同意保存xml類型的數據。
- 還有SGBM算法是怎麼工作的?如何計算得到視差的,函數可以隨意調用,但是參數設置應該和原理有關,因此還是需要細緻學習一下
- 重構出的點那麼醜,有一些點看起來不屬於重構物體或者誤差看起來很大,考慮刪除等等……
本篇博主選擇自己感興趣或認爲有用的進行較爲細緻的梳理
視差圖的填充
真實場景的雙目立體匹配中提到了對視差圖進行填充,我主要也是參考此博客理解視差填充的原理。
其實視差填充不難理解:視差圖在計算出來之後,會出現或大或小的非匹配點,這些非匹配點(塊)會使重構結果不好看。於是我們就希望通過使用周邊“計算相對準確的視差”對非匹配點進行填充。
引用真實場景的雙目立體匹配對於視差填充的描述:
① 以視差圖dispImg爲例。計算圖像的積分圖integral,並保存對應積分圖中每個積分值處所有累加的像素點個數n(空洞處的像素點不計入n中,因爲空洞處像素值爲0,對積分值沒有任何作用,反而會平滑圖像)。
② 採用多層次均值濾波。首先以一個較大的初始窗口去做均值濾波(積分圖實現均值濾波就不多做介紹了,可以參考我之前的一篇博客),將大區域的空洞賦值。然後下次濾波時,將窗口尺寸縮小爲原來的一半,利用原來的積分圖再次濾波,給較小的空洞賦值(覆蓋原來的值);依次類推,直至窗口大小變爲3x3,此時停止濾波,得到最終結果。
③ 多層次濾波考慮的是對於初始較大的空洞區域,需要參考更多的鄰域值,如果採用較小的濾波窗口,不能夠完全填充,而如果全部採用較大的窗口,則圖像會被嚴重平滑。因此根據空洞的大小,不斷調整濾波窗口。先用大窗口給所有空洞賦值,然後利用逐漸變成小窗口濾波覆蓋原來的值,這樣既能保證空洞能被填充上,也能保證圖像不會被過度平滑。
博主突然想偷懶:記錄一下函數接口好了,depth就是我們希望對之填充的視差,我在自己的代碼中輸入的是經過類型轉換的視差圖,除以16,且轉化爲CV_32F類型的數據,用於下列函數:
void two_Eyes::insertDepth32f(cv::Mat& depth)
{
const int width = depth.cols;
const int height = depth.rows;
float* data = (float*)depth.data;
//uchar* data = (uchar*)depth.data;
cv::Mat integralMap = cv::Mat::zeros(height, width, CV_64F);
cv::Mat ptsMap = cv::Mat::zeros(height, width, CV_32S);
double* integral = (double*)integralMap.data;
int* ptsIntegral = (int*)ptsMap.data;
memset(integral, 0, sizeof(double) * width * height);
memset(ptsIntegral, 0, sizeof(int) * width * height);
for (int i = 0; i < height; ++i)
{
int id1 = i * width;
for (int j = 0; j < width; ++j)
{
int id2 = id1 + j;
if (data[id2] > 1e-3)
{
integral[id2] = data[id2];
ptsIntegral[id2] = 1;
}
}
}
// 積分區間
for (int i = 0; i < height; ++i)
{
int id1 = i * width;
for (int j = 1; j < width; ++j)
{
int id2 = id1 + j;
integral[id2] += integral[id2 - 1];
ptsIntegral[id2] += ptsIntegral[id2 - 1];
}
}
for (int i = 1; i < height; ++i)
{
int id1 = i * width;
for (int j = 0; j < width; ++j)
{
int id2 = id1 + j;
integral[id2] += integral[id2 - width];
ptsIntegral[id2] += ptsIntegral[id2 - width];
}
}
int wnd;
double dWnd = 2;
while (dWnd > 1)
{
wnd = int(dWnd);
dWnd /= 2;
for (int i = 0; i < height; ++i)
{
int id1 = i * width;
for (int j = 0; j < width; ++j)
{
int id2 = id1 + j;
int left = j - wnd - 1;
int right = j + wnd;
int top = i - wnd - 1;
int bot = i + wnd;
left = max(0, left);
right = min(right, width - 1);
top = max(0, top);
bot = min(bot, height - 1);
int dx = right - left;
int dy = (bot - top) * width;
int idLeftTop = top * width + left;
int idRightTop = idLeftTop + dx;
int idLeftBot = idLeftTop + dy;
int idRightBot = idLeftBot + dx;
int ptsCnt = ptsIntegral[idRightBot] + ptsIntegral[idLeftTop] - (ptsIntegral[idLeftBot] + ptsIntegral[idRightTop]);
double sumGray = integral[idRightBot] + integral[idLeftTop] - (integral[idLeftBot] + integral[idRightTop]);
if (ptsCnt <= 0)
{
continue;
}
data[id2] = float(sumGray / ptsCnt);
}
}
int s = wnd / 2 * 2 + 1;
if (s > 201)
{
s = 201;
}
cv::GaussianBlur(depth, depth, cv::Size(s, s), s, s);
}
}
視差圖的彩色顯示
彩色顯示視差能夠將視差顯示得更加明顯
disp8是之前記錄下來的標準視差(除以16之後的,函數來自網絡,我將其封裝到了two_eyes類裏)
將彩色視差圖輸出到disp8_color中。該函數其實就是對灰度圖disp8進行一次規定模式的RGB轉化。
void two_Eyes::GenerateFalseMap( cv::Mat &disp8_color)
{
// color map
if (disp8.channels() > 1)
cvtColor(disp8, disp8, CV_RGB2GRAY);
float max_val = 255.0f;
float map[8][4] = { { 0,0,0,114 },{ 0,0,1,185 },{ 1,0,0,114 },{ 1,0,1,174 },
{ 0,1,0,114 },{ 0,1,1,185 },{ 1,1,0,114 },{ 1,1,1,0 } };
float sum = 0;
for (int i = 0; i < 8; i++)
sum += map[i][3];
float weights[8]; // relative weights
float cumsum[8]; // cumulative weights
cumsum[0] = 0;
for (int i = 0; i < 7; i++) {
weights[i] = sum / map[i][3];
cumsum[i + 1] = cumsum[i] + map[i][3] / sum;
}
int height_ = disp8.rows;
int width_ = disp8.cols;
// for all pixels do
for (int v = 0; v < height_; v++) {
for (int u = 0; u < width_; u++) {
// get normalized value
float val = std::min(std::max(disp8.data[v*width_ + u] / max_val, 0.0f), 1.0f);
// find bin
int i;
for (i = 0; i < 7; i++)
if (val < cumsum[i + 1])
break;
// compute red/green/blue values
float w = 1.0 - (val - cumsum[i])*weights[i];
uchar r = (uchar)((w*map[i][0] + (1.0 - w)*map[i + 1][0]) * 255.0);
uchar g = (uchar)((w*map[i][1] + (1.0 - w)*map[i + 1][1]) * 255.0);
uchar b = (uchar)((w*map[i][2] + (1.0 - w)*map[i + 1][2]) * 255.0);
//rgb內存連續存放
disp8_color.data[v*width_ * 3 + 3 * u + 0] = b;
disp8_color.data[v*width_ * 3 + 3 * u + 1] = g;
disp8_color.data[v*width_ * 3 + 3 * u + 2] = r;
}
}
}
視差圖的保存
兩種觀點:視差圖可以保存爲xml類型的數據,另外一種觀點爲視差圖可以保存爲.png類型的圖片。
// write
Mat src = (Mat_<double>(3, 3) << 1, 2, 3, 4, 5, 6, 7, 8, 9);
FileStorage fswrite("test.xml", FileStorage::WRITE);// 新建文件,覆蓋掉已有文件
fswrite << "src1" << src;
fswrite.release();
cout << "Write Fineshed!" << endl;
// read
FileStorage fsread("test.xml", FileStorage::READ);
Mat dst;
fsread["src1"] >> dst; // 讀出src1節點裏的數據
cout << dst << endl;
fsread.release();
cout << "Read Finishied" << endl;
SGBM算法的理解
參考SGBM算法介紹比較詳細的博客
一直待在草稿裏,今天發了吧。。。