在线会议中人脸面部轮廓图像提取(二)——HOG人脸面部轮廓图像特征提取

前言:所使用图片并无盈利等目的,如有侵犯他人肖像权请联系删除。

1. 模型介绍 HOG简介

Histogram of Oriented Gridients,缩写为HOG,是目前计算机视觉、模式识别领域很常用的一种描述图像局部纹理的特征。它的主要思想是在一副图像中,局部目标的表象和形状能够被梯度或边缘的方向密度分布很好地描述。其本质为:梯度的统计信息,而梯度主要存在于边缘的地方。

在这里插入图片描述

2. 实现方法

首先将图像分成小的连通区域,这些连通区域被叫做细胞单元。然后采集细胞单元中各像素点的梯度的或边缘的方向直方图。最后把这些直方图组合起来,就可以构成特征描述符。
将这些局部直方图在图像的更大的范围内(叫做区间)进行对比度归一化,可以提高该算法的性能,所采用的方法是:先计算各直方图在这个区间中的密度,然后根据这个密度对区间中的各个细胞单元做归一化。通过这个归一化后,能对光照变化和阴影获得更好的效果。

3. HOG特征提取优点

由于HOG是 在图像的局部方格单元上操作,所以它对图像几何的和光学的形变都能保持很好的不变性,这两种形变只会出现在更大的空间领域上。
在粗的空域抽样、精细的方向抽样以及较强的局部光学归一化等条件下,只要行人大体上能够保持直立的姿势,可以容许行人有一些细微的肢体动作,这些细微的动作可以被忽略而不影响检测效果。
HOG特征是特别适合于做图像中的人体检测的 。Hog特征结合SVM分类器已经被广泛应用于图像识别中,尤其在行人检测中获得了极大的成功。
在这里插入图片描述

4. HOG特征提取算法的实现过程

HOG特征提取方法就是将一个image:

(1) 灰度化(将图像看做一个x,y,z(灰度)的三维图像);图片灰度化,便于电脑处理

#encoding:utf-8

import numpy as np
import cv2

image = cv2.imread("C.jpg")
n = 2#进行适当的图片缩放
image = cv2.resize(image, (0, 0), fx=1/n, fy=1/n, interpolation=cv2.INTER_NEAREST)
cv2.imshow("Original",image)
# cv2.waitKey(0)

#彩色转灰色
gray = cv2.cvtColor(image,cv2.COLOR_BGR2GRAY)
cv2.imshow("Gray",gray)
cv2.waitKey(0)

(2) 采用Gamma校正法对输入图像进行颜色空间的标准化(归一化);目的是调节图像的对比度,降低图像局部的阴影和光照变化所造成的影响,同时可以抑制噪音的干扰;
Gamma校正前后对比,前图为矫正后
Gamma校正前后对比,前图为矫正后

#encoding:utf-8
import cv2
import numpy as np
import math
import os


def gamma_trans(img, gamma):  # gamma函数处理
    gamma_table = [np.power(x / 255.0, gamma) * 255.0 for x in range(256)]  # 建立映射表
    gamma_table = np.round(np.array(gamma_table)).astype(np.uint8)  # 颜色值为整数
    return cv2.LUT(img, gamma_table)  # 图片颜色查表。另外可以根据光强(颜色)均匀化原则设计自适应算法。


def nothing(x):
    pass


data_base_dir = r'face_recognition'  # 输入文件夹的路径
outfile_dir = r'1'  # 输出文件夹的路径

list = os.listdir(data_base_dir)
list.sort()
list2 = os.listdir(outfile_dir)
list2.sort()
for file in list:  # 遍历目标文件夹图片
    read_img_name = data_base_dir + '/' + file.strip()  # 取图片完整路径
    image = cv2.imread(read_img_name)  # 读入图片
    img_gray = cv2.imread(read_img_name, 0)  # 灰度图读取,用于计算gamma值

    mean = np.mean(img_gray)
    gamma_val = math.log10(0.5) / math.log10(mean / 255)  # 公式计算gamma

    image_gamma_correct = gamma_trans(image, gamma_val)  # gamma变换

    out_img_name = outfile_dir + '/' + file.strip()
    cv2.imwrite(out_img_name, image_gamma_correct)
    print("The photo which is processed is {}".format(file))


(3) 计算图像每个像素的梯度(包括大小和方向);主要是为了捕获轮廓信息,同时进一步弱化光照的干扰。
(4) 将图像划分成小cells(例如66像素/cell);
(5) 统计每个cell的梯度直方图(不同梯度的个数),即可形成每个cell的descriptor;
(6) 将每几个cell组成一个block(例如3
3个cell/block),一个block内所有cell的特征descriptor串联起来便得到该block的HOG特征descriptor。
(7) 将图像image内的所有block的HOG特征descriptor串联起来就可以得到该image(你要检测的目标)的HOG特征descriptor了。这个就是最终的可供分类使用的特征向量了。
HOG特征提取流程图:
HOG特征提取流程图

