2017-08-01 20:22:13 pianzang5201 阅读数 287
  • Kaggle 神器:XGBoost 从基础到实战

    主讲老师冒老师为计算机博士,现在中科院从事科研教学工作,十余年机器学习教学经验,主持国家级科研项目3项,研究方向为机器学习、计算机视觉及多媒体处理。 XGBoost是"极端梯度提升"(eXtreme Gradient Boosting)的简称。XGBoost源于梯度提升框架,但是能并行计算、近似建树、对稀疏数据的有效处理以及内存使用优化,这使得XGBoost至少比现有梯度提升实现有至少10倍的速度提升。XGBoost可以处理回归、分类和排序等多种任务。由于它在预测性能上的强大且训练速度快,XGBoost已屡屡斩获Kaggle各大竞赛的冠军宝座。

    39275 人正在学习 去看看 AI100讲师

Canny 边缘检测是一种非常流行的边缘检测算法是 John F.Canny 在 1986 年提出的。

1、噪声去除 

由于边缘检测很容易受到噪声影响,所以第一步是使用 5x5 的高斯滤波器去除噪声。

2、计算图像梯度 

对平滑后的图像使用 Sobel 算子计算水平方向和竖直方向的一阶导数(图像梯度)(Gx 和 Gy)。根据得到的这两幅梯度图(Gx 和 Gy)找到边界的梯度和方向,公式如下: 


梯度的方向一般总是与边界垂直。梯度方向被归为四类:垂直,水平,和 两个对角线。

3、非极大值抑制 

在获得梯度的方向和大小之后,应该对整幅图像做一个扫描,去除那些非边界上的点。对每一个像素进行检查看这个点的梯度是不是周围具有相同梯度方向的点中最大的。如下图所示:


现在你得到的是一个包含“窄边界”的二值图像

4、滞后阈值 

现在要确定那些边界才是真正的边界。这时需要设置两个阈值: minVal 和 maxVal。当图像的灰度梯度高于 maxVal时被认为是真的边界, 那些低于 minVal的边界会被抛弃。如果介于两者之间的话,就要看这个点是否与某个被确定为真正的边界点相连,如果是就认为它也是边界点,如果不是就抛弃。如下图:


A 高于阈值 maxVal 所以是真正的边界点。

C 虽然低于 maxVal 但高于 minVal 并且与 A 相连,所以也被认为是真正的边界点。

B 会被抛弃,因为它不仅低于 maxVal 而且不与真正的边界点相连。

所以选择合适的 maxVal 和 minVal 对于能否得到好的结果非常重要。 在这一步一些小的噪声点也会被除去,因为我们假设边界都是一些长的线段。

OpenCV 中的 Canny 边界检测 

在 OpenCV 中只需要一个函数:cv2.Canny(),就可以完成以上几步。

---这个函数的第一个参数是输入图像

---第二和第三个分别是 minVal 和 maxVal第四个参数设置计算图像梯度的 Sobel 卷积核的大小默认值为 3

---最后一个参数是 L2gradient,它可以用来设定求梯度大小的方程。如果设为 True,就会使用我们上面提到过的方程,否则使用方程:

代替,默认值为 False


#coding:utf-8
import cv2
import numpy as np
from matplotlib import pyplot as plt

img = cv2.imread('F:/moder.jpg')

b, g, r = cv2.split(img)#转换色彩,由bgr转为rgb
img = cv2.merge([r,g,b])#转换色彩,由bgr转为rgb

edges = cv2.Canny(img, 100, 200)

plt.subplot(121),plt.imshow(img, cmap='gray')
plt.title('Original Image'),plt.xticks([]),plt.yticks([])
plt.subplot(122),plt.imshow(edges, cmap='gray')
plt.title('Edge Image'),plt.xticks([]),plt.yticks([])


plt.show()

结果图:




2019-08-30 18:14:08 ctrigger 阅读数 65
  • Kaggle 神器:XGBoost 从基础到实战

    主讲老师冒老师为计算机博士,现在中科院从事科研教学工作,十余年机器学习教学经验,主持国家级科研项目3项,研究方向为机器学习、计算机视觉及多媒体处理。 XGBoost是"极端梯度提升"(eXtreme Gradient Boosting)的简称。XGBoost源于梯度提升框架,但是能并行计算、近似建树、对稀疏数据的有效处理以及内存使用优化,这使得XGBoost至少比现有梯度提升实现有至少10倍的速度提升。XGBoost可以处理回归、分类和排序等多种任务。由于它在预测性能上的强大且训练速度快,XGBoost已屡屡斩获Kaggle各大竞赛的冠军宝座。

    39275 人正在学习 去看看 AI100讲师

