axinc-ai / ailia-models

The collection of pre-trained, state-of-the-art AI models for ailia SDK
2k stars 319 forks source link

ADD flownet #257

Closed kyakuno closed 3 years ago

kyakuno commented 3 years ago

https://github.com/NVIDIA/flownet2-pytorch apache

kyakuno commented 3 years ago

CUDAがないとエクスポートできない感がある。

kyakuno commented 3 years ago

Resample2DはCUDAのModuleになっている。 https://github.com/NVIDIA/flownet2-pytorch/tree/master/networks/resample2d_package

kyakuno commented 3 years ago

Pytorch moduleが使えるかも https://github.com/wanghao9610/flownet2-pytorch-module

kyakuno commented 3 years ago

cuda moduleが必要なのは変わらないみたい

mucunwuxian commented 3 years ago

@kyakuno 担当させて頂いております作業が諸々落ち着いた後、こちらを担当させて頂いてもよろしいでしょうか? (他に優先すべきissueがあれば、気兼ねなく仰って頂ければと思います。🙇) 難しさについては、上記コメント等を参考にさせて頂こうと思います。

或いは、大変恐縮ながら、flownet2のonnx化がどうしても難しい場合には、その前身と言えるシンプル構造のflownetもあるようでして、技術実現としては代替となり得るかもしれないと思いました。 (私自身、まだ深くは見切れておらず、あくまで参考までにのコメントとなります。🙇)

kyakuno commented 3 years ago

了解しました!よろしくお願いします。

mucunwuxian commented 3 years ago

@kyakuno ありがとうございます!🙇

mucunwuxian commented 3 years ago

Optical Flow Estimationについての調査結果まとめ 📝

Optical Flowを推定する最新技術についてリサーチを行った結果を、冗長ながらまとめさせて頂きます。


1. 技術の大まかな歴史

とても良いsurvey論文がありましたので、そちらを参考にさせて頂きました。 特に、教師ありのDeep Learningベースに手法に関する記述を、追いかけました。 file:///Users/mucunwuxian/Downloads/1-s2.0-S0031320321000480-main-online.pdf



教師ありのDeep Learningベースの手法で言うと、以下の手法などが有名であるようです。

(教師ありDeep Learningベース手法の歴史) image



年表一番左に記載されるのが、2015年頃に発表されたFlowNetになります。 ドイツのフライブルク大学とミュンヘン大学から発表されています。 検索上位のpytorch implementationは、発表者による実装ではなさそうです。 FlowNetの論文に記載される、抽象的な概念図は、その後も幾つかの論文にてリファーされている、Optical Flow Estimationの基本形となっているようです。 それは、前後2frameを入力として、その間に生じるOptical Flowを推定する形です。

(FlowNetの構造) image



尚、FlowNetにおけるEncorder・Decorderの構造は、以下のようなシンプルな形状でした。 論文文章上ではU-Net状と表現がされており、論文図上はショートカット表現が無いですが、プログラム上はU-Netにて行われているようなショートカットがされています。 また、FlowNetSと、FlowNetCは、Encorderが異なり、Decorderは共通になります。

(FlowNetS、FlowNetCのEncoder構造) image

(FlowNetの共通Decorder構造) image



そして、このFlowNetの精度を大幅改善したのが、2017年頃に発表されたFlowNet2.0となります。 こちらも発表は、フライブルク大学によるものです。 検索上位のpytrcoh implementationは、NVIDIAによる実装となっています。 構造はFlowNet2.0の論文に記載されている、以下の複雑なものとなっています。 FlowNet1.0よりの単純なFlowNetCに加え、Image2をフロー推定値に基づいてwarpさせたものや、Image1とそのwarpさせたImage2とのフレーム間輝度差分や、flowの大きさを入力させています。 尚、warpに関しては、NVIDIAが実装したResample2Dにて実現がされているようです。

(FlowNet2.0の構造) image



その後、PWC-Netや、SelFlow等の手法が出てきたようで、2019年頃迄に徐々に精度が改善されていったようです。 昨今ですと、SOTAラインナップとしては、MaskFlownet、RAFT、GMAといった手法であるようです。 それらの精度を表にまとめると、以下の関係となっています。 大幅改善が分かりやすいよう、FlowNet2.0の精度も並べて表示します。

(昨今の手法と、その精度) image



