人脸识别与美颜算法实战-基于机器学习的人脸识别

81575647a85d438eb2f4dbb4180e59b7.png

机器学习根据输出的类型一般分为两类,分类和回归。分类的输出一般是离散值,回归输出的值一般是连续的。比如,人脸识别这种就属于分类问题,房价预测一般是一个回归问题。

鸢尾花分类

# -*- coding: UTF-8 -*-
# 导入数据集
from sklearn.datasets import load_iris
iris = load_iris()

# 标签,和特征
X = iris.data
y = iris.target
print('标签的特征维度',X.shape)
# 将数据集分为训练集,测试集
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.4, random_state=1)

# 定义模型并且训练
from sklearn.naive_bayes import GaussianNB
gnb = GaussianNB()
gnb.fit(X_train, y_train)

# 进行预测
y_pred = gnb.predict(X_test)

# 模型预测的准确度
from sklearn import metrics
print("Gaussian Naive Bayes model accuracy(in %):", metrics.accuracy_score(y_test, y_pred)*100)

41a8116b4e424896ba589ed884456376.png

 机器学习基本概念

数据集

完整的数据集包括3个数据集:训练数据集、验证数据集、测试数 据集。

·训练数据集(Training Data):用于训练学习模型,输入模型中 训练出最后的模型。

·验证数据集(Validation Data):用于衡量训练过程中模型的好 坏,因为机器学习算法是通过不断迭代来慢慢优化模型,所以验证数 据就可以用来监视模型训练时的性能变化。

·测试数据集(Testing Data):在模型训练好了之后,测试数据 用于衡量最终模型的性能好坏。 一般三者的比例约是70%、20%、10%。

特征

在图像处理算法中,输入的一张照片有可能会有几百万个像素, 这个数据量很大。在处理图像中,往往会通过对图像的一些数学运算 提取一些信息来表示图像的某些特性,这种数据被称为特征。

·降低数据维度:通过提取特征向量,把原始图像数据的维度大 大较低。

·提高模型性能:一个好的特征,可以表征原始图像数据最关键 的信息,提高模型的性能。

模型

机器学习中提到的模型指的是设计的算法以及训练后的参数。

损失函数

损失函数(Loss Function)是用来近似地衡量模型好坏的一个重 要指标,表示特征向量训练结果和响应向量之间的差距。

优化函数

模型的训练过程就是降低损失函数的过程,因此我们引入优化函 数来寻找在参数空间损失函数的最优解。

梯度下降法经常会陷入局部最优点,因此设计一个好的优化函数 不仅需要让损失函数拥有更快的收敛速度,也需要拥有找到全局最优解的能力,以避免局部最优。

模型的泛化能力、欠拟合和过拟合

泛化能力(Generalization Ability)是指训练出的机器学习模 型在实际应用中处理各种情况的能力及表征模型处理未知数据的准确 率,这个是模型的一个重要衡量指标。

欠拟合(Underfitting)是指模型复杂度太低,使得模型能表达 的泛化能力不够,对测试样本和训练样本都没有很好的预测性能欠 拟合时,模型在训练集和测试集上都有很大的误差。

过拟合(Overfitting)是指模型复杂度太高,使得模型对训练样 本有很好的预测性能,但是对测试样本的预测性能很差,最终导致泛 化能力也不行。

防止过拟合的方法:不选用过于复杂的模型、增加数据集和正则化等

偏差和方差

偏差:描述的是预测值(估计值)与真实值之间的差距。偏差越 大,越偏离真实数据。高偏差对应的是欠拟合。高偏差时,模型在训 练集和测试机上都有很大的误差。

方差:描述的是预测值的变化范围及离散程度,也就是离其期望 值的距离。方差越大,数据的分布越分散。高方差对应的是过拟合。 高方差时,模型在训练集上的误差很小,但是在测试集上的误差很 大。

归一化与标准化

归一化方法:把数线性映射到(0,1)之间,主要是为了数据处理 的方便,对输入变量x进行归一化公式: (x-min(x))/(max(x)-min(x))

