OpenCV学习三十五:distanceTransform 距离变换函数

1. API

该函数有两个初始化API

C++: void distanceTransform(InputArray src, OutputArray dst, int distanceType, int maskSize)

C++: void distanceTransform(
InputArray src, 
OutputArray dst, 
OutputArray labels, 
int distanceType, 
int maskSize, 
int labelType=DIST_LABEL_CCOMP )

2. 参数说明

  • src – 8-bit, 单通道(二值化)输入图片。
  • dst – 输出结果中包含计算的距离,这是一个32-bit  float 单通道的Mat类型数组,大小与输入图片相同。
  • distanceType – 计算距离的类型那个,可以是 CV_DIST_L1、CV_DIST_L2 、CV_DIST_C。
  • maskSize – 距离变换掩码矩阵的大小,可以是
  1. 3(CV_DIST_L1、 CV_DIST_L2 、CV_DIST_C)
  2. 5(CV_DIST_L2 )
  3. CV_DIST_MASK_PRECISE (这个只能在4参数的API中使用)
  • labels – 可选的2D标签输出(离散 Voronoi 图),类型为 CV_32SC1 大小同输入图片。
  • labelType – 输出标签的类型,这里有些两种。
  1. labelType==DIST_LABEL_CCOMP 将周围较近的白色像素点作为一个整体计算其到黑色边缘的距离
  2. labelType==DIST_LABEL_PIXEL 单独计算每个白色像素点到其黑色边缘的距离.
distanceType参数说明
distanceTypemaskSizea \ b \ c     
CV_DIST_C3(3X3)a = 1, b = 1
CV_DIST_L13(3X3)a = 1, b = 2
CV_DIST_L23(3X3)a=0.955, b=1.3693
CV_DIST_L25(5X5)a=1, b=1.4, c=2.1969

其中 a b c 含义:在这个函数中计算每个白色像素到黑色像素(0值像素)的最短距离,因此需要通过最短的移动方式找到这个点兵计算他们之间的值。通常来说移动有水平方向、竖直方向、对角方向、跳跃式几个移动方法。虽然计算距离的方法都是一些很基础的公式,但是这个这个掩码矩阵必须是对阵的(这里插一句,要求对是式为了卷积计算的过程可以使用加速算法),因此掩码矩阵上所有水平和竖直方向的变化量(原文是must have the same shift cost ),这里用 a 代表;对角方向的变化量用 b 代表;跳跃移动的变化量用 c 代表。CV_DIST_C、CV_DIST_L1、CV_DIST_L2(maskSize=5)的计算结果是精确的,CV_DIST_L2(maskSize=3)是一个快速计算方法。

3. 代码

我写的代码结果不明显

 #include<stdio.h>
 #include<opencv2/opencv.hpp>
 
 using namespace cv;
 using namespace std;
 
 int main(int argc, char* argv[]){
 
 	Mat src = imread("10.jpg");
 	resize(src, src, Size(), 0.25, 0.25, 1);
 	imshow("src", src);
 
 	Mat bin;
 	cvtColor(src, bin, CV_BGR2GRAY);
 	threshold(bin, bin, 80, 255, CV_THRESH_BINARY);
 	imshow("bin", bin);
 
 	Mat Dist, Labels;
 	distanceTransform(bin, Dist, CV_DIST_L1, 3);
 	normalize(Dist, Dist, 0, 1, NORM_MINMAX);
 	imshow("dist1", Dist);
 
 	distanceTransform(bin, Dist, Labels, CV_DIST_L1, 3, DIST_LABEL_CCOMP);
 	normalize(Dist, Dist, 0, 1, NORM_MINMAX);
 	imshow("dist2", Dist);
 	imshow("labels2", Labels);
 
 	distanceTransform(bin, Dist, Labels, CV_DIST_L1, 3, DIST_LABEL_PIXEL);
 	normalize(Dist, Dist, 0, 1, NORM_MINMAX);
 	//normalize(Labels, Labels, 0, 255, NORM_MINMAX);
 	imshow("dist3", Dist);
 	imshow("labels3", Labels);
  
 	waitKey(); 
 	return 0;
 
 }

后来又找来OpenCV官方的示例代码:

#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"

#include <stdio.h>

using namespace cv;

int maskSize0 = CV_DIST_MASK_5;
int voronoiType = -1;
int edgeThresh = 100;
int distType0 = CV_DIST_L1;

// The output and temporary images
Mat gray;

