vivado hls を使用する lucas-kanade オプティカル フロー …

43
XAPP1300 (v1.0) 2017 2 3 1 japan .xilinx.com この資料は表記のバージ ョ ンの英語版を翻訳し たもので、 内容に相違が生じ る場合には原文を優先し ます。 資料によっては英語版の更新に対応していないものがあります。 日本語版は参考用 と し てご使用の上、 最新情報につき ま し ては、 必ず最新英語版を ご参照 く だ さ い。 概要 密なオプテ ィ カル フローの推定に使用される Lucas-Kanade (LK) アルゴ リ ズムは、 画像処理アプ リ ケーシ ョ ンにおける物 体の検出や追跡の技法と して広く知られ、 採用されています。 このアルゴ リ ズムは演算負荷が大き く、 FPGA への実装に はデザイン とパフォーマンスの両面で容易ではあ り ません。 このアプ リ ケーシ ョ ン ノ ー ト では、 Zynq®-7000 All Programmable (AP) SoC で、 LK アルゴリズムをザイリンクス Vivado® 高位合成 (HLS) ツールを使用して実装し、 画像品質 を低下させることなく リアルタイム パフォーマンスを実現する方法について説明します。 Zynq-7000 AP SoC リファレンス ボードでのリアルタイムのデモンストレーションは、 SDSoC™ 開発環境の統合ツールを 使用して作成されました。 このデザインでは、 ビデオ データの読み出し /書き込みをフレーム バッファーで行うのではな く、 ファイルから読み出して処理したデータをファイルに書き込みます。 デザインは 1 名のエンジニアが 8 週間かからず に作成されました。 このアプリケーション ノ ー ト は さ ら に、 画像処理で Vivado HLS を用いてい最大のパフォーマンスを 引き出すために有効な C/C++ コーディング手法を具体的に説明するチュート リアルとしても役立ちます。 このアプリ ケーシ ョ ン ノートの は、ザイリンクスのウェブサイトからダウンロードできます。デザ イン フ ァ イ ルの詳細は、 「リファレンス デザイン」 を参照してください。 はじめに オプテ ィ カル フローは、 ビデオ シーケンスでの物体またはカ メ ラの移動によ る画像中の物体の仮現運動パターンの推定 に使用される技法です。 これは、 各ベク ト ルが最初の画像 ( フレームとも呼ばれる ) から 2 番目の画像への移動に よ り 生じ る ポ イ ン ト 間の変位を示す 2 次元ベク トル場で表現されます。 画像処理およびコンピューター ビジ ョ ンでは、 LK アルゴ リズムはオプティカル フ ローを推定する一般的な方式です [参照 1]。 このアルゴリズムは、 対象とする画素と隣接する我 画素は基本的に同じ動き をする と仮定し、 すべての隣接画素の基本オプテ ィ カル フローを最小二乗法を使用して解きま す。 画像内の一部画素のオプテ ィ カル フローを計算する手法をスパース () 型、 すべての画素を計算する手法をデンス () 型といいます。 LK アルゴリズムは既に最先端とは見なされなくなっていますが、 さまざまなプログラ ミング言語 (OpenCVPythonMATLAB® ソフトウェア、 OpenCL™ フレームワークなど) でパブ リ ッ ク ド メインとして実装されているため、 多くの学 術論文で参考と して使用されています。 LK アルゴ リ ズムは非常に複雑で、 FPGA への実装は膨大な時間をかけなければ ほとんど不可能と考えられてきました。 このアプリケーション ノ ー ト では、 LK アルゴ リ ズムの FPGA への実装が可能で あることを示します。密な LK オプテ ィ カル フロー アルゴ リ ズムは、 C/C++ プログラ ミ ング言語を使用して、 Vivado HLS によ り最大のパフォーマンスを実現する適切なコーティング ス タ イルでモデル化されています。 設計手順では、 ボ ト ムアップ方式を使用します。 オプティカル フローの IP コアは、 Vivado HLS を使用して実装されます。 ファイル I/O デオ データに基づいて ZC702 評価ボード または ZC706 評価ボー ド のいずれかで リ アルタ イ ム デモンス ト レーターを実装 するため、 SDSoC 環境でエンベデッ ド システム デザインが作成されます。 詳細は、 『Zynq-7000 XC7Z020 AP SoC 向け ZC702 評価ボード ユーザー ガイド』 (UG850) [参照 2] および『Zynq-7000 All Programmable SoC ZC706 評価キ ッ ト スタート アップ ガイド』 (UG961) [参照 3] をそれぞれ参照してください。 アプリケーション ノート : Zynq-7000 AP SoC XAPP1300 (v1.0) 2017 2 3 Vivado HLS を使用する LucasKanade オプテ ィ カル フ ロー アルゴ リ ズムの実装 著者: Daniele BagniPari KannanStephen Neuendorffer

Upload: others

Post on 17-Nov-2021

8 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: Vivado HLS を使用する Lucas-Kanade オプティカル フロー …

XAPP1300 (v1.0) 2017 年 2 月 3 日  1japan.xilinx.com

この資料は表記のバージ ョ ンの英語版を翻訳したもので、 内容に相違が生じる場合には原文を優先します。 資料によっては英語版の更新に対応していないものがあります。 日本語版は参考用としてご使用の上、 最新情報につきましては、 必ず最新英語版をご参照ください。

概要

密なオプティカル フローの推定に使用される Lucas-Kanade (LK) アルゴ リズムは、 画像処理アプリ ケーシ ョ ンにおける物体の検出や追跡の技法と して広く知られ、 採用されています。 このアルゴ リ ズムは演算負荷が大き く、 FPGA への実装にはデザインとパフォーマンスの両面で容易ではあ り ません。 このアプリ ケーシ ョ ン ノートでは、 Zynq®-7000 All Programmable (AP) SoC で、 LK アルゴ リズムをザイ リ ンクスVivado® 高位合成 (HLS) ツールを使用して実装し、 画像品質を低下させるこ とな く リ アルタイム パフォーマンスを実現する方法について説明します。

Zynq-7000 AP SoC リ ファレンス ボードでのリ アルタイムのデモンス ト レーシ ョ ンは、 SDSoC™ 開発環境の統合ツールを使用して作成されました。 このデザインでは、 ビデオ データの読み出し /書き込みをフレーム バッファーで行うのではなく、 ファ イルから読み出して処理したデータをファイルに書き込みます。 デザインは 1 名のエンジニアが 8 週間かからずに作成されました。 このアプリ ケーシ ョ ン ノートはさ らに、 画像処理で Vivado HLS を用いてい最大のパフォーマンスを引き出すために有効な C/C++ コーディング手法を具体的に説明するチュート リ アルと しても役立ちます。 このアプリケーシ ョ ン ノートの リ ファレンス デザイン ファ イルは、 ザイ リ ンクスのウェブサイ トからダウンロードできます。 デザイン ファ イルの詳細は、 「 リ ファレンス デザイン」 を参照して ください。

はじめに

オプティカル フローは、 ビデオ シーケンスでの物体またはカメ ラの移動による画像中の物体の仮現運動パターンの推定に使用される技法です。 これは、 各ベク トルが最初の画像 (フレームと も呼ばれる ) から 2 番目の画像への移動によ り生じるポイン ト間の変位を示す 2 次元ベク トル場で表現されます。 画像処理およびコンピューター ビジ ョ ンでは、 LK アルゴリズムはオプティカル フローを推定する一般的な方式です [参照 1]。 このアルゴ リズムは、 対象とする画素と隣接する我画素は基本的に同じ動きをする と仮定し、 すべての隣接画素の基本オプティカル フローを最小二乗法を使用して解きます。 画像内の一部画素のオプティカル フローを計算する手法をスパース (疎) 型、 すべての画素を計算する手法をデンス (密) 型といいます。

LK アルゴ リズムは既に最先端とは見なされなくなっていますが、 さまざまなプログラ ミ ング言語 (OpenCV、 Python、MATLAB® ソフ ト ウェア、 OpenCL™ フレームワークなど) でパブリ ッ ク ド メ インと して実装されているため、 多くの学術論文で参考と して使用されています。 LK アルゴ リ ズムは非常に複雑で、 FPGA への実装は膨大な時間をかけなければほとんど不可能と考えられてきました。 このアプリ ケーシ ョ ン ノートでは、 LK アルゴ リズムの FPGA への実装が可能であるこ とを示します。 密な LK オプティカル フロー アルゴ リズムは、 C/C++ プログラ ミ ング言語を使用して、 Vivado HLS によ り最大のパフォーマンスを実現する適切なコーティング スタイルでモデル化されています。 設計手順では、 ボト ムアップ方式を使用します。 オプティカル フローの IP コアは、 Vivado HLS を使用して実装されます。 ファ イル I/O ビデオ データに基づいて ZC702 評価ボードまたは ZC706 評価ボードのいずれかでリ アルタイム デモンス ト レーターを実装するため、 SDSoC 環境でエンベデッ ド システム デザインが作成されます。 詳細は、 『Zynq-7000 XC7Z020 AP SoC 向け ZC702 評価ボード ユーザー ガイ ド』 (UG850) [参照 2] および『Zynq-7000 All Programmable SoC ZC706 評価キッ ト スタートアップ ガイ ド』 (UG961) [参照 3] をそれぞれ参照して ください。

アプリケーシ ョ ン ノート : Zynq-7000 AP SoC

XAPP1300 (v1.0) 2017 年 2 月 3 日

Vivado HLS を使用する  Lucas‐Kanade オプティカル フロー アルゴリズムの実装著者: Daniele Bagni、 Pari Kannan、 Stephen Neuendorffer

Page 2: Vivado HLS を使用する Lucas-Kanade オプティカル フロー …

はじめに

XAPP1300 (v1.0) 2017 年 2 月 3 日  2japan.xilinx.com

ザイ リ ンクスの SDSoC は、 Zynq-7000 AP SoC を使用してヘテロジニアスなエンベデッ ド システムを実装するための、Eclipse ベースの統合開発環境 (IDE) です。 SDSoC 環境には、 プログラマブル ロジッ クでのソフ ト ウェア アクセラレーシ ョ ンの自動化やシステム コネクティ ビティの自動生成などを実行し、 システム全体の最適化を行う C/C++ コンパイラが含まれます。 アプリ ケーシ ョ ンは C/C++ コードで作成され、 プログラマがターゲッ ト プラ ッ ト フォームとハード ウェアにコンパイルするアプリ ケーシ ョ ン内の関数のサブセッ ト を特定します。 その後、 SDSoC システム コンパイラによ りアプリ ケーシ ョ ンがハード ウェアと ソフ ト ウェアにコンパイルされ、 ファームウェア、 オペレーティング システム、 アプリ ケーシ ョ ン実行ファイルを備えた完全なブート イ メージを含む、 完全なエンベデッ ド システムが Zynq-7000 AP SoC に実装されます。 詳細は、 『SDSoC 環境ユーザー ガイ ド』 (UG1027) [参照 4] を参照して ください。

Vivado HLS は、 SDSoC 環境の重要なコンポーネン トです。 Vivado HLS C/C++ コンパイラは、 一定の制約に従って高性能なパイプライン処理アーキテクチャを生成し、 HDL と C/C++ コードの動作が同じであるこ とを確認するテス ト ベンチを作成します。 多くの場合、 Vivado HLS 合成コードの効率と性能は、 経験豊富なハードウェア エンジニアが設計してコーディングした HDL と同程度とな り ます。 Vivado HLS の詳細は、 『Vivado Design Suite ユーザー ガイ ド : 高位合成』 (UG902) [参照 5] を参照してください。

C/C++ デザインは、 デザイン パラ メーター (画像のサイズ、 ビッ ト /画素 (bpp) の値、 スライディング ウ ィンド ウの寸法、浮動小数点、浮動小数点データ タイプなど) を含む C ヘッダー ファ イルを少し修正するだけで、新規アプリ ケーシ ョ ンに合わせて容易にカスタマイズできる IP コアにな り ます。

このアプリ ケーシ ョ ン ノートで説明されている設計手順は、Vivado HLS および SDSoC Vivado 2016.2 と 2016.3 IDE ツールに適用されます。

Page 3: Vivado HLS を使用する Lucas-Kanade オプティカル フロー …

動作理論

XAPP1300 (v1.0) 2017 年 2 月 3 日  3japan.xilinx.com

動作理論

密な LK オプティカル フロー アルゴ リズムの C/C++ モデルは、『Pyramidal Implementation of the Affine Lucas Kanade Feature Tracker Description Algorithm』 (著者: J. Y. Bouguet、 Intel Corporation、 2001) [参照 7] の数学的な説明と、MathWorks 社の資料『opticalFlowLK class』 [参照 8] の説明に基づいています。 これらについてこのセクシ ョ ンで要約します。

式 1 に、 オプティカル フローの式を示します。 この式で、 (vx, vy) は、 それぞれ水平方向と垂直方向の動きベク トルの 2 つの未知の成分を表します。 Ix、 Iy、 および It は、 画像輝度の空間的および時間的な導関数です。

式 1

1 つの式に未知数が 2 つ含まれていて式を解けないため、 LK 法では元の画像を小さいセクシ ョ ンに分割し、 それぞれのセクシ ョ ンでの一定速度を想定します。 次に、 各セクシ ョ ンで、 (vx, vy) の定数モデルに対してオプティカル フロー式の加重最小二乗を実行します。 LK 法では式 2 を最小化するこ とでこれを実現します。 式の中の W は、 各セクシ ョ ンの中央のオプティカル フロー制約式を強調する窓関数です。

式 2

式 3 は、 最小化の問題の解を示しています。

式 3

この式は 2 つの未知数を持つ 2 つの連立方程式であるため、 解く こ とができます。

図 1 は、 同じビデオ シーケンス内の 2 つの画像 I1(x, y) と I2(x, y) を処理する LK 方式の基本関数を示しています。 このプロセスには次のステージが含まれます。

1. 2 つの入力画像は、 サイズ 5x5 のノ イズ削減フ ィルターで滑らかにされます (3x3 や 7x7 もアルゴ リズム設計オプシ ョンと して有効)。 このステージでは画像のノ イズを削減し、 ノ イズが 2 番目のステージで増幅されないよ うにします。

2. 空間的導関数 Ix Iy が、 サイズ 5x5 のフ ィルターを使用して計算されます (3x3 や 7x7 も有効なオプシ ョ ン)。 時間的導関数 It は、 空間的導関数と揃う よ うに 5x5 のユニタ リ フ ィルターによって遅延された 2 つの入力画像間の差です。

