IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 人工智能 -> 手写HOG -> 正文阅读

[人工智能]手写HOG

GAMMA归一化:

import cv2
import numpy as np

img = cv2.imread('gamma0.jpg',0)


img1 = np.power(img/float(np.max(img)), 1/1.5)
img2 = np.power(img/float(np.max(img)), 1.5)

# 这里就是gamma归一化操作 这里的gamma值分别是1/1.5 和 1.5
cv2.imshow('src',img)
cv2.imshow('gamma=1/1.5',img1)
cv2.imshow('gamma=1.5',img2)
cv2.waitKey(0)
# img = img * 255 # 校正后 ---> 量化到0~255

为了减少光照因素的影响,降低图像局部的阴影和光照变化所造成的影响,我们首先采用Gamma校正法对输入图像的颜色空间进行标准化(或者说是归一化)。

所谓的Gamma校正可以理解为提高图像中偏暗或者偏亮部分的图像对比效果,能够有效地降低图像局部的阴影和光照变化。

##作者在他的博士论文里有提到,对于涉及大量的类内颜色变化,如猫,狗和马等动物,没标准化的RGB图效果更好,而牛,羊的图做gamma颜色校正后效果更好。 是否用gamma校正得分析具体的训练集情况。

像素梯度计算

举一个非常简单的例子

对于85这个像素点 他的梯度赋值为?

Gx?=∣89?78∣=11

Gy?=∣56?68∣=12

G=sqrt((Gx?)2+(Gy?)2)=13.6

梯度方向或者说是我们后面要用到的划分到bin中的角度为

theta=arctan(Gy?/Gx?)=36

HOG特征提取过程

把image划分为m个n*n的cells n就是我们的cell_size

然后统计每个cell里的梯度直方图 直方图划分为9个bin(论文中设置的最佳bin个数)

在0-180度范围内 一个bin代表20度 180度代表0(论文中提到使用180度无方向性的设置比360度好)

这里有个细节要注意,如果一个角度大于160度,也就是在160-180度之间,我们知道这里角度0,180度是一样的,所以在下面这个例子里,像素的角度为165度的时候,要把幅值按照比例放到0和160的bin里面去。如下图

?然后把所有cell的gradients的幅值都按direction分到这九个bin里可以得到一个梯度直方图

?然后对梯度直方图作归一化

将几个细胞单元(cell)组合成更大的块(block),这时整幅图像就可看成是待检测窗口,将更大的块看成是滑动窗口,依次从左到右从上到下进行滑动,得到一些有重复细胞单元的块及一些相同细胞单元(cell)在不同块(block)中的梯度信息,再对这些块(block)信息分别作归一化处理,不同的细胞单元尺寸大小及不同块的尺寸大小会影响最终的检测效果。然后block_vector就是我们抽取出来的hog_vector

具体代码为:

hog_vector = []
# 默认步长为一个cell大小,一个block由2x2个cell组成,遍历每一个block
for i in range(cell_gradient_vector.shape[0] - 1):  # 16-1 = 15
    for j in range(cell_gradient_vector.shape[1] - 1):  # 8-1 = 15
        # 每一个block由四个cell构成,因此,将相邻的四个cell的特征,存到一个列表中即可
        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])

完整代码

#!/user/bin/env python3
# -*- coding: utf-8 -*-
# @Time     : 2022-09-27 10:24
# @Author   : Lyt
# @IDE      : PyCharm    
# @FileName : HOG.py
# @Blog     : https://blog.csdn.net/m0_53292725?type=blog


"""
Histogram of Oriented Gradients

"""

# import cv2
# import numpy as np
#
#
# def is_inside(o, i):  # o is inside i
#     ox, oy, ow, oh = o
#     ix, iy, iw, ih = i
#     return ox < ix and oy < iy and ox+ow < ix+iw and oy+oh < iy+ih
#
#
# def draw_person(img, person):
#     x, y, w, h = person
#     cv2.rectangle(img, (x, y), (x+w, y+h), (0, 255, 255), 2)  # img , min_xy, max_xy, rgb, thickness
#
#
# def detect():
#     img = cv2.imread("/home/lyt/workspace/th.jpg")
#     # print(img.shape)
#     rows, cols = img.shape[:2]
#     sacle = 1.0  # sacle_rate
#     img = cv2.resize(img, dsize=(int(cols*sacle), int(rows*sacle)))
#     # print(img.shape)
#     """
#     create HOG descriptor
#     winSize = (64,128)
#     blockSize = (16,16)
#     blockStride = (8,8)
#     cellSize = (8,8)
#     nbins = 9
#     hog = cv2.HOGDescriptor(winSize,blockSize,blockStride,cellSize,nbins)
#
#     """
#     hog = cv2.HOGDescriptor()
#     detector = cv2.HOGDescriptor_getDefaultPeopleDetector()
#     print("detector", type(detector), detector.shape)
#     hog.setSVMDetector(detector)  # set svm detector
#
#
# # multi-scale detection
# # found is a list of all the detected target box
#
#     found, w = hog.detectMultiScale(img)
#     print('found', type(found), found.shape)
#     print(w)
#     found_filtered = []
#     for ri, r in enumerate(found):
#         for qi, q in enumerate(found):
#             if ri != qi and is_inside(r, q):
#                 break
#         else:
#             found_filtered.append(r)
#
#     for person in found_filtered:
#         draw_person(img, person)
#
#     cv2.imshow(img)
#     cv2.waitKey(0)   # Waitkey controls the duration of the imshow
#     cv2.destroyAllWindows()
#
#
#############################
import math
import cv2
import numpy as np
import matplotlib.pyplot as plt