Python深度学习:

在2016年和2017年,Kaggle上主要有两大方法:梯度提升机和深度学习。具体而言,梯度提升机用于处理结构化数据的问题,而深度学习则用于图像分类等感知问题。使用前一种方法的人几乎都使用优秀的XGBoost库,它同时支持数据科学最流行的两种语言:Python和R。使用深度学习的Kaggle参赛者则大多使用Keras库,因为它易于使用,非常灵活,并且支持Python。

要想在如今的应用机器学习中取得成功,你应该熟悉这两种技术:梯度提升机,用于浅层学习问题;深度学习,用于感知问题。用术语来说,你需要熟悉XGBoost和Keras,它们是目前主宰Kaggle竞赛的两个库。

2019-04-15 20:37:47 minjiuhong 阅读数 512
  • Kaggle 神器:XGBoost 从基础到实战

    主讲老师冒老师为计算机博士,现在中科院从事科研教学工作,十余年机器学习教学经验,主持国家级科研项目3项,研究方向为机器学习、计算机视觉及多媒体处理。 XGBoost是"极端梯度提升"(eXtreme Gradient Boosting)的简称。XGBoost源于梯度提升框架,但是能并行计算、近似建树、对稀疏数据的有效处理以及内存使用优化,这使得XGBoost至少比现有梯度提升实现有至少10倍的速度提升。XGBoost可以处理回归、分类和排序等多种任务。由于它在预测性能上的强大且训练速度快,XGBoost已屡屡斩获Kaggle各大竞赛的冠军宝座。

    39275 人正在学习 去看看 AI100讲师

Canny算法介绍

Canny边缘检测算法是一种多级边缘检测算法,John F. Canny于 1986 年开发出来。在许多方面的应用都有着它的身影。因此,不管是为了学习或者工作,学习Canny算法的原理和实现都是非常有必要的。废话不多说了,下面我将带领大家从原理和代码方面为您讲解Canny算法的基本思想。在本文的最后,会有一个基于Canny算法比较酷炫的简单项目,有兴趣的可以试试,比较有意思。

Canny算法原理

Canny边缘检测主要分为四步来实现的,每一步都有着其主要意义,基于这四个步骤,我将一一讲解其实现的细节。

第一步:高斯模糊

对于原图像我们需要对其进行高斯模糊,高斯模糊在之前我们已经实现过了,或者你可以使用Opencv自带的函数也行。对图像进行模糊能够去除图像的细节部分,图像噪声可大大减少,而图像的边缘信息不会损失什么,这样对后续检测的效果会更好。

/* 获取高斯分布数组               (核大小, sigma值) */
double **getGaussianArray(int arr_size, double sigma)
{
	int i, j;
	// [1] 初始化权值数组
	double **array = new double*[arr_size];
	for (i = 0; i < arr_size; i++) {
		array[i] = new double[arr_size];
	}
	// [2] 高斯分布计算
	int center_i, center_j;
	center_i = center_j = arr_size / 2;
	double pi = 3.141592653589793;
	double sum = 0.0f;
	// [2-1] 高斯函数
	for (i = 0; i < arr_size; i++) {
		for (j = 0; j < arr_size; j++) {
			array[i][j] =
				//后面进行归一化,这部分可以不用
				//0.5f *pi*(sigma*sigma) * 
				exp(-(1.0f)* (((i - center_i)*(i - center_i) + (j - center_j)*(j - center_j)) /
				(2.0f*sigma*sigma)));
			sum += array[i][j];
		}
	}
	// [2-2] 归一化求权值
	for (i = 0; i < arr_size; i++) {
		for (j = 0; j < arr_size; j++) {
			array[i][j] /= sum;
			//printf(" [%.15f] ", array[i][j]);
		}
		// printf("\n");
	}
	return array;
}


// 高斯模糊
Mat gaussianFilter(Mat &src, int size, double sigma)
{
	// 深拷贝
	Mat dst = src.clone();
	double** model = getGaussianArray(size, sigma);
	double sum;
	// 遍历像素
	for (int i = 0; i < src.rows; i++)
		for (int j = 0; j < src.cols; j++)
		{
			// 初始化
			sum = 0;
			// 遍历模板
			for (int n = -size / 2; n <= size / 2; n++)
				for (int m = -size / 2; m <= size / 2; m++)
				{
					// 边缘补零
					if (i + n < 0 || j + m < 0 || i + n >= src.rows || j + m >= src.cols)
						sum += 0;
					else
						sum += model[n + size / 2][m + size / 2] * src.at<uchar>(i + n, j + m);
				}
			dst.at<uchar>(i, j) = pixesDeal(sum);
		}
	// imshow("高斯模糊", dst);
	return dst;
}

