TanbaBASIC: Raspberry PI GPIO 対応 BASIC 言語実行環境!

教育のため、本BASIC言語実行環境を作成しました。
現状、私の要求(Raspberry PIのGPIO含む)に対して動作していますが、あまり時間がなく、十分なテスト等を行っていないです。
RPIを数台保有していて、問題なく動作していますが、ライセンスにも記載されていますように、本ソフトは NO Warrantyです。


コードはこちらです。

 

なぜBASIC言語

初めて学んだ言語はBASICでした。プログラミングを始めるためには最も良い言語でした。

現在になると、プログラミングを学ぶための言語について終わりのない議論が行われています。
多数意見を読み、私の結論はBASICです。この議論については述べませんが、現在のPCでどうしたらBASICを学ぶことがきるのかについて悩んでいました。

 

ある日、IchigoJamを見つけました。これだ!と思い、早速試しました。
私がプログラミングをはじめたころ、主にモニターで何かを表示するプログラムが面白かったので、PRINT等を子供に教えようとしたら、やってくれるけど、あまり興味を持って貰えなく。

 

しかし、LEDを操作して見せたら、面白いのか、興味を持つようになったので、LED、モータやセンサーをGPIOに接続して教えてきました。
モータを使った例では、GPIOから長い配線をしたおもちゃの車がありますが、配線が限界でした。

IchigoJamが無線になったらと思い、探しはじめました。無線の場合、さらにモニターがつけれなくても、遠隔でデバッグもできます。
RPI用のIchigoJamはありますが、これはISOのイメージが提供されていて、WiFiは使えないです。
よって、Linux用のBASICを探し始めました。これを蓄電池で動作するRPI Zeroに入れ、sshで入り、BASICを立ち上げて遠隔で操作する、って案です。
しかし、既存のLinux用BASICは、RPIのGPIOに対応していなかったり、重すぎたり(遅すぎ)、複雑すぎたり(コード理解&GPIO対応困難)、
軽い場合はIDEがなかったりでした。よって、自作することを決めました。

 

 

f:id:tanbalabs:20200712221943p:plain

 

私の要求は以下です。
・軽くて小さい
・簡単なIDEMSX同様)
WiFiが使える
・GPIOが使える
・GPIOが設定可能

オプションです。
・色対応
IDEで行全体が見れる
・LOAD/SAVEコマンド対応
・大文字、小文字対応。ただし、コマンドとして全て大文字に置き換え
・:でコマンドの区分け
・2進数、16進数入力可能
・AD変換機対応
・IF 用ELSEおよびEND。(限定的なELSEは実装済み)
 

実装項目

本ソフトは下記から構成されています。元の実装があったため、非常に助かりました。作成者に感謝です。


・BASIC実行部
豊四季さんのタイニーBASICを元にしています。
* IDE
AntirezさんおKiloエディタが元です。
* GPIO IF
pigpioを利用しています。

f:id:tanbalabs:20200712222006p:plain

 

 コンパイルおよび実行(GPIO無し。RPI限定ではないですが、GPIOコマンドは効果ないです)

    make

実行:

    ./tanbabasic


 GPIO有でコンパイルおよび実行(RPI用)。
   pigpioのインストるが必要。

      (sudo apt-get install pigpio) 

      make rpi

RPIでGPIOありの実行(GPIOの設定はファイル形式は後述):
     sudo ./tanbabasic <設定ファイ>


Tanba BASIC IDE コマンド

CTRL + S IDEエディタ情報を保存 (BASICのSAVEコマンドではなです。)
ESC プログラム実行を停止
CTRL + Q Tanba BASICを終了.プログラム実行の際、最初に実行を停止のこと(ESCキー)。

   問題
たまに ESCキーが使えなくなり、プログラムを停止できなくなります。
再現できていない状態です。唯一の停止方法をプロセスをkillすることです。
再現できたら直したいです。


Tanba BASIC コマンド:
  TinyBasic コマンドに対して, 以下を追加しています(IchigoJamMSXが元です)。

コマンド 詳細
INKEY() キーコードを出力。特別キー
    0: 何も押されていない。
      9: TAB
    13: ENTER
    28: 右
    29: 左
    30:上
    31:下