class Hog_descriptor():
    def __init__(self, img, cell_size=8, bin_size=9):
        self.img = img
        self.img = np.sqrt(img / float(np.max(img)))  # sqrt so gamma=0.5
        self.img = 255*self.img  # back to 0-255
        self.cell_size = cell_size
        self.bin_size = bin_size
        self.angle_unit = 180 / self.bin_size

    def extract(self):
        """
        计算图像的HOG描述符,以及HOG-image特征图
        """
        height, width = self.img.shape
        '''
        1、计算图像每一个像素点的梯度幅值和角度
        '''
        # gradient_magnitude:shape为(128,64)
        # 每个像素点的梯度值、梯度方向
        gradient_magnitude, gradient_angle = self.global_gradient()
        gradient_magnitude = abs(gradient_magnitude)
        '''
        2、计算输入图像的每个cell单元的梯度直方图,形成每个cell的descriptor 比如输入图像为128x64 可以得到16x8个cell,每个cell由9个bin组成
        '''
        # 梯度方向的特征向量初始化的shape为:(16,8,9)
        cell_gradient_vector = np.zeros((int(height / self.cell_size), int(width / self.cell_size), self.bin_size))
        # 遍历每一行、每一列
        for i in range(cell_gradient_vector.shape[0]):
            for j in range(cell_gradient_vector.shape[1]):
                # 计算第[i][j]个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的梯度方向直方图绘出,得到特征图
        hog_image = self.render_gradient(np.zeros([height, width]), cell_gradient_vector)

        '''
        3、将2x2个cell组成一个block,一个block内所有cell的特征串联起来得到该block的HOG特征descriptor
           将图像image内所有block的HOG特征descriptor串联起来得到该image(检测目标)的HOG特征descriptor,
           这就是最终分类的特征向量
        '''
        hog_vector = []
        # 默认步长为一个cell大小,一个block由2x2个cell组成,遍历每一个block
        for i in range(cell_gradient_vector.shape[0] - 1):  # 16-1 = 15
            for j in range(cell_gradient_vector.shape[1] - 1):  # 8-1 = 15
                # 每一个block由四个cell构成,因此,将相邻的四个cell的特征,存到一个列表中即可
                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])
                '''块内归一化梯度直方图,去除光照、阴影等变化,增加鲁棒性'''
                # 计算l2范数
                # 计算的目标是一个矩阵,4*9的矩阵
                # 将矩阵中的每一个元素按照L2范数进行归一化 。因此先求矩阵的平方和,再把元素除以平方和
                # 下面lambda函数的作用就是求和, 得到一个数字
                mag = lambda vector: math.sqrt(sum(i ** 2 for i in vector))
                magnitude = mag(block_vector) + 1e-5
                # 归一化
                if magnitude != 0:
                    # 第二行别看错了,normalize就是一个匿名函数而已, 跟 p没什么区别
                    normalize = lambda block_vector, magnitude: [element / magnitude for element in block_vector]
                    block_vector = normalize(block_vector, magnitude)
                hog_vector.append(block_vector)
        return np.asarray(hog_vector), hog_image

    def global_gradient(self):
        """
        分别计算图像沿x轴和y轴的梯度
        """
        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)
        # 计算梯度幅值 这个计算的是0.5*gradient_values_x + 0.5*gradient_values_y
        # 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)
        gradient_magnitude, gradient_angle = cv2.cartToPolar(gradient_values_x, gradient_values_y,
                                                             angleInDegrees=True)
        # 角度大于180°的,减去180度
        gradient_angle[gradient_angle > 180.0] -= 180
        # print('gradient',gradient_magnitude.shape,gradient_angle.shape,np.min(gradient_angle),np.max(gradient_angle))
        return gradient_magnitude, gradient_angle

    def cell_gradient(self, cell_magnitude, cell_angle):
        """
        为每个细胞单元构建梯度方向直方图

        args:
            cell_magnitude:cell中每个像素点的梯度幅值,
            cell_angle:cell中每个像素点的梯度方向
        return:
            返回该cell对应的梯度直方图,长度为bin_size
        """
        # 构建每一个cell的梯度直方图,因为分箱分成了9个,初始化时,orientation_centers=[0,0,0,0,0,0,0,0,0]

        orientation_centers = [0] * self.bin_size
        # 遍历cell中的每一个像素点
        for 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: 第i个索引, max_angle : 第i+1个索引, 这里的命名真是有问题
                # 下标的范围是0到8
                min_angle, max_angle, weight = self.get_closest_bins(gradient_angle)
                orientation_centers[min_angle] += (gradient_strength * (1 - weight))
                orientation_centers[max_angle] += (gradient_strength * weight)
        return orientation_centers

    def get_closest_bins(self, gradient_angle):
        """
        计算梯度方向gradient_angle位于哪一个bin中,这里采用的计算方式为双线性插值
        具体参考:https://www.leiphone.com/news/201708/ZKsGd2JRKr766wEd.html
        例如:当我们把180°划分为9个bin的时候,分别对应对应0,20,40,...160这些角度。
              角度是10,副值是4,因为角度10介于0-20度的中间(正好一半),所以把幅值
              一分为二地放到0和20两个bin里面去。
        args:
            gradient_angle:角度
        return:
            start,end,weight:起始bin索引,终止bin的索引,end索引对应bin所占权重
        """
        idx = int(gradient_angle / self.angle_unit)  # 如int(165/20) = 8
        mod = gradient_angle % self.angle_unit  # 165%20 = 15。 u= 15/20 = 0.75, 则分到idx的梯度的值的权重为1-u = 0.25
        return idx % self.bin_size, (idx + 1) % self.bin_size, mod / self.angle_unit  # 分到idx+1的权重为u = 0.75
        # 之所以要对9取余,防止越界。因为9*20=180。180度在分箱的时候实际上属于0度

    def render_gradient(self, image, cell_gradient):
        """
        将得到的每个cell的梯度方向直方图绘出,得到特征图
        args:
            image:画布,和输入图像一样大 [h,w]
            cell_gradient:输入图像的每个cell单元的梯度直方图,形状为[h/cell_size,w/cell_size,bin_size]
        return:
            image:特征图
        """
        cell_width = self.cell_size / 2
        max_mag = np.array(cell_gradient).max()  # 获取(累计)梯度最大值,用于归一化;在可视化画图时,采取的是整个图片的最大值归一化
        # 遍历每一个cell
        for x in range(cell_gradient.shape[0]):  # cell_gradient:(16,8,9),当然,只是一个例子
            for y in range(cell_gradient.shape[1]):
                # 获取第[i][j]个cell的梯度直方图
                cell_grad = cell_gradient[x][y]  # cell_grad是一个9维的向量
                # 归一化
                cell_grad /= max_mag
                angle = 0
                angle_gap = self.angle_unit  # 范围为20度
                # 遍历每一个bin区间
                for magnitude in cell_grad:
                    # 转换为弧度
                    angle_radian = math.radians(angle)
                    # 计算起始坐标和终点坐标,长度为幅值(归一化),幅值越大、绘制的线条越长、越亮
                    # 只是使用如下的方式,来可视化每个点的幅值大小
                    x1 = int(x * self.cell_size + cell_width + magnitude * cell_width * math.cos(angle_radian))
                    y1 = int(y * self.cell_size + cell_width + magnitude * cell_width * math.sin(angle_radian))
                    x2 = int(x * self.cell_size + cell_width - magnitude * cell_width * math.cos(angle_radian))
                    y2 = int(y * self.cell_size + cell_width - magnitude * cell_width * math.sin(angle_radian))
                    cv2.line(image, (y1, x1), (y2, x2), int(255 * math.sqrt(magnitude)))
                    angle += angle_gap
        return image



