Как построить центроиды на изображении после кластеризации kmeans?

У меня есть цветное изображение, и я хотел выполнить кластеризацию k-средних с помощью OpenCV.

Это изображение

Это изображение, на котором я хотел выполнить кластеризацию методом 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]]

Вывод цветного изображения

color_image_output

Может кто-нибудь объяснить мне, как я могу получить 2d точки вместо линий? Кроме того, как мне интерпретировать вывод, который я получил от переменной center при использовании цветного изображения?

Пожалуйста, дайте мне знать, если я неясно где-нибудь. Спасибо!!


person roronoa    schedule 10.07.2019    source источник
comment
Зачем вам выполнять кластеризацию kmeans, чтобы найти центр тяжести одного зеленого треугольника? Просто используйте контуры, капли или моменты изображения и получите из них статистику центроидов. Центроиды от kmeans находятся в цветовом пространстве, а не в декартовом пространстве.   -  person fmw42    schedule 11.07.2019
comment
на самом деле, это тестовое изображение. Мое реальное изображение выглядит примерно так: pasteboard.co/IntLuRr.png   -  person roronoa    schedule 11.07.2019


Ответы (2)


Кластеризация K-средних находит кластеры с похожими значениями. Ваш ввод представляет собой массив значений цвета, поэтому вы найдете цвета, которые описывают 2 кластера. [255 255 255] — белый цвет, [ 89 173 1] — зеленый цвет. Аналогично для [112] и [255] в версии с оттенками серого. Вы делаете следующее: квантование цвета

Это правильно центроиды, но их размерность - это цвет, а не местоположение. Поэтому вы не можете построить его нигде. Ну можно, но я выгляжу так:

введите здесь описание изображения
Посмотрите, как "расположение цвета" определяет, к какому классу принадлежит каждый пиксель?

Это не то, что вы можете найти на своем изображении. Что вы можете сделать, так это найти пиксели, принадлежащие разным кластерам, и использовать расположение найденных пикселей для определения их центроида или «среднего» положения.

Чтобы получить «среднее» положение каждого цвета, вы должны разделить координаты пикселей в соответствии с классом/цветом, к которому они принадлежат. В приведенном ниже коде я использовал 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()
person J.D.    schedule 10.07.2019
comment
Спасибо за ответ. так они не центроиды? как получить координаты центроидов двух кластеров? Я хочу нанести эти координаты на изображение. - person roronoa; 11.07.2019

Вот решение Imagemagick, так как я не разбираюсь в OpenCV.

По сути, я конвертирую ваше фактическое изображение (по вашей ссылке в комментариях) в двоичное, а затем использую моменты изображения для извлечения центроида и других статистических данных.

Я подозреваю, что вы можете сделать что-то подобное в OpenCV, Skimage или Python Wand, основанном на Imagemagick. (См., например:

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)


Вход:

введите здесь описание изображения

Ваше изображение состоит не только из двух цветов. Возможно, к этому изображению не применялась кластеризация 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)
person fmw42    schedule 11.07.2019