AI高位合成ツール「AIRTIPS」で生成した独自AIアクセラレータ(4DTC)をKV260で動かす方法~AIRTIPS実践編②~

sharp_engineer · note(ノート)

研究開発本部 中村 龍昇(なかむら たつのり)

はじめに

本稿では、独自AIアクセラレータ(4DTC)を用いて、AI超解像モデルをFPGA基板であるKV260上で動作させる方法について解説します。
本システムの紹介動画をYoutubeに公開しておりますので、ぜひご覧ください。

紹介動画にあるように、4DTCを活用することで、映像を高速に超解像処理することが可能になります。本稿では、その方法について詳しく解説します。

AI高位合成ツール「AIRTIPS」は、以下のリンクからダウンロードすることができます。

https://corporate.jp.sharp/8k5g/8klab/ai-edge_report_202505.html

本稿を読む前に、是非、

AIRTIPS導入編

実践編①

を見てください。

まず、動作させるAI超解像モデルについて説明します。
次に、4DTCを用いて、AI超解像モデルをKV260で動かす方法について説明します。
さらに、4DTCの特長機能であるレイヤ統合により、最適化したモデルへ変更する方法を説明します。最適化モデルへの変更は手動で行います。
最後に、Webカメラ等からUSB伝送される映像をAI超解像するサンプルプログラムについて説明します。

AI超解像モデル

本稿では、1 層 16 チャンネルの独自AI超解像モデル(sr_evalmodel)を動かします。FSRCNNをベースに、チャンネル数等を調整したもので、レイヤ構成は以下の通りです。

レイヤ構成(1層16チャンネル、RGBの場合)パラメータ数は、7779個で、内訳は以下の通りです。

================================================================= Layer (type:depth-idx) Param # ================================================================= FSRCNN -- ├─Sequential: 1-1 -- │ └─Conv2d: 2-1 448 │ └─ClipBLK: 2-2 -- ├─Sequential: 1-2 -- │ └─ResBlock: 2-3 -- │ │ └─Sequential: 3-1 4,640 │ └─ReLU: 2-4 -- │ └─Conv2d: 2-5 2,320 ├─Sequential: 1-3 -- │ └─ConvTranspose2d: 2-6 260 │ └─ClipBLK: 2-7 -- │ └─Conv2d: 2-8 111 │ └─ClipBLK: 2-9 -- ================================================================= Total params: 7,779 Trainable params: 7,779 Non-trainable params: 0 =================================================================本AI超解像モデルの先頭のレイヤは、ichannel=3、ochannel=16、kernel=3のConv2d(2-1)です。次に、ichannel=ochannel=16、kernel=3のConv2dが3層(2-3、2-5)つづきます。その次のレイヤは、ichannel=16、ochannel=4、kernel=2のConvTranspose2d(2-6)です。最後のレイヤは、ichannel=4、ochannel=3、kernel=3のConv2d(2-8)です。
ReLUの代わりにClipBLKを使用している部分があります。ClipBLKではMin値(0)とともに、Max値(255)でも値をクリップします。

AIRTIPSによる4DTC用Cプログラムの作成

AIRTIPSの実行

AI高位合成ツール(AIRTIPS_v1.0.zip)の sample/sr/sr_evalmodelフォルダにあるreadme.mdを参考に、4DTCのエミュレータの実行までの動作を確認して下さい。KV260でAIRTIPSの動作確認を行うと時間がかかるため、

実践編①

に記載するVivado合成用PC等で行うことを推奨します。

~/DEVREL_AIRTIPS/sample/sr/sr_evalmodel/readme.md動作を確認した結果、/sample/sr/sr_evalmodel/4dtcフォルダにmodels.cppがあることを確認して下さい。

KV260でのAI超解像の確認

KV260で、4DTCのROMロード

AIRTIPS実践編①

と同様に、まず、4DTCのROMロードを実行します。
sudo xmutil listappsで、ロードされているROMファイルを確認します。起動時はk26-starter-kitsがロードされています。

ubuntu@kria:~$ sudo xmutil listapps Accelerator Accel_type Base Base_type #slots(PL+AIE) Active_slot k26-starter-kits XRT_FLAT k26-starter-kits XRT_FLAT (0+0) 0, project_1 XRT_FLAT project_1 XRT_FLAT (0+0) -1sudo xmutil unloadappで、ROMファイルをアンロードします。

