xfOpenCV ライブラリを使用したデザイン例

ライブラリに含まれるすべてのハードウェア関数には、それに対応するデザイン例が GitHub から提供されています。このセクションでは、xfOpenCV に含まれるさまざまな関数を組み合わせてインプリメントした画像処理関数とパイプラインの詳細を説明します。プロセッサとプログラマブル ロジックの両方の機能を利用してさまざまな機能をインプリメントするのにベストな方法を示します。これらの例では、複雑なデータフロー パスをインプリメントする異なる方法も示します。このセクションでは、次の例について説明します。

反復ピラミッド型高密度オプティカル フロー

高密度ピラミッド型オプティカル フローの例は、xfOpenCV ライブラリの xf::pyrDown および xf::densePyrOpticalFlow ハードウェア関数を使用して画像ピラミッドを作成し、それに対して反復実行して、2 つの入力画像間のオプティカル フローを計算します。この例では、xf::pyrDown 関数のハードウェア インスタンスを 2 つ使用して、2 つの入力画像の画像ピラミッドを並列に計算します。2 つの画像ピラミッドは、xf::densePyrOpticalFlow 関数の 1 つのハードウェア インスタンスにより、最小画像サイズから最大画像サイズまで処理されます。各反復の出力フロー ベクターは、ハードウェア カーネルにハードウェア関数への入力としてフィードバックされます。最大画像サイズの最後の反復の出力が、高密度ピラミッド型オプティカル フローの出力となります。

図: 反復ピラミッド型高密度オプティカル フロー

次に、ホストにおけるこの例のインプリメンテーションの詳細を説明します。これらの詳細は、必要なスループットを達成するためのプロセスを理解するのに役立ちます。

pyrof_hw()

pyrof_hw() は、高密度オプティカル フローを計算するホスト関数です。

API 構文

void pyrof_hw(cv::Mat im0, cv::Mat im1, cv::Mat flowUmat, cv::Mat flowVmat, xf::Mat<XF_32UC1,HEIGHT,WIDTH,XF_NPPC1> & flow, xf::Mat<XF_32UC1,HEIGHT,WIDTH,XF_NPPC1> & flow_iter, xf::Mat<XF_8UC1,HEIGHT,WIDTH,XF_NPPC1> mat_imagepyr1[NUM_LEVELS] , xf::Mat<XF_8UC1,HEIGHT,WIDTH,XF_NPPC1> mat_imagepyr2[NUM_LEVELS] , int pyr_h[NUM_LEVELS], int pyr_w[NUM_LEVELS])

パラメーターの説明

次の表に、テンプレートと関数のパラメーターを説明します。
パラメーター 説明
im0 cv::Mat の 1 つ目の入力画像
im1 cv::Mat の 2 つ目の入力画像
flowUmat 出力フロー ベクターの水平要素を保存するために割り当てられた cv::Mat
flowVmat 出力フロー ベクターの垂直要素を保存するために割り当てられた cv::Mat
flow ハードウェア関数を使用した反復計算中に、パックされたフロー ベクターを一時的に格納するために割り当てられた xf::Mat
flow_iter ハードウェア関数を使用した反復計算中に、パックされたフロー ベクターを一時的に格納するために割り当てられた xf::Mat
mat_imagepyr1 xf::Mat のサイズがピラミッド レベル数に等しい配列。最初の画像のピラミッド レベル数を保存します。
mat_imagepyr2 xf::Mat のサイズがピラミッド レベル数に等しい配列。2 つ目の画像のピラミッド レベル数を保存します。
pyr_h 各ピラミッド レベルで画像の高さを格納するための、画像のピラミッド レベル数を含む整数の配列
pyr_w 各ピラミッド レベルで画像の幅を格納するための、画像のピラミッド レベル数を含む整数の配列

データフロー

pyrof_hw() 関数は、次を実行します。

  1. 画像ピラミッドのさまざまなレベルの画像サイズを設定
  2. 入力画像を cv::Mat フォーマットから最大画像ピラミッド レベルを含めるよう割り当てられている xf::Mat オブジェクトにコピー
  3. pyr_dense_optical_flow_pyr_down_accel() 関数を呼び出して画像ピラミッドを作成
  4. pyr_dense_optical_flow_accel() 関数を使用して、ユーザーが入力したピラミッド レベルすべてで反復してオプティカル フロー出力を計算
  5. フロー ベクターをアンパックして浮動小数点に変換し、返す

