You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

13 KiB

Лабораторная работа 3. «Кластеризация изображений»

назад

В рамках данной лабораторной работы будет производится кластеризация собранных из социальной сети изображений.

1. Скачивание фотографий из социальной сети

При выполнении данной лабораторной работы будем использовать vk_api для выполнения запросов https://vk.com/dev/photos к социальной сети вконтакте.

Код загрузки фотографий приведён ниже.

import os
import sys
import vk_api
import urllib

LOGIN = ''
PASSWORD = ''

target_ids = [123124, -1212412] 
# идентификаторы сообщества имеют отрицательные значения.

def auth_handler():
    """ При двухфакторной аутентификации вызывается эта функция.
    """
    # Код двухфакторной аутентификации
    key = input("Enter authentication code: ")
    # Если: True - сохранить, False - не сохранять.
    remember_device = True
    return key, remember_device

def main():
    # ======= Открываем сессию  с VK =======
    vk_session = vk_api.VkApi(LOGIN, PASSWORD,
                              auth_handler=auth_handler  # функция для обработки двухфакторной аутентификации
    )
    try:
        vk_session.auth()
    except vk_api.AuthError as error_msg:
        print(error_msg)
        #return

    vk = vk_session.get_api()
    tools = vk_api.VkTools(vk_session)

    # ======= начинаем перебирать каждого пользователя =======
    for target_id in target_ids:

        # создаем директорию с именем пользователя, если нет
        newpath = os.path.join(sys.path[0], id_user)
        if not os.path.exists(newpath):
            os.makedirs(newpath)

        # посылаем запрос к VK API, count свой, но не более 200
        
        if target_id >= 0:
            # Вариант 1 - скачать все фотографии пользователя
            response = vk.photos.getAll(owner_id=int(target_id), count=3)
        else:
            # Вариант 2 - скачать все фотографии сообщества
            response = tools.get_all("photos.getAll", 100, {'owner_id': target_id})

        # работаем с каждой полученной фотографией
        for i in range(len(response["items"])):
            # берём ссылку на максимальный размер фотографии
            photo_url = str(response["items"][i]["sizes"][len(response["items"][i]["sizes"]) - 1]["url"])

            # скачиваем фото в папку с ID пользователя
            urllib.urlretrieve(photo_url, newpath + '/' + str(response["items"][i]['id']) + '.jpg')


if __name__ == "__main__":
    main()

2. Кластеризация цветов на изображении

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

В рамках данной лабораторной работы будет решаться задача кластерного анализа изображений, собранных из задания 1.

Для начала попробуем обработать одну картинку, а именно, решим задачу определения доминирующих цветов.

Американский веб-разработчик Чарльз Лейфер (Charles Leifer) использовал метод k-средних для кластеризации цветов на изображении. Идея метода при кластеризации любых данных заключается в том, чтобы минимизировать суммарное квадратичное отклонение точек кластеров от центров этих кластеров. На первом этапе выбираются случайным образом начальные точки (центры масс) и вычисляется принадлежность каждого элемента к тому или иному центру. Затем на каждой итерации выполнения алгоритма происходит перевычисление центров масс до тех пор, пока алгоритм не сходится.

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

import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
import cv2
#read image
img = cv2.imread('colors.jpg')
#convert from BGR to RGB
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
#get rgb values from image to 1D array
r, g, b = cv2.split(img)
r = r.flatten()
g = g.flatten()
b = b.flatten()
#plotting
fig = plt.figure()
ax = Axes3D(fig)
ax.scatter(r, g, b)
plt.show()

Данный код обрабатывает изображение и определяет пять самых доминирующих цветов на изображении.

В результате исполнения кода получим график примерно такого вида (рис. 31). На нем можно увидеть распределение пикселей в трехмерном пространстве. Здесь также легко выделить пять доминирующих цветов (рис. 32).

Рис. 31. Распределение пикселей в трехмерном пространстве
Рис. 31. Распределение пикселей в трехмерном пространстве

Рис. 32. Выделение доминирующих цветов
Рис. 32. Выделение доминирующих цветов

Несколько преобразим имеющийся код.

