Skip to content

OpenCV Haar 级联分类器详解

一、核心原理:Viola-Jones 算法

Haar 级联分类器是基于 Viola-Jones 对象检测框架 的机器学习方法,由 Paul Viola 和 Michael Jones 在 2001 年的论文《Rapid Object Detection using a Boosted Cascade of Simple Features》中提出。它因其高速度和较好的准确度,尤其是在人脸检测任务上的表现,而变得非常流行。

该算法的核心思想是通过机器学习,从大量正样本(包含目标的图像)和负样本(不包含目标的图像)中训练出一个级联的强分类器,用于快速判断图像中的某个区域是否包含目标对象(如人脸)。

其成功依赖于三个关键创新:

1. Haar-like 特征(矩形特征)

Haar 特征不是像素值本身,而是通过计算图像中相邻矩形区域的像素和之差来得到的。这些特征类似于卷积核,用于捕捉图像的边缘、线条等简单结构。

常见的 Haar 特征类型

  • 边缘特征 (Edge Features):捕捉左右、上下之间的明暗变化
  • 线特征 (Line Features):捕捉更细的线条结构
  • 中心环绕特征 (Four-rectangle Feature):捕捉中心与周围区域的差异

为什么使用这些特征? 人脸的许多结构(如眼睛比脸颊深,鼻梁比两侧亮)可以被这些简单的矩形特征有效地描述。计算"像素和之差"比直接处理像素值更高效,且对光照变化有一定的不变性。

2. 积分图(Integral Image)—— 快速计算的核心

计算大量矩形区域的像素和如果使用传统方法会非常慢。积分图是一种数据结构,它允许在常数时间 O(1) 内计算出任何矩形区域的像素和,极大地加速了特征计算过程。

积分图定义:积分图中任意一点 (x, y) 的值是原图像中从 (0, 0)(x, y) 所构成的矩形区域内所有像素值的和。

如何计算矩形和:有了积分图 I,计算矩形 D 的和只需要四次引用操作:

$Sum(D) = I(x4, y4) - I(x2, y2) - I(x3, y3) + I(x1, y1)$

3. AdaBoost 算法 —— 特征选择和强分类器构建

一张图像可以提取出数万甚至数十万个 Haar 特征,但其中绝大多数是无关紧要的。AdaBoost 算法的作用是:

  • 特征选择:从海量特征中筛选出最能区分目标(如人脸)和非目标的少量关键特征
  • 构建强分类器:将多个"弱分类器"(每个弱分类器基于一个最好的 Haar 特征)组合成一个强分类器

一个弱分类器可能只比随机猜测好一点点,但 AdaBoost 通过加权组合多个弱分类器,可以形成一个非常精确的强分类器。

4. 级联结构(Cascade)—— 实现高速检测

这是实现实时检测的最关键思想。级联分类器由一系列阶段(Stage)组成,每个阶段都是一个由 AdaBoost 训练得到的强分类器。

工作流程:检测窗口像瀑布一样依次通过所有阶段: 1. 第一个阶段由最简单的几个特征组成,它可以快速拒绝掉明显不是人脸的窗口(例如,背景区域) 2. 只有通过第一阶段的窗口才会被送到更复杂的第二阶段 3. 以此类推,越到后面的阶段,分类器越复杂,使用的特征越多,判断也越精确 4. 一个检测窗口必须通过所有阶段,才会被最终认定为是一个目标(如人脸)

优势:这种结构使得非目标区域在最初的阶段就被快速丢弃,避免了后续不必要的复杂计算,将计算资源集中在"可能"的区域上,从而实现了高速检测。

二、OpenCV 中的预训练 Haar 分类器

OpenCV 提供了大量预训练好的 Haar 级联分类器模型(.xml 文件),无需用户自己训练即可使用。

模型文件位置

这些文件通常位于 OpenCV 的安装目录下: - Windows: C:\opencv\build\etc\haarcascades\ - Mac (通过brew安装): /usr/local/opt/opencv/share/opencv4/haarcascades/ - Python (可以直接下载): 也可以从 OpenCV GitHub 下载

常用预训练模型

人脸检测: - haarcascade_frontalface_default.xml(最常用,正面人脸) - haarcascade_frontalface_alt.xml - haarcascade_frontalface_alt2.xml - haarcascade_profileface.xml(侧面人脸)

