Vitisビジョンライブラリのxf::cv::Matクラスについて

ビジョンライブラリ
スポンサーリンク

本記事の概要

概要

Vitisビジョンライブラリで画像を取り扱うためのクラスxf::cv::Matの宣言の仕方とテンプレート引数、メンバについて解説。

Xilinxの統合ソフトウェア開発環境では、Vitisビジョンライブラリ(以下、ビジョンライブラリ)をインクルードすることによって、画像処理機能を高速化(アクセラレーション)することが可能です。例えば、OpenCVのフィルタ処理などを、FPGAに実装可能なロジックへと高位合成することができます。

ビジョンライブラリのインクルードの方法は以下の記事にまとめています。

ビジョンライブラリで用意されている関数は、OpenCVの関数と同様に扱うことができるのですが、完全に一緒ではないので、必要に応じてドキュメンテーションを参照しなくてはいけません。

https://xilinx.github.io/Vitis_Libraries/vision/2021.2/overview.html

中でも、xf::cv::Matクラスは画像を格納する変数のクラスで、ビジョンライブラリでは超基本的な概念なので、改めて使い方を整理しておこうと思いました。

本記事では、ビジョンライブラリ(バージョン2021.2)の用例を理解できる程度にxf::cv::Matクラスの扱い方をまとめたいと思います。詳細は以下のドキュメンテーションに掲載されているので、より深く知りたい方はこちらを参照いただければと思います。

https://xilinx.github.io/Vitis_Libraries/vision/2021.2/api-reference.html#vitis-vision-library-functions
この記事の対象読者
  • Vitisビジョンライブラリを初めて扱う
  • ビジョンライブラリのMatクラスのメンバやメソッドなど使い方を知りたい
  • OpenCVのMatクラスを知っていて、ビジョンライブラリのMatクラスとの違いを知りたい
おすすめ書籍

OpenCVに関する書籍は数多く出版されているので、まずはOpenCVのMatクラスを理解したうえで、ビジョンライブラリのMatクラスでは何が異なるかを確認するのがよいと思います。
ビジョンライブラリではソースコードがC/C++で記述されているので、私はOpenCVの記述の仕方もC++の環境に関する書籍で勉強しました。初学者向けの以下の書籍が参考になると思います。

xf::cv::Matクラス

宣言の仕方

まず、大前提として、ビジョンライブラリで用意されているクラスや関数を宣言するには、名前空間 xf::cv::で宣言する必要があります。OpenCVの名前空間cv::に類似の概念ですが、ビジョンライブラリではより上位の名前空間xf::から宣言しなくてはいけないのが違いです。

画像(厳密には行列)を格納するためのMatクラスのオブジェクト(srcという変数名)を宣言するときに、OpenCVでは例えば

cv::Mat src;

と宣言しますが、ビジョンライブラリでは

xf::cv::Mat<TYPE, HEIGHT, WIDTH, NPPC> src;

と宣言します。接頭辞にxf::cv::をつける必要があります。

テンプレート引数

OpenCVと異なる点がもう一つあります。<>で囲まれている4種類のテンプレート引数を指定する必要があります。

xf::cv::Matクラスのテンプレート引数
テンプレート引数説明
TYPEピクセルのデータ型
HEIGHT画像の最大高さ
WIDTH画像の最大幅
NPC1クロックサイクルごとに処理されるピクセル数
ビジョンライブラリのxf_structs.hppを参照

テンプレート引数というのは、関数やクラスを定義するときに、変数のデータ型を個別に定義できるようにするものです。データ型が異なっても、関数やクラスの形は全く同様に扱うことができ、関数とクラスを一括で定義することができます。

おそらく高位合成で生成されるロジックのリソースをユーザーが柔軟に変更できるように、データ型を別に指定できるようにしているのだと思います。例えば、より少ないリソースでロジックを合成したいときに、必要以上のビット深さ・チャネル数のピクセルや、無駄に大きいサイズの画像を格納できる領域のメモリを確保しないように、ピクセルのデータ型を縮小したり、画像のサイズを小さくしたりできるようにしています。

xf::cv::Matクラスのオブジェクトを宣言するときに必要なテンプレート引数のイメージを図示しました。

テンプレート引数HEIGHTWIDTHは、行列をxf::cv::Matクラスの変数に代入する際の最大の行数(row)列数(column)を表します。