IF用ELSE(全て一行) 元のTinyBasicはIFのみに対応しており、さらに全て(IFおよび条件後に実行するコマンド)は一行に記述する必要があります。ここではELSEも使えるようにしましたが、ここでも全てが一行にあり、かつこの一行には一つのみのIFに対応しています(複数IFとELSEがある場合、動作は未定義です)。
CLS 画面クリア
COLOR N1,N2

色の設定  

    N1: 文字の色  N2:(オプション)背景の色。  

    COLOR -1,N2で背景のみを変更。  

    N1とN2は0-255の数値、または後述の色コード。  

    本コマンドは下記のSGR 30-37,39,90-97,40-47,49,100-107で上書き。

SGR N

ターミナルのSGRを設定。  

    38 および 48は効果がないです (COLOR で設定).  

    TanbaBASIC 特別コード:HL または HIGHLIGHTはBASICの強調モードに切り替え。他のコードはHLを無効にする。

LOCATE X,Y カーソルをX,Yに設定。
LC X,Y 同上
LOAD "STR" STRで指定されたファイルのプログラムを読み込み。(Linux形式)(注意:ロードしない場合があります。デバッグ中)
SAVE "STR" STRで指定したファイルにプログラムを保存。
RESIZE W,H ターミナルの大きさをW,Hに変更。(注意:効果がないターミナルもあります)
WAIT TIME TIMEセンチ秒待ち(WSL1ではバグのため、待たない )。
? PRINTのかわりに?が使える。

RPI用GPIOコマンド。ポート番号は後述の設定ファイルを参考のこと。
IO利用にあたり、追加回路が必要です。

I/Oコマンド 詳細
IN(N) ポートNの入力を取得
OUT N1,N2 ポートN1にN2を出力。N2>0の場合、1を出力。
OUT N Nの各16 ビットは対応する論理ポートに出力。論理ポート選定は設定ファイルのOUT_MODEで決められる(後述)。


数値形式

数値は2, 10, 16進数で表現が可能です。2進数は ` または 0Bで指定. 16進
数は0Xで指定。例:以下は全て10進数の10です。

    `1010
    0B1010
    0XA

利用例

    A=0XA
    ? 0XA 
    OUT 0XA


対応RPI
下記でテストしております。

  * 初期 Model B
  * Pi3B
  * Pi zero

ここのように、三種類のGPIOがあります。
上記テストに使った機種は、type 1と3が対象のため、本ソフトはtype 2では動作しないようにしています
type 2 で動作させたい場合、rpi.c の if (type==2)部分をコメントして試しても良いですが、動作未確認です。

RPI用GPIO設定ファイル形式

設定ファイルはポート番号とモードを設定します。
RPIにはポート番号を指定する二つの方法があります:BOARD (pin番号)および BCM (GPIO 番号)
Tanba BASICでは以下のように論理ポート番号を設定できます。
1) BOARDと同じ番号を利用
2) BCMと同じ番号を利用
3) 別の論理番号をBOARDまたはBCMに利用。
例:IchigoJamと同じポート番号を利用
RPI用のIchigoJanピン配置も作成しました。pdfodsです。

f:id:tanbalabs:20200718095308j:plain




各ポートは入力( pullup/down)か出力に設定できます。

ファイル形式:

項目 詳細
# 行の#以降は全てコメントです。
BOARD または BCM 最初の有効行は必ず BOARD かBCMにする(上記説明の通り)。
OUT_MODE M 最初の有効行の BOARD または BCMの後のオプション。OUT Nに使われる。下記参考
ADC_MAIN ADC_TYPE 有効2行目のオプション。ADC_TYPEがサポートされているADCチップ。下記参考
P,OUT GPIOポートP (BOARD か BCM) を出力に設定。論理ポートもPになる。
P,OUTN

GPIOポートP (BOARD か BCM) を出力に設定。論理ポートはNになる。注意:OUTとNの間に空白はない。

P,IN,MODE  GPIOポートP (BOARD か BCM) を入力に設定。論理ポートもPになる。MODEはオプションであり、次のいずれか:PULLOFF(clear), PULLUP または PULLDOWN
P,INN,MODE  GPIOポートP (BOARD か BCM) を入力に設定。論理ポートはNになる。MODEは上記の通り。注意:INとNの間に空白はない。

 

上記より、最初の有効行は以下のいずれかです。

  BOARD
  BCM
  BOARD OUT_MODE M
  BCM OUT_MODE M