MaskFlownetは、Microsoft Research Asiaによって、2020年3月に論文プログラムがリリースされたようです。 2020年の3〜4月頃で、Optical Estimation用の各種データセットでSOTAになっていたようです。

構造としては、以下のような形となっています。

(MaskFlownetの構造) image


具体的なネットワークのパーツとしては、以下のようなモジュールがあるとのことです。 ポイントとしては、Ground Truth無しにOcclusion Maskの推定を目指すことと、その情報を用いてOptical Flowの推定精度を上げる、ということとのことです。

(MaskFlownetの特徴的な内部構造) image

image


feed forwardの概要としては、以下となります。

  - [MaskFlownet S]
      - (1-1) 6回のconvolutionを画像「I1」と画像「I2」とにそれぞれ実施する(空間方向への圧縮をしつつ)
      - (1-2) 画像「I1」の畳み特徴と、画像「I2」の畳み特徴との相関を測定する(cudaで高速化された謹製メソッドにて)
      - (1-3) (1-2)の特徴に対し、UNet状に特徴のconcatをしながらの5回のconvolutionを行う(空間方向への拡張をせずに)
      - (1-4) (1-3)の特徴を、1回convolutionして、Optical Flow(uvの2ch)を推定する
      - (1-5) (1-4)の特徴を、1回convolutionして、Occlusion Mask(1ch)を推定する
      - (2-1) (1-3)の特徴をdeconvして、空間方向2倍にupdampleする
      - (2-2) (1-4)のOptical Flow推定値をdeconvして、空間方向2倍にupdampleする
      - (2-3) (1-5)のOcclution Mask推定値をdeconvして、空間方向2倍にupsampleする
      - (2-4) (2-2)のupsampled Optical Flow推定値を、scalingする
      - (2-5) (2-4)のscaled upsampled Optical Flow推定値を使って、deformable convolutionをすることで、warpを実現する
      - (2-6) (2-5)のwarped scaled upsampled Optical Flow推定値と、(2-3)のupsampled Occlusion Mask推定値をsigmoidしたものとを、掛け合わせる
      - (2-7) (2-6)の特徴と、(2-1)の特徴とを、足し合わせる
      - (2-8) (2-7)の特徴と、(1-1)における画像「I1」の5回目の畳み込み特徴との、相関を測定する
      - (2-9) (2-8)の特徴と、(1-1)における画像「I1」の5回目の畳み込み特徴と、(2-1)の特徴と、(2-2)の特徴とを、チャンネル方向にconcatする
      - (2-10) (2-9)の特徴に対し、UNet状に特徴のconcatをしながらの5回のconvolutionを行う
      - (2-11) (2-10)の特徴を、1回convolutionして、Optical Flow(uvの2ch)を推定する
      - (2-12) (2-2)のupsampled Optical Flow推定値と、(2-11)のOptical Flow推定値とを足し合わせる
      - (2-13) (2-10)の特徴を、1回convolutionして、Occlusion Mask(1ch)を推定する
      - (3-1) (2-1)〜(2-13)の処理ステップと同様の処理を、もう3回実施する
      - (4-1) (3-1)のOptical Flow予測値の内の、最終的に予測をされたOptical Flowに対して、各種dilation値のconvolusionを複数回実施をする(その過程で、ResNet的なショートカットも実施)
      - (5-1) (1-1)〜(4-1)までで推論がされた5つのOptical Flow推定値に対して、重み付けをする

  - [MaskFlownet]
      - (1-1) 入力画像「I1」と「I2」とに対し、[MaskFlownet S]を適用する
      - (2-1) (1-1)で生成された特徴や、Optical Flow推定値や、Occlusion Mask推定値等々を用いて、[MaskFlownet S]と同様の処理をもう1ループ実施する



RAFTは、アメリカのプリンストン大学の論文発表になります。 2020年の5月〜8月の発表とのことで、Optical Estimation用の各種データセットでMaskFlownetの精度を上回り、SOTAになったようです。 プログラムも、プリンストン大学からのリリースであるようです。

ネットワークの構造としては、以下とのことです。

(RAFTの構造) image

image


尚、RAFTについては、有り難いことに日本語の記事を書いてくれている方がいらっしゃいました。 https://ichi.pro/oputhikarufuro-to-raft-o-rikaisuru-189988819120058