3. 式 3 の 5 つのパラ メーターは、 サイズ NxN (N は 7 ~ 53) のウ ィンド ウ W の 2 乗の累積で計算されます (このアプリケーシ ョ ン ノートでは統合ウ ィンド ウ と呼ぶ)。

4. ベク トル成分 vx と vy は、 前のステージの式 3 の 2x2 の実対称行列を逆にして計算されます。

Ixvx Iyvy It+ + 0=

W2 Ixvx Iyvy It+ + 2 0=

W2Ix2 W2IxIy

W2IxIy W2Iy2

vxvy

– W2IxIt

W2IyIt

=

Page 4: Vivado HLS を使用する Lucas-Kanade オプティカル フロー …

動作理論

XAPP1300 (v1.0) 2017 年 2 月 3 日  4japan.xilinx.com

動きベク トルが計算された後、 式 4 を使用して画像 I1 を画像 I2 から再構築できます。 これを動き補正と言います。

式 4

理想的な完全オプティカル フロー フ ィールドを使用する と、 次のよ うにな り ます。

式 5

実際には、 計算されたオプティカル フローが不適切になっていくほど、 動き補正された画像で歪みが大き く現れるよ うにな り ます。 たとえば、 画像または屋内シーンに閉鎖領域があ り、 反射する床や壁が数多く含まれている場合、 計算されるオプティカル フローの質が低下します。 LK オプティカル フロー方式では、 統合ウ ィンド ウのサイズが動きベク トルの整合性を決定します。 ウ ィンド ウが大き くなるほど、 動き補正された画像の質が向上します。

図 2、 図 3、 図 4、 および図 5 は、 2 つの入力画像と、 11x11 または 43x43 のどちらかの統合ウ ィンド ウ サイズで取得された、 動き補正済み画像をそれぞれ示しています。 2 番目の入力 (図 3) と最初の入力の違いは、 進入車両の左車線の車が (通常の走行方向の向きで) 少し前方に移動しているこ とです。 この情報は検出される必要があ り ます。 画像の品質は、図 4 よ り も図 5 の方が大幅に向上しています。

X-Ref Target - Figure 1

図 1:密な Lucas‐Kanade オプテ ィ カル フローのブロック図

Smooth 5x5

Smooth 5x5

Ix5x5

Iy5x5

It5x5

Compute Integralson a N x N

window size

Compute Vectors

(with 2x2 matrix inversion)

Dense optical flow

Block 4: ComputeVectors

I1

I2

Block 1: twoIsotropicFilters()

Block 3: ComputeIntegrals()

Block 2: SpatialTemporalDerivatives()X18179-012017

I1MC x y I2 x vx+ y vy+ =

I1MC x y I1 x y

Page 7: Vivado HLS を使用する Lucas-Kanade オプティカル フロー …

動作理論

XAPP1300 (v1.0) 2017 年 2 月 3 日  7japan.xilinx.com

図 6 と図 7 は、 複数の色でコーディングされた動きベク トルを示しています。 これらの図は、 統合ウ ィンド ウ サイズ 11x11 と 43x43 で計算された場合のオプティカル フローを示しています。 オプティカル フローは、統合ウ ィンド ウが大きい方がよ り滑らかで、 よ りわかりやすく表示されます。

X-Ref Target - Figure 6

図 6:統合ウィンドウ 11x11 で生成された動きベク トル

X18063-110116

Page 9: Vivado HLS を使用する Lucas-Kanade オプティカル フロー …

動作理論

XAPP1300 (v1.0) 2017 年 2 月 3 日  9japan.xilinx.com

4 番目のステージで計算される動きベク トルは、 整数以外の値を持つこ とができます。 結果と して、 それらはサブ画素精度になり、 補間される必要があ り ます。 文献からのオプティカル フロー方式の多くでは、 図 8 [参照 9] に示すよ うに、 サブ画素精度に双線形補間を適用します。 赤の点は、 輝度値が Q11、 Q12、 Q21、 Q22 である整数座標 x1、 y1、 x2、 y2 の画素です。 緑と青の点は、 輝度値が R1、 R2、 および P であるサブ画素座標です。 この MATLAB 擬似コード フラグメン トは、次の双線形補間を表します。

x1 = floor(x); y = floor(y); kx = x-x1; ky = y-y1;

R1 = Q11 * (1- kx) + kx * Q21; R2 = Q12 * (1- kx) + kx * Q22;

P = R1 * (1- ky) + ky * R2;X-Ref Target - Figure 8

図 8:サブ画素の双線形補間

X18180-012017

Page 10: Vivado HLS を使用する Lucas-Kanade オプティカル フロー …

HLS 用の C++ モデル

XAPP1300 (v1.0) 2017 年 2 月 3 日  10japan.xilinx.com

HLS 用の C++ モデル

Vivado HLS を使用してザイ リ ンクス FPGA で画像処理アルゴ リズムを効果的に実装するには、 C/C++ 言語での適切なコーディング スタイルが必要です。 ソフ ト ウェアでは、 画像はメモ リ内に格納されている 2D 配列です。 FPGA ハードウェアでは、 画像はラスター スキャン順 (左上から右下) で入力される画素のス ト リームで、 これを FPGA 内部メモリに完全に格納するこ とはできません。 通常はライン幅単位で入力ス ト リームを遅延させるライン バッファーに、 わずかな画像ラインのみが格納されます。 Vivado HLS はこれらのライン バッファーを、 デュアル ポートのブロ ッ ク RAM エレ メン トに実装された FIFO と してマップします (各ブロッ ク RAM は、 1024 画素を画素あたり最大 18 ビッ トで格納するよ うに構成可能)。

図 9 は、 カーネル サイズ 3x3 の 2D たたみ込みフ ィルターのアーキテクチャを示しています。 これは、 2 つのライン バッファー (3 番目のラインは入力ス ト リームそのもの) と、 サイズ 3x3 のスライディング ウ ィンド ウを必要と します。 画像処理の用語と してのカーネルは、 2D フ ィルター係数領域を指します (必ずしも正方形とは限らない)。 スラ イディング ウ ィンド ウは、 各出力画素を生成するためにフ ィルター カーネルの係数で重み付けおよび合計された、 近接入力画素を指します。 ウ ィンドウの中心は、 ラスター スキャン順序で生成される出力画素付近をスライ ド します。 カーネル (および形状) のサイズによ り、 2D フ ィルターのウ ィンド ウ サイズ (および形状) が決定されます。 ウ ィンド ウ サイズ N が大き くなるほど、 必要なライン バッファー数は増えます。 画像処理では、 2D たたみ込みフ ィルターは通常 N=16 を超えません。 さらに、 ライン バッファーには同時読み取り /書き込みアクセスが必要です。 このアクセスでは、 ブロ ッ ク RAM のデュアル ポート という特長を最大限に活用します。 これとは逆に、 スライディング ウ ィンド ウは並列レジスタ と して実装されます。 Vivado HLS で画像処理のメモ リ構造を効果的に実装する方法は、 『Vivado Design Suite ユーザー ガイ ド : 高位合成』 (UG902) [参照 5] を参照して ください。

X-Ref Target - Figure 9

図 9: ライン  バッファーおよび 3x3 スライディング ウィンドウのアーキテクチャ

= Register

3x3 sliding window

Filter Kernel

Line buffer

Line buffer

Pixel Stream

X18181-120216

Page 11: Vivado HLS を使用する Lucas-Kanade オプティカル フロー …

HLS 用の C++ モデル

XAPP1300 (v1.0) 2017 年 2 月 3 日  11japan.xilinx.com

HLS による  C/C++ での 2D たたみ込みフ ィルターの効果的なインプリ メンテーシ ョ ン

これまで説明した概念が、 次に示す C/C++ コードに適用されています。 これは、 密な LK オプティカル フロー デザインを実装するルーチンの基本です。

void hls_2DFilter(pix_t inp_img[MAX_HEIGHT*MAX_WIDTH], pix_t out_img[MAX_HEIGHT*MAX_WIDTH], short int height, short int width) {short int row, col; pix_t filt_out;

pix_t window[FILTER_SIZE][FILTER_SIZE]; // sliding window#pragma HLS ARRAY_PARTITION variable=window complete dim=0

pix_t right_col[FILTER_SIZE]; // right-most, incoming columnstatic pix_t line_buffer[FILTER_SIZE][MAX_WIDTH]; // line-buffers#pragma HLS ARRAY_PARTITION variable=line_buffer complete dim=1

// effective filtering 2D loopL1: for(row = 0; row < height+FILTER_OFFS; row++){L2: for(col = 0; col < width+FILTER_OFFS; col++){#pragma HLS PIPELINE II=1 // line-buffer fillif(col < width)

for(unsigned char ii = 0; ii < FILTER_SIZE-1; ii++){

right_col[ii]=line_buffer[ii][col]=line_buffer[ii+1][col];}

if((col < width) && (row < height)){

pix_t pix = inp_img[row*MAX_WIDTH+col];right_col[FILTER_SIZE-1] = line_buffer[FILTER_SIZE-1][col] = pix;

}

//Shift from left to right the sliding window to make room for the new columnfor(unsigned char ii = 0; ii < FILTER_SIZE; ii++)

for(unsigned char jj = 0; jj < FILTER_SIZE-1; jj++)window[ii][jj] = window[ii][jj+1];

for(unsigned char ii = 0; ii < FILTER_SIZE; ii++)window[ii][FILTER_SIZE-1] = right_col[ii];

//This design assumes there are no edges on the boundary of the imageif ( (row>=FILTER_OFFS) & (col>=FILTER_OFFS) & (row<height) & (col<width) )

filt_out = filter_kernel_operator(window);else

filt_out = 0;

if ( (row>=FILTER_OFFS) & (col>=FILTER_OFFS) & (row<height) & (col<width) )out_img[(row-FILTER_OFFS)*MAX_WIDTH+(col-FILTER_OFFS)] = (filt_out);

} // end of L2} // end of L1

} // end of function

Page 12: Vivado HLS を使用する Lucas-Kanade オプティカル フロー …

HLS 用の C++ モデル

XAPP1300 (v1.0) 2017 年 2 月 3 日  12japan.xilinx.com

• MAX_WIDTH および MAX_LINES は、 列と行の観点での画像解像度です。

• スライディング ウ ィンド ウは、 ARRAY_PARTITION dim=0 HLS 指示子によって別々のレジスタに分割された、 サイズ FILTER_SIZE の 2D 正方形配列ウ ィンド ウです。

• ラ イン バッファーは、 ARRAY_PARTITION dim=1 HLS 指示子によって FLTER_SIZE の別々のブロッ ク RAM に分割された、 ラインあたり MAX_WIDTH 画素の FILTER_SIZE ラインを含む 2D 矩形配列です。 これによ り、 各画像ラインがその固有のデュアル ポート ブロ ッ ク RAM に保存されます。

注記:読みやすさを向上させ、プログラ ミ ングを容易にするために C/C++ コードが FILTER_SIZE 個のラインを適用している場合でも、 Vivado HLS コンパイラは、ハード ウェア デザインでは FILTER_SIZE-1 個のラインのみ実装します。

• この処理スタイルでは、 入力画像と出力画像の間で FILTER_OFFS 行と FILTER_OFFS 画素に垂直および水平遅延がそれぞれ生じます (それ以外のすべての操作に必要な処理遅延を除く )。 そのため、 画像解像度の値を超える同じ値で L1 および L2 FOR ループを拡張する必要があ り ます。 これは、 出力画像の最初と最後の FILTER_OFFS 個の列および行に値 0 が埋め込まれるためです。 よ り高度な画像処理では、 この値 0 の埋め込みを避けるためにミ ラーリ ングを使用して入力画像解像度を上げます。 ただし、 これはこのアプリ ケーシ ョ ン ノートでは不要であ り、 C/C++ モデルでは簡単に実装できます。

• ラ イン バッファーにデータを格納する際に、 入力画素の右端の入力列が 1D 配列 right_col に格納され、 2D 配列のウ ィンド ウを右 (インデッ クス jj+1) から左 (インデッ クス jj) に列ごとに更新して左から右へのスライ ドをエ ミ ュレートするために使用されます。

hls_twoIsotropicFilters()

このルーチンは、 最も内部の hls_isotropic_kernel() サブルーチンを使用して、 2D の 5x5 カーネルで 2 つの画像イ メージをスムーズにフ ィルタ リ ングします。 その構造は、 「HLS による C/C++ での 2D たたみ込みフ ィルターの効果的なインプリメンテーシ ョ ン」 で リ ス ト されているものと同じです。 主な違いは、 理論上必要なブロ ッ ク RAM を約 50% 削減するコード スタイルが採用されているこ とです。 画像ライン全体を 2 つのブロ ッ ク RAM エレ メン トに格納できるよ うにするためにラインあたり 12 BITS_PER_PIXEL および 1920 MAX_WIDTH 画素であるこ とを想定する と、 画像 I1 の 4 つの行を格納するために 8 つのブロ ッ ク RAM が必要とな り、 画像 I2 のために 8 つのブロッ ク RAM が必要です (合計 16 ブロ ッ ク RAM)。 ザイ リ ンクスのブロ ッ ク RAM が 1024 ワード数で構成されている場合、 その最大幅は 18 ビッ トであるため、 2 つの 12 ビッ ト画素を 24 ビッ トのワードにパッキングする と、 ブロ ッ ク RAM の 25% が節約されます。 その結果、 2 つの入力画像のフ ィルタ リ ングに必要なブロ ッ ク RAM の合計は 16 ではなく 12 になり ます。

次の C/C++ コード フラグメン トに示すよ うに、 2 つの 12 ビッ ト画素のパッキングは、 可変サイズの整数データ型の ap_int C++ ク ラスからの HLS 範囲演算子を使用して行われます (ap_int.h ヘッダー ファ イルを含める必要がある )。

Page 13: Vivado HLS を使用する Lucas-Kanade オプティカル フロー …

HLS 用の C++ モデル

XAPP1300 (v1.0) 2017 年 2 月 3 日  13japan.xilinx.com

HLS C++ 範囲演算子を使用した 2 つの画素の大きいワードへのパッキング