OUT_MODEのMは、OUT NのNの各ビットがどの論理ポートに出力されるのかを定義します。
M>=0 : 論理ポート番号は M で始まるが、定義されていない論理ポートには出力しないです。
M< 0 : 定義された論理ポート順のみになります。
Mのデフォルトは 0 です。

例:(設定ファイルとプログラム例は後述)

f:id:tanbalabs:20200718095154p:plain

サポートしているADC

前述のGPIO設定ファイルの通り、ADCチップは下記の通りに設定できます。

  ADC_MAIN ADC_TYPE

ADC_MAINは、Main SPIを利用の意味です(Aux SPIは未実装)。

下記のADC_TYPEのソースコードを記述しています。
送信データが全タイプで確認していますが、MCP3202しか保有していないため、完全テストはこのチップのみです。

     MCP3002
     MCP3004
     MCP3008
     MCP3202 (完全テストはこれのみ)
     MCP3204
     MCP3208

RPIとADCの配線および交換されるデータは下記図の通りです。
MCPはSingle-Ended modeで動作します (SGL/DIFFを1に設定).存在する場合、MSBFは0に設定しています。詳細はそれぞれのハードの仕様をネットで確認ください。
回路図の通り、ADCをCE0に接続する必要があります。

ADCデータを取得するためには、前述の ANA(N)を使います。Nがチャンネル番号です。

f:id:tanbalabs:20200727181823p:plain


下記がいくつかのコマンド例です。LOADを使って読み込んで、RUNで実行します。

プログラム 詳細
test/lc.bas LC 例
test/inkey.bas INKEY 例。ESCで停止
test/resize.bas RESIZE 例(ターミナルにより効果がない場合があります)
test/color.bas COLOR 例
test/sgr.bas SGR 例
test/bin_hex.bas 2進数、16進数例

 

GPIO 例

GPIOはボタンやLED/モータでテストしました。

初心者の場合、特にボタンの動作の問題等を認識するため、ここの確認をおすすめします。
入力の場合、PULLUP/DOWN抵抗で試しました。

上記のように、論理ポートは以下の通りに設定できます。
a) ボードの番号に同じ
b) GPIO番号に同じ
c) 任意番号

以下が上記の例です。例では物理的に同じピンを、別の論理番号に設定しています。

f:id:tanbalabs:20200718095232p:plain



最後に、以下がテストに用いた設定ファイルとプログラムです。
入力にはPULLUP/DOWNボタン、出力にはLEDを使いました。

UARTポート(GPIO14と15)はテストしていないです。

バイス 設定ファイル プログラム
PI Z/3B test/rpiv3_bcm_in_out.cfg test/v3_bcm_in_2_7_out_8_13.bas
PI Z/3B test/rpiv3_bcm_in_out.cfg test/v3_bcm_in_16_21_out_22_27.bas
PI Z/3B test/rpiv3_bcm_out_in.cfg test/v3_bcm_out_2_7_in_8_13.bas
PI Z/3B test/rpiv3_bcm_out_in.cfg test/v3_bcm_out_16_21_in_22_17.bas
PI Z/3B test/rpiv3_bcm_ichigo.cfg test/v3_ichigo.bas
PI Z/3B test/rpiv3_board_in_out.cfg test/v3_board_in_3_13_out_15_22.bas
PI Z/3B test/rpiv3_board_in_out.cfg test/v3_board_in_23_32_out_33_40.bas
PI Z/3B test/rpiv3_board_out_in.cfg test/v3_board_out_3_13_in_15_22.bas
PI Z/3B test/rpiv3_board_out_in.cfg test/v3_board_out_23_32_in_33_40.bas
PI Z/3B test/rpiv3_board_ichigo.cfg test/v3_ichigo.bas
PI B test/rpiv1_bcm_in_out.cfg test/v1_bcm_in_0_7_out_8_11.bas
PI B test/rpiv1_bcm_in_out.cfg test/v1_bcm_in_17_21_out_22_25.bas
PI B test/rpiv1_bcm_out_in.cfg test/v1_bcm_out_0_7_in_8_11.bas
PI B test/rpiv1_bcm_out_in.cfg test/v1_bcm_out_7_21_in_22_25.bas
PI B test/rpiv1_bcm_ichigo.cfg test/v1_2_ichigo.bas
PI B test/rpiv1_2_board_in_out.cfg test/v1_2_board_in_3_11_out_12_16.bas
PI B test/rpiv1_2_board_in_out.cfg test/v1_2_board_in_18_21_out_22_26.bas
PI B test/rpiv1_2_board_out_in.cfg test/v1_2_board_out_3_11_in_12_16.bas
PI B test/rpiv1_2_board_out_in.cfg test/v1_2_board_out_11_21_in_22_26.bas
PI B test/rpiv1_2_board_ichigo.cfg test/v1_2_ichigo.bas


