Learn OpenCV----方向梯度直方图

本文详细介绍了如何使用OpenCV计算方向梯度直方图(HOG)特征描述符,包括预处理、图像梯度计算、8x8单元格梯度直方图构建、16x16图像块的正则化以及最终的HOG特征向量计算。HOG特征用于行人检测,通过提取图像的梯度信息和方向分布,形成紧凑的特征表示,对光照变化具有一定的鲁棒性。

阅读原文

概述

在这篇博文中,我们将会学习方向梯度直方图(HOG)特征描述符的一些细节。我们将会了解这个特征是什么,并且使用Opencv来对其进行计算。

这里有个很有趣的比喻,如果你是一名初学者,你的目标是星辰大海,那可以看看。
Q:你打算怎么吃掉大象。
A:一口一口的吃掉。

什么是图像特征描述符

简单来说,就是某个东西,能够很好的表示图像,而这个东西具有有效的信息,并且没有太多的冗余。

如果有看过一些深度学习的小伙伴,第一感觉应该是,害,不就是我绞尽脑汁搭建出来的网络所输出的东西嘛。其实不全是。

对于HOG描述符而言,如果输入图像的大小为64x128x3,那么,输出的特征向量长度为3780。

怎么计算的?后边会有给出,不要急噢~

但是,对于一幅图像来说,什么信息是有用的?什么信息又是无用的?因此,特征描述符还是得在实际任务中,才能给出明确的有用与否的定义。

一个简单的例子,如果你有一个任务,是要检测出圆形的按钮。这时,按钮的边缘对你来说,是一个十分有用的信息,而按钮的颜色,可能并没有多大的用处。

此外,特征应该还要能做出唯一性的代表。例如,你所提取的特征,在某些场合下,应该能够区分出硬币和汽车轮胎的不同。
在我们所讨论的HOG特征描述符中,将梯度(有方向的梯度)的方向分布(直方图)作为特征。

如何计算方向梯度直方图

在本节中,我们将对如何计算方向梯度直方图进行详细的讨论。为了方便描述,使用了一个图像块。

第一步,预处理

对于行人检测这个任务来说,HOG特征描述符是在一幅64x128的图像块上计算的。因此,对于我们要检测的对象,只要控制所选择的图像的宽高比为1:2即可。例如下图所示。
在这里插入图片描述

Dalal和Triggs的论文提到了使用gamma矫正来作为预处理步骤,但是增益较小,故作者跳过了这一步。

第二步,计算图像梯度

为了计算HOG描述符,我们首先先计算图像的水平梯度和垂直梯度,再用这些梯度信息构建梯度直方图。计算图像的梯度很简单,使用下图所示的内核对图像进行过滤计算即可。
在这里插入图片描述
借助Opencv,可以使用Sobel操作符(内核大小为1)可以获得相同的效果。

im = cv2.imread('image.png')
# 归一化
img = np.float32(im) / 255.0
# 计算水平梯度
gx = cv2.Sobel(img, cv2.CV_32F, 1, 0, ksize=1)
# 计算垂直梯度
gy = cv2.Sobel(img, cv2.CV_32F, 0, 1, ksize=1)

接下来,使用下面的公式来计算梯度的大小和方向。在这里插入图片描述
当然,有了Opencv的助力,可以使用cartToPolar来完成计算。

mag, angle = cv2.cartToPolar(gx, gy, angleInDegrees=True)

下图给出了计算梯度后的图像表示。

上图中,第一幅图为水平方向的梯度图;第二幅图为垂直方向的梯度图;第三幅图为梯度的大小。

值得注意的是,水平方向的梯度检测的是垂直方向的线,垂直方向的梯度检测的是水平方向的线。从第一幅和第二幅图像左边的道路线可以看出一二。

梯度图像删除了很多不重要的信息(例如,不变的彩色背景),但突出轮廓。

这里也好理解,轮廓部分通常会带来梯度的陡变。

第三步,计算8x8单元格的梯度直方图

什么意思呢,简单来说,就是用一个8x8的小方块,复制N份,已达到全部覆盖图像的地步。如下图所示。
在这里插入图片描述
这里有个疑问,就是我们为什么要这么做呢?
首先,我们使用特征描述符来描述一个图像块的重要原因是,特征描述符提供了一个紧凑的表示。
其次,对于每个8x8的图像块,它的每个像素包含两个值(梯度的大小和方向),因此,共有8x8x2=128个值。在文章的后边,会使用9个bin的直方图来表示这128个值。而这样的表示更为紧凑。
为什么是8x8呢?
可以这么理解,这是对行人进行检测,8x8能够更好的处理人脸和头顶的部分。
得到图像块的梯度方向和大小后,该如何构建直方图呢?
在这里插入图片描述
对于上幅图像,首先看梯度直方图,即最底端的那个条状方块。可以看到,这个条状方块分为9个块,每个块占20,总共表示了0-180。其次,看到上图的蓝色标注的数据。梯度方向为80,对应的梯度大小为2,因此,对应到梯度直方图中,就直接把2这个数据放入标号为80的块中即可。再看到上图中红色标注的数据。梯度方向为10,梯度大小为4,但是梯度直方图中没有合适的标号数据。因为10正好在0-20的中间,因此,将4平均分为2份,并分别放入标号为0和标号为20的块中。

