几种视差图后处理方法,包括滤波去噪(中值滤波或双边滤波)、连通域检测和左右一致性检测。

1)滤波去噪

滤波去躁主要用于去除视差图中由于误匹配造成的孤立噪点,视差图后处理中常用的两种滤波方法有中值滤波和双边滤波。

中值滤波是一种典型的非线性滤波技术,其基本思想是用像素点邻域灰度值的中值来代替该像素点的灰度值,该方法在去除脉冲噪声、椒盐噪声的同时又能保留图像的边缘细节。如下图所示:

中值滤波和方框滤波可以一起用吗_连通域

 

双边滤波是结合图像的空间邻近度和像素值相似度的一种折衷处理,同时考虑空域信息和灰度相似性,达到保边去噪的目的。双边滤波器顾名思义比高斯滤波多了一个高斯方差sigma-d,它是基于空间分布的高斯滤波函数,所以在边缘附近,离的较远的像素不会太多影响到边缘上的像素值,这样就保证了边缘附近像素值的保存。如下图所示:

中值滤波和方框滤波可以一起用吗_邻域_02

2)连通域检测

连通域检测主要用于去除视差图中由于误匹配造成的小团块。其算法大概流程如下:

0.首先设定speckleRange和speckleWindowSize,speckleWindowSize是指设置检测出的连通域中像素点个数,也就是连通域的大小。speckleRange是指设置判断两个点是否属于同一个连通域的阈值条件。

1.判断当前像素点四邻域的邻域点与当前像素点的差值diff,如果diff<speckleRange,则表示该邻域点与当前像素点是一个连通域,设置一个标记。然后再以该邻域点为中心判断其四邻域点,步骤同上。直至某一像素点四邻域的点均不满足条件,则停止。

2.步骤1完成后,判断被标记的像素点个数count,如果像素点个数count<=speckleWindowSize,则说明该连通域是一个小团块(blob),则将当前像素点值设置为newValue(表示错误的视差值,newValue一般设置为负数或者0值)。否则,表示该连通域是个大团块,不做处理。同时建立标记值与是否为小团块的关系表rtype[label],rtype[label]为0,表示label值对应的像素点属于小团块,为1则不属于小团块。

3.处理下一个像素点时,先判断其是否已经被标记:如果已经被标记,则根据关系表rtype[label]判断是否为小团块(blob),如果是,则直接将该像素值设置为newValue;如果不是,则不做处理。继续处理下一个像素。如果没有被标记,则按照步骤1处理。

4.所有像素点处理后,满足条件的区域会被设置为newValue值。

代码:

typedef cv::Point_<short> Point2s;
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);

	for (int i = 0; i < height; i++)
	{
		float *pData = img.ptr<float>(i);
		int *pLabel = pLabelBuf + width * i;
		for (int j = 0; j < width; j++)
		{
			if ((pData[j] - newVal) > 1e-1)
			{
				if (pLabel[j])
				{
					if (pTypeBuf[pLabel[j]])
					{
						pData[j] = newVal;
					}
				}
				else
				{
					Point2s *pWave = pPointBuf;
					Point2s curPoint(j, i);
					currentLabel++;
					int count = 0;
					pLabel[j] = currentLabel;
					while (pWave >= pPointBuf)
					{
						count++;
						float *pCurPos = &img.at<float>(curPoint.y, curPoint.x);
						float curValue = *pCurPos;
						int *pCurLabel = pLabelBuf + width * curPoint.y + curPoint.x;
						//bot
						if (curPoint.y < height - 1 && !pCurLabel[+width] && pCurPos[+width] != newVal && abs(curValue - pCurPos[+width]) <= maxDiff)
						{
							pCurLabel[+width] = currentLabel;
							*pWave++ = Point2s(curPoint.x, curPoint.y + 1);
						}
						//top
						if (curPoint.y > 0 && !pCurLabel[-width] && pCurPos[-width] != newVal && abs(curValue - pCurPos[-width]) <= maxDiff)
						{
							pCurLabel[-width] = currentLabel;
							*pWave++ = Point2s(curPoint.x, curPoint.y - 1);
						}
						//right
						if (curPoint.x < width - 1 && !pCurLabel[+1] && pCurPos[+1] != newVal && abs(curValue - pCurPos[+1]) <= maxDiff)
						{
							pCurLabel[+1] = currentLabel;
							*pWave++ = Point2s(curPoint.x + 1, curPoint.y);
						}
						//left
						if (curPoint.x > 0 && !pCurLabel[-1] && pCurPos[-1] != newVal && abs(curValue - pCurPos[-1]) <= maxDiff)
						{
							pCurLabel[-1] = currentLabel;
							*pWave++ = Point2s(curPoint.x - 1, curPoint.y);
						}

						--pWave;
						curPoint = *pWave;
					}

					if (count <= maxSpeckleSize)
					{
						pTypeBuf[pLabel[j]] = 1;
						pData[j] = (float)newVal;
					}
					else
					{
						pTypeBuf[pLabel[j]] = 0;
					}
				}
			}
		}
	}

	free(pLabelBuf);
	free(pPointBuf);
	free(pTypeBuf);
}

3)左右一致性检测

左右一致性检测是用于遮挡检测的,具体做法:

根据左右两幅输入图像,分别得到左右两幅视差图。对于左图中的一个点p,求得的视差值是d1,那么p在右图里的对应点应该是(p-d1),(p-d1)的视差值记作d2。若|d1-d2|>threshold,p标记为遮挡点(occluded point)。