OUT_MODE 例(上記図等の参考のこと)

バイス 設定ファイル プログラム
PI Z/3B test/rpiv3_bcm_out_mode_default.cfg test/v3_out_mode.bas
PI Z/3B test/rpiv3_bcm_out_mode_0.cfg test/v3_out_mode.bas
PI Z/3B test/rpiv3_bcm_out_mode_2.cfg test/v3_out_mode.bas
PI Z/3B test/rpiv3_bcm_out_mode_defined.cfg test/v3_out_mode.bas

 

ADC MCP3202 例

 

バイス 設定ファイル プログラム
PI B test/rpiv1_2_board_ichigo_mcp3202.cfg test/adc.bas
PI Z/3B test/test/rpiv3_bcm_ichigo_mcp3202.cfg test/adc.bas

 色コード

 色選定方法はこちら(pdf)こちら(ods)を確認
Check this file ([pdf](color.pdf), [ods](color.ods)) on how I defined the color codes.

利用できる色のコードは次です。

BLACK
BLUE
BROWN
CYAN
DARKBLUE
DARKGREEN
DARKRED
DARKVIOLET
DEEPPINK
GOLD
GRAY
GREEN
LIME
MAGENTA
MAROON
ORANGE
ORANGERED
PINK
PURPLE
RED
VIOLET
WHITE
YELLOW

制限

* 1行は256文字まで
* Basic プログラムは8192バイトまで。これは静的にlistbuf (t_ttbasic.c)で確保されます。私の使ったシステムでは上限は32kです (tanba.hのSIZE_LIST を変更)

 

Darknet/YOLOのJetson最適化

f:id:tanbalabs:20200507220500j:plain

Mixed Precision学習結果

 

ソースコードこちら

表題の通り、Darknet/YOLOv3をJetson Nanoに最適化しました。

本最適化により、Jetson Nanoでより少ないメモリ利用で学習・検出ができるようになりました。
すなわち、通常Darknet/YOLOv3に対して、より大きい解像度で学習・検出ができます。
学習は二つのクラス 320x320で11日程(416x416で14.5日)でとても遅いけど、Jetson Nanoしか持っていない方は自分のデータで学習ができます。

Jetson Nanoでしか動作確認をしていないが、TX1/2等のMaxwellアーキテクチャを実装するJetsonでは動作すると思います。
Xavierは一部のコード(AtomicAdd)で正しくアーキテクチャの切替ができるのか分からないです。
一方、通常のNvidia GPUカードでは未対応です(現状、JetsonのUnified Memoryに特化)。

最適化内容は下記二つです:
・JetsonのUnified Memoryを利用 
・FP32/16混合学習(Mixed Precision) (論文実装実装NVidia)

 

状況:
・テストは最低限しか行っていないです。
・対応層:convolutional, route, shortcut, upsample, yolo, cost.通常YOLOv3しか動作しないです。v4, v2やTinyは動作しないです。

 

cfg設定ファイルへのMixed Precision追加項目([net]部分のみ)は以下です。説明は設定項目名の通りです。詳細は後述のアルゴリズムを参考のこと。

 

追加項目名 デフォルト値 備考
max_loss_scale 16384.0  
min_loss_scale .0009765625  
initial_loss_scale 1.0  
loss_scale_inc_iterations 1000 burn_in以降に利用


コンパイル
Makefileに二つ項目を追加しました:
* JETMEM: JetsonのUnified Memoryを利用の際、1にします。
* CUDNN_MIX: FP16/Mixed Precisionを利用の際、1にします。
現状、JETMEM=1, CUDNN_MIX=1でしか動作確認していないです。。
また、Jetson用のARCHのコメントも外します。(compute_53を利用)


検出
既存の重み、または独自データで学習した重みが利用できます。
使い方はこちらこちら