import cv2
from sklearn.cluster import KMeans
class DominantColors:
    CLUSTERS = None
    IMAGE = None
    COLORS = None
    LABELS = None
    def __init__(self, image, clusters=3):
        self.CLUSTERS = clusters
        self.IMAGE = image
    def dominantColors(self):
        #read image
        img = cv2.imread(self.IMAGE)
        #convert to rgb from bgr
        img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
        #reshaping to a list of pixels
        img = img.reshape((img.shape[0] *img.shape[1], 3))
        #save image after operations
        self.IMAGE = img
        #using k-means to cluster pixels
        kmeans = KMeans(n_clusters = self.CLUSTERS)
        kmeans.fit(img)
        #the cluster centers are our dominant colors.
        self.COLORS = kmeans.cluster_centers_
        #save labels
        self.LABELS = kmeans.labels_
        #returning after converting to integer from float
        return self.COLORS.astype(int)
img = 'colors.jpg'
clusters = 5
dc = DominantColors(img, clusters)
colors = dc.dominantColors()
print(colors)

На выходе мы получим пять векторов, где будут закодированы 5 основных цветов в формате RGB.

Далее построим гистограмму использования этих пяти основных цветов. Для этого добавим функцию plotHistogramm() в класс DominantColors.

def plotHistogram(self):
    #labels form 0 to no. of clusters
    numLabels = np.arange(0, self.CLUSTERS+1)
    #create frequency count tables
    (hist, _) = np.histogram(self.LABELS, bins= numLabels)
    hist = hist.astype("float")
    hist /= hist.sum()
    #appending frequencies to cluster centers
    colors = self.COLORS
    #descending order sorting as per frequency count
    colors = colors[(-hist).argsort()]
    hist = hist[(-hist).argsort()]
    #creating empty chart
    chart = np.zeros((50, 500, 3), np.uint8)
    start = 0
    #creating color rectangles
    for i in range(self.CLUSTERS):
        end = start + hist[i] * 500
        #getting rgb values
        r = colors[i][0]
        g = colors[i][1]
        b = colors[i][2]
        #using cv2.rectangle to plot colors
        cv2.rectangle(
            chart, 
            (int(start), 0),
            (int(end), 50), 
            (r,g,b), 
            -1)
        start = end
    #display chart
    plt.figure()
    plt.axis("off")
    plt.imshow(chart)
    plt.show()

Далее вызовем эту функцию.

 dc.plotHistogram()

На выходе получим пропорциональное отображение использования доминирующих цветов на изображении. Пример отображения представлен на рис. 33.

Рис. 33. Отображение использования доминирующих цветов на изображении
Рис. 33. Отображение использования доминирующих цветов на изображении

3. Кластеризация изображении на основе доминирующих цветов

Теперь, когда получена характеристика одного изображения, у нас есть инструмент, с помощью которого можно произвести кластеризацию множества изображений на основе их доминирующих цветов. Для этого необходимо обработать каждое изображение в папке, получить его уникальную цветовую характеристику и сравнить ее с другими изображениями. Поэтому немного модифицируем имеющийся код:

  1. добавим глобальную переменную, в которой будем хранить цветовые характеристики каждой картинки;
  2. за место конкретной картинки будем указывать директорию и будем итерационно обрабатывать каждую картинку.
directory = '<ПУТЬ К ДИРЕКТОРИИ>'

for filename in os.listdir(directory):
    data_iter = []
    filenames.append(filename)
    img = str(directory) + '\\' + filename
    #print (img)
    clusters = 2
    dc = DominantColors(img, clusters)
    colors = dc.dominantColors()
    for i in colors:
        for j in i:
            data_iter.append(j)
    data.append(data_iter)
    print(colors)

Преобразуем список в массив данных и добавим код метода главных компонент для того, чтобы понизить размерность вектора характеристик с 15 до 3.

np_data = np.asarray(data, dtype=np.float32)
import numpy as np
from sklearn.decomposition import PCA
pca = PCA(n_components = 3)
XPCAreduced = pca.fit_transform(np_data)
print(XPCAreduced)
print(filenames)

Далее представим точки изображения на плоскости, где координатами будут элементы вектора характеристик.

xs, ys, zs = np_data[:, 0], np_data[:, 1], np_data[:,2]

from mpl_toolkits.mplot3d import Axes3D

fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.scatter(xs, ys, zs)
ax.set_xlabel('X')
ax.set_ylabel('Y')
ax.set_zlabel('Z')
plt.show()