百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 热门文章 > 正文

Opencv从零开始 - 「进阶篇」- HOG特征及其描述子提取

bigegpt 2024-08-19 12:03 3 浏览


?? HOG特征是对象识别与模式匹配中是一种常见的特征提取算法, 本文主要介绍了HOG特征以及利用其描述子加SVM方法实现检测功能,具体的内容可以参考下文~


目录

  • 概述
  • HOG特征描述子提取
  • HOG+SVM 检测示例
  • 小结

概述

?? HOG(Histogram of Oriented Gradient)特征在对象识别与模式匹配中是一种常见的特征提取算法,是基于本地像素块进行特征直方图提取的一种算法,对象局部的变形与光照影响有很好的稳定性。

HOG应用-行人检测

?? 用HOG特征来来识别人像,通过HOG特征提取+SVM训练,可以得到很好的效果,Opencv也集成了HOG进行的行人检测算法。

OpenCV函数

  1. hog = cv2.HOGDescriptor() :创建HOG特征描述;
  2. hog.setSVMDetector(cv.HOGDescriptor_getDefaultPeopleDetector()) :创建HOG+SVM行人检测器;
  3. 多尺度检测API:
rects, weights = hog.detectMultiScale(img, foundLocations,
                     hitThreshold = 0,
                     winStride, padding, 
                     scale = 1.05,
                     finalThreshold = 2.0,
                     useMeanshiftGrouping = false)

输入

  • Img --> 表示输入图像;
  • foundLocations --> 表示发现对象矩形框;
  • hitThreshold --> 表示SVM距离度量(特征与SVM分类超平面之间距离),默认0表示;
  • winStride --> 表示窗口步长;
  • padding --> 表示填充;
  • scale --> 表示尺度空间;
  • finalThreshold --> 最终阈值,默认为2.0;
  • useMeanshiftGrouping --> 不建议使用,速度太慢;

PS:其中窗口步长与Scale对结果影响最大,特别是Scale,小的尺度变化有利于检出低分辨率对象,同时也会导致FP发生,高的可以避免FP但是会产生FN(对象漏检)。

行人检测代码示例

import cv2 as cv

src = cv.imread("people.png")
cv.imshow("input", src)
# hog特征描述
hog = cv.HOGDescriptor()
# 创建SVM检测器
hog.setSVMDetector(cv.HOGDescriptor_getDefaultPeopleDetector())
# 检测行人
(rects, weights) = hog.detectMultiScale(src,
                                        winStride=(4, 4),
                                        padding=(8, 8),
                                        scale=1.25,
                                        useMeanshiftGrouping=False)
for (x, y, w, h) in rects:
    cv.rectangle(src, (x, y), (x + w, y + h), (0, 255, 0), 2)

cv.imshow("hog-people", src)
cv.waitKey(0)
cv.destroyAllWindows()

HOG特征描述子提取

提取过程

1. Gamma矫正

?? 为了提高检测器对关照等干扰因素的鲁棒性,需要对图像进行Gamma矫正,完成对整个图像的归一化,调整对比度,降低噪声影响;


一般 r=1/2

2. 灰度化

3. 计算图像XY梯度和方向

使用sobel可以出水平和垂直方向的梯度:

gx = cv2.Sobel(img, cv2.CV_32F, 1, 0, ksize=1)
gy = cv2.Sobel(img, cv2.CV_32F, 0, 1, ksize=1)

利用公式求取梯度幅值和方向:

Opencv中使用:

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

4. 8x8网格方向梯度权重直方图统计

?? 流程:首先将图像划分成若干个块(Block),每个块又由若干个细胞单元(cell)组成,细胞单元由更小的单位像素(Pixel)组成,然后在每个细胞单元中对内部的所有像素的梯度方向进行统计。

  • 默认HOG的描述子窗口为64x128, 窗口移动步长为 8x8
  • 每个窗口的cell为8x8,每个block由4个cell组成,block移动步长为一个cell,因此可以得到7x15个block
  • 直方图把180度分为9个bin,每个区间为20度,如果像素落在某个区间,就把该像素的直方图累计到对应区间的直方图上
  • 每个block有4个cell,每个cell有9个向量值,即每个block有36个向量,所以整个窗口有7x15x36=3780个特征描述子。

5. 块描述子和特征向量归一化

?? 每个block可以得到4个9维的向量,需要再次进行一次归一化,这样可以进一步提高泛化能力,同传使用L2-nrom进行归一化(还有L1-norm, L1-sqrt,etc.)

整体流程图


HOG+SVM 检测示例

?? 这里,我们使用前面所了解到HOG知识,结合SVM,进行一个简单的水表检测案例。

  1. 使用描述子特征生成样本数据
  2. 通过SVM进行分类学习与训练
  3. load模型,进行预测结果
  • 数据生成