学習
学習の実験はCOCOデータセットの猫と犬のみで行いました。
YOLOv3用のCOCOデータセットの取得はこちらに従ってから行いましたが、二つのクラスのみを使うために新規設定ファイルを作成しました:

data/coco/coco_cat_dog.names 猫と犬のみのクラス名
cfg/coco_cat_dog.data 学習および評価ファイルリストを更新(下記二つ)。
data/coco/trainval_cat_dog.txt 学習用ファイルリスト
data/coco/val_cat_dog.txt 評価用ファイルリスト
cfg/yolov3_cat_dog*.cfg 二つのクラスのみを利用するためにフィルタ数対応

また、新しいラベルに対応するファイルも必要です。以下を行います。(注意:上記の通りに取得したCOCOデータセットを上書きします。)

  tar xvf coco_cat_dog_labels.tar.gz

学習例は以下です:
./darknet detector train cfg/coco_cat_dog.data cfg/yolov3_cat_dog.cfg weights/darknet53.conv.74


最適化実装の詳細:


Unified Memory
Darknetは、CPUとGPUに同じデータを保存します。しかし、JetsonのDRAMは両CPU/GPUで共有されるため、同じデータを2回確保する必要はないです。
よって、共有することにより、より少ないメモリで動作が可能になり、より大きい解像度で検出が可能です。

 

FP32/16混合学習(Mixed Precision)
Mixed Precisionは学習の際にNVidia GPUのTensorCore(Turingアーキテクチャ以降)を利用して高速化を達成するために開発されました。FP32に対し、FP16での畳込み演算のみが8倍速くなります。FP16を用いることにより、利用するメモリ量も減ります。
(重み更新用の配列等はFP32/16の両方を保持する必要があるため増えますが、全体は減ります)
Jetson NanoはTensorCoreを実現しないため学習は遅くなりますが(後述)、より少ないメモリ(より大きい解像度)の学習が可能になります。

 演算部の実装はこちらのTensorFlow部分を元に行いました。

論文のように、FP32⇔16変換を行う際、FP16はより少ない数値幅のためloss scaleで乗算/除算を行いますがこれの調整が必要になります。
更新案はこちらこちらにあり、基本は重みを更新するweight_updatesがNaNまたは無限になった場合にloss scaleを半分にし、
NaN/無限が 1000または2000回生じなかった場合に倍にしています。
ここでは倍にする手順は上記と同じにしていますが、YOLOv3の特徴によりburn_in(YOLOv3のcfgファイルでは1000に設定されています)回までは最大の
weight_updatesにより更新し、burn_in後は従来の通りにしています。理由や詳細は次の解析の通りです。

 

f:id:tanbalabs:20200507222448p:plain

上記図は各回毎の、全て畳込層のweight_updatesの最大絶対値グラフの形よびロスのFP32例です。
パラメータ調整や、二回目の学習だと別結果になりますが、雰囲気は同じです。
最初はすぐ大きくなります。この例だと、最大が165,055です。この時点でFP16の最大値(65,504)を超えています。
よって、この時点でloss scaleの適切な値は0.25です。その後、徐々に下がり、数百回後に2^10程下がります。
これにより、1000回に一回倍にするとFP32⇔16では精度を失う可能性があるため、より早く更新するために今回および今回までのweight_updatesの最大絶対値に基づいて
burn_inまで更新するようにしました。


また、下記が層毎のweight_updatesです。(最大絶対値が大きい場合)
ここでは500回目程度まで、一番大きい絶対値は層81、93、105です。これらはlossが計算されるyolo_layerの直前の畳み込み層です。
500回目以降になると、最も大きい絶対値は最初の方の層になります。

f:id:tanbalabs:20200507222708p:plain

 

大きさ変更による水増し
Darknet/YOLOv3のデータ水増しとして、色調整や、大きさ変更(Resize)、横反転、切り出しがあります。
Resizeは10回毎に、ランダムに1/1.4~1.4倍に、width/heightが32の倍数で調整されまる。
320x320の場合、256x256から480x480に変更されますが、最初はメモリを最大で初期化するため、必ず最大の480x480になります。


メモリ使用量評価方法

