본문 바로가기
Programming/AI

[머신러닝/딥러닝] 이미지 검색 - 유사한 이미지 찾기

by 제타 2018. 7. 8.
반응형

이미지 파일들을 비교하는 것은 이미지를 해시로 변경 후에 해시값을 비교하면 된다.

하지만 이미지 밝기를 조절한다거나 등 조금만 조작해도 해시값이 바뀌기 때문에 바로 비교하는 것은 의미가 없다.


이 때 Average Hash를 활용하면 유사한지 검출이 가능하다.


방법은 다음과 같다

1. 이미지의 크기를 줄이고(예를들어 8x8)

2. 색을 그레이스케일로 변환

3. 이미지의 각 픽셀 평균을 구한 뒤

4. 각 픽셀의 어두운 정도가 평균보다 크면 1, 평균보다 작으면 0으로 변환


이런식으로 하면 어떠한 이미지이든지 64비트로 변환된 해시값을 구할 수 있고 이 64비츠만 비교하면 되기 때문에 유사한지 검색할 수 있다.



그럼 한번 테스트 해보자.



먼저 캘리포니아 공과대학에서 제공하는 학습 이미지를 다운받자.

>> 다운로드 바로가기 <<



다운로드 받은 이미지들이 폴더 안에 잘 정리가 되어있다.


ant 폴더에 image_0001.jpg 이미지를 찾는 것을 목표로 하자.


다음 코드를 한번 돌려보자.


from PIL import Image

import numpy as np
import os, re

search_dir = "./image/101_ObjectCategories"
cache_dir = "./image/cache_avhash"
if not os.path.exists(cache_dir):
os.mkdir(cache_dir)
# 이미지를 Average Hash로 변환
def average_hash(fname, size = 16):
fname2 = fname[len(search_dir):]
# 이미지 캐시
cache_file = cache_dir + "/" + fname2.replace('/', '_') + ".csv"
if not os.path.exists(cache_file): # 해시 생성
img = Image.open(fname)
img = img.convert('L').resize((size, size), Image.ANTIALIAS)
pixels = np.array(img.getdata()).reshape((size, size))
avg = pixels.mean()
px = 1 * (pixels > avg)
np.savetxt(cache_file, px, fmt="%.0f", delimiter=",")
else:
px = np.loadtxt(cache_file, delimiter=",")
return px
# 해밍 거리 구하기
def hamming_dist(a, b):
aa = a.reshape(1, -1) # 1차원 배열로 변환
ab = b.reshape(1, -1)
dist = (aa != ab).sum()
return dist
# 모든 폴더에 처리 적용
def enum_all_files(path):
for root, dirs, files in os.walk(path):
for f in files:
fname = os.path.join(root, f)
if re.search(r'\.(jpg|jpeg|png)$', fname):
yield fname
# 이미지 검색
def find_image(fname, rate):
src = average_hash(fname)
for fname in enum_all_files(search_dir):
dst = average_hash(fname)
diff_r = hamming_dist(src, dst) / 256
if diff_r < rate:
yield (diff_r, fname)
# 검색
srcfile = search_dir + "/ant/image_0001.jpg"
html = ""
sim = list(find_image(srcfile, 0.25))
sim = sorted(sim, key=lambda x:x[0])
for r, f in sim:
print(r, ">", f)
s = '<div style="float:left;"><h3>[ 차이 :' + str(r) + '-' + \
os.path.basename(f) + ']</h3>'+ \
'<p><a href="' + f + '"><img src="' + f + '" width=400>'+ \
'</a></p></div>'
html += s
# HTML로 결과 만들기
html = """<html><head><meta charset="utf8"></head>
<body><h3>원래 이미지</h3><p>
<img src='{0}' width=400></p>{1}
</body></html>""".format(srcfile, html)
with open("./avhash-search-output.html", "w", encoding="utf-8") as f:
f.write(html)
print("완료")


실행 결과는 다음과 같다.



그리고 생성된 html파일을 실행해보면 유사한 이미지가 검색되었는데.. 정말 결과가 안습이었다.


어떻게 오토바이, 총 등이 나오는지.. 잘 모르겠지만 어차피 예제 수준의 코드이기 때문에 이를 더 정교화를 해서 정확한 이미지 검색이 되도록 하는 것이 목표일 것이다.


이미지 전체를 해시하지 않고 특정 object를 찾아서 해시한다면 더 좋은 결과를 낼 수 있지 않을까 생각해본다.

반응형