論文と、上記日本語の記事を読ませて頂くと、アプローチとしてはどう解くかを数学的に追求しているような印象を受けました。 論文に記載されるRAFTのアプローチとしては、要約すると以下3つとのことでした。

  - (1)特徴抽出を工夫
      - [A]2つの入力画像である、Frame1とFrame2とから、ピクセル毎の特徴を抽出する特徴エンコーダーの採用
      - [B]入力画像 Frame1からのみ特徴を抽出する、Context Encoderの採用

  - (2)視覚的類似性の計算を導入
      - [C]ピクセル毎の特徴ベクトルについて、全ペアの内積を取ることによって、4DとなるWxHxWxHの相関ボリュームを構築する、相関層の採用
      - [D][C]の4Dの最後の2次元は、マルチスケールボリュームのセットを構築し、複数スケールでプールする形とした

  - (3)予測値の反復計算を導入
      - [E]カレントの推定値と、相関ボリュームの関係性を鑑みることで、オプティカルフローを繰り返し更新する演算の実施
      - [F]ゼロで初期化されたフローフィールドを、反復GRUベースのアプローチによって、繰り返し更新する


また、以下の点がユニークとのことです。

  - (4)空間報告に圧縮した後、アップリングされて高解像度で洗練されるような形にしておらず、高解像度の縦横サイズを維持しつつ更新する形となっており、それにより、小さな動きのオブジェクトを見逃す傾向や、学習に相当数のEpochを要する課題などを解決している

  - (5)更新オペレーターが軽量である上に、反復回数が固定されていない、reccurent形式であるという特徴がある
      - [G]IRR(Iterative Residual Refinement for Joint Optical Flow and Occlusion Estimation)が、これを唯一行っており、FlowNetS、または、PWC-Netを、回帰ユニットして使用し、残差を埋めていくような形で、最大5回の反復を適用している(特に、PWC-Netを使用する場合、reccurentはピラミッドレベルの数によって制限される)
      - [H]対照的に、RAFTにおいては、更新演算子は270万個のパラメーターしかなく、発散すること無く100回のreccurentが適用可能

  - (6)更新演算子が、4Dのマルチスケール相関ボリュームでルックアップを実行する、畳込みGRUで構成されるという、新しい設計を備えている
      - [I]以前のモジュールであれば、単純な畳込み層、または、相関層のみの使用であった

(以下、IRR論文より、IRRのネットワーク図) image


また、縦1088x横436のビデオを、1080TiのGPUにて、毎秒10フレームで計算ができるとのことで、高速化の面でも利点があるとのことです。


具体的に行われているfeed forwardの計算を、プログラムで確認してみると、以下のようになっていました。

  - (1) 入力画像の値を正規化する
      - image1 = 2 * (image1 / 255.0) - 1.0
      - image2 = 2 * (image2 / 255.0) - 1.0

  - (2) convolution、normalization、reluや、ResidualBlockなどにて、入力画像指定のnormalizationをした後、stride=2のdown convolutionなどを行うことで、特徴を生成する
      - normalizationは、「GroupNorm」「BatchNorm2d」「InstanceNorm2d」という選択肢がある
      - fmap1, fmap2 = self.fnet([image1, image2])   

  - (3) (2)にて画像「Frame 1」と画像「Frame 2」とをconvolutionすることで生成した、2つの特徴の相関を要素とした、相関ボリュームクラスを生成する
      - 通常の相関ボリュームクラス「CorrBlock」と、高速計算のための「AlternateCorrBlock」とを選択できるようになっている

  - (4) 画像「Frame 1」のみに対して、別途、少し計算仕様の異なる(2)の処理を実施し、特徴を生成する

  - (5) (4)の特徴をチャンネルで2分し、一方にはhypebolic tanent、もう一方はreluにて活性化する

  - (6) Optical Flowを推定するための、画像「Frame 1」のpixelのジャンプ先座標と、画像「Frame 2」のpixelのジャンプ先座標の初期値を計算する
      - プログラム中のコメント「""" Flow is represented as difference between two coordinate grids flow = coords1 - coords0"""」
      - 座標の初期値計算方法「coords = torch.meshgrid(torch.arange(ht), torch.arange(wd))」

  - (7) 任意の繰り返し数だけ、反復のpredictionを実施する
      - 画像「Frame 1」のpixelのジャンプ先座標を、キャリブレーションするような形で繰り返し調整をするような形
      - Optical Flowの推定値は、ループの都度「flow = coords1 - coords0」という処理にて求める
      - 残差計算、及び、その計算の本となる(5)で2分した生成特徴を、「net, up_mask, delta_flow = self.update_block(net, inp, corr, flow)」という処理にて求める
          - 「update_block」処理の中に、GRUが含まれている
      - Optical Flowを推定するための座標値は、「coords1 = coords1 + delta_flow」という処理にて更新する
      - ループ毎のOptical Flow推定値は、upsampleを行う