ubuntu@kria:~$ sudo xmutil unloadapp Accelerator successfully removed.project_1のROMファイルをロードします。

ubuntu@kria:~$ sudo xmutil loadapp project_1 Accelerator loaded to slot 04DTCを動かすためのファイルを、KV260へコピー

KV260上で、AI高位合成ツール(AIRTIPS_v1.0.zip)を展開して下さい。展開したフォルダをAIRTIPSとして説明します。
「AIRTIPSの実行」の節で確認した/sample/sr/sr_evalmodel/4dtcフォルダのmodels.cppを、KV260の同じフォルダへコピーします。KV260でのフォルダ内のファイルを以下に示します。

ubuntu@kria:~/AIRTIPS/sample/sr/sr_evalmodel/4dtc$ ls 4dtc.h 4dtc_emu.cpp main_sr_models.cpp make_emu.sh pams.cpp 4dtc_dmac.cpp kiyomizu.bmp make.sh models.cpp pams.h4DTCの動作確認

make.shを実行します。modelsという名前の実行ファイルができていることを確認して下さい。

ubuntu@kria:~/AIRTIPS/sample/sr/sr_evalmodel/4dtc$ sh make.sh./modelsを実行します。sudoコマンドをつけて実行して下さい。sudoコマンドをつけない場合、Segmentation fault (core dumped)になります。./modelsを実行した結果の一例を以下に示します。

ubuntu@kria:~/AIRTIPS/sample/sr/sr_evalmodel/4dtc$ sudo ./models racehorses.bmp bo0.size() :0xc000000 bo0.address():0x37300000 bo0.map() :0xffffa9670000 O.K. AI FFFFB571D000 A1000000 AI RAM FFFFA9670000 37300000 Load racehorses.bmp ! *** AI Super Resolution(4DTC) *** Time : 144.918ms Save out_4dtc.bmp !入力画像は別途準備したracehorses.bmp(解像度 160x120)を使いました。もともとフォルダにあるkiyomizu.bmpを使用しても問題ありません。
AI超解像により2倍に拡大された画像はout_4dtc.bmp(解像度 320x240)です。

入力画像

AI超解像後の画像参考として、エミュレータ(4DTCを使用せず、CPUのみで計算)を実行した結果を以下に示します。

ubuntu@kria:~/AIRTIPS/sample/sr/sr_evalmodel/4dtc$ ./models_emu racehorses.bmp Load racehorses.bmp ! *** AI Super Resolution(4DTC) *** Time : 8991.181ms Save out_4dtc.bmp !レイヤ統合による最適化モデル

レイヤ統合による最適化とは

AIアクセラレータの高速化および低消費電力化を実現するためには、DDRなどの外部メモリへのアクセスを削減し、内部メモリ(オンチップメモリ)へのアクセスを増加させる手法が有効です。
我々が開発したAIアクセラレータである4DTCは、多数のラインバッファを用い、複数のレイヤの演算をライン単位でパイプライン処理することで、フレームメモリであるDDRメモリへのアクセスを削減することができます。
複数のレイヤの演算を統合し、フレームメモリへのアクセスを削減することを、レイヤ統合による最適化とよんでいます。

レイヤ統合前の処理ブロック図を以下に示します。AI高位合成ツールであるAIRTIPSを用いると、Pythonプログラム(models.py)から、以下のような処理のCプログラム(models.cpp)を自動で生成することができます。

レイヤ統合前の処理ブロック図今回は、レイヤ統合により以下のような処理ブロックへ手動で変更し、動作を確認します。

レイヤ統合後の処理ブロック図レイヤ統合前は、加算処理とReLU(Clip)とをそれぞれ単独でCPU処理しています。レイヤ統合後は、Conv2dと同様に、加算処理やReLUを4DTCでHW処理するため、処理時間を短縮することができます。

models.cppの変更

models.cppのvoid make_param()の後段を以下のように修正してください。make_conv2d_param、及びmake_convtranspose2d_paramの引数の値は参考値です。引数の値については、以下で説明しますので、作成したmodels.cppの各パラメータを考慮して変更してください。

