회사에서 진행했던 일들 정리(1)
회사에서 정리했던 자료들이 아깝기도하고 해서 블로그로 이전할 겸 적어 본다.
아마 나중에도 또 활용할 수도 있겠다 싶은 것도 많아서...
이 정도면 크게 문제될 사항은 아닌거 같은 것만 모아 정리할 생각이다.
🧹 이미지 중복 제거
데이터를 관리하면서 모델 학습 때마다 다시 데이터셋을 만들어 제공하는 일을 반복하게 되어 중복되는 이미지가 대량으로 발생했다.
중복되는 이미지가 있는 데이터셋으로 학습시킬 경우 모델 결과가 떨어지는 문제도 있고, 서버 용량도 문제였기 때문에
원할한 데이터 관리를 위해 방법을 찾아야 했다.
💡 OpenCV 방식
초기에는 OpenCV를 사용하여 이미지를 배열로 변환한 뒤, 구조적 유사도를 비교했다.
이렇게 구조적 유사도를 사용할 경우 데이터가 많을 수록 속도가 급격히 저하되기 때문에
일차적으로 같은 크기를 가진 파일들을 그룹으로 묶고, 그 그룹 안의 이미지를 Array로 전환 후 각각 비교하는 식으로 진행했다.
# 예시
import cv2
import numpy as np
def is_similar_image(img1_path, img2_path, threshold=0.95):
img1 = cv2.imread(img1_path, 0)
img2 = cv2.imread(img2_path, 0)
img1 = cv2.resize(img1, (256, 256))
img2 = cv2.resize(img2, (256, 256))
diff = cv2.absdiff(img1, img2)
similarity = 1 - np.mean(diff) / 255
return similarity > threshold
✅ 장점
- 실제로 이미지의 구조적 유사성을 확인할 수 있음 (예: 같은 배경 + 텍스트 다름 등 판단 가능)
❌ 단점
- 비교 대상마다 이미지를 직접 열고 계산해야 하므로, 데이터가 많을수록 속도가 급격히 저하
- 전체 이미지가 수천 장 이상일 경우, 비교 비용은
O(N^2)
유사도를 중복 제거 시 정확도가 나쁘진 않았으나,
단점은 속도였다.
당시 거의 10000장 이상의 파일들을 검사해야 했는데 이 방식을 사용하니까 검사하는데 3~4시간 정도는 걸렸던 것 같다.
때문에 비동기 처리도 적용해보고 별의별 방법을 써보고 있었는데 그룹장님이 해시를 이용해서 수정하라고 조언해주셔서, 해시를 사용한 중복 제거 방법을 조사했다.
💡 해시 기반 방식
해시로 중복되는 이미지(혹은 파일)을 제거하는 데에는 여러 방법이 있다.
🔹 1. MD5 / SHA 계열 (Cryptographic Hash)
- 종류:
MD5,SHA-1,SHA-256등 - 원리: 파일의 바이트(byte) 데이터를 기반으로 고유한 해시값을 생성
- 특징:
- 완전히 동일한 파일만 중복으로 인식
- 이미지 크기나 포맷, 메타데이터가 다르면 다른 파일로 인식
🔹 2. Perceptual Hash (pHash)
- 원리: 이미지의 시각적 특징을 바탕으로 해시 생성 (보통 DCT 변환 기반)
- 특징:
- 회전, 크기 변경, 포맷 변경, 밝기 변화에도 일정 부분 동일한 해시값 유지
- **해시 거리(Hamming distance)**로 유사도 판단 가능
- 시각적으로 유사한 이미지도 탐지 가능하지만, 완전히 다른 이미지도 우연히 비슷한 해시일 수 있음.
🔹 3. Average Hash (aHash)
- 원리: 이미지의 평균 밝기를 기준으로 이진 벡터를 생성
- 특징:
- 8x8 그레이스케일로 변환 → 각 픽셀이 평균보다 높은지 낮은지 비교
- 속도가 빠름
- 간단한 형태 변화에도 어느 정도 유사하게 판단
- 대비, 패턴에 민감하여 구분력이 떨어질 수 있음
🔹 4. Difference Hash (dHash)
- 원리: 이미지의 이웃 픽셀 간 밝기 차이를 이용해 해시 생성
- 특징:
- 픽셀 간 차이의 방향성을 반영하기 때문에 구조 변화에 민감
- 단순 로고/레이아웃 중복 탐지에 유리
- 밝기, 대비 변화에 약할 수 있음
표로 대충 정리하면 밑과 같다
| 해시 종류 | 유사 이미지 탐지 | 속도 | 정확도 |
|---|---|---|---|
| MD5/SHA | ❌ (완전 동일만) | ✅ 매우 빠름 | ✅ 확실함 |
| pHash | ✅ (강력) | ⏳ 느림 | ✅ 매우 우수 |
| aHash | ✅ (보통) | ✅ 빠름 | ⚠️ 약간 부정확 |
| dHash | ✅ (구조에 민감) | ✅ 빠름 | ✅ 구조 강함 |
🔚 결과
일단 가장 급한 문제는 데이터셋을 구축하면서 섞여들어간 복사된 중복 파일들이었기 때문에 MD5를 사용한 중복 파일 제거 스크립트를 구현했다.
import hashlib
from PIL import Image
import io
def calculate_image_hash(image_path):
with Image.open(image_path) as img:
img_byte_arr = io.BytesIO()
img.save(img_byte_arr, format=img.format)
return hashlib.md5(img_byte_arr.getvalue()).hexdigest()
전체 코드는 아래와 같은 흐름으로 작동한다.
- 모든 이미지에 대해 해시값을 계산
- 이미 존재하는 해시값이면 → 중복 이미지로 간주
- 중복된 이미지는
duplicates폴더로 이동
해시 기반 방식으로 전환 후 5분~10분 내외에 중복 탐지 및 이동까지 완료되면서 나름대로 보람찼었던 기억이 있다.
나중엔 이걸 바탕으로 두 폴더를 비교하여 중복 제거하거나 파일 이동 후 복구가 가능하도록 배치 스크립트를 자동으로 짜는 부분을 추가해 복구도 쉽게 처리할 수 있도록 만들었다.
ℹ️ 추가 정보
사실 제일 먼저 고려한 건 pHash 였다. 포맷이 다른 중복되는 이미지도 정리하고 싶어서였는데..
그냥 문제없어 보이는 파일들도 중복으로 처리 되는 경우가 좀 있어서 추가 검수를 해야 하는 문제가 있었다.
결국 따로 스크립트를 구현해두는 것으로 완료했다.
근데 아무래도 검수를 다시 해야 한다는 부분이 문제라 사용은 한 적이 없다....