GMAは、2021年の4月に論文が発表された最新手法です。 RAFTを超えるSOTA記録を打ち出しています。 オーストラリア国立大学、オーストラリアのクイーンズランド工科大学、アメリカのオックスフォード大学からの発表とのことです。 プログラムの実装者は、彼ら本人とのことです。

ネットワーク構造は、以下とのことです。

(GMAの構造) image

image


先述のRAFTをベースに構築された手法であるようです。 Githubリポジトリにも、「The overall code framework is adapted from RAFT.」と記載があり、実際にプログラムも非常に似ています。


PFNの岡野原さんが、twitterで以下のコメントをしています。 「GMAはオプティカルフローで、前フレームで見えない場合(オクルージョン、フレーム外にある)でも推定できるよう、一つの物体は同じように移動することを利用し、前フレームで見た目特徴が似ている位置から移動特徴量を自己注意機構で読み込む。SOTA(RAFT)を大きく改善」

このコメントを見る限りも、RAFTと行っていることは同様であるようです。


論文を追っていくと、RAFTネットワークアーキテクチャーの踏襲の旨が記載されています。 RAFTでは、空間の滑らかさをモデル化することを学習するGRUデコーダーで畳み込みが使用されますが、畳み込みの局所的な性質により、小さなオクルージョンに対応することはできるものの、大きなオクルージョンでは失敗する傾向があったそうです。 それは、1976年のGeoffrey Hintonさんの論文においても「ローカルの曖昧さは、最良のグローバルな解釈によって、解決する必要がある」と書いているそうです。 そこでGMAは、大局的なモーションベクトルの偏り等の、統計的バイアスを使用して、高い暗黙の信頼性を備えた非遮蔽ピクセルから、低い信頼性を備えた遮蔽ピクセルにモーションを伝搬するとのことです。 尚、このアイデアは、長距離の依存関係をモデル化する能力で知られる、トランスフォーマーからインスピレーションを得ているそうです。 RAFTをベースとしたネットワークにて、トランスフォーマーで実現される処理概念を実現する形で、定式化を行っているとのことです。


実際にプログラムを追ってみると、以下のような形になっていました。 尚、(1)、(2)、(3)、(4)、(5)、(6)の処理は、RAFTと全く同じ内容か、ほぼ同様の内容です。 (7)のみが異なる形になっています。

  - (1) 入力画像の値を正規化する
      - image1 = 2 * (image1 / 255.0) - 1.0
      - image2 = 2 * (image2 / 255.0) - 1.0

  - (2) convolution、normalization、reluや、ResidualBlockなどにて、入力画像指定のnormalizationをした後、stride=2のdown convolutionなどを行うことで、特徴を生成する
      - normalizationは、「GroupNorm」「BatchNorm2d」「InstanceNorm2d」という選択肢がある
      - fmap1, fmap2 = self.fnet([image1, image2])   

  - (3) (2)にて画像「Frame 1」と画像「Frame 2」とをconvolutionすることで生成した、2つの特徴の相関を要素とした、相関ボリュームクラスを生成する
      - 通常の相関ボリュームクラス「CorrBlock」のみが存在(RAFTには、高速計算のための「AlternateCorrBlock」とで選択できるようになっていた)

  - (4) 画像「Frame 1」のみに対して、別途、少し計算仕様の異なる(2)の処理を実施し、特徴を生成する

  - (5) (4)の特徴をチャンネルで2分し、一方にはhypebolic tanent、もう一方はreluにて活性化する

  - (6) Optical Flowを推定するための、画像「Frame 1」のpixelのジャンプ先座標と、画像「Frame 2」のpixelのジャンプ先座標の初期値を計算する
      - プログラム中のコメント「""" Flow is represented as difference between two coordinate grids flow = coords1 - coords0"""」
      - 座標の初期値計算方法「coords = torch.meshgrid(torch.arange(ht), torch.arange(wd))」

  - (7) 任意の繰り返し数だけ、反復のpredictionを実施する
      - 画像「Frame 1」のpixelのジャンプ先座標を、キャリブレーションするような形で繰り返し調整をするような形
      - Optical Flowの推定値は、ループの都度「flow = coords1 - coords0」という処理にて求める
      - 残差計算、及び、その計算の本となる(5)で2分した生成特徴を、「net, up_mask, delta_flow = self.update_block(net, inp, corr, flow)」という処理にて求める
          - 「update_block」処理の中にて、GMAが実現されている
            (※ここのみがGMAの独自コードで、attensionを用いた「motion_features_global = self.aggregator(attention, motion_features)」というコードが含まれている)
      - Optical Flowを推定するための座標値は、「coords1 = coords1 + delta_flow」という処理にて更新する
      - ループ毎のOptical Flow推定値は、upsampleを行う




