Vivado HLSでHOG+SVMの高速物体検出をする1(2つめのコンポーネントまで作成)
あらまし
前回(FPGAデザインコンテスト@FPT2018 開発記 - lp6m’s blog)からかなり時間が経ってしまった。
コンテストの際の信号検出の実装は、
カメラ画像から物体を検出する際にウインドウを動かしていくスライディングウィンドウ法を使用していた。
FPGAに実装したIPは1ウインドウに対する特徴量の抽出しかできないため、1フレームの処理のために何度もHWを呼び出す必要があり、あまり高速化できているとは言えない状態だった。
次のコンテストに向けて、というか趣味的にももう少し性能を向上したい。
色々と「物体検出をFPGAで高速化」みたいなものを探した結果、今回はこの論文にたどり着いた。
ポイントは、
- HOG+SVMによる人間認識
- FPGAのみ(Pure)で、CPUやDRAMを使用しない
- 1フレームの画像をパイプライン的に処理できる
- スライディングウインドウのウインドウの動く間隔は、セルサイズと同じ(8pix)
- 600*800のフレーム画像に対して162fpsで判定が可能
ということ。
論文は全部で16ページあって、結構詳しめに書いてくれている。
とりあえずこの論文を読んで、パクってそのまま実装していくことにする。
多分論文ではHLSは使用しておらず、RTLで書いている気がする。
将来的にはRGB/HSV特徴量も加えた判定ができるようにしたい。
論文の図をココに貼るのは気が引けるので、番号で参照することにする。
HOG+SVMによる高速検出は、大きく4つのコンポーネントからなっており、順に作っていく予定。
- FIgure3: Diagram of the Gradient Calculation sub-module in the proposed structure
- FIgure5: Diagram of the Cell Histogram Generation sub-module in the proposed structure
- Figure6: Diagram of the Block Histogram Normalization sub-module in the proposed structure
- Figure7: Diagram of the SVM Classification in the proposed stucture.
(以後1.〜4.の番号で参照する)
今回やったこと
1.と2.の2つのコンポーネントを作った。
1.はグレースケールのデータからmagnitudeとbin_indexを計算し、出力する
2.は8pix*8pixのセルごとに・ヒストグラムのbin_indexごとにmagnitudeを合算して、出力する。
今回のコミットのリンクを貼る。
github.com
合成のメイン:main.cppは以下。
ImageDetectionHW2/main.cpp at afe31ceca667bf795b03494b05265073333af3f3 · lp6m/ImageDetectionHW2 · GitHub
テストベンチmain_tb.cppは、
1,の処理はHW用の実装を動作させ、次に2.の処理をHW向けとSW向けそれぞれの実装を動作させて、出力の一致を確かめている。
(2.の入力のために両者で1.のHW用の実装を使っている理由は、SW用の実装とHWの実装の結果が違うため。ここ参照)
論文と今回の実装の違い
- 入力画像サイズが論文は600*800, 今回の実装は480*640
- bin_indexの決め方や、gradientの計算方法は論文よりも軽い計算にしている(特に意味はない、前の実装を引き継いでいる)
- ウインドウのサイズが論文では7*15, 今回の実装では未定
問題点1
テストベンチで出力を確認したが、問題なかった。
合成結果は以下の通り。(Detail->Instanceの上が2.で下が1.)
1.はLatencyが307206≒480*640となっており、1cycleで1pixの処理ができている。
2.のInitiation Intervalが1を達成できず、2になってしまっている。
合成中のログは以下の通り。
INFO: [SCHED 204-61] Pipelining loop 'loop_y_loop_winx_loop_cell_index'. WARNING: [SCHED 204-68] Unable to enforce a carried dependence constraint (II = 1, distance = 1, offset = 1) between 'store' operation (/home/lp6m/Xilinx/Vivado/2017.4/common/technology/autopilot/hls/hls_video_mem.h:765->hog_svm/src/main.cpp:123) of variable 'tmp.data.V', hog_svm/src/main.cpp:121 on array 'cellbuf[0].val[1]', hog_svm/src/main.cpp:100 and 'load' operation ('tmp.data.V', /home/lp6m/Xilinx/Vivado/2017.4/common/technology/autopilot/hls/hls_video_mem.h:729->hog_svm/src/main.cpp:122) on array 'cellbuf[0].val[1]', hog_svm/src/main.cpp:100. INFO: [SCHED 204-61] Pipelining result : Target II = 1, Final II = 2, Depth = 6.
うーん、どうやら依存があるみたい。
問題点2
2.のコンポーネントでは、
8*480個の入力ごとに、「8個の入力ごとに9個の出力データが完成」になる。
ループ内でデータが完成したときに9個連続で同じポートから出力していると、出力のほうに律速してしまう。
とりあえず今は出力を9個つくって並列に出力できるようにしている。
ちなみに、ap_axisのdataメンバを配列にする、といったことはできなかった。(FPGAの部屋 Vivado HLSでのAXI4-Stream のテンプレートを作成する1参照)
他の解決策としては、
- 9個のデータをビットを並べた1つのデータとして出す
- 何らかのバッファをおいて後から出力する
が考えられる。
とりあえず3.4.を作ってから2.の問題点を考えるようにしよう。