上記の重要な手順 3 および 4 について、次に詳細に説明します。

pyr_dense_optical_flow_pyr_down_accel()

API 構文

void pyr_dense_optical_flow_pyr_down_accel(xf::Mat<XF_8UC1,HEIGHT,WIDTH,XF_NPPC1> mat_imagepyr1[NUM_LEVELS], xf::Mat<XF_8UC1,HEIGHT,WIDTH,XF_NPPC1> mat_imagepyr2[NUM_LEVELS])

パラメーターの説明

次の表に、テンプレートと関数のパラメーターを説明します。
パラメーター 説明
mat_imagepyr1 xf::Mat のサイズがピラミッド レベル数に等しい配列。最初の画像のピラミッド レベル数を保存します。この割り当てられたメモリの最上位ピラミッド レベル [0] に対応するメモリ ロケーションに、最初の入力画像が含まれている必要があります。
mat_imagepyr2 xf::Mat のサイズがピラミッド レベル数に等しい配列。2 つ目の画像のピラミッド レベル数を保存します。この割り当てられたメモリの最上位ピラミッド レベル [0] に対応するメモリ ロケーションに、2 番目の入力画像が含まれている必要があります。

pyr_dense_optical_flow_pyr_down_accel() は、次のように、xf::pyrDown ハードウェア関数を呼び出して 1 つの for ループを実行します。

for(int pyr_comp=0;pyr_comp<NUM_LEVELS-1; pyr_comp++)
	{
	#pragma SDS async(1)
	#pragma SDS resource(1)
		xf::pyrDown<XF_8UC1,HEIGHT,WIDTH,XF_NPPC1,XF_USE_URAM>(mat_imagepyr1[pyr_comp], mat_imagepyr1[pyr_comp+1]);
	#pragma SDS async(2)
	#pragma SDS resource(2)
		xf::pyrDown<XF_8UC1,HEIGHT,WIDTH,XF_NPPC1,XF_USE_URAM>(mat_imagepyr2[pyr_comp], mat_imagepyr2[pyr_comp+1]);
	#pragma SDS wait(1)
	#pragma SDS wait(2)	
	}

プラグマを除けばコードは単純で、各反復で xf::pyrDown 関数が 2 回呼び出されます。1 回目は最初の画像に対して、2 回目は 2 つ目の画像に対して実行されます。次の反復への入力は、現在の反復の出力です。プラグマ #pragma SDS async(ID) は、Arm® プロセッサがハードウェア関数を呼び出し、ハードウェア関数が返されるのを待たないように指示します。Arm プロセッサが関数を呼び出すのに数サイクルかかります。これには、DAM のプログラムが含まれます。プラグマ #pragma SDS wait(ID) は、Arm プロセッサが async(ID) プラグマで呼び出されたハードウェア関数が処理を完了するのを待つように指示します。プラグマ #pragma SDS resource(ID) は、ハードウェア関数が異なる ID で呼び出されるたびに、別のハードウェア インスタンスを作成します。つまり、上記のホスト関数のループは、xf::pyrDown 関数のハードウェア インスタンス 2 つを並列に呼び出し、両方の関数が戻るまで待ってから、次の反復に進みます。

高密度ピラミッド型オプティカル フローの計算