// 像素判定
uchar pixesDeal(double n)
{
	if (n < 0)
		return 0;
	else if (n > 255)
		return 255;
	else
		return static_cast<uchar>(n);
}

第二步:Sobel梯度算子

我们需要对模糊后的图像进行Sobel梯度算子的卷积,但这里的处理和之前我们实现过的Sobel图像锐化稍稍有点不同,之前我们是将x方向的Sobel模板和y方向的Sobel模板进行绝对值的相加(L1范数),但现在我们需要进行平方相加后开平方(L2范数),即幅值。还需要梯度方向的信息为下一步处理提供条件。获取的幅值图像大概描绘了整个图像的轮廓,但是这个边缘存在着很多不必要的信息,例如:边缘描绘过宽和噪声信息。

// sobel梯度计算
Mat sobelFilter(Mat &src)
{
	// 深拷贝
	Mat gradValue = src.clone();
	Mat gradDirect = src.clone();
	double sum1, sum2;
	// x方向边缘的y模板
	double model1[9] = { -1, -2, -1, 0, 0, 0, 1, 2, 1 };
	// y方向边缘的x模板
	double model2[9] = { -1, 0, 1, -2, 0, 2, -1, 0, 1 };
	int p;
	// 遍历像素
	for (int i = 0; i < src.rows; i++)
		for (int j = 0; j < src.cols; j++)
		{
			// 初始化
			p = 0, sum1 = 0, sum2 = 0;
			// 遍历模板
			for (int n = -1; n <= 1; n++)
				for (int m = -1; m <= 1; m++)
				{
					// 边缘补零
					if (i + n < 0 || j + m < 0 || i + n >= src.rows || j + m >= src.cols)
						p++;
					else
					{
						sum1 += src.at<uchar>(i + n, j + m) * model1[p];
						sum2 += src.at<uchar>(i + n, j + m) * model2[p];
						p++;
					}
				}
			gradValue.at<uchar>(i, j) = pixesDeal(pow(pow(sum1, 2) + pow(sum2, 2), 1.0 / 2));
			// 0,7垂直方向, 1,2斜对角方向\, 3,4水平方向, 5,6斜对角方向/
			gradDirect.at<uchar>(i, j) = static_cast<uchar>(atan(sum1 / (sum2 + 0.001)) * 8 / CV_PI + 4);
		}
	Mat temp[] = { gradValue, gradDirect };
	Mat grad;
	merge(temp, 2, grad);
	// imshow("梯度图像", gradValue);

	return grad;
}

第三步:非极大值抑制(NMS)

非极大值抑制是一种将宽边缘带细化,只保留边缘峰值的处理。主要的手法是根据上一步获取的梯度方向,将图像像素点沿梯度方向或逆方向的邻域像素点进行比较,如果为最大值则保留,否则抑制,即设置像素点为0。