#include "ap_int.h" // HLS arbitrary width integer data typestypedef ap_uint<BITS_PER_PIXEL > pix_t; // input pixeltypedef ap_int<BITS_PER_PIXEL*2> dualpix_t;// to pack 2 pixels

void hls_twoIsotropicFilters( . . . ){unsigned short int row, col;pix_t filt_out1, filt_out2, pix1, pix2;dualpix_t two_pixels;

dualpix_t pixels[FILTER_SIZE]; #pragma HLS ARRAY_PARTITION variable=pixels complete dim=0

dualpix_t window[FILTER_SIZE*FILTER_SIZE];#pragma HLS ARRAY_PARTITION variable=window complete dim=0

static dualpix_t lpf_lines_buffer[FILTER_SIZE][MAX_WIDTH];#pragma HLS ARRAY_PARTITION variable=lpf_lines_buffer complete dim=1

// effective filteringL1: for(row = 0; row < height+FILTER_OFFS; row++)

{#pragma HLS LOOP_TRIPCOUNT min=hls_MIN_H max=hls_MAX_HL2: for(col = 0; col < width+FILTER_OFFS; col++)

{#pragma HLS PIPELINE#pragma HLS LOOP_TRIPCOUNT min=hls_MIN_W max=hls_MAX_W

// Line Buffer fillif(col < width)for(unsigned char ii = 0; ii < FILTER_SIZE-1; ii++)

{ pixels[ii]=lpf_lines_buffer[ii][col]= lpf_lines_buffer[ii+1][col]; }

//There is an offset to accomodate the active pixel regionif((col < width) && (row < height)){

pix1 = (pix_t) inp1_img[row*MAX_WIDTH+col];pix2 = (pix_t) inp2_img[row*MAX_WIDTH+col];two_pixels.range(2*BITS_PER_PIXEL-1, BITS_PER_PIXEL) = pix2;two_pixels.range( BITS_PER_PIXEL-1, 0) = pix1;pixels[FILTER_SIZE-1]=lpf_lines_buffer[FILTER_SIZE-1][col]=two_pixels;

}

//Shift right the processing window to make room for the new columnL3:for(unsigned char ii = 0; ii < FILTER_SIZE; ii++)

L4:for(unsigned char jj = 0; jj < FILTER_SIZE-1; jj++){window[ii*FILTER_SIZE+jj]=window[ii*FILTER_SIZE+jj+1];

}L5:for(unsigned char ii = 0; ii < FILTER_SIZE; ii++){

window[ii*FILTER_SIZE+FILTER_SIZE-1] = pixels[ii];}

//This design assumes there are no edges on the boundary of the imageif ((row>=FILTER_OFFS)&(col>=FILTER_OFFS)&(row<height)&(col< width)){

two_pixels = hls_isotropic_kernel(window);filt_out1 = two_pixels.range( BITS_PER_PIXEL-1, 0);filt_out2 = two_pixels.range(2*BITS_PER_PIXEL-1, BITS_PER_PIXEL);

}else { filt_out1 = 0; filt_out2 = 0; }

if ( (row >= FILTER_OFFS) & (col >= FILTER_OFFS)) {

Page 14: Vivado HLS を使用する Lucas-Kanade オプティカル フロー …

HLS 用の C++ モデル

XAPP1300 (v1.0) 2017 年 2 月 3 日  14japan.xilinx.com

out1_img[(row-FILTER_OFFS)*MAX_WIDTH+(col-FILTER_OFFS)] = filt_out1;out2_img[(row-FILTER_OFFS)*MAX_WIDTH+(col-FILTER_OFFS)] = filt_out2;

}

} // end of L2} // end of L1

}

次の C/C++ コード フラグメン トに示すよ うに、 hls_isotropic_kernel() サブルーチンは 2D たたみ込みを実行します。 5x5 係数配列には、 MathWorks 社の資料 『opticalFlowLK class』 [参照 8] で推奨されている値が入っています。

hls_isotropic_kernel() ルーチン C/C++ コード  フラグメン トの 2D たたみ込み 

const coe_t coeff[FILTER_SIZE][FILTER_SIZE] = {{ 1, 4, 6, 4, 1},{ 4, 16, 24, 16, 4},{ 6, 24, 36, 24, 6},{ 4, 16, 24, 16, 4},{ 1, 4, 6, 4, 1}

};// local variablesint accum1 = 0, accum2 = 0;int normalized_accum1, normalized_accum2;pix_t pix1, pix2, final_val1, final_val2;dualpix_t two_pixels;

//Compute the 2D convolutionL1:for (i = 0; i < FILTER_SIZE; i++) {L2:for (j = 0; j < FILTER_SIZE; j++) { two_pixels = window[i][j];

pix1 = two_pixels.range( BITS_PER_PIXEL-1, 0);pix2 = two_pixels.range(2*BITS_PER_PIXEL-1, BITS_PER_PIXEL);accum1 = accum1 + ((short int) pix1 * (short int) coeff[i][j]);accum2 = accum2 + ((short int) pix2 * (short int) coeff[i][j]);

} // end of L2} // end of L1// do the correct normalization if needednormalized_accum1 = accum1 / 256;normalized_accum2 = accum2 / 256;final_val1 = (pix_t) normalized_accum1;final_val2 = (pix_t) normalized_accum2;

hls_SpatialTemporalDerivatives 

このルーチンは、 2D たたみ込みカーネル サイズ 5x5 を使用して、空間的導関数 Ix および Iy を実行します。時間的導関数 It は、 2 つの入力画像間の誤差です。 コードの構造は前のルーチンに類似しています。 8 ビッ ト画素の場合、 導関数は 9 ビッ ト サンプルです。 Ix と Iy は 18 ビッ トの大きいワードにパッキングされ、 ライン バッファーに格納されます。「hls_derivatives_kernel() の 2D たたみ込み」 に示されるよ うに、 hls_derivative_kernel() は最も内部のサブルーチン構造で、MathWorks 社の資料 『opticalFlowLK class』 [参照 8] で推奨されている係数値を適用します。 固定の係数カーネル サイズを使用する場合、 twoIsotropicFilters() ルーチンと SpatialTemporalDerivatives() ルーチンのFPGA リ ソース使用率は、 画像解像度に関係なくほぼ一定です。 画像ラインの有効幅に応じてブロ ッ ク RAM の数のみが変化します。

Page 15: Vivado HLS を使用する Lucas-Kanade オプティカル フロー …

HLS 用の C++ モデル

XAPP1300 (v1.0) 2017 年 2 月 3 日  15japan.xilinx.com

hls_derivatives_kernel() の 2D たたみ込み

typedef ap_int< BITS_PER_PIXEL+1> flt_t; // for the 3 derivatives

// derivative filter in a 5x5 kernel size: [-1 8 0 -8 1]// coefficients are swapped to get same results as MATLABconst coe_t y_coeff[FILTER_SIZE][FILTER_SIZE] = { { 0, 0, 1, 0, 0},{ 0, 0, -8, 0, 0}, { 0, 0, 0, 0, 0}, { 0, 0, 8, 0, 0}, { 0, 0, -1, 0, 0} }; const coe_t x_coeff[FILTER_SIZE][FILTER_SIZE] = { { 0, 0, 0, 0, 0},{ 0, 0, 0, 0, 0}, { 1, -8, 0, 8, -1}, { 0, 0, 0, 0, 0}, { 0, 0, 0, 0, 0} };

int accum_x = 0, accum_y = 0;dualpix_t two_pix;pix_t pix1, pix2;int normalized_accum_x, normalized_accum_y;flt_t final_val_x, final_val_y, final_val_t;

//Compute the 2D convolutionL1:for (i = 0; i < FILTER_SIZE; i++)

{L2:for (j = 0; j < FILTER_SIZE; j++) {

two_pix = window[i][j]; pix1 = two_pix.range(2*BITS_PER_PIXEL-1, BITS_PER_PIXEL);pix2 = two_pix.range( BITS_PER_PIXEL-1, 0);

signed short int loc_mult_x = ( pix1 * x_coeff[i][j]); accum_x = accum_x + loc_mult_x;signed short int loc_mult_y = ( pix1 * y_coeff[i][j]); accum_y = accum_y + loc_mult_y;

if ( (i==2)&(j==2) )final_val_t = pix2 - pix1; //central pix is window[2][2]

} // end of L2} // end of L1

// do the correct normalizationnormalized_accum_x = accum_x / 12;normalized_accum_y = accum_y / 12;final_val_x = (flt_t) normalized_accum_x;final_val_y = (flt_t) normalized_accum_y;Ix = final_val_x;Iy = final_val_y;

It = final_val_t;

Page 16: Vivado HLS を使用する Lucas-Kanade オプティカル フロー …

HLS 用の C++ モデル

XAPP1300 (v1.0) 2017 年 2 月 3 日  16japan.xilinx.com

hls_ComputeIntegrals() 

このモジュールは、 式 3 の 5 つのパラ メーターを計算します。 こ こでは、 サイズ NxN (N は 7 ~ 53) のウ ィンド ウ内の空間的導関数の 2 乗値を統合するこ とで、 C/C++ コード変数の名前と さ らに一致するよ うに書き換えられています。

式 6

これは、 FPGA リ ソースの観点では最も重要なルーチンです。 NxN の統合ウ ィンド ウ サイズのモジュールでは、 N–1 行をバッファーに入れ (1920 サンプルの画像幅の場合、 各行は 2 つのブロ ッ ク RAM を使用)、 画素ごとに 5xN2 の積和 (MAC) 演算を行う必要があ り ます。 ただし、 MAC 演算の多くは冗長であ り、 この冗長性を利用したり、 データ ス ト レージを最小化したりするよ うにコードを構成できます。 次に具体例を示します (例と して、 8 ビッ ト /画素で N = 53 サイズのスライディング ウ ィンド ウを想定)。

• ラ イン バッファーに、 大きい 27 ビッ ト ワードにパッキングされた 3 つの導関数を格納します。 この手法によ り、 深さの観点 (格納されるワードの数) でのブロ ッ ク RAM と、 ワードあた りのビッ ト数を最も効率的に使用できます。

• 5 つの 2 次モメンタム Ixx Iyy Ixy Itx Ity はオンザフライで計算され、 スライディング ウ ィンド ウの同じ右端の列で累積されます。 それぞれの最終累積結果は 30 ビッ ト変数に格納されます (9x9 ビッ トの乗算に 18 ビッ トが必要とな り、同じ垂直列の 53x53 の累積に 12 ビッ トが必要)。 その後、 5 つの結果は 5x30 = 150 ビッ トの非常に大きいワードにパッキングされます。

• 53x53 のスライディング ウ ィンド ウ全体を最も内部のルーチン hls_tyx_integration_kernel() に渡す代わりに、 スライディング ウ ィンド ウの最新の右端の入力列のみが渡されます。 最も内部のルーチンはこれらの列の合計を内部で保存します。 さ らに、 新しい列の合計を加算し、 左端の古い列の合計を減算するこ とによ り、 古い出力から新しい出力を計算します。 右端の列の N = 53 の部分累積 (スライディング ウ ィンド ウが存在した場合) は、hls_tyx_integration_kernel() を呼び出す前に既に計算されています。 つま り、 スライディング ウ ィンド ウは N 個の値のスライディング ス ト ラ イプになっており、 各値には供給元の列の部分累積が含まれています。 この手法を使用する と、 画素あたりの MAC 演算の数が 5xN2 から 5x(N+2) に減り ます。 5 は、 式 6 のパラ メーターの数です。 N は列に含まれるエレ メン トの数です。 2 は右端の列を加算し、 左端の列を減算するこ とを表します。 図 10 は、 5x5 の小さい画像と、 3x3 の統合ウ ィンド ウ領域を使用してこの概念を表しています。 スライディング ス ト ラ イプは、 ウ ィンド ウ列ごとの列の合計を保持します。 ウ ィンド ウがある位置の左から右にスライ ドする と、 右の新しい列の合計が計算され、 ウ ィンド ウの総数は以前の合計に右の列の合計 (R) を加算し、 左端の列の合計 (L) を減算したものになり ます。

• 式 6 の 5 つのパラ メーターは、 スライディング ウ ィンド ウのサイズ (NxN) で正規化する必要があ り ます。 この正規化には浮動小数点の分割が必要とな り、 FPGA リ ソースの観点では負担になり ます。 式 6 の数値解法は、 正規化を行うかど うかにかかわらず同じであるため、 FPGA リ ソースの節約のために正規化は実装されません。

A11 A12

A12 A22

vxvy

B0

B1–=

Page 17: Vivado HLS を使用する Lucas-Kanade オプティカル フロー …

HLS 用の C++ モデル

XAPP1300 (v1.0) 2017 年 2 月 3 日  17japan.xilinx.com

hls_ComputeIntegrals() と hls_txy_integration_kernel() ルーチンの C/C++ コード フラグメン ト を次に示します。

hls_ComputeIntegrals() ルーチンの C/C++ コード  フラグメン ト

typedef ap_uint<3*(BITS_PER_PIXEL+1)> p3dtyx_t; // to pack 3 local derivativestypedef ap_int< 2*(BITS_PER_PIXEL+1)> sqflt_t; // for 2nd order momenta#define W_VSUM (2*(BITS_PER_PIXEL+1)+ACC_BITS)typedef ap_int<W_VSUM> vsum_t; // for the accumulators of integrals computationtypedef ap_uint<5*W_VSUM> p5sqflt_t;// for 5 packed squared values of W_SUM bits

sum_t a11, a12, a22, b1, b2;flt_t x_der, y_der, t_der;sqflt_t Ixx, Iyy, Ixy, Itx, Ity;p3dtyx_t three_data;sum_t sum_Ixx, sum_Ixy, sum_Iyy, sum_Itx, sum_Ityp5sqflt_t packed5_last_column, five_sqdata;

p3dtyx_t packed3_column[WINDOW_SIZE];static p3dtyx_t packed3_lines_buffer[WINDOW_SIZE][MAX_WIDTH];#pragma HLS ARRAY_PARTITION variable=packed3_lines_buffer complete dim=1L1: for(row = 0; row < height+WINDOW_OFFS; row++){L2: for(col = 0; col < width +WINDOW_OFFS; col++){#pragma HLS PIPELINE

// lines-buffer fillif(col < width)

for(unsigned char ii = 0; ii < WINDOW_SIZE-1; ii++){

packed3_column[ii] = packed3_lines_buffer[ii][col] =packed3_lines_buffer[ii+1][col];

}if((col < width) & (row < height)){

x_der = Ix_img[row*MAX_WIDTH+col];y_der = Iy_img[row*MAX_WIDTH+col];t_der = It_img[row*MAX_WIDTH+col];//pack data for the lines-bufferthree_data.range( (BITS_PER_PIXEL+1)-1, 0) = x_der;three_data.range(2*(BITS_PER_PIXEL+1)-1, (BITS_PER_PIXEL+1)) = y_der;

X-Ref Target - Figure 10

図 10:右端の入力列のみ加算して古い左端の列を減算することによる、ComputeIntegrals の最適化

X18182-012017

Page 18: Vivado HLS を使用する Lucas-Kanade オプティカル フロー …

HLS 用の C++ モデル

XAPP1300 (v1.0) 2017 年 2 月 3 日  18japan.xilinx.com

three_data.range(3*(BITS_PER_PIXEL+1)-1, 2*(BITS_PER_PIXEL+1)) = t_der;packed3_column[WINDOW_SIZE-1] = packed3_lines_buffer[WINDOW_SIZE-1][col]= three_data;

}// compute the new, incoming columnsum_Ixx=0; sum_Iyy=0; sum_Ixy=0; sum_Itx=0; sum_Ity=0; L3:for(unsigned char ii = 0; ii < WINDOW_SIZE; ii++){

#pragma HLS PIPELINEthree_data = packed3_column[ii];x_der = three_data.range( (BITS_PER_PIXEL+1)-1, 0);y_der = three_data.range (2*(BITS_PER_PIXEL+1)-1, (BITS_PER_PIXEL+1));t_der = three_data.range (3*(BITS_PER_PIXEL+1)-1, 2*(BITS_PER_PIXEL+1));Ixx = (sqflt_t) x_der * (sqflt_t) x_der; // 2nd order momentumIyy = (sqflt_t) y_der * (sqflt_t) y_der; // 2nd order momentumIxy = (sqflt_t) x_der * (sqflt_t) y_der; // 2nd order momentumItx = (sqflt_t) t_der * (sqflt_t) x_der; // 2nd order momentumIty = (sqflt_t) t_der * (sqflt_t) y_der; // 2nd order momentumsum_Ixx += Ixx;sum_Iyy += Iyy;sum_Ixy += Ixy;sum_Itx += Itx;sum_Ity += Ity;

} // end of L3five_sqdata.range( W_VSUM-1, 0) = sum_Ixx; five_sqdata.range(2*W_VSUM-1, W_VSUM) = sum_Iyy;five_sqdata.range(3*W_VSUM-1, 2*W_VSUM) = sum_Ixy;five_sqdata.range(4*W_VSUM-1, 3*W_VSUM) = sum_Itx;five_sqdata.range(5*W_VSUM-1, 4*W_VSUM) = sum_Ity;packed5_last_column = five_sqdata;