for (int l=NUM_LEVELS-1; l>=0; l--) {
		//compute current level height
		int curr_height = pyr_h[l];
		int curr_width = pyr_w[l];
		
		//compute the flow vectors for the current pyramid level iteratively
		for(int iterations=0;iterations<NUM_ITERATIONS; iterations++)
		{
			bool scale_up_flag = (iterations==0)&&(l != NUM_LEVELS-1);
			int next_height = (scale_up_flag==1)?pyr_h[l+1]:pyr_h[l]; 
			int next_width  = (scale_up_flag==1)?pyr_w[l+1]:pyr_w[l]; 
			float scale_in = (next_height - 1)*1.0/(curr_height - 1);
			ap_uint<1> init_flag = ((iterations==0) && (l==NUM_LEVELS-1))? 1 : 0;
			if(flag_flowin)
			{
				flow.rows = pyr_h[l];
				flow.cols = pyr_w[l];
				flow.size = pyr_h[l]*pyr_w[l];
				pyr_dense_optical_flow_accel(mat_imagepyr1[l], mat_imagepyr2[l], flow_iter, flow, l, scale_up_flag, scale_in, init_flag);
				flag_flowin = 0;
			}
			else
			{
				flow_iter.rows = pyr_h[l];
				flow_iter.cols = pyr_w[l];
				flow_iter.size = pyr_h[l]*pyr_w[l];
				pyr_dense_optical_flow_accel(mat_imagepyr1[l], mat_imagepyr2[l], flow, flow_iter, l, scale_up_flag, scale_in, init_flag);
				flag_flowin = 1;
			}
		}//end iterative coptical flow computation
	} // end pyramidal iterative optical flow HLS computation

反復ピラミッド型オプティカル フローは、入れ子の for ループで計算されます。ループは、反復回数にピラミッド レベル数をかけた回数実行されます。メインのループは最小の画像サイズから開始し、最大の画像サイズまで繰り返されます。1 ピラミッド レベルの反復が実行される前に、現在のピラミッド レベルの高さと幅が curr_height および current_width 変数に設定されます。入れ子のループでは、スケール アップが必要な場合は next_height 変数が最初の反復にある前の画像の高さに設定されます。除算はコストが高く、ハードウェアでは一度だけの除算は回避されるので、ホストでスケール係数が計算され、引数としてハードウェア カーネルに渡されます。各ピラミッド レベルの後、スケール アップ フラグがセットされ、入力フロー ベクターを次に大きい画像サイズにスケール アップする必要があることがハードウェア関数に通知されます。スケール アップは、ハードウェア カーネルのバイリニア補間を使用して実行されます。

すべての入力データが準備でき、フラグがセットされると、ホスト プロセッサによりハードウェア関数が呼び出されます。ホスト関数では、最適化の問題を反復的に解決するため、ハードウェア関数へのフロー ベクター入力および出力がスワップされることに注意してください。また、pyr_dense_optical_flow_accel() 関数はハードウェア関数 xf::densePyrOpticalFlow の単なるラッパーです。ハードウェア関数へのテンプレート パラメーターは、このラッパー関数内で渡されます。

疎なオプティカル フローを使用したコーナー追跡

この例では、連続するビデオ フレームのセット内の特徴点を検出して追跡するところを示します。特徴検出には Harris コーナー検出器を使用し、追跡には Lucas-Kanade (LK) オプティカル フローを変更したものを使用しています。アルゴリズムの主要部分は、現在のフレームと次のフレームを入力として取り込み、追跡されたコーナーのリストを出力します。現在の画像がセット内の最初のフレームの場合、コーナー検出を実行し、追跡する特徴を検出します。追跡が必要な点を含むフレーム数も入力として供給します。

コーナー追跡の例では、xfOpenCV ライブラリからの 5 つのハードウェア関数 (xf::cornerHarrisxf:: cornersImgToListxf::cornerUpdatexf::pyrDown、および xf::densePyrOpticalFlow) が使用されています。

図: 疎なオプティカル フローを使用したコーナー追跡

xf::densePyrOpticalFlow 関数の出力からの高密度フロー ベクターがまばらに選択されるようにするため、新しいハードウェア関数 xf::cornerUpdate が追加されています。これは、パイプラインの次の関数がメモリにランダムにアクセスして検索する必要がないようにするため実行されます。この関数は、Harris コーナー検出器からのコーナーと高密度ピラミッド型オプティカル フロー関数からの高密度オプティカル フロー ベクターを取り込み、高密度フロー ベクターを使用して入力コーナーを追跡して、アップデートされたコーナー位置を出力することにより、疎なオプティカル フローの動作を模倣しています。このハードウェア関数は、720p の画像に対して 300 MHz で実行されて 10,000 個のコーナーを処理します。パイプラインにレイテンシはほとんど追加されません。

