HOG特征使用OPENCV提取梯度幅度和方向的实现,主要是可视化理解
2019-07-25 17:50
453 查看
HOG特征提取全Numpy实现
HOG步骤详解
本项目使用的代码原地址,我改编的地址
代码详解:
创建一个HOG的类:
- __init__是图片,cell,划分角度等基础信息(基础)
- extract 是提取cell中的HOG特征,然后可视化,在提取block中的HOG特征(主要流程)
- global_gradient 用于得到图片梯度幅度和梯度方向(在统计HOG特征前面需要得到的信息)
- get_closest_bins 用于在对角度进行划分的时候,找到这个角度夹杂那两个角度之间。(在统计HOG的时候,用于对梯度幅度的分配)
- render_gradient 用于可视化统计的变量。
[code]import cv2 import numpy as np import math import matplotlib.pyplot as plt class Hog_descriptor(): def __init__(self, img, cell_size=16, bin_size=8): self.img = img #gamma校准,0.5的n次方 self.img = np.sqrt(img / float(np.max(img))) #从0-1还原图片为0-255 self.img = self.img * 255 self.cell_size = cell_size #统计角度划分的数量 self.bin_size = bin_size #每个范围对应的角度范围 self.angle_unit = 360 / self.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" def extract(self): #得到图片的高和宽 height, width = self.img.shape #得到梯度和角度 gradient_magnitude, gradient_angle = self.global_gradient() #得到正负幅度只是方向相反的问题 gradient_magnitude = abs(gradient_magnitude) #创建一整个图的统计量,因为一个正方形cell统计一次,所以整个图缩小了cell倍。 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]): #对应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) #可视化 hog_image = self.render_gradient(np.zeros([height, width]), cell_gradient_vector) hog_vector = [] #2*2个cell是一个block,滑动形式和卷积一样,所以遍历需要减一 for i in range(cell_gradient_vector.shape[0] - 1): for j in range(cell_gradient_vector.shape[1] - 1): #得到一个block 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)) magnitude = mag(block_vector) if magnitude != 0: normalize = lambda block_vector, magnitude: [element / magnitude for element in block_vector] block_vector = normalize(block_vector, magnitude) hog_vector.append(block_vector) return hog_vector, hog_image def global_gradient(self): 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横梯度+0.5纵梯度合成近似梯度,并不是直接求解梯度 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) return gradient_magnitude, gradient_angle #cell统计量 def cell_gradient(self, cell_magnitude, cell_angle): #创建统计量计数 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, max_angle, mod = self.get_closest_bins(gradient_angle) #用多出来的角度对两边直接分配梯度幅度值 orientation_centers[min_angle] += (gradient_strength * (1 - (mod / self.angle_unit))) orientation_centers[max_angle] += (gradient_strength * (mod / self.angle_unit)) return orientation_centers def get_closest_bins(self, gradient_angle): #这部分最难理解的是维度的划分,因为360和0是接在一起的,所以会有一个统计量维度是多出来的, #可以把下列print取消注释,就可以清晰的了解这个过程 #print('\n\n')B #print('寻找最近的索引值,当前梯度角度是{},划分的角度范围是{}'.format(gradient_angle, # self.angle_unit)) #当前角度除以角度范围,得到对应索引,是如果角度加载两个范围之间,idx是前一个数值 idx = int(gradient_angle / self.angle_unit) #print('理他最左圆整的索引值是{},一共拥有的索引值是{}'.format(idx,self.bin_size)) #mod是对应的余数,就是按角度划分,多出来的部分 mod = gradient_angle % self.angle_unit #如果得到前一个数值就已经是是最大的索引了的情况,只有在角度等于360度的时候才会遇上,因为0°和360°重叠 if idx == self.bin_size: #print('遇到特殊情况360°,最近的索引值是{},对应的角度是{},第二进的索引值是{},对应角度是{}'.format(idx-1,(idx-1)*self.angle_unit, # (idx)%self.bin_size,(idx)%self.bin_size*self.angle_unit)) return idx - 1, (idx) % self.bin_size, mod #(idx + 1) % self.bin_size是防止超界。 #print('最近的索引值是{},对应的角度是{},第二进的索引值是{},对应角度是{}'.format(idx,idx*self.angle_unit, # (idx+1)%self.bin_size,(idx+1)%self.bin_size*self.angle_unit)) return idx, (idx + 1) % self.bin_size, mod def render_gradient(self, image, cell_gradient): #这里的image是图片大小 #这里的cell_gradient只有所有cell中的统计量 #得到cell的宽度 cell_width = self.cell_size / 2 #得到cell中最大的梯度幅度 max_mag = np.array(cell_gradient).max() #遍历所有cell for x in range(cell_gradient.shape[0]): for y in range(cell_gradient.shape[1]): #取出统计量,统计量维度是16维 cell_grad = cell_gradient[x][y] #归一化,最大为1 cell_grad /= max_mag angle = 0 angle_gap = self.angle_unit #遍历一个cell中的16维统计量 #改成8维度 cell_grad = [(cell_grad[idx]+cell_grad[idx+int(len(cell_grad)/2)])/2 for idx in range(int(len(cell_grad)/2))] for magnitude in cell_grad: #print(angle) angle_radian = math.radians(angle) xcent = int((x)* self.cell_size + cell_width) ycent = int ((y)* self.cell_size + cell_width) x1 = int(xcent + (cell_width-1)*math.cos(angle_radian)) y1 = int(ycent + (cell_width-1) * math.sin(angle_radian)) x2 = int(xcent - (cell_width-1) * math.cos(angle_radian)) y2 = int(ycent - (cell_width-1) * math.sin(angle_radian)) cv2.line(image, (y1, x1), (y2, x2), int(255 * (magnitude))) angle += angle_gap return image
调用用HOG类:
[code]from skimage import exposure img_ori = cv2.imread('HOG1.jpg') img_ori = img_ori[:,:,::-1] img = cv2.imread('HOG1.jpg', cv2.IMREAD_GRAYSCALE) hog = Hog_descriptor(img, cell_size=16, bin_size=16) vector, image = hog.extract() plt.figure(figsize=(40,40)) plt.subplot(2,1,1) plt.imshow(img_ori) plt.subplot(2,1,2) hog_image_rescaled = exposure.rescale_intensity(image, in_range=(0, 50)) plt.imshow(hog_image_rescaled, cmap='gray') plt.show()
结果如下:
这里我对原代码的划线部分进行改动,效果如下,我将同一直线的相反方向的幅度相加用颜色表示幅度强度:
skimage的第三方软件包的可视化结果如下:效果非常好,意义一眼就了然于心,比我做的好多了,可能是方向选取的问题才造成这样的结果:
[code]import matplotlib.pyplot as plt from skimage.feature import hog from skimage import data, exposure image = cv2.imread('HOG1.jpg') image = image[:,:,::-1] fd, hog_image = hog(image, orientations=8, pixels_per_cell=(16, 16), cells_per_block=(1, 1), visualize=True, multichannel=True) print(image.shape,hog_image.shape) fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(50, 50), sharex=True, sharey=True) ax1.axis('on') ax1.imshow(image, cmap=plt.cm.gray) ax1.set_title('Input image') # Rescale histogram for better display hog_image_rescaled = exposure.rescale_intensity(hog_image, in_range=(0, 10)) ax2.axis('off') ax2.imshow(hog_image_rescaled, cmap=plt.cm.gray) ax2.set_title('Histogram of Oriented Gradients') plt.show()
结果如下:
相关文章推荐
- 【图像特征提取1】方向梯度直方图HOG---从理论到实践------附带积分图像的解析
- 深入浅出理解HOG特征---梯度方向直方图
- 【图像特征提取1】方向梯度直方图HOG---从理论到实践------附带积分图像的解析
- HOG特征提取代码(opencv处理基本数据,c语言实现基本算法)
- HOG特征提取代码(opencv处理基本数据,c语言实现基本算法)
- 使用openCV提取sift;surf;hog特征
- 使用OpenCV中的SIFT实现(尺度不变特征提取)
- Opencv3特征提取与目标检测之HOG——使用HOG加SVM训练行人检测
- OpenCV中HOG特征的提取实现
- 【图像特征提取1】方向梯度直方图HOG---从理论到实践------附带积分图像的解析
- 特征提取方法(一):HOG原理及OpenCV实现
- 使用opencv中cvCopy的mask参数实现对不规制图形的提取
- 使用opencv作物件识别(一) —— 积分直方图加速HOG特征计算
- ORB特征提取匹配opencv3代码实现
- HOG(方向梯度直方图)与特征识别
- OpenCV实现图像颜色特征提取
- 特征提取方法(二):LBP原理与OpenCV实现
- opencv2.4.3特征提取的实现表示方法
- HOG特征与opencv类实现
- 基于vlfeat的HOG特征提取c++代码实现