hls_tyx_integration_kernel(packed5_last_column, a11, a12, a22, b1, b2);

if ( (row < WINDOW_OFFS)&(col < WINDOW_OFFS)&(row >= height)&(col>= width) ){

a11=0; a12=0; a22=0; b1=0; b2=0;}if ( (row >= WINDOW_OFFS) & (col >= WINDOW_OFFS) ) {

A11_img[(row-WINDOW_OFFS)*MAX_WIDTH+(col-WINDOW_OFFS)] = a11; A12_img[(row-WINDOW_OFFS)*MAX_WIDTH+(col-WINDOW_OFFS)] = a12; A22_img[(row-WINDOW_OFFS)*MAX_WIDTH+(col-WINDOW_OFFS)] = a22;

B1_img[(row-WINDOW_OFFS)*MAX_WIDTH+(col-WINDOW_OFFS)] = b1; B2_img[(row-WINDOW_OFFS)*MAX_WIDTH+(col-WINDOW_OFFS)] = b2;

}} // end of L2} // end of L1} // end of function

Page 19: Vivado HLS を使用する Lucas-Kanade オプティカル フロー …

HLS 用の C++ モデル

XAPP1300 (v1.0) 2017 年 2 月 3 日  19japan.xilinx.com

hls_txy_integration_kernel() ルーチンの C/C++ Code コード  フラグメン ト

void hls_tyx_integration_kernel(p5sqflt_t packed5_last_column,sum_t &a11, sum_t &a12, sum_t &a22, sum_t &b1, sum_t &b2)

{

static p5sqflt_t packed5_window[WINDOW_SIZE];#pragma HLS ARRAY_PARTITION variable=packed5_window complete dim=1

// local accumulatorsstatic sum_t sum_Ixx, sum_Ixy, sum_Iyy, sum_Ity, sum_Itx;

vsum_t sum_xx, sum_xy, sum_yy, sum_tx, sum_ty;p5sqflt_t five_sqdata, packed5_first_column;

//Shift right the sliding window to make room for the new columnpacked5_first_column = packed5_window[0];L0:for(unsigned char jj = 0; jj < WINDOW_SIZE-1; jj++){

packed5_window[jj] = packed5_window[jj+1];}packed5_window[WINDOW_SIZE-1] = packed5_last_column;

//Compute the 2D integration//add incoming right-most column five_sqdata = packed5_window[WINDOW_SIZE-1];sum_xx = five_sqdata.range( W_VSUM-1, 0);sum_yy = five_sqdata.range(2*W_VSUM-1, W_VSUM);sum_xy = five_sqdata.range(3*W_VSUM-1, 2*W_VSUM);sum_tx = five_sqdata.range(4*W_VSUM-1, 3*W_VSUM);sum_ty = five_sqdata.range(5*W_VSUM-1, 4*W_VSUM);sum_Ixx += sum_xx; sum_Ixy += sum_xy;sum_Iyy += sum_yy; sum_Ity += sum_ty;sum_Itx += sum_tx;

//remove older left-most columnfive_sqdata = packed5_first_column;sum_xx = five_sqdata.range( W_VSUM-1, 0);sum_yy = five_sqdata.range(2*W_VSUM-1, W_VSUM);sum_xy = five_sqdata.range(3*W_VSUM-1, 2*W_VSUM);sum_tx = five_sqdata.range(4*W_VSUM-1, 3*W_VSUM);sum_ty = five_sqdata.range(5*W_VSUM-1, 4*W_VSUM);sum_Ixx -= sum_xx; sum_Ixy -= sum_xy;sum_Iyy -= sum_yy; sum_Ity -= sum_ty;sum_Itx -= sum_tx;

a11 = sum_Ixx; a12 = sum_Ixy; a22 = sum_Iyy; b1 = sum_Itx; b2 = sum_Ity;}

Page 20: Vivado HLS を使用する Lucas-Kanade オプティカル フロー …

HLS 用の C++ モデル

XAPP1300 (v1.0) 2017 年 2 月 3 日  20japan.xilinx.com

hls_ComputeVectors() 

式 7 に示すよ うに、 この関数は最も内部の hls_matrix_inversion() ルーチンを使用して 式 6 を解く こ とによ り、 vx ベク トル成分と vy ベク トル成分を計算します。 これは、 LK オプティカル フロー デザインで浮動小数点演算を使用する唯一の関数です。

注記: detA がヌルの場合または特定のしきい値に満たない場合、 2x2 行列を逆にするこ とはできません。

行列の要素には大きい値があるため (8 ヒ ッ トの入力画素で 53x53 の統合ウ ィンド ウの場合は 30 ビッ ト )、 しきい値は 1.0 および ComputeIntegrals() によって生成される正規化されたデータになり ます。 ただし、 この最後のルーチンには、 統合ウ ィンド ウ サイズごとの正規化があ り ません。 したがって、 埋め合わせるために、 実際のしきい値は 1.0 x WINDOW_SIZE x WINDOW_SIZE になり ます。

その後、 最後の動きベク トル成分が 8 倍され、 16 ビッ ト値に切り捨てられます。 そのうちの 3 ビッ トはサブ画素精度を表し (このデザインでは SUBPIX_BITS = 3)、 1/8 画素を意味します。

式 7

次に示すのは、 上記の 2 つのルーチンで最も重要な C/C++ コード フラグメン トです。

ComputeVectors() ルーチンの C/C++ コード  フラグメン ト

typedef ap_int<2*W_VSUM> sum2_t; // for matrix inversiontypedef ap_int<2*W_VSUM+3> det_t; // for determinant in matrix inversion

float Vx, Vy; signed short int qVx, qVy; A[0][0] = A11_img[(row)*MAX_WIDTH+(col)];//a11A[0][1] = A12_img[(row)*MAX_WIDTH+(col)]; //a12; A[1][0] = A[0][1]; //a21A[1][1] = A22_img[(row)*MAX_WIDTH+(col)]; //a22;B[0] = B1_img[(row)*MAX_WIDTH+(col)]; //b1B[1] = B2_img[(row)*MAX_WIDTH+(col)]; //b2

bool invertible = hls_matrix_inversion(A, B, THRESHOLD, Vx, Vy);cnt = cnt + ((int) invertible); //number of invertible points found

//quantize motion vectorsout_vx[(row)*MAX_WIDTH+(col)] = (signed short int ) (Vx *(1<<SUBPIX_BITS));out_vy[(row)*MAX_WIDTH+(col)] = (signed short int ) (Vy *(1<<SUBPIX_BITS));

vxvy

A22 A12–

A12– A11

B0

B1

1det A‐‐‐‐‐‐‐‐‐‐‐‐‐‐ =

det A A11 A22 A12– A12 =

Page 21: Vivado HLS を使用する Lucas-Kanade オプティカル フロー …

HLS 用の C++ モデル

XAPP1300 (v1.0) 2017 年 2 月 3 日  21japan.xilinx.com

hls_matrix_inversion() ルーチンの C/C++ コード  フラグメン ト

bool hls_matrix_inversion(sum_t A[2][2], sum_t B[2], int threshold, float &Vx, float &Vy){

bool invertible = 0;sum_t inv_A[2][2], a, b, c, d; det_t det_A, abs_det_A, neg_det_A, zero = 0;float recipr_det_A;

a = A[0][0]; b = A[0][1]; c = A[1][0]; d = A[1][1];

sum2_t a_x_d, b_x_c, mult1, mult2, mult3, mult4;det_t t_Vx, t_Vy;

a_x_d = (sum2_t) a * (sum2_t) d;b_x_c = (sum2_t) b * (sum2_t) c;det_A = a_x_d - b_x_c;neg_det_A = (zero-det_A);abs_det_A = (det_A > zero)? det_A : neg_det_A;recipr_det_A = (1.0f)/det_A;

//compute the inverse of matrix A anywayif (det_A == 0) recipr_det_A = 0;inv_A[0][0] = d;inv_A[0][1] = -b; inv_A[1][0] = -c; inv_A[1][1] = a;

mult1 = (sum2_t) inv_A[0][0] * (sum2_t) B[0];mult2 = (sum2_t) inv_A[0][1] * (sum2_t) B[1];mult3 = (sum2_t) inv_A[1][0] * (sum2_t) B[0];mult4 = (sum2_t) inv_A[1][1] * (sum2_t) B[1];t_Vx = -(mult1 + mult2);t_Vy = -(mult3 + mult4);Vx = t_Vx * recipr_det_A;Vy = t_Vy * recipr_det_A;

if (det_A == 0) { // zero input pixelsinvertible = 0; Vx = 0; Vy = 0;

}else if (abs_det_A < threshold) { // the matrix is not invertible

invertible = 0; Vx = 0; Vy = 0;}else invertible = 1;

return invertible;}

hls_LK() 

この関数は、 図 1 に示されるものと同じブロ ッ ク図を実装し、 Vivado HLS によって生成される RTL (Verilog または VHDL) で最上位モジュールになり ます。 2 つの入力画像は画素あたり 16 ビッ トに制限されます。 これは、 将来のデザインで画素あたり 8 ビッ ト よ り多く を使用する可能性があるためです。 また、 SDSoC 環境では、 8、 16、 32、 または 64 ビットに調整された ARM® プロセッサのメモ リ サブシステムへのインターフェイスが必要となるためです。 これまでのすべてのデータ タイプは、 HLS の C++ テンプレートに基づき、 入力画素あたりのビッ ト数に応じてビッ ト総数が自動的に増加します。 出力画像には 32 ビッ トのサンプルがあ り ます。 各サンプルは、 2 つの 16 ビッ トの動きベク トル成分 (vx と vy) のパッキング ワードです。 入力画像と出力画像は、 プラグマ HLS INTERFACE 指示子によ り FIFO インターフェイスを持つと宣言されています。

Page 22: Vivado HLS を使用する Lucas-Kanade オプティカル フロー …

HLS 用の C++ モデル

XAPP1300 (v1.0) 2017 年 2 月 3 日  22japan.xilinx.com

10 個のローカル 2D 配列は、プラグマ HLS STREAM 指示子によって 10 個の (HLS_STREAM_DEPTH) レジスタの深さのスト リームに変換されます。 4 つの内部ルーチンは、 プラグマ HLS DATAFLOW 指示子によって並列処理に変換され、 各スト リームは 1 回限り生成/使用されます。 「合成される最上位関数 hls_LK() ルーチンの C/C++ コード フラグメン ト 」 に C/C++ コード フラグメン ト を示します。

合成される最上位関数 hls_LK() ルーチンの C/C++ コード  フラグメン ト

const int HLS_STREAM_DEPTH = 10;

