SVM算法应用综合练习:人脸表情识别

news/2024/7/21 7:50:14 标签: 计算机视觉, python, 图像处理

目录

  • 一、步骤流程及相关介绍
    • 1.流程
    • 2.相关介绍
  • 二、编写代码
  • 三、总结
  • 参考

一、步骤流程及相关介绍

1.流程

在这里插入图片描述

在这里插入图片描述

2.相关介绍

相关准备:
dlib:
链接
https://pan.baidu.com/s/1ZmrMD7rk4VGZ4x6KHfSa6w
提取码:1713
68个人脸特征点:
链接:https://pan.baidu.com/s/1Ys3gI03GQOGudfJ5whnBzA
提取码:0526
图片集:
https://pan.baidu.com/s/1_A5gM64_dPI3offCMInhMw
提取码:1713
HOG介绍
 HOG是Histogram of Oriented Gradient的缩写,是一种在计算机视觉图像处理中用来进行目标检测的特征描述子。可结合OPENCV的SVM分类器等用于图像的识别。
特征提取流程:
 HOG特征提取流程可分为6个部分:检测窗口归一化图像计算梯度统计直方图梯度直方图归一化得到HOG特征向量,以下分步骤介绍。
(1)检测窗口:
HOG通过窗口(window)和(block)将图像进行分割。通过以细胞(cell)为单位,对图像某一区域的像素值进行数学计算处理。在此先介绍窗口(window)、块(block)和细胞(cell)的概念及之间的联系。

  • 窗口(window):将图像按一定大小分割成多个相同的窗口,滑动。
  • (block):将每个窗口按一定大小分割成多个相同的块,滑动。
  • 细胞(cell):将每个窗口按一定大小分割成多个相同的细胞,属于特征提取的单元,静止不动。
  • 图像(image)->检测窗口(win)->图像块(block)->细胞单元(cell)

(2)归一化图像:
归一化分为gamma空间和颜色空间归一化。为减少光照因素影响,将整个图像进行规范化(归一化)。(归一化公式:y=(x-MinValue)/(MaxValue-MinValue))。归一化同时可以避免在图像的纹理强度中,局部的表层曝光贡献度的比重较大的情况。标准化Gamma压缩公式:I(x,y)=I(x,y)^gamma. gamma根据自己效果取值,如1/2.
(3)计算梯度:
计算图像横坐标和纵坐标方向的梯度,并根据横坐标和纵坐标的梯度,计算梯度方向。下图为计算公式图:
        在这里插入图片描述

在算法中,常先用[-1,0,1]进行卷积操作求得x方向的梯度值,再采用[-1,0,1]T进行卷积操作求得y方向。而后采用上述公式求梯度幅值和方向。
(4)构建梯度直方图:
HOG构建方向梯度直方图在细胞(cell)中完成:
bins(可理解为划分的个数)决定方向的划分。一般bins取9,将梯度方向划分为9个区间。(注:关于划分区间,有些博主以360°计算。鄙人查opencv书籍,发现确应按180度进行计算,artan所得值得范围即为180°。)例如,假设一个细胞尺寸为6*6,则对这个细胞内的36个像素点,先判断像素点梯度方向所属的区间,后根据像素点的梯度幅值大小和梯度方向的大小进行加权于对应的梯度方向区间。(加权方法可有线性加权、平方根等等各种高大尚的加权方法)以下是按照9个区间,进行角度划分的图像。
                    在这里插入图片描述