人眼检测: - haarcascade_eye.xml - haarcascade_eye_tree_eyeglasses.xml(可检测戴眼镜的眼睛)

身体检测: - haarcascade_fullbody.xml - haarcascade_upperbody.xml - haarcascade_lowerbody.xml

其他: - haarcascade_smile.xml(微笑) - haarcascade_license_plate.xml(车牌) - haarcascade_russian_plate_number.xml(俄罗斯车牌)

三、代码示例:使用预训练模型进行人脸和眼睛检测

import cv2
import numpy as np
from tqdm import trange

# 回调函数
def nothing(x):
    print(x)

def main():

    # 加载haar连级模型
    face_cascade = cv2.CascadeClassifier('.venv\Lib\site-packages\cv2\data\haarcascade_frontalface_default.xml')
    eyes_cascade = cv2.CascadeClassifier('.venv\Lib\site-packages\cv2\data\haarcascade_eye.xml')

    cap = cv2.VideoCapture('935167194_nb3-1-30080.mp4')
    if cap.isOpened() is None:
        print('open faild')
        exit()

    frame_w = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
    frame_h = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
    FPS = int(cap.get(cv2.CAP_PROP_FPS))
    fourcc = cv2.VideoWriter_fourcc(*'XVID')
    print(f"shape:{frame_w},{frame_h}\nFPS:{FPS}")
    delay = int(1/FPS*1000-3)
    print(f'delay:{delay}')

    writer = cv2.VideoWriter('output.avi', fourcc, FPS, (int(frame_w * 0.25), int(frame_h * 0.25)))

    while cap.isOpened():
        ret, frame = cap.read()
        frame = cv2.resize(frame,(0,0),fx=0.25,fy=0.25)

        if ret is None:
            print("ret is none")
            break
        img_gray = cv2.cvtColor(frame,cv2.COLOR_BGR2GRAY)

        # 人脸检测
        faces = face_cascade.detectMultiScale(
            img_gray,
            scaleFactor=1.1,
            minNeighbors=5,
            minSize=(30,30)
        )
        # 人脸框选
        for (x,y,w,h) in faces :
            roi_face = cv2.rectangle(frame,(x,y),(x+w,y+h),(255,0,0),2)
            eyes = eyes_cascade.detectMultiScale(
                roi_face,
                scaleFactor=1.1, 
                minNeighbors=5,
                minSize=(30,30)
            )
            # 人眼检测并框选
            for(ex,ey,ew,eh) in eyes:
                roi_eyes = cv2.rectangle(roi_face,(ex,ey),(ex+ew,ey+eh),(255,0,0),2)
                writer.write(roi_eyes)
                cv2.imshow('FACE',roi_eyes)
                key = cv2.waitKey(delay) & 0xFF
                if key == ord('q'):
                    cap.release()
                    cv2.destroyAllWindows()
                    exit()

if __name__ == '__main__':
    main()

detectMultiScale 参数详解

  • scaleFactor: 必须大于 1.0(如 1.05, 1.1, 1.2)。值越小,能检测到更小的人脸,但计算速度越慢
  • minNeighbors: 控制检测的灵敏度。值越低,越容易检测到对象,但假正例(错误检测)也越多。值越高,检测越严格,越可靠,但可能漏掉一些人脸
  • minSize / maxSize: 指定要检测的目标的尺寸范围,可以有效地减少计算量和错误检测

四、优缺点总结

优点 缺点
速度快,适合实时应用 检测精度有限,不如现代深度学习模型(如YOLO, SSD)
💾 模型文件小(.xml文件) ❌ 容易出现误检(False Positives)漏检(False Negatives)
🔧 开源,OpenCV 内置,开箱即用 ❌ 对遮挡、非正面、极端光照条件的鲁棒性差
🎯 在正面、光照良好的人脸检测上效果很好 ❌ 需要为不同对象(猫、车等) 重新训练模型,而预训练模型有限

五、与现代深度学习方法的对比

虽然 Haar 级联分类器在历史上非常重要,但现在对于要求高精度的应用,通常会被基于深度学习的目标检测器所取代,例如:

  • 更准确:YOLO, SSD, Faster R-CNN
  • 集成在 OpenCV 中cv2.dnn.readNetFrom... 系列函数,可以加载如 Caffe, TensorFlow, Darknet 等框架训练的网络

Haar 级联分类器由于其轻量级和速度,在计算资源有限的嵌入式设备上仍然有其用武之地