한 사람과 비교하는 것을 넘어 "여러 명 중 누구인가"를 알아내려면, 먼저 등록부가 필요합니다. 이름과 인코딩을 짝지어 저장해 둔 것이 얼굴 DB입니다. 이 장에서는 인물별 사진으로 DB를 만들고, 새 얼굴을 그 DB와 비교해 이름을 찾고, DB를 파일로 저장·재사용하는 흐름을 익힙니다.
등록부의 구조
얼굴 DB는 단순합니다. "이름 목록"과 "그에 대응하는 인코딩 목록" 두 개를 나란히 두면 됩니다. 인물별로 폴더를 나눠 사진을 담아 두고, 폴더 이름을 그 사람의 이름표로 씁니다.
known/
├── 홍길동/ 1.jpg 2.jpg
├── 김철수/ 1.jpg
└── 이영희/ 1.jpg 2.jpg
known/이름/*.jpg] --> B[각 사진 인코딩] B --> C[이름·인코딩 짝지어 저장] C --> D[얼굴 DB]
DB 구축 코드
폴더를 돌며 인코딩과 이름을 모읍니다.
# 파일: build_db.py"""known/ 폴더에서 이름·인코딩 등록부를 만든다."""import osimport globimport pickleimport face_recognitionDATA_DIR = "known"known_encodings, known_names = [], []for person in sorted(os.listdir(DATA_DIR)): person_dir = os.path.join(DATA_DIR, person) if not os.path.isdir(person_dir): continue for path in glob.glob(os.path.join(person_dir, "*.jpg")): img = face_recognition.load_image_file(path) encs = face_recognition.face_encodings(img) if not encs: # 얼굴 못 찾으면 건너뛴다 print(f" [건너뜀] 얼굴 없음: {path}") continue known_encodings.append(encs[0]) known_names.append(person)# 파일로 저장해 다음에 재사용 (매번 다시 인코딩하지 않도록)with open("face_db.pkl", "wb") as f: pickle.dump({"encodings": known_encodings, "names": known_names}, f)print(f"등록 완료: {len(known_names)}건, 인물 {len(set(known_names))}명")
한 사람의 사진이 여러 장이면 인코딩도 여러 개 등록됩니다. 같은 이름이 여러 인코딩에 붙는 셈인데, 이렇게 한 사람당 여러 장을 등록하면 다양한 각도·표정을 포괄해 인식이 더 안정적입니다. 마지막에 pickle로 파일에 저장해 두는 것이 중요합니다. 인코딩 계산은 비싸므로, 한 번 만들어 저장해 두고 다음부터는 불러 쓰는 것이 정석입니다.
새 얼굴의 이름 찾기
DB가 있으면 새 사진의 이름을 찾는 것은 PART 04의 1:N 식별 그대로입니다. 모든 등록 인코딩과 거리를 재서 가장 가까운 사람을 고릅니다.
# 파일: identify.py"""저장한 DB로 새 얼굴의 이름을 찾는다."""import pickleimport numpy as npimport face_recognitionwith open("face_db.pkl", "rb") as f: db = pickle.load(f)known_encodings, known_names = db["encodings"], db["names"]img = face_recognition.load_image_file("query.jpg")for enc in face_recognition.face_encodings(img): distances = face_recognition.face_distance(known_encodings, enc) best = np.argmin(distances) # 가장 가까운 등록 인코딩 if distances[best] < 0.6: # 임계값 통과해야 인정 name = known_names[best] else: name = "미등록자" print(f"{name} (거리 {distances[best]:.3f})")
face_distance로 한 번에 모든 등록 인코딩과의 거리를 구하고, np.argmin으로 가장 가까운 것을 찾습니다. 그 거리가 임계값(0.6)을 넘으면 등록되지 않은 사람으로 처리합니다. "가장 가깝다"는 이유만으로 이름을 붙이면 안 되고, 반드시 임계값을 함께 확인해야 모르는 사람을 아는 사람으로 오인하지 않습니다.
실무 팁. 사람마다 사진 장수가 들쭉날쭉하면 인식이 한쪽으로 치우칠 수 있습니다. 사진이 많은 사람일수록 우연히 가까운 인코딩이 하나쯤 걸릴 확률이 높아지기 때문입니다. 가능하면 인물당 사진 장수를 비슷하게 맞추고, 화질이 좋고 정면에 가까운 사진을 골라 등록하세요. 등록 사진의 품질이 인식 정확도를 좌우합니다.
이 장에서 기억할 것
얼굴 DB는 이름 목록과 인코딩 목록을 짝지은 등록부이며, 인물별 폴더로 만들어 pickle로 저장해 재사용합니다. 한 사람당 여러 장을 등록하면 안정적이고, 새 얼굴은 face_distance로 전체와 비교해 argmin으로 가장 가까운 사람을 찾되 임계값으로 미등록자를 걸러야 합니다. 다음 장에서는 이름표가 아예 없는 사진 더미를, 비슷한 얼굴끼리 자동으로 묶는 클러스터링을 다룹니다.