구글 포토 API가 얼굴 태그로 직접 검색하는 기능을 제공하지 않아서 많은 분들이 어려움을 겪고 있어요. 하지만 Google Takeout과 파이썬의 face_recognition 라이브러리를 조합하면 특정 인물의 사진만 자동으로 필터링해서 다운로드할 수 있어요.
macOS에서 face_recognition 설치하기
터미널을 열고 다음 명령어를 순서대로 실행해요. macOS의 경우 cmake와 dlib 설치가 필요해요.
# Homebrew가 없다면 먼저 설치
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
# cmake 설치
brew install cmake
# face_recognition 설치
pip3 install face_recognition
설치 중 에러가 발생하면 Xcode Command Line Tools를 먼저 설치해야 해요.
xcode-select --install
Google Takeout으로 사진 백업받기
구글 포토의 모든 사진을 먼저 다운로드해야 해요. takeout.google.com에 접속해서 Google Photos만 선택한 후 백업을 요청해요.
백업 파일 크기가 크면 여러 개로 분할되니 모두 다운로드해서 하나의 폴더에 압축을 풀어요. 예를 들어 Downloads/GooglePhotos 폴더에 모든 사진을 모아두면 돼요.
기본 얼굴 인식 코드 작성하기
특정 인물의 정면 사진을 준비해요. 이 사진을 기준으로 다른 사진들을 검색하게 돼요.
import face_recognition
import os
import shutil
from datetime import datetime
# 찾고자 하는 사람의 기준 사진
target_person = face_recognition.load_image_file("target_person.jpg")
target_encoding = face_recognition.face_encodings(target_person)[0]
# 구글 포토 백업 폴더와 결과 저장 폴더
source_folder = "/Users/username/Downloads/GooglePhotos"
output_folder = "/Users/username/Desktop/FilteredPhotos"
# 출력 폴더가 없으면 생성
os.makedirs(output_folder, exist_ok=True)
# 처리된 파일 수 카운터
processed = 0
matched = 0
print(f"검색 시작: {datetime.now()}")
# 모든 하위 폴더 포함해서 이미지 파일 검색
for root, dirs, files in os.walk(source_folder):
for filename in files:
if filename.lower().endswith(('.jpg', '.jpeg', '.png', '.gif')):
filepath = os.path.join(root, filename)
processed += 1
if processed % 100 == 0:
print(f"처리 중: {processed}개 파일 검사, {matched}개 매칭")
try:
# 이미지 로드 및 얼굴 인코딩
image = face_recognition.load_image_file(filepath)
face_encodings = face_recognition.face_encodings(image)
# 각 얼굴과 대상 인물 비교
for face_encoding in face_encodings:
matches = face_recognition.compare_faces(
[target_encoding], face_encoding, tolerance=0.6
)
if matches[0]:
# 매칭된 파일 복사
dest_path = os.path.join(output_folder, filename)
shutil.copy2(filepath, dest_path)
matched += 1
print(f"매칭 발견: {filename}")
break
except Exception as e:
print(f"에러 발생 {filename}: {str(e)}")
print(f"완료: 총 {processed}개 중 {matched}개 파일 매칭")
정확도 향상을 위한 고급 설정
얼굴 인식 정확도를 높이려면 tolerance 값을 조정해요. 기본값은 0.6이지만 상황에 따라 조정이 필요해요.
# 엄격한 매칭 (오탐 감소, 미탐 증가)
strict_tolerance = 0.4
# 느슨한 매칭 (미탐 감소, 오탐 증가)
loose_tolerance = 0.7
# 다양한 각도의 기준 사진 사용
reference_photos = [
"person_front.jpg",
"person_side.jpg",
"person_smile.jpg"
]
# 여러 기준 사진의 인코딩 생성
target_encodings = []
for photo in reference_photos:
image = face_recognition.load_image_file(photo)
encoding = face_recognition.face_encodings(image)
if encoding:
target_encodings.append(encoding[0])
메타데이터 보존하며 폴더 구조 유지하기
구글 포토의 날짜별 폴더 구조를 유지하면서 파일을 복사하는 코드예요.
import face_recognition
import os
import shutil
from pathlib import Path
def preserve_folder_structure(source_path, dest_base, source_base):
"""원본 폴더 구조를 유지하며 파일 복사"""
# 상대 경로 계산
relative_path = os.path.relpath(source_path, source_base)
dest_path = os.path.join(dest_base, relative_path)
# 대상 디렉토리 생성
dest_dir = os.path.dirname(dest_path)
os.makedirs(dest_dir, exist_ok=True)
# 파일 복사 (메타데이터 포함)
shutil.copy2(source_path, dest_path)
return dest_path
# 메인 처리 함수
def filter_photos_with_structure(source_folder, output_folder, target_encodings):
for root, dirs, files in os.walk(source_folder):
for filename in files:
if filename.lower().endswith(('.jpg', '.jpeg', '.png')):
filepath = os.path.join(root, filename)
try:
image = face_recognition.load_image_file(filepath)
face_encodings = face_recognition.face_encodings(image)
for face_encoding in face_encodings:
# 여러 기준 사진과 비교
matches = face_recognition.compare_faces(
target_encodings, face_encoding, tolerance=0.6
)
if any(matches):
dest = preserve_folder_structure(
filepath, output_folder, source_folder
)
print(f"복사됨: {relative_path}")
break
except Exception as e:
continue
성능 최적화를 위한 병렬 처리
대량의 사진을 처리할 때는 병렬 처리로 속도를 높일 수 있어요.
import face_recognition
import concurrent.futures
import os
from functools import partial
def process_single_image(filepath, target_encodings, output_folder):
"""단일 이미지 처리 함수"""
try:
image = face_recognition.load_image_file(filepath)
face_encodings = face_recognition.face_encodings(image)
for face_encoding in face_encodings:
matches = face_recognition.compare_faces(
target_encodings, face_encoding, tolerance=0.6
)
if any(matches):
filename = os.path.basename(filepath)
dest_path = os.path.join(output_folder, filename)
shutil.copy2(filepath, dest_path)
return f"매칭: {filename}"
return None
except Exception as e:
return f"에러: {filepath} - {str(e)}"
def parallel_face_search(source_folder, output_folder, target_encodings):
"""병렬 처리로 얼굴 검색"""
# 모든 이미지 파일 경로 수집
image_files = []
for root, dirs, files in os.walk(source_folder):
for f in files:
if f.lower().endswith(('.jpg', '.jpeg', '.png')):
image_files.append(os.path.join(root, f))
print(f"총 {len(image_files)}개 파일 발견")
# 병렬 처리 실행
with concurrent.futures.ProcessPoolExecutor(max_workers=4) as executor:
process_func = partial(
process_single_image,
target_encodings=target_encodings,
output_folder=output_folder
)
results = list(executor.map(process_func, image_files))
# 결과 집계
matched = sum(1 for r in results if r and r.startswith("매칭"))
print(f"완료: {matched}개 파일 매칭")
JSON 파일로 처리 결과 저장하기
나중에 재사용할 수 있도록 처리 결과를 JSON으로 저장하는 방법이에요.
import json
from datetime import datetime
def save_processing_log(matched_files, output_folder):
"""처리 결과를 JSON으로 저장"""
log_data = {
"processing_date": datetime.now().isoformat(),
"total_matched": len(matched_files),
"matched_files": matched_files,
"settings": {
"tolerance": 0.6,
"reference_photos": reference_photos
}
}
log_path = os.path.join(output_folder, "processing_log.json")
with open(log_path, 'w', encoding='utf-8') as f:
json.dump(log_data, f, ensure_ascii=False, indent=2)
print(f"처리 로그 저장됨: {log_path}")
실전 활용 팁과 주의사항
tolerance 값은 사진의 품질과 촬영 환경에 따라 조정이 필요해요. 셀카나 근접 촬영은 0.5-0.6, 단체 사진이나 원거리 촬영은 0.6-0.7 정도가 적당해요.
기준 사진은 정면, 측면, 웃는 표정 등 다양한 각도와 표정을 포함하면 인식률이 크게 향상돼요. 최소 3-5장의 다양한 사진을 사용하는 것을 추천해요.
구글 포토에서 HEIC 형식 파일도 많이 사용하는데, face_recognition이 직접 지원하지 않아요. pillow-heif 라이브러리를 추가로 설치하면 처리할 수 있어요.
# HEIC 지원 추가
pip3 install pillow-heif
from PIL import Image
import pillow_heif
# HEIC 파일 처리
pillow_heif.register_heif_opener()
def load_image_file_extended(filepath):
"""HEIC 포함 다양한 형식 지원"""
if filepath.lower().endswith('.heic'):
image = Image.open(filepath)
return np.array(image)
else:
return face_recognition.load_image_file(filepath)
처리 속도가 느리다면 이미지 크기를 줄여서 처리하는 방법도 있어요. 얼굴 인식에는 고해상도가 필요하지 않으니 1024픽셀 정도로 리사이즈하면 속도가 크게 향상돼요.
이 방법으로 수만 장의 사진에서도 특정 인물의 사진만 정확하게 찾아낼 수 있어요. 가족 앨범 정리나 행사 사진 분류 등 다양한 용도로 활용해보세요.