影像特徵之HOG特徵(附python程式碼)
- 2020 年 6 月 26 日
- AI
嘻!long time no see…最近主要再忙課程論文(期末了 害)論文有看但也沒有更新,一下就都月底了。接下來打算把影像課程論文的一些東西搬運過來這邊(又懶了我)
影像特徵介紹
為了更好地對影像進行形式化的描述和表達,我們引入了影像特徵,它是可以用來描述影像或影像區域所對應的景物的性質特點。影像特徵可以是人類視覺能夠識別的自然特徵,也可以是人為所定義的某些特徵。良好的影像特徵對電腦視覺以及模式識別等領域起到了至關重要的作用。影像特徵的分類多種多樣,從直觀視覺上,可以大致分為顏色特徵、紋理特徵、形狀特徵和空間關係特徵。
1)顏色特徵:它是一種全局特徵,描述了影像或影像區域所對應的景物的顏色性質特點。一般顏色特徵是基於像素點的特徵,此時所有屬於影像或影像區域的像素都有各自的貢獻。常用的顏色特徵提取方法有顏色直方圖、顏色矩、顏色集、顏色一致性向量等。其中,顏色直方圖是最常用的表達顏色特徵的方法,它可以不受影像旋轉和平移變化的影響,但在表達出顏色空間分布的資訊上有所欠缺。
2)紋理特徵:它同顏色特徵一樣為全局特徵,用來描述的是表面性質。與顏色特徵不同,紋理特徵不是基於像素點的特徵,而是在擁有多個像素點的影像區域中進行統計計算。在檢索具有粗細、疏密等方面具有較大差別的紋理影像時,紋理特徵可以發揮較大作用。常用的紋理特徵描述方法包括幾何法、統計法、模型法以及訊號處理法等。
3)形狀特徵:此特徵是基於內容影像檢索中最底層特徵,卻是人類視覺直接相關和顯著的特徵。形狀的表示以及過程中描述子的使用分析對於基於形狀特徵的影像檢索至關重要。按照傳統的分類方法,形狀特徵的表示方法可以分為基於輪廓方法和基於區域的方法,前者主要是針對物體的外邊界,而後者關係到整個形狀的區域。
4)空間關係特徵:空間關係,是指影像中分割出來的多個目標之間的相互的空間位置或相對方向關係。按照空間位置資訊可以分為兩類:相對空間位置資訊和絕對空間位置資訊。空間關係特徵的使用可加強對影像內容的描述區分能力。影像空間關係特徵的提取可分為兩種:一種是首先對影像進行自動分割,劃分出影像中所包含的對象或顏色區域,然後根據這些區域提取影像特徵,並建立索引;另一種方法則簡單地將影像均勻地劃分為若干規則子塊,然後對每個影像子塊提取特徵,並建立索引。
當然除了上述從直觀視覺上對影像特徵進行分類,還可以從特徵的提取方式、是否為全局特徵等角度對影像特徵進行劃分。
HOG特徵
2005 年,Dalal等人在研究行人檢測的時候,首次提出HOG特徵來處理行人檢測問題,並且取得了相當好的檢測效果。它是目前電腦視覺、模式識別領域常用的一種描述影像局部紋理的特徵。HOG特徵核心思想即影像中局部對象的外觀或形狀,可以通過梯度方向和強度分布來描述。此特徵可以較好地描述物體的形狀,成為刻畫形狀的常用特徵之一。
HOG特徵的本質是梯度的統計資訊,而這些梯度主要存在於邊緣的地方。HOG特徵提取演算法的過程主要分為四大步驟:
第一,輸入影像,對影像進行顏色空間的歸一化;
第二,計算影像的梯度;
第三,為每個cell單元構建梯度方向直方圖;
第四,把cell單元組合成block,塊內歸一化梯度直方圖。
(如圖所示即為完整過程)。每一步驟的具體實現如下:
1)顏色空間的歸一化:為了減少光照因素的影響,首先需要將整個影像進行歸一化。由於在影像紋理強度中,局部的表層曝光貢獻的比重較大,所以進行Gamma歸一化處理,可以有效地降低影像局部陰影和光照的變化,還可以抑制噪音的干擾。這邊的Gamma歸一化處理可以是對每個顏色通道分別計算平方根或者對每個顏色通道分別求log。
2)計算影像的梯度:計算影像的梯度主要是為了通過梯度資訊來描述影像中物體的邊緣、輪廓、形狀等紋理資訊。在這個步驟中,首先需要計算影像的橫縱坐標方向的梯度,然後根據計算出的影像橫坐標和縱坐標方向梯度算出每個像素位置的梯度方向值。假設一張影像中像素點用(x,y)表示,則該像素點的水平梯度和垂直梯度分別為Gx(x,y)和Gy(x,y),計算如公式如下:其中H(x,y)表示輸入的影像在像素點(x,y)的像素值。根據得到的像素點水平梯度和垂直梯度就可以得到影像中素點(x,y)處的梯度幅值G(x,y)和梯度方向α(x,y)如下所示:
在這邊最常用的方法是:首先用[-1,0,1]梯度運算元對原影像做卷積運算,得到x方向(水平方向)的梯度分量,然後用[1,0,-1]T梯度運算元對原影像做卷積運算,得到y方向(豎直方向)的梯度分。然後再用以上公式計算該像素點的梯度大小和方向。
3)為每個cell單元構建梯度方向直方圖:此步驟主要是為局部影像區域提供一個編碼,同時能夠保持對影像中人體對象的姿勢和外觀的弱敏感性。首先把影像分割成許多個cell單元格,然後把所有cell單元的全部像素通過梯度方向在直方圖中執行加權投影的操作,則可獲得每個cell單元格的梯度方向直方圖。
4)把cell單元組合成block,塊內歸一化梯度直方圖。 由於局部光照的變化以及前景-背景對比度的變化,使得梯度強度的變化範圍非常大。所以必須歸一化梯度強度,歸一化梯度強度可以實現對邊緣、光照和陰影的壓縮。最後再合併每個塊的特徵描述子就能夠獲得這張影像的 HOG 特徵描述子,也就是 HOG 特徵向量。小結
與其他的特徵描述方法相比,HOG特徵是在影像的局部方格單元上操作的,因此它對影像幾何的和光學的形變都能保持很好的不變性。故,HOG特徵憑藉其優勢得到了廣泛應用,同時它還與其他先進演算法相結合應用到了其他的領域,例如HOG特徵結合SVM分類器的方法<已經被廣泛應用於影像識別,尤其在行人檢測中獲得了巨大的成功。
程式碼實現
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
self.img = np.sqrt(img / np.max(img))
self.img = 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_gradient_vector = np.zeros((height / self.cell_size, 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_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 = []
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))
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)
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
def cell_gradient(self, cell_magnitude, cell_angle):
orientation_centers = [0] * self.bin_size
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):
idx = int(gradient_angle / self.angle_unit)
mod = gradient_angle % self.angle_unit
return idx, (idx + 1) % self.bin_size, mod
def render_gradient(self, image, cell_gradient):
cell_width = self.cell_size / 2
max_mag = np.array(cell_gradient).max()
for x in range(cell_gradient.shape[0]):
for y in range(cell_gradient.shape[1]):
cell_grad = cell_gradient[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(image, (y1, x1), (y2, x2), int(255 * math.sqrt(magnitude)))
angle += angle_gap
return image
img = cv2.imread('person_037.png', cv2.IMREAD_GRAYSCALE)
hog = Hog_descriptor(img, cell_size=8, bin_size=8)
vector, image = hog.extract()
print np.array(vector).shape
plt.imshow(image, cmap=plt.cm.gray)
plt.show()
Ending~端午安康