1. 実験の紹介
1. 実験内容
この実験では、HOG 特徴抽出アルゴリズムを学習します。
2. 実験のポイント
- HOG アルゴリズム
- HOG アルゴリズムが機能する理由
- HOG 記述子の作成
- HOG 記述子の要素の数
- HOG 記述子の視覚化
- ヒストグラムを理解する
3. 実験環境
- Python 3.6.6
- しこり
- マットプロットライブラリ
- CV2
- コピー
2. 実験手順
序章
ORB アルゴリズムで見られるように、画像内のオブジェクトを検出するためのマッチングに画像内のキーポイントを使用できます。これらのタイプのアルゴリズムは、多くの一貫した内部プロパティを持ち、背景の影響を受けないオブジェクトを検出する場合に役立ちます。たとえば、顔には画像の背景の影響を受けない目、鼻、口などの多くの一貫した内部特徴があるため、これらのアルゴリズムは顔検出で良好な結果を達成できます。ただし、この種のアルゴリズムは、画像内の歩行者検出など、より一般的な物体認識を行う場合にはあまりうまく機能しません。その理由は、体型やスタイルは人それぞれ異なるため、人の内面の特徴は顔ほど一貫していないためです(下の画像を参照)。これは、人はそれぞれ異なる内部特性を持っていることを意味するため、その人をより完全に描写するものが必要になります。
1 つのオプションは、シルエットによって歩行者を検出しようとすることです。画像内の輪郭 (境界) によってオブジェクトを検出することは、背景と前景の間のコントラストの難しさに対処する必要があるため、非常に困難です。たとえば、画像内で歩行者を検出したいとします。歩行者は白いコートと黒いズボンを着て白い建物の前を歩いています。下の画像では、画像の背景の大部分が白であるため、黒のパンツのコントラストが非常に高いことがわかりますが、コートも白であるため、コントラストが非常に低くなります。この場合、パンツの端の検出は容易ですが、ジャケットの端の検出は非常に困難です。だからこそHOGが必要なのです。それは、Navneet Dalal と Bill Triggs によって 2005 年に初めて導入された、方向性勾配のヒストグラムです。
Hog アルゴリズムは、画像内の勾配方向の分布のヒストグラムを作成することによって機能し、その後、非常に特殊な方法で正規化されます。この特別な正規化により、Hog はコントラストの低い状況でもオブジェクトのエッジを効率的に検出できます。これらの正規化されたヒストグラムは、特徴ベクトル (HOG 記述子と呼ばれる) に入れられます。これを使用して、サポート ベクター マシン (SVM) などの機械学習アルゴリズムをトレーニングし、画像内の境界 (エッジ) に基づいてオブジェクトを検出できます。その大きな成功と信頼性により、HOG はコンピュータ ビジョンで最も広く使用されている物体検出アルゴリズムの 1 つになりました。
このチュートリアルで取り上げる内容は次のとおりです。
- HOG アルゴリズムの仕組み
- OpenCV を使用して HOG 記述子を作成する方法
- HOG 記述子を視覚化する方法
1 HOGアルゴリズム
名前が示すように、HOG アルゴリズムは、画像の勾配方向からヒストグラムを作成することに基づいています。HOG アルゴリズムは、次の一連の手順を通じて実装されます。
-
検出ウィンドウ内の各ピクセルの勾配の大きさと方向を計算します。
-
検出ウィンドウ内の各ピクセルの勾配の大きさと方向を計算します。
-
検出ウィンドウを、すべて同じサイズの接続されたピクセルのセルに分割します (下の画像を参照)。セルのサイズは自由なパラメーターであり、通常、検出される特徴のスケールに一致するように選択されます。たとえば、64 x 128 ピクセルの検出ウィンドウでは、幅 6 ~ 8 ピクセルの正方形のセルが人間の手足の検出に適しています。
-
各セルのヒストグラムを作成するには、最初に各セル内のすべてのピクセルの勾配方向を特定の数の方向 (角度) ビンにグループ化し、次に各角度ビンの勾配の勾配の大きさを合計します (下の図を参照)。ヒストグラム内のビンの数は自由パラメータであり、通常は 9 つのコーナー ビンに設定されます。
-
隣接するセルをブロックにグループ化します (下の画像を参照)。各ブロック内のセルの数は自由なパラメーターであり、すべてのブロックが同じサイズである必要があります。各ブロック間の距離 (ストライドと呼ばれる) は自由なパラメーターですが、通常はブロック サイズの半分に設定され、その場合、重複するブロックが取得されます (アニメーションを参照)。経験上、このアルゴリズムは重複するブロックをより適切に処理することがわかっています。
-
各ブロックに含まれるセルを使用して、そのブロック内のセルのヒストグラムを正規化します (下図を参照)。重複するブロックがある場合、ほとんどのセルが異なるブロックに対して正規化されることを意味します (アニメーションを参照)。したがって、同じユニットに複数の異なる正規化が適用される場合があります。
-
すべてのブロック内のすべての正規化されたヒストグラムを HOG 記述子と呼ばれる 1 つの特徴ベクトルに収集します。
-
同じ物体を含む多くの画像から取得した HOG 記述子を使用して、SVM などを使用して機械学習アルゴリズムをトレーニングし、画像内のこれらの物体を検出します。たとえば、多くの歩行者の画像からの HOG 記述子を使用して、画像内の歩行者を検出するように SVM をトレーニングできます。トレーニングは、オブジェクトを含む肯定的な例と、オブジェクトを含まない否定的な例を使用して行われます。
-
SVM がトレーニングされると、スライディング ウィンドウ アプローチを使用して、画像内のオブジェクトの検出と位置特定が試みられます。画像内のオブジェクトを検出するには、SVM によって学習された HOG パターンに類似した画像の部分を見つける必要があります。
2 HOG アルゴリズムが機能する理由
上で学んだように、HOG は画像の局所領域に特定の方向の勾配の大きさを追加することにより、「セル」と呼ばれるヒストグラムを作成します。そうすることで、ノイズによって引き起こされる弱いランダムな向きの勾配の影響を最小限に抑えながら、より強い勾配がそれぞれの角度ヒストグラムのサイズに大きく寄与することが保証されます。このように、ヒストグラムは各セルの主な勾配の方向を示します。
2.1 相対性理論の問題への対処
ここで、局所的な照明の変化と、背景と前景の間のコントラストによって、グラデーションの方向の大きさが大きく変化する可能性がある問題を考えてみましょう。
背景と前景のコントラストの違いを考慮するために、HOG アルゴリズムはエッジを局所的に検出しようとします。これを行うために、ブロックと呼ばれるセルのグループを定義し、そのローカルなセルのグループを使用してヒストグラムを正規化します。HOG アルゴリズムは、ブロック正規化と呼ばれるローカル正規化を通じて、各ブロック内のエッジを非常に高い信頼性で検出できます。
HOG アルゴリズムは、ブロック正規化の使用に加えて、重複ブロックも使用してパフォーマンスを向上させます。重複するブロックを使用することにより、各ユニットは最終的な HOG 記述子にいくつかの独立したコンポーネントを提供します。各コンポーネントは、異なるブロックに対して正規化されたユニットに対応します。これは冗長に思えるかもしれませんが、経験上、各セルを異なるローカル ブロックに複数回正規化することにより、HOG アルゴリズムのパフォーマンスが大幅に向上することがわかっています。
画像をロードしてアセットをインポートする
HOG 記述子を構築する最初のステップは、必要なパッケージを Python にロードし、イメージをロードすることです。
まず、OpenCV を使用して三角形のタイルの画像を読み込みます。この関数は画像を BGR としてロードするためcv2.imread()
、正しい色で表示できるように画像を RGB に変換します。いつものように、分析のために BGR 画像をグレースケールに変換します。
import cv2
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
# 设置默认图形的尺寸
plt.rcParams['figure.figsize'] = [17.0, 7.0]
# 载入图片
image = cv2.imread('./images/triangle_tile.jpeg')
# 将原始图像转换为RGB
original_image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
# 将原始图像转换为灰度
gray_image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# 输出原始图像和灰度图像的形状
print('The original image has shape: ', original_image.shape)
print('The gray scale image has shape: ', gray_image.shape)
# 输出图像
plt.subplot(121)
plt.imshow(original_image)
plt.title('Original Image')
plt.subplot(122)
plt.imshow(gray_image, cmap='gray')
plt.title('Gray Scale Image')
plt.show()
The original image has shape: (250, 250, 3)
The gray scale image has shape: (250, 250)
3 HOG 記述子の作成
OpenCV クラスを使用してHOGDescriptor
HOG 記述子を作成します。HOG ディスクリプタのパラメータは、関数を使用して設定されますHOGDescriptor()
。関数のパラメータ。HOGDescriptor()
HOGDescriptor() 関数のパラメーターとそのデフォルト値は次のとおりです。
cv2.HOGDescriptor(win_size = (64, 128), block_size = (16, 16), block_stride = (8, 8), cell_size = (8, 8), nbins = 9, win_sigma = DEFAULT_WIN_SIGMA, threshold_L2hys = 0.2, gamma_correction = true, nlevels = DEFAULT_NLEVELS)
公式パラメータは次のように説明されています。
-
win_size –サイズ
ピクセル単位の検出ウィンドウのサイズ (幅、高さ)。対象領域を定義します。ピクセル サイズの整数倍でなければなりません。 -
block_size –ブロックサイズの
サイズをピクセル単位で指定します (幅、高さ)。各ブロック内のセルの数を定義します。ピクセル サイズの整数倍であり、検出ウィンドウより小さい必要があります。正方形が小さいほど、より詳細な情報を取得できます。 -
block_stride –サイズブロック ストライド
ピクセル単位のブロック ストライド (水平、垂直)。セル サイズの整数倍でなければなりません。block_stride
隣接するブロック間の距離を定義します (たとえば、水平方向に 8 ピクセル、垂直方向に 8 ピクセル)。長いものはblock_strides
アルゴリズムの実行を高速化します (評価されるブロックが少なくなるため) が、アルゴリズムのパフォーマンスが低下する可能性があります。 -
cell_size –サイズ
ピクセル単位のセル サイズ (幅、高さ)。セルのサイズを決定します。セルが小さいほど、より詳細な情報を取得できます。 -
nbins – intヒストグラム (ビン) の数
ヒストグラム内のビンの数。ヒストグラムの作成に使用される角度ビンの数を決定します。ビンの数が増えると、より多くの勾配方向をキャプチャできます。HOG は符号なしの勾配を使用するため、角度単位の値は 0 ~ 180 度になります。 -
win_sigma –ダブル
ガウス平滑化ウィンドウ パラメーター。ヒストグラムを計算する前に各ピクセルにガウス空間ウィンドウを適用すると、ブロック エッジ付近のピクセルが平滑化され、HOG アルゴリズムのパフォーマンスが向上します。 -
Threshold_L2hys –ダブル
L2-Hys (Lowe スタイル プルーニングを使用した L2 ノルム) 正規化手法の縮小。L2-Hys メソッドは、L2 ノルム、クリッピング、繰り込みから構成されるブロックを正規化するために使用されます。クリッピングにより、各チャンクの記述子ベクトルの最大値が、指定されたしきい値 (デフォルトでは 0.2) の値に制限されます。クリッピング後、*IJCV*、60(2):91-110、2004 に記載されているように記述子ベクトルを再正規化しました。 -
gamma_correction –ガンマ補正の前処理が必要かどうかを指定するブールフラグ。
ガンマ補正を実行すると、HOG アルゴリズムのパフォーマンスがわずかに向上します。 -
nlevels – int
増加する検出ウィンドウの最大数。
cv2.HOGDescriptor()
この関数がさまざまなパラメーターをサポートしていることがわかります。最初のいくつかのパラメータ ( block_size, block_stride, cell_size
、およびnbins
) は、おそらく最も一般的に使用されるパラメータです。他のパラメータは通常、デフォルト値を維持して良好な結果を得ることができます。
以下のコードでは、cv2.HOGDescriptor()
関数を使用して、HOG 記述子ヒストグラムのセル サイズ、ブロック サイズ、ブロック ストライド、およびビンの数を設定します。次に、このメソッドを使用して、.compute(image)
指定された HOG 記述子 (特徴ベクトル) が計算されますimage
。
# 为HOG描述符指定参数
# 像素大小(以像素为单位)(宽度,高度)。 它必须小于检测窗口的大小,
# 并且必须进行选择,以使生成的块大小小于检测窗口的大小。
cell_size = (6, 6)
# 每个方向(x,y)上每个块的单元数。 必须选择为使结果
# 块大小小于检测窗口
num_cells_per_block = (2, 2)
# 块大小(以像素为单位)(宽度,高度)。必须是“单元格大小”的整数倍。
# 块大小必须小于检测窗口。
block_size = (num_cells_per_block[0] * cell_size[0],
num_cells_per_block[1] * cell_size[1])
# 计算在x和y方向上适合我们图像的像素数
x_cells = gray_image.shape[1] // cell_size[0]
y_cells = gray_image.shape[0] // cell_size[1]
# 块之间的水平距离,以像元大小为单位。 必须为整数,并且必须
# 将其设置为(x_cells-num_cells_per_block [0])/ h_stride =整数。
h_stride = 1
# 块之间的垂直距离,以像元大小为单位。 必须为整数,并且必须
# 将其设置为 (y_cells - num_cells_per_block[1]) / v_stride = integer.
v_stride = 1
# 块跨距(以像素为单位)(水平,垂直)。 必须是像素大小的整数倍。
block_stride = (cell_size[0] * h_stride, cell_size[1] * v_stride)
# 梯度定向箱的数量
num_bins = 9
# 指定检测窗口(感兴趣区域)的大小,以像素(宽度,高度)为单位。
# 它必须是“单元格大小”的整数倍,并且必须覆盖整个图像。
# 由于检测窗口必须是像元大小的整数倍,具体取决于您像元的大小,
# 因此生成的检测窗可能会比图像小一些。
# 完全可行
win_size = (x_cells * cell_size[0] , y_cells * cell_size[1])
# 输出灰度图像的形状以供参考
print('\nThe gray scale image has shape: ', gray_image.shape)
print()
# 输出HOG描述符的参数
print('HOG Descriptor Parameters:\n')
print('Window Size:', win_size)
print('Cell Size:', cell_size)
print('Block Size:', block_size)
print('Block Stride:', block_stride)
print('Number of Bins:', num_bins)
print()
# 使用上面定义的变量设置HOG描述符的参数
hog = cv2.HOGDescriptor(win_size, block_size, block_stride, cell_size, num_bins)
# 计算灰度图像的HOG描述符
hog_descriptor = hog.compute(gray_image)
The gray scale image has shape: (250, 250)
HOG Descriptor Parameters:
Window Size: (246, 246)
Cell Size: (6, 6)
Block Size: (12, 12)
Block Stride: (6, 6)
Number of Bins: 9
4 HOG 記述子の要素数
HOG 記述子 (特徴ベクトル) は、検出ウィンドウ内のすべてのブロックのすべてのセルの正規化されたヒストグラムを連結した長いベクトルです。したがって、HOG 特徴ベクトルのサイズは、検出ウィンドウ内のブロックの総数、ブロックあたりのセル数、方向ビンの数を乗算したもので求められます。
\begin{equation} \mbox{total_elements} = (\mbox{total_of_blocks})\mbox{ } \times \mbox{ } (\mbox{ブロックあたりのセル数})\mbox{ } \times \mbox{ } (\mbox{number_of_bins) }) \end{式}重複するブロックがない場合 (つまり、block_stride
等しいblock_size
場合)、ブロックの総数は、検出ウィンドウのサイズをブロック サイズで割ることによって簡単に計算できます。ただし、一般的なケースでは、重複するブロックがあるという事実を考慮する必要があります。block_stride
一般に (つまり、 anyと の)ブロックの合計数を見つけるにはblock_size
、以下の式を使用できます。
ここで、合計x _x×、検出ウィンドウ幅に沿ったブロックの合計数です。Total y _yはい、は検出ウィンドウの高さに沿ったブロックの総数です。合計x_x×和合計y _yはいの式では、オーバーラップによって生成される余分なブロックが考慮されます。合計x _xの計算中×和合計y _yはいその後、検出ウィンドウ内のブロックの総数Total x _xを乗算して取得できます。× × \回×合計y _yはい。block_size
、 、block_stride
および はwindow_size
すべて定義上であるため、上記の式は大幅に簡略化できますcell_size
。適切な置換とキャンセルをすべて行うと、上記の式は次のように単純化されます。
ここで、セルx _x×検出ウィンドウの幅に沿ったセルの総数、セルy _yはいは、検出ウィンドウの高さに沿ったセルの総数です。N×N_xN×cell_size
単位の水平ブロックストライドです、 N y N_yNはいcell_size
は、 の単位で表される垂直ブロック ストライドです。
HOG 特徴ベクトルの要素数を計算し、上で計算した HOG 記述子の形状と一致するかどうかを確認してみましょう
# 计算沿着检测窗口宽度的总块数
tot_bx = np.uint32(((x_cells - num_cells_per_block[0]) / h_stride) + 1)
# 计算沿着检测窗口高度的块总数
tot_by = np.uint32(((y_cells - num_cells_per_block[1]) / v_stride) + 1)
# 计算特征向量中的元素总数
tot_els = (tot_bx) * (tot_by) * num_cells_per_block[0] * num_cells_per_block[1] * num_bins
# 输出HOG特征向量应具有的元素总数
print('\nThe total number of elements in the HOG Feature Vector should be: ',
tot_bx, 'x',
tot_by, 'x',
num_cells_per_block[0], 'x',
num_cells_per_block[1], 'x',
num_bins, '=',
tot_els)
# 打印HOG描述符的形状,看它是否与上面的匹配
print('\nThe HOG Descriptor has shape:', hog_descriptor.shape)
print()
The total number of elements in the HOG Feature Vector should be: 40 x 40 x 2 x 2 x 9 = 57600
The HOG Descriptor has shape: (57600, 1)
5 HOG 記述子の視覚化
OpenCV には HOG 記述子を視覚化する簡単な方法がないため、最初に視覚化するためにいくつかの操作を行う必要があります。まず、計算を容易にするために HOG 記述子を再構成します。次に、各セルの平均ヒストグラムを計算し、最後にヒストグラム ビンをベクトルに変換します。ベクトルを取得したら、画像内の各セルに対応するベクトルをプロットできます。
以下のコードは対話型プロットを生成するため、プロットを操作できます。この図には次のものが含まれます。
- グレースケール画像、
- HOG 記述子 (特徴ベクトル)、
- HOG 記述子の拡大部分、および
- 選択したセルのヒストグラム。
グレースケール イメージまたは HOG 記述子イメージ上の任意の場所をクリックして、特定のセルを選択できます。いずれかの画像をクリックすると、選択したセルを示すマゼンタの四角形が表示されます。ズーム ウィンドウには、選択したセルの周囲の HOG 記述子の拡大バージョンが表示され、ヒストグラムには、選択したセルに対応するヒストグラムが表示されます。インタラクティブ ウィンドウの下部には、パンなどの追加機能を使用したり、必要に応じてグラフを保存したりできるボタンもあります。ホームボタンを押すとグラフィックスがデフォルトに戻ります。
注: このノートブックを Udacity ワークスペースで実行すると、インタラクティブ プロットに約 2 秒の遅延が発生します。これは、画像をクリックして拡大すると、グラフが更新されるまでに約 2 秒かかることを意味します。
%matplotlib notebook
%matplotlib inline
import copy
import matplotlib.patches as patches
# 设置默认图形尺寸
plt.rcParams['figure.figsize'] = [9.8, 9]
# 将特征向量重塑为 [blocks_y, blocks_x, num_cells_per_block_x, num_cells_per_block_y, num_bins].
# blocks_x和blocks_y将被换位,以便第一个索引(blocks_y)引用行号
# 第二个索引引用列号。 稍后在绘制特征向量时这将很有用,
# 以便特征向量索引与图像索引匹配。
hog_descriptor_reshaped = hog_descriptor.reshape(tot_bx,
tot_by,
num_cells_per_block[0],
num_cells_per_block[1],
num_bins).transpose((1, 0, 2, 3, 4))
# 输出特征向量的形状以供参考
print('The feature vector has shape:', hog_descriptor.shape)
# 输出重塑的特征向量
print('The reshaped feature vector has shape:', hog_descriptor_reshaped.shape)
# 创建一个数组,该数组将保存每个单元的平均梯度
ave_grad = np.zeros((y_cells, x_cells, num_bins))
# 输出ave_grad数组的形状以供参考
print('The average gradient array has shape: ', ave_grad.shape)
# 创建一个数组,该数组将计算每个单元格的直方图数量
hist_counter = np.zeros((y_cells, x_cells, 1))
# 将每个单元格的所有直方图相加并计算每个单元格的直方图数
for i in range (num_cells_per_block[0]):
for j in range(num_cells_per_block[1]):
ave_grad[i:tot_by + i,
j:tot_bx + j] += hog_descriptor_reshaped[:, :, i, j, :]
hist_counter[i:tot_by + i,
j:tot_bx + j] += 1
# 计算每个单元的平均梯度
ave_grad /= hist_counter
# 计算在所有单元格中拥有的向量总数。
len_vecs = ave_grad.shape[0] * ave_grad.shape[1] * ave_grad.shape[2]
# 创建一个数组,该数组的num_bins的弧度在0至180度之间。
deg = np.linspace(0, np.pi, num_bins, endpoint = False)
# 每个单元格都有一个带有num_bins的直方图。 对于每个像元,将每个仓绘制为矢量
# (其大小等于直方图中仓的高度,其角度与直方图中的仓对应)。
# 为此,创建第1级数组,该数组将保留图像中所有单元格中所有向量的(x,y)坐标。
# 此外,创建等级1数组,该数组将保存图像中所有单元格中所有向量的所有(U,V)分量。
# 创建将包含所有向量位置和成分的数组。
U = np.zeros((len_vecs))
V = np.zeros((len_vecs))
X = np.zeros((len_vecs))
Y = np.zeros((len_vecs))
# 将计数器设置为零
counter = 0
# 使用余弦和正弦函数从其大小计算矢量分量(U,V)。
# 请记住,余弦和正弦函数采用弧度表示角度。
# 从平均梯度数组计算矢量位置和大小
for i in range(ave_grad.shape[0]):
for j in range(ave_grad.shape[1]):
for k in range(ave_grad.shape[2]):
U[counter] = ave_grad[i,j,k] * np.cos(deg[k])
V[counter] = ave_grad[i,j,k] * np.sin(deg[k])
X[counter] = (cell_size[0] / 2) + (cell_size[0] * i)
Y[counter] = (cell_size[1] / 2) + (cell_size[1] * j)
counter = counter + 1
# 创建以度为单位的箱,以绘制直方图。
angle_axis = np.linspace(0, 180, num_bins, endpoint = False)
angle_axis += ((angle_axis[1] - angle_axis[0]) / 2)
# 创建一个以2 x 2排列的4个子图的图形
fig, ((a,b),(c,d)) = plt.subplots(2,2)
# 设置每个子图的标题
a.set(title = 'Gray Scale Image\n(Click to Zoom)')
b.set(title = 'HOG Descriptor\n(Click to Zoom)')
c.set(title = 'Zoom Window', xlim = (0, 18), ylim = (0, 18), autoscale_on = False)
d.set(title = 'Histogram of Gradients')
# 绘制灰度图像
a.imshow(gray_image, cmap = 'gray')
a.set_aspect(aspect = 1)
# 绘制特征向量(HOG描述符)
b.quiver(Y, X, U, V, color = 'white', headwidth = 0, headlength = 0, scale_units = 'inches', scale = 5)
b.invert_yaxis()
b.set_aspect(aspect = 1)
b.set_facecolor('black')
# 定义交互式缩放函数
def onpress(event):
#除非按下鼠标左键,否则什么都不做
if event.button != 1:
return
# 仅接受子图a和b的点击
if event.inaxes in [a, b]:
# 获取鼠标点击坐标
x, y = event.xdata, event.ydata
# 选择最接近鼠标单击坐标的单元格
cell_num_x = np.uint32(x / cell_size[0])
cell_num_y = np.uint32(y / cell_size[1])
# 设置矩形面片的边缘坐标
edgex = x - (x % cell_size[0])
edgey = y - (y % cell_size[1])
# 创建一个与上面所选单元格匹配的矩形补丁
rect = patches.Rectangle((edgex, edgey),
cell_size[0], cell_size[1],
linewidth = 1,
edgecolor = 'magenta',
facecolor='none')
# 单个补丁只能在单个图中使用。
# 创建补丁副本以在其他子图中使用
rect2 = copy.copy(rect)
rect3 = copy.copy(rect)
# 更新所有子图
a.clear()
a.set(title = 'Gray Scale Image\n(Click to Zoom)')
a.imshow(gray_image, cmap = 'gray')
a.set_aspect(aspect = 1)
a.add_patch(rect)
b.clear()
b.set(title = 'HOG Descriptor\n(Click to Zoom)')
b.quiver(Y, X, U, V, color = 'white', headwidth = 0, headlength = 0, scale_units = 'inches', scale = 5)
b.invert_yaxis()
b.set_aspect(aspect = 1)
b.set_facecolor('black')
b.add_patch(rect2)
c.clear()
c.set(title = 'Zoom Window')
c.quiver(Y, X, U, V, color = 'white', headwidth = 0, headlength = 0, scale_units = 'inches', scale = 1)
c.set_xlim(edgex - cell_size[0], edgex + (2 * cell_size[0]))
c.set_ylim(edgey - cell_size[1], edgey + (2 * cell_size[1]))
c.invert_yaxis()
c.set_aspect(aspect = 1)
c.set_facecolor('black')
c.add_patch(rect3)
d.clear()
d.set(title = 'Histogram of Gradients')
d.grid()
d.set_xlim(0, 180)
d.set_xticks(angle_axis)
d.set_xlabel('Angle')
d.bar(angle_axis,
ave_grad[cell_num_y, cell_num_x, :],
180 // num_bins,
align = 'center',
alpha = 0.5,
linewidth = 1.2,
edgecolor = 'k')
fig.canvas.draw()
# 在图形和鼠标单击之间创建连接
fig.canvas.mpl_connect('button_press_event', onpress)
plt.show()
The feature vector has shape: (57600, 1)
The reshaped feature vector has shape: (40, 40, 2, 2, 9)
The average gradient array has shape: (41, 41, 9)
6 ヒストグラムを理解する
上図のいくつかの静的なスクリーンショットを分析して、選択したセルのヒストグラムが意味があるかどうかを確認してみましょう。まず、三角形のエッジ近くではなく、三角形の内側のセルを見てみましょう。
この場合、三角形はほぼすべて同じ色であるため、選択したセルに大きなグラデーションは存在しないはずです。ズーム ウィンドウとヒストグラムではっきりとわかるように、これは実際に当てはまります。多くのグラデーションがありますが、どれも他のものを明確に支配しません。
次に、水平方向の端近くのセルを見てみましょう。
エッジとは、強度が突然変化する画像内の領域であることに注意してください。このような場合、特定の方向に大きな強度勾配が存在します。これは、選択したセルの対応するヒストグラムとズーム ウィンドウに表示されるものとまったく同じです。ズーム ウィンドウでは、主な勾配が上向き (ほぼ 90 度) であることがわかります。これは、強度の急激な変化の方向であるためです。したがって、ヒストグラム内の 90 度の領域が他の領域よりも強いと予想する必要があります。それが実際に私たちが見ていることなのです。
次に、垂直方向の端近くのセルを見てみましょう。
この場合、セル内の主な勾配は 180 度に近い水平方向であると予想されます。これは、強度が急激に変化する方向であるためです。したがって、ヒストグラム内の 170 度の領域は他の領域よりも勾配の影響が大きいと予想する必要があります。これはヒストグラムで見られるものですが、セル内に別の主要な勾配、10 度ビン内の勾配があることもわかります。これは、HOG アルゴリズムが符号なしの勾配を使用するためです。つまり、0 度と 180 度は同じとみなされます。したがって、ヒストグラムを作成する場合、160 度と 180 度の間の角度は 10 度のビンと 170 度のビンに比例します。これにより、垂直エッジ付近のセルに 1 つだけではなく 2 つの主要な勾配が生じます。
要約すると、対角線のエッジ近くのセルを見てみましょう。
私たちが見ているものを理解するために、まず、グラデーションはベクトルと同じように、x 部分 (コンポーネント) と y 部分 (コンポーネント) で構成されていることを思い出してください。したがって、勾配の最終的な方向は、その成分のベクトル和によって与えられます。したがって、上の画像に示すように、垂直エッジでは、グラデーションは x コンポーネントのみを持つため水平になります。上の画像に示すように、水平エッジでは、グラデーションは y コンポーネントのみを持つため垂直になります。したがって、*x* 成分と *y* 成分の両方がゼロではないため、対角エッジでは、勾配も対角になります。画像の対角エッジは 45 度に近いため、50 度のビンに重要な勾配方向が見られることが予想されます。これは実際にヒストグラムで見られるものですが、上に示したように、1 つではなく 2 つの主要な勾配があることがわかります。その理由は、ヒストグラムを作成するときに、ビン境界付近の角度が隣接するビンに比例して作用するためです。たとえば、角度 40 度のグラデーションは、30 度のビンと 50 度のビンの中間になります。したがって、勾配の大きさは 30 度のビンと 50 度のビンに均等に分割されます。これにより、対角エッジ付近のセルに 1 つだけではなく 2 つの主要な勾配が生じます。
HOG の実装方法がわかったので、ワークスペースにExamplesというノートブックが表示されます。ここでは、さまざまな画像の HOG 記述子に独自のパラメーターを設定できます。楽しむ!