Python,OpenCV中的K均值聚類
- 1. 效果圖
- 2. 原理
- 2.1 什么是K均值聚類?
- 2.2 K均值聚類程序
- 2.3 cv2.kmeans(z, 2, None, criteria, 10, flags)
- 3. 原始碼
- 3.1 抽樣生成點后聚類
- 3.2 單特征聚類、多特征聚類、顏色量化
- 參考
這篇博客將介紹什么是 K-Means 聚類以及 如何使用 cv2.kmeans() 函式進行資料聚類,
- K-Means Cluster K均值聚類
- cv2.kmeans() 進行資料聚類
1. 效果圖
抽樣生成5堆點后聚類,分別以不同的顏色繪制每一種分類,效果圖1如下:

同樣生成5堆點聚類5,得到效果圖如下:

抽樣生成10堆點后聚類,效果圖如下:

單特征聚類圖:
T恤size只根據身高進行分類為3種

2個特征聚類圖:
T恤size根據身高和體重共同來進行聚類,

顏色量化以減少色彩數量,并且得到新的影像,原圖 VS 聚類15種色彩效果圖如下:
聚類色彩數越多,演算法要相對慢一些,但看起來跟原圖越接近,

2. 原理
2.1 什么是K均值聚類?
考慮一家公司將向市場推出一種新型 T 恤,顯然,他們將不得不制造不同尺寸的模型以滿足各種尺寸的人,于是公司做了一個人的身高體重資料,繪制成圖表,如下圖:

公司無法生產所有尺寸的 T 恤,相反,他們將人分為小、中、大,只生產這三種適合所有人的型號,這種將人分成三組可以通過 k-means 聚類來完成,演算法為我們提供了最好的 3 種尺寸,這將滿足所有人的需求,如果沒有,公司可以將人分成更多組,可能是五個,如下圖:

2.2 K均值聚類程序
K均值聚類是一個迭代程序,最基本的K均值聚類有以下4個步驟,
-
假設有一堆點,要分成2類;
-
隨機選擇倆個點C1,C2作為質心,計算周圍點到C1,C2的距離,距離C1近則歸入C1類,距離C2近則歸入C2類,
-
分割完后,計算C1類所有點的平均值作為C1的新質心,計算C2類所有點的平均值作為C2的新質心;
-
迭代2,3程序,根據設定的條件終止(迭代程序達到多少次,或達到設定的精度)終止,
-
最終的收斂是C1到其所有點的距離 + C2到其所有點的距離 = 最小值,

這些點使得測驗資料與其對應的質心之間的距離之和最小,
以上4步只是 K-Means 聚類的頂層,該演算法有很多優化,例如:如何選擇初始質心,如何加快迭代程序,
該迭代程序可以用以下圖片近似表示,分別代表以上4個程序,
下圖迭代1,2步驟,隨機選中點作為質心點,計算距離進行分類;

下圖重新計算質心后迭代3,4步驟然后得到結果圖,可以看到多次迭代后隨機點被分成2堆點,紅色,藍色;質心點也相對居中,比較規整,