int hls_LK(unsigned short int inp1_img[MAX_HEIGHT*MAX_WIDTH], unsigned short int inp2_img[MAX_HEIGHT*MAX_WIDTH], signed short int out_vx[MAX_HEIGHT*MAX_WIDTH], signed short int out_vy[MAX_HEIGHT*MAX_WIDTH], unsigned short int height, unsigned short int width){

#pragma HLS INTERFACE ap_fifo port=inp1_img#pragma HLS INTERFACE ap_fifo port=inp2_img#pragma HLS INTERFACE ap_fifo port=out_Vxy

#pragma HLS DATAFLOW

sum_t A11_img[MAX_HEIGHT*MAX_WIDTH];sum_t A12_img[MAX_HEIGHT*MAX_WIDTH];sum_t A22_img[MAX_HEIGHT*MAX_WIDTH];sum_t B1_img[MAX_HEIGHT*MAX_WIDTH];sum_t B2_img[MAX_HEIGHT*MAX_WIDTH];flt_t Dx1_img[MAX_HEIGHT*MAX_WIDTH]; // horizontal derivativeflt_t Dy1_img[MAX_HEIGHT*MAX_WIDTH]; // vertical derivativeflt_t Dt_img[MAX_HEIGHT*MAX_WIDTH]; // temporal derivativepix_t flt1_img[MAX_HEIGHT*MAX_WIDTH]; // filtered imagespix_t flt2_img[MAX_HEIGHT*MAX_WIDTH];

#pragma HLS STREAM variable=A11_img depth=HLS_STREAM_DEPTH#pragma HLS STREAM variable=A12_img depth=HLS_STREAM_DEPTH#pragma HLS STREAM variable=A22_img depth=HLS_STREAM_DEPTH#pragma HLS STREAM variable=B1_img depth=HLS_STREAM_DEPTH#pragma HLS STREAM variable=B2_img depth=HLS_STREAM_DEPTH#pragma HLS STREAM variable=Dx1_img depth=HLS_STREAM_DEPTH#pragma HLS STREAM variable=Dy1_img depth=HLS_STREAM_DEPTH#pragma HLS STREAM variable=Dt_img depth=HLS_STREAM_DEPTH#pragma HLS STREAM variable=flt1_img depth=HLS_STREAM_DEPTH#pragma HLS STREAM variable=flt2_img depth=HLS_STREAM_DEPTH

// smooth both images with same 2D filter kernelhls_twoIsotropicFilters(inp1_img, inp2_img, flt1_img, flt2_img, height, width);//compute horizontal & vertical derivatives of image 1, plus temporal derivativehls_SpatialTemporalDerivatives(flt1_img, flt2_img, Dx1_img, Dy1_img, Dt_img, height, width);// compute integrals of second order momenta Ixx, Ixy, Iyy, Itx, Ityhls_ComputeIntegrals(Dx1_img, Dy1_img, Dt_img, A11_img, A12_img, A22_img, B1_img, B2_img, height, width);// compute vectorsint cnt = hls_ComputeVectors(A11_img, A12_img, A22_img, B1_img, B2_img, out_vx, out_vy, height, width);

return cnt;}

Page 23: Vivado HLS を使用する Lucas-Kanade オプティカル フロー …

HLS デザイン  パフォーマンス

XAPP1300 (v1.0) 2017 年 2 月 3 日  23japan.xilinx.com

HLS デザイン  パフォーマンス

図 11 は、 統合ウ ィンド ウのサイズを (左から右に) 7x7 (8 ビッ ト サンプルを使用)、 11x11 (8 ビッ ト サンプルを使用)、11x11 (12 ビッ ト サンプルを使用)、 53x53 (8 ビッ ト サンプルを使用) に増やす場合に、 HLS で得られるさまざまなパフォーマンスの見積も り を示しています(ターゲッ ト デバイスは Zynq 7045-2)。

X-Ref Target - Figure 11

図 11:統合ウィンドウのサイズを増やした場合の HLS による使用率の見積もり

X18065-012017

Page 24: Vivado HLS を使用する Lucas-Kanade オプティカル フロー …

HLS デザイン  パフォーマンス

XAPP1300 (v1.0) 2017 年 2 月 3 日  24japan.xilinx.com

表 1 と表 2 に、 配置配線 (PAR) 後のインプ リ メンテーシ ョ ンの結果を示します。 表 1 は、 FPGA リ ソースの観点から見たパフォーマンス と達成されたフレーム レート を要約しています。 特に統合ウ ィンド ウのサイズが 53x53 の場合、 結果は BRAM18K が 172、 DSP48 が 307、 FF が 9881、 LUT が 7001 で、 フレーム レートは約 121Hz です (C/RTL コシ ミ ュレーシ ョ ン中に測定された有効なクロ ッ ク周期が 3.976ns、 レイテンシが 2073697 ク ロ ッ ク サイクルの場合)。 表 1 では、 12 ビッ ト画素の入力画像が影付きの行で示されています。 その他の行は 8 ビッ トの入力サンプルを表します。

アプリ ケーシ ョ ンごとに異なる統合ウ ィンド ウ サイズが必要になる場合があ り ます。 このセクシ ョ ンでは、 53x53 の統合ウ ィンド ウ サイズが最も難易度が高いデザインであるため、 これを例と して使用します。 表 2 は、 ComputeIntegrals() ルーチン内で乗算を実装するために、 DSP48 リ ソースを使用する場合と FF/LUT リ ソースを使用する場合の考えられる トレードオフを示しています。 HLS RESOURCE 指示子を 「hls_ComputeIntegrals() ルーチンの C/C++ コード フラグメン ト 」 の 5 つの変数 Ixx、 Iyy、 Ixy、 Itx、 Ity のどれにも追加しないか、 またはすべてに追加するこ とによ り、 DSP48 リ ソースの数を 307 (DSP48 スライスで 5 つの乗算を行う場合) から 197 (DSP48 スライスで 4 つの乗算を行う場合) または 38 (DSP48 で乗算を行わない場合) に減らすこ とができます。 その結果、 FF/LUT リ ソースの数は 9881/7001 からそれぞれ 16044/17779 または 22160/32476 に増えます。 これらの結果のどれが最善であるかは、 アーキテクチャを考慮して選択します。 この選択は統合ウ ィンド ウのサイズと同様に、 システム レベルのその他のデザイン要因 (ほかの機能を実行するために同じターゲッ ト デバイスで使用可能な リ ソースの数や、 必要な作業フレーム レート など) によって決ま り ます。 「HLS リ ソース指示子による LUT ベースの乗算器への変数の割り当て」 の C/C++ コード フラグメン トは、 5 つの乗算をすべて LUT ベースの演算子にするために、 HLS RESOURCE 指示子がどのよ うに適用されるのかを示しています。

表 1:統合ウィンドウのサイズを増やした場合の (配置配線後の) HLS インプリ メンテーシ ョ ンの結果

概要レイテンシ (サイクル)

CP(ns)

クロック周波数 (MHz)

フレーム レート  (Hz)

BRAM18K DSP48E FF LUT

1920x1080 の解像度 08 ビッ ト 、7x7 ウ ィンド ウ、 Z-7045

2073697 3.65 274.0 132.1 34 63 8076 4898

1920x1080 の解像度 08 ビッ ト 、11x11 ウ ィンド ウ、 Z-7045

2073697 3.48 287.4 138.6 46 97 8574 4948

1920x1080 の解像度 12 ビッ ト 、11x11 ウ ィンド ウ、 Z-7045

2073697 3.561 280.8 135.4 74 97 10147 5832

1920x1080 の解像度 08 ビッ ト 、35x35 ウ ィンド ウ、 Z-7045

2073697 3.909 255.8 123.4 118 217 9490 6372

1920x1080 の解像度 08 ビッ ト 、53x53 ウ ィンド ウ、 Z-7045

2073697 3.976 251.5 121.3 172 307 9881 7001

Page 25: Vivado HLS を使用する Lucas-Kanade オプティカル フロー …

HLS デザイン  パフォーマンス

XAPP1300 (v1.0) 2017 年 2 月 3 日  25japan.xilinx.com

表 2 の最後の 2 行では、 HLS RESOURCE 指示子を 5 つある変数のうち任意のものに適用する代わりに、ComputeIntegrals() 内の乗算演算の総数が 100 または 150 に制限されています。 これによ り、 DSP48 リ ソースの数が 307 から 137 または 187 に減る一方で、FF/LUT リ ソースの数は 13440/10501 (#pragma HLS ALLOCATION インスタンス = 乗算の制限 =100 回の演算) または 14530/10833 (#pragma HLS ALLOCATION インスタンス = 乗算の制限 = 150 回の演算) になり ます。 この最適化手法の主な影響は、 DSP48 リ ソースを減らすこ とだけでなく、 実現可能なスループッ ト を低下させることです。 これによ り、 有効フレーム レートがそれぞれ 41Hz または 61Hz に下がり ます。 ただし、 オプティカル フローを使用する多くの画像処理アプリ ケーシ ョ ン (一般にオートモーティブ) は 15/30Hz を超えるフレーム レートで稼働する必要がないため、 有効なフレーム レートはシステム レベルのデザインの判断要素と して使用します。

表 2 は、 いくつかの指示子を追加または削除するこ とで実現する Vivado HLS デザイン フローの主な利点を示しています。 リ ソースおよびフレーム レートのデザイン要件にどれが最も適しているかによって、 アーキテクチャでのさまざまな選択肢を分析し、 選択できます。

HLS リソース指示子による  LUT ベースの乗算器への変数の割り当て

sqflt_t Ixx, Iyy, Ixy, Itx, Ity;#pragma HLS RESOURCE variable=Ixx core=Mul_LUT #pragma HLS RESOURCE variable=Iyy core=Mul_LUT #pragma HLS RESOURCE variable=Ixy core=Mul_LUT #pragma HLS RESOURCE variable=Itx core=Mul_LUT #pragma HLS RESOURCE variable=Ity core=Mul_LUT

表 3 および表 4 は、最上位のデザインによって呼び出される 4 つのサブルーチンにリ ソースがどのよ うに配分されるかを示しています。 ComputeIntegrals() ルーチンが、 BRAM18K、 DSP48、 および FF 使用率のほとんどを占めています。 表 3 では、 ターゲッ ト デバイスは Zynq 7045-2 で、 すべての乗算が DSP48 スライスに実装され、 フレーム レートは 121Hz です。表 4 では、 ターゲッ ト デバイスは Zynq 7020-1 で、 最大乗算数は 150 に制限され、 フレーム レートは 41Hz です。

表 2:乗算器を実装する場合の DSP48 と  FF/LUT の間のト レードオフ

1920x1080 の解像度 08 ビッ ト、53x53 ウィンドウ、 Z‐7045

レイテンシ (サイクル)

CP(ns)

クロック周波数 (MHz)

フレーム レート  (Hz)

BRAM18K DSP48E FF LUT

ComputeIntegrals: 5 つすべての 9x9 MULS を DSP48 に適用

2073697 3.976 251.5 121.3 172 307 9881 7001

ComputeIntegrals: 5 つのうち 1 つの 9x9 MULS を FF および LUT に適用

2073697 4.084 244.9 118.1 172 197 16044 17779

ComputeIntegrals: 5 つすべての 9x9 MULS を FF および LUT に適用

2073697 4.027 248.3 119.7 172 38 22160 32476

ComputeIntegrals: すべての MUL が 100 に制限 (DSP48 で実行)

6220818 3.849 259.8 41.8 172 137 13440 10501

ComputeIntegrals: すべての MUL が 150 に制限 (DSP48 で実行)

4147219 3.91 255.8 61.7 172 187 14530 10833

Page 26: Vivado HLS を使用する Lucas-Kanade オプティカル フロー …

HLS デザイン  パフォーマンス

XAPP1300 (v1.0) 2017 年 2 月 3 日  26japan.xilinx.com

表 3: Zynq 7045‐2 ターゲッ ト  デバイスのリソース使用率の詳細

1920x1080 解像度 8 ビッ ト、 PAR インプリ メンテーシ ョ ン

機能レイテンシ (サイクル)

CP(ns)

クロック周波数 (MHz)

フレーム レート (Hz)

BRAM18K DSP48E FF LUT

TwoIsotropicFilters 8 1 665 1022

SpatialTemporalDerivatives 8 3 316 356

ComputeIntegrals: 5 つすべての 9x9 MULS を DSP48 に適用

156 266 6804 1881

ComputeVectors 0 37 2060 3436

LK 53x53 統合ウ ィンド ウ 2073697 3.976 251.5 121.3

合計 172 307 9881 7001

Z-7045 の使用可能な リ ソース 1090 900 437200 218600

パーセンテージ (%) 15.78 34.11 2 3

表 4: Zynq 7020‐1 ターゲッ ト  デバイスのリソース使用率の詳細

1920x1080 解像度 8 ビッ ト、 PAR インプリ メンテーシ ョ ン

機能レイテンシ (サイクル)

CP(ns)

クロック周波数 (MHz)

フレーム レート  (Hz)

BRAM18K DSP48E FF LUT

TwoIsotropicFilters 8 1 1351 1022

SpatialTemporalDerivatives 8 3 627 354

ComputeIntegrals: すべての MUL が 150 に制限 (DSP48 で実行)

156 150 13305 6057

ComputeVectors 0 33 3583 3350

LK 53x53 統合ウ ィンド ウ 4147222 5.859 170.7.5 41.2

合計 172 187 18930 11083

Z-7020 の使用可能な リ ソース 280 220 106400 53200

パーセンテージ (%) 61.43 85.00 17.79 20.83

Page 27: Vivado HLS を使用する Lucas-Kanade オプティカル フロー …

ZC706 ボードでの SDSoC デモンスト レーシ ョ ン

XAPP1300 (v1.0) 2017 年 2 月 3 日  27japan.xilinx.com

ZC706 ボードでの SDSoC デモンスト レーシ ョ ン

LK オプティカル フローの HLS デザインを検証する最も容易な方法は、 入力信号がデジタル ビデオ シーケンス ファ イルから発信され、 出力がファイルに書き込まれるよ うに Zynq-7000 AP SoC ボード (ZC702 または ZC706) のいずれかに実装するこ とです。 こ うするこ とで、 LK オプティカル フロー コア自体に集中できるため、 リ アルタイムの高精細度マルチメディア インターフェイス (HDMI™) ビデオ信号や関連するフレーム バッファーを搭載した、 よ り複雑なシステムを設計する必要がなくな り ます。 SDSoC 開発環境では、 Zynq-7000 AP SoC ボード上で I/O デモンス ト レーシ ョ ン ファ イルを設計する作業には数時間しか要しません。 「合成される最上位関数 hls_LK() ルーチンの C/C++ コード フラグメン ト 」 のコード フラグメン トは SDSoC 環境に適しています。 このコード フラグメン トでは、 関数プロ ト タイプの I/O パラ メーターは真の ANSI-C データ型であ り、 ARM CPU のメモ リ空間で 8、 16、 または 32 ビッ トに配置されます ( 『SDSoC 環境ユーザー ガイ ド』 [参照 4] を参照)。

最上位関数プロ ト タイプに適用される SDSoC 指示子 (ヘッダー ファ イル LK_defines.h 内) が 「最上位関数プロ ト タイプに適用される SDSoC 指示子」 に示されています。

最上位関数プロ ト タイプに適用される SDSoC 指示子

#ifdef __SDSCC__#pragma SDS data access_pattern(inp1_img:SEQUENTIAL)#pragma SDS data access_pattern(inp2_img:SEQUENTIAL)#pragma SDS data access_pattern( vx_img:SEQUENTIAL)#pragma SDS data access_pattern( vy_img:SEQUENTIAL)#pragma SDS data copy(inp1_img[0:hls_IMGSZ])#pragma SDS data copy(inp2_img[0:hls_IMGSZ])#pragma SDS data copy( vx_img[0:hls_IMGSZ])#pragma SDS data copy( vy_img[0:hls_IMGSZ])#pragma SDS data sys_port(inp1_img:ACP,inp2_img:ACP,vx_img:ACP,vy_img:ACP)#endifint hls_LK(unsigned short int *inp1_img, unsigned short int *inp2_img,

signed short int *vx_img, signed short int *vy_img, unsigned short int height, unsigned short int width);

