iBetter Books
수정

얼굴 검출의 역사는 2001년 비올라와 존스가 발표한 Haar Cascade에서 본격적으로 시작됩니다. 20년이 훌쩍 지난 지금도 이 방식이 교과서 첫머리에 등장하는 이유는, 딥러닝 없이도 실시간으로 동작할 만큼 빠르고 OpenCV에 기본 내장되어 있어 추가 설치가 전혀 필요 없기 때문입니다. PART 01의 첫 실행에서 이미 슬쩍 써 봤던 그 검출기를, 이번에는 원리까지 들여다보며 제대로 다뤄 봅니다.

Haar Cascade의 아이디어

Haar Cascade는 얼굴에서 반복적으로 나타나는 밝고 어두운 패턴을 이용합니다. 예를 들어 "눈 부위는 그 아래 뺨보다 어둡다", "콧대는 양옆보다 밝다" 같은 명암 대비가 그것입니다. 이런 단순한 명암 차이 패턴을 Haar 특징(Haar-like feature)이라 부릅니다.

검출기는 이미지 위를 작은 창으로 훑으면서 각 위치가 이 패턴들에 얼마나 들어맞는지 검사합니다. 핵심은 캐스케이드(cascade, 단계적 폭포) 구조입니다. 수천 개의 검사를 한꺼번에 하지 않고 쉬운 검사부터 단계로 나누어, 초반 단계에서 "얼굴이 아니다"가 분명한 영역은 즉시 버립니다. 덕분에 대부분의 배경은 빠르게 걸러지고, 얼굴 후보에만 정밀 검사를 집중해 속도가 빠릅니다.

코드로 검출하기

OpenCV는 미리 학습된 Haar Cascade 파일을 함께 배포합니다. 따로 모델을 내려받을 필요 없이 cv2.data.haarcascades 경로에서 바로 불러 쓸 수 있습니다. 다음 내용을 haar_detect.py로 저장합니다.

# 파일: haar_detect.py"""이미지에서 Haar Cascade로 얼굴을 검출해 사각형을 그린다."""import osimport sysimport globimport cv2def frontalface_cascade():    """정면 얼굴 캐스케이드 xml 경로를 conda·pip 어디서나 찾는다."""    name = "haarcascade_frontalface_default.xml"    # pip의 opencv-python에는 cv2.data가 있다    if hasattr(cv2, "data"):        p = os.path.join(cv2.data.haarcascades, name)        if os.path.exists(p):            return p    # conda-forge opencv는 $PREFIX/share/opencv*/haarcascades/ 에 둔다    hits = glob.glob(os.path.join(sys.prefix, "share", "opencv*", "haarcascades", name))    if hits:        return hits[0]    raise FileNotFoundError(name)# OpenCV에 기본 포함된 정면 얼굴 검출기를 불러온다detector = cv2.CascadeClassifier(frontalface_cascade())img = cv2.imread("sample.jpg")gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)   # Haar는 흑백에서 동작# 얼굴 검출: (x, y, w, h) 목록을 돌려준다faces = detector.detectMultiScale(gray, scaleFactor=1.1, minNeighbors=5, minSize=(30, 30))for (x, y, w, h) in faces:    cv2.rectangle(img, (x, y), (x + w, y + h), (0, 255, 0), 2)print(f"검출된 얼굴: {len(faces)}개")cv2.imwrite("haar_result.jpg", img)

검출 결과로 얻은 (x, y, w, h)를 좌상단 (x, y)와 우하단 (x + w, y + h) 두 점으로 바꿔 cv2.rectangle에 넘기는 부분이, 앞 장에서 말한 좌표 변환의 실제 모습입니다.

conda 사용자가 자주 막히는 곳. 많은 예제는 캐스케이드 경로를 cv2.data.haarcascades로 적습니다. 그런데 이 cv2.data는 pip의 opencv-python에는 있지만 conda-forge의 opencv 빌드에는 없어서, 우리 실습 환경에서는 AttributeError: module 'cv2' has no attribute 'data'가 납니다. conda-forge는 캐스케이드 파일을 대신 $CONDA_PREFIX/share/opencv4/haarcascades/에 둡니다. 그래서 위 코드의 frontalface_cascade() 함수는 두 위치를 모두 시도해 어디서든 동작하도록 만든 것입니다. 이후 장에서도 Haar 파일이 필요할 때 이 방식을 씁니다.

Haar Cascade 검출 결과

detectMultiScale의 세 가지 손잡이

detectMultiScale의 인자들은 검출 품질을 좌우하는 손잡이입니다. 이름이 낯설지만 역할은 직관적입니다.

인자 의미 올리면 내리면
scaleFactor 이미지를 단계마다 줄이는 비율 빠르지만 놓치는 얼굴 증가 느리지만 꼼꼼
minNeighbors 진짜로 인정할 이웃 검출 수 오검출 감소, 누락 증가 더 많이 잡되 오검출 증가
minSize 검출할 최소 얼굴 크기 작은 얼굴 무시 작은 얼굴도 시도

scaleFactor는 보통 1.05에서 1.3 사이를 씁니다. 1.1이면 매 단계 이미지를 10%씩 줄여 가며 다양한 크기의 얼굴을 찾는다는 뜻입니다. minNeighbors는 같은 얼굴 주위에 검출이 몇 번 겹쳐야 진짜로 인정할지를 정하는데, 이 값을 올리면 우연한 오검출이 줄어듭니다.

Haar의 명확한 한계

Haar Cascade는 빠르고 간편하지만 약점이 뚜렷합니다.

  • 정면 얼굴에만 강합니다. 고개를 옆으로 돌리거나 기울이면 검출률이 뚝 떨어집니다.
  • 오검출이 잦습니다. 명암 패턴만 보기 때문에 배경의 무늬나 옷을 얼굴로 착각하기도 합니다.
  • 조명에 민감합니다. 역광이나 그림자가 심하면 패턴이 깨져 놓치기 쉽습니다.

이 한계들이 바로 이후 등장한 딥러닝 검출기들이 풀어낸 문제입니다.

실무 팁. 그럼에도 Haar Cascade는 여전히 쓸모가 있습니다. 라즈베리파이처럼 연산 자원이 빠듯한 기기에서, 정면을 바라보는 사용자 한 명만 빠르게 잡으면 되는 상황이라면 가장 가벼운 선택지입니다. "정확도가 최고"가 아니라 "조건이 맞으면 가장 싸다"가 Haar의 자리입니다.

이 장에서 기억할 것

Haar Cascade는 얼굴의 명암 패턴(Haar 특징)을 캐스케이드 구조로 빠르게 검사하는 고전 검출기로, OpenCV에 내장되어 추가 설치 없이 detectMultiScale 한 번으로 쓸 수 있습니다. scaleFactor·minNeighbors·minSize로 품질을 조절하지만, 정면 의존·오검출·조명 민감이라는 한계가 있습니다. 다음 장에서는 정면 얼굴에 더 강건한 또 다른 고전 검출기, dlib의 HOG + SVM을 만나 봅니다.