시놀로지 포토 스테이션의 중복 이미지 검출

지난 포스트에서는 시놀로지 포토 스테이션의 중복 이미지를 찾기 위해 도커를 설치하고 준비해봤습니다. 이번에는 이미지 검색을 진행해보겠습니다.

우선 ImageHash 라이브러리를 설치합니다. Jupyter Notebook에 들어가서 오른쪽 New 메뉴를 열고 Terminal을 선택합니다.

$ pip install ImageHash

터미널에서 pip로 라이브러리를 설치합니다. 이제 코드를 작성하면 됩니다. Jupyter Notebook을 하나 열고 시작합니다.

ImageHash 코드

이미지 해시를 계산하는 코드입니다. 라이브러리에서는 average_hash, perception_hashing, difference_hashing, wavelet_hashing 을 구현해서 제공하고 있습니다. 이 중에 average hash를 사용하려고 합니다.


# ImageHash 테스트
from PIL import Image
img_path = '/var/nas/photo/Family/'
img_name = '20141122_195058.jpg'
im = Image.open(img_path + img_name)
h = imagehash.average_hash(im)
print(h)

파이썬 이미지 라이브러리 PIL 로 파일을 열고 ImageHash의 해시 함수에 전달해주면 됩니다. 결과로는 ImageHash 타입의 배열이 나오는데 str() 로 형변환을 하면 16진수 16글자(64bit)가 나옵니다. 이 결과값과 파일명을 저장하고, 해시값이 동일한 파일들을 찾으면 될것 같습니다.

디렉토리 돌아다니기

Photo Station은 ‘앨범’으로 구성되어 있는데 이 앨범은 그냥 디렉토리인 것으로 보입니다. 디렉토리를 돌아다니며 이미지 파일들을 찾아와야 합니다.


rootdir = "/var/nas/photo/"
for root, subdirs, files in os.walk(rootdir):
subdirs.remove('@eaDir')
print(root, subdirs, len(files))

@eaDir 이라는 디렉토리가 있는데 Photo Station이 내부적으로 사용하는 디렉토리로 보입니다. 이 디렉토리를 빼고(4번 행) 검색을 하도록 했습니다.

DB 준비하기

데이터 저장은 간단하게 사용할 수 있는 SQLite를 사용하려고 합니다. 파이썬 기본 배포판에 들어가 있으므로 별도로 설치할 필요는 없습니다.


# Database 준비
# http://www.sqlitetutorial.net/sqlite-python/create-tables/ 참조
import sqlite3
conn = sqlite3.connect("img_dup_find.db") # sqlite DB 파일 만들기
# 테이블 만들기 함수
def create_table(conn, create_table_sql):
""" create a table from the create_table_sql statement
:param conn: Connection object
:param create_table_sql: a CREATE TABLE statement
:return:
"""
try:
c = conn.cursor()
c.execute(create_table_sql)
except Error as e:
print(e)
def create_row(conn, img_dup):
"""
Create a new row
:param conn:
:param img_dup: 파일명, 해시값
:return: id
"""
sql = ''' INSERT INTO img_dup(file_name,hash)
VALUES(?,?) '''
cur = conn.cursor()
cur.execute(sql, img_dup)
return cur.lastrowid
sql_create_img_dup_table = """ CREATE TABLE IF NOT EXISTS img_dup (
id integer PRIMARY KEY,
file_name text NOT NULL,
hash varchar(16) NOT NULL
); """
create_table(conn, sql_create_img_dup_table) # 테이블 생성

몇 가지 함수

해시 구하는 부분과 디렉토리의 이미지 파일명을 가져오는 부분을 함수로 만들었습니다. 추가로 얼마나 처리되는지 알 수 있게 100개 처리할 때 마다 시간을 출력하는 함수를 만들어놨습니다.


def get_image_average_hash(file_fullpath):
# TODO Check file_fullpath
from PIL import Image
import imagehash
im = Image.open(file_fullpath)
return imagehash.average_hash(im)
# 이미지 파일 배열로 가져오기
def get_image_files(path):
"""Get array of image files in directory
Args:
path (str): directory to find.
"""
# 이미지 파일 필터링
import os, re
return [filename for filename in os.listdir(path)
if re.search(r'\.(bmp|gif|jpeg|jpg|png|tif)$', filename, re.IGNORECASE)]
cnt = 0
# 처리된 이미지 카운트, 100개 마다 출력
def count():
import datetime
global cnt
cnt += 1
if (cnt % 100 == 0):
print(datetime.datetime.now(), cnt)

view raw

functions.py

hosted with ❤ by GitHub

메인 코드

준비가 다 되었습니다. 메인 코드 부분입니다.


# Main 코드
rootdir = "/var/nas/photo"
for root, subdirs, files in os.walk(rootdir):
subdirs.remove('@eaDir')
#print(root, subdirs, len(files))
files = get_image_files(root)
for f in files:
count()
try:
h = get_image_average_hash(join(root, f))
create_row(conn, (join(root, f), str(h)))
except:
print("Error on", join(root, f), sys.exc_info()[0])
continue
conn.commit() # 디렉토리 하나 처리할 때 마다 커밋
conn.close()

중복 이미지 찾기

작업 결과는 SQLite DB에 있으므로 SQL로 검색을 하면 됩니다. 간단히 쿼리로..

SELECT *
FROM IMG_DUP
WHERE HASH IN (SELECT DISTINCT HASH
FROM IMG_DUP
GROUP BY HASH
HAVING COUNT(*) > 1)
ORDER BY HASH

정리

대략 100개 이미지 처리하는데 2분 정도 걸리는 것 같습니다. 약 3만 개 가량의 이미지를 처리했는데 1만 개 가량이 중복 이미지라고 나오네요. 좀 많이 나온 것 같아서 확인해보니 전혀 다른 이미지 같은데 동일한 해시가 나오는 경우가 있네요..다른 해시 알고리즘을 테스트해보고 이미지를 삭제하기 전에 확인해보는게 좋을 것 같습니다.

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다