3Dカメラの利用と、画像認識・3D再構築・機械学習などを扱いやすくする設計方針を示すこと
- 視差画像・深度画像などの3D再構築関係の表示方法の例を提供する。
- 視差画像のcolormap変換とそれ以外の結果表示の方法を提供する。
- ステレオ計算の各種アルゴリズムの結果を上記の表示ツールと組合せやすいインタフェース設計案を示すこと
- アルゴリズムの置き換えを簡単にしていくこと
- 以下に示すサンプル実装などを例として、OSSのステレオ計算の実装を書き換える際のヒントを与えること
- 3Dカメラの方式の違いと、3Dカメラの典型例を紹介しつつ、3Dカメラの選択のヒントを与えること
- 3Dカメラそれ自体についてのインタフェースについて、SDKごとの差異をわかりやすくする。
- 可能ならば、それらが類似のインタフェースによってデータの取得できること。
- ステレオ計測の初心者によい取っ掛かりを提供すること
- 3Dカメラと画像認識・画像を利用する大規模言語モデルとの連携をしやすくすること
- 3DカメラのSDKの例にある画像認識との連携例を紹介する。
- 3DカメラのSDKで取得した点群データと、OSSの画像認識(例:物体検出)との組合せ方の例を示すこと
- 画像認識タスクを、自分たちの用途のシステムの一部とする際に、必要なAPI設計のあり方を示すこと
- 例:単一フレームのステレオ画像からの深度推定
- 例:過去の複数単一フレームのステレオ画像からの深度推定
- APIのインタフェースが違ってきます。
- 画像認識タスクを、前提とする条件をごとに、最近の実装の1例を示すことで、調査・実装するうえでのよい最初のとっかかりを与えること。
- 例:StereoSGBMは2020年代なかばにおいては、もう過去の実装である。もっとよい実装についての知見を共有していくこと。
- ロボットでのカメラのように制約が強い分野での、知見の共有をめざす。
- 仮説の生成と検証という枠組み
- ヒトは、見えていない領域に対しても仮説を立てながら行動している。
- 紙コップに手をやるとき、見えていない側の側面の状況について仮説を立てながら行動をしている。
- そして、常に仮説を検証して、仮説を修正することを繰りかえしてる。無意識的推論
- ベイズ脳仮説、自由エネルギー原理
- 個々の単一のフレームからの画像認識の推論では、ノイズによる影響を受けやすい。しかし、観測条件(例:対象物を見る向きなど)を変えると、前回の観測結果をふまえた条件付きの推論をすることで、 推論の妥当性を改善することができる(はずである)。
- どこかの時点で、感覚入力ー>隠れ状態の期待値ー>行動ー>隠れ状態ー>感覚入力 の繰り返しのできる3Dカメラでの画像認識であってほしいと願っている。
- 唯一の正しい設計指針はない。
- よいと思う設計指針を共有すること。
- よい実装は、時代によって変わる。
- マルチスレッドがなかった時代と、マルチスレッドの時代で、よい設計は変わっている。
- マルチコアのCPUでも動くonnxruntimeがある時代と、SIMD命令に頼るしかなかった時代でも、良い設計は変わっている。
- その時代のライブラリの状況によって、よいと思う設計指針は変わる。
- 実装が困難すぎる設計指針にしない。
- ステレオカメラの視差画像の可視化のためのライブラリを提供すること
- ステレオカメラ画像からの視差算出アルゴリズムの実行のためのライブラリ化のためのAPIの例を提供すること。
disparity_calculator = stereosgbm.DisparityCalculator(
window_size=window_size, min_disp=min_disp, max_disp=max_disp
)
disparity = disparity_calculator.predict(left.copy(), right.copy())
別なステレオ計測での実行例の1部では、次のようになる。 どちらでも、似たコードになる。 アルゴリズムの違いによる設定パラメータの違いは、インスタンスを作成する際のパラメータの違いとして吸収される。
disparity_calculator = stereoigev.DisparityCalculator(args=args)
disparity = disparity_calculator.predict(left.copy(), right.copy())
- どちらの場合も同じライブラリを用いて、
- 視差画像の可視化ができる
- 点群への変換ができる
- 再投影画像を作れる
- 視差(もしくは深度)をcolormap で表示するだけでは、ステレオ計算の結果の妥当性について目視レベルのチェックをするには不十分過ぎる。
- 点群に変換したあとに、視点の位置と向きを変えた再投影画像は、目視レベルのチェックを楽にする。
- また、法線ベクトルの向きに応じた色表示は、表面の状況を理解しやすくするものだ。
- アルゴリズムを置換え可能にする。
- 入力出力が同じ形式のアルゴリズムは、置換え可能なようにインタフェースを設計する。
- ちょうど、scikit-learnがそれぞれのアルゴリズムでfit(), predict()が共通の設計思想で実装されているのに近づける。
- スレレオ画像からの視差もしくは深度を算出するメソッドのインタフェースは、アルゴリズムの種類を変えても共通にする。
- 目的が同じ3Dカメラのインタフェースも、可能な範囲で置換え可能にする。
- 3Dカメラを用いた上位のアプリケーションと、3Dカメラ自体の機能とはインタフェースで切り離した設計にする。
- これによって、3Dカメラを用いた上位のアプリケーションを1から作り直すことを減らす。
- アルゴリズムのための各種設定パラメータのデフォルト値
- アルゴリズムごとに、必要な設定パラメータは異なる。
- その設定パラメータにデフォルト値が用意されていること。
- そのデフォルト値を使えば、それなりの動作をすること。
- 理由:従来の実装によっては、全てのパラメータを明示的に与えないと使用できず、しかもそのアルゴリズムでの推奨のパラメータが明示されないことがある。
- それが、利用者にとって使い勝手の悪さをもたらす。
- 推論のメソッド
- 推論のメソッドには、その実装で一番効率的なインタフェースを持つこと
- 他に、np.ndarray型の画像で、どの実装でも利用できるメソッドも持つこと 5. 推論メソッドの戻り値
- 利用目的が同一であれば、戻り値のデータ構造は極力同一であること。
- 例:矩形検出枠での検出結果の戻り値
- 差し替え可能なインタフェース:
- 検出アルゴリズムのライブラリが変わっても、同一であること。
- 学習用のライブラリとの整合性のよい戻り値のインタフェース:
- 推論した結果は、機械学習の学習用に転用される場合もあります。
- そのためにも、学習用のライブラリとの整合性のよい戻り値のインタフェースが必要になります。 - 推論結果のファイルインタフェース:
- 最近のアノテーションツールで利用されているデータ形式を検討しましょう。
- 理由:
- アノテーションツールの自作はしたくないため。
- 標準化されているツールだと、評価結果の集計がしやすい。
- 差し替え可能なインタフェース:
- 学習用・評価用のデータセットのデータ構造を気にする。
- 着目しているタスク: 代表的なデータデータ・セット
- そのデータセットで学習・評価するためのスクリプト
- 表示系の機能を、個々のアルゴリズム・個々の3Dカメラの実装から切り離す。
- 表示系の開発コストを削減する。
- 画像ファイルでの確認用のインタフェース
- リアルタイム表示用のインタフェース
- 3D表示用のインタフェース
- アルゴリズム利用の際の結果データのデータ形式
- ネットワーク間でそのデータを転送する際のインタフェース
- それぞれのモジュールがカメラを接続しようとはしないこと
- 実装例:モジュールじたいの中では,カメラのインスタンスを生成しない。mainの中でカメラのインスタンスを生成する。
- 理由:1つのカメラに接続できるのは、1つのアプリケーションだけである。
設計の例
- インスタンスを生成する際に、最小限の設定をすれば、動作すること
- defaultでも動作できるようにしておく
- メソッドの実行の際には、画像を渡せば実行できるようにする。
- 推論のメソッドで公開するインタフェース
- np.ndarray のインタフェース
- その実装で、もっとも効果的なインタフェース
推奨:機械学習を含む場合でも、ユーザーに公開するAPIにおいては、後処理込みのものとすること 非推奨: 深層学習後のネットワークの出力そのままを返すので、解釈可能にするためにユーザー側が後処理を別に実施する。
- グローバル変数を極力残さない
- 意味のまとまりが、classにまとめられていない。
-
1:np.ndarray型 (numpy, opencv)
- np.ndarray型の場合には、色順序がRGBなのかBGRなのかは明示する。
- cv2.imread(filename)
- skimage.io.imread(filename)
- imageio.imread(filename) np.ndarray型では、画像の画素値を整数値で扱う場合もあるし、float型を使う場合もある。 float型の使われ方としては、[0.0 - 1.0]の間の連続値として用いられている場合もある。 skimageの方がfloat型の場合の対応されている範囲が広い。
-
2:PIL.Image型
- Image.open(imfile)
-
3: open3d.geometry.Image型
- open3d.io.read_image(filename)
-
4: Torch, TorchVision Tensor 型では、データをCPUだけではなく、GPUを指定できる。 (そのため、処理をGPUで完結させたいときには、CPUとGPU間のデータ転送を発生させないようにコーディングするのがのぞましい。)
-
torch.from_numpy()
-
class torchvision.tv_tensors.Image(data: Any, *, dtype: Optional[dtype] = None, device: Optional[Union[device, str, int]] = None, requires_grad: Optional[bool] = None https://pytorch.org/vision/main/generated/torchvision.tv_tensors.Image.html
-
5: tf.Tensor 型 Tensorflow GPU, TPUに対応している。
- 商用の画像認識ライブラリは、独自のデータ型を用いている場合がある。
- そして多くの場合は、np.ndarray型と相互変換できる補助関数を用意している。
- 1: StereoLabs ZED SDK の sl.Mat() 型
- 画像の取得時刻情報などを含んだデータ形式であり、get_data()でnp.ndarray型に変換できる。
- ただ、チャネル数が4になっていることに注意
import pyzed.sl as sl
zed = sl.Camera()
image = sl.Mat()
zed.retrieve_image(image, sl.VIEW.LEFT, sl.MEM.CPU, display_resolution)
cv_img = image.get_data()
- チャネル数が、そのAPIで使用するものと一致していること
- 部分画像を渡したいときには
image[100:200, 300:400, :].copy()
などとして連続な領域として渡す
TensorFlowのTensor型ではデータは、CPU、GPUのどちらにも置ける。 Open3DのデータもCPU、GPUのどちらにも置ける。 画像データを一貫してGPU上に置ける場合には、GPU-CPU間のデータ転送が不要になる。
しかし、大半の画像データ形式は、CPU上に限られる。
- 手法1:3DカメラのSDK依存のデータ構造とライブラリ
- この場合だと3DカメラのSDKの種類ごとに、異なるデータ形式になる。
- 手法2:Open3D のデータ構造とライブラリ - Open3Dの場合、それぞれのデータが明示的に型設定されている。 - そのため、定型的な操作は実装済みの関数・メソッドの呼び出しとなる。 - 引数がどの型なのかによって、変数の意味が明解になる。
- 手法3:OpenCVやnumpyの範囲のデータ構造とライブラリ
- OpenCVでは3次元のデータとしてむき出しのデータ構造になっている。 - 線形代数を扱うにはちょうどよい。 - 引数のデータの型がnp.ndarray 型になんでもなってしまうので、変数の意味合いがわかりにくい。 - OpenCVそれ自体には3次元データを扱うための関数群が不足している。 - そのため、Open3Dならば用意されている機能を各自が実装してしまう。
- 手法4:Unityなどの3Dのゲームエンジン
- 3DカメラのSDKを利用して、点群データを取り込む
- OpenCVを使うよりは、Open3Dを使うほうがのぞましい
- 理由:
- OpenCVベースの自作ライブラリは負債になる。
- 可読性が低い。間違えた使い方を見つけにくい。
- 並列演算のプラットフォーム対応を、利用するライブラリのコミュニティに委ねたい。
- たとえ1行で書ける関数であっても、テストなしの自作関数はバグの温床になる。
- OpenCVでの不具合の例
- データの型が大半がnp.ndarrayもしくはcv::Mat型になってしまうので、データの型によって間違えた使い方を防止することができない。
ロボットに使われる画像計測・画像認識についてトレース可能なテストがほしい。 そのカメラとそのアルゴリズムに対して、どのようなデータでどのようなテストをしているのかが トレースできる環境を構築する。
- 個々のリポジトリごとにテストを行う。
- アルゴリズム自体のテストのためには、カメラが不要でテストする。
- テスト用に使用するデータは、誰でもがダウンロード可能にして、誰でもがテスト可能な状況を目指す。
- CircleCIなどでは対応していないボード・CPUの場合には、ローカルなデバイス上でテスト自動化を目指す。
画像計測アルゴリズム・画像認識アルゴリズムの単体テストは、センサなしでも実現できる。 入力データをファイルにしておいて、それを読んで動作させて、結果を期待値と比較することでテストが可能である。 画像データは、pngファイルなどの損失のないデータを用いる。 pythonの場合:npyファイル, npz ファイルを用いることで、numpyで表されるデータを保存できる。 それらを使うことで、センサなしで、アルゴリズムへの単体テストができる。
- センサとそのSDKによっては、センサデータを独自形式でファイルに保存できるものがある。 それを使うと、カメラデバイスを外した状況で、あたかもカメラを接続したような動作ができる。 そのことによって、同じセンサデータの時系列を与えたときの出力を、アルゴリズムの種類・パラメータの変更によってどうなるのかを比較できる。 例: StereoLabs ZED SDK recording/playback
ros2bag という仕組みがあって、センサデータをほrecordしplayback する機能がある。
python3 -m pip install .
でモジュールをパッケージとしてインストールできるようにする。 あるいは、pypi に登録して
python3 -m pip install パッケージ名
ができることを目指す。 そのため、モジュール名、パッケージ名は既存のpypi に登録してあるのと名前がぶつからないように付ける。
組み込み分野の画像計測・画像認識処理では、本番の運用の中では、画像表示を持たないことが多い。 そのため、アルゴリズムの実行の中では、画面表示をもつべきではない。 Linuxの場合にはX11の表示先を期待してはならない。 cv2.imshow()を必要としてはならない。
- アルゴリズムのモジュール自体は標準出力にモジュールはメッセージを表示すべきではない。
- 実装先のシステムが用意しているlogシステムに出力する。
- pythonの場合にはloggingがある。
なるべく最新のC++の規格を採用したコーディングを心がけましょう。
- The OpenCV Coding Style Guide
- Open3D style guide
- ROS2 coding style
ROS2では
Rolling targets C++17.
と言っている。 3Dカメラを利用したアルゴリズムもそれに従うのがよい。
CMakeを使ってライブラリの作成、サンプルの実行形式を作れるようにしましょう。
- CPUの種類(bit数、エンディアン)によらないデータ形式を用いる。
- 機械可読性が必要とされるテキストファイルの場合には、以下のようなコンピュータ言語によらず可読な標準的なデータ形式を用いる。
- json
- yaml
- toml
- pythonの場合の数値データの保存
- npy ファイルを用います。NaN, PosInf, NegInfなどの値を持つことができます。
- OpenEXR: ハイダイナミックレンジイメージのための画像ファイルフォーマット
- ply: 点群データのファイルフォーマット。Open3Dで読み書きできます。
pickle pythonのオブジェクトをほとんどなんでも保存できてしまうデータ形式です。 そのため、loadしたpickle オブジェクトを含むなにかを実行した時に、何が起こるかわからないという致命的な欠陥があります。
- 可能な範囲でOpenSourceで標準になりつつある実装に寄せていく。
- 商用実装の利用が避けられない場合でも、APIの実装は極力標準化するようにつとめる。
- 有用な実装は、世界のどこでも利用できるべきです。
- 有用な実装は、英語の範囲でソースコードが記述され、英語のドキュメントを含んでいなければなりません。
- C++ を置き換える言語の候補の一つがRustです。
- Rust bindings for OpenCV 3 & 4
- https://github.com/rust-cv
- RustでGCP VisionAIを使ってチョコっと物体検出してみる.APIキーで.