标准化方法:将数据按比例缩放,使之落入一个较小的特定区 间。(x-mean(x))/std(x)

协方差

协方差表示两个变量在变化过程中变化趋势的相似程度,或者说 是相关程度。协方差计算公式如下:

Cov(X,Y)=E[(X-μx)(Y-μy)]

当X增大时,Y也增大,说明两变量是同向变化的,这时协方差 >0;当X增大时,Y减小,说明两个变量是反向变化的,协方差<0。协 方差越大,说明X、Y同向程度越高;协方差越小,说明X、Y反向程度 越高。

模型的误差、偏差和方差

模型误差(Error)反映的是整个模型的准确度模型偏差 (Bias)反映的是模型在样本上的输出与真实值之间的误差,即模型 本身的精准度。模型方差(Variance)反映的是模型每一次输出结果 与模型输出期望之间的误差,即模型的稳定性。

Error^2=Bias^2+Variance

图像预处理

在进行人脸识别的过程中需要进行两个步骤。首先需要检测图片中是否有人脸。如果有人脸则需要进行特征提取,然后与特征库中的对象进行比对判断是哪个人脸。

5d54885fbc9448a082df8964b19c6946.png

图像降噪

import cv2
import numpy as np
import matplotlib.pyplot as plt
def main():
    img = cv2.imread("9.jpg")
    img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
    # 均值滤波
    # 用3*3的核对图片进行卷积操作,核上的参数都是1/9,达到均值的效果
    blur = cv2.blur(img, (3, 3))
    # 方框滤波(归一化)=均值滤波
    box1 = cv2.boxFilter(img, -1, (3, 3), normalize=True)
    # 方框滤波(不归一化)
    box2 = cv2.boxFilter(img, -1, (3, 3), normalize=False)
    # 高斯滤波
    # 用5*5的核进行卷积操作,但核上离中心像素近的参数大。
    guassian = cv2.GaussianBlur(img, (5, 5), 1)
    # 中值滤波
    # 将某像素点周围5*5的像素点提取出来,排序,取中值写入此像素点。
    mean = cv2.medianBlur(img, 5)

    # 展示效果
    titles = ['Original figure', 'blur', 'box_norm', 'box_no_norm', 'guassian', 'mean']
    images = [img, blur, box1, box2, guassian, mean]
    for i in range(6):
        plt.subplot(2, 3, i + 1), plt.imshow(images[i])
        plt.title(titles[i])
        plt.xticks([]), plt.yticks([])
    plt.show()


if __name__ == '__main__':
    main()

图像增强中滤波计算就是对图像做一个均值处理,去掉噪声后的 图像会呈现比较模糊的状态,这时候可以尝试采用图像增强操作。对 于比较模糊的图像采用图像增强操作,也可以调高图像的质量。 

Python的PIL模块中有一个叫ImageEnhance的类,该类专门用于图 像的增强处理,不仅可以增强(或减弱)图像的亮度、对比度和色 度,还可以用于增强图像的锐度。因此可以非常简单地实现图像增强 操作。

图像增强

# -*- coding: UTF-8 -*-

from PIL import Image
from PIL import ImageEnhance

# 原始图像
image = Image.open('10.jpg')
image.show()

# 亮度增强
enh_bri = ImageEnhance.Brightness(image)
brightness = 1.3
image_brightened = enh_bri.enhance(brightness)
image_brightened.show()

# 色度增强
enh_col = ImageEnhance.Color(image)
color = 1.5
image_colored = enh_col.enhance(color)
image_colored.show()

# 对比度增强
enh_con = ImageEnhance.Contrast(image)
contrast = 1.5
image_contrasted = enh_con.enhance(contrast)
image_contrasted.show()

# 锐度增强
enh_sha = ImageEnhance.Sharpness(image)
sharpness = 3.0
image_sharped = enh_sha.enhance(sharpness)
image_sharped.show()

图像特征

所谓的图像特征就如同一个人有高、矮、胖、瘦等特征一样,图 像具有纹理、色彩、形状、空间等特征。一张图像包含的信息太多 了,对于计算机而言,需要建立一个能够代表这幅图像用于相关特性 的数学描述,因此就有了提取图像特征的概念。

