OpenCV-距离变换实现

距离变换实现图像的每个像素到最近前景目标或到图像边界的距离,距离变换的步骤如下:

1.将图像进行二值化,子图像值为0,背景为255;

2.利用maskL从左向右,从上到下扫描,p点是当前像素点,q点是maskL中邻域的点,D()为距离计算,包括棋盘距离、城市距离和欧式距离。F(p)为p点的像素值,计算F(p) = min( F(p),  F(q)+D(p,q) ), 其中,q属于maskL

3.再利用maskR从右向左,从下向上扫描,计算F(p) = min( F(p),  F(q)+D(p,q) ), 其中q属于maskR

4.F(p) 则为距离变换后的图像。


代码实现:

//计算欧式距离
float calcEuclideanDistance(int x1, int y1, int x2, int y2)
{
	return sqrt(float((x1 - x2)*(x1 - x2) + (y1 - y2)*(y1 - y2)));
}

//计算棋盘距离
int calcChessboardDistance(int x1, int y1, int x2, int y2)
{
	return cv::max(abs(x1 - x2), abs(y1 - y2));
}

//计算街区距离
int calcBlockDistance(int x1, int y1, int x2, int y2)
{
	return abs(x1 - x2) + abs(y1 - y2);
}

//距离变换函数实现
void distanceTrans(cv::Mat &image, cv::Mat &result)
{
	//CV_Assert()若括号中的表达式值为false,则返回一个错误信息
	CV_Assert(!image.empty());
	cv::Mat imageGray, imageBinary;
	//转换成灰度图像
	cv::cvtColor(image, imageGray, CV_RGB2GRAY);
	//转换成二值图
	cv::threshold(imageGray, imageBinary, 100, 255, cv::THRESH_BINARY);
	cv::imshow("imageBinary", imageBinary);
	int rows = imageBinary.rows, cols = imageBinary.cols;
	uchar* pDataOne, *pDataTwo;
	float disPara = 0, fDisMin = 0;
	//第一遍遍历图像,用左模板更新像素值
	for (int i = 1; i < rows - 1; i++)
	{
		//图像指针获取
		pDataOne = imageBinary.ptr(i);
		for (int j = 1; j < cols; j++)
		{
			//分别计算其左掩码的相关距离
			//maskL
		    // pL pL
			// pL p
			// pL
			pDataTwo = imageBinary.ptr(i - 1);
			disPara = calcEuclideanDistance(i, j, i - 1, j - 1);
			fDisMin = cv::min((float)pDataOne[j], disPara + pDataTwo[j - 1]);
			disPara = calcEuclideanDistance(i, j, i - 1, j);
			fDisMin = cv::min(fDisMin, disPara + pDataTwo[j]);
			pDataTwo = imageBinary.ptr(i);
			disPara = calcEuclideanDistance(i, j, i, j - 1);
			fDisMin = cv::min(fDisMin, disPara + pDataTwo[j - 1]);
			pDataTwo = imageBinary.ptr(i + 1);
			disPara = calcEuclideanDistance(i, j, i + 1, j - 1);
			fDisMin = cv::min(fDisMin, disPara + pDataTwo[j - 1]);
			pDataOne[j] = (uchar)cvRound(fDisMin);
		}
	}
	//第二遍遍历图像,用右模板更新图像值
	for (int i = rows - 2; i > 0; i--)
	{
		//图像指针获取
		pDataOne = imageBinary.ptr(i);
		for (int j = cols - 2; j >= 0; j--)
		{
			//分别计算其右掩码的相关距离
			//maskR
			//    pR
			// p  pR
			// pR pR
			pDataTwo = imageBinary.ptr(i + 1);
			disPara = calcEuclideanDistance(i, j, i + 1, j);
			fDisMin = cv::min((float)pDataOne[j], disPara + pDataTwo[j]);
			disPara = calcEuclideanDistance(i, j, i + 1, j + 1);
			fDisMin = cv::min(fDisMin, disPara + pDataTwo[j + 1]);
			pDataTwo = imageBinary.ptr(i);
			disPara = calcEuclideanDistance(i, j, i, j + 1);
			fDisMin = cv::min(fDisMin, disPara + pDataTwo[j + 1]);
			pDataTwo = imageBinary.ptr(i - 1);
			disPara = calcEuclideanDistance(i, j, i - 1, j + 1);
			fDisMin = cv::min(fDisMin, disPara + pDataTwo[j + 1]);
			pDataOne[j] = (uchar)cvRound(fDisMin);
		}
	}
	result = imageBinary.clone();
}

运行结果:

OpenCV-距离变换实现_第1张图片


OpenCV距离变换函数:

void distanceTransform(InputArray src, OutputArray dst, int distanceType, int maskSize, int dstType=CV_32F )
src:输入矩阵
dst:输出矩阵
distanceType:距离类型。可以的类型是CV_DIST_L1、CV_DIST_L2、CV_DIST_C(CV_DIST_L1街区距离,CV_DIST_L2欧几里得距离,CV_DIST_C棋盘距离)。
maskSize:距离变换运算时的掩码大小。值可以是3、5或CV_DIST_MASK_PRECISE(当distanceType=CV_DIST_L1 或 CV_DIST_C时,maskSize只能为3)
dstType:输出图像的类型,可以是CV_8U 或 CV_32F。


代码实现:

int main()
{
	cv::Mat image = cv::imread("1.jpg");
	if (image.empty()) return -1;
	cv::imshow("image", image);
	//转换为灰度图
	cv::Mat imageGray, imageBinary, result1;
	cv::cvtColor(image, imageGray, CV_RGB2GRAY);
	//转换为二值图
	cv::threshold(imageGray, imageBinary, 160, 255, cv::THRESH_BINARY);
	//距离变换
	cv::distanceTransform(imageBinary, result1, CV_DIST_L2, CV_DIST_MASK_PRECISE);
	//归一化矩阵
	cv::normalize(result1, result1, 0, 1, cv::NORM_MINMAX);
	cv::imshow("imageBinary1", imageBinary);
	cv::imshow("result1", result1);
	cv::waitKey(0);
	return 0;
}

运行结果:

OpenCV-距离变换实现_第2张图片

你可能感兴趣的:(----opencv学习)