高分辨率图像分割--竞争Hopfield神经网络

  这是《智能机器人》的课程设计(2014年7月),课程要求是:采用人工智能算法(如Adaline、Art、Bam、Boltzmann、BP、CP、Hopfield、Som、SVM、Hamming等神经网络,或者其他遗传算法、免疫算法、模糊算法),对服务机器人(无人机)拍摄的高分辨率图像(存放在image文件夹中)进行模式识别研究。每组只需要对其中的一至两幅图像进行深入的实验研究,要求准确地分割和识别高分辨率数字图像中的所有目标。

  我也只是完成了其中的分割部分。分割的基本原理就是根据图像每个像素的信息对其进行分类,通常比如颜色、灰度相近的话会当做一类。这里使用了opencv的k-means算法和mean-shift算法做了个简单的分割。另外单独实现了竞争Hopfield神经网络。

这里需要先介绍k-means算法和mean-shift算法的大概思想,就不贴代码了。

K-means算法

  K-means的主要思想是,首选确定分成K类。之后随机初始化不同的K个中心点,之后对于样本中的每个元素看它与中心点的“距离”,之后根据选择“最近”的作为它的类别。之后根据每类中的所有元素,计算其中心点。不断的重复,直到中心点的变化小于某个临界值或者迭代足够的次数。

  opencv有直接的kmeans函数。

mean-shift算法

  Mean-shift中文称为均值漂移,它的核心思想是从某个点出发,通过不断的迭代,每次迭代都把该点附近范围内的元素重新计算其重心,之后将该点移到该重心,这就完成了一步均值漂移。可以发现,这个迭代是可以收敛的,当漂移的距离小到一定的临界点时,我们就可以认为它是稳定了。

mean-shift.png

  可以发现,对于每个点都做这样的迭代均值漂移,那么它们是可以分为若干类。和k-means不同,mean-shift不需要提前设立好分类数。但也有不好的一面是,有时分类很多,有些分类的中心点距离很近。

  同样opencv也自带有meanshift的相关函数,名为cvPyrMeanShiftFiltering。只需要把确定的spatialRadius和colorRadius和max_level传进去。就可以生成分割后的图像,使用起来比k-means更简单。

竞争Hopfield神经网络

  竞争Hopfield的英文全称是Competitive Hopfield neural network,简称CHNN。

  在题目给出的人工智能算法中,有两类学习模式,一类是有监督学习,有监督学习需要给定训练集并确定好分类,之后在拿给人工神经网络进行处理学习,另外还要制定几组数据用来验证学习效果。另一类是无监督学习,根据样本自身来进行学习,自行猜测答案,不断的修正。通过比较,最后我选定了竞争Hopfield神经网络。因为它不需要提前制定训练集来训练,自适应强,另外它融入了WTA(winner-takes-all)学习机制,使得它的收敛速度很快。

  在本程序中,需要将原图转换成灰度图像(一共256个灰度级),之后统计每个灰度级x的像素数为n_x,设原图一共有N个像素,那么直方图可表示为h_x=n_x/N。另外用d_(x,y)表示灰度之间的距离,这里定义为(y-x)^2。另设期望分成C类。之后可通过最小化类内灰度级之间的平均距离来实现。

  由于一共有256个灰度值和需要分成C类,那么可以使用256*C个神经元组成神经网络。其中V_(x,i)表示(x,i)处神经元的状态,该值只能为0或者1(因为是WTA,winner-takes-all学习机制)

  由于是竞争Hopfield,可构造出如下能量函数:

hopfield_1.png

  根据能量函数,可以推出神经元(x,i)的总输入〖net〗_(x,i)为

hopfield_2.png

  由于WTA学习,每一灰度级上只有具有最大输入的获胜神经元才输出1,其它均为0。所以有:

hopfield_3.png

  根据刚才最后的两个式子不断的迭代,最后可以训练出最终稳定的分类结果。

具体的核心程序如下:

  1. CHNN程序初始化
CHNN::CHNN(int count = 3,int graylevel = 256){
	InitializeRandoms();
	L = graylevel;
	C = count;
	for (int i = 0; i < 256; i++)
	for (int j = 0; j < 256; j++)
		d[i][j] = (i - j)*(i - j);
	memset(V, 0, sizeof(V));
	for (int i = 0; i < L; i++){
		V[i][rand()%C] = 1;
	}
}
  1. 计算net_(x,i)的函数
