这是《智能机器人》的课程设计(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中文称为均值漂移,它的核心思想是从某个点出发,通过不断的迭代,每次迭代都把该点附近范围内的元素重新计算其重心,之后将该点移到该重心,这就完成了一步均值漂移。可以发现,这个迭代是可以收敛的,当漂移的距离小到一定的临界点时,我们就可以认为它是稳定了。
可以发现,对于每个点都做这样的迭代均值漂移,那么它们是可以分为若干类。和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,可构造出如下能量函数:
根据能量函数,可以推出神经元(x,i)的总输入〖net〗_(x,i)为
由于WTA学习,每一灰度级上只有具有最大输入的获胜神经元才输出1,其它均为0。所以有:
根据刚才最后的两个式子不断的迭代,最后可以训练出最终稳定的分类结果。
具体的核心程序如下:
- 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;
}
}
- 计算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;
}
- 不断的迭代计算〖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;
}
}
}
}
}
- 输出分类结果和各分类的聚类中心(某个灰度值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;
}
}
- 测试代码,输入图像,计算直方图分布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));
}
}
大概效果:
收获和心得和问题等
在kmeans和meanshift的算法上,由于直接调用了opencv的函数,所以只要了解参数就可以了。在基于竞争Hopfield网络的图像分割中,也是首次接触神经网络算法,由于不太会生成测试数据,所以只敢写无监督的神经网络。看了其它同学都用了som,我打算学些不同的,就用了Hopfield。刚开始遇到的难题就是不知道神经网络和图像分割怎么联系起来。看了几篇论文之后,终于有了些眉目。但是没有找到现成的代码,也让我无法保证我的理解和其它人的是否有偏差。其中主要的原因就是,要求提供分类数,但是使用了256*C神经元去训练时,发现部分分类是没有元素的。也就是说,比如我定了参数为5,想分成5类,但是实际上可能只分出了3类。也就是说有某类i其所有V_(x,i)都为0。
另外,有个遗憾是只做了灰度值上的CHNN,所以其抗噪性能差,区域连通性不能很好保证。看了别人论文说可以利用邻域相关信息来达到更好的分割效果。这个看以后再进一步深究吧。