629 /* 630 make_conv2d_param(parameter_0_0, para_0_0, 3, 3, 16, 0x40, 0x0, 127, -128); 631 para_a_0_0_0[0]=(int8_t *)parameter_a_0_0_0; 632 make_conv2d_param(parameter_1_0_0_0, para_1_0_0_0, 3, 16, 16, 0x40, 0x0, 127, -128); 633 para_a_0_0_1[0]=(int8_t *)parameter_a_0_0_1; 634 make_conv2d_param(parameter_1_0_0_2, para_1_0_0_2, 3, 16, 16, 0x40, 0x0, 127, -128); 635 para_a_0_0_2[0]=(int8_t *)parameter_a_0_0_2; 636 para_a_0_0_3[0]=(int8_t *)parameter_a_0_0_3; 637 make_add_param(parameter_a_0_0_4, para_a_0_0_4, 127, -128); 638 para_1_1[0]=(int8_t *)parameter_1_1; 639 make_conv2d_param(parameter_1_2, para_1_2, 3, 16, 16, 0x40, 0x0, 127, -128); 640 make_add_param(parameter_a_0_0_5, para_a_0_0_5, 127, -128); 641 make_convtranspose2d_param(parameter_2_0, para_2_0, 2, 16, 4, 0x40, 0x0, 127, -128); 642 para_a_0_0_6[0]=(int8_t *)parameter_a_0_0_6; 643 make_conv2d_param(parameter_2_2, para_2_2, 3, 4, 3, 0x40, 0x0, 127, -128); 644 para_a_0_0_7[0]=(int8_t *)parameter_a_0_0_7; 645 para_a_0_0_8[0]=(int8_t *)parameter_a_0_0_8; 646 */ 647 make_conv2d_param(parameter_0_0, para_0_0, 3, 3, 16, 0x40, 0x0, 127, 0); 648 make_conv2d_param(parameter_1_0_0_0, para_1_0_0_0, 3, 16, 16, 0x40, 0x0, 127, 0); 649 make_conv2d_param(parameter_1_0_0_2, para_1_0_0_2, 3, 16, 16, 0x4, 0x20, 127, 0); 650 make_conv2d_param(parameter_1_2, para_1_2, 3, 16, 16, 0x20, 0x8, 127, -128); 651 make_convtranspose2d_param(parameter_2_0, para_2_0, 2, 16, 4, 0x40, 0x0, 127, 0); 652 make_conv2d_param(parameter_2_2, para_2_2, 3, 4, 3, 0x40, 0x0, 127, 0); 653 } 次に、models.cppのvoid modelsを変更します。変更前と変更後とを以下に記載します。