• SDS data access_pattern 指示子は、 すべての I/O アレイが順次アクセス パターンを持つよ う指定します。 その結果、SDSoC 環境に FIFO インターフェイスが実装されます。

• SDS data copy 指示子は、 DMA 通信転送の全体ペイロード サイズを指定します。

• SDS data sys_port 指示子は、 FPGA ハードウェア アクセラレータのインターフェイスを ARM メモ リ サブシステムのアクセラレータ コ ヒーレンシ ポート (ACP) に接続します。

注記: __SDSCC__ は、 SDSoC コンパイラにのみ認識され、 ほかのコンパイラ (Vivado HLS コンパイラなど) からは無視される、 ビルド済み C プリプロセッサ マクロです。

HLS または SDSoC 環境向けのセルフチェッ ク関数テス ト ベンチを構成する、main() ルーチンの最も重要な C/C++ コード フラグメン ト を 「SDSoC 関数テス ト ベンチの C/C++ コード フラグメン ト 」 に示します。 事前定義マクロ __SYNTHESIS__ (HLS 環境向け) および __SDSCC__ (SDSoC 環境向け) によ り、 どちらかのコンパイラのみで表示可能なソフ ト ウェアを簡単に作成できます。 これによ り開発時間が短縮されます。

Page 28: Vivado HLS を使用する Lucas-Kanade オプティカル フロー …

ZC706 ボードでの SDSoC デモンスト レーシ ョ ン

XAPP1300 (v1.0) 2017 年 2 月 3 日  28japan.xilinx.com

SDSoC 関数テスト  ベンチの C/C++ コード  フラグメン ト

int main(int argc, char** argv){

unsigned short x, y, width, height;int check_results, ret_res=0, ref_pt, inv_points;unsigned short *inp1_img, *inp2_img; signed short *vx_ref, *vy_ref, *vx_img, *vy_img; // memory allocationvx_ref =( signed short *) malloc(MAX_HEIGHT*MAX_WIDTH*sizeof(signed short));vy_ref =( signed short *) malloc(MAX_HEIGHT*MAX_WIDTH*sizeof(signed short));inp1_img=(unsigned short*)sds_alloc(MAX_HEIGHT*MAX_WIDTH*sizeof(unsigned short));inp2_img=(unsigned short*)sds_alloc(MAX_HEIGHT*MAX_WIDTH*sizeof(unsigned short));vx_img =( signed short*)sds_alloc(MAX_HEIGHT*MAX_WIDTH*sizeof( signed short));vy_img =( signed short*)sds_alloc(MAX_HEIGHT*MAX_WIDTH*sizeof( signed short));

printf("REF design\n");for (int i = 0; i < NUM_TESTS; i++) {sw_sds_clk_start() ref_pt = ref_LK(inp1_img, inp2_img, vx_ref, vy_ref, MAX_HEIGHT, MAX_WIDTH); sw_sds_clk_stop()}printf("num of invertible pt = %d, which represents %2.2f%%\n", ref_pt, (ref_pt*100.0)/(height*width));

printf("HLS DUT\n");for (int i = 0; i < NUM_TESTS; i++) { hw_sds_clk_start() inv_points = hls_LK(inp1_img, inp2_img, vx_img, vy_img, MAX_HEIGHT, MAX_WIDTH); hw_sds_clk_stop() }printf("numb of invertible pt = %d, which represents %2.2f%%\n", inv_points, (inv_points*100.0)/(height*width));sds_print_results()

// self checking test benchprintf("Checking results: REF vs. HLS\n");double diff1, abs_diff1, diff2, abs_diff; check_results = 0;L1:for (y=(WINDOW_OFFS+2*FILTER_OFFS); y < height-(WINDOW_OFFS+2*FILTER_OFFS); y++){L2:for (x=(WINDOW_OFFS+2*FILTER_OFFS); x < width -(WINDOW_OFFS+2*FILTER_OFFS); x++{ int vect1x, vect2x, vect1y, vect2y; vect1x = vx_img[y*MAX_WIDTH + x]; vect2x = vx_ref[y*MAX_WIDTH + x]; vect1y = vy_img[y*MAX_WIDTH + x]; vect2y = vy_ref[y*MAX_WIDTH + x]; diff1 = vect2x - vect1x; diff2 = vect2y - vect1y; abs_diff1 = ABS(diff1); abs_diff2 = ABS(diff2); if (abs_diff1 > 1) { printf("Vx: expected %20.10f got %20.10f\n", (float) vect2x, (float) vect1x);check_results++;

} if (abs_diff2 > 1) { printf("Vy: expected %20.10f got %20.10f\n", (float) vect2y, (float) vect1y);check_results++;

}} // end of L1} // end of L2

printf("Test done\n");

Page 29: Vivado HLS を使用する Lucas-Kanade オプティカル フロー …

ZC706 ボードでの SDSoC デモンスト レーシ ョ ン

XAPP1300 (v1.0) 2017 年 2 月 3 日  29japan.xilinx.com

if (check_results > MAX_NUM_OF_WRONG_VECTORS) { printf("TEST FAILED!: error = %d\n", check_results); ret_res = 1; }else { printf("TEST SUCCESSFUL!\n"); ret_res = 0; }

// free memoryfree(vx_ref); free(vy_ref); sds_free(inp1_img); sds_free(inp2_img); sds_free(vx_img); sds_free(vy_img);

return ret_res;

}

個別に説明する と、 sds_alloc は SDSoC メモ リ アロケータで、 Linux OS の仮想メモリ と物理メモ リの両空間で連続的にページングされる動的メモ リ を生成します。 そのため、 アクセラレータ ポート を ARM メモ リ サブシステム ポート と接続するためにデータ ムーバーと して DMA_SIMPLE が必要です。 DMA_SIMPLE は、 強力なスキャ ッ ター ギャザー DMA (DMA_SG) と比較して、 セッ ト アップの CPU ク ロ ッ ク サイクルと FPGA リ ソースが少なくて済みます。 DMA_SG はペイロード サイズ制限なしに連続ページング仮想メモ リ を非連続ページング物理メモ リにマップできますが、 DMA_SIMPLE には 8MB のペイロード サイズ制限があ り ます。 8MB よ り も大きいデータの移動では、 DMA_SG が SDSoC 環境によ り自動的にインスタンシエート されます。

D キャ ッシュ ミ スを平滑化するために、リ ファレンス関数と DUT (Design Under Test) 関数が NUM_TESTS の回数実行されます。 これによ り、 長いビデオ シーケンスを処理する という現実的なシナリオがエミ ュレート されます。

sw_sds_clock_start()、 sw_sds_clock_stop()、 hw_sds_clock_start()、 および hw_sds_clock_stop() は、 SDSoC 環境の事前定義マクロであ り、 64 ビッ ト ARM パフォーマンス カウンターを呼び出します。 これによ り、 ランタイム時に実行時間が CPU ク ロ ッ ク サイクルで直接測定されます。 これらのマクロは純粋な HLS デザイン フロー中は無視されます。

HLS から SDSoC 環境へのボ ト ムアップ方式では、 HLS C/RTL 協調シ ミ ュレーシ ョ ンを実行して、 HLS によ り生成される RTL が正し く機能しているこ とを (特にスタンドアロン モードでは) 確認するこ とを推奨します。 さ らに、 PAR で HLS インプリ メンテーシ ョ ンを実行する と、 スタンドアロン コアを SDSoC 環境でさ らに大きいデザインに統合する前に、 タイミ ング制約を満たす際に起こ り う る問題を明らかにできます。 ComputeIntegrals() ルーチンでは、 35x35 の統合ウ ィンド ウ サイズと 150 の乗算演算の制限があ り ます (ZC702 または ZC706 のいずれかのボードに適合)。図 12 および図 13 はそれぞれ、 HLS 協調シ ミ ュレーシ ョ ンと インプリ メンテーシ ョ ンのレポートです。 図 14 は、 SDSoC 環境によ り自動的に生成されたエンベデッ ド システムのデータ モーシ ョ ン ネッ ト ワーク レポート を示しています。 図 15 は、 ZC706 ボードでのランタイム時のアプリ ケーシ ョ ン出力を示しています。 150MHz で稼働するハード ウェア アクセラレータは、 800MHz で稼働する ARM CPU 上での純粋なソフ ト ウェア実行よ り も 68 倍高速です。

Page 30: Vivado HLS を使用する Lucas-Kanade オプティカル フロー …

ZC706 ボードでの SDSoC デモンスト レーシ ョ ン

XAPP1300 (v1.0) 2017 年 2 月 3 日  30japan.xilinx.com

X-Ref Target - Figure 12

図 12: 35x35 統合ウィンドウ サイズの HLS 協調シミ ュレーシ ョ ン  レポート  (150 の乗算制限がある)

X-Ref Target - Figure 13

図 13: 35x35 統合ウィンドウ サイズの HLS インプリ メンテーシ ョ ン  レポート  (150 の乗算制限がある)

X18066-012017

X18067-012017

Page 33: Vivado HLS を使用する Lucas-Kanade オプティカル フロー …

HLS 最適化手法の詳細

XAPP1300 (v1.0) 2017 年 2 月 3 日  33japan.xilinx.com

HLS 最適化手法の詳細

このオプティカル フロー デザインでは、 高いフレーム レート と少ない FPGA リ ソース との間での最良のト レードオフを実現するために、 高度な HLS 最適化手法を適用しています。 フレーム レートが高くなる と、 データ スループッ ト を維持するために必要な並列操作数が多くなるため、 これらは相反するパフォーマンス要件です。 このオプティカル フロー コアが、 画像処理パイプライン内でオブジェク トの追跡などのための単なる 1 コアである場合、 ブロ ッ ク RAM、 LUT、DSP48、 および FF を節約するこ とは非常に重要です。 画像処理ルーチンではこれらのリ ソースが大量に消費される可能性があるためです。

BRAM18K の節約

2 つの入力画像の同じ座標を持つ 2 つの画素をさらに大きな 1 つのワードにパッキングするこ とは ( 「HLS C++ 範囲演算子を使用した 2 つの画素の大きいワードへのパッキング」 の dual_pix_t データ型プを参照)、 hls_twoIsotropicFilter() ルーチンで BRAM18K を節約するための鍵です。 「BRAM18K を節約するよ うに最適化されていない hls_twoIsotropicFilter」 にあるコード フラグメン トで示されているよ うに (このコードはマクロ ISOTROPIC_NOT_OPTIMIZED を定義する と有効にできる )、 2 つの個別のスライディング ウ ィンド ウ と ライン バッファーを使用した場合、 Vivado HLS コンパイラは、 入力画素が 8 ビッ ト よ り も大きい場合に (たとえば 12 ビッ ト )、 生成するブロ ッ ク RAM の数を減らすこ とはできません。 図 16 の HLS 合成レポートは、 8 ビッ トのサンプルでの hls_twoIsotropicFilter() の最適化されていないバージ ョ ン ( 「BRAM18K を節約するよ うに最適化されていない hls_twoIsotropicFilter」 ) と最適化されたバージ ョ ン ( 「HLS C++ 範囲演算子を使用した 2 つの画素の大きいワードへのパッキング」 ) でブロ ッ ク RAM の数が同じ場合は、 ブロッ ク RAM 数が少ない 12 ビッ ト サンプルの方が有利であるこ とを示しています。

BRAM18K を節約するように最適化されていない hls_twoIsotropicFilter 

#ifdef ISOTROPIC_NOT_OPTIMIZEDvoid hls_twoIsotropicFilters(. . . ){pix_t filt_out1, filt_out2, pix1, pix2;pix_t pixel1[FILTER_SIZE], pixel2[FILTER_SIZE];pix_t window1[FILTER_SIZE*FILTER_SIZE];#pragma HLS ARRAY_PARTITION variable=window1 complete dim=0pix_t window2[FILTER_SIZE*FILTER_SIZE];#pragma HLS ARRAY_PARTITION variable=window2 complete dim=0static pix_t lpf1_line_buffer[FILTER_SIZE][MAX_WIDTH];#pragma HLS ARRAY_PARTITION variable=lpf1_line_buffer complete dim=1static pix_t lpf2_line_buffer[FILTER_SIZE][MAX_WIDTH];#pragma HLS ARRAY_PARTITION variable=lpf2_line_buffer complete dim=1

// effective filteringL1: for(row = 0; row < height+FILTER_OFFS; row++)

{#pragma HLS LOOP_TRIPCOUNT max=480L2: for(col = 0; col < width+FILTER_OFFS; col++)

{#pragma HLS PIPELINE II=1#pragma HLS LOOP_TRIPCOUNT max=640

// Line Buffer fillif(col < width)for(unsigned char ii = 0; ii < FILTER_SIZE-1; ii++) {pixel1[ii] = lpf1_line_buffer[ii][col]

= lpf1_line_buffer[ii+1][col];pixel2[ii] = lpf2_line_buffer[ii][col]

= lpf2_line_buffer[ii+1][col];}

Page 34: Vivado HLS を使用する Lucas-Kanade オプティカル フロー …

HLS 最適化手法の詳細

XAPP1300 (v1.0) 2017 年 2 月 3 日  34japan.xilinx.com

//There is an offset to accomodate the active pixel regionif((col < width) && (row < height)){pix1 = inp1_img[row*MAX_WIDTH+col];pix2 = inp2_img[row*MAX_WIDTH+col];pixel1[FILTER_SIZE-1] = lpf1_line_buffer[FILTER_SIZE-1][col] = pix1;pixel2[FILTER_SIZE-1] = lpf2_line_buffer[FILTER_SIZE-1][col] = pix2;

}. . .