cornerUpdate()

API 構文

template <unsigned int MAXCORNERSNO, unsigned int TYPE, unsigned int ROWS, unsigned int COLS, unsigned int NPC>
void cornerUpdate(ap_uint<64> *list_fix, unsigned int *list, uint32_t nCorners, xf::Mat<TYPE,ROWS,COLS,NPC> &flow_vectors, ap_uint<1> harris_flag)

パラメーターの説明

次の表に、テンプレートと関数のパラメーターを説明します。

表 1. cornerUpdate 関数のパラメーターの説明
パラメーター 説明
MAXCORNERSNO 関数で処理する必要のある最大コーナー数
TYPE 入力ピクセルのデータ型。8 ビット、符号なし、1 チャネル (XF_8UC1) のみサポート。
ROWS 入力および出力画像の最大高さ (8 の倍数で指定)。
COLS 入力および出力画像の最大幅 (8 の倍数で指定)。
NPC 1 サイクルごとに処理されるピクセル数。XF_NPPC1 (1 サイクルごとに 1 ピクセル動作) のみサポート。
list_fix 16, 5 フォーマット (16 整数ビットと 5 小数ビット) で記述されたコーナー ロケーションのパックされた浮動小数点座標のリスト。ビット 20 ~ 0 は列番号、ビット 41 ~ 21 は行番号を示します。残りのビットはフラグとして使用されます。このフラグは、追跡されているコーナーが有効である場合にセットされます。
list 符号なしの short 型で記述されたコーナー ロケーションのパックされた正の short 整数座標のリスト。ビット 15 ~ 0 は列番号、ビット 31 ~ 16 は行番号を示します。このリストは、Harris コーナー検出器で出力されるリストと同じです。
nCorners 追跡するコーナーの数。
flow_vectors xf::DensePyrOpticalFlow 関数のパックされたフロー ベクター
harris_flag

1 の場合、関数は入力コーナーを list から取得します。

0 の場合、関数は入力コーナーを list_fix から取得します。

例のコードは、xfOpenCV ライブラリを使用して読み出しおよび処理された入力ビデオで動作します。ホストでのコーナーの処理と追跡は、xf_corner_tracker_accel() 関数で実行します。

cornersImgToList()

API 構文

template <unsigned int MAXCORNERSNO, unsigned int TYPE, unsigned int ROWS, unsigned int COLS, unsigned int NPC>
void cornersImgToList(xf::Mat<TYPE,ROWS,COLS,NPC> &_src, unsigned int list[MAXCORNERSNO], unsigned int *ncorners)

パラメーターの説明

次の表に、テンプレートと Kintex® UltraScale+™ 関数のパラメーターを説明します。

表 2. CornerImgToList 関数のパラメーターの説明
パラメーター 説明
_src Harris コーナー検出器の出力画像。この xf::Mat オブジェクトのサイズが Harris コーナー検出器への入力画像のサイズです。コーナーがその位置にある場合は各ピクセルの値は 255 となり、それ以外の場所は 0 になります。
list Harris 検出器で検出されたコーナーを格納するために割り当てられた 32 ビット メモリ (MAXCORNERS のサイズ)
ncorners Harris で検出されるコーナーの合計 (リスト内のコーナーの数)。

cornerTracker()

xf_corner_tracker_accel() 関数は、ホストでコーナーの処理と追跡を実行します。

API 構文

void cornerTracker(xf::Mat<XF_32UC1,HEIGHT,WIDTH,XF_NPPC1> & flow, xf::Mat<XF_32UC1,HEIGHT,WIDTH,XF_NPPC1> & flow_iter, xf::Mat<XF_8UC1,HEIGHT,WIDTH,XF_NPPC1> mat_imagepyr1[NUM_LEVELS] , xf::Mat<XF_8UC1,HEIGHT,WIDTH,XF_NPPC1> mat_imagepyr2[NUM_LEVELS] , xf::Mat<XF_8UC1, HEIGHT, WIDTH, XF_NPPC1> &inHarris, xf::Mat<XF_8UC1, HEIGHT, WIDTH, XF_NPPC1> &outHarris, unsigned int *list, ap_uint<64> *listfixed, int pyr_h[NUM_LEVELS], int pyr_w[NUM_LEVELS], unsigned int *num_corners, unsigned int harrisThresh, bool *harris_flag)

