Young87

当前位置:首页 >个人收藏

【机器学习】Haar特征+AdaBoost+cascade实现人脸检测

Haar特征

图0现在opnecv中的Haar特征不止上面四种。

Haar特征是一种反映图像的灰度变化的,像素分模块求差值的一种特征。用黑白两种矩形框组合成特征模板,在特征模板内用黑色矩形像素和减去白色矩形像素和来表示这个模版的特征值。

例如:脸部的一些特征能由矩形模块差值特征简单的描述,如:眼睛要比脸颊颜色要深,鼻梁两侧比鼻梁颜色要深,嘴巴比周围颜色要深等。

但矩形特征只对一些简单的图形结构,如边缘、线段较敏感,所以只能描述在特定方向(水平、垂直、对角)上有明显像素模块梯度变化的图像结构

对于上图的c,特征值为两倍的黑色矩形像素和减白色矩形像素和,因为要保证两种矩形区域中像素数目一致。

假阳性率、假阴性率

  1. 正确率(Precision):
    正 确 率 ( P r e c i s i o n ) = T P T P + F P 正确率(Precision) = \dfrac{TP}{TP + FP} (Precision)=TP+FPTP
    判断为True的样本中,真实为True的比例
  2. 真阳性率(True Positive Rate,TPR),灵敏度(Sensitivity),召回率(Recall):
    真 阳 性 率 = T P T P + F N 真阳性率 = \dfrac{TP}{TP + FN} =TP+FNTP
    真实为True的样本中,判断为True的比例
  3. 真阴性率(True Negative Rate,TNR),特异度(Specificity):
    真 阴 性 率 = T N F P + T N 真阴性率 = \dfrac{TN}{FP + TN} =FP+TNTN
    真实为False的样本中,判断为False的比例
  4. 假阴性率(False Negatice Rate,FNR),漏诊率( = 1 - 灵敏度)
    假 阴 性 率 = 漏 诊 率 = F N T P + F N 假阴性率=漏诊率 = \dfrac{FN}{TP + FN} ==TP+FNFN
    真实为Ture的样本中,判断为False的比例
  5. 假阳性率(False Positice Rate,FPR),误诊率( = 1 - 特异度):
    假 阳 性 率 = 误 诊 率 = F P F P + T N 假阳性率=误诊率 = \dfrac{FP}{FP + TN} ==FP+TNFP
    真实为False的样本中,判断为True的比例

AdaBoost算法

图1

  • 给出数据集(图1,1)(图2,1)(图3,0)…
    1代表图片中有需要检测到的目标,0代表图片中没有需要检测到的目标

  • 初始化权重:假设有目标的样本有 m m m个,没有目标的样本有 l l l个。有目标的样本初始化权重为 1 / 2 m 1/2m 1/2m,没有目标的样本初始化权重为 1 / 2 l 1/2l 1/2l

  • 循环 T T T次,每次得到一个弱分类器,一共得到 T T T个弱分类器
    每次循环中的内容如下:

  1. 将权重归一化处理:每个权重/权重和
  2. 假设有若干的特征(注意弱分类器的个数 T T T和这个特征的个数没有关系)对每个特征重复如下操作:对于特征 j j j,对应一个分类器 h j h_j hj,对于分类器 h j h_j hj,计算下面错误率:

图2

  1. h j h_j hj的计算方法如下:
    θ j \theta_j θj是阈值, f j f_j fj是分类器 h j h_j hj选中的特征, p j p_j pj用于确定不等号方向。
    图3

  2. 选取错误率最低的 h j h_j hj作为这个循环的结果 h t h_t ht
    选取最优 f j f_j fj和最优 θ j \theta_j θj

  3. 更新每个训练样本的权重,然后返回步骤一。循环 T T T次,得到 T T T个弱分类器

  • 得到最后的强分类器。

AdaBoost算法针对不同的训练集训练同一个基本分类器(弱分类器),然后把这些在不同训练集上得到的分类器集合起来,构成一个更强的最终的分类器(强分类器)

理论证明,只要每个弱分类器分类能力比随机猜测要好,当其个数趋向于无穷个数时,强分类器的错误率将趋向于零

AdaBoost算法中不同的训练集是通过调整每个样本对应的权重实现的。最开始的时候,每个样本对应的权重是相同的,在此样本分布下训练出一个基本分类器 h 1 ( x ) h_1(x) h1(x)对于 h 1 ( x ) h_1(x) h1(x)错分的样本,则增加其对应样本的权重而对于正确分类的样本,则降低其权重。这样可以使得错分的样本突出出来,并得到一个新的样本分布。

同时,根据错分的情况赋予 h 1 ( x ) h_1(x) h1(x)一个权重表示该基本分类器的重要程度错分得越少权重越大

