利用K_means分割图像
step 1 准备工作
安装opencv
,opencv-python
的版本应该要与python
的版本一致才可以
首先需要查看自己的python
版本号,
这里我的版本号就是3.11.1
,然后根据版本号安装opencv-python
,各版本Opencv的对应的各版本Python
安装包:,这里只需要找到自己所需要的版本号即可,不用手动安装。下面有教程。
https://www.lfd.uci.edu/~gohlke/pythonlibs/#opencv
像我的版本3.11.1
就是cp311
,对应opencv-python-4.5.5
然后看下面这个教程,跟着教程做
opencv 指定版本的安装_opencv_迷路爸爸180-DevPress官方社区 (csdn.net)
安装完成之后,就可以写代码了。
step 2 准备图片
我们目测一下图中有7中不同颜色的区域,故k=7。
代码如下:
import cv2
import numpy as np
# k-means 参数
k = 7
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 100, 0.001)
flags = cv2.KMEANS_RANDOM_CENTERS
# 读取图像
img = cv2.imread('img_1.png')
#img.shape (1000,1000,3)
# 将图像转换为二维数组
Z = img.reshape((-1, 3))#(1000000,3)
#Z必须要把他转化为float32的形式。不转的话下面的cv2.kmeans会报错
Z=np.float32(Z)
# 进行k-means聚类
ret, label, center = cv2.kmeans(Z, k, None, criteria, 10, flags)
#label.shape(1000000,1) label 即把1000000个像素点分类,值为[0-6]
# center.shape(7,3) 即7个中心点,每一个中心点的维度为(1,3) ,三列分别代表RGB的值
# 将结果转换回图像形式
# center = np.uint8(center)
# res = center[label.flatten()]
# res2 = res.reshape((img.shape))
# 设置各类别对应的颜色
colors = [(255, 0, 0), (0, 255, 0), (0, 0, 255), (255, 255, 0), (255, 0, 255), (0, 255, 255), (255, 255, 255)]
# 遍历每个像素点,根据类别给它着色
for i in range(k):
Z[label.flatten() == i] = colors[i]
#Z.shape(1000000,3)
#把着色的像素点 Z.shape为(1000000,3),转化为原图片的维度 即(1000,1000,3).
result = Z.reshape((img.shape))
#展示图片
cv2.imshow('kmeans-image-demo',result)
cv2.waitKey(0)
cv2.destroyAllWindows()
最终的结果为
自己手写K_means
原始的图片
import numpy as np
import cv2
img = cv2.imread('img_5.jpg')
print(img.shape)
img_new = img.reshape((-1, 3))
print(img_new.shape)
k = 5
init_center = np.array([[166,246,255], [22,97,55], [195,161,182], [50,54,36],[0,0,0]])
# init_center = np.array([[172,208,196], [16,93,51], [137,219,210], [65,67,46],[0,0,0]])
#找离自己最近的中心点
def findminest(img_new, init_center, k):
m = img_new.shape[0]
#记录自己所属的类
label = np.zeros(m)
for i in range(m):
minnest = np.inf
for j in range(k):
dis = np.sum((img_new[i, :] - init_center[j, :]) ** 2)
if dis < minnest:
minnest = dis
label[i] = j
return label
#通过算平均值更新中心点
def update_center(img_new, k, label):
new_center = np.zeros((k, 3))
for i in range(k):
new_center[i, :] = np.mean(img_new[i == label, :], axis=0)
return new_center
#迭代更新
def run_K_means(img_new, init_center, k, iters):
m = img_new.shape[0]
label = np.zeros(m)
for i in range(iters):
label = findminest(img_new, init_center, k)
init_center = update_center(img_new, k, label)
print(label)
return label
#迭代次数为10
iters = 10
label = run_K_means(img_new, init_center, k, iters)
#给每一个类着色
colors = [(255, 0, 0), (0, 255, 0), (0, 0, 255), (255, 255, 0),(255,0,255)]
for i in range(k):
img_new[label.flatten() == i] = colors[i]
#展示图片
cv2.imshow('kmeans-image-demo',img)
cv2.waitKey(0)
cv2.destroyAllWindows()
最终的结果:
自己手写fuzzy c-means
原始的图片
import numpy as np
import cv2
#读入图片
img=cv2.imread("img_5.jpg")
#将图像转换为二维数组
img_new=img.reshape((-1,3))
#FCM
#X代表数据,c代表聚类数目,m是加权指标,eps是差别,max_its是最大迭代次数
def FCM(X, c, m, eps, max_its):
sample_num = X.shape[0]
# U为隶属度矩阵
u = np.random.random((sample_num, c))
# U归一化
u = u / np.sum(u, axis=1)[:, np.newaxis]
it = 0
#迭代
while it < max_its:
it += 1
um = u ** m
# 计算中心
center = np.dot(um.T, X) / (np.sum(um.T, axis=1))[:, np.newaxis]
# 计算各个样本点离各中心点的距离
distance = np.zeros((sample_num, c))
for i, x in enumerate(X):
for j, v in enumerate(center):
distance[i][j] = np.sum((x - v) ** 2)
# 计算新的u
new_u = np.zeros((sample_num, c))
for i in range(sample_num):
for j in range(c):
new_u[i][j] = 1. / np.sum((distance[i][j] / distance[i]) ** (2 / (m - 1)))
if np.sum(abs(new_u - u)) < eps:
break
u = new_u
# 返回每一行隶属度最大的类
return np.argmax(u, axis=1)
c = 5
m=2
max_its = 15
eps=1e-15
idx = FCM(img_new, c,m,eps ,max_its)
print(idx)
#给每一个聚类着色
colors = [(255, 0, 0), (0, 255, 0), (0, 0, 255), (255, 255, 0),(255,0,255)]
for i in range(c):
img_new[idx.flatten() == i] = colors[i]
#展示着色之后的图片
cv2.imshow('kmeans-image-demo',img)
cv2.waitKey(0)
cv2.destroyAllWindows()
聚类之后的图片