Интересная реализация алгоритма кластеризации K-средних

Вступление

Я только что закончил курс машинного обучения Эндрю Н.Г. на Coursera и был рад попробовать свои силы в различных проектах, реализовав все, что я узнал за последние пять месяцев, помимо еженедельного задания курса. Как и все остальные, я поискал в Google несколько интересных проектов машинного обучения для начинающих, когда наткнулся на эту идею. Этот проект не был новаторским или чем-то в этом роде, но дал мне хороший старт в этой области.

Это руководство поможет вам реализовать алгоритм кластеризации K-средних для извлечения доминирующих цветов из существующего изображения. Прежде чем углубляться в код, давайте рассмотрим предысторию алгоритма K-средних.

Алгоритм кластеризации K-средних

Одним из самых популярных, но простых алгоритмов без учителя является алгоритм K-средних. Для всех точек данных, разбросанных в n-мерном пространстве, он группирует точки данных с некоторым сходством в один кластер. После случайной инициализации k центроидов кластера алгоритм выполняет итеративно два шага:

  1. Назначение кластера: каждой точке данных назначается кластер в зависимости от ее расстояния от центроида кластера.
  2. Переместить центроид: вычисляется среднее значение всех точек кластера, и центроид кластера перемещается в среднее положение.

В соответствии с новым расположением центроидов точки данных переназначены кластерам.

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

Теперь, когда мы знаем, как работает алгоритм K-средних, давайте погрузимся в код для извлечения цветов из существующего изображения.

Мы будем следовать функциональному подходу к разработке этой программы, то есть вся программа разбита на несколько функций. Импорт модулей, необходимых для этой программы, приведен ниже.

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

В аргументе кластеров вам нужно будет указать количество цветов, которые вы хотите извлечь из изображения, в то время как путь к изображению используется для передачи пути к изображению с именем изображения. По умолчанию программа извлекает из изображения 5 цветов и выбирает изображение с именем poster.jpg из папки images. Вы можете установить значения по умолчанию по вашему выбору. Мы также определим WIDTH и HEIGHT для изменения размера изображения перед извлечением из него цветов. Я сохранил ширину и высоту 128 пикселей.

Для шестнадцатеричных кодов и соответствующих им названий цветов я использовал файл JSON. Весь словарь названий цветов и их шестнадцатеричных кодов взят из файла JavaScript (доступного для общего пользования), приведенного ниже:
http://chir.ag/projects/ntc/ ntc.js (файл JavaScript)
http://chir.ag/projects/ntc/ (ссылка на сайт автора)

Мы будем читать файл JSON в переменной с именем color_dict. Теперь пары ключ-значение JSON могут быть легко доступны в нашей программе с помощью этой словарной переменной.

Давайте теперь начнем с использования изображения в качестве входных данных и передачи его в алгоритм K-средних.

Как видите, функция TrainKMeans принимает файл изображения в качестве аргумента. На самом первом этапе мы изменяем размер изображения до размеров, которые мы определили ранее в программе. Опять же, если вы заметили, я использовал пользовательскую функцию для изменения размера изображений.

В моей настраиваемой функции изменения размера я изменил размер более длинного размера изображения до фиксированного размера HEIGHT или WIDTH и изменил масштаб другого измерения, сохранив соотношение высоты к ширине изображения постоянным.

Возвращаясь к функции TrainKMeans, после изменения размера изображения я конвертирую изображение в массив numpy, а затем преобразовываю его в трехмерный вектор для представления значений RGB на следующем шаге.

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

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

Это очень простая функция, использующая функцию to_hex из matplotlib.colors. Я нормализовал значения RGB, чтобы они лежали в диапазоне от 0 до 1, а затем преобразовал их в соответствующие шестнадцатеричные коды. Теперь у нас есть шестнадцатеричные коды каждого цветового кластера.

На следующем этапе мы находим названия каждого цвета с помощью функции findColorName ().

В функции findColorName мы вызываем еще одну пользовательскую функцию с именем get_color_name (), которая возвращает два значения: aname (фактическое имя) и cname (имя ближайшего цвета).

В этой функции я использую сторонний модуль webcolors для преобразования RGB в название цвета. По умолчанию функция webcolors ищет в списке цветов CSS3. Если он не может найти цвет в своем списке, он вызывает ошибку ValueError, которую я обработал с помощью другой пользовательской функции, называемой closest_colour (). В этой функции я вычисляю евклидово расстояние между входными значениями RGB и всеми значениями RGB, присутствующими в JSON. Затем выбирается и возвращается цвет с наименьшим расстоянием от входного значения RGB.

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

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

Затем в столбцах color и color_name я сохранил шестнадцатеричный код и соответствующие им названия цветов для каждого пикселя изображения. Наконец, мы возвращаем фрейм данных cluster_map и объект kmeans.

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

Это все для этой статьи. В следующей части я покажу вам, как создать API для этой программы с помощью Flask. Вы можете найти полный код этого проекта по адресу https://github.com/nandinib1999/DominantColors.

По любым вопросам и предложениям, пожалуйста, свяжитесь со мной в LinkedIn.

Спасибо за прочтение!

~ Нандини