Mat NMS(Mat &src)
{
	vector<Mat> grad;
	// 梯度通道和方向通道
	split(src, grad);
	// 遍历中梯度值会改变,先拷贝一份
	Mat gradValue = grad[0].clone();
	// 遍历像素
	for (int i = 0; i < gradValue.rows; i++)
		for (int j = 0; j < gradValue.cols; j++)
		{
			if (grad[1].at<uchar>(i, j) == 0 || grad[1].at<uchar>(i, j) == 7)
			{
				if (i < gradValue.rows - 1)
					if (gradValue.at<uchar>(i, j) <= gradValue.at<uchar>(i + 1, j))
					{
						grad[0].at<uchar>(i, j) = 0;
						continue;
					}
				if (i > 0)
					if (gradValue.at<uchar>(i, j) <= gradValue.at<uchar>(i - 1, j))
					{
						grad[0].at<uchar>(i, j) = 0;
						continue;
					}
			}
			if (grad[1].at<uchar>(i, j) == 1 || grad[1].at<uchar>(i, j) == 2)
			{
				if (i < gradValue.rows - 1 && j < gradValue.cols - 1)
					if (gradValue.at<uchar>(i, j) <= gradValue.at<uchar>(i + 1, j + 1))
					{
						grad[0].at<uchar>(i, j) = 0;
						continue;
					}
				if (i > 0 && j > 0)
					if (gradValue.at<uchar>(i, j) <= gradValue.at<uchar>(i - 1, j - 1))
					{
						grad[0].at<uchar>(i, j) = 0;
						continue;
					}
			}
			if (grad[1].at<uchar>(i, j) == 3 || grad[1].at<uchar>(i, j) == 4)
			{
				if (j < gradValue.cols - 1)
					if (gradValue.at<uchar>(i, j) <= gradValue.at<uchar>(i, j + 1))
					{
						grad[0].at<uchar>(i, j) = 0;
						continue;
					}
				if (j > 0)
					if (gradValue.at<uchar>(i, j) <= gradValue.at<uchar>(i, j - 1))
					{
						grad[0].at<uchar>(i, j) = 0;
						continue;
					}
			}
			if (grad[1].at<uchar>(i, j) == 5 || grad[1].at<uchar>(i, j) == 6)
			{
				if (i < gradValue.rows - 1 && j > 0)
					if (gradValue.at<uchar>(i, j) <= gradValue.at<uchar>(i + 1, j - 1))
					{
						grad[0].at<uchar>(i, j) = 0;
						continue;
					}
				if (i > 0 && j < gradValue.cols - 1)
					if (gradValue.at<uchar>(i, j) <= gradValue.at<uchar>(i - 1, j + 1))
					{
						grad[0].at<uchar>(i, j) = 0;
						continue;
					}
			}
		}
	//imshow("非极大值处理", grad[0]);
	merge(grad, src);
	return src;
}

第四步:双阈值连接

设置两个阈值,一个高阈值和一个低阈值。图像像素点如果高于高阈值则表示是强边缘点,是真实边缘点,高于低阈值但小于高阈值表示是弱边缘点,其它不是边缘,抑制。之后需要对弱边缘点进行处理,若其邻域存在强边缘点则表示是真实边缘点,否则不是。这样设置两个阈值可以滤除图像中噪声,改善图像质量。弱边缘的处理原则是因为真实边缘的弱边缘点都存在强边缘点。

Mat doubleThrCon(Mat &src, uchar high, uchar low)
{
	vector<Mat> grad;
	// 梯度通道和方向通道
	split(src, grad);
	for (int i = 0; i < grad[0].rows; i++)
		for (int j = 0; j < grad[0].cols; j++)
		{
			if (grad[0].at<uchar>(i, j) > high)
			{
				grad[0].at<uchar>(i, j) = 255;
				// 标记强边缘点的位置
				grad[1].at<uchar>(i, j) = 2;
			}
			else if (grad[0].at<uchar>(i, j) > low)
			{
				grad[0].at<uchar>(i, j) = 0;
				// 标记弱边缘点的位置
				grad[1].at<uchar>(i, j) = 1;
			}
			else
			{
				grad[0].at<uchar>(i, j) = 0;
				grad[1].at<uchar>(i, j) = 0;
			}
		}
	// 真实的边缘会在弱边缘点的邻域内存在强边缘点
	for (int i = 0; i < grad[0].rows; i++)
		for (int j = 0; j < grad[0].cols; j++)
		{
			if (grad[1].at<uchar>(i, j) == 1)
			{
				for (int n = -1; n <= 1; n++)
					for (int m = -1; m <= 1; m++)
					{
						if (i + n >= 0 && j + m >= 0 && i + n < src.rows && j + m < src.cols && grad[1].at<uchar>(i + n, j + m) == 2)
							grad[0].at<uchar>(i, j) = 255;
					}
			}
		}

	return grad[0];

}

视频流Canny边缘检测(阈值可调)

应用我们自己编写的Canny算法速度比较慢,因此我们选择系统提供的Canny算法效果会流畅。可以减少对摄像头的帧数处理来加快速度。

// 主函数
int main()
{
	Mat frameImage;
	VideoCapture capture1(0 + CAP_DSHOW);
	// 我用的是手机摄像头,用电脑摄像头的不用填open()里面的参数
	capture1.open("http://admin:admin@172.30.71.223:8081");
	if (!capture1.isOpened())
		return 0;

	int n = 0;
	int highThreshold=70, lowThreshold=70;
	//创建窗口
	namedWindow("out", 1);

	//创建轨迹条
	createTrackbar("high threshold", "out", &highThreshold, 255);
	createTrackbar("low threshold", "out", &lowThreshold, 255);

	while (true)
	{
		capture1 >> frameImage;
		if (n % 3 == 0)
		{
			cvtColor(frameImage, frameImage, COLOR_BGR2GRAY);
			Canny(frameImage, frameImage, lowThreshold, highThreshold);
			//frameImage = myCanny(frameImage);
			imshow("out", frameImage);
		}
		if (char(waitKey(1)) == 'q') break;
		n++;
	}
	return 0;
}

