在将图像输入机器学习算法之前,通常会对其进行预处理,其中一步就是将图像转换为特征向量。正如我们将在本教程中看到的,将图像转换为特征向量有几个优点,可以使其更高效。
在将图像转换为特征向量的不同技术中,两种最常与不同机器学习算法结合使用的流行技术是定向梯度直方图(Histogram of Oriented Gradients)和词袋(Bag-of-Words)技术。
在本教程中,您将了解用于图像向量表示的定向梯度直方图(HOG)和词袋(BoW)技术。
完成本教程后,您将了解:
- 使用定向梯度直方图和词袋技术进行图像向量表示有什么优点?
- 如何在OpenCV中使用定向梯度直方图技术。
- 如何在OpenCV中使用词袋技术。
通过我的书《OpenCV 机器学习》启动您的项目。它提供了带有可用代码的自学教程。
让我们开始吧。

使用 OpenCV 为机器学习进行图像向量表示
图片来源:John Fowler,保留部分权利。
教程概述
本教程分为四个部分;它们是
- 使用HOG或BoW进行图像向量表示的优点是什么?
- 定向梯度直方图技术
- 词袋技术
- 对技术进行测试
使用HOG或BoW进行图像向量表示的优点是什么?
在使用机器学习算法时,图像数据通常会经过一个数据预处理步骤,其结构是为了让机器学习算法能够处理它。
例如,在OpenCV中,ml模块要求将图像数据以等长特征向量的形式输入机器学习算法。
每个训练样本都是一个值向量(在计算机视觉中有时被称为特征向量)。通常所有向量都具有相同数量的组件(特征);OpenCV ml模块假设如此。
– OpenCV, 2023。
一种构建图像数据的方法是将其展平为一维向量,其中向量的长度等于图像中的像素数。例如,一个$20\times 20$像素的图像将生成一个长度为400像素的一维向量。这个一维向量作为输入机器学习算法的特征集,其中每个像素的强度值代表每个特征。
然而,虽然这是我们可以创建的最简单的特征集,但它并不是最有效的,尤其是在处理更大的图像时,这会导致过多的输入特征,机器学习算法无法有效处理。
这可能会显著影响在具有许多输入特征的数据上拟合的机器学习算法的性能,通常被称为“维度灾难”。
– 机器学习的降维介绍, 2020。
相反,我们希望减少表示每个图像的输入特征数量,从而使机器学习算法更好地泛化到输入数据。更专业的说法是,希望进行降维,将图像数据从高维空间转换到低维空间。
一种方法是应用特征提取和表示技术,例如定向梯度直方图(HOG)或词袋(BoW),以更紧凑的方式表示图像,从而减少特征集中的冗余并降低处理它的计算要求。
使用上述技术将图像数据转换为特征向量的另一个优点是,图像的向量表示对光照、比例或视点变化更具鲁棒性。
想开始学习 OpenCV 机器学习吗?
立即参加我的免费电子邮件速成课程(附示例代码)。
点击注册,同时获得该课程的免费PDF电子书版本。
在以下部分中,我们将探讨使用HOG和BoW技术进行图像向量表示。
定向梯度直方图技术
HOG是一种特征提取技术,旨在通过其边缘方向的分布来表示图像空间内物体的局部形状和外观。
简而言之,HOG技术在应用于图像时执行以下步骤
-
- 使用例如Prewitt算子计算图像的水平和垂直梯度。然后计算图像中每个像素的梯度幅度和方向。
-
- 将图像划分为固定大小的非重叠单元格,并计算每个单元格的梯度直方图。这种每个图像单元格的直方图表示更紧凑,对噪声更具鲁棒性。单元格大小通常根据我们想要捕获的图像特征大小进行设置。
-
- 将单元格块上的直方图连接成一维特征向量并进行归一化。这使得描述符对光照变化更具鲁棒性。
-
- 最后,它将代表单元格块的所有归一化特征向量连接起来,以获得整个图像的最终特征向量表示。
OpenCV中的HOG实现接受几个输入参数,这些参数对应于上述步骤,包括
-
- 窗口大小(winSize),对应于要检测的最小对象大小。
- 单元格大小(cellSize),通常捕获感兴趣的图像特征的大小。
- 块大小(blockSize),解决光照变化问题。
- 块步幅(blockStride),控制相邻块重叠的程度。
- 直方图bin的数量(nbins),用于捕获0到180度之间的梯度。
让我们创建一个函数hog_descriptors()
,它使用HOG技术计算一组图像的特征向量
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
def hog_descriptors(imgs): # 创建一个列表来存储HOG特征向量 hog_features = [] # 根据所使用的图像数据设置HOG描述符的参数值 winSize = (20, 20) blockSize = (10, 10) blockStride = (5, 5) cellSize = (10, 10) nbins = 9 # 将其余参数设置为默认值 derivAperture = 1 winSigma = -1. histogramNormType = 0 L2HysThreshold = 0.2 gammaCorrection = False nlevels = 64 # 创建HOG描述符 hog = HOGDescriptor(winSize, blockSize, blockStride, cellSize, nbins, derivAperture, winSigma, histogramNormType, L2HysThreshold, gammaCorrection, nlevels) # 计算输入图像的HOG描述符并将特征向量添加到列表中 for img in imgs: hist = hog.compute(img.reshape(20, 20).astype(uint8)) hog_features.append(hist) return array(hog_features) |
注意:重要的是,图像在这里的重塑方式与本教程稍后将使用的图像数据集相对应。如果您使用不同的数据集,请不要忘记相应地调整这部分代码。
词袋技术
BoW技术已在本教程中介绍,应用于使用机器学习算法对文本进行建模。
然而,这项技术也可以应用于计算机视觉,其中图像被视为可以从中提取特征的视觉词。因此,当应用于计算机视觉时,BoW技术通常被称为视觉词袋技术。
简而言之,BoW技术在应用于图像时执行以下步骤
-
- 使用尺度不变特征变换(SIFT)或加速稳健特征(SURF)等算法从图像中提取特征描述符。理想情况下,提取的特征应不随强度、尺度、旋转和仿射变化而变化。
-
- 从特征描述符生成码字,其中每个码字代表相似的图像块。生成这些码字的一种方法是使用k-means聚类将相似的描述符聚合成簇,其中簇的中心将代表视觉词,而簇的数量代表词汇量。
-
- 将特征描述符映射到词汇表中最近的簇,本质上为每个特征描述符分配一个码字。
-
- 将码字分箱到直方图中,并使用此直方图作为图像的特征向量表示。
让我们创建一个函数bow_descriptors()
,它使用SIFT将BoW技术应用于一组图像
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
def bow_descriptors(imgs): # 创建SIFT描述符 sift = SIFT_create() # 创建BoW描述符 # 聚类数量为50(类似于词汇量)是根据经验选择的 bow_trainer = BOWKMeansTrainer(50) bow_extractor = BOWImgDescriptorExtractor(sift, BFMatcher(NORM_L2)) for img in imgs: # 重塑每个RGB图像并将其转换为灰度图像 img = reshape(img, (32, 32, 3), 'F') img = cvtColor(img, COLOR_RGB2GRAY).transpose() # 提取SIFT描述符 _, descriptors = sift.detectAndCompute(img, None) # 将SIFT描述符添加到BoW词汇训练器中 if descriptors is not None: bow_trainer.add(descriptors) # 执行k-means聚类并返回词汇表 voc = bow_trainer.cluster() # 将词汇表分配给BoW描述符提取器 bow_extractor.setVocabulary(voc) # 创建一个列表来存储BoW特征向量 bow_features = [] for img in imgs: # 重塑每个RGB图像并将其转换为灰度图像 img = reshape(img, (32, 32, 3), 'F') img = cvtColor(img, COLOR_RGB2GRAY).transpose() # 计算BoW特征向量 hist = bow_extractor.compute(img, sift.detect(img)) # 将特征向量添加到列表中 if hist is not None: bow_features.append(hist[0]) return array(bow_features) |
注意:重要的是,图像在这里的重塑方式与本教程稍后将使用的图像数据集相对应。如果您使用不同的数据集,请不要忘记相应地调整这部分代码。
对技术进行测试
对于所有情况而言,不一定存在单一的最佳技术,而且为正在处理的图像数据选择技术通常需要进行对照实验。
在本教程中,作为示例,我们将把HOG技术应用于OpenCV附带的数字数据集,并将BoW技术应用于CIFAR-10数据集的图像。为了本教程的目的,我们将只考虑这些数据集中图像的一个子集,以减少所需的处理时间。尽管如此,相同的代码可以很容易地扩展到完整的数据集。
我们将从加载我们将要使用的数据集开始。回顾一下,我们已经在本教程中看到如何从每个数据集中提取图像。digits_dataset
和cifar_dataset
是我创建的Python脚本,分别包含加载数字和CIFAR-10数据集的代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
from digits_dataset import split_images, split_data from cifar_dataset import load_images # 加载数字图像 img, sub_imgs = split_images('Images/digits.png', 20) # 从数字图像中获取数据集 digits_imgs, _, _, _ = split_data(20, sub_imgs, 0.8) # 从CIFAR数据集中加载一批图像 cifar_imgs = load_images('Images/cifar-10-batches-py/data_batch_1') # 只考虑图像的一个子集 digits_subset = digits_imgs[0:100, :] cifar_subset = cifar_imgs[0:100, :] |
然后,我们可以将数据集传递给我们之前在本教程中创建的hog_descriptors()
和bow_descriptors()
函数
1 2 3 4 5 |
digits_hog = hog_descriptors(digits_subset) print('HOG特征向量的大小:', digits_hog.shape) cifar_bow = bow_descriptors(cifar_subset) print('BoW特征向量的大小:', cifar_bow.shape) |
完整的代码清单如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 |
from cv2 import (imshow, waitKey, HOGDescriptor, SIFT_create, BOWKMeansTrainer, BOWImgDescriptorExtractor, BFMatcher, NORM_L2, cvtColor, COLOR_RGB2GRAY) from digits_dataset import split_images, split_data from cifar_dataset import load_images from numpy import uint8, array, reshape # 加载数字图像 img, sub_imgs = split_images('Images/digits.png', 20) # 从数字图像中获取数据集 digits_imgs, _, _, _ = split_data(20, sub_imgs, 0.8) # 从CIFAR数据集中加载一批图像 cifar_imgs = load_images('Images/cifar-10-batches-py/data_batch_1') # 只考虑图像的一个子集 digits_subset = digits_imgs[0:100, :] cifar_subset = cifar_imgs[0:100, :] def hog_descriptors(imgs): # 创建一个列表来存储HOG特征向量 hog_features = [] # 根据所使用的图像数据设置HOG描述符的参数值 winSize = (20, 20) blockSize = (10, 10) blockStride = (5, 5) cellSize = (10, 10) nbins = 9 # 将其余参数设置为默认值 derivAperture = 1 winSigma = -1. histogramNormType = 0 L2HysThreshold = 0.2 gammaCorrection = False nlevels = 64 # 创建HOG描述符 hog = HOGDescriptor(winSize, blockSize, blockStride, cellSize, nbins, derivAperture, winSigma, histogramNormType, L2HysThreshold, gammaCorrection, nlevels) # 计算输入图像的HOG描述符并将特征向量添加到列表中 for img in imgs: hist = hog.compute(img.reshape(20, 20).astype(uint8)) hog_features.append(hist) return array(hog_features) def bow_descriptors(imgs): # 创建SIFT描述符 sift = SIFT_create() # 创建BoW描述符 # 聚类数量为50(类似于词汇量)是根据经验选择的 bow_trainer = BOWKMeansTrainer(50) bow_extractor = BOWImgDescriptorExtractor(sift, BFMatcher(NORM_L2)) for img in imgs: # 重塑每个RGB图像并将其转换为灰度图像 img = reshape(img, (32, 32, 3), 'F') img = cvtColor(img, COLOR_RGB2GRAY).transpose() # 提取SIFT描述符 _, descriptors = sift.detectAndCompute(img, None) # 将SIFT描述符添加到BoW词汇训练器中 if descriptors is not None: bow_trainer.add(descriptors) # 执行k-means聚类并返回词汇表 voc = bow_trainer.cluster() # 将词汇表分配给BoW描述符提取器 bow_extractor.setVocabulary(voc) # 创建一个列表来存储BoW特征向量 bow_features = [] for img in imgs: # 重塑每个RGB图像并将其转换为灰度图像 img = reshape(img, (32, 32, 3), 'F') img = cvtColor(img, COLOR_RGB2GRAY).transpose() # 计算BoW特征向量 hist = bow_extractor.compute(img, sift.detect(img)) # 将特征向量添加到列表中 if hist is not None: bow_features.append(hist[0]) return array(bow_features) digits_hog = hog_descriptors(digits_subset) print('HOG特征向量的大小:', digits_hog.shape) cifar_bow = bow_descriptors(cifar_subset) print('BoW特征向量的大小:', cifar_bow.shape) |
上述代码返回以下输出
1 2 |
HOG特征向量的大小: (100, 81) BoW特征向量的大小: (100, 50) |
根据我们选择的参数值,我们可以看到HOG技术为每张图像返回大小为$1\times 81$的特征向量。这意味着每张图像现在由81维空间中的点表示。另一方面,BoW技术为每张图像返回大小为$1\times 50$的向量,其中向量长度由所选k-means聚类的数量决定,这也类似于词汇量。
因此,我们可以看到,我们通过应用HOG和BoW技术,将每张图像更紧凑地表示出来,而不是简单地将每张图像展平为一维向量。
我们的下一步将是看看如何利用这些数据使用不同的机器学习算法。
进一步阅读
如果您想深入了解,本节提供了更多关于该主题的资源。
书籍
- 使用Python 3学习OpenCV 4计算机视觉, 2020。
网站
- OpenCV,https://opencv.ac.cn/
- 使用OpenCV解释定向梯度直方图,https://learnopencv.com/histogram-of-oriented-gradients/
- 计算机视觉中的词袋模型,https://en.wikipedia.org/wiki/Bag-of-words_model_in_computer_vision
总结
在本教程中,您将了解定向梯度直方图和词袋技术用于图像向量表示。
具体来说,你学到了:
- 使用定向梯度直方图和词袋技术进行图像向量表示的优点
- 如何在OpenCV中使用定向梯度直方图技术。
- 如何在OpenCV中使用词袋技术。
你有什么问题吗?
在下面的评论中提出您的问题,我将尽力回答。
暂无评论。