近一个月不见,光忙着上课、作报告、回家、结课了,下午才结束数字图像处理的视频报告,可以作更新了。
其实分类之前应该还有个迭代阈值分割的内容,不过这个内容比较简单,还不知道要不要讲,所以打算先讲Kmeans分类。
1.Kmeans
其实在网上能搜到好多资料,这里直接结合代码和大家讲得更细一点。
首先,熟悉给定的几个变量:
System.Drawing.Bitmap old_bitmap //输入的需要分类的BMP图像
int cluster_count //分类的类数
double stop_rate //停止分类的变化率
int maxIter //最大分类迭代次数
在这里我们使用的是像素的RGB作为分类标准,在C#中一个像素也就是一个Color类,所以我们的任务就是对BMP里的所有像素,也就是所有Color进行分类。进行分类之前,我们需要一定数量的聚类中心,这些聚类中心的类型也是Color。先来初始化这些聚类中心:
Random rd = new Random();
Color[] cluster_center = new Color[cluster_count]; //聚类中心数组
for(int i = 0;i<cluster_count;i++)//随机初始化
{
cluster_center[i] = new Color(System.Drawing.Color.FromArgb(rd.Next(0, 255), rd.Next(0, 255), rd.Next(0, 255)));
}
对我们的聚类中心数组cluster_center进行了初始化获得了指定数量的初始聚类中心Color。
那么接下来,我们需要计算,聚类中心和单个像素之间的距离,也就是两个Color之间的距离。我采用的是欧氏距离,函数如下:
static public double Dis_between_color(System.Drawing.Color c1, System.Drawing.Color c2)
{
double trt_dis = (c1.R - c2.R) * (c1.R - c2.R) + (c1.G - c2.G) * (c1.G - c2.G) + (c1.B - c2.B) * (c1.B - c2.B);
return Math.Pow(trt_dis, 1.0 / 3.0);
}
那么,我们就可以遍历bmp上每一个像素,计算它到每一个聚类中心的距离,然后找出最小的距离,把这个像素分到这一类中。(由于我自己没手敲Kmeans,这里就不放代码给大家了)
这里有一个问题,就是我们知道该像素属于某一类之后,我们要怎么去存储这个类别信息。其实很简单,给大家作个图:
假设我们有左边这么一张BMP图,那么我们就创建一个相同形状的int型二维矩阵如右图示。遍历bmp每个像素,讲像素归属的类放到这个二维矩阵中,如:
这样的设计就可通过一组坐标分别访问像素的Color值与类别了。
经过遍历,可以得到每个像素的类别,获得这个类别矩阵,那接下来就是更新聚类中心了。更新聚类中心需要计算每一类的均值,那我们就可以定义一个 cluster_count行3列 的double数组,来存储新聚类中心的值。
遍历每一个像素,访问它的类别并将 该像素的RGB值 累加 在 刚刚定义的数组的对应行 中,在遍历完成后,再把 该数组的每一个数 除以 该类的像素个数 即可得到平均值 用作新的聚类中心了。
在满足循环条件下不停循环即可。
最后会得到一个分类矩阵,对于不同的类用不同的颜色显示,输出新的bitmap即可。
这篇代码少了很多,因为我自己也没手打Kmeans。