[PYTHON] J'ai essayé de compresser l'image en utilisant l'apprentissage automatique

original_compressed.png

introduction

Compressons l'image en utilisant ** K-Means **, qui est un algorithme de clustering typique. Tout d'abord, je vais expliquer l'algorithme K-Means. Après cela, j'expliquerai la compression d'image à l'aide de K-Means. Pour le contenu, reportez-vous à Coursera Machine Learning.

Qu'est-ce que le clustering?
Divisez une collection de données en plusieurs groupes (grappes) en fonction de la similitude entre les données.

Algorithme K-Means

Description intuitive de l'algorithme

L'algorithme K-Means peut être grossièrement divisé en trois processus:

Comprenons chaque étape avec une image.

1. Initialisation du centre de gravité

Tout d'abord, ** déterminez la position d'un point appelé centre de gravité **. Chaque centre de gravité est considéré comme un modèle standard pour chaque cluster. Par conséquent, vous avez besoin du centre de gravité pour le nombre de clusters dans lesquels vous souhaitez diviser les données. Dans la figure ci-dessous, le symbole étoile représente le centre de gravité et le symbole circulaire représente les données. Le nombre de centres de gravité étant ici trois, les données sont ** red ** </ font>, ** blue ** </ font>, ** Green ** </ font> Il sera divisé en 3 groupes.

Sur cette figure, le centre de gravité est déterminé aléatoirement, mais nous présenterons une autre méthode plus tard. init.png

2. Allocation de cluster

Après avoir décidé de la position du centre de gravité, l'étape suivante consiste à ** attribuer un cluster à chaque donnée **. Ici, regardez chaque donnée, ** red ** </ font>, ** blue ** </ font>, <font color Attribuez des données à l'un des clusters, selon lequel des centroïdes de cluster de = "green"> ** green ** </ font> est le plus proche. assignment0.png

3. Recalcul du centre de gravité

Après avoir attribué le cluster à toutes les données, ** recalculez la position du centre de gravité **. À ce stade, la valeur moyenne des données dans chaque cluster est définie comme la position du nouveau centre de gravité. Par exemple, pour calculer le centre de gravité de ** green ** </ font>, ajoutez tous les cercles de ** green ** </ font>. De plus, le centre de gravité est calculé en divisant par le nombre. Si vous comparez les chiffres supérieurs et inférieurs, vous pouvez voir que la position du centre de gravité a changé. Surtout, vous pouvez voir que ** blue ** </ font> et ** green ** </ font> bougent. assignment.png

4. Répétez les étapes 2 et 3

Après cela, l'allocation des grappes et le recalcul du centre de gravité sont ** répétés **. Vous pouvez voir à quoi cela ressemble dans l'animation ci-dessous. Ici, la couleur du centre de gravité est noire pour une visualisation facile. À la fin, vous pouvez voir qu'il a convergé et que la position du centre de gravité n'a pas changé. 1449023850iBinuHaXtI5xPbA1449023805.gif

De plus, si vous regardez l'image fixe, vous pouvez voir que le centre de gravité se déplace dans la trajectoire suivante. result.png

Formalisation de l'algorithme

L'algorithme K-Means décrit ci-dessus peut être résumé dans le code comme suit.

#Étape d'initialisation du centre de gravité
centroids = kmeans_init_centroids(X, K)

for iter in range(iterations):

    #Étapes d'allocation de cluster:Attribuez le cluster auquel appartient le centre de gravité le plus proche de chaque donnée.
    #idx est une liste, idx[i]Stocke l'indice du centre de gravité attribué aux i-èmes données
    idx = find_closest_centroids(X, centroids)

    #Étape de mouvement du centre de gravité:Calculer la moyenne dans le cluster en fonction de l'affectation du centre de gravité
    centroids = compute_centroids(X, idx, K)

Il y a deux entrées dans l'algorithme:

  • Nombre de clusters $ K $
  • Ensemble d'entraînement {$ x ^ {(1)}, x ^ {(2)}, ..., x ^ {(m)} $}, $ x ^ {(i)} \ in \ mathbb {R} ^ n $

Puisque K-Means est un apprentissage non supervisé, l'ensemble d'apprentissage n'est pas étiqueté. Chaque donnée d'apprentissage $ x ^ {(i)} $ est un vecteur de dimension $ n $. Chacun correspond au cercle de la figure. De plus, le nombre de grappes doit être déterminé à l'avance.

Maintenant que nous connaissons les données d'entrée, jetons un œil à chaque étape de l'algorithme.

Initialisation du centre de gravité