パラメーターの説明

次の表に、テンプレートと関数のパラメーターを説明します。
パラメーター 説明
flow ハードウェア関数を使用した反復計算中に、パックされたフロー ベクターを一時的に格納するために割り当てられた xf::Mat
flow_iter ハードウェア関数を使用した反復計算中に、パックされたフロー ベクターを一時的に格納するために割り当てられた xf::Mat
mat_imagepyr1 xf::Mat のサイズがピラミッド レベル数に等しい配列。最初の画像のピラミッド レベル数を保存します。
mat_imagepyr2 xf::Mat のサイズがピラミッド レベル数に等しい配列。2 つ目の画像のピラミッド レベル数を保存します。
inHarris Harris コーナー検出器への xf::Mat の入力画像
outHarris Harris 検出器からの出力画像。コーナーがその位置にある場合は画像は 255 となり、それ以外の場所は 0 になります。
list Harris 検出器で検出されたコーナーを格納するために割り当てられた 32 ビット メモリ (MAXCORNERS のサイズ)
listfixed xf::cornerUpdate で追跡するコーナーを格納するために割り当てられた 64 ビット メモリ (MAXCORNERS のサイズ)
pyr_h 各ピラミッド レベルで画像の高さを格納するための、画像のピラミッド レベル数を含む整数の配列
pyr_w 各ピラミッド レベルで画像の幅を格納するための、画像のピラミッド レベル数を含む整数の配列
num_corners Harris コーナー検出器で検出されたコーナーの数と同じサイズの配列
harrisThresh Harris コーナー検出器 (xf::harris) へのしきい値入力
harris_flag 入力画像のセットに対して xf::harris で検出されたコーナーを使用するために、この関数の呼び出し元で使用されるフラグ

画像処理

次に、ハードウェア パイプラインでの画像処理の手順を示します。

  1. xf::cornerharris を呼び出して最初の入力画像の処理を開始します。
  2. xf::cornerHarris の出力が SDSoC™ によりハードウェアでパイプライン処理され、 xf::cornersImgToList に送信されます。この関数は、コーナーが 255 か、それ以外の場合は 0 とマークされたコーナーを含む画像を取り込んで、それらをコーナーのリストに変換します。
  3. 同時に、 xf::pyrDown が 2 つの画像ピラミッドを作成し、反復ピラミッド型高密度オプティカル フローの例で説明されているように 2 つの画像ピラミッドを使用して高密度オプティカル フローを計算します。
  4. xf::densePyrOpticalFlow を 2 つの画像ピラミッドと一緒に入力として呼び出します。
  5. xf::cornerUpdate 関数を呼び出して、2 つ目の画像のコーナーを追跡します。harris_flag がイネーブルの場合 cornerUpdate はリストの出力からのコーナーを追跡し、それ以外の場合は前に追跡したコーナーを追跡します。
if(*harris_flag == true)
	{
	#pragma SDS async(1)
		xf::cornerHarris<FILTER_WIDTH,BLOCK_WIDTH,NMS_RADIUS,XF_8UC1,HEIGHT,WIDTH,XF_NPPC1,XF_USE_URAM>(inHarris, outHarris, Thresh, k);
	#pragma SDS async(2)
		xf::cornersImgToList<MAXCORNERS,XF_8UC1,HEIGHT,WIDTH,XF_NPPC1>(outHarris, list, &nCorners);
	}
	//Code to compute Iterative Pyramidal Dense Optical Flow
	if(*harris_flag == true)
	{
	#pragma SDS wait(1) 
	#pragma SDS wait(2)	
		*num_corners = nCorners;
	}
	if(flag_flowin)
	{
		xf::cornerUpdate<MAXCORNERS,XF_32UC1,HEIGHT,WIDTH,XF_NPPC1>(listfixed, list, *num_corners, flow_iter, (ap_uint<1>)(*harris_flag));
	}                                                                                
	else                                                                             
	{                                                                                
		xf::cornerUpdate<MAXCORNERS,XF_32UC1,HEIGHT,WIDTH,XF_NPPC1>(listfixed, list, *num_corners, flow, (ap_uint<1>)(*harris_flag));
	}
	if(*harris_flag == true)
	{
		*harris_flag = false;
	}