(5)块内进行细胞归一化梯度直方图。
原因:局部光照的变化及前景-背景对比度的变化,使梯度强度的变化范围很大,在此需要进行归一化,(查资料,使用的归一化函数有L2-norm、L2-Hys、L1-norm于L1-sqrt等,O(∩_∩)O,没听过?没办法,谁让你那么菜呢???)进一步对光照、阴影和边缘进行压缩。根据上述介绍,把
(6)生成HOG特征向量:
最后组合所有的块,生成特征向量:例对于一个64128的窗口而言,每88的像素组成一个cell,每22个cell组成一个块,每个块有94个特征,以8个像素为步长,水平方向将有7个扫描窗口,垂直方向将有15个扫描窗口。所以,一个64128的窗口共367*15=3780个特征,代码中一个hog描述子针对一个检测窗口。
HOG加权方法:
HOG采用三线性加权法,有两个部分用到加权,分别是构建梯度直方图和细胞归一化。以下分别介绍:
在构建梯度直方图时,假若一个像素点的梯度方向为25°,距离020°和2040°最近,采用加权方法,对相邻两个区间进行幅度值相加,分别为(25-10)/20=0.75和(25-20)/20=0.25的权重值进行累加。
同时,在收集块内梯度方向直方图时,存在一个既定假设,即位于不同细胞内的像素点只会对其从属的细胞进行投影,并不会对其周围的细胞产生影响。显然,若对于细胞交界处的像素点和在块滑动情况下,这样的假设未免显得有点牵强,因为它们与其周围所有的细胞都是相关的。
HOG算法优缺点:
优点:
(1)HOG表示边缘的结构特征,可以描述局部的形状信息。
(2)位置和方向空间的量化一定程度上可以抑制平移和旋转带来的影响。
(3)采用归一化,可以抵消光照带来的变化
缺点:
(1)描述子生成过程冗长,维度较高
(2)很难处理遮挡问题。
(3)对噪点敏感

二、编写代码

版本:

python 3.8 + opencv 4.0.1

导包(环境内没有的包自己pip install xx下载):

python"># 导入包
import numpy as np
import cv2
import dlib
import random#构建随机测试集和训练集
from sklearn.svm import SVC #导入svm
from sklearn.svm import LinearSVC #导入线性svm
from sklearn.pipeline import Pipeline #导入python里的管道
import os
import joblib#保存模型
from sklearn.preprocessing import StandardScaler,PolynomialFeatures #导入多项式回归和标准化
import tqdm

定义路径:

python">folder_path='C:/Users/86150/Downloads/genki4k (1)/genki4k/'#这里直接写的绝对路径
label='labels.txt'#标签文件
pic_folder='files/'#图片文件路径

获得默认的人脸检测器和训练好的人脸68特征点检测器:

python">#获得默认的人脸检测器和训练好的人脸68特征点检测器
def get_detector_and_predicyor():
    #使用dlib自带的frontal_face_detector作为我们的特征提取器
    detector = dlib.get_frontal_face_detector()
    """
    功能:人脸检测画框
    参数:PythonFunction和in Classes
    in classes表示采样次数,次数越多获取的人脸的次数越多,但更容易框错
    返回值是矩形的坐标,每个矩形为一个人脸(默认的人脸检测器)
    """
    #返回训练好的人脸68特征点检测器
    predictor = dlib.shape_predictor('shape_predictor_68_face_landmarks.dat')
    return detector,predictor
#获取检测器
detector,predictor=get_detector_and_predicyor()

定义截取面部的函数:

python">def cut_face(img,detector,predictor):   
    #截取面部
    img_gry=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
    rects = detector(img_gry, 0)  
    if len(rects)!=0:
        mouth_x=0
        mouth_y=0
        landmarks = np.matrix([[p.x, p.y] for p in predictor(img,rects[0]).parts()])
        for i in range(47,67):#嘴巴范围
            mouth_x+=landmarks[i][0,0]
            mouth_y+=landmarks[i][0,1]
        mouth_x=int(mouth_x/20)
        mouth_y=int(mouth_y/20)
        #裁剪图片
        img_cut=img_gry[mouth_y-20:mouth_y+20,mouth_x-20:mouth_x+20]
        return img_cut
    else:
        return 0#检测不到人脸返回0

定义提取特征值的函数:

