Python版本是Python3.7.3,OpenCV版本OpenCV3.4.1,开发环境为PyCharm
22.3 简单示例
本节通过几个简单示例来说明如何使用OpenCV中提供的函数cv2.kmeans()实现K均值聚类。
eg1:随机生成一组数据,使用函数cv2.kmeans()对其分类。
为了方便理解,假设有两种豆子,其中一种是“xiaoMI”,另外一种是“daMI”。它们的直径不一样,xiaoMI的直径在[0, 50]区间;daMI的直径在[200, 250]区间。用随机数模拟两种豆子的直径,并使用函数cv2.kmeans()对它们分类。
根据题目要求,主要步骤如下:
1.数据预处理
使用随机函数随机生成两组豆子的直径数据,并将它们转换为函数cv2.kmeans()可以处理的格式。
2.设置参数
设置函数cv2.kmeans()的参数形式。将参数criteria的值设置为“(cv2.TERM_CRITERIA_EPS+ cv2.TERM_CRITERIA_MAX_ITER, 10, 1.0)”,在达到一定次数或者满足一定精度时终止迭代。
3.调用函数cv2.kmeans()
调用函数cv2.kmeans(),获取返回值,用于后续步骤的操作。
4.确定分类
根据函数cv2.kmeans()返回的标签(“0”和“1”),将原始数据分为两组。
5.显示结果
绘制经过分类的数据及中心点,观察分类结果。
根据上述分析,编写代码如下:
import numpy as np
import cv2
from matplotlib import pyplot as plt
#随机生成两组数组
#生成60粒直径大小在[0,50]之间的xiaoMI
xiaoMI = np.random.randint(0,50,60)
#生成60粒直径大小在[200,250]之间的daMI
daMI = np.random.randint(200,250,60)
#将xiaoMI和daMI组合为MI
MI = np.hstack((xiaoMI,daMI))
#使用reshape函数将其转换为(120,1)
MI = MI.reshape((120,1))
#将MI的数据类型转换为float32
MI = np.float32(MI)
#调用kmeans模块
#设置参数criteria的值
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 10, 1.0)
#设置参数flags的值
flags = cv2.KMEANS_RANDOM_CENTERS
#调用函数kmeans
retval,bestLabels,centers = cv2.kmeans(MI,2,None,criteria,10,flags)
'''
#打印返回值
print(retval)
print(bestLabels)
print(centers)
'''
#获取分类结果
XM = MI[bestLabels==0]
DM = MI[bestLabels==1]
#绘制分类结果
#绘制原始数据
plt.plot(XM,'ro')
plt.plot(DM,'bs')
#绘制中心点
plt.plot(centers[0],'rx')
plt.plot(centers[1],'bx')
plt.show()
运行上述程序,程序运行结果如下图所示。

在图中,上面的小方块是标签为“0”的数据点,下方的圆点是标签为“1”的数据点。上方的“x”标记是标签为“0”的数据组的中心点,其值大概在225左右;下方的“x”是标签为“1”的数据组的中心点,其值大概在25左右。在需要时,可以通过print语句打印centers[0]和centers[1]获取两个中心点的值。
eg2:有一堆米粒,按照长度和宽度对它们分类。
为了方便理解,假设米粒有两种,其中一种是XM,另外一种是DM。它们的直径不一样,XM的长和宽都在[0, 20]内,DM的长和宽都在[40, 60]内。使用随机数模拟两种米粒的长度和宽度,并使用函数cv2.kmeans()对它们分类。
根据题目要求,主要步骤如下:
(1)随机生成两组米粒的数据,并将它们转换为函数cv2.kmeans()可以处理的形式。
(2)设置函数cv2.kmeans()的参数形式。
(3)调用函数cv2.kmeans()。
(4)根据函数cv2.kmeans()的返回值,确定分类结果。
(5)绘制经过分类的数据及中心点,观察分类结果。
根据上述分析,编写代码如下:
import numpy as np
import cv2
from matplotlib import pyplot as plt
#随机生成两组数值
#xiaomi组,长和宽的大小都在[0,20]
xiaomi = np.random.randint(0,20,(30,2))
#dami组,长和宽的大小都在[40,60]
dami = np.random.randint(40,60,(30,2))
#组合数据
MI = np.vstack((xiaomi,dami))
# 转换为float32类型
MI = np.float32(MI)
# 调用kmeans模块
#设置参数criteria值
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 10, 1.0)
#调用kmeans函数
ret,label,center=cv2.kmeans(MI,2,None,criteria,10,cv2.KMEANS_RANDOM_CENTERS)
'''
#打印返回值
print(ret)
print(label)
print(center)
'''
# 根据kmeans处理结果,将数据分类,分为XM和DM两大类
XM = MI[label.ravel()==0]
DM = MI[label.ravel()==1]
# 绘制分类结果数据及中心点
plt.scatter(XM[:,0],XM[:,1],c = 'g', marker = 's')
plt.scatter(DM[:,0],DM[:,1],c = 'r', marker = 'o')
plt.scatter(center[0,0],center[0,1],s = 200,c = 'b', marker = 'o')
plt.scatter(center[1,0],center[1,1],s = 200,c = 'b', marker = 's')
plt.xlabel('Height'),plt.ylabel('Width')
plt.show()
运行上述程序,结果如下图所示。