xf_corner_tracker_accel() 関数は、最初のフレームの処理中またはコーナーの再検出が必要なときにセットされる harris_flag というフラグを入力として使用します。xf::cornerUpdate 関数は、アップデートされたコーナーを xf::cornerImgToList の出力コーナー リストと同じメモリ ロケーションに出力します。つまり、harris_flag のセットが解除されたときの xf::cornerUpdate へのコーナー入力は、前のサイクルで追跡されたコーナー (現在の入力フレームの最初のフレームのコーナー) です。

高密度オプティカル フローが計算された後、harris_flag がセットされている場合は、xf::cornerharris で検出され、xf::cornersImgToList でアップデートされたコーナーの数が num_corners 変数にコピーされます。この変数は、xf_corner_tracker_accel() 関数の出力の 1 つです。もう 1 つの出力は、追跡されたコーナーのリスト listfixed です。harris_flag がセットされている場合は xf::cornerUpdate で list メモリ ロケーション内のコーナーが追跡され、セットされていない場合は listfixed メモリ ロケーション内のコーナーが追跡されます。

色検出

色検出アルゴリズムは、物体の色に基づいた色物体追跡および物体検出に使用されます。色に基づく方法は、物体と背景の色が大きく異なる場合に、物体の検出および分割に非常に有益です。

色検出の例では、xfOpenCV ライブラリから次の 4 つのハードウェア関数を使用します。

  • xf::RGB2HSV
  • xf::colorthresholding
  • xf:: erode
  • xf:: dilate

色検出の例では、まず元の BGR 画像の色空間を HSV 色空間に変換します。これは、HSV 色空間が色に基づく画像分割に最も適した色空間であるからです。その後、H (色相)、S (彩度)、および V (値) 値に基づいて HSV 画像に対してしきい値処理を実行し、255 または 0 を返します。画像のしきい値処理後、erode (モルフォロジカル オープニング) および dilate (モルフォロジカル オープニング) 関数を適用して画像の不要な白い斑点 (ノイズ) を削減します。この例では、erode および dilate 関数の 2 つのハードウェア インスタンスを使用しして、erode の後に dilate を適用し、もう一度 dilate を適用してその後に erode を適用します。

図: 色検出

次の例に、色検出アルゴリズムを示します。

void colordetect_accel(xf::Mat<XF_8UC3, HEIGHT, WIDTH, XF_NPPC1> &_src,
		xf::Mat<XF_8UC3, HEIGHT, WIDTH, XF_NPPC1> &_rgb2hsv,
		xf::Mat<XF_8UC1, HEIGHT, WIDTH, XF_NPPC1> &_thresholdedimg,
		xf::Mat<XF_8UC1, HEIGHT, WIDTH, XF_NPPC1> &_erodeimage1,
		xf::Mat<XF_8UC1, HEIGHT, WIDTH, XF_NPPC1> &_dilateimage1,
		xf::Mat<XF_8UC1, HEIGHT, WIDTH, XF_NPPC1> &_dilateimage2,
		xf::Mat<XF_8UC1, HEIGHT, WIDTH, XF_NPPC1> &_dst,
		unsigned char *low_thresh, unsigned char *high_thresh){

xf::RGB2HSV< XF_8UC3,HEIGHT, WIDTH, XF_NPPC1>(_src, _rgb2hsv);
xf::colorthresholding<XF_8UC3,XF_8UC1,MAXCOLORS,HEIGHT,WIDTH, XF_NPPC1>(_rgb2hsv,_  thresholdedimage, low_thresh, high_thresh);
xf::erode<XF_BORDER_CONSTANT,XF_8UC1,HEIGHT, WIDTH, XF_NPPC1>(_thresholdedimg, _      erodeimage1);
	xf::dilate<XF_BORDER_CONSTANT,XF_8UC1,HEIGHT, WIDTH, XF_NPPC1>(_ erodeimage1, _ dilateimage1);
	xf::dilate<XF_BORDER_CONSTANT,XF_8UC1,HEIGHT, WIDTH, XF_NPPC1>(_ dilateimage1, _ dilateimage2);
	xf::erode<XF_BORDER_CONSTANT,XF_8UC1,HEIGHT, WIDTH, XF_NPPC1>(_ dilateimage2, _dst);

}