python">#提取特征值
def get_feature(files_train,face,face_feature):
    for i in tqdm.tqdm(range(len(files_train))):
        img=cv2.imread(folder_path+pic_folder+files_train[i])
        cut_img=cut_face(img,detector,predictor)
        if type(cut_img)!=int:
            face.append(True)
            cut_img=cv2.resize(cut_img,(64,64))
            #padding:边界处理的padding
            padding=(8,8)
            winstride=(16,16)
            hogdescrip=hog.compute(cut_img,winstride,padding).reshape((-1,))
            face_feature.append(hogdescrip)
        else:
            face.append(False)#没有检测到脸的
            face_feature.append(0)

定义筛选函数:

python">def filtrate_face(face,face_feature,face_site): #去掉检测不到脸的图片的特征并返回特征数组和相应标签   
    face_features=[]
    #获取标签
    label_flag=[]
    with open(folder_path+label,'r') as f:
        lines=f.read().splitlines()
    #筛选出能检测到脸的,并收集对应的label
    for i in tqdm.tqdm(range(len(face_site))):
        if face[i]:#判断是否检测到脸
            #pop之后要删掉当前元素,后面的元素也要跟着前移,所以每次提取第一位就行了
            face_features.append(face_feature.pop(0))
            label_flag.append(int(lines[face_site[i]][0])) 
        else:
            face_feature.pop(0)
    datax=np.float64(face_features)
    datay=np.array(label_flag)
    return datax,datay

定义多项式SVM:

python">def PolynomialSVC(degree,c=10):#多项式svm
    return Pipeline([
            # 将源数据 映射到 3阶多项式
            ("poly_features", PolynomialFeatures(degree=degree)),
            # 标准化
            ("scaler", StandardScaler()),
            # SVC线性分类器
            ("svm_clf", LinearSVC(C=10, loss="hinge", random_state=42,max_iter=10000))
        ])

训练函数:

python">def train(files_train,train_site):#训练
    '''
    files_train:训练文件名的集合
    train_site :训练文件在文件夹里的位置
    '''
    #是否检测到人脸
    train_face=[]
    #人脸的特征数组
    train_feature=[]
    #提取训练集的特征数组
    get_feature(files_train,train_face,train_feature)
    #筛选掉检测不到脸的特征数组
    train_x,train_y=filtrate_face(train_face,train_feature,train_site)
    svc=PolynomialSVC(degree=1)
    svc.fit(train_x,train_y)
    return svc#返回训练好的模型

测试函数:

python">def test(files_test,test_site,svc):#预测,查看结果集
    '''
    files_train:训练文件名的集合
    train_site :训练文件在文件夹里的位置
    '''
    #是否检测到人脸
    test_face=[]
    #人脸的特征数组
    test_feature=[]
    #提取训练集的特征数组
    get_feature(files_test,test_face,test_feature)
    #筛选掉检测不到脸的特征数组
    test_x,test_y=filtrate_face(test_face,test_feature,test_site)
    pre_y=svc.predict(test_x)
    ac_rate=0
    for i in range(len(pre_y)):
        if(pre_y[i]==test_y[i]):
            ac_rate+=1
    ac=ac_rate/len(pre_y)*100
    print("准确率为"+str(ac)+"%")
    return ac

构建HOG特征提取器:

python">#设置hog的参数
winsize=(64,64)
blocksize=(32,32)
blockstride=(16,16)
cellsize=(8,8)
nbin=9
#定义hog
hog=cv2.HOGDescriptor(winsize,blocksize,blockstride,cellsize,nbin)
#获取文件夹里有哪些文件
files=os.listdir(folder_path+pic_folder)

使用10-fold cross validation,就是把数据集中随机的9/10做为训练集,剩下的1/10做为测试集,进行十次:

python">ac=float(0)
for j in range(10):
    site=[i for i in range(4000)]
    #训练所用的样本所在的位置
    train_site=random.sample(site,3600)
    #预测所用样本所在的位置
    test_site=[]
    for i in range(len(site)):
        if site[i] not in train_site:
            test_site.append(site[i])
    files_train=[]
    #训练集,占总数的十分之九
    for i in range(len(train_site)):
        files_train.append(files[train_site[i]])
    #测试集
    files_test=[]
    for i in range(len(test_site)):
        files_test.append(files[test_site[i]])
    svc=train(files_train,train_site)
    ac=ac+test(files_test,test_site,svc)
    save_path='C:/Users/86150/JupyterProject/source/model/smile'+str(j)+'(hog).pkl'
    joblib.dump(svc,save_path)
ac=ac/10
print("平均准确率为"+str(ac)+"%")

检测结果(一部分):
在这里插入图片描述

检测函数:

python">def test1(files_test,test_site,svc):#预测,查看结果集
    '''
    files_train:训练文件名的集合
    train_site :训练文件在文件夹里的位置
    '''
    #是否检测到人脸
    test_face=[]
    #人脸的特征数组
    test_feature=[]
    #提取训练集的特征数组
    get_feature(files_test,test_face,test_feature)
    #筛选掉检测不到脸的特征数组
    test_x,test_y=filtrate_face(test_face,test_feature,test_site)
    pre_y=svc.predict(test_x)
    tp=0
    tn=0
    for i in range(len(pre_y)):
        if pre_y[i]==test_y[i] and pre_y[i]==1:
            tp+=1
        elif pre_y[i]==test_y[i] and pre_y[i]==0:
            tn+=1
    f1=2*tp/(tp+len(pre_y)-tn)
    print(f1)

加载刚刚保存本地模型然后调用检测函数看一下结果:

python">svc7=joblib.load('C:/Users/86150/JupyterProject/source/model/smile9(hog).pkl')
site=[i for i in range(4000)]
#训练所用的样本所在的位置
train_site=random.sample(site,3600)
#预测所用样本所在的位置
test_site=[]
for i in range(len(site)):
    if site[i] not in train_site:
        test_site.append(site[i])
#测试集
files_test=[]
for i in range(len(test_site)):
    files_test.append(files[test_site[i]])
test1(files_test,test_site,svc7)

在这里插入图片描述

调用模型来检测,定义一个笑脸检测函数,输入图片直接得到预测结果:

python">def smile_detector(img,svc):
    cut_img=cut_face(img,detector,predictor)
    a=[]
    
    if type(cut_img)!=int:
        cut_img=cv2.resize(cut_img,(64,64))
    #padding:边界处理的padding
        padding=(8,8)
        winstride=(16,16)
        hogdescrip=hog.compute(cut_img,winstride,padding).reshape((-1,))
        a.append(hogdescrip)
        result=svc.predict(a)
        a=np.array(a)
        return result[0]
    else :
        return 2

图片检测实例:

python">##图片检测
pic_path='C:/Users/86150/JupyterProject/source/picture/test3.jpg'
img=cv2.imread(pic_path)
result=smile_detector(img,svc7)
if result==1:
    img=cv2.putText(img,'smile',(21,50),cv2.FONT_HERSHEY_COMPLEX,2.0,(0,255,0),1)
elif result==0:
    img=cv2.putText(img,'no smile',(21,50),cv2.FONT_HERSHEY_COMPLEX,2.0,(0,255,0),1)
else:
    img=cv2.putText(img,'no face',(21,50),cv2.FONT_HERSHEY_COMPLEX,2.0,(0,255,0),1)
cv2.imshow('test', img)
cv2.waitKey(0)

在这里插入图片描述

在这里插入图片描述

摄像头实时检测并保持,按s键保存刚刚的识别的图片10张,按esc退出:

python">camera = cv2.VideoCapture(0)#打开摄像头
ok=True
flag=0
ss='000000'
# 打开摄像头 参数为输入流,可以为摄像头或视频文件
while ok:
    ok,img = camera.read()
     # 转换成灰度图像
    result=smile_detector(img,svc7)
    if result==1:
        img=cv2.putText(img,'smile',(21,50),cv2.FONT_HERSHEY_COMPLEX,2.0,(0,255,0),1)
    elif result==0:
        img=cv2.putText(img,'no smile',(21,50),cv2.FONT_HERSHEY_COMPLEX,2.0,(0,255,0),1)
    else:
        img=cv2.putText(img,'no face',(21,50),cv2.FONT_HERSHEY_COMPLEX,2.0,(0,255,0),1)
    cv2.imshow('test', img)
    k = cv2.waitKey(1)
    if k == 27:    # press 'ESC' to quit
        break
    elif k==115:
        for i in range(10):
            pic_save_path='C:/Users/86150/JupyterProject/source/picture/result/'+str(flag)+ss+str(i)+'.jpg' #给图片编号以00000000起
            cv2.imwrite(pic_save_path,img)
        flag+=1
camera.release()
cv2.destroyAllWindows()

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

三、总结

使用这种方法准确度只有85%左右,不算高,会有一定误差。

参考

https://www.cnblogs.com/urglyfish/articles/12417343.html
https://blog.csdn.net/junseven164/article/details/121831011


http://www.niftyadmin.cn/n/1313994.html

相关文章

网管“北向接口”与“南向接口”

北向接口 北向接口(Northbound Interface)提供给其他厂家或运营商进行接入和管理的接口,即向上提供的接口。它是驻留在底层网管上的一个进程,与通常所说的Agent功能相同,负责处理来自上层网管的请求报文,发…

用java做操作系统内核:软盘读写

在前两节,我们将一段代码通过软盘加载到了系统内存中,并指示cpu执行加入到内存的代码,事实上,操作系统内核加载也是这么做的。只不过我们加载的代码,最大只能512 byte, 一个操作系统内核,少说也要几百兆&am…

RT-thread Nano移植

目录一、移植准备二、打开STM32CubeMx创建项目三、编写代码四、结果五、总结参考一、移植准备 STM32CubeMx 添加RT-Thread操作系统组件: 首先要获取 RT-Thread NANO软件包:https://www.rt-thread.org/download/cube/RealThread.RT-Thread.pdsc 二、打…

峰值速率、系统容量、吞吐量、带宽之间的区别

1、峰值速率 峰值速率定义为单用户在系统中被分配最大的带宽(在TDD系统中上下行分别定义峰值速率,对应上下行分别最有利的TDD配置)、最高的调制编码方式、处于理想的无线环境时所能达到的最高速率。对应到实际网络测试中,当一个用…

Python reduce函数

reduce函数使用一个二元函数和一个序列,序列中的前两个元素先放到二元函数运算,然后在用运算后的结果与列别的第三个元素进行运算,以此类推,知道列表的元素用完,返回计算结果。 from functools import reducedef add1(…

峰值速率与用户速率

1 HSDPA峰值速率14.4M究竟是指小区内所有用户的速率和还是单用户速率? 移动通信,通信工程师的家园,通信人才,求职招聘,网络优化,通信工程,出差住宿,通信企业黑名单!dF,N8U%m9h9$A移动通信,通信工程师的家园,通信人才,求职招聘,网络优化,通信工程,出差住宿,通信企业黑…

AD原理图设计与Clion源码工具学习

目录一、AD绘制电路原理图1.准备工作2.绘制STM32F103C8T6原理图1.新建工程2.开始绘制3.STM32W5500的系统电路原理图设计二、Clion写点亮LED的程序1.配置环境2.实现LED点灯程序1.新建工程2.配置 CLion 工程文件3.添加代码4.编译烧录三、总结参考资料一、AD绘制电路原理图 1.准备…

几个通信速率

1.比特率 比特率(bit rate)又称传信率、信息传输速率(简称信息速率,information rate)。其定义是:通信线路(或系统)单位时间(每秒)内传输的信息量,即每秒能传输的二进制位数,通常用Rb表示,其单位是比特/秒(bit/s或b/s&…