# 把目标图放在64x128的灰色图片中间,方便计算描述子
def get_hog_descriptor(image):
    hog = cv.HOGDescriptor()
    h, w = image.shape[:2]
    rate = 64 / w
    image = cv.resize(image, (64, np.int(rate*h)))
    gray = cv.cvtColor(image, cv.COLOR_BGR2GRAY)
    bg = np.zeros((128, 64), dtype=np.uint8)
    bg[:,:] = 127
    h, w = gray.shape
    dy = (128 - h) // 2
    bg[dy:h+dy,:] = gray
    descriptors = hog.compute(bg, winStride=(8, 8), padding=(0, 0))
    return descriptors

def get_data(train_data, labels, path, lableType):
    for file_name in os.listdir(path):
        img_dir = os.path.join(path, file_name)
        img = cv.imread(img_dir)
        hog_desc = get_hog_descriptor(img)
        one_fv = np.zeros([len(hog_desc)], dtype=np.float32)
        for i in range(len(hog_desc)):
            one_fv[i] = hog_desc[i][0]
        train_data.append(one_fv)
        labels.append(lableType)
    return train_data, labels

def get_dataset(pdir, ndir):
    train_data = []
    labels = []
    # 获取正样本
    train_data, labels =  get_data(train_data, labels, pdir, lableType=1)
    # 获取负样本
    train_data, labels =  get_data(train_data, labels, ndir, lableType=-1)

    return np.array(train_data, dtype=np.float32), np.array(labels, dtype=np.int32)
    
if __name__ == '__main__':
    # train_data的shape为(n, 3780), labels(n,)
    # n为样本数
    train_data, labels = get_dataset("pdir/", "ndir/")
  • 构建SVM训练器

?? Opencv中SVM有线性分类器和非线性的径向分类器。

这里使用线性分类器:

svm.train(trainData, cv.ml.ROW_SAMPLE, responses)
  • Sample --> 表示训练样本数据/HOG特征数据
  • Layout --> 有两种组织方式ROW_SAMPLE与COL_SAMPLE
  • Responses --> 每个输入样本的标签

训练代码

def svm_train(pdir, ndir):
    # 创建SVM
    svm = cv.ml.SVM_create()
    # 设置相应的SVM参数
    svm.setKernel(cv.ml.SVM_LINEAR)
    svm.setType(cv.ml.SVM_C_SVC)
    svm.setC(2.67)
    svm.setGamma(5.383)
    # 获取正负样本和labels
    trainData, responses = get_dataset(pdir, ndir)
    # reshape (n,)-->(n,1)
    responses = np.reshape(responses, [-1, 1])
    # 训练
    svm.train(trainData, cv.ml.ROW_SAMPLE, responses)
    svm.save('svm_data.dat')
  • 预测目标
import cv2 as cv
import numpy as np

image = cv.imread("test_01.jpg")
# 原图太大,降低原图分辨率
test_img = cv.resize(image, (0, 0), fx=0.2, fy=0.2)
# 灰度
gray = cv.cvtColor(test_img, cv.COLOR_BGR2GRAY)
# 获取大小
h, w = test_img.shape[:2]
# 加载训练好的模型
svm = cv.ml.SVM_load('../code_104/svm_data.dat')
# 为了筛选框,记录框坐标总和以及框的个数,为了最后求出所有候选框的均值框
sum_x = 0
sum_y = 0
count = 0
# 创建hog特征描述子函数
hog = cv.HOGDescriptor()
# 为了加快计算,窗口滑动的步长为4,一个cell是8个像素
for row in range(64, h-64, 4):
    for col in range(32, w-32, 4):
        win_roi = gray[row-64:row+64,col-32:col+32]
        hog_desc = hog.compute(win_roi, winStride=(8, 8), padding=(0, 0))
        one_fv = np.zeros([len(hog_desc)], dtype=np.float32)
        for i in range(len(hog_desc)):
            one_fv[i] = hog_desc[i][0]
        one_fv = one_fv.reshape(-1, len(hog_desc))
        # 预测
        result = svm.predict(one_fv)[1]
        # 统计正样本
        if result[0][0] > 0:
            sum_x += (col-32)
            sum_y += (row-64)
            count += 1
            # 画出所有框
            cv.rectangle(test_img, (col-32, row-64), (col+32, row+64), (0, 233, 255), 1, 8, 0)

# 求取均值框
x = sum_x // count
y = sum_y // count
# 画出均值框
cv.rectangle(test_img, (x, y), (x+64, y+128), (0, 0, 255), 2, 8, 0)

