C++
画像認識
テスト駆動開発

画像認識技術とオブジェクト指向とテスト駆動開発

More than 1 year has passed since last update.

オブジェクト指向プログラミングを採用する以前、テスト駆動開発という言葉を耳にする前から画像認識技術の開発をしてきた。
その後、オブジェクト指向の設計とテスト駆動開発を心がけるようになってきてから思ったことを述べる。

オブジェクト指向とテスト駆動開発を知る前の状況

  • 性能の評価を自動化している。
    • 評価プログラムの実行結果をテキストファイルでログとして保存する。
    • そのテキストファイルから性能評価の指標を抽出する。
    • その指標の抽出プログラムは、awkやpythonやperlなどのプログラムで実現するなり、C/C++の言語でプログラムを書く。
    • その抽出プログラムも含めてバージョン管理する。
    • 時としてその結果もバージョン管理の対象とする。
    • 結果を変えないリファクタリングと、性能の変化を生じる改版とを区別しやすくする。
  • 再現性のある評価のできないソースコードでは、品質を確保することができないことを理解していた。
    • そのため,RCSやCVSなどのバージョン管理システムを利用していた。ただチームとしてのバージョン管理システムの共同での運用にはなりきらなくて、「ある目的のライブラリのなになに版は、○○さんが管理している。」といった状況であった。
  • 自動化した単体テストを利用しきってしていなかった。
    • 上記のように認識性能に関する部分は自動化していた。
    • 例外処理の仕組みがない言語を使っていたので、ライブラリとしての使われ方を確認しきれていなかった。
  • Doxygenを使ってライブラリのドキュメントを自動生成させることや、ヘッダーファイル中のドキュメンテーションコメントの活用をできていなかった。

    • 次第に、ドキュメンテーションコメントの有用性に気づき、Doxygenを使って関数のインタフェースを仕様として定めることをチームとして利用するようになりだした。
  • 動作ログをとるための記述のために、コアの動作がわかりにくくなってしまっていた。

    • 動作ログをとるためのインタフェースのために、追加の引数が必要になってしまっていた。そのために大元のソースコードと別のコードになってしまう。
    • 動作ログをとるためのインタフェースのために、大元のソースコードと#ifdef によって動作ログを出力する版との乖離が生じてしまう。

オブジェクト指向とテスト駆動開発を知って変わってきたこと

  • どう使いたいのかは、不完全な実装でも使ってみてからの方がわかりやすい。

    • 関数にするかクラスを利用するか
      • 選択肢:ひとつの巨大な関数での実装(設定と動作)
        • 実装が簡単。しかし、同じ設定を繰り返す分の無駄を生じてしまう。
      • 選択肢:クラスとそのメソッドによる実装(設定のメソッドと動作のメソッド)
        • 少しばかり実装の仕方を考える。設定を行う部分と動作を分離するので、同じ設定を何度も繰り返す無駄がなくなる。
        • 利用する関数の中で、初期化と設定のメソッドの記述する位置について考えておくこと。
    • 入出力
      • ある機能がファイルインタフェースなのか、画像キャプチャのインタフェースなのか。
      • 別々の実装にしない。
      • アルゴリズムのコアの部分はDRY原則を満たすように実装して、コードの乖離の問題を持ち込まない。
  • 動作ログの保存のような機能はメソッドとして実装する。

    • 動作ログの保存のような機能が通常の動作機能のコアの部分の動作をわかりにくくさせない。
    • 通常の動作のAPIを動作ログのon/offなどによって変えてしまわない。
    • 動作ログのon/offなどの違いはソースコードのごく少数の場所に局所化する。
  • APIの入出力に関わる部分を動作検証をしておくと、後々のトラブルが少ない。

    • ファイルがない、ファイルが空。ファイルが壊れている。そういったトラブルを予防しやすい。
    • アルゴリズムの実装者は、エラー処理に対して関心が低い場合が多い。
    • そのままエラー処理の不完全なソースコードが提供されてしまうことが防げる。
  • 性能評価のためのログ機能のあるソースコードと運用に使うコードとの差分を少なくできる。

  • エラー処理の部分について扱いを開発チームとしての方針を明確にもって反映させる。

    • 個人的な推奨:
      • 関数の戻り値によるエラー処理よりは、例外処理による方式を用いる。
      • 関数の戻り値を複数の階層でリレーすることを防ぐ。
      • 関数の戻り値をチェックするしないを、そのモジュールの利用者に委ねない。
  • 数値の戻り値のチェックには、それに適したライブラリを使う。

    • 数の大小関係のチェックや行列の値の比較には、numpyのような行列を上手に扱うライブラリを使ってチェックする。
  • github でのオープンソースの配布のしかたと、ドキュメンテーションの書き方が、仕事のソースコードの管理と仕事のチーム内での共有の仕方の見本となりだしている。

    • gitによるソースコードの管理、README.md というマークダウンファイルによる記述の標準化。
      • Requirement
      • Install
      • Usage などの項目が標準的なものとして期待されるようになる。

入力データ・正解データの管理

  • 画像認識の入力データ・正解データに適切な値が入っているとは限らない。
  • 入力データ・正解データはソースコードに比べてファイルサイズが大きすぎて、バージョン管理の対象にするのに適さない。
  • 入力データ・正解データは圧縮して日付付きのファイル名にするなどして、バージョン管理システムとは別途管理する。

追記

  • データの集計にはpandas を使うこと。

    • データの集計を楽にすることが、本業の開発を楽にする。
    • 集計しなおしを楽にしてドキュメンテーションを楽にするにはJupyter Notebook を使う。
  • 簡単に自動生成できる部分はファイルとして保存しない。

    • 画像の面内回転、画像の縮小、画像へのノイズの付加、ボケの付加などの影響評価などの場合には、自動でできる。
    • 評価プログラムの中で、自動生成して、その中間ファイルは保存しない。
    • その分、管理コストが減るし、ディスクを無駄に消費しない。
  • OpenCV3.x でのAPIの再設計

    • OpenCVの追跡アルゴリズムの関係が大幅に書き換えられている。
      • OpenCV 3.3 Introduction to OpenCV Tracker
      • 追跡アルゴリズムを使う際には、手法の違いを極力意識しなくて済むこと。
      • ソートの利用には、アルゴリズムの違いを意識しなくてよいようになっているのと同じ。
      • 2012年頃の追跡のコードのヘッダファイルの例では、track optical flowなどのアルゴリズムの詳細にかかわる部分が追跡クラスの最上位に見えてしまっていますが、OpenCV3.xのコードでは、アルゴリズムの違いの部分が隠されています。このことによって、追跡アルゴリズムの利用の仕方が大幅に簡単になっています。
  • Synopsys experience with OpenVX™ for a Face Tracking Application

Capturing real-life vision applications in OpenVX™ is challenging
- Some of the standard vision kernels are not flexible enough
- Not all vision kernels can easily be tiled
- Not all applications nicely fit into a data flow representation with
nodes that can be well balanced on the available core

OpenVXは画像認識アルゴリズムの標準化への試みの1つです。適切に標準化できて、しかもハードウェアでの高速化を容易にするAPIになっていてくれれば、ソフトウェアで実績をつんだアルゴリズムをハードウェアで高速化するのがやりやすくなると期待します。


今の時点で心がけていることを書きました。もしお役に立つ部分があれば幸いです。
「もっとこういういい方法がある」という情報は大歓迎です。