2. Githubリポジトリの性質について

FlowNetPytorchリポジトリは、実装が5年以上前から実装がされている老舗リポジトリです。 https://github.com/ClementPinard/FlowNetTorch

このリポジトリは、学習済みモデルを用意してくれていて、推論と学習が実施しやすい形にしてくれている有り難いものです。 しかし、対応しているデータセットが、the flying chair datasetという椅子が浮遊するようなデータへの対応モデルだけである為、若干、汎用性は低いかと思われます。



flownet2-pytorchリポジトリは、こちらも4年以上前から実装がされている老舗リポジトリとなります。 https://github.com/NVIDIA/flownet2-pytorch

このリポジトリも学習モデルを用意してくれていて、推論と学習が実施しやすい形にしてくれている有り難いものです。 尚、学習済みモデルは、MPI-SintelというゲームキャラクターのCG動画フレームにて学習がされたものですので、汎化性は比較的高いかと思われます。

但し、requirementがかなり厳しく、RTX3090を使ってる為にcudaのバージョンが新しかったり、Google Colabを使っていたりする場合、実施が難しいです。 issueなどにも書かれていますが、Optical Flow推定値を元に、画像をwarpするためのResample2Dというモジュールが、cudaで実施されている為に、versionの指定が厳しい形であるようです。



MaskFlownetリポジトリは、2020年3月頃から実装されている最新目のリポジトリになります。 こちらは、officialのリポジトリが、mxnetによる実装となっています。 https://github.com/microsoft/MaskFlownet

mxnetは、GPUサポートが昨今遅れているようで、こちらのリポジトリについても、flownet2-pytorch同様に若干動かしづらい形となっています。 尚、pytorchでのimplementationも存在し、それが以下となっています。 https://github.com/cattaneod/MaskFlownet-Pytorch

ただし、pytorch implementationは、mxnetからconvertしたと思われる重みによる推論実施のみが行えるリポジトリで、学習は実施ができません。 issueにて、学習の実装を行う予定は未定と、オーナーの方が返答されていました。 その為、学習を行いたいとすると、officialのmxnetにて実現する必要があります。

尚、pytorchでのimplementationにて、推論を行った結果は以下となります。

(MaskFlownetをpixabay動画にかけた結果) car_flow_5adNov03

(MaskFlownetをMPI-Sintel動画にかけた結果) sintel_flow_5adNov03



RAFTリポジトリは、2020年5月頃から実装されている最新のリポジトリになります。 こちらは、officialのリポジトリが、pytorchによる実装となっています。 https://github.com/princeton-vl/RAFT

学習済みモデルはデータセット別に存在し、推論も学習もスクリプトが用意されている、素晴らしいリポジトリとなっています。 また、コードもスッキリとしていて、非常に読みやすいです。 尚、推論を試しに行ってみた結果は以下となります。

(RAFTをpixabay動画にかけた結果) car_flow_thing

(RAFTをMPI-Sintel動画にかけた結果) sintel_flow_sintel



GMAリポジトリは、2021年4月頃から実装されている最新のリポジトリになります。 こちらは、officialのリポジトリが、pytorchによる実装となっています。 https://github.com/zacjiang/GMA

こちらは、殆どRAFTの実装と合致します。 RAFTと同様に、学習済みモデルはデータセット別に存在し、推論も学習もスクリプトが用意されている形です。 尚、推論を試しに行ってみた結果は以下となります。

(GMAをpixabay動画にかけた結果) car_flow

(GMAをMPI-Sintel動画にかけた結果) sintel_flow