hog+svm图像检测流程 --python

本文介绍如何使用HOG特征提取方法处理行人检测任务,通过Python实现正负样本数据集的准备,利用OpenCV计算梯度和直方图,然后结合SVM进行模型训练。过程包括特征提取、特征保存、样本统一和模型训练,最后展示模型应用与可视化检测。
摘要由CSDN通过智能技术生成

1. 准备正负样本数据集,保证尺寸一致。其中正样本数据集是包含被测试物体的图片,如检测行人,车牌等此类图像,相反负样本则不包含被检测物体的图像;

 

2. 利用python实现hog特征的提取:可以直接利用opencv中封装好的函数cv.HOGDescriptor()

import math
import os
from itertools import chain

import cv2
import joblib
import numpy as np
import skimage.novice
from PIL import Image
from matplotlib import pyplot as plt
from sklearn.svm import LinearSVC


class Hog_feature_extraction():
    def __init__(self, img, cell_size=8, bin_size=8):
        self.img = img
        self.img = np.sqrt(img / float(np.max(img)))
        self.cell_size = cell_size
        self.bin_size = bin_size
        self.angle_unit = 360 // bin_size
        assert type(self.bin_size) == int, "bin_size should be integer,"
        assert type(self.cell_size) == int, "cell_size should be integer,"
        assert type(self.angle_unit) == int, "bin_size should be divisible by 360"

    # 1-计算cell梯度;3-可视化直方图;
    def caculate_block(self, img):
        height, width = self.img.shape
        gradient_value_x = cv2.Sobel(img, cv2.CV_64F, 1, 0, ksize=5)  # 1代表在x方向求导
        gradient_value_y = cv2.Sobel(img, cv2.CV_64F, 0, 1, ksize=5)
        gradient_magnitude = cv2.addWeighted(gradient_value_x, 0.5, gradient_value_y, 0.5, 0)  # 计算该点像素点的梯度大小和方向
        gradient_angle = cv2.phase(gradient_value_x, gradient_value_y, angleInDegrees=True)
        gradient_magnitude = abs(gradient_magnitude)
        cell_gradient_vector = np.zeros((height // self.cell_size, width // self.cell_size, self.bin_size))
        for i in range(cell_gradient_vector.shape[0]):  # 遍历cell的高
            for j in range(cell_gradient_vector.shape[1]):  # 遍历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)  # 将cell的梯度和方向填充到先前的矩阵中
        # 可视化直方图
        cell_width = self.cell_size // 2
        hog_image = np.zeros([height, width])
        max_mag = np.array(cell_gradient_vector).max()
        for x in range(cell_gradient_vector.shape[0]):
            for y in range(cell_gradient_vector.shape[1]):
                cell_grad = cell_gradient_vector[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(hog_image, (y1, x1), (y2, x2), int(255 * math.sqrt(magnitude)))
                    angle += angle_gap
        hog_vector = []
        for i in range(cell_gradient_vector.shape[0] - 1):
            for j in range(cell_gradient_vector.shape[1] - 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))
                magnitue = mag(block_vector)
                if magnitude != 0:
                    normalize = lambda block_vector, magnitude: [element // magnitude for element in block_vector]
                    block_vector = normalize(block_vector, magnitude)  # block混叠空间块归一化
                hog_vector.append(block_vector)
        return hog_image, hog_vector  # 特征描述子

    # 2-构建直方图;
    def cell_gradient(self, cell_magnitude, cell_angle):  # 将同一个cell的梯度值根据分的角度值用一权重分别赋给bin_size个维度
        orientation_centers = [0] * self.bin_size  # 建立需填充矩阵
        for k in range(cell_magnitude.shape[0]):  # 遍历每个cell的高
            for l in range(cell_magnitude.shape[1]):  # 遍历每个cell的宽
                gradient_strength = cell_magnitude[k][l]   # 获得该位置的梯度值
                gradient_angle = cell_angle[k][l]  # 获得该位置的角度值
                min_angle = int(gradient_angle // self.angle_unit) % self.bin_size  # 找到该角度处于bin_size角度范围的最小区间
                max_angle = (min_angle + 1) % self.bin_size  # 找到该角度处于bin_size角度范围的最大区间
                mod = gradient_angle % self.angle_unit
                orientation_centers[min_angle] += (gradient_strength * (1 - (mod // self.angle_unit)))
                orientation_centers[max_angle] += (gradient_strength * (mod // self.angle_unit))
                return orientation_centers

3. 计算正负样本特征并保存:

    pos_path = './positive_samples'
    neg_path = './negative_samples'
    pos_feature_path = './sample_features/pos_feature/'
    neg_feature_path = './sample_features/neg_feature/'

    features = []  
    labels = []
    print("开始计算正样本特征:")
    for pos_img in os.listdir(pos_path):
        pos_img_path = pos_path + "/" + pos_img
        img = cv2.imread(pos_img_path, cv2.IMREAD_GRAYSCALE)
        height, width = img.shape
        pos_hog = Hog_feature_extraction(img, cell_size=8, bin_size=8)
        hog_image, pos_hog_vector = pos_hog.caculate_block(img)  # 获取图像的hog特征,二维列表
        pos_hog_vector1 = list(chain.from_iterable(pos_hog_vector))
        pos_feat_name = os.path.split(pos_img_path)[1].split(".")[0] + ".feat"  # 将特征以图片名字命名
        # [1]--获得链表的尾,[0]获得链表的头,split(".")[0]--从链表的尾中.获得头->person
        pos_feat_path = os.path.join(pos_feature_path, pos_feat_name)  # 路径拼接
        joblib.dump(pos_hog_vector, pos_feat_path)
        print(pos_feat_name)
    print("正样本保存在{}".format(pos_feature_path))
    print("----->正样本特征计算完毕")

    print("开始计算负样本特征:")
    features1 = []
    for neg_img in os.listdir(neg_path):
        neg_img_path = neg_path + "/" + neg_img
        img = cv2.imread(neg_img_path, cv2.IMREAD_GRAYSCALE)
        height, width = img.shape
        neg_hog = Hog_feature_extraction(img, cell_size=8, bin_size=8)
        neg_image, neg_hog_vector = neg_hog.caculate_block(img)  # 获取图像的hog特征
        neg_feat_name = os.path.split(neg_img_path)[1].split(".")[0] + ".feat"  # 将特征以图片名字命名
        # [1]--获得链表的尾,[0]获得链表的头,split(".")[0]--从链表的尾中.获得头->person
        neg_feat_path = os.path.join(neg_feature_path, neg_feat_name)  # 路径拼接
        joblib.dump(neg_hog_vector, neg_feat_path)
        print(neg_feat_name)
    print("负样本保存在{}".format(neg_feature_path))

4. 加载正负样本特征并附上标签,训练svm模型:


import os.path
import random
import joblib
from itertools import chain
from sklearn.svm import LinearSVC


# feats中列表的长度不一致, 统一列表长度,自动补0
def caculate_len(numbers):
    length = []
    for index, char in enumerate(numbers):
        leng = len(char)
        length.append(leng)
    a = max(length)
    return a


def unify(feats):
    feats_result = []
    a = caculate_len(feats)
    for index, char in enumerate(feats):
        featss = feats[index] + [0] * (a - len(char))
        feats_result.append(featss)
    return feats_result


if __name__ == '__main__':
    # 1 - 各种所需路径
    # 正负样本特征路径:
    pos_feature_path = './sample_features/pos_feature'
    neg_feature_path = './sample_features/neg_feature'
    # 2-加载正负样本特征
    feats = []
    labels = []
    print("加载正样本特征:********")
    pos_dir = os.listdir(pos_feature_path)
    pos_path = random.sample(pos_dir, 5000)
    for pos_index, pos_feature in enumerate(pos_path):  # 返回所有的匹配的文件路径
        pos_ph = pos_feature_path + '/' + pos_feature
        pos_ft = joblib.load(pos_ph)
        pos_ft = list(chain.from_iterable(pos_ft))
        feats.append(pos_ft)
        labels.append(1)
        print("正样本特征加载to {}".format(pos_index))

    print("加载负样本特征:********")
    neg_dir = os.listdir(neg_feature_path)
    neg_path = random.sample(neg_dir, 5000)
    for neg_index, neg_feature in enumerate(neg_path):
        neg_ph = neg_feature_path + '/' + neg_feature
        neg_ft = joblib.load(neg_ph)
        neg_ft = list(chain.from_iterable(neg_ft))
        feats.append(neg_ft)
        labels.append(0)
        print("负样本特征加载to {}".format(neg_index))

    feat_result = unify(feats)  # 统一样本的长度
    # 3-训练svm
    clf = LinearSVC()  # 线性分类支持向量机
    print("训练线性SVM分类器")
    clf.fit(feat_result, labels)  # 根据训练数据拟合分类器

    '''对输入数据有要求,feat_result: numpy array hog features, as [[hog1], [hog2], ...]
                     labels: numpy array labels (1-dim), 
所以在加载正负样本的时候,有格式的转换,二维变一维度,并且将正负样本放在一起后,每一个特征长度不一样,同样进行了长度的统一'''

# 保存模型:
    model_path = './models/model2.pkl'
    joblib.dump(clf, model_path)
    print("Classifier saved to {}".format(model_path))

5. svm训练完毕保存模型,以方便后续的使用;

# 测试图像
img_path = './test_samples/0.jpg'
img = cv2.imread(img_path)
print(img.shape)
gray_img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 加载模型
clf = joblib.load('./models/model1.pkl')
max_length = len(clf.coef_[0])  # 模板中最大长度

6. svm模型的预测,在这里涉及到滑动窗口,非极大值抑制,以及模型的自举(难例样本);

# 滑动窗口
def sliding_window(image, window_size, step_size):
    for y in range(0, image.shape[0], step_size[1]):
        for x in range(0, image.shape[1], step_size[0]):
            yield x, y, image[y:y + window_size[1], x:x + window_size[0]]

7. 模型调整好后,就可以进行图像的检测,为了更好的突出显示检测效果,可以将其可视化,画矩形框等操作。

  • 1
    点赞
  • 46
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值