// threshold trackbar callback
static void onTrackbar( int, void* )
{
	static const Scalar colors[] =
	{
		Scalar(0,0,0),
		Scalar(255,0,0),
		Scalar(255,128,0),
		Scalar(255,255,0),
		Scalar(0,255,0),
		Scalar(0,128,255),
		Scalar(0,255,255),
		Scalar(0,0,255),
		Scalar(255,0,255)
	};

	int maskSize = voronoiType >= 0 ? CV_DIST_MASK_5 : maskSize0;
	int distType = voronoiType >= 0 ? CV_DIST_L2 : distType0;

	Mat edge = gray >= edgeThresh, dist, labels, dist8u;

	if( voronoiType < 0 )
		distanceTransform( edge, dist, distType, maskSize );
	else
		distanceTransform( edge, dist, labels, distType, maskSize, voronoiType );

	if( voronoiType < 0 )
	{
		// begin "painting" the distance transform result
		dist *= 5000;
		pow(dist, 0.5, dist);

		Mat dist32s, dist8u1, dist8u2;

		dist.convertTo(dist32s, CV_32S, 1, 0.5);
		dist32s &= Scalar::all(255);

		dist32s.convertTo(dist8u1, CV_8U, 1, 0);
		dist32s *= -1;

		dist32s += Scalar::all(255);
		dist32s.convertTo(dist8u2, CV_8U);

		Mat planes[] = {dist8u1, dist8u2, dist8u2};
		merge(planes, 3, dist8u);
	}
	else
	{
		dist8u.create(labels.size(), CV_8UC3);
		for( int i = 0; i < labels.rows; i++ )
		{
			const int* ll = (const int*)labels.ptr(i);
			const float* dd = (const float*)dist.ptr(i);
			uchar* d = (uchar*)dist8u.ptr(i);
			for( int j = 0; j < labels.cols; j++ )
			{
				int idx = ll[j] == 0 || dd[j] == 0 ? 0 : (ll[j]-1)%8 + 1;
				float scale = 1.f/(1 + dd[j]*dd[j]*0.0004f);
				int b = cvRound(colors[idx][0]*scale);
				int g = cvRound(colors[idx][1]*scale);
				int r = cvRound(colors[idx][2]*scale);
				d[j*3] = (uchar)b;
				d[j*3+1] = (uchar)g;
				d[j*3+2] = (uchar)r;
			}
		}
	}

	imshow("Distance Map", dist8u );
}

static void help()
{
	printf("\nProgram to demonstrate the use of the distance transform function between edge images.\n"
		"Usage:\n"
		"./distrans [image_name -- default image is stuff.jpg]\n"
		"\nHot keys: \n"
		"\tESC - quit the program\n"
		"\tC - use C/Inf metric\n"
		"\tL1 - use L1 metric\n"
		"\tL2 - use L2 metric\n"
		"\t3 - use 3x3 mask\n"
		"\t5 - use 5x5 mask\n"
		"\t0 - use precise distance transform\n"
		"\tv - switch to Voronoi diagram mode\n"
		"\tp - switch to pixel-based Voronoi diagram mode\n"
		"\tSPACE - loop through all the modes\n\n");
}

const char* keys =
{
	"{1| |stuff.jpg|input image file}"
};

int main( int argc, const char** argv )
{
	help();
	CommandLineParser parser(argc, argv, keys);
	string filename = parser.get<string>("1");
	//gray = imread("stuff.jpg", 0);
	gray = imread("10.jpg", 0);
	resize(gray, gray, Size(), 0.25, 0.25, 1);
	if(gray.empty())
	{
		printf("Cannot read image file: %s\n", filename.c_str());
		help();
		return -1;
	}

	namedWindow("Distance Map", 1);
	createTrackbar("Brightness Threshold", "Distance Map", &edgeThresh, 255, onTrackbar, 0);

	for(;;)
	{
		// Call to update the view
		onTrackbar(0, 0);

		int c = waitKey(0) & 255;

		if( c == 27 )
			break;

		if( c == 'c' || c == 'C' || c == '1' || c == '2' ||
			c == '3' || c == '5' || c == '0' )
			voronoiType = -1;

		if( c == 'c' || c == 'C' )
			distType0 = CV_DIST_C;
		else if( c == '1' )
			distType0 = CV_DIST_L1;
		else if( c == '2' )
			distType0 = CV_DIST_L2;
		else if( c == '3' )
			maskSize0 = CV_DIST_MASK_3;
		else if( c == '5' )
			maskSize0 = CV_DIST_MASK_5;
		else if( c == '0' )
			maskSize0 = CV_DIST_MASK_PRECISE;
		else if( c == 'v' )
			voronoiType = 0;
		else if( c == 'p' )
			voronoiType = 1;
		else if( c == ' ' )
		{
			if( voronoiType == 0 )
				voronoiType = 1;
			else if( voronoiType == 1 )
			{
				voronoiType = -1;
				maskSize0 = CV_DIST_MASK_3;
				distType0 = CV_DIST_C;
			}
			else if( distType0 == CV_DIST_C )
				distType0 = CV_DIST_L1;
			else if( distType0 == CV_DIST_L1 )
				distType0 = CV_DIST_L2;
			else if( maskSize0 == CV_DIST_MASK_3 )
				maskSize0 = CV_DIST_MASK_5;
			else if( maskSize0 == CV_DIST_MASK_5 )
				maskSize0 = CV_DIST_MASK_PRECISE;
			else if( maskSize0 == CV_DIST_MASK_PRECISE )
				voronoiType = 0;
		}
	}

	return 0;
}
我的测试图片
OpenCV官方的测试图片

 

评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值