HOG特征

284d98c0403045deb585f13d0798c809.png

方向梯度直方图(Histogram of Oriented Gradient,HOG)特征 是一种在计算机视觉和图像处理中用来进行物体检测的特征描述子。 它通过计算和统计图像局部区域的梯度方向直方图来构成特征。

首先将图像分成小的连通区域,我们把它叫细胞单元。然后采集 细胞单元中各像素点的梯度或边缘方向的直方图。最后把这些直方图 组合起来就可以构成HOG特征描述器。

图像归一化处理

为了减少光照因素的影响,需要将整个图 像进行归一化处理。选取的是Gamma压缩算法,因为光照对图像局部的 表层影响最大,对输入的灰度图像进行Gamma压缩,Gamma的大小小于 1。Gamma压缩公式如下:

a1bdde86381b4af5a1f751284f642d6d.png

计算图像梯度

计算图像横坐标和纵坐标方向的梯度,并据 此计算每个像素位置的梯度方向值。首先用[-1,0,1]梯度算子对图像 做卷积运算,得到x方向的梯度分量gradx,然后用[1,0,-1]T梯度算子对图像做卷积运算,得到y方向的梯度分量grady。然后根据下面的公 式计算该像素点的梯度大小和方向。

 7897c276820145f0a726860d5a1c5d76.png

将图像分割成cell,每个cell构建梯度方向的直方图。

将图 像分成若干个cell,例如分割后的每个cell为6×6个像素,采用9个 bin的直方图来统计这6×6个像素的梯度信息。统计方法是对cell内每 个像素的梯度方向在直方图中进行加权投影,就可以得到这个cell梯 度方向的直方图了,这也构成了每个cell对应的9维特征向量。图像分 割了N个cell,那么就建立了图像的HOG特征描述。

把cell组合成大的block,每个block内做归一化梯度直方 图。

之前已经得到了图像的9N个特征描述。那么可不可以直接拿来用 呢?答案是否定的。因为整张图像上局部光照的变化及前景与背景对 比度的变化有时候会很强烈,使得梯度强度的变化范围非常大。所以 每个cell的局部特征会对光照特别敏感,这是我们不想看到的。

解决思路:把各个cell组合成大的且空间上连通的区间 (blocks),并将一个block内所有cell的特征向量串联起来便得到该 block的特征集。所有block连起来的归一化描述符被称为HOG特征描述 符。

python实现

import cv2
import numpy as np
import math
import matplotlib.pyplot as plt