结果视频我就不发了,有兴趣的可以自己运行一下~

2019-12-02 08:22:33 HeroIsUseless 阅读数 47
  • Kaggle 神器:XGBoost 从基础到实战

    主讲老师冒老师为计算机博士,现在中科院从事科研教学工作,十余年机器学习教学经验,主持国家级科研项目3项,研究方向为机器学习、计算机视觉及多媒体处理。 XGBoost是"极端梯度提升"(eXtreme Gradient Boosting)的简称。XGBoost源于梯度提升框架,但是能并行计算、近似建树、对稀疏数据的有效处理以及内存使用优化,这使得XGBoost至少比现有梯度提升实现有至少10倍的速度提升。XGBoost可以处理回归、分类和排序等多种任务。由于它在预测性能上的强大且训练速度快,XGBoost已屡屡斩获Kaggle各大竞赛的冠军宝座。

    39275 人正在学习 去看看 AI100讲师

这节实现图像的预处理。

在最开始的时候,我们仅仅是把图像进行灰度化,并为了训练效率,强行缩小了图像,导致损失了许多图像信息,实际上,有更多的方法来保存这些信息。

现在流行的图像预处理算法非常多,例如图像反转,灰度线性变换,非线性变换,直方图均衡化,线性平滑滤波器,中值滤波器,Sobel算子和拉普拉斯对图像锐化,梯度算子检测边缘,LOG算子检测边缘,Canny算子检测边缘,边界跟踪(bwtraceboundary函数),Hough变换,直方图阈值法,自动阈值法:Otsu法,膨胀操作,腐蚀操作,开启和闭合操作,开启和闭合组合操作,形态学边界提取等等。

例如对于车牌识别或者指纹检测,我们可以使用图像锐化的方法,这样更容易提取出特征,但是对于一般图像检测的话,锐化可能导致信息丢失,究竟用什么预处理方法,最好的方法就是挨个试一下,哪个有用就用哪个。

说实在的,既然把图像都压缩到60*80大小了,而且还不能变大,变大那么运算力就会吃紧,导致丢失大量信息,另外就是图片也是灰度化的,完全丢失了颜色信息,任何要增加信息量的方法都必然会使运算负担加重,导致图像预处理也就是并没有太大作用了,锐化了的图像就一定能比不锐化的图像识别的更成功吗?不一定,但是可以预见的是,即便是精度有损失,也不一定会损失多少。

因此,我的结论是,对图像不进行图形学上的预处理,直接进行卷积池化获得特征。

在一开始我们怎么压缩的图片呢?是这样的:

img = imresize(img, [60, 80]);

这个imresize是怎么调整到任意大小的呢?它是按照双三次插值,这仍是图形学上的操作,我们用卷积来代替,会得到更多的信息。

卷积参数和池化参数怎么确定呢?这里面可以调整的参数太多了,只能说一个一个试,现在随便选择一个。先把图像大小固定到28*28,用20个9*9的卷积过滤器,池化层为2*2的平均池化,隐藏层100个节点,输出层10个节点,那么,我们需要训练的就只有三个参数,分别是卷积时候的参数,隐藏层的参数和输出层的参数。整个运算过程就可以直接用一个函数代表了。我们可以这样用:


那么这里就会有一个问题,就是一般是有一个b,但是这里没有b的位置了,为了代码清晰,就没用,实际上,既然都这么不准了,或者说,整个网络都没有什么特别清晰的判定标准,多一个b,少一个b又能降低多少精确度呢?you never know.
其中X是输出数据,L为标签,这样,每次输入一个批量的数据,返还修改后的参数,然后循环个几千遍,就算好了。

下节讲代码。









 

2018-12-11 17:40:06 shanwenkang 阅读数 1229
  • Kaggle 神器:XGBoost 从基础到实战

    主讲老师冒老师为计算机博士,现在中科院从事科研教学工作,十余年机器学习教学经验,主持国家级科研项目3项,研究方向为机器学习、计算机视觉及多媒体处理。 XGBoost是"极端梯度提升"(eXtreme Gradient Boosting)的简称。XGBoost源于梯度提升框架,但是能并行计算、近似建树、对稀疏数据的有效处理以及内存使用优化,这使得XGBoost至少比现有梯度提升实现有至少10倍的速度提升。XGBoost可以处理回归、分类和排序等多种任务。由于它在预测性能上的强大且训练速度快,XGBoost已屡屡斩获Kaggle各大竞赛的冠军宝座。

    39275 人正在学习 去看看 AI100讲师