5. 详细操作流程

具体每一步的详细过程如下:
● 色彩和伽马归一化
为了减少光照因素的影响,首先需要将整个图像进行规范化(归一化)。在图像的纹理强度中,局部的表层曝光贡献的比重较大,所以,这种压缩处理能够有效地降低图像局部的阴影和光照变化。
● 计算图像梯度
计算图像横坐标和纵坐标方向的梯度,并据此计算每个像素位置的梯度方向值;求导操作不仅能够捕获轮廓,人影和一些纹理信息,还能进一步弱化光照的影响。最常用的方法是:简单地使用一个一维的离散微分模板在一个方向上或者同时在水平和垂直两个方向上对图像进行处理,更确切地说,这个方法需要使用滤波器核滤除图像中的色彩或变化剧烈的数据
● 构建方向直方图
细胞单元中的每一个像素点都为某个基于方向的直方图通道投票。投票是采取加权投票的方式,即每一票都是带有权值的,这个权值是根据该像素点的梯度幅度计算出来。可以采用幅值本身或者它的函数来表示这个权值,实际测试表明: 使用幅值来表示权值能获得最佳的效果,当然,也可以选择幅值的函数来表示,比如幅值的平方根、幅值的平方、幅值的截断形式等。细胞单元可以是矩形的,也可以是星形的。直方图通道是平均分布在0-1800(无向)或0-3600(有向)范围内。经研究发现,采用无向的梯度和9个直方图通道,能在行人检测试验中取得最佳的效果。
星型细胞单元
星型细胞单元

● 将细胞单元组合成大的区间
由于局部光照的变化以及前景-背景对比度的变化,使得梯度强度的变化范围非常大。这就需要对梯度强度做归一化。归一化能够进一步地对光照、阴影和边缘进行压缩。
采取的办法是:把各个细胞单元组合成大的、空间上连通的区间。这样,HOG描述符就变成了由各区间所有细胞单元的直方图成分所组成的一个向量。这些区间是互有重叠的,这就意味着:每一个细胞单元的输出都多次作用于最终的描述器。
区间有两个主要的几何形状——矩形区间(R-HOG)和环形区间(C-HOG)。R-HOG区间大体上是一些方形的格子,它可以有三个参数来表征:每个区间中细胞单元的数目、每个细胞单元中像素点的数目、每个细胞的直方图通道数目。
矩形区间(R-HOG)和环形区间(C-HOG)

矩形区间(R-HOG)和环形区间(C-HOG)
● 收集HOG特征
把提取的HOG特征输入到SVM分类器中,寻找一个最优超平面作为决策函数。
hog提取特征的函数:dlib.get_frontal_face_detector() #人脸特征提取器,该函数是在C++里面定义的

(6) 测试效果:

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=256, 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('C.jpg', cv2.IMREAD_GRAYSCALE)
n = 8
img = cv2.resize(img, (0, 0), fx=1/n, fy=1/n, interpolation=cv2.INTER_NEAREST)
hog = Hog_descriptor(img, cell_size=20, bin_size=12)
vector, image = hog.extract()
plt.imshow(image, cmap=plt.cm.gray)
plt.show()

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

gray = cv2.imread('C.jpg', 0)
n = 8
gray = cv2.resize(gray, (0, 0), fx=1/n, fy=1/n, interpolation=cv2.INTER_NEAREST)
im = np.float32(gray) / 255.0
# Calculate gradient
gx = cv2.Sobel(im, cv2.CV_32F, 1, 0, ksize=1)
gy = cv2.Sobel(im, cv2.CV_32F, 0, 1, ksize=1)
mag, angle = cv2.cartToPolar(gx, gy, angleInDegrees=True)

plt.figure(figsize=(12,8))
plt.imshow(mag)
plt.show()

在这里插入图片描述

import matplotlib.pyplot as plt
import seaborn as sns; sns.set()
import numpy as np
from skimage import data, color, feature
import skimage.data
import cv2

image1 = cv2.imread("C.jpg")
n = 8
image1 = cv2.resize(image1, (0, 0), fx=1/n, fy=1/n, interpolation=cv2.INTER_NEAREST)
image = color.rgb2gray(image1)
hog_vec, hog_vis = feature.hog(image, visualize=True)
fig, ax = plt.subplots(1, 2, figsize=(12, 6),subplot_kw=dict(xticks=[], yticks=[]))
ax[0].imshow(image, cmap='gray')
ax[0].set_title('input image')
ax[1].imshow(hog_vis)
ax[1].set_title('visualization of HOG features');
plt.show()

在这里插入图片描述

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
THE END
分享
二维码
< <上一篇
下一篇>>