この例では、ソース画像が xf::RGB2HSV 関数に渡され、その出力が xf::colorthresholding モジュールに渡され、しきい値処理された画像が xf::erode 関数、そして xf::dilate 関数に渡されて、最終的な出力画像が返されます。

ガウシアン フィルターの差

ガウシアン フィルターの差の例では、xfOpenCV ライブラリから次の 4 つのハードウェア関数を使用します。
  • xf::GaussianBlur
  • xf::duplicateMat
  • xf::delayMat
  • xf::subtract

ガウシアン フィルターの差関数は、元のソース画像にガウシアン フィルターを適用し、そのガウシアンぼけ画像を複製して 2 つの画像とします。複製された画像の 1 つに GaussianBlur 関数を適用し、もう 1 つの画像はそのまま保存します。その後、ガウシアンが 2 回適用された画像と複製されたもう 1 つの画像に対して減算関数を実行します。この時点で、複製されたもう 1 つの画像は、もう 1 つの画像に 2 回目のガウシアンが適用されて少なくとも 1 ピクセルの出力が生成されるまで待機する必要があります。そのため、xf::delayMat 関数を使用して遅延を追加します。

図: ガウシアン フィルターの差

次に、ガウシアン フィルターの差の例を示します。

void gaussian_diff_accel(xf::Mat<XF_8UC1,HEIGHT,WIDTH,NPC1> &imgInput,
		xf::Mat<XF_8UC1,HEIGHT,WIDTH,XF_NPPC1> &imgin1,
		xf::Mat<XF_8UC1,HEIGHT,WIDTH, XF_NPPC1> &imgin2,
		xf::Mat<XF_8UC1,HEIGHT,WIDTH, XF_NPPC1> &imgin3,
		xf::Mat<XF_8UC1,HEIGHT,WIDTH, XF_NPPC1> &imgin4,
		xf::Mat<XF_8UC1,HEIGHT,WIDTH, XF_NPPC1> &imgin5,
		xf::Mat<XF_8UC1,HEIGHT,WIDTH, XF_NPPC1>&imgOutput,
float sigma)
{

	xf::GaussianBlur<FILTER_WIDTH, XF_BORDER_CONSTANT, XF_8UC1, HEIGHT, WIDTH, XF_NPPC1>
(imgInput, imgin1, sigma);
	xf::duplicateMat<XF_8UC1, HEIGHT, WIDTH, XF_NPPC1>(imgin1,imgin2,imgin3);
	xf::delayMat<MAXDELAY, XF_8UC1, HEIGHT, WIDTH, XF_NPPC1>(imgin3,imgin5);
	xf::GaussianBlur<FILTER_WIDTH, XF_BORDER_CONSTANT, XF_8UC1, HEIGHT, WIDTH, XF_NPPC1>
(imgin2, imgin4, sigma);
xf::subtract<XF_CONVERT_POLICY_SATURATE, XF_8UC1, HEIGHT, WIDTH, XF_NPPC1>(imgin5,imgin4,imgOutput);

}

この例では、ソース画像 imginput に GaussainBlur 関数が適用され、その結果の画像 imgin1 が xf::duplicateMat に渡されます。imgin2imgin3 は、ガウシアンが適用された画像の複製です。imgin2 にはもう一度 GaussianBlur が適用され、その結果が imgin4 に保存されます。その後、imgin4imgin3 の間で減算が実行されます。ただし、imgin3imgin4 の少なくとも 1 ピクセルが生成されるまで待機する必要があります。そのため imgin3 に遅延が適用され、imgin5 に保存されます。最後に imgin4imgin5 の間で減算が実行されます。

ステレオ ビジョン パイプライン

視差マップの生成は、環境の 3 次元マップを作成する最初の手順の 1 つです。xfOpenCV ライブラリには、ステレオ カメラからのカメラ パラメーターおよび入力を取り込んで視差マップを計算する画像処理パイプラインをビルドするためのコンポーネントが含まれています。