Tegraでは下記方法があります:
* tegrastats コマンド(メモリ使用量を指定周期で指定ファイルに出力可能)
* jtop コマンド(tegrastatsと同じ情報であるが、ファイルに出力できない)
* cudaMemGetInfo (c ファイルに埋め込み)
* /usr/bin/time -v (Maximum resident set sizeを参考)

ここではファイルに指定周期で出力可能なtegrastatsを使うことにしました。
なお、cudaMemGetInfo, /usr/bin/time, tegrastatsは別数値を出力します。


Nano上での実験結果
以下が学習中のメモリ使用量です。
メモリ使用量はtegrastatsによる RAM + SWAPであり、他のプロセスのメモリ使用量も含まれています。
上記のように、Darknet/YOLOv3の最大メモリ使用量はResizeが最大の場合であり、最初の10回はこのResizeに設定されます。
よって、最初の10回のメモリ使用量が最大になること考えられるけど、学習中、他のプロセスのため全体のメモリ使用量はさらに増えます。
このため、Darknetは下記二つの理由により強制終了(Killed)される可能性があります:
1) 最初のメモリ確保の場合
2) 学習中、他のプロセスによるメモリ増加のため

例えば以下が320x320のメモリ使用量です。
FP32の方が最初に完了します。最初の10回のメモリ使用量(RAM + SWAP)は 3800MB(FP32)および3200MB(Mixed Precision)です。
しかし、学習中、メモリ使用量はさらに増えますが、解析が必要です。

f:id:tanbalabs:20200507222952p:plain

 

なお、上記のNanoの設定として、SWAPがデフォルトの2GBと、ターミナルモードです。

以下が学習一覧です。

 

設定 元Darknet/YOLOv3 本最適化
yolov3 320x320 OK OK
yolov3 416x416 100回程度で強制終了 OK
yolov3 544x544 初期に強制終了 300回程度で強制終了
yolov3 tiny 解像度依存 未実装
yolov2 解像度依存 未実装

 

実行時間解析

320x320設定で、Resize 480x480の一回の学習にかかる時間は以下です。

  FP32 : 186 秒

  Mix : 272 秒 

以下の情報は次のコマンドのログからまとめています。

  nvprof --print-gpu-trace --log-file log.txt ./darknet detector train ...

 上記で、学習を3回繰り返し、二回目の結果を分析しました。

 FP32の場合、GPUの合計時間は、上記186秒とほぼ同じです、

 しかし、Mixed Precisionでは、GPU時間が232秒であり、上記272秒より40秒少ないです。

 一方、私の解析が正しければ、下記のようにMixとFP32で最も大きい差分は畳み込み演算です(Mixが47秒遅い)。よって、畳み込み演算が大きな差分でありますが、さらに40秒の解析が必要です。

 

f:id:tanbalabs:20200507224446p:plain

mAP@0.50%結果(猫、犬のみ)

w x h モード 最良mAP
320x320 2クラス (FP32) 66.4%
320x320 2 クラス (Mixed Precision) 68.0%
416x416 2 クラス (Mixed Precision) 73.3%

ここでは5000回学習し、最も良いmAPの重みになります(100回毎に重みを保存)。
mAPは同じ設定でも、学習毎にかわります。
よって、上記結果はMixed Precision方式がFP32より良いと限りません。

 

1歳10ヶ月でお箸!

 子供が1歳半を過ぎたころ、自分もお箸を欲しがりはじめました。小さいのがあったので渡しました。

 以降、毎食にとりあえず子供のお箸も食卓に並べました。言葉も通じないので教えることができず、使えるなら使ってみ〜って感じです。

 気がつくと、勝手になんとか使えそうになり、1歳10ヶ月でご覧のとおり。

 youtubeにも映像をあげました。

f:id:tanbalabs:20200214002546j:plain

お箸

 

 

深層学習用形状生成スクリプト

Pythonの形状(丸、三角、四角、星)を生成するツールを作成しました。

コードはgitlabにおきました。役立てそうなら使って頂けたらと思います。

機能として:

・○、△、□、☆を生成可能

・画像に一つ、または複数の形状を生成

・モノクロ、または色付き

・形状、または背景に対して色の範囲を指定可能

・回転

darknet/yoloまたはPascal VOCのマーキングができる。

 

以下がPascal VOC例です:

左:生成画像、中:Pascal VOC object、右:Pascal VOC class

f:id:tanbalabs:20190922223414p:plain

Pascal VOC