最近,研究了下CVPR2014上的一篇基于多尺度代价聚合的立体匹配算法,这个作者提供了原代码,运行了下,发现效果真心不错,不开后端处理的话,时间在0.4s左右。这个算法比较牛逼的有两点:
1:结合多尺度思想,对原始图像进行下采样,然后在每层图像上计算匹配代价,进行代价聚合,然后多尺度得到的视差进行结合,作为最终的代价聚合值。
2:提供了一个框架,里面包含立体匹配很多常用的经典的方法,可以在每一步使用不同的方式完成。
参考一篇博客:http://blog.youkuaiyun.com/wsj998689aa/article/details/44411215
本文结合源码,对这个算法的流程进行详细分析。
首先进行匹配代价计算:
源码里有三种方法,CEN(就是census),CG(Census + TAD + 梯度),GRD(梯度+TAD) ,这三种方式几乎是当下进行第一步匹配代价计算的常用方式。
1:CEN:选定一个窗口,对窗口中每一个像素与中心像素进行比较,大于中心像素即为0,否则为1。从而得到一个二进制系列,分别对左图和右图进行计算每个像素的匹配代价。并得到初步的代价聚合,计算每个像素在视差范围内每个可能视差的代价聚合值。左右匹配,即两个二进制系列相似值,每个对应位是否相等。
void CenCC::buildCV( const Mat& lImg, const Mat& rImg, const int maxDis, Mat* costVol )
{
// for TAD + Grd input image must be CV_64FC3//输入的图像必须是64FC3
CV_Assert( lImg.type() == CV_64FC3 && rImg.type() == CV_64FC3 );
int hei = lImg.rows;//图像宽高
int wid = lImg.cols;
Mat lGray, rGray;
Mat tmp;
lImg.convertTo( tmp, CV_32F );//64FC3图像数据转化成32F
cvtColor( tmp, lGray, CV_RGB2GRAY );//灰度化
lGray.convertTo( lGray, CV_8U, 255 );//灰度图像转换成8U数据格式,并把开始除的255乘回来
rImg.convertTo( tmp, CV_32F );
cvtColor( tmp, rGray, CV_RGB2GRAY );
rGray.convertTo( rGray, CV_8U, 255 );
// prepare binary code
int H_WD = CENCUS_WND / 2;
bitset<CENCUS_BIT>* lCode = new bitset<CENCUS_BIT>[ wid * hei ];
bitset<CENCUS_BIT>* rCode = new bitset<CENCUS_BIT>[ wid * hei ];
bitset<CENCUS_BIT>* pLCode = lCode;
bitset<CENCUS_BIT>* pRCode = rCode;
for( int y = 0; y < hei; y ++ ) { //求得中心像素的CENSUS码
uchar* pLData = ( uchar* ) ( lGray.ptr<uchar>( y ) );
uchar* pRData = ( uchar* ) ( rGray.ptr<uchar>( y ) );
for( int x = 0; x < wid; x ++ ) {
int bitCnt = 0;
for( int wy = - H_WD; wy <= H_WD; wy ++ ) {
int qy = ( y + wy + hei ) % hei;
uchar* qLData = ( uchar* ) ( lGray.ptr<uchar>( qy ) );
uchar* qRData = ( uchar* ) ( rGray.ptr<uchar>( qy ) );
for( int wx = - H_WD; wx <= H_WD; wx ++ ) {
if( wy != 0 || wx != 0 ) {
int qx = ( x + wx + wid ) % wid;
( *pLCode )[ bitCnt ] = ( pLData[ x ] > qLData[ qx ] );//这里是对窗口内的每个像素与中心像素比较,小于则为1,大于则为0
( *pRCode )[ bitCnt ] = ( pRData[ x ] > qRData[ qx ] );
bitCnt ++;
}
}
}
pLCode ++;
pRCode ++;
}
}
// build cost volume 初始代价聚合
bitset<CENCUS_BIT> lB;
bitset<CENCUS_BIT> rB;
pLCode = lCode;
for( int y = 0; y < hei; y ++ ) {
int index = y * wid;
for( int x = 0; x < wid; x ++ ) {
lB = *pLCode;
for( int d = 0; d < maxDis; d ++ ) {
double* cost = ( double* ) costVol[ d ].ptr<double>( y );//costVol是视差为d时的匹配代价Mat集合
cost[ x ] = CENCUS_BIT;
if( x - d >= 0 ) {
rB = rCode[ index + x - d ];
cost[ x ] = ( lB ^ rB ).count();//这里是求左右图的census值的相似性,作为视差为d时,当前像素的匹配代价。
}
}
pLCode ++;
}
}
delete [] lCode;
delete [] rCode;
}
下面是从右向左进行匹配的,就是在寻找视差方向上时不同而已
// build cost volume
bitset<CENCUS_BIT> lB;
bitset<CENCUS_BIT> rB;
pRCode = rCode;
for( int y = 0; y < hei; y ++ ) {
int index = y * wid;
for( int x = 0; x < wid; x ++ ) {
rB = *pRCode;
for(