パイプラインに関連する 2 つの主なコンポーネントは、ステレオ平行化とローカル ブロック マッチング方法を使用した視差推定です。ローカル ブロック マッチングを使用した視差推定は xfOpenCV の個別のコンポーネントですが、平行化ブロックは xf::InitUndistortRectifyMapInverse()xf::Remap() を使用して作成できます。次に、データフローパイプラインを示します。カメラのパラメーターは、パイプラインへの追加の入力です。

図: ステレオ ビジョン パイプライン

次に、パイプラインのコードを示します。

void stereopipeline_accel(xf::Mat<XF_8UC1, XF_HEIGHT, XF_WIDTH, XF_NPPC1> &leftMat, xf::Mat<XF_8UC1, XF_HEIGHT, XF_WIDTH, XF_NPPC1> &rightMat, xf::Mat<XF_16UC1, XF_HEIGHT, XF_WIDTH, XF_NPPC1> &dispMat,
	xf::Mat<XF_32FC1, XF_HEIGHT, XF_WIDTH, XF_NPPC1> &mapxLMat, xf::Mat<XF_32FC1, XF_HEIGHT, XF_WIDTH, XF_NPPC1> &mapyLMat, xf::Mat<XF_32FC1, XF_HEIGHT, XF_WIDTH, XF_NPPC1> &mapxRMat, 
	xf::Mat<XF_32FC1, XF_HEIGHT, XF_WIDTH, XF_NPPC1> &mapyRMat, xf::Mat<XF_8UC1, XF_HEIGHT, XF_WIDTH, XF_NPPC1> &leftRemappedMat, xf::Mat<XF_8UC1, XF_HEIGHT, XF_WIDTH, XF_NPPC1> &rightRemappedMat,
	xf::xFSBMState<SAD_WINDOW_SIZE,NO_OF_DISPARITIES,PARALLEL_UNITS> &bm_state, ap_fixed<32,12> *cameraMA_l_fix, ap_fixed<32,12> *cameraMA_r_fix, ap_fixed<32,12> *distC_l_fix, ap_fixed<32,12> *distC_r_fix, 
	ap_fixed<32,12> *irA_l_fix, ap_fixed<32,12> *irA_r_fix, int _cm_size, int _dc_size)
{
	xf::InitUndistortRectifyMapInverse<XF_CAMERA_MATRIX_SIZE,XF_DIST_COEFF_SIZE,XF_32FC1,XF_HEIGHT,XF_WIDTH,XF_NPPC1>(cameraMA_l_fix,distC_l_fix,irA_l_fix,mapxLMat,mapyLMat,_cm_size,_dc_size);
	xf::remap<XF_REMAP_BUFSIZE,XF_INTERPOLATION_BILINEAR,XF_8UC1,XF_32FC1,XF_8UC1,XF_HEIGHT,XF_WIDTH,XF_NPPC1,XF_USE_URAM>(leftMat,leftRemappedMat,mapxLMat,mapyLMat);

	xf::InitUndistortRectifyMapInverse<XF_CAMERA_MATRIX_SIZE,XF_DIST_COEFF_SIZE,XF_32FC1,XF_HEIGHT,XF_WIDTH,XF_NPPC1>(cameraMA_r_fix,distC_r_fix,irA_r_fix,mapxRMat,mapyRMat,_cm_size,_dc_size);
	xf::remap<XF_REMAP_BUFSIZE,XF_INTERPOLATION_BILINEAR,XF_8UC1,XF_32FC1,XF_8UC1,XF_HEIGHT,XF_WIDTH,XF_NPPC1,XF_USE_URAM>(rightMat,rightRemappedMat,mapxRMat,mapyRMat);

	xf::StereoBM<SAD_WINDOW_SIZE,NO_OF_DISPARITIES,PARALLEL_UNITS,XF_8UC1,XF_16UC1,XF_HEIGHT,XF_WIDTH,XF_NPPC1,XF_USE_URAM>(leftRemappedMat, rightRemappedMat, dispMat, bm_state);
}