검출기를 일곱 가지나 만나 봤습니다. 이제 흩어진 인상을 하나의 표로 모을 차례입니다. "정확한 게 좋다"는 막연한 느낌 대신, 같은 사진으로 직접 시간을 재 보고 각 검출기가 어느 상황에 맞는지를 숫자와 함께 정리합니다. 이 장을 마치면 새 프로젝트에서 "무엇을 쓸지"를 근거를 가지고 고를 수 있게 됩니다.
무엇을 비교하는가
검출기를 고를 때 따지는 기준은 크게 셋입니다.
- 정확도: 얼마나 잘 잡는가. 특히 옆얼굴·작은 얼굴·가려진 얼굴 같은 어려운 경우.
- 속도: 한 장(또는 한 프레임)을 처리하는 데 걸리는 시간. 실시간 가능 여부를 가른다.
- 자원: 메모리 사용량과 GPU 필요 여부. 라즈베리파이 같은 작은 기기에서 중요하다.
이 셋은 보통 맞바꿈 관계입니다. 정확도를 높이면 느려지고 자원을 더 먹습니다. "최고의 검출기"는 없고, "내 상황에 맞는 검출기"가 있을 뿐입니다.
속도를 직접 재는 코드
같은 이미지를 여러 번 검출해 평균 시간을 재는 간단한 벤치마크입니다. 검출기마다 호출 방식이 다르므로, 각 검출 함수를 같은 형태로 감싸 비교합니다. 다음 내용을 benchmark.py로 저장합니다.
# 파일: benchmark.py"""여러 검출기의 평균 처리 시간을 같은 이미지로 비교한다."""import osimport sysimport globimport timeimport cv2def frontalface_cascade(): # 02장에서 설명한 conda·pip 양쪽 호환 경로 찾기 name = "haarcascade_frontalface_default.xml" if hasattr(cv2, "data"): p = os.path.join(cv2.data.haarcascades, name) if os.path.exists(p): return p return glob.glob(os.path.join(sys.prefix, "share", "opencv*", "haarcascades", name))[0]img = cv2.imread("sample.jpg")gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)def bench(name, fn, repeat=20): fn() # 첫 호출은 모델 로딩 등 워밍업이므로 측정에서 제외 start = time.perf_counter() for _ in range(repeat): fn() avg_ms = (time.perf_counter() - start) / repeat * 1000 print(f" {name:14s} 평균 {avg_ms:7.1f} ms")# 1) Haarhaar = cv2.CascadeClassifier(frontalface_cascade())bench("Haar", lambda: haar.detectMultiScale(gray, 1.1, 5))# 2) dlib HOGimport dlibhog = dlib.get_frontal_face_detector()rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)bench("dlib HOG", lambda: hog(rgb, 1))# 3) YuNet (모델 파일 필요)h, w = img.shape[:2]yunet = cv2.FaceDetectorYN.create( "face_detection_yunet_2023mar.onnx", "", (w, h), 0.9, 0.3, 5000)yunet.setInputSize((w, h))bench("YuNet", lambda: yunet.detect(img))# 같은 방식으로 MediaPipe·MTCNN·RetinaFace·YOLO도 추가해 비교할 수 있다
bench 함수에서 첫 호출을 따로 떼어 워밍업으로 버리는 점이 중요합니다. 딥러닝 검출기는 첫 호출에서 모델을 메모리에 올리느라 유독 오래 걸리므로, 이를 측정에 넣으면 결과가 왜곡됩니다.
한눈에 보는 검출기 비교
직접 재 보면 기기마다 절대 시간은 다르지만, 검출기 사이의 상대적 경향은 대체로 일정합니다. CPU 환경(GPU 미사용) 기준의 일반적인 경향을 정리하면 다음과 같습니다.
| 검출기 | 정확도 | 속도(CPU) | 옆·작은 얼굴 | 랜드마크 | 추가 설치 |
|---|---|---|---|---|---|
| Haar | 낮음 | 매우 빠름 | 약함 | 없음 | 불필요(내장) |
| dlib HOG | 보통 | 빠름 | 약함 | 없음 | dlib |
| OpenCV DNN(SSD) | 보통 | 보통 | 보통 | 없음 | 모델 파일 |
| YuNet | 높음 | 빠름 | 보통~좋음 | 5점 | 불필요(내장) |
| MediaPipe | 보통~높음 | 매우 빠름 | 보통 | 키포인트 | mediapipe |
| MTCNN | 높음 | 느림 | 좋음 | 5점 | mtcnn |
| RetinaFace | 매우 높음 | 매우 느림 | 매우 좋음 | 5점 | retina-face |
| YOLO-face | 높음 | 빠름(GPU 시 매우 빠름) | 좋음 | 5점(가중치별) | ultralytics |

이 표에서 두 가지 경향이 보입니다. 첫째, 내장이면서 균형이 좋은 YuNet이 "무난한 첫 선택"으로 두드러집니다. 둘째, RetinaFace는 정확도 최강이지만 CPU에서는 실시간이 어렵습니다.
상황별 추천
| 상황 | 추천 검출기 | 이유 |
|---|---|---|
| 라즈베리파이, 정면 한 명 | Haar / MediaPipe | 가장 가볍고 빠름 |
| 일반 데스크톱, 균형 | YuNet | 내장, 정확도·속도 균형, 랜드마크 |
| 실시간 웹캠 다인원 | MediaPipe / YOLO-face | 빠른 다중 검출 |
| 정확도 최우선(등록·검색 앞단) | RetinaFace / MTCNN | 어려운 얼굴까지 검출 |
| GPU 보유, 속도+정확도 | YOLO-face | GPU 가속으로 둘 다 확보 |
실무 팁. 벤치마크는 반드시 "내 실제 데이터"로 하세요. 위 경향은 일반적인 사진 기준이고, 여러분의 환경(조명, 카메라 해상도, 얼굴 크기, 마스크 착용 여부)에 따라 순위가 바뀔 수 있습니다. 후보를 두세 개로 좁힌 뒤, 실제 운영 환경과 비슷한 사진 수십 장으로 정확도와 시간을 직접 재 보는 것이 가장 확실합니다.
이 장에서 기억할 것
검출기는 정확도·속도·자원의 맞바꿈 위에 놓여 있어 "최고"가 아니라 "상황에 맞는" 선택이 정답입니다. time.perf_counter로 워밍업을 뺀 평균 시간을 직접 재 보면 검출기 간 상대 경향을 확인할 수 있고, 균형이 필요하면 내장 YuNet, 정확도가 최우선이면 RetinaFace, 실시간 다인원이면 MediaPipe나 YOLO-face가 무난합니다. 다음 장에서는 지금까지 배운 것을 모아, 웹캠에서 여러 얼굴을 실시간으로 검출하는 작은 실습으로 PART 02를 마무리합니다.