从上述例子可以看出,所谓的平分,仅仅只是10在0-20中所占有的比例为0.5。为了更好的展示,给出下列的例子。

在这里插入图片描述
可以看到,5/20 = 0.25。因此,标号为160的块占地图重量的0.75,即85*0.75 = 63.75。
最后,得到的梯度直方图如下所示。在这里插入图片描述
上图中,标号0和标号7和标号8占有了较大的比重,因此可以认为,在这个图像块中,上下为梯度的主方向。

第四步,将16x16的图像块正则化

在上述的步骤中,图像的梯度对光照十分的敏感,但这是一个令人讨厌的缺点。怎么解决呢?一种很简单的方法是将直方图归一化,让它们不受光照变化的影响。
在这里插入图片描述
上图中的蓝色方框就是所谓的16x16,它包含了4个直方图,即可以得到36x1的向量。对着36x1的向量做归一化,可以得到一个对光照不敏感的直方图。

第五步,计算HOG特征向量

在第四步中,我们知道,每个归一化块(16x16)包含了36x1的向量。为了计算整幅图像块的最终特征向量,我们需要将每个归一化块的向量进行拼接。
可以简单计算得到,每行有7个块,每列有15个块,即,共有7x15=105个归一化块。
所以,最终的特征向量的大小为36x105=3780!这个数字就是该篇博文最先出现的那个数字啦。