class Hog_descriptor():
    #---------------------------#
    #   初始化
    #   cell_size每个细胞单元的像素数
    #   bin_size表示把360分为多少边
    #---------------------------#
    def __init__(self, img, cell_size=16, bin_size=8):
        self.img = img
        self.img = np.sqrt(img / np.max(img))
        self.img = img * 255
        self.cell_size = cell_size
        self.bin_size = bin_size
        self.angle_unit = 360 / self.bin_size
    #---------------------------#
    #   获取hog向量和图片
    #---------------------------#
    def extract(self):
        # 获得原图的shape
        height, width = self.img.shape
        # 计算原图的梯度大小
        gradient_magnitude, gradient_angle = self.global_gradient()
        gradient_magnitude = abs(gradient_magnitude)

        # cell_gradient_vector用来保存每个细胞的梯度向量
        cell_gradient_vector = np.zeros((int(height / self.cell_size), int(width / self.cell_size), self.bin_size))
        height_cell,width_cell,_ = np.shape(cell_gradient_vector)
        #---------------------------#
        #   计算每个细胞的梯度直方图
        #---------------------------#
        for i in range(height_cell):
            for j in range(width_cell):
                # 获取这个细胞内的梯度大小
                cell_magnitude = gradient_magnitude[i * self.cell_size:(i + 1) * self.cell_size,
                                 j * self.cell_size:(j + 1) * self.cell_size]
                # 获得这个细胞内的角度大小
                cell_angle = gradient_angle[i * self.cell_size:(i + 1) * self.cell_size,
                             j * self.cell_size:(j + 1) * self.cell_size]
                # 转化为梯度直方图格式
                cell_gradient_vector[i][j] = self.cell_gradient(cell_magnitude, cell_angle)

        # hog图像
        hog_image = self.render_gradient(np.zeros([height, width]), cell_gradient_vector)
        hog_vector = []
        # block为2x2
        for i in range(height_cell - 1):
            for j in range(width_cell - 1):
                block_vector = []
                block_vector.extend(cell_gradient_vector[i][j])
                block_vector.extend(cell_gradient_vector[i][j + 1])
                block_vector.extend(cell_gradient_vector[i + 1][j])
                block_vector.extend(cell_gradient_vector[i + 1][j + 1])
                mag = lambda vector: math.sqrt(sum(i ** 2 for i in vector))
                magnitude = mag(block_vector)
                if magnitude != 0:
                    normalize = lambda block_vector, magnitude: [element / magnitude for element in block_vector]
                    block_vector = normalize(block_vector, magnitude)
                hog_vector.append(block_vector)
        return hog_vector, hog_image
    #---------------------------#
    #   计算原图的梯度大小
    #   角度大小
    #---------------------------#
    def global_gradient(self):
        gradient_values_x = cv2.Sobel(self.img, cv2.CV_64F, 1, 0, ksize=5)
        gradient_values_y = cv2.Sobel(self.img, cv2.CV_64F, 0, 1, ksize=5)
        gradient_magnitude = cv2.addWeighted(gradient_values_x, 0.5, gradient_values_y, 0.5, 0)
        gradient_angle = cv2.phase(gradient_values_x, gradient_values_y, angleInDegrees=True)
        return gradient_magnitude, gradient_angle
    #---------------------------#
    #   分解角度信息到
    #   不同角度的直方图上
    #---------------------------#
    def cell_gradient(self, cell_magnitude, cell_angle):
        orientation_centers = [0] * self.bin_size
        for i in range(cell_magnitude.shape[0]):
            for j in range(cell_magnitude.shape[1]):
                gradient_strength = cell_magnitude[i][j]
                gradient_angle = cell_angle[i][j]
                min_angle, max_angle, mod = self.get_closest_bins(gradient_angle)
                orientation_centers[min_angle] += (gradient_strength * (1 - (mod / self.angle_unit)))
                orientation_centers[max_angle] += (gradient_strength * (mod / self.angle_unit))
        return orientation_centers
    #---------------------------#
    #   计算每个像素点所属的角度
    #---------------------------#
    def get_closest_bins(self, gradient_angle):
        idx = int(gradient_angle / self.angle_unit)
        mod = gradient_angle % self.angle_unit
        return idx, (idx + 1) % self.bin_size, mod
    #---------------------------#
    #   将梯度直方图进行绘图
    #---------------------------#
    def render_gradient(self, image, cell_gradient):
        cell_width = self.cell_size / 2
        max_mag = np.array(cell_gradient).max()
        for x in range(cell_gradient.shape[0]):
            for y in range(cell_gradient.shape[1]):
                cell_grad = cell_gradient[x][y]
                cell_grad /= max_mag
                angle = 0
                angle_gap = self.angle_unit
                for magnitude in cell_grad:
                    angle_radian = math.radians(angle)
                    x1 = int(x * self.cell_size + magnitude * cell_width * math.cos(angle_radian))
                    y1 = int(y * self.cell_size + magnitude * cell_width * math.sin(angle_radian))
                    x2 = int(x * self.cell_size - magnitude * cell_width * math.cos(angle_radian))
                    y2 = int(y * self.cell_size - magnitude * cell_width * math.sin(angle_radian))
                    cv2.line(image, (y1, x1), (y2, x2), int(255 * math.sqrt(magnitude)))
                    angle += angle_gap
        return image

img = cv2.imread('10.jpg', cv2.IMREAD_GRAYSCALE)
print(img.shape)
hog = Hog_descriptor(img, cell_size=20, bin_size=12)
vector, image = hog.extract()
print(len(vector[0]))
print(len(vector))
plt.imshow(image, cmap=plt.cm.gray)
plt.show()