在下图中,右上方的小方块是标签为“0”的数据点,左下方的圆点是标签为“1”的数据点。右上方稍大的圆点是标签“0”的数据组的中心点;左下方稍大的方块是标签为“1”的数据组的中心点。
在程序中,“#打印返回值”下面3行的打印语句被注释掉了。在需要时,可以去掉注释,通过print语句打印对应的值。例如,语句“print(center)”可以打印center所表示的两个中心点的值。
使用函数cv2.kmeans()将灰度图像处理为只有两个灰度级的二值图像。
eg3:根据题目要求,需要对灰度图像内的色彩进行分类,将所有的像素点划分为两类。然后,用这两类的中心点像素值替代原有像素值,满足题目的要求。
根据分析,主要步骤如下:
1.图像预处理
读取图像,并将图像转换为函数cv2.kmeans()可以处理的形式。
在读取图像时,如果是3个通道的RGB图像,需要将图像的RGB值处理为一个单独的特征值。具体实现时,用函数cv2.reshape()完成对图像特征值的调整。
为了满足函数cv2.kmeans()的要求,需要将图像的数据类型转换为numpy.float32类型。
2.设置函数cv2.kmeans()的参数形式
设置参数criteria的值为“(cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 10, 1.0)”,让函数cv2.kmeans()在达到一定精度或者达到一定迭代次数时,即停止迭代。
设置参数K的值为2,将所有像素划分为两类。
3.调用函数cv2.kmeans()
调用函数cv2.kmeans(),得到距离值、分类中心点和分类标签,用于后续操作。
4.值替换
将像素点的值替换为当前分类的中心点的像素值。
5.显示变换前后的图像
分别显示原始图像和二值化图像。
根据上述分析,编写代码如下:
import numpy as np
import cv2
import matplotlib.pyplot as plt
#读取待处理图像
img = cv2.imread('lena.bmp')
#使用reshape将一个RGB像素点值的三个值作为一个单元
data = img.reshape((-1,3))
#转换为kmeans可以处理的类型
data = np.float32(data)
#调用kmeans模块
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 10, 1.0)
K =2
ret,label,center=cv2.kmeans(data,K,None,criteria,10,cv2.KMEANS_RANDOM_CENTERS)
# 转换为uint8数据类型,将每个像素点值都赋值为当前组的中心点值
#将center的值转换为uint8
center = np.uint8(center)
#使用center内的值替换原有像素点值
res1 = center[label.flatten()]
#使用reshape调整替换后图像
res2 = res1.reshape((img.shape))
#显示处理结果
plt.subplot(121)
plt.imshow(img)
plt.axis('off')
plt.subplot(122)
plt.imshow(res2)
plt.axis('off')
plt.show()
运行上述程序,结果如下图所示。其中,左图是原始图像,右图是二值化图像。调整程序中的K值,就能改变图像的显示结果。例如,K=8,则可以让图像显示8个灰度级。

本文通过实例展示如何使用OpenCV中的cv2.kmeans()函数进行数据聚类,包括对不同形状和尺寸的米粒分类,以及将灰度图像转化为二值图像的过程。
275

被折叠的 条评论
为什么被折叠?