在新的样本分布下,再次对基本分类器进行训练,得到基本分类器 h 2 ( x ) h_2(x) h2(x)及其权重。

依次类推,经过 T T T次这样的循环,就得到了 T T T个基本分类器,以及 T T T个对应的权重。
最后把这T个基本分类器按一定权重累加起来,就得到了最终所期望的强分类器。

Rapid Object Detection using a Boosted Cascade of Simple Features

Integral Image(积分图)——更快速的计算Haar特征

涂
积分图让矩形区域像素和计算更加快速,这让Haar特征值的计算更加快速。
tu
左上角为原点,向右为 x x x轴方向,向下为 y y y轴方向。积分图中,每一个点的值由上面公式计算得出。
也就是说,积分图中 1 1 1点的数值为原图中 A A A区域的像素和;积分图中 3 3 3点的数值为原图中 A + C A+C A+C区域的像素和。
有下面公式:
土
S ( x , y ) S(x, y) S(x,y)是原图 x x x固定为 x x x值, y y y轴方向的像素和

因此,得到积分图后,计算本文第一节内容中的A类和B类特征,只需要6个值做加减运算;C类特征,只需要8个值做加减运算;D类特征,只需要9个值做加减运算。

可见特征图大大提高了Haar特征值的计算速度。这也就大大提高了通过Haar分类器(Haar-like特征 + 积分图方法 + AdaBoost +级联)识别人脸的算法速度。

下面内容截取自:https://www.cnblogs.com/zyly/p/9410563.html#_label2

tutu

这个曲线图是特征得到特征值后,按特征值从小到大排序的曲线。
原文作者说的将图像矩阵映射为一维特征值,是指一个Haar特征计算一张图的特征值是一个数,这个数代表原图。

下面内容截取自:https://www.cnblogs.com/zyly/p/9410563.html#_label2

图5
一个Haar特征计算出的特征值大于阈值就算检测到目标,否则就算没检测到目标。

a learning algorithm based on AdaBoost ——从大量的特征中选取一部分关键特征,提高分类器的效率

内容见本文第三节:AdaBoost算法

cascade(级联)——快速的剔除背景,让分类器更关注可能有目标的区域,类似attention机制

t
上图中每一个stage都代表一级强分类器。当检测窗口通过所有的强分类器时才被认为是正样本,否则拒绝

由于每一个强分类器对负样本的判别准确度非常高,所以一旦发现检测到的目标位负样本,就不在继续调用下面的强分类器,减少了很多的检测时间。

因为一幅图像中待检测的区域很多都是负样本,这样由级联分类器在分类器的初期就抛弃了很多负样本的复杂检测,所以级联分类器的速度是非常快的;只有正样本才会送到下一个强分类器进行再次检验,这样就保证了最后输出的正样本的伪正(false positive)的可能性非常低。

这套算法训练时,训练每个弱分类器,在得到一个强分类器,最后再把若干的强分类器级联,得到最终的Haar分类器。

如何训练Haar-cascades分类器:https://docs.opencv.org/2.4/doc/user_guide/ug_traincascade.html

python实现

opencv源码编译安装后,在路径

./opencv/share/OpenCV/haarcascades

中,有opencv自带的训练好的人脸识别Haar分类器,是.xml文件格式。

利用这些分类器可以实现各种人脸识别功能,python的实现代码如下:

  1. 静态图像正面人脸检测
# 静态图像正面人脸检测
import cv2


filename = "/home/zhangchen/task/code/others/test.jpeg"


def detect(filename):
    face_cscade = cv2.CascadeClassifier(
        "./cascades/haarcascade_frontalface_default.xml")

    img = cv2.imread(filename)
    img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

    face = face_cscade.detectMultiScale(img_gray, 1.3, 5)

    for (x, y, w, h) in face:
        img = cv2.rectangle(img, (x, y), (x+w, y+h), (255, 0, 0), 2)

    cv2.namedWindow("face detected")
    cv2.imshow("face detected", img)
    cv2.imwrite("./test_result.png", img)

    cv2.waitKey(0)

detect(filename)
detectMultiScale(待检测的图像(灰度图), 一个大于1的数(表示在前后两次相继的扫描中,搜索窗口的比例系数。默认为1.1即每次搜索窗口依次扩大10%), 一个大于等于0的整数(表示构成检测目标的相邻矩形的最小个数,默认为3个。如果为0, 则函数不做任何操作就返回所有的被检候选矩形框), 默认为0, 最小范围, 最大范围)
# 六个参数

在原图
在这里插入描述检测人脸后

  1. 视频实时显示检测人脸和眼睛
# 视频实时显示检测人脸和眼睛
import cv2