Snakes算法

上一讲我们讲的图像分割算法主要是基于像素的,这一讲主要是基于曲线的。我们希望能得到一个能够包围住图像轮廓的平滑的曲线,snakes算法就是一个很有用的算法。首先我们将曲线的坐标x、y同一用参数s表示,s范围从0-1代表从起点绕曲线一周再回到原点

我们假定初始化的时候这个曲线已经给定,我们定义这个曲线的能量函数,曲线衍进的过程就是让能量函数降低的的过程,能量函数分为外部能量和内部能量

如下是内部能量的定义,内部能量只与曲线的形状有关。能量由两部分组成:一阶导部分与二阶导部分。一阶导代表曲线的“弹性”,也就是曲线是否被拉伸的非常长;二阶导代表曲线曲折程度,也就是曲线是否弯曲不平滑。曲线弹性越低,弯曲越少能量越低

第二部分是曲线的外部能量,代表曲线与边缘的重合程度。重合程度大的时候能量低。

我们有了能量函数以后如何求它的最小值呢?这涉及到变分法,在此就不细讲了。对于数字图像,我们一般用一定数量的点对曲线进行逼近,之后我们可以用梯度下降法来求得函数的最小值

在曲线衍进的时候还需要注意有时我们需要对曲线的点进行重排(再采样)来保证下一次循环(衍进)时我们有更好的效果

当然这种方法也存在问题,我们的活动轮廓无法看到远处的边缘,因此当周围像素值都差不多的时候曲线可能不会继续向内收敛;第二点是当图像中存在噪声时轮廓很可能和噪声点重合。为了解决这个问题我们可以对图像先进行模糊处理,这相当于将原本的边界变得更加模糊了(即扩大的边界的影响范围),这样就能使轮廓有更好的收敛性

 

梯度向量流

采用模糊处理的效果实际上并不是很好,更好的方法是采用梯度向量流(gradient vector flow)。梯度向量流用一个新的矢量场来代替梯度场,从而替换外部能量的部分。

我们用如下方式定义这个新的矢量场v,当梯度值较大的时候,我们另这个矢量场与梯度场的值大致相同;当梯度值较小的时候我们让v的值尽量平滑的变化。这也就意味着在图像边缘处,v的值与梯度场的值相同,当我们逐渐远离边界的时候,v的值不像梯度场一样立马变小,而是有一个逐渐变小的过程。实际上这个新的场v也是扩大了边界的影响范围,使得轮廓能够在更远的地方捕捉到边缘

如果我们将这个场画出来可以得到如下结果。我们可以看出在这种情况下如果初始轮廓在边界外部,那么轮廓将会收缩;如果初始轮廓在边界内部,那么轮廓将会外扩,即朝着向v的模大的地方衍进

Snakes算法也有很多令人头疼的地方,例如追踪每一个点不是一件容易的事,例如snake无法包围多个物体等等

除此以外由于这种算法是基于边缘的,它还会使曲线与我们不需要的边缘重合,例如以下这个例子:曲线并不能很好地包围手掌,而是被背景木桌的条纹所吸引了

 

水平集

为了解决snakes算法不能包围多个物体与洞的问题,我们有水平集(level set)这个算法。与定义一条曲线不同的是,我们定义了一个三维的函数,二维平面上的曲线实际上是这个函数在z=0的横截面

我们用这种方法可以很好地表示包含多个物体或洞的曲线

举个例子,下面右图是我们需要的曲线,左边是这个曲线对应的函数

这种算法还有很多细节我们就不在这讨论了

我们在以上讨论的算法都是基于图像边缘的,当然我们还有基于图像区域的算法,这种算法的能量只与曲线本身的性质以及两个区域块内的像素有关,而与图像的边界无关

比如以下的例子,我们运用基于区域的算法可以让曲线很好地收敛到斑马周围,因为这种情况下曲线外部的颜色基本是绿色,曲线内部基本是黑白的。但如果我们采用基于边缘的算法来计算,那么这个曲线就会收敛到斑马身上的条纹上

 

 

 

 

没有更多推荐了,返回首页