double CHNN::calnet(int x, int t){
	double tmp1 = 0, tmp2 = 0;
	for (int y = 0; y < L; y++){
		tmp1 += d[x][y] * h[y] * V[y][t];
		tmp2 += h[y] * V[y][t];
	}
	if (tmp1 == 0 || tmp2 == 0) return -1.0*(numeric_limits<double>::max)();
	else return -1.0 * tmp1 / tmp2;
}
  1. 不断的迭代计算〖net〗(x,i)和V(x,i)
void CHNN::RunNetwork(){
	bool finish = false;
	int times = 1;
	while (!finish){
		cout << "running " << times++ << " time"<<endl;
		finish = true;
		for (int i = 0; i < L; i++)
		for (int j = 0; j < C; j++)
			net[i][j] = calnet(i, j);
		for (int i = 0; i < L; i++){
			double tmpdouble = net[i][0];
			int tmpi = 0;
			for (int j = 1; j < C; j++){
				if (net[i][j]>tmpdouble){
					tmpdouble = net[i][j];
					tmpi = j;
				}
			}
			for (int j = 0; j < C; j++){
				if (tmpi == j&&V[i][j] != 1){
					finish = false;
					V[i][j] = 1;
				}
				else if (tmpi != j&&V[i][j] == 1){
					finish = false;
					V[i][j] = 0;
				}
			}
		}
	}
}
  1. 输出分类结果和各分类的聚类中心(某个灰度值x,满足其到同类其它个灰度级之间的平均距离最小)
void CHNN::outputResult(){
	double tmp[100];
	for (int i = 0; i < C; i++) tmp[i] = -1.0*(numeric_limits<double>::max)();
	for (int i = 0; i < L; i++){
		for (int j = 0; j < C; j++){
			if (V[i][j]){
				result[i] = j;
				break;
			}
		}
		cout << i << ' ' << result[i]<< '\t';
		if (i % 4 == 3) cout << endl;
	}
	for (int i = 0; i < L; i++){
		int dqtype = result[i];
		if (net[i][dqtype]>tmp[dqtype]){
			tmp[dqtype] = net[i][dqtype];
			res[dqtype] = i;
		}
	}
	for (int i = 0; i < C; i++){
		cout << i << " " << res[i] << endl;
	}
}
  1. 测试代码,输入图像,计算直方图分布h_x,并调用CHNN进行处理
IplImage* img = cvLoadImage(imgPath, 0);	//cvCreateImage(cvSize(100, 100), 8, 3);
	IplImage* dst = cvCloneImage((img));
	//int kernelSize = 3;
	//cvSmooth(img, img, 2, kernelSize);
	int graypixes[256];
	memset(graypixes, 0, sizeof(graypixes));
	int height = img->height;
	int width = img->width;
	for (int i = 0; i < height; i++){
		for (int j = 0; j < width; j++){
			int val = cvGet2D(img, i, j).val[0];
			graypixes[val]++;
		}
	}
	double N = height*width;
	double grayh[256];
	for (int i = 0; i < 256; i++){
		grayh[i] = graypixes[i] / N;
	}
	CHNN chnn(Para, 256);
	chnn.setH(grayh);
	chnn.RunNetwork();
	chnn.outputResult();
	for (int i = 0; i < height; i++){
		for (int j = 0; j < width; j++){
			int val = cvGet2D(img, i, j).val[0];
			int tmp = chnn.res[chnn.result[val]];
			cvSet2D(dst,i, j, cvScalar(tmp, 0, 0));
		}
	}

大概效果:

hopfield_test1.png

hopfield_test2.png

hopfield_test3.png

收获和心得和问题等

  在kmeans和meanshift的算法上,由于直接调用了opencv的函数,所以只要了解参数就可以了。在基于竞争Hopfield网络的图像分割中,也是首次接触神经网络算法,由于不太会生成测试数据,所以只敢写无监督的神经网络。看了其它同学都用了som,我打算学些不同的,就用了Hopfield。刚开始遇到的难题就是不知道神经网络和图像分割怎么联系起来。看了几篇论文之后,终于有了些眉目。但是没有找到现成的代码,也让我无法保证我的理解和其它人的是否有偏差。其中主要的原因就是,要求提供分类数,但是使用了256*C神经元去训练时,发现部分分类是没有元素的。也就是说,比如我定了参数为5,想分成5类,但是实际上可能只分出了3类。也就是说有某类i其所有V_(x,i)都为0。

  另外,有个遗憾是只做了灰度值上的CHNN,所以其抗噪性能差,区域连通性不能很好保证。看了别人论文说可以利用邻域相关信息来达到更好的分割效果。这个看以后再进一步深究吧。

Mikzone