使用scikit-image库

from skimage import feature as ft
import cv2
import matplotlib.pyplot as plt

img = cv2.imread('9.jpg', cv2.IMREAD_GRAYSCALE)
features = ft.hog(img,orientations=6,pixels_per_cell=[20,20],cells_per_block=[2,2],visualize=True)
print(len(features))
print(features[0].shape)
print(features[1].shape)
plt.imshow(features[1],cmap=plt.cm.gray)
plt.show()

 23a92ed636824734bcaeff9042be2614.png

LBP特征

局部二值模式(Local Binary Pattern,LBP)是一种用来描述图 像局部纹理特征的算子

3b660febbe3148a09ac60adade712eaa.png

(1)将检测窗口划分为16×16个cell。

(2)对于每个cell中的一个像素,将相邻的8个像素的灰度值与 其进行比较,若周围的像素值大于中心的像素值,则该像素点的位置 被标记为1,否则标记为0。这样,3×3邻域内的8个点经比较可产生8 位二进制数,即得到了该窗口中心像素点的LBP值。

(3)计算每个cell的直方图,即每个数字出现的频率,然后对该 直方图进行归一化处理。

(4)将得到的每个cell的统计直方图进行连接,成为一个特征向 量,也就是整幅图的LBP特征描述。

python 实现

import matplotlib.pyplot as plt
import cv2 as cv
from skimage import data
img = data.coffee()
def lbp_basic(img):
    basic_array = np.zeros(img.shape,np.uint8)
    for i in range(basic_array.shape[0]-1):
        for j in range(basic_array.shape[1]-1):
            basic_array[i,j] = bin_to_decimal(cal_basic_lbp(img,i,j))
    return basic_array
def cal_basic_lbp(img,i,j):#比中心像素大的点赋值为1,比中心像素小的赋值为0,返回得到的二进制序列
    sum = []
    if img[i - 1, j ] > img[i, j]:
        sum.append(1)
    else:
        sum.append(0)
    if img[i - 1, j+1 ] > img[i, j]:
        sum.append(1)
    else:
        sum.append(0)
    if img[i , j + 1] > img[i, j]:
        sum.append(1)
    else:
        sum.append(0)
    if img[i + 1, j+1 ] > img[i, j]:
        sum.append(1)
    else:
        sum.append(0)
    if img[i + 1, j ] > img[i, j]:
        sum.append(1)
    else:
        sum.append(0)
    if img[i + 1, j - 1] > img[i, j]:
        sum.append(1)
    else:
        sum.append(0)
    if img[i , j - 1] > img[i, j]:
        sum.append(1)
    else:
        sum.append(0)
    if img[i - 1, j - 1] > img[i, j]:
        sum.append(1)
    else:
        sum.append(0)
    return sum
def bin_to_decimal(bin):#二进制转十进制
    res = 0
    bit_num = 0 #左移位数
    for i in bin[::-1]:
        res += i << bit_num   # 左移n位相当于乘以2的n次方
        bit_num += 1
    return res
def show_basic_hist(a): #画原始lbp的直方图      
    hist = cv.calcHist([a],[0],None,[256],[0,256])
    hist = cv.normalize(hist,hist)
    plt.figure(figsize = (8,4))
    plt.plot(hist, color='r')
    plt.xlim([0,256])
    plt.show()
img1 = cv.cvtColor(img,cv.COLOR_BGR2GRAY)
basic_array = lbp_basic(img1)
show_basic_hist(basic_array)
plt.figure(figsize=(11,11))
plt.subplot(1,2,1)
plt.imshow(img1)
plt.subplot(1,2,2)
plt.imshow(basic_array,cmap='Greys_r')
plt.show()  

 skimage调库

import skimage.feature
import skimage.segmentation
import matplotlib.pyplot as plt
import cv2 as cv
import numpy as np
img1=cv.imread('9.jpg',0)

