Сегментация изображения на кластеры.
Алгоритмы кластеризации — это тип алгоритмов обучения без учителя, широко используемый для выполнения исследовательского анализа данных без использования целевых меток или когда мы просто хотим разделить набор данных на несколько кластеров. В дополнение к типичным сценариям анализа данных, обычно встречающимся в большинстве задач по науке о данных, оказывается, что алгоритмы кластеризации также используются в других областях, таких как компьютерное зрение! В этой статье мы рассмотрим, как выполнять сегментацию изображений с помощью K-средних!
Алгоритм кластеризации K-средних
Алгоритм кластеризации K-средних пытается разбить набор данных на k кластеров, при этом i-й кластер определяется его центроидом µᵢ . Центроид каждого кластера µᵢ по существу представляет собой среднее значение всех точек nᵢ в этом кластере.
Таким образом, алгоритм K-средних пытается найти наиболее подходящие значения центроидов k для всего набора данных, используя следующие шаги:
- Случайным образом выберите k точек данных из набора данных в качестве исходных k значений центроида.
- Для каждой точки данных в наборе данных назначьте ее ближайшему центроиду. Расстояние измеряется с помощью евклидова расстояния.
- Пересчитайте значение k центроидов как среднее значение всех nᵢ точек данных, назначенных ему.
- Повторяйте шаги 2–3 до тех пор, пока не будет достигнуто максимальное количество итераций или некоторое значение допуска, или назначения кластеров не сойдутся.
После завершения шага 4 алгоритм k-средних должен минимизировать инерцию I всех k кластеров:
I = ΣᵢΣₗ||xᵢₗ - µᵢ||²,
где первый индекс суммирования i проходит по всем k кластерам, а второй индекс суммирования l проходит по всем nᵢ точки данных в каждом кластере. xᵢₗ — это l-я точка данных в i-м кластере, а µ ᵢ — значение центра тяжести i-го кластера.
Какое значение k использовать?
Одна из основных проблем с алгоритмом кластеризации K-средних заключается в том, что нам нужно указать количество кластеров k для использования. Для чрезвычайно простых низкоразмерных наборов данных, как показано на рисунке ниже, идеальное значение k можно легко угадать, визуализируя точки данных.
К сожалению, большинство данных в реальной жизни не образуют хороших сферических кластеров, а количество измерений настолько велико, что о визуализации данных не может быть и речи.
Один из методов, который мы можем использовать для определения идеального количества кластеров, — это инерция кластеров I. Обычно, когда мы увеличиваем количество кластеров k с 2, инерция I быстро уменьшается. По мере того, как мы приближаемся к идеальному значению кластеров, уменьшение I начинает выходить на плато, образуя изгиб на графике зависимости I от k. . Для простого набора 2D-данных выше мы обнаружили, что изгиб инерции I возникает при k = 4.
Сегментация изображения с использованием K-средних
Теперь, когда мы знаем, как работают алгоритмы кластеризации K-средних, мы будем использовать алгоритм K-средних для сегментации изображения, загруженного с Pexels.
Кажется, что изображение имеет 4 различных цвета: белые стены, мебель и платье, оранжевые настенные плитки и пол, темно-синее платье и бежевый оттенок кожи, но давайте посмотрим, как алгоритм K-средних сегментирует изображение!
Подготовка изображения для кластеризации с помощью K-средних
Во-первых, мы загружаем загруженное изображение, используя cv2
как трехмерный массив numpy
. Исходное изображение очень большое — мы изменим размер изображения до гораздо меньшего размера, чтобы сократить время вычислений.
Кроме того, поскольку мы будем использовать sklearn
для выполнения кластеризации K-средних, нам нужно будет изменить форму массива в форму, которую может принять sklearn
API.
import cv2 import numpy as np import matplotlib.pyplot as plt from sklearn.cluster import KMeans import tqdm # Photo by Ferdinand Studio, downloaded from Pixels. file_path = "pexels-ferdinand-studio-1020057.jpg" img = cv2.imread(file_path) img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) def resize(image, width = None, height = None, scale = 1): # Resizes an RGB image. if width is None or height is None: width, height = image_width_height(image) width = int(width * scale) height = int(height * scale) return cv2.resize(image, (width, height)) def image_width_height(image): # Get an RGB image's width and height. width = image.shape[1] height = image.shape[0] return width, height def image2X(image, width, height, channels = 3): # Converts an RGB image to an array X for use with sklearn. return image.reshape([width * height, channels]) def X2image(X, width, height, channels = 3): # Converts an array X from sklearn to an RGB image. return X.reshape([height, width, channels]) # Resize the image to a 400 x 600 resolution. img = resize(img, width = 400, height = 600) width, height = image_width_height(img) # Convert the image to an array which can be input into # sklearn's API. X = image2X(img, width, height, 3) print(img.shape, X.shape) >>> (600, 400, 3) (240000, 3)
Использование метода локтя для определения количества кластеров k
Затем мы используем метод локтя, чтобы определить идеальное количество кластеров k для сегментации изображения.
# Try 2 to 30 clusters. n_clusters = list(range(2, 30 + 1, 1)) kmeans = [] inertias = [] for i in tqdm.trange(len(n_clusters)): kmeans.append(KMeans(n_clusters = n_clusters[i], random_state = 42)) kmeans[-1].fit(X) inertias.append(kmeans[-1].inertia_) plt.figure(figsize = [20, 5]) plt.subplot(1, 2, 1) plt.plot(n_clusters, inertias, "-o") plt.xlabel("$k$", fontsize = 14) plt.ylabel("Inertia", fontsize = 14) plt.grid(True) plt.subplot(1, 2, 2) plt.plot(n_clusters[:-1], np.diff(inertias), "-o") plt.xlabel("$k$", fontsize = 14) plt.ylabel("Change in inertia", fontsize = 14) plt.grid(True) plt.show()
На графике слева показано, как изменяется инерция I при увеличении количества кластеров k от 2 до 30, а на графике справа показана скорость изменения инерция I относительно k: dI/dk.
Оба графика показывают, что инерция I выходит на плато после k = 6, что означает, что загруженное изображение лучше всего сегментировать с использованием 6 кластеров!
Сегментация загруженного изображения с помощью 6 кластеров
Наконец, мы используем 6 кластеров для сегментации исходного изображения и получаем значения центроидов кластера для каждого пикселя изображения. Затем мы преобразуем выходной массив numpy
в трехканальное изображение RGB для визуализации.
# Use 6 clusters in the K-means. kmeans = KMeans(n_clusters = 6, random_state = 42) kmeans.fit(X) # Obtain the cluster centroids for each pixel in the image. # These centroids are essentially our image segments. X_kmeans = kmeans.cluster_centers_[kmeans.predict(X)] X_kmeans = X_kmeans.astype("uint8") # Convert the numpy array into an RGB image. img_kmeans = X2image(X_kmeans, width, height, 3) # Visualize the original image with the segmented one. plt.figure(figsize = [10, 10]) plt.subplot(1, 2, 1) plt.imshow(img) plt.subplot(1, 2, 2) plt.imshow(img_kmeans) plt.show()
Алгоритму K-средних удалось разделить исходное изображение на следующие 6 сегментов:
- Белые стены, мебель, платье и отдельные участки кожи.
- Настенная и напольная плитка оранжевого цвета.
- Светло-бежевый оттенок кожи.
- Бежевые тона кожи.
- Темно-бежевые тона кожи и тени на мебели и стенах.
- Темное сине-черное платье и темные тени.
Заключительные замечания
K-means — чрезвычайно простой и эффективный алгоритм, используемый для кластеризации данных в k отдельных кластеров без использования целевых меток. Этот мощный алгоритм можно использовать не только в типичных задачах анализа данных, но и для других задач, таких как сегментация изображений. Одной из проблем с алгоритмом K-средних является определение количества кластеров k, которое нужно использовать, и мы показали, как использовать метод локтя для определения идеального значения k. Мы затем использовали алгоритм K-средних для сегментации изображения на несколько сегментов, каждый из которых представлен средним значением всех пикселей в этом кластере.
Рекомендации
- Дмитрий Джульгаков, Лю Юйси и Себастьян Рашка (2022). Машинное обучение с помощью PyTorch и Scikit-Learn, Packt Publishing Ltd.
- https://docs.opencv.org/4.x/d1/d5c/tutorial_py_kmeans_opencv.html
- https://scikit-learn.org/stable/modules/generated/sklearn.cluster.KMeans.html