Tout d'abord, initialisez le centre de gravité. Le centre de gravité est un vecteur de dimension $ n $ représenté par $ u ^ {(j)} $, et il y a $ K $. Sur la figure, cela correspond au point de l'étoile.

u^{(1)}, u^{(2)},...,u^{(K)} \in \mathbb{R}^n

Comme méthode d'initialisation du centre de gravité, à partir de l'ensemble d'apprentissage $ x ^ {(1)}, x ^ {(2)}, ..., x ^ {(m)} \ in \ mathbb {R} ^ n $ Il existe une méthode pour sélectionner au hasard des pièces de $ K $. Plus précisément, après avoir mélangé au hasard l'ensemble d'entraînement, sélectionnez le premier $ K $ comme centre de gravité.

Lorsqu'il est implémenté sous forme de code, il ressemble à ceci:

from numpy import *


def kmeans_init_centroids(X, K):
    #Initialisez pour que le centre de gravité soit des données aléatoires

    #Trier les données d'entraînement de manière aléatoire
    rand_X = random.permutation(X)

    #Adoptez le premier K comme centre de gravité
    centroids = rand_X[:K]

    return centroids

Allocation de cluster

Une fois le centre de gravité initialisé, attribuez un cluster à chaque donnée. Plus précisément, trouvez le centre de gravité $ u_j $ le plus proche de chaque donnée d'apprentissage $ x ^ {(i)} $ et attribuez un cluster de ce centre de gravité.

Pour trouver le centre de gravité le plus proche des données, recherchez le carré de la distance comme suit. Allouez un groupe de centres de gravité qui minimise le carré de la distance.

idx^{(i)} := argmin_j ||x^{(i)} − μ_j||^2

Où $ idx ^ {(i)} $ est l'indice du centre de gravité le plus proche de $ x ^ {(i)} $ et $ u_j $ est la coordonnée du $ j $ ème centre de gravité.

Une implémentation simple utilisant numpy ressemble à ceci:

import sys
from numpy import *


def find_closest_centroids(X, centroids):
    K = centroids.shape[0]
    m = X.shape[0]
    idx = zeros((m, 1))
    for i in range(m):
        lowest = sys.maxint
        lowest_index = 0

        for j in range(K):
            cost = X[i] - centroids[j]
            cost = cost.T.dot(cost)
            if cost < lowest:
                lowest_index = j
                lowest = cost

        idx[i] = lowest_index

    return idx

En utilisant scipy.spatial.distance.cdist de scipy, vous pouvez l'écrire brièvement comme suit. Différentes méthodes de calcul de distance sont définies dans cdist. Parmi eux, cette fois j'ai utilisé la distance au carré. Les performances sont également meilleures ici que le code naïf ci-dessus.

import scipy.spatial.distance
from numpy import *


def find_closest_centroids(X, centroids):
    sqdists = scipy.spatial.distance.cdist(centroids, X, 'sqeuclidean')  #Trouvez la distance au carré
    idx = argmin(sqdists, axis=0)  #Index du centre de gravité le plus proche pour chaque donnée

    return idx

Recalcul du centre de gravité

Après avoir alloué les données au cluster, recalculez le centre de gravité.

Une fois que toutes les données ont été affectées au cluster, l'étape suivante consiste à recalculer le centre de gravité. Ici, nous calculons la moyenne des données affectées au cluster. Pour tout centre de gravité $ k $, exécutez la formule suivante.

u_k := \frac{1}{|C_k|} \sum_{i \in C_k} x^{(i)}

Où $ C_k $ est un jeu de données avec un centre de gravité $ k $ attribué. Plus précisément, si les deux données $ x ^ {(3)} $ et $ x ^ {(5)} $ sont affectées au centre de gravité $ k = 2 $, alors $ u_2 = \ frac {1} {2 } (x ^ {(3)} + x ^ {(5)}) $.

Une implémentation simple ressemblerait à ceci:

from numpy import *


def compute_centroids(X, idx, K):
    m, n = X.shape
    centroids = zeros((K, n))

    for k in range(K):
        temp = X[idx == k]     #Récupérer les données affectées au cluster k
        count = temp.shape[0]  #Nombre de données allouées au cluster k

        for j in range(n):
            centroids[k, j] = sum(temp[:, j]) / count

    return centroids

Si vous l'écrivez court en utilisant la fonction moyenne de numpy, cela ressemblera à ceci.

from numpy import *


def compute_centroids(X, idx, K):
    m, n = X.shape
    centroids = zeros((K, n))

    for k in range(K):
        centroids[k] = mean(X[idx == k], axis=0)

    return centroids

