kmeans聚类后如何在图像上绘制质心?

How to plot centroids on image after kmeans clustering?

我有一张彩色图像,想使用 OpenCV 对其进行 k 均值聚类。

这是我要对其进行 k 均值聚类的图像。

这是我的代码:

import numpy as np
import cv2
import matplotlib.pyplot as plt

image1 = cv2.imread("./triangle.jpg", 0)
Z1 = image1.reshape((-1))

Z1 = np.float32(Z1)

criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 10, 1.0)

K1 = 2

ret, mask, center =cv2.kmeans(Z1,K1,None,criteria,10,cv2.KMEANS_RANDOM_CENTERS)

center = np.uint8(center)
print(center)
res_image1 = center[mask.flatten()]
clustered_image1 = res_image1.reshape((image1.shape))

for c in center:
        plt.hlines(c, xmin=0, xmax=max(clustered_image1.shape[0], clustered_image1.shape[1]), lw=1.)

plt.imshow(clustered_image1)
plt.show()

这是我从 center 变量中得到的。

[[112]
 [255]]

这是输出图像

我的问题是我无法理解输出。我在 center 变量中有两个列表,因为我想要两个 类。但是为什么它们只有一个值呢?

不应该是这样的吗(这是有道理的,因为质心应该是点):

[[x1, y1]
[x2, y2]]

而不是这个:

[[x]
[y]]

如果我像这样将图像读取为彩色图像:

image1 = cv2.imread("./triangle.jpg")
Z1 = image1.reshape((-1, 3))

我得到这个输出:

[[255 255 255]
 [ 89 173   1]]

彩色图像输出

有人可以向我解释如何获得二维点而不是线吗?另外,如何解释使用彩色图像时从 center 变量获得的输出?

如果我有任何不清楚的地方,请告诉我。谢谢!!

K-Means-clustering 查找相似值的聚类。您的输入是一组颜色值,因此您可以找到描述这 2 个簇的颜色。 [255 255 255] 是白色,[ 89 173 1] 是绿色。与灰度版本中的 [112][255] 类似。你正在做的是 color quantization

它们是正确的质心,但它们的尺寸是颜色,而不是位置。因此你不能在任何地方绘制它。你可以,但我看起来像这样:


看看 'color location' 如何确定每个像素属于哪个 class?

这不是您可以在图片中找到的东西。您可以做的是找到属于不同簇的像素,并使用找到的像素的位置来确定它们的质心或 'average' 位置。

要得到每种颜色的'average'位置,就得根据它们所属的class/color分离出像素坐标。在下面的代码中,我使用了 np.where( img <= 240),其中 240 是阈值。我不方便使用 240,但您可以使用 K-Means 来确定阈值的位置。 (inRange() 在某些时候可能会有用))如果您对坐标求和并将其除以找到的像素数,您将得到我认为您正在寻找的内容:

结果:

代码:

import cv2 

# load image as grayscale
img = cv2.imread('D21VU.jpg',0)

# get the positions of all pixels that are not full white (= triangle)
triangle_px = np.where( img <= 240)
# dividing the sum of the values by the number of pixels
# to get the average location
ty = int(sum(triangle_px[0])/len(triangle_px[0]))
tx = int(sum(triangle_px[1])/len(triangle_px[1]))
# print location and draw filled black circle
print("Triangle ({},{})".format(tx,ty))
cv2.circle(img, (tx,ty), 10,(0), -1)


# the same process, but now with only white pixels
white_px = np.where( img > 240)
wy = int(sum(white_px[0])/len(white_px[0]))
wx = int(sum(white_px[1])/len(white_px[1]))
# print location and draw white filled circle
print("White: ({},{})".format(wx,wy))
cv2.circle(img, (wx,wy), 10,(255), -1)

# display result
cv2.imshow('Result',img)
cv2.waitKey(0)
cv2.destroyAllWindows()

这是一个 Imagemagick 解决方案,因为我不精通 OpenCV。

基本上,我将您的实际图像(来自评论中的 link)转换为二进制,然后使用图像矩提取质心和其他统计信息。

我怀疑您可以在基于 Imagemagick 的 OpenCV、Skimage 或 Python Wand 中做类似的事情。 (参见示例:

https://docs.opencv.org/3.4/d3/dc0/group__imgproc__shape.html#ga556a180f43cab22649c23ada36a8a139

https://scikit-image.org/docs/dev/api/skimage.measure.html#skimage.measure.moments_coords_central

https://en.wikipedia.org/wiki/Image_moment)


输入:

您的图像不只有两种颜色。也许这张图片没有仅应用 2 种颜色的 kmeans 聚类。所以我将使用我构建的 Imagemagick 脚本来做到这一点。

kmeans -n 2 -m 5 img.png img2.png

final colors:
count,hexcolor
99234,#65345DFF
36926,#27AD0EFF


然后我通过简单地设置阈值并将动态范围拉伸为全黑和白,将两种颜色转换为黑和白。

convert img2.png -threshold 50% -auto-level img3.png


然后我得到白色像素的所有图像力矩统计数据,其中包括相对于图像左上角的 x,y 质心(以像素为单位)。还包括等效椭圆长短轴、长轴夹角、椭圆偏心率、椭圆等效亮度,加上8个Hu像矩。

identify -verbose -moments img3.png
  Channel moments:
    Gray:
      --> Centroid: 208.523,196.302 <--
      Ellipse Semi-Major/Minor axis: 170.99,164.34
      Ellipse angle: 140.853
      Ellipse eccentricity: 0.197209
      Ellipse intensity: 106.661 (0.41828)
      I1: 0.00149333 (0.380798)
      I2: 3.50537e-09 (0.000227937)
      I3: 2.10942e-10 (0.00349771)
      I4: 7.75424e-13 (1.28576e-05)
      I5: 9.78445e-24 (2.69016e-09)
      I6: -4.20164e-17 (-1.77656e-07)
      I7: 1.61745e-24 (4.44704e-10)
      I8: 9.25127e-18 (3.91167e-08)