OpenCV 从浮点型数据的视差图中分割并滤除小的斑块

OpenCV 在BM匹配方法中提供了一个非常有用的函数:filterSpeckles(),用于从视差图中直接去除一些小的(甚至粘连在一起的)斑块。但也许这个函数在设计之初只是为了在BM算法中使用,所以遗憾的是这个函数的接口在设计上只能接收CV_8UC和CV_16SC两种数据类型的视差图像,这一点在OpenCV的手册中已经明确了。

而在很多基于立体视觉原理的高精度测量中,我们所使用的视差图都是CV_32F类型的数据,倘若每次为了滤除斑块而重新做遍历一次图像转换数据,多少有点让人觉得多此一举却又无能为力。

所以笔者找到这个接口方法的内部实现,欣喜的发现在底层实现中其实并未限制数据的类型,因此,兴致来了,就把底层的代码搬了出来,重新自己定义了一个接口,使这个方法可以适用于更多数据类型的视差图,因此为实现代码,仅供参考。

typedef cv::Point_ Point2s;

template 
void FilterSpeckles(Mat& img, int maxSpeckleSize, float maxDiff)
{
	using namespace cv;

	int newVal = img.cols;

	cv::Mat _buf;

	int width = img.cols, height = img.rows, npixels = width*height;
	size_t bufSize = npixels*(int)(sizeof(Point2s) + sizeof(int) + sizeof(uchar));
	if (!_buf.isContinuous() || !_buf.data || _buf.cols*_buf.rows*_buf.elemSize() < bufSize)
		_buf.create(1, (int)bufSize, CV_8U);

	uchar* buf = _buf.data;
	int i, j, dstep = (int)(img.step / sizeof(T));
	int* labels = (int*)buf;
	buf += npixels*sizeof(labels[0]);
	Point2s* wbuf = (Point2s*)buf;
	buf += npixels*sizeof(wbuf[0]);
	uchar* rtype = (uchar*)buf;
	int curlabel = 0;

	// clear out label assignments
	memset(labels, 0, npixels*sizeof(labels[0]));

	for (i = 0; i < height; i++)
	{
		T* ds = img.ptr(i);
		int* ls = labels + width*i;

		for (j = 0; j < width; j++)
		{
			if (ds[j] != newVal)   // not a bad disparity
			{
				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;

					// wavefront propagation
					while (ws >= wbuf) // wavefront not empty
					{
						count++;
						// put neighbors onto wavefront
						T* dpp = &img.at(p.y, p.x);
						T dp = *dpp;
						int* lpp = labels + width*p.y + p.x;

						if (p.y < height - 1 && !lpp[+width] && dpp[+dstep] != newVal && std::abs(dp - dpp[+dstep]) <= maxDiff)
						{
							lpp[+width] = curlabel;
							*ws++ = Point2s(p.x, p.y + 1);
						}

						if (p.y > 0 && !lpp[-width] && dpp[-dstep] != newVal && std::abs(dp - dpp[-dstep]) <= maxDiff)
						{
							lpp[-width] = curlabel;
							*ws++ = Point2s(p.x, p.y - 1);
						}

						if (p.x < width - 1 && !lpp[+1] && dpp[+1] != newVal && std::abs(dp - dpp[+1]) <= maxDiff)
						{
							lpp[+1] = curlabel;
							*ws++ = Point2s(p.x + 1, p.y);
						}

						if (p.x > 0 && !lpp[-1] && dpp[-1] != newVal && std::abs(dp - dpp[-1]) <= maxDiff)
						{
							lpp[-1] = curlabel;
							*ws++ = Point2s(p.x - 1, p.y);
						}

						// pop most recent and propagate
						// NB: could try least recent, maybe better convergence
						p = *--ws;
					}

					// assign label type
					if (count <= maxSpeckleSize)   // speckle region
					{
						rtype[ls[j]] = 1;   // small region label
						ds[j] = (T)newVal;
					}
					else
						rtype[ls[j]] = 0;   // large region label
				}
			}
		}
	}
}



你可能感兴趣的:(OpenCV,拾遗)