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

时间:2019-07-10 15:23:22

标签: python opencv k-means centroid

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

This is the image

这是我要进行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]]

这是输出图像

This is the output image

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

应该不是这样(这很有意义,因为质心应该是点):

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

代替此:

[[x]
[y]]

如果我将图像读为彩色图像,则是这样的:

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

我得到以下输出:

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

彩色图像输出

color_image_output

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

请让我知道我是否不清楚。谢谢!

2 个答案:

答案 0 :(得分:0)

K均值聚类发现相似值的聚类。您的输入是一个颜色值数组,因此可以找到描述2个聚类的颜色。 [255 255 255]是白色,[ 89 173 1]是绿色。灰度版本中的[112][255]类似。您正在做的是color quantization

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

enter image description here
看看“颜色位置”如何确定每个像素属于哪个类别?

这不是您可以在图像中找到的东西。您可以做的是找到属于不同聚类的像素,然后使用找到的像素的位置确定其质心或“平均”位置。

要获得每种颜色的“平均”位置,必须根据像素所属的类/颜色来分离出像素坐标。在下面的代码中,我使用了np.where( img <= 240),其中240是阈值。我很不爽地使用了240,但是您可以使用K-Means来确定阈值应该在哪里。 (inRange()在某些时候可能很有用))如果将坐标相加并除以找到的像素数,将得到我想寻找的内容:

结果:

enter image description here

代码:

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()

答案 1 :(得分:0)

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

基本上,我将您的实际图像(从注释中的链接)转换为二进制图像,然后使用图像矩提取质心和其他统计信息。

我怀疑您可以在基于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)


输入:

enter image description here

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

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

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


enter image description here

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

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


enter image description here

然后,我获得所有白色像素的图像矩统计信息,其中包括相对于图像左上角的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)