本文主要介绍如何使用Android Studio4.0+OpenCv4.3.0中的KMeans算法实现图像处理。(阅读本文之前最好对KMeans算法基础原理有一点了解)
KMeans算法,又叫做K均值聚类算法,是一种迭代求解的聚类分析算法,也是非监督的机器学习算法。(大白话:就是把一个东西,按照一定的标准,多次计算后自动分成K个部分)由于其具有原理比较简单、实现也是很容易、收敛速度快等优点,一般作为掌握聚类算法的第一个算法。
关于KMeans算法介绍就说这么多,具体的公式理论什么的网上很多,感兴趣的读者自己研究一下(虽然理论很枯燥,但还是建议认真理解一下)
古人云:工欲善其事必先利其器。大干一场之前,向让我们了解一下相关API以及相关参数。
以下就是关于KMeans算法的API(来自Core.java)
public static double kmeans(Mat data, int K, Mat bestLabels, TermCriteria criteria, int attempts, int flags) {
return kmeans_1(data.nativeObj, K, bestLabels.nativeObj, criteria.type, criteria.maxCount, criteria.epsilon, attempts, flags);
}
相关参数介绍
参数data:需要被处理的图片,参数类型为Mat类型。当然这样不是你随便整一个Mat类型数据往里一塞就完事了,官方给出要求如图1所示。
啥意思?花里胡哨的。
简单的来说,我们需要把导入的图片转换成浮点型的行矩阵或者列矩阵。如果你转换为行矩阵,那么每一列都会作为一个样本,如果你转换为列矩阵,那么每一行都会作为一个样本。具体这么转换第三部分会以代码的形式展现出来。
参数K:就是你想把传入数据分成多少类,直接填数字就好了。当然最小为2,因为你至少要分成两类叭。
参数bestLabels:这个参数是存储每个样本的聚类索引整数数组,可以理解为输出结果存储位置。也就是说你定义一个新的Mat,整个新的Mat会存储通过KMeans运算之后每一个样本到底属于哪一类。如果你上一个参数K填的是2,那么新的Mat中的数据只有0或者,如果你上一个参数K填的是3,那么新的Mat中的数据有0或者1或者2,然后以此类推。
参数criteria:算法终止准则,可以设置最大迭代次也可以设置数期望精度,也可以两个都设置。迭代次数应该就不用解释了,设置数期望精度意思是当聚类的中心点移动小于期望精度时算法就会认为计算完成。
参数attempts:执行算法的次数,最后返回最好的一次结果。
参数flags:这个参数有三个选择,主要作用是设置初始聚类中心。
首先你得有一张花里胡哨的图片
好,那我们先使用KMeans算法做一个简单的以灰度作为分类规则的二分类;
首先我们需要把图片导入Mat矩阵中,导入图片方法有很多也比较简单。这里我默认大家都导入了。我这里把矩阵起名为rgbMat,核心代码以及注释如下。
//创建一个新的Mat(用于存放灰度化图像)
Mat grayMat = new Mat();
//图像灰度化
Imgproc.cvtColor(rgbMat, grayMat, Imgproc.COLOR_RGB2GRAY);
//生成样本,把灰度图像转会为列矩阵(如果你想转换为行矩阵也行 row:1)
Mat sample = grayMat.reshape(0,grayMat.rows()*grayMat.cols());
//创建一个新的Mat(用于格式转换)
Mat sample1 = new Mat();
//将样本转换为浮点型(官方要求)
sample.convertTo(sample1,CvType.CV_32FC1);
//创建一个新的Mat(用于存放样本聚类索引)
Mat label = new Mat();
//指定算法终止规则
TermCriteria term = new TermCriteria((TermCriteria.EPS + TermCriteria.COUNT),1000,0.1);
//使用Kmeans算法运算
Core.kmeans(sample1, 2, label,term, 100, Core.KMEANS_PP_CENTERS);
//创建一个新的Mat(用于存放绘制处理后的图像)
Mat result = new Mat(grayMat.rows(),grayMat.cols(),CvType.CV_8UC3);
int result1 = 0;
//绘制运算后图像
for(int i = 0 ; i < grayMat.rows();i++)
for(int j = 0 ; j < grayMat.cols();j++){
//读取运算后每一个点归属于哪一个类
result1 = (int) label.get(i*grayMat.cols()+j,0)[0];
//归属于0聚类画黑色点,归属于1聚类画白色点
if(result1 == 0)
result.put(i,j,0,0,0);
else if(result1 == 1)
result.put(i,j,255,255,255);
}
//最后把绘制好的 result 图像显示出来
运行之后我们可以看到结果如下,细心的小伙伴会发现这部就是二值化嘛。对得,我们有掌握了一种实现二值化的方面(尽管我们通常不会这么干,哈哈哈)。到这里KMeans算法的使用算是讲完了。
最后让我们瞎搞一下。
上面我们实现了把图像分成两个聚类,如果那我们想实现分成多个聚类那。其实很简单,以分成四个聚类为例。继承上面的代码,首先需要把K值修改为4,然后在绘制图像需要添加两个判断(我直接把if else语句换成switch case语句了)如下图所示。
好好的图片被搞成这个样子。但是不得不承认与原图对比肉眼上看聚类的划分没有任何问题。
那到这里就结束了,感谢您可以看到这里。
希望本文可以对您的学习有帮助,您有任何疑问或者新的想法欢迎评论留言。
本人能力有限,如文中存在错误或者不足还请指出,非常感谢。