如上图,类似于目标检测中NMS的作用,这里使用均值的方法获得最终的框。

小结

?? 对于简单的识别,HOG和SVM的识别效果还是很不错的,为了提高效果,可以增加正负样本进行,再次进行训练。

?? SVM不需要GPU,所以在针对一些简单的识别任务,可以采用这个方法,但是复杂问题,还是建议使用神经网络,效果会更好!

未完待续~

更多Opencv教程将持续发布!

欢迎关注哟~??????

相关推荐

悠悠万事,吃饭为大(悠悠万事吃饭为大,什么意思)

新媒体编辑:杜岷赵蕾初审:程秀娟审核:汤小俊审签:周星...

高铁扒门事件升级版!婚宴上‘冲喜’老人团:我们抢的是社会资源

凌晨两点改方案时,突然收到婚庆团队发来的视频——胶东某酒店宴会厅,三个穿大红棉袄的中年妇女跟敢死队似的往前冲,眼瞅着就要扑到新娘的高额钻石项链上。要不是门口小伙及时阻拦,这婚礼造型团队熬了三个月的方案...

微服务架构实战:商家管理后台与sso设计,SSO客户端设计

SSO客户端设计下面通过模块merchant-security对SSO客户端安全认证部分的实现进行封装,以便各个接入SSO的客户端应用进行引用。安全认证的项目管理配置SSO客户端安全认证的项目管理使...

还在为 Spring Boot 配置类加载机制困惑?一文为你彻底解惑

在当今微服务架构盛行、项目复杂度不断攀升的开发环境下,SpringBoot作为Java后端开发的主流框架,无疑是我们手中的得力武器。然而,当我们在享受其自动配置带来的便捷时,是否曾被配置类加载...

Seata源码—6.Seata AT模式的数据源代理二

大纲1.Seata的Resource资源接口源码2.Seata数据源连接池代理的实现源码3.Client向Server发起注册RM的源码4.Client向Server注册RM时的交互源码5.数据源连接...

30分钟了解K8S(30分钟了解微积分)

微服务演进方向o面向分布式设计(Distribution):容器、微服务、API驱动的开发;o面向配置设计(Configuration):一个镜像,多个环境配置;o面向韧性设计(Resista...

SpringBoot条件化配置(@Conditional)全面解析与实战指南

一、条件化配置基础概念1.1什么是条件化配置条件化配置是Spring框架提供的一种基于特定条件来决定是否注册Bean或加载配置的机制。在SpringBoot中,这一机制通过@Conditional...

一招解决所有依赖冲突(克服依赖)

背景介绍最近遇到了这样一个问题,我们有一个jar包common-tool,作为基础工具包,被各个项目在引用。突然某一天发现日志很多报错。一看是NoSuchMethodError,意思是Dis...

你读过Mybatis的源码?说说它用到了几种设计模式

学习设计模式时,很多人都有类似的困扰——明明概念背得滚瓜烂熟,一到写代码就完全想不起来怎么用。就像学了一堆游泳技巧,却从没下过水实践,很难真正掌握。其实理解一个知识点,就像看立体模型,单角度观察总...

golang对接阿里云私有Bucket上传图片、授权访问图片

1、为什么要设置私有bucket公共读写:互联网上任何用户都可以对该Bucket内的文件进行访问,并且向该Bucket写入数据。这有可能造成您数据的外泄以及费用激增,若被人恶意写入违法信息还可...

spring中的资源的加载(spring加载原理)

最近在网上看到有人问@ContextConfiguration("classpath:/bean.xml")中除了classpath这种还有其他的写法么,看他的意思是想从本地文件...

Android资源使用(android资源文件)

Android资源管理机制在Android的开发中,需要使用到各式各样的资源,这些资源往往是一些静态资源,比如位图,颜色,布局定义,用户界面使用到的字符串,动画等。这些资源统统放在项目的res/独立子...

如何深度理解mybatis?(如何深度理解康乐服务质量管理的5个维度)

深度自定义mybatis回顾mybatis的操作的核心步骤编写核心类SqlSessionFacotryBuild进行解析配置文件深度分析解析SqlSessionFacotryBuild干的核心工作编写...

@Autowired与@Resource原理知识点详解

springIOCAOP的不多做赘述了,说下IOC:SpringIOC解决的是对象管理和对象依赖的问题,IOC容器可以理解为一个对象工厂,我们都把该对象交给工厂,工厂管理这些对象的创建以及依赖关系...

java的redis连接工具篇(java redis client)

在Java里,有不少用于连接Redis的工具,下面为你介绍一些主流的工具及其特点:JedisJedis是Redis官方推荐的Java连接工具,它提供了全面的Redis命令支持,且...