def video_detect():
    face_cscade = cv2.CascadeClassifier("./cascades/haarcascade_frontalface_default.xml")
    eye_cascade = cv2.CascadeClassifier("./cascades/haarcascade_eye.xml")

    camera = cv2.VideoCapture(0)

    while True:
        ret, frame = camera.read()
        # 如果正确读到帧,ret返回值为True
        frame_gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)

        face = face_cscade.detectMultiScale(frame_gray, 1.3, 5)

        for (x, y, w, h) in face:
            img = cv2.rectangle(frame, (x, y), (x + w, y + h), (255, 0, 0), 2)

            roi_gray = frame_gray[y:y+h, x:x+w]

            eye = eye_cascade.detectMultiScale(roi_gray, 1.03, 5,  0, (40, 40))

            for (ex, ey, ew, eh) in eye:
                cv2.rectangle(img, (x + ex, y + ey), (x + ex+ew, y + ey+eh), (0, 255, 0), 2)

        cv2.imshow("camera", frame)

        if cv2.waitKey(1000 // 12) & 0xff == ord("q"):
            break

    camera.release()
    cv2.destroyAllWindows()

video_detect()
  1. 视频实时人脸识别
# 生成人脸库
import cv2


def generate():
    face_cascade = cv2.CascadeClassifier("./cascades/haarcascade_frontalface_default.xml")

    camera = cv2.VideoCapture(0)
    count = 0

    while True:
        ret, frame = camera.read()
        gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)

        face = face_cascade.detectMultiScale(gray, 1.3, 5)

        for (x, y, w, h) in face:
            frame = cv2.rectangle(frame, (x, y), (x+w, y+h), (255, 0, 0), 2)

            f = cv2.resize(gray[y:y+h, x:x+w], (200, 200))
            # 把人脸数据图像缩放至200x200

            cv2.imwrite("./dataset/data_0/%s.pgm" % str(count), f)
            # 不同人的人脸数据图像放在不同的文件夹:data_0、data_1、data_2...
            count += 1
            print("Save Image {}.pgm".format(count))
        cv2.imshow("camera", frame)
        if cv2.waitKey(1000 // 12) & 0xff == ord("q"):
            break

    camera.release()
    cv2.destroyAllWindows()

generate()
# 人脸识别
import cv2
import os


def read_images(path):
    c = 0
    X, y = [], []
    for dirname, dirnames, filenames in os.walk(path):
        for subdirname in dirnames:
            subject_path = os.path.join(dirname, subdirname)
            for filename in os.listdir(subject_path):
                filepath = os.path.join(subject_path, filename)
                img = cv2.imread(filepath, cv2.IMREAD_GRAYSCALE)
                # 以灰度模式读入

                X.append(np.asarray(img, dtype=np.uint8))
                y.append(c)

            c = c + 1

    return [X, y]
    # [Image, label]


def face_recognition():
    names = ["Tom", "Jerry"]

    [X, y] = read_images("./dataset")
    y = np.asarray(y, dtype=np.int32)

    model = cv2.face.EigenFaceRecognizer_create()
    # Confidence评分低于4000是可靠
    # model = cv2.face.FisherFaceRecognizer_create()
    # Confidence评分低于4000是可靠
    # model = cv2.face.LBPHFaceRecognizer_create()
    # Confidence评分低于50是可靠

    model.train(X, y)
    # train
    camera = cv2.VideoCapture(0)

    face_cascade = cv2.CascadeClassifier("./cascades/haarcascade_frontalface_default.xml")

    while True:
        read, img = camera.read()
        face = face_cascade.detectMultiScale(img, 1.3, 5)

        for (x, y, w, h) in face:
            img = cv2.rectangle(img, (x, y), (x + w, y + h), (255, 0, 0), 2)
            gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
            roi_gray = gray[y:y+h, x:x+w]

            roi_gray = cv2.resize(roi_gray, (200, 200), interpolation=cv2.INTER_LINEAR)
            params = model.predict(roi_gray)
            # predict
            # 返回params是包含两个元素的列表,第一个元素是label,第二个元素是Confidence
            if params[1] > 4000:
            # LBPH时,设定值为50
            	continue
            print("Label:%s, Confidence: %.2f" % (params[0], params[1]))
            cv2.putText(img, names[params[0]], (x, y - 20), cv2.FONT_HERSHEY_SIMPLEX, 1, 255, 2)

        cv2.imshow("camera", img)
        if cv2.waitKey(1000 // 12) & 0xff == ord("q"):
            break

    cv2.destroyAllWindows()

face_recognition()

结语

如果您有修改意见或问题,欢迎留言或者通过邮箱和我联系。
手打很辛苦,如果我的文章对您有帮助,转载请注明出处

除特别声明,本站所有文章均为原创,如需转载请以超级链接形式注明出处:SmartCat's Blog

上一篇: 启动ActiveMQ异常:java.net.URISyntaxException: Illegal character in hostname at index解决办法

下一篇: 外卖类应用的竞争与趋势

精华推荐