//変更前 647 void models( int8_t *x, int in_sizex, int in_sizey, int8_t *y, int iwidth, int owidth) 648 { 649 ai_sizex=in_sizex; 650 ai_sizey=in_sizey; 651 pams_conv2d(dd_0_, x, &ai_sizex, &ai_sizey, para_0_0, 3, 3, 16, 1, 1, iwidth, SizeX*8*1); 652 pams_clip(dd_1_, dd_0_, &ai_sizex, &ai_sizey, 0.0, 255.0, para_a_0_0_0, 16, SizeX*8*1); 653 pams_conv2d(dd_2_, dd_1_, &ai_sizex, &ai_sizey, para_1_0_0_0, 3, 16, 16, 1, 1, SizeX*8*1, SizeX*8*1); 654 pams_clip(dd_3_, dd_2_, &ai_sizex, &ai_sizey, 0.0, 255.0, para_a_0_0_1, 16, SizeX*8*1); 655 pams_conv2d(dd_4_, dd_3_, &ai_sizex, &ai_sizey, para_1_0_0_2, 3, 16, 16, 1, 1, SizeX*8*1, SizeX*8*1); 656 pams_clip(dd_5_, dd_4_, &ai_sizex, &ai_sizey, -255.0, 255.0, para_a_0_0_2, 16, SizeX*8*1); 657 float tmp0[1]={0.125}; 658 pams_init(init_0, &ai_sizex, &ai_sizey, 16, 1, tmp0, SizeX*8*1); 659 pams_mul(dd_6_, dd_5_, init_0, &ai_sizex, &ai_sizey, para_a_0_0_3, 16, SizeX*8*1); 660 pams_add(dd_7_, dd_6_, dd_1_, &ai_sizex, &ai_sizey, para_a_0_0_4, 16, SizeX*8*1); 661 pams_relu(dd_8_, dd_7_, &ai_sizex, &ai_sizey, para_1_1, 16, SizeX*8*1); 662 pams_conv2d(dd_9_, dd_8_, &ai_sizex, &ai_sizey, para_1_2, 3, 16, 16, 1, 1, SizeX*8*1, SizeX*8*1); 663 pams_add(dd_10_, dd_9_, dd_1_, &ai_sizex, &ai_sizey, para_a_0_0_5, 16, SizeX*8*1); 664 pams_convtranspose2d(dd_11_, dd_10_, &ai_sizex, &ai_sizey, para_2_0, 2, 16, 4, 2, 0, 0, SizeX*8*1, SizeX*8*2); 665 pams_clip(dd_12_, dd_11_, &ai_sizex, &ai_sizey, 0.0, 255.0, para_a_0_0_6, 4, SizeX*8*2); 666 pams_conv2d(dd_13_, dd_12_, &ai_sizex, &ai_sizey, para_2_2, 3, 4, 3, 1, 1, SizeX*8*2, owidth); 667 pams_clip(dd_14_, dd_13_, &ai_sizex, &ai_sizey, 0.0, 255.0, para_a_0_0_7, 3, owidth); 668 pams_shift(y, dd_14_, &ai_sizex, &ai_sizey, para_a_0_0_8, 3, owidth); 669 }//変更後 655 void models( int8_t *x, int in_sizex, int in_sizey, int8_t *y, int iwidth, int owidth) 656 { 657 ai_sizex=in_sizex; 658 ai_sizey=in_sizey; 659 660 pams_conv2d(dd_1_, x, &ai_sizex, &ai_sizey, 661 para_0_0, 3, 3, 16, 1, 1, iwidth, SizeX*8); 662 663 pams_conv2dx2_add(dd_8_, dd_1_, dd_1_, &ai_sizex, &ai_sizey, 664 para_1_0_0_0, 3, 665 para_1_0_0_2, 3, 16, 16, 16, SizeX*8, SizeX*8); 666 667 pams_conv2d_add(dd_10_, dd_8_, dd_1_, &ai_sizex, &ai_sizey, 668 para_1_2, 3, 16, 16, 1, 1, SizeX*8, SizeX*8); 669 670 pams_convtranspose2d_conv2d(y, dd_10_, &ai_sizex, &ai_sizey, 671 para_2_0, 2, 672 para_2_2, 3, 16, 4, 3, 2, SizeX*8, owidth); 673 674 }参考のため行数を記載しています。
一旦、本修正を行った後、models.cppをコンパイルして出力画像を確認してみてください。出力画像の色などがおかしい場合、以下の記載を参照し、make_conv2d_paramや、make_convtranspose2d_paramの引数の値を調整し、再度、コンパイルし出力画像を確認して下さい。

models.cppの修正内容について説明します。
まずは一番先頭のConv2D処理(660行目、661行目のpams_conv2d)について説明します。
pams.hでの関数宣言は、以下の通りです。

void pams_conv2d(int8_t *output, int8_t *input, int *sizex, int *sizey, int8_t *param[], int kernel, int ichannel, int ochannel, int stride, int padding, int iwidth, int owidth);入力画像はinput(x)です。出力画像はoutput(dd_1_)です。weightやbiasやReLU(Clip)の設定値等は、para_0_0に格納されています。

para_0_0は、make_conv2d_param関数で作ります。元の値は、parameter_0_0に格納されています。
make_conv2d_paramの関数宣言は、以下の通りです。

void make_conv2d_param(uint8_t *param, int8_t *buf[], int kernel, int ichannel, int ochannel, int conv, int add, int imax, int imin);ReLU相当の処理をするために、imaxとiminとを設定します。映像データの型は符号付8bitのため、imaxは127と設定します。また、ReLU相当の処理をするためiminは0と設定します。変更前後の設定を以下に示します。

//変更前 630 make_conv2d_param(parameter_0_0, para_0_0, 3, 3, 16, 0x40, 0x0, 127, -128); //変更後 647 make_conv2d_param(parameter_0_0, para_0_0, 3, 3, 16, 0x40, 0x0, 127, 0);次のレイヤであるpams_conv2dx2_addについて説明します。
本レイヤのブロック図を以下に示します。

pams_conv2dx2_addのブロック図models.cppのvoid modelsでは、以下のように記述しています。