if __name__ == '__main__':
    # 加载图像
    img_copy = cv2.imread('/home/user/lyt/th.jpg')
    img_rgb = cv2.cvtColor(img_copy, cv2.COLOR_BGR2RGB)
    width = 64
    height = 128
    # img_copy = img[320:320 + height, 570:570 + width][:, :, ::-1]
    # gray_copy = cv2.cvtColor(img_copy, cv2.COLOR_BGR2GRAY)
    gray_copy = cv2.cvtColor(img_copy, cv2.COLOR_BGR2GRAY)
    # 显示原图像
    plt.figure(figsize=(6.4, 2.0 * 3.2))
    plt.subplot(1, 2, 1)
    plt.imshow(img_rgb)

    # HOG特征提取

    hog = Hog_descriptor(gray_copy, cell_size=8, bin_size=9)
    hog_vector, hog_image = hog.extract()
    print('hog_vector', hog_vector.shape)
    print('hog_image', hog_image.shape)

    # 绘制特征图
    plt.subplot(1, 2, 2)
    plt.imshow(hog_image, cmap=plt.cm.gray)
    plt.show()

TODO:svm,双线性插值, normalize

  人工智能 最新文章
2022吴恩达机器学习课程——第二课(神经网
第十五章 规则学习
FixMatch: Simplifying Semi-Supervised Le
数据挖掘Java——Kmeans算法的实现
大脑皮层的分割方法
【翻译】GPT-3是如何工作的
论文笔记:TEACHTEXT: CrossModal Generaliz
python从零学(六)
详解Python 3.x 导入(import)
【答读者问27】backtrader不支持最新版本的
上一篇文章      下一篇文章      查看所有文章
加:2022-09-30 00:52:59  更:2022-09-30 00:55:27 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/19 23:50:52-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码