Compression d'image

Aperçu

Maintenant que l'explication de l'algorithme est terminée, passons au sujet principal de la compression d'image. Les images peuvent également être compressées en appliquant l'algorithme K-Means. En gros, utilisez K-Means pour sélectionner la couleur $ K $ utilisée pour représenter l'image compressée. Il compresse en remplaçant les pixels de l'image d'origine par cette couleur $ K $. Dans ce qui suit, il sera expliqué comme $ K = 16 $.

Comment exprimer les couleurs

Si les pixels de l'image sont une représentation couleur 24 bits, ils peuvent être décomposés en 3 représentations 8 bits. Chaque 8 bits est ** rouge ** </ font>, ** bleu ** </ font>, ** Il correspond au vert ** </ font> et s'appelle RVB. Puisqu'il fait 8 bits, il peut prendre des valeurs de 0 à 255 ($ 2 ^ 0 à 2 ^ 8 -1 $) respectivement. C'est pourquoi la combinaison de ces valeurs fait que l'image contient une myriade de couleurs, mais ici nous réduisons ce nombre à $ K = 16 $ couleurs. En réduisant le nombre de couleurs à 16 couleurs, chaque pixel devrait pouvoir être représenté par 4 bits. (16 $ = 2 ^ 4 $)

La figure ci-dessous montre que lorsque la valeur RVB de chaque pixel contenu dans l'image de gauche est mappée dans l'espace 3D, l'image de droite est obtenue. to3d.png

Application des K-Means

En utilisant l'algorithme K-Means, nous sélectionnons les 16 couleurs utilisées pour représenter l'image compressée. Plus précisément, chaque pixel de l'image d'origine est entré en tant que données et regroupé en groupes de 16 couleurs en utilisant l'algorithme K-Means. Une fois le centre de gravité calculé, il est possible de représenter l'image en 16 couleurs en remplaçant la valeur RVB des pixels de l'image d'origine par la valeur RVB du centre de gravité.

L'image est comme indiqué dans la figure ci-dessous. Dans la figure ci-dessous, le symbole étoile représente le centre de gravité, le symbole circulaire représente chaque pixel et les pixels appartenant au même cluster ont la même couleur. A ce moment, la valeur du pixel appartenant au même cluster est remplacée par la valeur du centre de gravité de ce cluster. En parlant de l'image, c'est une image que les valeurs des cercles de même couleur sont agrégées dans la valeur du centre de gravité de l'amas. Pour faciliter le calcul, la valeur RVB a été convertie de 0,0 à 1,0. kmean3d.png

L'animation ci-dessous montre comment K-Means est réellement appliqué à l'image. Vous pouvez voir que l'allocation du cluster et le recalcul du centre de gravité sont répétés. optimize.gif

L'image d'origine et l'image compressée auxquelles K-Means est réellement appliqué. La gauche est l'image originale et la droite est l'image compressée. Vous pouvez voir que l'image compressée a une représentation plus grossière avec moins de couleurs. original_compressed.png

Taille compressée

Une fois que vous avez trouvé les 16 couleurs typiques qui représentent votre image, remplacez la valeur de chaque pixel par la valeur du centre de gravité le plus proche. Cela équivaut à représenter l'image d'origine avec le centre de gravité attribué à chaque pixel. Puisqu'il n'y a que 16 centres de gravité, le nombre de bits requis pour représenter l'image est considérablement réduit. Voyons le nombre de bits requis pour l'image d'origine et l'image compressée.

L'image originale utilisée cette fois est de 128 x 128 pixels. Nous avions besoin de 24 bits d'informations pour chaque pixel. En conséquence, au total

128 x 128 x 24 = 393216 bit

Était nécessaire.

L'image compressée nécessite le nombre de bits pour stocker 16 informations de couleur. De plus, chaque couleur nécessite 24 bits, mais l'image elle-même ne nécessite que 4 bits par pixel. En effet, il suffit de montrer laquelle des 16 couleurs est utilisée dans 4 bits. En conséquence, à la fin

16 x 24 + 128 x 128 x 4 = 65920 bit

Vous pourrez le faire. C'est environ un sixième de la taille de l'image originale.

Exemple de code

en conclusion

Cette fois, j'ai essayé de compresser l'image en utilisant l'algorithme K-Means. L'image du résultat compressé n'est pas bonne, mais je pense que c'est un algorithme avec une large gamme d'applications. Veuillez l'essayer avec l'exemple de code.

référence

Recommended Posts