例えば、640×480ピクセルの解像度の画像を格納する場合には、HEIGHTは480以上、WIDTHは640以上に指定したxf::cv::Matクラスで変数を宣言しなくてはいけません。なお、テンプレート引数で指定した最大高さや幅よりも大きい画像を格納しようとすると、コンストラクタで初期化するときにエラーが発生します。

OpenCVのMatクラスでは、画像の高さと幅の情報はクラスのメンバに含まれているので、最大高さと幅を指定する必要はありませんでした。ビジョンライブラリでも、後述する通り、画像の高さと幅もクラスのメンバに含まれています。しかし、メンバとは別にテンプレート引数も用意されている理由は、高位合成で画像を格納するためのメモリを用意するときに、配列の深さが画像の最大幅・高さによって変わってしまうため、最低限のメモリだけ用意できるようにデータ型として別に指定できるようにしているのだと想像します。

ピクセルのデータ型

画像の1画素をピクセルといいますが、1ピクセルのデータ型、例えば1画素の分解能を何ビットとするか、白黒なのかグレースケールなのかカラーなのか(チャネル数)といった情報をTYPEで指定します。

ピクセルのデータ型は、以下のような命名規則で宣言されています。

データ型の命名規則

XF_ ビット深さn 符号の有無・固定 or 浮動 チャネル数C

  • ビット深さn: 1ピクセルの量子化ビット
  • 符号の有無・固定or浮動: 符号なし整数(U)、符号あり整数(S)、浮動小数点数(F)
  • チャネル数: 1~4の整数。グレースケールでは1、BGR3色扱う場合は3、BGRAを扱う場合は4など。

例えば、8ビットグレースケール(チャネル数1)の画像を扱いたい場合は、XF_8UC1と指定。

ビジョンライブラリのドキュメンテーションに一覧表がありますが、そこから有用なデータ型を抜粋しておきます。

ピクセルのデータ型の例
TYPEビット深さデータ型チャネル数
XF_8UC18bitunsigned char(符号無し8bit整数)1
XF_8UC38bit unsigned char(符号無し8bit整数)3
XF_16SC116bit short (符号付き16bit整数)1
XF_16SC316bit short (符号付き16bit整数)3
XF_32FC132bit float (単精度浮動小数点型)1
XF_32FC332bit float (単精度浮動小数点型)3

NPC、NPPC (The Numbers of Pixels Per Cycle)

NPC(NPPCも同義)は、ビジョンライブラリにおける関数で画像処理するときに、1クロックサイクルごとに処理するピクセル数を表します。ビジョンライブラリでは、1クロックサイクルで処理する分のデータを、1ワードとして配列の1要素に格納しているようで、NPCは1ワードあたりのピクセル数とも等しいです。

NPCは1クロックサイクルごとに処理するピクセル数。
画素値を格納する配列の1要素(ワード)のピクセルサイズに等しい。

※ビジョンライブラリのxf_structs.hppとxf_types.hppを参照

ほとんどの関数では、シングルピクセル処理(XF_NPPC1)と8ピクセル並列処理(XF_NPPC8)の2種類のオプションをサポートしています。この2つを抑えておけばよいでしょう。レイテンシーに余裕があるときはシングルピクセル処理、リソースに余裕があるときは8ピクセル並列処理という使い分けが可能です。

メンバ

次にMatクラスのメンバを説明します。xf::cv::Matクラスには、5種類のメンバが含まれています。OpenCV4.4.0のMatクラスではもっと多くのメンバが含まれているようですが、ビジョンライブラリでは5種類だけなので把握しやすいです。

xf::cv::Matクラスのメンバ
メンバデータ型説明
rowsint
4byte符号付き整数
画像内の行数または画像の高さ
colsint
4byte符号付き整数
画像内の列数または画像の幅
sizeint
4byte符号付き整数
dataメンバーに格納されるワード数
全ピクセル数rows × colsを1ワードあたりのピクセル数NPCで割った値から算出
allocatedFlagunsigned char
1byte符号無し整数
メモリアロケーション(メモリ領域を確保しているかどうか)のステータスを示すフラグ
data1ワードのデータ型に依存画素を格納した配列へのポインタ変数。配列は1ワードを1要素として格納している。