<think>我们正在处理用户关于使用scikit-learn进行车辆目标检测训练的问题。根据引用内容,scikit-learn主要用于传统机器学习算法(如SVM)的分类任务,而目标检测通常涉及定位和识别,这通常需要更复杂的模型(如深度学习和目标检测框架)。然而,我们可以基于引用内容提供一些指导思路。 引用[1]和[2]提到scikit-learn是一个强大的机器学习工具,可以用于分类和预测,并提供了构建模型的通用模板。引用[4]详细展示了使用SVM进行分类的步骤,包括数据预处理、特征提取、模型训练和评估。引用[3]则提到了显著目标检测和室内空间实时识别方法,其中提到了预训练网络和k-Means算法。 因此,我们可以这样考虑:在scikit-learn中实现车辆目标检测,通常需要将目标检测问题转化为分类问题(例如,通过滑动窗口提取图像块,然后对每个图像块进行分类,判断是否包含车辆)。但是,这种方法效率较低且效果可能不如现代深度学习目标检测方法(如YOLO、Faster R-CNN等)。不过,如果用户坚持使用scikit-learn,我们可以按照以下步骤进行: 步骤1:数据准备 - 收集车辆和非车辆图像数据集。 - 对图像进行预处理(缩放、归一化等)。 步骤2:特征提取 - 使用颜色直方图(如引用[4]中提到的)、HOG(方向梯度直方图)、SIFT等特征描述子提取特征。 步骤3:构造训练集 - 使用滑动窗口在图像上提取多个图像块(每个图像块作为一个样本)。 - 对每个图像块提取特征,并标记该图像块是否包含车辆(正样本和负样本)。 步骤4:训练分类器 - 使用引用[4]中的SVM模板或其他分类器(如随机森林)进行训练。 步骤5:检测 - 在测试图像上使用滑动窗口,用训练好的分类器对每个窗口进行分类。 - 合并重叠的检测窗口(如非极大值抑制)。 但是,这种方法计算量大,且准确率可能不高。因此,我们也可以参考引用[3]中提到的思路,结合预训练模型和聚类方法(如k-Means)来改进。 考虑到scikit-learn的定位,它并不直接支持深度学习目标检测。如果用户需要更先进的目标检测方法,建议使用深度学习框架(如TensorFlow, PyTorch)和相应的目标检测库(如Detectron2, MMDetection)。 然而,根据用户要求,我们将重点放在scikit-learn上。 具体步骤: 1. **数据收集与标注**: - 收集包含车辆的正样本图像和不包含车辆的负样本图像(或使用背景图像)。 - 对于正样本,通常需要标注车辆的位置(边界框)。但注意,在训练分类器时,我们只需要知道每个图像块(通过滑动窗口得到)是否包含车辆即可。 2. **特征提取**: - 对于每个图像块,提取特征。常用的特征有: - 颜色直方图:计算每个颜色通道的直方图并连接。 - HOG特征:捕获图像的局部形状和纹理信息。 - 也可以结合其他特征,如LBP(局部二值模式)等。 3. **构建数据集**: - 使用滑动窗口遍历所有训练图像,提取图像块并提取特征,同时根据标注信息给每个图像块打标签(有车辆为1,无车辆为0)。 - 注意:负样本可以从不包含车辆的图像中随机裁剪,或者从包含车辆的图像中裁剪不包含车辆的部分。 4. **训练分类器**: - 使用scikit-learn中的SVM(如引用[4]所示)或其他分类器(如随机森林、AdaBoost)进行训练。 - 由于正负样本可能不平衡,需要采取平衡策略(如类权重、过采样等)。 5. **在测试图像上检测**: - 在测试图像上以不同尺度滑动窗口,对每个窗口提取特征并分类。 - 将分类为正样本的窗口记录下来(包括位置和置信度)。 - 使用非极大值抑制(NMS)合并重叠的检测框。 6. **评估**: - 使用准确率、召回率、F1值等指标评估,或者使用目标检测常用的mAP(平均精度均值)指标。 但是,需要提醒用户:这种方法在计算效率上较低,因为需要遍历图像的每个位置和多个尺度,并且特征提取和分类的计算开销较大。 此外,我们还可以参考引用[3]中提到的思路,使用k-Means聚类来生成候选区域(类似于Selective Search的简化),但实现起来较为复杂。 因此,我们给出一个简化的代码框架(基于引用[4]的SVM分类器): ```python import numpy as np from sklearn.svm import SVC from skimage.feature import hog from skimage.transform import resize import cv2 # 假设我们有一个函数来加载数据集,返回图像列表和标注列表(这里省略) # 标注信息:每张图片的车辆边界框(用于生成正负样本) def extract_features(image, window_size=(64,64)): # 将图像块缩放到固定大小 resized = cv2.resize(image, window_size) # 提取HOG特征,也可以结合颜色直方图 hog_feature = hog(resized, orientations=9, pixels_per_cell=(8,8), cells_per_block=(2,2), channel_axis=-1) # 颜色直方图(这里以HSV空间的H通道为例) hsv = cv2.cvtColor(resized, cv2.COLOR_BGR2HSV) hist = cv2.calcHist([hsv], [0], None, [256], [0,256]) hist = hist.flatten() # 组合特征 feature = np.hstack([hog_feature, hist]) return feature # 生成训练数据 def generate_training_data(images, annotations): features = [] labels = [] window_size = (64, 64) for i, image in enumerate(images): # 获取当前图片的标注(边界框列表) boxes = annotations[i] # 生成正样本:从标注的边界框中裁剪图像块 for box in boxes: x1, y1, x2, y2 = box patch = image[y1:y2, x1:x2] feature = extract_features(patch, window_size) features.append(feature) labels.append(1) # 正样本 # 生成负样本:随机裁剪不包含车辆的区域(或者使用背景图像) # 此处简化:随机裁剪10个负样本(实际需要更多且确保不包含车辆) height, width = image.shape[:2] for _ in range(10): x = np.random.randint(0, width - window_size[0]) y = np.random.randint(0, height - window_size[1]) # 检查是否与标注框重叠(简单起见,这里假设随机裁剪的都是负样本,实际需要检查) patch = image[y:y+window_size[1], x:x+window_size[0]] feature = extract_features(patch, window_size) features.append(feature) labels.append(0) # 负样本 return np.array(features), np.array(labels) # 加载数据集(此处省略,用户需自行实现) # images, annotations = load_dataset(...) # X, y = generate_training_data(images, annotations) # 划分训练集和测试集 from sklearn.model_selection import train_test_split X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42) # 训练SVM clf = SVC(kernel='linear', probability=True) # 使用概率以便后续设置阈值 clf.fit(X_train, y_train) # 评估 from sklearn.metrics import accuracy_score, classification_report y_pred = clf.predict(X_test) print(classification_report(y_test, y_pred)) # 检测函数(在测试图像上应用滑动窗口) def detect_vehicles(image, clf, window_size=(64,64), step_size=16, scale=1.0): # 图像金字塔(多尺度)这里仅以一个尺度为例,实际需要多尺度 detections = [] (winH, winW) = window_size # 滑动窗口 for y in range(0, image.shape[0] - winH, step_size): for x in range(0, image.shape[1] - winW, step_size): patch = image[y:y+winH, x:x+winW] feature = extract_features(patch, window_size) pred = clf.predict_proba([feature])[0][1] # 正类的概率 if pred > 0.9: # 设置阈值 detections.append((x, y, pred)) # 非极大值抑制 # 这里省略NMS的实现,可以使用OpenCV的groupRectangles或者自己实现 # 返回检测框 return detections ``` 注意:以上代码仅为示例框架,实际应用中需要调整参数和优化。 最后,我们生成相关问题: §§相关问题§§ 1. 在scikit-learn中,除了SVM,还有哪些分类器可以用于目标检测任务? 2. 如何提高scikit-learn在目标检测任务中的性能? 3. 滑动窗口目标检测中,如何处理多尺度目标? 4. 非极大值抑制(NMS)的原理是什么?如何实现? 5. 在车辆目标检测中,有哪些常用的特征提取方法? </think>### Scikit-Learn进行车辆目标检测训练的方法 在Scikit-Learn上进行车辆目标检测训练,需要将目标检测任务转化为分类问题。以下是完整的实现步骤: #### 1. 数据准备 - **数据集收集**: - 获取包含车辆的正样本图像(如PASCAL VOC数据集) - 收集不包含车辆的负样本图像(背景图像) - **数据预处理**: ```python from sklearn.preprocessing import StandardScaler import cv2 # 图像标准化 def preprocess_image(img): resized = cv2.resize(img, (64, 64)) # 统一尺寸 gray = cv2.cvtColor(resized, cv2.COLOR_BGR2GRAY) if len(img.shape)==3 else resized return gray.flatten() # 展平为特征向量 ``` - **数据标注**:为正样本标记1,负样本标记0 #### 2. 特征提取 使用适合车辆检测的特征: ```python from skimage.feature import hog # HOG特征提取 def extract_hog_features(img): resized = cv2.resize(img, (64, 64)) features, _ = hog(resized, orientations=9, pixels_per_cell=(8,8), cells_per_block=(2,2), visualize=True) return features # 颜色直方图特征 def extract_color_hist(img, bins=32): hist = cv2.calcHist([img], [0,1,2], None, [bins,bins,bins], [0,256,0,256,0,256]) return cv2.normalize(hist, hist).flatten() ``` #### 3. 模型训练(使用SVM) ```python from sklearn.svm import SVC from sklearn.model_selection import train_test_split # 特征矩阵X和标签y准备 X = np.array([extract_hog_features(img) for img in images]) y = labels # 0/1标签 # 数据集划分 X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42) # SVM训练 clf = svm.SVC(kernel='rbf', probability=True, class_weight='balanced') clf.fit(X_train, y_train) # 评估 y_pred = clf.predict(X_test) accuracy = accuracy_score(y_test, y_pred) print(f"测试准确率: {accuracy:.2%}") ``` #### 4. 目标检测流程 1. **滑动窗口检测**: ```python def sliding_window(image, step_size, window_size): for y in range(0, image.shape[0] - window_size[1], step_size): for x in range(0, image.shape[1] - window_size[0], step_size): yield (x, y, image[y:y+window_size[1], x:x+window_size[0]]) ``` 2. **多尺度检测**: ```python scales = [0.5, 1.0, 1.5, 2.0] detections = [] for scale in scales: resized = cv2.resize(image, (int(image.shape[1]*scale), int(image.shape[0]*scale))) for (x, y, window) in sliding_window(resized, step_size=8, window_size=(64,64)): features = extract_hog_features(window) if clf.predict([features])[0] == 1: detections.append((int(x/scale), int(y/scale), int((x+64)/scale), int((y+64)/scale))) ``` 3. **非极大值抑制(NMS)**: ```python import imutils from imutils.object_detection import non_max_suppression rects = np.array([[x, y, x+w, y+h] for (x, y, w, h) in detections]) pick = non_max_suppression(rects, probs=None, overlapThresh=0.3) ``` #### 5. 优化建议 1. **数据增强**:旋转、平移、亮度调整增加样本多样性 2. **特征优化**: - 结合HOG与颜色直方图特征 - 使用PCA降维减少计算量 3. **模型调优**: ```python from sklearn.model_selection import GridSearchCV param_grid = {'C': [0.1, 1, 10], 'gamma': [0.01, 0.1, 1]} grid = GridSearchCV(SVC(), param_grid, cv=3) grid.fit(X_train, y_train) ``` #### 6. 实际应用 ```python # 视频流检测示例 cap = cv2.VideoCapture('traffic.mp4') while cap.isOpened(): ret, frame = cap.read() if not ret: break # 检测并绘制结果 detections = detect_vehicles(frame, clf) for (startX, startY, endX, endY) in detections: cv2.rectangle(frame, (startX, startY), (endX, endY), (0,255,0), 2) cv2.imshow("Vehicle Detection", frame) if cv2.waitKey(1) == 27: break ``` > **注意**:Scikit-Learn适用于传统机器学习方法,对于复杂场景建议结合深度学习框架(如YOLO、SSD)[^3][^4]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值