본문 바로가기
AI 인공지능/AI Vision

이미지 유사도 embedding

by yscho03 2021. 12. 29.
728x90
반응형

검증 목표

resnet50 모델을 사용하여 이미지 전체 데이터에 관해서 임베딩을 하여 유사한 거리에 있는 이미지를 추천한다.

사전준비

이미지 분류가 되어있는 폴더 안에 이미지가 N장 준비되어 있다.
이 파일들을 가지고 데이터셋을 생성하고 유사도를 검증해 볼 생각이다.

데이터셋 준비

from mpl_toolkits.mplot3d import Axes3D
from sklearn.preprocessing import StandardScaler
import matplotlib.pyplot as plt
import matplotlib.image as mpimg
import numpy as np 
import pandas as pd
import os 
import sklearn
# 데이터가 많으므로 테스트에서는 1000건만 처리하도록 한다.
NUM_ROWS = 1000

DATASET_PATH = "D:\\dataset\\sample"
print(os.listdir(DATASET_PATH))
# ['bag', 'coat', 'jacket', 'shirt', 'skirt']
categories = []
filenames = []
images = []

df = pd.DataFrame(columns=['category', 'filename', 'image'])
for folder in os.listdir(DATASET_PATH):    
    for file in os.listdir(os.path.join(DATASET_PATH, folder)):
        categories.append(folder)  
        filenames.append(file)
        images.append(os.path.join(DATASET_PATH, folder, file))

df['category'] = categories
df['filename'] = filenames
df['image'] = images

df = df.iloc[np.random.permutation(df.index)].reset_index(drop=True)
df = df[:NUM_ROWS]

df.head(10)
 categoryfilenameimage
0coatsample1.jpgD:\dataset\sample\sample1.jpg
1shirtsample2.jpgD:\dataset\sample\sample2.jpg
2shirtsample3.jpgD:\dataset\sample\sample3.jpg

이미지 시각화

import cv2
import matplotlib.pyplot as plt
import numpy as np
# 딕셔너리 데이터를 인자값을 받아서 이미지를 출력한다.
def plot_figures(figures, nrows = 1, ncols=1,figsize=(5, 3)):    
    fig, axeslist = plt.subplots(ncols=ncols, nrows=nrows, figsize=figsize)
    for ind,title in enumerate(figures):
        axeslist.ravel()[ind].imshow(cv2.cvtColor(figures[title], cv2.COLOR_BGR2RGB))
        axeslist.ravel()[ind].set_title(title)
        axeslist.ravel()[ind].set_axis_off()
    plt.tight_layout()
# 이미지 경로를 읽어서 numpy로 변환한다.
# 기본 0.1 비율로 리사이즈를 처리한다.
def load_image(img_path, resized_fac = 0.1):
    img  = cv2.imread(img_path)
    w, h, _ = img.shape
    resized = cv2.resize(img, (int(h*resized_fac), int(w*resized_fac)), interpolation = cv2.INTER_AREA)
    return resized
figures = {'sample'+str(i) : load_image(row.image) for i, row in df.sample(6).iterrows()}
figures.keys()
plot_figures(figures, 2, 3)
# 막대 그래프로 타입별로 갯수 출력
plt.figure(figsize=(6, 3))
df.category.value_counts().sort_values().plot(kind='barh')

추천 모델 (resnet50) 사용

import tensorflow as tf
import keras
from keras import Model
from keras.applications.resnet50 import ResNet50
from keras.preprocessing import image
from keras.applications.resnet50 import preprocess_input, decode_predictions
from keras.layers import GlobalMaxPooling2D
tf.__version__
# 입력 이미지
img_width, img_height, img_channel = 224, 224, 3

# 훈련된 모델 사용
base_model = ResNet50(weights='imagenet', include_top=False, input_shape = (img_width, img_height, img_channel))
base_model.trainable = False

model = keras.Sequential([
    base_model,
    GlobalMaxPooling2D()
])

