diff --git a/Retrieve_combined_features.ipynb b/Retrieve_combined_features.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..6c4931f4d54b72a6d0919c955f17a2456051d63d --- /dev/null +++ b/Retrieve_combined_features.ipynb @@ -0,0 +1,309 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "f8b76e74", + "metadata": {}, + "outputs": [], + "source": [ + "import pandas as pd\n", + "import numpy as np\n", + "import matplotlib.pyplot as plt\n", + "from PIL import Image\n", + "from annoy import AnnoyIndex\n", + "import random\n", + "\n", + "base_dir = 'path_to_the_dataset'\n", + "top_count = 100\n", + "top_K = 11" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "43c9c917", + "metadata": {}, + "outputs": [], + "source": [ + "#the 1st column is index, the second is path to the paintings, rest columns are pre-calculated embeddings\n", + "df_artist = pd.read_csv('dino_features_wikiart_artist.csv')\n", + "df_genre = pd.read_csv('dino_features_wikiart_genre.csv')\n", + "df_style = pd.read_csv('dino_features_wikiart_style.csv')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "be5120fe", + "metadata": {}, + "outputs": [], + "source": [ + "def build_index(df, filename):\n", + " NUMBER_OF_TREES = 25\n", + " feature_dim = len(df.columns) - 2\n", + "\n", + " t = AnnoyIndex(feature_dim, metric='euclidean')\n", + "\n", + " for i in range(len(df)):\n", + " vector = df.loc[i][2:]\n", + " t.add_item(i, vector)\n", + " \n", + " _ = t.build(NUMBER_OF_TREES)\n", + "\n", + " #save indexes\n", + " t.save(base_dir + filename)\n", + " return t" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "afa13d41", + "metadata": {}, + "outputs": [], + "source": [ + "t_genre = build_index(df_genre, 'dino_genre.ann')\n", + "t_style = build_index(df_style, 'dino_style.ann')\n", + "t_artist = build_index(df_artist, 'dino_artist.ann')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "feff4c2f", + "metadata": {}, + "outputs": [], + "source": [ + "#download saved indexes\n", + "feature_dim = len(df_genre.columns) - 2\n", + "\n", + "t_genre = AnnoyIndex(feature_dim, metric='euclidean')\n", + "t_genre.load('dino_genre.ann')\n", + "\n", + "t_style = AnnoyIndex(feature_dim, metric='euclidean')\n", + "t_style.load('dino_style.ann')\n", + "\n", + "t_artist = AnnoyIndex(feature_dim, metric='euclidean')\n", + "t_artist.load('dino_artist.ann')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ee4c29ab", + "metadata": {}, + "outputs": [], + "source": [ + "#receive similar images using ANNOY\n", + "def get_similar_images_annoy(vector, t):\n", + " indices, dists = t.get_nns_by_vector(vector, top_count+1, include_distances=True)\n", + " return indices, dists" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1b10c4d0", + "metadata": {}, + "outputs": [], + "source": [ + "def show_similar(img_str, img_list, k, distances):\n", + " img = Image.open(base_dir + img_str)\n", + " plt.title('Request painting')\n", + " plt.axis(\"off\")\n", + " plt.imshow(img)\n", + "\n", + " plt.figure(figsize=(30, 30))\n", + " ax = plt.subplot(1, k, 1)\n", + " \n", + " i = 0\n", + " for key in img_list:\n", + " ax = plt.subplot(1, k, i+1)\n", + " img = Image.open(base_dir + key)\n", + " plt.title(\"{}\".format(round(distances[i], 4)))\n", + " plt.imshow(img)\n", + " i+=1\n", + "\n", + " plt.axis(\"off\")\n", + " plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "29ccae77", + "metadata": {}, + "outputs": [], + "source": [ + "#request image\n", + "img_str = 'Impressionism/pierre-auguste-renoir_children-on-the-seashore-1883.jpg'\n", + "\n", + "#pre-calculated embeddings of the request painting\n", + "img_req_g = np.array(df_genre[df_genre['file_path']==img_str])[0][2:]\n", + "img_req_s = np.array(df_style[df_style['file_path']==img_str])[0][2:]\n", + "img_req_a = np.array(df_artist[df_artist['file_path']==img_str])[0][2:]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9c2c37c1", + "metadata": {}, + "outputs": [], + "source": [ + "similar_images_g = []\n", + "similar_images_s = []\n", + "similar_images_a = []\n", + "\n", + "similar_img_ids_g, distances_g = get_similar_images_annoy(img_req_g, t_genre)\n", + "similar_images_g = list(df_genre.iloc[similar_img_ids_g]['file_path'])\n", + "\n", + "similar_img_ids_s, distances_s = get_similar_images_annoy(img_req_s, t_style)\n", + "similar_images_s = list(df_style.iloc[similar_img_ids_s]['file_path'])\n", + "\n", + "similar_img_ids_a, distances_a = get_similar_images_annoy(img_req_a, t_artist)\n", + "similar_images_a = list(df_artist.iloc[similar_img_ids_a]['file_path'])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "eac7636a", + "metadata": {}, + "outputs": [], + "source": [ + "#pre-calculated values of mean and std of eucledian distances on WikiArt dataset\n", + "mean_genre = 19.6129883775738\n", + "std_genre = 2.9717604654152274\n", + "mean_style = 18.328733753985198\n", + "std_style = 2.5707980486965547\n", + "mean_artist = 20.973407871326454\n", + "std_artist = 2.9063593849149183" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "08e0941d", + "metadata": {}, + "outputs": [], + "source": [ + "# Calculating cosine distance\n", + "#request painting belongs to the dataset, so we ignore the first similar painting\n", + "df_genre_sim = df_genre[df_genre['file_path'].isin(similar_images_g)]\n", + "cosines_genre = distances_g[1:]\n", + "\n", + "df_style_sim = df_style[df_style['file_path'].isin(similar_images_s)]\n", + "cosines_style = distances_s[1:]\n", + "\n", + "df_artist_sim = df_artist[df_artist['file_path'].isin(similar_images_a)]\n", + "cosines_artist = distances_a[1:]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b48542d3", + "metadata": {}, + "outputs": [], + "source": [ + "d = {'file_path': list(df_genre_sim['file_path'].iloc[1:]), 'd_g': cosines_genre}\n", + "df_g = pd.DataFrame(data=d)\n", + "\n", + "d = {'file_path': list(df_style_sim['file_path'].iloc[1:]), 'd_s': cosines_style}\n", + "df_s = pd.DataFrame(data=d)\n", + "\n", + "d = {'file_path': list(df_artist_sim['file_path'].iloc[1:]), 'd_a': cosines_artist}\n", + "df_a = pd.DataFrame(data=d)\n", + "\n", + "df_u = pd.merge(df_g, df_s, on='file_path', how='outer')\n", + "df_u = pd.merge(df_u, df_a, on='file_path', how='outer')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "92bb15c8", + "metadata": {}, + "outputs": [], + "source": [ + "def func(x):\n", + " missing_indices = x.index[x.isnull()]\n", + " for index in missing_indices:\n", + " if x.name == 'd_g':\n", + " img_sim = df_u.loc[index, 'file_path']\n", + " eucl_distance = np.linalg.norm(img_req_g - df_genre[df_genre['file_path'] == img_sim].iloc[0, 2:])\n", + " x.loc[index] = eucl_distance\n", + " elif x.name == 'd_s': \n", + " img_sim = df_u.loc[index, 'file_path']\n", + " eucl_distance = np.linalg.norm(img_req_s - df_style[df_style['file_path'] == img_sim].iloc[0, 2:])\n", + " x.loc[index] = eucl_distance\n", + " elif x.name == 'd_a': \n", + " img_sim = df_u.loc[index, 'file_path']\n", + " eucl_distance = np.linalg.norm(img_req_a - df_artist[df_artist['file_path'] == img_sim].iloc[0, 2:])\n", + " x.loc[index] = eucl_distance\n", + "\n", + " return x\n", + "\n", + "df_u = df_u.apply(func, axis=0)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7e464ba0", + "metadata": {}, + "outputs": [], + "source": [ + "df_u['d_g'] = (df_u['d_g'] - mean_genre)/std_genre\n", + "df_u['d_s'] = (df_u['d_s'] - mean_style)/std_style\n", + "df_u['d_a'] = (df_u['d_a'] - mean_style)/std_style" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5ea5a057", + "metadata": {}, + "outputs": [], + "source": [ + "#combined recommendations with the same weights\n", + "df_u['combined'] = df_u['d_g'] + df_u['d_s'] + df_u['d_a']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b9e0d83f", + "metadata": {}, + "outputs": [], + "source": [ + "paintings = list(df_u.sort_values(by=['combined'])['file_path'])[:top_K]\n", + "dist = list(df_u.sort_values(by=['combined'])['combined'])[:top_K]\n", + "show_similar(img_str, paintings, top_K, dist)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.14" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +}