본문 바로가기

인공지능/코드구현

Distance & Angle을 활용한 이미지 유사도 계산


전체코드 및 결과에 대한 내용은 아래 GitHub 에 PDF 로 올려두었으니 참고해 직접 작성해보면 도움될 듯하다.


< 구현내용 >

이미지 벡터간 유클라디언 거리 또는 내각 계산을 활용하여 이미지간 유사도를 찾는 실험을 수행하였는데, 그 내용은 다음과 같다. 

  1. 이미지벡터간 거리비교
  2. 거리기반 가장 유사한 이미지 도출
  3. MNIST 클래스별 유사성 비교

아래는 MNIST 데이터셋의 거리를 활용하여 유사성을 계산한 코드이며 세부 실험별 내용은 아래와 같다.

 

(1) 이미지벡터간 거리비교

직관적으로 같은 클래스 내의 이미지일수록 혹은 유사한 이미지일수록 거리값이 낮은 것을 확인할 수 있다. 

import numpy as np
import matplotlib.pyplot as plt
from ipywidgets import interact
from sklearn.datasets import fetch_openml

# MNIST 데이터셋 다운로드
MNIST = fetch_openml('mnist_784', version=1)

images = MNIST['data'].to_numpy().astype(np.double) / 255. # 정규화는 안해도 됨
labels = MNIST['target'].to_numpy().astype(np.int)

# 이미지 벡터간 거리계산
def distance(x0, x1):
    x = x0 - x1
    distance = np.sqrt(x @ x)
    return distance

# 이미지 벡터간 각도계산
def angle(x0, x1):
    nominator = x0@x1
    denominator = np.sqrt((x0@x0)*(x1@x1))
    angle = np.arccos(nominator/denominator)
    return angle
    

# 500장 이미지 벡터간 거리비교
distances = []
for i in range(len(images[:500])):
    for j in range(len(images[:500])):
        distances.append(distance(images[i], images[j]))
        
@interact(first=(0, 499), second=(0, 499), continuous_update=False)
def show_img(first, second):
    plt.figure(figsize=(8,4))
    f = images[first].reshape(28, 28)
    s = images[second].reshape(28, 28)

    ax0 = plt.subplot2grid((2, 2), (0, 0))
    ax1 = plt.subplot2grid((2, 2), (1, 0))
    ax2 = plt.subplot2grid((2, 2), (0, 1), rowspan=2)

    ax0.imshow(f, cmap='gray')
    ax1.imshow(s, cmap='gray')
    ax2.hist(np.array(distances), bins=50)
    d = distance(f.ravel(), s.ravel())
    ax2.axvline(x=d, ymin=0, ymax=40000, color='C1', linewidth=4)
    ax2.text(0, 16000, "Distance is {:.2f}".format(d), size=12)
    ax2.set(xlabel='distance', ylabel='number of images')
    plt.show()

 

이미지 벡터간 거리를 히스토그램 분포로 나타내어 실제 두 이미지간에 거리가 얼마나 차이나는지 비교한 결과

 

(2) 거리기반 가장 유사한 이미지 도출

계산된 거리를 이용해서 가장 유사한 이미지의 인덱스값을 반환받아 실제로 정말 유사한지 비교해보니, 얼핏보면 거의 같은 이미지라고 생각될 수 있을정도로 유사한 것으로 확인할 수 있다.

# 가장 가까운 거리의 이미지 인덱스 찾기

def most_similar_image(idx):
    distances = np.zeros((500))
    for i in range(500):
        distances[i] = distance(images[idx], images[i])
    idx = np.where(distances == sorted(distances)[1])[0] # 자기 자신은 제외
    return idx

@interact(idx=(0, 499), continuous_update=False)
def show_most_similar(idx):
    plt.figure(figsize=(8,4))
    f = images[idx].reshape(28, 28)
    similar_idx = most_similar_image(idx)
    s = images[similar_idx].reshape(28, 28)
    
    ax0 = plt.subplot(1,2,1)
    ax1 = plt.subplot(1,2,2) 
    ax0.imshow(f, cmap='gray')
    ax0.set_title('original')
    ax1.imshow(s, cmap='gray')
    ax1.set_title('most_similar')
    plt.show()

왼쪽 입력이미지(original)와 가장 유사한 이미지(most_similar)를 오른쪽에 보여주는 결과 

(3) MNIST 클래스별 유사성 비교

클래스별 평균을 가지고 유사도를 비교했을 때 어느 클래스끼리 비슷한지 확인할 수 있고, 여기서는 거리기반보다 벡터간 각도를 통해 유사도(코사인유사도)를 계산한 것이 기대했던 바와 더 근사했다. 아래는 거리 기반으로 평가한 결과를 시각화한 내용이다.  

# 클래스별 유사도 비교

mean_images = {}
for n in np.unique(labels):
    mean_images[n] = np.mean(images[labels==n], axis=0)

MD = np.zeros((10, 10))
AG = np.zeros((10, 10))
for i in mean_images.keys():
    for j in mean_images.keys():
        MD[i, j] = distance(mean_images[i], mean_images[j])
        AG[i, j] = angle(mean_images[i].ravel(), mean_images[j].ravel())
        
# 결과 시각화

fig, ax = plt.subplots(figsize=(8,8))
grid = ax.imshow(MD, interpolation='nearest')
ax.set(title='Distances between different classes of digits', 
       xticks=range(10), 
       xlabel='class of digits', 
       ylabel='class of digits',
       yticks=range(10))
fig.colorbar(grid)
plt.show()

0~9까지 클래스별 거리비교 그래프 (어두운 쪽에 가까울수록 유사함) 


728x90
반응형