model.summary()
Model: "sequential_4"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
resnet50 (Model)             (None, 7, 7, 2048)        23587712  
_________________________________________________________________
global_max_pooling2d_4 (Glob (None, 2048)              0         
=================================================================
Total params: 23,587,712
Trainable params: 0
Non-trainable params: 23,587,712
_________________________________________________________________

전체 데이터 임베딩

def get_embedding(model, img_path):
    # Pillow 이미지 로드
    img = image.load_img(img_path, target_size=(img_width, img_height))
    # numpy 데이터로 변환 (224, 224, 3)
    x = image.img_to_array(img)
    # 차원을 추가해준다. (1, 224, 224, 3)
    x = np.expand_dims(x, axis=0)
    x = preprocess_input(x)
    # 1차원의 배열로 재배열해준다. [[5.232, 2.12, ...]] -> [5.232, 2.12, ...]
    return model.predict(x).reshape(-1)
# 첫 행 이미지 임베딩을 출력
emb = get_embedding(model, df.iloc[0].image)
emb.shape
# (2048,)
# 첫 행 이미지 시각화
img_array = load_image(df.iloc[0].image)
plt.figure(figsize = (2,2))
plt.imshow(cv2.cvtColor(img_array, cv2.COLOR_BGR2RGB))
print(img_array.shape)
print(emb)
# (42, 30, 3)
# [2.853276   8.025141   1.2043287  ... 0.17113084 0.45344853 8.057759  ]
%%time

# 전체 데이터 임베딩
df_sample = df
map_embeddings = df_sample['image'].apply(lambda img: get_embedding(model, img))
df_embs = map_embeddings.apply(pd.Series)

print(df_embs.shape)
df_embs.head()

(1000, 2048)
Wall time: 49.5 s

 
 

 0123456...2041204220432044204520462047
02.8532768.0251411.2043293.5099950.0000000.0172244.020164...4.9262729.7210590.6272012.8479000.1711310.4534498.057759
111.0379019.6104943.2514221.8415706.1558550.3005330.000000...3.2318232.3247852.4152662.8733973.0334424.2739429.356295
26.24956911.4928231.3107590.0000002.6599371.0609476.596041...0.1206670.0000000.0000001.8623073.5810213.85677514.282220
34.45265615.1252803.3682663.5617700.0000002.3161343.634057...0.0000000.0000000.6233733.6066570.0000005.1770929.513126
44.1575379.7346140.4297341.0336242.8467680.0000003.768886...0.0000002.0472600.4387554.2123110.0000001.66025612.978857

5 rows × 2048 columns

유사도 계산

from sklearn.metrics.pairwise import pairwise_distances

# cosine 거리 계산
pairwise_distances(df_embs, metric='cosine')

# 정규화
cosine_sim = 1 - pairwise_distances(df_embs, metric='cosine')
cosine_sim[:4, :4]
array([[1.        , 0.6729132 , 0.7783639 , 0.7777964 ],
       [0.6729132 , 1.        , 0.68061113, 0.6479302 ],
       [0.7783639 , 0.68061113, 0.99999917, 0.6702682 ],
       [0.7777964 , 0.6479302 , 0.6702682 , 0.9999989 ]], dtype=float32)

유사한 이미지

가까운 거리에 있는 유사한 이미지를 출력한다.

indices = pd.Series(range(len(df)), index=df.index)

def get_recommender(idx, df, top_n = 5):
    sim_idx = indices[idx]
    sim_scores = list(enumerate(cosine_sim[sim_idx]))
    sim_scores = sorted(sim_scores, key=lambda x: x[1], reverse=True)
    sim_scores = sim_scores[1:top_n+1]
    idx_rec    = [i[0] for i in sim_scores]
    idx_sim    = [i[1] for i in sim_scores]
    
    return indices.iloc[idx_rec].index, idx_sim
# 유사한 데이터 출력해보기
idx_ref = 1
idx_rec, idx_sim = get_recommender(idx_ref, df, top_n = 6)

plt.figure(figsize = (2,2))
plt.imshow(cv2.cvtColor(load_image(df.iloc[idx_ref].image), cv2.COLOR_BGR2RGB))

figures = {'img-'+str(i): load_image(row.image) for i, row in df.loc[idx_rec].iterrows()}
plot_figures(figures, 2, 3)

임베딩 시각화

from sklearn.manifold import TSNE
import time
import seaborn as sns
time_start = time.time()
tsne = TSNE(n_components=2, verbose=0, perplexity=40, n_iter=300)
tsne_results = tsne.fit_transform(df_embs)
print('t-SNE done! Time elapsed: {} seconds'.format(time.time()-time_start))
df['number'] = tsne_results[:,0]
df['distance'] = tsne_results[:,1]

plt.figure(figsize=(10,6))
sns.scatterplot(x="number", y="distance", hue="category", data=df, legend="full", alpha=0.8)
728x90
반응형

'AI 인공지능 > AI Vision' 카테고리의 다른 글

YOLOv7에 대해 알아보자  (0) 2022.11.22
YOLOv7를 활용한 Object Detection  (0) 2022.11.21
패션 의류 분류 (Fashion Classification)  (0) 2021.09.15
Fashion MNIST  (0) 2021.09.12
Object Detection 개념  (0) 2021.09.12