これらの最適化手法は、「hls_derivatives_kernel() の 2D たたみ込み」 と 「hls_ComputeIntegrals() ルーチンの C/C++ コード フラグメン ト 」 で説明したルーチンの hls_SpatialTemporal-Derivatives() および ComputeIntegrals() で使用されます。 この場合、8 ビッ ト サンプルでは 3 つの 9 ビッ ト データが 27 ビッ トの大きいワードにパッキングされます (または画素あたり 12 ビッ トの場合は 3 つの 13 ビッ ト データを 39 ビッ トの大きいワードにパッキング)。

X-Ref Target - Figure 16

図 16: hls_twoIsotropicFilter の最適化されているバージ ョ ンと最適化されていないバージ ョ ンとで比較した合成リソース数の見積もり

X18070-012017

Page 35: Vivado HLS を使用する Lucas-Kanade オプティカル フロー …

HLS 最適化手法の詳細

XAPP1300 (v1.0) 2017 年 2 月 3 日  35japan.xilinx.com

フリ ップフロップと  LUT の節約

最適化されていないバージ ョ ンの ComputeIntegrals() で、 スライディング ウ ィンド ウ全体は最も内側の hls_tyx_integration_kernel() に渡されます。 これは 「最適化されていない hls_txy_integration_kernel() ルーチンの C/C++ コード フラグメン ト 」 に示すとおり、 NxN 累積を 5 回計算します (このコードはマクロ INTEGRALS_NOT_OPTIMIZED を定義する と有効にできる )。

注記: 5 つのパラ メーター Ixx、 Ixy、 Iyy、 Itx、 および Ity を計算する乗算は、 冗長な演算を避けるために関数の外部で実行されます。 ただしそれでも累積結果は、 「hls_ComputeIntegrals() ルーチンの C/C++ コード フラグメン ト 」 の最適化されたバージ ョ ンと比較する とかな り多くなっています。

図 17 に、 53x53 統合ウ ィンド ウ サイズの 2 つのソ リ ューシ ョ ンで比較した HLS の見積も り を示します。

最適化されていない hls_txy_integration_kernel() ルーチンの C/C++ コード  フラグメン ト

#ifdef INTEGRALS_NOT_OPTIMIZEDvoid hls_tyx_integration_kernel(p5sqflt_t packed_window[WINDOW_SIZE*WINDOW_SIZE],

sum_t &a11, sum_t &a12, sum_t &a22, sum_t &b1, sum_t &b2) {

// local accumulatorssum_t sum_xx = (sum_t) 0; sum_t sum_xy = (sum_t) 0;sum_t sum_yy = (sum_t) 0; sum_t sum_ty = (sum_t) 0;sum_t sum_tx = (sum_t) 0;

sqflt_t mult_xx, mult_xy, mult_yy, mult_tx, mult_ty;p5sqflt_t five_sqdata;

unsigned short int i;//Compute the 2D integrationL1:for (i = 0; i < WINDOW_SIZE*WINDOW_SIZE; i++){ five_sqdata = packed_window[i]; mult_xx = five_sqdata( 2*(BITS_PER_PIXEL+1)-1, 0); mult_yy = five_sqdata( 4*(BITS_PER_PIXEL+1)-1, 2*(BITS_PER_PIXEL+1)); mult_xy = five_sqdata( 6*(BITS_PER_PIXEL+1)-1, 4*(BITS_PER_PIXEL+1)); mult_tx = five_sqdata( 8*(BITS_PER_PIXEL+1)-1, 6*(BITS_PER_PIXEL+1)); mult_ty = five_sqdata(10*(BITS_PER_PIXEL+1)-1, 8*(BITS_PER_PIXEL+1)); sum_xx += mult_xx; sum_xy += mult_xy; sum_yy += mult_yy; sum_ty += mult_ty; sum_tx += mult_tx;}

a11 = sum_xx; a12 = sum_xy; a22 = sum_yy; b1 = sum_tx; b2 = sum_ty;}

Page 36: Vivado HLS を使用する Lucas-Kanade オプティカル フロー …

HLS 最適化手法の詳細

XAPP1300 (v1.0) 2017 年 2 月 3 日  36japan.xilinx.com

ブロック  RAM の使用による  DSP48 スライスの節約

24 ページの表 1 に示すとおり、 DSP48 スライスの数は、 統合ウ ィンド ウの辺の幅 (N) と共に直線的に増加します。 これによ り hls_ComputeIntegrals() サブルーチンの複雑性は、 コンピューター サイエンスの公式見解からする と O(N) になり ます。 図 10 に示すとおり、 スラ イディング ウ ィンド ウが左から右に 1 列移動する と、 一番右側の入力列全体が加算され、一番左側の古い列が減算されます。 サブルーチンを飛躍的に最適化するさ らに優れた方法は、 画像幅全体の列合計を維持し、 ウ ィンド ウの移動に基づいて列合計を更新するこ とです。 これには、 一番右下のサンプル (ス ト リーミ ング順での現在のサンプル) を加算し、 一番右上のサンプルのみを減算します。 このよ うにする と、 アルゴ リズムの複雑性は、 ウ ィンド ウの辺 N (列の長さでもある ) に依存しなくな り ます。 この手法は、 画像処理で使用されるエリ ア総和テーブル メ ソ ッド [参照 10] と類似しています。 この手法によ り、 矩形域計算のサイズに関係なく計算時間を一定にできるため、 O(1) の複雑性を実現できます。

「最も最適化された hls_ComputeIntegrals() ルーチンの C/C++ コード フラグメン ト 」 に示されているコード (これはマクロ OPTIMIZED_TO_SAVE_DSP48 を定義するこ とで有効にできる ) は、 この手法を実装しています。 コード内のコ メン トは、図 18 に言及しています。 この図では簡潔明瞭に示すために、 5 つのパラ メーターの 1 つのみ (たとえば Ixx) を示しています。

X-Ref Target - Figure 17

図 17: 53x53 統合ウィンドウ サイズで hls_txy_integration_kernel() の 最適化されたバージ ョ ンと最適化されていないバージ ョ ンとで比較した 合成リソース数の見積もり

X18071-012017

Page 37: Vivado HLS を使用する Lucas-Kanade オプティカル フロー …

HLS 最適化手法の詳細

XAPP1300 (v1.0) 2017 年 2 月 3 日  37japan.xilinx.com

最も最適化された hls_ComputeIntegrals() ルーチンの C/C++ コード  フラグメン ト

#ifdef OPTIMIZED_TO_SAVE_DSP48