663 pams_conv2dx2_add(dd_8_, dd_1_, dd_1_, &ai_sizex, &ai_sizey, 664 para_1_0_0_0, 3, 665 para_1_0_0_2, 3, 16, 16, 16, SizeX*8, SizeX*8);pams.hでの関数宣言は、以下の通りです。

void pams_conv2dx2_add(int8_t *output, int8_t *input, int8_t *add, int *sizex, int *sizey, int8_t *param0[], int kernel0, int8_t *param1[], int kernel1, int ichannel0, int ichannel1, int ochannel, int iwidth, int owidth);前段のConv2dのパラメータはparam0(para_1_0_0_0)、後段のConv2dのパラメータはparam1(para_1_0_0_2)です。
まず、para_1_0_0_0の作り方について説明します。前段のConv2dの後にReLUがあるため、make_conv2d_paramのiminの値を0に変更します。

//変更前 make_conv2d_param(parameter_1_0_0_0, para_1_0_0_0, 3, 16, 16, 0x40, 0x0, 127, -128); //変更後 make_conv2d_param(parameter_1_0_0_0, para_1_0_0_0, 3, 16, 16, 0x40, 0x0, 127, 0);次に、para_1_0_0_2の作り方について説明します。本パラメータを作る場合、make_conv2d_paramの、int conv、int addを調整する必要があります。convはConv2dの出力データをビットシフトするための引数で、addは加算データをビットシフトするため引数です。

//変更前 make_conv2d_param(parameter_1_0_0_2, para_1_0_0_2, 3, 16, 16, 0x40, 0x0, 127, -128); //変更後 make_conv2d_param(parameter_1_0_0_2, para_1_0_0_2, 3, 16, 16, 0x4, 0x20, 127, 0);変更前は、scaleによる乗算(x0.125)と加算処理とは、pams_init、pams_mul、pams_addで行っています。
変更前のmake_conv2d_paramは、Conv2dの出力データを変更する必要はありません。0x40(64)を基準にしているため、conv=0x40となります。また、加算データを考慮する必要がないため、add=0x0となります。
以下が対応するmodels.cppのvoid models()の記述です。

657 float tmp0[1]={0.125}; 658 pams_init(init_0, &ai_sizex, &ai_sizey, 16, 1, tmp0, SizeX*8*1); 659 pams_mul(dd_6_, dd_5_, init_0, &ai_sizex, &ai_sizey, para_a_0_0_3, 16, SizeX*8*1); 660 pams_add(dd_7_, dd_6_, dd_1_, &ai_sizex, &ai_sizey, para_a_0_0_4, 16, SizeX*8*1); 661 pams_relu(dd_8_, dd_7_, &ai_sizex, &ai_sizey, para_1_1, 16, SizeX*8*1);pams_initで、scale=0.125のデータであるinit_0を作ります。pams_mulは、init_0と、para_a_0_0_3(parameter_a_0_0_3)とで入力データ(dd_5_)を処理し、出力データ(dd_6_)を返します。
scale=0.125のため、入力データを右に3ビットシフトします。
models.cppでは、parameter_a_0_0_3[0]=0x7です。int8_tとint8_tとの掛け算の結果を、右に7ビットシフトするという意味で、変更後のmake_conv2d_paramで考慮する必要はありません。

pams_addは、para_a_0_0_4(parameter_a_0_0_4)で、dd_6_と加算データ(dd_1_)とを加算処理し、出力データ(dd_7_)を返します。
models.cppでは、parameter_a_0_0_4 = {0x0,0x0,0x8}です。入力データに対応するパラメータは、parameter_a_0_0_4[0] = 0x0で、ビットシフト不要です。また、加算データに対応するパラメータは、parameter_a_0_0_4[1] = 0x0で、同様にビットシフト不要です。出力データに対応するパラメータは、parameter_a_0_0_4[2] = 0x8です。0x7が基準のため、右に1ビットシフトする必要があります。

pams_reluでは、入力データ(dd_7_)の最小値を0にクリップし、出力データ(dd_8_)を返します。

よって、変更後のmake_conv2d_paramは、以下を考慮する必要があります。

Conv2dの出力側のパラメータであるconv

scale=0.125のため、右に3bitシフト

parameter_a_0_0_4より、右に1ビットシフト

→0x40を右に4ビットシフトするため、0x4

加算データのパラメータであるadd

parameter_a_0_0_4より、右に1ビットシフト

→0x40を右に1ビットシフトするため、0x20