img_ku = skimage.feature.local_binary_pattern(img1,8,1.0,method='default')
img_ku = img_ku.astype(np.uint8)
hist = cv.calcHist([img_ku],[0],None,[256],[0,256])
hist = cv.normalize(hist,hist)
plt.subplot(1,2,1)
plt.plot(hist, color='r')
plt.xlim([0,256])
plt.subplot(1,2,2)
plt.imshow(img_ku,cmap='Greys_r')
plt.show()

006115a4407d4e9eafc85cd73ce4c446.png

Haar-like特征 

它定义了四个基本特征结构,如下A,B,C,D所示,可以将它们理解成为一个窗口,这个窗口将在图像中做步长为1的滑动,最终遍历整个图像。

2c309d643d924f6eacab9b0a099733d4.png

 用上述图片中的特征模板在图片中滑动。比如图6.27中第一个边 缘特征的模板a是2×1个像素,定义a在24×24的图片子窗口中滑动。 每滑动一次会有一个值,则可产生23x24个值,也就是Harr特征值。同 理,其他模板都可以产生若干个Harr特征值。将它们组成一个向量, 就是Harr-like特征向量。

from skimage.transform import integral_image
from skimage.feature import haar_like_feature
import cv2
import numpy as np
from PIL import Image ,ImageEnhance
img=cv2.imread('9.jpg',0)

#图像滤波
img=cv2.medianBlur(img,5)
#图像增强
img=Image.fromarray(img)

# 亮度增强
enh_bri = ImageEnhance.Brightness(img)
brightness = 1.3
image_brightened = enh_bri.enhance(brightness)

# 色度增强
enh_col = ImageEnhance.Color(image_brightened)
color = 1.5
image_colored = enh_col.enhance(color)

# 对比度增强
enh_con = ImageEnhance.Contrast(image_colored)
contrast = 1.5
image_contrasted = enh_con.enhance(contrast)

# 锐度增强
enh_sha = ImageEnhance.Sharpness(image_contrasted)
sharpness = 3.0
image_sharped = enh_sha.enhance(sharpness)

img=np.array(image_sharped)
#积分图像,用于加速harr特征提取
img_ii=integral_image(img)
feature = haar_like_feature(img_ii, 0, 0, 5, 5, 'type-3-x')
print(feature)
print(len(feature))

人脸识别解决方案中的特征选取:在人脸识别算法中需要提取大 量的人脸特征,Harr-like特征包含了3个图像特征类——边缘特征、 线性特征、中心和对角特征,能够最大程度地保留人脸图像的信息, 是人脸识别中最常用的特征,也是OpenCV人脸识别算法解决方案才有 的特征

调用opencv现有的库进行人脸检测

import cv2
import logging
import os

#设置日志
logging.basicConfig(level = logging.INFO, 
                    format = '%(asctime)s - %(levelname)s: %(message)s')
logger = logging.getLogger(__name__)

#待检测图片路径
images_list_path = os.listdir('images')
print(images_list_path)

#读取图片
images = []
gray_images = []
for _image in images_list_path:
    logger.info('读取图片: ' + _image)
    _image_path = os.path.join('./images/', _image)
    image = cv2.imread(_image_path)
    images.append(image)
    #将图片转换为灰度图
    gray_image = cv2.cvtColor(image,cv2.COLOR_RGB2GRAY)
    gray_images.append(gray_image)
logger.info('图片读取完成')

#检测人脸
logger.info('人脸检测...')
#调用训练好的人脸参数数据,进行人脸检测
face_cascade = cv2.CascadeClassifier(r'./human_front_face.xml')
for i in range(len(images)):
    faces = face_cascade.detectMultiScale(gray_images[i]) #scaleFactor = 1.1, minNeighbors = 3, minSize = (3,3)

    search_info = "检测到 %d 张人脸." % len(faces)
    logger.info(search_info)

    #绘制人脸的矩阵区域
    for (x, y, w, h) in faces:
        cv2.rectangle(images[i], (x,y), (x+w, y+h), (0,0,255), 2)

    #显示图片
    cv2.imshow('Find faces! ', images[i])
    cv2.waitKey(500)

参考文献:

skimage.feature函数使用说明_香博士的博客-CSDN博客_peak_local_max

 

 

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一壶浊酒..

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值