From ea2119f04c7d59634f3147cd6025906a3217afde Mon Sep 17 00:00:00 2001 From: Christian Date: Tue, 31 Oct 2017 09:52:30 +0200 Subject: [PATCH 1/4] added ability to return vector as pytorch floatTensor, instead of numpy array --- img_to_vec.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/img_to_vec.py b/img_to_vec.py index ca088b2..97511d2 100644 --- a/img_to_vec.py +++ b/img_to_vec.py @@ -26,7 +26,7 @@ def __init__(self, cuda=False, model='resnet-18', layer='default', layer_output_ std=[0.229, 0.224, 0.225]) self.to_tensor = transforms.ToTensor() - def get_vec(self, img): + def get_vec(self, img, tensor=False): """ Parameters: img: PIL image @@ -47,7 +47,10 @@ def copy_data(m, i, o): h_x = self.model(image) h.remove() - return my_embedding.numpy() + if tensor: + return my_embedding + else: + return my_embedding.numpy() def _get_model_and_layer(self, model_name, layer): if model_name == 'resnet-18': From 5784156676fe6f80c7415881830739f35d3db92e Mon Sep 17 00:00:00 2001 From: Christian Date: Tue, 31 Oct 2017 10:00:49 +0200 Subject: [PATCH 2/4] updated docstrings --- img_to_vec.py | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/img_to_vec.py b/img_to_vec.py index 97511d2..c91ee38 100644 --- a/img_to_vec.py +++ b/img_to_vec.py @@ -8,9 +8,11 @@ class Img2Vec(): def __init__(self, cuda=False, model='resnet-18', layer='default', layer_output_size=512): - """ - Parameters: - see docs: https://github.com/christiansafka/img2vec + """ Img2Vec + :param cuda: If set to True, will run forward pass on GPU + :param model: String name of requested model + :param layer: String or Int depending on model. See more docs: https://github.com/christiansafka/img2vec.git + :param layer_output_size: Int depicting the output size of the requested layer """ self.cuda = cuda self.layer_output_size = layer_output_size @@ -27,11 +29,10 @@ def __init__(self, cuda=False, model='resnet-18', layer='default', layer_output_ self.to_tensor = transforms.ToTensor() def get_vec(self, img, tensor=False): - """ - Parameters: - img: PIL image - Returns: - Numpy vector representing input image + """ Get vector embedding from PIL image + :param img: PIL Image + :param tensor: If True, get_vec will return a FloatTensor instead of Numpy array + :returns: Numpy ndarray """ if self.cuda: image = Variable(self.normalize(self.to_tensor(self.scaler(img))).unsqueeze(0)).cuda() @@ -53,6 +54,11 @@ def copy_data(m, i, o): return my_embedding.numpy() def _get_model_and_layer(self, model_name, layer): + """ Internal method for getting layer from model + :param model_name: model name such as 'resnet-18' + :param layer: layer as a string for resnet-18 or int for alexnet + :returns: pytorch model, selected layer + """ if model_name == 'resnet-18': model = models.resnet18(pretrained=True) if layer == 'default': From 09eb882230c5dd8ced778caaa740dfc8b223c47c Mon Sep 17 00:00:00 2001 From: Christian Date: Tue, 31 Oct 2017 14:06:48 +0200 Subject: [PATCH 3/4] t-sne visualization --- example/test_visualize.py | 50 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 example/test_visualize.py diff --git a/example/test_visualize.py b/example/test_visualize.py new file mode 100644 index 0000000..5f9cb40 --- /dev/null +++ b/example/test_visualize.py @@ -0,0 +1,50 @@ +import sys +import os +sys.path.append("..") # Adds higher directory to python modules path. +from img_to_vec import Img2Vec +from PIL import Image +from sklearn.manifold import TSNE +import numpy as np +from ggplot import * +import pandas as pd + +input_path = './test_images' +files = os.listdir(input_path) + +img2vec = Img2Vec() +vec_length = 512 # Using resnet-18 as default + +samples = 50 # Amount of samples to take from input path + +vec_mat = np.zeros((samples, vec_length)) +sample_indices = np.random.choice(range(0, len(files)), size=samples, replace=False) + +# For each test sample, we store the label and append the img vector to our matrix +labels = [] +for index, i in enumerate(sample_indices): + file = files[i] + filename = os.fsdecode(file) + img = Image.open(os.path.join(input_path, filename)) + vec = img2vec.get_vec(img) + vec_mat[index, :] = vec + if 'dog' in filename: + labels.append('dog') + elif 'cat' in filename: + labels.append('cat') + elif 'face' in filename: + labels.append('face') + +t_sne = TSNE() + +tsne_output = t_sne.fit_transform(vec_mat) + +df_tsne = pd.DataFrame(columns=['x-tsne', 'y-tsne', 'label']) +df_tsne['x-tsne'] = tsne_output[:, 0] +df_tsne['y-tsne'] = tsne_output[:, 1] +df_tsne['label'] = labels + +chart = ggplot(df_tsne, aes(x='x-tsne', y='y-tsne', color='label')) \ + + geom_point(size=90, alpha=0.8) \ + + ggtitle("tSNE dimensions colored by image label") + +print(chart) From 8704a1e9bd422798309e96865f977e699b061542 Mon Sep 17 00:00:00 2001 From: Christian Date: Tue, 31 Oct 2017 14:51:45 +0200 Subject: [PATCH 4/4] added clustering example --- example/test_clustering.py | 50 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 example/test_clustering.py diff --git a/example/test_clustering.py b/example/test_clustering.py new file mode 100644 index 0000000..222c33d --- /dev/null +++ b/example/test_clustering.py @@ -0,0 +1,50 @@ +import sys +import os +sys.path.append("..") # Adds higher directory to python modules path. +from img_to_vec import Img2Vec +from PIL import Image +import numpy as np +from sklearn.cluster import KMeans +from sklearn.decomposition import PCA + +input_path = './test_images' +files = os.listdir(input_path) + +img2vec = Img2Vec() +vec_length = 512 # Using resnet-18 as default + +samples = 5 # Amount of samples to take from input path +k_value = 2 # How many clusters + +vec_mat = np.zeros((samples, vec_length)) +sample_indices = np.random.choice(range(0, len(files)), size=samples, replace=False) + +print('Reading images...') +for index, i in enumerate(sample_indices): + file = files[i] + filename = os.fsdecode(file) + img = Image.open(os.path.join(input_path, filename)) + vec = img2vec.get_vec(img) + vec_mat[index, :] = vec + +print('Applying PCA...') +reduced_data = PCA(n_components=2).fit_transform(vec_mat) +kmeans = KMeans(init='k-means++', n_clusters=k_value, n_init=10) +kmeans.fit(reduced_data) + +for i in set(kmeans.labels_): + try: + os.mkdir('./' + str(i)) + except FileExistsError: + continue + +print('Predicting...') +preds = kmeans.predict(reduced_data) + +print('Moving images...') +for index, i in enumerate(sample_indices): + file = files[i] + filename = os.fsdecode(file) + os.rename(input_path + '/' + filename, './' + str(preds[index]) + '/' + filename) + +print('Done!')