image processing

Average Hash

  • 이미지를 비교 가능한 해시 값으로 나타낸 것
  • 해시 함수 MD5, SHA256 등을 이용해 데이터 값을 간단한 해시 값으로 변환할 수 있음
  • 이미지가 비슷한지 등을 검출할 때는 해시함수를 사용하면 안됨. 해상도 크기 조정, 색조 보정, 압축 형식 변경 등으로 해시값이 달라짐

In [1]:
from PIL import Image
import numpy as np

In [2]:
def average_hash(fname, size = 16):
    img = Image.open(fname)
    img = img.convert('L') # 1을 지정하면 이진화, RGB, RGBA, CMYK 등의 모드도 지원
    img = img.resize((size, size), Image.ANTIALIAS)
    pixel_data = img.getdata()
    pixels = np.array(pixel_data)
    pixels = pixels.reshape((size, size))
    avg = pixels.mean()
    diff = 1 * (pixels > avg)
    return diff

In [8]:
def np2hash(ahash):
    bhash = []
    for nl in ahash.tolist():
        s1 = [str(i)for i in nl]
        s2 = ''.join(s1)
        i = int(s2, 2)
        bhash.append('%04x' % i)
    return ''.join(bhash)

In [9]:
ahash = average_hash('eiffel_tower.jpeg')

In [10]:
print(ahash)


[[0 1 1 1 1 1 1 1 1 0 0 0 0 0 0 1]
 [0 0 1 1 1 1 1 1 0 0 0 0 0 0 1 1]
 [0 0 1 1 1 1 1 1 0 0 0 0 1 1 1 1]
 [0 1 1 1 1 1 1 1 0 0 0 0 1 1 1 1]
 [0 1 1 1 1 1 1 1 0 0 0 1 1 1 1 1]
 [1 1 1 1 1 1 1 0 0 1 1 1 1 1 1 1]
 [1 1 1 1 1 1 1 0 0 1 1 1 1 1 1 1]
 [1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1]
 [1 1 1 1 1 1 1 0 0 1 1 1 1 1 1 1]
 [1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1]
 [0 1 1 1 1 1 1 0 0 0 1 1 1 1 1 1]
 [0 0 1 1 1 1 1 0 0 0 1 1 1 1 0 0]
 [0 0 0 0 0 1 0 0 0 0 1 1 1 0 0 0]
 [0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0]
 [0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
 [0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0]]

In [11]:
print(np2hash(ahash))


7f813f033f0f7f0f7f1ffe7ffe7fff7ffe7fff7f7e3f3e3c0438008000000200

Caltech 101 데이터

  • 해밍 거리 : 같은 문자수를 가진 2개의 문자열에서 대응하는 위치에 있는 문자 중 다른 것의 개수
    • 256글자의 해시값 중 얼마나 다른지 찾고 이 기반으로 이미지 차이를 구분

In [12]:
import os, re

In [18]:
search_dir = "./image/101_ObjectCategories/"
cache_dir = "./image/cache_avhash"

if not os.path.exists(cache_dir):
    os.mkdir(cache_dir)
    
def average_hash(fname, size = 16):
    fname2 = fname[len(search_dir):]
    # image cache
    
    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)
    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|pnp)$', 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 + "/chair/image_0016.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 herf="' + f + '"><img src="' + f + '" width=400>' + '</a></p></div>'
    html += s

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("./avgash-search-output.html", "w", encoding="utf-8") as f:
    f.write(html)
print("ok")


0.0 > ./image/101_ObjectCategories/chair/image_0016.jpg
0.22265625 > ./image/101_ObjectCategories/stop_sign/image_0019.jpg
0.2265625 > ./image/101_ObjectCategories/chair/image_0031.jpg
0.23046875 > ./image/101_ObjectCategories/airplanes/image_0129.jpg
0.234375 > ./image/101_ObjectCategories/umbrella/image_0009.jpg
0.23828125 > ./image/101_ObjectCategories/airplanes/image_0124.jpg
0.24609375 > ./image/101_ObjectCategories/chair/image_0001.jpg
0.24609375 > ./image/101_ObjectCategories/chair/image_0002.jpg
0.24609375 > ./image/101_ObjectCategories/dragonfly/image_0001.jpg
ok

CNN

  • 일정한 크기로 리사이즈한 후, 24비트 RGB 형식으로 변환 -> Numpy 배열로 저장

In [ ]:
from PIL import Image
import os, glob
import numpy as np
from sklearn.model_selection import train_test_split

# 분류 대상 카테고리 선택
caltech_dir