转自https://www.cnblogs.com/riddick/p/8318997.html
以OpenCV自带的Aloe图像对为例:
1.BM算法(Block Matching)
参数设置如下:
int numberOfDisparities = ((imgSize.width / 8) + 15) & -16;
cv::Ptr<cv::StereoBM> bm = cv::StereoBM::create(16, 9);
cv::Rect roi1, roi2;
bm->setROI1(roi1);
bm->setROI2(roi2);
bm->setPreFilterCap(31);
bm->setBlockSize(9);
bm->setMinDisparity(0);
bm->setNumDisparities(numberOfDisparities);
bm->setTextureThreshold(10);
bm->setUniquenessRatio(15);
bm->setSpeckleWindowSize(100);
bm->setSpeckleRange(32);
bm->setDisp12MaxDiff(1);
bm->compute(imgL, imgR, disp);
效果如下:
BM算法得到的视差图(左),空洞填充后得到的视差图(右)
2.SGBM(Semi-Global Block matching)算法:
参数设置如下:
enum { STEREO_BM = 0, STEREO_SGBM = 1, STEREO_HH = 2, STEREO_VAR = 3, STEREO_3WAY = 4 };
int numberOfDisparities = ((imgSize.width / 8) + 15) & -16;
cv::Ptr<cv::StereoSGBM> sgbm = cv::StereoSGBM::create(0, 16, 3);
sgbm->setPreFilterCap(63);
int SADWindowSize = 9;
int sgbmWinSize = SADWindowSize > 0 ? SADWindowSize : 3;
sgbm->setBlockSize(sgbmWinSize);
int cn = imgL.channels();
sgbm->setP1(8 * cn*sgbmWinSize*sgbmWinSize);
sgbm->setP2(32 * cn*sgbmWinSize*sgbmWinSize);
sgbm->setMinDisparity(0);
sgbm->setNumDisparities(numberOfDisparities);
sgbm->setUniquenessRatio(10);
sgbm->setSpeckleWindowSize(100);
sgbm->setSpeckleRange(32);
sgbm->setDisp12MaxDiff(1);
</span><span style="color: #0000ff">int</span> alg =<span style="color: #000000"> STEREO_SGBM;
</span><span style="color: #0000ff">if</span> (alg ==<span style="color: #000000"> STEREO_HH)
sgbm</span>-><span style="color: #000000">setMode(cv::StereoSGBM::MODE_HH);
</span><span style="color: #0000ff">else</span> <span style="color: #0000ff">if</span> (alg ==<span style="color: #000000"> STEREO_SGBM)
sgbm</span>-><span style="color: #000000">setMode(cv::StereoSGBM::MODE_SGBM);
</span><span style="color: #0000ff">else</span> <span style="color: #0000ff">if</span> (alg ==<span style="color: #000000"> STEREO_3WAY)
sgbm</span>-><span style="color: #000000">setMode(cv::StereoSGBM::MODE_SGBM_3WAY);
sgbm</span>->compute(imgL, imgR, disp);</pre>
效果如图:
SGBM算法得到的视差图(左),空洞填充后得到的视差图(右)
可见SGBM算法得到的视差图相比于BM算法来说,减少了很多不准确的匹配点,尤其是在深度不连续区域,速度上SGBM要慢于BM算法。OpenCV3.0以后没有实现GC算法,可能是出于速度考虑,以后找时间补上对比图,以及各个算法的详细原理分析。
后面我填充空洞的效果不是很好,如果有更好的方法,望不吝赐教。
preFilterCap()匹配图像预处理
两种立体匹配算法都要先对输入图像做预处理,OpenCV源码中中调用函数 static void prefilterXSobel(const cv::Mat& src, cv::Mat& dst, int preFilterCap),参数设置中preFilterCap在此函数中用到。函数步骤如下,作用主要有两点:对于无纹理区域,能够排除噪声干扰;对于边界区域,能够提高边界的区分性,利于后续的匹配代价计算:
先利用水平Sobel算子求输入图像x方向的微分值Value; 如果Value<-preFilterCap, 则Value=0; 如果Value>preFilterCap,则Value=2*preFilterCap; 如果Value>=-preFilterCap &&Value<=preFilterCap,则Value=Value+preFilterCap; 输出处理后的图像作为下一步计算匹配代价的输入图像。
static void prefilterXSobel(const cv::Mat& src, cv::Mat& dst, int ftzero)
{
int x, y;
const int OFS = 256 * 4, TABSZ = OFS * 2 + 256;
uchar tab[TABSZ];
cv::Size size = src.size();
</span><span style="color: #0000ff">for</span> (x = <span style="color: #800080">0</span>; x < TABSZ; x++<span style="color: #000000">)
tab[x] </span>= (uchar)(x - OFS < -ftzero ? <span style="color: #800080">0</span> : x - OFS > ftzero ? ftzero * <span style="color: #800080">2</span> : x - OFS +<span style="color: #000000"> ftzero);
uchar val0 </span>= tab[<span style="color: #800080">0</span> +<span style="color: #000000"> OFS];
</span><span style="color: #0000ff">for</span> (y = <span style="color: #800080">0</span>; y < size.height - <span style="color: #800080">1</span>; y += <span style="color: #800080">2</span><span style="color: #000000">)
{
</span><span style="color: #0000ff">const</span> uchar* srow1 = src.ptr<uchar><span style="color: #000000">(y);
</span><span style="color: #0000ff">const</span> uchar* srow0 = y > <span style="color: #800080">0</span> ? srow1 - src.step : size.height > <span style="color: #800080">1</span> ? srow1 +<span style="color: #000000"> src.step : srow1;
</span><span style="color: #0000ff">const</span> uchar* srow2 = y < size.height - <span style="color: #800080">1</span> ? srow1 + src.step : size.height > <span style="color: #800080">1</span> ? srow1 -<span style="color: #000000"> src.step : srow1;
</span><span style="color: #0000ff">const</span> uchar* srow3 = y < size.height - <span style="color: #800080">2</span> ? srow1 + src.step * <span style="color: #800080">2</span><span style="color: #000000"> : srow1;
uchar</span>* dptr0 = dst.ptr<uchar><span style="color: #000000">(y);
uchar</span>* dptr1 = dptr0 +<span style="color: #000000"> dst.step;
dptr0[</span><span style="color: #800080">0</span>] = dptr0[size.width - <span style="color: #800080">1</span>] = dptr1[<span style="color: #800080">0</span>] = dptr1[size.width - <span style="color: #800080">1</span>] =<span style="color: #000000"> val0;
x </span>= <span style="color: #800080">1</span><span style="color: #000000">;
</span><span style="color: #0000ff">for</span> (; x < size.width - <span style="color: #800080">1</span>; x++<span style="color: #000000">)
{
</span><span style="color: #0000ff">int</span> d0 = srow0[x + <span style="color: #800080">1</span>] - srow0[x - <span style="color: #800080">1</span>], d1 = srow1[x + <span style="color: #800080">1</span>] - srow1[x - <span style="color: #800080">1</span><span style="color: #000000">],
d2 </span>= srow2[x + <span style="color: #800080">1</span>] - srow2[x - <span style="color: #800080">1</span>], d3 = srow3[x + <span style="color: #800080">1</span>] - srow3[x - <span style="color: #800080">1</span><span style="color: #000000">];
</span><span style="color: #0000ff">int</span> v0 = tab[d0 + d1 * <span style="color: #800080">2</span> + d2 +<span style="color: #000000"> OFS];
</span><span style="color: #0000ff">int</span> v1 = tab[d1 + d2 * <span style="color: #800080">2</span> + d3 +<span style="color: #000000"> OFS];
dptr0[x] </span>=<span style="color: #000000"> (uchar)v0;
dptr1[x] </span>=<span style="color: #000000"> (uchar)v1;
}
}
</span><span style="color: #0000ff">for</span> (; y < size.height; y++<span style="color: #000000">)
{
uchar</span>* dptr = dst.ptr<uchar><span style="color: #000000">(y);
x </span>= <span style="color: #800080">0</span><span style="color: #000000">;
</span><span style="color: #0000ff">for</span> (; x < size.width; x++<span style="color: #000000">)
dptr[x] </span>=<span style="color: #000000"> val0;
}
}
View Code
自己实现的函数如下:
void mySobelX(cv::Mat srcImg, cv::Mat dstImg, int preFilterCap)
{
assert(srcImg.channels() == 1);
int radius = 1;
int width = srcImg.cols;
int height = srcImg.rows;
uchar *pSrcData = srcImg.data;
uchar *pDstData = dstImg.data;
for (int i = 0; i < height; i++)
{
for (int j = 0; j < width; j++)
{
int idx = i*width + j;
if (i >= radius && i < height - radius && j >= radius && j < width - radius)
{
int diff0 = pSrcData[(i - 1)*width + j + 1] - pSrcData[(i - 1)*width + j - 1];
int diff1 = pSrcData[i*width + j + 1] - pSrcData[i*width + j - 1];
int diff2 = pSrcData[(i + 1)*width + j + 1] - pSrcData[(i + 1)*width + j - 1];
</span><span style="color: #0000ff">int</span> value = diff0 + <span style="color: #800080">2</span> * diff1 +<span style="color: #000000"> diff2;
</span><span style="color: #0000ff">if</span> (value < -<span style="color: #000000">preFilterCap)
{
pDstData[idx] </span>= <span style="color: #800080">0</span><span style="color: #000000">;
}
</span><span style="color: #0000ff">else</span> <span style="color: #0000ff">if</span> (value >= -preFilterCap && value <=<span style="color: #000000"> preFilterCap)
{
pDstData[idx] </span>= uchar(value +<span style="color: #000000"> preFilterCap);
}
</span><span style="color: #0000ff">else</span><span style="color: #000000">
{
pDstData[idx] </span>= uchar(<span style="color: #800080">2</span> *<span style="color: #000000"> preFilterCap);
}
}
</span><span style="color: #0000ff">else</span><span style="color: #000000">
{
pDstData[idx] </span>= <span style="color: #800080">0</span><span style="color: #000000">;
}
}
}
}
View Code
函数输入,输出结果如图:
filterSpeckles()视差图后处理
两种立体匹配算法在算出初始视差图后会进行视差图后处理,包括中值滤波,连通域检测等。其中中值滤波能够有效去除视差图中孤立的噪点,而连通域检测能够检测出视差图中因噪声引起小团块(blob)。在BM和SGBM中都有speckleWindowSize和speckleRange这两个参数,speckleWindowSize是指设置检测出的连通域中像素点个数,也就是连通域的大小。speckleRange是指设置判断两个点是否属于同一个连通域的阈值条件。大概流程如下:
判断当前像素点四邻域的邻域点与当前像素点的差值diff,如果diff<speckRange,则表示该邻域点与当前像素点是一个连通域,设置一个标记。然后再以该邻域点为中心判断其四邻域点,步骤同上。直至某一像素点四邻域的点均不满足条件,则停止。 步骤1完成后,判断被标记的像素点个数count,如果像素点个数count<=speckleWindowSize,则说明该连通域是一个小团块(blob),则将当前像素点值设置为newValue(表示错误的视差值,newValue一般设置为负数或者0值)。否则,表示该连通域是个大团块,不做处理。同时建立标记值与是否为小团块的关系表rtype[label],rtype[label]为0,表示label值对应的像素点属于小团块,为1则不属于小团块。 处理下一个像素点时,先判断其是否已经被标记: 如果已经被标记,则根据关系表rtype[label]判断是否为小团块(blob),如果是,则直接将该像素值设置为newValue;如果不是,则不做处理。继续处理下一个像素。 如果没有被标记,则按照步骤1处理。 所有像素点处理后,满足条件的区域会被设置为newValue值,后续可以用空洞填充等方法重新估计其视差值。
OpenCV中有对应的API函数,void filterSpeckles(InputOutputArray img, double newVal, int maxSpeckleSize, double maxDiff, InputOutputArray buf=noArray() )
函数源码如下,使用时根据视差图或者深度图数据类型设置模板中的数据类型:
typedef cv::Point_<short> Point2s;
template <typename T> void filterSpecklesImpl(cv::Mat& img, int newVal, int maxSpeckleSize, int maxDiff, cv::Mat& _buf)
{
using namespace cv;
</span><span style="color: #0000ff">int</span> width = img.cols, height = img.rows, npixels = width*<span style="color: #000000">height;
size_t bufSize </span>= npixels*(<span style="color: #0000ff">int</span>)(<span style="color: #0000ff">sizeof</span>(Point2s) + <span style="color: #0000ff">sizeof</span>(<span style="color: #0000ff">int</span>) + <span style="color: #0000ff">sizeof</span><span style="color: #000000">(uchar));
</span><span style="color: #0000ff">if</span> (!_buf.isContinuous() || _buf.empty() || _buf.cols*_buf.rows*_buf.elemSize() <<span style="color: #000000"> bufSize)
_buf.create(</span><span style="color: #800080">1</span>, (<span style="color: #0000ff">int</span><span style="color: #000000">)bufSize, CV_8U);
uchar</span>* buf =<span style="color: #000000"> _buf.ptr();
</span><span style="color: #0000ff">int</span> i, j, dstep = (<span style="color: #0000ff">int</span>)(img.step / <span style="color: #0000ff">sizeof</span><span style="color: #000000">(T));
</span><span style="color: #0000ff">int</span>* labels = (<span style="color: #0000ff">int</span>*<span style="color: #000000">)buf;
buf </span>+= npixels * <span style="color: #0000ff">sizeof</span>(labels[<span style="color: #800080">0</span><span style="color: #000000">]);
Point2s</span>* wbuf = (Point2s*<span style="color: #000000">)buf;
buf </span>+= npixels * <span style="color: #0000ff">sizeof</span>(wbuf[<span style="color: #800080">0</span><span style="color: #000000">]);
uchar</span>* rtype = (uchar*<span style="color: #000000">)buf;
</span><span style="color: #0000ff">int</span> curlabel = <span style="color: #800080">0</span><span style="color: #000000">;
</span><span style="color: #008000">//</span><span style="color: #008000"> clear out label assignments</span>
memset(labels, <span style="color: #800080">0</span>, npixels * <span style="color: #0000ff">sizeof</span>(labels[<span style="color: #800080">0</span><span style="color: #000000">]));
</span><span style="color: #0000ff">for</span> (i = <span style="color: #800080">0</span>; i < height; i++<span style="color: #000000">)
{
T</span>* ds = img.ptr<T><span style="color: #000000">(i);
</span><span style="color: #0000ff">int</span>* ls = labels + width*<span style="color: #000000">i;
</span><span style="color: #0000ff">for</span> (j = <span style="color: #800080">0</span>; j < width; j++<span style="color: #000000">)
{
</span><span style="color: #0000ff">if</span> (ds[j] != newVal) <span style="color: #008000">//</span><span style="color: #008000"> not a bad disparity</span>
{ if (ls[j]) // has a label, check for bad label { if (rtype[ls[j]]) // small region, zero out disparity ds[j] = (T)newVal; } // no label, assign and propagate else { Point2s* ws = wbuf; // initialize wavefront Point2s p((short)j, (short)i); // current pixel curlabel++; // next label int count = 0; // current region size ls[j] = curlabel;
</span><span style="color: #008000">//</span><span style="color: #008000"> wavefront propagation</span>
<span style="color: #0000ff">while</span> (ws >= wbuf) <span style="color: #008000">//</span><span style="color: #008000"> wavefront not empty</span>
{ count++; // put neighbors onto wavefront T* dpp = &img.at<T>(p.y, p.x); //current pixel value T dp = dpp; int lpp = labels + width*p.y + p.x; //current label value
</span><span style="color: #008000">//</span><span style="color: #008000">bot</span>
<span style="color: #0000ff">if</span> (p.y < height - <span style="color: #800080">1</span> && !lpp[+width] && dpp[+dstep] != newVal && std::abs(dp - dpp[+dstep]) <=<span style="color: #000000"> maxDiff)
{
lpp[</span>+width] =<span style="color: #000000"> curlabel;
</span>*ws++ = Point2s(p.x, p.y + <span style="color: #800080">1</span><span style="color: #000000">);
}
</span><span style="color: #008000">//</span><span style="color: #008000">top</span>
<span style="color: #0000ff">if</span> (p.y > <span style="color: #800080">0</span> && !lpp[-width] && dpp[-dstep] != newVal && std::abs(dp - dpp[-dstep]) <=<span style="color: #000000"> maxDiff)
{
lpp[</span>-width] =<span style="color: #000000"> curlabel;
</span>*ws++ = Point2s(p.x, p.y - <span style="color: #800080">1</span><span style="color: #000000">);
}
</span><span style="color: #008000">//</span><span style="color: #008000">right</span>
<span style="color: #0000ff">if</span> (p.x < width - <span style="color: #800080">1</span> && !lpp[+<span style="color: #800080">1</span>] && dpp[+<span style="color: #800080">1</span>] != newVal && std::abs(dp - dpp[+<span style="color: #800080">1</span>]) <=<span style="color: #000000"> maxDiff)
{
lpp[</span>+<span style="color: #800080">1</span>] =<span style="color: #000000"> curlabel;
</span>*ws++ = Point2s(p.x + <span style="color: #800080">1</span><span style="color: #000000">, p.y);
}
</span><span style="color: #008000">//</span><span style="color: #008000">left</span>
<span style="color: #0000ff">if</span> (p.x > <span style="color: #800080">0</span> && !lpp[-<span style="color: #800080">1</span>] && dpp[-<span style="color: #800080">1</span>] != newVal && std::abs(dp - dpp[-<span style="color: #800080">1</span>]) <=<span style="color: #000000"> maxDiff)
{
lpp[</span>-<span style="color: #800080">1</span>] =<span style="color: #000000"> curlabel;
</span>*ws++ = Point2s(p.x - <span style="color: #800080">1</span><span style="color: #000000">, p.y);
}
</span><span style="color: #008000">//</span><span style="color: #008000"> pop most recent and propagate
</span><span style="color: #008000">//</span><span style="color: #008000"> NB: could try least recent, maybe better convergence</span>
p = *--<span style="color: #000000">ws;
}
</span><span style="color: #008000">//</span><span style="color: #008000"> assign label type</span>
<span style="color: #0000ff">if</span> (count <= maxSpeckleSize) <span style="color: #008000">//</span><span style="color: #008000"> speckle region</span>
{ rtype[ls[j]] = 1; // small region label ds[j] = (T)newVal; } else rtype[ls[j]] = 0; // large region label } } } } }
View Code
或者下面博主自己整理一遍的代码:
typedef cv::Point_<short> Point2s;
template <typename T> void myFilterSpeckles(cv::Mat &img, int newVal, int maxSpeckleSize, int maxDiff)
{
int width = img.cols;
int height = img.rows;
int imgSize = width*height;
int *pLabelBuf = (int*)malloc(sizeof(int)*imgSize);//标记值buffer
Point2s *pPointBuf = (Point2s*)malloc(sizeof(short)*imgSize);//点坐标buffer
uchar *pTypeBuf = (uchar*)malloc(sizeof(uchar)*imgSize);//blob判断标记buffer
//初始化Labelbuffer
int currentLabel = 0;
memset(pLabelBuf, 0, sizeof(int)*imgSize);
</span><span style="color: #0000ff">for</span> (<span style="color: #0000ff">int</span> i = <span style="color: #800080">0</span>; i < height; i++<span style="color: #000000">)
{
T </span>*pData = img.ptr<T><span style="color: #000000">(i);
</span><span style="color: #0000ff">int</span> *pLabel = pLabelBuf + width*<span style="color: #000000">i;
</span><span style="color: #0000ff">for</span> (<span style="color: #0000ff">int</span> j = <span style="color: #800080">0</span>; j < width; j++<span style="color: #000000">)
{
</span><span style="color: #0000ff">if</span> (pData[j] !=<span style="color: #000000"> newVal)
{
</span><span style="color: #0000ff">if</span><span style="color: #000000"> (pLabel[j])
{
</span><span style="color: #0000ff">if</span><span style="color: #000000"> (pTypeBuf[pLabel[j]])
{
pData[j] </span>=<span style="color: #000000"> (T)newVal;
}
}
</span><span style="color: #0000ff">else</span><span style="color: #000000">
{
Point2s </span>*pWave =<span style="color: #000000"> pPointBuf;
Point2s curPoint((T)j, (T)i);
currentLabel</span>++<span style="color: #000000">;
</span><span style="color: #0000ff">int</span> count = <span style="color: #800080">0</span><span style="color: #000000">;
pLabel[j] </span>=<span style="color: #000000"> currentLabel;
</span><span style="color: #0000ff">while</span> (pWave >=<span style="color: #000000"> pPointBuf)
{
count</span>++<span style="color: #000000">;
T </span>*pCurPos = &img.at<T><span style="color: #000000">(curPoint.y, curPoint.x);
T curValue </span>= *<span style="color: #000000">pCurPos;
</span><span style="color: #0000ff">int</span> *pCurLabel = pLabelBuf + width*curPoint.y +<span style="color: #000000"> curPoint.x;
</span><span style="color: #008000">//</span><span style="color: #008000">bot</span>
<span style="color: #0000ff">if</span> (curPoint.y < height - <span style="color: #800080">1</span> && !pCurLabel[+width] && pCurPos[+width] != newVal && abs(curValue - pCurPos[+width]) <=<span style="color: #000000"> maxDiff)
{
pCurLabel[</span>+width] =<span style="color: #000000"> currentLabel;
</span>*pWave++ = Point2s(curPoint.x, curPoint.y + <span style="color: #800080">1</span><span style="color: #000000">);
}
</span><span style="color: #008000">//</span><span style="color: #008000">top</span>
<span style="color: #0000ff">if</span> (curPoint.y > <span style="color: #800080">0</span> && !pCurLabel[-width] && pCurPos[-width] != newVal && abs(curValue - pCurPos[-width]) <=<span style="color: #000000"> maxDiff)
{
pCurLabel[</span>-width] =<span style="color: #000000"> currentLabel;
</span>*pWave++ = Point2s(curPoint.x, curPoint.y - <span style="color: #800080">1</span><span style="color: #000000">);
}
</span><span style="color: #008000">//</span><span style="color: #008000">right</span>
<span style="color: #0000ff">if</span> (curPoint.x < width-<span style="color: #800080">1</span> && !pCurLabel[+<span style="color: #800080">1</span>] && pCurPos[+<span style="color: #800080">1</span>] != newVal && abs(curValue - pCurPos[+<span style="color: #800080">1</span>]) <=<span style="color: #000000"> maxDiff)
{
pCurLabel[</span>+<span style="color: #800080">1</span>] =<span style="color: #000000"> currentLabel;
</span>*pWave++ = Point2s(curPoint.x + <span style="color: #800080">1</span><span style="color: #000000">, curPoint.y);
}
</span><span style="color: #008000">//</span><span style="color: #008000">left</span>
<span style="color: #0000ff">if</span> (curPoint.x > <span style="color: #800080">0</span> && !pCurLabel[-<span style="color: #800080">1</span>] && pCurPos[-<span style="color: #800080">1</span>] != newVal && abs(curValue - pCurPos[-<span style="color: #800080">1</span>]) <=<span style="color: #000000"> maxDiff)
{
pCurLabel[</span>-<span style="color: #800080">1</span>] =<span style="color: #000000"> currentLabel;
</span>*pWave++ = Point2s(curPoint.x - <span style="color: #800080">1</span><span style="color: #000000">, curPoint.y);
}
</span>--<span style="color: #000000">pWave;
curPoint </span>= *<span style="color: #000000">pWave;
}
</span><span style="color: #0000ff">if</span> (count <=<span style="color: #000000"> maxSpeckleSize)
{
pTypeBuf[pLabel[j]] </span>= <span style="color: #800080">1</span><span style="color: #000000">;
pData[j] </span>=<span style="color: #000000"> (T)newVal;
}
</span><span style="color: #0000ff">else</span><span style="color: #000000">
{
pTypeBuf[pLabel[j]] </span>= <span style="color: #800080">0</span><span style="color: #000000">;
}
}
}
}
}
</span><span style="color: #0000ff">free</span><span style="color: #000000">(pLabelBuf);
</span><span style="color: #0000ff">free</span><span style="color: #000000">(pPointBuf);
</span><span style="color: #0000ff">free</span><span style="color: #000000">(pTypeBuf);
}
View Code
如下视差图中左上角部分有7个小团块,设置speckleWindowSize和speckleRange分别为50和32,连通域检测后结果为如下图右,小团块能够全部检测出来,方便后续用周围视差填充。当然还有一个缺点就是,图像中其他地方尤其是边界区域也会被检测为小团块,后续填充可能会对边界造成平滑。