ReLUのため、iminを0に設定

次に、pams_conv2d_addについて説明します。
本レイヤのブロック図を以下に示します。

pams_conv2d_addのブロック図models.cppのvoid modelsでは、以下のように記述しています。

667 pams_conv2d_add(dd_10_, dd_8_, dd_1_, &ai_sizex, &ai_sizey, 668 para_1_2, 3, 16, 16, 1, 1, SizeX*8, SizeX*8);pams.hでの関数宣言は、以下の通りです。

void pams_conv2d_add(int8_t *output, int8_t *input, int8_t *add, int *sizex, int *sizey, int8_t *param[], int kernel, int ichannel, int ochannel, int stride, int padding, int iwidth, int owidth);para_1_2の作り方について説明します。本パラメータを作る場合も、make_conv2d_paramの、int conv、int addを調整する必要があります。

//変更前 make_conv2d_param(parameter_1_2, para_1_2, 3, 16, 16, 0x40, 0x0, 127, -128); //変更後 make_conv2d_param(parameter_1_2, para_1_2, 3, 16, 16, 0x20, 0x8, 127, -128);変更前は、Conv2dの出力データは変更しません。0x40(64)を基準にしているため、conv=0x40となります。また、加算処理は、pams_addで行います。
以下が対応するmodels.cppのvoid models()の記述です。

663 pams_add(dd_10_, dd_9_, dd_1_, &ai_sizex, &ai_sizey, para_a_0_0_5, 16, SizeX*8*1);pams_addは、para_a_0_0_5(parameter_a_0_0_5)で、dd_9_と加算データ(dd_1_)とを加算処理し、出力データ(dd_10_)を返します。
models.cppでは、parameter_a_0_0_5 = {0x0,0x2,0x8}です。入力データに対応するパラメータは、parameter_a_0_0_5[0]=0x0で、ビットシフト不要です。加算データに対応するパラメータは、parameter_a_0_0_5[1]=0x2のため、右に2ビットシフトが必要です。出力データに対応するパラメータは、parameter_a_0_0_5[2] = 0x8です。0x7基準のため、右に1ビットシフトする必要があります。

よって、変更後のmake_conv2d_paramは、以下を考慮する必要があります。

Conv2dの出力側のパラメータであるconv

parameter_a_0_0_5[2] = 0x8です。0x7基準のため、右に1ビットシフト

→0x40を右に1ビットシフトするため、0x20

加算データのパラメータであるadd

parameter_a_0_0_5[1]=0x2のため、右に2ビットシフト

parameter_a_0_0_5[2] = 0x8です。0x7基準のため、右に1ビットシフト

→0x40を右に3ビットシフトするため、0x8

ReLUはないので、iminは-128のまま

最後に、pams_convtranspose2d_conv2dについて説明します。
本レイヤのブロック図を以下に示します。

pams_convtranspose2d_conv2dのブロック図models.cppのvoid modelsでは、以下のように記述しています。

670 pams_convtranspose2d_conv2d(y, dd_10_, &ai_sizex, &ai_sizey, 671 para_2_0, 2, 672 para_2_2, 3, 16, 4, 3, 2, SizeX*8, owidth);pams.hでの関数宣言は、以下の通りです。

void pams_convtranspose2d_conv2d(int8_t *output, int8_t *input, int *sizex, int *sizey, int8_t *param0[], int kernel0, int8_t *param1[], int kernel1, int ichannel, int mchannel, int ochannel, int stride, int iwidth, int owidth);前段のConvTranspose2dのパラメータはparam0(para_2_0)、後段のConv2dのパラメータはparam1(para_2_2)です。

para_2_0の作り方について説明します。前段のConvTranspose2dの後にReLUがあるため、make_convtranspose2d_paramのiminの値を0に変更します。
para_2_2の作り方について、後段のConv2dの後にReLUがあるため、make_conv2d_paramのiminの値を0に変更します。

//変更前 make_convtranspose2d_param(parameter_2_0, para_2_0, 2, 16, 4, 0x40, 0x0, 127, -128); make_conv2d_param(parameter_2_2, para_2_2, 3, 4, 3, 0x40, 0x0, 127, -128); //変更後 make_convtranspose2d_param(parameter_2_0, para_2_0, 2, 16, 4, 0x40, 0x0, 127, 0); make_conv2d_param(parameter_2_2, para_2_2, 3, 4, 3, 0x40, 0x0, 127, 0);pams_shiftで出力値を調整すためにbitシフトを行う必要がありますが、pams_shiftに対応するパラメータは、parameter_a_0_0_8[1] = {0x7}で、0x7基準のため、変更はありません。

