【读书笔记】OpenCV 3计算机视觉 Python语言实现(第二版)(五)

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}
    判断为True的样本中,真实为True的比例
  2. 真阳性率(True Positive Rate,TPR),灵敏度(Sensitivity),召回率(Recall):
    = T P T P + F N 真阳性率 = \dfrac{TP}{TP + FN}
    真实为True的样本中,判断为True的比例
  3. 真阴性率(True Negative Rate,TNR),特异度(Specificity):
    = T N F P + T N 真阴性率 = \dfrac{TN}{FP + TN}
    真实为False的样本中,判断为False的比例
  4. 假阴性率(False Negatice Rate,FNR),漏诊率( = 1 - 灵敏度)
    = = F N T P + F N 假阴性率=漏诊率 = \dfrac{FN}{TP + FN}
    真实为Ture的样本中,判断为False的比例
  5. 假阳性率(False Positice Rate,FPR),误诊率( = 1 - 特异度):
    = = F P F P + T N 假阳性率=误诊率 = \dfrac{FP}{FP + TN}
    真实为False的样本中,判断为True的比例

AdaBoost算法

图1

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

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

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

1.将权重归一化:每个权重/权重和
2.假设有j个特征,一个特征对应一个分类器hj。对于每一个分类器hj,计算下面错误率:

图2
h j h_j 的计算方法如下:
θ j \theta_j 是阈值, f j f_j 是分类器 h j h_j 选中的特征, p j p_j 用于确定不等号方向。
图3
选取错误率最低的hj作为这个循环的结果ht。
选取最优 f j f_j 和最优 θ j \theta_j

3.更新每个训练样本的权重。

得到最后的强分类器。

AdaBoost算法针对不同的训练集训练同一个基本分类器(弱分类器),然后把这些在不同训练集上得到的分类器集合起来,构成一个更强的最终的分类器(强分类器)。
理论证明,只要每个弱分类器分类能力比随机猜测要好,当其个数趋向于无穷个数时,强分类器的错误率将趋向于零
AdaBoost算法中不同的训练集是通过调整每个样本对应的权重实现的。最开始的时候,每个样本对应的权重是相同的,在此样本分布下训练出一个基本分类器h1(x)。对于h1(x)错分的样本,则增加其对应样本的权重;而对于正确分类的样本,则降低其权重。这样可以使得错分的样本突出出来,并得到一个新的样本分布。
同时,根据错分的情况赋予h1(x)一个权重表示该基本分类器的重要程度错分得越少权重越大
在新的样本分布下,再次对基本分类器进行训练,得到基本分类器h2(x)及其权重。
依次类推,经过T次这样的循环,就得到了T个基本分类器,以及T个对应的权重
最后把这T个基本分类器按一定权重累加起来,就得到了最终所期望的强分类器。

《Rapid Object Detection usig a Boosted Cascade of Simple Features》

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

涂
积分图让矩形区域像素和计算更加快速,这让Haar特征值的计算更加快速。
tu
左上角为原点,向右为 x x 轴方向,向下为 y y 轴方向。积分图中,每一个点的值由上面公式计算得出。
也就是说,积分图中 1 1 点的数值为原图中 A A 区域的像素和;积分图中 3 3 点的数值为原图中 A + C A+C 区域的像素和。
有下面公式:
土
S ( x , y ) S(x, y) 是原图 x x 固定为 x x 值, 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源码编译安装后,在路径

/home/zhangchen/thirdparty/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("/home/zhangchen/task/code/face_recognition/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()

结语

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

猜你喜欢

转载自blog.csdn.net/Zhang_Chen_/article/details/93743881