void hls_ComputeIntegrals(flt_t Ix_img[MAX_HEIGHT*MAX_WIDTH], flt_t Iy_img[MAX_HEIGHT*MAX_WIDTH], flt_t It_img[MAX_HEIGHT*MAX_WIDTH], sum_t A11_img[MAX_HEIGHT*MAX_WIDTH], sum_t A12_img[MAX_HEIGHT*MAX_WIDTH], sum_t A22_img[MAX_HEIGHT*MAX_WIDTH],

sum_t B1_img[MAX_HEIGHT*MAX_WIDTH], sum_t B2_img[MAX_HEIGHT*MAX_WIDTH], unsigned short int height, unsigned short int width) {

unsigned short int row, col; sum_t a11, a12, a22, b1, b2; flt_t x_der, y_der, t_der;p3dtyx_t three_data, packed3_column[WINDOW_SIZE+1];static p3dtyx_t packed3_lines_buffer[WINDOW_SIZE+1][MAX_WIDTH];#pragma HLS ARRAY_PARTITION variable=packed3_lines_buffer complete dim=1sqflt_t top_Ixx, top_Iyy, top_Ixy, top_Itx, top_Ity;sqflt_t bot_Ixx, bot_Iyy, bot_Ixy, bot_Itx, bot_Ity;

// sliding window sums. Gray color cell in Figure 18.staticint sum_Ixx, sum_Ixy, sum_Iyy, sum_Itx, sum_Ity;

// column sums for the entire image width. Yellow color cells in Figure 18static int csIxix[MAX_WIDTH], csIxiy[MAX_WIDTH], csIyiy[MAX_WIDTH], csDix[MAX_WIDTH];static int csDiy[MAX_WIDTH], cbIxix[MAX_WIDTH], cbIxiy[MAX_WIDTH], cbIyiy[MAX_WIDTH];static int cbDix[MAX_WIDTH], cbDiy[MAX_WIDTH];#pragma HLS RESOURCE variable=csIxix core=RAM_2P_BRAM#pragma HLS RESOURCE variable=csIxiy core=RAM_2P_BRAM#pragma HLS RESOURCE variable=csIyiy core=RAM_2P_BRAM#pragma HLS RESOURCE variable=csDix core=RAM_2P_BRAM#pragma HLS RESOURCE variable=csDiy core=RAM_2P_BRAM#pragma HLS RESOURCE variable=cbIxix core=RAM_2P_BRAM#pragma HLS RESOURCE variable=cbIxiy core=RAM_2P_BRAM#pragma HLS RESOURCE variable=cbIyiy core=RAM_2P_BRAM#pragma HLS RESOURCE variable=cbDix core=RAM_2P_BRAM#pragma HLS RESOURCE variable=cbDiy core=RAM_2P_BRAM#pragma HLS DEPENDENCE variable=csIxix inter WAR false#pragma HLS DEPENDENCE variable=cbIxix inter WAR false#pragma HLS DEPENDENCE variable=cbIxiy inter WAR false#pragma HLS DEPENDENCE variable=cbIyiy inter WAR false#pragma HLS DEPENDENCE variable=cbDix inter WAR false#pragma HLS DEPENDENCE variable=cbDiy inter WAR false#pragma HLS DEPENDENCE variable=csIxiy inter WAR false#pragma HLS DEPENDENCE variable=csIyiy inter WAR false#pragma HLS DEPENDENCE variable=csDix inter WAR false#pragma HLS DEPENDENCE variable=csDiy inter WAR false

X-Ref Target - Figure 18

図 18: DSP48 スライスの数を減らすための ComputeIntegrals の最適化 

X18670-012017

Page 38: Vivado HLS を使用する Lucas-Kanade オプティカル フロー …

HLS 最適化手法の詳細

XAPP1300 (v1.0) 2017 年 2 月 3 日  38japan.xilinx.com

int csIxixR, csIxiyR, csIyiyR, csDixR, csDiyR; // Blue color cell in Figure 18

// the left and right indices onto the column sumsint zIdx = -WINDOW_SIZE; int nIdx = zIdx + WINDOW_SIZE;

L1: for (row = 0; row < height + WINDOW_OFFS; row++) { #pragma HLS LOOP_TRIPCOUNT min=hls_MIN_H max=hls_MAX_HL2: for (col = 0; col < width + WINDOW_OFFS; col++){ #pragma HLS LOOP_TRIPCOUNT min=hls_MIN_W max=hls_MAX_W #pragma HLS PIPELINE

// line-buffer fillif (col < width)for (unsigned char ii = 0; ii < WINDOW_SIZE; ii++) { packed3_column[ii] = packed3_lines_buffer[ii][col] = packed3_lines_buffer[ii + 1][col]; }

if ((col < width) & (row < height)) {x_der = Ix_img[row * MAX_WIDTH + col]; y_der = Iy_img[row * MAX_WIDTH + col]; t_der = It_img[row * MAX_WIDTH + col];

// pack data for the line-bufferthree_data( (BITS_PER_PIXEL + 1) - 1, 0) = x_der;three_data(2 * (BITS_PER_PIXEL + 1) - 1, (BITS_PER_PIXEL + 1)) = y_der;three_data(3 * (BITS_PER_PIXEL + 1) - 1, 2*(BITS_PER_PIXEL + 1)) = t_der;packed3_column[WINDOW_SIZE] = packed3_lines_buffer[WINDOW_SIZE][col] = three_data;// line-buffer done

// the leftSumsint csIxixL = 0, csIxiyL = 0, csIyiyL = 0, csDixL = 0, csDiyL = 0;if (zIdx >= 0){csIxixL = csIxix[zIdx]; csIxiyL = csIxiy[zIdx]; csIyiyL = csIyiy[zIdx];csDixL = csDix[zIdx]; csDiyL = csDiy[zIdx];

}

// incoming column: data on the topthree_data = packed3_column[0];x_der = three_data((BITS_PER_PIXEL + 1) - 1, 0);y_der = three_data(2 * (BITS_PER_PIXEL + 1) - 1, (BITS_PER_PIXEL + 1));t_der = three_data(3 * (BITS_PER_PIXEL + 1) - 1, 2 * (BITS_PER_PIXEL + 1));top_Ixx = (sqflt_t) x_der * (sqflt_t) x_der; top_Iyy = (sqflt_t) y_der * (sqflt_t) y_der;top_Ixy = (sqflt_t) x_der * (sqflt_t) y_der; top_Itx = (sqflt_t) t_der * (sqflt_t) x_der;top_Ity = (sqflt_t) t_der * (sqflt_t) y_der;

// incoming column: data on the bottomthree_data = packed3_column[WINDOW_SIZE];x_der = three_data((BITS_PER_PIXEL + 1) - 1, 0);y_der = three_data(2 * (BITS_PER_PIXEL + 1) - 1, (BITS_PER_PIXEL + 1));t_der = three_data(3 * (BITS_PER_PIXEL + 1) - 1, 2*(BITS_PER_PIXEL + 1));bot_Ixx = (sqflt_t) x_der * (sqflt_t) x_der; bot_Iyy = (sqflt_t) y_der * (sqflt_t) y_der;bot_Ixy = (sqflt_t) x_der * (sqflt_t) y_der; bot_Itx = (sqflt_t) t_der * (sqflt_t) x_der;bot_Ity = (sqflt_t) t_der * (sqflt_t) y_der;

// compute rightSums incrementallycsIxixR=cbIxix[nIdx] + bot_Ixx - top_Ixx; csIxiyR=cbIxiy[nIdx] + bot_Ixy - top_Ixy; csIyiyR=cbIyiy[nIdx] + bot_Iyy - top_Iyy;csDixR = cbDix[nIdx] + bot_Itx - top_Itx; csDiyR= cbDiy[nIdx] + bot_Ity - top_Ity;

// sums += (rightSums - leftLums)sum_Ixx += (csIxixR - csIxixL); sum_Ixy += (csIxiyR - csIxiyL); sum_Iyy += (csIyiyR - csIyiyL);sum_Itx += (csDixR - csDixL); sum_Ity += (csDiyR - csDiyL);

// outputsa11 = sum_Ixx; a12 = sum_Ixy; a22 = sum_Iyy; b1 = sum_Itx; b2 = sum_Ity;

// update new rightSums: Blue cell in State+1 goes to Yellow cell in State+2 of Figure 18cbIxix[nIdx] = csIxixR; cbIxiy[nIdx] = csIxiyR;

Page 39: Vivado HLS を使用する Lucas-Kanade オプティカル フロー …

HLS 最適化手法の詳細

XAPP1300 (v1.0) 2017 年 2 月 3 日  39japan.xilinx.com

cbIyiy[nIdx] = csIyiyR; cbDix[nIdx] = csDixR; cbDiy[nIdx] = csDiyR;csIxix[nIdx] = csIxixR; csIxiy[nIdx] = csIxiyR;csIyiy[nIdx] = csIyiyR; csDix[nIdx] = csDixR; csDiy[nIdx] = csDiyR;

// update indexzIdx++; if (zIdx == width) zIdx = 0; nIdx++; if (nIdx == width) nIdx = 0;

} //end of if ((col < width) & (row < height))

if ((row < WINDOW_OFFS) & (col < WINDOW_OFFS) & (row >= height) & (col >= width)) { a11 = 0; a12 = 0; a22 = 0; b1 = 0; b2 = 0; }if ((row >= WINDOW_OFFS) & (col >= WINDOW_OFFS)) { //output data are not normalized (so that thresholding will be dependent on window size) A11_img[(row - WINDOW_OFFS) * MAX_WIDTH + (col - WINDOW_OFFS)] = a11; A12_img[(row - WINDOW_OFFS) * MAX_WIDTH + (col - WINDOW_OFFS)] = a12; A22_img[(row - WINDOW_OFFS) * MAX_WIDTH + (col - WINDOW_OFFS)] = a22; B1_img[(row - WINDOW_OFFS) * MAX_WIDTH + (col - WINDOW_OFFS)] = b1; B2_img[(row - WINDOW_OFFS) * MAX_WIDTH + (col - WINDOW_OFFS)] = b2;}} // end of L2} // end of L1}

変数 csIxix、 csIxiy、 csIyiy、 csDix、 および csDiy は、 画像幅全体の列合計をモデル化しています。 これらの変数は、 ブロ ッ ク RAM エレ メン トのデュアル ポート RAM に、 #pragma HLS RESOURCE variable=… core=RAM_2P_BRAM 指示子で割り当てられます。 すべてのブロ ッ ク RAM エレ メン トは、 #pragma HLS DEPENDENCE variable=… inter WAR false 指示子で設定される WAR (write-after-read) ポ リシーで構成されます。 このポ リシーによ り、 特にループ パイプライン処理中の読み取り /書き込み操作の HLS スケジューリ ングに影響を与える可能性がある依存関係が緩和されます。 ただし、 HLS で正しいデータ レート を維持するために内部ループ L2 で II = 1 を実現するにはこれでは不十分です。 実際、 有効な II 値は 2 です。 結果と して、 帯域幅を広げるために変数 cbIxix、 cbIxiy、 cbIyiy、 cbDix、 および cbDiy が追加され、 よ り多くのブロ ッ ク RAM エレ メン トが必要である と しても、 最終的には II = 1 が実現します。

図 19 の左側は、 53x53 統合ウ ィンド ウ サイズの考えられる 3 つのソ リ ューシ ョ ンについての HLS の比較を示しています。 図 19 で、 solution0_ON は 172 のブロ ッ ク RAM を使用する、 複雑性が O(N) の ComputeIntegrals のバージ ョ ンを表し、solution0_O1_1mem は 195 のブロ ッ ク RAM を使用する、II = 2 を実現する複雑性が O(1) の最も最適化されたルーチンを表し、 solution1_O1 は 215 のブロ ッ ク RAM を使用する、 II = 1 を実現する複雑性が O(1) の最も最適化されたルーチンを表しています。 これら 3 つのソ リ ューシ ョ ンによって実現するフレーム レートはそれぞれ、 123.6Hz、 61.8Hz、 および 123.5Hz です。

Page 40: Vivado HLS を使用する Lucas-Kanade オプティカル フロー …

まとめ

XAPP1300 (v1.0) 2017 年 2 月 3 日  40japan.xilinx.com

まとめ

このアプリ ケーシ ョ ン ノートでは、 密な LK オプティカル フローなどの複雑なアルゴ リズムにおける Vivado HLS デザイン ツール フローの性能について説明しました。 このデザインは高度な最適化手法を適用し、 FPGA リ ソースを過剰に消費するこ とな く最高のフレーム レート を実現します。 このデザインでは、 入力サンプルあたりのビッ ト数 (このアプリケーシ ョ ン ノートの 8 ビッ トや 12 ビッ トのサンプルなど)、 画像解像度 (文書をとおして 1920x1080)、 および統合ウ ィンド ウ サイズ (7x7、 11x11、 最大 53x53) はパラ メーターになっています。 ユーザーは、 フレーム レート と DSP48 スライス リ ソース数との間でさまざまな ト レードオフを判断できます。 最大統合ウ ィンド ウ サイズ (53x53) のワース ト ケースでは、 PAR インプリ メンテーシ ョ ン後に Zynq 7045-2 デバイスで 215 の BRAM18K、 48 の DSP48、 9133 の FF、 および 5642 の LUT を消費し、 ク ロ ッ ク周波数 256MHz での有効フレーム レートが 123.5Hz でした。ZC706 ボードの SDSoC 環境でリ アルタイムのデモンス ト レーターが生成されていますが、 ZC702 ボードでも簡単に生成できます。

X-Ref Target - Figure 19

図 19:最も最適化されたバージ ョ ンの ComputeIntegrals の HLS レポート

X18671-012017

Page 41: Vivado HLS を使用する Lucas-Kanade オプティカル フロー …

リファレンス デザイン

XAPP1300 (v1.0) 2017 年 2 月 3 日  41japan.xilinx.com

リファレンス デザイン

このアプリ ケーシ ョ ン ノートの リ ファレンス デザイン ファ イルは、 ザイ リ ンクスのウェブサイ トからダウンロードできます。

表 5 に、 リ ファレンス デザインの詳細を示します。

表 5: リファレンス デザインの詳細

パラメーター 説明

一般

開発者 Daniele Bagni、 Pari Kannan、 Stephen Neuendorrfer

ターゲッ ト デバイス Zynq-7000 AP SoC

ソース コードの提供 あ り

ソース コードの形式 C および合成スク リプ ト

既存のザイ リ ンクス アプリ ケーシ ョ ン ノート /リ ファレンス デザイン、 またはサードパーティからデザインへのコード /IP の使用

なし

シミ ュレーシ ョ ン

論理シ ミ ュレーシ ョ ンの実施 あ り

タイ ミ ング シ ミ ュレーシ ョ ンの実施 なし

論理シ ミ ュレーシ ョ ンおよびタイ ミ ング シ ミ ュレーシ ョ ンでのテス トベンチの利用

あ り

テス トベンチの形式 C

使用したシ ミ ュレータ /バージ ョ ン Vivado シ ミ ュレータ 2016.2 および 2016.3

SPICE/IBIS シ ミ ュレーシ ョ ンの実施 なし

インプリ メンテーシ ョ ン

使用した合成ツール/バージ ョ ン Vivado 合成

使用したインプリ メンテーシ ョ ン ツール/バージ ョ ン

Vivado HLS/SDSoC 環境 2016.2 および 2016.3

スタティ ッ ク タイ ミ ング解析の実施 あ り

ハードウェア検証

ハードウェア検証の実施 あ り

使用したハード ウェア プラ ッ ト フォーム ZC706 ボード

Page 42: Vivado HLS を使用する Lucas-Kanade オプティカル フロー …

参考資料

XAPP1300 (v1.0) 2017 年 2 月 3 日  42japan.xilinx.com

参考資料 

注記:日本語版のバージ ョ ンは、 英語版よ り古い場合があ り ます。

1. 『Generalized Image Matching by the Method of Differences』 (著者: B. D. Lucas、 1984 年発表の博士論文)

2. 『Zynq-7000 XC7Z020 AP SoC 向け ZC702 評価ボード ユーザー ガイ ド』 (UG850)

3. 『Zynq-7000 All Programmable SoC ZC706 評価キッ ト スタート アップ ガイ ド』 (UG961)

4. 『SDSoC 環境ユーザー ガイ ド』 (UG1027: 英語版、 日本語版)

5. 『Vivado Design Suite ユーザー ガイ ド : 高位合成』 (UG902: 英語版、 日本語版)

6. 『Vivado HLS ツールを使用したビデオ処理用メモ リ構造のインプリ メン ト 』 (XAPP793)

7. 『Pyramidal Implementation of the Affine Lucas Kanade Feature Tracker Description Algorithm』 (著者: J. Y. Bouguet、 Intel Corporation, 2001)

8. MathWorks 資料 『opticalFlowLK class』 (MathWorks 社ウェブサイ トから入手可能)

9. https://en.wikipedia.org/wiki/Bilinear_interpolation

10. https://en.wikipedia.org/wiki/Summed_area_table

改訂履歴

次の表に、 この文書の改訂履歴を示します。

法的通知本通知に基づいて貴殿または貴社 (本通知の被通知者が個人の場合には 「貴殿」、 法人その他の団体の場合には 「貴社」。 以下同じ ) に開

示される情報 (以下 「本情報」 といいます) は、 ザイ リ ンクスの製品を選択および使用するこ とのためにのみ提供されます。 適用される

法律が許容する最大限の範囲で、 (1) 本情報は 「現状有姿」、 およびすべて受領者の責任で (with all faults) とい う状態で提供され、 ザイ リ

ンクスは、 本通知をもって、 明示、 黙示、 法定を問わず (商品性、 非侵害、 特定目的適合性の保証を含みますがこれらに限られません)、すべての保証および条件を負わない (否認する ) ものと します。 また、 (2) ザイ リ ンクスは、 本情報 (貴殿または貴社による本情報の使用

を含む) に関係し、 起因し、 関連する、 いかなる種類 ・ 性質の損失または損害についても、 責任を負わない (契約上、 不法行為上 (過失の

場合を含む)、 その他のいかなる責任の法理によるかを問わない) ものと し、 当該損失または損害には、 直接、 間接、 特別、 付随的、 結

果的な損失または損害 (第三者が起こした行為の結果被った、 データ、 利益、 業務上の信用の損失、 その他あらゆる種類の損失や損害を

含みます) が含まれるものと し、 それは、 たとえ当該損害や損失が合理的に予見可能であったり、 ザイ リ ンクスがそれらの可能性につい

て助言を受けていた場合であったと しても同様です。 ザイ リ ンクスは、 本情報に含まれるいかなる誤り も訂正する義務を負わず、 本情

報または製品仕様のアップデート を貴殿または貴社に知らせる義務も負いません。 事前の書面による同意のない限り、 貴殿または貴社

は本情報を再生産、 変更、 頒布、 または公に展示してはなり ません。 一定の製品は、 ザイ リ ンクスの限定的保証の諸条件に従う こ と と

なるので https://japan.xilinx.com/legal.htm#tos で見られるザイ リ ンクスの販売条件を参照して ください。 IP コアは、 ザイ リ ンクスが貴殿ま

たは貴社に付与したライセンスに含まれる保証と補助的条件に従う こ とになり ます。 ザイ リ ンクスの製品は、 フェイルセーフと して、

または、 フェイルセーフの動作を要求するアプリ ケーシ ョ ンに使用するために、 設計されたり意図されたり していません。 そのよ うな

重大なアプリ ケーシ ョ ンにザイ リ ンクスの製品を使用する場合のリ スク と責任は、 貴殿または貴社が単独で負う ものです。

https://japan.xilinx.com/legal.htm#tos で見られるザイ リ ンクスの販売条件を参照してください。

自動車用のアプリ ケーシ ョ ンの免責条項

オートモーティブ製品 (製品番号に 「XA」 が含まれる ) は、 ISO 26262 自動車用機能安全規格に従った安全コンセプ ト または余剰性の機

能 ( 「セーフティ設計」 ) がない限り、 エアバッグの展開における使用または車両の制御に影響するアプリ ケーシ ョ ン ( 「セーフティ アプリ ケーシ ョ ン」 ) における使用は保証されていません。 顧客は、 製品を組み込むすべてのシステムについて、 その使用前または提供前

に安全を目的と して十分なテス ト を行う ものと します。 セーフティ設計なしにセーフティ アプリ ケーシ ョ ンで製品を使用する リ スクは

すべて顧客が負い、 製品の責任の制限を規定する適用法令および規則にのみ従う ものと します。

© Copyright 2017 Xilinx, Inc. Xilinx、 Xilinx のロゴ、 Artix、 ISE、 Kintex、 Spartan、 Virtex、 Vivado、 Zynq、 およびこの文書に含まれるその

他の指定されたブランドは、 米国およびその他各国のザイ リ ンクス社の商標です。 すべてのその他の商標は、 それぞれの保有者に帰属

日付 バージョ ン 内容

2017 年 2 月 3 日 1.0 初版

Page 43: Vivado HLS を使用する Lucas-Kanade オプティカル フロー …

法的通知

XAPP1300 (v1.0) 2017 年 2 月 3 日  43japan.xilinx.com

します。 ARM は、 EU およびその他各国の ARM 社の登録商標です。 HDMI、 HDMI ロゴ、 および High-Definition Multimedia Interface は、

HDMI Licensing LLC の商標です。 MATLAB は、 The MathWorks, Inc. OpenCL の登録商標です。 OpenCL および OpenCL ロゴは、 Khronos の許可を得て使用されている Apple Inc. の商標です。 すべてのその他の商標は、 それぞれの保有者に帰属します。

この資料に関するフ ィードバッ クおよびリ ンクなどの問題につきましては、 [email protected] まで、 または各ページの右下

にある [フ ィードバッ ク送信] ボタンをク リ ッ クする と表示されるフォームからお知らせください。 フ ィードバッ クは日本語で入力可能

です。 いただきましたご意見を参考に早急に対応させていただきます。 なお、 このメール アドレスへのお問い合わせは受け付けており

ません。 あらかじめご了承ください。