2.3 cv2.kmeans(z, 2, None, criteria, 10, flags)
compactness, labels, centers = cv2.kmeans(z, 2, None, criteria, 10, flags)
入參:
- samples: np.float32 資料型別,每個特征放在一個列中,
- nclusters(K) : 最后需要的簇數
- creteria: 迭代終止標準,當滿足此條件時,演算法迭代停止,實際上,它應該是一個包含 3 個引數的元組,
(type、max_iter、epsilon):
a - 終止條件的型別:它有 3 個標志,cv2.TERM_CRITERIA_EPS - 如果達到指定的準確度 epsilon,則停止演算法迭代,cv2.TERM_CRITERIA_MAX_ITER - 在指定的迭代次數 max_iter 后停止演算法,cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER - 當滿足上述任何條件時停止迭代,
b - 最大迭代次數 - 指定最大迭代次數的整數,
c - 精度 - 要求的準確性- attempts :標記以指定使用不同的初始標簽執行演算法的次數,該演算法回傳產生最佳緊湊性的標簽,這種緊湊性作為輸出回傳,
- flags :此標志用于指定如何采用初始中心,通常為此使用兩個標志:cv2.KMEANS_PP_CENTERS 和 cv2.KMEANS_RANDOM_CENTERS,
回傳值:
- compactness: 緊湊度,指每個點到其相應中心的距離平方和
- labels: 標簽陣列,其中每個元素都標記為“0”、“1”…
- centers: 聚簇中心的陣列
3. 原始碼
3.1 抽樣生成點后聚類
# K均值聚類隨機生成一堆點并聚類demo
from __future__ import print_function
import cv2 as cv
import numpy as np
from numpy import random
def make_gaussians(cluster_n, img_size):
points = []
ref_distrs = []
for _i in range(cluster_n):
mean = (0.1 + 0.8 * random.rand(2)) * img_size
a = (random.rand(2, 2) - 0.5) * img_size * 0.1
cov = np.dot(a.T, a) + img_size * 0.05 * np.eye(2)
n = 100 + random.randint(900)
pts = random.multivariate_normal(mean, cov, n)
points.append(pts)
ref_distrs.append((mean, cov))
points = np.float32(np.vstack(points))
return points, ref_distrs
def main():
cluster_n = 5
img_size = 300
# 生成明亮的調色板
colors = np.zeros((1, cluster_n, 3), np.uint8)
colors[0, :] = 255
colors[0, :, 0] = np.arange(0, 180, 180.0 / cluster_n)
colors = cv.cvtColor(colors, cv.COLOR_HSV2BGR)[0]
while True:
print('sampling distributions...')
points, _ = make_gaussians(cluster_n, img_size)
term_crit = (cv.TERM_CRITERIA_EPS, 30, 0.1)
_ret, labels, _centers = cv.kmeans(points, cluster_n, None, term_crit, 10, 0)
img = np.zeros((img_size, img_size, 3), np.uint8)
for (x, y), label in zip(np.int32(points), labels.ravel()):
c = list(map(int, colors[label]))
cv.circle(img, (x, y), 1, c, -1)
cv.imshow('kmeans', img)
ch = cv.waitKey(0)
if ch == 27:
break
print('Done')
if __name__ == '__main__':
print(__doc__)
main()
cv.destroyAllWindows()
3.2 單特征聚類、多特征聚類、顏色量化
# OpenCV實作K-Means Cluster K均值聚類
# 輸入引數
# - samples: np.float32 資料型別,每個特征放在一個列中,
# - nclusters(K) : 最后需要的簇數
# - creteria: 迭代終止標準,當滿足此條件時,演算法迭代停止,實際上,它應該是一個包含 3 個引數的元組,它們是(型別、max_iter、epsilon):
# a - 終止條件的型別:它有 3 個標志,如下所示:cv2.TERM_CRITERIA_EPS - 如果達到指定的準確度 epsilon,則停止演算法迭代,
# cv2.TERM_CRITERIA_MAX_ITER - 在指定的迭代次數 max_iter 后停止演算法,
# cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER - 當滿足上述任何條件時停止迭代,
# b - 最大迭代次數 - 指定最大迭代次數的整數,
# c - 精度 - 要求的準確性
# - attempts :標記以指定使用不同的初始標簽執行演算法的次數,該演算法回傳產生最佳緊湊性的標簽,這種緊湊性作為輸出回傳,
# - flags :此標志用于指定如何采用初始中心,通常為此使用兩個標志:cv2.KMEANS_PP_CENTERS 和 cv2.KMEANS_RANDOM_CENTERS,
#
# 輸出引數
#
# - compactness: 緊湊度,指每個點到其相應中心的距離平方和
# - labels: 標簽陣列,其中每個元素都標記為“0”、“1”.....
# - centers: 聚簇中心的陣列
# 1. 只有一個特征的資料(T恤問題,只根據身高)
import numpy as np
import cv2
from matplotlib import pyplot as plt
x = np.random.randint(25, 100, 25)
y = np.random.randint(175, 255, 25)
z = np.hstack((x, y))
z = z.reshape((50, 1))
z = np.float32(z)
plt.hist(z, 256, [0, 256]), plt.show()
# 每當運行 10 次演算法迭代或達到 epsilon = 1.0 的準確度時,停止演算法并回傳
# 定義終止準則 criteria = ( type, max_iter = 10 , epsilon = 1.0 )
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 10, 1.0)
# 設定標志(只是為了避免代碼中的換行符)
flags = cv2.KMEANS_RANDOM_CENTERS
# 應用K均值聚類演算法
compactness, labels, centers = cv2.kmeans(z, 2, None, criteria, 10, flags)
A = z[labels == 0]
B = z[labels == 1]
# 以紅色繪制 A,以藍色繪制 B,以黃色繪制它們的質心,
plt.hist(A, 256, [0, 256], color='r')
plt.hist(B, 256, [0, 256], color='b')
plt.hist(centers, 32, [0, 256], color='y')
plt.title("one feature res")
plt.show()
# 2. 具有多重資料的特征(T恤問題,根據身高、體重)
import numpy as np
import cv2
from matplotlib import pyplot as plt
X = np.random.randint(25, 50, (25, 2)) # 身高
Y = np.random.randint(60, 85, (25, 2)) # 體重
Z = np.vstack((X, Y))
# 轉換為 np.float32
Z = np.float32(Z)
# 定義終止準則以及應用KMeans聚類
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 10, 1.0)
ret, label, center = cv2.kmeans(Z, 2, None, criteria, 10, cv2.KMEANS_RANDOM_CENTERS)
# 分離資料,并拉平flatten
A = Z[label.ravel() == 0]
B = Z[label.ravel() == 1]
# 繪制資料
plt.scatter(A[:, 0], A[:, 1])
plt.scatter(B[:, 0], B[:, 1], c='r')
plt.scatter(center[:, 0], center[:, 1], s=80, c='y', marker='s')
plt.xlabel('Height'), plt.ylabel('Weight')
plt.title("two features res")
plt.show()
## 3. 顏色量化
# 顏色量化是減少影像中顏色數量的程序,這樣做的原因之一是減少記憶體,有時某些設備可能有限制,以至于它只能產生有限數量的顏色,
# 在這些情況下,也會執行顏色量化,這里我們使用 k-means 聚類進行顏色量化,有3個特征,例如 R、G、B,需要將影像重塑為 Mx3 大小的陣列(M 是影像中的像素數),
# 在聚類之后將質心值(也是 R、G、B)應用于所有像素,這樣生成的影像將具有指定數量的顏色,最后需要將其重塑回原始影像的形狀,
import numpy as np
import cv2
img = cv2.imread('images/ml.jpg')
Z = img.reshape((-1, 3))
# 轉換 np.float32
Z = np.float32(Z)
# 定義終止準則以及應用KMeans聚類
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 10, 1.0)
K = 15
ret, label, center = cv2.kmeans(Z, K, None, criteria, 10, cv2.KMEANS_RANDOM_CENTERS)
# 轉換回uint8, 制作原始影像
center = np.uint8(center)
res = center[label.flatten()]
res2 = res.reshape((img.shape))
cv2.imshow("origin",img)
cv2.imshow('res2', res2)
cv2.waitKey(0)
cv2.destroyAllWindows()
參考
- https://docs.opencv.org/3.0-beta/doc/py_tutorials/py_ml/py_kmeans/py_kmeans_understanding/py_kmeans_understanding.html#kmeans-clustering-understanding
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/292601.html
標籤:其他
