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

1. 模型介绍 HOG简介

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

2. 实现方法

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

3. HOG特征提取优点

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

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

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

(1) 灰度化(将图像看做一个x,y,z(灰度)的三维图像);

#encoding:utf-8import numpy as npimport cv2image = 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校正前后对比,前图为矫正后

#encoding:utf-8import cv2import numpy as npimport mathimport osdef 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):passdata_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)# 公式计算gammaimage_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特征提取流程图:

5. 详细操作流程

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

星型细胞单元

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

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

(6) 测试效果:

import cv2import numpy as npimport mathimport matplotlib.pyplot as pltclass Hog_descriptor():#---------------------------## 初始化# cell_size每个细胞单元的像素数# bin_size表示把360分为多少边#---------------------------#def __init__(self, img, cell_size=256, bin_size=8):self.img = imgself.img = np.sqrt(img / np.max(img))self.img = img * 255self.cell_size = cell_sizeself.bin_size = bin_sizeself.angle_unit = 360 / self.bin_size#---------------------------## 获取hog向量和图片#---------------------------#def extract(self):# 获得原图的shapeheight, 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为2x2for 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_sizefor 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_unitreturn idx, (idx + 1) % self.bin_size, mod#---------------------------## 将梯度直方图进行绘图#---------------------------#def render_gradient(self, image, cell_gradient):cell_width = self.cell_size / 2max_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_magangle = 0angle_gap = self.angle_unitfor 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_gapreturn imageimg = cv2.imread('C.jpg', cv2.IMREAD_GRAYSCALE)n = 8img = 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 cv2import numpy as npimport matplotlib.pyplot as pltgray = cv2.imread('C.jpg', 0)n = 8gray = cv2.resize(gray, (0, 0), fx=1/n, fy=1/n, interpolation=cv2.INTER_NEAREST)im = np.float32(gray) / 255.0# Calculate gradientgx = 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 pltimport seaborn as sns; sns.set()import numpy as npfrom skimage import data, color, featureimport skimage.dataimport cv2image1 = cv2.imread("C.jpg")n = 8image1 = 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()