レイヤ統合した場合の実行時間

レイヤ統合したmodels.cppを再度コンパイルしてください。
レイヤ統合後に、AI超解像した場合の処理結果を以下に示します。

ubuntu@kria:~/AIRTIPS/sample/sr/sr_evalmodel/4dtc$ sudo ./models racehorses.bmp ... Load racehorses.bmp ! *** AI Super Resolution(4DTC) *** Time : 8.357ms Save out_4dtc.bmp !レイヤ統合前の処理時間が約145msecに対し、レイヤ統合後の処理時間は約8msecと処理時間が短くなっていることが分かります。
レイヤ統合後の処理画像について、左端と右端とが変になる不具合がありますが、今後、修正していきたいと思います。

レイヤ統合後にAI超解像した画像(補足)コンパイルの際の最適化オプション

g++でコンパイルする際に、コンパイルオプション(-O3)をつけることで、コンパイル時間は長くなりますが、より高速な処理が可能です。変更後のmake.shを以下に示します。

#!/bin/sh g++ -O3 -o models main_sr_models.cpp models.cpp 4dtc_dmac.cpp pams.cpp \ -D XRT_BO \ -I/usr/include/xrt \ -L/usr/lib \ -lxrt_coreutil -pthread実行後のログを以下に示します。

ubuntu@kria:~/AIRTIPS/sample/sr/sr_evalmodel/4dtc$ sudo ./models racehorses.bmp ... Load racehorses.bmp ! *** AI Super Resolution(4DTC) *** Time : 5.751ms Save out_4dtc.bmp !映像をAI超解像

映像をAI超解像するためのシステム構成図

AI超解像した映像を確認するためのシステムの構成図を以下に示します。

AI超解像の映像を確認するためのシステム構成図Webカメラや映像源のデータをUSBでKV260へ入力します。AI超解像した映像をHDMIで出力し、モニタで確認します。

USBの映像データの確認

v4l2コマンドでUSBカメラからの情報を確認します。私が確認した場合のログは以下の通りです。解像度は320x240でフォーマットはYUYVです。

ubuntu@kria:~$ v4l2-ctl --all ... Format Video Capture: Width/Height : 320/240 Pixel Format : 'YUYV' (YUYV 4:2:2) ...以下の説明は、入力映像の解像度が320x240の場合についてです。

AI超解像の映像の確認

USBカメラ映像をOpenCVで受信し、ウィンドウ表示するプログラムは、以下に添付します。sample/sr/sr_evalmodel/4dtcのフォルダに入れてください。

入力解像度は320x240です。AI超解像により、出力解像度は640x480になります。サンプル画像(160x120)より入力解像度が大きいため、一部のパラメータを変更する必要があります。
4dtc.hのP_AI_LINE_BUFを512から1024へ変更してください。

//変更前 #define P_AI_LINE_BUF 512 //変更後 #define P_AI_LINE_BUF 1024以下のコマンドでopencvをダウンロードしてください。

sudo apt update sudo apt install libopencv-dev以下のコマンドでコンパイルすることができます。

ubuntu@kria:~/AIRTIPS/sample/sr/sr_evalmodel/4dtc$ sh make_sr_cam.sh実行時のログは、以下の通りです。Warningがでますが、特に問題ありません。処理速度は26~27fpsぐらいです。

ubuntu@kria:~/AIRTIPS/sample/sr/sr_evalmodel/4dtc$ sudo ./sr_cam [ WARN:0] global ./modules/videoio/src/cap_gstreamer.cpp (1100) open OpenCV | GStreamer warning: Cannot query video position: status=0, value=-1, duration=-1 bo0.size() :0xc000000 bo0.address():0x38c00000 bo0.map() :0xffff54000000 O.K. AI FFFF6EBB0000 A1000000 AI RAM FFFF54000000 38C00000 diff:0.036914sec, 27.089993frame/sec diff:0.037524sec, 26.649611frame/sec diff:0.037438sec, 26.710829frame/sec ...4DTCでAI超解像を動作させる方法の説明は、以上です。

参考文献

最後まで読んでいただき、ありがとうございました。