先ほど、テンプレート引数を説明した図にメンバを追加しました。テンプレート引数では格納される画像の最大高さと最大幅をそれぞれ指定しました。実際にxf::cv::Matクラスのオブジェクトを宣言し、そのオブジェクトに画像を割り当てるとき、実際の画像の高さと幅をrowsとcolsに割り当てます。

次に、画像の各ピクセルの画素値を配列に割り当てるときに、配列のデータ型、配列へのポインタ、そして要素数をそれぞれ決める必要があります。

まず、配列のデータ型は、テンプレート引数のTYPEとNPCから一意に決まります

例えば、TYPEがXF_8UC1、NPCがXF_NPPC8の場合を考えてみましょう。
XF_8UC1では1ピクセルあたり8bitの分解能で、それが1チャネル(C=1)だけ与えられます。そして、NPPCが8なので、1ワードあたり8ピクセルの画素を格納します。つまり、1ワードのサイズは8bit×1チャネル×8ピクセルの64bitとなります。
したがって、配列の1要素のデータ型は符号無し64bit整数のap_uint<64>です。この1ワードを一かたまりにして配列の1要素に格納しています。

次に、配列の要素数画像の全ピクセル数(rows × cols)を、1ワードあたりのピクセル数NPCで割った値に等しいです。これを、Matクラスのメンバsizeとして割り当てています。

そして、画素値を格納している配列をdata[size]とし、配列へのポインタをメンバdataとしています。

実際に、オブジェクト内で配列のためのメモリが確保されているかどうかをallocatedFlagで管理し、必要以上にメモリが確保されている場合は、free()関数でメモリの解放を行うかどうか判断することができるようになっています。

メソッド(メンバ関数)

簡単にMatクラスに含まれる有用なメソッドを紹介します。紹介するメソッド以外にも多くのメソッドが含まれるので、詳細はドキュメンテーションを参照いただければと思います。

コンストラクタ

まずは、xf::cv::Matクラスのオブジェクトを宣言・初期化するためのコンストラクタを紹介します。先ほど、Matクラスのオブジェクトを宣言するための記述として、デフォルトコンストラクタの

xf::cv::Mat<TYPE, HEIGHT, WIDTH, NPC> src;

を示しました。Matクラスには、デフォルトコントラクタ以外にもいくつか別のコンストラクタが用意されています。

例えば、行数(rows)と列数(columns)をそれぞれ指定することもできます。

xf::cv::Mat<TYPE, HEIGHT, WIDTH, NPC> in_img(rows, cols);

デフォルトコンストラクタのようにrowsとcolsを指定しない場合は、HEIGHTとWIDTHがそのままrowsとcolsになります。

あるいは、別のMatオブジェクトをクローンすることもできます。例えば

xf::cv::Mat<TYPE, HEIGHT, WIDTH, NPC> dst(src);

とすると、srcというオブジェクトを、dstにクローンすることができます。

有用なメソッド

copyData

Matクラスのオブジェクトdstに、別のオブジェクトsrcの画像をコピーするときにcopyDataというメソッドを使うことができます。

dst.copyData(src);

copyDataは画像の高さrowsや幅cols、配列へのポインタをコピーすることはせず、オブジェクトdstのdata配列にsrcのdata配列の値のみをコピーします。

read

配列dataの1要素を参照したいときには、readメソッドが有効です。
次のように、readの引数に参照したい要素のインデックスを入力すれば、要素の値が返されます。1画素の値ではなく1ワード分の値が返されるのに注意しましょう。例えば、チャネルやNPPCが複数ある場合、1ピクセルの画素値を得るためにはビットを分割する必要があります。

xf::cv::Mat<XF_8UC3, HEIGHT, WIDTH, XF_NPPC1> src(rows, cols);
ap_uint<24> a;

a = src.read(index);
write

readとは逆に配列dataの1要素を書き換えたいときには、writeメソッドを使うことができます。書き換えたい要素のインデックスと変更後の値をそれぞれ代入すれば、変更ができます。

xf::cv::Mat<XF_8UC3, HEIGHT, WIDTH, XF_NPPC1> src(rows, cols);
ap_uint<24> val = hogehoge;

src.write(index, val);

まとめ

Vitis HLSでビジョンライブラリを使うにあたり、ドキュメントを参考に、Matクラスの使い方をまとめました。最後までご覧いただきありがとうございました!

スポンサーリンク


参考にしたサイト

コメント